diff --git a/.github/workflows/makefile.yml b/.github/workflows/makefile.yml new file mode 100644 index 00000000000..6a80cf191a9 --- /dev/null +++ b/.github/workflows/makefile.yml @@ -0,0 +1,91 @@ +name: test +run-name: ${{ github.workflow }} - ${{ github.sha }} +on: + push: + branches: [ main, up ] + pull_request: + branches: [ main, up ] + workflow_dispatch: + + +jobs: + ffvvc-test: + name: ffvvc-test / ${{ matrix.os.name }}/${{ matrix.compiler.name }}/${{ matrix.assembler.name }} + env: + configure_flags: --enable-ffmpeg --disable-everything --enable-decoder=vvc --enable-parser=vvc --enable-demuxer=vvc --enable-protocol=file,pipe --enable-encoder=rawvideo --enable-muxer=rawvideo,md5 + strategy: + fail-fast: false + matrix: + os: + - { name: linux, runner: ubuntu-latest, shell: bash, runner_threads: 4 } + - { name: windows, runner: windows-latest, shell: 'msys2 {0}', runner_threads: 1 } + compiler: + - { name: gcc, flags: --cc=gcc } + - { name: clang, flags: --cc=clang } + - { name: msvc, flags: --toolchain=msvc } + assembler: + - { name: no asm, flags: --disable-asm } + - { name: yasm, flags: --as=yasm } + - { name: nasm, flags: --as=nasm } + exclude: + - os: { name: linux, runner: ubuntu-latest, shell: bash, runner_threads: 4} + compiler: { name: msvc, flags: --toolchain=msvc } + - os: { name: linux, runner: ubuntu-latest, shell: bash, runner_threads: 4 } + assembler: { name: yasm, flags: --as=yasm } + - os: { name: linux, runner: ubuntu-latest, shell: bash, runner_threads: 4 } + assembler: { name: nasm, flags: --as=nasm } + - os: { name: windows, runner: windows-latest, shell: 'msys2 {0}', runner_threads: 1 } + compiler: { name: gcc, flags: --cc=gcc } + - os: { name: windows, runner: windows-latest, shell: 'msys2 {0}', runner_threads: 1 } + compiler: { name: clang, flags: --cc=clang } + + runs-on: ${{ matrix.os.runner }} + defaults: + run: + shell: ${{ matrix.os.shell }} + + steps: + - name: Get MSVC + if: ${{ matrix.compiler.name == 'msvc' && matrix.os.name == 'windows' }} + uses: ilammy/msvc-dev-cmd@v1 + + - name: Set up MSYS2 + if: ${{ matrix.os.shell == 'msys2 {0}' }} + uses: msys2/setup-msys2@v2 + with: + release: false + msystem: UCRT64 + path-type: inherit + install: >- + make + diffutils + + - name: Get assembler + if: ${{ matrix.os.shell == 'msys2 {0}' && matrix.assembler.name != 'no asm' }} + run: pacman --noconfirm -S ${{ matrix.assembler.name }} + + - name: Get source + uses: actions/checkout@v3 + with: + path: FFmpeg + + - name: Configure + run: cd FFmpeg && ./configure ${{ matrix.compiler.flags }} ${{ matrix.assembler.flags }} ${{ env.configure_flags }} || (tail ffbuild/config.log; false) + + - name: Build + run: cd FFmpeg && make -j8 + + - name: Get tests + uses: actions/checkout@v3 + with: + repository: ffvvc/tests + path: tests + + - name: Unit test + run: python3 tests/tools/ffmpeg.py --threads ${{ matrix.os.runner_threads }} --ffmpeg-path=./FFmpeg/ffmpeg tests/conformance/passed + + - name: Check ASM + run: cd FFmpeg && make checkasm -j && ./tests/checkasm/checkasm + + - name: Negative test + run: python3 tests/tools/ffmpeg.py --threads ${{ matrix.os.runner_threads }} --ffmpeg-path=./FFmpeg/ffmpeg tests/conformance/failed || true diff --git a/CREDITS b/CREDITS index e29f0b853c9..f1aea93d6b1 100644 --- a/CREDITS +++ b/CREDITS @@ -1,6 +1,6 @@ -See the Git history of the project (git://source.ffmpeg.org/ffmpeg) to +See the Git history of the project (https://git.ffmpeg.org/ffmpeg) to get the names of people who have contributed to FFmpeg. To check the log, you can type the command "git log" in the FFmpeg source directory, or browse the online repository at -http://source.ffmpeg.org. +https://git.ffmpeg.org/ffmpeg diff --git a/Changelog b/Changelog index 179f63c7d53..fa48d2811fb 100644 --- a/Changelog +++ b/Changelog @@ -2,6 +2,34 @@ Entries are sorted chronologically from oldest to youngest within each release, releases are sorted from youngest to oldest. version : +- libaribcaption decoder +- Playdate video decoder and demuxer +- Extend VAAPI support for libva-win32 on Windows +- afireqsrc audio source filter +- arls filter +- ffmpeg CLI new option: -readrate_initial_burst +- zoneplate video source filter +- command support in the setpts and asetpts filters +- Vulkan decode hwaccel, supporting H264, HEVC and AV1 +- color_vulkan filter +- bwdif_vulkan filter +- nlmeans_vulkan filter +- RivaTuner video decoder +- xfade_vulkan filter +- vMix video decoder +- Essential Video Coding parser, muxer and demuxer +- Essential Video Coding frame merge bsf +- bwdif_cuda filter +- Microsoft RLE video encoder +- Raw AC-4 muxer and demuxer +- Raw VVC bitstream parser, muxer and demuxer +- Bitstream filter for editing metadata in VVC streams +- Bitstream filter for converting VVC from MP4 to Annex B +- scale_vt filter for videotoolbox +- transpose_vt filter for videotoolbox +- support for the P_SKIP hinting to speed up libx264 encoding + +version 6.0: - Radiance HDR image support - ddagrab (Desktop Duplication) video capture filter - ffmpeg -shortest_buf_duration option @@ -28,6 +56,23 @@ version : - showcwt multimedia filter - corr video filter - adrc audio filter +- afdelaysrc audio filter +- WADY DPCM decoder and demuxer +- CBD2 DPCM decoder +- ssim360 video filter +- ffmpeg CLI new options: -stats_enc_pre[_fmt], -stats_enc_post[_fmt], + -stats_mux_pre[_fmt] +- hstack_vaapi, vstack_vaapi and xstack_vaapi filters +- XMD ADPCM decoder and demuxer +- media100 to mjpegb bsf +- ffmpeg CLI new option: -fix_sub_duration_heartbeat +- WavArc decoder and demuxer +- CrystalHD decoders deprecated +- SDNS demuxer +- RKA decoder and demuxer +- filtergraph syntax in ffmpeg CLI now supports passing file contents + as option values, by prefixing option name with '/' +- hstack_qsv, vstack_qsv and xstack_qsv filters version 5.1: diff --git a/MAINTAINERS b/MAINTAINERS index 48e2ec4fd4d..b5c116c3d4d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -151,7 +151,6 @@ Codecs: ccaption_dec.c Anshul Maheshwari, Aman Gupta cljr Alex Beregszaszi cpia.c Stephan Hilb - crystalhd.c Philip Langdale cscd.c Reimar Doeffinger cuviddec.c Timo Rothenpieler dca* foo86 @@ -211,6 +210,7 @@ Codecs: mqc* Nicolas Bertrand msmpeg4.c, msmpeg4data.h Michael Niedermayer msrle.c Mike Melanson + msrleenc.c Tomas Härdin msvideo1.c Mike Melanson nuv.c Reimar Doeffinger nvdec*, nvenc* Timo Rothenpieler @@ -265,7 +265,6 @@ Codecs: xwd* Paul B Mahol Hardware acceleration: - crystalhd.c Philip Langdale dxva2* Hendrik Leppkes, Laurent Aimar, Steve Lhomme d3d11va* Steve Lhomme mediacodec* Matthieu Bouron, Aman Gupta @@ -419,6 +418,7 @@ Muxers/Demuxers: dv.c Roman Shaposhnik electronicarts.c Peter Ross epafdec.c Paul B Mahol + evc* Samsung (Dawid Kozinski) ffm* Baptiste Coudurier flic.c Mike Melanson flvdec.c Michael Niedermayer @@ -429,10 +429,12 @@ Muxers/Demuxers: idcin.c Mike Melanson idroqdec.c Mike Melanson iff.c Jaikrishnan Menon + imf* Pierre-Anthony Lemieux img2*.c Michael Niedermayer ipmovie.c Mike Melanson ircam* Paul B Mahol iss.c Stefan Gehrer + jpegxl_anim_dec.c Leo Izen jpegxl_probe.* Leo Izen jvdec.c Peter Ross kvag.c Zane van Iperen @@ -544,6 +546,7 @@ LoongArch Shiyou Yin Mac OS X / PowerPC Romain Dolbeau, Guillaume Poirier Amiga / PowerPC Colin Ward Linux / PowerPC Lauri Kasanen +RISC-V Rémi Denis-Courmont Windows MinGW Alex Beregszaszi, Ramiro Polla Windows Cygwin Victor Paesa Windows MSVC Matthew Oliver, Hendrik Leppkes @@ -615,7 +618,7 @@ Haihao Xiang (haihao) 1F0C 31E8 B4FE F7A4 4DC1 DC99 E0F5 76D4 76FC 437F Jaikrishnan Menon 61A1 F09F 01C9 2D45 78E1 C862 25DC 8831 AF70 D368 James Almer 7751 2E8C FD94 A169 57E6 9A7A 1463 01AD 7376 59E0 Jean Delvare 7CA6 9F44 60F1 BDC4 1FD2 C858 A552 6B9B B3CD 4E6A -Leo Izen (thebombzen) B6FD 3CFC 7ACF 83FC 9137 6945 5A71 C331 FD2F A19A +Leo Izen (Traneptora) B6FD 3CFC 7ACF 83FC 9137 6945 5A71 C331 FD2F A19A Loren Merritt ABD9 08F4 C920 3F65 D8BE 35D7 1540 DAA7 060F 56DE Lynne FE50 139C 6805 72CA FD52 1F8D A2FE A5F0 3F03 4464 Michael Niedermayer 9FF2 128B 147E F673 0BAD F133 611E C787 040B 0FAB @@ -626,6 +629,7 @@ Nikolay Aleksandrov 8978 1D8C FB71 588E 4B27 EAA8 C4F0 B5FC E011 13B1 Panagiotis Issaris 6571 13A3 33D9 3726 F728 AA98 F643 B12E ECF3 E029 Peter Ross A907 E02F A6E5 0CD2 34CD 20D2 6760 79C5 AC40 DD6B Philip Langdale 5DC5 8D66 5FBA 3A43 18EC 045E F8D6 B194 6A75 682E +Pierre-Anthony Lemieux (pal) F4B3 9492 E6F2 E4AF AEC8 46CB 698F A1F0 F8D4 EED4 Ramiro Polla 7859 C65B 751B 1179 792E DAE8 8E95 8B2F 9B6C 5700 Reimar Doeffinger C61D 16E5 9E2C D10C 8958 38A4 0899 A2B9 06D4 D9C7 Reinhard Tartler 9300 5DC2 7E87 6C37 ED7B CA9A 9808 3544 9453 48A4 diff --git a/Makefile b/Makefile index 1fb742f3909..bf1b69f96b0 100644 --- a/Makefile +++ b/Makefile @@ -67,6 +67,8 @@ tools/target_io_dem_fuzzer$(EXESUF): tools/target_io_dem_fuzzer.o $(FF_DEP_LIBS) tools/enum_options$(EXESUF): ELIBS = $(FF_EXTRALIBS) tools/enum_options$(EXESUF): $(FF_DEP_LIBS) +tools/enc_recon_frame_test$(EXESUF): $(FF_DEP_LIBS) +tools/enc_recon_frame_test$(EXESUF): ELIBS = $(FF_EXTRALIBS) tools/scale_slice_test$(EXESUF): $(FF_DEP_LIBS) tools/scale_slice_test$(EXESUF): ELIBS = $(FF_EXTRALIBS) tools/sofa2wavs$(EXESUF): ELIBS = $(FF_EXTRALIBS) diff --git a/compat/w32pthreads.h b/compat/w32pthreads.h index 6405e72b64f..dae8d9420db 100644 --- a/compat/w32pthreads.h +++ b/compat/w32pthreads.h @@ -66,7 +66,14 @@ typedef CONDITION_VARIABLE pthread_cond_t; #define PTHREAD_CANCEL_ENABLE 1 #define PTHREAD_CANCEL_DISABLE 0 -static av_unused unsigned __stdcall attribute_align_arg win32thread_worker(void *arg) +#if HAVE_WINRT +#define THREADFUNC_RETTYPE DWORD +#else +#define THREADFUNC_RETTYPE unsigned +#endif + +static av_unused THREADFUNC_RETTYPE +__stdcall attribute_align_arg win32thread_worker(void *arg) { pthread_t *h = (pthread_t*)arg; h->ret = h->func(h->arg); diff --git a/compat/windows/mswindres b/compat/windows/mswindres index 450525a33e3..8c14c96bae4 100755 --- a/compat/windows/mswindres +++ b/compat/windows/mswindres @@ -1,7 +1,7 @@ #!/bin/sh if [ "$1" = "--version" ]; then - rc.exe /? + rc.exe -? exit $? fi @@ -10,12 +10,12 @@ if [ $# -lt 2 ]; then exit 0 fi -EXTRA_OPTS="/nologo" +EXTRA_OPTS="-nologo" while [ $# -gt 2 ]; do case $1 in - -D*) EXTRA_OPTS="$EXTRA_OPTS /d$(echo $1 | sed -e "s/^..//" -e "s/ /\\\\ /g")" ;; - -I*) EXTRA_OPTS="$EXTRA_OPTS /i$(echo $1 | sed -e "s/^..//" -e "s/ /\\\\ /g")" ;; + -D*) EXTRA_OPTS="$EXTRA_OPTS -d$(echo $1 | sed -e "s/^..//" -e "s/ /\\\\ /g")" ;; + -I*) EXTRA_OPTS="$EXTRA_OPTS -i$(echo $1 | sed -e "s/^..//" -e "s/ /\\\\ /g")" ;; -o) OPT_OUT="$2"; shift ;; esac shift @@ -29,4 +29,4 @@ else fi eval set -- $EXTRA_OPTS -rc.exe "$@" /fo "$OUT" "$IN" +rc.exe "$@" -fo "$OUT" "$IN" diff --git a/configure b/configure index df69d396692..f751f15d023 100755 --- a/configure +++ b/configure @@ -218,6 +218,7 @@ External library support: --enable-lcms2 enable ICC profile support via LittleCMS 2 [no] --enable-libaom enable AV1 video encoding/decoding via libaom [no] --enable-libaribb24 enable ARIB text and caption decoding via libaribb24 [no] + --enable-libaribcaption enable ARIB text and caption decoding via libaribcaption [no] --enable-libass enable libass subtitles rendering, needed for subtitles and ass filter [no] --enable-libbluray enable BluRay reading using libbluray [no] @@ -235,6 +236,7 @@ External library support: --enable-libfontconfig enable libfontconfig, useful for drawtext filter [no] --enable-libfreetype enable libfreetype, needed for drawtext filter [no] --enable-libfribidi enable libfribidi, improves drawtext filter [no] + --enable-libharfbuzz enable libharfbuzz, needed for drawtext filter [no] --enable-libglslang enable GLSL->SPIRV compilation via libglslang [no] --enable-libgme enable Game Music Emu via libgme [no] --enable-libgsm enable GSM de/encoding via libgsm [no] @@ -326,7 +328,6 @@ External library support: --disable-securetransport disable Secure Transport, needed for TLS support on OSX if openssl and gnutls are not used [autodetect] --enable-vapoursynth enable VapourSynth demuxer [no] - --disable-vulkan disable Vulkan code [autodetect] --disable-xlib disable xlib [autodetect] --disable-zlib disable zlib [autodetect] @@ -353,6 +354,7 @@ External library support: --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] + --disable-vulkan disable Vulkan code [autodetect] Toolchain options: --arch=ARCH select architecture [$arch] @@ -412,7 +414,7 @@ Toolchain options: --build-suffix=SUFFIX library name suffix [] --enable-pic build position-independent code --enable-thumb compile for Thumb instruction set - --enable-lto use link-time optimization + --enable-lto[=arg] use link-time optimization --env="ENV=override" override the environment variables Advanced options (experts only): @@ -453,6 +455,8 @@ Optimization options (experts only): --disable-armv6t2 disable armv6t2 optimizations --disable-vfp disable VFP optimizations --disable-neon disable NEON optimizations + --disable-dotprod disable DOTPROD optimizations + --disable-i8mm disable I8MM 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 @@ -1153,6 +1157,43 @@ check_insn(){ check_as ${1}_external "$2" } +check_arch_level(){ + log check_arch_level "$@" + level="$1" + check_as tested_arch_level ".arch $level" + enabled tested_arch_level && as_arch_level="$level" +} + +check_archext_insn(){ + log check_archext_insn "$@" + feature="$1" + instr="$2" + # Check if the assembly is accepted in inline assembly. + check_inline_asm ${feature}_inline "\"$instr\"" + # We don't check if the instruction is supported out of the box by the + # external assembler (we don't try to set ${feature}_external) as we don't + # need to use these instructions in non-runtime detected codepaths. + + disable $feature + + enabled as_arch_directive && arch_directive=".arch $as_arch_level" || arch_directive="" + + # Test if the assembler supports the .arch_extension $feature directive. + arch_extension_directive=".arch_extension $feature" + test_as <= 3 && AVS_MINOR_VER >= 7 && AVS_BUGFIX_VER >= 1 || AVS_MAJOR_VER >= 3 && AVS_MINOR_VER > 7 || AVS_MAJOR_VER > 3" || - die "ERROR: AviSynth+ header version must be >= 3.7.1"; } } + { 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 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; } @@ -6554,6 +6676,7 @@ enabled libaom && require_pkg_config libaom "aom >= 1.0.0" aom/aom_co 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 >= 0.1.0" "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 @@ -6577,6 +6700,7 @@ 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 libglslang && { check_lib spirv_compiler glslang/Include/glslang_c_interface.h glslang_initialize_process \ -lglslang -lMachineIndependent -lOSDependent -lHLSL -lOGLCompiler -lGenericCodeGen \ -lSPVRemapper -lSPIRV -lSPIRV-Tools-opt -lSPIRV-Tools -lpthread -lstdc++ -lm || @@ -6644,7 +6768,8 @@ enabled libopenh264 && require_pkg_config libopenh264 openh264 wels/codec_ 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 && require libopenvino c_api/ie_c_api.h ie_c_api_version -linference_engine_c_api +enabled libopenvino && { 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 @@ -6895,8 +7020,7 @@ enabled alsa && { check_pkg_config alsa alsa "alsa/asoundlib.h" snd_pcm_htimesta 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 || - check_lib sndio sndio.h sio_open -lsndio; } +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 || @@ -6932,6 +7056,21 @@ 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 requires linking directly to mfplat if building for uwp target enabled uwp && mediafoundation_extralibs="-lmfplat -lmfuuid -lole32 -lstrmiids" || mediafoundation_extralibs="-lmfuuid -lole32 -lstrmiids" @@ -6942,7 +7081,14 @@ enabled vaapi && check_pkg_config vaapi "libva >= 0.35.0" "va/va.h" vaInitialize if enabled vaapi; then - check_pkg_config vaapi_drm "libva-drm" "va/va_drm.h" vaGetDisplayDRM + 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 @@ -6994,11 +7140,13 @@ enabled vdpau && enabled vdpau && check_lib vdpau_x11 "vdpau/vdpau.h vdpau/vdpau_x11.h" vdp_device_create_x11 -lvdpau -lX11 -enabled crystalhd && check_lib crystalhd "stdint.h libcrystalhd/libcrystalhd_if.h" DtsCrystalHDVersion -lcrystalhd +enabled crystalhd && check_lib crystalhd "stdint.h libcrystalhd/libcrystalhd_if.h" DtsCrystalHDVersion -lcrystalhd && \ + warn "CrystalHD support is deprecated and will be removed, please contact the developers if you are interested" \ + "in maintaining it." if enabled vulkan; then - check_pkg_config_header_only vulkan "vulkan >= 1.2.189" "vulkan/vulkan.h" "defined VK_VERSION_1_2" || - check_cpp_condition vulkan "vulkan/vulkan.h" "defined(VK_VERSION_1_3) || (defined(VK_VERSION_1_2) && VK_HEADER_VERSION >= 189)" + check_pkg_config_header_only vulkan "vulkan >= 1.3.238" "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 >= 238)" fi if enabled x86; then @@ -7027,7 +7175,7 @@ enabled nvenc && test_cc -I$source_path < NV_ENCODE_API_FUNCTION_LIST flist; -void f(void) { struct { const GUID guid; } s[] = { { NV_ENC_PRESET_HQ_GUID } }; } +void f(void) { struct { const GUID guid; } s[] = { { NV_ENC_CODEC_H264_GUID } }; } int main(void) { return 0; } EOF @@ -7041,7 +7189,7 @@ 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) >= 0x0001000400090000" + "(AMF_VERSION_MAJOR << 48 | AMF_VERSION_MINOR << 32 | AMF_VERSION_RELEASE << 16 | AMF_VERSION_BUILD_NUM) >= 0x00010004001d0000" # Funny iconv installations are not unusual, so check it after all flags have been set if enabled libc_iconv; then @@ -7167,17 +7315,17 @@ fi check_optflags(){ check_cflags "$@" - enabled lto && check_ldflags "$@" + [ -n "$lto" ] && check_ldflags "$@" } check_optflags $optflags check_optflags -fno-math-errno check_optflags -fno-signed-zeros -if enabled lto; then +if [ -n "$lto" ]; then test "$cc_type" != "$ld_type" && die "LTO requires same compiler and linker" - check_cflags -flto - check_ldflags -flto $cpuflags + check_cflags $lto + check_ldflags $lto $cpuflags disable inline_asm_direct_symbol_refs fi @@ -7208,7 +7356,7 @@ if enabled icc; then # icc 11.0 and 11.1 work with ebp_available, but don't pass the test enable ebp_available # The test above does not test linking - enabled lto && disable symver_asm_label + [ -n "$lto" ] && disable symver_asm_label if enabled x86_32; then icc_version=$($cc -dumpversion) test ${icc_version%%.*} -ge 11 && @@ -7340,12 +7488,6 @@ esac enable frame_thread_encoder -# these filters depend on removed avcodec APIs -# they are kept disabled for now, but will be removed if -# nobody updates and re-enables them -disable mcdeint_filter -disable uspp_filter - enabled asm || { arch=c; disable $ARCH_LIST $ARCH_EXT_LIST; } check_deps $CONFIG_LIST \ @@ -7563,7 +7705,8 @@ if enabled x86; then fi if enabled aarch64; then echo "NEON enabled ${neon-no}" - echo "VFP enabled ${vfp-no}" + echo "DOTPROD enabled ${dotprod-no}" + echo "I8MM enabled ${i8mm-no}" fi if enabled arm; then echo "ARMv5TE enabled ${armv5te-no}" @@ -7854,6 +7997,9 @@ test -n "$assert_level" && 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 <. + +@anchor{Resolution Process} +@section Resolution Process + +The Technical Committee (TC) is here to arbitrate and make decisions when technical conflicts occur in the project. + +The TC main role is to resolve technical conflicts. It is therefore not a technical steering committee, but it is understood that some decisions might impact the future of the project. + +@subsection Seizing + +The TC can take possession of any technical matter that it sees fit. + +To involve the TC in a matter, email tc@ or CC them on an ongoing discussion. + +As members of TC are developers, they also can email tc@ to raise an issue. +@subsection Announcement + +The TC, once seized, must announce itself on the main mailing list, with a [TC] tag. + +The TC has 2 modes of operation: a RFC one and an internal one. + +If the TC thinks it needs the input from the larger community, the TC can call for a RFC. Else, it can decide by itself. + +If the disagreement involves a member of the TC, that member should recuse themselves from the decision. + +The decision to use a RFC process or an internal discussion is a discretionary decision of the TC. + +The TC can also reject a seizure for a few reasons such as: the matter was not discussed enough previously; it lacks expertise to reach a beneficial decision on the matter; or the matter is too trivial. +@subsection RFC call + +In the RFC mode, one person from the TC posts on the mailing list the technical question and will request input from the community. + +The mail will have the following specification: + + a precise title + a specific tag [TC RFC] + a top-level email + contain a precise question that does not exceed 100 words and that is answerable by developers + may have an extra description, or a link to a previous discussion, if deemed necessary, + contain a precise end date for the answers. + +The answers from the community must be on the main mailing list and must have the following specification: + + keep the tag and the title unchanged + limited to 400 words + a first-level, answering directly to the main email + answering to the question. + +Further replies to answers are permitted, as long as they conform to the community standards of politeness, they are limited to 100 words, and are not nested more than once. (max-depth=2) + +After the end-date, mails on the thread will be ignored. + +Violations of those rules will be escalated through the Community Committee. + +After all the emails are in, the TC has 96 hours to give its final decision. Exceptionally, the TC can request an extra delay, that will be notified on the mailing list. +@subsection Within TC + +In the internal case, the TC has 96 hours to give its final decision. Exceptionally, the TC can request an extra delay. +@subsection Decisions + +The decisions from the TC will be sent on the mailing list, with the [TC] tag. + +Internally, the TC should take decisions with a majority, or using ranked-choice voting. + +The decision from the TC should be published with a summary of the reasons that lead to this decision. + +The decisions from the TC are final, until the matters are reopened after no less than one year. + +@anchor{Community Committee} +@chapter Community Committee + +The Community Committee (CC) is here to arbitrage and make decisions when inter-personal conflicts occur in the project. It will decide quickly and take actions, for the sake of the project. + +The CC can remove privileges of offending members, including removal of commit access and temporary ban from the community. + +Decisions made by the CC can be re-opened after 1 year or by a majority vote of the General Assembly. Indefinite bans from the community must be confirmed by the General Assembly, in a majority vote. + +The CC is elected by the General Assembly for a duration of 1 year, and is composed of 5 members. Members can be re-elected if they wish. A majority vote in the General Assembly can trigger a new election of the CC. + +The members of the CC can be elected from outside of the GA. Candidates for election can either be suggested or self-nominated. + +The CC is governed by and responsible for enforcing the Code of Conduct. + +The CC can be contacted at . + +@anchor{Code of Conduct} +@chapter Code of Conduct + +Be friendly and respectful towards others and third parties. +Treat others the way you yourself want to be treated. + +Be considerate. Not everyone shares the same viewpoint and priorities as you do. +Different opinions and interpretations help the project. +Looking at issues from a different perspective assists development. + +Do not assume malice for things that can be attributed to incompetence. Even if +it is malice, it's rarely good to start with that as initial assumption. + +Stay friendly even if someone acts contrarily. Everyone has a bad day +once in a while. +If you yourself have a bad day or are angry then try to take a break and reply +once you are calm and without anger if you have to. + +Try to help other team members and cooperate if you can. + +The goal of software development is to create technical excellence, not for any +individual to be better and "win" against the others. Large software projects +are only possible and successful through teamwork. + +If someone struggles do not put them down. Give them a helping hand +instead and point them in the right direction. + +Finally, keep in mind the immortal words of Bill and Ted, +"Be excellent to each other." + +@bye diff --git a/doc/decoders.texi b/doc/decoders.texi index 5ba85cf9b14..09b8314dd2a 100644 --- a/doc/decoders.texi +++ b/doc/decoders.texi @@ -353,6 +353,156 @@ Enabled by default. @end table +@section libaribcaption + +Yet another ARIB STD-B24 caption decoder using external @dfn{libaribcaption} +library. + +Implements profiles A and C of the Japanse ARIB STD-B24 standard, +Brazilian ABNT NBR 15606-1, and Philippines version of ISDB-T. + +Requires the presence of the libaribcaption headers and library +(@url{https://github.com/xqq/libaribcaption}) during configuration. +You need to explicitly configure the build with @code{--enable-libaribcaption}. +If both @dfn{libaribb24} and @dfn{libaribcaption} are enabled, @dfn{libaribcaption} +decoder precedes. + +@subsection libaribcaption Decoder Options + +@table @option + +@item -sub_type @var{subtitle_type} +Specifies the format of the decoded subtitles. + +@table @samp +@item bitmap +Graphical image. +@item ass +ASS formatted text. +@item text +Simple text based output without formatting. +@end table + +The default is @dfn{ass} as same as @dfn{libaribb24} decoder. +Some present players (e.g., @dfn{mpv}) expect ASS format for ARIB caption. + +@item -caption_encoding @var{encoding_scheme} +Specifies the encoding scheme of input subtitle text. + +@table @samp +@item auto +Automatically detect text encoding. +@item jis +8bit-char JIS encoding defined in ARIB STD B24. +This encoding used in Japan for ISDB captions. +@item utf8 +UTF-8 encoding defined in ARIB STD B24. +This encoding is used in Philippines for ISDB-T captions. +@item latin +Latin character encoding defined in ABNT NBR 15606-1. +This encoding is used in South America for SBTVD / ISDB-Tb captions. +@end table + +The default is @dfn{ass} as same as @dfn{libaribb24} decoder. +Some present players (e.g., @dfn{mpv}) expect ASS format for ARIB caption. + +@item -font @var{font_name[,font_name2,...]} +Specify comma-separated list of font family names to be used for @dfn{bitmap} +or @dfn{ass} type subtitle rendering. +Only first font name is used for @dfn{ass} type subtitle. + +If not specified, use internaly defined default font family. + +@item -ass_single_rect @var{boolean} +ARIB STD-B24 specifies that some captions may be displayed at different +positions at a time (multi-rectangle subtitle). +Since some players (e.g., old @dfn{mpv}) can't handle multiple ASS rectangles +in a single AVSubtitle, or multiple ASS rectangles of indeterminate duration +with the same start timestamp, this option can change the behavior so that +all the texts are displayed in a single ASS rectangle. + +The default is @var{false}. + +If your player cannot handle AVSubtitles with multiple ASS rectangles properly, +set this option to @var{true} or define @env{ASS_SINGLE_RECT=1} to change +default behavior at compilation. + +@item -replace_fullwidth_ascii @var{boolean} +Specify whether to replace MSZ (Middle Size, half width) fullwidth +alphanumerics with halfwidth alphanumerics. + +The default is @var{true}. + +@item -force_outline_text @var{boolean} +Specify whether always render outline text for all characters regardless of +the indication by charactor style. + +The default is @var{false}. + +@item -outline_width @var{number} (0.0 - 3.0) +Specify width for outline text, in dots (relative). + +The default is @var{1.5}. + +@item -ignore_background @var{boolean} +Specify whether to ignore background color rendering. + +The default is @var{false}. + +@item -ignore_ruby @var{boolean} +Specify whether to ignore rendering for ruby-like (furigana) characters. + +The default is @var{false}. + +@item -replace_drcs @var{boolean} +Specify whether to render replaced DRCS characters as Unicode characters. + +The default is @var{true}. + +@item -canvas_size @var{image_size} +Specify the resolution of the canvas to render subtitles to; usually, this +should be frame size of input video. +This only applies when @code{-subtitle_type} is set to @var{bitmap}. + +The libaribcaption decoder assumes input frame size for bitmap rendering as below: +@enumerate +@item +PROFILE_A : 1440 x 1080 with SAR (PAR) 4:3 +@item +PROFILE_C : 320 x 180 with SAR (PAR) 1:1 +@end enumerate + +If actual frame size of input video does not match above assumption, +the rendered captions may be distorted. +To make the captions undistorted, add @code{-canvas_size} option to specify +actual input video size. + +Note that the @code{-canvas_size} option is not required for video with +different size but same aspect ratio. +In such cases, the caption will be stretched or shrunk to actual video size +if @code{-canvas_size} option is not specified. +If @code{-canvas_size} option is specified with different size, +the caption will be stretched or shrunk as specified size with calculated SAR. + +@end table + +@subsection libaribcaption decoder usage examples + +Display MPEG-TS file with ARIB subtitle by @code{ffplay} tool: +@example +ffplay -sub_type bitmap MPEG.TS +@end example + +Display MPEG-TS file with input frame size 1920x1080 by @code{ffplay} tool: +@example +ffplay -sub_type bitmap -canvas_size 1920x1080 MPEG.TS +@end example + +Embed ARIB subtitle in transcoded video: +@example +ffmpeg -sub_type bitmap -i src.m2t -filter_complex "[0:v][0:s]overlay" -vcodec h264 dest.mp4 +@end example + @section dvbsub @subsection Options diff --git a/doc/demuxers.texi b/doc/demuxers.texi index f07f3f53181..2d33b47a569 100644 --- a/doc/demuxers.texi +++ b/doc/demuxers.texi @@ -307,7 +307,15 @@ stream which contains the alpha channel in addition to the ordinary video. Interoperable Master Format demuxer. -This demuxer presents audio and video streams found in an IMF Composition. +This demuxer presents audio and video streams found in an IMF Composition, as +specified in @url{https://doi.org/10.5594/SMPTE.ST2067-2.2020, SMPTE ST 2067-2}. + +@example +ffmpeg [-assetmaps ,,...] -i ... +@end example + +If @code{-assetmaps} is not specified, the demuxer looks for a file called +@file{ASSETMAP.xml} in the same directory as the CPL. @section flv, live_flv, kux @@ -419,6 +427,10 @@ Use HTTP partial requests for downloading HTTP segments. @item seg_format_options Set options for the demuxer of media segments using a list of key=value pairs separated by @code{:}. + +@item seg_max_retry +Maximum number of times to reload a segment on error, useful when segment skip on network error is not desired. +Default value is 0. @end table @section image2 diff --git a/doc/dev_community/community.md b/doc/dev_community/community.md deleted file mode 100644 index 21e08e20e3b..00000000000 --- a/doc/dev_community/community.md +++ /dev/null @@ -1,79 +0,0 @@ -# FFmpeg project - -## Organisation - -The FFmpeg project is organized through a community working on global consensus. - -Decisions are taken by the ensemble of active members, through voting and -are aided by two committees. - -## General Assembly - -The ensemble of active members is called the General Assembly (GA). - -The General Assembly is sovereign and legitimate for all its decisions -regarding the FFmpeg project. - -The General Assembly is made up of active contributors. - -Contributors are considered "active contributors" if they have pushed more -than 20 patches in the last 36 months in the main FFmpeg repository, or -if they have been voted in by the GA. - -Additional members are added to the General Assembly through a vote after -proposal by a member of the General Assembly. -They are part of the GA for two years, after which they need a confirmation by -the GA. - -## Voting - -Voting is done using a ranked voting system, currently running on https://vote.ffmpeg.org/ . - -Majority vote means more than 50% of the expressed ballots. - -## Technical Committee - -The Technical Committee (TC) is here to arbitrate and make decisions when -technical conflicts occur in the project. -They will consider the merits of all the positions, judge them and make a -decision. - -The TC resolves technical conflicts but is not a technical steering committee. - -Decisions by the TC are binding for all the contributors. - -Decisions made by the TC can be re-opened after 1 year or by a majority vote -of the General Assembly, requested by one of the member of the GA. - -The TC is elected by the General Assembly for a duration of 1 year, and -is composed of 5 members. -Members can be re-elected if they wish. A majority vote in the General Assembly -can trigger a new election of the TC. - -The members of the TC can be elected from outside of the GA. -Candidates for election can either be suggested or self-nominated. - -The conflict resolution process is detailed in the [resolution process](resolution_process.md) document. - -## Community committee - -The Community Committee (CC) is here to arbitrage and make decisions when -inter-personal conflicts occur in the project. It will decide quickly and -take actions, for the sake of the project. - -The CC can remove privileges of offending members, including removal of -commit access and temporary ban from the community. - -Decisions made by the CC can be re-opened after 1 year or by a majority vote -of the General Assembly. Indefinite bans from the community must be confirmed -by the General Assembly, in a majority vote. - -The CC is elected by the General Assembly for a duration of 1 year, and is -composed of 5 members. -Members can be re-elected if they wish. A majority vote in the General Assembly -can trigger a new election of the CC. - -The members of the CC can be elected from outside of the GA. -Candidates for election can either be suggested or self-nominated. - -The CC is governed by and responsible for enforcing the Code of Conduct. diff --git a/doc/dev_community/resolution_process.md b/doc/dev_community/resolution_process.md deleted file mode 100644 index 4ed0b63c433..00000000000 --- a/doc/dev_community/resolution_process.md +++ /dev/null @@ -1,91 +0,0 @@ -# Technical Committee - -_This document only makes sense with the rules from [the community document](community)_. - -The Technical Committee (**TC**) is here to arbitrate and make decisions when -technical conflicts occur in the project. - -The TC main role is to resolve technical conflicts. -It is therefore not a technical steering committee, but it is understood that -some decisions might impact the future of the project. - -# Process - -## Seizing - -The TC can take possession of any technical matter that it sees fit. - -To involve the TC in a matter, email tc@ or CC them on an ongoing discussion. - -As members of TC are developers, they also can email tc@ to raise an issue. - -## Announcement - -The TC, once seized, must announce itself on the main mailing list, with a _[TC]_ tag. - -The TC has 2 modes of operation: a RFC one and an internal one. - -If the TC thinks it needs the input from the larger community, the TC can call -for a RFC. Else, it can decide by itself. - -If the disagreement involves a member of the TC, that member should recuse -themselves from the decision. - -The decision to use a RFC process or an internal discussion is a discretionary -decision of the TC. - -The TC can also reject a seizure for a few reasons such as: -the matter was not discussed enough previously; it lacks expertise to reach a -beneficial decision on the matter; or the matter is too trivial. - -### RFC call - -In the RFC mode, one person from the TC posts on the mailing list the -technical question and will request input from the community. - -The mail will have the following specification: -* a precise title -* a specific tag [TC RFC] -* a top-level email -* contain a precise question that does not exceed 100 words and that is answerable by developers -* may have an extra description, or a link to a previous discussion, if deemed necessary, -* contain a precise end date for the answers. - -The answers from the community must be on the main mailing list and must have -the following specification: -* keep the tag and the title unchanged -* limited to 400 words -* a first-level, answering directly to the main email -* answering to the question. - -Further replies to answers are permitted, as long as they conform to the -community standards of politeness, they are limited to 100 words, and are not -nested more than once. (max-depth=2) - -After the end-date, mails on the thread will be ignored. - -Violations of those rules will be escalated through the Community Committee. - -After all the emails are in, the TC has 96 hours to give its final decision. -Exceptionally, the TC can request an extra delay, that will be notified on the -mailing list. - -### Within TC - -In the internal case, the TC has 96 hours to give its final decision. -Exceptionally, the TC can request an extra delay. - - -## Decisions - -The decisions from the TC will be sent on the mailing list, with the _[TC]_ tag. - -Internally, the TC should take decisions with a majority, or using -ranked-choice voting. - -The decision from the TC should be published with a summary of the reasons that -lead to this decision. - -The decisions from the TC are final, until the matters are reopened after -no less than one year. - diff --git a/doc/developer.texi b/doc/developer.texi index 31b485b0f62..0c2f2cd7d1b 100644 --- a/doc/developer.texi +++ b/doc/developer.texi @@ -56,9 +56,9 @@ and should try to fix issues their commit causes. @anchor{Coding Rules} @chapter Coding Rules -@section C language features +@section Language -FFmpeg is programmed in the ISO C99 language, extended with: +FFmpeg is mainly programmed in the ISO C99 language, extended with: @itemize @bullet @item Atomic operations from C11 @file{stdatomic.h}. They are emulated on @@ -83,6 +83,44 @@ complex numbers; mixed statements and declarations. @end itemize +@subsection SIMD/DSP +@anchor{SIMD/DSP} + +As modern compilers are unable to generate efficient SIMD or other +performance-critical DSP code from plain C, handwritten assembly is used. +Usually such code is isolated in a separate function. Then the standard approach +is writing multiple versions of this function – a plain C one that works +everywhere and may also be useful for debugging, and potentially multiple +architecture-specific optimized implementations. Initialization code then +chooses the best available version at runtime and loads it into a function +pointer; the function in question is then always called through this pointer. + +The specific syntax used for writing assembly is: +@itemize @bullet +@item +NASM on x86; + +@item +GAS on ARM. +@end itemize + +A unit testing framework for assembly called @code{checkasm} lives under +@file{tests/checkasm}. All new assembly should come with @code{checkasm} tests; +adding tests for existing assembly that lacks them is also strongly encouraged. + +@subsection Other languages + +Other languages than C may be used in special cases: +@itemize @bullet +@item +Compiler intrinsics or inline assembly when the code in question cannot be +written in the standard way described in the @ref{SIMD/DSP} section. This +typically applies to code that needs to be inlined. + +@item +Objective-C where required for interacting with macOS-specific interfaces. +@end itemize + @section Code formatting conventions There are the following guidelines regarding the indentation in files: @@ -179,6 +217,7 @@ int myfunc(int my_parameter) ... @end example +@anchor{Naming conventions} @section Naming conventions Names of functions, variables, and struct members must be lowercase, using @@ -349,22 +388,6 @@ time-frame (12h for build failures and security fixes, 3 days small changes, Also note, the maintainer can simply ask for more time to review! @section Code -@subheading API/ABI changes should be discussed before they are made. -Do not change behavior of the programs (renaming options etc) or public -API or ABI without first discussing it on the ffmpeg-devel mailing list. -Do not remove widely used functionality or features (redundant code can be removed). - -@subheading Remember to check if you need to bump versions for libav*. -Depending on the change, you may need to change the version integer. -Incrementing the first component means no backward compatibility to -previous versions (e.g. removal of a function from the public API). -Incrementing the second component means backward compatible change -(e.g. addition of a function to the public API or extension of an -existing data structure). -Incrementing the third component means a noteworthy binary compatible -change (e.g. encoder bug fix that matters for the decoder). The third -component always starts at 100 to distinguish FFmpeg from Libav. - @subheading Warnings for correct code may be disabled if there is no other option. Compiler warnings indicate potential bugs or code with bad style. If a type of warning always points to correct and clean code, that warning should @@ -379,6 +402,151 @@ Never write to unallocated memory, never write over the end of arrays, always check values read from some untrusted source before using them as array index or other risky things. +@section Library public interfaces +Every library in FFmpeg provides a set of public APIs in its installed headers, +which are those listed in the variable @code{HEADERS} in that library's +@file{Makefile}. All identifiers defined in those headers (except for those +explicitly documented otherwise), and corresponding symbols exported from +compiled shared or static libraries are considered public interfaces and must +comply with the API and ABI compatibility rules described in this section. + +Public APIs must be backward compatible within a given major version. I.e. any +valid user code that compiles and works with a given library version must still +compile and work with any later version, as long as the major version number is +unchanged. "Valid user code" here means code that is calling our APIs in a +documented and/or intended manner and is not relying on any undefined behavior. +Incrementing the major version may break backward compatibility, but only to the +extent described in @ref{Major version bumps}. + +We also guarantee backward ABI compatibility for shared and static libraries. +I.e. it should be possible to replace a shared or static build of our library +with a build of any later version (re-linking the user binary in the static +case) without breaking any valid user binaries, as long as the major version +number remains unchanged. + +@subsection Adding new interfaces +Any new public identifiers in installed headers are considered new API - this +includes new functions, structs, macros, enum values, typedefs, new fields in +existing functions, new installed headers, etc. Consider the following +guidelines when adding new APIs. + +@subsubheading Motivation +While new APIs can be added relatively easily, changing or removing them is much +harder due to abovementioned compatibility requirements. You should then +consider carefully whether the functionality you are adding really needs to be +exposed to our callers as new public API. + +Your new API should have at least one well-established use case outside of the +library that cannot be easily achieved with existing APIs. Every library in +FFmpeg also has a defined scope - your new API must fit within it. + +@subsubheading Replacing existing APIs +If your new API is replacing an existing one, it should be strictly superior to +it, so that the advantages of using the new API outweight the cost to the +callers of changing their code. After adding the new API you should then +deprecate the old one and schedule it for removal, as described in +@ref{Removing interfaces}. + +If you deem an existing API deficient and want to fix it, the preferred approach +in most cases is to add a differently-named replacement and deprecate the +existing API rather than modify it. It is important to make the changes visible +to our callers (e.g. through compile- or run-time deprecation warnings) and make +it clear how to transition to the new API (e.g. in the Doxygen documentation or +on the wiki). + +@subsubheading API design +The FFmpeg libraries are used by a variety of callers to perform a wide range of +multimedia-related processing tasks. You should therefore - within reason - try +to design your new API for the broadest feasible set of use cases and avoid +unnecessarily limiting it to a specific type of callers (e.g. just media +playback or just transcoding). + +@subsubheading Consistency +Check whether similar APIs already exist in FFmpeg. If they do, try to model +your new addition on them to achieve better overall consistency. + +The naming of your new identifiers should follow the @ref{Naming conventions} +and be aligned with other similar APIs, if applicable. + +@subsubheading Extensibility +You should also consider how your API might be extended in the future in a +backward-compatible way. If you are adding a new struct @code{AVFoo}, the +standard approach is requiring the caller to always allocate it through a +constructor function, typically named @code{av_foo_alloc()}. This way new fields +may be added to the end of the struct without breaking ABI compatibility. +Typically you will also want a destructor - @code{av_foo_free(AVFoo**)} that +frees the indirectly supplied object (and its contents, if applicable) and +writes @code{NULL} to the supplied pointer, thus eliminating the potential +dangling pointer in the caller's memory. + +If you are adding new functions, consider whether it might be desirable to tweak +their behavior in the future - you may want to add a flags argument, even though +it would be unused initially. + +@subsubheading Documentation +All new APIs must be documented as Doxygen-formatted comments above the +identifiers you add to the public headers. You should also briefly mention the +change in @file{doc/APIchanges}. + +@subsubheading Bump the version +Backward-incompatible API or ABI changes require incrementing (bumping) the +major version number, as described in @ref{Major version bumps}. Major +bumps are significant events that happen on a schedule - so if your change +strictly requires one you should add it under @code{#if} preprocesor guards that +disable it until the next major bump happens. + +New APIs that can be added without breaking API or ABI compatibility require +bumping the minor version number. + +Incrementing the third (micro) version component means a noteworthy binary +compatible change (e.g. encoder bug fix that matters for the decoder). The third +component always starts at 100 to distinguish FFmpeg from Libav. + +@anchor{Removing interfaces} +@subsection Removing interfaces +Due to abovementioned compatibility guarantees, removing APIs is an involved +process that should only be undertaken with good reason. Typically a deficient, +restrictive, or otherwise inadequate API is replaced by a superior one, though +it does at times happen that we remove an API without any replacement (e.g. when +the feature it provides is deemed not worth the maintenance effort, out of scope +of the project, fundamentally flawed, etc.). + +The removal has two steps - first the API is deprecated and scheduled for +removal, but remains present and functional. The second step is actually +removing the API - this is described in @ref{Major version bumps}. + +To deprecate an API you should signal to our users that they should stop using +it. E.g. if you intend to remove struct members or functions, you should mark +them with @code{attribute_deprecated}. When this cannot be done, it may be +possible to detect the use of the deprecated API at runtime and print a warning +(though take care not to print it too often). You should also document the +deprecation (and the replacement, if applicable) in the relevant Doxygen +documentation block. + +Finally, you should define a deprecation guard along the lines of +@code{#define FF_API_ (LIBAVBAR_VERSION_MAJOR < XX)} (where XX is the major +version in which the API will be removed) in @file{libavbar/version_major.h} +(@file{version.h} in case of @code{libavutil}). Then wrap all uses of the +deprecated API in @code{#if FF_API_ .... #endif}, so that the code will +automatically get disabled once the major version reaches XX. You can also use +@code{FF_DISABLE_DEPRECATION_WARNINGS} and @code{FF_ENABLE_DEPRECATION_WARNINGS} +to suppress compiler deprecation warnings inside these guards. You should test +that the code compiles and works with the guard macro evaluating to both true +and false. + +@anchor{Major version bumps} +@subsection Major version bumps +A major version bump signifies an API and/or ABI compatibility break. To reduce +the negative effects on our callers, who are required to adapt their code, +backward-incompatible changes during a major bump should be limited to: +@itemize @bullet +@item +Removing previously deprecated APIs. + +@item +Performing ABI- but not API-breaking changes, like reordering struct contents. +@end itemize + @section Documentation/Other @subheading Subscribe to the ffmpeg-devel mailing list. It is important to be subscribed to the @@ -421,35 +589,6 @@ finding a new maintainer and also don't forget to update the @file{MAINTAINERS} We think our rules are not too hard. If you have comments, contact us. -@chapter Code of conduct - -Be friendly and respectful towards others and third parties. -Treat others the way you yourself want to be treated. - -Be considerate. Not everyone shares the same viewpoint and priorities as you do. -Different opinions and interpretations help the project. -Looking at issues from a different perspective assists development. - -Do not assume malice for things that can be attributed to incompetence. Even if -it is malice, it's rarely good to start with that as initial assumption. - -Stay friendly even if someone acts contrarily. Everyone has a bad day -once in a while. -If you yourself have a bad day or are angry then try to take a break and reply -once you are calm and without anger if you have to. - -Try to help other team members and cooperate if you can. - -The goal of software development is to create technical excellence, not for any -individual to be better and "win" against the others. Large software projects -are only possible and successful through teamwork. - -If someone struggles do not put them down. Give them a helping hand -instead and point them in the right direction. - -Finally, keep in mind the immortal words of Bill and Ted, -"Be excellent to each other." - @anchor{Submitting patches} @chapter Submitting patches @@ -666,7 +805,10 @@ Lines with similar content should be aligned vertically when doing so improves readability. @item -Consider adding a regression test for your code. +Consider adding a regression test for your code. All new modules +should be covered by tests. That includes demuxers, muxers, decoders, encoders +filters, bitstream filters, parsers. If its not possible to do that, add +an explanation why to your patchset, its ok to not test if theres a reason. @item If you added YASM code please check that things still work with --disable-yasm. @@ -725,6 +867,8 @@ accordingly]. @section Adding files to the fate-suite dataset +If you need a sample uploaded send a mail to samples-request. + When there is no muxer or encoder available to generate test media for a specific test then the media has to be included in the fate-suite. First please make sure that the sample file is as small as possible to test the diff --git a/doc/encoders.texi b/doc/encoders.texi index 727f12a59dc..6f8f5e127e8 100644 --- a/doc/encoders.texi +++ b/doc/encoders.texi @@ -789,6 +789,11 @@ about 80-96 kbps/channel @end table Default value is 0. + +@item frame_length +Set the audio frame length in samples. Default value is the internal +default of the library. Refer to the library's documentation for information +about supported values. @end table @subsection Examples @@ -859,6 +864,13 @@ Enable the encoder to use ABR when set to 1. The @command{lame} @option{--abr} sets the target bitrate, while this options only tells FFmpeg to use ABR still relies on @option{b} to set bitrate. +@item copyright (@emph{-c}) +Set MPEG audio copyright flag when set to 1. The default value is 0 +(disabled). + +@item original (@emph{-o}) +Set MPEG audio original flag when set to 1. The default value is 1 +(enabled). @end table @section libopencore-amrnb @@ -1949,22 +1961,6 @@ Set the number of slices, used in parallelized encoding. Default value is 0. This is only used when @option{slice_mode} is set to @samp{fixed}. -@item slice_mode -Set slice mode. Can assume one of the following possible values: - -@table @samp -@item fixed -a fixed number of slices -@item rowmb -one slice per row of macroblocks -@item auto -automatic number of slices according to number of threads -@item dyn -dynamic slicing -@end table - -Default value is @samp{auto}. - @item loopfilter Enable loop filter, if set to 1 (automatically enabled). To disable set a value of 0. @@ -2742,6 +2738,10 @@ Only the mpeg2 and h264 decoders provide these. Default is 1 (on). @item udu_sei @var{boolean} Import user data unregistered SEI if available into output. Default is 0 (off). +@item mb_info @var{boolean} +Set mb_info data through AVFrameSideData, only useful when used from the +API. Default is 0 (off). + @item x264-params (N.A.) Override the x264 configuration using a :-separated list of key=value parameters. @@ -3065,6 +3065,20 @@ Video encoders can take input in either of nv12 or yuv420p form (some encoders support both, some support only either - in practice, nv12 is the safer choice, especially among HW encoders). +@section Microsoft RLE + +Microsoft RLE aka MSRLE encoder. +Only 8-bit palette mode supported. +Compatible with Windows 3.1 and Windows 95. + +@subsection Options + +@table @option +@item g @var{integer} +Keyframe interval. +A keyframe is inserted at least every @code{-g} frames, sometimes sooner. +@end table + @section mpeg2 MPEG-2 video encoder. @@ -3204,8 +3218,8 @@ recommended value) and do not set a size constraint. @section QSV Encoders -The family of Intel QuickSync Video encoders (MPEG-2, H.264, HEVC, JPEG/MJPEG -and VP9) +The family of Intel QuickSync Video encoders (MPEG-2, H.264, HEVC, JPEG/MJPEG, +VP9, AV1) @subsection Ratecontrol Method The ratecontrol method is selected as follows: @@ -3360,7 +3374,7 @@ Supported in h264_qsv. Change these value to reset qsv codec's max/min qp configuration. @item @var{low_delay_brc} -Supported in h264_qsv and hevc_qsv. +Supported in h264_qsv, hevc_qsv and av1_qsv. Change this value to reset qsv codec's low_delay_brc configuration. @item @var{framerate} diff --git a/doc/examples/Makefile b/doc/examples/Makefile index f937fbefda5..4efed6b11d8 100644 --- a/doc/examples/Makefile +++ b/doc/examples/Makefile @@ -1,24 +1,24 @@ +EXAMPLES-$(CONFIG_AVIO_HTTP_SERVE_FILES) += avio_http_serve_files EXAMPLES-$(CONFIG_AVIO_LIST_DIR_EXAMPLE) += avio_list_dir -EXAMPLES-$(CONFIG_AVIO_READING_EXAMPLE) += avio_reading +EXAMPLES-$(CONFIG_AVIO_READ_CALLBACK_EXAMPLE) += avio_read_callback EXAMPLES-$(CONFIG_DECODE_AUDIO_EXAMPLE) += decode_audio +EXAMPLES-$(CONFIG_DECODE_FILTER_AUDIO_EXAMPLE) += decode_filter_audio +EXAMPLES-$(CONFIG_DECODE_FILTER_VIDEO_EXAMPLE) += decode_filter_video EXAMPLES-$(CONFIG_DECODE_VIDEO_EXAMPLE) += decode_video -EXAMPLES-$(CONFIG_DEMUXING_DECODING_EXAMPLE) += demuxing_decoding +EXAMPLES-$(CONFIG_DEMUX_DECODE_EXAMPLE) += demux_decode EXAMPLES-$(CONFIG_ENCODE_AUDIO_EXAMPLE) += encode_audio EXAMPLES-$(CONFIG_ENCODE_VIDEO_EXAMPLE) += encode_video EXAMPLES-$(CONFIG_EXTRACT_MVS_EXAMPLE) += extract_mvs EXAMPLES-$(CONFIG_FILTER_AUDIO_EXAMPLE) += filter_audio -EXAMPLES-$(CONFIG_FILTERING_AUDIO_EXAMPLE) += filtering_audio -EXAMPLES-$(CONFIG_FILTERING_VIDEO_EXAMPLE) += filtering_video -EXAMPLES-$(CONFIG_HTTP_MULTICLIENT_EXAMPLE) += http_multiclient EXAMPLES-$(CONFIG_HW_DECODE_EXAMPLE) += hw_decode -EXAMPLES-$(CONFIG_METADATA_EXAMPLE) += metadata -EXAMPLES-$(CONFIG_MUXING_EXAMPLE) += muxing -EXAMPLES-$(CONFIG_QSVDEC_EXAMPLE) += qsvdec -EXAMPLES-$(CONFIG_REMUXING_EXAMPLE) += remuxing -EXAMPLES-$(CONFIG_RESAMPLING_AUDIO_EXAMPLE) += resampling_audio -EXAMPLES-$(CONFIG_SCALING_VIDEO_EXAMPLE) += scaling_video +EXAMPLES-$(CONFIG_MUX_EXAMPLE) += mux +EXAMPLES-$(CONFIG_QSV_DECODE_EXAMPLE) += qsv_decode +EXAMPLES-$(CONFIG_REMUX_EXAMPLE) += remux +EXAMPLES-$(CONFIG_RESAMPLE_AUDIO_EXAMPLE) += resample_audio +EXAMPLES-$(CONFIG_SCALE_VIDEO_EXAMPLE) += scale_video +EXAMPLES-$(CONFIG_SHOW_METADATA_EXAMPLE) += show_metadata EXAMPLES-$(CONFIG_TRANSCODE_AAC_EXAMPLE) += transcode_aac -EXAMPLES-$(CONFIG_TRANSCODING_EXAMPLE) += transcoding +EXAMPLES-$(CONFIG_TRANSCODE_EXAMPLE) += transcode EXAMPLES-$(CONFIG_VAAPI_ENCODE_EXAMPLE) += vaapi_encode EXAMPLES-$(CONFIG_VAAPI_TRANSCODE_EXAMPLE) += vaapi_transcode EXAMPLES-$(CONFIG_QSV_TRANSCODE_EXAMPLE) += qsv_transcode diff --git a/doc/examples/Makefile.example b/doc/examples/Makefile.example index a232d97f987..dee9ebf2f0b 100644 --- a/doc/examples/Makefile.example +++ b/doc/examples/Makefile.example @@ -11,33 +11,40 @@ CFLAGS += -Wall -g CFLAGS := $(shell pkg-config --cflags $(FFMPEG_LIBS)) $(CFLAGS) LDLIBS := $(shell pkg-config --libs $(FFMPEG_LIBS)) $(LDLIBS) -EXAMPLES= avio_list_dir \ - avio_reading \ +# missing the following targets, since they need special options in the FFmpeg build: +# qsv_decode +# qsv_transcode +# vaapi_encode +# vaapi_transcode + +EXAMPLES=\ + avio_http_serve_files \ + avio_list_dir \ + avio_read_callback \ decode_audio \ + decode_filter_audio \ + decode_filter_video \ decode_video \ - demuxing_decoding \ + demux_decode \ encode_audio \ encode_video \ extract_mvs \ - filtering_video \ - filtering_audio \ - http_multiclient \ hw_decode \ - metadata \ - muxing \ - remuxing \ - resampling_audio \ - scaling_video \ + mux \ + remux \ + resample_audio \ + scale_video \ + show_metadata \ transcode_aac \ - transcoding \ + transcode OBJS=$(addsuffix .o,$(EXAMPLES)) # the following examples make explicit use of the math library avcodec: LDLIBS += -lm encode_audio: LDLIBS += -lm -muxing: LDLIBS += -lm -resampling_audio: LDLIBS += -lm +mux: LDLIBS += -lm +resample_audio: LDLIBS += -lm .phony: all clean-test clean diff --git a/doc/examples/README b/doc/examples/README index c1ce619d350..6cb4b6e17a2 100644 --- a/doc/examples/README +++ b/doc/examples/README @@ -7,8 +7,10 @@ that you have them installed and working on your system. Method 1: build the installed examples in a generic read/write user directory -Copy to a read/write user directory and just use "make", it will link -to the libraries on your system, assuming the PKG_CONFIG_PATH is +Copy to a read/write user directory and run: +make -f Makefile.example + +It will link to the libraries on your system, assuming the PKG_CONFIG_PATH is correctly configured. Method 2: build the examples in-tree @@ -20,4 +22,4 @@ examples using "make examplesclean" If you want to try the dedicated Makefile examples (to emulate the first method), go into doc/examples and run a command such as -PKG_CONFIG_PATH=pc-uninstalled make. +PKG_CONFIG_PATH=pc-uninstalled make -f Makefile.example diff --git a/doc/examples/http_multiclient.c b/doc/examples/avio_http_serve_files.c similarity index 95% rename from doc/examples/http_multiclient.c rename to doc/examples/avio_http_serve_files.c index 831e89c60a4..2aae3870c2d 100644 --- a/doc/examples/http_multiclient.c +++ b/doc/examples/avio_http_serve_files.c @@ -21,12 +21,11 @@ */ /** - * @file - * libavformat multi-client network API usage example. + * @file libavformat multi-client network API usage example + * @example avio_http_serve_files.c * - * @example http_multiclient.c - * This example will serve a file without decoding or demuxing it over http. - * Multiple clients can connect and will receive the same file. + * Serve a file without decoding or demuxing it over the HTTP protocol. Multiple + * clients can connect and will receive the same file. */ #include diff --git a/doc/examples/avio_list_dir.c b/doc/examples/avio_list_dir.c index 3073baaefa9..bb19debad31 100644 --- a/doc/examples/avio_list_dir.c +++ b/doc/examples/avio_list_dir.c @@ -20,6 +20,13 @@ * THE SOFTWARE. */ +/** + * @file libavformat AVIOContext list directory API usage example + * @example avio_list_dir.c + * + * Show how to list directories through the libavformat AVIOContext API. + */ + #include #include #include diff --git a/doc/examples/avio_reading.c b/doc/examples/avio_read_callback.c similarity index 97% rename from doc/examples/avio_reading.c rename to doc/examples/avio_read_callback.c index 36ee02afa54..4cf81ad72ee 100644 --- a/doc/examples/avio_reading.c +++ b/doc/examples/avio_read_callback.c @@ -21,12 +21,11 @@ */ /** - * @file - * libavformat AVIOContext API example. + * @file libavformat AVIOContext read callback API usage example + * @example avio_read_callback.c * * Make libavformat demuxer access media content through a custom * AVIOContext read callback. - * @example avio_reading.c */ #include diff --git a/doc/examples/decode_audio.c b/doc/examples/decode_audio.c index 49ad22cba63..bcb3d87a695 100644 --- a/doc/examples/decode_audio.c +++ b/doc/examples/decode_audio.c @@ -21,10 +21,11 @@ */ /** - * @file - * audio decoding with libavcodec API example - * + * @file libavcodec audio decoding API usage example * @example decode_audio.c + * + * Decode data from an MP2 input file and generate a raw audio file to + * be played with ffplay. */ #include diff --git a/doc/examples/filtering_audio.c b/doc/examples/decode_filter_audio.c similarity index 98% rename from doc/examples/filtering_audio.c rename to doc/examples/decode_filter_audio.c index 51fc47be2a9..2046419819a 100644 --- a/doc/examples/filtering_audio.c +++ b/doc/examples/decode_filter_audio.c @@ -23,9 +23,11 @@ */ /** - * @file - * API example for audio decoding and filtering - * @example filtering_audio.c + * @file audio decoding and filtering usage example + * @example decode_filter_audio.c + * + * Demux, decode and filter audio input file, generate a raw audio + * file to be played with ffplay. */ #include diff --git a/doc/examples/filtering_video.c b/doc/examples/decode_filter_video.c similarity index 99% rename from doc/examples/filtering_video.c rename to doc/examples/decode_filter_video.c index 7b3e16c40c0..454c19222f2 100644 --- a/doc/examples/filtering_video.c +++ b/doc/examples/decode_filter_video.c @@ -24,7 +24,7 @@ /** * @file * API example for decoding and filtering - * @example filtering_video.c + * @example decode_filter_video.c */ #define _XOPEN_SOURCE 600 /* for usleep */ diff --git a/doc/examples/decode_video.c b/doc/examples/decode_video.c index 7238e381039..b0b3a6ae928 100644 --- a/doc/examples/decode_video.c +++ b/doc/examples/decode_video.c @@ -21,10 +21,11 @@ */ /** - * @file - * video decoding with libavcodec API example + * @file libavcodec video decoding API usage example + * @example decode_video.c * * - * @example decode_video.c + * Read from an MPEG1 video file, decode frames, and generate PGM images as + * output. */ #include @@ -69,12 +70,12 @@ static void decode(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt, exit(1); } - printf("saving frame %3d\n", dec_ctx->frame_number); + printf("saving frame %3"PRId64"\n", dec_ctx->frame_num); fflush(stdout); /* the picture is allocated by the decoder. no need to free it */ - snprintf(buf, sizeof(buf), "%s-%d", filename, dec_ctx->frame_number); + snprintf(buf, sizeof(buf), "%s-%"PRId64, filename, dec_ctx->frame_num); pgm_save(frame->data[0], frame->linesize[0], frame->width, frame->height, buf); } diff --git a/doc/examples/demuxing_decoding.c b/doc/examples/demux_decode.c similarity index 97% rename from doc/examples/demuxing_decoding.c rename to doc/examples/demux_decode.c index 999a78db0d6..298a369f436 100644 --- a/doc/examples/demuxing_decoding.c +++ b/doc/examples/demux_decode.c @@ -21,12 +21,12 @@ */ /** - * @file - * Demuxing and decoding example. + * @file libavformat and libavcodec demuxing and decoding API usage example + * @example demux_decode.c * - * Show how to use the libavformat and libavcodec API to demux and - * decode audio and video data. - * @example demuxing_decoding.c + * Show how to use the libavformat and libavcodec API to demux and decode audio + * and video data. Write the output as raw audio and input files to be played by + * ffplay. */ #include @@ -73,8 +73,8 @@ static int output_video_frame(AVFrame *frame) return -1; } - printf("video_frame n:%d coded_n:%d\n", - video_frame_count++, frame->coded_picture_number); + printf("video_frame n:%d\n", + video_frame_count++); /* copy decoded frame to destination buffer: * this is required since rawvideo expects non aligned data */ diff --git a/doc/examples/encode_audio.c b/doc/examples/encode_audio.c index 9a1792b7250..bb16683d946 100644 --- a/doc/examples/encode_audio.c +++ b/doc/examples/encode_audio.c @@ -21,10 +21,10 @@ */ /** - * @file - * audio encoding with libavcodec API example. - * + * @file libavcodec encoding audio API usage examples * @example encode_audio.c + * + * Generate a synthetic audio signal and encode it to an output MP2 file. */ #include diff --git a/doc/examples/encode_video.c b/doc/examples/encode_video.c index 939ed68324e..4fae146f2e9 100644 --- a/doc/examples/encode_video.c +++ b/doc/examples/encode_video.c @@ -21,10 +21,10 @@ */ /** - * @file - * video encoding with libavcodec API example - * + * @file libavcodec encoding video API usage example * @example encode_video.c + * + * Generate synthetic video data and encode it to an output file. */ #include @@ -202,7 +202,7 @@ int main(int argc, char **argv) It makes only sense because this tiny examples writes packets directly. This is called "elementary stream" and only works for some codecs. To create a valid file, you usually need to write packets - into a proper file format or protocol; see muxing.c. + into a proper file format or protocol; see mux.c. */ if (codec->id == AV_CODEC_ID_MPEG1VIDEO || codec->id == AV_CODEC_ID_MPEG2VIDEO) fwrite(endcode, 1, sizeof(endcode), f); diff --git a/doc/examples/extract_mvs.c b/doc/examples/extract_mvs.c index b80ba26bb7c..5603064d723 100644 --- a/doc/examples/extract_mvs.c +++ b/doc/examples/extract_mvs.c @@ -21,6 +21,14 @@ * THE SOFTWARE. */ +/** + * @file libavcodec motion vectors extraction API usage example + * @example extract_mvs.c + * + * Read from input file, decode video stream and print a motion vectors + * representation to stdout. + */ + #include #include #include diff --git a/doc/examples/filter_audio.c b/doc/examples/filter_audio.c index f53e52562b8..9e4039b9000 100644 --- a/doc/examples/filter_audio.c +++ b/doc/examples/filter_audio.c @@ -19,13 +19,11 @@ */ /** - * @file - * libavfilter API usage example. - * + * @file libavfilter audio filtering API usage example * @example filter_audio.c - * This example will generate a sine wave audio, - * pass it through a simple filter chain, and then compute the MD5 checksum of - * the output data. + * + * This example will generate a sine wave audio, pass it through a simple filter + * chain, and then compute the MD5 checksum of the output data. * * The filter chain it uses is: * (input) -> abuffer -> volume -> aformat -> abuffersink -> (output) diff --git a/doc/examples/hw_decode.c b/doc/examples/hw_decode.c index 0d23f451e69..6a4a4fb83d5 100644 --- a/doc/examples/hw_decode.c +++ b/doc/examples/hw_decode.c @@ -24,12 +24,11 @@ */ /** - * @file - * HW-Accelerated decoding example. - * + * @file HW-accelerated decoding API usage.example * @example hw_decode.c - * This example shows how to do HW-accelerated decoding with output - * frames from the HW video surfaces. + * + * Perform HW-accelerated decoding with output frames from HW video + * surfaces. */ #include diff --git a/doc/examples/muxing.c b/doc/examples/mux.c similarity index 96% rename from doc/examples/muxing.c rename to doc/examples/mux.c index cd997d5431d..b034aad56f7 100644 --- a/doc/examples/muxing.c +++ b/doc/examples/mux.c @@ -21,12 +21,11 @@ */ /** - * @file - * libavformat API example. + * @file libavformat muxing API usage example + * @example mux.c * - * Output a media file in any supported libavformat format. The default - * codecs are used. - * @example muxing.c + * Generate a synthetic audio and video signal and mux them to a media file in + * any supported libavformat format. The default codecs are used. */ #include @@ -380,27 +379,27 @@ static int write_audio_frame(AVFormatContext *oc, OutputStream *ost) /**************************************************************/ /* video output */ -static AVFrame *alloc_picture(enum AVPixelFormat pix_fmt, int width, int height) +static AVFrame *alloc_frame(enum AVPixelFormat pix_fmt, int width, int height) { - AVFrame *picture; + AVFrame *frame; int ret; - picture = av_frame_alloc(); - if (!picture) + frame = av_frame_alloc(); + if (!frame) return NULL; - picture->format = pix_fmt; - picture->width = width; - picture->height = height; + frame->format = pix_fmt; + frame->width = width; + frame->height = height; /* allocate the buffers for the frame data */ - ret = av_frame_get_buffer(picture, 0); + ret = av_frame_get_buffer(frame, 0); if (ret < 0) { fprintf(stderr, "Could not allocate frame data.\n"); exit(1); } - return picture; + return frame; } static void open_video(AVFormatContext *oc, const AVCodec *codec, @@ -421,7 +420,7 @@ static void open_video(AVFormatContext *oc, const AVCodec *codec, } /* allocate and init a re-usable frame */ - ost->frame = alloc_picture(c->pix_fmt, c->width, c->height); + ost->frame = alloc_frame(c->pix_fmt, c->width, c->height); if (!ost->frame) { fprintf(stderr, "Could not allocate video frame\n"); exit(1); @@ -432,9 +431,9 @@ static void open_video(AVFormatContext *oc, const AVCodec *codec, * output format. */ ost->tmp_frame = NULL; if (c->pix_fmt != AV_PIX_FMT_YUV420P) { - ost->tmp_frame = alloc_picture(AV_PIX_FMT_YUV420P, c->width, c->height); + ost->tmp_frame = alloc_frame(AV_PIX_FMT_YUV420P, c->width, c->height); if (!ost->tmp_frame) { - fprintf(stderr, "Could not allocate temporary picture\n"); + fprintf(stderr, "Could not allocate temporary video frame\n"); exit(1); } } diff --git a/doc/examples/qsvdec.c b/doc/examples/qsv_decode.c similarity index 96% rename from doc/examples/qsvdec.c rename to doc/examples/qsv_decode.c index b662ae91c3c..cc2662d5bdd 100644 --- a/doc/examples/qsvdec.c +++ b/doc/examples/qsv_decode.c @@ -21,12 +21,11 @@ */ /** - * @file - * Intel QSV-accelerated H.264 decoding example. + * @file Intel QSV-accelerated H.264 decoding API usage example + * @example qsv_decode.c * - * @example qsvdec.c - * This example shows how to do QSV-accelerated H.264 decoding with output - * frames in the GPU video surfaces. + * Perform QSV-accelerated H.264 decoding with output frames in the + * GPU video surfaces, write the decoded frames to an output file. */ #include "config.h" diff --git a/doc/examples/qsv_transcode.c b/doc/examples/qsv_transcode.c index 9b37bbea9fa..48128b200c5 100644 --- a/doc/examples/qsv_transcode.c +++ b/doc/examples/qsv_transcode.c @@ -1,6 +1,4 @@ /* - * Quick Sync Video (video transcoding) transcode sample - * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights @@ -21,12 +19,12 @@ */ /** - * @file - * Intel QSV-accelerated transcoding example. - * + * @file Intel QSV-accelerated video transcoding API usage example * @example qsv_transcode.c - * This example shows how to do QSV-accelerated transcoding and how to - * dynamically change encoder's option. + * + * Perform QSV-accelerated transcoding and show to dynamically change + * encoder's options. + * * Usage: qsv_transcode input_stream codec output_stream initial option * { frame_number new_option } * e.g: - qsv_transcode input.mp4 h264_qsv output_h264.mp4 "g 60" @@ -90,7 +88,7 @@ static int dynamic_set_parameter(AVCodecContext *avctx) if (current_setting_number < setting_number && frame_number == dynamic_setting[current_setting_number].frame_number) { AVDictionaryEntry *e = NULL; - ret = str_to_dict(dynamic_setting[current_setting_number].optstr, &opts); + ret = str_to_dict(dynamic_setting[current_setting_number++].optstr, &opts); if (ret < 0) { fprintf(stderr, "The dynamic parameter is wrong\n"); goto fail; diff --git a/doc/examples/remuxing.c b/doc/examples/remux.c similarity index 96% rename from doc/examples/remuxing.c rename to doc/examples/remux.c index 2657f9dc669..ecf30489f13 100644 --- a/doc/examples/remuxing.c +++ b/doc/examples/remux.c @@ -21,11 +21,11 @@ */ /** - * @file - * libavformat/libavcodec demuxing and muxing API example. + * @file libavformat/libavcodec demuxing and muxing API usage example + * @example remux.c * - * Remux streams from one container format to another. - * @example remuxing.c + * Remux streams from one container format to another. Data is copied from the + * input to the output without transcoding. */ #include diff --git a/doc/examples/resampling_audio.c b/doc/examples/resample_audio.c similarity index 97% rename from doc/examples/resampling_audio.c rename to doc/examples/resample_audio.c index 9f1521a5a5f..db9b4e5e087 100644 --- a/doc/examples/resampling_audio.c +++ b/doc/examples/resample_audio.c @@ -21,8 +21,12 @@ */ /** - * @example resampling_audio.c - * libswresample API use example. + * @file audio resampling API usage example + * @example resample_audio.c + * + * Generate a synthetic audio signal, and Use libswresample API to perform audio + * resampling. The output is written to a raw audio file to be played with + * ffplay. */ #include diff --git a/doc/examples/scaling_video.c b/doc/examples/scale_video.c similarity index 97% rename from doc/examples/scaling_video.c rename to doc/examples/scale_video.c index 587f3abe4f1..cb4da4a5767 100644 --- a/doc/examples/scaling_video.c +++ b/doc/examples/scale_video.c @@ -21,9 +21,10 @@ */ /** - * @file - * libswscale API use example. - * @example scaling_video.c + * @file libswscale API usage example + * @example scale_video.c + * + * Generate a synthetic video signal and use libswscale to perform rescaling. */ #include diff --git a/doc/examples/metadata.c b/doc/examples/show_metadata.c similarity index 93% rename from doc/examples/metadata.c rename to doc/examples/show_metadata.c index 734b12df165..abe3cc0cae3 100644 --- a/doc/examples/metadata.c +++ b/doc/examples/show_metadata.c @@ -21,9 +21,10 @@ */ /** - * @file - * Shows how the metadata API can be used in application programs. - * @example metadata.c + * @file libavformat metadata extraction API usage example + * @example show_metadata.c + * + * Show metadata from an input file. */ #include diff --git a/doc/examples/transcoding.c b/doc/examples/transcode.c similarity index 96% rename from doc/examples/transcoding.c rename to doc/examples/transcode.c index 013f89fc7d2..305181663c7 100644 --- a/doc/examples/transcoding.c +++ b/doc/examples/transcode.c @@ -23,9 +23,11 @@ */ /** - * @file - * API example for demuxing, decoding, filtering, encoding and muxing - * @example transcoding.c + * @file demuxing, decoding, filtering, encoding and muxing API usage example + * @example transcode.c + * + * Convert input to output file, applying some hard-coded filter-graph on both + * audio and video streams. */ #include @@ -95,6 +97,11 @@ static int open_input_file(const char *filename) "for stream #%u\n", i); return ret; } + + /* Inform the decoder about the timebase for the packet timestamps. + * This is highly recommended, but not mandatory. */ + codec_ctx->pkt_timebase = stream->time_base; + /* Reencode video & audio and remux subtitles etc. */ if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO || codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { @@ -264,7 +271,7 @@ static int init_filter(FilteringContext* fctx, AVCodecContext *dec_ctx, snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt, - dec_ctx->time_base.num, dec_ctx->time_base.den, + dec_ctx->pkt_timebase.num, dec_ctx->pkt_timebase.den, dec_ctx->sample_aspect_ratio.num, dec_ctx->sample_aspect_ratio.den); @@ -304,7 +311,7 @@ static int init_filter(FilteringContext* fctx, AVCodecContext *dec_ctx, av_channel_layout_describe(&dec_ctx->ch_layout, buf, sizeof(buf)); snprintf(args, sizeof(args), "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=%s", - dec_ctx->time_base.num, dec_ctx->time_base.den, dec_ctx->sample_rate, + dec_ctx->pkt_timebase.num, dec_ctx->pkt_timebase.den, dec_ctx->sample_rate, av_get_sample_fmt_name(dec_ctx->sample_fmt), buf); ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", @@ -434,6 +441,10 @@ static int encode_write_frame(unsigned int stream_index, int flush) /* encode filtered frame */ av_packet_unref(enc_pkt); + if (filt_frame && filt_frame->pts != AV_NOPTS_VALUE) + filt_frame->pts = av_rescale_q(filt_frame->pts, filt_frame->time_base, + stream->enc_ctx->time_base); + ret = avcodec_send_frame(stream->enc_ctx, filt_frame); if (ret < 0) @@ -488,6 +499,7 @@ static int filter_encode_write_frame(AVFrame *frame, unsigned int stream_index) break; } + filter->filtered_frame->time_base = av_buffersink_get_time_base(filter->buffersink_ctx);; filter->filtered_frame->pict_type = AV_PICTURE_TYPE_NONE; ret = encode_write_frame(stream_index, 0); av_frame_unref(filter->filtered_frame); @@ -542,9 +554,6 @@ int main(int argc, char **argv) av_log(NULL, AV_LOG_DEBUG, "Going to reencode&filter the frame\n"); - av_packet_rescale_ts(packet, - ifmt_ctx->streams[stream_index]->time_base, - stream->dec_ctx->time_base); ret = avcodec_send_packet(stream->dec_ctx, packet); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Decoding failed\n"); diff --git a/doc/examples/transcode_aac.c b/doc/examples/transcode_aac.c index 2d4f9a59d37..cf6edc98907 100644 --- a/doc/examples/transcode_aac.c +++ b/doc/examples/transcode_aac.c @@ -19,12 +19,11 @@ */ /** - * @file - * Simple audio converter - * + * @file audio transcoding to MPEG/AAC API usage example * @example transcode_aac.c - * Convert an input audio file to AAC in an MP4 container using FFmpeg. - * Formats other than MP4 are supported based on the output file extension. + * + * Convert an input audio file to AAC in an MP4 container. Formats other than + * MP4 are supported based on the output file extension. * @author Andreas Unterweger (dustsigns@gmail.com) */ @@ -448,26 +447,17 @@ static int init_converted_samples(uint8_t ***converted_input_samples, int error; /* Allocate as many pointers as there are audio channels. - * Each pointer will later point to the audio samples of the corresponding + * Each pointer will point to the audio samples of the corresponding * channels (although it may be NULL for interleaved formats). - */ - if (!(*converted_input_samples = calloc(output_codec_context->ch_layout.nb_channels, - sizeof(**converted_input_samples)))) { - fprintf(stderr, "Could not allocate converted input sample pointers\n"); - return AVERROR(ENOMEM); - } - - /* Allocate memory for the samples of all channels in one consecutive + * Allocate memory for the samples of all channels in one consecutive * block for convenience. */ - if ((error = av_samples_alloc(*converted_input_samples, NULL, + if ((error = av_samples_alloc_array_and_samples(converted_input_samples, NULL, output_codec_context->ch_layout.nb_channels, frame_size, output_codec_context->sample_fmt, 0)) < 0) { fprintf(stderr, "Could not allocate converted input samples (error '%s')\n", av_err2str(error)); - av_freep(&(*converted_input_samples)[0]); - free(*converted_input_samples); return error; } return 0; @@ -599,10 +589,9 @@ static int read_decode_convert_and_store(AVAudioFifo *fifo, ret = 0; cleanup: - if (converted_input_samples) { + if (converted_input_samples) av_freep(&converted_input_samples[0]); - free(converted_input_samples); - } + av_freep(&converted_input_samples); av_frame_free(&input_frame); return ret; diff --git a/doc/examples/vaapi_encode.c b/doc/examples/vaapi_encode.c index e232fa579a1..d5f472f6dd8 100644 --- a/doc/examples/vaapi_encode.c +++ b/doc/examples/vaapi_encode.c @@ -1,6 +1,4 @@ /* - * Video Acceleration API (video encoding) encode sample - * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights @@ -21,13 +19,12 @@ */ /** - * @file - * Intel VAAPI-accelerated encoding example. - * + * @file Intel VAAPI-accelerated encoding API usage example * @example vaapi_encode.c - * This example shows how to do VAAPI-accelerated encoding. now only support NV12 - * raw file, usage like: vaapi_encode 1920 1080 input.yuv output.h264 * + * Perform VAAPI-accelerated encoding. Read input from an NV12 raw + * file, and write the H.264 encoded data to an output raw file. + * Usage: vaapi_encode 1920 1080 input.yuv output.h264 */ #include diff --git a/doc/examples/vaapi_transcode.c b/doc/examples/vaapi_transcode.c index a174bb643a7..8367cb30404 100644 --- a/doc/examples/vaapi_transcode.c +++ b/doc/examples/vaapi_transcode.c @@ -1,6 +1,4 @@ /* - * Video Acceleration API (video transcoding) transcode sample - * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights @@ -21,11 +19,10 @@ */ /** - * @file - * Intel VAAPI-accelerated transcoding example. - * + * @file Intel VAAPI-accelerated transcoding API usage example * @example vaapi_transcode.c - * This example shows how to do VAAPI-accelerated transcoding. + * + * Perform VAAPI-accelerated transcoding. * Usage: vaapi_transcode input_stream codec output_stream * e.g: - vaapi_transcode input.mp4 h264_vaapi output_h264.mp4 * - vaapi_transcode input.mp4 vp9_vaapi output_vp9.ivf diff --git a/doc/fate.texi b/doc/fate.texi index 84508560157..2fa8c34c2d5 100644 --- a/doc/fate.texi +++ b/doc/fate.texi @@ -223,6 +223,14 @@ meaning only while running the regression tests. Specify how many threads to use while running regression tests, it is quite useful to detect thread-related regressions. +This variable may be set to the string "random", optionally followed by a +number, like "random99", This will cause each test to use a random number of +threads. If a number is specified, it is used as a maximum number of threads, +otherwise 16 is the maximum. + +In case a test fails, the thread count used for it will be written into the +errfile. + @item THREAD_TYPE Specify which threading strategy test, either @samp{slice} or @samp{frame}, by default @samp{slice+frame} diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi index 67b32942563..2273c39214a 100644 --- a/doc/ffmpeg.texi +++ b/doc/ffmpeg.texi @@ -17,9 +17,9 @@ ffmpeg [@var{global_options}] @{[@var{input_file_options}] -i @file{input_url}@} @chapter Description @c man begin DESCRIPTION -@command{ffmpeg} is a very fast video and audio converter that can also grab from -a live audio/video source. It can also convert between arbitrary sample -rates and resize video on the fly with a high quality polyphase filter. +@command{ffmpeg} is a universal media converter. It can read a wide variety of +inputs - including live grabbing/recording devices - filter, and transcode them +into a plethora of output formats. @command{ffmpeg} reads from an arbitrary number of input "files" (which can be regular files, pipes, network streams, grabbing devices, etc.), specified by the @@ -49,24 +49,32 @@ Do not mix input and output files -- first specify all input files, then all output files. Also do not mix options which belong to different files. All options apply ONLY to the next input or output file and are reset between files. +Some simple examples follow. + @itemize @item -To set the video bitrate of the output file to 64 kbit/s: +Convert an input media file to a different format, by re-encoding media streams: +@example +ffmpeg -i input.avi output.mp4 +@end example + +@item +Set the video bitrate of the output file to 64 kbit/s: @example -ffmpeg -i input.avi -b:v 64k -bufsize 64k output.avi +ffmpeg -i input.avi -b:v 64k -bufsize 64k output.mp4 @end example @item -To force the frame rate of the output file to 24 fps: +Force the frame rate of the output file to 24 fps: @example -ffmpeg -i input.avi -r 24 output.avi +ffmpeg -i input.avi -r 24 output.mp4 @end example @item -To force the frame rate of the input file (valid for raw formats only) -to 1 fps and the frame rate of the output file to 24 fps: +Force the frame rate of the input file (valid for raw formats only) to 1 fps and +the frame rate of the output file to 24 fps: @example -ffmpeg -r 1 -i input.m2v -r 24 output.avi +ffmpeg -r 1 -i input.m2v -r 24 output.mp4 @end example @end itemize @@ -769,6 +777,7 @@ syntax. See the @ref{filter_complex_option,,-filter_complex option} if you want to create filtergraphs with multiple inputs and/or outputs. +@anchor{filter_script option} @item -filter_script[:@var{stream_specifier}] @var{filename} (@emph{output,per-stream}) This option is similar to @option{-filter}, the only difference is that its argument is the name of the file from which a filtergraph description is to be @@ -1014,7 +1023,12 @@ If @var{pix_fmt} is a single @code{+}, ffmpeg selects the same pixel format as the input (or graph output) and automatic conversions are disabled. @item -sws_flags @var{flags} (@emph{input/output}) -Set SwScaler flags. +Set default flags for the libswscale library. These flags are used by +automatically inserted @code{scale} filters and those within simple +filtergraphs, if not overridden within the filtergraph definition. + +See the @ref{scaler_options,,ffmpeg-scaler manual,ffmpeg-scaler} for a list +of scaler options. @item -rc_override[:@var{stream_specifier}] @var{override} (@emph{output,per-stream}) Rate control override for specific intervals, formatted as "int,int,int" @@ -1025,28 +1039,23 @@ factor if negative. @item -psnr Calculate PSNR of compressed frames. This option is deprecated, pass the PSNR flag to the encoder instead, using @code{-flags +psnr}. -@item -vstats -Dump video coding statistics to @file{vstats_HHMMSS.log}. -@item -vstats_file @var{file} -Dump video coding statistics to @var{file}. -@item -vstats_version @var{file} -Specifies which version of the vstats format to use. Default is 2. -version = 1 : +@item -vstats +Dump video coding statistics to @file{vstats_HHMMSS.log}. See the +@ref{vstats_file_format,,vstats file format} section for the format description. -@code{frame= %5d q= %2.1f PSNR= %6.2f f_size= %6d s_size= %8.0fkB time= %0.3f br= %7.1fkbits/s avg_br= %7.1fkbits/s} +@item -vstats_file @var{file} +Dump video coding statistics to @var{file}. See the +@ref{vstats_file_format,,vstats file format} section for the format description. -version > 1: +@item -vstats_version @var{file} +Specify which version of the vstats format to use. Default is @code{2}. See the +@ref{vstats_file_format,,vstats file format} section for the format description. -@code{out= %2d st= %2d frame= %5d q= %2.1f PSNR= %6.2f f_size= %6d s_size= %8.0fkB time= %0.3f br= %7.1fkbits/s avg_br= %7.1fkbits/s} @item -top[:@var{stream_specifier}] @var{n} (@emph{output,per-stream}) top=1/bottom=0/auto=-1 field first -@item -dc @var{precision} -Intra_dc_precision. @item -vtag @var{fourcc/tag} (@emph{output}) Force video tag/fourcc. This is an alias for @code{-tag:v}. -@item -qphist (@emph{global}) -Show QP histogram @item -vbsf @var{bitstream_filter} Deprecated see -bsf @@ -1165,9 +1174,10 @@ Choose the first device and use the primary device context. @var{device} is the number of the Direct3D 11 display adapter. @item vaapi -@var{device} is either an X11 display name or a DRM render node. +@var{device} is either an X11 display name, a DRM render node or a DirectX adapter index. If not specified, it will attempt to open the default X11 display (@emph{$DISPLAY}) -and then the first DRM render node (@emph{/dev/dri/renderD128}). +and then the first DRM render node (@emph{/dev/dri/renderD128}), or the default +DirectX adapter on Windows. @item vdpau @var{device} is an X11 display name. @@ -1344,6 +1354,22 @@ List all hardware acceleration components enabled in this build of ffmpeg. Actual runtime availability depends on the hardware and its suitable driver being installed. +@item -fix_sub_duration_heartbeat[:@var{stream_specifier}] +Set a specific output video stream as the heartbeat stream according to which +to split and push through currently in-progress subtitle upon receipt of a +random access packet. + +This lowers the latency of subtitles for which the end packet or the following +subtitle has not yet been received. As a drawback, this will most likely lead +to duplication of subtitle events in order to cover the full duration, so +when dealing with use cases where latency of when the subtitle event is passed +on to output is not relevant this option should not be utilized. + +Requires @option{-fix_sub_duration} to be set for the relevant input subtitle +stream for this to have any effect, as well as for the input subtitle stream +having to be directly mapped to the same output in which the heartbeat stream +resides. + @end table @section Audio Options @@ -1687,6 +1713,9 @@ it may cause packet loss. It is useful for when flow speed of output packets is important, such as live streaming. @item -re (@emph{input}) Read input at native frame rate. This is equivalent to setting @code{-readrate 1}. +@item -readrate_initial_burst @var{seconds} +Set an initial read burst time, in seconds, after which @option{-re/-readrate} +will be enforced. @item -vsync @var{parameter} (@emph{global}) @itemx -fps_mode[:@var{stream_specifier}] @var{parameter} (@emph{output,per-stream}) Set video sync method / framerate mode. vsync is applied to all output video streams @@ -1728,12 +1757,6 @@ The default is -1.1. One possible usecase is to avoid framedrops in case of noisy timestamps or to increase frame drop precision in case of exact timestamps. -@item -adrift_threshold @var{time} -Set the minimum difference between timestamps and audio data (in seconds) to trigger -adding/dropping samples to make it match the timestamps. This option effectively is -a threshold to select between hard (add/drop) and soft (squeeze/stretch) compensation. -@code{-async} must be set to a positive value. - @item -apad @var{parameters} (@emph{output,per-stream}) Pad the output audio stream(s). This is the same as applying @code{-af apad}. Argument is a string of filter parameters composed the same as with the @code{apad} filter. @@ -1780,8 +1803,7 @@ Try to make the choice automatically, in order to generate a sane output. Default value is -1. @item -enc_time_base[:@var{stream_specifier}] @var{timebase} (@emph{output,per-stream}) -Set the encoder timebase. @var{timebase} is a floating point number, -and can assume one of the following values: +Set the encoder timebase. @var{timebase} can assume one of the following values: @table @option @item 0 @@ -1789,16 +1811,17 @@ Assign a default value according to the media type. For video - use 1/framerate, for audio - use 1/samplerate. -@item -1 -Use the input stream timebase when possible. +@item demux +Use the timebase from the demuxer. -If an input stream is not available, the default timebase will be used. +@item filter +Use the timebase from the filtergraph. -@item >0 +@item a positive number Use the provided number as the timebase. This field can be provided as a ratio of two integers (e.g. 1:24, 1:48000) -or as a floating point number (e.g. 0.04166, 2.0833e-5) +or as a decimal number (e.g. 0.04166, 2.0833e-5) @end table Default value is 0. @@ -1823,12 +1846,38 @@ results, but increase memory use and latency. The default value is 10 seconds. -@item -dts_delta_threshold -Timestamp discontinuity delta threshold. -@item -dts_error_threshold @var{seconds} -Timestamp error delta threshold. This threshold use to discard crazy/damaged -timestamps and the default is 30 hours which is arbitrarily picked and quite -conservative. +@item -dts_delta_threshold @var{threshold} +Timestamp discontinuity delta threshold, expressed as a decimal number +of seconds. + +The timestamp discontinuity correction enabled by this option is only +applied to input formats accepting timestamp discontinuity (for which +the @code{AV_FMT_DISCONT} flag is enabled), e.g. MPEG-TS and HLS, and +is automatically disabled when employing the @code{-copy_ts} option +(unless wrapping is detected). + +If a timestamp discontinuity is detected whose absolute value is +greater than @var{threshold}, ffmpeg will remove the discontinuity by +decreasing/increasing the current DTS and PTS by the corresponding +delta value. + +The default value is 10. + +@item -dts_error_threshold @var{threshold} +Timestamp error delta threshold, expressed as a decimal number of +seconds. + +The timestamp correction enabled by this option is only applied to +input formats not accepting timestamp discontinuity (for which the +@code{AV_FMT_DISCONT} flag is not enabled). + +If a timestamp discontinuity is detected whose absolute value is +greater than @var{threshold}, ffmpeg will drop the PTS/DTS timestamp +value. + +The default value is @code{3600*30} (30 hours), which is arbitrarily +picked and quite conservative. + @item -muxdelay @var{seconds} (@emph{output}) Set the maximum demux-decode delay. @item -muxpreload @var{seconds} (@emph{output}) @@ -1939,6 +1988,7 @@ The default is the number of available CPUs. Define a complex filtergraph, i.e. one with arbitrary number of inputs and/or outputs. Equivalent to @option{-filter_complex}. +@anchor{filter_complex_script option} @item -filter_complex_script @var{filename} (@emph{global}) This option is similar to @option{-filter_complex}, the only difference is that its argument is the name of the file from which a complex filtergraph @@ -2047,6 +2097,125 @@ encoder/muxer, it does not change the stream to conform to this value. Setting values that do not match the stream properties may result in encoding failures or invalid output files. +@anchor{stats_enc_options} +@item -stats_enc_pre[:@var{stream_specifier}] @var{path} (@emph{output,per-stream}) +@item -stats_enc_post[:@var{stream_specifier}] @var{path} (@emph{output,per-stream}) +@item -stats_mux_pre[:@var{stream_specifier}] @var{path} (@emph{output,per-stream}) +Write per-frame encoding information about the matching streams into the file +given by @var{path}. + +@option{-stats_enc_pre} writes information about raw video or audio frames right +before they are sent for encoding, while @option{-stats_enc_post} writes +information about encoded packets as they are received from the encoder. +@option{-stats_mux_pre} writes information about packets just as they are about to +be sent to the muxer. Every frame or packet produces one line in the specified +file. The format of this line is controlled by @option{-stats_enc_pre_fmt} / +@option{-stats_enc_post_fmt} / @option{-stats_mux_pre_fmt}. + +When stats for multiple streams are written into a single file, the lines +corresponding to different streams will be interleaved. The precise order of +this interleaving is not specified and not guaranteed to remain stable between +different invocations of the program, even with the same options. + +@item -stats_enc_pre_fmt[:@var{stream_specifier}] @var{format_spec} (@emph{output,per-stream}) +@item -stats_enc_post_fmt[:@var{stream_specifier}] @var{format_spec} (@emph{output,per-stream}) +@item -stats_mux_pre_fmt[:@var{stream_specifier}] @var{format_spec} (@emph{output,per-stream}) +Specify the format for the lines written with @option{-stats_enc_pre} / +@option{-stats_enc_post} / @option{-stats_mux_pre}. + +@var{format_spec} is a string that may contain directives of the form +@var{@{fmt@}}. @var{format_spec} is backslash-escaped --- use \@{, \@}, and \\ +to write a literal @{, @}, or \, respectively, into the output. + +The directives given with @var{fmt} may be one of the following: +@table @option +@item fidx +Index of the output file. + +@item sidx +Index of the output stream in the file. + +@item n +Frame number. Pre-encoding: number of frames sent to the encoder so far. +Post-encoding: number of packets received from the encoder so far. +Muxing: number of packets submitted to the muxer for this stream so far. + +@item ni +Input frame number. Index of the input frame (i.e. output by a decoder) that +corresponds to this output frame or packet. -1 if unavailable. + +@item tb +Timebase in which this frame/packet's timestamps are expressed, as a rational +number @var{num/den}. Note that encoder and muxer may use different timebases. + +@item tbi +Timebase for @var{ptsi}, as a rational number @var{num/den}. Available when +@var{ptsi} is available, @var{0/1} otherwise. + +@item pts +Presentation timestamp of the frame or packet, as an integer. Should be +multiplied by the timebase to compute presentation time. + +@item ptsi +Presentation timestamp of the input frame (see @var{ni}), as an integer. Should +be multiplied by @var{tbi} to compute presentation time. Printed as +(2^63 - 1 = 9223372036854775807) when not available. + +@item t +Presentation time of the frame or packet, as a decimal number. Equal to +@var{pts} multiplied by @var{tb}. + +@item ti +Presentation time of the input frame (see @var{ni}), as a decimal number. Equal +to @var{ptsi} multiplied by @var{tbi}. Printed as inf when not available. + +@item dts (@emph{packet}) +Decoding timestamp of the packet, as an integer. Should be multiplied by the +timebase to compute presentation time. + +@item dt (@emph{packet}) +Decoding time of the frame or packet, as a decimal number. Equal to +@var{dts} multiplied by @var{tb}. + +@item sn (@emph{frame,audio}) +Number of audio samples sent to the encoder so far. + +@item samp (@emph{frame,audio}) +Number of audio samples in the frame. + +@item size (@emph{packet}) +Size of the encoded packet in bytes. + +@item br (@emph{packet}) +Current bitrate in bits per second. Post-encoding only. + +@item abr (@emph{packet}) +Average bitrate for the whole stream so far, in bits per second, -1 if it cannot +be determined at this point. Post-encoding only. +@end table + +Directives tagged with @emph{packet} may only be used with +@option{-stats_enc_post_fmt} and @option{-stats_mux_pre_fmt}. + +Directives tagged with @emph{frame} may only be used with +@option{-stats_enc_pre_fmt}. + +Directives tagged with @emph{audio} may only be used with audio streams. + +The default format strings are: +@table @option +@item pre-encoding +@{fidx@} @{sidx@} @{n@} @{t@} +@item post-encoding +@{fidx@} @{sidx@} @{n@} @{t@} +@end table +In the future, new items may be added to the end of the default formatting +strings. Users who depend on the format staying exactly the same, should +prescribe it manually. + +Note that stats for different streams written into the same file may have +different formats. + @end table @section Preset files @@ -2104,6 +2273,63 @@ search for the file @file{libvpx-1080p.avpreset}. If no such file is found, then ffmpeg will search for a file named @var{arg}.avpreset in the same directories. +@anchor{vstats_file_format} +@section vstats file format +The @code{-vstats} and @code{-vstats_file} options enable generation of a file +containing statistics about the generated video outputs. + +The @code{-vstats_version} option controls the format version of the generated +file. + +With version @code{1} the format is: +@example +frame= @var{FRAME} q= @var{FRAME_QUALITY} PSNR= @var{PSNR} f_size= @var{FRAME_SIZE} s_size= @var{STREAM_SIZE}kB time= @var{TIMESTAMP} br= @var{BITRATE}kbits/s avg_br= @var{AVERAGE_BITRATE}kbits/s +@end example + +With version @code{2} the format is: +@example +out= @var{OUT_FILE_INDEX} st= @var{OUT_FILE_STREAM_INDEX} frame= @var{FRAME_NUMBER} q= @var{FRAME_QUALITY}f PSNR= @var{PSNR} f_size= @var{FRAME_SIZE} s_size= @var{STREAM_SIZE}kB time= @var{TIMESTAMP} br= @var{BITRATE}kbits/s avg_br= @var{AVERAGE_BITRATE}kbits/s +@end example + +The value corresponding to each key is described below: +@table @option +@item avg_br +average bitrate expressed in Kbits/s + +@item br +bitrate expressed in Kbits/s + +@item frame +number of encoded frame + +@item out +out file index + +@item PSNR +Peak Signal to Noise Ratio + +@item q +quality of the frame + +@item f_size +encoded packet size expressed as number of bytes + +@item s_size +stream size expressed in KiB + +@item st +out file stream index + +@item time +time of the packet + +@item type +picture type +@end table + +See also the @ref{stats_enc_options,,-stats_enc options} for an alternative way +to show encoding statistics. + @c man end OPTIONS @chapter Examples @@ -2367,7 +2593,7 @@ ffmpeg-devices(1), ffmpeg-protocols(1), ffmpeg-filters(1) @ignore @setfilename ffmpeg -@settitle ffmpeg video converter +@settitle ffmpeg media converter @end ignore diff --git a/doc/ffprobe.xsd b/doc/ffprobe.xsd index 0920380108f..87ca265d63d 100644 --- a/doc/ffprobe.xsd +++ b/doc/ffprobe.xsd @@ -106,6 +106,10 @@ + + + + diff --git a/doc/filters.texi b/doc/filters.texi index 57088ccc6c4..43e9c037b9a 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -171,6 +171,17 @@ within the quoted text; otherwise the argument string is considered terminated when the next special character (belonging to the set @samp{[]=;,}) is encountered. +A special syntax implemented in the @command{ffmpeg} CLI tool allows loading +option values from files. This is done be prepending a slash '/' to the option +name, then the supplied value is interpreted as a path from which the actual +value is loaded. E.g. +@example +ffmpeg -i -vf drawtext=/text=/tmp/some_text +@end example +will load the text to be drawn from @file{/tmp/some_text}. API users wishing to +implement a similar feature should use the @code{avfilter_graph_segment_*()} +functions together with custom IO code. + The name and arguments of the filter are optionally preceded and followed by a list of link labels. A link label allows one to name a link and associate it to a filter output @@ -203,6 +214,23 @@ In a complete filterchain all the unlabelled filter input and output pads must be connected. A filtergraph is considered valid if all the filter input and output pads of all the filterchains are connected. +Leading and trailing whitespaces (space, tabs, or line feeds) separating tokens +in the filtergraph specification are ignored. This means that the filtergraph +can be expressed using empty lines and spaces to improve redability. + +For example, the filtergraph: +@example +testsrc,split[L1],hflip[L2];[L1][L2] hstack +@end example + +can be represented as: +@example +testsrc, +split [L1], hflip [L2]; + +[L1][L2] hstack +@end example + Libavfilter will automatically insert @ref{scale} filters where format conversion is required. It is possible to specify swscale flags for those automatically inserted scalers by prepending @@ -271,6 +299,18 @@ previous string will finally result in: -vf "drawtext=text=this is a \\\\\\'string\\\\\\'\\\\: may contain one\\, or more\\, special characters" @end example +In order to avoid cumbersome escaping when using a commandline tool accepting a +filter specification as input, it is advisable to avoid direct inclusion of the +filter or options specification in the shell. + +For example, in case of the @ref{drawtext,,drawtext filter}, you might prefer to +use the @option{textfile} option in place of @option{text} to specify the text +to render. + +When using the @command{ffmpeg} tool, you might consider to use the +@ref{filter_script option,,-filter_script option,ffmpeg} or +@ref{filter_complex_script option,,-filter_complex_script option,ffmpeg}. + @chapter Timeline editing Some filters support a generic @option{enable} option. For the filters @@ -288,7 +328,8 @@ timestamp expressed in seconds, NAN if the input timestamp is unknown sequential number of the input frame, starting from 0 @item pos -the position in the file of the input frame, NAN if unknown +the position in the file of the input frame, NAN if unknown; deprecated, do +not use @item w @item h @@ -966,15 +1007,15 @@ A description of the accepted options follows. @table @option @item threshold Set the detection threshold used to trigger equalization. -Threshold detection is using bandpass filter. +Threshold detection is using detection filter. Default value is 0. Allowed range is from 0 to 100. @item dfrequency -Set the detection frequency in Hz used for bandpass filter used to trigger equalization. +Set the detection frequency in Hz used for detection filter used to trigger equalization. Default value is 1000 Hz. Allowed range is between 2 and 1000000 Hz. @item dqfactor -Set the detection resonance factor for bandpass filter used to trigger equalization. +Set the detection resonance factor for detection filter used to trigger equalization. Default value is 1. Allowed range is from 0.001 to 1000. @item tfrequency @@ -1012,7 +1053,7 @@ Set the mode of filter operation, can be one of the following: @table @samp @item listen -Output only isolated bandpass signal. +Output only isolated detection signal. @item cut Cut frequencies above detection threshold. @item boost @@ -1020,6 +1061,17 @@ Boost frequencies bellow detection threshold. @end table Default mode is @samp{cut}. +@item dftype +Set the type of detection filter, can be one of the following: + +@table @samp +@item bandpass +@item lowpass +@item highpass +@item peak +@end table +Default type is @samp{bandpass}. + @item tftype Set the type of target filter, can be one of the following: @@ -1034,11 +1086,41 @@ Default type is @samp{bell}. Set processing direction relative to threshold. @table @samp @item downward -Boost or cut if threshhold is higher than detected volume. +Boost/Cut if threshold is higher/lower than detected volume. @item upward -Boost or cut if threshhold is lower than detected volume. +Boost/Cut if threshold is lower/higher than detected volume. @end table Default direction is @samp{downward}. + +@item auto +Automatically gather threshold from detection filter. By default +is @samp{disabled}. +This option is useful to detect threshold in certain time frame of +input audio stream, in such case option value is changed at runtime. + +Available values are: +@table @samp +@item disabled +Disable using automatically gathered threshold value. +@item off +Stop picking threshold value. +@item on +Start picking threshold value. +@end table + +@item precision +Set which precision to use when processing samples. + +@table @option +@item auto +Auto pick internal sample format depending on other filters. + +@item float +Always use single-floating point precision sample format. + +@item double +Always use double-floating point precision sample format. +@end table @end table @subsection Commands @@ -1792,6 +1874,12 @@ Always use double-floating point precision sample format. @end table Default value is auto. + +@item irload +Set when to load IR stream. Can be @code{init} or @code{access}. +First one load and prepares all IRs on initialization, second one +once on first access of specific IR. +Default is @code{init}. @end table @subsection Examples @@ -2275,6 +2363,10 @@ Set maximal number of samples. Default is 0. @item start Set first sample of loop. Default is 0. + +@item time +Set the time of loop start in seconds. +Only used if option named @var{start} is set to @code{-1}. @end table @anchor{amerge} @@ -2586,10 +2678,13 @@ Pass the 1st input. Pass the 2nd input. @item o -Pass filtered samples. +Pass difference between desired, 2nd input and error signal estimate. @item n -Pass difference between desired and filtered samples. +Pass difference between input, 1st input and error signal estimate. + +@item e +Pass error signal estimated samples. Default value is @var{o}. @end table @@ -2887,6 +2982,47 @@ atrim=end=5,areverse @end example @end itemize +@section arls +Apply Recursive Least Squares algorithm to the first audio stream using the second audio stream. + +This adaptive filter is used to mimic a desired filter by recursively finding the filter coefficients that +relate to producing the minimal weighted linear least squares cost function of the error signal (difference +between the desired, 2nd input audio stream and the actual signal, the 1st input audio stream). + +A description of the accepted options follows. + +@table @option +@item order +Set the filter order. + +@item lambda +Set the forgetting factor. + +@item delta +Set the coefficient to initialize internal covariance matrix. + +@item out_mode +Set the filter output samples. It accepts the following values: +@table @option +@item i +Pass the 1st input. + +@item d +Pass the 2nd input. + +@item o +Pass difference between desired, 2nd input and error signal estimate. + +@item n +Pass difference between input, 1st input and error signal estimate. + +@item e +Pass error signal estimated samples. + +Default value is @var{o}. +@end table +@end table + @section arnndn Reduce noise from speech using Recurrent Neural Networks. @@ -2978,10 +3114,6 @@ depends on the filter input pad, and is usually 1/@var{sample_rate}. @item pts_time The presentation timestamp of the input frame in seconds. -@item pos -position of the frame in the input stream, -1 if this information in -unavailable and/or meaningless (for example in case of synthetic audio) - @item fmt The sample format. @@ -3167,62 +3299,63 @@ where @code{X} is channel number starting from 1 or string @code{Overall}. Defau disabled. Available keys for each channel are: -DC_offset -Min_level -Max_level -Min_difference -Max_difference -Mean_difference -RMS_difference -Peak_level -RMS_peak -RMS_trough -Crest_factor -Flat_factor -Peak_count -Noise_floor -Noise_floor_count -Entropy -Bit_depth -Dynamic_range -Zero_crossings -Zero_crossings_rate -Number_of_NaNs -Number_of_Infs -Number_of_denormals - -and for Overall: -DC_offset -Min_level -Max_level -Min_difference -Max_difference -Mean_difference -RMS_difference -Peak_level -RMS_level -RMS_peak -RMS_trough -Flat_factor -Peak_count -Noise_floor -Noise_floor_count -Entropy -Bit_depth -Number_of_samples -Number_of_NaNs -Number_of_Infs -Number_of_denormals - -For example full key look like this @code{lavfi.astats.1.DC_offset} or -this @code{lavfi.astats.Overall.Peak_count}. - -For description what each key means read below. +@var{Bit_depth} +@var{Crest_factor} +@var{DC_offset} +@var{Dynamic_range} +@var{Entropy} +@var{Flat_factor} +@var{Max_difference} +@var{Max_level} +@var{Mean_difference} +@var{Min_difference} +@var{Min_level} +@var{Noise_floor} +@var{Noise_floor_count} +@var{Number_of_Infs} +@var{Number_of_NaNs} +@var{Number_of_denormals} +@var{Peak_count} +@var{Abs_Peak_count} +@var{Peak_level} +@var{RMS_difference} +@var{RMS_peak} +@var{RMS_trough} +@var{Zero_crossings} +@var{Zero_crossings_rate} + +and for @code{Overall}: +@var{Bit_depth} +@var{DC_offset} +@var{Entropy} +@var{Flat_factor} +@var{Max_difference} +@var{Max_level} +@var{Mean_difference} +@var{Min_difference} +@var{Min_level} +@var{Noise_floor} +@var{Noise_floor_count} +@var{Number_of_Infs} +@var{Number_of_NaNs} +@var{Number_of_denormals} +@var{Number_of_samples} +@var{Peak_count} +@var{Abs_Peak_count} +@var{Peak_level} +@var{RMS_difference} +@var{RMS_level} +@var{RMS_peak} +@var{RMS_trough} + +For example, a full key looks like @code{lavfi.astats.1.DC_offset} or +@code{lavfi.astats.Overall.Peak_count}. + +Read below for the description of the keys. @item reset Set the number of frames over which cumulative stats are calculated before -being reset -Default is disabled. +being reset. Default is disabled. @item measure_perchannel Select the parameters which are measured per channel. The metadata keys can @@ -3236,71 +3369,95 @@ be used as flags, default is @option{all} which measures everything. @end table -A description of each shown parameter follows: +A description of the measure keys follow: @table @option -@item DC offset -Mean amplitude displacement from zero. +@item none +no measures -@item Min level -Minimal sample level. +@item all +all measures + +@item Bit_depth +overall bit depth of audio, i.e. number of bits used for each sample -@item Max level -Maximal sample level. +@item Crest_factor +standard ratio of peak to RMS level (note: not in dB) -@item Min difference -Minimal difference between two consecutive samples. +@item DC_offset +mean amplitude displacement from zero -@item Max difference -Maximal difference between two consecutive samples. +@item Dynamic_range +measured dynamic range of audio in dB -@item Mean difference -Mean difference between two consecutive samples. -The average of each difference between two consecutive samples. +@item Entropy +entropy measured across whole audio, entropy of value near 1.0 is typically measured for white noise -@item RMS difference -Root Mean Square difference between two consecutive samples. +@item Flat_factor +flatness (i.e. consecutive samples with the same value) of the signal at its peak levels +(i.e. either @var{Min_level} or @var{Max_level}) -@item Peak level dB -@item RMS level dB -Standard peak and RMS level measured in dBFS. +@item Max_difference +maximal difference between two consecutive samples -@item RMS peak dB -@item RMS trough dB -Peak and trough values for RMS level measured over a short window. +@item Max_level +maximal sample level -@item Crest factor -Standard ratio of peak to RMS level (note: not in dB). +@item Mean_difference +mean difference between two consecutive samples, i.e. the average of each difference between two consecutive samples -@item Flat factor -Flatness (i.e. consecutive samples with the same value) of the signal at its peak levels -(i.e. either @var{Min level} or @var{Max level}). +@item Min_difference +minimal difference between two consecutive samples -@item Peak count -Number of occasions (not the number of samples) that the signal attained either -@var{Min level} or @var{Max level}. +@item Min_level +minimal sample level -@item Noise floor dB -Minimum local peak measured in dBFS over a short window. +@item Noise_floor +minimum local peak measured in dBFS over a short window -@item Noise floor count -Number of occasions (not the number of samples) that the signal attained -@var{Noise floor}. +@item Noise_floor_count +number of occasions (not the number of samples) that the signal attained +@var{Noise floor} -@item Entropy -Entropy measured across whole audio. Entropy of value near 1.0 is typically measured for white noise. +@item Number_of_Infs +number of samples with an infinite value + +@item Number_of_NaNs +number of samples with a NaN (not a number) value + +@item Number_of_denormals +number of samples with a subnormal value + +@item Number_of_samples +number of samples + +@item Peak_count +number of occasions (not the number of samples) that the signal attained either +@var{Min_level} or @var{Max_level} -@item Bit depth -Overall bit depth of audio. Number of bits used for each sample. +@item Abs_Peak_count +number of occasions that the absolute samples taken from the signal attained +max absolute value of @var{Min_level} and @var{Max_level} -@item Dynamic range -Measured dynamic range of audio in dB. +@item Peak_level +standard peak level measured in dBFS + +@item RMS_difference +Root Mean Square difference between two consecutive samples + +@item RMS_level +standard RMS level measured in dBFS + +@item RMS_peak +@item RMS_trough +peak and trough values for RMS level measured over a short window, +measured in dBFS. @item Zero crossings -Number of points where the waveform crosses the zero level axis. +number of points where the waveform crosses the zero level axis @item Zero crossings rate -Rate of Zero crossings and number of audio samples. +rate of Zero crossings and number of audio samples @end table @section asubboost @@ -3605,8 +3762,8 @@ Set size of segment over which cross-correlation is calculated. Default is 256. Allowed range is from 2 to 131072. @item algo -Set algorithm for cross-correlation. Can be @code{slow} or @code{fast}. -Default is @code{slow}. Fast algorithm assumes mean values over any given segment +Set algorithm for cross-correlation. Can be @code{slow} or @code{fast} or @code{best}. +Default is @code{best}. Fast algorithm assumes mean values over any given segment are always zero and thus need much less calculations to make. This is generally not true, but is valid for typical audio streams. @end table @@ -5911,6 +6068,16 @@ ReplayGain scanner filter. This filter takes an audio stream as an input and outputs it unchanged. At end of filtering it displays @code{track_gain} and @code{track_peak}. +The filter accepts the following exported read-only options: + +@table @option +@item track_gain +Exported track gain in dB at end of stream. + +@item track_peak +Exported track peak at end of stream. +@end table + @section resample Convert the audio sample format, sample rate and channel layout. It is @@ -6252,19 +6419,20 @@ trimming. Default is 0, which is equal to trimming all samples detected as silence. @item start_mode -Specify mode of detection of silence end in start of multi-channel audio. +Specify mode of detection of silence end at start of multi-channel audio. Can be @var{any} or @var{all}. Default is @var{any}. -With @var{any}, any sample that is detected as non-silence will cause -stopped trimming of silence. -With @var{all}, only if all channels are detected as non-silence will cause -stopped trimming of silence. +With @var{any}, any sample from any channel that is detected as non-silence +will trigger end of silence trimming at start of audio stream. +With @var{all}, only if every sample from every channel is detected as non-silence +will trigger end of silence trimming at start of audio stream, limited usage. @item stop_periods -Set the count for trimming silence from the end of audio. +Set the count for trimming silence from the end of audio. When specifying a +positive value, it trims audio after it finds specified silence period. To remove silence from the middle of a file, specify a @var{stop_periods} that is negative. This value is then treated as a positive value and is used to indicate the effect should restart processing as specified by -@var{start_periods}, making it suitable for removing periods of silence +@var{stop_periods}, making it suitable for removing periods of silence in the middle of the audio. Default value is @code{0}. @@ -6286,22 +6454,49 @@ trimming. Default is 0, which is equal to trimming all samples detected as silence. @item stop_mode -Specify mode of detection of silence start in end of multi-channel audio. -Can be @var{any} or @var{all}. Default is @var{any}. -With @var{any}, any sample that is detected as non-silence will cause -stopped trimming of silence. -With @var{all}, only if all channels are detected as non-silence will cause -stopped trimming of silence. +Specify mode of detection of silence start after start of multi-channel audio. +Can be @var{any} or @var{all}. Default is @var{all}. +With @var{any}, any sample from any channel that is detected as silence +will trigger start of silence trimming after start of audio stream, limited usage. +With @var{all}, only if every sample from every channel is detected as silence +will trigger start of silence trimming after start of audio stream. @item detection -Set how is silence detected. Can be @code{rms} or @code{peak}. Second is faster -and works better with digital silence which is exactly 0. +Set how is silence detected. +@table @option +@item avg +Mean of absolute values of samples in moving window. +@item rms +Root squared mean of absolute values of samples in moving window. +@item peak +Maximum of absolute values of samples in moving window. +@item median +Median of absolute values of samples in moving window. +@item ptp +Absolute of max peak to min peak difference of samples in moving window. +@item dev +Standard deviation of values of samples in moving window. +@end table Default value is @code{rms}. @item window Set duration in number of seconds used to calculate size of window in number -of samples for detecting silence. +of samples for detecting silence. Using @code{0} will effectively disable +any windowing and use only single sample per channel for silence detection. +In that case it may be needed to also set @option{start_silence} and/or +@option{stop_silence} to nonzero values with also @option{start_duration} and/or +@option{stop_duration} to nonzero values. Default value is @code{0.02}. Allowed range is from @code{0} to @code{10}. + +@item timestamp +Set processing mode of every audio frame output timestamp. +@table @option +@item write +Full timestamps rewrite, keep only the start time for the first output frame. +@item copy +Non-dropped frames are left with same timestamp as input audio frame. +@end table +Defaults value is @code{write}. @end table @subsection Examples @@ -6329,8 +6524,31 @@ silence is detected in all channels at same positions in stream: @example silenceremove=window=0:detection=peak:stop_mode=all:start_mode=all:stop_periods=-1:stop_threshold=0 @end example + +@item +Trim every 2nd encountered silence period from beginning to end where there is +more than 1 second of silence per silence period in audio: +@example +silenceremove=stop_periods=-2:stop_duration=1:stop_threshold=-90dB +@end example + +@item +Similar as above, but keep maximum of 0.5 seconds of silence from each trimmed period: +@example +silenceremove=stop_periods=-2:stop_duration=1:stop_threshold=-90dB:stop_silence=0.5 +@end example + +@item +Similar as above, but keep maximum of 1.5 seconds of silence from start of audio: +@example +silenceremove=stop_periods=-2:stop_duration=1:stop_threshold=-90dB:stop_silence=0.5:start_periods=1:start_duration=1:start_silence=1.5:stop_threshold=-90dB +@end example @end itemize +@subsection Commands + +This filter supports some above options as @ref{commands}. + @section sofalizer SOFAlizer uses head-related transfer functions (HRTFs) to create virtual @@ -7260,7 +7478,7 @@ number of samples consumed by the filter @item nb_samples number of samples in the current frame @item pos -original frame position in the file +original frame position in the file; deprecated, do not use @item pts frame PTS @item sample_rate @@ -7519,6 +7737,98 @@ aevalsrc="0.1*sin(2*PI*(360-2.5/2)*t) | 0.1*sin(2*PI*(360+2.5/2)*t)" @end itemize +@section afdelaysrc + +Generate a fractional delay FIR coefficients. + +The resulting stream can be used with @ref{afir} filter for filtering the audio signal. + +The filter accepts the following options: + +@table @option +@item delay, d +Set the fractional delay. Default is 0. + +@item sample_rate, r +Set the sample rate, default is 44100. + +@item nb_samples, n +Set the number of samples per each frame. Default is 1024. + +@item taps, t +Set the number of filter coefficents in output audio stream. +Default value is 0. + +@item channel_layout, c +Specifies the channel layout, and can be a string representing a channel layout. +The default value of @var{channel_layout} is "stereo". +@end table + +@section afireqsrc + +Generate a FIR equalizer coefficients. + +The resulting stream can be used with @ref{afir} filter for filtering the audio signal. + +The filter accepts the following options: + +@table @option +@item preset, p +Set equalizer preset. +Default preset is @code{flat}. + +Available presets are: +@table @samp +@item custom +@item flat +@item acoustic +@item bass +@item beats +@item classic +@item clear +@item deep bass +@item dubstep +@item electronic +@item hard-style +@item hip-hop +@item jazz +@item metal +@item movie +@item pop +@item r&b +@item rock +@item vocal booster +@end table + +@item gains, g +Set custom gains for each band. Only used if the preset option is set to @code{custom}. +Gains are separated by white spaces and each gain is set in dBFS. +Default is @code{0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0}. + +@item bands, b +Set the custom bands from where custon equalizer gains are set. +This must be in strictly increasing order. Only used if the preset option is set to @code{custom}. +Bands are separated by white spaces and each band represent frequency in Hz. +Default is @code{25 40 63 100 160 250 400 630 1000 1600 2500 4000 6300 10000 16000 24000}. + +@item taps, t +Set number of filter coefficents in output audio stream. +Default value is @code{4096}. + +@item sample_rate, r +Set sample rate of output audio stream, default is @code{44100}. + +@item nb_samples, n +Set number of samples per each frame in output audio stream. Default is @code{1024}. + +@item interp, i +Set interpolation method for FIR equalizer coefficients. Can be @code{linear} or @code{cubic}. + +@item phase, h +Set phase type of FIR filter. Can be @code{linear} or @code{min}: minimum-phase. +Default is minimum-phase filter. +@end table + @section afirsrc Generate a FIR coefficients using frequency sampling method. @@ -7702,6 +8012,9 @@ Specify a value used to seed the PRNG. @item nb_samples, n Set the number of samples per each output frame, default is 1024. + +@item density +Set the density (0.0 - 1.0) for the velvet noise generator, default is 0.05. @end table @subsection Examples @@ -8169,10 +8482,10 @@ This filter supports the all above options as @ref{commands}. @section bbox Compute the bounding box for the non-black pixels in the input frame -luminance plane. +luma plane. This filter computes the bounding box containing all the pixels with a -luminance value greater than the minimum allowed value. +luma value greater than the minimum allowed value. The parameters describing the bounding box are printed on the filter log. @@ -8180,7 +8493,7 @@ The filter accepts the following option: @table @option @item min_val -Set the minimal luminance value. Default is @code{16}. +Set the minimal luma value. Default is @code{16}. @end table @subsection Commands @@ -8303,14 +8616,14 @@ Default value is 0.98. @item pixel_black_th, pix_th Set the threshold for considering a pixel "black". -The threshold expresses the maximum pixel luminance value for which a +The threshold expresses the maximum pixel luma value for which a pixel is considered "black". The provided value is scaled according to the following equation: @example -@var{absolute_threshold} = @var{luminance_minimum_value} + @var{pixel_black_th} * @var{luminance_range_size} +@var{absolute_threshold} = @var{luma_minimum_value} + @var{pixel_black_th} * @var{luma_range_size} @end example -@var{luminance_range_size} and @var{luminance_minimum_value} depend on +@var{luma_range_size} and @var{luma_minimum_value} depend on the input video format, the range is [0-255] for YUV full-range formats and [16-235] for YUV non full-range formats. @@ -8778,6 +9091,7 @@ boxblur=luma_radius=min(h\,w)/10:luma_power=1:chroma_radius=min(cw\,ch)/10:chrom @end example @end itemize +@anchor{bwdif} @section bwdif Deinterlace the input video ("bwdif" stands for "Bob Weaver @@ -8831,6 +9145,68 @@ Only deinterlace frames marked as interlaced. The default value is @code{all}. @end table +@section bwdif_cuda + +Deinterlace the input video using the @ref{bwdif} algorithm, but implemented +in CUDA so that it can work as part of a GPU accelerated pipeline with nvdec +and/or nvenc. + +It accepts the following parameters: + +@table @option +@item mode +The interlacing mode to adopt. It accepts one of the following values: + +@table @option +@item 0, send_frame +Output one frame for each frame. +@item 1, send_field +Output one frame for each field. +@end table + +The default value is @code{send_field}. + +@item parity +The picture field parity assumed for the input interlaced video. It accepts one +of the following values: + +@table @option +@item 0, tff +Assume the top field is first. +@item 1, bff +Assume the bottom field is first. +@item -1, auto +Enable automatic detection of field parity. +@end table + +The default value is @code{auto}. +If the interlacing is unknown or the decoder does not export this information, +top field first will be assumed. + +@item deint +Specify which frames to deinterlace. Accepts one of the following +values: + +@table @option +@item 0, all +Deinterlace all frames. +@item 1, interlaced +Only deinterlace frames marked as interlaced. +@end table + +The default value is @code{all}. +@end table + +@section ccrepack + +Repack CEA-708 closed captioning side data + +This filter fixes various issues seen with commerical encoders +related to upstream malformed CEA-708 payloads, specifically +incorrect number of tuples (wrong cc_count for the target FPS), +and incorrect ordering of tuples (i.e. the CEA-608 tuples are not at +the first entries in the payload). + @section cas Apply Contrast Adaptive Sharpen filter to video stream. @@ -9294,7 +9670,7 @@ Set the red highlight spot. Allowed range is from -1.0 to 1.0. Default value is 0. @item bh -Set the red highlight spot. Allowed range is from -1.0 to 1.0. +Set the blue highlight spot. Allowed range is from -1.0 to 1.0. Default value is 0. @item saturation @@ -10355,7 +10731,8 @@ pixel format "yuv422p" @var{hsub} is 2 and @var{vsub} is 1. The number of the input frame, starting from 0. @item pos -the position in the file of the input frame, NAN if unknown +the position in the file of the input frame, NAN if unknown; deprecated, +do not use @item t The timestamp expressed in seconds. It's NAN if the input timestamp is unknown. @@ -10444,7 +10821,7 @@ crop=in_w/2:in_h/2:(in_w-out_w)/2+((in_w-out_w)/2)*sin(n/10):(in_h-out_h)/2 +((i @item Apply erratic camera effect depending on timestamp: @example -crop=in_w/2:in_h/2:(in_w-out_w)/2+((in_w-out_w)/2)*sin(t*10):(in_h-out_h)/2 +((in_h-out_h)/2)*sin(t*13)" +crop=in_w/2:in_h/2:(in_w-out_w)/2+((in_w-out_w)/2)*sin(t*10):(in_h-out_h)/2 +((in_h-out_h)/2)*sin(t*13) @end example @item @@ -10560,6 +10937,16 @@ ffmpeg -flags2 +export_mvs -i file.mp4 -vf cropdetect=mode=mvedges,metadata=mode @end example @end itemize +@subsection Commands + +This filter supports the following commands: + +@table @option +@item limit +The command accepts the same syntax of the corresponding option. +If the specified expression is not valid, it is kept at its current value. +@end table + @anchor{cue} @section cue @@ -11222,9 +11609,6 @@ See @url{http://openaccess.thecvf.com/content_ECCV_2018/papers/Xia_Li_Recurrent_ Training as well as model generation scripts are provided in the repository at @url{https://github.com/XueweiMeng/derain_filter.git}. -Native model files (.model) can be generated from TensorFlow model -files (.pb) by using tools/python/convert.py - The filter accepts the following options: @table @option @@ -11245,21 +11629,16 @@ Specify which DNN backend to use for model loading and execution. This option ac the following values: @table @samp -@item native -Native implementation of DNN loading and execution. - @item tensorflow TensorFlow backend. To enable this backend you need to install the TensorFlow for C library (see @url{https://www.tensorflow.org/install/lang_c}) and configure FFmpeg with @code{--enable-libtensorflow} @end table -Default value is @samp{native}. @item model Set path to model file specifying network architecture and its parameters. -Note that different backends use different file formats. TensorFlow and native -backend can load files for only its format. +Note that different backends use different file formats. TensorFlow can load files for only its format. @end table To get full functionality (such as async execution), please use the @ref{dnn_processing} filter. @@ -11583,9 +11962,6 @@ Specify which DNN backend to use for model loading and execution. This option ac the following values: @table @samp -@item native -Native implementation of DNN loading and execution. - @item tensorflow TensorFlow backend. To enable this backend you need to install the TensorFlow for C library (see @@ -11601,14 +11977,9 @@ be needed if the header files and libraries are not installed into system path) @end table -Default value is @samp{native}. - @item model Set path to model file specifying network architecture and its parameters. -Note that different backends use different file formats. TensorFlow, OpenVINO and native -backend can load files for only its format. - -Native model file (.model) can be generated from TensorFlow model file (.pb) by using tools/python/convert.py +Note that different backends use different file formats. TensorFlow, OpenVINO backend can load files for only its format. @item input Set the input name of the dnn network. @@ -11634,12 +12005,6 @@ Remove rain in rgb24 frame with can.pb (see @ref{derain} filter): ./ffmpeg -i rain.jpg -vf format=rgb24,dnn_processing=dnn_backend=tensorflow:model=can.pb:input=x:output=y derain.jpg @end example -@item -Halve the pixel value of the frame with format gray32f: -@example -ffmpeg -i input.jpg -vf format=grayf32,dnn_processing=model=halve_gray_float.model:input=dnn_in:output=dnn_out:dnn_backend=native -y out.native.png -@end example - @item Handle the Y channel with srcnn.pb (see @ref{sr} filter) for frame with yuv420p (planar YUV formats supported): @example @@ -11979,7 +12344,7 @@ Draw a text string or text from a specified file on top of a video, using the libfreetype library. To enable compilation of this filter, you need to configure FFmpeg with -@code{--enable-libfreetype}. +@code{--enable-libfreetype} and @code{--enable-libharfbuzz}. To enable default font fallback and the @var{font} option you need to configure FFmpeg with @code{--enable-libfontconfig}. To enable the @var{text_shaping} option, you need to configure FFmpeg with @@ -11998,7 +12363,17 @@ The default value of @var{box} is 0. @item boxborderw Set the width of the border to be drawn around the box using @var{boxcolor}. -The default value of @var{boxborderw} is 0. +The value must be specified using one of the following formats: +@itemize @bullet +@item @code{boxborderw=10} set the width of all the borders to 10 +@item @code{boxborderw=10|20} set the width of the top and bottom borders to 10 + and the width of the left and right borders to 20 +@item @code{boxborderw=10|20|30} set the width of the top border to 10, the width + of the bottom border to 30 and the width of the left and right borders to 20 +@item @code{boxborderw=10|20|30|40} set the borders width to 10 (top), 20 (right), + 30 (bottom), 40 (left) +@end itemize +The default value of @var{boxborderw} is "0". @item boxcolor The color to be used for drawing box around text. For the syntax of this @@ -12007,8 +12382,23 @@ option, check the @ref{color syntax,,"Color" section in the ffmpeg-utils manual, The default value of @var{boxcolor} is "white". @item line_spacing -Set the line spacing in pixels of the border to be drawn around the box using @var{box}. -The default value of @var{line_spacing} is 0. +Set the line spacing in pixels. The default value of @var{line_spacing} is 0. + +@item text_align +Set the vertical and horizontal alignment of the text with respect to the box boundaries. +The value is combination of flags, one for the vertical alignment (T=top, +M=middle, B=bottom) and one for the horizontal alignment (L=left, C=center, R=right). +Please note that tab characters are only supported with the left horizontal alignment. + +@item y_align +Specify what the @var{y} value is referred to. Possible values are: +@itemize @bullet +@item @code{text} the top of the highest glyph of the first text line is placed at @var{y} +@item @code{baseline} the baseline of the first text line is placed at @var{y} +@item @code{font} the baseline of the first text line is placed at @var{y} plus the + ascent (in pixels) defined in the font metrics +@end itemize +The default value of @var{y_align} is "text" for backward compatibility. @item borderw Set the width of the border to be drawn around the text using @var{bordercolor}. @@ -12022,13 +12412,12 @@ The default value of @var{bordercolor} is "black". @item expansion Select how the @var{text} is expanded. Can be either @code{none}, -@code{strftime} (deprecated) or -@code{normal} (default). See the @ref{drawtext_expansion, Text expansion} section -below for details. +@code{strftime} (deprecated) or @code{normal} (default). See the +@ref{drawtext_expansion, Text expansion} section below for details. @item basetime Set a start time for the count. Value is in microseconds. Only applied -in the deprecated strftime expansion mode. To emulate in normal expansion +in the deprecated @code{strftime} expansion mode. To emulate in normal expansion mode use the @code{pts} function, supplying the start time (in seconds) as the second argument. @@ -12105,6 +12494,14 @@ ffmpeg-utils manual,ffmpeg-utils}. The default value of @var{shadowcolor} is "black". +@item boxw +Set the width of the box to be drawn around text. +The default value of @var{boxw} is computed automatically to match the text width + +@item boxh +Set the height of the box to be drawn around text. +The default value of @var{boxh} is computed automatically to match the text height + @item shadowx @item shadowy The x and y offsets for the text shadow position with respect to the @@ -12216,6 +12613,18 @@ contained in the rendered text, it is equivalent to @var{ascent} - maximum glyph width, that is the maximum width for all the glyphs contained in the rendered text +@item font_a +the ascent size defined in the font metrics + +@item font_d +the descent size defined in the font metrics + +@item top_a +the maximum ascender of the glyphs of the first text line + +@item bottom_d +the maximum descender of the glyphs of the last text line + @item n the number of input frame, starting from 0 @@ -12259,10 +12668,12 @@ The current packet's size (in bytes). @anchor{drawtext_expansion} @subsection Text expansion -If @option{expansion} is set to @code{strftime}, -the filter recognizes strftime() sequences in the provided text and -expands them accordingly. Check the documentation of strftime(). This -feature is deprecated. +If @option{expansion} is set to @code{strftime}, the filter recognizes +sequences accepted by the @code{strftime} C function in the provided +text and expands them accordingly. Check the documentation of +@code{strftime}. This feature is deprecated in favor of @code{normal} +expansion with the @code{gmtime} or @code{localtime} expansion +functions. If @option{expansion} is set to @code{none}, the text is printed verbatim. @@ -12277,11 +12688,11 @@ braces is a function name, possibly followed by arguments separated by ':'. If the arguments contain special characters or delimiters (':' or '@}'), they should be escaped. -Note that they probably must also be escaped as the value for the -@option{text} option in the filter argument string and as the filter -argument in the filtergraph description, and possibly also for the shell, -that makes up to four levels of escaping; using a text file avoids these -problems. +Note that they probably must also be escaped as the value for the @option{text} +option in the filter argument string and as the filter argument in the +filtergraph description, and possibly also for the shell, that makes up to four +levels of escaping; using a text file with the @option{textfile} option avoids +these problems. The following functions are available: @@ -12309,13 +12720,13 @@ It can be used to add padding with zeros from the left. @item gmtime The time at which the filter is running, expressed in UTC. -It can accept an argument: a strftime() format string. +It can accept an argument: a @code{strftime} C function format string. The format string is extended to support the variable @var{%[1-6]N} which prints fractions of the second with optionally specified number of digits. @item localtime The time at which the filter is running, expressed in the local time zone. -It can accept an argument: a strftime() format string. +It can accept an argument: a @code{strftime} C function format string. The format string is extended to support the variable @var{%[1-6]N} which prints fractions of the second with optionally specified number of digits. @@ -12357,8 +12768,8 @@ If the format is set to @code{hms}, a third argument @code{24HH} may be supplied to present the hour part of the formatted timestamp in 24h format (00-23). -If the format is set to @code{localtime} or @code{gmtime}, -a third argument may be supplied: a strftime() format string. +If the format is set to @code{localtime} or @code{gmtime}, a third +argument may be supplied: a @code{strftime} C function format string. By default, @var{YYYY-MM-DD HH:MM:SS} format will be used. @end table @@ -12380,21 +12791,44 @@ Full filter invocation with sendcmd would look like this: @example sendcmd=c='56.0 drawtext reinit fontsize=56\:fontcolor=green\:text=Hello\\ World' @end example -@end table If the entire argument can't be parsed or applied as valid values then the filter will continue with its existing parameters. -@subsection Examples +@end table -@itemize -@item -Draw "Test Text" with font FreeSerif, using the default values for the -optional parameters. +The following options are also supported as @ref{commands}: -@example -drawtext="fontfile=/usr/share/fonts/truetype/freefont/FreeSerif.ttf: text='Test Text'" -@end example +@itemize @bullet +@item x +@item y +@item alpha +@item fontsize +@item fontcolor +@item boxcolor +@item bordercolor +@item shadowcolor +@item box +@item boxw +@item boxh +@item boxborderw +@item line_spacing +@item text_align +@item shadowx +@item shadowy +@item borderw +@end itemize + +@subsection Examples + +@itemize +@item +Draw "Test Text" with font FreeSerif, using the default values for the +optional parameters. + +@example +drawtext="fontfile=/usr/share/fonts/truetype/freefont/FreeSerif.ttf: text='Test Text'" +@end example @item Draw 'Test Text' with font FreeSerif of size 24 at position x=100 @@ -12462,7 +12896,8 @@ drawtext="text='Test Text': fontsize=h/30: x=(w-text_w)/2: y=(h-text_h*2)" @end example @item -Print the date of a real-time encoding (see strftime(3)): +Print the date of a real-time encoding (see documentation for the +@code{strftime} C function): @example drawtext='fontfile=FreeSans.ttf:text=%@{localtime\:%a %b %d %Y@}' @end example @@ -12506,6 +12941,9 @@ For more information about fontconfig, check: For more information about libfribidi, check: @url{http://fribidi.org/}. +For more information about libharfbuzz, check: +@url{https://github.com/harfbuzz/harfbuzz}. + @section edgedetect Detect and draw edges. The filter uses the Canny Edge Detection algorithm. @@ -12689,7 +13127,7 @@ frame count of the input frame starting from 0 @item pos byte position of the corresponding packet in the input file, NAN if -unspecified +unspecified; deprecated, do not use @item r frame rate of the input video, NAN if the input frame rate is unknown @@ -12826,16 +13264,16 @@ Specify the search radius for best edge matching. Default value is 2. Allowed range is from 0 to 15. @item ecost -Specify the edge cost for edge matching. Default value is 1.0. -Allowed range is from 0 to 9. +Specify the edge cost for edge matching. Default value is 2. +Allowed range is from 0 to 50. @item mcost -Specify the middle cost for edge matching. Default value is 0.5. -Allowed range is from 0 to 1. +Specify the middle cost for edge matching. Default value is 1. +Allowed range is from 0 to 50. @item dcost -Specify the distance cost for edge matching. Default value is 0.5. -Allowed range is from 0 to 1. +Specify the distance cost for edge matching. Default value is 1. +Allowed range is from 0 to 50. @item interp Specify the interpolation used. Default is 4-point interpolation. It accepts one @@ -13656,7 +14094,35 @@ value. @section find_rect -Find a rectangular object +Find a rectangular object in the input video. + +The object to search for must be specified as a gray8 image specified with the +@option{object} option. + +For each possible match, a score is computed. If the score reaches the specified +threshold, the object is considered found. + +If the input video contains multiple instances of the object, the filter will +find only one of them. + +When an object is found, the following metadata entries are set in the matching +frame: +@table @option +@item lavfi.rect.w +width of object + +@item lavfi.rect.h +height of object + +@item lavfi.rect.x +x position of object + +@item lavfi.rect.y +y position of object + +@item lavfi.rect.score +match score of the found object +@end table It accepts the following options: @@ -13665,7 +14131,12 @@ It accepts the following options: Filepath of the object image, needs to be in gray8. @item threshold -Detection threshold, default is 0.5. +Detection threshold, expressed as a decimal number in the range 0-1. + +A threshold value of 0.01 means only exact matches, a threshold of 0.99 means +almost everything matches. + +Default value is 0.5. @item mipmaps Number of mipmaps, default is 3. @@ -13685,6 +14156,16 @@ Cover a rectangular object by the supplied image of a given video using @command @example ffmpeg -i file.ts -vf find_rect=newref.pgm,cover_rect=cover.jpg:mode=cover new.mkv @end example + +@item +Find the position of an object in each frame using @command{ffprobe} and write +it to a log file: +@example +ffprobe -f lavfi movie=test.mp4,find_rect=object=object.pgm:threshold=0.3 \ + -show_entries frame=pkt_pts_time:frame_tags=lavfi.rect.x,lavfi.rect.y \ + -of csv -o find_rect.csv +@end example + @end itemize @section floodfill @@ -14132,7 +14613,7 @@ The filter accepts the following options: @table @option @item lum_expr, lum -Set the luminance expression. +Set the luma expression. @item cb_expr, cb Set the chrominance blue expression. @item cr_expr, cr @@ -14157,7 +14638,7 @@ colorspace. If one of the chrominance expression is not defined, it falls back on the other one. If no alpha expression is specified it will evaluate to opaque value. If none of chrominance expressions are specified, they will evaluate -to the luminance expression. +to the luma expression. The expressions can use the following variables and functions: @@ -14188,7 +14669,7 @@ Return the value of the pixel at location (@var{x},@var{y}) of the current plane. @item lum(x, y) -Return the value of the pixel at location (@var{x},@var{y}) of the luminance +Return the value of the pixel at location (@var{x},@var{y}) of the luma plane. @item cb(x, y) @@ -14339,14 +14820,33 @@ Set video output size. Default is @var{hd720}. Set video opacity. Default is @var{0.9}. Allowed range is from @var{0} to @var{1}. @item mode, m -Set output mode, can be @var{fulll} or @var{compact}. -In @var{compact} mode only filters with some queued frames have displayed stats. +Set output mode flags. + +Available values for flags are: +@table @samp +@item full +No any filtering. Default. +@item compact +Show only filters with queued frames. +@item nozero +Show only filters with non-zero stats. +@item noeof +Show only filters with non-eof stat. +@item nodisabled +Show only filters that are enabled in timeline. +@end table @item flags, f Set flags which enable which stats are shown in video. Available values for flags are: @table @samp +@item none +All flags turned off. + +@item all +All flags turned on. + @item queue Display number of queued frames in each link. @@ -14394,6 +14894,9 @@ Display number of samples given out from filter. @item sample_count_delta Display delta number of samples between above two values. + +@item disabled +Show the timeline filter status. @end table @item rate, r @@ -14888,6 +15391,7 @@ Set the scaling dimension: @code{2} for @code{hq2x}, @code{3} for Default is @code{3}. @end table +@anchor{hstack} @section hstack Stack input videos horizontally. @@ -15708,8 +16212,7 @@ ffmpeg -i input.mov -vf lensfun=make=Canon:model="Canon EOS 100D":lens_model="Ca @section libplacebo Flexible GPU-accelerated processing filter based on libplacebo -(@url{https://code.videolan.org/videolan/libplacebo}). Note that this filter -currently only accepts Vulkan input frames. +(@url{https://code.videolan.org/videolan/libplacebo}). @subsection Options @@ -15721,12 +16224,45 @@ to preserve the source colorimetry and size as best as it can, but it will apply any embedded film grain, dolby vision metadata or anamorphic SAR present in source frames. @table @option +@item inputs +Set the number of inputs. This can be used, alongside the @code{idx} variable, +to allow placing/blending multiple inputs inside the output frame. This +effectively enables functionality similar to @ref{hstack}, @ref{overlay}, etc. + @item w @item h -Set the output video dimension expression. Default value is the input dimension. +Set the output video dimension expression. Default values are @code{iw} and +@code{ih}. Allows for the same expressions as the @ref{scale} filter. +@item crop_x +@item crop_y +Set the input crop x/y expressions, default values are @code{(iw-cw)/2} and +@code{(ih-ch)/2}. + +@item crop_w +@item crop_h +Set the input crop width/height expressions, default values are @code{iw} and +@code{ih}. + +@item pos_x +@item pos_y +Set the output placement x/y expressions, default values are @code{(ow-pw)/2} +and @code{(oh-ph)/2}. + +@item pos_w +@item pos_h +Set the output placement width/height expressions, default values are @code{ow} +and @code{oh}. + +@item fps +Set the output frame rate. This can be rational, e.g. @code{60000/1001}. If +set to the special string @code{none} (the default), input timestamps will +instead be passed through to the output unmodified. Otherwise, the input video +frames will be interpolated as necessary to rescale the video to the specified +target framerate, in a manner as determined by the @option{frame_mixer} option. + @item format Set the output format override. If unset (the default), frames will be output in the same format as the respective input frames. Otherwise, format conversion @@ -15738,9 +16274,9 @@ Work the same as the identical @ref{scale} filter options. @item normalize_sar If enabled, output frames will always have a pixel aspect ratio of 1:1. This -will introduce padding/cropping as necessary. If disabled (the default), any -aspect ratio mismatches, including those from e.g. anamorphic video sources, -are forwarded to the output pixel aspect ratio. +will introduce additional padding/cropping as necessary. If disabled (the +default), any aspect ratio mismatches, including those from e.g. anamorphic +video sources, are forwarded to the output pixel aspect ratio. @item pad_crop_ratio Specifies a ratio (between @code{0.0} and @code{1.0}) between padding and @@ -15750,6 +16286,18 @@ content with black borders, while a value of @code{1.0} always crops off parts of the content. Intermediate values are possible, leading to a mix of the two approaches. +@item fillcolor +Set the color used to fill the output area not covered by the output image, for +example as a result of @option{normalize_sar}. For the general syntax of this +option, check the @ref{color syntax,,"Color" section in the ffmpeg-utils +manual,ffmpeg-utils}. Defaults to @code{black}. + +@item corner_rounding +Render frames with rounded corners. The value, given as a float ranging from +@code{0.0} to @code{1.0}, indicates the relative degree of rounding, from fully +square to fully circular. In other words, it gives the radius divided by half +the smaller side length. Defaults to @code{0.0}. + @item colorspace @item color_primaries @item color_trc @@ -15771,6 +16319,32 @@ BT.2020+PQ, overriding the usual input frame metadata. These will also be picked as the values of @code{auto} for the respective frame output options. @end table +In addition to the expression constants documented for the @ref{scale} filter, +the @option{crop_w}, @option{crop_h}, @option{crop_x}, @option{crop_y}, +@option{pos_w}, @option{pos_h}, @option{pos_x} and @option{pos_y} options can +also contain the following constants: + +@table @option +@item in_idx, idx +The (0-based) numeric index of the currently active input stream. +@item crop_w, cw +@item crop_h, ch +The computed values of @option{crop_w} and @option{crop_h}. + +@item pos_w, pw +@item pos_h, ph +The computed values of @option{pos_w} and @option{pos_h}. + +@item in_t, t +The input frame timestamp, in seconds. NAN if input timestamp is unknown. + +@item out_t, ot +The input frame timestamp, in seconds. NAN if input timestamp is unknown. + +@item n +The input frame number, starting with 0. +@end table + @subsubsection Scaling The options in this section control how libplacebo performs upscaling and (if necessary) downscaling. Note that libplacebo will always internally operate on @@ -15823,6 +16397,31 @@ Cubic BC spline with parameters recommended by Mitchell and Netravali. Very little ringing. @end table +@item frame_mixer +Controls the kernel used for mixing frames temporally. The default value is +@code{none}, which disables frame mixing. For a full list of possible values, +pass @code{help} to this option. The most important values are: +@table @samp +@item none +Disables frame mixing, giving a result equivalent to "nearest neighbour" +semantics. + +@item oversample +Oversamples the input video to create a "Smooth Motion"-type effect: if an +output frame would exactly fall on the transition between two video frames, it +is blended according to the relative overlap. This is the recommended option +whenever preserving the original subjective appearance is desired. + +@item mitchell_clamp +Larger filter kernel that smoothly interpolates multiple frames in a manner +designed to eliminate ringing and other artefacts as much as possible. This is +the recommended option wherever maximum visual smoothness is desired. + +@item linear +Linear blend/fade between frames. Especially useful for constructing e.g. +slideshows. +@end table + @item lut_entries Configures the size of scaler LUTs, ranging from @code{1} to @code{256}. The default of @code{0} will pick libplacebo's internal default, typically @@ -15936,10 +16535,11 @@ logarithmic scale between @code{0.0} and @code{100.0}. Default to @code{5.5} and @code{10.0}, respectively. Setting either to a negative value disables this functionality. -@item overshoot -Peak smoothing overshoot margin, between @code{0.0} and @code{1.0}. Provides a -safety margin to prevent clipping as a result of peak smoothing. Defaults to -@code{0.05}, corresponding to a margin of 5%. +@item percentile +Which percentile of the frame brightness histogram to use as the source peak +for tone-mapping. Defaults to @code{99.995}, a fairly conservative value. +Setting this to @code{100.0} disables frame histogram measurement and instead +uses the true peak brightness for tone-mapping. @end table @subsubsection Tone mapping @@ -15948,37 +16548,36 @@ gamut-mapping when dealing with mismatches between wide-gamut or HDR content. In general, libplacebo relies on accurate source tagging and mastering display gamut information to produce the best results. @table @option -@item intent -Rendering intent to use when adapting between different primary color gamuts -(after tone-mapping). -@table @samp -@item perceptual -Perceptual gamut mapping. Currently equivalent to relative colorimetric. -@item relative -Relative colorimetric. This is the default. -@item absolute -Absolute colorimetric. -@item saturation -Saturation mapping. Forcibly stretches the source gamut to the target gamut. -@end table - @item gamut_mode How to handle out-of-gamut colors that can occur as a result of colorimetric gamut mapping. @table @samp @item clip -Do nothing, simply clip out-of-range colors to the RGB volume. This is the -default. -@item warn -Highlight out-of-gamut pixels (by coloring them pink). -@item darken -Linearly reduces content brightness to preserves saturated details, followed by -clipping the remaining out-of-gamut colors. As the name implies, this makes -everything darker, but provides a good balance between preserving details and -colors. +Do nothing, simply clip out-of-range colors to the RGB volume. Low quality but +extremely fast. +@item perceptual +Perceptually soft-clip colors to the gamut volume. This is the default. +@item relative +Relative colorimetric hard-clip. Similar to @code{perceptual} but without +the soft knee. +@item saturation +Saturation mapping, maps primaries directly to primaries in RGB space. +Not recommended except for artificial computer graphics for which a bright, +saturated display is desired. +@item absolute +Absolute colorimetric hard-clip. Performs no adjustment of the white point. @item desaturate Hard-desaturates out-of-gamut colors towards white, while preserving the -luminance. Has a tendency to shift colors. +luminance. Has a tendency to distort the visual appearance of bright objects. +@item darken +Linearly reduces content brightness to preserves saturated details, followed by +clipping the remaining out-of-gamut colors. +@item warn +Highlight out-of-gamut pixels (by inverting/marking them). +@item linear +Linearly reduces chromaticity of the entire image to make it fit within the +target color volume. Be careful when using this on BT.2020 sources without +proper mastering metadata, as doing so will lead to excessive desaturation. @end table @item tonemapping @@ -15990,6 +16589,18 @@ Automatic selection based on internal heuristics. This is the default. Performs no tone-mapping, just clips out-of-range colors. Retains perfect color accuracy for in-range colors but completely destroys out-of-range information. Does not perform any black point adaptation. Not configurable. +@item st2094-40 +EETF from SMPTE ST 2094-40 Annex B, which applies the Bezier curves from HDR10+ +dynamic metadata based on Bezier curves to perform tone-mapping. The OOTF used +is adjusted based on the ratio between the targeted and actual display peak +luminances. +@item st2094-10 +EETF from SMPTE ST 2094-10 Annex B.2, which takes into account the input signal +average luminance in addition to the maximum/minimum. The configurable contrast +parameter influences the slope of the linear output segment, defaulting to +@code{1.0} for no increase/decrease in contrast. Note that this does not +currently include the subjective gain/offset/gamma controls defined in Annex +B.3. @item bt.2390 EETF from the ITU-R Report BT.2390, a hermite spline roll-off with linear segment. The knee point offset is configurable. Note that this parameter @@ -16045,42 +16656,27 @@ For tunable tone mapping functions, this parameter can be used to fine-tune the curve behavior. Refer to the documentation of @code{tonemapping}. The default value of @code{0.0} is replaced by the curve's preferred default setting. -@item tonemapping_mode -This option determines how the tone mapping function specified by -@code{tonemapping} is applied to the colors in a scene. Possible values are: -@table @samp -@item auto -Automatic selection based on internal heuristics. This is the default. -@item rgb -Apply the function per-channel in the RGB colorspace. -Per-channel tone-mapping in RGB. Guarantees no clipping and heavily desaturates -the output, but distorts the colors quite significantly. Very similar to the -"Hollywood" look and feel. -@item max -Tone-mapping is performed on the brightest component found in the signal. Good -at preserving details in highlights, but has a tendency to crush blacks. -@item hybrid -Tone-map per-channel for highlights and linearly (luma-based) for -midtones/shadows, based on a fixed gamma @code{2.4} coefficient curve. -@item luma -Tone-map linearly on the luma component (CIE Y), and adjust (desaturate) the -chromaticities to compensate using a simple constant factor. This is -essentially the mode used in ITU-R BT.2446 method A. -@end table - @item inverse_tonemapping If enabled, this filter will also attempt stretching SDR signals to fill HDR output color volumes. Disabled by default. -@item tonemapping_crosstalk -Extra tone-mapping crosstalk factor, between @code{0.0} and @code{0.3}. This -can help reduce issues tone-mapping certain bright spectral colors. Defaults to -@code{0.04}. - @item tonemapping_lut_size Size of the tone-mapping LUT, between @code{2} and @code{1024}. Defaults to @code{256}. Note that this figure is squared when combined with @code{peak_detect}. + +@item contrast_recovery +Contrast recovery strength. If set to a value above @code{0.0}, the source +image will be divided into high-frequency and low-frequency components, and a +portion of the high-frequency image is added back onto the tone-mapped output. +May cause excessive ringing artifacts for some HDR sources, but can improve the +subjective sharpness and detail left over in the image after tone-mapping. +Defaults to @code{0.30}. + +@item contrast_smoothness +Contrast recovery lowpass kernel size. Defaults to @code{3.5}. Increasing or +decreasing this will affect the visual appearance substantially. Has no effect +when @code{contrast_recovery} is disabled. @end table @subsubsection Dithering @@ -16146,9 +16742,6 @@ Disable linear light scaling. @item disable_builtin Disable built-in GPU sampling (forces LUT). -@item force_icc_lut -Force the use of a full ICC 3DLUT for gamut mapping. - @item disable_fbos Forcibly disable FBOs, resulting in loss of almost all functionality, but offering the maximum possible speed. @@ -16159,16 +16752,6 @@ This filter supports almost all of the above options as @ref{commands}. @subsection Examples @itemize -@item -Complete example for how to initialize the Vulkan device, upload frames to the -GPU, perform filter conversion to yuv420p, and download frames back to the CPU -for output. Note that in specific cases you can get around the need to perform -format conversion by specifying the correct @code{format} filter option -corresponding to the input frames. -@example -ffmpeg -i $INPUT -init_hw_device vulkan -vf hwupload,libplacebo=format=yuv420p,hwdownload,format=yuv420p $OUTPUT -@end example - @item Tone-map input to standard gamut BT.709 output: @example @@ -16181,6 +16764,12 @@ Rescale input to fit into standard 1080p, with high quality scaling: libplacebo=w=1920:h=1080:force_original_aspect_ratio=decrease:normalize_sar=true:upscaler=ewa_lanczos:downscaler=ewa_lanczos @end example +@item +Interpolate low FPS / VFR input to smoothed constant 60 fps output: +@example +libplacebo=fps=60:frame_mixer=mitchell_clamp +@end example + @item Convert input to standard sRGB JPEG: @example @@ -16208,6 +16797,12 @@ since otherwise the VRAM roundtrip will more than offset any expected speedup. @example ffmpeg -export_side_data +film_grain ... -vf libplacebo=apply_filmgrain=true @end example + +@item +Interop with VAAPI hwdec to avoid round-tripping through RAM: +@example +ffmpeg -init_hw_device vulkan -hwaccel vaapi -hwaccel_output_format vaapi ... -vf libplacebo +@end example @end itemize @section libvmaf @@ -16369,6 +16964,10 @@ Set maximal size in number of frames. Default is 0. @item start Set first frame of loop. Default is 0. + +@item time +Set the time of loop start in seconds. +Only used if option named @var{start} is set to @code{-1}. @end table @subsection Examples @@ -16537,7 +17136,7 @@ set blue component expression alpha component expression @item y -set Y/luminance component expression +set Y/luma component expression @item u set U/Cb component expression @item v @@ -16612,7 +17211,7 @@ lutyuv="y=negval:u=negval:v=negval" @end example @item -Negate luminance: +Negate luma: @example lutyuv=y=negval @end example @@ -16642,7 +17241,7 @@ format=rgba,lutrgb=a="maxval-minval/2" @end example @item -Correct luminance gamma by a factor of 0.5: +Correct luma gamma by a factor of 0.5: @example lutyuv=y=gammaval(0.5) @end example @@ -16895,8 +17494,6 @@ Apply motion-compensation deinterlacing. It needs one field per frame as input and must thus be used together with yadif=1/3 or equivalent. -This filter is only available in ffmpeg version 4.4 or earlier. - This filter accepts the following options: @table @option @item mode @@ -17319,6 +17916,13 @@ number of previous sequentially dropped frames. Default value is 0. +@item keep +Set the maximum number of consecutive similar frames to ignore before to start dropping them. +If the value is 0, the frame is dropped disregarding the +number of previous sequentially similar frames. + +Default value is 0. + @item hi @item lo @item frac @@ -17994,25 +18598,28 @@ Set the format for the output video. It accepts the following values: @table @samp @item yuv420 -force YUV420 output +force YUV 4:2:0 8-bit planar output @item yuv420p10 -force YUV420p10 output +force YUV 4:2:0 10-bit planar output @item yuv422 -force YUV422 output +force YUV 4:2:2 8-bit planar output @item yuv422p10 -force YUV422p10 output +force YUV 4:2:2 10-bit planar output @item yuv444 -force YUV444 output +force YUV 4:4:4 8-bit planar output + +@item yuv444p10 +force YUV 4:4:4 10-bit planar output @item rgb -force packed RGB output +force RGB 8-bit packed output @item gbrp -force planar RGB output +force RGB 8-bit planar output @item auto automatically pick format @@ -18055,7 +18662,8 @@ format. For example for the pixel format "yuv422p" @var{hsub} is 2 and the number of input frame, starting from 0 @item pos -the position in the file of the input frame, NAN if unknown +the position in the file of the input frame, NAN if unknown; deprecated, +do not use @item t The timestamp, expressed in seconds. It's NAN if the input timestamp is unknown. @@ -18064,7 +18672,7 @@ The timestamp, expressed in seconds. It's NAN if the input timestamp is unknown. This filter also supports the @ref{framesync} options. -Note that the @var{n}, @var{pos}, @var{t} variables are available only +Note that the @var{n}, @var{t} variables are available only when evaluation is done @emph{per frame}, and will evaluate to NAN when @option{eval} is set to @samp{init}. @@ -18219,6 +18827,7 @@ The ordinal index of the main input frame, starting from 0. @item pos The byte offset position in the file of the main input frame, NAN if unknown. +Deprecated, do not use. @item t The timestamp of the main input frame, expressed in seconds, NAN if unknown. @@ -18530,6 +19139,8 @@ Frankie Sierra dithering v3 (error diffusion) Burkes dithering (error diffusion) @item atkinson Atkinson dithering by Bill Atkinson at Apple Computer (error diffusion) +@item none +Disable dithering. @end table Default is @var{sierra2_4a}. @@ -18842,10 +19453,10 @@ Honor the quality commands for this subfilter. Do chrominance filtering, too (default). @item y/nochrom -Do luminance filtering only (no chrominance). +Do luma filtering only (no chrominance). @item n/noluma -Do chrominance filtering only (no luminance). +Do chrominance filtering only (no luma). @end table These options can be appended after the subfilter name, separated by a '|'. @@ -18917,7 +19528,7 @@ larger -> stronger filtering @item al/autolevels[:f/fullyrange], automatic brightness / contrast correction @table @option @item f/fullyrange -Stretch luminance to @code{0-255}. +Stretch luma to @code{0-255}. @end table @item lb/linblenddeint @@ -18985,7 +19596,7 @@ pp=default/tmpnoise|1|2|3 @end example @item -Apply deblocking on luminance only, and switch vertical deblocking on or off +Apply deblocking on luma only, and switch vertical deblocking on or off automatically depending on available CPU time: @example pp=hb|y/vb|a @@ -19098,6 +19709,12 @@ Available LUTs: @item preferred @item total @item spectral +@item cool +@item heat +@item fiery +@item blues +@item green +@item helix @end table @item opacity @@ -20103,6 +20720,7 @@ seconds. Only available with @code{eval=frame}. The position (byte offset) of the frame in the input stream, or NaN if this information is unavailable and/or meaningless (for example in case of synthetic video). Only available with @code{eval=frame}. +Deprecated, do not use. @end table @subsection Examples @@ -20435,6 +21053,7 @@ seconds. Only available with @code{eval=frame}. The position (byte offset) of the frame in the input stream, or NaN if this information is unavailable and/or meaningless (for example in case of synthetic video). Only available with @code{eval=frame}. +Deprecated, do not use. @end table @section scale2ref @@ -20565,6 +21184,27 @@ Scale a logo to 1/10th the height of a video, while preserving its display aspec @end example @end itemize +@section scale_vt + +Scale and convert the color parameters using VTPixelTransferSession. + +The filter accepts the following options: +@table @option +@item w +@item h +Set the output video dimension expression. Default value is the input dimension. + +@item color_matrix +Set the output colorspace matrix. + +@item color_primaries +Set the output color primaries. + +@item color_transfer +Set the output transfer characteristics. + +@end table + @section scharr Apply scharr operator to input video stream. @@ -20764,12 +21404,9 @@ It accepts the following parameters: @item r, ratio, dar (@code{setdar} only), sar (@code{setsar} only) Set the aspect ratio used by the filter. -The parameter can be a floating point number string, an expression, or -a string of the form @var{num}:@var{den}, where @var{num} and -@var{den} are the numerator and denominator of the aspect ratio. If -the parameter is not specified, it is assumed the value "0". -In case the form "@var{num}:@var{den}" is used, the @code{:} character -should be escaped. +The parameter can be a floating point number string, or an expression. If the +parameter is not specified, the value "0" is assumed, meaning that the same +input value is used. @item max Set the maximum integer value to use for expressing numerator and @@ -20778,19 +21415,14 @@ Default value is @code{100}. @end table -The parameter @var{sar} is an expression containing -the following constants: +The parameter @var{sar} is an expression containing the following constants: @table @option -@item E, PI, PHI -These are approximated values for the mathematical constants e -(Euler's number), pi (Greek pi), and phi (the golden ratio). - @item w, h The input width and height. @item a -These are the same as @var{w} / @var{h}. +Same as @var{w} / @var{h}. @item sar The input sample aspect ratio. @@ -21058,10 +21690,6 @@ time base units. The time base unit depends on the filter input pad. The Presentation TimeStamp of the input frame, expressed as a number of seconds. -@item pos -The position of the frame in the input stream, or -1 if this information is -unavailable and/or meaningless (for example in case of synthetic video). - @item fmt The pixel format name. @@ -21497,9 +22125,11 @@ ffmpeg -i input1.mkv -i input2.mkv -filter_complex "[0:v][1:v] signature=nb_inpu @anchor{siti} @section siti -Calculate Spatial Info (SI) and Temporal Info (TI) scores for a video, as defined -in ITU-T P.910: Subjective video quality assessment methods for multimedia -applications. Available PDF at @url{https://www.itu.int/rec/T-REC-P.910-199909-S/en }. +Calculate Spatial Information (SI) and Temporal Information (TI) scores for a video, +as defined in ITU-T Rec. P.910 (11/21): Subjective video quality assessment methods +for multimedia applications. Available PDF at @url{https://www.itu.int/rec/T-REC-P.910-202111-S/en}. +Note that this is a legacy implementation that corresponds to a superseded recommendation. +Refer to ITU-T Rec. P.910 (07/22) for the latest version: @url{https://www.itu.int/rec/T-REC-P.910-202207-I/en} It accepts the following option: @@ -21652,9 +22282,6 @@ Training scripts as well as scripts for model file (.pb) saving can be found at @url{https://github.com/XueweiMeng/sr/tree/sr_dnn_native}. Original repository is at @url{https://github.com/HighVoltageRocknRoll/sr.git}. -Native model files (.model) can be generated from TensorFlow model -files (.pb) by using tools/python/convert.py - The filter accepts the following options: @table @option @@ -21663,9 +22290,6 @@ Specify which DNN backend to use for model loading and execution. This option ac the following values: @table @samp -@item native -Native implementation of DNN loading and execution. - @item tensorflow TensorFlow backend. To enable this backend you need to install the TensorFlow for C library (see @@ -21673,13 +22297,10 @@ need to install the TensorFlow for C library (see @code{--enable-libtensorflow} @end table -Default value is @samp{native}. - @item model Set path to model file specifying network architecture and its parameters. -Note that different backends use different file formats. TensorFlow backend -can load files for both formats, while native backend can load files for only -its format. +Note that different backends use different file formats. TensorFlow, OpenVINO backend +can load files for only its format. @item scale_factor Set scale factor for SRCNN model. Allowed values are @code{2}, @code{3} and @code{4}. @@ -22050,6 +22671,13 @@ Set subtitles stream index. @code{subtitles} filter only. @item force_style Override default style or script info parameters of the subtitles. It accepts a string containing ASS style format @code{KEY=VALUE} couples separated by ",". + +@item wrap_unicode +Break lines according to the Unicode Line Breaking Algorithm. Availability requires +at least libass release 0.17.0 (or LIBASS_VERSION 0x01600010), @emph{and} libass must +have been built with libunibreak. + +The option is enabled by default except for native ASS. @end table If the first key is not specified, it is assumed that the first value @@ -22140,7 +22768,8 @@ The number of the input frame, starting from 0. The timestamp expressed in seconds. It's NAN if the input timestamp is unknown. @item pos -the position in the file of the input frame, NAN if unknown +the position in the file of the input frame, NAN if unknown; deprecated, +do not use @end table @subsection Commands @@ -23269,6 +23898,9 @@ that value the speed drops by a factor of approximately 2. Default value is @item qp Force a constant quantization parameter. If not set, the filter will use the QP from the video stream (if available). + +@item codec +Use specified codec instead of snow. @end table @section v360 @@ -24339,6 +24971,7 @@ Example: ffmpeg -i ref.mpg -vf vmafmotion -f null - @end example +@anchor{vstack} @section vstack Stack input videos vertically. @@ -24436,7 +25069,7 @@ This filter supports same @ref{commands} as options. @section waveform Video waveform monitor. -The waveform monitor plots color component intensity. By default luminance +The waveform monitor plots color component intensity. By default luma only. Each column of the waveform corresponds to a column of pixels in the source video. @@ -24491,7 +25124,7 @@ correction is easy to perform by making level adjustments the three waveforms. Default is @code{stack}. @item components, c -Set which color components to display. Default is 1, which means only luminance +Set which color components to display. Default is 1, which means only luma or red color component if input is in RGB colorspace. If is set for example to 7 it will display all 3 (if) available color components. @@ -24601,6 +25234,12 @@ Set sample aspect ration to 1/1. Set sample aspect ratio to match input size of video @end table Default is @samp{none}. + +@item input +Set input formats for filter to pick from. +Can be @samp{all}, for selecting from all available formats, +or @samp{first}, for selecting first available format. +Default is @samp{first}. @end table @section weave, doubleweave @@ -24731,6 +25370,18 @@ Set one of available transition effects: @item zoomin @item fadefast @item fadeslow +@item hlwind +@item hrwind +@item vuwind +@item vdwind +@item coverleft +@item coverright +@item coverup +@item coverdown +@item revealleft +@item revealright +@item revealup +@item revealdown @end table Default transition effect is fade. @@ -24820,6 +25471,7 @@ minimum values, and @code{1} maximum values. This filter supports all above options as @ref{commands}, excluding option @code{inputs}. +@anchor{xstack} @section xstack Stack video inputs into custom layout. @@ -25888,6 +26540,7 @@ Apply dilation filter with threshold0 set to 30, threshold1 set 40, threshold2 s @end example @end itemize +@anchor{nlmeans_opencl} @section nlmeans_opencl Non-local Means denoise filter through OpenCL, this filter accepts same options as @ref{nlmeans}. @@ -26636,92 +27289,645 @@ tonemap_vaapi=format=p010:t=bt2020-10 @end example @end itemize -@c man end VAAPI VIDEO FILTERS - -@chapter Video Sources -@c man begin VIDEO SOURCES +@section hstack_vaapi +Stack input videos horizontally. -Below is a description of the currently available video sources. +This is the VA-API variant of the @ref{hstack} filter, each input stream may +have different height, this filter will scale down/up each input stream while +keeping the orignal aspect. -@section buffer +It accepts the following options: -Buffer video frames, and make them available to the filter chain. +@table @option +@item inputs +See @ref{hstack}. -This source is mainly intended for a programmatic use, in particular -through the interface defined in @file{libavfilter/buffersrc.h}. +@item shortest +See @ref{hstack}. -It accepts the following parameters: +@item height +Set height of output. If set to 0, this filter will set height of output to +height of the first input stream. Default value is 0. +@end table -@table @option +@section vstack_vaapi +Stack input videos vertically. -@item video_size -Specify the size (width and height) of the buffered video frames. For the -syntax of this option, check the -@ref{video size syntax,,"Video size" section in the ffmpeg-utils manual,ffmpeg-utils}. +This is the VA-API variant of the @ref{vstack} filter, each input stream may +have different width, this filter will scale down/up each input stream while +keeping the orignal aspect. -@item width -The input video width. +It accepts the following options: -@item height -The input video height. +@table @option +@item inputs +See @ref{vstack}. -@item pix_fmt -A string representing the pixel format of the buffered video frames. -It may be a number corresponding to a pixel format, or a pixel format -name. +@item shortest +See @ref{vstack}. -@item time_base -Specify the timebase assumed by the timestamps of the buffered frames. +@item width +Set width of output. If set to 0, this filter will set width of output to +width of the first input stream. Default value is 0. +@end table -@item frame_rate -Specify the frame rate expected for the video stream. +@section xstack_vaapi +Stack video inputs into custom layout. -@item pixel_aspect, sar -The sample (pixel) aspect ratio of the input video. +This is the VA-API variant of the @ref{xstack} filter, each input stream may +have different size, this filter will scale down/up each input stream to the +given output size, or the size of the first input stream. -@item sws_param -This option is deprecated and ignored. Prepend @code{sws_flags=@var{flags};} -to the filtergraph description to specify swscale flags for automatically -inserted scalers. See @ref{Filtergraph syntax}. +It accepts the following options: -@item hw_frames_ctx -When using a hardware pixel format, this should be a reference to an -AVHWFramesContext describing input frames. -@end table +@table @option +@item inputs +See @ref{xstack}. -For example: -@example -buffer=width=320:height=240:pix_fmt=yuv410p:time_base=1/24:sar=1 -@end example +@item shortest +See @ref{xstack}. -will instruct the source to accept video frames with size 320x240 and -with format "yuv410p", assuming 1/24 as the timestamps timebase and -square pixels (1:1 sample aspect ratio). -Since the pixel format with name "yuv410p" corresponds to the number 6 -(check the enum AVPixelFormat definition in @file{libavutil/pixfmt.h}), -this example corresponds to: +@item layout +See @ref{xstack}. +Moreover, this permits the user to supply output size for each input stream. @example -buffer=size=320x240:pixfmt=6:time_base=1/24:pixel_aspect=1/1 +xstack_vaapi=inputs=4:layout=0_0_1920x1080|0_h0_1920x1080|w0_0_1920x1080|w0_h0_1920x1080 @end example -Alternatively, the options can be specified as a flat string, but this -syntax is deprecated: +@item grid +See @ref{xstack}. -@var{width}:@var{height}:@var{pix_fmt}:@var{time_base.num}:@var{time_base.den}:@var{pixel_aspect.num}:@var{pixel_aspect.den} +@item grid_tile_size +Set output size for each input stream when @option{grid} is set. If this option +is not set, this filter will set output size by default to the size of the +first input stream. For the syntax of this option, check the +@ref{video size syntax,,"Video size" section in the ffmpeg-utils manual,ffmpeg-utils}. -@section cellauto +@item fill +See @ref{xstack}. +@end table -Create a pattern generated by an elementary cellular automaton. +@c man end VAAPI VIDEO FILTERS -The initial state of the cellular automaton can be defined through the -@option{filename} and @option{pattern} options. If such options are -not specified an initial state is created randomly. +@chapter Vulkan Video Filters +@c man begin VULKAN VIDEO FILTERS -At each new frame a new row in the video is filled with the result of -the cellular automaton next generation. The behavior when the whole -frame is filled is defined by the @option{scroll} option. +Below is a description of the currently available Vulkan video filters. -This source accepts the following options: +To enable compilation of these filters you need to configure FFmpeg with +@code{--enable-vulkan} and either @code{--enable-libglslang} or @code{--enable-libshaderc}. + +Running Vulkan filters requires you to initialize a hardware device and to pass that device to all filters in any filter graph. +@table @option + +@item -init_hw_device vulkan[=@var{name}][:@var{device}[,@var{key=value}...]] +Initialise a new hardware device of type @var{vulkan} called @var{name}, using the +given device parameters and options in @var{key=value}. The following options +are supported: + +@table @option +@item debug +Switches validation layers on if set to 1. + +@item linear_images +Allocates linear images. Does not apply to decoding. + +@item disable_multiplane +Disables multiplane images. Does not apply to decoding. +@end table + +@item -filter_hw_device @var{name} +Pass the hardware device called @var{name} to all filters in any filter graph. + +@end table + +For more detailed information see @url{https://www.ffmpeg.org/ffmpeg.html#Advanced-Video-options} + +@itemize +@item +Example of choosing the first device and running nlmeans_vulkan filter with default parameters on it. +@example +-init_hw_device vulkan=vk:0 -filter_hw_device vk -i INPUT -vf "hwupload,nlmeans_vulkan,hwdownload" OUTPUT +@end example +@end itemize + +As Vulkan filters are not able to access frame data in normal memory, all frame data needs to be uploaded (@ref{hwupload}) to hardware surfaces connected to the appropriate device before being used and then downloaded (@ref{hwdownload}) back to normal memory. Note that @ref{hwupload} will upload to a frame with the same layout as the software frame, so it may be necessary to add a @ref{format} filter immediately before to get the input into the right format and @ref{hwdownload} does not support all formats on the output - it is usually necessary to insert an additional @ref{format} filter immediately following in the graph to get the output in a supported format. + +@section avgblur_vulkan + +Apply an average blur filter, implemented on the GPU using Vulkan. + +The filter accepts the following options: + +@table @option +@item sizeX +Set horizontal radius size. +Range is @code{[1, 32]} and default value is @code{3}. + +@item sizeY +Set vertical radius size. Range is @code{[1, 32]} and default value is @code{3}. + +@item planes +Set which planes to filter. Default value is @code{0xf}, by which all planes are processed. +@end table + +@section blend_vulkan + +Blend two Vulkan frames into each other. + +The @code{blend} filter takes two input streams and outputs one +stream, the first input is the "top" layer and second input is +"bottom" layer. By default, the output terminates when the longest input terminates. + +A description of the accepted options follows. + +@table @option +@item c0_mode +@item c1_mode +@item c2_mode +@item c3_mode +@item all_mode +Set blend mode for specific pixel component or all pixel components in case +of @var{all_mode}. Default value is @code{normal}. + +Available values for component modes are: +@table @samp +@item normal +@item multiply +@end table + +@end table + +@section bwdif_vulkan + +Deinterlacer using @ref{bwdif}, the "Bob Weaver Deinterlacing Filter" algorithm, implemented +on the GPU using Vulkan. + +It accepts the following parameters: + +@table @option +@item mode +The interlacing mode to adopt. It accepts one of the following values: + +@table @option +@item 0, send_frame +Output one frame for each frame. +@item 1, send_field +Output one frame for each field. +@end table + +The default value is @code{send_field}. + +@item parity +The picture field parity assumed for the input interlaced video. It accepts one +of the following values: + +@table @option +@item 0, tff +Assume the top field is first. +@item 1, bff +Assume the bottom field is first. +@item -1, auto +Enable automatic detection of field parity. +@end table + +The default value is @code{auto}. +If the interlacing is unknown or the decoder does not export this information, +top field first will be assumed. + +@item deint +Specify which frames to deinterlace. Accepts one of the following +values: + +@table @option +@item 0, all +Deinterlace all frames. +@item 1, interlaced +Only deinterlace frames marked as interlaced. +@end table + +The default value is @code{all}. +@end table + +@section chromaber_vulkan + +Apply an effect that emulates chromatic aberration. Works best with RGB inputs, +but provides a similar effect with YCbCr inputs too. + +@table @option +@item dist_x +Horizontal displacement multiplier. Each chroma pixel's position will be multiplied +by this amount, starting from the center of the image. Default is @code{0}. + +@item dist_y +Similarly, this sets the vertical displacement multiplier. Default is @code{0}. + +@end table + +@section color_vulkan + +Video source that creates a Vulkan frame of a solid color. +Useful for benchmarking, or overlaying. + +It accepts the following parameters: + +@table @option +@item color +The color to use. Either a name, or a hexadecimal value. +The default value is @code{black}. + +@item size +The size of the output frame. Default value is @code{1920x1080}. + +@item rate +The framerate to output at. Default value is @code{60} frames per second. + +@item duration +The video duration. Default value is @code{-0.000001}. + +@item sar +The video signal aspect ratio. Default value is @code{1/1}. + +@item format +The pixel format of the output Vulkan frames. Default value is @code{yuv444p}. + +@item out_range +Set the output YCbCr sample range. + +This allows the autodetected value to be overridden as well as allows forcing +a specific value used for the output and encoder. If not specified, the +range depends on the pixel format. Possible values: + +@table @samp +@item auto/unknown +Choose automatically. + +@item jpeg/full/pc +Set full range (0-255 in case of 8-bit luma). + +@item mpeg/limited/tv +Set "MPEG" range (16-235 in case of 8-bit luma). +@end table + +@end table + +@section vflip_vulkan + +Flips an image vertically. + +@section hflip_vulkan + +Flips an image horizontally. + +@section flip_vulkan + +Flips an image along both the vertical and horizontal axis. + +@section gblur_vulkan + +Apply Gaussian blur filter on Vulkan frames. + +The filter accepts the following options: + +@table @option +@item sigma +Set horizontal sigma, standard deviation of Gaussian blur. Default is @code{0.5}. + +@item sigmaV +Set vertical sigma, if negative it will be same as @code{sigma}. +Default is @code{-1}. + +@item planes +Set which planes to filter. By default all planes are filtered. + +@item size +Set the kernel size along the horizontal axis. Default is @code{19}. + +@item sizeV +Set the kernel size along the vertical axis. Default is @code{0}, +which sets to use the same value as @var{size}. + +@end table + +@section nlmeans_vulkan + +Denoise frames using Non-Local Means algorithm, implemented on the GPU using +Vulkan. +Supports more pixel formats than @ref{nlmeans} or @ref{nlmeans_opencl}, including +alpha channel support. + +The filter accepts the following options. + +@table @option +@item s +Set denoising strength for all components. Default is 1.0. Must be in range [1.0, 100.0]. + +@item p +Set patch size for all planes. Default is 7. Must be odd number in range [0, 99]. + +@item r +Set research size. Default is 15. Must be odd number in range [0, 99]. + +@item t +Set parallelism. Default is 36. Must be a number in the range [1, 168]. +Larger values may speed up processing, at the cost of more VRAM. +Lower values will slow it down, reducing VRAM usage. +Only supported on GPUs with atomic float operations (RDNA3+, Ampere+). + +@item s0 +@item s1 +@item s2 +@item s3 +Set denoising strength for a specific component. Default is @var{1}, equal to @option{s}. +Must be odd number in range [1, 100]. + +@item p0 +@item p1 +@item p2 +@item p3 +Set patch size for a specific component. Default is @var{7}, equal to @option{p}. +Must be odd number in range [0, 99]. + +@end table + +@section overlay_vulkan + +Overlay one video on top of another. + +It takes two inputs and has one output. The first input is the "main" video on which the second input is overlaid. +This filter requires all inputs to use the same pixel format. So, format conversion may be needed. + +The filter accepts the following options: + +@table @option +@item x +Set the x coordinate of the overlaid video on the main video. +Default value is @code{0}. + +@item y +Set the y coordinate of the overlaid video on the main video. +Default value is @code{0}. + +@end table + +@section transpose_vt + +Transpose rows with columns in the input video and optionally flip it. +For more in depth examples see the @ref{transpose} video filter, which shares mostly the same options. + +It accepts the following parameters: + +@table @option + +@item dir +Specify the transposition direction. + +Can assume the following values: +@table @samp +@item cclock_flip +Rotate by 90 degrees counterclockwise and vertically flip. (default) + +@item clock +Rotate by 90 degrees clockwise. + +@item cclock +Rotate by 90 degrees counterclockwise. + +@item clock_flip +Rotate by 90 degrees clockwise and vertically flip. + +@item hflip +Flip the input video horizontally. + +@item vflip +Flip the input video vertically. + +@end table + +@item passthrough +Do not apply the transposition if the input geometry matches the one +specified by the specified value. It accepts the following values: +@table @samp +@item none +Always apply transposition. (default) +@item portrait +Preserve portrait geometry (when @var{height} >= @var{width}). +@item landscape +Preserve landscape geometry (when @var{width} >= @var{height}). +@end table + +@end table + +@section transpose_vulkan + +Transpose rows with columns in the input video and optionally flip it. +For more in depth examples see the @ref{transpose} video filter, which shares mostly the same options. + +It accepts the following parameters: + +@table @option + +@item dir +Specify the transposition direction. + +Can assume the following values: +@table @samp +@item cclock_flip +Rotate by 90 degrees counterclockwise and vertically flip. (default) + +@item clock +Rotate by 90 degrees clockwise. + +@item cclock +Rotate by 90 degrees counterclockwise. + +@item clock_flip +Rotate by 90 degrees clockwise and vertically flip. +@end table + +@item passthrough +Do not apply the transposition if the input geometry matches the one +specified by the specified value. It accepts the following values: +@table @samp +@item none +Always apply transposition. (default) +@item portrait +Preserve portrait geometry (when @var{height} >= @var{width}). +@item landscape +Preserve landscape geometry (when @var{width} >= @var{height}). +@end table + +@end table + +@c man end VULKAN VIDEO FILTERS + +@chapter QSV Video Filters +@c man begin QSV VIDEO FILTERS + +Below is a description of the currently available QSV video filters. + +To enable compilation of these filters you need to configure FFmpeg with +@code{--enable-libmfx} or @code{--enable-libvpl}. + +To use QSV filters, you need to setup the QSV device correctly. For more information, please read @url{https://trac.ffmpeg.org/wiki/Hardware/QuickSync} + +@section hstack_qsv +Stack input videos horizontally. + +This is the QSV variant of the @ref{hstack} filter, each input stream may +have different height, this filter will scale down/up each input stream while +keeping the orignal aspect. + +It accepts the following options: + +@table @option +@item inputs +See @ref{hstack}. + +@item shortest +See @ref{hstack}. + +@item height +Set height of output. If set to 0, this filter will set height of output to +height of the first input stream. Default value is 0. +@end table + +@section vstack_qsv +Stack input videos vertically. + +This is the QSV variant of the @ref{vstack} filter, each input stream may +have different width, this filter will scale down/up each input stream while +keeping the orignal aspect. + +It accepts the following options: + +@table @option +@item inputs +See @ref{vstack}. + +@item shortest +See @ref{vstack}. + +@item width +Set width of output. If set to 0, this filter will set width of output to +width of the first input stream. Default value is 0. +@end table + +@section xstack_qsv +Stack video inputs into custom layout. + +This is the QSV variant of the @ref{xstack} filter. + +It accepts the following options: + +@table @option +@item inputs +See @ref{xstack}. + +@item shortest +See @ref{xstack}. + +@item layout +See @ref{xstack}. +Moreover, this permits the user to supply output size for each input stream. +@example +xstack_qsv=inputs=4:layout=0_0_1920x1080|0_h0_1920x1080|w0_0_1920x1080|w0_h0_1920x1080 +@end example + +@item grid +See @ref{xstack}. + +@item grid_tile_size +Set output size for each input stream when @option{grid} is set. If this option +is not set, this filter will set output size by default to the size of the +first input stream. For the syntax of this option, check the +@ref{video size syntax,,"Video size" section in the ffmpeg-utils manual,ffmpeg-utils}. + +@item fill +See @ref{xstack}. +@end table + +@c man end QSV VIDEO FILTERS + +@chapter Video Sources +@c man begin VIDEO SOURCES + +Below is a description of the currently available video sources. + +@section buffer + +Buffer video frames, and make them available to the filter chain. + +This source is mainly intended for a programmatic use, in particular +through the interface defined in @file{libavfilter/buffersrc.h}. + +It accepts the following parameters: + +@table @option + +@item video_size +Specify the size (width and height) of the buffered video frames. For the +syntax of this option, check the +@ref{video size syntax,,"Video size" section in the ffmpeg-utils manual,ffmpeg-utils}. + +@item width +The input video width. + +@item height +The input video height. + +@item pix_fmt +A string representing the pixel format of the buffered video frames. +It may be a number corresponding to a pixel format, or a pixel format +name. + +@item time_base +Specify the timebase assumed by the timestamps of the buffered frames. + +@item frame_rate +Specify the frame rate expected for the video stream. + +@item pixel_aspect, sar +The sample (pixel) aspect ratio of the input video. + +@item hw_frames_ctx +When using a hardware pixel format, this should be a reference to an +AVHWFramesContext describing input frames. +@end table + +For example: +@example +buffer=width=320:height=240:pix_fmt=yuv410p:time_base=1/24:sar=1 +@end example + +will instruct the source to accept video frames with size 320x240 and +with format "yuv410p", assuming 1/24 as the timestamps timebase and +square pixels (1:1 sample aspect ratio). +Since the pixel format with name "yuv410p" corresponds to the number 6 +(check the enum AVPixelFormat definition in @file{libavutil/pixfmt.h}), +this example corresponds to: +@example +buffer=size=320x240:pixfmt=6:time_base=1/24:pixel_aspect=1/1 +@end example + +Alternatively, the options can be specified as a flat string, but this +syntax is deprecated: + +@var{width}:@var{height}:@var{pix_fmt}:@var{time_base.num}:@var{time_base.den}:@var{pixel_aspect.num}:@var{pixel_aspect.den} + +@section cellauto + +Create a pattern generated by an elementary cellular automaton. + +The initial state of the cellular automaton can be defined through the +@option{filename} and @option{pattern} options. If such options are +not specified an initial state is created randomly. + +At each new frame a new row in the video is filled with the result of +the cellular automaton next generation. The behavior when the whole +frame is filled is defined by the @option{scroll} option. + +This source accepts the following options: @table @option @item filename, f @@ -27468,7 +28674,7 @@ color=c=red@@0.2:s=qcif:r=10 @item If the input content is to be ignored, @code{nullsrc} can be used. The -following command generates noise in the luminance plane by employing +following command generates noise in the luma plane by employing the @code{geq} filter: @example nullsrc=s=256x256, geq=random(1)*255:128:128 @@ -27582,6 +28788,104 @@ Set max jump for single pan destination. Allowed range is from 1 to 10000. Set fractal type, can be default @code{carpet} or @code{triangle}. @end table +@section zoneplate +Generate a zoneplate test video pattern. + +This source accepts the following options: + +@table @option +@item size, s +Set frame size. For the syntax of this option, check the @ref{video size syntax,,"Video +size" section in the ffmpeg-utils manual,ffmpeg-utils}. Default value is "320x240". + +@item rate, r +Set frame rate, expressed as number of frames per second. Default +value is "25". + +@item duration, d +Set the duration of the sourced video. See +@ref{time duration syntax,,the Time duration section in the ffmpeg-utils(1) manual,ffmpeg-utils} +for the accepted syntax. + +If not specified, or the expressed duration is negative, the video is +supposed to be generated forever. + +@item sar +Set the sample aspect ratio of the sourced video. + +@item precision +Set precision in bits for look-up table for sine calculations. Default value is 10. +Allowed range is from 4 to 16. + +@item xo +Set horizontal axis offset for output signal. Default value is 0. + +@item yo +Set vertical axis offset for output signal. Default value is 0. + +@item to +Set time axis offset for output signal. Default value is 0. + +@item k0 +Set 0-order, constant added to signal phase. Default value is 0. + +@item kx +Set 1-order, phase factor multiplier for horizontal axis. Default value is 0. + +@item ky +Set 1-order, phase factor multiplier for vertical axis. Default value is 0. + +@item kt +Set 1-order, phase factor multiplier for time axis. Default value is 0. + +@item kxt, kyt, kxy +Set phase factor multipliers for combination of spatial and temporal axis. +Default value is 0. + +@item kx2 +Set 2-order, phase factor multiplier for horizontal axis. Default value is 0. + +@item ky2 +Set 2-order, phase factor multiplier for vertical axis. Default value is 0. + +@item kt2 +Set 2-order, phase factor multiplier for time axis. Default value is 0. + +@item ku +Set the constant added to final phase to produce chroma-blue component of signal. +Default value is 0. + +@item kv +Set the constant added to final phase to produce chroma-red component of signal. +Default value is 0. +@end table + +@subsection Commands + +This source supports the some above options as @ref{commands}. + +@subsection Examples + +@itemize +@item +Generate horizontal color sine sweep: +@example +zoneplate=ku=512:kv=0:kt2=0:kx2=256:s=wvga:xo=-426:kt=11 +@end example + +@item +Generate vertical color sine sweep: +@example +zoneplate=ku=512:kv=0:kt2=0:ky2=156:s=wvga:yo=-240:kt=11 +@end example + +@item +Generate circular zone-plate: +@example +zoneplate=ku=512:kv=100:kt2=0:ky2=256:kx2=556:s=wvga:yo=0:kt=11 +@end example +@end itemize + @c man end VIDEO SOURCES @chapter Video Sinks @@ -28235,6 +29539,24 @@ live mixing). Sets the display scale for the loudness. Valid parameters are @code{absolute} (in LUFS) or @code{relative} (LU) relative to the target. This only affects the video output, not the summary or continuous log output. + +@item integrated +Read-only exported value for measured integrated loudness, in LUFS. + +@item range +Read-only exported value for measured loudness range, in LU. + +@item lra_low +Read-only exported value for measured LRA low, in LUFS. + +@item lra_high +Read-only exported value for measured LRA high, in LUFS. + +@item sample_peak +Read-only exported value for measured sample peak, in dBFS. + +@item true_peak +Read-only exported value for measured true peak, in dBFS. @end table @subsection Examples @@ -28649,7 +29971,7 @@ This is 1 if the filtered frame is a key-frame, 0 otherwise. @item pos the position in the file of the filtered frame, -1 if the information -is not available (e.g. for synthetic video) +is not available (e.g. for synthetic video); deprecated, do not use @item scene @emph{(video only)} value between 0 and 1 to indicate a new scene; a low value reflects a low @@ -28839,7 +30161,7 @@ constants: @table @option @item POS Original position in the file of the frame, or undefined if undefined -for the current frame. +for the current frame. Deprecated, do not use. @item PTS The presentation timestamp in input. @@ -28987,7 +30309,7 @@ the time in seconds of the current frame @item POS original position in the file of the frame, or undefined if undefined -for the current frame +for the current frame; deprecated, do not use @item PREV_INPTS The previous input PTS. @@ -29011,6 +30333,9 @@ The wallclock (RTC) time at the start of the movie in microseconds. @item TB The timebase of the input timestamps. +@item T_CHANGE +Time of the first frame after command was applied or time of the first frame if no commands. + @end table @subsection Examples @@ -29066,6 +30391,10 @@ asetpts=N/SR/TB @end itemize +@subsection Commands + +Both filters support all above options as @ref{commands}. + @section setrange Force color range for the output video frame. @@ -29454,13 +30783,28 @@ Set the frequency scale used. Allowed values are: @table @option @item linear -@item log2 +@item log @item bark @item mel @item erbs +@item sqrt +@item cbrt +@item qdrt @end table Default value is @code{linear}. +@item iscale +Set the intensity scale used. Allowed values are: + +@table @option +@item linear +@item log +@item sqrt +@item cbrt +@item qdrt +@end table +Default value is @code{log}. + @item min Set the minimum frequency that will be used in output. Default is @code{20} Hz. @@ -29471,6 +30815,12 @@ Default is @code{20000} Hz. The real frequency upper limit depends on input audio's sample rate and such will be enforced on this value when it is set to value greater than Nyquist frequency. +@item imin +Set the minimum intensity that will be used in output. + +@item imax +Set the maximum intensity that will be used in output. + @item logb Set the logarithmic basis for brightness strength when mapping calculated magnitude values to pixel values. @@ -29530,6 +30880,13 @@ Direction from up to down. @item du Direction from down to up. @end table + +@item bar +Set the ratio of bargraph display to display size. Default is 0. + +@item rotation +Set color rotation, must be in [-1.0, 1.0] range. +Default value is @code{0}. @end table @section showfreqs @@ -30636,6 +31993,10 @@ Set stream output duration. By default duration is unlimited. Set foreground/background/additional color. @end table +@subsection Commands + +This source supports the some above options as @ref{commands}. + @anchor{movie} @section movie diff --git a/doc/general_contents.texi b/doc/general_contents.texi index 8399fcb6b7b..8ac121dee17 100644 --- a/doc/general_contents.texi +++ b/doc/general_contents.texi @@ -534,6 +534,8 @@ library: @item Metal Gear Solid: The Twin Snakes @tab @tab X @item Megalux Frame @tab @tab X @tab Used by Megalux Ultimate Paint +@item MobiClip MODS @tab @tab X +@item MobiClip MOFLEX @tab @tab X @item Mobotix .mxg @tab @tab X @item Monkey's Audio @tab @tab X @item Motion Pixels MVI @tab @tab X @@ -577,6 +579,7 @@ library: @item Ogg @tab X @tab X @item Playstation Portable PMP @tab @tab X @item Portable Voice Format @tab @tab X +@item RK Audio (RKA) @tab @tab X @item TechnoTrend PVA @tab @tab X @tab Used by TechnoTrend DVB PCI boards. @item QCP @tab @tab X @@ -663,8 +666,10 @@ library: @item Sample Dump eXchange @tab @tab X @item SAP @tab X @tab X @item SBG @tab @tab X +@item SDNS @tab @tab X @item SDP @tab @tab X @item SER @tab @tab X +@item Digital Pictures SGA @tab @tab X @item Sega FILM/CPK @tab X @tab X @tab Used in many Sega Saturn console games. @item Silicon Graphics Movie @tab @tab X @@ -702,7 +707,9 @@ library: @item Vivo @tab @tab X @item VPK @tab @tab X @tab Audio format used in Sony PS games. +@item Marble WADY @tab @tab X @item WAV @tab X @tab X +@item Waveform Archiver @tab @tab X @item WavPack @tab X @tab X @item WebM @tab X @tab X @item Windows Televison (WTV) @tab X @tab X @@ -714,6 +721,7 @@ library: @tab Multimedia format used in Westwood Studios games. @item Wideband Single-bit Data (WSD) @tab @tab X @item WVE @tab @tab X +@item Konami XMD @tab @tab X @item XMV @tab @tab X @tab Microsoft video container used in Xbox games. @item XVAG @tab @tab X @@ -763,6 +771,7 @@ following image formats are supported: @item JPEG-LS @tab X @tab X @item LJPEG @tab X @tab @tab Lossless JPEG +@item Media 100 @tab @tab X @item MSP @tab @tab X @tab Microsoft Paint image @item PAM @tab X @tab X @@ -997,7 +1006,7 @@ following image formats are supported: @tab Also known as Microsoft Screen 3. @item Microsoft Expression Encoder Screen @tab @tab X @tab Also known as Microsoft Titanium Screen 2. -@item Microsoft RLE @tab @tab X +@item Microsoft RLE @tab X @tab X @item Microsoft Screen 1 @tab @tab X @tab Also known as Windows Media Video V7 Screen. @item Microsoft Screen 2 @tab @tab X @@ -1057,6 +1066,8 @@ following image formats are supported: @item RealVideo 4.0 @tab @tab X @item Renderware TXD (TeXture Dictionary) @tab @tab X @tab Texture dictionaries used by the Renderware Engine. +@item RivaTuner Video @tab @tab X + @tab fourcc: 'RTV1' @item RL2 video @tab @tab X @tab used in some games by Entertainment Software Partners @item ScreenPressor @tab @tab X @@ -1093,6 +1104,8 @@ following image formats are supported: @item v408 QuickTime uncompressed 4:4:4:4 @tab X @tab X @item v410 QuickTime uncompressed 4:4:4 10-bit @tab X @tab X @item VBLE Lossless Codec @tab @tab X +@item vMix Video @tab @tab X + @tab fourcc: 'VMX1' @item VMware Screen Codec / VMware Video @tab @tab X @tab Codec used in videos captured by VMware. @item Westwood Studios VQA (Vector Quantized Animation) video @tab @tab X @@ -1159,6 +1172,7 @@ following image formats are supported: @item ADPCM IMA Electronic Arts SEAD @tab @tab X @item ADPCM IMA Funcom @tab @tab X @item ADPCM IMA High Voltage Software ALP @tab X @tab X +@item ADPCM IMA Mobiclip MOFLEX @tab @tab X @item ADPCM IMA QuickTime @tab X @tab X @item ADPCM IMA Simon & Schuster Interactive @tab X @tab X @item ADPCM IMA Ubisoft APM @tab X @tab X @@ -1188,6 +1202,7 @@ following image formats are supported: @item ADPCM Sound Blaster Pro 4-bit @tab @tab X @item ADPCM VIMA @tab @tab X @tab Used in LucasArts SMUSH animations. +@item ADPCM Konami XMD @tab @tab X @item ADPCM Westwood Studios IMA @tab X @tab X @tab Used in Westwood Studios games like Command and Conquer. @item ADPCM Yamaha @tab X @tab X @@ -1225,9 +1240,12 @@ following image formats are supported: @item DCA (DTS Coherent Acoustics) @tab X @tab X @tab supported extensions: XCh, XXCH, X96, XBR, XLL, LBR (partially) @item Dolby E @tab @tab X +@item DPCM Cuberoot-Delta-Exact @tab @tab X + @tab Used in few games. @item DPCM Gremlin @tab @tab X @item DPCM id RoQ @tab X @tab X @tab Used in Quake III, Jedi Knight 2 and other computer games. +@item DPCM Marble WADY @tab @tab X @item DPCM Interplay @tab @tab X @tab Used in various Interplay computer games. @item DPCM Squareroot-Delta-Exact @tab @tab X @@ -1273,6 +1291,7 @@ following image formats are supported: @item MP3 (MPEG audio layer 3) @tab E @tab IX @tab encoding supported through external library LAME, ADU MP3 and MP3onMP4 also supported @item MPEG-4 Audio Lossless Coding (ALS) @tab @tab X +@item MobiClip FastAudio @tab @tab X @item Musepack SV7 @tab @tab X @item Musepack SV8 @tab @tab X @item Nellymoser Asao @tab X @tab X @@ -1307,6 +1326,7 @@ following image formats are supported: @item PCM unsigned 24-bit little-endian @tab X @tab X @item PCM unsigned 32-bit big-endian @tab X @tab X @item PCM unsigned 32-bit little-endian @tab X @tab X +@item PCM SGA @tab @tab X @item QCELP / PureVoice @tab @tab X @item QDesign Music Codec 1 @tab @tab X @item QDesign Music Codec 2 @tab @tab X @@ -1319,6 +1339,7 @@ following image formats are supported: @tab Real low bitrate AC-3 codec @item RealAudio Lossless @tab @tab X @item RealAudio SIPR / ACELP.NET @tab @tab X +@item RK Audio (RKA) @tab @tab X @item SBC (low-complexity subband codec) @tab X @tab X @tab Used in Bluetooth A2DP @item Shorten @tab @tab X @@ -1343,6 +1364,7 @@ following image formats are supported: @item Vorbis @tab E @tab X @tab A native but very primitive encoder exists. @item Voxware MetaSound @tab @tab X +@item Waveform Archiver @tab @tab X @item WavPack @tab X @tab X @item Westwood Audio (SND1) @tab @tab X @item Windows Media Audio 1 @tab X @tab X diff --git a/doc/git-howto.texi b/doc/git-howto.texi index 5bb39bb9866..f4e2f2ec232 100644 --- a/doc/git-howto.texi +++ b/doc/git-howto.texi @@ -53,7 +53,7 @@ Most distribution and operating system provide a package for it. @section Cloning the source tree @example -git clone git://source.ffmpeg.org/ffmpeg +git clone https://git.ffmpeg.org/ffmpeg.git @end example This will put the FFmpeg sources into the directory @var{}. diff --git a/doc/indevs.texi b/doc/indevs.texi index 8a198c4b441..863536a34d5 100644 --- a/doc/indevs.texi +++ b/doc/indevs.texi @@ -991,9 +991,8 @@ This input device reads data from the open output pads of a libavfilter filtergraph. For each filtergraph open output, the input device will create a -corresponding stream which is mapped to the generated output. Currently -only video data is supported. The filtergraph is specified through the -option @option{graph}. +corresponding stream which is mapped to the generated output. +The filtergraph is specified through the option @option{graph}. @subsection Options diff --git a/doc/mailing-list-faq.texi b/doc/mailing-list-faq.texi index 534ef3f8020..b2028eeee13 100644 --- a/doc/mailing-list-faq.texi +++ b/doc/mailing-list-faq.texi @@ -344,7 +344,7 @@ recommended. Avoid sending the same message to multiple mailing lists. @item -Please follow our @url{https://ffmpeg.org/developer.html#Code-of-conduct, Code of Conduct}. +Please follow our @url{https://ffmpeg.org/community.html#Code-of-conduct, Code of Conduct}. @end itemize @chapter Help diff --git a/doc/muxers.texi b/doc/muxers.texi index ed5341be393..f6071484ff6 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -795,12 +795,6 @@ deletes them. Increase this to allow continue clients to download segments which were recently referenced in the playlist. Default value is 1, meaning segments older than @code{hls_list_size+1} will be deleted. -@item hls_ts_options @var{options_list} -Set output format options using a :-separated list of key=value -parameters. Values containing @code{:} special characters must be -escaped. -@code{hls_ts_options} is deprecated, use hls_segment_options instead of it.. - @item hls_start_number_source Start the playlist sequence number (@code{#EXT-X-MEDIA-SEQUENCE}) according to the specified source. Unless @code{hls_flags single_file} is set, it also specifies source of starting sequence numbers of @@ -1331,7 +1325,7 @@ Use persistent HTTP connections. Applicable only for HTTP output. @item timeout Set timeout for socket I/O operations. Applicable only for HTTP output. -@item -ignore_io_errors +@item ignore_io_errors Ignore IO errors during open, write and delete. Useful for long-duration runs with network output. @item headers @@ -1474,7 +1468,7 @@ ffmpeg -f v4l2 -r 1 -i /dev/video0 -f image2 -strftime 1 "%Y-%m-%d_%H-%M-%S.jpg" You can set the file name with current frame's PTS: @example -ffmpeg -f v4l2 -r 1 -i /dev/video0 -copyts -f image2 -frame_pts true %d.jpg" +ffmpeg -f v4l2 -r 1 -i /dev/video0 -copyts -f image2 -frame_pts true %d.jpg @end example A more complex example is to publish contents of your desktop directly to a @@ -1636,8 +1630,10 @@ MOV/MP4/ISMV (Smooth Streaming) muxer. The mov/mp4/ismv muxer supports fragmentation. Normally, a MOV/MP4 file has all the metadata about all packets stored in one location (written at the end of the file, it can be moved to the start for -better playback by adding @var{faststart} to the @var{movflags}, or -using the @command{qt-faststart} tool). A fragmented +better playback by adding @code{+faststart} to the @code{-movflags}, or +using the @command{qt-faststart} tool). + +A fragmented file consists of a number of fragments, where packets and metadata about these packets are stored together. Writing a fragmented file has the advantage that the file is decodable even if the @@ -1647,40 +1643,34 @@ very long files (since writing normal MOV/MP4 files stores info about every single packet in memory until the file is closed). The downside is that it is less compatible with other applications. -@subsection Options +Fragmentation is enabled by setting one of the options that define +how to cut the file into fragments: @code{-frag_duration}, @code{-frag_size}, +@code{-min_frag_duration}, @code{-movflags +frag_keyframe} and +@code{-movflags +frag_custom}. If more than one condition is specified, +fragments are cut when one of the specified conditions is fulfilled. The +exception to this is @code{-min_frag_duration}, which has to be fulfilled for +any of the other conditions to apply. -Fragmentation is enabled by setting one of the AVOptions that define -how to cut the file into fragments: +@subsection Options @table @option -@item -moov_size @var{bytes} -Reserves space for the moov atom at the beginning of the file instead of placing the -moov atom at the end. If the space reserved is insufficient, muxing will fail. -@item -movflags frag_keyframe -Start a new fragment at each video keyframe. -@item -frag_duration @var{duration} +@item frag_duration @var{duration} Create fragments that are @var{duration} microseconds long. -@item -frag_size @var{size} +@item frag_size @var{size} Create fragments that contain up to @var{size} bytes of payload data. -@item -movflags frag_custom +@item min_frag_duration @var{duration} +Don't create fragments that are shorter than @var{duration} microseconds long. +@item movflags @var{flags} +Set various muxing switches. The following flags can be used: +@table @samp +@item frag_keyframe +Start a new fragment at each video keyframe. +@item frag_custom Allow the caller to manually choose when to cut fragments, by calling @code{av_write_frame(ctx, NULL)} to write a fragment with the packets written so far. (This is only useful with other applications integrating libavformat, not from @command{ffmpeg}.) -@item -min_frag_duration @var{duration} -Don't create fragments that are shorter than @var{duration} microseconds long. -@end table - -If more than one condition is specified, fragments are cut when -one of the specified conditions is fulfilled. The exception to this is -@code{-min_frag_duration}, which has to be fulfilled for any of the other -conditions to apply. - -Additionally, the way the output file is written can be adjusted -through a few other options: - -@table @option -@item -movflags empty_moov +@item empty_moov Write an initial moov atom directly at the start of the file, without describing any samples in it. Generally, an mdat/moov pair is written at the start of the file, as a normal MOV/MP4 file, containing only @@ -1689,43 +1679,40 @@ mdat atom, and the moov atom only describes the tracks but has a zero duration. This option is implicitly set when writing ismv (Smooth Streaming) files. -@item -movflags separate_moof +@item separate_moof Write a separate moof (movie fragment) atom for each track. Normally, packets for all tracks are written in a moof atom (which is slightly more efficient), but with this option set, the muxer writes one moof/mdat pair for each track, making it easier to separate tracks. This option is implicitly set when writing ismv (Smooth Streaming) files. -@item -movflags skip_sidx +@item skip_sidx Skip writing of sidx atom. When bitrate overhead due to sidx atom is high, this option could be used for cases where sidx atom is not mandatory. When global_sidx flag is enabled, this option will be ignored. -@item -movflags faststart +@item faststart Run a second pass moving the index (moov atom) to the beginning of the file. This operation can take a while, and will not work in various situations such as fragmented output, thus it is not enabled by default. -@item -movflags rtphint +@item rtphint Add RTP hinting tracks to the output file. -@item -movflags disable_chpl +@item disable_chpl Disable Nero chapter markers (chpl atom). Normally, both Nero chapters and a QuickTime chapter track are written to the file. With this option set, only the QuickTime chapter track will be written. Nero chapters can cause failures when the file is reprocessed with certain tagging programs, like mp3Tag 2.61a and iTunes 11.3, most likely other versions are affected as well. -@item -movflags omit_tfhd_offset +@item omit_tfhd_offset Do not write any absolute base_data_offset in tfhd atoms. This avoids tying fragments to absolute byte positions in the file/streams. -@item -movflags default_base_moof +@item default_base_moof Similarly to the omit_tfhd_offset, this flag avoids writing the absolute base_data_offset field in tfhd atoms, but does so by using the new default-base-is-moof flag instead. This flag is new from 14496-12:2012. This may make the fragments easier to parse in certain circumstances (avoiding basing track fragment location calculations on the implicit end of the previous track fragment). -@item -write_tmcd -Specify @code{on} to force writing a timecode track, @code{off} to disable it -and @code{auto} to write a timecode track only for mov and mp4 output (default). -@item -movflags negative_cts_offsets +@item negative_cts_offsets Enables utilization of version 1 of the CTTS box, in which the CTS offsets can be negative. This enables the initial sample to have DTS/CTS of zero, and reduces the need for edit lists for some cases such as video tracks with @@ -1733,15 +1720,24 @@ B-frames. Additionally, eases conformance with the DASH-IF interoperability guidelines. This option is implicitly set when writing ismv (Smooth Streaming) files. +@end table + +@item moov_size @var{bytes} +Reserves space for the moov atom at the beginning of the file instead of placing the +moov atom at the end. If the space reserved is insufficient, muxing will fail. + +@item write_tmcd +Specify @code{on} to force writing a timecode track, @code{off} to disable it +and @code{auto} to write a timecode track only for mov and mp4 output (default). -@item -write_btrt @var{bool} +@item write_btrt @var{bool} Force or disable writing bitrate box inside stsd box of a track. The box contains decoding buffer size (in bytes), maximum bitrate and average bitrate for the track. The box will be skipped if none of these values can be computed. Default is @code{-1} or @code{auto}, which will write the box only in MP4 mode. -@item -write_prft +@item write_prft Write producer time reference box (PRFT) with a specified time source for the NTP field in the PRFT box. Set value as @samp{wallclock} to specify timesource as wallclock time and @samp{pts} to specify timesource as input packets' PTS @@ -1752,15 +1748,15 @@ where PTS values are set as as wallclock time at the source. For example, an encoding use case with decklink capture source where @option{video_pts} and @option{audio_pts} are set to @samp{abs_wallclock}. -@item -empty_hdlr_name @var{bool} +@item empty_hdlr_name @var{bool} Enable to skip writing the name inside a @code{hdlr} box. Default is @code{false}. -@item -movie_timescale @var{scale} +@item movie_timescale @var{scale} Set the timescale written in the movie header box (@code{mvhd}). Range is 1 to INT_MAX. Default is 1000. -@item -video_track_timescale @var{scale} +@item video_track_timescale @var{scale} Set the timescale used for video tracks. Range is 0 to INT_MAX. If set to @code{0}, the timescale is automatically set based on the native stream time base. Default is 0. @@ -2126,6 +2122,12 @@ DTS Coherent Acoustics (DCA) audio. Dolby Digital Plus, also known as Enhanced AC-3, audio. +@subsection evc + +MPEG-5 Essential Video Coding (EVC) / EVC / MPEG-5 Part 1 EVC video. + +Extensions: evc + @subsection g722 ITU-T G.722 audio. diff --git a/doc/outdevs.texi b/doc/outdevs.texi index aa41e295230..f0484bbf8fb 100644 --- a/doc/outdevs.texi +++ b/doc/outdevs.texi @@ -235,6 +235,11 @@ Enable SMPTE Level A mode on the used output. Must be @samp{unset}, @samp{true} or @samp{false}. Defaults to @option{unset}. +@item vanc_queue_size +Sets maximum output buffer size in bytes for VANC data. If the buffering reaches this value, +outgoing VANC data will be dropped. +Defaults to @samp{1048576}. + @end table @subsection Examples @@ -421,13 +426,18 @@ For more information about SDL, check: @table @option -@item window_title -Set the SDL window title, if not specified default to the filename -specified for the output device. +@item window_borderless +Set SDL window border off. +Default value is 0 (enable window border). + +@item window_enable_quit +Enable quit action (using window button or keyboard key) +when non-zero value is provided. +Default value is 1 (enable quit action). -@item icon_title -Set the name of the iconified SDL window, if not specified it is set -to the same value of @var{window_title}. +@item window_fullscreen +Set fullscreen mode when non-zero value is provided. +Default value is zero. @item window_size Set the SDL window size, can be a string of the form @@ -435,18 +445,13 @@ Set the SDL window size, can be a string of the form If not specified it defaults to the size of the input video, downscaled according to the aspect ratio. +@item window_title +Set the SDL window title, if not specified default to the filename +specified for the output device. + @item window_x @item window_y Set the position of the window on the screen. - -@item window_fullscreen -Set fullscreen mode when non-zero value is provided. -Default value is zero. - -@item window_enable_quit -Enable quit action (using window button or keyboard key) -when non-zero value is provided. -Default value is 1 (enable quit action) @end table @subsection Interactive commands diff --git a/doc/platform.texi b/doc/platform.texi index 4090b856700..764911d2302 100644 --- a/doc/platform.texi +++ b/doc/platform.texi @@ -92,9 +92,6 @@ For information about compiling FFmpeg on OS/2 see @chapter Windows -To get help and instructions for building FFmpeg under Windows, check out -the FFmpeg Windows Help Forum at @url{http://ffmpeg.zeranoe.com/forum/}. - @section Native Windows compilation using MinGW or MinGW-w64 FFmpeg can be built to run natively on Windows using the MinGW-w64 diff --git a/doc/protocols.texi b/doc/protocols.texi index 21ae6181a05..b3fad55591d 100644 --- a/doc/protocols.texi +++ b/doc/protocols.texi @@ -1882,6 +1882,12 @@ The list of supported options follows. Listen for an incoming connection. 0 disables listen, 1 enables listen in single client mode, 2 enables listen in multi-client mode. Default value is 0. +@item local_addr=@var{addr} +Local IP address of a network interface used for tcp socket connect. + +@item local_port=@var{port} +Local port used for tcp socket connect. + @item timeout=@var{microseconds} Set raise error timeout, expressed in microseconds. diff --git a/doc/resampler.texi b/doc/resampler.texi index 5ed3f4377a2..a6224eefb30 100644 --- a/doc/resampler.texi +++ b/doc/resampler.texi @@ -11,18 +11,8 @@ programmatic use. @table @option -@item ich, in_channel_count -Set the number of input channels. Default value is 0. Setting this -value is not mandatory if the corresponding channel layout -@option{in_channel_layout} is set. - -@item och, out_channel_count -Set the number of output channels. Default value is 0. Setting this -value is not mandatory if the corresponding channel layout -@option{out_channel_layout} is set. - -@item uch, used_channel_count -Set the number of used input channels. Default value is 0. This option is +@item uchl, used_chlayout +Set used input channel layout. Default is unset. This option is only used for special remapping. @item isr, in_sample_rate @@ -41,8 +31,8 @@ Specify the output sample format. It is set by default to @code{none}. Set the internal sample format. Default value is @code{none}. This will automatically be chosen when it is not explicitly set. -@item icl, in_channel_layout -@item ocl, out_channel_layout +@item ichl, in_chlayout +@item ochl, out_chlayout Set the input/output channel layout. See @ref{channel layout syntax,,the Channel Layout section in the ffmpeg-utils(1) manual,ffmpeg-utils} diff --git a/fftools/Makefile b/fftools/Makefile index 8ac38e75d2f..56820e6bc83 100644 --- a/fftools/Makefile +++ b/fftools/Makefile @@ -10,7 +10,9 @@ ALLAVPROGS = $(AVBASENAMES:%=%$(PROGSSUF)$(EXESUF)) ALLAVPROGS_G = $(AVBASENAMES:%=%$(PROGSSUF)_g$(EXESUF)) OBJS-ffmpeg += \ + fftools/ffmpeg_dec.o \ fftools/ffmpeg_demux.o \ + fftools/ffmpeg_enc.o \ fftools/ffmpeg_filter.o \ fftools/ffmpeg_hw.o \ fftools/ffmpeg_mux.o \ diff --git a/fftools/cmdutils.c b/fftools/cmdutils.c index a1de621d1c2..156c13801a0 100644 --- a/fftools/cmdutils.c +++ b/fftools/cmdutils.c @@ -83,29 +83,8 @@ void init_dynload(void) #endif } -static void (*program_exit)(int ret); - -void register_exit(void (*cb)(int ret)) -{ - program_exit = cb; -} - -void report_and_exit(int ret) -{ - av_log(NULL, AV_LOG_FATAL, "%s\n", av_err2str(ret)); - exit_program(AVUNERROR(ret)); -} - -void exit_program(int ret) -{ - if (program_exit) - program_exit(ret); - - exit(ret); -} - -double parse_number_or_die(const char *context, const char *numstr, int type, - double min, double max) +int parse_number(const char *context, const char *numstr, int type, + double min, double max, double *dst) { char *tail; const char *error; @@ -118,23 +97,13 @@ double parse_number_or_die(const char *context, const char *numstr, int type, error = "Expected int64 for %s but found %s\n"; else if (type == OPT_INT && (int)d != d) error = "Expected int for %s but found %s\n"; - else - return d; - av_log(NULL, AV_LOG_FATAL, error, context, numstr, min, max); - exit_program(1); - return 0; -} - -int64_t parse_time_or_die(const char *context, const char *timestr, - int is_duration) -{ - int64_t us; - if (av_parse_time(&us, timestr, is_duration) < 0) { - av_log(NULL, AV_LOG_FATAL, "Invalid %s specification for %s: %s\n", - is_duration ? "duration" : "date", context, timestr); - exit_program(1); + else { + *dst = d; + return 0; } - return us; + + av_log(NULL, AV_LOG_FATAL, error, context, numstr, min, max); + return AVERROR(EINVAL); } void show_help_options(const OptionDef *options, const char *msg, int req_flags, @@ -262,6 +231,8 @@ static int write_option(void *optctx, const OptionDef *po, const char *opt, void *dst = po->flags & (OPT_OFFSET | OPT_SPEC) ? (uint8_t *)optctx + po->u.off : po->u.dst_ptr; int *dstcount; + double num; + int ret; if (po->flags & OPT_SPEC) { SpecifierOpt **so = dst; @@ -269,7 +240,10 @@ static int write_option(void *optctx, const OptionDef *po, const char *opt, char *str; dstcount = (int *)(so + 1); - *so = grow_array(*so, sizeof(**so), dstcount, *dstcount + 1); + ret = grow_array((void**)so, sizeof(**so), dstcount, *dstcount + 1); + if (ret < 0) + return ret; + str = av_strdup(p ? p + 1 : ""); if (!str) return AVERROR(ENOMEM); @@ -285,15 +259,36 @@ static int write_option(void *optctx, const OptionDef *po, const char *opt, return AVERROR(ENOMEM); *(char **)dst = str; } else if (po->flags & OPT_BOOL || po->flags & OPT_INT) { - *(int *)dst = parse_number_or_die(opt, arg, OPT_INT64, INT_MIN, INT_MAX); + ret = parse_number(opt, arg, OPT_INT64, INT_MIN, INT_MAX, &num); + if (ret < 0) + return ret; + + *(int *)dst = num; } else if (po->flags & OPT_INT64) { - *(int64_t *)dst = parse_number_or_die(opt, arg, OPT_INT64, INT64_MIN, INT64_MAX); + ret = parse_number(opt, arg, OPT_INT64, INT64_MIN, INT64_MAX, &num); + if (ret < 0) + return ret; + + *(int64_t *)dst = num; } else if (po->flags & OPT_TIME) { - *(int64_t *)dst = parse_time_or_die(opt, arg, 1); + ret = av_parse_time(dst, arg, 1); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "Invalid duration for option %s: %s\n", + opt, arg); + return ret; + } } else if (po->flags & OPT_FLOAT) { - *(float *)dst = parse_number_or_die(opt, arg, OPT_FLOAT, -INFINITY, INFINITY); + ret = parse_number(opt, arg, OPT_FLOAT, -INFINITY, INFINITY, &num); + if (ret < 0) + return ret; + + *(float *)dst = num; } else if (po->flags & OPT_DOUBLE) { - *(double *)dst = parse_number_or_die(opt, arg, OPT_DOUBLE, -INFINITY, INFINITY); + ret = parse_number(opt, arg, OPT_DOUBLE, -INFINITY, INFINITY, &num); + if (ret < 0) + return ret; + + *(double *)dst = num; } else if (po->u.func_arg) { int ret = po->u.func_arg(optctx, opt, arg); if (ret < 0) { @@ -304,7 +299,7 @@ static int write_option(void *optctx, const OptionDef *po, const char *opt, } } if (po->flags & OPT_EXIT) - exit_program(0); + return AVERROR_EXIT; return 0; } @@ -348,8 +343,8 @@ int parse_option(void *optctx, const char *opt, const char *arg, return !!(po->flags & HAS_ARG); } -void parse_options(void *optctx, int argc, char **argv, const OptionDef *options, - void (*parse_arg_function)(void *, const char*)) +int parse_options(void *optctx, int argc, char **argv, const OptionDef *options, + int (*parse_arg_function)(void *, const char*)) { const char *opt; int optindex, handleoptions = 1, ret; @@ -370,13 +365,18 @@ void parse_options(void *optctx, int argc, char **argv, const OptionDef *options opt++; if ((ret = parse_option(optctx, opt, argv[optindex], options)) < 0) - exit_program(1); + return ret; optindex += ret; } else { - if (parse_arg_function) - parse_arg_function(optctx, opt); + if (parse_arg_function) { + ret = parse_arg_function(optctx, opt); + if (ret < 0) + return ret; + } } } + + return 0; } int parse_optgroup(void *optctx, OptionGroup *g) @@ -605,13 +605,17 @@ static int match_group_separator(const OptionGroupDef *groups, int nb_groups, * @param group_idx which group definition should this group belong to * @param arg argument of the group delimiting option */ -static void finish_group(OptionParseContext *octx, int group_idx, - const char *arg) +static int finish_group(OptionParseContext *octx, int group_idx, + const char *arg) { OptionGroupList *l = &octx->groups[group_idx]; OptionGroup *g; + int ret; + + ret = GROW_ARRAY(l->groups, l->nb_groups); + if (ret < 0) + return ret; - GROW_ARRAY(l->groups, l->nb_groups); g = &l->groups[l->nb_groups - 1]; *g = octx->cur_group; @@ -628,25 +632,33 @@ static void finish_group(OptionParseContext *octx, int group_idx, swr_opts = NULL; memset(&octx->cur_group, 0, sizeof(octx->cur_group)); + + return ret; } /* * Add an option instance to currently parsed group. */ -static void add_opt(OptionParseContext *octx, const OptionDef *opt, - const char *key, const char *val) +static int add_opt(OptionParseContext *octx, const OptionDef *opt, + const char *key, const char *val) { int global = !(opt->flags & (OPT_PERFILE | OPT_SPEC | OPT_OFFSET)); OptionGroup *g = global ? &octx->global_opts : &octx->cur_group; + int ret; + + ret = GROW_ARRAY(g->opts, g->nb_opts); + if (ret < 0) + return ret; - GROW_ARRAY(g->opts, g->nb_opts); g->opts[g->nb_opts - 1].opt = opt; g->opts[g->nb_opts - 1].key = key; g->opts[g->nb_opts - 1].val = val; + + return 0; } -static void init_parse_context(OptionParseContext *octx, - const OptionGroupDef *groups, int nb_groups) +static int init_parse_context(OptionParseContext *octx, + const OptionGroupDef *groups, int nb_groups) { static const OptionGroupDef global_group = { "global" }; int i; @@ -656,13 +668,15 @@ static void init_parse_context(OptionParseContext *octx, octx->nb_groups = nb_groups; octx->groups = av_calloc(octx->nb_groups, sizeof(*octx->groups)); if (!octx->groups) - report_and_exit(AVERROR(ENOMEM)); + return AVERROR(ENOMEM); for (i = 0; i < octx->nb_groups; i++) octx->groups[i].group_def = &groups[i]; octx->global_opts.group_def = &global_group; octx->global_opts.arg = ""; + + return 0; } void uninit_parse_context(OptionParseContext *octx) @@ -694,13 +708,17 @@ int split_commandline(OptionParseContext *octx, int argc, char *argv[], const OptionDef *options, const OptionGroupDef *groups, int nb_groups) { + int ret; int optindex = 1; int dashdash = -2; /* perform system-dependent conversions for arguments list */ prepare_app_arguments(&argc, &argv); - init_parse_context(octx, groups, nb_groups); + ret = init_parse_context(octx, groups, nb_groups); + if (ret < 0) + return ret; + av_log(NULL, AV_LOG_DEBUG, "Splitting the commandline.\n"); while (optindex < argc) { @@ -716,7 +734,10 @@ int split_commandline(OptionParseContext *octx, int argc, char *argv[], } /* unnamed group separators, e.g. output filename */ if (opt[0] != '-' || !opt[1] || dashdash+1 == optindex) { - finish_group(octx, 0, opt); + ret = finish_group(octx, 0, opt); + if (ret < 0) + return ret; + av_log(NULL, AV_LOG_DEBUG, " matched as %s.\n", groups[0].name); continue; } @@ -734,7 +755,10 @@ do { \ /* named group separators, e.g. -i */ if ((ret = match_group_separator(groups, nb_groups, opt)) >= 0) { GET_ARG(arg); - finish_group(octx, ret, arg); + ret = finish_group(octx, ret, arg); + if (ret < 0) + return ret; + av_log(NULL, AV_LOG_DEBUG, " matched as %s with argument '%s'.\n", groups[ret].name, arg); continue; @@ -752,7 +776,10 @@ do { \ arg = "1"; } - add_opt(octx, po, opt, arg); + ret = add_opt(octx, po, opt, arg); + if (ret < 0) + return ret; + av_log(NULL, AV_LOG_DEBUG, " matched as option '%s' (%s) with " "argument '%s'.\n", po->name, po->help, arg); continue; @@ -777,7 +804,10 @@ do { \ if (opt[0] == 'n' && opt[1] == 'o' && (po = find_option(options, opt + 2)) && po->name && po->flags & OPT_BOOL) { - add_opt(octx, po, opt, "0"); + ret = add_opt(octx, po, opt, "0"); + if (ret < 0) + return ret; + av_log(NULL, AV_LOG_DEBUG, " matched as option '%s' (%s) with " "argument 0.\n", po->name, po->help); continue; @@ -892,8 +922,9 @@ int check_stream_specifier(AVFormatContext *s, AVStream *st, const char *spec) return ret; } -AVDictionary *filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id, - AVFormatContext *s, AVStream *st, const AVCodec *codec) +int filter_codec_opts(const AVDictionary *opts, enum AVCodecID codec_id, + AVFormatContext *s, AVStream *st, const AVCodec *codec, + AVDictionary **dst) { AVDictionary *ret = NULL; const AVDictionaryEntry *t = NULL; @@ -926,12 +957,16 @@ AVDictionary *filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id, char *p = strchr(t->key, ':'); /* check stream specification in opt name */ - if (p) - switch (check_stream_specifier(s, st, p + 1)) { - case 1: *p = 0; break; - case 0: continue; - default: exit_program(1); - } + if (p) { + int err = check_stream_specifier(s, st, p + 1); + if (err < 0) { + av_dict_free(&ret); + return err; + } else if (!err) + continue; + + *p = 0; + } if (av_opt_find(&cc, t->key, NULL, flags, AV_OPT_SEARCH_FAKE_OBJ) || !codec || @@ -947,41 +982,58 @@ AVDictionary *filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id, if (p) *p = ':'; } - return ret; + + *dst = ret; + return 0; } -AVDictionary **setup_find_stream_info_opts(AVFormatContext *s, - AVDictionary *codec_opts) +int setup_find_stream_info_opts(AVFormatContext *s, + AVDictionary *codec_opts, + AVDictionary ***dst) { - int i; + int ret; AVDictionary **opts; + *dst = NULL; + if (!s->nb_streams) - return NULL; + return 0; + opts = av_calloc(s->nb_streams, sizeof(*opts)); if (!opts) - report_and_exit(AVERROR(ENOMEM)); - for (i = 0; i < s->nb_streams; i++) - opts[i] = filter_codec_opts(codec_opts, s->streams[i]->codecpar->codec_id, - s, s->streams[i], NULL); - return opts; + return AVERROR(ENOMEM); + + for (int i = 0; i < s->nb_streams; i++) { + ret = filter_codec_opts(codec_opts, s->streams[i]->codecpar->codec_id, + s, s->streams[i], NULL, &opts[i]); + if (ret < 0) + goto fail; + } + *dst = opts; + return 0; +fail: + for (int i = 0; i < s->nb_streams; i++) + av_dict_free(&opts[i]); + av_freep(&opts); + return ret; } -void *grow_array(void *array, int elem_size, int *size, int new_size) +int grow_array(void **array, int elem_size, int *size, int new_size) { if (new_size >= INT_MAX / elem_size) { av_log(NULL, AV_LOG_ERROR, "Array too big.\n"); - exit_program(1); + return AVERROR(ERANGE); } if (*size < new_size) { - uint8_t *tmp = av_realloc_array(array, new_size, elem_size); + uint8_t *tmp = av_realloc_array(*array, new_size, elem_size); if (!tmp) - report_and_exit(AVERROR(ENOMEM)); + return AVERROR(ENOMEM); memset(tmp + *size*elem_size, 0, (new_size-*size) * elem_size); *size = new_size; - return tmp; + *array = tmp; + return 0; } - return array; + return 0; } void *allocate_array_elem(void *ptr, size_t elem_size, int *nb_elems) @@ -990,11 +1042,11 @@ void *allocate_array_elem(void *ptr, size_t elem_size, int *nb_elems) if (!(new_elem = av_mallocz(elem_size)) || av_dynarray_add_nofree(ptr, nb_elems, new_elem) < 0) - report_and_exit(AVERROR(ENOMEM)); + return NULL; return new_elem; } -double get_rotation(int32_t *displaymatrix) +double get_rotation(const int32_t *displaymatrix) { double theta = 0; if (displaymatrix) diff --git a/fftools/cmdutils.h b/fftools/cmdutils.h index 44962219839..8b67d827cc7 100644 --- a/fftools/cmdutils.h +++ b/fftools/cmdutils.h @@ -49,27 +49,6 @@ extern AVDictionary *swr_opts; extern AVDictionary *format_opts, *codec_opts; extern int hide_banner; -/** - * Register a program-specific cleanup routine. - */ -void register_exit(void (*cb)(int ret)); - -/** - * Reports an error corresponding to the provided - * AVERROR code and calls exit_program() with the - * corresponding POSIX error code. - * @note ret must be an AVERROR-value of a POSIX error code - * (i.e. AVERROR(EFOO) and not AVERROR_FOO). - * library functions can return both, so call this only - * with AVERROR(EFOO) of your own. - */ -void report_and_exit(int ret) av_noreturn; - -/** - * Wraps exit with a program-specific cleanup routine. - */ -void exit_program(int ret) av_noreturn; - /** * Initialize dynamic library loading */ @@ -100,8 +79,6 @@ int opt_timelimit(void *optctx, const char *opt, const char *arg); /** * Parse a string and return its corresponding value as a double. - * Exit from the application if the string cannot be correctly - * parsed or the corresponding value is invalid. * * @param context the context of the value to be set (e.g. the * corresponding command line option name) @@ -111,25 +88,8 @@ int opt_timelimit(void *optctx, const char *opt, const char *arg); * @param min the minimum valid accepted value * @param max the maximum valid accepted value */ -double parse_number_or_die(const char *context, const char *numstr, int type, - double min, double max); - -/** - * Parse a string specifying a time and return its corresponding - * value as a number of microseconds. Exit from the application if - * the string cannot be correctly parsed. - * - * @param context the context of the value to be set (e.g. the - * corresponding command line option name) - * @param timestr the string to be parsed - * @param is_duration a flag which tells how to interpret timestr, if - * not zero timestr is interpreted as a duration, otherwise as a - * date - * - * @see av_parse_time() - */ -int64_t parse_time_or_die(const char *context, const char *timestr, - int is_duration); +int parse_number(const char *context, const char *numstr, int type, + double min, double max, double *dst); typedef struct SpecifierOpt { char *specifier; /**< stream/chapter/program/... specifier */ @@ -213,8 +173,8 @@ void show_help_default(const char *opt, const char *arg); * argument without a leading option name flag. NULL if such arguments do * not have to be processed. */ -void parse_options(void *optctx, int argc, char **argv, const OptionDef *options, - void (* parse_arg_function)(void *optctx, const char*)); +int parse_options(void *optctx, int argc, char **argv, const OptionDef *options, + int (* parse_arg_function)(void *optctx, const char*)); /** * Parse one given option. @@ -352,10 +312,12 @@ int check_stream_specifier(AVFormatContext *s, AVStream *st, const char *spec); * @param st A stream from s for which the options should be filtered. * @param codec The particular codec for which the options should be filtered. * If null, the default one is looked up according to the codec id. - * @return a pointer to the created dictionary + * @param dst a pointer to the created dictionary + * @return a non-negative number on success, a negative error code on failure */ -AVDictionary *filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id, - AVFormatContext *s, AVStream *st, const AVCodec *codec); +int filter_codec_opts(const AVDictionary *opts, enum AVCodecID codec_id, + AVFormatContext *s, AVStream *st, const AVCodec *codec, + AVDictionary **dst); /** * Setup AVCodecContext options for avformat_find_stream_info(). @@ -364,12 +326,10 @@ AVDictionary *filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id, * contained in s. * Each dictionary will contain the options from codec_opts which can * be applied to the corresponding stream codec context. - * - * @return pointer to the created array of dictionaries. - * Calls exit() on failure. */ -AVDictionary **setup_find_stream_info_opts(AVFormatContext *s, - AVDictionary *codec_opts); +int setup_find_stream_info_opts(AVFormatContext *s, + AVDictionary *codec_opts, + AVDictionary ***dst); /** * Print an error message to stderr, indicating filename and a human @@ -418,35 +378,31 @@ FILE *get_preset_file(char *filename, size_t filename_size, /** * Realloc array to hold new_size elements of elem_size. - * Calls exit() on failure. * - * @param array array to reallocate + * @param array pointer to the array to reallocate, will be updated + * with a new pointer on success * @param elem_size size in bytes of each element * @param size new element count will be written here * @param new_size number of elements to place in reallocated array - * @return reallocated array + * @return a non-negative number on success, a negative error code on failure */ -void *grow_array(void *array, int elem_size, int *size, int new_size); +int grow_array(void **array, int elem_size, int *size, int new_size); /** * Atomically add a new element to an array of pointers, i.e. allocate * a new entry, reallocate the array of pointers and make the new last * member of this array point to the newly allocated buffer. - * Calls exit() on failure. * * @param array array of pointers to reallocate * @param elem_size size of the new element to allocate * @param nb_elems pointer to the number of elements of the array array; * *nb_elems will be incremented by one by this function. - * @return pointer to the newly allocated entry + * @return pointer to the newly allocated entry or NULL on failure */ void *allocate_array_elem(void *array, size_t elem_size, int *nb_elems); #define GROW_ARRAY(array, nb_elems)\ - array = grow_array(array, sizeof(*array), &nb_elems, nb_elems + 1) - -#define ALLOC_ARRAY_ELEM(array, nb_elems)\ - allocate_array_elem(&array, sizeof(*array[0]), &nb_elems) + grow_array((void**)&array, sizeof(*array), &nb_elems, nb_elems + 1) #define GET_PIX_FMT_NAME(pix_fmt)\ const char *name = av_get_pix_fmt_name(pix_fmt); @@ -461,6 +417,6 @@ void *allocate_array_elem(void *array, size_t elem_size, int *nb_elems); char name[16];\ snprintf(name, sizeof(name), "%d", rate); -double get_rotation(int32_t *displaymatrix); +double get_rotation(const int32_t *displaymatrix); #endif /* FFTOOLS_CMDUTILS_H */ diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c index f722ae76329..6130fd06fcb 100644 --- a/fftools/ffmpeg.c +++ b/fftools/ffmpeg.c @@ -24,14 +24,14 @@ */ #include "config.h" -#include -#include -#include -#include + #include #include #include #include +#include +#include +#include #if HAVE_IO_H #include @@ -40,37 +40,6 @@ #include #endif -#include "libavformat/avformat.h" -#include "libavdevice/avdevice.h" -#include "libswresample/swresample.h" -#include "libavutil/opt.h" -#include "libavutil/channel_layout.h" -#include "libavutil/parseutils.h" -#include "libavutil/samplefmt.h" -#include "libavutil/fifo.h" -#include "libavutil/hwcontext.h" -#include "libavutil/internal.h" -#include "libavutil/intreadwrite.h" -#include "libavutil/dict.h" -#include "libavutil/display.h" -#include "libavutil/mathematics.h" -#include "libavutil/pixdesc.h" -#include "libavutil/avstring.h" -#include "libavutil/libm.h" -#include "libavutil/imgutils.h" -#include "libavutil/timestamp.h" -#include "libavutil/bprint.h" -#include "libavutil/time.h" -#include "libavutil/thread.h" -#include "libavutil/threadmessage.h" -#include "libavcodec/mathops.h" -#include "libavcodec/version.h" -#include "libavformat/os_support.h" - -# include "libavfilter/avfilter.h" -# include "libavfilter/buffersrc.h" -# include "libavfilter/buffersink.h" - #if HAVE_SYS_RESOURCE_H #include #include @@ -86,7 +55,6 @@ #include #endif - #if HAVE_SYS_SELECT_H #include #endif @@ -100,18 +68,47 @@ #include #endif -#include +#include "libavutil/avassert.h" +#include "libavutil/avstring.h" +#include "libavutil/bprint.h" +#include "libavutil/channel_layout.h" +#include "libavutil/dict.h" +#include "libavutil/display.h" +#include "libavutil/fifo.h" +#include "libavutil/hwcontext.h" +#include "libavutil/imgutils.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/libm.h" +#include "libavutil/mathematics.h" +#include "libavutil/opt.h" +#include "libavutil/parseutils.h" +#include "libavutil/pixdesc.h" +#include "libavutil/samplefmt.h" +#include "libavutil/thread.h" +#include "libavutil/threadmessage.h" +#include "libavutil/time.h" +#include "libavutil/timestamp.h" + +#include "libavcodec/version.h" + +#include "libavformat/avformat.h" + +#include "libavdevice/avdevice.h" + +#include "libswresample/swresample.h" + +#include "libavfilter/avfilter.h" +#include "libavfilter/buffersrc.h" +#include "libavfilter/buffersink.h" -#include "ffmpeg.h" #include "cmdutils.h" +#include "ffmpeg.h" #include "sync_queue.h" -#include "libavutil/avassert.h" - const char program_name[] = "ffmpeg"; const int program_birth_year = 2000; -static FILE *vstats_file; +FILE *vstats_file; typedef struct BenchmarkTimeStamps { int64_t real_usec; @@ -121,12 +118,7 @@ typedef struct BenchmarkTimeStamps { static BenchmarkTimeStamps get_benchmark_time_stamps(void); static int64_t getmaxrss(void); -static int ifilter_has_all_input_formats(FilterGraph *fg); -static int64_t nb_frames_dup = 0; -static uint64_t dup_warning = 1000; -static int64_t nb_frames_drop = 0; -static int64_t decode_error_stat[2]; unsigned nb_output_dumped = 0; static BenchmarkTimeStamps current_time; @@ -153,153 +145,20 @@ static int restore_tty; This is a temporary solution until libavfilter gets real subtitles support. */ -static int sub2video_get_blank_frame(InputStream *ist) -{ - int ret; - AVFrame *frame = ist->sub2video.frame; - - av_frame_unref(frame); - ist->sub2video.frame->width = ist->dec_ctx->width ? ist->dec_ctx->width : ist->sub2video.w; - ist->sub2video.frame->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h; - ist->sub2video.frame->format = AV_PIX_FMT_RGB32; - if ((ret = av_frame_get_buffer(frame, 0)) < 0) - return ret; - memset(frame->data[0], 0, frame->height * frame->linesize[0]); - return 0; -} - -static void sub2video_copy_rect(uint8_t *dst, int dst_linesize, int w, int h, - AVSubtitleRect *r) -{ - uint32_t *pal, *dst2; - uint8_t *src, *src2; - int x, y; - - if (r->type != SUBTITLE_BITMAP) { - av_log(NULL, AV_LOG_WARNING, "sub2video: non-bitmap subtitle\n"); - return; - } - if (r->x < 0 || r->x + r->w > w || r->y < 0 || r->y + r->h > h) { - av_log(NULL, AV_LOG_WARNING, "sub2video: rectangle (%d %d %d %d) overflowing %d %d\n", - r->x, r->y, r->w, r->h, w, h - ); - return; - } - - dst += r->y * dst_linesize + r->x * 4; - src = r->data[0]; - pal = (uint32_t *)r->data[1]; - for (y = 0; y < r->h; y++) { - dst2 = (uint32_t *)dst; - src2 = src; - for (x = 0; x < r->w; x++) - *(dst2++) = pal[*(src2++)]; - dst += dst_linesize; - src += r->linesize[0]; - } -} - -static void sub2video_push_ref(InputStream *ist, int64_t pts) -{ - AVFrame *frame = ist->sub2video.frame; - int i; - int ret; - - av_assert1(frame->data[0]); - ist->sub2video.last_pts = frame->pts = pts; - for (i = 0; i < ist->nb_filters; i++) { - ret = av_buffersrc_add_frame_flags(ist->filters[i]->filter, frame, - AV_BUFFERSRC_FLAG_KEEP_REF | - AV_BUFFERSRC_FLAG_PUSH); - if (ret != AVERROR_EOF && ret < 0) - av_log(NULL, AV_LOG_WARNING, "Error while add the frame to buffer source(%s).\n", - av_err2str(ret)); - } -} - -void sub2video_update(InputStream *ist, int64_t heartbeat_pts, AVSubtitle *sub) -{ - AVFrame *frame = ist->sub2video.frame; - int8_t *dst; - int dst_linesize; - int num_rects, i; - int64_t pts, end_pts; - - if (!frame) - return; - if (sub) { - pts = av_rescale_q(sub->pts + sub->start_display_time * 1000LL, - AV_TIME_BASE_Q, ist->st->time_base); - end_pts = av_rescale_q(sub->pts + sub->end_display_time * 1000LL, - AV_TIME_BASE_Q, ist->st->time_base); - num_rects = sub->num_rects; - } else { - /* If we are initializing the system, utilize current heartbeat - PTS as the start time, and show until the following subpicture - is received. Otherwise, utilize the previous subpicture's end time - as the fall-back value. */ - pts = ist->sub2video.initialize ? - heartbeat_pts : ist->sub2video.end_pts; - end_pts = INT64_MAX; - num_rects = 0; - } - if (sub2video_get_blank_frame(ist) < 0) { - av_log(NULL, AV_LOG_ERROR, - "Impossible to get a blank canvas.\n"); - return; - } - dst = frame->data [0]; - dst_linesize = frame->linesize[0]; - for (i = 0; i < num_rects; i++) - sub2video_copy_rect(dst, dst_linesize, frame->width, frame->height, sub->rects[i]); - sub2video_push_ref(ist, pts); - ist->sub2video.end_pts = end_pts; - ist->sub2video.initialize = 0; -} - -static void sub2video_heartbeat(InputStream *ist, int64_t pts) +static void sub2video_heartbeat(InputFile *infile, int64_t pts, AVRational tb) { - InputFile *infile = input_files[ist->file_index]; - int i, j, nb_reqs; - int64_t pts2; - /* When a frame is read from a file, examine all sub2video streams in the same file and send the sub2video frame again. Otherwise, decoded video frames could be accumulating in the filter graph while a filter (possibly overlay) is desperately waiting for a subtitle frame. */ - for (i = 0; i < infile->nb_streams; i++) { - InputStream *ist2 = infile->streams[i]; - if (!ist2->sub2video.frame) - continue; - /* subtitles seem to be usually muxed ahead of other streams; - if not, subtracting a larger time here is necessary */ - pts2 = av_rescale_q(pts, ist->st->time_base, ist2->st->time_base) - 1; - /* do not send the heartbeat frame if the subtitle is already ahead */ - if (pts2 <= ist2->sub2video.last_pts) - continue; - if (pts2 >= ist2->sub2video.end_pts || ist2->sub2video.initialize) - /* if we have hit the end of the current displayed subpicture, - or if we need to initialize the system, update the - overlayed subpicture and its start/end times */ - sub2video_update(ist2, pts2 + 1, NULL); - for (j = 0, nb_reqs = 0; j < ist2->nb_filters; j++) - nb_reqs += av_buffersrc_get_nb_failed_requests(ist2->filters[j]->filter); - if (nb_reqs) - sub2video_push_ref(ist2, pts2); - } -} + for (int i = 0; i < infile->nb_streams; i++) { + InputStream *ist = infile->streams[i]; -static void sub2video_flush(InputStream *ist) -{ - int i; - int ret; + if (ist->dec_ctx->codec_type != AVMEDIA_TYPE_SUBTITLE) + continue; - if (ist->sub2video.end_pts < INT64_MAX) - sub2video_update(ist, INT64_MAX, NULL); - for (i = 0; i < ist->nb_filters; i++) { - ret = av_buffersrc_add_frame(ist->filters[i]->filter, NULL); - if (ret != AVERROR_EOF && ret < 0) - av_log(NULL, AV_LOG_WARNING, "Flush the frame error.\n"); + for (int j = 0; j < ist->nb_filters; j++) + ifilter_sub2video_heartbeat(ist->filters[j], pts, tb); } } @@ -323,7 +182,6 @@ static volatile int received_sigterm = 0; static volatile int received_nb_signals = 0; static atomic_int transcode_init_done = ATOMIC_VAR_INIT(0); static volatile int ffmpeg_exited = 0; -int main_return_code = 0; static int64_t copy_ts_first_pts = AV_NOPTS_VALUE; static void @@ -493,51 +351,15 @@ const AVIOInterruptCB int_cb = { decode_interrupt_cb, NULL }; static void ffmpeg_cleanup(int ret) { - int i, j; + int i; if (do_benchmark) { int maxrss = getmaxrss() / 1024; av_log(NULL, AV_LOG_INFO, "bench: maxrss=%ikB\n", maxrss); } - for (i = 0; i < nb_filtergraphs; i++) { - FilterGraph *fg = filtergraphs[i]; - avfilter_graph_free(&fg->graph); - for (j = 0; j < fg->nb_inputs; j++) { - InputFilter *ifilter = fg->inputs[j]; - struct InputStream *ist = ifilter->ist; - - if (ifilter->frame_queue) { - AVFrame *frame; - while (av_fifo_read(ifilter->frame_queue, &frame, 1) >= 0) - av_frame_free(&frame); - av_fifo_freep2(&ifilter->frame_queue); - } - av_freep(&ifilter->displaymatrix); - if (ist->sub2video.sub_queue) { - AVSubtitle sub; - while (av_fifo_read(ist->sub2video.sub_queue, &sub, 1) >= 0) - avsubtitle_free(&sub); - av_fifo_freep2(&ist->sub2video.sub_queue); - } - av_buffer_unref(&ifilter->hw_frames_ctx); - av_freep(&ifilter->name); - av_freep(&fg->inputs[j]); - } - av_freep(&fg->inputs); - for (j = 0; j < fg->nb_outputs; j++) { - OutputFilter *ofilter = fg->outputs[j]; - - avfilter_inout_free(&ofilter->out_tmp); - av_freep(&ofilter->name); - av_channel_layout_uninit(&ofilter->ch_layout); - av_freep(&fg->outputs[j]); - } - av_freep(&fg->outputs); - av_freep(&fg->graph_desc); - - av_freep(&filtergraphs[i]); - } + for (i = 0; i < nb_filtergraphs; i++) + fg_free(&filtergraphs[i]); av_freep(&filtergraphs); /* close files */ @@ -554,6 +376,10 @@ static void ffmpeg_cleanup(int ret) av_err2str(AVERROR(errno))); } av_freep(&vstats_filename); + of_enc_stats_close(); + + hw_device_free_all(); + av_freep(&filter_nbthreads); av_freep(&input_files); @@ -573,9 +399,7 @@ static void ffmpeg_cleanup(int ret) ffmpeg_exited = 1; } -/* iterate over all output streams in all output files; - * pass NULL to start iteration */ -static OutputStream *ost_iter(OutputStream *prev) +OutputStream *ost_iter(OutputStream *prev) { int of_idx = prev ? prev->file_index : 0; int ost_idx = prev ? prev->index + 1 : 0; @@ -594,7 +418,7 @@ static OutputStream *ost_iter(OutputStream *prev) InputStream *ist_iter(InputStream *prev) { int if_idx = prev ? prev->file_index : 0; - int ist_idx = prev ? prev->st->index + 1 : 0; + int ist_idx = prev ? prev->index + 1 : 0; for (; if_idx < nb_input_files; if_idx++) { InputFile *f = input_files[if_idx]; @@ -607,6 +431,23 @@ InputStream *ist_iter(InputStream *prev) return NULL; } +FrameData *frame_data(AVFrame *frame) +{ + if (!frame->opaque_ref) { + FrameData *fd; + + frame->opaque_ref = av_buffer_allocz(sizeof(*fd)); + if (!frame->opaque_ref) + return NULL; + fd = (FrameData*)frame->opaque_ref->data; + + fd->dec.frame_num = UINT64_MAX; + fd->dec.pts = AV_NOPTS_VALUE; + } + + return (FrameData*)frame->opaque_ref->data; +} + void remove_avoptions(AVDictionary **a, AVDictionary *b) { const AVDictionaryEntry *t = NULL; @@ -616,21 +457,18 @@ void remove_avoptions(AVDictionary **a, AVDictionary *b) } } -void assert_avoptions(AVDictionary *m) +int check_avoptions(AVDictionary *m) { const AVDictionaryEntry *t; if ((t = av_dict_get(m, "", NULL, AV_DICT_IGNORE_SUFFIX))) { av_log(NULL, AV_LOG_FATAL, "Option %s not found.\n", t->key); - exit_program(1); + return AVERROR_OPTION_NOT_FOUND; } -} -static void abort_codec_experimental(const AVCodec *c, int encoder) -{ - exit_program(1); + return 0; } -static void update_benchmark(const char *fmt, ...) +void update_benchmark(const char *fmt, ...) { if (do_benchmark_all) { BenchmarkTimeStamps t = get_benchmark_time_stamps(); @@ -651,7 +489,7 @@ static void update_benchmark(const char *fmt, ...) } } -static void close_output_stream(OutputStream *ost) +void close_output_stream(OutputStream *ost) { OutputFile *of = output_files[ost->file_index]; ost->finished |= ENCODER_FINISHED; @@ -660,954 +498,108 @@ static void close_output_stream(OutputStream *ost) sq_send(of->sq_encode, ost->sq_idx_encode, SQFRAME(NULL)); } -static int check_recording_time(OutputStream *ost, int64_t ts, AVRational tb) -{ - OutputFile *of = output_files[ost->file_index]; - - if (of->recording_time != INT64_MAX && - av_compare_ts(ts, tb, of->recording_time, AV_TIME_BASE_Q) >= 0) { - close_output_stream(ost); - return 0; - } - return 1; -} - -static double adjust_frame_pts_to_encoder_tb(OutputFile *of, OutputStream *ost, - AVFrame *frame) -{ - double float_pts = AV_NOPTS_VALUE; // this is identical to frame.pts but with higher precision - const int64_t start_time = (of->start_time == AV_NOPTS_VALUE) ? - 0 : of->start_time; - - AVCodecContext *const enc = ost->enc_ctx; - - AVRational tb = enc->time_base; - AVRational filter_tb = frame->time_base; - const int extra_bits = av_clip(29 - av_log2(tb.den), 0, 16); - - if (frame->pts == AV_NOPTS_VALUE) - goto early_exit; - - tb.den <<= extra_bits; - float_pts = av_rescale_q(frame->pts, filter_tb, tb) - - av_rescale_q(start_time, AV_TIME_BASE_Q, tb); - float_pts /= 1 << extra_bits; - // avoid exact midoints to reduce the chance of rounding differences, this - // can be removed in case the fps code is changed to work with integers - float_pts += FFSIGN(float_pts) * 1.0 / (1<<17); - - frame->pts = av_rescale_q(frame->pts, filter_tb, enc->time_base) - - av_rescale_q(start_time, AV_TIME_BASE_Q, enc->time_base); - frame->time_base = enc->time_base; - -early_exit: - - if (debug_ts) { - av_log(NULL, AV_LOG_INFO, "filter -> pts:%s pts_time:%s exact:%f time_base:%d/%d\n", - frame ? av_ts2str(frame->pts) : "NULL", - (enc && frame) ? av_ts2timestr(frame->pts, &enc->time_base) : "NULL", - float_pts, - enc ? enc->time_base.num : -1, - enc ? enc->time_base.den : -1); - } - - return float_pts; -} - -static int init_output_stream(OutputStream *ost, AVFrame *frame, - char *error, int error_len); - -static int init_output_stream_wrapper(OutputStream *ost, AVFrame *frame, - unsigned int fatal) -{ - int ret = AVERROR_BUG; - char error[1024] = {0}; - - if (ost->initialized) - return 0; - - ret = init_output_stream(ost, frame, error, sizeof(error)); - if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Error initializing output stream %d:%d -- %s\n", - ost->file_index, ost->index, error); - - if (fatal) - exit_program(1); - } - - return ret; -} - -static double psnr(double d) -{ - return -10.0 * log10(d); -} - -static void update_video_stats(OutputStream *ost, const AVPacket *pkt, int write_vstats) +static void print_report(int is_last_report, int64_t timer_start, int64_t cur_time) { - const uint8_t *sd = av_packet_get_side_data(pkt, AV_PKT_DATA_QUALITY_STATS, - NULL); - AVCodecContext *enc = ost->enc_ctx; - int64_t frame_number; - double ti1, bitrate, avg_bitrate; - - ost->quality = sd ? AV_RL32(sd) : -1; - ost->pict_type = sd ? sd[4] : AV_PICTURE_TYPE_NONE; - - for (int i = 0; ierror); i++) { - if (sd && i < sd[5]) - ost->error[i] = AV_RL64(sd + 8 + 8*i); - else - ost->error[i] = -1; - } + AVBPrint buf, buf_script; + int64_t total_size = of_filesize(output_files[0]); + int vid; + double bitrate; + double speed; + int64_t pts = AV_NOPTS_VALUE; + static int64_t last_time = -1; + static int first_report = 1; + uint64_t nb_frames_dup = 0, nb_frames_drop = 0; + int mins, secs, us; + int64_t hours; + const char *hours_sign; + int ret; + float t; - if (!write_vstats) + if (!print_stats && !is_last_report && !progress_avio) return; - /* this is executed just the first time update_video_stats is called */ - if (!vstats_file) { - vstats_file = fopen(vstats_filename, "w"); - if (!vstats_file) { - perror("fopen"); - exit_program(1); + if (!is_last_report) { + if (last_time == -1) { + last_time = cur_time; } + if (((cur_time - last_time) < stats_period && !first_report) || + (first_report && nb_output_dumped < nb_output_files)) + return; + last_time = cur_time; } - frame_number = ost->packets_encoded; - if (vstats_version <= 1) { - fprintf(vstats_file, "frame= %5"PRId64" q= %2.1f ", frame_number, - ost->quality / (float)FF_QP2LAMBDA); - } else { - fprintf(vstats_file, "out= %2d st= %2d frame= %5"PRId64" q= %2.1f ", ost->file_index, ost->index, frame_number, - ost->quality / (float)FF_QP2LAMBDA); - } - - if (ost->error[0]>=0 && (enc->flags & AV_CODEC_FLAG_PSNR)) - fprintf(vstats_file, "PSNR= %6.2f ", psnr(ost->error[0] / (enc->width * enc->height * 255.0 * 255.0))); - - fprintf(vstats_file,"f_size= %6d ", pkt->size); - /* compute pts value */ - ti1 = pkt->dts * av_q2d(ost->mux_timebase); - if (ti1 < 0.01) - ti1 = 0.01; - - bitrate = (pkt->size * 8) / av_q2d(enc->time_base) / 1000.0; - avg_bitrate = (double)(ost->data_size_enc * 8) / ti1 / 1000.0; - fprintf(vstats_file, "s_size= %8.0fkB time= %0.3f br= %7.1fkbits/s avg_br= %7.1fkbits/s ", - (double)ost->data_size_enc / 1024, ti1, bitrate, avg_bitrate); - fprintf(vstats_file, "type= %c\n", av_get_picture_type_char(ost->pict_type)); -} - -static int encode_frame(OutputFile *of, OutputStream *ost, AVFrame *frame) -{ - AVCodecContext *enc = ost->enc_ctx; - AVPacket *pkt = ost->pkt; - const char *type_desc = av_get_media_type_string(enc->codec_type); - const char *action = frame ? "encode" : "flush"; - int ret; + t = (cur_time-timer_start) / 1000000.0; - if (frame) { - ost->frames_encoded++; - ost->samples_encoded += frame->nb_samples; + vid = 0; + av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC); + av_bprint_init(&buf_script, 0, AV_BPRINT_SIZE_AUTOMATIC); + for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { + const float q = ost->enc ? ost->quality / (float) FF_QP2LAMBDA : -1; - if (debug_ts) { - av_log(NULL, AV_LOG_INFO, "encoder <- type:%s " - "frame_pts:%s frame_pts_time:%s time_base:%d/%d\n", - type_desc, - av_ts2str(frame->pts), av_ts2timestr(frame->pts, &enc->time_base), - enc->time_base.num, enc->time_base.den); + if (vid && ost->type == AVMEDIA_TYPE_VIDEO) { + av_bprintf(&buf, "q=%2.1f ", q); + av_bprintf(&buf_script, "stream_%d_%d_q=%.1f\n", + ost->file_index, ost->index, q); } - } - - update_benchmark(NULL); - - ret = avcodec_send_frame(enc, frame); - if (ret < 0 && !(ret == AVERROR_EOF && !frame)) { - av_log(NULL, AV_LOG_ERROR, "Error submitting %s frame to the encoder\n", - type_desc); - return ret; - } - - while (1) { - ret = avcodec_receive_packet(enc, pkt); - update_benchmark("%s_%s %d.%d", action, type_desc, - ost->file_index, ost->index); + if (!vid && ost->type == AVMEDIA_TYPE_VIDEO) { + float fps; + uint64_t frame_number = atomic_load(&ost->packets_written); - /* if two pass, output log on success and EOF */ - if ((ret >= 0 || ret == AVERROR_EOF) && ost->logfile && enc->stats_out) - fprintf(ost->logfile, "%s", enc->stats_out); + fps = t > 1 ? frame_number / t : 0; + av_bprintf(&buf, "frame=%5"PRId64" fps=%3.*f q=%3.1f ", + frame_number, fps < 9.95, fps, q); + av_bprintf(&buf_script, "frame=%"PRId64"\n", frame_number); + av_bprintf(&buf_script, "fps=%.2f\n", fps); + av_bprintf(&buf_script, "stream_%d_%d_q=%.1f\n", + ost->file_index, ost->index, q); + if (is_last_report) + av_bprintf(&buf, "L"); - if (ret == AVERROR(EAGAIN)) { - av_assert0(frame); // should never happen during flushing - return 0; - } else if (ret == AVERROR_EOF) { - of_output_packet(of, pkt, ost, 1); - return ret; - } else if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "%s encoding failed\n", type_desc); - return ret; - } + nb_frames_dup = ost->nb_frames_dup; + nb_frames_drop = ost->nb_frames_drop; - if (debug_ts) { - av_log(NULL, AV_LOG_INFO, "encoder -> type:%s " - "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s " - "duration:%s duration_time:%s\n", - type_desc, - av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &enc->time_base), - av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &enc->time_base), - av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, &enc->time_base)); + vid = 1; } - - av_packet_rescale_ts(pkt, enc->time_base, ost->mux_timebase); - - if (debug_ts) { - av_log(NULL, AV_LOG_INFO, "encoder -> type:%s " - "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s " - "duration:%s duration_time:%s\n", - type_desc, - av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &enc->time_base), - av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &enc->time_base), - av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, &enc->time_base)); + /* compute min output value */ + if (ost->last_mux_dts != AV_NOPTS_VALUE) { + if (pts == AV_NOPTS_VALUE || ost->last_mux_dts > pts) + pts = ost->last_mux_dts; + if (copy_ts) { + if (copy_ts_first_pts == AV_NOPTS_VALUE && pts > 1) + copy_ts_first_pts = pts; + if (copy_ts_first_pts != AV_NOPTS_VALUE) + pts -= copy_ts_first_pts; + } } - ost->data_size_enc += pkt->size; - - if (enc->codec_type == AVMEDIA_TYPE_VIDEO) - update_video_stats(ost, pkt, !!vstats_filename); - - ost->packets_encoded++; - - of_output_packet(of, pkt, ost, 0); + if (is_last_report) + nb_frames_drop += ost->last_dropped; } - av_assert0(0); -} - -static int submit_encode_frame(OutputFile *of, OutputStream *ost, - AVFrame *frame) -{ - int ret; + us = FFABS64U(pts) % AV_TIME_BASE; + secs = FFABS64U(pts) / AV_TIME_BASE % 60; + mins = FFABS64U(pts) / AV_TIME_BASE / 60 % 60; + hours = FFABS64U(pts) / AV_TIME_BASE / 3600; + hours_sign = (pts < 0) ? "-" : ""; - if (ost->sq_idx_encode < 0) - return encode_frame(of, ost, frame); + bitrate = pts != AV_NOPTS_VALUE && pts && total_size >= 0 ? total_size * 8 / (pts / 1000.0) : -1; + speed = pts != AV_NOPTS_VALUE && t != 0.0 ? (double)pts / AV_TIME_BASE / t : -1; - if (frame) { - ret = av_frame_ref(ost->sq_frame, frame); - if (ret < 0) - return ret; - frame = ost->sq_frame; + if (total_size < 0) av_bprintf(&buf, "size=N/A time="); + else av_bprintf(&buf, "size=%8.0fkB time=", total_size / 1024.0); + if (pts == AV_NOPTS_VALUE) { + av_bprintf(&buf, "N/A "); + } else { + av_bprintf(&buf, "%s%02"PRId64":%02d:%02d.%02d ", + hours_sign, hours, mins, secs, (100 * us) / AV_TIME_BASE); } - ret = sq_send(of->sq_encode, ost->sq_idx_encode, - SQFRAME(frame)); - if (ret < 0) { - if (frame) - av_frame_unref(frame); - if (ret != AVERROR_EOF) - return ret; - } - - while (1) { - AVFrame *enc_frame = ost->sq_frame; - - ret = sq_receive(of->sq_encode, ost->sq_idx_encode, - SQFRAME(enc_frame)); - if (ret == AVERROR_EOF) { - enc_frame = NULL; - } else if (ret < 0) { - return (ret == AVERROR(EAGAIN)) ? 0 : ret; - } - - ret = encode_frame(of, ost, enc_frame); - if (enc_frame) - av_frame_unref(enc_frame); - if (ret < 0) { - if (ret == AVERROR_EOF) - close_output_stream(ost); - return ret; - } - } -} - -static void do_audio_out(OutputFile *of, OutputStream *ost, - AVFrame *frame) -{ - AVCodecContext *enc = ost->enc_ctx; - int ret; - - if (frame->pts == AV_NOPTS_VALUE) - frame->pts = ost->next_pts; - else { - int64_t start_time = (of->start_time == AV_NOPTS_VALUE) ? 0 : of->start_time; - frame->pts = - av_rescale_q(frame->pts, frame->time_base, enc->time_base) - - av_rescale_q(start_time, AV_TIME_BASE_Q, enc->time_base); - } - frame->time_base = enc->time_base; - - if (!check_recording_time(ost, frame->pts, frame->time_base)) - return; - - ost->next_pts = frame->pts + frame->nb_samples; - - ret = submit_encode_frame(of, ost, frame); - if (ret < 0 && ret != AVERROR_EOF) - exit_program(1); -} - -static void do_subtitle_out(OutputFile *of, - OutputStream *ost, - AVSubtitle *sub) -{ - int subtitle_out_max_size = 1024 * 1024; - int subtitle_out_size, nb, i, ret; - AVCodecContext *enc; - AVPacket *pkt = ost->pkt; - int64_t pts; - - if (sub->pts == AV_NOPTS_VALUE) { - av_log(NULL, AV_LOG_ERROR, "Subtitle packets must have a pts\n"); - if (exit_on_error) - exit_program(1); - return; - } - - enc = ost->enc_ctx; - - /* Note: DVB subtitle need one packet to draw them and one other - packet to clear them */ - /* XXX: signal it in the codec context ? */ - if (enc->codec_id == AV_CODEC_ID_DVB_SUBTITLE) - nb = 2; - else - nb = 1; - - /* shift timestamp to honor -ss and make check_recording_time() work with -t */ - pts = sub->pts; - if (output_files[ost->file_index]->start_time != AV_NOPTS_VALUE) - pts -= output_files[ost->file_index]->start_time; - for (i = 0; i < nb; i++) { - unsigned save_num_rects = sub->num_rects; - - if (!check_recording_time(ost, pts, AV_TIME_BASE_Q)) - return; - - ret = av_new_packet(pkt, subtitle_out_max_size); - if (ret < 0) - report_and_exit(AVERROR(ENOMEM)); - - sub->pts = pts; - // start_display_time is required to be 0 - sub->pts += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q); - sub->end_display_time -= sub->start_display_time; - sub->start_display_time = 0; - if (i == 1) - sub->num_rects = 0; - - ost->frames_encoded++; - - subtitle_out_size = avcodec_encode_subtitle(enc, pkt->data, pkt->size, sub); - if (i == 1) - sub->num_rects = save_num_rects; - if (subtitle_out_size < 0) { - av_log(NULL, AV_LOG_FATAL, "Subtitle encoding failed\n"); - exit_program(1); - } - - av_shrink_packet(pkt, subtitle_out_size); - pkt->pts = av_rescale_q(sub->pts, AV_TIME_BASE_Q, ost->mux_timebase); - pkt->duration = av_rescale_q(sub->end_display_time, (AVRational){ 1, 1000 }, ost->mux_timebase); - if (enc->codec_id == AV_CODEC_ID_DVB_SUBTITLE) { - /* XXX: the pts correction is handled here. Maybe handling - it in the codec would be better */ - if (i == 0) - pkt->pts += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, ost->mux_timebase); - else - pkt->pts += av_rescale_q(sub->end_display_time, (AVRational){ 1, 1000 }, ost->mux_timebase); - } - pkt->dts = pkt->pts; - of_output_packet(of, pkt, ost, 0); - } -} - -/* Convert frame timestamps to the encoder timebase and decide how many times - * should this (and possibly previous) frame be repeated in order to conform to - * desired target framerate (if any). - */ -static void video_sync_process(OutputFile *of, OutputStream *ost, - AVFrame *next_picture, double duration, - int64_t *nb_frames, int64_t *nb_frames_prev) -{ - double delta0, delta; - - double sync_ipts = adjust_frame_pts_to_encoder_tb(of, ost, next_picture); - /* delta0 is the "drift" between the input frame (next_picture) and - * where it would fall in the output. */ - delta0 = sync_ipts - ost->next_pts; - delta = delta0 + duration; - - // tracks the number of times the PREVIOUS frame should be duplicated, - // mostly for variable framerate (VFR) - *nb_frames_prev = 0; - /* by default, we output a single frame */ - *nb_frames = 1; - - if (delta0 < 0 && - delta > 0 && - ost->vsync_method != VSYNC_PASSTHROUGH && - ost->vsync_method != VSYNC_DROP) { - if (delta0 < -0.6) { - av_log(NULL, AV_LOG_VERBOSE, "Past duration %f too large\n", -delta0); - } else - av_log(NULL, AV_LOG_DEBUG, "Clipping frame in rate conversion by %f\n", -delta0); - sync_ipts = ost->next_pts; - duration += delta0; - delta0 = 0; - } - - switch (ost->vsync_method) { - case VSYNC_VSCFR: - if (ost->vsync_frame_number == 0 && delta0 >= 0.5) { - av_log(NULL, AV_LOG_DEBUG, "Not duplicating %d initial frames\n", (int)lrintf(delta0)); - delta = duration; - delta0 = 0; - ost->next_pts = llrint(sync_ipts); - } - case VSYNC_CFR: - // FIXME set to 0.5 after we fix some dts/pts bugs like in avidec.c - if (frame_drop_threshold && delta < frame_drop_threshold && ost->vsync_frame_number) { - *nb_frames = 0; - } else if (delta < -1.1) - *nb_frames = 0; - else if (delta > 1.1) { - *nb_frames = llrintf(delta); - if (delta0 > 1.1) - *nb_frames_prev = llrintf(delta0 - 0.6); - } - next_picture->duration = 1; - break; - case VSYNC_VFR: - if (delta <= -0.6) - *nb_frames = 0; - else if (delta > 0.6) - ost->next_pts = llrint(sync_ipts); - next_picture->duration = duration; - break; - case VSYNC_DROP: - case VSYNC_PASSTHROUGH: - next_picture->duration = duration; - ost->next_pts = llrint(sync_ipts); - break; - default: - av_assert0(0); - } -} - -static enum AVPictureType forced_kf_apply(KeyframeForceCtx *kf, AVRational tb, - const AVFrame *in_picture, int dup_idx) -{ - double pts_time; - - if (kf->ref_pts == AV_NOPTS_VALUE) - kf->ref_pts = in_picture->pts; - - pts_time = (in_picture->pts - kf->ref_pts) * av_q2d(tb); - if (kf->index < kf->nb_pts && - av_compare_ts(in_picture->pts, tb, kf->pts[kf->index], AV_TIME_BASE_Q) >= 0) { - kf->index++; - goto force_keyframe; - } else if (kf->pexpr) { - double res; - kf->expr_const_values[FKF_T] = pts_time; - res = av_expr_eval(kf->pexpr, - kf->expr_const_values, NULL); - ff_dlog(NULL, "force_key_frame: n:%f n_forced:%f prev_forced_n:%f t:%f prev_forced_t:%f -> res:%f\n", - kf->expr_const_values[FKF_N], - kf->expr_const_values[FKF_N_FORCED], - kf->expr_const_values[FKF_PREV_FORCED_N], - kf->expr_const_values[FKF_T], - kf->expr_const_values[FKF_PREV_FORCED_T], - res); - - kf->expr_const_values[FKF_N] += 1; - - if (res) { - kf->expr_const_values[FKF_PREV_FORCED_N] = kf->expr_const_values[FKF_N] - 1; - kf->expr_const_values[FKF_PREV_FORCED_T] = kf->expr_const_values[FKF_T]; - kf->expr_const_values[FKF_N_FORCED] += 1; - goto force_keyframe; - } - } else if (kf->type == KF_FORCE_SOURCE && - in_picture->key_frame == 1 && !dup_idx) { - goto force_keyframe; - } else if (kf->type == KF_FORCE_SOURCE_NO_DROP && !dup_idx) { - kf->dropped_keyframe = 0; - if ((in_picture->key_frame == 1) || kf->dropped_keyframe) - goto force_keyframe; - } - - return AV_PICTURE_TYPE_NONE; - -force_keyframe: - av_log(NULL, AV_LOG_DEBUG, "Forced keyframe at time %f\n", pts_time); - return AV_PICTURE_TYPE_I; -} - -/* May modify/reset next_picture */ -static void do_video_out(OutputFile *of, - OutputStream *ost, - AVFrame *next_picture) -{ - int ret; - AVCodecContext *enc = ost->enc_ctx; - AVRational frame_rate; - int64_t nb_frames, nb_frames_prev, i; - double duration = 0; - InputStream *ist = ost->ist; - AVFilterContext *filter = ost->filter->filter; - - init_output_stream_wrapper(ost, next_picture, 1); - - frame_rate = av_buffersink_get_frame_rate(filter); - if (frame_rate.num > 0 && frame_rate.den > 0) - duration = 1/(av_q2d(frame_rate) * av_q2d(enc->time_base)); - - if(ist && ist->st->start_time != AV_NOPTS_VALUE && ist->first_dts != AV_NOPTS_VALUE && ost->frame_rate.num) - duration = FFMIN(duration, 1/(av_q2d(ost->frame_rate) * av_q2d(enc->time_base))); - - if (!ost->filters_script && - !ost->filters && - (nb_filtergraphs == 0 || !filtergraphs[0]->graph_desc) && - next_picture && - ist && - lrintf(next_picture->duration * av_q2d(ist->st->time_base) / av_q2d(enc->time_base)) > 0) { - duration = lrintf(next_picture->duration * av_q2d(ist->st->time_base) / av_q2d(enc->time_base)); - } - - if (!next_picture) { - //end, flushing - nb_frames_prev = nb_frames = mid_pred(ost->last_nb0_frames[0], - ost->last_nb0_frames[1], - ost->last_nb0_frames[2]); - } else { - video_sync_process(of, ost, next_picture, duration, - &nb_frames, &nb_frames_prev); - } - - memmove(ost->last_nb0_frames + 1, - ost->last_nb0_frames, - sizeof(ost->last_nb0_frames[0]) * (FF_ARRAY_ELEMS(ost->last_nb0_frames) - 1)); - ost->last_nb0_frames[0] = nb_frames_prev; - - if (nb_frames_prev == 0 && ost->last_dropped) { - nb_frames_drop++; - av_log(NULL, AV_LOG_VERBOSE, - "*** dropping frame %"PRId64" from stream %d at ts %"PRId64"\n", - ost->vsync_frame_number, ost->st->index, ost->last_frame->pts); - } - if (nb_frames > (nb_frames_prev && ost->last_dropped) + (nb_frames > nb_frames_prev)) { - if (nb_frames > dts_error_threshold * 30) { - av_log(NULL, AV_LOG_ERROR, "%"PRId64" frame duplication too large, skipping\n", nb_frames - 1); - nb_frames_drop++; - return; - } - nb_frames_dup += nb_frames - (nb_frames_prev && ost->last_dropped) - (nb_frames > nb_frames_prev); - av_log(NULL, AV_LOG_VERBOSE, "*** %"PRId64" dup!\n", nb_frames - 1); - if (nb_frames_dup > dup_warning) { - av_log(NULL, AV_LOG_WARNING, "More than %"PRIu64" frames duplicated\n", dup_warning); - dup_warning *= 10; - } - } - ost->last_dropped = nb_frames == nb_frames_prev && next_picture; - ost->kf.dropped_keyframe = ost->last_dropped && next_picture && next_picture->key_frame; - - /* duplicates frame if needed */ - for (i = 0; i < nb_frames; i++) { - AVFrame *in_picture; - - if (i < nb_frames_prev && ost->last_frame->buf[0]) { - in_picture = ost->last_frame; - } else - in_picture = next_picture; - - if (!in_picture) - return; - - in_picture->pts = ost->next_pts; - - if (!check_recording_time(ost, in_picture->pts, ost->enc_ctx->time_base)) - return; - - in_picture->quality = enc->global_quality; - in_picture->pict_type = forced_kf_apply(&ost->kf, enc->time_base, in_picture, i); - - ret = submit_encode_frame(of, ost, in_picture); - if (ret == AVERROR_EOF) - break; - else if (ret < 0) - exit_program(1); - - ost->next_pts++; - ost->vsync_frame_number++; - } - - av_frame_unref(ost->last_frame); - if (next_picture) - av_frame_move_ref(ost->last_frame, next_picture); -} - -/** - * Get and encode new output from any of the filtergraphs, without causing - * activity. - * - * @return 0 for success, <0 for severe errors - */ -static int reap_filters(int flush) -{ - AVFrame *filtered_frame = NULL; - - /* Reap all buffers present in the buffer sinks */ - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { - OutputFile *of = output_files[ost->file_index]; - AVFilterContext *filter; - AVCodecContext *enc = ost->enc_ctx; - int ret = 0; - - if (!ost->filter || !ost->filter->graph->graph) - continue; - filter = ost->filter->filter; - - /* - * Unlike video, with audio the audio frame size matters. - * Currently we are fully reliant on the lavfi filter chain to - * do the buffering deed for us, and thus the frame size parameter - * needs to be set accordingly. Where does one get the required - * frame size? From the initialized AVCodecContext of an audio - * encoder. Thus, if we have gotten to an audio stream, initialize - * the encoder earlier than receiving the first AVFrame. - */ - if (av_buffersink_get_type(filter) == AVMEDIA_TYPE_AUDIO) - init_output_stream_wrapper(ost, NULL, 1); - - filtered_frame = ost->filtered_frame; - - while (1) { - ret = av_buffersink_get_frame_flags(filter, filtered_frame, - AV_BUFFERSINK_FLAG_NO_REQUEST); - if (ret < 0) { - if (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) { - av_log(NULL, AV_LOG_WARNING, - "Error in av_buffersink_get_frame_flags(): %s\n", av_err2str(ret)); - } else if (flush && ret == AVERROR_EOF) { - if (av_buffersink_get_type(filter) == AVMEDIA_TYPE_VIDEO) - do_video_out(of, ost, NULL); - } - break; - } - if (ost->finished) { - av_frame_unref(filtered_frame); - continue; - } - - if (filtered_frame->pts != AV_NOPTS_VALUE) { - AVRational tb = av_buffersink_get_time_base(filter); - ost->last_filter_pts = av_rescale_q(filtered_frame->pts, tb, - AV_TIME_BASE_Q); - filtered_frame->time_base = tb; - - if (debug_ts) - av_log(NULL, AV_LOG_INFO, "filter_raw -> pts:%s pts_time:%s time_base:%d/%d\n", - av_ts2str(filtered_frame->pts), - av_ts2timestr(filtered_frame->pts, &tb), - tb.num, tb.den); - } - - switch (av_buffersink_get_type(filter)) { - case AVMEDIA_TYPE_VIDEO: - if (!ost->frame_aspect_ratio.num) - enc->sample_aspect_ratio = filtered_frame->sample_aspect_ratio; - - do_video_out(of, ost, filtered_frame); - break; - case AVMEDIA_TYPE_AUDIO: - if (!(enc->codec->capabilities & AV_CODEC_CAP_PARAM_CHANGE) && - enc->ch_layout.nb_channels != filtered_frame->ch_layout.nb_channels) { - av_log(NULL, AV_LOG_ERROR, - "Audio filter graph output is not normalized and encoder does not support parameter changes\n"); - break; - } - do_audio_out(of, ost, filtered_frame); - break; - default: - // TODO support subtitle filters - av_assert0(0); - } - - av_frame_unref(filtered_frame); - } - } - - return 0; -} - -static void print_final_stats(int64_t total_size) -{ - uint64_t video_size = 0, audio_size = 0, extra_size = 0, other_size = 0; - uint64_t subtitle_size = 0; - uint64_t data_size = 0; - float percent = -1.0; - int i, j; - int pass1_used = 1; - - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { - AVCodecParameters *par = ost->st->codecpar; - const uint64_t s = ost->data_size_mux; - - switch (par->codec_type) { - case AVMEDIA_TYPE_VIDEO: video_size += s; break; - case AVMEDIA_TYPE_AUDIO: audio_size += s; break; - case AVMEDIA_TYPE_SUBTITLE: subtitle_size += s; break; - default: other_size += s; break; - } - extra_size += par->extradata_size; - data_size += s; - if (ost->enc_ctx && - (ost->enc_ctx->flags & (AV_CODEC_FLAG_PASS1 | AV_CODEC_FLAG_PASS2)) - != AV_CODEC_FLAG_PASS1) - pass1_used = 0; - } - - if (data_size && total_size>0 && total_size >= data_size) - percent = 100.0 * (total_size - data_size) / data_size; - - av_log(NULL, AV_LOG_INFO, "video:%1.0fkB audio:%1.0fkB subtitle:%1.0fkB other streams:%1.0fkB global headers:%1.0fkB muxing overhead: ", - video_size / 1024.0, - audio_size / 1024.0, - subtitle_size / 1024.0, - other_size / 1024.0, - extra_size / 1024.0); - if (percent >= 0.0) - av_log(NULL, AV_LOG_INFO, "%f%%", percent); - else - av_log(NULL, AV_LOG_INFO, "unknown"); - av_log(NULL, AV_LOG_INFO, "\n"); - - /* print verbose per-stream stats */ - for (i = 0; i < nb_input_files; i++) { - InputFile *f = input_files[i]; - uint64_t total_packets = 0, total_size = 0; - - av_log(NULL, AV_LOG_VERBOSE, "Input file #%d (%s):\n", - i, f->ctx->url); - - for (j = 0; j < f->nb_streams; j++) { - InputStream *ist = f->streams[j]; - enum AVMediaType type = ist->par->codec_type; - - total_size += ist->data_size; - total_packets += ist->nb_packets; - - av_log(NULL, AV_LOG_VERBOSE, " Input stream #%d:%d (%s): ", - i, j, av_get_media_type_string(type)); - av_log(NULL, AV_LOG_VERBOSE, "%"PRIu64" packets read (%"PRIu64" bytes); ", - ist->nb_packets, ist->data_size); - - if (ist->decoding_needed) { - av_log(NULL, AV_LOG_VERBOSE, "%"PRIu64" frames decoded", - ist->frames_decoded); - if (type == AVMEDIA_TYPE_AUDIO) - av_log(NULL, AV_LOG_VERBOSE, " (%"PRIu64" samples)", ist->samples_decoded); - av_log(NULL, AV_LOG_VERBOSE, "; "); - } - - av_log(NULL, AV_LOG_VERBOSE, "\n"); - } - - av_log(NULL, AV_LOG_VERBOSE, " Total: %"PRIu64" packets (%"PRIu64" bytes) demuxed\n", - total_packets, total_size); - } - - for (i = 0; i < nb_output_files; i++) { - OutputFile *of = output_files[i]; - uint64_t total_packets = 0, total_size = 0; - - av_log(NULL, AV_LOG_VERBOSE, "Output file #%d (%s):\n", - i, of->url); - - for (j = 0; j < of->nb_streams; j++) { - OutputStream *ost = of->streams[j]; - enum AVMediaType type = ost->st->codecpar->codec_type; - - total_size += ost->data_size_mux; - total_packets += atomic_load(&ost->packets_written); - - av_log(NULL, AV_LOG_VERBOSE, " Output stream #%d:%d (%s): ", - i, j, av_get_media_type_string(type)); - if (ost->enc_ctx) { - av_log(NULL, AV_LOG_VERBOSE, "%"PRIu64" frames encoded", - ost->frames_encoded); - if (type == AVMEDIA_TYPE_AUDIO) - av_log(NULL, AV_LOG_VERBOSE, " (%"PRIu64" samples)", ost->samples_encoded); - av_log(NULL, AV_LOG_VERBOSE, "; "); - } - - av_log(NULL, AV_LOG_VERBOSE, "%"PRIu64" packets muxed (%"PRIu64" bytes); ", - atomic_load(&ost->packets_written), ost->data_size_mux); - - av_log(NULL, AV_LOG_VERBOSE, "\n"); - } - - av_log(NULL, AV_LOG_VERBOSE, " Total: %"PRIu64" packets (%"PRIu64" bytes) muxed\n", - total_packets, total_size); - } - if(video_size + data_size + audio_size + subtitle_size + extra_size == 0){ - av_log(NULL, AV_LOG_WARNING, "Output file is empty, nothing was encoded "); - if (pass1_used) { - av_log(NULL, AV_LOG_WARNING, "\n"); - } else { - av_log(NULL, AV_LOG_WARNING, "(check -ss / -t / -frames parameters if used)\n"); - } - } -} - -static void print_report(int is_last_report, int64_t timer_start, int64_t cur_time) -{ - AVBPrint buf, buf_script; - int64_t total_size = of_filesize(output_files[0]); - int vid; - double bitrate; - double speed; - int64_t pts = INT64_MIN + 1; - static int64_t last_time = -1; - static int first_report = 1; - static int qp_histogram[52]; - int hours, mins, secs, us; - const char *hours_sign; - int ret; - float t; - - if (!print_stats && !is_last_report && !progress_avio) - return; - - if (!is_last_report) { - if (last_time == -1) { - last_time = cur_time; - } - if (((cur_time - last_time) < stats_period && !first_report) || - (first_report && nb_output_dumped < nb_output_files)) - return; - last_time = cur_time; - } - - t = (cur_time-timer_start) / 1000000.0; - - vid = 0; - av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC); - av_bprint_init(&buf_script, 0, AV_BPRINT_SIZE_AUTOMATIC); - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { - const AVCodecContext * const enc = ost->enc_ctx; - const float q = enc ? ost->quality / (float) FF_QP2LAMBDA : -1; - - if (vid && ost->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { - av_bprintf(&buf, "q=%2.1f ", q); - av_bprintf(&buf_script, "stream_%d_%d_q=%.1f\n", - ost->file_index, ost->index, q); - } - if (!vid && ost->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { - float fps; - uint64_t frame_number = atomic_load(&ost->packets_written); - - fps = t > 1 ? frame_number / t : 0; - av_bprintf(&buf, "frame=%5"PRId64" fps=%3.*f q=%3.1f ", - frame_number, fps < 9.95, fps, q); - av_bprintf(&buf_script, "frame=%"PRId64"\n", frame_number); - av_bprintf(&buf_script, "fps=%.2f\n", fps); - av_bprintf(&buf_script, "stream_%d_%d_q=%.1f\n", - ost->file_index, ost->index, q); - if (is_last_report) - av_bprintf(&buf, "L"); - if (qp_hist) { - int j; - int qp = lrintf(q); - if (qp >= 0 && qp < FF_ARRAY_ELEMS(qp_histogram)) - qp_histogram[qp]++; - for (j = 0; j < 32; j++) - av_bprintf(&buf, "%X", av_log2(qp_histogram[j] + 1)); - } - - if (enc && (enc->flags & AV_CODEC_FLAG_PSNR) && - (ost->pict_type != AV_PICTURE_TYPE_NONE || is_last_report)) { - int j; - double error, error_sum = 0; - double scale, scale_sum = 0; - double p; - char type[3] = { 'Y','U','V' }; - av_bprintf(&buf, "PSNR="); - for (j = 0; j < 3; j++) { - if (is_last_report) { - error = enc->error[j]; - scale = enc->width * enc->height * 255.0 * 255.0 * frame_number; - } else { - error = ost->error[j]; - scale = enc->width * enc->height * 255.0 * 255.0; - } - if (j) - scale /= 4; - error_sum += error; - scale_sum += scale; - p = psnr(error / scale); - av_bprintf(&buf, "%c:%2.2f ", type[j], p); - av_bprintf(&buf_script, "stream_%d_%d_psnr_%c=%2.2f\n", - ost->file_index, ost->index, type[j] | 32, p); - } - p = psnr(error_sum / scale_sum); - av_bprintf(&buf, "*:%2.2f ", psnr(error_sum / scale_sum)); - av_bprintf(&buf_script, "stream_%d_%d_psnr_all=%2.2f\n", - ost->file_index, ost->index, p); - } - vid = 1; - } - /* compute min output value */ - if (ost->last_mux_dts != AV_NOPTS_VALUE) { - pts = FFMAX(pts, ost->last_mux_dts); - if (copy_ts) { - if (copy_ts_first_pts == AV_NOPTS_VALUE && pts > 1) - copy_ts_first_pts = pts; - if (copy_ts_first_pts != AV_NOPTS_VALUE) - pts -= copy_ts_first_pts; - } - } - - if (is_last_report) - nb_frames_drop += ost->last_dropped; - } - - secs = FFABS(pts) / AV_TIME_BASE; - us = FFABS(pts) % AV_TIME_BASE; - mins = secs / 60; - secs %= 60; - hours = mins / 60; - mins %= 60; - hours_sign = (pts < 0) ? "-" : ""; - - bitrate = pts && total_size >= 0 ? total_size * 8 / (pts / 1000.0) : -1; - speed = t != 0.0 ? (double)pts / AV_TIME_BASE / t : -1; - - if (total_size < 0) av_bprintf(&buf, "size=N/A time="); - else av_bprintf(&buf, "size=%8.0fkB time=", total_size / 1024.0); - if (pts == AV_NOPTS_VALUE) { - av_bprintf(&buf, "N/A "); - } else { - av_bprintf(&buf, "%s%02d:%02d:%02d.%02d ", - hours_sign, hours, mins, secs, (100 * us) / AV_TIME_BASE); - } - - if (bitrate < 0) { - av_bprintf(&buf, "bitrate=N/A"); - av_bprintf(&buf_script, "bitrate=N/A\n"); - }else{ - av_bprintf(&buf, "bitrate=%6.1fkbits/s", bitrate); - av_bprintf(&buf_script, "bitrate=%6.1fkbits/s\n", bitrate); + if (bitrate < 0) { + av_bprintf(&buf, "bitrate=N/A"); + av_bprintf(&buf_script, "bitrate=N/A\n"); + }else{ + av_bprintf(&buf, "bitrate=%6.1fkbits/s", bitrate); + av_bprintf(&buf_script, "bitrate=%6.1fkbits/s\n", bitrate); } if (total_size < 0) av_bprintf(&buf_script, "total_size=N/A\n"); @@ -1619,7 +611,7 @@ static void print_report(int is_last_report, int64_t timer_start, int64_t cur_ti } else { av_bprintf(&buf_script, "out_time_us=%"PRId64"\n", pts); av_bprintf(&buf_script, "out_time_ms=%"PRId64"\n", pts); - av_bprintf(&buf_script, "out_time=%s%02d:%02d:%02d.%06d\n", + av_bprintf(&buf_script, "out_time=%s%02"PRId64":%02d:%02d.%06d\n", hours_sign, hours, mins, secs, us); } @@ -1634,1486 +626,246 @@ static void print_report(int is_last_report, int64_t timer_start, int64_t cur_ti } else { av_bprintf(&buf, " speed=%4.3gx", speed); av_bprintf(&buf_script, "speed=%4.3gx\n", speed); - } - - if (print_stats || is_last_report) { - const char end = is_last_report ? '\n' : '\r'; - if (print_stats==1 && AV_LOG_INFO > av_log_get_level()) { - fprintf(stderr, "%s %c", buf.str, end); - } else - av_log(NULL, AV_LOG_INFO, "%s %c", buf.str, end); - - fflush(stderr); - } - av_bprint_finalize(&buf, NULL); - - if (progress_avio) { - av_bprintf(&buf_script, "progress=%s\n", - is_last_report ? "end" : "continue"); - avio_write(progress_avio, buf_script.str, - FFMIN(buf_script.len, buf_script.size - 1)); - avio_flush(progress_avio); - av_bprint_finalize(&buf_script, NULL); - if (is_last_report) { - if ((ret = avio_closep(&progress_avio)) < 0) - av_log(NULL, AV_LOG_ERROR, - "Error closing progress log, loss of information possible: %s\n", av_err2str(ret)); - } - } - - first_report = 0; - - if (is_last_report) - print_final_stats(total_size); -} - -static int ifilter_parameters_from_codecpar(InputFilter *ifilter, AVCodecParameters *par) -{ - int ret; - - // We never got any input. Set a fake format, which will - // come from libavformat. - ifilter->format = par->format; - ifilter->sample_rate = par->sample_rate; - ifilter->width = par->width; - ifilter->height = par->height; - ifilter->sample_aspect_ratio = par->sample_aspect_ratio; - ret = av_channel_layout_copy(&ifilter->ch_layout, &par->ch_layout); - if (ret < 0) - return ret; - - return 0; -} - -static void flush_encoders(void) -{ - int ret; - - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { - OutputFile *of = output_files[ost->file_index]; - if (ost->sq_idx_encode >= 0) - sq_send(of->sq_encode, ost->sq_idx_encode, SQFRAME(NULL)); - } - - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { - AVCodecContext *enc = ost->enc_ctx; - OutputFile *of = output_files[ost->file_index]; - - if (!enc) - continue; - - // Try to enable encoding with no input frames. - // Maybe we should just let encoding fail instead. - if (!ost->initialized) { - FilterGraph *fg = ost->filter->graph; - - av_log(NULL, AV_LOG_WARNING, - "Finishing stream %d:%d without any data written to it.\n", - ost->file_index, ost->st->index); - - if (ost->filter && !fg->graph) { - int x; - for (x = 0; x < fg->nb_inputs; x++) { - InputFilter *ifilter = fg->inputs[x]; - if (ifilter->format < 0 && - ifilter_parameters_from_codecpar(ifilter, ifilter->ist->par) < 0) { - av_log(NULL, AV_LOG_ERROR, "Error copying paramerets from input stream\n"); - exit_program(1); - } - } - - if (!ifilter_has_all_input_formats(fg)) - continue; - - ret = configure_filtergraph(fg); - if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Error configuring filter graph\n"); - exit_program(1); - } - - of_output_packet(of, ost->pkt, ost, 1); - } - - init_output_stream_wrapper(ost, NULL, 1); - } - - if (enc->codec_type != AVMEDIA_TYPE_VIDEO && enc->codec_type != AVMEDIA_TYPE_AUDIO) - continue; - - ret = submit_encode_frame(of, ost, NULL); - if (ret != AVERROR_EOF) - exit_program(1); - } -} - -/* - * Check whether a packet from ist should be written into ost at this time - */ -static int check_output_constraints(InputStream *ist, OutputStream *ost) -{ - OutputFile *of = output_files[ost->file_index]; - - if (ost->ist != ist) - return 0; - - if (ost->finished & MUXER_FINISHED) - return 0; - - if (of->start_time != AV_NOPTS_VALUE && ist->pts < of->start_time) - return 0; - - return 1; -} - -static void do_streamcopy(InputStream *ist, OutputStream *ost, const AVPacket *pkt) -{ - OutputFile *of = output_files[ost->file_index]; - InputFile *f = input_files [ist->file_index]; - int64_t start_time = (of->start_time == AV_NOPTS_VALUE) ? 0 : of->start_time; - int64_t ost_tb_start_time = av_rescale_q(start_time, AV_TIME_BASE_Q, ost->mux_timebase); - AVPacket *opkt = ost->pkt; - - av_packet_unref(opkt); - // EOF: flush output bitstream filters. - if (!pkt) { - of_output_packet(of, opkt, ost, 1); - return; - } - - if (!ost->streamcopy_started && !(pkt->flags & AV_PKT_FLAG_KEY) && - !ost->copy_initial_nonkeyframes) - return; - - if (!ost->streamcopy_started && !ost->copy_prior_start) { - if (pkt->pts == AV_NOPTS_VALUE ? - ist->pts < ost->ts_copy_start : - pkt->pts < av_rescale_q(ost->ts_copy_start, AV_TIME_BASE_Q, ist->st->time_base)) - return; - } - - if (of->recording_time != INT64_MAX && - ist->pts >= of->recording_time + start_time) { - close_output_stream(ost); - return; - } - - if (f->recording_time != INT64_MAX) { - start_time = 0; - if (copy_ts) { - start_time += f->start_time != AV_NOPTS_VALUE ? f->start_time : 0; - start_time += start_at_zero ? 0 : f->start_time_effective; - } - if (ist->pts >= f->recording_time + start_time) { - close_output_stream(ost); - return; - } - } - - if (av_packet_ref(opkt, pkt) < 0) - exit_program(1); - - if (pkt->pts != AV_NOPTS_VALUE) - opkt->pts = av_rescale_q(pkt->pts, ist->st->time_base, ost->mux_timebase) - ost_tb_start_time; - - if (pkt->dts == AV_NOPTS_VALUE) { - opkt->dts = av_rescale_q(ist->dts, AV_TIME_BASE_Q, ost->mux_timebase); - } else if (ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { - int duration = av_get_audio_frame_duration2(ist->par, pkt->size); - if(!duration) - duration = ist->par->frame_size; - opkt->dts = av_rescale_delta(ist->st->time_base, pkt->dts, - (AVRational){1, ist->par->sample_rate}, duration, - &ist->filter_in_rescale_delta_last, ost->mux_timebase); - /* dts will be set immediately afterwards to what pts is now */ - opkt->pts = opkt->dts - ost_tb_start_time; - } else - opkt->dts = av_rescale_q(pkt->dts, ist->st->time_base, ost->mux_timebase); - opkt->dts -= ost_tb_start_time; - - opkt->duration = av_rescale_q(pkt->duration, ist->st->time_base, ost->mux_timebase); - - of_output_packet(of, opkt, ost, 0); - - ost->streamcopy_started = 1; -} - -static void check_decode_result(InputStream *ist, int *got_output, int ret) -{ - if (*got_output || ret<0) - decode_error_stat[ret<0] ++; - - if (ret < 0 && exit_on_error) - exit_program(1); - - if (*got_output && ist) { - if (ist->decoded_frame->decode_error_flags || (ist->decoded_frame->flags & AV_FRAME_FLAG_CORRUPT)) { - av_log(NULL, exit_on_error ? AV_LOG_FATAL : AV_LOG_WARNING, - "%s: corrupt decoded frame in stream %d\n", input_files[ist->file_index]->ctx->url, ist->st->index); - if (exit_on_error) - exit_program(1); - } - } -} - -// Filters can be configured only if the formats of all inputs are known. -static int ifilter_has_all_input_formats(FilterGraph *fg) -{ - int i; - for (i = 0; i < fg->nb_inputs; i++) { - if (fg->inputs[i]->format < 0 && (fg->inputs[i]->type == AVMEDIA_TYPE_AUDIO || - fg->inputs[i]->type == AVMEDIA_TYPE_VIDEO)) - return 0; - } - return 1; -} - -static int ifilter_send_frame(InputFilter *ifilter, AVFrame *frame, int keep_reference) -{ - FilterGraph *fg = ifilter->graph; - AVFrameSideData *sd; - int need_reinit, ret; - int buffersrc_flags = AV_BUFFERSRC_FLAG_PUSH; - - if (keep_reference) - buffersrc_flags |= AV_BUFFERSRC_FLAG_KEEP_REF; - - /* determine if the parameters for this input changed */ - need_reinit = ifilter->format != frame->format; - - switch (ifilter->ist->par->codec_type) { - case AVMEDIA_TYPE_AUDIO: - need_reinit |= ifilter->sample_rate != frame->sample_rate || - av_channel_layout_compare(&ifilter->ch_layout, &frame->ch_layout); - break; - case AVMEDIA_TYPE_VIDEO: - need_reinit |= ifilter->width != frame->width || - ifilter->height != frame->height; - break; - } - - if (!ifilter->ist->reinit_filters && fg->graph) - need_reinit = 0; - - if (!!ifilter->hw_frames_ctx != !!frame->hw_frames_ctx || - (ifilter->hw_frames_ctx && ifilter->hw_frames_ctx->data != frame->hw_frames_ctx->data)) - need_reinit = 1; - - if (sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX)) { - if (!ifilter->displaymatrix || memcmp(sd->data, ifilter->displaymatrix, sizeof(int32_t) * 9)) - need_reinit = 1; - } else if (ifilter->displaymatrix) - need_reinit = 1; - - if (need_reinit) { - ret = ifilter_parameters_from_frame(ifilter, frame); - if (ret < 0) - return ret; - } - - /* (re)init the graph if possible, otherwise buffer the frame and return */ - if (need_reinit || !fg->graph) { - if (!ifilter_has_all_input_formats(fg)) { - AVFrame *tmp = av_frame_clone(frame); - if (!tmp) - return AVERROR(ENOMEM); - - ret = av_fifo_write(ifilter->frame_queue, &tmp, 1); - if (ret < 0) - av_frame_free(&tmp); - - return ret; - } - - ret = reap_filters(1); - if (ret < 0 && ret != AVERROR_EOF) { - av_log(NULL, AV_LOG_ERROR, "Error while filtering: %s\n", av_err2str(ret)); - return ret; - } - - ret = configure_filtergraph(fg); - if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Error reinitializing filters!\n"); - return ret; - } - } - - ret = av_buffersrc_add_frame_flags(ifilter->filter, frame, buffersrc_flags); - if (ret < 0) { - if (ret != AVERROR_EOF) - av_log(NULL, AV_LOG_ERROR, "Error while filtering: %s\n", av_err2str(ret)); - return ret; - } - - return 0; -} - -static int ifilter_send_eof(InputFilter *ifilter, int64_t pts) -{ - int ret; - - ifilter->eof = 1; - - if (ifilter->filter) { - ret = av_buffersrc_close(ifilter->filter, pts, AV_BUFFERSRC_FLAG_PUSH); - if (ret < 0) - return ret; - } else { - // the filtergraph was never configured - if (ifilter->format < 0) { - ret = ifilter_parameters_from_codecpar(ifilter, ifilter->ist->par); - if (ret < 0) - return ret; - } - if (ifilter->format < 0 && (ifilter->type == AVMEDIA_TYPE_AUDIO || ifilter->type == AVMEDIA_TYPE_VIDEO)) { - av_log(NULL, AV_LOG_ERROR, "Cannot determine format of input stream %d:%d after EOF\n", ifilter->ist->file_index, ifilter->ist->st->index); - return AVERROR_INVALIDDATA; - } - } - - return 0; -} - -// This does not quite work like avcodec_decode_audio4/avcodec_decode_video2. -// There is the following difference: if you got a frame, you must call -// it again with pkt=NULL. pkt==NULL is treated differently from pkt->size==0 -// (pkt==NULL means get more output, pkt->size==0 is a flush/drain packet) -static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *pkt) -{ - int ret; - - *got_frame = 0; - - if (pkt) { - ret = avcodec_send_packet(avctx, pkt); - // In particular, we don't expect AVERROR(EAGAIN), because we read all - // decoded frames with avcodec_receive_frame() until done. - if (ret < 0 && ret != AVERROR_EOF) - return ret; - } - - ret = avcodec_receive_frame(avctx, frame); - if (ret < 0 && ret != AVERROR(EAGAIN)) - return ret; - if (ret >= 0) - *got_frame = 1; - - return 0; -} - -static int send_frame_to_filters(InputStream *ist, AVFrame *decoded_frame) -{ - int i, ret; - - av_assert1(ist->nb_filters > 0); /* ensure ret is initialized */ - for (i = 0; i < ist->nb_filters; i++) { - ret = ifilter_send_frame(ist->filters[i], decoded_frame, i < ist->nb_filters - 1); - if (ret == AVERROR_EOF) - ret = 0; /* ignore */ - if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, - "Failed to inject frame into filter network: %s\n", av_err2str(ret)); - break; - } - } - return ret; -} - -static int decode_audio(InputStream *ist, AVPacket *pkt, int *got_output, - int *decode_failed) -{ - AVFrame *decoded_frame = ist->decoded_frame; - AVCodecContext *avctx = ist->dec_ctx; - int ret, err = 0; - AVRational decoded_frame_tb; - - update_benchmark(NULL); - ret = decode(avctx, decoded_frame, got_output, pkt); - update_benchmark("decode_audio %d.%d", ist->file_index, ist->st->index); - if (ret < 0) - *decode_failed = 1; - - if (ret != AVERROR_EOF) - check_decode_result(ist, got_output, ret); - - if (!*got_output || ret < 0) - return ret; - - ist->samples_decoded += decoded_frame->nb_samples; - ist->frames_decoded++; - - /* increment next_dts to use for the case where the input stream does not - have timestamps or there are multiple frames in the packet */ - ist->next_pts += ((int64_t)AV_TIME_BASE * decoded_frame->nb_samples) / - decoded_frame->sample_rate; - ist->next_dts += ((int64_t)AV_TIME_BASE * decoded_frame->nb_samples) / - decoded_frame->sample_rate; - - if (decoded_frame->pts != AV_NOPTS_VALUE) { - decoded_frame_tb = ist->st->time_base; - } else if (pkt && pkt->pts != AV_NOPTS_VALUE) { - decoded_frame->pts = pkt->pts; - decoded_frame_tb = ist->st->time_base; - }else { - decoded_frame->pts = ist->dts; - decoded_frame_tb = AV_TIME_BASE_Q; - } - if (pkt && pkt->duration && ist->prev_pkt_pts != AV_NOPTS_VALUE && - pkt->pts != AV_NOPTS_VALUE && pkt->pts - ist->prev_pkt_pts > pkt->duration) - ist->filter_in_rescale_delta_last = AV_NOPTS_VALUE; - if (pkt) - ist->prev_pkt_pts = pkt->pts; - if (decoded_frame->pts != AV_NOPTS_VALUE) - decoded_frame->pts = av_rescale_delta(decoded_frame_tb, decoded_frame->pts, - (AVRational){1, decoded_frame->sample_rate}, - decoded_frame->nb_samples, - &ist->filter_in_rescale_delta_last, - (AVRational){1, decoded_frame->sample_rate}); - ist->nb_samples = decoded_frame->nb_samples; - err = send_frame_to_filters(ist, decoded_frame); - - av_frame_unref(decoded_frame); - return err < 0 ? err : ret; -} - -static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output, int64_t *duration_pts, int eof, - int *decode_failed) -{ - AVFrame *decoded_frame = ist->decoded_frame; - int i, ret = 0, err = 0; - int64_t best_effort_timestamp; - int64_t dts = AV_NOPTS_VALUE; - - // With fate-indeo3-2, we're getting 0-sized packets before EOF for some - // reason. This seems like a semi-critical bug. Don't trigger EOF, and - // skip the packet. - if (!eof && pkt && pkt->size == 0) - return 0; - - if (ist->dts != AV_NOPTS_VALUE) - dts = av_rescale_q(ist->dts, AV_TIME_BASE_Q, ist->st->time_base); - if (pkt) { - pkt->dts = dts; // ffmpeg.c probably shouldn't do this - } - - // The old code used to set dts on the drain packet, which does not work - // with the new API anymore. - if (eof) { - void *new = av_realloc_array(ist->dts_buffer, ist->nb_dts_buffer + 1, sizeof(ist->dts_buffer[0])); - if (!new) - return AVERROR(ENOMEM); - ist->dts_buffer = new; - ist->dts_buffer[ist->nb_dts_buffer++] = dts; - } - - update_benchmark(NULL); - ret = decode(ist->dec_ctx, decoded_frame, got_output, pkt); - update_benchmark("decode_video %d.%d", ist->file_index, ist->st->index); - if (ret < 0) - *decode_failed = 1; - - // The following line may be required in some cases where there is no parser - // or the parser does not has_b_frames correctly - if (ist->par->video_delay < ist->dec_ctx->has_b_frames) { - if (ist->dec_ctx->codec_id == AV_CODEC_ID_H264) { - ist->par->video_delay = ist->dec_ctx->has_b_frames; - } else - av_log(ist->dec_ctx, AV_LOG_WARNING, - "video_delay is larger in decoder than demuxer %d > %d.\n" - "If you want to help, upload a sample " - "of this file to https://streams.videolan.org/upload/ " - "and contact the ffmpeg-devel mailing list. (ffmpeg-devel@ffmpeg.org)\n", - ist->dec_ctx->has_b_frames, - ist->par->video_delay); - } - - if (ret != AVERROR_EOF) - check_decode_result(ist, got_output, ret); - - if (*got_output && ret >= 0) { - if (ist->dec_ctx->width != decoded_frame->width || - ist->dec_ctx->height != decoded_frame->height || - ist->dec_ctx->pix_fmt != decoded_frame->format) { - av_log(NULL, AV_LOG_DEBUG, "Frame parameters mismatch context %d,%d,%d != %d,%d,%d\n", - decoded_frame->width, - decoded_frame->height, - decoded_frame->format, - ist->dec_ctx->width, - ist->dec_ctx->height, - ist->dec_ctx->pix_fmt); - } - } - - if (!*got_output || ret < 0) - return ret; - - if(ist->top_field_first>=0) - decoded_frame->top_field_first = ist->top_field_first; - - ist->frames_decoded++; - - if (ist->hwaccel_retrieve_data && decoded_frame->format == ist->hwaccel_pix_fmt) { - err = ist->hwaccel_retrieve_data(ist->dec_ctx, decoded_frame); - if (err < 0) - goto fail; - } - - best_effort_timestamp= decoded_frame->best_effort_timestamp; - *duration_pts = decoded_frame->duration; - - if (ist->framerate.num) - best_effort_timestamp = ist->cfr_next_pts++; - - if (eof && best_effort_timestamp == AV_NOPTS_VALUE && ist->nb_dts_buffer > 0) { - best_effort_timestamp = ist->dts_buffer[0]; - - for (i = 0; i < ist->nb_dts_buffer - 1; i++) - ist->dts_buffer[i] = ist->dts_buffer[i + 1]; - ist->nb_dts_buffer--; - } - - if(best_effort_timestamp != AV_NOPTS_VALUE) { - int64_t ts = av_rescale_q(decoded_frame->pts = best_effort_timestamp, ist->st->time_base, AV_TIME_BASE_Q); - - if (ts != AV_NOPTS_VALUE) - ist->next_pts = ist->pts = ts; - } - - if (debug_ts) { - av_log(NULL, AV_LOG_INFO, "decoder -> ist_index:%d type:video " - "frame_pts:%s frame_pts_time:%s best_effort_ts:%"PRId64" best_effort_ts_time:%s keyframe:%d frame_type:%d time_base:%d/%d\n", - ist->st->index, av_ts2str(decoded_frame->pts), - av_ts2timestr(decoded_frame->pts, &ist->st->time_base), - best_effort_timestamp, - av_ts2timestr(best_effort_timestamp, &ist->st->time_base), - decoded_frame->key_frame, decoded_frame->pict_type, - ist->st->time_base.num, ist->st->time_base.den); - } - - if (ist->st->sample_aspect_ratio.num) - decoded_frame->sample_aspect_ratio = ist->st->sample_aspect_ratio; - - err = send_frame_to_filters(ist, decoded_frame); - -fail: - av_frame_unref(decoded_frame); - return err < 0 ? err : ret; -} - -static int transcode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output, - int *decode_failed) -{ - AVSubtitle subtitle; - int free_sub = 1; - int ret = avcodec_decode_subtitle2(ist->dec_ctx, - &subtitle, got_output, pkt); - - check_decode_result(NULL, got_output, ret); - - if (ret < 0 || !*got_output) { - *decode_failed = 1; - if (!pkt->size) - sub2video_flush(ist); - return ret; - } - - if (ist->fix_sub_duration) { - int end = 1; - if (ist->prev_sub.got_output) { - end = av_rescale(subtitle.pts - ist->prev_sub.subtitle.pts, - 1000, AV_TIME_BASE); - if (end < ist->prev_sub.subtitle.end_display_time) { - av_log(NULL, AV_LOG_DEBUG, - "Subtitle duration reduced from %"PRId32" to %d%s\n", - ist->prev_sub.subtitle.end_display_time, end, - end <= 0 ? ", dropping it" : ""); - ist->prev_sub.subtitle.end_display_time = end; - } - } - FFSWAP(int, *got_output, ist->prev_sub.got_output); - FFSWAP(int, ret, ist->prev_sub.ret); - FFSWAP(AVSubtitle, subtitle, ist->prev_sub.subtitle); - if (end <= 0) - goto out; - } - - if (!*got_output) - return ret; - - if (ist->sub2video.frame) { - sub2video_update(ist, INT64_MIN, &subtitle); - } else if (ist->nb_filters) { - if (!ist->sub2video.sub_queue) - ist->sub2video.sub_queue = av_fifo_alloc2(8, sizeof(AVSubtitle), AV_FIFO_FLAG_AUTO_GROW); - if (!ist->sub2video.sub_queue) - report_and_exit(AVERROR(ENOMEM)); - - ret = av_fifo_write(ist->sub2video.sub_queue, &subtitle, 1); - if (ret < 0) - exit_program(1); - free_sub = 0; - } - - if (!subtitle.num_rects) - goto out; - - ist->frames_decoded++; - - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { - if (!check_output_constraints(ist, ost) || !ost->enc_ctx - || ost->enc_ctx->codec_type != AVMEDIA_TYPE_SUBTITLE) - continue; - - do_subtitle_out(output_files[ost->file_index], ost, &subtitle); - } - -out: - if (free_sub) - avsubtitle_free(&subtitle); - return ret; -} - -static int send_filter_eof(InputStream *ist) -{ - int i, ret; - /* TODO keep pts also in stream time base to avoid converting back */ - int64_t pts = av_rescale_q_rnd(ist->pts, AV_TIME_BASE_Q, ist->st->time_base, - AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); - - for (i = 0; i < ist->nb_filters; i++) { - ret = ifilter_send_eof(ist->filters[i], pts); - if (ret < 0) - return ret; - } - return 0; -} - -/* pkt = NULL means EOF (needed to flush decoder buffers) */ -static int process_input_packet(InputStream *ist, const AVPacket *pkt, int no_eof) -{ - const AVCodecParameters *par = ist->par; - int ret = 0; - int repeating = 0; - int eof_reached = 0; - - AVPacket *avpkt = ist->pkt; - - if (!ist->saw_first_ts) { - ist->first_dts = - ist->dts = ist->st->avg_frame_rate.num ? - ist->dec_ctx->has_b_frames * AV_TIME_BASE / av_q2d(ist->st->avg_frame_rate) : 0; - ist->pts = 0; - if (pkt && pkt->pts != AV_NOPTS_VALUE && !ist->decoding_needed) { - ist->first_dts = - ist->dts += av_rescale_q(pkt->pts, ist->st->time_base, AV_TIME_BASE_Q); - ist->pts = ist->dts; //unused but better to set it to a value thats not totally wrong - } - ist->saw_first_ts = 1; - } - - if (ist->next_dts == AV_NOPTS_VALUE) - ist->next_dts = ist->dts; - if (ist->next_pts == AV_NOPTS_VALUE) - ist->next_pts = ist->pts; - - if (pkt) { - av_packet_unref(avpkt); - ret = av_packet_ref(avpkt, pkt); - if (ret < 0) - return ret; - } - - if (pkt && pkt->dts != AV_NOPTS_VALUE) { - ist->next_dts = ist->dts = av_rescale_q(pkt->dts, ist->st->time_base, AV_TIME_BASE_Q); - if (par->codec_type != AVMEDIA_TYPE_VIDEO || !ist->decoding_needed) - ist->next_pts = ist->pts = ist->dts; - } - - // while we have more to decode or while the decoder did output something on EOF - while (ist->decoding_needed) { - int64_t duration_dts = 0; - int64_t duration_pts = 0; - int got_output = 0; - int decode_failed = 0; - - ist->pts = ist->next_pts; - ist->dts = ist->next_dts; - - switch (par->codec_type) { - case AVMEDIA_TYPE_AUDIO: - ret = decode_audio (ist, repeating ? NULL : avpkt, &got_output, - &decode_failed); - av_packet_unref(avpkt); - break; - case AVMEDIA_TYPE_VIDEO: - ret = decode_video (ist, repeating ? NULL : avpkt, &got_output, &duration_pts, !pkt, - &decode_failed); - if (!repeating || !pkt || got_output) { - if (pkt && pkt->duration) { - duration_dts = av_rescale_q(pkt->duration, ist->st->time_base, AV_TIME_BASE_Q); - } else if(ist->dec_ctx->framerate.num != 0 && ist->dec_ctx->framerate.den != 0) { - int ticks = ist->last_pkt_repeat_pict >= 0 ? - ist->last_pkt_repeat_pict + 1 : - ist->dec_ctx->ticks_per_frame; - duration_dts = ((int64_t)AV_TIME_BASE * - ist->dec_ctx->framerate.den * ticks) / - ist->dec_ctx->framerate.num / ist->dec_ctx->ticks_per_frame; - } - - if(ist->dts != AV_NOPTS_VALUE && duration_dts) { - ist->next_dts += duration_dts; - }else - ist->next_dts = AV_NOPTS_VALUE; - } - - if (got_output) { - if (duration_pts > 0) { - ist->next_pts += av_rescale_q(duration_pts, ist->st->time_base, AV_TIME_BASE_Q); - } else { - ist->next_pts += duration_dts; - } - } - av_packet_unref(avpkt); - break; - case AVMEDIA_TYPE_SUBTITLE: - if (repeating) - break; - ret = transcode_subtitles(ist, avpkt, &got_output, &decode_failed); - if (!pkt && ret >= 0) - ret = AVERROR_EOF; - av_packet_unref(avpkt); - break; - default: - return -1; - } - - if (ret == AVERROR_EOF) { - eof_reached = 1; - break; - } - - if (ret < 0) { - if (decode_failed) { - av_log(NULL, AV_LOG_ERROR, "Error while decoding stream #%d:%d: %s\n", - ist->file_index, ist->st->index, av_err2str(ret)); - } else { - av_log(NULL, AV_LOG_FATAL, "Error while processing the decoded " - "data for stream #%d:%d\n", ist->file_index, ist->st->index); - } - if (!decode_failed || exit_on_error) - exit_program(1); - break; - } - - if (got_output) - ist->got_output = 1; - - if (!got_output) - break; - - // During draining, we might get multiple output frames in this loop. - // ffmpeg.c does not drain the filter chain on configuration changes, - // which means if we send multiple frames at once to the filters, and - // one of those frames changes configuration, the buffered frames will - // be lost. This can upset certain FATE tests. - // Decode only 1 frame per call on EOF to appease these FATE tests. - // The ideal solution would be to rewrite decoding to use the new - // decoding API in a better way. - if (!pkt) - break; - - repeating = 1; - } - - /* after flushing, send an EOF on all the filter inputs attached to the stream */ - /* except when looping we need to flush but not to send an EOF */ - if (!pkt && ist->decoding_needed && eof_reached && !no_eof) { - int ret = send_filter_eof(ist); - if (ret < 0) { - av_log(NULL, AV_LOG_FATAL, "Error marking filters as finished\n"); - exit_program(1); - } - } - - /* handle stream copy */ - if (!ist->decoding_needed && pkt) { - ist->dts = ist->next_dts; - switch (par->codec_type) { - case AVMEDIA_TYPE_AUDIO: - av_assert1(pkt->duration >= 0); - if (par->sample_rate) { - ist->next_dts += ((int64_t)AV_TIME_BASE * par->frame_size) / - par->sample_rate; - } else { - ist->next_dts += av_rescale_q(pkt->duration, ist->st->time_base, AV_TIME_BASE_Q); - } - break; - case AVMEDIA_TYPE_VIDEO: - if (ist->framerate.num) { - // TODO: Remove work-around for c99-to-c89 issue 7 - AVRational time_base_q = AV_TIME_BASE_Q; - int64_t next_dts = av_rescale_q(ist->next_dts, time_base_q, av_inv_q(ist->framerate)); - ist->next_dts = av_rescale_q(next_dts + 1, av_inv_q(ist->framerate), time_base_q); - } else if (pkt->duration) { - ist->next_dts += av_rescale_q(pkt->duration, ist->st->time_base, AV_TIME_BASE_Q); - } else if(ist->dec_ctx->framerate.num != 0) { - int ticks = ist->last_pkt_repeat_pict >= 0 ? - ist->last_pkt_repeat_pict + 1 : - ist->dec_ctx->ticks_per_frame; - ist->next_dts += ((int64_t)AV_TIME_BASE * - ist->dec_ctx->framerate.den * ticks) / - ist->dec_ctx->framerate.num / ist->dec_ctx->ticks_per_frame; - } - break; - } - ist->pts = ist->dts; - ist->next_pts = ist->next_dts; - } else if (!ist->decoding_needed) - eof_reached = 1; - - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { - if (!check_output_constraints(ist, ost) || ost->enc_ctx || - (!pkt && no_eof)) - continue; - - do_streamcopy(ist, ost, pkt); - } - - return !eof_reached; -} - -static enum AVPixelFormat get_format(AVCodecContext *s, const enum AVPixelFormat *pix_fmts) -{ - InputStream *ist = s->opaque; - const enum AVPixelFormat *p; - int ret; - - for (p = pix_fmts; *p != AV_PIX_FMT_NONE; p++) { - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(*p); - const AVCodecHWConfig *config = NULL; - int i; - - if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) - break; - - if (ist->hwaccel_id == HWACCEL_GENERIC || - ist->hwaccel_id == HWACCEL_AUTO) { - for (i = 0;; i++) { - config = avcodec_get_hw_config(s->codec, i); - if (!config) - break; - if (!(config->methods & - AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)) - continue; - if (config->pix_fmt == *p) - break; - } - } - if (config && config->device_type == ist->hwaccel_device_type) { - ret = hwaccel_decode_init(s); - if (ret < 0) { - if (ist->hwaccel_id == HWACCEL_GENERIC) { - av_log(NULL, AV_LOG_FATAL, - "%s hwaccel requested for input stream #%d:%d, " - "but cannot be initialized.\n", - av_hwdevice_get_type_name(config->device_type), - ist->file_index, ist->st->index); - return AV_PIX_FMT_NONE; - } - continue; - } - - ist->hwaccel_pix_fmt = *p; - break; - } - } - - return *p; -} - -static int init_input_stream(InputStream *ist, char *error, int error_len) -{ - int ret; - - if (ist->decoding_needed) { - const AVCodec *codec = ist->dec; - if (!codec) { - snprintf(error, error_len, "Decoder (codec %s) not found for input stream #%d:%d", - avcodec_get_name(ist->dec_ctx->codec_id), ist->file_index, ist->st->index); - return AVERROR(EINVAL); - } - - ist->dec_ctx->opaque = ist; - ist->dec_ctx->get_format = get_format; -#if LIBAVCODEC_VERSION_MAJOR < 60 - AV_NOWARN_DEPRECATED({ - ist->dec_ctx->thread_safe_callbacks = 1; - }) -#endif - - if (ist->dec_ctx->codec_id == AV_CODEC_ID_DVB_SUBTITLE && - (ist->decoding_needed & DECODING_FOR_OST)) { - av_dict_set(&ist->decoder_opts, "compute_edt", "1", AV_DICT_DONT_OVERWRITE); - if (ist->decoding_needed & DECODING_FOR_FILTER) - av_log(NULL, AV_LOG_WARNING, "Warning using DVB subtitles for filtering and output at the same time is not fully supported, also see -compute_edt [0|1]\n"); - } - - /* Useful for subtitles retiming by lavf (FIXME), skipping samples in - * audio, and video decoders such as cuvid or mediacodec */ - ist->dec_ctx->pkt_timebase = ist->st->time_base; - - if (!av_dict_get(ist->decoder_opts, "threads", NULL, 0)) - av_dict_set(&ist->decoder_opts, "threads", "auto", 0); - /* Attached pics are sparse, therefore we would not want to delay their decoding till EOF. */ - if (ist->st->disposition & AV_DISPOSITION_ATTACHED_PIC) - av_dict_set(&ist->decoder_opts, "threads", "1", 0); - - ret = hw_device_setup_for_decode(ist); - if (ret < 0) { - snprintf(error, error_len, "Device setup failed for " - "decoder on input stream #%d:%d : %s", - ist->file_index, ist->st->index, av_err2str(ret)); - return ret; - } - - if ((ret = avcodec_open2(ist->dec_ctx, codec, &ist->decoder_opts)) < 0) { - if (ret == AVERROR_EXPERIMENTAL) - abort_codec_experimental(codec, 0); - - snprintf(error, error_len, - "Error while opening decoder for input stream " - "#%d:%d : %s", - ist->file_index, ist->st->index, av_err2str(ret)); - return ret; - } - assert_avoptions(ist->decoder_opts); - } - - ist->next_pts = AV_NOPTS_VALUE; - ist->next_dts = AV_NOPTS_VALUE; - - return 0; -} - -static int init_output_stream_streamcopy(OutputStream *ost) -{ - OutputFile *of = output_files[ost->file_index]; - InputStream *ist = ost->ist; - InputFile *ifile = input_files[ist->file_index]; - AVCodecParameters *par = ost->st->codecpar; - AVCodecContext *codec_ctx; - AVRational sar; - int i, ret; - uint32_t codec_tag = par->codec_tag; - - av_assert0(ist && !ost->filter); - - codec_ctx = avcodec_alloc_context3(NULL); - if (!codec_ctx) - return AVERROR(ENOMEM); - - ret = avcodec_parameters_to_context(codec_ctx, ist->par); - if (ret >= 0) - ret = av_opt_set_dict(codec_ctx, &ost->encoder_opts); - if (ret < 0) { - av_log(NULL, AV_LOG_FATAL, - "Error setting up codec context options.\n"); - avcodec_free_context(&codec_ctx); - return ret; - } - - ret = avcodec_parameters_from_context(par, codec_ctx); - avcodec_free_context(&codec_ctx); - if (ret < 0) { - av_log(NULL, AV_LOG_FATAL, - "Error getting reference codec parameters.\n"); - return ret; - } - - if (!codec_tag) { - unsigned int codec_tag_tmp; - if (!of->format->codec_tag || - av_codec_get_id (of->format->codec_tag, par->codec_tag) == par->codec_id || - !av_codec_get_tag2(of->format->codec_tag, par->codec_id, &codec_tag_tmp)) - codec_tag = par->codec_tag; - } - - par->codec_tag = codec_tag; - - if (!ost->frame_rate.num) - ost->frame_rate = ist->framerate; - - if (ost->frame_rate.num) - ost->st->avg_frame_rate = ost->frame_rate; - else - ost->st->avg_frame_rate = ist->st->avg_frame_rate; - - ret = avformat_transfer_internal_stream_timing_info(of->format, ost->st, ist->st, copy_tb); - if (ret < 0) - return ret; - - // copy timebase while removing common factors - if (ost->st->time_base.num <= 0 || ost->st->time_base.den <= 0) { - if (ost->frame_rate.num) - ost->st->time_base = av_inv_q(ost->frame_rate); - else - ost->st->time_base = av_add_q(av_stream_get_codec_timebase(ost->st), (AVRational){0, 1}); - } - - // copy estimated duration as a hint to the muxer - if (ost->st->duration <= 0 && ist->st->duration > 0) - ost->st->duration = av_rescale_q(ist->st->duration, ist->st->time_base, ost->st->time_base); - - if (!ost->copy_prior_start) { - ost->ts_copy_start = (of->start_time == AV_NOPTS_VALUE) ? - 0 : of->start_time; - if (copy_ts && ifile->start_time != AV_NOPTS_VALUE) { - ost->ts_copy_start = FFMAX(ost->ts_copy_start, - ifile->start_time + ifile->ts_offset); - } - } - - if (ist->st->nb_side_data) { - for (i = 0; i < ist->st->nb_side_data; i++) { - const AVPacketSideData *sd_src = &ist->st->side_data[i]; - uint8_t *dst_data; - - dst_data = av_stream_new_side_data(ost->st, sd_src->type, sd_src->size); - if (!dst_data) - return AVERROR(ENOMEM); - memcpy(dst_data, sd_src->data, sd_src->size); - } - } - -#if FFMPEG_ROTATION_METADATA - if (ost->rotate_overridden) { - uint8_t *sd = av_stream_new_side_data(ost->st, AV_PKT_DATA_DISPLAYMATRIX, - sizeof(int32_t) * 9); - if (sd) - av_display_rotation_set((int32_t *)sd, -ost->rotate_override_value); - } -#endif - - switch (par->codec_type) { - case AVMEDIA_TYPE_AUDIO: - if ((par->block_align == 1 || par->block_align == 1152 || par->block_align == 576) && - par->codec_id == AV_CODEC_ID_MP3) - par->block_align = 0; - if (par->codec_id == AV_CODEC_ID_AC3) - par->block_align = 0; - break; - case AVMEDIA_TYPE_VIDEO: - if (ost->frame_aspect_ratio.num) { // overridden by the -aspect cli option - sar = - av_mul_q(ost->frame_aspect_ratio, - (AVRational){ par->height, par->width }); - av_log(NULL, AV_LOG_WARNING, "Overriding aspect ratio " - "with stream copy may produce invalid files\n"); - } - else if (ist->st->sample_aspect_ratio.num) - sar = ist->st->sample_aspect_ratio; - else - sar = par->sample_aspect_ratio; - ost->st->sample_aspect_ratio = par->sample_aspect_ratio = sar; - ost->st->avg_frame_rate = ist->st->avg_frame_rate; - ost->st->r_frame_rate = ist->st->r_frame_rate; - break; - } - - ost->mux_timebase = ist->st->time_base; - - return 0; -} - -static void set_encoder_id(OutputFile *of, OutputStream *ost) -{ - const char *cname = ost->enc_ctx->codec->name; - uint8_t *encoder_string; - int encoder_string_len; - - if (av_dict_get(ost->st->metadata, "encoder", NULL, 0)) - return; - - encoder_string_len = sizeof(LIBAVCODEC_IDENT) + strlen(cname) + 2; - encoder_string = av_mallocz(encoder_string_len); - if (!encoder_string) - report_and_exit(AVERROR(ENOMEM)); - - if (!of->bitexact && !ost->bitexact) - av_strlcpy(encoder_string, LIBAVCODEC_IDENT " ", encoder_string_len); - else - av_strlcpy(encoder_string, "Lavc ", encoder_string_len); - av_strlcat(encoder_string, cname, encoder_string_len); - av_dict_set(&ost->st->metadata, "encoder", encoder_string, - AV_DICT_DONT_STRDUP_VAL | AV_DICT_DONT_OVERWRITE); -} + } -static void init_encoder_time_base(OutputStream *ost, AVRational default_time_base) -{ - InputStream *ist = ost->ist; - AVCodecContext *enc_ctx = ost->enc_ctx; + if (print_stats || is_last_report) { + const char end = is_last_report ? '\n' : '\r'; + if (print_stats==1 && AV_LOG_INFO > av_log_get_level()) { + fprintf(stderr, "%s %c", buf.str, end); + } else + av_log(NULL, AV_LOG_INFO, "%s %c", buf.str, end); - if (ost->enc_timebase.num > 0) { - enc_ctx->time_base = ost->enc_timebase; - return; + fflush(stderr); } + av_bprint_finalize(&buf, NULL); - if (ost->enc_timebase.num < 0) { - if (ist) { - enc_ctx->time_base = ist->st->time_base; - return; + if (progress_avio) { + av_bprintf(&buf_script, "progress=%s\n", + is_last_report ? "end" : "continue"); + avio_write(progress_avio, buf_script.str, + FFMIN(buf_script.len, buf_script.size - 1)); + avio_flush(progress_avio); + av_bprint_finalize(&buf_script, NULL); + if (is_last_report) { + if ((ret = avio_closep(&progress_avio)) < 0) + av_log(NULL, AV_LOG_ERROR, + "Error closing progress log, loss of information possible: %s\n", av_err2str(ret)); } - - av_log(NULL, AV_LOG_WARNING, - "Input stream data for output stream #%d:%d not available, " - "using default time base\n", ost->file_index, ost->index); } - enc_ctx->time_base = default_time_base; + first_report = 0; } -static int init_output_stream_encode(OutputStream *ost, AVFrame *frame) +int copy_av_subtitle(AVSubtitle *dst, const AVSubtitle *src) { - InputStream *ist = ost->ist; - AVCodecContext *enc_ctx = ost->enc_ctx; - AVCodecContext *dec_ctx = NULL; - OutputFile *of = output_files[ost->file_index]; - int ret; - - set_encoder_id(output_files[ost->file_index], ost); - - if (ist) { - dec_ctx = ist->dec_ctx; - } - - if (enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO) { - if (!ost->frame_rate.num) - ost->frame_rate = av_buffersink_get_frame_rate(ost->filter->filter); - if (!ost->frame_rate.num && !ost->max_frame_rate.num) { - ost->frame_rate = (AVRational){25, 1}; - av_log(NULL, AV_LOG_WARNING, - "No information " - "about the input framerate is available. Falling " - "back to a default value of 25fps for output stream #%d:%d. Use the -r option " - "if you want a different framerate.\n", - ost->file_index, ost->index); - } + int ret = AVERROR_BUG; + AVSubtitle tmp = { + .format = src->format, + .start_display_time = src->start_display_time, + .end_display_time = src->end_display_time, + .num_rects = 0, + .rects = NULL, + .pts = src->pts + }; + + if (!src->num_rects) + goto success; + + if (!(tmp.rects = av_calloc(src->num_rects, sizeof(*tmp.rects)))) + return AVERROR(ENOMEM); - if (ost->max_frame_rate.num && - (av_q2d(ost->frame_rate) > av_q2d(ost->max_frame_rate) || - !ost->frame_rate.den)) - ost->frame_rate = ost->max_frame_rate; + for (int i = 0; i < src->num_rects; i++) { + AVSubtitleRect *src_rect = src->rects[i]; + AVSubtitleRect *dst_rect; - if (enc_ctx->codec->supported_framerates && !ost->force_fps) { - int idx = av_find_nearest_q_idx(ost->frame_rate, enc_ctx->codec->supported_framerates); - ost->frame_rate = enc_ctx->codec->supported_framerates[idx]; - } - // reduce frame rate for mpeg4 to be within the spec limits - if (enc_ctx->codec_id == AV_CODEC_ID_MPEG4) { - av_reduce(&ost->frame_rate.num, &ost->frame_rate.den, - ost->frame_rate.num, ost->frame_rate.den, 65535); + if (!(dst_rect = tmp.rects[i] = av_mallocz(sizeof(*tmp.rects[0])))) { + ret = AVERROR(ENOMEM); + goto cleanup; } - } - - switch (enc_ctx->codec_type) { - case AVMEDIA_TYPE_AUDIO: - enc_ctx->sample_fmt = av_buffersink_get_format(ost->filter->filter); - enc_ctx->sample_rate = av_buffersink_get_sample_rate(ost->filter->filter); - ret = av_buffersink_get_ch_layout(ost->filter->filter, &enc_ctx->ch_layout); - if (ret < 0) - return ret; - if (ost->bits_per_raw_sample) - enc_ctx->bits_per_raw_sample = ost->bits_per_raw_sample; - else if (dec_ctx && ost->filter->graph->is_meta) - enc_ctx->bits_per_raw_sample = FFMIN(dec_ctx->bits_per_raw_sample, - av_get_bytes_per_sample(enc_ctx->sample_fmt) << 3); - - init_encoder_time_base(ost, av_make_q(1, enc_ctx->sample_rate)); - break; - - case AVMEDIA_TYPE_VIDEO: - init_encoder_time_base(ost, av_inv_q(ost->frame_rate)); - - if (!(enc_ctx->time_base.num && enc_ctx->time_base.den)) - enc_ctx->time_base = av_buffersink_get_time_base(ost->filter->filter); - if ( av_q2d(enc_ctx->time_base) < 0.001 && ost->vsync_method != VSYNC_PASSTHROUGH - && (ost->vsync_method == VSYNC_CFR || ost->vsync_method == VSYNC_VSCFR || - (ost->vsync_method == VSYNC_AUTO && !(of->format->flags & AVFMT_VARIABLE_FPS)))){ - av_log(NULL, AV_LOG_WARNING, "Frame rate very high for a muxer not efficiently supporting it.\n" - "Please consider specifying a lower framerate, a different muxer or " - "setting vsync/fps_mode to vfr\n"); - } + tmp.num_rects++; - enc_ctx->width = av_buffersink_get_w(ost->filter->filter); - enc_ctx->height = av_buffersink_get_h(ost->filter->filter); - enc_ctx->sample_aspect_ratio = ost->st->sample_aspect_ratio = - ost->frame_aspect_ratio.num ? // overridden by the -aspect cli option - av_mul_q(ost->frame_aspect_ratio, (AVRational){ enc_ctx->height, enc_ctx->width }) : - av_buffersink_get_sample_aspect_ratio(ost->filter->filter); - - enc_ctx->pix_fmt = av_buffersink_get_format(ost->filter->filter); - - if (ost->bits_per_raw_sample) - enc_ctx->bits_per_raw_sample = ost->bits_per_raw_sample; - else if (dec_ctx && ost->filter->graph->is_meta) - enc_ctx->bits_per_raw_sample = FFMIN(dec_ctx->bits_per_raw_sample, - av_pix_fmt_desc_get(enc_ctx->pix_fmt)->comp[0].depth); - - if (frame) { - enc_ctx->color_range = frame->color_range; - enc_ctx->color_primaries = frame->color_primaries; - enc_ctx->color_trc = frame->color_trc; - enc_ctx->colorspace = frame->colorspace; - enc_ctx->chroma_sample_location = frame->chroma_location; - } + dst_rect->type = src_rect->type; + dst_rect->flags = src_rect->flags; - enc_ctx->framerate = ost->frame_rate; + dst_rect->x = src_rect->x; + dst_rect->y = src_rect->y; + dst_rect->w = src_rect->w; + dst_rect->h = src_rect->h; + dst_rect->nb_colors = src_rect->nb_colors; - ost->st->avg_frame_rate = ost->frame_rate; + if (src_rect->text) + if (!(dst_rect->text = av_strdup(src_rect->text))) { + ret = AVERROR(ENOMEM); + goto cleanup; + } - // Field order: autodetection - if (frame) { - if (enc_ctx->flags & (AV_CODEC_FLAG_INTERLACED_DCT | AV_CODEC_FLAG_INTERLACED_ME) && - ost->top_field_first >= 0) - frame->top_field_first = !!ost->top_field_first; + if (src_rect->ass) + if (!(dst_rect->ass = av_strdup(src_rect->ass))) { + ret = AVERROR(ENOMEM); + goto cleanup; + } - if (frame->interlaced_frame) { - if (enc_ctx->codec->id == AV_CODEC_ID_MJPEG) - enc_ctx->field_order = frame->top_field_first ? AV_FIELD_TT:AV_FIELD_BB; - else - enc_ctx->field_order = frame->top_field_first ? AV_FIELD_TB:AV_FIELD_BT; - } else - enc_ctx->field_order = AV_FIELD_PROGRESSIVE; - } + for (int j = 0; j < 4; j++) { + // SUBTITLE_BITMAP images are special in the sense that they + // are like PAL8 images. first pointer to data, second to + // palette. This makes the size calculation match this. + size_t buf_size = src_rect->type == SUBTITLE_BITMAP && j == 1 ? + AVPALETTE_SIZE : + src_rect->h * src_rect->linesize[j]; - // Field order: override - if (ost->top_field_first == 0) { - enc_ctx->field_order = AV_FIELD_BB; - } else if (ost->top_field_first == 1) { - enc_ctx->field_order = AV_FIELD_TT; - } + if (!src_rect->data[j]) + continue; - break; - case AVMEDIA_TYPE_SUBTITLE: - enc_ctx->time_base = AV_TIME_BASE_Q; - if (!enc_ctx->width) { - enc_ctx->width = ost->ist->par->width; - enc_ctx->height = ost->ist->par->height; - } - if (dec_ctx && dec_ctx->subtitle_header) { - /* ASS code assumes this buffer is null terminated so add extra byte. */ - ost->enc_ctx->subtitle_header = av_mallocz(dec_ctx->subtitle_header_size + 1); - if (!ost->enc_ctx->subtitle_header) - return AVERROR(ENOMEM); - memcpy(ost->enc_ctx->subtitle_header, dec_ctx->subtitle_header, - dec_ctx->subtitle_header_size); - ost->enc_ctx->subtitle_header_size = dec_ctx->subtitle_header_size; - } - if (ist && ist->dec->type == AVMEDIA_TYPE_SUBTITLE && - enc_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) { - int input_props = 0, output_props = 0; - AVCodecDescriptor const *input_descriptor = - avcodec_descriptor_get(ist->dec->id); - AVCodecDescriptor const *output_descriptor = - avcodec_descriptor_get(ost->enc_ctx->codec_id); - if (input_descriptor) - input_props = input_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB); - if (output_descriptor) - output_props = output_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB); - if (input_props && output_props && input_props != output_props) { - av_log(NULL, AV_LOG_ERROR, - "Subtitle encoding currently only possible from text to text " - "or bitmap to bitmap"); - return AVERROR_INVALIDDATA; + if (!(dst_rect->data[j] = av_memdup(src_rect->data[j], buf_size))) { + ret = AVERROR(ENOMEM); + goto cleanup; } + dst_rect->linesize[j] = src_rect->linesize[j]; } - - break; - case AVMEDIA_TYPE_DATA: - break; - default: - abort(); - break; } - if (ost->bitexact) - enc_ctx->flags |= AV_CODEC_FLAG_BITEXACT; +success: + *dst = tmp; - if (ost->sq_idx_encode >= 0) - sq_set_tb(of->sq_encode, ost->sq_idx_encode, enc_ctx->time_base); + return 0; - ost->mux_timebase = enc_ctx->time_base; +cleanup: + avsubtitle_free(&tmp); - return 0; + return ret; } -static int init_output_stream(OutputStream *ost, AVFrame *frame, - char *error, int error_len) +static void subtitle_free(void *opaque, uint8_t *data) { - int ret = 0; - - if (ost->enc_ctx) { - const AVCodec *codec = ost->enc_ctx->codec; - InputStream *ist = ost->ist; - - ret = init_output_stream_encode(ost, frame); - if (ret < 0) - return ret; + AVSubtitle *sub = (AVSubtitle*)data; + avsubtitle_free(sub); + av_free(sub); +} - if (!av_dict_get(ost->encoder_opts, "threads", NULL, 0)) - av_dict_set(&ost->encoder_opts, "threads", "auto", 0); +int subtitle_wrap_frame(AVFrame *frame, AVSubtitle *subtitle, int copy) +{ + AVBufferRef *buf; + AVSubtitle *sub; + int ret; - ret = hw_device_setup_for_encode(ost); + if (copy) { + sub = av_mallocz(sizeof(*sub)); + ret = sub ? copy_av_subtitle(sub, subtitle) : AVERROR(ENOMEM); if (ret < 0) { - snprintf(error, error_len, "Device setup failed for " - "encoder on output stream #%d:%d : %s", - ost->file_index, ost->index, av_err2str(ret)); + av_freep(&sub); return ret; } + } else { + sub = av_memdup(subtitle, sizeof(*subtitle)); + if (!sub) + return AVERROR(ENOMEM); + memset(subtitle, 0, sizeof(*subtitle)); + } - if ((ret = avcodec_open2(ost->enc_ctx, codec, &ost->encoder_opts)) < 0) { - if (ret == AVERROR_EXPERIMENTAL) - abort_codec_experimental(codec, 1); - snprintf(error, error_len, - "Error while opening encoder for output stream #%d:%d - " - "maybe incorrect parameters such as bit_rate, rate, width or height", - ost->file_index, ost->index); - return ret; - } - if (codec->type == AVMEDIA_TYPE_AUDIO && - !(codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)) - av_buffersink_set_frame_size(ost->filter->filter, - ost->enc_ctx->frame_size); - assert_avoptions(ost->encoder_opts); - if (ost->enc_ctx->bit_rate && ost->enc_ctx->bit_rate < 1000 && - ost->enc_ctx->codec_id != AV_CODEC_ID_CODEC2 /* don't complain about 700 bit/s modes */) - av_log(NULL, AV_LOG_WARNING, "The bitrate parameter is set too low." - " It takes bits/s as argument, not kbits/s\n"); - - ret = avcodec_parameters_from_context(ost->st->codecpar, ost->enc_ctx); - if (ret < 0) { - av_log(NULL, AV_LOG_FATAL, - "Error initializing the output stream codec context.\n"); - exit_program(1); - } + buf = av_buffer_create((uint8_t*)sub, sizeof(*sub), + subtitle_free, NULL, 0); + if (!buf) { + avsubtitle_free(sub); + av_freep(&sub); + return AVERROR(ENOMEM); + } - if (ost->enc_ctx->nb_coded_side_data) { - int i; + frame->buf[0] = buf; - for (i = 0; i < ost->enc_ctx->nb_coded_side_data; i++) { - const AVPacketSideData *sd_src = &ost->enc_ctx->coded_side_data[i]; - uint8_t *dst_data; + return 0; +} - dst_data = av_stream_new_side_data(ost->st, sd_src->type, sd_src->size); - if (!dst_data) - return AVERROR(ENOMEM); - memcpy(dst_data, sd_src->data, sd_src->size); - } - } +int trigger_fix_sub_duration_heartbeat(OutputStream *ost, const AVPacket *pkt) +{ + OutputFile *of = output_files[ost->file_index]; + int64_t signal_pts = av_rescale_q(pkt->pts, pkt->time_base, + AV_TIME_BASE_Q); - /* - * Add global input side data. For now this is naive, and copies it - * from the input stream's global side data. All side data should - * really be funneled over AVFrame and libavfilter, then added back to - * packet side data, and then potentially using the first packet for - * global side data. - */ - if (ist) { - int i; - for (i = 0; i < ist->st->nb_side_data; i++) { - AVPacketSideData *sd = &ist->st->side_data[i]; - if (sd->type != AV_PKT_DATA_CPB_PROPERTIES) { - uint8_t *dst = av_stream_new_side_data(ost->st, sd->type, sd->size); - if (!dst) - return AVERROR(ENOMEM); - memcpy(dst, sd->data, sd->size); - if (ist->autorotate && sd->type == AV_PKT_DATA_DISPLAYMATRIX) - av_display_rotation_set((int32_t *)dst, 0); - } - } - } + if (!ost->fix_sub_duration_heartbeat || !(pkt->flags & AV_PKT_FLAG_KEY)) + // we are only interested in heartbeats on streams configured, and + // only on random access points. + return 0; - // copy timebase while removing common factors - if (ost->st->time_base.num <= 0 || ost->st->time_base.den <= 0) - ost->st->time_base = av_add_q(ost->enc_ctx->time_base, (AVRational){0, 1}); + for (int i = 0; i < of->nb_streams; i++) { + OutputStream *iter_ost = of->streams[i]; + InputStream *ist = iter_ost->ist; + int ret = AVERROR_BUG; + + if (iter_ost == ost || !ist || !ist->decoding_needed || + ist->dec_ctx->codec_type != AVMEDIA_TYPE_SUBTITLE) + // We wish to skip the stream that causes the heartbeat, + // output streams without an input stream, streams not decoded + // (as fix_sub_duration is only done for decoded subtitles) as + // well as non-subtitle streams. + continue; - // copy estimated duration as a hint to the muxer - if (ost->st->duration <= 0 && ist && ist->st->duration > 0) - ost->st->duration = av_rescale_q(ist->st->duration, ist->st->time_base, ost->st->time_base); - } else if (ost->ist) { - ret = init_output_stream_streamcopy(ost); - if (ret < 0) + if ((ret = fix_sub_duration_heartbeat(ist, signal_pts)) < 0) return ret; } - ret = of_stream_init(output_files[ost->file_index], ost); - if (ret < 0) - return ret; - - return ret; + return 0; } -static int transcode_init(void) +/* pkt = NULL means EOF (needed to flush decoder buffers) */ +static int process_input_packet(InputStream *ist, const AVPacket *pkt, int no_eof) { + InputFile *f = input_files[ist->file_index]; + int64_t dts_est = AV_NOPTS_VALUE; int ret = 0; - char error[1024] = {0}; - - /* init framerate emulation */ - for (int i = 0; i < nb_input_files; i++) { - InputFile *ifile = input_files[i]; - if (ifile->readrate || ifile->rate_emu) - for (int j = 0; j < ifile->nb_streams; j++) - ifile->streams[j]->start = av_gettime_relative(); + int eof_reached = 0; + int duration_exceeded; + + if (ist->decoding_needed) { + ret = dec_packet(ist, pkt, no_eof); + if (ret < 0 && ret != AVERROR_EOF) + return ret; + } + if (ret == AVERROR_EOF || (!pkt && !ist->decoding_needed)) + eof_reached = 1; + + if (pkt && pkt->opaque_ref) { + DemuxPktData *pd = (DemuxPktData*)pkt->opaque_ref->data; + dts_est = pd->dts_est; } - /* init input streams */ - for (InputStream *ist = ist_iter(NULL); ist; ist = ist_iter(ist)) - if ((ret = init_input_stream(ist, error, sizeof(error))) < 0) - goto dump_format; - - /* - * initialize stream copy and subtitle/data streams. - * Encoded AVFrame based streams will get initialized as follows: - * - when the first AVFrame is received in do_video_out - * - just before the first AVFrame is received in either transcode_step - * or reap_filters due to us requiring the filter chain buffer sink - * to be configured with the correct audio frame size, which is only - * known after the encoder is initialized. - */ - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { - if (ost->enc_ctx && - (ost->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || - ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)) + duration_exceeded = 0; + if (f->recording_time != INT64_MAX) { + int64_t start_time = 0; + if (copy_ts) { + start_time += f->start_time != AV_NOPTS_VALUE ? f->start_time : 0; + start_time += start_at_zero ? 0 : f->start_time_effective; + } + if (dts_est >= f->recording_time + start_time) + duration_exceeded = 1; + } + + for (int oidx = 0; oidx < ist->nb_outputs; oidx++) { + OutputStream *ost = ist->outputs[oidx]; + if (ost->enc || (!pkt && no_eof)) + continue; + + if (duration_exceeded) { + close_output_stream(ost); continue; + } - ret = init_output_stream_wrapper(ost, NULL, 0); + ret = of_streamcopy(ost, pkt, dts_est); if (ret < 0) - goto dump_format; + return ret; } - /* discard unused programs */ - for (int i = 0; i < nb_input_files; i++) { - InputFile *ifile = input_files[i]; - for (int j = 0; j < ifile->ctx->nb_programs; j++) { - AVProgram *p = ifile->ctx->programs[j]; - int discard = AVDISCARD_ALL; - - for (int k = 0; k < p->nb_stream_indexes; k++) - if (!ifile->streams[p->stream_index[k]]->discard) { - discard = AVDISCARD_DEFAULT; - break; - } - p->discard = discard; - } - } + return !eof_reached; +} - dump_format: - /* dump the stream mapping */ +static void print_stream_maps(void) +{ av_log(NULL, AV_LOG_INFO, "Stream mapping:\n"); for (InputStream *ist = ist_iter(NULL); ist; ist = ist_iter(ist)) { for (int j = 0; j < ist->nb_filters; j++) { if (!filtergraph_is_simple(ist->filters[j]->graph)) { av_log(NULL, AV_LOG_INFO, " Stream #%d:%d (%s) -> %s", - ist->file_index, ist->st->index, ist->dec ? ist->dec->name : "?", + ist->file_index, ist->index, ist->dec ? ist->dec->name : "?", ist->filters[j]->name); if (nb_filtergraphs > 1) av_log(NULL, AV_LOG_INFO, " (graph %d)", ist->filters[j]->graph->index); @@ -3143,7 +895,7 @@ static int transcode_init(void) av_log(NULL, AV_LOG_INFO, " Stream #%d:%d -> #%d:%d", ost->ist->file_index, - ost->ist->st->index, + ost->ist->index, ost->file_index, ost->index); if (ost->enc_ctx) { @@ -3180,36 +932,16 @@ static int transcode_init(void) av_log(NULL, AV_LOG_INFO, " (copy)"); av_log(NULL, AV_LOG_INFO, "\n"); } - - if (ret) { - av_log(NULL, AV_LOG_ERROR, "%s\n", error); - return ret; - } - - atomic_store(&transcode_init_done, 1); - - return 0; -} - -/* Return 1 if there remain streams where more output is wanted, 0 otherwise. */ -static int need_output(void) -{ - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { - if (ost->finished) - continue; - - return 1; - } - - return 0; } /** * Select the output stream to process. * - * @return selected output stream, or NULL if none available + * @retval 0 an output stream was selected + * @retval AVERROR(EAGAIN) need to wait until more input is available + * @retval AVERROR_EOF no more streams need output */ -static OutputStream *choose_output(void) +static int choose_output(OutputStream **post) { int64_t opts_min = INT64_MAX; OutputStream *ost_min = NULL; @@ -3217,26 +949,26 @@ static OutputStream *choose_output(void) for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { int64_t opts; - if (ost->filter && ost->last_filter_pts != AV_NOPTS_VALUE) { - opts = ost->last_filter_pts; + if (ost->filter && ost->filter->last_pts != AV_NOPTS_VALUE) { + opts = ost->filter->last_pts; } else { opts = ost->last_mux_dts == AV_NOPTS_VALUE ? INT64_MIN : ost->last_mux_dts; - if (ost->last_mux_dts == AV_NOPTS_VALUE) - av_log(NULL, AV_LOG_DEBUG, - "cur_dts is invalid st:%d (%d) [init:%d i_done:%d finish:%d] (this is harmless if it occurs once at the start per stream)\n", - ost->st->index, ost->st->id, ost->initialized, ost->inputs_done, ost->finished); } - if (!ost->initialized && !ost->inputs_done) - return ost->unavailable ? NULL : ost; - + if (!ost->initialized && !ost->inputs_done && !ost->finished) { + ost_min = ost; + break; + } if (!ost->finished && opts < opts_min) { opts_min = opts; - ost_min = ost->unavailable ? NULL : ost; + ost_min = ost; } } - return ost_min; + if (!ost_min) + return AVERROR_EOF; + *post = ost_min; + return ost_min->unavailable ? AVERROR(EAGAIN) : 0; } static void set_tty_echo(int on) @@ -3269,7 +1001,6 @@ static int check_keyboard_interaction(int64_t cur_time) } if (key == '+') av_log_set_level(av_log_get_level()+10); if (key == '-') av_log_set_level(av_log_get_level()-10); - if (key == 's') qp_hist ^= 1; if (key == 'c' || key == 'C'){ char buf[4096], target[64], command[256], arg[256] = {0}; double time; @@ -3310,40 +1041,6 @@ static int check_keyboard_interaction(int64_t cur_time) "only %d given in string '%s'\n", n, buf); } } - if (key == 'd' || key == 'D'){ - int debug=0; - if(key == 'D') { - InputStream *ist = ist_iter(NULL); - - if (ist) - debug = ist->dec_ctx->debug << 1; - - if(!debug) debug = 1; - while (debug & FF_DEBUG_DCT_COEFF) //unsupported, would just crash - debug += debug; - }else{ - char buf[32]; - int k = 0; - i = 0; - set_tty_echo(1); - while ((k = read_key()) != '\n' && k != '\r' && i < sizeof(buf)-1) - if (k > 0) - buf[i++] = k; - buf[i] = 0; - set_tty_echo(0); - fprintf(stderr, "\n"); - if (k <= 0 || sscanf(buf, "%d", &debug)!=1) - fprintf(stderr,"error parsing debug value\n"); - } - for (InputStream *ist = ist_iter(NULL); ist; ist = ist_iter(ist)) - ist->dec_ctx->debug = debug; - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { - if (ost->enc_ctx) - ost->enc_ctx->debug = debug; - } - if(debug) av_log_set_level(AV_LOG_DEBUG); - fprintf(stderr,"debug=%d\n", debug); - } if (key == '?'){ fprintf(stderr, "key function\n" "? show this help\n" @@ -3351,7 +1048,6 @@ static int check_keyboard_interaction(int64_t cur_time) "- decrease verbosity\n" "c Send command to first matching filter supporting it\n" "C Send/Queue command to all matching filters\n" - "D cycle through available debug modes\n" "h dump packets/hex press to cycle through the 3 states\n" "q quit\n" "s Show QP histogram\n" @@ -3360,14 +1056,6 @@ static int check_keyboard_interaction(int64_t cur_time) return 0; } -static int got_eagain(void) -{ - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) - if (ost->unavailable) - return 1; - return 0; -} - static void reset_eagain(void) { int i; @@ -3381,115 +1069,12 @@ static void decode_flush(InputFile *ifile) { for (int i = 0; i < ifile->nb_streams; i++) { InputStream *ist = ifile->streams[i]; - int ret; - if (!ist->processing_needed) + if (ist->discard || !ist->decoding_needed) continue; - do { - ret = process_input_packet(ist, NULL, 1); - } while (ret > 0); - - if (ist->decoding_needed) { - /* report last frame duration to the demuxer thread */ - if (ist->par->codec_type == AVMEDIA_TYPE_AUDIO) { - LastFrameDuration dur; - - dur.stream_idx = i; - dur.duration = av_rescale_q(ist->nb_samples, - (AVRational){ 1, ist->dec_ctx->sample_rate}, - ist->st->time_base); - - av_thread_message_queue_send(ifile->audio_duration_queue, &dur, 0); - } - - avcodec_flush_buffers(ist->dec_ctx); - } - } -} - -static void ts_discontinuity_detect(InputFile *ifile, InputStream *ist, - AVPacket *pkt) -{ - const int fmt_is_discont = ifile->ctx->iformat->flags & AVFMT_TS_DISCONT; - int disable_discontinuity_correction = copy_ts; - int64_t pkt_dts = av_rescale_q_rnd(pkt->dts, ist->st->time_base, AV_TIME_BASE_Q, - AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); - - if (copy_ts && ist->next_dts != AV_NOPTS_VALUE && - fmt_is_discont && ist->st->pts_wrap_bits < 60) { - int64_t wrap_dts = av_rescale_q_rnd(pkt->dts + (1LL<st->pts_wrap_bits), - ist->st->time_base, AV_TIME_BASE_Q, - AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX); - if (FFABS(wrap_dts - ist->next_dts) < FFABS(pkt_dts - ist->next_dts)/10) - disable_discontinuity_correction = 0; - } - - if (ist->next_dts != AV_NOPTS_VALUE && !disable_discontinuity_correction) { - int64_t delta = pkt_dts - ist->next_dts; - if (fmt_is_discont) { - if (FFABS(delta) > 1LL * dts_delta_threshold * AV_TIME_BASE || - pkt_dts + AV_TIME_BASE/10 < FFMAX(ist->pts, ist->dts)) { - ifile->ts_offset_discont -= delta; - av_log(NULL, AV_LOG_DEBUG, - "timestamp discontinuity for stream #%d:%d " - "(id=%d, type=%s): %"PRId64", new offset= %"PRId64"\n", - ist->file_index, ist->st->index, ist->st->id, - av_get_media_type_string(ist->par->codec_type), - delta, ifile->ts_offset_discont); - pkt->dts -= av_rescale_q(delta, AV_TIME_BASE_Q, ist->st->time_base); - if (pkt->pts != AV_NOPTS_VALUE) - pkt->pts -= av_rescale_q(delta, AV_TIME_BASE_Q, ist->st->time_base); - } - } else { - if (FFABS(delta) > 1LL * dts_error_threshold * AV_TIME_BASE) { - av_log(NULL, AV_LOG_WARNING, "DTS %"PRId64", next:%"PRId64" st:%d invalid dropping\n", pkt->dts, ist->next_dts, pkt->stream_index); - pkt->dts = AV_NOPTS_VALUE; - } - if (pkt->pts != AV_NOPTS_VALUE){ - int64_t pkt_pts = av_rescale_q(pkt->pts, ist->st->time_base, AV_TIME_BASE_Q); - delta = pkt_pts - ist->next_dts; - if (FFABS(delta) > 1LL * dts_error_threshold * AV_TIME_BASE) { - av_log(NULL, AV_LOG_WARNING, "PTS %"PRId64", next:%"PRId64" invalid dropping st:%d\n", pkt->pts, ist->next_dts, pkt->stream_index); - pkt->pts = AV_NOPTS_VALUE; - } - } - } - } else if (ist->next_dts == AV_NOPTS_VALUE && !copy_ts && - fmt_is_discont && ifile->last_ts != AV_NOPTS_VALUE) { - int64_t delta = pkt_dts - ifile->last_ts; - if (FFABS(delta) > 1LL * dts_delta_threshold * AV_TIME_BASE) { - ifile->ts_offset_discont -= delta; - av_log(NULL, AV_LOG_DEBUG, - "Inter stream timestamp discontinuity %"PRId64", new offset= %"PRId64"\n", - delta, ifile->ts_offset_discont); - pkt->dts -= av_rescale_q(delta, AV_TIME_BASE_Q, ist->st->time_base); - if (pkt->pts != AV_NOPTS_VALUE) - pkt->pts -= av_rescale_q(delta, AV_TIME_BASE_Q, ist->st->time_base); - } + dec_packet(ist, NULL, 1); } - - ifile->last_ts = av_rescale_q(pkt->dts, ist->st->time_base, AV_TIME_BASE_Q); -} - -static void ts_discontinuity_process(InputFile *ifile, InputStream *ist, - AVPacket *pkt) -{ - int64_t offset = av_rescale_q(ifile->ts_offset_discont, AV_TIME_BASE_Q, - ist->st->time_base); - - // apply previously-detected timestamp-discontinuity offset - // (to all streams, not just audio/video) - if (pkt->dts != AV_NOPTS_VALUE) - pkt->dts += offset; - if (pkt->pts != AV_NOPTS_VALUE) - pkt->pts += offset; - - // detect timestamp discontinuities for audio/video - if ((ist->par->codec_type == AVMEDIA_TYPE_VIDEO || - ist->par->codec_type == AVMEDIA_TYPE_AUDIO) && - pkt->dts != AV_NOPTS_VALUE) - ts_discontinuity_detect(ifile, ist, pkt); } /* @@ -3502,12 +1087,10 @@ static void ts_discontinuity_process(InputFile *ifile, InputStream *ist, static int process_input(int file_index) { InputFile *ifile = input_files[file_index]; - AVFormatContext *is; InputStream *ist; AVPacket *pkt; int ret, i; - is = ifile->ctx; ret = ifile_get_packet(ifile, &pkt); if (ret == AVERROR(EAGAIN)) { @@ -3521,26 +1104,30 @@ static int process_input(int file_index) } if (ret < 0) { if (ret != AVERROR_EOF) { - print_error(is->url, ret); + av_log(ifile, AV_LOG_ERROR, + "Error retrieving a packet from demuxer: %s\n", av_err2str(ret)); if (exit_on_error) - exit_program(1); + return ret; } for (i = 0; i < ifile->nb_streams; i++) { ist = ifile->streams[i]; - if (ist->processing_needed) { + if (!ist->discard) { ret = process_input_packet(ist, NULL, 0); if (ret>0) return 0; + else if (ret < 0) + return ret; } /* mark all outputs that don't go through lavfi as finished */ - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { - if (ost->ist == ist && - (!ost->enc_ctx || ost->enc_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE)) { - OutputFile *of = output_files[ost->file_index]; - of_output_packet(of, ost->pkt, ost, 1); - } + for (int oidx = 0; oidx < ist->nb_outputs; oidx++) { + OutputStream *ost = ist->outputs[oidx]; + OutputFile *of = output_files[ost->file_index]; + close_output_stream(ost); + ret = of_output_packet(of, ost, NULL); + if (ret < 0) + return ret; } } @@ -3552,102 +1139,13 @@ static int process_input(int file_index) ist = ifile->streams[pkt->stream_index]; - ist->data_size += pkt->size; - ist->nb_packets++; - - if (ist->discard) - goto discard_packet; - - /* add the stream-global side data to the first packet */ - if (ist->nb_packets == 1) { - for (i = 0; i < ist->st->nb_side_data; i++) { - AVPacketSideData *src_sd = &ist->st->side_data[i]; - uint8_t *dst_data; + sub2video_heartbeat(ifile, pkt->pts, pkt->time_base); - if (src_sd->type == AV_PKT_DATA_DISPLAYMATRIX) - continue; - - if (av_packet_get_side_data(pkt, src_sd->type, NULL)) - continue; - - dst_data = av_packet_new_side_data(pkt, src_sd->type, src_sd->size); - if (!dst_data) - report_and_exit(AVERROR(ENOMEM)); - - memcpy(dst_data, src_sd->data, src_sd->size); - } - } - - // detect and try to correct for timestamp discontinuities - ts_discontinuity_process(ifile, ist, pkt); - - if (debug_ts) { - av_log(NULL, AV_LOG_INFO, "demuxer+ffmpeg -> ist_index:%d:%d type:%s pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s duration:%s duration_time:%s off:%s off_time:%s\n", - ifile->index, pkt->stream_index, - av_get_media_type_string(ist->par->codec_type), - av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &ist->st->time_base), - av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &ist->st->time_base), - av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, &ist->st->time_base), - av_ts2str(input_files[ist->file_index]->ts_offset), - av_ts2timestr(input_files[ist->file_index]->ts_offset, &AV_TIME_BASE_Q)); - } - - sub2video_heartbeat(ist, pkt->pts); + ret = process_input_packet(ist, pkt, 0); - process_input_packet(ist, pkt, 0); - -discard_packet: av_packet_free(&pkt); - return 0; -} - -/** - * Perform a step of transcoding for the specified filter graph. - * - * @param[in] graph filter graph to consider - * @param[out] best_ist input stream where a frame would allow to continue - * @return 0 for success, <0 for error - */ -static int transcode_from_filter(FilterGraph *graph, InputStream **best_ist) -{ - int i, ret; - int nb_requests, nb_requests_max = 0; - InputFilter *ifilter; - InputStream *ist; - - *best_ist = NULL; - ret = avfilter_graph_request_oldest(graph->graph); - if (ret >= 0) - return reap_filters(0); - - if (ret == AVERROR_EOF) { - ret = reap_filters(1); - for (i = 0; i < graph->nb_outputs; i++) - close_output_stream(graph->outputs[i]->ost); - return ret; - } - if (ret != AVERROR(EAGAIN)) - return ret; - - for (i = 0; i < graph->nb_inputs; i++) { - ifilter = graph->inputs[i]; - ist = ifilter->ist; - if (input_files[ist->file_index]->eagain || - input_files[ist->file_index]->eof_reached) - continue; - nb_requests = av_buffersrc_get_nb_failed_requests(ifilter->filter); - if (nb_requests > nb_requests_max) { - nb_requests_max = nb_requests; - *best_ist = ist; - } - } - - if (!*best_ist) - for (i = 0; i < graph->nb_outputs; i++) - graph->outputs[i]->ost->unavailable = 1; - - return 0; + return ret < 0 ? ret : 0; } /** @@ -3655,75 +1153,16 @@ static int transcode_from_filter(FilterGraph *graph, InputStream **best_ist) * * @return 0 for success, <0 for error */ -static int transcode_step(void) +static int transcode_step(OutputStream *ost) { - OutputStream *ost; InputStream *ist = NULL; int ret; - ost = choose_output(); - if (!ost) { - if (got_eagain()) { - reset_eagain(); - av_usleep(10000); - return 0; - } - av_log(NULL, AV_LOG_VERBOSE, "No more inputs to read from, finishing.\n"); - return AVERROR_EOF; - } - - if (ost->filter && !ost->filter->graph->graph) { - if (ifilter_has_all_input_formats(ost->filter->graph)) { - ret = configure_filtergraph(ost->filter->graph); - if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Error reinitializing filters!\n"); - return ret; - } - } - } - - if (ost->filter && ost->filter->graph->graph) { - /* - * Similar case to the early audio initialization in reap_filters. - * Audio is special in ffmpeg.c currently as we depend on lavfi's - * audio frame buffering/creation to get the output audio frame size - * in samples correct. The audio frame size for the filter chain is - * configured during the output stream initialization. - * - * Apparently avfilter_graph_request_oldest (called in - * transcode_from_filter just down the line) peeks. Peeking already - * puts one frame "ready to be given out", which means that any - * update in filter buffer sink configuration afterwards will not - * help us. And yes, even if it would be utilized, - * av_buffersink_get_samples is affected, as it internally utilizes - * the same early exit for peeked frames. - * - * In other words, if avfilter_graph_request_oldest would not make - * further filter chain configuration or usage of - * av_buffersink_get_samples useless (by just causing the return - * of the peeked AVFrame as-is), we could get rid of this additional - * early encoder initialization. - */ - if (av_buffersink_get_type(ost->filter->filter) == AVMEDIA_TYPE_AUDIO) - init_output_stream_wrapper(ost, NULL, 1); - - if ((ret = transcode_from_filter(ost->filter->graph, &ist)) < 0) + if (ost->filter) { + if ((ret = fg_transcode_step(ost->filter->graph, &ist)) < 0) return ret; if (!ist) return 0; - } else if (ost->filter) { - int i; - for (i = 0; i < ost->filter->graph->nb_inputs; i++) { - InputFilter *ifilter = ost->filter->graph->inputs[i]; - if (!ifilter->ist->got_output && !input_files[ifilter->ist->file_index]->eof_reached) { - ist = ifilter->ist; - break; - } - } - if (!ist) { - ost->inputs_done = 1; - return 0; - } } else { ist = ost->ist; av_assert0(ist); @@ -3739,22 +1178,30 @@ static int transcode_step(void) if (ret < 0) return ret == AVERROR_EOF ? 0 : ret; - return reap_filters(0); + // process_input() above might have caused output to become available + // in multiple filtergraphs, so we process all of them + for (int i = 0; i < nb_filtergraphs; i++) { + ret = reap_filters(filtergraphs[i], 0); + if (ret < 0) + return ret; + } + + return 0; } /* * The following code is the main loop of the file converter */ -static int transcode(void) +static int transcode(int *err_rate_exceeded) { - int ret, i; + int ret = 0, i; InputStream *ist; int64_t timer_start; - int64_t total_packets_written = 0; - ret = transcode_init(); - if (ret < 0) - goto fail; + print_stream_maps(); + + *err_rate_exceeded = 0; + atomic_store(&transcode_init_done, 1); if (stdin_interaction) { av_log(NULL, AV_LOG_INFO, "Press [q] to stop, [?] for help\n"); @@ -3763,6 +1210,7 @@ static int transcode(void) timer_start = av_gettime_relative(); while (!received_sigterm) { + OutputStream *ost; int64_t cur_time= av_gettime_relative(); /* if 'q' pressed, exits */ @@ -3770,13 +1218,18 @@ static int transcode(void) if (check_keyboard_interaction(cur_time) < 0) break; - /* check if there's any stream where output is still needed */ - if (!need_output()) { + ret = choose_output(&ost); + if (ret == AVERROR(EAGAIN)) { + reset_eagain(); + av_usleep(10000); + continue; + } else if (ret < 0) { av_log(NULL, AV_LOG_VERBOSE, "No more output streams to write to, finishing.\n"); + ret = 0; break; } - ret = transcode_step(); + ret = transcode_step(ost); if (ret < 0 && ret != AVERROR_EOF) { av_log(NULL, AV_LOG_ERROR, "Error while filtering: %s\n", av_err2str(ret)); break; @@ -3788,47 +1241,35 @@ static int transcode(void) /* at the end of stream, we must flush the decoder buffers */ for (ist = ist_iter(NULL); ist; ist = ist_iter(ist)) { + float err_rate; + if (!input_files[ist->file_index]->eof_reached) { - process_input_packet(ist, NULL, 0); + int err = process_input_packet(ist, NULL, 0); + ret = err_merge(ret, err); } + + err_rate = (ist->frames_decoded || ist->decode_errors) ? + ist->decode_errors / (ist->frames_decoded + ist->decode_errors) : 0.f; + if (err_rate > max_error_rate) { + av_log(ist, AV_LOG_FATAL, "Decode error rate %g exceeds maximum %g\n", + err_rate, max_error_rate); + *err_rate_exceeded = 1; + } else if (err_rate) + av_log(ist, AV_LOG_VERBOSE, "Decode error rate %g\n", err_rate); } - flush_encoders(); + ret = err_merge(ret, enc_flush()); term_exit(); /* write the trailer if needed */ for (i = 0; i < nb_output_files; i++) { - ret = of_write_trailer(output_files[i]); - if (ret < 0 && exit_on_error) - exit_program(1); + int err = of_write_trailer(output_files[i]); + ret = err_merge(ret, err); } /* dump report by using the first video and audio streams */ print_report(1, timer_start, av_gettime_relative()); - /* close each encoder */ - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { - uint64_t packets_written; - packets_written = atomic_load(&ost->packets_written); - total_packets_written += packets_written; - if (!packets_written && (abort_on_flags & ABORT_ON_FLAG_EMPTY_OUTPUT_STREAM)) { - av_log(NULL, AV_LOG_FATAL, "Empty output on stream %d:%d.\n", - ost->file_index, ost->index); - exit_program(1); - } - } - - if (!total_packets_written && (abort_on_flags & ABORT_ON_FLAG_EMPTY_OUTPUT)) { - av_log(NULL, AV_LOG_FATAL, "Empty output\n"); - exit_program(1); - } - - hw_device_free_all(); - - /* finished ! */ - ret = 0; - - fail: return ret; } @@ -3878,13 +1319,11 @@ static int64_t getmaxrss(void) int main(int argc, char **argv) { - int ret; + int ret, err_rate_exceeded; BenchmarkTimeStamps ti; init_dynload(); - register_exit(ffmpeg_cleanup); - setvbuf(stderr,NULL,_IONBF,0); /* win32 runtime needs this */ av_log_set_flags(AV_LOG_SKIP_REPEATED); @@ -3900,24 +1339,24 @@ int main(int argc, char **argv) /* parse options and open all input/output files */ ret = ffmpeg_parse_options(argc, argv); if (ret < 0) - exit_program(1); + goto finish; if (nb_output_files <= 0 && nb_input_files == 0) { show_usage(); av_log(NULL, AV_LOG_WARNING, "Use -h to get full help or, even better, run 'man %s'\n", program_name); - exit_program(1); + ret = 1; + goto finish; } - /* file converter / grab */ if (nb_output_files <= 0) { av_log(NULL, AV_LOG_FATAL, "At least one output file must be specified\n"); - exit_program(1); + ret = 1; + goto finish; } current_time = ti = get_benchmark_time_stamps(); - if (transcode() < 0) - exit_program(1); - if (do_benchmark) { + ret = transcode(&err_rate_exceeded); + if (ret >= 0 && do_benchmark) { int64_t utime, stime, rtime; current_time = get_benchmark_time_stamps(); utime = current_time.user_usec - ti.user_usec; @@ -3927,11 +1366,14 @@ int main(int argc, char **argv) "bench: utime=%0.3fs stime=%0.3fs rtime=%0.3fs\n", utime / 1000000.0, stime / 1000000.0, rtime / 1000000.0); } - av_log(NULL, AV_LOG_DEBUG, "%"PRIu64" frames successfully decoded, %"PRIu64" decoding errors\n", - decode_error_stat[0], decode_error_stat[1]); - if ((decode_error_stat[0] + decode_error_stat[1]) * max_error_rate < decode_error_stat[1]) - exit_program(69); - exit_program(received_nb_signals ? 255 : main_return_code); - return main_return_code; + ret = received_nb_signals ? 255 : + err_rate_exceeded ? 69 : ret; + +finish: + if (ret == AVERROR_EXIT) + ret = 0; + + ffmpeg_cleanup(ret); + return ret; } diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h index 5527dbe49b0..d53181e427b 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -54,6 +54,9 @@ #define FFMPEG_OPT_MAP_CHANNEL 1 #define FFMPEG_OPT_MAP_SYNC 1 #define FFMPEG_ROTATION_METADATA 1 +#define FFMPEG_OPT_QPHIST 1 +#define FFMPEG_OPT_ADRIFT_THRESHOLD 1 +#define FFMPEG_OPT_ENC_TIME_BASE_NUM 1 enum VideoSyncMethod { VSYNC_AUTO = -1, @@ -64,7 +67,10 @@ enum VideoSyncMethod { VSYNC_DROP, }; -#define MAX_STREAMS 1024 /* arbitrary sanity check value */ +enum EncTimeBase { + ENC_TIME_BASE_DEMUX = -1, + ENC_TIME_BASE_FILTER = -2, +}; enum HWAccelID { HWACCEL_NONE = 0, @@ -93,6 +99,12 @@ typedef struct { } AudioChannelMap; #endif +typedef struct DemuxPktData { + // estimated dts in AV_TIME_BASE_Q, + // to be used when real dts is missing + int64_t dts_est; +} DemuxPktData; + typedef struct OptionsContext { OptionGroup *g; @@ -124,6 +136,7 @@ typedef struct OptionsContext { int loop; int rate_emu; float readrate; + double readrate_initial_burst; int accurate_seek; int thread_queue_size; int input_sync_ref; @@ -168,9 +181,8 @@ typedef struct OptionsContext { int subtitle_disable; int data_disable; - /* indexed by output file stream index */ - int *streamid_map; - int nb_streamid_map; + // keys are stream indices + AVDictionary *streamid; SpecifierOpt *metadata; int nb_metadata; @@ -224,6 +236,8 @@ typedef struct OptionsContext { int nb_reinit_filters; SpecifierOpt *fix_sub_duration; int nb_fix_sub_duration; + SpecifierOpt *fix_sub_duration_heartbeat; + int nb_fix_sub_duration_heartbeat; SpecifierOpt *canvas_sizes; int nb_canvas_sizes; SpecifierOpt *pass; @@ -252,65 +266,45 @@ typedef struct OptionsContext { int nb_autoscale; SpecifierOpt *bits_per_raw_sample; int nb_bits_per_raw_sample; + SpecifierOpt *enc_stats_pre; + int nb_enc_stats_pre; + SpecifierOpt *enc_stats_post; + int nb_enc_stats_post; + SpecifierOpt *mux_stats; + int nb_mux_stats; + SpecifierOpt *enc_stats_pre_fmt; + int nb_enc_stats_pre_fmt; + SpecifierOpt *enc_stats_post_fmt; + int nb_enc_stats_post_fmt; + SpecifierOpt *mux_stats_fmt; + int nb_mux_stats_fmt; } OptionsContext; typedef struct InputFilter { - AVFilterContext *filter; - struct InputStream *ist; struct FilterGraph *graph; uint8_t *name; - enum AVMediaType type; // AVMEDIA_TYPE_SUBTITLE for sub2video - - AVFifo *frame_queue; - - // parameters configured for this input - int format; - - int width, height; - AVRational sample_aspect_ratio; - - int sample_rate; - AVChannelLayout ch_layout; - - AVBufferRef *hw_frames_ctx; - int32_t *displaymatrix; - - int eof; } InputFilter; typedef struct OutputFilter { - AVFilterContext *filter; struct OutputStream *ost; struct FilterGraph *graph; uint8_t *name; - /* temporary storage until stream maps are processed */ - AVFilterInOut *out_tmp; + /* for filters that are not yet bound to an output stream, + * this stores the output linklabel, if any */ + uint8_t *linklabel; + enum AVMediaType type; - /* desired output stream properties */ - int width, height; - AVRational frame_rate; - int format; - int sample_rate; - AVChannelLayout ch_layout; - - // those are only set if no format is specified and the encoder gives us multiple options - // They point directly to the relevant lists of the encoder. - const int *formats; - const AVChannelLayout *ch_layouts; - const int *sample_rates; + /* pts of the last frame received from this filter, in AV_TIME_BASE_Q */ + int64_t last_pts; } OutputFilter; typedef struct FilterGraph { + const AVClass *class; int index; - const char *graph_desc; AVFilterGraph *graph; - int reconfiguration; - // true when the filtergraph contains only meta filters - // that do not modify the frame data - int is_meta; InputFilter **inputs; int nb_inputs; @@ -318,15 +312,20 @@ typedef struct FilterGraph { int nb_outputs; } FilterGraph; +typedef struct Decoder Decoder; + typedef struct InputStream { + const AVClass *class; + int file_index; + int index; + AVStream *st; int discard; /* true if stream data should be discarded */ int user_set_discard; int decoding_needed; /* non zero if the packets must be decoded in 'raw_fifo', see DECODING_FOR_* */ #define DECODING_FOR_OST 1 #define DECODING_FOR_FILTER 2 - int processing_needed; /* non zero if the packets must be processed */ /** * Codec parameters - to be used by the decoding/streamcopy code. @@ -334,65 +333,25 @@ typedef struct InputStream { * concurrently by the demuxing thread. */ AVCodecParameters *par; + Decoder *decoder; AVCodecContext *dec_ctx; const AVCodec *dec; - AVFrame *decoded_frame; - AVPacket *pkt; + const AVCodecDescriptor *codec_desc; AVRational framerate_guessed; - int64_t prev_pkt_pts; - int64_t start; /* time when read started */ - /* predicted dts of the next packet read for this stream or (when there are - * several frames in a packet) of the next frame in current packet (in AV_TIME_BASE units) */ - int64_t next_dts; - int64_t first_dts; ///< dts of the first packet read for this stream (in AV_TIME_BASE units) - int64_t dts; ///< dts of the last packet read for this stream (in AV_TIME_BASE units) - - int64_t next_pts; ///< synthetic pts for the next decode frame (in AV_TIME_BASE units) - int64_t pts; ///< current pts of the decoded frame (in AV_TIME_BASE units) - int wrap_correction_done; - - // the value of AVCodecParserContext.repeat_pict from the AVStream parser - // for the last packet returned from ifile_get_packet() - // -1 if unknown - // FIXME: this is a hack, the avstream parser should not be used - int last_pkt_repeat_pict; - - int64_t filter_in_rescale_delta_last; - - int64_t min_pts; /* pts with the smallest value in a current stream */ - int64_t max_pts; /* pts with the higher value in a current stream */ - - // when forcing constant input framerate through -r, - // this contains the pts that will be given to the next decoded frame - int64_t cfr_next_pts; - int64_t nb_samples; /* number of samples in the last decoded audio frame before looping */ - double ts_scale; - int saw_first_ts; AVDictionary *decoder_opts; AVRational framerate; /* framerate forced with -r */ int top_field_first; - int guess_layout_max; int autorotate; int fix_sub_duration; - struct { /* previous decoded subtitle and related variables */ - int got_output; - int ret; - AVSubtitle subtitle; - } prev_sub; struct sub2video { - int64_t last_pts; - int64_t end_pts; - AVFifo *sub_queue; ///< queue of AVSubtitle* before filter init - AVFrame *frame; int w, h; - unsigned int initialize; ///< marks if sub2video_update should force an initialization } sub2video; /* decoded data from this stream goes into all those filters @@ -400,6 +359,14 @@ typedef struct InputStream { InputFilter **filters; int nb_filters; + /* + * Output targets that do not go through lavfi, i.e. subtitles or + * streamcopy. Those two cases are distinguished by the OutputStream + * having an encoder or not. + */ + struct OutputStream **outputs; + int nb_outputs; + int reinit_filters; /* hwaccel options */ @@ -408,22 +375,11 @@ typedef struct InputStream { char *hwaccel_device; enum AVPixelFormat hwaccel_output_format; - int (*hwaccel_retrieve_data)(AVCodecContext *s, AVFrame *frame); - enum AVPixelFormat hwaccel_pix_fmt; - /* stats */ - // combined size of all the packets read - uint64_t data_size; - /* number of packets successfully read for this stream */ - uint64_t nb_packets; // number of frames/samples retrieved from the decoder uint64_t frames_decoded; uint64_t samples_decoded; - - int64_t *dts_buffer; - int nb_dts_buffer; - - int got_output; + uint64_t decode_errors; } InputStream; typedef struct LastFrameDuration { @@ -432,8 +388,13 @@ typedef struct LastFrameDuration { } LastFrameDuration; typedef struct InputFile { + const AVClass *class; + int index; + // input format has no timestamps + int format_nots; + AVFormatContext *ctx; int eof_reached; /* true if eof reached */ int eagain; /* true if last read attempt returned EAGAIN */ @@ -444,11 +405,6 @@ typedef struct InputFile { */ int64_t start_time_effective; int64_t ts_offset; - /** - * Extra timestamp offset added by discontinuity handling. - */ - int64_t ts_offset_discont; - int64_t last_ts; int64_t start_time; /* user-specified start time in AV_TIME_BASE or AV_NOPTS_VALUE */ int64_t recording_time; @@ -458,7 +414,6 @@ typedef struct InputFile { InputStream **streams; int nb_streams; - int rate_emu; float readrate; int accurate_seek; @@ -480,6 +435,41 @@ enum forced_keyframes_const { #define ABORT_ON_FLAG_EMPTY_OUTPUT (1 << 0) #define ABORT_ON_FLAG_EMPTY_OUTPUT_STREAM (1 << 1) +enum EncStatsType { + ENC_STATS_LITERAL = 0, + ENC_STATS_FILE_IDX, + ENC_STATS_STREAM_IDX, + ENC_STATS_FRAME_NUM, + ENC_STATS_FRAME_NUM_IN, + ENC_STATS_TIMEBASE, + ENC_STATS_TIMEBASE_IN, + ENC_STATS_PTS, + ENC_STATS_PTS_TIME, + ENC_STATS_PTS_IN, + ENC_STATS_PTS_TIME_IN, + ENC_STATS_DTS, + ENC_STATS_DTS_TIME, + ENC_STATS_SAMPLE_NUM, + ENC_STATS_NB_SAMPLES, + ENC_STATS_PKT_SIZE, + ENC_STATS_BITRATE, + ENC_STATS_AVG_BITRATE, +}; + +typedef struct EncStatsComponent { + enum EncStatsType type; + + uint8_t *str; + size_t str_len; +} EncStatsComponent; + +typedef struct EncStats { + EncStatsComponent *components; + int nb_components; + + AVIOContext *io; +} EncStats; + extern const char *const forced_keyframes_const_names[]; typedef enum { @@ -508,42 +498,41 @@ typedef struct KeyframeForceCtx { int dropped_keyframe; } KeyframeForceCtx; +typedef struct Encoder Encoder; + typedef struct OutputStream { + const AVClass *class; + + enum AVMediaType type; + int file_index; /* file index */ int index; /* stream index in the output file */ + /** + * Codec parameters for packets submitted to the muxer (i.e. before + * bitstream filtering, if any). + */ + AVCodecParameters *par_in; + /* input stream that is the source for this output stream; * may be NULL for streams with no well-defined source, e.g. * attachments or outputs from complex filtergraphs */ InputStream *ist; AVStream *st; /* stream in the output file */ - /* number of frames emitted by the video-encoding sync code */ - int64_t vsync_frame_number; - /* predicted pts of the next frame to be encoded - * audio/video encoding only */ - int64_t next_pts; /* dts of the last packet sent to the muxing queue, in AV_TIME_BASE_Q */ int64_t last_mux_dts; - /* pts of the last frame received from the filters, in AV_TIME_BASE_Q */ - int64_t last_filter_pts; - - // timestamp from which the streamcopied streams should start, - // in AV_TIME_BASE_Q; - // everything before it should be discarded - int64_t ts_copy_start; // the timebase of the packets sent to the muxer AVRational mux_timebase; AVRational enc_timebase; + Encoder *enc; AVCodecContext *enc_ctx; - AVFrame *filtered_frame; - AVFrame *last_frame; - AVFrame *sq_frame; - AVPacket *pkt; + + uint64_t nb_frames_dup; + uint64_t nb_frames_drop; int64_t last_dropped; - int64_t last_nb0_frames[3]; /* video only */ AVRational frame_rate; @@ -576,9 +565,6 @@ typedef struct OutputStream { FILE *logfile; OutputFilter *filter; - char *avfilter; - char *filters; ///< filtergraph associated to the -filter option - char *filters_script; ///< filtergraph script associated to the -filter_script option AVDictionary *encoder_opts; AVDictionary *sws_dict; @@ -595,39 +581,35 @@ typedef struct OutputStream { int inputs_done; const char *attachment_filename; - int streamcopy_started; - int copy_initial_nonkeyframes; - int copy_prior_start; int keep_pix_fmt; /* stats */ - // combined size of all the packets sent to the muxer - uint64_t data_size_mux; - // combined size of all the packets received from the encoder - uint64_t data_size_enc; // number of packets send to the muxer atomic_uint_least64_t packets_written; // number of frames/samples sent to the encoder uint64_t frames_encoded; uint64_t samples_encoded; - // number of packets received from the encoder - uint64_t packets_encoded; /* packet quality factor */ int quality; - /* packet picture type */ - int pict_type; - - /* frame encode sum of squared error values */ - int64_t error[4]; - int sq_idx_encode; int sq_idx_mux; + + EncStats enc_stats_pre; + EncStats enc_stats_post; + + /* + * bool on whether this stream should be utilized for splitting + * subtitles utilizing fix_sub_duration at random access points. + */ + unsigned int fix_sub_duration_heartbeat; } OutputStream; typedef struct OutputFile { + const AVClass *class; + int index; const AVOutputFormat *format; @@ -645,6 +627,21 @@ typedef struct OutputFile { int bitexact; } OutputFile; +// optionally attached as opaque_ref to decoded AVFrames +typedef struct FrameData { + // properties that come from the decoder + struct { + uint64_t frame_num; + + int64_t pts; + AVRational tb; + } dec; + + AVRational frame_rate_filter; + + int bits_per_raw_sample; +} FrameData; + extern InputFile **input_files; extern int nb_input_files; @@ -657,7 +654,6 @@ extern int nb_filtergraphs; extern char *vstats_filename; extern char *sdp_filename; -extern float audio_drift_threshold; extern float dts_delta_threshold; extern float dts_error_threshold; @@ -675,7 +671,6 @@ extern int exit_on_error; extern int abort_on_flags; extern int print_stats; extern int64_t stats_period; -extern int qp_hist; extern int stdin_interaction; extern AVIOContext *progress_avio; extern float max_error_rate; @@ -691,13 +686,14 @@ extern const OptionDef options[]; extern HWDevice *filter_hw_device; extern unsigned nb_output_dumped; -extern int main_return_code; extern int ignore_unknown_streams; extern int copy_unknown_streams; extern int recast_media; +extern FILE *vstats_file; + #if FFMPEG_OPT_PSNR extern int do_psnr; #endif @@ -708,35 +704,115 @@ void term_exit(void); void show_usage(void); void remove_avoptions(AVDictionary **a, AVDictionary *b); -void assert_avoptions(AVDictionary *m); +int check_avoptions(AVDictionary *m); -void assert_file_overwrite(const char *filename); +int assert_file_overwrite(const char *filename); char *file_read(const char *filename); AVDictionary *strip_specifiers(const AVDictionary *dict); -const AVCodec *find_codec_or_die(const char *name, enum AVMediaType type, int encoder); +int find_codec(void *logctx, const char *name, + enum AVMediaType type, int encoder, const AVCodec **codec); int parse_and_set_vsync(const char *arg, int *vsync_var, int file_idx, int st_idx, int is_global); -int configure_filtergraph(FilterGraph *fg); -void check_filter_outputs(void); -int filtergraph_is_simple(FilterGraph *fg); -int init_simple_filtergraph(InputStream *ist, OutputStream *ost); +int check_filter_outputs(void); +int filtergraph_is_simple(const FilterGraph *fg); +int init_simple_filtergraph(InputStream *ist, OutputStream *ost, + char *graph_desc); int init_complex_filtergraph(FilterGraph *fg); -void sub2video_update(InputStream *ist, int64_t heartbeat_pts, AVSubtitle *sub); +int copy_av_subtitle(AVSubtitle *dst, const AVSubtitle *src); +int subtitle_wrap_frame(AVFrame *frame, AVSubtitle *subtitle, int copy); + +/** + * Get our axiliary frame data attached to the frame, allocating it + * if needed. + */ +FrameData *frame_data(AVFrame *frame); + +int ifilter_send_frame(InputFilter *ifilter, AVFrame *frame, int keep_reference); +int ifilter_send_eof(InputFilter *ifilter, int64_t pts, AVRational tb); +int ifilter_sub2video(InputFilter *ifilter, const AVFrame *frame); +void ifilter_sub2video_heartbeat(InputFilter *ifilter, int64_t pts, AVRational tb); + +/** + * Set up fallback filtering parameters from a decoder context. They will only + * be used if no frames are ever sent on this input, otherwise the actual + * parameters are taken from the frame. + */ +int ifilter_parameters_from_dec(InputFilter *ifilter, const AVCodecContext *dec); + +int ofilter_bind_ost(OutputFilter *ofilter, OutputStream *ost); -int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame); +/** + * Create a new filtergraph in the global filtergraph list. + * + * @param graph_desc Graph description; an av_malloc()ed string, filtergraph + * takes ownership of it. + */ +int fg_create(FilterGraph **pfg, char *graph_desc); + +void fg_free(FilterGraph **pfg); + +/** + * Perform a step of transcoding for the specified filter graph. + * + * @param[in] graph filter graph to consider + * @param[out] best_ist input stream where a frame would allow to continue + * @return 0 for success, <0 for error + */ +int fg_transcode_step(FilterGraph *graph, InputStream **best_ist); + +/** + * Get and encode new output from specified filtergraph, without causing + * activity. + * + * @return 0 for success, <0 for severe errors + */ +int reap_filters(FilterGraph *fg, int flush); int ffmpeg_parse_options(int argc, char **argv); +void enc_stats_write(OutputStream *ost, EncStats *es, + const AVFrame *frame, const AVPacket *pkt, + uint64_t frame_num); + HWDevice *hw_device_get_by_name(const char *name); +HWDevice *hw_device_get_by_type(enum AVHWDeviceType type); int hw_device_init_from_string(const char *arg, HWDevice **dev); +int hw_device_init_from_type(enum AVHWDeviceType type, + const char *device, + HWDevice **dev_out); void hw_device_free_all(void); -int hw_device_setup_for_decode(InputStream *ist); -int hw_device_setup_for_encode(OutputStream *ost); -int hw_device_setup_for_filter(FilterGraph *fg); +/** + * Get a hardware device to be used with this filtergraph. + * The returned reference is owned by the callee, the caller + * must ref it explicitly for long-term use. + */ +AVBufferRef *hw_device_for_filter(void); + +int hwaccel_retrieve_data(AVCodecContext *avctx, AVFrame *input); -int hwaccel_decode_init(AVCodecContext *avctx); +int dec_open(InputStream *ist); +void dec_free(Decoder **pdec); + +/** + * Submit a packet for decoding + * + * When pkt==NULL and no_eof=0, there will be no more input. Flush decoders and + * mark all downstreams as finished. + * + * When pkt==NULL and no_eof=1, the stream was reset (e.g. after a seek). Flush + * decoders and await further input. + */ +int dec_packet(InputStream *ist, const AVPacket *pkt, int no_eof); + +int enc_alloc(Encoder **penc, const AVCodec *codec); +void enc_free(Encoder **penc); + +int enc_open(OutputStream *ost, AVFrame *frame); +int enc_subtitle(OutputFile *of, OutputStream *ost, const AVSubtitle *sub); +int enc_frame(OutputStream *ost, AVFrame *frame); +int enc_flush(void); /* * Initialize muxing state for the given stream, should be called @@ -749,18 +825,15 @@ int of_write_trailer(OutputFile *of); int of_open(const OptionsContext *o, const char *filename); void of_close(OutputFile **pof); -/* - * Send a single packet to the output, applying any bitstream filters - * associated with the output stream. This may result in any number - * of packets actually being written, depending on what bitstream - * filters are applied. The supplied packet is consumed and will be - * blank (as if newly-allocated) when this function returns. - * - * If eof is set, instead indicate EOF to all bitstream filters and - * therefore flush any delayed packets to the output. A blank packet - * must be supplied in this case. +void of_enc_stats_close(void); + +int of_output_packet(OutputFile *of, OutputStream *ost, AVPacket *pkt); + +/** + * @param dts predicted packet dts in AV_TIME_BASE_Q */ -void of_output_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int eof); +int of_streamcopy(OutputStream *ost, const AVPacket *pkt, int64_t dts); + int64_t of_filesize(OutputFile *of); int ifile_open(const OptionsContext *o, const char *filename); @@ -778,10 +851,38 @@ void ifile_close(InputFile **f); */ int ifile_get_packet(InputFile *f, AVPacket **pkt); +int ist_output_add(InputStream *ist, OutputStream *ost); +int ist_filter_add(InputStream *ist, InputFilter *ifilter, int is_simple); + +/** + * Find an unused input stream of given type. + */ +InputStream *ist_find_unused(enum AVMediaType type); + /* iterate over all input streams in all input files; * pass NULL to start iteration */ InputStream *ist_iter(InputStream *prev); +/* iterate over all output streams in all output files; + * pass NULL to start iteration */ +OutputStream *ost_iter(OutputStream *prev); + +void close_output_stream(OutputStream *ost); +int trigger_fix_sub_duration_heartbeat(OutputStream *ost, const AVPacket *pkt); +int fix_sub_duration_heartbeat(InputStream *ist, int64_t signal_pts); +void update_benchmark(const char *fmt, ...); + +/** + * Merge two return codes - return one of the error codes if at least one of + * them was negative, 0 otherwise. + * Currently just picks the first one, eventually we might want to do something + * more sophisticated, like sorting them by priority. + */ +static inline int err_merge(int err0, int err1) +{ + return (err0 < 0) ? err0 : FFMIN(err1, 0); +} + #define SPECIFIER_OPT_FMT_str "%s" #define SPECIFIER_OPT_FMT_i "%i" #define SPECIFIER_OPT_FMT_i64 "%"PRId64 @@ -810,7 +911,7 @@ InputStream *ist_iter(InputStream *prev); so = &o->name[_i];\ _matches++;\ } else if (_ret < 0)\ - exit_program(1);\ + return _ret;\ }\ if (_matches > 1)\ WARN_MULTIPLE_OPT_USAGE(name, type, so, st);\ @@ -831,4 +932,14 @@ extern const char * const opt_name_codec_tags[]; extern const char * const opt_name_frame_rates[]; extern const char * const opt_name_top_field_first[]; +static inline void pkt_move(void *dst, void *src) +{ + av_packet_move_ref(dst, src); +} + +static inline void frame_move(void *dst, void *src) +{ + av_frame_move_ref(dst, src); +} + #endif /* FFTOOLS_FFMPEG_H */ diff --git a/fftools/ffmpeg_dec.c b/fftools/ffmpeg_dec.c new file mode 100644 index 00000000000..8a3b52fd7ee --- /dev/null +++ b/fftools/ffmpeg_dec.c @@ -0,0 +1,1135 @@ +/* + * 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 "libavutil/avassert.h" +#include "libavutil/dict.h" +#include "libavutil/error.h" +#include "libavutil/log.h" +#include "libavutil/pixdesc.h" +#include "libavutil/pixfmt.h" +#include "libavutil/timestamp.h" + +#include "libavcodec/avcodec.h" +#include "libavcodec/codec.h" + +#include "libavfilter/buffersrc.h" + +#include "ffmpeg.h" +#include "thread_queue.h" + +struct Decoder { + AVFrame *frame; + AVPacket *pkt; + + enum AVPixelFormat hwaccel_pix_fmt; + + // pts/estimated duration of the last decoded frame + // * in decoder timebase for video, + // * in last_frame_tb (may change during decoding) for audio + int64_t last_frame_pts; + int64_t last_frame_duration_est; + AVRational last_frame_tb; + int64_t last_filter_in_rescale_delta; + int last_frame_sample_rate; + + /* previous decoded subtitles */ + AVFrame *sub_prev[2]; + AVFrame *sub_heartbeat; + + pthread_t thread; + /** + * Queue for sending coded packets from the main thread to + * the decoder thread. + * + * An empty packet is sent to flush the decoder without terminating + * decoding. + */ + ThreadQueue *queue_in; + /** + * Queue for sending decoded frames from the decoder thread + * to the main thread. + * + * An empty frame is sent to signal that a single packet has been fully + * processed. + */ + ThreadQueue *queue_out; +}; + +// data that is local to the decoder thread and not visible outside of it +typedef struct DecThreadContext { + AVFrame *frame; + AVPacket *pkt; +} DecThreadContext; + +static int dec_thread_stop(Decoder *d) +{ + void *ret; + + if (!d->queue_in) + return 0; + + tq_send_finish(d->queue_in, 0); + tq_receive_finish(d->queue_out, 0); + + pthread_join(d->thread, &ret); + + tq_free(&d->queue_in); + tq_free(&d->queue_out); + + return (intptr_t)ret; +} + +void dec_free(Decoder **pdec) +{ + Decoder *dec = *pdec; + + if (!dec) + return; + + dec_thread_stop(dec); + + av_frame_free(&dec->frame); + av_packet_free(&dec->pkt); + + for (int i = 0; i < FF_ARRAY_ELEMS(dec->sub_prev); i++) + av_frame_free(&dec->sub_prev[i]); + av_frame_free(&dec->sub_heartbeat); + + av_freep(pdec); +} + +static int dec_alloc(Decoder **pdec) +{ + Decoder *dec; + + *pdec = NULL; + + dec = av_mallocz(sizeof(*dec)); + if (!dec) + return AVERROR(ENOMEM); + + dec->frame = av_frame_alloc(); + if (!dec->frame) + goto fail; + + dec->pkt = av_packet_alloc(); + if (!dec->pkt) + goto fail; + + dec->last_filter_in_rescale_delta = AV_NOPTS_VALUE; + dec->last_frame_pts = AV_NOPTS_VALUE; + dec->last_frame_tb = (AVRational){ 1, 1 }; + dec->hwaccel_pix_fmt = AV_PIX_FMT_NONE; + + *pdec = dec; + + return 0; +fail: + dec_free(&dec); + return AVERROR(ENOMEM); +} + +static int send_frame_to_filters(InputStream *ist, AVFrame *decoded_frame) +{ + int i, ret; + + av_assert1(ist->nb_filters > 0); /* ensure ret is initialized */ + for (i = 0; i < ist->nb_filters; i++) { + ret = ifilter_send_frame(ist->filters[i], decoded_frame, i < ist->nb_filters - 1); + if (ret == AVERROR_EOF) + ret = 0; /* ignore */ + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, + "Failed to inject frame into filter network: %s\n", av_err2str(ret)); + break; + } + } + return ret; +} + +static AVRational audio_samplerate_update(void *logctx, Decoder *d, + const AVFrame *frame) +{ + const int prev = d->last_frame_tb.den; + const int sr = frame->sample_rate; + + AVRational tb_new; + int64_t gcd; + + if (frame->sample_rate == d->last_frame_sample_rate) + goto finish; + + gcd = av_gcd(prev, sr); + + if (prev / gcd >= INT_MAX / sr) { + av_log(logctx, AV_LOG_WARNING, + "Audio timestamps cannot be represented exactly after " + "sample rate change: %d -> %d\n", prev, sr); + + // LCM of 192000, 44100, allows to represent all common samplerates + tb_new = (AVRational){ 1, 28224000 }; + } else + tb_new = (AVRational){ 1, prev / gcd * sr }; + + // keep the frame timebase if it is strictly better than + // the samplerate-defined one + if (frame->time_base.num == 1 && frame->time_base.den > tb_new.den && + !(frame->time_base.den % tb_new.den)) + tb_new = frame->time_base; + + if (d->last_frame_pts != AV_NOPTS_VALUE) + d->last_frame_pts = av_rescale_q(d->last_frame_pts, + d->last_frame_tb, tb_new); + d->last_frame_duration_est = av_rescale_q(d->last_frame_duration_est, + d->last_frame_tb, tb_new); + + d->last_frame_tb = tb_new; + d->last_frame_sample_rate = frame->sample_rate; + +finish: + return d->last_frame_tb; +} + +static void audio_ts_process(void *logctx, Decoder *d, AVFrame *frame) +{ + AVRational tb_filter = (AVRational){1, frame->sample_rate}; + AVRational tb; + int64_t pts_pred; + + // on samplerate change, choose a new internal timebase for timestamp + // generation that can represent timestamps from all the samplerates + // seen so far + tb = audio_samplerate_update(logctx, d, frame); + pts_pred = d->last_frame_pts == AV_NOPTS_VALUE ? 0 : + d->last_frame_pts + d->last_frame_duration_est; + + if (frame->pts == AV_NOPTS_VALUE) { + frame->pts = pts_pred; + frame->time_base = tb; + } else if (d->last_frame_pts != AV_NOPTS_VALUE && + frame->pts > av_rescale_q_rnd(pts_pred, tb, frame->time_base, + AV_ROUND_UP)) { + // there was a gap in timestamps, reset conversion state + d->last_filter_in_rescale_delta = AV_NOPTS_VALUE; + } + + frame->pts = av_rescale_delta(frame->time_base, frame->pts, + tb, frame->nb_samples, + &d->last_filter_in_rescale_delta, tb); + + d->last_frame_pts = frame->pts; + d->last_frame_duration_est = av_rescale_q(frame->nb_samples, + tb_filter, tb); + + // finally convert to filtering timebase + frame->pts = av_rescale_q(frame->pts, tb, tb_filter); + frame->duration = frame->nb_samples; + frame->time_base = tb_filter; +} + +static int64_t video_duration_estimate(const InputStream *ist, const AVFrame *frame) +{ + const Decoder *d = ist->decoder; + const InputFile *ifile = input_files[ist->file_index]; + int64_t codec_duration = 0; + + // XXX lavf currently makes up frame durations when they are not provided by + // the container. As there is no way to reliably distinguish real container + // durations from the fake made-up ones, we use heuristics based on whether + // the container has timestamps. Eventually lavf should stop making up + // durations, then this should be simplified. + + // prefer frame duration for containers with timestamps + if (frame->duration > 0 && (!ifile->format_nots || ist->framerate.num)) + return frame->duration; + + if (ist->dec_ctx->framerate.den && ist->dec_ctx->framerate.num) { + int fields = frame->repeat_pict + 2; + AVRational field_rate = av_mul_q(ist->dec_ctx->framerate, + (AVRational){ 2, 1 }); + codec_duration = av_rescale_q(fields, av_inv_q(field_rate), + frame->time_base); + } + + // prefer codec-layer duration for containers without timestamps + if (codec_duration > 0 && ifile->format_nots) + return codec_duration; + + // when timestamps are available, repeat last frame's actual duration + // (i.e. pts difference between this and last frame) + if (frame->pts != AV_NOPTS_VALUE && d->last_frame_pts != AV_NOPTS_VALUE && + frame->pts > d->last_frame_pts) + return frame->pts - d->last_frame_pts; + + // try frame/codec duration + if (frame->duration > 0) + return frame->duration; + if (codec_duration > 0) + return codec_duration; + + // try average framerate + if (ist->st->avg_frame_rate.num && ist->st->avg_frame_rate.den) { + int64_t d = av_rescale_q(1, av_inv_q(ist->st->avg_frame_rate), + frame->time_base); + if (d > 0) + return d; + } + + // last resort is last frame's estimated duration, and 1 + return FFMAX(d->last_frame_duration_est, 1); +} + +static int video_frame_process(InputStream *ist, AVFrame *frame) +{ + Decoder *d = ist->decoder; + + // The following line may be required in some cases where there is no parser + // or the parser does not has_b_frames correctly + if (ist->par->video_delay < ist->dec_ctx->has_b_frames) { + if (ist->dec_ctx->codec_id == AV_CODEC_ID_H264) { + ist->par->video_delay = ist->dec_ctx->has_b_frames; + } else + av_log(ist->dec_ctx, AV_LOG_WARNING, + "video_delay is larger in decoder than demuxer %d > %d.\n" + "If you want to help, upload a sample " + "of this file to https://streams.videolan.org/upload/ " + "and contact the ffmpeg-devel mailing list. (ffmpeg-devel@ffmpeg.org)\n", + ist->dec_ctx->has_b_frames, + ist->par->video_delay); + } + + if (ist->dec_ctx->width != frame->width || + ist->dec_ctx->height != frame->height || + ist->dec_ctx->pix_fmt != frame->format) { + av_log(NULL, AV_LOG_DEBUG, "Frame parameters mismatch context %d,%d,%d != %d,%d,%d\n", + frame->width, + frame->height, + frame->format, + ist->dec_ctx->width, + ist->dec_ctx->height, + ist->dec_ctx->pix_fmt); + } + + if(ist->top_field_first>=0) + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; + + if (frame->format == d->hwaccel_pix_fmt) { + int err = hwaccel_retrieve_data(ist->dec_ctx, frame); + if (err < 0) + return err; + } + + frame->pts = frame->best_effort_timestamp; + + // forced fixed framerate + if (ist->framerate.num) { + frame->pts = AV_NOPTS_VALUE; + frame->duration = 1; + frame->time_base = av_inv_q(ist->framerate); + } + + // no timestamp available - extrapolate from previous frame duration + if (frame->pts == AV_NOPTS_VALUE) + frame->pts = d->last_frame_pts == AV_NOPTS_VALUE ? 0 : + d->last_frame_pts + d->last_frame_duration_est; + + // update timestamp history + d->last_frame_duration_est = video_duration_estimate(ist, frame); + d->last_frame_pts = frame->pts; + d->last_frame_tb = frame->time_base; + + if (debug_ts) { + av_log(ist, AV_LOG_INFO, + "decoder -> pts:%s pts_time:%s " + "pkt_dts:%s pkt_dts_time:%s " + "duration:%s duration_time:%s " + "keyframe:%d frame_type:%d time_base:%d/%d\n", + av_ts2str(frame->pts), + av_ts2timestr(frame->pts, &frame->time_base), + av_ts2str(frame->pkt_dts), + av_ts2timestr(frame->pkt_dts, &frame->time_base), + av_ts2str(frame->duration), + av_ts2timestr(frame->duration, &frame->time_base), + !!(frame->flags & AV_FRAME_FLAG_KEY), frame->pict_type, + frame->time_base.num, frame->time_base.den); + } + + if (ist->st->sample_aspect_ratio.num) + frame->sample_aspect_ratio = ist->st->sample_aspect_ratio; + + return 0; +} + +static void sub2video_flush(InputStream *ist) +{ + for (int i = 0; i < ist->nb_filters; i++) { + int ret = ifilter_sub2video(ist->filters[i], NULL); + if (ret != AVERROR_EOF && ret < 0) + av_log(NULL, AV_LOG_WARNING, "Flush the frame error.\n"); + } +} + +static int process_subtitle(InputStream *ist, AVFrame *frame) +{ + Decoder *d = ist->decoder; + const AVSubtitle *subtitle = (AVSubtitle*)frame->buf[0]->data; + int ret = 0; + + if (ist->fix_sub_duration) { + AVSubtitle *sub_prev = d->sub_prev[0]->buf[0] ? + (AVSubtitle*)d->sub_prev[0]->buf[0]->data : NULL; + int end = 1; + if (sub_prev) { + end = av_rescale(subtitle->pts - sub_prev->pts, + 1000, AV_TIME_BASE); + if (end < sub_prev->end_display_time) { + av_log(NULL, AV_LOG_DEBUG, + "Subtitle duration reduced from %"PRId32" to %d%s\n", + sub_prev->end_display_time, end, + end <= 0 ? ", dropping it" : ""); + sub_prev->end_display_time = end; + } + } + + av_frame_unref(d->sub_prev[1]); + av_frame_move_ref(d->sub_prev[1], frame); + + frame = d->sub_prev[0]; + subtitle = frame->buf[0] ? (AVSubtitle*)frame->buf[0]->data : NULL; + + FFSWAP(AVFrame*, d->sub_prev[0], d->sub_prev[1]); + + if (end <= 0) + return 0; + } + + if (!subtitle) + return 0; + + for (int i = 0; i < ist->nb_filters; i++) { + ret = ifilter_sub2video(ist->filters[i], frame); + if (ret < 0) { + av_log(ist, AV_LOG_ERROR, "Error sending a subtitle for filtering: %s\n", + av_err2str(ret)); + return ret; + } + } + + subtitle = (AVSubtitle*)frame->buf[0]->data; + if (!subtitle->num_rects) + return 0; + + for (int oidx = 0; oidx < ist->nb_outputs; oidx++) { + OutputStream *ost = ist->outputs[oidx]; + if (!ost->enc || ost->type != AVMEDIA_TYPE_SUBTITLE) + continue; + + ret = enc_subtitle(output_files[ost->file_index], ost, subtitle); + if (ret < 0) + return ret; + } + + return 0; +} + +int fix_sub_duration_heartbeat(InputStream *ist, int64_t signal_pts) +{ + Decoder *d = ist->decoder; + int ret = AVERROR_BUG; + AVSubtitle *prev_subtitle = d->sub_prev[0]->buf[0] ? + (AVSubtitle*)d->sub_prev[0]->buf[0]->data : NULL; + AVSubtitle *subtitle; + + if (!ist->fix_sub_duration || !prev_subtitle || + !prev_subtitle->num_rects || signal_pts <= prev_subtitle->pts) + return 0; + + av_frame_unref(d->sub_heartbeat); + ret = subtitle_wrap_frame(d->sub_heartbeat, prev_subtitle, 1); + if (ret < 0) + return ret; + + subtitle = (AVSubtitle*)d->sub_heartbeat->buf[0]->data; + subtitle->pts = signal_pts; + + return process_subtitle(ist, d->sub_heartbeat); +} + +static int transcode_subtitles(InputStream *ist, const AVPacket *pkt, + AVFrame *frame) +{ + Decoder *d = ist->decoder; + AVPacket *flush_pkt = NULL; + AVSubtitle subtitle; + int got_output; + int ret; + + if (!pkt) { + flush_pkt = av_packet_alloc(); + if (!flush_pkt) + return AVERROR(ENOMEM); + } + + ret = avcodec_decode_subtitle2(ist->dec_ctx, &subtitle, &got_output, + pkt ? pkt : flush_pkt); + av_packet_free(&flush_pkt); + + if (ret < 0) { + av_log(ist, AV_LOG_ERROR, "Error decoding subtitles: %s\n", + av_err2str(ret)); + ist->decode_errors++; + return exit_on_error ? ret : 0; + } + + if (!got_output) + return pkt ? 0 : AVERROR_EOF; + + ist->frames_decoded++; + + // XXX the queue for transferring data back to the main thread runs + // on AVFrames, so we wrap AVSubtitle in an AVBufferRef and put that + // inside the frame + // eventually, subtitles should be switched to use AVFrames natively + ret = subtitle_wrap_frame(frame, &subtitle, 0); + if (ret < 0) { + avsubtitle_free(&subtitle); + return ret; + } + + frame->width = ist->dec_ctx->width; + frame->height = ist->dec_ctx->height; + + ret = tq_send(d->queue_out, 0, frame); + if (ret < 0) + av_frame_unref(frame); + + return ret; +} + +static int send_filter_eof(InputStream *ist) +{ + Decoder *d = ist->decoder; + int i, ret; + + for (i = 0; i < ist->nb_filters; i++) { + int64_t end_pts = d->last_frame_pts == AV_NOPTS_VALUE ? AV_NOPTS_VALUE : + d->last_frame_pts + d->last_frame_duration_est; + ret = ifilter_send_eof(ist->filters[i], end_pts, d->last_frame_tb); + if (ret < 0) + return ret; + } + return 0; +} + +static int packet_decode(InputStream *ist, const AVPacket *pkt, AVFrame *frame) +{ + Decoder *d = ist->decoder; + AVCodecContext *dec = ist->dec_ctx; + const char *type_desc = av_get_media_type_string(dec->codec_type); + int ret; + + if (dec->codec_type == AVMEDIA_TYPE_SUBTITLE) + return transcode_subtitles(ist, pkt, frame); + + // With fate-indeo3-2, we're getting 0-sized packets before EOF for some + // reason. This seems like a semi-critical bug. Don't trigger EOF, and + // skip the packet. + if (pkt && pkt->size == 0) + return 0; + + ret = avcodec_send_packet(dec, pkt); + if (ret < 0 && !(ret == AVERROR_EOF && !pkt)) { + // In particular, we don't expect AVERROR(EAGAIN), because we read all + // decoded frames with avcodec_receive_frame() until done. + if (ret == AVERROR(EAGAIN)) { + av_log(ist, AV_LOG_FATAL, "A decoder returned an unexpected error code. " + "This is a bug, please report it.\n"); + return AVERROR_BUG; + } + av_log(ist, AV_LOG_ERROR, "Error submitting %s to decoder: %s\n", + pkt ? "packet" : "EOF", av_err2str(ret)); + + if (ret != AVERROR_EOF) { + ist->decode_errors++; + if (!exit_on_error) + ret = 0; + } + + return ret; + } + + while (1) { + FrameData *fd; + + av_frame_unref(frame); + + update_benchmark(NULL); + ret = avcodec_receive_frame(dec, frame); + update_benchmark("decode_%s %d.%d", type_desc, + ist->file_index, ist->index); + + if (ret == AVERROR(EAGAIN)) { + av_assert0(pkt); // should never happen during flushing + return 0; + } else if (ret == AVERROR_EOF) { + return ret; + } else if (ret < 0) { + av_log(ist, AV_LOG_ERROR, "Decoding error: %s\n", av_err2str(ret)); + ist->decode_errors++; + + if (exit_on_error) + return ret; + + continue; + } + + if (frame->decode_error_flags || (frame->flags & AV_FRAME_FLAG_CORRUPT)) { + av_log(ist, exit_on_error ? AV_LOG_FATAL : AV_LOG_WARNING, + "corrupt decoded frame\n"); + if (exit_on_error) + return AVERROR_INVALIDDATA; + } + + + av_assert0(!frame->opaque_ref); + fd = frame_data(frame); + if (!fd) { + av_frame_unref(frame); + return AVERROR(ENOMEM); + } + fd->dec.pts = frame->pts; + fd->dec.tb = dec->pkt_timebase; + fd->dec.frame_num = dec->frame_num - 1; + fd->bits_per_raw_sample = dec->bits_per_raw_sample; + + frame->time_base = dec->pkt_timebase; + + if (dec->codec_type == AVMEDIA_TYPE_AUDIO) { + ist->samples_decoded += frame->nb_samples; + ist->nb_samples = frame->nb_samples; + + audio_ts_process(ist, ist->decoder, frame); + } else { + ret = video_frame_process(ist, frame); + if (ret < 0) { + av_log(NULL, AV_LOG_FATAL, "Error while processing the decoded " + "data for stream #%d:%d\n", ist->file_index, ist->index); + return ret; + } + } + + ist->frames_decoded++; + + ret = tq_send(d->queue_out, 0, frame); + if (ret < 0) + return ret; + } +} + +static void dec_thread_set_name(const InputStream *ist) +{ + char name[16]; + snprintf(name, sizeof(name), "dec%d:%d:%s", ist->file_index, ist->index, + ist->dec_ctx->codec->name); + ff_thread_setname(name); +} + +static void dec_thread_uninit(DecThreadContext *dt) +{ + av_packet_free(&dt->pkt); + av_frame_free(&dt->frame); + + memset(dt, 0, sizeof(*dt)); +} + +static int dec_thread_init(DecThreadContext *dt) +{ + memset(dt, 0, sizeof(*dt)); + + dt->frame = av_frame_alloc(); + if (!dt->frame) + goto fail; + + dt->pkt = av_packet_alloc(); + if (!dt->pkt) + goto fail; + + return 0; + +fail: + dec_thread_uninit(dt); + return AVERROR(ENOMEM); +} + +static void *decoder_thread(void *arg) +{ + InputStream *ist = arg; + InputFile *ifile = input_files[ist->file_index]; + Decoder *d = ist->decoder; + DecThreadContext dt; + int ret = 0, input_status = 0; + + ret = dec_thread_init(&dt); + if (ret < 0) + goto finish; + + dec_thread_set_name(ist); + + while (!input_status) { + int dummy, flush_buffers; + + input_status = tq_receive(d->queue_in, &dummy, dt.pkt); + flush_buffers = input_status >= 0 && !dt.pkt->buf; + if (!dt.pkt->buf) + av_log(ist, AV_LOG_VERBOSE, "Decoder thread received %s packet\n", + flush_buffers ? "flush" : "EOF"); + + ret = packet_decode(ist, dt.pkt->buf ? dt.pkt : NULL, dt.frame); + + av_packet_unref(dt.pkt); + av_frame_unref(dt.frame); + + if (ret == AVERROR_EOF) { + av_log(ist, AV_LOG_VERBOSE, "Decoder returned EOF, %s\n", + flush_buffers ? "resetting" : "finishing"); + + if (!flush_buffers) + break; + + /* report last frame duration to the demuxer thread */ + if (ist->dec->type == AVMEDIA_TYPE_AUDIO) { + LastFrameDuration dur; + + dur.stream_idx = ist->index; + dur.duration = av_rescale_q(ist->nb_samples, + (AVRational){ 1, ist->dec_ctx->sample_rate}, + ist->st->time_base); + + av_thread_message_queue_send(ifile->audio_duration_queue, &dur, 0); + } + + avcodec_flush_buffers(ist->dec_ctx); + } else if (ret < 0) { + av_log(ist, AV_LOG_ERROR, "Error processing packet in decoder: %s\n", + av_err2str(ret)); + break; + } + + // signal to the consumer thread that the entire packet was processed + ret = tq_send(d->queue_out, 0, dt.frame); + if (ret < 0) { + if (ret != AVERROR_EOF) + av_log(ist, AV_LOG_ERROR, "Error communicating with the main thread\n"); + break; + } + } + + // EOF is normal thread termination + if (ret == AVERROR_EOF) + ret = 0; + +finish: + tq_receive_finish(d->queue_in, 0); + tq_send_finish (d->queue_out, 0); + + // make sure the demuxer does not get stuck waiting for audio durations + // that will never arrive + if (ifile->audio_duration_queue && ist->dec->type == AVMEDIA_TYPE_AUDIO) + av_thread_message_queue_set_err_recv(ifile->audio_duration_queue, AVERROR_EOF); + + dec_thread_uninit(&dt); + + av_log(ist, AV_LOG_VERBOSE, "Terminating decoder thread\n"); + + return (void*)(intptr_t)ret; +} + +int dec_packet(InputStream *ist, const AVPacket *pkt, int no_eof) +{ + Decoder *d = ist->decoder; + int ret = 0, thread_ret; + + // thread already joined + if (!d->queue_in) + return AVERROR_EOF; + + // send the packet/flush request/EOF to the decoder thread + if (pkt || no_eof) { + av_packet_unref(d->pkt); + + if (pkt) { + ret = av_packet_ref(d->pkt, pkt); + if (ret < 0) + goto finish; + } + + ret = tq_send(d->queue_in, 0, d->pkt); + if (ret < 0) + goto finish; + } else + tq_send_finish(d->queue_in, 0); + + // retrieve all decoded data for the packet + while (1) { + int dummy; + + ret = tq_receive(d->queue_out, &dummy, d->frame); + if (ret < 0) + goto finish; + + // packet fully processed + if (!d->frame->buf[0]) + return 0; + + // process the decoded frame + if (ist->dec->type == AVMEDIA_TYPE_SUBTITLE) { + ret = process_subtitle(ist, d->frame); + } else { + ret = send_frame_to_filters(ist, d->frame); + } + av_frame_unref(d->frame); + if (ret < 0) + goto finish; + } + +finish: + thread_ret = dec_thread_stop(d); + if (thread_ret < 0) { + av_log(ist, AV_LOG_ERROR, "Decoder thread returned error: %s\n", + av_err2str(thread_ret)); + ret = err_merge(ret, thread_ret); + } + // non-EOF errors here are all fatal + if (ret < 0 && ret != AVERROR_EOF) + return ret; + + // signal EOF to our downstreams + if (ist->dec->type == AVMEDIA_TYPE_SUBTITLE) + sub2video_flush(ist); + else { + ret = send_filter_eof(ist); + if (ret < 0) { + av_log(NULL, AV_LOG_FATAL, "Error marking filters as finished\n"); + return ret; + } + } + + return AVERROR_EOF; +} + +static int dec_thread_start(InputStream *ist) +{ + Decoder *d = ist->decoder; + ObjPool *op; + int ret = 0; + + op = objpool_alloc_packets(); + if (!op) + return AVERROR(ENOMEM); + + d->queue_in = tq_alloc(1, 1, op, pkt_move); + if (!d->queue_in) { + objpool_free(&op); + return AVERROR(ENOMEM); + } + + op = objpool_alloc_frames(); + if (!op) + goto fail; + + d->queue_out = tq_alloc(1, 4, op, frame_move); + if (!d->queue_out) { + objpool_free(&op); + goto fail; + } + + ret = pthread_create(&d->thread, NULL, decoder_thread, ist); + if (ret) { + ret = AVERROR(ret); + av_log(ist, AV_LOG_ERROR, "pthread_create() failed: %s\n", + av_err2str(ret)); + goto fail; + } + + return 0; +fail: + if (ret >= 0) + ret = AVERROR(ENOMEM); + + tq_free(&d->queue_in); + tq_free(&d->queue_out); + return ret; +} + +static enum AVPixelFormat get_format(AVCodecContext *s, const enum AVPixelFormat *pix_fmts) +{ + InputStream *ist = s->opaque; + Decoder *d = ist->decoder; + const enum AVPixelFormat *p; + + for (p = pix_fmts; *p != AV_PIX_FMT_NONE; p++) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(*p); + const AVCodecHWConfig *config = NULL; + int i; + + if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) + break; + + if (ist->hwaccel_id == HWACCEL_GENERIC || + ist->hwaccel_id == HWACCEL_AUTO) { + for (i = 0;; i++) { + config = avcodec_get_hw_config(s->codec, i); + if (!config) + break; + if (!(config->methods & + AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)) + continue; + if (config->pix_fmt == *p) + break; + } + } + if (config && config->device_type == ist->hwaccel_device_type) { + d->hwaccel_pix_fmt = *p; + break; + } + } + + return *p; +} + +static HWDevice *hw_device_match_by_codec(const AVCodec *codec) +{ + const AVCodecHWConfig *config; + HWDevice *dev; + int i; + for (i = 0;; i++) { + config = avcodec_get_hw_config(codec, i); + if (!config) + return NULL; + if (!(config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)) + continue; + dev = hw_device_get_by_type(config->device_type); + if (dev) + return dev; + } +} + +static int hw_device_setup_for_decode(InputStream *ist) +{ + const AVCodecHWConfig *config; + enum AVHWDeviceType type; + HWDevice *dev = NULL; + int err, auto_device = 0; + + if (ist->hwaccel_device) { + dev = hw_device_get_by_name(ist->hwaccel_device); + if (!dev) { + if (ist->hwaccel_id == HWACCEL_AUTO) { + auto_device = 1; + } else if (ist->hwaccel_id == HWACCEL_GENERIC) { + type = ist->hwaccel_device_type; + err = hw_device_init_from_type(type, ist->hwaccel_device, + &dev); + } else { + // This will be dealt with by API-specific initialisation + // (using hwaccel_device), so nothing further needed here. + return 0; + } + } else { + if (ist->hwaccel_id == HWACCEL_AUTO) { + ist->hwaccel_device_type = dev->type; + } else if (ist->hwaccel_device_type != dev->type) { + av_log(NULL, AV_LOG_ERROR, "Invalid hwaccel device " + "specified for decoder: device %s of type %s is not " + "usable with hwaccel %s.\n", dev->name, + av_hwdevice_get_type_name(dev->type), + av_hwdevice_get_type_name(ist->hwaccel_device_type)); + return AVERROR(EINVAL); + } + } + } else { + if (ist->hwaccel_id == HWACCEL_AUTO) { + auto_device = 1; + } else if (ist->hwaccel_id == HWACCEL_GENERIC) { + type = ist->hwaccel_device_type; + dev = hw_device_get_by_type(type); + + // When "-qsv_device device" is used, an internal QSV device named + // as "__qsv_device" is created. Another QSV device is created too + // if "-init_hw_device qsv=name:device" is used. There are 2 QSV devices + // if both "-qsv_device device" and "-init_hw_device qsv=name:device" + // are used, hw_device_get_by_type(AV_HWDEVICE_TYPE_QSV) returns NULL. + // To keep back-compatibility with the removed ad-hoc libmfx setup code, + // call hw_device_get_by_name("__qsv_device") to select the internal QSV + // device. + if (!dev && type == AV_HWDEVICE_TYPE_QSV) + dev = hw_device_get_by_name("__qsv_device"); + + if (!dev) + err = hw_device_init_from_type(type, NULL, &dev); + } else { + dev = hw_device_match_by_codec(ist->dec); + if (!dev) { + // No device for this codec, but not using generic hwaccel + // and therefore may well not need one - ignore. + return 0; + } + } + } + + if (auto_device) { + int i; + if (!avcodec_get_hw_config(ist->dec, 0)) { + // Decoder does not support any hardware devices. + return 0; + } + for (i = 0; !dev; i++) { + config = avcodec_get_hw_config(ist->dec, i); + if (!config) + break; + type = config->device_type; + dev = hw_device_get_by_type(type); + if (dev) { + av_log(NULL, AV_LOG_INFO, "Using auto " + "hwaccel type %s with existing device %s.\n", + av_hwdevice_get_type_name(type), dev->name); + } + } + for (i = 0; !dev; i++) { + config = avcodec_get_hw_config(ist->dec, i); + if (!config) + break; + type = config->device_type; + // Try to make a new device of this type. + err = hw_device_init_from_type(type, ist->hwaccel_device, + &dev); + if (err < 0) { + // Can't make a device of this type. + continue; + } + if (ist->hwaccel_device) { + av_log(NULL, AV_LOG_INFO, "Using auto " + "hwaccel type %s with new device created " + "from %s.\n", av_hwdevice_get_type_name(type), + ist->hwaccel_device); + } else { + av_log(NULL, AV_LOG_INFO, "Using auto " + "hwaccel type %s with new default device.\n", + av_hwdevice_get_type_name(type)); + } + } + if (dev) { + ist->hwaccel_device_type = type; + } else { + av_log(NULL, AV_LOG_INFO, "Auto hwaccel " + "disabled: no device found.\n"); + ist->hwaccel_id = HWACCEL_NONE; + return 0; + } + } + + if (!dev) { + av_log(NULL, AV_LOG_ERROR, "No device available " + "for decoder: device type %s needed for codec %s.\n", + av_hwdevice_get_type_name(type), ist->dec->name); + return err; + } + + ist->dec_ctx->hw_device_ctx = av_buffer_ref(dev->device_ref); + if (!ist->dec_ctx->hw_device_ctx) + return AVERROR(ENOMEM); + + return 0; +} + +int dec_open(InputStream *ist) +{ + Decoder *d; + const AVCodec *codec = ist->dec; + int ret; + + if (!codec) { + av_log(ist, AV_LOG_ERROR, + "Decoding requested, but no decoder found for: %s\n", + avcodec_get_name(ist->dec_ctx->codec_id)); + return AVERROR(EINVAL); + } + + ret = dec_alloc(&ist->decoder); + if (ret < 0) + return ret; + d = ist->decoder; + + if (codec->type == AVMEDIA_TYPE_SUBTITLE && ist->fix_sub_duration) { + for (int i = 0; i < FF_ARRAY_ELEMS(d->sub_prev); i++) { + d->sub_prev[i] = av_frame_alloc(); + if (!d->sub_prev[i]) + return AVERROR(ENOMEM); + } + d->sub_heartbeat = av_frame_alloc(); + if (!d->sub_heartbeat) + return AVERROR(ENOMEM); + } + + ist->dec_ctx->opaque = ist; + ist->dec_ctx->get_format = get_format; + + if (ist->dec_ctx->codec_id == AV_CODEC_ID_DVB_SUBTITLE && + (ist->decoding_needed & DECODING_FOR_OST)) { + av_dict_set(&ist->decoder_opts, "compute_edt", "1", AV_DICT_DONT_OVERWRITE); + if (ist->decoding_needed & DECODING_FOR_FILTER) + av_log(NULL, AV_LOG_WARNING, "Warning using DVB subtitles for filtering and output at the same time is not fully supported, also see -compute_edt [0|1]\n"); + } + + /* Useful for subtitles retiming by lavf (FIXME), skipping samples in + * audio, and video decoders such as cuvid or mediacodec */ + ist->dec_ctx->pkt_timebase = ist->st->time_base; + + if (!av_dict_get(ist->decoder_opts, "threads", NULL, 0)) + av_dict_set(&ist->decoder_opts, "threads", "auto", 0); + /* Attached pics are sparse, therefore we would not want to delay their decoding till EOF. */ + if (ist->st->disposition & AV_DISPOSITION_ATTACHED_PIC) + av_dict_set(&ist->decoder_opts, "threads", "1", 0); + + ret = hw_device_setup_for_decode(ist); + if (ret < 0) { + av_log(ist, AV_LOG_ERROR, + "Hardware device setup failed for decoder: %s\n", + av_err2str(ret)); + return ret; + } + + if ((ret = avcodec_open2(ist->dec_ctx, codec, &ist->decoder_opts)) < 0) { + av_log(ist, AV_LOG_ERROR, "Error while opening decoder: %s\n", + av_err2str(ret)); + return ret; + } + + ret = check_avoptions(ist->decoder_opts); + if (ret < 0) + return ret; + + ret = dec_thread_start(ist); + if (ret < 0) { + av_log(ist, AV_LOG_ERROR, "Error starting decoder thread: %s\n", + av_err2str(ret)); + return ret; + } + + return 0; +} diff --git a/fftools/ffmpeg_demux.c b/fftools/ffmpeg_demux.c index 4b5c62b0d59..48edbd7f6b1 100644 --- a/fftools/ffmpeg_demux.c +++ b/fftools/ffmpeg_demux.c @@ -52,9 +52,50 @@ static const char *const opt_name_display_rotations[] = {"display_rotati static const char *const opt_name_display_hflips[] = {"display_hflip", NULL}; static const char *const opt_name_display_vflips[] = {"display_vflip", NULL}; +typedef struct DemuxStream { + InputStream ist; + + // name used for logging + char log_name[32]; + + double ts_scale; + + int streamcopy_needed; + + int wrap_correction_done; + int saw_first_ts; + ///< dts of the first packet read for this stream (in AV_TIME_BASE units) + int64_t first_dts; + + /* predicted dts of the next packet read for this stream or (when there are + * several frames in a packet) of the next frame in current packet (in AV_TIME_BASE units) */ + int64_t next_dts; + ///< dts of the last packet read for this stream (in AV_TIME_BASE units) + int64_t dts; + + int64_t min_pts; /* pts with the smallest value in a current stream */ + int64_t max_pts; /* pts with the higher value in a current stream */ + + /* number of packets successfully read for this stream */ + uint64_t nb_packets; + // combined size of all the packets read + uint64_t data_size; +} DemuxStream; + typedef struct Demuxer { InputFile f; + // name used for logging + char log_name[32]; + + int64_t wallclock_start; + + /** + * Extra timestamp offset added by discontinuity handling. + */ + int64_t ts_offset_discont; + int64_t last_ts; + /* number of times input stream should be looped */ int loop; /* actual duration of the longest stream in a file at the moment when @@ -66,53 +107,68 @@ typedef struct Demuxer { /* number of streams that the user was warned of */ int nb_streams_warn; + double readrate_initial_burst; + AVThreadMessageQueue *in_thread_queue; int thread_queue_size; pthread_t thread; int non_blocking; + + int read_started; } Demuxer; typedef struct DemuxMsg { AVPacket *pkt; int looping; - - // repeat_pict from the demuxer-internal parser - int repeat_pict; } DemuxMsg; +static DemuxStream *ds_from_ist(InputStream *ist) +{ + return (DemuxStream*)ist; +} + static Demuxer *demuxer_from_ifile(InputFile *f) { return (Demuxer*)f; } +InputStream *ist_find_unused(enum AVMediaType type) +{ + for (InputStream *ist = ist_iter(NULL); ist; ist = ist_iter(ist)) { + if (ist->par->codec_type == type && ist->discard && + ist->user_set_discard != AVDISCARD_ALL) + return ist; + } + return NULL; +} + static void report_new_stream(Demuxer *d, const AVPacket *pkt) { AVStream *st = d->f.ctx->streams[pkt->stream_index]; if (pkt->stream_index < d->nb_streams_warn) return; - av_log(NULL, AV_LOG_WARNING, - "New %s stream %d:%d at pos:%"PRId64" and DTS:%ss\n", + av_log(d, AV_LOG_WARNING, + "New %s stream with index %d at pos:%"PRId64" and DTS:%ss\n", av_get_media_type_string(st->codecpar->codec_type), - d->f.index, pkt->stream_index, - pkt->pos, av_ts2timestr(pkt->dts, &st->time_base)); + pkt->stream_index, pkt->pos, av_ts2timestr(pkt->dts, &st->time_base)); d->nb_streams_warn = pkt->stream_index + 1; } -static void ifile_duration_update(Demuxer *d, InputStream *ist, +static void ifile_duration_update(Demuxer *d, DemuxStream *ds, int64_t last_duration) { /* the total duration of the stream, max_pts - min_pts is * the duration of the stream without the last frame */ - if (ist->max_pts > ist->min_pts && - ist->max_pts - (uint64_t)ist->min_pts < INT64_MAX - last_duration) - last_duration += ist->max_pts - ist->min_pts; + if (ds->max_pts > ds->min_pts && + ds->max_pts - (uint64_t)ds->min_pts < INT64_MAX - last_duration) + last_duration += ds->max_pts - ds->min_pts; if (!d->duration || av_compare_ts(d->duration, d->time_base, - last_duration, ist->st->time_base) < 0) { + last_duration, ds->ist.st->time_base) < 0) { d->duration = last_duration; - d->time_base = ist->st->time_base; + d->time_base = ds->ist.st->time_base; } } @@ -120,7 +176,6 @@ static int seek_to_start(Demuxer *d) { InputFile *ifile = &d->f; AVFormatContext *is = ifile->ctx; - InputStream *ist; int ret; ret = avformat_seek_file(is, -1, INT64_MIN, is->start_time, is->start_time, 0); @@ -134,19 +189,21 @@ static int seek_to_start(Demuxer *d) int got_durations = 0; while (got_durations < ifile->audio_duration_queue_size) { + DemuxStream *ds; LastFrameDuration dur; ret = av_thread_message_queue_recv(ifile->audio_duration_queue, &dur, 0); if (ret < 0) return ret; got_durations++; - ist = ifile->streams[dur.stream_idx]; - ifile_duration_update(d, ist, dur.duration); + ds = ds_from_ist(ifile->streams[dur.stream_idx]); + ifile_duration_update(d, ds, dur.duration); } } else { for (int i = 0; i < ifile->nb_streams; i++) { int64_t duration = 0; - ist = ifile->streams[i]; + InputStream *ist = ifile->streams[i]; + DemuxStream *ds = ds_from_ist(ist); if (ist->framerate.num) { duration = av_rescale_q(1, av_inv_q(ist->framerate), ist->st->time_base); @@ -156,7 +213,7 @@ static int seek_to_start(Demuxer *d) duration = 1; } - ifile_duration_update(d, ist, duration); + ifile_duration_update(d, ds, duration); } } @@ -166,65 +223,338 @@ static int seek_to_start(Demuxer *d) return ret; } -static void ts_fixup(Demuxer *d, AVPacket *pkt, int *repeat_pict) +static void ts_discontinuity_detect(Demuxer *d, InputStream *ist, + AVPacket *pkt) +{ + InputFile *ifile = &d->f; + DemuxStream *ds = ds_from_ist(ist); + const int fmt_is_discont = ifile->ctx->iformat->flags & AVFMT_TS_DISCONT; + int disable_discontinuity_correction = copy_ts; + int64_t pkt_dts = av_rescale_q_rnd(pkt->dts, pkt->time_base, AV_TIME_BASE_Q, + AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); + + if (copy_ts && ds->next_dts != AV_NOPTS_VALUE && + fmt_is_discont && ist->st->pts_wrap_bits < 60) { + int64_t wrap_dts = av_rescale_q_rnd(pkt->dts + (1LL<st->pts_wrap_bits), + pkt->time_base, AV_TIME_BASE_Q, + AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX); + if (FFABS(wrap_dts - ds->next_dts) < FFABS(pkt_dts - ds->next_dts)/10) + disable_discontinuity_correction = 0; + } + + if (ds->next_dts != AV_NOPTS_VALUE && !disable_discontinuity_correction) { + int64_t delta = pkt_dts - ds->next_dts; + if (fmt_is_discont) { + if (FFABS(delta) > 1LL * dts_delta_threshold * AV_TIME_BASE || + pkt_dts + AV_TIME_BASE/10 < ds->dts) { + d->ts_offset_discont -= delta; + av_log(ist, AV_LOG_WARNING, + "timestamp discontinuity " + "(stream id=%d): %"PRId64", new offset= %"PRId64"\n", + ist->st->id, delta, d->ts_offset_discont); + pkt->dts -= av_rescale_q(delta, AV_TIME_BASE_Q, pkt->time_base); + if (pkt->pts != AV_NOPTS_VALUE) + pkt->pts -= av_rescale_q(delta, AV_TIME_BASE_Q, pkt->time_base); + } + } else { + if (FFABS(delta) > 1LL * dts_error_threshold * AV_TIME_BASE) { + av_log(NULL, AV_LOG_WARNING, + "DTS %"PRId64", next:%"PRId64" st:%d invalid dropping\n", + pkt->dts, ds->next_dts, pkt->stream_index); + pkt->dts = AV_NOPTS_VALUE; + } + if (pkt->pts != AV_NOPTS_VALUE){ + int64_t pkt_pts = av_rescale_q(pkt->pts, pkt->time_base, AV_TIME_BASE_Q); + delta = pkt_pts - ds->next_dts; + if (FFABS(delta) > 1LL * dts_error_threshold * AV_TIME_BASE) { + av_log(NULL, AV_LOG_WARNING, + "PTS %"PRId64", next:%"PRId64" invalid dropping st:%d\n", + pkt->pts, ds->next_dts, pkt->stream_index); + pkt->pts = AV_NOPTS_VALUE; + } + } + } + } else if (ds->next_dts == AV_NOPTS_VALUE && !copy_ts && + fmt_is_discont && d->last_ts != AV_NOPTS_VALUE) { + int64_t delta = pkt_dts - d->last_ts; + if (FFABS(delta) > 1LL * dts_delta_threshold * AV_TIME_BASE) { + d->ts_offset_discont -= delta; + av_log(NULL, AV_LOG_DEBUG, + "Inter stream timestamp discontinuity %"PRId64", new offset= %"PRId64"\n", + delta, d->ts_offset_discont); + pkt->dts -= av_rescale_q(delta, AV_TIME_BASE_Q, pkt->time_base); + if (pkt->pts != AV_NOPTS_VALUE) + pkt->pts -= av_rescale_q(delta, AV_TIME_BASE_Q, pkt->time_base); + } + } + + d->last_ts = av_rescale_q(pkt->dts, pkt->time_base, AV_TIME_BASE_Q); +} + +static void ts_discontinuity_process(Demuxer *d, InputStream *ist, + AVPacket *pkt) +{ + int64_t offset = av_rescale_q(d->ts_offset_discont, AV_TIME_BASE_Q, + pkt->time_base); + + // apply previously-detected timestamp-discontinuity offset + // (to all streams, not just audio/video) + if (pkt->dts != AV_NOPTS_VALUE) + pkt->dts += offset; + if (pkt->pts != AV_NOPTS_VALUE) + pkt->pts += offset; + + // detect timestamp discontinuities for audio/video + if ((ist->par->codec_type == AVMEDIA_TYPE_VIDEO || + ist->par->codec_type == AVMEDIA_TYPE_AUDIO) && + pkt->dts != AV_NOPTS_VALUE) + ts_discontinuity_detect(d, ist, pkt); +} + +static int ist_dts_update(DemuxStream *ds, AVPacket *pkt) +{ + InputStream *ist = &ds->ist; + const AVCodecParameters *par = ist->par; + + if (!ds->saw_first_ts) { + ds->first_dts = + ds->dts = ist->st->avg_frame_rate.num ? - ist->par->video_delay * AV_TIME_BASE / av_q2d(ist->st->avg_frame_rate) : 0; + if (pkt->pts != AV_NOPTS_VALUE) { + ds->first_dts = + ds->dts += av_rescale_q(pkt->pts, pkt->time_base, AV_TIME_BASE_Q); + } + ds->saw_first_ts = 1; + } + + if (ds->next_dts == AV_NOPTS_VALUE) + ds->next_dts = ds->dts; + + if (pkt->dts != AV_NOPTS_VALUE) + ds->next_dts = ds->dts = av_rescale_q(pkt->dts, pkt->time_base, AV_TIME_BASE_Q); + + ds->dts = ds->next_dts; + switch (par->codec_type) { + case AVMEDIA_TYPE_AUDIO: + av_assert1(pkt->duration >= 0); + if (par->sample_rate) { + ds->next_dts += ((int64_t)AV_TIME_BASE * par->frame_size) / + par->sample_rate; + } else { + ds->next_dts += av_rescale_q(pkt->duration, pkt->time_base, AV_TIME_BASE_Q); + } + break; + case AVMEDIA_TYPE_VIDEO: + if (ist->framerate.num) { + // TODO: Remove work-around for c99-to-c89 issue 7 + AVRational time_base_q = AV_TIME_BASE_Q; + int64_t next_dts = av_rescale_q(ds->next_dts, time_base_q, av_inv_q(ist->framerate)); + ds->next_dts = av_rescale_q(next_dts + 1, av_inv_q(ist->framerate), time_base_q); + } else if (pkt->duration) { + ds->next_dts += av_rescale_q(pkt->duration, pkt->time_base, AV_TIME_BASE_Q); + } else if (ist->par->framerate.num != 0) { + AVRational field_rate = av_mul_q(ist->par->framerate, + (AVRational){ 2, 1 }); + int fields = 2; + + if (ist->codec_desc && + (ist->codec_desc->props & AV_CODEC_PROP_FIELDS) && + av_stream_get_parser(ist->st)) + fields = 1 + av_stream_get_parser(ist->st)->repeat_pict; + + ds->next_dts += av_rescale_q(fields, av_inv_q(field_rate), AV_TIME_BASE_Q); + } + break; + } + + av_assert0(!pkt->opaque_ref); + if (ds->streamcopy_needed) { + DemuxPktData *pd; + + pkt->opaque_ref = av_buffer_allocz(sizeof(*pd)); + if (!pkt->opaque_ref) + return AVERROR(ENOMEM); + pd = (DemuxPktData*)pkt->opaque_ref->data; + + pd->dts_est = ds->dts; + } + + return 0; +} + +static int ts_fixup(Demuxer *d, AVPacket *pkt) { InputFile *ifile = &d->f; InputStream *ist = ifile->streams[pkt->stream_index]; + DemuxStream *ds = ds_from_ist(ist); const int64_t start_time = ifile->start_time_effective; int64_t duration; + int ret; - if (debug_ts) { - av_log(NULL, AV_LOG_INFO, "demuxer -> ist_index:%d:%d type:%s " - "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s duration:%s duration_time:%s\n", - ifile->index, pkt->stream_index, - av_get_media_type_string(ist->st->codecpar->codec_type), - av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &ist->st->time_base), - av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &ist->st->time_base), - av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, &ist->st->time_base)); + pkt->time_base = ist->st->time_base; + +#define SHOW_TS_DEBUG(tag_) \ + if (debug_ts) { \ + av_log(ist, AV_LOG_INFO, "%s -> ist_index:%d:%d type:%s " \ + "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s duration:%s duration_time:%s\n", \ + tag_, ifile->index, pkt->stream_index, \ + av_get_media_type_string(ist->st->codecpar->codec_type), \ + av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &pkt->time_base), \ + av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &pkt->time_base), \ + av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, &pkt->time_base)); \ } - if (!ist->wrap_correction_done && start_time != AV_NOPTS_VALUE && + SHOW_TS_DEBUG("demuxer"); + + if (!ds->wrap_correction_done && start_time != AV_NOPTS_VALUE && ist->st->pts_wrap_bits < 64) { int64_t stime, stime2; - stime = av_rescale_q(start_time, AV_TIME_BASE_Q, ist->st->time_base); + stime = av_rescale_q(start_time, AV_TIME_BASE_Q, pkt->time_base); stime2= stime + (1ULL<st->pts_wrap_bits); - ist->wrap_correction_done = 1; + ds->wrap_correction_done = 1; if(stime2 > stime && pkt->dts != AV_NOPTS_VALUE && pkt->dts > stime + (1LL<<(ist->st->pts_wrap_bits-1))) { pkt->dts -= 1ULL<st->pts_wrap_bits; - ist->wrap_correction_done = 0; + ds->wrap_correction_done = 0; } if(stime2 > stime && pkt->pts != AV_NOPTS_VALUE && pkt->pts > stime + (1LL<<(ist->st->pts_wrap_bits-1))) { pkt->pts -= 1ULL<st->pts_wrap_bits; - ist->wrap_correction_done = 0; + ds->wrap_correction_done = 0; } } if (pkt->dts != AV_NOPTS_VALUE) - pkt->dts += av_rescale_q(ifile->ts_offset, AV_TIME_BASE_Q, ist->st->time_base); + pkt->dts += av_rescale_q(ifile->ts_offset, AV_TIME_BASE_Q, pkt->time_base); if (pkt->pts != AV_NOPTS_VALUE) - pkt->pts += av_rescale_q(ifile->ts_offset, AV_TIME_BASE_Q, ist->st->time_base); + pkt->pts += av_rescale_q(ifile->ts_offset, AV_TIME_BASE_Q, pkt->time_base); if (pkt->pts != AV_NOPTS_VALUE) - pkt->pts *= ist->ts_scale; + pkt->pts *= ds->ts_scale; if (pkt->dts != AV_NOPTS_VALUE) - pkt->dts *= ist->ts_scale; + pkt->dts *= ds->ts_scale; - duration = av_rescale_q(d->duration, d->time_base, ist->st->time_base); + duration = av_rescale_q(d->duration, d->time_base, pkt->time_base); if (pkt->pts != AV_NOPTS_VALUE) { pkt->pts += duration; - ist->max_pts = FFMAX(pkt->pts, ist->max_pts); - ist->min_pts = FFMIN(pkt->pts, ist->min_pts); + ds->max_pts = FFMAX(pkt->pts, ds->max_pts); + ds->min_pts = FFMIN(pkt->pts, ds->min_pts); } if (pkt->dts != AV_NOPTS_VALUE) pkt->dts += duration; - *repeat_pict = -1; - if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && - av_stream_get_parser(ist->st)) - *repeat_pict = av_stream_get_parser(ist->st)->repeat_pict; + SHOW_TS_DEBUG("demuxer+tsfixup"); + + // detect and try to correct for timestamp discontinuities + ts_discontinuity_process(d, ist, pkt); + + // update estimated/predicted dts + ret = ist_dts_update(ds, pkt); + if (ret < 0) + return ret; + + return 0; +} + +// process an input packet into a message to send to the consumer thread +// src is always cleared by this function +static int input_packet_process(Demuxer *d, DemuxMsg *msg, AVPacket *src) +{ + InputFile *f = &d->f; + InputStream *ist = f->streams[src->stream_index]; + DemuxStream *ds = ds_from_ist(ist); + AVPacket *pkt; + int ret = 0; + + pkt = av_packet_alloc(); + if (!pkt) { + av_packet_unref(src); + return AVERROR(ENOMEM); + } + av_packet_move_ref(pkt, src); + + ret = ts_fixup(d, pkt); + if (ret < 0) + goto fail; + + ds->data_size += pkt->size; + ds->nb_packets++; + + /* add the stream-global side data to the first packet */ + if (ds->nb_packets == 1) { + for (int i = 0; i < ist->st->nb_side_data; i++) { + AVPacketSideData *src_sd = &ist->st->side_data[i]; + uint8_t *dst_data; + + if (src_sd->type == AV_PKT_DATA_DISPLAYMATRIX) + continue; + + if (av_packet_get_side_data(pkt, src_sd->type, NULL)) + continue; + + dst_data = av_packet_new_side_data(pkt, src_sd->type, src_sd->size); + if (!dst_data) { + ret = AVERROR(ENOMEM); + goto fail; + } + + memcpy(dst_data, src_sd->data, src_sd->size); + } + } + + if (debug_ts) { + av_log(NULL, AV_LOG_INFO, "demuxer+ffmpeg -> ist_index:%d:%d type:%s pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s duration:%s duration_time:%s off:%s off_time:%s\n", + f->index, pkt->stream_index, + av_get_media_type_string(ist->par->codec_type), + av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &pkt->time_base), + av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &pkt->time_base), + av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, &pkt->time_base), + av_ts2str(input_files[ist->file_index]->ts_offset), + av_ts2timestr(input_files[ist->file_index]->ts_offset, &AV_TIME_BASE_Q)); + } + + msg->pkt = pkt; + pkt = NULL; + +fail: + av_packet_free(&pkt); + + return ret; +} + +static void readrate_sleep(Demuxer *d) +{ + InputFile *f = &d->f; + int64_t file_start = copy_ts * ( + (f->start_time_effective != AV_NOPTS_VALUE ? f->start_time_effective * !start_at_zero : 0) + + (f->start_time != AV_NOPTS_VALUE ? f->start_time : 0) + ); + int64_t burst_until = AV_TIME_BASE * d->readrate_initial_burst; + for (int i = 0; i < f->nb_streams; i++) { + InputStream *ist = f->streams[i]; + DemuxStream *ds = ds_from_ist(ist); + int64_t stream_ts_offset, pts, now; + stream_ts_offset = FFMAX(ds->first_dts != AV_NOPTS_VALUE ? ds->first_dts : 0, file_start); + pts = av_rescale(ds->dts, 1000000, AV_TIME_BASE); + now = (av_gettime_relative() - d->wallclock_start) * f->readrate + stream_ts_offset; + if (pts - burst_until > now) + av_usleep(pts - burst_until - now); + } +} + +static void discard_unused_programs(InputFile *ifile) +{ + for (int j = 0; j < ifile->ctx->nb_programs; j++) { + AVProgram *p = ifile->ctx->programs[j]; + int discard = AVDISCARD_ALL; + + for (int k = 0; k < p->nb_stream_indexes; k++) + if (!ifile->streams[p->stream_index[k]]->discard) { + discard = AVDISCARD_DEFAULT; + break; + } + p->discard = discard; + } } static void thread_set_name(InputFile *f) @@ -250,6 +580,10 @@ static void *input_thread(void *arg) thread_set_name(f); + discard_unused_programs(f); + + d->wallclock_start = av_gettime_relative(); + while (1) { DemuxMsg msg = { NULL }; @@ -273,10 +607,10 @@ static void *input_thread(void *arg) } if (ret == AVERROR_EOF) - av_log(NULL, AV_LOG_VERBOSE, "EOF in input file %d\n", f->index); + av_log(d, AV_LOG_VERBOSE, "EOF while reading input\n"); else - av_log(NULL, AV_LOG_ERROR, "Error demuxing input file %d: %s\n", - f->index, av_err2str(ret)); + av_log(d, AV_LOG_ERROR, "Error during demuxing: %s\n", + av_err2str(ret)); break; } @@ -288,16 +622,17 @@ static void *input_thread(void *arg) /* the following test is needed in case new streams appear dynamically in stream : we ignore them */ - if (pkt->stream_index >= f->nb_streams) { + if (pkt->stream_index >= f->nb_streams || + f->streams[pkt->stream_index]->discard) { report_new_stream(d, pkt); av_packet_unref(pkt); continue; } if (pkt->flags & AV_PKT_FLAG_CORRUPT) { - av_log(NULL, exit_on_error ? AV_LOG_FATAL : AV_LOG_WARNING, - "%s: corrupt input packet in stream %d\n", - f->ctx->url, pkt->stream_index); + av_log(d, exit_on_error ? AV_LOG_FATAL : AV_LOG_WARNING, + "corrupt input packet in stream %d\n", + pkt->stream_index); if (exit_on_error) { av_packet_unref(pkt); ret = AVERROR_INVALIDDATA; @@ -305,27 +640,25 @@ static void *input_thread(void *arg) } } - ts_fixup(d, pkt, &msg.repeat_pict); - - msg.pkt = av_packet_alloc(); - if (!msg.pkt) { - av_packet_unref(pkt); - ret = AVERROR(ENOMEM); + ret = input_packet_process(d, &msg, pkt); + if (ret < 0) break; - } - av_packet_move_ref(msg.pkt, pkt); + + if (f->readrate) + readrate_sleep(d); + ret = av_thread_message_queue_send(d->in_thread_queue, &msg, flags); if (flags && ret == AVERROR(EAGAIN)) { flags = 0; ret = av_thread_message_queue_send(d->in_thread_queue, &msg, flags); - av_log(f->ctx, AV_LOG_WARNING, + av_log(f, AV_LOG_WARNING, "Thread message queue blocking; consider raising the " "thread_queue_size option (current value: %d)\n", d->thread_queue_size); } if (ret < 0) { if (ret != AVERROR_EOF) - av_log(f->ctx, AV_LOG_ERROR, + av_log(f, AV_LOG_ERROR, "Unable to send packet to main thread: %s\n", av_err2str(ret)); av_packet_free(&msg.pkt); @@ -339,7 +672,7 @@ static void *input_thread(void *arg) av_packet_free(&pkt); - av_log(NULL, AV_LOG_VERBOSE, "Terminating demuxer thread %d\n", f->index); + av_log(d, AV_LOG_VERBOSE, "Terminating demuxer thread\n"); return NULL; } @@ -396,11 +729,13 @@ static int thread_start(Demuxer *d) } if ((ret = pthread_create(&d->thread, NULL, input_thread, d))) { - av_log(NULL, AV_LOG_ERROR, "pthread_create failed: %s. Try to increase `ulimit -v` or decrease `ulimit -s`.\n", strerror(ret)); + av_log(d, AV_LOG_ERROR, "pthread_create failed: %s. Try to increase `ulimit -v` or decrease `ulimit -s`.\n", strerror(ret)); ret = AVERROR(ret); goto fail; } + d->read_started = 1; + return 0; fail: av_thread_message_queue_free(&d->in_thread_queue); @@ -410,7 +745,6 @@ static int thread_start(Demuxer *d) int ifile_get_packet(InputFile *f, AVPacket **pkt) { Demuxer *d = demuxer_from_ifile(f); - InputStream *ist; DemuxMsg msg; int ret; @@ -420,25 +754,6 @@ int ifile_get_packet(InputFile *f, AVPacket **pkt) return ret; } - if (f->readrate || f->rate_emu) { - int i; - int64_t file_start = copy_ts * ( - (f->start_time_effective != AV_NOPTS_VALUE ? f->start_time_effective * !start_at_zero : 0) + - (f->start_time != AV_NOPTS_VALUE ? f->start_time : 0) - ); - float scale = f->rate_emu ? 1.0 : f->readrate; - for (i = 0; i < f->nb_streams; i++) { - InputStream *ist = f->streams[i]; - int64_t stream_ts_offset, pts, now; - if (!ist->nb_packets || (ist->decoding_needed && !ist->got_output)) continue; - stream_ts_offset = FFMAX(ist->first_dts != AV_NOPTS_VALUE ? ist->first_dts : 0, file_start); - pts = av_rescale(ist->dts, 1000000, AV_TIME_BASE); - now = (av_gettime_relative() - ist->start) * scale + stream_ts_offset; - if (pts > now) - return AVERROR(EAGAIN); - } - } - ret = av_thread_message_queue_recv(d->in_thread_queue, &msg, d->non_blocking ? AV_THREAD_MESSAGE_NONBLOCK : 0); @@ -447,13 +762,50 @@ int ifile_get_packet(InputFile *f, AVPacket **pkt) if (msg.looping) return 1; - ist = f->streams[msg.pkt->stream_index]; - ist->last_pkt_repeat_pict = msg.repeat_pict; - *pkt = msg.pkt; return 0; } +static void demux_final_stats(Demuxer *d) +{ + InputFile *f = &d->f; + uint64_t total_packets = 0, total_size = 0; + + av_log(f, AV_LOG_VERBOSE, "Input file #%d (%s):\n", + f->index, f->ctx->url); + + for (int j = 0; j < f->nb_streams; j++) { + InputStream *ist = f->streams[j]; + DemuxStream *ds = ds_from_ist(ist); + enum AVMediaType type = ist->par->codec_type; + + if (ist->discard || type == AVMEDIA_TYPE_ATTACHMENT) + continue; + + total_size += ds->data_size; + total_packets += ds->nb_packets; + + av_log(f, AV_LOG_VERBOSE, " Input stream #%d:%d (%s): ", + f->index, j, av_get_media_type_string(type)); + av_log(f, AV_LOG_VERBOSE, "%"PRIu64" packets read (%"PRIu64" bytes); ", + ds->nb_packets, ds->data_size); + + if (ist->decoding_needed) { + av_log(f, AV_LOG_VERBOSE, + "%"PRIu64" frames decoded; %"PRIu64" decode errors", + ist->frames_decoded, ist->decode_errors); + if (type == AVMEDIA_TYPE_AUDIO) + av_log(f, AV_LOG_VERBOSE, " (%"PRIu64" samples)", ist->samples_decoded); + av_log(f, AV_LOG_VERBOSE, "; "); + } + + av_log(f, AV_LOG_VERBOSE, "\n"); + } + + av_log(f, AV_LOG_VERBOSE, " Total: %"PRIu64" packets (%"PRIu64" bytes) demuxed\n", + total_packets, total_size); +} + static void ist_free(InputStream **pist) { InputStream *ist = *pist; @@ -461,14 +813,12 @@ static void ist_free(InputStream **pist) if (!ist) return; - av_frame_free(&ist->decoded_frame); - av_packet_free(&ist->pkt); + dec_free(&ist->decoder); + av_dict_free(&ist->decoder_opts); - avsubtitle_free(&ist->prev_sub.subtitle); - av_frame_free(&ist->sub2video.frame); av_freep(&ist->filters); + av_freep(&ist->outputs); av_freep(&ist->hwaccel_device); - av_freep(&ist->dts_buffer); avcodec_free_context(&ist->dec_ctx); avcodec_parameters_free(&ist->par); @@ -486,6 +836,9 @@ void ifile_close(InputFile **pf) thread_stop(d); + if (d->read_started) + demux_final_stats(d); + for (int i = 0; i < f->nb_streams; i++) ist_free(&f->streams[i]); av_freep(&f->streams); @@ -495,19 +848,85 @@ void ifile_close(InputFile **pf) av_freep(pf); } -static const AVCodec *choose_decoder(const OptionsContext *o, AVFormatContext *s, AVStream *st, - enum HWAccelID hwaccel_id, enum AVHWDeviceType hwaccel_device_type) +static int ist_use(InputStream *ist, int decoding_needed) +{ + DemuxStream *ds = ds_from_ist(ist); + + if (ist->user_set_discard == AVDISCARD_ALL) { + av_log(ist, AV_LOG_ERROR, "Cannot %s a disabled input stream\n", + decoding_needed ? "decode" : "streamcopy"); + return AVERROR(EINVAL); + } + + ist->discard = 0; + ist->st->discard = ist->user_set_discard; + ist->decoding_needed |= decoding_needed; + ds->streamcopy_needed |= !decoding_needed; + + if (decoding_needed && !avcodec_is_open(ist->dec_ctx)) { + int ret = dec_open(ist); + if (ret < 0) + return ret; + } + + return 0; +} + +int ist_output_add(InputStream *ist, OutputStream *ost) +{ + int ret; + + ret = ist_use(ist, ost->enc ? DECODING_FOR_OST : 0); + if (ret < 0) + return ret; + + ret = GROW_ARRAY(ist->outputs, ist->nb_outputs); + if (ret < 0) + return ret; + + ist->outputs[ist->nb_outputs - 1] = ost; + + return 0; +} + +int ist_filter_add(InputStream *ist, InputFilter *ifilter, int is_simple) +{ + int ret; + + ret = ist_use(ist, is_simple ? DECODING_FOR_OST : DECODING_FOR_FILTER); + if (ret < 0) + return ret; + + ret = GROW_ARRAY(ist->filters, ist->nb_filters); + if (ret < 0) + return ret; + + ist->filters[ist->nb_filters - 1] = ifilter; + + // initialize fallback parameters for filtering + ret = ifilter_parameters_from_dec(ifilter, ist->dec_ctx); + if (ret < 0) + return ret; + + return 0; +} + +static int choose_decoder(const OptionsContext *o, AVFormatContext *s, AVStream *st, + enum HWAccelID hwaccel_id, enum AVHWDeviceType hwaccel_device_type, + const AVCodec **pcodec) { char *codec_name = NULL; MATCH_PER_STREAM_OPT(codec_names, str, codec_name, s, st); if (codec_name) { - const AVCodec *codec = find_codec_or_die(codec_name, st->codecpar->codec_type, 0); - st->codecpar->codec_id = codec->id; - if (recast_media && st->codecpar->codec_type != codec->type) - st->codecpar->codec_type = codec->type; - return codec; + int ret = find_codec(NULL, codec_name, st->codecpar->codec_type, 0, pcodec); + if (ret < 0) + return ret; + st->codecpar->codec_id = (*pcodec)->id; + if (recast_media && st->codecpar->codec_type != (*pcodec)->type) + st->codecpar->codec_type = (*pcodec)->type; + return 0; } else { if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && hwaccel_id == HWACCEL_GENERIC && @@ -526,38 +945,40 @@ static const AVCodec *choose_decoder(const OptionsContext *o, AVFormatContext *s if (config->device_type == hwaccel_device_type) { av_log(NULL, AV_LOG_VERBOSE, "Selecting decoder '%s' because of requested hwaccel method %s\n", c->name, av_hwdevice_get_type_name(hwaccel_device_type)); - return c; + *pcodec = c; + return 0; } } } } - return avcodec_find_decoder(st->codecpar->codec_id); + *pcodec = avcodec_find_decoder(st->codecpar->codec_id); + return 0; } } -static int guess_input_channel_layout(InputStream *ist) +static int guess_input_channel_layout(InputStream *ist, int guess_layout_max) { AVCodecContext *dec = ist->dec_ctx; if (dec->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) { char layout_name[256]; - if (dec->ch_layout.nb_channels > ist->guess_layout_max) + if (dec->ch_layout.nb_channels > guess_layout_max) return 0; av_channel_layout_default(&dec->ch_layout, dec->ch_layout.nb_channels); if (dec->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) return 0; av_channel_layout_describe(&dec->ch_layout, layout_name, sizeof(layout_name)); - av_log(NULL, AV_LOG_WARNING, "Guessed Channel Layout for Input Stream " - "#%d.%d : %s\n", ist->file_index, ist->st->index, layout_name); + av_log(ist, AV_LOG_WARNING, "Guessed Channel Layout: %s\n", layout_name); } return 1; } -static void add_display_matrix_to_stream(const OptionsContext *o, - AVFormatContext *ctx, AVStream *st) +static int add_display_matrix_to_stream(const OptionsContext *o, + AVFormatContext *ctx, InputStream *ist) { + AVStream *st = ist->st; double rotation = DBL_MAX; int hflip = -1, vflip = -1; int hflip_set = 0, vflip_set = 0, rotation_set = 0; @@ -572,12 +993,12 @@ static void add_display_matrix_to_stream(const OptionsContext *o, vflip_set = vflip != -1; if (!rotation_set && !hflip_set && !vflip_set) - return; + return 0; buf = (int32_t *)av_stream_new_side_data(st, AV_PKT_DATA_DISPLAYMATRIX, sizeof(int32_t) * 9); if (!buf) { - av_log(NULL, AV_LOG_FATAL, "Failed to generate a display matrix!\n"); - exit_program(1); + av_log(ist, AV_LOG_FATAL, "Failed to generate a display matrix!\n"); + return AVERROR(ENOMEM); } av_display_rotation_set(buf, @@ -586,257 +1007,360 @@ static void add_display_matrix_to_stream(const OptionsContext *o, av_display_matrix_flip(buf, hflip_set ? hflip : 0, vflip_set ? vflip : 0); + + return 0; } -/* Add all the streams from the given input file to the demuxer */ -static void add_input_streams(const OptionsContext *o, Demuxer *d) +static const char *input_stream_item_name(void *obj) { - InputFile *f = &d->f; - AVFormatContext *ic = f->ctx; - int i, ret; + const DemuxStream *ds = obj; - for (i = 0; i < ic->nb_streams; i++) { - AVStream *st = ic->streams[i]; - AVCodecParameters *par = st->codecpar; - InputStream *ist; - char *framerate = NULL, *hwaccel_device = NULL; - const char *hwaccel = NULL; - char *hwaccel_output_format = NULL; - char *codec_tag = NULL; - char *next; - char *discard_str = NULL; - const AVClass *cc = avcodec_get_class(); - const AVOption *discard_opt = av_opt_find(&cc, "skip_frame", NULL, - 0, AV_OPT_SEARCH_FAKE_OBJ); - - ist = ALLOC_ARRAY_ELEM(f->streams, f->nb_streams); - ist->st = st; - ist->file_index = f->index; - ist->discard = 1; - st->discard = AVDISCARD_ALL; - ist->nb_samples = 0; - ist->first_dts = AV_NOPTS_VALUE; - ist->min_pts = INT64_MAX; - ist->max_pts = INT64_MIN; - - ist->ts_scale = 1.0; - MATCH_PER_STREAM_OPT(ts_scale, dbl, ist->ts_scale, ic, st); - - ist->autorotate = 1; - MATCH_PER_STREAM_OPT(autorotate, i, ist->autorotate, ic, st); - - MATCH_PER_STREAM_OPT(codec_tags, str, codec_tag, ic, st); - if (codec_tag) { - uint32_t tag = strtol(codec_tag, &next, 0); - if (*next) - tag = AV_RL32(codec_tag); - st->codecpar->codec_tag = tag; - } + return ds->log_name; +} - if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { - add_display_matrix_to_stream(o, ic, st); +static const AVClass input_stream_class = { + .class_name = "InputStream", + .version = LIBAVUTIL_VERSION_INT, + .item_name = input_stream_item_name, + .category = AV_CLASS_CATEGORY_DEMUXER, +}; - MATCH_PER_STREAM_OPT(hwaccels, str, hwaccel, ic, st); - MATCH_PER_STREAM_OPT(hwaccel_output_formats, str, - hwaccel_output_format, ic, st); +static DemuxStream *demux_stream_alloc(Demuxer *d, AVStream *st) +{ + const char *type_str = av_get_media_type_string(st->codecpar->codec_type); + InputFile *f = &d->f; + DemuxStream *ds; - if (!hwaccel_output_format && hwaccel && !strcmp(hwaccel, "cuvid")) { - av_log(NULL, AV_LOG_WARNING, - "WARNING: defaulting hwaccel_output_format to cuda for compatibility " - "with old commandlines. This behaviour is DEPRECATED and will be removed " - "in the future. Please explicitly set \"-hwaccel_output_format cuda\".\n"); - ist->hwaccel_output_format = AV_PIX_FMT_CUDA; - } else if (!hwaccel_output_format && hwaccel && !strcmp(hwaccel, "qsv")) { - av_log(NULL, AV_LOG_WARNING, - "WARNING: defaulting hwaccel_output_format to qsv for compatibility " - "with old commandlines. This behaviour is DEPRECATED and will be removed " - "in the future. Please explicitly set \"-hwaccel_output_format qsv\".\n"); - ist->hwaccel_output_format = AV_PIX_FMT_QSV; - } else if (!hwaccel_output_format && hwaccel && !strcmp(hwaccel, "mediacodec")) { - // There is no real AVHWFrameContext implementation. Set - // hwaccel_output_format to avoid av_hwframe_transfer_data error. - ist->hwaccel_output_format = AV_PIX_FMT_MEDIACODEC; - } else if (hwaccel_output_format) { - ist->hwaccel_output_format = av_get_pix_fmt(hwaccel_output_format); - if (ist->hwaccel_output_format == AV_PIX_FMT_NONE) { - av_log(NULL, AV_LOG_FATAL, "Unrecognised hwaccel output " - "format: %s", hwaccel_output_format); - } - } else { - ist->hwaccel_output_format = AV_PIX_FMT_NONE; - } + ds = allocate_array_elem(&f->streams, sizeof(*ds), &f->nb_streams); + if (!ds) + return NULL; - if (hwaccel) { - // The NVDEC hwaccels use a CUDA device, so remap the name here. - if (!strcmp(hwaccel, "nvdec") || !strcmp(hwaccel, "cuvid")) - hwaccel = "cuda"; - - if (!strcmp(hwaccel, "none")) - ist->hwaccel_id = HWACCEL_NONE; - else if (!strcmp(hwaccel, "auto")) - ist->hwaccel_id = HWACCEL_AUTO; - else { - enum AVHWDeviceType type = av_hwdevice_find_type_by_name(hwaccel); - if (type != AV_HWDEVICE_TYPE_NONE) { - ist->hwaccel_id = HWACCEL_GENERIC; - ist->hwaccel_device_type = type; - } + ds->ist.st = st; + ds->ist.file_index = f->index; + ds->ist.index = st->index; + ds->ist.class = &input_stream_class; - if (!ist->hwaccel_id) { - av_log(NULL, AV_LOG_FATAL, "Unrecognized hwaccel: %s.\n", - hwaccel); - av_log(NULL, AV_LOG_FATAL, "Supported hwaccels: "); - type = AV_HWDEVICE_TYPE_NONE; - while ((type = av_hwdevice_iterate_types(type)) != - AV_HWDEVICE_TYPE_NONE) - av_log(NULL, AV_LOG_FATAL, "%s ", - av_hwdevice_get_type_name(type)); - av_log(NULL, AV_LOG_FATAL, "\n"); - exit_program(1); - } - } - } + snprintf(ds->log_name, sizeof(ds->log_name), "%cist#%d:%d/%s", + type_str ? *type_str : '?', d->f.index, st->index, + avcodec_get_name(st->codecpar->codec_id)); - MATCH_PER_STREAM_OPT(hwaccel_devices, str, hwaccel_device, ic, st); - if (hwaccel_device) { - ist->hwaccel_device = av_strdup(hwaccel_device); - if (!ist->hwaccel_device) - report_and_exit(AVERROR(ENOMEM)); - } + return ds; +} - ist->hwaccel_pix_fmt = AV_PIX_FMT_NONE; - } +static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st) +{ + AVFormatContext *ic = d->f.ctx; + AVCodecParameters *par = st->codecpar; + DemuxStream *ds; + InputStream *ist; + char *framerate = NULL, *hwaccel_device = NULL; + const char *hwaccel = NULL; + char *hwaccel_output_format = NULL; + char *codec_tag = NULL; + char *next; + char *discard_str = NULL; + const AVClass *cc = avcodec_get_class(); + const AVOption *discard_opt = av_opt_find(&cc, "skip_frame", NULL, + 0, AV_OPT_SEARCH_FAKE_OBJ); + int ret; + + ds = demux_stream_alloc(d, st); + if (!ds) + return AVERROR(ENOMEM); + + ist = &ds->ist; - ist->dec = choose_decoder(o, ic, st, ist->hwaccel_id, ist->hwaccel_device_type); - ist->decoder_opts = filter_codec_opts(o->g->codec_opts, ist->st->codecpar->codec_id, ic, st, ist->dec); + ist->discard = 1; + st->discard = AVDISCARD_ALL; + ist->nb_samples = 0; + ds->first_dts = AV_NOPTS_VALUE; + ds->next_dts = AV_NOPTS_VALUE; - ist->reinit_filters = -1; - MATCH_PER_STREAM_OPT(reinit_filters, i, ist->reinit_filters, ic, st); + ds->min_pts = INT64_MAX; + ds->max_pts = INT64_MIN; - MATCH_PER_STREAM_OPT(discard, str, discard_str, ic, st); - ist->user_set_discard = AVDISCARD_NONE; + ds->ts_scale = 1.0; + MATCH_PER_STREAM_OPT(ts_scale, dbl, ds->ts_scale, ic, st); - if ((o->video_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) || - (o->audio_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) || - (o->subtitle_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) || - (o->data_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_DATA)) - ist->user_set_discard = AVDISCARD_ALL; + ist->autorotate = 1; + MATCH_PER_STREAM_OPT(autorotate, i, ist->autorotate, ic, st); - if (discard_str && av_opt_eval_int(&cc, discard_opt, discard_str, &ist->user_set_discard) < 0) { - av_log(NULL, AV_LOG_ERROR, "Error parsing discard %s.\n", - discard_str); - exit_program(1); + MATCH_PER_STREAM_OPT(codec_tags, str, codec_tag, ic, st); + if (codec_tag) { + uint32_t tag = strtol(codec_tag, &next, 0); + if (*next) { + uint8_t buf[4] = { 0 }; + memcpy(buf, codec_tag, FFMIN(sizeof(buf), strlen(codec_tag))); + tag = AV_RL32(buf); } - ist->filter_in_rescale_delta_last = AV_NOPTS_VALUE; - ist->prev_pkt_pts = AV_NOPTS_VALUE; + st->codecpar->codec_tag = tag; + } - ist->dec_ctx = avcodec_alloc_context3(ist->dec); - if (!ist->dec_ctx) - report_and_exit(AVERROR(ENOMEM)); + if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + ret = add_display_matrix_to_stream(o, ic, ist); + if (ret < 0) + return ret; - ret = avcodec_parameters_to_context(ist->dec_ctx, par); - if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Error initializing the decoder context.\n"); - exit_program(1); + MATCH_PER_STREAM_OPT(hwaccels, str, hwaccel, ic, st); + MATCH_PER_STREAM_OPT(hwaccel_output_formats, str, + hwaccel_output_format, ic, st); + + if (!hwaccel_output_format && hwaccel && !strcmp(hwaccel, "cuvid")) { + av_log(ist, AV_LOG_WARNING, + "WARNING: defaulting hwaccel_output_format to cuda for compatibility " + "with old commandlines. This behaviour is DEPRECATED and will be removed " + "in the future. Please explicitly set \"-hwaccel_output_format cuda\".\n"); + ist->hwaccel_output_format = AV_PIX_FMT_CUDA; + } else if (!hwaccel_output_format && hwaccel && !strcmp(hwaccel, "qsv")) { + av_log(ist, AV_LOG_WARNING, + "WARNING: defaulting hwaccel_output_format to qsv for compatibility " + "with old commandlines. This behaviour is DEPRECATED and will be removed " + "in the future. Please explicitly set \"-hwaccel_output_format qsv\".\n"); + ist->hwaccel_output_format = AV_PIX_FMT_QSV; + } else if (!hwaccel_output_format && hwaccel && !strcmp(hwaccel, "mediacodec")) { + // There is no real AVHWFrameContext implementation. Set + // hwaccel_output_format to avoid av_hwframe_transfer_data error. + ist->hwaccel_output_format = AV_PIX_FMT_MEDIACODEC; + } else if (hwaccel_output_format) { + ist->hwaccel_output_format = av_get_pix_fmt(hwaccel_output_format); + if (ist->hwaccel_output_format == AV_PIX_FMT_NONE) { + av_log(ist, AV_LOG_FATAL, "Unrecognised hwaccel output " + "format: %s", hwaccel_output_format); + } + } else { + ist->hwaccel_output_format = AV_PIX_FMT_NONE; + } + + if (hwaccel) { + // The NVDEC hwaccels use a CUDA device, so remap the name here. + if (!strcmp(hwaccel, "nvdec") || !strcmp(hwaccel, "cuvid")) + hwaccel = "cuda"; + + if (!strcmp(hwaccel, "none")) + ist->hwaccel_id = HWACCEL_NONE; + else if (!strcmp(hwaccel, "auto")) + ist->hwaccel_id = HWACCEL_AUTO; + else { + enum AVHWDeviceType type = av_hwdevice_find_type_by_name(hwaccel); + if (type != AV_HWDEVICE_TYPE_NONE) { + ist->hwaccel_id = HWACCEL_GENERIC; + ist->hwaccel_device_type = type; + } + + if (!ist->hwaccel_id) { + av_log(ist, AV_LOG_FATAL, "Unrecognized hwaccel: %s.\n", + hwaccel); + av_log(ist, AV_LOG_FATAL, "Supported hwaccels: "); + type = AV_HWDEVICE_TYPE_NONE; + while ((type = av_hwdevice_iterate_types(type)) != + AV_HWDEVICE_TYPE_NONE) + av_log(ist, AV_LOG_FATAL, "%s ", + av_hwdevice_get_type_name(type)); + av_log(ist, AV_LOG_FATAL, "\n"); + return AVERROR(EINVAL); + } + } } - ist->decoded_frame = av_frame_alloc(); - if (!ist->decoded_frame) - report_and_exit(AVERROR(ENOMEM)); + MATCH_PER_STREAM_OPT(hwaccel_devices, str, hwaccel_device, ic, st); + if (hwaccel_device) { + ist->hwaccel_device = av_strdup(hwaccel_device); + if (!ist->hwaccel_device) + return AVERROR(ENOMEM); + } + } + + ret = choose_decoder(o, ic, st, ist->hwaccel_id, ist->hwaccel_device_type, + &ist->dec); + if (ret < 0) + return ret; + + ret = filter_codec_opts(o->g->codec_opts, ist->st->codecpar->codec_id, + ic, st, ist->dec, &ist->decoder_opts); + if (ret < 0) + return ret; + + ist->reinit_filters = -1; + MATCH_PER_STREAM_OPT(reinit_filters, i, ist->reinit_filters, ic, st); - ist->pkt = av_packet_alloc(); - if (!ist->pkt) - report_and_exit(AVERROR(ENOMEM)); + MATCH_PER_STREAM_OPT(discard, str, discard_str, ic, st); + ist->user_set_discard = AVDISCARD_NONE; - if (o->bitexact) - ist->dec_ctx->flags |= AV_CODEC_FLAG_BITEXACT; + if ((o->video_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) || + (o->audio_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) || + (o->subtitle_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) || + (o->data_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_DATA)) + ist->user_set_discard = AVDISCARD_ALL; - switch (par->codec_type) { - case AVMEDIA_TYPE_VIDEO: - // avformat_find_stream_info() doesn't set this for us anymore. - ist->dec_ctx->framerate = st->avg_frame_rate; + if (discard_str) { + ret = av_opt_eval_int(&cc, discard_opt, discard_str, &ist->user_set_discard); + if (ret < 0) { + av_log(ist, AV_LOG_ERROR, "Error parsing discard %s.\n", discard_str); + return ret; + } + } + + ist->dec_ctx = avcodec_alloc_context3(ist->dec); + if (!ist->dec_ctx) + return AVERROR(ENOMEM); + + ret = avcodec_parameters_to_context(ist->dec_ctx, par); + if (ret < 0) { + av_log(ist, AV_LOG_ERROR, "Error initializing the decoder context.\n"); + return ret; + } - MATCH_PER_STREAM_OPT(frame_rates, str, framerate, ic, st); - if (framerate && av_parse_video_rate(&ist->framerate, - framerate) < 0) { - av_log(NULL, AV_LOG_ERROR, "Error parsing framerate %s.\n", + if (o->bitexact) + ist->dec_ctx->flags |= AV_CODEC_FLAG_BITEXACT; + + switch (par->codec_type) { + case AVMEDIA_TYPE_VIDEO: + MATCH_PER_STREAM_OPT(frame_rates, str, framerate, ic, st); + if (framerate) { + ret = av_parse_video_rate(&ist->framerate, framerate); + if (ret < 0) { + av_log(ist, AV_LOG_ERROR, "Error parsing framerate %s.\n", framerate); - exit_program(1); + return ret; } + } - ist->top_field_first = -1; - MATCH_PER_STREAM_OPT(top_field_first, i, ist->top_field_first, ic, st); + ist->top_field_first = -1; + MATCH_PER_STREAM_OPT(top_field_first, i, ist->top_field_first, ic, st); - ist->framerate_guessed = av_guess_frame_rate(ic, st, NULL); + ist->framerate_guessed = av_guess_frame_rate(ic, st, NULL); - break; - case AVMEDIA_TYPE_AUDIO: - ist->guess_layout_max = INT_MAX; - MATCH_PER_STREAM_OPT(guess_layout_max, i, ist->guess_layout_max, ic, st); - guess_input_channel_layout(ist); - break; - case AVMEDIA_TYPE_DATA: - case AVMEDIA_TYPE_SUBTITLE: { - char *canvas_size = NULL; - MATCH_PER_STREAM_OPT(fix_sub_duration, i, ist->fix_sub_duration, ic, st); - MATCH_PER_STREAM_OPT(canvas_sizes, str, canvas_size, ic, st); - if (canvas_size && - av_parse_video_size(&ist->dec_ctx->width, &ist->dec_ctx->height, canvas_size) < 0) { - av_log(NULL, AV_LOG_FATAL, "Invalid canvas size: %s.\n", canvas_size); - exit_program(1); + break; + case AVMEDIA_TYPE_AUDIO: { + int guess_layout_max = INT_MAX; + MATCH_PER_STREAM_OPT(guess_layout_max, i, guess_layout_max, ic, st); + guess_input_channel_layout(ist, guess_layout_max); + break; + } + case AVMEDIA_TYPE_DATA: + case AVMEDIA_TYPE_SUBTITLE: { + char *canvas_size = NULL; + MATCH_PER_STREAM_OPT(fix_sub_duration, i, ist->fix_sub_duration, ic, st); + MATCH_PER_STREAM_OPT(canvas_sizes, str, canvas_size, ic, st); + if (canvas_size) { + ret = av_parse_video_size(&ist->dec_ctx->width, &ist->dec_ctx->height, + canvas_size); + if (ret < 0) { + av_log(ist, AV_LOG_FATAL, "Invalid canvas size: %s.\n", canvas_size); + return ret; } - break; - } - case AVMEDIA_TYPE_ATTACHMENT: - case AVMEDIA_TYPE_UNKNOWN: - break; - default: - abort(); } - ist->par = avcodec_parameters_alloc(); - if (!ist->par) - report_and_exit(AVERROR(ENOMEM)); + /* Compute the size of the canvas for the subtitles stream. + If the subtitles codecpar has set a size, use it. Otherwise use the + maximum dimensions of the video streams in the same file. */ + ist->sub2video.w = ist->dec_ctx->width; + ist->sub2video.h = ist->dec_ctx->height; + if (!(ist->sub2video.w && ist->sub2video.h)) { + for (int j = 0; j < ic->nb_streams; j++) { + AVCodecParameters *par1 = ic->streams[j]->codecpar; + if (par1->codec_type == AVMEDIA_TYPE_VIDEO) { + ist->sub2video.w = FFMAX(ist->sub2video.w, par1->width); + ist->sub2video.h = FFMAX(ist->sub2video.h, par1->height); + } + } + } - ret = avcodec_parameters_from_context(ist->par, ist->dec_ctx); - if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Error initializing the decoder context.\n"); - exit_program(1); + if (!(ist->sub2video.w && ist->sub2video.h)) { + ist->sub2video.w = FFMAX(ist->sub2video.w, 720); + ist->sub2video.h = FFMAX(ist->sub2video.h, 576); } + + break; + } + case AVMEDIA_TYPE_ATTACHMENT: + case AVMEDIA_TYPE_UNKNOWN: + break; + default: + abort(); + } + + ist->par = avcodec_parameters_alloc(); + if (!ist->par) + return AVERROR(ENOMEM); + + ret = avcodec_parameters_from_context(ist->par, ist->dec_ctx); + if (ret < 0) { + av_log(ist, AV_LOG_ERROR, "Error initializing the decoder context.\n"); + return ret; } + + ist->codec_desc = avcodec_descriptor_get(ist->par->codec_id); + + return 0; } -static void dump_attachment(AVStream *st, const char *filename) +static int dump_attachment(InputStream *ist, const char *filename) { + AVStream *st = ist->st; int ret; AVIOContext *out = NULL; const AVDictionaryEntry *e; if (!st->codecpar->extradata_size) { - av_log(NULL, AV_LOG_WARNING, "No extradata to dump in stream #%d:%d.\n", - nb_input_files - 1, st->index); - return; + av_log(ist, AV_LOG_WARNING, "No extradata to dump.\n"); + return 0; } if (!*filename && (e = av_dict_get(st->metadata, "filename", NULL, 0))) filename = e->value; if (!*filename) { - av_log(NULL, AV_LOG_FATAL, "No filename specified and no 'filename' tag" - "in stream #%d:%d.\n", nb_input_files - 1, st->index); - exit_program(1); + av_log(ist, AV_LOG_FATAL, "No filename specified and no 'filename' tag"); + return AVERROR(EINVAL); } - assert_file_overwrite(filename); + ret = assert_file_overwrite(filename); + if (ret < 0) + return ret; if ((ret = avio_open2(&out, filename, AVIO_FLAG_WRITE, &int_cb, NULL)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Could not open file %s for writing.\n", + av_log(ist, AV_LOG_FATAL, "Could not open file %s for writing.\n", filename); - exit_program(1); + return ret; } avio_write(out, st->codecpar->extradata, st->codecpar->extradata_size); - avio_flush(out); - avio_close(out); + ret = avio_close(out); + + if (ret >= 0) + av_log(ist, AV_LOG_INFO, "Wrote attachment (%d bytes) to '%s'\n", + st->codecpar->extradata_size, filename); + + return ret; +} + +static const char *input_file_item_name(void *obj) +{ + const Demuxer *d = obj; + + return d->log_name; +} + +static const AVClass input_file_class = { + .class_name = "InputFile", + .version = LIBAVUTIL_VERSION_INT, + .item_name = input_file_item_name, + .category = AV_CLASS_CATEGORY_DEMUXER, +}; + +static Demuxer *demux_alloc(void) +{ + Demuxer *d = allocate_array_elem(&input_files, sizeof(*d), &nb_input_files); + + if (!d) + return NULL; + + d->f.class = &input_file_class; + d->f.index = nb_input_files - 1; + + snprintf(d->log_name, sizeof(d->log_name), "in#%d", d->f.index); + + return d; } int ifile_open(const OptionsContext *o, const char *filename) @@ -845,7 +1369,7 @@ int ifile_open(const OptionsContext *o, const char *filename) InputFile *f; AVFormatContext *ic; const AVInputFormat *file_iformat = NULL; - int err, i, ret; + int err, i, ret = 0; int64_t timestamp; AVDictionary *unused_opts = NULL; const AVDictionaryEntry *e = NULL; @@ -860,16 +1384,22 @@ int ifile_open(const OptionsContext *o, const char *filename) int64_t stop_time = o->stop_time; int64_t recording_time = o->recording_time; + d = demux_alloc(); + if (!d) + return AVERROR(ENOMEM); + + f = &d->f; + if (stop_time != INT64_MAX && recording_time != INT64_MAX) { stop_time = INT64_MAX; - av_log(NULL, AV_LOG_WARNING, "-t and -to cannot be used together; using -t.\n"); + av_log(d, AV_LOG_WARNING, "-t and -to cannot be used together; using -t.\n"); } if (stop_time != INT64_MAX && recording_time == INT64_MAX) { int64_t start = start_time == AV_NOPTS_VALUE ? 0 : start_time; if (stop_time <= start) { - av_log(NULL, AV_LOG_ERROR, "-to value smaller than -ss; aborting.\n"); - exit_program(1); + av_log(d, AV_LOG_ERROR, "-to value smaller than -ss; aborting.\n"); + return AVERROR(EINVAL); } else { recording_time = stop_time - start; } @@ -877,8 +1407,8 @@ int ifile_open(const OptionsContext *o, const char *filename) if (o->format) { if (!(file_iformat = av_find_input_format(o->format))) { - av_log(NULL, AV_LOG_FATAL, "Unknown input format: '%s'\n", o->format); - exit_program(1); + av_log(d, AV_LOG_FATAL, "Unknown input format: '%s'\n", o->format); + return AVERROR(EINVAL); } } @@ -892,7 +1422,7 @@ int ifile_open(const OptionsContext *o, const char *filename) /* get default parameters from command line */ ic = avformat_alloc_context(); if (!ic) - report_and_exit(AVERROR(ENOMEM)); + return AVERROR(ENOMEM); if (o->nb_audio_sample_rate) { av_dict_set_int(&o->g->format_opts, "sample_rate", o->audio_sample_rate[o->nb_audio_sample_rate - 1].u.i, 0); } @@ -937,13 +1467,19 @@ int ifile_open(const OptionsContext *o, const char *filename) MATCH_PER_TYPE_OPT(codec_names, str, data_codec_name, ic, "d"); if (video_codec_name) - ic->video_codec = find_codec_or_die(video_codec_name , AVMEDIA_TYPE_VIDEO , 0); + ret = err_merge(ret, find_codec(NULL, video_codec_name , AVMEDIA_TYPE_VIDEO , 0, + &ic->video_codec)); if (audio_codec_name) - ic->audio_codec = find_codec_or_die(audio_codec_name , AVMEDIA_TYPE_AUDIO , 0); + ret = err_merge(ret, find_codec(NULL, audio_codec_name , AVMEDIA_TYPE_AUDIO , 0, + &ic->audio_codec)); if (subtitle_codec_name) - ic->subtitle_codec = find_codec_or_die(subtitle_codec_name, AVMEDIA_TYPE_SUBTITLE, 0); + ret = err_merge(ret, find_codec(NULL, subtitle_codec_name, AVMEDIA_TYPE_SUBTITLE, 0, + &ic->subtitle_codec)); if (data_codec_name) - ic->data_codec = find_codec_or_die(data_codec_name , AVMEDIA_TYPE_DATA , 0); + ret = err_merge(ret, find_codec(NULL, data_codec_name , AVMEDIA_TYPE_DATA, 0, + &ic->data_codec)); + if (ret < 0) + return ret; ic->video_codec_id = video_codec_name ? ic->video_codec->id : AV_CODEC_ID_NONE; ic->audio_codec_id = audio_codec_name ? ic->audio_codec->id : AV_CODEC_ID_NONE; @@ -962,24 +1498,41 @@ int ifile_open(const OptionsContext *o, const char *filename) /* open the input file with generic avformat function */ err = avformat_open_input(&ic, filename, file_iformat, &o->g->format_opts); if (err < 0) { - print_error(filename, err); + av_log(d, AV_LOG_ERROR, + "Error opening input: %s\n", av_err2str(err)); if (err == AVERROR_PROTOCOL_NOT_FOUND) - av_log(NULL, AV_LOG_ERROR, "Did you mean file:%s?\n", filename); - exit_program(1); + av_log(d, AV_LOG_ERROR, "Did you mean file:%s?\n", filename); + return err; } + + av_strlcat(d->log_name, "/", sizeof(d->log_name)); + av_strlcat(d->log_name, ic->iformat->name, sizeof(d->log_name)); + if (scan_all_pmts_set) av_dict_set(&o->g->format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE); remove_avoptions(&o->g->format_opts, o->g->codec_opts); - assert_avoptions(o->g->format_opts); + + ret = check_avoptions(o->g->format_opts); + if (ret < 0) + return ret; /* apply forced codec ids */ - for (i = 0; i < ic->nb_streams; i++) - choose_decoder(o, ic, ic->streams[i], HWACCEL_NONE, AV_HWDEVICE_TYPE_NONE); + for (i = 0; i < ic->nb_streams; i++) { + const AVCodec *dummy; + ret = choose_decoder(o, ic, ic->streams[i], HWACCEL_NONE, AV_HWDEVICE_TYPE_NONE, + &dummy); + if (ret < 0) + return ret; + } if (o->find_stream_info) { - AVDictionary **opts = setup_find_stream_info_opts(ic, o->g->codec_opts); + AVDictionary **opts; int orig_nb_streams = ic->nb_streams; + ret = setup_find_stream_info_opts(ic, o->g->codec_opts, &opts); + if (ret < 0) + return ret; + /* If not enough info to get the stream parameters, we decode the first frames to get it. (used in mpeg case for example) */ ret = avformat_find_stream_info(ic, opts); @@ -989,32 +1542,32 @@ int ifile_open(const OptionsContext *o, const char *filename) av_freep(&opts); if (ret < 0) { - av_log(NULL, AV_LOG_FATAL, "%s: could not find codec parameters\n", filename); + av_log(d, AV_LOG_FATAL, "could not find codec parameters\n"); if (ic->nb_streams == 0) { avformat_close_input(&ic); - exit_program(1); + return ret; } } } if (start_time != AV_NOPTS_VALUE && start_time_eof != AV_NOPTS_VALUE) { - av_log(NULL, AV_LOG_WARNING, "Cannot use -ss and -sseof both, using -ss for %s\n", filename); + av_log(d, AV_LOG_WARNING, "Cannot use -ss and -sseof both, using -ss\n"); start_time_eof = AV_NOPTS_VALUE; } if (start_time_eof != AV_NOPTS_VALUE) { if (start_time_eof >= 0) { - av_log(NULL, AV_LOG_ERROR, "-sseof value must be negative; aborting\n"); - exit_program(1); + av_log(d, AV_LOG_ERROR, "-sseof value must be negative; aborting\n"); + return AVERROR(EINVAL); } if (ic->duration > 0) { start_time = start_time_eof + ic->duration; if (start_time < 0) { - av_log(NULL, AV_LOG_WARNING, "-sseof value seeks to before start of file %s; ignored\n", filename); + av_log(d, AV_LOG_WARNING, "-sseof value seeks to before start of file; ignored\n"); start_time = AV_NOPTS_VALUE; } } else - av_log(NULL, AV_LOG_WARNING, "Cannot use -sseof, duration of %s not known\n", filename); + av_log(d, AV_LOG_WARNING, "Cannot use -sseof, file duration not known\n"); } timestamp = (start_time == AV_NOPTS_VALUE) ? 0 : start_time; /* add the stream start time */ @@ -1040,41 +1593,58 @@ int ifile_open(const OptionsContext *o, const char *filename) } ret = avformat_seek_file(ic, -1, INT64_MIN, seek_timestamp, seek_timestamp, 0); if (ret < 0) { - av_log(NULL, AV_LOG_WARNING, "%s: could not seek to position %0.3f\n", - filename, (double)timestamp / AV_TIME_BASE); + av_log(d, AV_LOG_WARNING, "could not seek to position %0.3f\n", + (double)timestamp / AV_TIME_BASE); } } - d = allocate_array_elem(&input_files, sizeof(*d), &nb_input_files); - f = &d->f; - f->ctx = ic; - f->index = nb_input_files - 1; f->start_time = start_time; f->recording_time = recording_time; f->input_sync_ref = o->input_sync_ref; f->input_ts_offset = o->input_ts_offset; f->ts_offset = o->input_ts_offset - (copy_ts ? (start_at_zero && ic->start_time != AV_NOPTS_VALUE ? ic->start_time : 0) : timestamp); - f->rate_emu = o->rate_emu; f->accurate_seek = o->accurate_seek; d->loop = o->loop; d->duration = 0; d->time_base = (AVRational){ 1, 1 }; + d->nb_streams_warn = ic->nb_streams; + + f->format_nots = !!(ic->iformat->flags & AVFMT_NOTIMESTAMPS); f->readrate = o->readrate ? o->readrate : 0.0; if (f->readrate < 0.0f) { - av_log(NULL, AV_LOG_ERROR, "Option -readrate for Input #%d is %0.3f; it must be non-negative.\n", f->index, f->readrate); - exit_program(1); + av_log(d, AV_LOG_ERROR, "Option -readrate is %0.3f; it must be non-negative.\n", f->readrate); + return AVERROR(EINVAL); + } + if (o->rate_emu) { + if (f->readrate) { + av_log(d, AV_LOG_WARNING, "Both -readrate and -re set. Using -readrate %0.3f.\n", f->readrate); + } else + f->readrate = 1.0f; } - if (f->readrate && f->rate_emu) { - av_log(NULL, AV_LOG_WARNING, "Both -readrate and -re set for Input #%d. Using -readrate %0.3f.\n", f->index, f->readrate); - f->rate_emu = 0; + + if (f->readrate) { + d->readrate_initial_burst = o->readrate_initial_burst ? o->readrate_initial_burst : 0.5; + if (d->readrate_initial_burst < 0.0) { + av_log(d, AV_LOG_ERROR, + "Option -readrate_initial_burst is %0.3f; it must be non-negative.\n", + d->readrate_initial_burst); + return AVERROR(EINVAL); + } + } else if (o->readrate_initial_burst) { + av_log(d, AV_LOG_WARNING, "Option -readrate_initial_burst ignored " + "since neither -readrate nor -re were given\n"); } d->thread_queue_size = o->thread_queue_size; - /* update the current parameters so that they match the one of the input stream */ - add_input_streams(o, d); + /* Add all the streams from the given input file to the demuxer */ + for (int i = 0; i < ic->nb_streams; i++) { + ret = ist_add(o, d, ic->streams[i]); + if (ret < 0) + return ret; + } /* dump the file content */ av_dump_format(ic, f->index, filename, 0); @@ -1100,30 +1670,30 @@ int ifile_open(const OptionsContext *o, const char *filename) if (!(option->flags & AV_OPT_FLAG_DECODING_PARAM)) { - av_log(NULL, AV_LOG_ERROR, "Codec AVOption %s (%s) specified for " - "input file #%d (%s) is not a decoding option.\n", e->key, - option->help ? option->help : "", f->index, - filename); - exit_program(1); + av_log(d, AV_LOG_ERROR, "Codec AVOption %s (%s) is not a decoding " + "option.\n", e->key, option->help ? option->help : ""); + return AVERROR(EINVAL); } - av_log(NULL, AV_LOG_WARNING, "Codec AVOption %s (%s) specified for " - "input file #%d (%s) has not been used for any stream. The most " - "likely reason is either wrong type (e.g. a video option with " - "no video streams) or that it is a private option of some decoder " - "which was not actually used for any stream.\n", e->key, - option->help ? option->help : "", f->index, filename); + av_log(d, AV_LOG_WARNING, "Codec AVOption %s (%s) has not been used " + "for any stream. The most likely reason is either wrong type " + "(e.g. a video option with no video streams) or that it is a " + "private option of some decoder which was not actually used " + "for any stream.\n", e->key, option->help ? option->help : ""); } av_dict_free(&unused_opts); for (i = 0; i < o->nb_dump_attachment; i++) { int j; - for (j = 0; j < ic->nb_streams; j++) { - AVStream *st = ic->streams[j]; + for (j = 0; j < f->nb_streams; j++) { + InputStream *ist = f->streams[j]; - if (check_stream_specifier(ic, st, o->dump_attachment[i].specifier) == 1) - dump_attachment(st, o->dump_attachment[i].u.str); + if (check_stream_specifier(ic, ist->st, o->dump_attachment[i].specifier) == 1) { + ret = dump_attachment(ist, o->dump_attachment[i].u.str); + if (ret < 0) + return ret; + } } } diff --git a/fftools/ffmpeg_enc.c b/fftools/ffmpeg_enc.c new file mode 100644 index 00000000000..96424272bf7 --- /dev/null +++ b/fftools/ffmpeg_enc.c @@ -0,0 +1,1190 @@ +/* + * 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 +#include + +#include "ffmpeg.h" + +#include "libavutil/avassert.h" +#include "libavutil/avstring.h" +#include "libavutil/avutil.h" +#include "libavutil/dict.h" +#include "libavutil/display.h" +#include "libavutil/eval.h" +#include "libavutil/frame.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/log.h" +#include "libavutil/pixdesc.h" +#include "libavutil/rational.h" +#include "libavutil/timestamp.h" + +#include "libavcodec/avcodec.h" + +// FIXME private header, used for mid_pred() +#include "libavcodec/mathops.h" + +#include "libavformat/avformat.h" + +struct Encoder { + /* predicted pts of the next frame to be encoded */ + int64_t next_pts; + + AVFrame *last_frame; + /* number of frames emitted by the video-encoding sync code */ + int64_t vsync_frame_number; + /* history of nb_frames_prev, i.e. the number of times the + * previous frame was duplicated by vsync code in recent + * do_video_out() calls */ + int64_t frames_prev_hist[3]; + + AVFrame *sq_frame; + + // packet for receiving encoded output + AVPacket *pkt; + + // combined size of all the packets received from the encoder + uint64_t data_size; + + // number of packets received from the encoder + uint64_t packets_encoded; + + uint64_t dup_warning; + + int opened; +}; + +void enc_free(Encoder **penc) +{ + Encoder *enc = *penc; + + if (!enc) + return; + + av_frame_free(&enc->last_frame); + av_frame_free(&enc->sq_frame); + + av_packet_free(&enc->pkt); + + av_freep(penc); +} + +int enc_alloc(Encoder **penc, const AVCodec *codec) +{ + Encoder *enc; + + *penc = NULL; + + enc = av_mallocz(sizeof(*enc)); + if (!enc) + return AVERROR(ENOMEM); + + if (codec->type == AVMEDIA_TYPE_VIDEO) { + enc->last_frame = av_frame_alloc(); + if (!enc->last_frame) + goto fail; + } + + enc->pkt = av_packet_alloc(); + if (!enc->pkt) + goto fail; + + enc->dup_warning = 1000; + + *penc = enc; + + return 0; +fail: + enc_free(&enc); + return AVERROR(ENOMEM); +} + +static int hw_device_setup_for_encode(OutputStream *ost, AVBufferRef *frames_ref) +{ + const AVCodecHWConfig *config; + HWDevice *dev = NULL; + int i; + + if (frames_ref && + ((AVHWFramesContext*)frames_ref->data)->format == + ost->enc_ctx->pix_fmt) { + // Matching format, will try to use hw_frames_ctx. + } else { + frames_ref = NULL; + } + + for (i = 0;; i++) { + config = avcodec_get_hw_config(ost->enc_ctx->codec, i); + if (!config) + break; + + if (frames_ref && + config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX && + (config->pix_fmt == AV_PIX_FMT_NONE || + config->pix_fmt == ost->enc_ctx->pix_fmt)) { + av_log(ost->enc_ctx, AV_LOG_VERBOSE, "Using input " + "frames context (format %s) with %s encoder.\n", + av_get_pix_fmt_name(ost->enc_ctx->pix_fmt), + ost->enc_ctx->codec->name); + ost->enc_ctx->hw_frames_ctx = av_buffer_ref(frames_ref); + if (!ost->enc_ctx->hw_frames_ctx) + return AVERROR(ENOMEM); + return 0; + } + + if (!dev && + config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) + dev = hw_device_get_by_type(config->device_type); + } + + if (dev) { + av_log(ost->enc_ctx, AV_LOG_VERBOSE, "Using device %s " + "(type %s) with %s encoder.\n", dev->name, + av_hwdevice_get_type_name(dev->type), ost->enc_ctx->codec->name); + ost->enc_ctx->hw_device_ctx = av_buffer_ref(dev->device_ref); + if (!ost->enc_ctx->hw_device_ctx) + return AVERROR(ENOMEM); + } else { + // No device required, or no device available. + } + return 0; +} + +static int set_encoder_id(OutputFile *of, OutputStream *ost) +{ + const char *cname = ost->enc_ctx->codec->name; + uint8_t *encoder_string; + int encoder_string_len; + + if (av_dict_get(ost->st->metadata, "encoder", NULL, 0)) + return 0; + + encoder_string_len = sizeof(LIBAVCODEC_IDENT) + strlen(cname) + 2; + encoder_string = av_mallocz(encoder_string_len); + if (!encoder_string) + return AVERROR(ENOMEM); + + if (!of->bitexact && !ost->bitexact) + av_strlcpy(encoder_string, LIBAVCODEC_IDENT " ", encoder_string_len); + else + av_strlcpy(encoder_string, "Lavc ", encoder_string_len); + av_strlcat(encoder_string, cname, encoder_string_len); + av_dict_set(&ost->st->metadata, "encoder", encoder_string, + AV_DICT_DONT_STRDUP_VAL | AV_DICT_DONT_OVERWRITE); + + return 0; +} + +int enc_open(OutputStream *ost, AVFrame *frame) +{ + InputStream *ist = ost->ist; + Encoder *e = ost->enc; + AVCodecContext *enc_ctx = ost->enc_ctx; + AVCodecContext *dec_ctx = NULL; + const AVCodec *enc = enc_ctx->codec; + OutputFile *of = output_files[ost->file_index]; + FrameData *fd; + int ret; + + if (e->opened) + return 0; + + // frame is always non-NULL for audio and video + av_assert0(frame || (enc->type != AVMEDIA_TYPE_VIDEO && enc->type != AVMEDIA_TYPE_AUDIO)); + + if (frame) { + fd = frame_data(frame); + if (!fd) + return AVERROR(ENOMEM); + } + + ret = set_encoder_id(output_files[ost->file_index], ost); + if (ret < 0) + return ret; + + if (ist) { + dec_ctx = ist->dec_ctx; + } + + switch (enc_ctx->codec_type) { + case AVMEDIA_TYPE_AUDIO: + enc_ctx->sample_fmt = frame->format; + enc_ctx->sample_rate = frame->sample_rate; + ret = av_channel_layout_copy(&enc_ctx->ch_layout, &frame->ch_layout); + if (ret < 0) + return ret; + + if (ost->bits_per_raw_sample) + enc_ctx->bits_per_raw_sample = ost->bits_per_raw_sample; + else + enc_ctx->bits_per_raw_sample = FFMIN(fd->bits_per_raw_sample, + av_get_bytes_per_sample(enc_ctx->sample_fmt) << 3); + + enc_ctx->time_base = ost->enc_timebase.num > 0 ? ost->enc_timebase : + av_make_q(1, enc_ctx->sample_rate); + break; + + case AVMEDIA_TYPE_VIDEO: { + AVRational fr = ost->frame_rate; + + if (!fr.num) + fr = fd->frame_rate_filter; + if (!fr.num && !ost->max_frame_rate.num) { + fr = (AVRational){25, 1}; + av_log(ost, AV_LOG_WARNING, + "No information " + "about the input framerate is available. Falling " + "back to a default value of 25fps. Use the -r option " + "if you want a different framerate.\n"); + } + + if (ost->max_frame_rate.num && + (av_q2d(fr) > av_q2d(ost->max_frame_rate) || + !fr.den)) + fr = ost->max_frame_rate; + + if (enc->supported_framerates && !ost->force_fps) { + int idx = av_find_nearest_q_idx(fr, enc->supported_framerates); + fr = enc->supported_framerates[idx]; + } + // reduce frame rate for mpeg4 to be within the spec limits + if (enc_ctx->codec_id == AV_CODEC_ID_MPEG4) { + av_reduce(&fr.num, &fr.den, + fr.num, fr.den, 65535); + } + + if (ost->enc_timebase.num == ENC_TIME_BASE_DEMUX) { + if (fd->dec.tb.num <= 0 || fd->dec.tb.den <= 0) { + av_log(ost, AV_LOG_ERROR, + "Demuxing timebase not available - cannot use it for encoding\n"); + return AVERROR(EINVAL); + } + + enc_ctx->time_base = fd->dec.tb; + } else if (ost->enc_timebase.num == ENC_TIME_BASE_FILTER) { + enc_ctx->time_base = frame->time_base; + } else { + enc_ctx->time_base = ost->enc_timebase.num > 0 ? ost->enc_timebase : + av_inv_q(fr); + } + + if (!(enc_ctx->time_base.num && enc_ctx->time_base.den)) + enc_ctx->time_base = frame->time_base; + if ( av_q2d(enc_ctx->time_base) < 0.001 && ost->vsync_method != VSYNC_PASSTHROUGH + && (ost->vsync_method == VSYNC_CFR || ost->vsync_method == VSYNC_VSCFR || + (ost->vsync_method == VSYNC_AUTO && !(of->format->flags & AVFMT_VARIABLE_FPS)))){ + av_log(ost, AV_LOG_WARNING, "Frame rate very high for a muxer not efficiently supporting it.\n" + "Please consider specifying a lower framerate, a different muxer or " + "setting vsync/fps_mode to vfr\n"); + } + + enc_ctx->width = frame->width; + enc_ctx->height = frame->height; + enc_ctx->sample_aspect_ratio = ost->st->sample_aspect_ratio = + ost->frame_aspect_ratio.num ? // overridden by the -aspect cli option + av_mul_q(ost->frame_aspect_ratio, (AVRational){ enc_ctx->height, enc_ctx->width }) : + frame->sample_aspect_ratio; + + enc_ctx->pix_fmt = frame->format; + + if (ost->bits_per_raw_sample) + enc_ctx->bits_per_raw_sample = ost->bits_per_raw_sample; + else + enc_ctx->bits_per_raw_sample = FFMIN(fd->bits_per_raw_sample, + av_pix_fmt_desc_get(enc_ctx->pix_fmt)->comp[0].depth); + + enc_ctx->color_range = frame->color_range; + enc_ctx->color_primaries = frame->color_primaries; + enc_ctx->color_trc = frame->color_trc; + enc_ctx->colorspace = frame->colorspace; + enc_ctx->chroma_sample_location = frame->chroma_location; + + enc_ctx->framerate = fr; + + ost->st->avg_frame_rate = fr; + + // Field order: autodetection + if (enc_ctx->flags & (AV_CODEC_FLAG_INTERLACED_DCT | AV_CODEC_FLAG_INTERLACED_ME) && + ost->top_field_first >= 0) + if (ost->top_field_first) + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; + else + frame->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; + + if (frame->flags & AV_FRAME_FLAG_INTERLACED) { + if (enc->id == AV_CODEC_ID_MJPEG) + enc_ctx->field_order = (frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? AV_FIELD_TT:AV_FIELD_BB; + else + enc_ctx->field_order = (frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? AV_FIELD_TB:AV_FIELD_BT; + } else + enc_ctx->field_order = AV_FIELD_PROGRESSIVE; + + // Field order: override + if (ost->top_field_first == 0) { + enc_ctx->field_order = AV_FIELD_BB; + } else if (ost->top_field_first == 1) { + enc_ctx->field_order = AV_FIELD_TT; + } + + break; + } + case AVMEDIA_TYPE_SUBTITLE: + enc_ctx->time_base = AV_TIME_BASE_Q; + if (!enc_ctx->width) { + enc_ctx->width = ost->ist->par->width; + enc_ctx->height = ost->ist->par->height; + } + if (dec_ctx && dec_ctx->subtitle_header) { + /* ASS code assumes this buffer is null terminated so add extra byte. */ + enc_ctx->subtitle_header = av_mallocz(dec_ctx->subtitle_header_size + 1); + if (!enc_ctx->subtitle_header) + return AVERROR(ENOMEM); + memcpy(enc_ctx->subtitle_header, dec_ctx->subtitle_header, + dec_ctx->subtitle_header_size); + enc_ctx->subtitle_header_size = dec_ctx->subtitle_header_size; + } + + break; + default: + av_assert0(0); + break; + } + + if (ost->bitexact) + enc_ctx->flags |= AV_CODEC_FLAG_BITEXACT; + + if (!av_dict_get(ost->encoder_opts, "threads", NULL, 0)) + av_dict_set(&ost->encoder_opts, "threads", "auto", 0); + + if (enc->capabilities & AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE) { + ret = av_dict_set(&ost->encoder_opts, "flags", "+copy_opaque", AV_DICT_MULTIKEY); + if (ret < 0) + return ret; + } + + av_dict_set(&ost->encoder_opts, "flags", "+frame_duration", AV_DICT_MULTIKEY); + + ret = hw_device_setup_for_encode(ost, frame ? frame->hw_frames_ctx : NULL); + if (ret < 0) { + av_log(ost, AV_LOG_ERROR, + "Encoding hardware device setup failed: %s\n", av_err2str(ret)); + return ret; + } + + if ((ret = avcodec_open2(ost->enc_ctx, enc, &ost->encoder_opts)) < 0) { + if (ret != AVERROR_EXPERIMENTAL) + av_log(ost, AV_LOG_ERROR, "Error while opening encoder - maybe " + "incorrect parameters such as bit_rate, rate, width or height.\n"); + return ret; + } + + e->opened = 1; + + if (ost->sq_idx_encode >= 0) { + e->sq_frame = av_frame_alloc(); + if (!e->sq_frame) + return AVERROR(ENOMEM); + } + + if (ost->enc_ctx->frame_size) { + av_assert0(ost->sq_idx_encode >= 0); + sq_frame_samples(output_files[ost->file_index]->sq_encode, + ost->sq_idx_encode, ost->enc_ctx->frame_size); + } + + ret = check_avoptions(ost->encoder_opts); + if (ret < 0) + return ret; + + if (ost->enc_ctx->bit_rate && ost->enc_ctx->bit_rate < 1000 && + ost->enc_ctx->codec_id != AV_CODEC_ID_CODEC2 /* don't complain about 700 bit/s modes */) + av_log(ost, AV_LOG_WARNING, "The bitrate parameter is set too low." + " It takes bits/s as argument, not kbits/s\n"); + + ret = avcodec_parameters_from_context(ost->par_in, ost->enc_ctx); + if (ret < 0) { + av_log(ost, AV_LOG_FATAL, + "Error initializing the output stream codec context.\n"); + return ret; + } + + if (ost->enc_ctx->nb_coded_side_data) { + int i; + + for (i = 0; i < ost->enc_ctx->nb_coded_side_data; i++) { + const AVPacketSideData *sd_src = &ost->enc_ctx->coded_side_data[i]; + uint8_t *dst_data; + + dst_data = av_stream_new_side_data(ost->st, sd_src->type, sd_src->size); + if (!dst_data) + return AVERROR(ENOMEM); + memcpy(dst_data, sd_src->data, sd_src->size); + } + } + + /* + * Add global input side data. For now this is naive, and copies it + * from the input stream's global side data. All side data should + * really be funneled over AVFrame and libavfilter, then added back to + * packet side data, and then potentially using the first packet for + * global side data. + */ + if (ist) { + int i; + for (i = 0; i < ist->st->nb_side_data; i++) { + AVPacketSideData *sd = &ist->st->side_data[i]; + if (sd->type != AV_PKT_DATA_CPB_PROPERTIES) { + uint8_t *dst = av_stream_new_side_data(ost->st, sd->type, sd->size); + if (!dst) + return AVERROR(ENOMEM); + memcpy(dst, sd->data, sd->size); + if (ist->autorotate && sd->type == AV_PKT_DATA_DISPLAYMATRIX) + av_display_rotation_set((int32_t *)dst, 0); + } + } + } + + // copy timebase while removing common factors + if (ost->st->time_base.num <= 0 || ost->st->time_base.den <= 0) + ost->st->time_base = av_add_q(ost->enc_ctx->time_base, (AVRational){0, 1}); + + ost->mux_timebase = enc_ctx->time_base; + + ret = of_stream_init(of, ost); + if (ret < 0) + return ret; + + return 0; +} + +static int check_recording_time(OutputStream *ost, int64_t ts, AVRational tb) +{ + OutputFile *of = output_files[ost->file_index]; + + if (of->recording_time != INT64_MAX && + av_compare_ts(ts, tb, of->recording_time, AV_TIME_BASE_Q) >= 0) { + close_output_stream(ost); + return 0; + } + return 1; +} + +int enc_subtitle(OutputFile *of, OutputStream *ost, const AVSubtitle *sub) +{ + Encoder *e = ost->enc; + int subtitle_out_max_size = 1024 * 1024; + int subtitle_out_size, nb, i, ret; + AVCodecContext *enc; + AVPacket *pkt = e->pkt; + int64_t pts; + + if (sub->pts == AV_NOPTS_VALUE) { + av_log(ost, AV_LOG_ERROR, "Subtitle packets must have a pts\n"); + return exit_on_error ? AVERROR(EINVAL) : 0; + } + if (ost->finished || + (of->start_time != AV_NOPTS_VALUE && sub->pts < of->start_time)) + return 0; + + enc = ost->enc_ctx; + + /* Note: DVB subtitle need one packet to draw them and one other + packet to clear them */ + /* XXX: signal it in the codec context ? */ + if (enc->codec_id == AV_CODEC_ID_DVB_SUBTITLE) + nb = 2; + else if (enc->codec_id == AV_CODEC_ID_ASS) + nb = FFMAX(sub->num_rects, 1); + else + nb = 1; + + /* shift timestamp to honor -ss and make check_recording_time() work with -t */ + pts = sub->pts; + if (output_files[ost->file_index]->start_time != AV_NOPTS_VALUE) + pts -= output_files[ost->file_index]->start_time; + for (i = 0; i < nb; i++) { + AVSubtitle local_sub = *sub; + + if (!check_recording_time(ost, pts, AV_TIME_BASE_Q)) + return 0; + + ret = av_new_packet(pkt, subtitle_out_max_size); + if (ret < 0) + return AVERROR(ENOMEM); + + local_sub.pts = pts; + // start_display_time is required to be 0 + local_sub.pts += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q); + local_sub.end_display_time -= sub->start_display_time; + local_sub.start_display_time = 0; + + if (enc->codec_id == AV_CODEC_ID_DVB_SUBTITLE && i == 1) + local_sub.num_rects = 0; + else if (enc->codec_id == AV_CODEC_ID_ASS && sub->num_rects > 0) { + local_sub.num_rects = 1; + local_sub.rects += i; + } + + ost->frames_encoded++; + + subtitle_out_size = avcodec_encode_subtitle(enc, pkt->data, pkt->size, &local_sub); + if (subtitle_out_size < 0) { + av_log(ost, AV_LOG_FATAL, "Subtitle encoding failed\n"); + return subtitle_out_size; + } + + av_shrink_packet(pkt, subtitle_out_size); + pkt->time_base = AV_TIME_BASE_Q; + pkt->pts = sub->pts; + pkt->duration = av_rescale_q(sub->end_display_time, (AVRational){ 1, 1000 }, pkt->time_base); + if (enc->codec_id == AV_CODEC_ID_DVB_SUBTITLE) { + /* XXX: the pts correction is handled here. Maybe handling + it in the codec would be better */ + if (i == 0) + pkt->pts += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, pkt->time_base); + else + pkt->pts += av_rescale_q(sub->end_display_time, (AVRational){ 1, 1000 }, pkt->time_base); + } + pkt->dts = pkt->pts; + + ret = of_output_packet(of, ost, pkt); + if (ret < 0) + return ret; + } + + return 0; +} + +void enc_stats_write(OutputStream *ost, EncStats *es, + const AVFrame *frame, const AVPacket *pkt, + uint64_t frame_num) +{ + Encoder *e = ost->enc; + AVIOContext *io = es->io; + AVRational tb = frame ? frame->time_base : pkt->time_base; + int64_t pts = frame ? frame->pts : pkt->pts; + + AVRational tbi = (AVRational){ 0, 1}; + int64_t ptsi = INT64_MAX; + + const FrameData *fd; + + if ((frame && frame->opaque_ref) || (pkt && pkt->opaque_ref)) { + fd = (const FrameData*)(frame ? frame->opaque_ref->data : pkt->opaque_ref->data); + tbi = fd->dec.tb; + ptsi = fd->dec.pts; + } + + for (size_t i = 0; i < es->nb_components; i++) { + const EncStatsComponent *c = &es->components[i]; + + switch (c->type) { + case ENC_STATS_LITERAL: avio_write (io, c->str, c->str_len); continue; + case ENC_STATS_FILE_IDX: avio_printf(io, "%d", ost->file_index); continue; + case ENC_STATS_STREAM_IDX: avio_printf(io, "%d", ost->index); continue; + case ENC_STATS_TIMEBASE: avio_printf(io, "%d/%d", tb.num, tb.den); continue; + case ENC_STATS_TIMEBASE_IN: avio_printf(io, "%d/%d", tbi.num, tbi.den); continue; + case ENC_STATS_PTS: avio_printf(io, "%"PRId64, pts); continue; + case ENC_STATS_PTS_IN: avio_printf(io, "%"PRId64, ptsi); continue; + case ENC_STATS_PTS_TIME: avio_printf(io, "%g", pts * av_q2d(tb)); continue; + case ENC_STATS_PTS_TIME_IN: avio_printf(io, "%g", ptsi == INT64_MAX ? + INFINITY : ptsi * av_q2d(tbi)); continue; + case ENC_STATS_FRAME_NUM: avio_printf(io, "%"PRIu64, frame_num); continue; + case ENC_STATS_FRAME_NUM_IN: avio_printf(io, "%"PRIu64, fd ? fd->dec.frame_num : -1); continue; + } + + if (frame) { + switch (c->type) { + case ENC_STATS_SAMPLE_NUM: avio_printf(io, "%"PRIu64, ost->samples_encoded); continue; + case ENC_STATS_NB_SAMPLES: avio_printf(io, "%d", frame->nb_samples); continue; + default: av_assert0(0); + } + } else { + switch (c->type) { + case ENC_STATS_DTS: avio_printf(io, "%"PRId64, pkt->dts); continue; + case ENC_STATS_DTS_TIME: avio_printf(io, "%g", pkt->dts * av_q2d(tb)); continue; + case ENC_STATS_PKT_SIZE: avio_printf(io, "%d", pkt->size); continue; + case ENC_STATS_BITRATE: { + double duration = FFMAX(pkt->duration, 1) * av_q2d(tb); + avio_printf(io, "%g", 8.0 * pkt->size / duration); + continue; + } + case ENC_STATS_AVG_BITRATE: { + double duration = pkt->dts * av_q2d(tb); + avio_printf(io, "%g", duration > 0 ? 8.0 * e->data_size / duration : -1.); + continue; + } + default: av_assert0(0); + } + } + } + avio_w8(io, '\n'); + avio_flush(io); +} + +static inline double psnr(double d) +{ + return -10.0 * log10(d); +} + +static int update_video_stats(OutputStream *ost, const AVPacket *pkt, int write_vstats) +{ + Encoder *e = ost->enc; + const uint8_t *sd = av_packet_get_side_data(pkt, AV_PKT_DATA_QUALITY_STATS, + NULL); + AVCodecContext *enc = ost->enc_ctx; + enum AVPictureType pict_type; + int64_t frame_number; + double ti1, bitrate, avg_bitrate; + double psnr_val = -1; + + ost->quality = sd ? AV_RL32(sd) : -1; + pict_type = sd ? sd[4] : AV_PICTURE_TYPE_NONE; + + if ((enc->flags & AV_CODEC_FLAG_PSNR) && sd && sd[5]) { + // FIXME the scaling assumes 8bit + double error = AV_RL64(sd + 8) / (enc->width * enc->height * 255.0 * 255.0); + if (error >= 0 && error <= 1) + psnr_val = psnr(error); + } + + if (!write_vstats) + return 0; + + /* this is executed just the first time update_video_stats is called */ + if (!vstats_file) { + vstats_file = fopen(vstats_filename, "w"); + if (!vstats_file) { + perror("fopen"); + return AVERROR(errno); + } + } + + frame_number = e->packets_encoded; + if (vstats_version <= 1) { + fprintf(vstats_file, "frame= %5"PRId64" q= %2.1f ", frame_number, + ost->quality / (float)FF_QP2LAMBDA); + } else { + fprintf(vstats_file, "out= %2d st= %2d frame= %5"PRId64" q= %2.1f ", ost->file_index, ost->index, frame_number, + ost->quality / (float)FF_QP2LAMBDA); + } + + if (psnr_val >= 0) + fprintf(vstats_file, "PSNR= %6.2f ", psnr_val); + + fprintf(vstats_file,"f_size= %6d ", pkt->size); + /* compute pts value */ + ti1 = pkt->dts * av_q2d(pkt->time_base); + if (ti1 < 0.01) + ti1 = 0.01; + + bitrate = (pkt->size * 8) / av_q2d(enc->time_base) / 1000.0; + avg_bitrate = (double)(e->data_size * 8) / ti1 / 1000.0; + fprintf(vstats_file, "s_size= %8.0fkB time= %0.3f br= %7.1fkbits/s avg_br= %7.1fkbits/s ", + (double)e->data_size / 1024, ti1, bitrate, avg_bitrate); + fprintf(vstats_file, "type= %c\n", av_get_picture_type_char(pict_type)); + + return 0; +} + +static int encode_frame(OutputFile *of, OutputStream *ost, AVFrame *frame) +{ + Encoder *e = ost->enc; + AVCodecContext *enc = ost->enc_ctx; + AVPacket *pkt = e->pkt; + const char *type_desc = av_get_media_type_string(enc->codec_type); + const char *action = frame ? "encode" : "flush"; + int ret; + + if (frame) { + if (ost->enc_stats_pre.io) + enc_stats_write(ost, &ost->enc_stats_pre, frame, NULL, + ost->frames_encoded); + + ost->frames_encoded++; + ost->samples_encoded += frame->nb_samples; + + if (debug_ts) { + av_log(ost, AV_LOG_INFO, "encoder <- type:%s " + "frame_pts:%s frame_pts_time:%s time_base:%d/%d\n", + type_desc, + av_ts2str(frame->pts), av_ts2timestr(frame->pts, &enc->time_base), + enc->time_base.num, enc->time_base.den); + } + + if (frame->sample_aspect_ratio.num && !ost->frame_aspect_ratio.num) + enc->sample_aspect_ratio = frame->sample_aspect_ratio; + } + + update_benchmark(NULL); + + ret = avcodec_send_frame(enc, frame); + if (ret < 0 && !(ret == AVERROR_EOF && !frame)) { + av_log(ost, AV_LOG_ERROR, "Error submitting %s frame to the encoder\n", + type_desc); + return ret; + } + + while (1) { + av_packet_unref(pkt); + + ret = avcodec_receive_packet(enc, pkt); + update_benchmark("%s_%s %d.%d", action, type_desc, + ost->file_index, ost->index); + + pkt->time_base = enc->time_base; + + /* if two pass, output log on success and EOF */ + if ((ret >= 0 || ret == AVERROR_EOF) && ost->logfile && enc->stats_out) + fprintf(ost->logfile, "%s", enc->stats_out); + + if (ret == AVERROR(EAGAIN)) { + av_assert0(frame); // should never happen during flushing + return 0; + } else if (ret == AVERROR_EOF) { + ret = of_output_packet(of, ost, NULL); + return ret < 0 ? ret : AVERROR_EOF; + } else if (ret < 0) { + av_log(ost, AV_LOG_ERROR, "%s encoding failed\n", type_desc); + return ret; + } + + if (enc->codec_type == AVMEDIA_TYPE_VIDEO) { + ret = update_video_stats(ost, pkt, !!vstats_filename); + if (ret < 0) + return ret; + } + + if (ost->enc_stats_post.io) + enc_stats_write(ost, &ost->enc_stats_post, NULL, pkt, + e->packets_encoded); + + if (debug_ts) { + av_log(ost, AV_LOG_INFO, "encoder -> type:%s " + "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s " + "duration:%s duration_time:%s\n", + type_desc, + av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &enc->time_base), + av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &enc->time_base), + av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, &enc->time_base)); + } + + if ((ret = trigger_fix_sub_duration_heartbeat(ost, pkt)) < 0) { + av_log(NULL, AV_LOG_ERROR, + "Subtitle heartbeat logic failed in %s! (%s)\n", + __func__, av_err2str(ret)); + return ret; + } + + e->data_size += pkt->size; + + e->packets_encoded++; + + ret = of_output_packet(of, ost, pkt); + if (ret < 0) + return ret; + } + + av_assert0(0); +} + +static int submit_encode_frame(OutputFile *of, OutputStream *ost, + AVFrame *frame) +{ + Encoder *e = ost->enc; + int ret; + + if (ost->sq_idx_encode < 0) + return encode_frame(of, ost, frame); + + if (frame) { + ret = av_frame_ref(e->sq_frame, frame); + if (ret < 0) + return ret; + frame = e->sq_frame; + } + + ret = sq_send(of->sq_encode, ost->sq_idx_encode, + SQFRAME(frame)); + if (ret < 0) { + if (frame) + av_frame_unref(frame); + if (ret != AVERROR_EOF) + return ret; + } + + while (1) { + AVFrame *enc_frame = e->sq_frame; + + ret = sq_receive(of->sq_encode, ost->sq_idx_encode, + SQFRAME(enc_frame)); + if (ret == AVERROR_EOF) { + enc_frame = NULL; + } else if (ret < 0) { + return (ret == AVERROR(EAGAIN)) ? 0 : ret; + } + + ret = encode_frame(of, ost, enc_frame); + if (enc_frame) + av_frame_unref(enc_frame); + if (ret < 0) { + if (ret == AVERROR_EOF) + close_output_stream(ost); + return ret; + } + } +} + +static int do_audio_out(OutputFile *of, OutputStream *ost, + AVFrame *frame) +{ + Encoder *e = ost->enc; + AVCodecContext *enc = ost->enc_ctx; + int ret; + + if (!(enc->codec->capabilities & AV_CODEC_CAP_PARAM_CHANGE) && + enc->ch_layout.nb_channels != frame->ch_layout.nb_channels) { + av_log(ost, AV_LOG_ERROR, + "Audio channel count changed and encoder does not support parameter changes\n"); + return 0; + } + + if (frame->pts == AV_NOPTS_VALUE) + frame->pts = e->next_pts; + else { + int64_t start_time = (of->start_time == AV_NOPTS_VALUE) ? 0 : of->start_time; + frame->pts = + av_rescale_q(frame->pts, frame->time_base, enc->time_base) - + av_rescale_q(start_time, AV_TIME_BASE_Q, enc->time_base); + } + frame->time_base = enc->time_base; + frame->duration = av_rescale_q(frame->nb_samples, (AVRational){1, frame->sample_rate}, + enc->time_base); + + if (!check_recording_time(ost, frame->pts, frame->time_base)) + return 0; + + e->next_pts = frame->pts + frame->nb_samples; + + ret = submit_encode_frame(of, ost, frame); + return (ret < 0 && ret != AVERROR_EOF) ? ret : 0; +} + +static double adjust_frame_pts_to_encoder_tb(OutputFile *of, OutputStream *ost, + AVFrame *frame) +{ + double float_pts = AV_NOPTS_VALUE; // this is identical to frame.pts but with higher precision + const int64_t start_time = (of->start_time == AV_NOPTS_VALUE) ? + 0 : of->start_time; + + AVCodecContext *const enc = ost->enc_ctx; + + AVRational tb = enc->time_base; + AVRational filter_tb = frame->time_base; + const int extra_bits = av_clip(29 - av_log2(tb.den), 0, 16); + + if (frame->pts == AV_NOPTS_VALUE) + goto early_exit; + + tb.den <<= extra_bits; + float_pts = av_rescale_q(frame->pts, filter_tb, tb) - + av_rescale_q(start_time, AV_TIME_BASE_Q, tb); + float_pts /= 1 << extra_bits; + // when float_pts is not exactly an integer, + // avoid exact midpoints to reduce the chance of rounding differences, this + // can be removed in case the fps code is changed to work with integers + if (float_pts != llrint(float_pts)) + float_pts += FFSIGN(float_pts) * 1.0 / (1<<17); + + frame->pts = av_rescale_q(frame->pts, filter_tb, enc->time_base) - + av_rescale_q(start_time, AV_TIME_BASE_Q, enc->time_base); + frame->time_base = enc->time_base; + +early_exit: + + if (debug_ts) { + av_log(NULL, AV_LOG_INFO, "filter -> pts:%s pts_time:%s exact:%f time_base:%d/%d\n", + frame ? av_ts2str(frame->pts) : "NULL", + (enc && frame) ? av_ts2timestr(frame->pts, &enc->time_base) : "NULL", + float_pts, + enc ? enc->time_base.num : -1, + enc ? enc->time_base.den : -1); + } + + return float_pts; +} + +/* Convert frame timestamps to the encoder timebase and decide how many times + * should this (and possibly previous) frame be repeated in order to conform to + * desired target framerate (if any). + */ +static void video_sync_process(OutputFile *of, OutputStream *ost, + AVFrame *frame, double duration, + int64_t *nb_frames, int64_t *nb_frames_prev) +{ + Encoder *e = ost->enc; + double delta0, delta, sync_ipts; + + if (!frame) { + *nb_frames_prev = *nb_frames = mid_pred(e->frames_prev_hist[0], + e->frames_prev_hist[1], + e->frames_prev_hist[2]); + goto finish; + } + + sync_ipts = adjust_frame_pts_to_encoder_tb(of, ost, frame); + /* delta0 is the "drift" between the input frame and + * where it would fall in the output. */ + delta0 = sync_ipts - e->next_pts; + delta = delta0 + duration; + + // tracks the number of times the PREVIOUS frame should be duplicated, + // mostly for variable framerate (VFR) + *nb_frames_prev = 0; + /* by default, we output a single frame */ + *nb_frames = 1; + + if (delta0 < 0 && + delta > 0 && + ost->vsync_method != VSYNC_PASSTHROUGH && + ost->vsync_method != VSYNC_DROP) { + if (delta0 < -0.6) { + av_log(ost, AV_LOG_VERBOSE, "Past duration %f too large\n", -delta0); + } else + av_log(ost, AV_LOG_DEBUG, "Clipping frame in rate conversion by %f\n", -delta0); + sync_ipts = e->next_pts; + duration += delta0; + delta0 = 0; + } + + switch (ost->vsync_method) { + case VSYNC_VSCFR: + if (e->vsync_frame_number == 0 && delta0 >= 0.5) { + av_log(ost, AV_LOG_DEBUG, "Not duplicating %d initial frames\n", (int)lrintf(delta0)); + delta = duration; + delta0 = 0; + e->next_pts = llrint(sync_ipts); + } + case VSYNC_CFR: + // FIXME set to 0.5 after we fix some dts/pts bugs like in avidec.c + if (frame_drop_threshold && delta < frame_drop_threshold && e->vsync_frame_number) { + *nb_frames = 0; + } else if (delta < -1.1) + *nb_frames = 0; + else if (delta > 1.1) { + *nb_frames = llrintf(delta); + if (delta0 > 1.1) + *nb_frames_prev = llrintf(delta0 - 0.6); + } + frame->duration = 1; + break; + case VSYNC_VFR: + if (delta <= -0.6) + *nb_frames = 0; + else if (delta > 0.6) + e->next_pts = llrint(sync_ipts); + frame->duration = duration; + break; + case VSYNC_DROP: + case VSYNC_PASSTHROUGH: + frame->duration = duration; + e->next_pts = llrint(sync_ipts); + break; + default: + av_assert0(0); + } + +finish: + memmove(e->frames_prev_hist + 1, + e->frames_prev_hist, + sizeof(e->frames_prev_hist[0]) * (FF_ARRAY_ELEMS(e->frames_prev_hist) - 1)); + e->frames_prev_hist[0] = *nb_frames_prev; +} + +static enum AVPictureType forced_kf_apply(void *logctx, KeyframeForceCtx *kf, + AVRational tb, const AVFrame *in_picture, + int dup_idx) +{ + double pts_time; + + if (kf->ref_pts == AV_NOPTS_VALUE) + kf->ref_pts = in_picture->pts; + + pts_time = (in_picture->pts - kf->ref_pts) * av_q2d(tb); + if (kf->index < kf->nb_pts && + av_compare_ts(in_picture->pts, tb, kf->pts[kf->index], AV_TIME_BASE_Q) >= 0) { + kf->index++; + goto force_keyframe; + } else if (kf->pexpr) { + double res; + kf->expr_const_values[FKF_T] = pts_time; + res = av_expr_eval(kf->pexpr, + kf->expr_const_values, NULL); + av_log(logctx, AV_LOG_TRACE, + "force_key_frame: n:%f n_forced:%f prev_forced_n:%f t:%f prev_forced_t:%f -> res:%f\n", + kf->expr_const_values[FKF_N], + kf->expr_const_values[FKF_N_FORCED], + kf->expr_const_values[FKF_PREV_FORCED_N], + kf->expr_const_values[FKF_T], + kf->expr_const_values[FKF_PREV_FORCED_T], + res); + + kf->expr_const_values[FKF_N] += 1; + + if (res) { + kf->expr_const_values[FKF_PREV_FORCED_N] = kf->expr_const_values[FKF_N] - 1; + kf->expr_const_values[FKF_PREV_FORCED_T] = kf->expr_const_values[FKF_T]; + kf->expr_const_values[FKF_N_FORCED] += 1; + goto force_keyframe; + } + } else if (kf->type == KF_FORCE_SOURCE && + (in_picture->flags & AV_FRAME_FLAG_KEY) && !dup_idx) { + goto force_keyframe; + } else if (kf->type == KF_FORCE_SOURCE_NO_DROP && !dup_idx) { + kf->dropped_keyframe = 0; + if ((in_picture->flags & AV_FRAME_FLAG_KEY) || kf->dropped_keyframe) + goto force_keyframe; + } + + return AV_PICTURE_TYPE_NONE; + +force_keyframe: + av_log(logctx, AV_LOG_DEBUG, "Forced keyframe at time %f\n", pts_time); + return AV_PICTURE_TYPE_I; +} + +/* May modify/reset frame */ +static int do_video_out(OutputFile *of, OutputStream *ost, AVFrame *frame) +{ + int ret; + Encoder *e = ost->enc; + AVCodecContext *enc = ost->enc_ctx; + int64_t nb_frames, nb_frames_prev, i; + double duration = 0; + + if (frame) { + FrameData *fd = frame_data(frame); + + duration = lrintf(frame->duration * av_q2d(frame->time_base) / av_q2d(enc->time_base)); + + if (duration <= 0 && + fd->frame_rate_filter.num > 0 && fd->frame_rate_filter.den > 0) + duration = 1 / (av_q2d(fd->frame_rate_filter) * av_q2d(enc->time_base)); + } + + video_sync_process(of, ost, frame, duration, + &nb_frames, &nb_frames_prev); + + if (nb_frames_prev == 0 && ost->last_dropped) { + ost->nb_frames_drop++; + av_log(ost, AV_LOG_VERBOSE, + "*** dropping frame %"PRId64" at ts %"PRId64"\n", + e->vsync_frame_number, e->last_frame->pts); + } + if (nb_frames > (nb_frames_prev && ost->last_dropped) + (nb_frames > nb_frames_prev)) { + if (nb_frames > dts_error_threshold * 30) { + av_log(ost, AV_LOG_ERROR, "%"PRId64" frame duplication too large, skipping\n", nb_frames - 1); + ost->nb_frames_drop++; + return 0; + } + ost->nb_frames_dup += nb_frames - (nb_frames_prev && ost->last_dropped) - (nb_frames > nb_frames_prev); + av_log(ost, AV_LOG_VERBOSE, "*** %"PRId64" dup!\n", nb_frames - 1); + if (ost->nb_frames_dup > e->dup_warning) { + av_log(ost, AV_LOG_WARNING, "More than %"PRIu64" frames duplicated\n", e->dup_warning); + e->dup_warning *= 10; + } + } + ost->last_dropped = nb_frames == nb_frames_prev && frame; + ost->kf.dropped_keyframe = ost->last_dropped && frame && (frame->flags & AV_FRAME_FLAG_KEY); + + /* duplicates frame if needed */ + for (i = 0; i < nb_frames; i++) { + AVFrame *in_picture; + + if (i < nb_frames_prev && e->last_frame->buf[0]) { + in_picture = e->last_frame; + } else + in_picture = frame; + + if (!in_picture) + return 0; + + in_picture->pts = e->next_pts; + + if (!check_recording_time(ost, in_picture->pts, ost->enc_ctx->time_base)) + return 0; + + in_picture->quality = enc->global_quality; + in_picture->pict_type = forced_kf_apply(ost, &ost->kf, enc->time_base, in_picture, i); + + ret = submit_encode_frame(of, ost, in_picture); + if (ret == AVERROR_EOF) + break; + else if (ret < 0) + return ret; + + e->next_pts++; + e->vsync_frame_number++; + } + + av_frame_unref(e->last_frame); + if (frame) + av_frame_move_ref(e->last_frame, frame); + + return 0; +} + +int enc_frame(OutputStream *ost, AVFrame *frame) +{ + OutputFile *of = output_files[ost->file_index]; + int ret; + + ret = enc_open(ost, frame); + if (ret < 0) + return ret; + + return ost->enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO ? + do_video_out(of, ost, frame) : do_audio_out(of, ost, frame); +} + +int enc_flush(void) +{ + int ret; + + for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { + OutputFile *of = output_files[ost->file_index]; + if (ost->sq_idx_encode >= 0) + sq_send(of->sq_encode, ost->sq_idx_encode, SQFRAME(NULL)); + } + + for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { + Encoder *e = ost->enc; + AVCodecContext *enc = ost->enc_ctx; + OutputFile *of = output_files[ost->file_index]; + + if (!enc || !e->opened || + (enc->codec_type != AVMEDIA_TYPE_VIDEO && enc->codec_type != AVMEDIA_TYPE_AUDIO)) + continue; + + ret = submit_encode_frame(of, ost, NULL); + if (ret != AVERROR_EOF) + return ret; + } + + return 0; +} diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c index 7eb656dbe5f..925b5116cc8 100644 --- a/fftools/ffmpeg_filter.c +++ b/fftools/ffmpeg_filter.c @@ -36,109 +36,284 @@ #include "libavutil/pixfmt.h" #include "libavutil/imgutils.h" #include "libavutil/samplefmt.h" +#include "libavutil/timestamp.h" -// FIXME: YUV420P etc. are actually supported with full color range, -// yet the latter information isn't available here. -static const enum AVPixelFormat *get_compliance_normal_pix_fmts(const AVCodec *codec, const enum AVPixelFormat default_formats[]) +typedef struct FilterGraphPriv { + FilterGraph fg; + + // name used for logging + char log_name[32]; + + int is_simple; + // true when the filtergraph contains only meta filters + // that do not modify the frame data + int is_meta; + int disable_conversions; + + const char *graph_desc; + + // frame for temporarily holding output from the filtergraph + AVFrame *frame; +} FilterGraphPriv; + +static FilterGraphPriv *fgp_from_fg(FilterGraph *fg) { - static const enum AVPixelFormat mjpeg_formats[] = - { AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, - AV_PIX_FMT_NONE }; + return (FilterGraphPriv*)fg; +} - if (!strcmp(codec->name, "mjpeg")) { - return mjpeg_formats; - } else { - return default_formats; +static const FilterGraphPriv *cfgp_from_cfg(const FilterGraph *fg) +{ + return (const FilterGraphPriv*)fg; +} + +typedef struct InputFilterPriv { + InputFilter ifilter; + + AVFilterContext *filter; + + InputStream *ist; + + // used to hold submitted input + AVFrame *frame; + + /* for filters that are not yet bound to an input stream, + * this stores the input linklabel, if any */ + uint8_t *linklabel; + + // filter data type + enum AVMediaType type; + // source data type: AVMEDIA_TYPE_SUBTITLE for sub2video, + // same as type otherwise + enum AVMediaType type_src; + + int eof; + + // parameters configured for this input + int format; + + int width, height; + AVRational sample_aspect_ratio; + + int sample_rate; + AVChannelLayout ch_layout; + + AVRational time_base; + + AVFifo *frame_queue; + + AVBufferRef *hw_frames_ctx; + + int displaymatrix_present; + int32_t displaymatrix[9]; + + // fallback parameters to use when no input is ever sent + struct { + int format; + + int width; + int height; + AVRational sample_aspect_ratio; + + int sample_rate; + AVChannelLayout ch_layout; + } fallback; + + struct { + AVFrame *frame; + + int64_t last_pts; + int64_t end_pts; + + ///< marks if sub2video_update should force an initialization + unsigned int initialize; + } sub2video; +} InputFilterPriv; + +static InputFilterPriv *ifp_from_ifilter(InputFilter *ifilter) +{ + return (InputFilterPriv*)ifilter; +} + +typedef struct OutputFilterPriv { + OutputFilter ofilter; + + AVFilterContext *filter; + + /* desired output stream properties */ + int format; + int width, height; + int sample_rate; + AVChannelLayout ch_layout; + + AVRational time_base; + AVRational sample_aspect_ratio; + + // those are only set if no format is specified and the encoder gives us multiple options + // They point directly to the relevant lists of the encoder. + const int *formats; + const AVChannelLayout *ch_layouts; + const int *sample_rates; + + // set to 1 after at least one frame passed through this output + int got_frame; +} OutputFilterPriv; + +static OutputFilterPriv *ofp_from_ofilter(OutputFilter *ofilter) +{ + return (OutputFilterPriv*)ofilter; +} + +static int configure_filtergraph(FilterGraph *fg); + +static int sub2video_get_blank_frame(InputFilterPriv *ifp) +{ + AVFrame *frame = ifp->sub2video.frame; + int ret; + + av_frame_unref(frame); + + frame->width = ifp->width; + frame->height = ifp->height; + frame->format = ifp->format; + + ret = av_frame_get_buffer(frame, 0); + if (ret < 0) + return ret; + + memset(frame->data[0], 0, frame->height * frame->linesize[0]); + + return 0; +} + +static void sub2video_copy_rect(uint8_t *dst, int dst_linesize, int w, int h, + AVSubtitleRect *r) +{ + uint32_t *pal, *dst2; + uint8_t *src, *src2; + int x, y; + + if (r->type != SUBTITLE_BITMAP) { + av_log(NULL, AV_LOG_WARNING, "sub2video: non-bitmap subtitle\n"); + return; + } + if (r->x < 0 || r->x + r->w > w || r->y < 0 || r->y + r->h > h) { + av_log(NULL, AV_LOG_WARNING, "sub2video: rectangle (%d %d %d %d) overflowing %d %d\n", + r->x, r->y, r->w, r->h, w, h + ); + return; + } + + dst += r->y * dst_linesize + r->x * 4; + src = r->data[0]; + pal = (uint32_t *)r->data[1]; + for (y = 0; y < r->h; y++) { + dst2 = (uint32_t *)dst; + src2 = src; + for (x = 0; x < r->w; x++) + *(dst2++) = pal[*(src2++)]; + dst += dst_linesize; + src += r->linesize[0]; } } -static enum AVPixelFormat -choose_pixel_fmt(const AVCodec *codec, enum AVPixelFormat target, - int strict_std_compliance) +static void sub2video_push_ref(InputFilterPriv *ifp, int64_t pts) { - if (codec && codec->pix_fmts) { - const enum AVPixelFormat *p = codec->pix_fmts; - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(target); - //FIXME: This should check for AV_PIX_FMT_FLAG_ALPHA after PAL8 pixel format without alpha is implemented - int has_alpha = desc ? desc->nb_components % 2 == 0 : 0; - enum AVPixelFormat best= AV_PIX_FMT_NONE; - - if (strict_std_compliance > FF_COMPLIANCE_UNOFFICIAL) { - p = get_compliance_normal_pix_fmts(codec, p); - } - for (; *p != AV_PIX_FMT_NONE; p++) { - best = av_find_best_pix_fmt_of_2(best, *p, target, has_alpha, NULL); - if (*p == target) - break; - } - if (*p == AV_PIX_FMT_NONE) { - if (target != AV_PIX_FMT_NONE) - av_log(NULL, AV_LOG_WARNING, - "Incompatible pixel format '%s' for codec '%s', auto-selecting format '%s'\n", - av_get_pix_fmt_name(target), - codec->name, - av_get_pix_fmt_name(best)); - return best; - } + AVFrame *frame = ifp->sub2video.frame; + int ret; + + av_assert1(frame->data[0]); + ifp->sub2video.last_pts = frame->pts = pts; + ret = av_buffersrc_add_frame_flags(ifp->filter, frame, + AV_BUFFERSRC_FLAG_KEEP_REF | + AV_BUFFERSRC_FLAG_PUSH); + if (ret != AVERROR_EOF && ret < 0) + av_log(NULL, AV_LOG_WARNING, "Error while add the frame to buffer source(%s).\n", + av_err2str(ret)); +} + +static void sub2video_update(InputFilterPriv *ifp, int64_t heartbeat_pts, + const AVSubtitle *sub) +{ + AVFrame *frame = ifp->sub2video.frame; + int8_t *dst; + int dst_linesize; + int num_rects, i; + int64_t pts, end_pts; + + if (sub) { + pts = av_rescale_q(sub->pts + sub->start_display_time * 1000LL, + AV_TIME_BASE_Q, ifp->time_base); + end_pts = av_rescale_q(sub->pts + sub->end_display_time * 1000LL, + AV_TIME_BASE_Q, ifp->time_base); + num_rects = sub->num_rects; + } else { + /* If we are initializing the system, utilize current heartbeat + PTS as the start time, and show until the following subpicture + is received. Otherwise, utilize the previous subpicture's end time + as the fall-back value. */ + pts = ifp->sub2video.initialize ? + heartbeat_pts : ifp->sub2video.end_pts; + end_pts = INT64_MAX; + num_rects = 0; + } + if (sub2video_get_blank_frame(ifp) < 0) { + av_log(NULL, AV_LOG_ERROR, + "Impossible to get a blank canvas.\n"); + return; } - return target; + dst = frame->data [0]; + dst_linesize = frame->linesize[0]; + for (i = 0; i < num_rects; i++) + sub2video_copy_rect(dst, dst_linesize, frame->width, frame->height, sub->rects[i]); + sub2video_push_ref(ifp, pts); + ifp->sub2video.end_pts = end_pts; + ifp->sub2video.initialize = 0; } -/* May return NULL (no pixel format found), a static string or a string - * backed by the bprint. Nothing has been written to the AVBPrint in case +/* *dst may return be set to NULL (no pixel format found), a static string or a + * string backed by the bprint. Nothing has been written to the AVBPrint in case * NULL is returned. The AVBPrint provided should be clean. */ -static const char *choose_pix_fmts(OutputFilter *ofilter, AVBPrint *bprint) +static int choose_pix_fmts(OutputFilter *ofilter, AVBPrint *bprint, + const char **dst) { + OutputFilterPriv *ofp = ofp_from_ofilter(ofilter); OutputStream *ost = ofilter->ost; - AVCodecContext *enc = ost->enc_ctx; - const AVDictionaryEntry *strict_dict = av_dict_get(ost->encoder_opts, "strict", NULL, 0); - if (strict_dict) - // used by choose_pixel_fmt() and below - av_opt_set(ost->enc_ctx, "strict", strict_dict->value, 0); - - if (ost->keep_pix_fmt) { - avfilter_graph_set_auto_convert(ofilter->graph->graph, - AVFILTER_AUTO_CONVERT_NONE); - if (ost->enc_ctx->pix_fmt == AV_PIX_FMT_NONE) - return NULL; - return av_get_pix_fmt_name(ost->enc_ctx->pix_fmt); - } - if (ost->enc_ctx->pix_fmt != AV_PIX_FMT_NONE) { - return av_get_pix_fmt_name(choose_pixel_fmt(enc->codec, enc->pix_fmt, - ost->enc_ctx->strict_std_compliance)); - } else if (enc->codec->pix_fmts) { - const enum AVPixelFormat *p; - - p = enc->codec->pix_fmts; - if (ost->enc_ctx->strict_std_compliance > FF_COMPLIANCE_UNOFFICIAL) { - p = get_compliance_normal_pix_fmts(enc->codec, p); - } + + *dst = NULL; + + if (ost->keep_pix_fmt || ofp->format != AV_PIX_FMT_NONE) { + *dst = ofp->format == AV_PIX_FMT_NONE ? NULL : + av_get_pix_fmt_name(ofp->format); + } else if (ofp->formats) { + const enum AVPixelFormat *p = ofp->formats; for (; *p != AV_PIX_FMT_NONE; p++) { const char *name = av_get_pix_fmt_name(*p); av_bprintf(bprint, "%s%c", name, p[1] == AV_PIX_FMT_NONE ? '\0' : '|'); } if (!av_bprint_is_complete(bprint)) - report_and_exit(AVERROR(ENOMEM)); - return bprint->str; - } else - return NULL; + return AVERROR(ENOMEM); + + *dst = bprint->str; + } + + return 0; } /* Define a function for appending a list of allowed formats * to an AVBPrint. If nonempty, the list will have a header. */ #define DEF_CHOOSE_FORMAT(name, type, var, supported_list, none, printf_format, get_name) \ -static void choose_ ## name (OutputFilter *ofilter, AVBPrint *bprint) \ +static void choose_ ## name (OutputFilterPriv *ofp, AVBPrint *bprint) \ { \ - if (ofilter->var == none && !ofilter->supported_list) \ + if (ofp->var == none && !ofp->supported_list) \ return; \ av_bprintf(bprint, #name "="); \ - if (ofilter->var != none) { \ - av_bprintf(bprint, printf_format, get_name(ofilter->var)); \ + if (ofp->var != none) { \ + av_bprintf(bprint, printf_format, get_name(ofp->var)); \ } else { \ const type *p; \ \ - for (p = ofilter->supported_list; *p != none; p++) { \ + for (p = ofp->supported_list; *p != none; p++) { \ av_bprintf(bprint, printf_format "|", get_name(*p)); \ } \ if (bprint->len > 0) \ @@ -156,16 +331,16 @@ DEF_CHOOSE_FORMAT(sample_fmts, enum AVSampleFormat, format, formats, DEF_CHOOSE_FORMAT(sample_rates, int, sample_rate, sample_rates, 0, "%d", ) -static void choose_channel_layouts(OutputFilter *ofilter, AVBPrint *bprint) +static void choose_channel_layouts(OutputFilterPriv *ofp, AVBPrint *bprint) { - if (av_channel_layout_check(&ofilter->ch_layout)) { + if (av_channel_layout_check(&ofp->ch_layout)) { av_bprintf(bprint, "channel_layouts="); - av_channel_layout_describe_bprint(&ofilter->ch_layout, bprint); - } else if (ofilter->ch_layouts) { + av_channel_layout_describe_bprint(&ofp->ch_layout, bprint); + } else if (ofp->ch_layouts) { const AVChannelLayout *p; av_bprintf(bprint, "channel_layouts="); - for (p = ofilter->ch_layouts; p->nb_channels; p++) { + for (p = ofp->ch_layouts; p->nb_channels; p++) { av_channel_layout_describe_bprint(p, bprint); av_bprintf(bprint, "|"); } @@ -176,39 +351,186 @@ static void choose_channel_layouts(OutputFilter *ofilter, AVBPrint *bprint) av_bprint_chars(bprint, ':', 1); } -int init_simple_filtergraph(InputStream *ist, OutputStream *ost) +static int read_binary(const char *path, uint8_t **data, int *len) { - FilterGraph *fg = av_mallocz(sizeof(*fg)); - OutputFilter *ofilter; - InputFilter *ifilter; + AVIOContext *io = NULL; + int64_t fsize; + int ret; - if (!fg) - report_and_exit(AVERROR(ENOMEM)); - fg->index = nb_filtergraphs; + *data = NULL; + *len = 0; - ofilter = ALLOC_ARRAY_ELEM(fg->outputs, fg->nb_outputs); - ofilter->ost = ost; - ofilter->graph = fg; - ofilter->format = -1; + ret = avio_open2(&io, path, AVIO_FLAG_READ, &int_cb, NULL); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "Cannot open file '%s': %s\n", + path, av_err2str(ret)); + return ret; + } - ost->filter = ofilter; + fsize = avio_size(io); + if (fsize < 0 || fsize > INT_MAX) { + av_log(NULL, AV_LOG_ERROR, "Cannot obtain size of file %s\n", path); + ret = AVERROR(EIO); + goto fail; + } - ifilter = ALLOC_ARRAY_ELEM(fg->inputs, fg->nb_inputs); - ifilter->ist = ist; - ifilter->graph = fg; - ifilter->format = -1; + *data = av_malloc(fsize); + if (!*data) { + ret = AVERROR(ENOMEM); + goto fail; + } - ifilter->frame_queue = av_fifo_alloc2(8, sizeof(AVFrame*), AV_FIFO_FLAG_AUTO_GROW); - if (!ifilter->frame_queue) - report_and_exit(AVERROR(ENOMEM)); + ret = avio_read(io, *data, fsize); + if (ret != fsize) { + av_log(NULL, AV_LOG_ERROR, "Error reading file %s\n", path); + ret = ret < 0 ? ret : AVERROR(EIO); + goto fail; + } - GROW_ARRAY(ist->filters, ist->nb_filters); - ist->filters[ist->nb_filters - 1] = ifilter; + *len = fsize; - GROW_ARRAY(filtergraphs, nb_filtergraphs); - filtergraphs[nb_filtergraphs - 1] = fg; + ret = 0; +fail: + avio_close(io); + if (ret < 0) { + av_freep(data); + *len = 0; + } + return ret; +} + +static int filter_opt_apply(AVFilterContext *f, const char *key, const char *val) +{ + const AVOption *o = NULL; + int ret; + + ret = av_opt_set(f, key, val, AV_OPT_SEARCH_CHILDREN); + if (ret >= 0) + return 0; + + if (ret == AVERROR_OPTION_NOT_FOUND && key[0] == '/') + o = av_opt_find(f, key + 1, NULL, 0, AV_OPT_SEARCH_CHILDREN); + if (!o) + goto err_apply; + + // key is a valid option name prefixed with '/' + // interpret value as a path from which to load the actual option value + key++; + + if (o->type == AV_OPT_TYPE_BINARY) { + uint8_t *data; + int len; + + ret = read_binary(val, &data, &len); + if (ret < 0) + goto err_load; + + ret = av_opt_set_bin(f, key, data, len, AV_OPT_SEARCH_CHILDREN); + av_freep(&data); + } else { + char *data = file_read(val); + if (!data) { + ret = AVERROR(EIO); + goto err_load; + } + + ret = av_opt_set(f, key, data, AV_OPT_SEARCH_CHILDREN); + av_freep(&data); + } + if (ret < 0) + goto err_apply; return 0; + +err_apply: + av_log(NULL, AV_LOG_ERROR, + "Error applying option '%s' to filter '%s': %s\n", + key, f->filter->name, av_err2str(ret)); + return ret; +err_load: + av_log(NULL, AV_LOG_ERROR, + "Error loading value for option '%s' from file '%s'\n", + key, val); + return ret; +} + +static int graph_opts_apply(AVFilterGraphSegment *seg) +{ + for (size_t i = 0; i < seg->nb_chains; i++) { + AVFilterChain *ch = seg->chains[i]; + + for (size_t j = 0; j < ch->nb_filters; j++) { + AVFilterParams *p = ch->filters[j]; + const AVDictionaryEntry *e = NULL; + + av_assert0(p->filter); + + while ((e = av_dict_iterate(p->opts, e))) { + int ret = filter_opt_apply(p->filter, e->key, e->value); + if (ret < 0) + return ret; + } + + av_dict_free(&p->opts); + } + } + + return 0; +} + +static int graph_parse(AVFilterGraph *graph, const char *desc, + AVFilterInOut **inputs, AVFilterInOut **outputs, + AVBufferRef *hw_device) +{ + AVFilterGraphSegment *seg; + int ret; + + *inputs = NULL; + *outputs = NULL; + + ret = avfilter_graph_segment_parse(graph, desc, 0, &seg); + if (ret < 0) + return ret; + + ret = avfilter_graph_segment_create_filters(seg, 0); + if (ret < 0) + goto fail; + + if (hw_device) { + for (int i = 0; i < graph->nb_filters; i++) { + AVFilterContext *f = graph->filters[i]; + + if (!(f->filter->flags & AVFILTER_FLAG_HWDEVICE)) + continue; + f->hw_device_ctx = av_buffer_ref(hw_device); + if (!f->hw_device_ctx) { + ret = AVERROR(ENOMEM); + goto fail; + } + } + } + + ret = graph_opts_apply(seg); + if (ret < 0) + goto fail; + + ret = avfilter_graph_segment_apply(seg, 0, inputs, outputs); + +fail: + avfilter_graph_segment_free(&seg); + return ret; +} + +// Filters can be configured only if the formats of all inputs are known. +static int ifilter_has_all_input_formats(FilterGraph *fg) +{ + int i; + for (i = 0; i < fg->nb_inputs; i++) { + InputFilterPriv *ifp = ifp_from_ifilter(fg->inputs[i]); + if (ifp->format < 0) + return 0; + } + return 1; } static char *describe_filter_link(FilterGraph *fg, AVFilterInOut *inout, int in) @@ -216,42 +538,427 @@ static char *describe_filter_link(FilterGraph *fg, AVFilterInOut *inout, int in) AVFilterContext *ctx = inout->filter_ctx; AVFilterPad *pads = in ? ctx->input_pads : ctx->output_pads; int nb_pads = in ? ctx->nb_inputs : ctx->nb_outputs; - char *res; if (nb_pads > 1) - res = av_strdup(ctx->filter->name); - else - res = av_asprintf("%s:%s", ctx->filter->name, - avfilter_pad_get_name(pads, inout->pad_idx)); - if (!res) - report_and_exit(AVERROR(ENOMEM)); - return res; + return av_strdup(ctx->filter->name); + return av_asprintf("%s:%s", ctx->filter->name, + avfilter_pad_get_name(pads, inout->pad_idx)); } -static void init_input_filter(FilterGraph *fg, AVFilterInOut *in) +static OutputFilter *ofilter_alloc(FilterGraph *fg) { - InputStream *ist = NULL; - enum AVMediaType type = avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx); + OutputFilterPriv *ofp; + OutputFilter *ofilter; + + ofp = allocate_array_elem(&fg->outputs, sizeof(*ofp), &fg->nb_outputs); + if (!ofp) + return NULL; + + ofilter = &ofp->ofilter; + ofilter->graph = fg; + ofp->format = -1; + ofilter->last_pts = AV_NOPTS_VALUE; + + return ofilter; +} + +static int ifilter_bind_ist(InputFilter *ifilter, InputStream *ist) +{ + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); + int ret; + + av_assert0(!ifp->ist); + + ifp->ist = ist; + ifp->type_src = ist->st->codecpar->codec_type; + + ret = ist_filter_add(ist, ifilter, filtergraph_is_simple(ifilter->graph)); + if (ret < 0) + return ret; + + if (ifp->type_src == AVMEDIA_TYPE_SUBTITLE) { + ifp->sub2video.frame = av_frame_alloc(); + if (!ifp->sub2video.frame) + return AVERROR(ENOMEM); + } + + return 0; +} + +static int set_channel_layout(OutputFilterPriv *f, OutputStream *ost) +{ + const AVCodec *c = ost->enc_ctx->codec; + int i, err; + + if (ost->enc_ctx->ch_layout.order != AV_CHANNEL_ORDER_UNSPEC) { + /* Pass the layout through for all orders but UNSPEC */ + err = av_channel_layout_copy(&f->ch_layout, &ost->enc_ctx->ch_layout); + if (err < 0) + return err; + return 0; + } + + /* Requested layout is of order UNSPEC */ + if (!c->ch_layouts) { + /* Use the default native layout for the requested amount of channels when the + encoder doesn't have a list of supported layouts */ + av_channel_layout_default(&f->ch_layout, ost->enc_ctx->ch_layout.nb_channels); + return 0; + } + /* Encoder has a list of supported layouts. Pick the first layout in it with the + same amount of channels as the requested layout */ + for (i = 0; c->ch_layouts[i].nb_channels; i++) { + if (c->ch_layouts[i].nb_channels == ost->enc_ctx->ch_layout.nb_channels) + break; + } + if (c->ch_layouts[i].nb_channels) { + /* Use it if one is found */ + err = av_channel_layout_copy(&f->ch_layout, &c->ch_layouts[i]); + if (err < 0) + return err; + return 0; + } + /* If no layout for the amount of channels requested was found, use the default + native layout for it. */ + av_channel_layout_default(&f->ch_layout, ost->enc_ctx->ch_layout.nb_channels); + + return 0; +} + +int ofilter_bind_ost(OutputFilter *ofilter, OutputStream *ost) +{ + OutputFilterPriv *ofp = ofp_from_ofilter(ofilter); + FilterGraph *fg = ofilter->graph; + FilterGraphPriv *fgp = fgp_from_fg(fg); + const AVCodec *c = ost->enc_ctx->codec; + + av_assert0(!ofilter->ost); + + ofilter->ost = ost; + av_freep(&ofilter->linklabel); + + switch (ost->enc_ctx->codec_type) { + case AVMEDIA_TYPE_VIDEO: + ofp->width = ost->enc_ctx->width; + ofp->height = ost->enc_ctx->height; + if (ost->enc_ctx->pix_fmt != AV_PIX_FMT_NONE) { + ofp->format = ost->enc_ctx->pix_fmt; + } else { + ofp->formats = c->pix_fmts; + + // MJPEG encoder exports a full list of supported pixel formats, + // but the full-range ones are experimental-only. + // Restrict the auto-conversion list unless -strict experimental + // has been specified. + if (!strcmp(c->name, "mjpeg")) { + // FIXME: YUV420P etc. are actually supported with full color range, + // yet the latter information isn't available here. + static const enum AVPixelFormat mjpeg_formats[] = + { AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, + AV_PIX_FMT_NONE }; + + const AVDictionaryEntry *strict = av_dict_get(ost->encoder_opts, "strict", NULL, 0); + int strict_val = ost->enc_ctx->strict_std_compliance; + + if (strict) { + const AVOption *o = av_opt_find(ost->enc_ctx, strict->key, NULL, 0, 0); + av_assert0(o); + av_opt_eval_int(ost->enc_ctx, o, strict->value, &strict_val); + } + + if (strict_val > FF_COMPLIANCE_UNOFFICIAL) + ofp->formats = mjpeg_formats; + } + } + + fgp->disable_conversions |= ost->keep_pix_fmt; + + break; + case AVMEDIA_TYPE_AUDIO: + if (ost->enc_ctx->sample_fmt != AV_SAMPLE_FMT_NONE) { + ofp->format = ost->enc_ctx->sample_fmt; + } else { + ofp->formats = c->sample_fmts; + } + if (ost->enc_ctx->sample_rate) { + ofp->sample_rate = ost->enc_ctx->sample_rate; + } else { + ofp->sample_rates = c->supported_samplerates; + } + if (ost->enc_ctx->ch_layout.nb_channels) { + int ret = set_channel_layout(ofp, ost); + if (ret < 0) + return ret; + } else if (c->ch_layouts) { + ofp->ch_layouts = c->ch_layouts; + } + break; + } + + // if we have all input parameters and all outputs are bound, + // the graph can now be configured + if (ifilter_has_all_input_formats(fg)) { + int ret; + + for (int i = 0; i < fg->nb_outputs; i++) + if (!fg->outputs[i]->ost) + return 0; + + ret = configure_filtergraph(fg); + if (ret < 0) { + av_log(fg, AV_LOG_ERROR, "Error configuring filter graph: %s\n", + av_err2str(ret)); + return ret; + } + } + + return 0; +} + +static InputFilter *ifilter_alloc(FilterGraph *fg) +{ + InputFilterPriv *ifp; InputFilter *ifilter; - int i; + + ifp = allocate_array_elem(&fg->inputs, sizeof(*ifp), &fg->nb_inputs); + if (!ifp) + return NULL; + + ifilter = &ifp->ifilter; + ifilter->graph = fg; + + ifp->frame = av_frame_alloc(); + if (!ifp->frame) + return NULL; + + ifp->format = -1; + ifp->fallback.format = -1; + + ifp->frame_queue = av_fifo_alloc2(8, sizeof(AVFrame*), AV_FIFO_FLAG_AUTO_GROW); + if (!ifp->frame_queue) + return NULL; + + return ifilter; +} + +void fg_free(FilterGraph **pfg) +{ + FilterGraph *fg = *pfg; + FilterGraphPriv *fgp; + + if (!fg) + return; + fgp = fgp_from_fg(fg); + + avfilter_graph_free(&fg->graph); + for (int j = 0; j < fg->nb_inputs; j++) { + InputFilter *ifilter = fg->inputs[j]; + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); + + if (ifp->frame_queue) { + AVFrame *frame; + while (av_fifo_read(ifp->frame_queue, &frame, 1) >= 0) + av_frame_free(&frame); + av_fifo_freep2(&ifp->frame_queue); + } + av_frame_free(&ifp->sub2video.frame); + + av_channel_layout_uninit(&ifp->fallback.ch_layout); + + av_frame_free(&ifp->frame); + + av_buffer_unref(&ifp->hw_frames_ctx); + av_freep(&ifp->linklabel); + av_freep(&ifilter->name); + av_freep(&fg->inputs[j]); + } + av_freep(&fg->inputs); + for (int j = 0; j < fg->nb_outputs; j++) { + OutputFilter *ofilter = fg->outputs[j]; + OutputFilterPriv *ofp = ofp_from_ofilter(ofilter); + + av_freep(&ofilter->linklabel); + av_freep(&ofilter->name); + av_channel_layout_uninit(&ofp->ch_layout); + av_freep(&fg->outputs[j]); + } + av_freep(&fg->outputs); + av_freep(&fgp->graph_desc); + + av_frame_free(&fgp->frame); + + av_freep(pfg); +} + +static const char *fg_item_name(void *obj) +{ + const FilterGraphPriv *fgp = obj; + + return fgp->log_name; +} + +static const AVClass fg_class = { + .class_name = "FilterGraph", + .version = LIBAVUTIL_VERSION_INT, + .item_name = fg_item_name, + .category = AV_CLASS_CATEGORY_FILTER, +}; + +int fg_create(FilterGraph **pfg, char *graph_desc) +{ + FilterGraphPriv *fgp; + FilterGraph *fg; + + AVFilterInOut *inputs, *outputs; + AVFilterGraph *graph; + int ret = 0; + + fgp = allocate_array_elem(&filtergraphs, sizeof(*fgp), &nb_filtergraphs); + if (!fgp) + return AVERROR(ENOMEM); + fg = &fgp->fg; + + if (pfg) + *pfg = fg; + + fg->class = &fg_class; + fg->index = nb_filtergraphs - 1; + fgp->graph_desc = graph_desc; + fgp->disable_conversions = !auto_conversion_filters; + + snprintf(fgp->log_name, sizeof(fgp->log_name), "fc#%d", fg->index); + + fgp->frame = av_frame_alloc(); + if (!fgp->frame) + return AVERROR(ENOMEM); + + /* this graph is only used for determining the kinds of inputs + * and outputs we have, and is discarded on exit from this function */ + graph = avfilter_graph_alloc(); + if (!graph) + return AVERROR(ENOMEM);; + graph->nb_threads = 1; + + ret = graph_parse(graph, fgp->graph_desc, &inputs, &outputs, NULL); + if (ret < 0) + goto fail; + + for (AVFilterInOut *cur = inputs; cur; cur = cur->next) { + InputFilter *const ifilter = ifilter_alloc(fg); + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); + + ifp->linklabel = cur->name; + cur->name = NULL; + + ifp->type = avfilter_pad_get_type(cur->filter_ctx->input_pads, + cur->pad_idx); + ifilter->name = describe_filter_link(fg, cur, 1); + if (!ifilter->name) { + ret = AVERROR(ENOMEM); + goto fail; + } + } + + for (AVFilterInOut *cur = outputs; cur; cur = cur->next) { + OutputFilter *const ofilter = ofilter_alloc(fg); + + if (!ofilter) + goto fail; + + ofilter->linklabel = cur->name; + cur->name = NULL; + + ofilter->type = avfilter_pad_get_type(cur->filter_ctx->output_pads, + cur->pad_idx); + ofilter->name = describe_filter_link(fg, cur, 0); + if (!ofilter->name) { + ret = AVERROR(ENOMEM); + goto fail; + } + } + + if (!fg->nb_outputs) { + av_log(fg, AV_LOG_FATAL, "A filtergraph has zero outputs, this is not supported\n"); + ret = AVERROR(ENOSYS); + goto fail; + } + +fail: + avfilter_inout_free(&inputs); + avfilter_inout_free(&outputs); + avfilter_graph_free(&graph); + + if (ret < 0) + return ret; + + return 0; +} + +int init_simple_filtergraph(InputStream *ist, OutputStream *ost, + char *graph_desc) +{ + FilterGraph *fg; + FilterGraphPriv *fgp; + int ret; + + ret = fg_create(&fg, graph_desc); + if (ret < 0) + return ret; + fgp = fgp_from_fg(fg); + + fgp->is_simple = 1; + + snprintf(fgp->log_name, sizeof(fgp->log_name), "%cf#%d:%d", + av_get_media_type_string(ost->type)[0], + ost->file_index, ost->index); + + if (fg->nb_inputs != 1 || fg->nb_outputs != 1) { + av_log(fg, AV_LOG_ERROR, "Simple filtergraph '%s' was expected " + "to have exactly 1 input and 1 output. " + "However, it had %d input(s) and %d output(s). Please adjust, " + "or use a complex filtergraph (-filter_complex) instead.\n", + graph_desc, fg->nb_inputs, fg->nb_outputs); + return AVERROR(EINVAL); + } + + ost->filter = fg->outputs[0]; + + ret = ifilter_bind_ist(fg->inputs[0], ist); + if (ret < 0) + return ret; + + ret = ofilter_bind_ost(fg->outputs[0], ost); + if (ret < 0) + return ret; + + return 0; +} + +static int init_input_filter(FilterGraph *fg, InputFilter *ifilter) +{ + FilterGraphPriv *fgp = fgp_from_fg(fg); + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); + InputStream *ist = NULL; + enum AVMediaType type = ifp->type; + int i, ret; // TODO: support other filter types if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO) { - av_log(NULL, AV_LOG_FATAL, "Only video and audio filters supported " + av_log(fg, AV_LOG_FATAL, "Only video and audio filters supported " "currently.\n"); - exit_program(1); + return AVERROR(ENOSYS); } - if (in->name) { + if (ifp->linklabel) { AVFormatContext *s; AVStream *st = NULL; char *p; - int file_idx = strtol(in->name, &p, 0); + int file_idx = strtol(ifp->linklabel, &p, 0); if (file_idx < 0 || file_idx >= nb_input_files) { - av_log(NULL, AV_LOG_FATAL, "Invalid file index %d in filtergraph description %s.\n", - file_idx, fg->graph_desc); - exit_program(1); + av_log(fg, AV_LOG_FATAL, "Invalid file index %d in filtergraph description %s.\n", + file_idx, fgp->graph_desc); + return AVERROR(EINVAL); } s = input_files[file_idx]->ctx; @@ -267,89 +974,41 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in) } } if (!st) { - av_log(NULL, AV_LOG_FATAL, "Stream specifier '%s' in filtergraph description %s " - "matches no streams.\n", p, fg->graph_desc); - exit_program(1); + av_log(fg, AV_LOG_FATAL, "Stream specifier '%s' in filtergraph description %s " + "matches no streams.\n", p, fgp->graph_desc); + return AVERROR(EINVAL); } ist = input_files[file_idx]->streams[st->index]; - if (ist->user_set_discard == AVDISCARD_ALL) { - av_log(NULL, AV_LOG_FATAL, "Stream specifier '%s' in filtergraph description %s " - "matches a disabled input stream.\n", p, fg->graph_desc); - exit_program(1); - } } else { - /* find the first unused stream of corresponding type */ - for (ist = ist_iter(NULL); ist; ist = ist_iter(ist)) { - if (ist->user_set_discard == AVDISCARD_ALL) - continue; - if (ist->dec_ctx->codec_type == type && ist->discard) - break; - } + ist = ist_find_unused(type); if (!ist) { - av_log(NULL, AV_LOG_FATAL, "Cannot find a matching stream for " - "unlabeled input pad %d on filter %s\n", in->pad_idx, - in->filter_ctx->name); - exit_program(1); + av_log(fg, AV_LOG_FATAL, "Cannot find a matching stream for " + "unlabeled input pad %s\n", ifilter->name); + return AVERROR(EINVAL); } } av_assert0(ist); - ist->discard = 0; - ist->decoding_needed |= DECODING_FOR_FILTER; - ist->processing_needed = 1; - ist->st->discard = AVDISCARD_NONE; - - ifilter = ALLOC_ARRAY_ELEM(fg->inputs, fg->nb_inputs); - ifilter->ist = ist; - ifilter->graph = fg; - ifilter->format = -1; - ifilter->type = ist->st->codecpar->codec_type; - ifilter->name = describe_filter_link(fg, in, 1); - - ifilter->frame_queue = av_fifo_alloc2(8, sizeof(AVFrame*), AV_FIFO_FLAG_AUTO_GROW); - if (!ifilter->frame_queue) - report_and_exit(AVERROR(ENOMEM)); + ret = ifilter_bind_ist(ifilter, ist); + if (ret < 0) { + av_log(fg, AV_LOG_ERROR, + "Error binding an input stream to complex filtergraph input %s.\n", + ifilter->name); + return ret; + } - GROW_ARRAY(ist->filters, ist->nb_filters); - ist->filters[ist->nb_filters - 1] = ifilter; + return 0; } int init_complex_filtergraph(FilterGraph *fg) { - AVFilterInOut *inputs, *outputs, *cur; - AVFilterGraph *graph; - int ret = 0; - - /* this graph is only used for determining the kinds of inputs - * and outputs we have, and is discarded on exit from this function */ - graph = avfilter_graph_alloc(); - if (!graph) - return AVERROR(ENOMEM); - graph->nb_threads = 1; - - ret = avfilter_graph_parse2(graph, fg->graph_desc, &inputs, &outputs); - if (ret < 0) - goto fail; - - for (cur = inputs; cur; cur = cur->next) - init_input_filter(fg, cur); - - for (cur = outputs; cur;) { - OutputFilter *const ofilter = ALLOC_ARRAY_ELEM(fg->outputs, fg->nb_outputs); - - ofilter->graph = fg; - ofilter->out_tmp = cur; - ofilter->type = avfilter_pad_get_type(cur->filter_ctx->output_pads, - cur->pad_idx); - ofilter->name = describe_filter_link(fg, cur, 0); - cur = cur->next; - ofilter->out_tmp->next = NULL; + // bind filtergraph inputs to input streams + for (int i = 0; i < fg->nb_inputs; i++) { + int ret = init_input_filter(fg, fg->inputs[i]); + if (ret < 0) + return ret; } - -fail: - avfilter_inout_free(&inputs); - avfilter_graph_free(&graph); - return ret; + return 0; } static int insert_trim(int64_t start_time, int64_t duration, @@ -427,6 +1086,7 @@ static int insert_filter(AVFilterContext **last_filter, int *pad_idx, static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) { + OutputFilterPriv *ofp = ofp_from_ofilter(ofilter); OutputStream *ost = ofilter->ost; OutputFile *of = output_files[ost->file_index]; AVFilterContext *last_filter = out->filter_ctx; @@ -437,20 +1097,20 @@ static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, char name[255]; snprintf(name, sizeof(name), "out_%d_%d", ost->file_index, ost->index); - ret = avfilter_graph_create_filter(&ofilter->filter, + ret = avfilter_graph_create_filter(&ofp->filter, avfilter_get_by_name("buffersink"), name, NULL, NULL, fg->graph); if (ret < 0) return ret; - if ((ofilter->width || ofilter->height) && ofilter->ost->autoscale) { + if ((ofp->width || ofp->height) && ofilter->ost->autoscale) { char args[255]; AVFilterContext *filter; const AVDictionaryEntry *e = NULL; snprintf(args, sizeof(args), "%d:%d", - ofilter->width, ofilter->height); + ofp->width, ofp->height); while ((e = av_dict_iterate(ost->sws_dict, e))) { av_strlcatf(args, sizeof(args), ":%s=%s", e->key, e->value); @@ -469,7 +1129,11 @@ static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, } av_bprint_init(&bprint, 0, AV_BPRINT_SIZE_UNLIMITED); - if ((pix_fmts = choose_pix_fmts(ofilter, &bprint))) { + ret = choose_pix_fmts(ofilter, &bprint, &pix_fmts); + if (ret < 0) + return ret; + + if (pix_fmts) { AVFilterContext *filter; ret = avfilter_graph_create_filter(&filter, @@ -485,26 +1149,6 @@ static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, pad_idx = 0; } - if (ost->frame_rate.num && 0) { - AVFilterContext *fps; - char args[255]; - - snprintf(args, sizeof(args), "fps=%d/%d", ost->frame_rate.num, - ost->frame_rate.den); - snprintf(name, sizeof(name), "fps_out_%d_%d", - ost->file_index, ost->index); - ret = avfilter_graph_create_filter(&fps, avfilter_get_by_name("fps"), - name, args, NULL, fg->graph); - if (ret < 0) - return ret; - - ret = avfilter_link(last_filter, pad_idx, fps, 0); - if (ret < 0) - return ret; - last_filter = fps; - pad_idx = 0; - } - snprintf(name, sizeof(name), "trim_out_%d_%d", ost->file_index, ost->index); ret = insert_trim(of->start_time, of->recording_time, @@ -513,7 +1157,7 @@ static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, return ret; - if ((ret = avfilter_link(last_filter, pad_idx, ofilter->filter, 0)) < 0) + if ((ret = avfilter_link(last_filter, pad_idx, ofp->filter, 0)) < 0) return ret; return 0; @@ -521,9 +1165,9 @@ static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) { + OutputFilterPriv *ofp = ofp_from_ofilter(ofilter); OutputStream *ost = ofilter->ost; OutputFile *of = output_files[ost->file_index]; - AVCodecContext *codec = ost->enc_ctx; AVFilterContext *last_filter = out->filter_ctx; int pad_idx = out->pad_idx; AVBPrint args; @@ -531,18 +1175,18 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter, int ret; snprintf(name, sizeof(name), "out_%d_%d", ost->file_index, ost->index); - ret = avfilter_graph_create_filter(&ofilter->filter, + ret = avfilter_graph_create_filter(&ofp->filter, avfilter_get_by_name("abuffersink"), name, NULL, NULL, fg->graph); if (ret < 0) return ret; - if ((ret = av_opt_set_int(ofilter->filter, "all_channel_counts", 1, AV_OPT_SEARCH_CHILDREN)) < 0) + if ((ret = av_opt_set_int(ofp->filter, "all_channel_counts", 1, AV_OPT_SEARCH_CHILDREN)) < 0) return ret; #define AUTO_INSERT_FILTER(opt_name, filter_name, arg) do { \ AVFilterContext *filt_ctx; \ \ - av_log(NULL, AV_LOG_INFO, opt_name " is forwarded to lavfi " \ + av_log(fg, AV_LOG_INFO, opt_name " is forwarded to lavfi " \ "similarly to -af " filter_name "=%s.\n", arg); \ \ ret = avfilter_graph_create_filter(&filt_ctx, \ @@ -574,12 +1218,9 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter, } #endif - if (codec->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) - av_channel_layout_default(&codec->ch_layout, codec->ch_layout.nb_channels); - - choose_sample_fmts(ofilter, &args); - choose_sample_rates(ofilter, &args); - choose_channel_layouts(ofilter, &args); + choose_sample_fmts(ofp, &args); + choose_sample_rates(ofp, &args); + choose_channel_layouts(ofp, &args); if (!av_bprint_is_complete(&args)) { ret = AVERROR(ENOMEM); goto fail; @@ -622,7 +1263,7 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter, if (ret < 0) goto fail; - if ((ret = avfilter_link(last_filter, pad_idx, ofilter->filter, 0)) < 0) + if ((ret = avfilter_link(last_filter, pad_idx, ofp->filter, 0)) < 0) goto fail; fail: av_bprint_finalize(&args, NULL); @@ -634,8 +1275,8 @@ static int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) { if (!ofilter->ost) { - av_log(NULL, AV_LOG_FATAL, "Filter %s has an unconnected output\n", ofilter->name); - exit_program(1); + av_log(fg, AV_LOG_FATAL, "Filter %s has an unconnected output\n", ofilter->name); + return AVERROR(EINVAL); } switch (avfilter_pad_get_type(out->filter_ctx->output_pads, out->pad_idx)) { @@ -645,7 +1286,7 @@ static int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter, } } -void check_filter_outputs(void) +int check_filter_outputs(void) { int i; for (i = 0; i < nb_filtergraphs; i++) { @@ -653,70 +1294,37 @@ void check_filter_outputs(void) for (n = 0; n < filtergraphs[i]->nb_outputs; n++) { OutputFilter *output = filtergraphs[i]->outputs[n]; if (!output->ost) { - av_log(NULL, AV_LOG_FATAL, "Filter %s has an unconnected output\n", output->name); - exit_program(1); + av_log(filtergraphs[i], AV_LOG_FATAL, + "Filter %s has an unconnected output\n", output->name); + return AVERROR(EINVAL); } } } + + return 0; } -static int sub2video_prepare(InputStream *ist, InputFilter *ifilter) +static void sub2video_prepare(InputFilterPriv *ifp) { - AVFormatContext *avf = input_files[ist->file_index]->ctx; - int i, w, h; - - /* Compute the size of the canvas for the subtitles stream. - If the subtitles codecpar has set a size, use it. Otherwise use the - maximum dimensions of the video streams in the same file. */ - w = ifilter->width; - h = ifilter->height; - if (!(w && h)) { - for (i = 0; i < avf->nb_streams; i++) { - if (avf->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { - w = FFMAX(w, avf->streams[i]->codecpar->width); - h = FFMAX(h, avf->streams[i]->codecpar->height); - } - } - if (!(w && h)) { - w = FFMAX(w, 720); - h = FFMAX(h, 576); - } - av_log(avf, AV_LOG_INFO, "sub2video: using %dx%d canvas\n", w, h); - } - ist->sub2video.w = ifilter->width = w; - ist->sub2video.h = ifilter->height = h; - - ifilter->width = ist->dec_ctx->width ? ist->dec_ctx->width : ist->sub2video.w; - ifilter->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h; - - /* rectangles are AV_PIX_FMT_PAL8, but we have no guarantee that the - palettes for all rectangles are identical or compatible */ - ifilter->format = AV_PIX_FMT_RGB32; - - ist->sub2video.frame = av_frame_alloc(); - if (!ist->sub2video.frame) - return AVERROR(ENOMEM); - ist->sub2video.last_pts = INT64_MIN; - ist->sub2video.end_pts = INT64_MIN; + ifp->sub2video.last_pts = INT64_MIN; + ifp->sub2video.end_pts = INT64_MIN; /* sub2video structure has been (re-)initialized. Mark it as such so that the system will be initialized with the first received heartbeat. */ - ist->sub2video.initialize = 1; - - return 0; + ifp->sub2video.initialize = 1; } static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, AVFilterInOut *in) { + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); + AVFilterContext *last_filter; const AVFilter *buffer_filt = avfilter_get_by_name("buffer"); const AVPixFmtDescriptor *desc; - InputStream *ist = ifilter->ist; + InputStream *ist = ifp->ist; InputFile *f = input_files[ist->file_index]; - AVRational tb = ist->framerate.num ? av_inv_q(ist->framerate) : - ist->st->time_base; AVRational fr = ist->framerate; AVRational sar; AVBPrint args; @@ -731,7 +1339,7 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, par->format = AV_PIX_FMT_NONE; if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { - av_log(NULL, AV_LOG_ERROR, "Cannot connect video filter to audio input\n"); + av_log(fg, AV_LOG_ERROR, "Cannot connect video filter to audio input\n"); ret = AVERROR(EINVAL); goto fail; } @@ -739,46 +1347,46 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, if (!fr.num) fr = ist->framerate_guessed; - if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) { - ret = sub2video_prepare(ist, ifilter); - if (ret < 0) - goto fail; - } + if (ifp->type_src == AVMEDIA_TYPE_SUBTITLE) + sub2video_prepare(ifp); + + ifp->time_base = ist->framerate.num ? av_inv_q(ist->framerate) : + ist->st->time_base; - sar = ifilter->sample_aspect_ratio; + sar = ifp->sample_aspect_ratio; if(!sar.den) sar = (AVRational){0,1}; av_bprint_init(&args, 0, AV_BPRINT_SIZE_AUTOMATIC); av_bprintf(&args, "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:" "pixel_aspect=%d/%d", - ifilter->width, ifilter->height, ifilter->format, - tb.num, tb.den, sar.num, sar.den); + ifp->width, ifp->height, ifp->format, + ifp->time_base.num, ifp->time_base.den, sar.num, sar.den); if (fr.num && fr.den) av_bprintf(&args, ":frame_rate=%d/%d", fr.num, fr.den); snprintf(name, sizeof(name), "graph %d input from stream %d:%d", fg->index, - ist->file_index, ist->st->index); + ist->file_index, ist->index); - if ((ret = avfilter_graph_create_filter(&ifilter->filter, buffer_filt, name, + if ((ret = avfilter_graph_create_filter(&ifp->filter, buffer_filt, name, args.str, NULL, fg->graph)) < 0) goto fail; - par->hw_frames_ctx = ifilter->hw_frames_ctx; - ret = av_buffersrc_parameters_set(ifilter->filter, par); + par->hw_frames_ctx = ifp->hw_frames_ctx; + ret = av_buffersrc_parameters_set(ifp->filter, par); if (ret < 0) goto fail; av_freep(&par); - last_filter = ifilter->filter; + last_filter = ifp->filter; - desc = av_pix_fmt_desc_get(ifilter->format); + desc = av_pix_fmt_desc_get(ifp->format); av_assert0(desc); // TODO: insert hwaccel enabled filters like transpose_vaapi into the graph if (ist->autorotate && !(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) { - int32_t *displaymatrix = ifilter->displaymatrix; + int32_t *displaymatrix = ifp->displaymatrix; double theta; - if (!displaymatrix) + if (!ifp->displaymatrix_present) displaymatrix = (int32_t *)av_stream_get_side_data(ist->st, AV_PKT_DATA_DISPLAYMATRIX, NULL); theta = get_rotation(displaymatrix); @@ -811,7 +1419,7 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, } snprintf(name, sizeof(name), "trim_in_%d_%d", - ist->file_index, ist->st->index); + ist->file_index, ist->index); if (copy_ts) { tsoffset = f->start_time == AV_NOPTS_VALUE ? 0 : f->start_time; if (!start_at_zero && f->ctx->start_time != AV_NOPTS_VALUE) @@ -835,9 +1443,10 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, static int configure_input_audio_filter(FilterGraph *fg, InputFilter *ifilter, AVFilterInOut *in) { + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); AVFilterContext *last_filter; const AVFilter *abuffer_filt = avfilter_get_by_name("abuffer"); - InputStream *ist = ifilter->ist; + InputStream *ist = ifp->ist; InputFile *f = input_files[ist->file_index]; AVBPrint args; char name[255]; @@ -845,53 +1454,34 @@ static int configure_input_audio_filter(FilterGraph *fg, InputFilter *ifilter, int64_t tsoffset = 0; if (ist->dec_ctx->codec_type != AVMEDIA_TYPE_AUDIO) { - av_log(NULL, AV_LOG_ERROR, "Cannot connect audio filter to non audio input\n"); + av_log(fg, AV_LOG_ERROR, "Cannot connect audio filter to non audio input\n"); return AVERROR(EINVAL); } + ifp->time_base = (AVRational){ 1, ifp->sample_rate }; + av_bprint_init(&args, 0, AV_BPRINT_SIZE_AUTOMATIC); av_bprintf(&args, "time_base=%d/%d:sample_rate=%d:sample_fmt=%s", - 1, ifilter->sample_rate, - ifilter->sample_rate, - av_get_sample_fmt_name(ifilter->format)); - if (av_channel_layout_check(&ifilter->ch_layout) && - ifilter->ch_layout.order != AV_CHANNEL_ORDER_UNSPEC) { + ifp->time_base.num, ifp->time_base.den, + ifp->sample_rate, + av_get_sample_fmt_name(ifp->format)); + if (av_channel_layout_check(&ifp->ch_layout) && + ifp->ch_layout.order != AV_CHANNEL_ORDER_UNSPEC) { av_bprintf(&args, ":channel_layout="); - av_channel_layout_describe_bprint(&ifilter->ch_layout, &args); + av_channel_layout_describe_bprint(&ifp->ch_layout, &args); } else - av_bprintf(&args, ":channels=%d", ifilter->ch_layout.nb_channels); + av_bprintf(&args, ":channels=%d", ifp->ch_layout.nb_channels); snprintf(name, sizeof(name), "graph_%d_in_%d_%d", fg->index, - ist->file_index, ist->st->index); + ist->file_index, ist->index); - if ((ret = avfilter_graph_create_filter(&ifilter->filter, abuffer_filt, + if ((ret = avfilter_graph_create_filter(&ifp->filter, abuffer_filt, name, args.str, NULL, fg->graph)) < 0) return ret; - last_filter = ifilter->filter; - -#define AUTO_INSERT_FILTER_INPUT(opt_name, filter_name, arg) do { \ - AVFilterContext *filt_ctx; \ - \ - av_log(NULL, AV_LOG_INFO, opt_name " is forwarded to lavfi " \ - "similarly to -af " filter_name "=%s.\n", arg); \ - \ - snprintf(name, sizeof(name), "graph_%d_%s_in_%d_%d", \ - fg->index, filter_name, ist->file_index, ist->st->index); \ - ret = avfilter_graph_create_filter(&filt_ctx, \ - avfilter_get_by_name(filter_name), \ - name, arg, NULL, fg->graph); \ - if (ret < 0) \ - return ret; \ - \ - ret = avfilter_link(last_filter, 0, filt_ctx, 0); \ - if (ret < 0) \ - return ret; \ - \ - last_filter = filt_ctx; \ -} while (0) + last_filter = ifp->filter; snprintf(name, sizeof(name), "trim for input stream %d:%d", - ist->file_index, ist->st->index); + ist->file_index, ist->index); if (copy_ts) { tsoffset = f->start_time == AV_NOPTS_VALUE ? 0 : f->start_time; if (!start_at_zero && f->ctx->start_time != AV_NOPTS_VALUE) @@ -912,13 +1502,7 @@ static int configure_input_audio_filter(FilterGraph *fg, InputFilter *ifilter, static int configure_input_filter(FilterGraph *fg, InputFilter *ifilter, AVFilterInOut *in) { - if (!ifilter->ist->dec) { - av_log(NULL, AV_LOG_ERROR, - "No decoder for stream #%d:%d, filtering impossible\n", - ifilter->ist->file_index, ifilter->ist->st->index); - return AVERROR_DECODER_NOT_FOUND; - } - switch (avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx)) { + switch (ifp_from_ifilter(ifilter)->type) { case AVMEDIA_TYPE_VIDEO: return configure_input_video_filter(fg, ifilter, in); case AVMEDIA_TYPE_AUDIO: return configure_input_audio_filter(fg, ifilter, in); default: av_assert0(0); return 0; @@ -929,9 +1513,9 @@ static void cleanup_filtergraph(FilterGraph *fg) { int i; for (i = 0; i < fg->nb_outputs; i++) - fg->outputs[i]->filter = (AVFilterContext *)NULL; + ofp_from_ofilter(fg->outputs[i])->filter = NULL; for (i = 0; i < fg->nb_inputs; i++) - fg->inputs[i]->filter = (AVFilterContext *)NULL; + ifp_from_ifilter(fg->inputs[i])->filter = NULL; avfilter_graph_free(&fg->graph); } @@ -959,12 +1543,13 @@ static int graph_is_meta(AVFilterGraph *graph) return 1; } -int configure_filtergraph(FilterGraph *fg) +static int configure_filtergraph(FilterGraph *fg) { + FilterGraphPriv *fgp = fgp_from_fg(fg); + AVBufferRef *hw_device; AVFilterInOut *inputs, *outputs, *cur; int ret, i, simple = filtergraph_is_simple(fg); - const char *graph_desc = simple ? fg->outputs[0]->ost->avfilter : - fg->graph_desc; + const char *graph_desc = fgp->graph_desc; cleanup_filtergraph(fg); if (!(fg->graph = avfilter_graph_alloc())) @@ -1004,38 +1589,10 @@ int configure_filtergraph(FilterGraph *fg) fg->graph->nb_threads = filter_complex_nbthreads; } - if ((ret = avfilter_graph_parse2(fg->graph, graph_desc, &inputs, &outputs)) < 0) - goto fail; - - ret = hw_device_setup_for_filter(fg); - if (ret < 0) - goto fail; + hw_device = hw_device_for_filter(); - if (simple && (!inputs || inputs->next || !outputs || outputs->next)) { - const char *num_inputs; - const char *num_outputs; - if (!outputs) { - num_outputs = "0"; - } else if (outputs->next) { - num_outputs = ">1"; - } else { - num_outputs = "1"; - } - if (!inputs) { - num_inputs = "0"; - } else if (inputs->next) { - num_inputs = ">1"; - } else { - num_inputs = "1"; - } - av_log(NULL, AV_LOG_ERROR, "Simple filtergraph '%s' was expected " - "to have exactly 1 input and 1 output." - " However, it had %s input(s) and %s output(s)." - " Please adjust, or use a complex filtergraph (-filter_complex) instead.\n", - graph_desc, num_inputs, num_outputs); - ret = AVERROR(EINVAL); + if ((ret = graph_parse(fg->graph, graph_desc, &inputs, &outputs, hw_device)) < 0) goto fail; - } for (cur = inputs, i = 0; cur; cur = cur->next, i++) if ((ret = configure_input_filter(fg, fg->inputs[i], cur)) < 0) { @@ -1045,49 +1602,53 @@ int configure_filtergraph(FilterGraph *fg) } avfilter_inout_free(&inputs); - for (cur = outputs, i = 0; cur; cur = cur->next, i++) - configure_output_filter(fg, fg->outputs[i], cur); + for (cur = outputs, i = 0; cur; cur = cur->next, i++) { + ret = configure_output_filter(fg, fg->outputs[i], cur); + if (ret < 0) { + avfilter_inout_free(&outputs); + goto fail; + } + } avfilter_inout_free(&outputs); - if (!auto_conversion_filters) + if (fgp->disable_conversions) avfilter_graph_set_auto_convert(fg->graph, AVFILTER_AUTO_CONVERT_NONE); if ((ret = avfilter_graph_config(fg->graph, NULL)) < 0) goto fail; - fg->is_meta = graph_is_meta(fg->graph); + fgp->is_meta = graph_is_meta(fg->graph); /* limit the lists of allowed formats to the ones selected, to * make sure they stay the same if the filtergraph is reconfigured later */ for (i = 0; i < fg->nb_outputs; i++) { OutputFilter *ofilter = fg->outputs[i]; - AVFilterContext *sink = ofilter->filter; + OutputFilterPriv *ofp = ofp_from_ofilter(ofilter); + AVFilterContext *sink = ofp->filter; - ofilter->format = av_buffersink_get_format(sink); + ofp->format = av_buffersink_get_format(sink); - ofilter->width = av_buffersink_get_w(sink); - ofilter->height = av_buffersink_get_h(sink); + ofp->width = av_buffersink_get_w(sink); + ofp->height = av_buffersink_get_h(sink); - ofilter->sample_rate = av_buffersink_get_sample_rate(sink); - av_channel_layout_uninit(&ofilter->ch_layout); - ret = av_buffersink_get_ch_layout(sink, &ofilter->ch_layout); + ofp->time_base = av_buffersink_get_time_base(sink); + ofp->sample_aspect_ratio = av_buffersink_get_sample_aspect_ratio(sink); + + ofp->sample_rate = av_buffersink_get_sample_rate(sink); + av_channel_layout_uninit(&ofp->ch_layout); + ret = av_buffersink_get_ch_layout(sink, &ofp->ch_layout); if (ret < 0) goto fail; } - fg->reconfiguration = 1; - - for (i = 0; i < fg->nb_outputs; i++) { - OutputStream *ost = fg->outputs[i]->ost; - if (ost->enc_ctx->codec_type == AVMEDIA_TYPE_AUDIO && - !(ost->enc_ctx->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)) - av_buffersink_set_frame_size(ost->filter->filter, - ost->enc_ctx->frame_size); - } - for (i = 0; i < fg->nb_inputs; i++) { + InputFilterPriv *ifp = ifp_from_ifilter(fg->inputs[i]); AVFrame *tmp; - while (av_fifo_read(fg->inputs[i]->frame_queue, &tmp, 1) >= 0) { - ret = av_buffersrc_add_frame(fg->inputs[i]->filter, tmp); + while (av_fifo_read(ifp->frame_queue, &tmp, 1) >= 0) { + if (ifp->type_src == AVMEDIA_TYPE_SUBTITLE) { + sub2video_update(ifp, INT64_MIN, (const AVSubtitle*)tmp->buf[0]->data); + } else { + ret = av_buffersrc_add_frame(ifp->filter, tmp); + } av_frame_free(&tmp); if (ret < 0) goto fail; @@ -1096,25 +1657,14 @@ int configure_filtergraph(FilterGraph *fg) /* send the EOFs for the finished inputs */ for (i = 0; i < fg->nb_inputs; i++) { - if (fg->inputs[i]->eof) { - ret = av_buffersrc_add_frame(fg->inputs[i]->filter, NULL); + InputFilterPriv *ifp = ifp_from_ifilter(fg->inputs[i]); + if (ifp->eof) { + ret = av_buffersrc_add_frame(ifp->filter, NULL); if (ret < 0) goto fail; } } - /* process queued up subtitle packets */ - for (i = 0; i < fg->nb_inputs; i++) { - InputStream *ist = fg->inputs[i]->ist; - if (ist->sub2video.sub_queue && ist->sub2video.frame) { - AVSubtitle tmp; - while (av_fifo_read(ist->sub2video.sub_queue, &tmp, 1) >= 0) { - sub2video_update(ist, INT64_MIN, &tmp); - avsubtitle_free(&tmp); - } - } - } - return 0; fail: @@ -1122,39 +1672,446 @@ int configure_filtergraph(FilterGraph *fg) return ret; } -int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame) +int ifilter_parameters_from_dec(InputFilter *ifilter, const AVCodecContext *dec) +{ + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); + + if (dec->codec_type == AVMEDIA_TYPE_VIDEO) { + ifp->fallback.format = dec->pix_fmt; + ifp->fallback.width = dec->width; + ifp->fallback.height = dec->height; + ifp->fallback.sample_aspect_ratio = dec->sample_aspect_ratio; + } else if (dec->codec_type == AVMEDIA_TYPE_AUDIO) { + int ret; + + ifp->fallback.format = dec->sample_fmt; + ifp->fallback.sample_rate = dec->sample_rate; + + ret = av_channel_layout_copy(&ifp->fallback.ch_layout, &dec->ch_layout); + if (ret < 0) + return ret; + } else { + // for subtitles (i.e. sub2video) we set the actual parameters, + // rather than just fallback + ifp->width = ifp->ist->sub2video.w; + ifp->height = ifp->ist->sub2video.h; + + /* rectangles are AV_PIX_FMT_PAL8, but we have no guarantee that the + palettes for all rectangles are identical or compatible */ + ifp->format = AV_PIX_FMT_RGB32; + + av_log(NULL, AV_LOG_VERBOSE, "sub2video: using %dx%d canvas\n", ifp->width, ifp->height); + } + + return 0; +} + +static int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame) { + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); AVFrameSideData *sd; int ret; - av_buffer_unref(&ifilter->hw_frames_ctx); + ret = av_buffer_replace(&ifp->hw_frames_ctx, frame->hw_frames_ctx); + if (ret < 0) + return ret; - ifilter->format = frame->format; + ifp->format = frame->format; - ifilter->width = frame->width; - ifilter->height = frame->height; - ifilter->sample_aspect_ratio = frame->sample_aspect_ratio; + ifp->width = frame->width; + ifp->height = frame->height; + ifp->sample_aspect_ratio = frame->sample_aspect_ratio; - ifilter->sample_rate = frame->sample_rate; - ret = av_channel_layout_copy(&ifilter->ch_layout, &frame->ch_layout); + ifp->sample_rate = frame->sample_rate; + ret = av_channel_layout_copy(&ifp->ch_layout, &frame->ch_layout); if (ret < 0) return ret; - av_freep(&ifilter->displaymatrix); sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX); if (sd) - ifilter->displaymatrix = av_memdup(sd->data, sizeof(int32_t) * 9); + memcpy(ifp->displaymatrix, sd->data, sizeof(ifp->displaymatrix)); + ifp->displaymatrix_present = !!sd; + + return 0; +} + +int filtergraph_is_simple(const FilterGraph *fg) +{ + const FilterGraphPriv *fgp = cfgp_from_cfg(fg); + return fgp->is_simple; +} + +int reap_filters(FilterGraph *fg, int flush) +{ + FilterGraphPriv *fgp = fgp_from_fg(fg); + AVFrame *filtered_frame = fgp->frame; + + if (!fg->graph) + return 0; + + /* Reap all buffers present in the buffer sinks */ + for (int i = 0; i < fg->nb_outputs; i++) { + OutputFilter *ofilter = fg->outputs[i]; + OutputStream *ost = ofilter->ost; + OutputFilterPriv *ofp = ofp_from_ofilter(ofilter); + AVFilterContext *filter = ofp->filter; + + int ret = 0; + + while (1) { + FrameData *fd; + + ret = av_buffersink_get_frame_flags(filter, filtered_frame, + AV_BUFFERSINK_FLAG_NO_REQUEST); + if (ret < 0) { + if (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) { + av_log(fgp, AV_LOG_WARNING, + "Error in av_buffersink_get_frame_flags(): %s\n", av_err2str(ret)); + } else if (flush && ret == AVERROR_EOF && ofp->got_frame && + av_buffersink_get_type(filter) == AVMEDIA_TYPE_VIDEO) { + ret = enc_frame(ost, NULL); + if (ret < 0) + return ret; + } + + break; + } + if (ost->finished) { + av_frame_unref(filtered_frame); + continue; + } + + if (filtered_frame->pts != AV_NOPTS_VALUE) { + AVRational tb = av_buffersink_get_time_base(filter); + ost->filter->last_pts = av_rescale_q(filtered_frame->pts, tb, + AV_TIME_BASE_Q); + filtered_frame->time_base = tb; + + if (debug_ts) + av_log(fgp, AV_LOG_INFO, "filter_raw -> pts:%s pts_time:%s time_base:%d/%d\n", + av_ts2str(filtered_frame->pts), + av_ts2timestr(filtered_frame->pts, &tb), + tb.num, tb.den); + } + + fd = frame_data(filtered_frame); + if (!fd) { + av_frame_unref(filtered_frame); + return AVERROR(ENOMEM); + } + + // only use bits_per_raw_sample passed through from the decoder + // if the filtergraph did not touch the frame data + if (!fgp->is_meta) + fd->bits_per_raw_sample = 0; + + if (ost->type == AVMEDIA_TYPE_VIDEO) + fd->frame_rate_filter = av_buffersink_get_frame_rate(filter); + + ret = enc_frame(ost, filtered_frame); + av_frame_unref(filtered_frame); + if (ret < 0) + return ret; + + ofp->got_frame = 1; + } + } + + return 0; +} + +void ifilter_sub2video_heartbeat(InputFilter *ifilter, int64_t pts, AVRational tb) +{ + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); + int64_t pts2; + + if (!ifilter->graph->graph) + return; + + /* subtitles seem to be usually muxed ahead of other streams; + if not, subtracting a larger time here is necessary */ + pts2 = av_rescale_q(pts, tb, ifp->time_base) - 1; + + /* do not send the heartbeat frame if the subtitle is already ahead */ + if (pts2 <= ifp->sub2video.last_pts) + return; + + if (pts2 >= ifp->sub2video.end_pts || ifp->sub2video.initialize) + /* if we have hit the end of the current displayed subpicture, + or if we need to initialize the system, update the + overlayed subpicture and its start/end times */ + sub2video_update(ifp, pts2 + 1, NULL); + + if (av_buffersrc_get_nb_failed_requests(ifp->filter)) + sub2video_push_ref(ifp, pts2); +} + +int ifilter_sub2video(InputFilter *ifilter, const AVFrame *frame) +{ + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); + int ret; + + if (ifilter->graph->graph) { + if (!frame) { + if (ifp->sub2video.end_pts < INT64_MAX) + sub2video_update(ifp, INT64_MAX, NULL); + + return av_buffersrc_add_frame(ifp->filter, NULL); + } + + ifp->width = frame->width ? frame->width : ifp->width; + ifp->height = frame->height ? frame->height : ifp->height; - if (frame->hw_frames_ctx) { - ifilter->hw_frames_ctx = av_buffer_ref(frame->hw_frames_ctx); - if (!ifilter->hw_frames_ctx) + sub2video_update(ifp, INT64_MIN, (const AVSubtitle*)frame->buf[0]->data); + } else if (frame) { + AVFrame *tmp = av_frame_clone(frame); + + if (!tmp) return AVERROR(ENOMEM); + + ret = av_fifo_write(ifp->frame_queue, &tmp, 1); + if (ret < 0) { + av_frame_free(&tmp); + return ret; + } + } + + return 0; +} + +int ifilter_send_eof(InputFilter *ifilter, int64_t pts, AVRational tb) +{ + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); + int ret; + + ifp->eof = 1; + + if (ifp->filter) { + pts = av_rescale_q_rnd(pts, tb, ifp->time_base, + AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); + + ret = av_buffersrc_close(ifp->filter, pts, AV_BUFFERSRC_FLAG_PUSH); + if (ret < 0) + return ret; + } else { + if (ifp->format < 0) { + // the filtergraph was never configured, use the fallback parameters + ifp->format = ifp->fallback.format; + ifp->sample_rate = ifp->fallback.sample_rate; + ifp->width = ifp->fallback.width; + ifp->height = ifp->fallback.height; + ifp->sample_aspect_ratio = ifp->fallback.sample_aspect_ratio; + + ret = av_channel_layout_copy(&ifp->ch_layout, + &ifp->fallback.ch_layout); + if (ret < 0) + return ret; + + if (ifilter_has_all_input_formats(ifilter->graph)) { + ret = configure_filtergraph(ifilter->graph); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "Error initializing filters!\n"); + return ret; + } + } + } + + if (ifp->format < 0) { + av_log(NULL, AV_LOG_ERROR, + "Cannot determine format of input stream %d:%d after EOF\n", + ifp->ist->file_index, ifp->ist->index); + return AVERROR_INVALIDDATA; + } + } + + return 0; +} + +int ifilter_send_frame(InputFilter *ifilter, AVFrame *frame, int keep_reference) +{ + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); + FilterGraph *fg = ifilter->graph; + AVFrameSideData *sd; + int need_reinit, ret; + + /* determine if the parameters for this input changed */ + need_reinit = ifp->format != frame->format; + + switch (ifp->type) { + case AVMEDIA_TYPE_AUDIO: + need_reinit |= ifp->sample_rate != frame->sample_rate || + av_channel_layout_compare(&ifp->ch_layout, &frame->ch_layout); + break; + case AVMEDIA_TYPE_VIDEO: + need_reinit |= ifp->width != frame->width || + ifp->height != frame->height; + break; + } + + if (!ifp->ist->reinit_filters && fg->graph) + need_reinit = 0; + + if (!!ifp->hw_frames_ctx != !!frame->hw_frames_ctx || + (ifp->hw_frames_ctx && ifp->hw_frames_ctx->data != frame->hw_frames_ctx->data)) + need_reinit = 1; + + if (sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX)) { + if (!ifp->displaymatrix_present || + memcmp(sd->data, ifp->displaymatrix, sizeof(ifp->displaymatrix))) + need_reinit = 1; + } else if (ifp->displaymatrix_present) + need_reinit = 1; + + if (need_reinit) { + ret = ifilter_parameters_from_frame(ifilter, frame); + if (ret < 0) + return ret; + } + + /* (re)init the graph if possible, otherwise buffer the frame and return */ + if (need_reinit || !fg->graph) { + if (!ifilter_has_all_input_formats(fg)) { + AVFrame *tmp = av_frame_clone(frame); + if (!tmp) + return AVERROR(ENOMEM); + + ret = av_fifo_write(ifp->frame_queue, &tmp, 1); + if (ret < 0) + av_frame_free(&tmp); + + return ret; + } + + ret = reap_filters(fg, 0); + if (ret < 0 && ret != AVERROR_EOF) { + av_log(fg, AV_LOG_ERROR, "Error while filtering: %s\n", av_err2str(ret)); + return ret; + } + + ret = configure_filtergraph(fg); + if (ret < 0) { + av_log(fg, AV_LOG_ERROR, "Error reinitializing filters!\n"); + return ret; + } + } + + if (keep_reference) { + ret = av_frame_ref(ifp->frame, frame); + if (ret < 0) + return ret; + } else + av_frame_move_ref(ifp->frame, frame); + frame = ifp->frame; + + frame->pts = av_rescale_q(frame->pts, frame->time_base, ifp->time_base); + frame->duration = av_rescale_q(frame->duration, frame->time_base, ifp->time_base); + frame->time_base = ifp->time_base; +#if LIBAVUTIL_VERSION_MAJOR < 59 + AV_NOWARN_DEPRECATED( + frame->pkt_duration = frame->duration; + ) +#endif + + ret = av_buffersrc_add_frame_flags(ifp->filter, frame, + AV_BUFFERSRC_FLAG_PUSH); + if (ret < 0) { + av_frame_unref(frame); + if (ret != AVERROR_EOF) + av_log(fg, AV_LOG_ERROR, "Error while filtering: %s\n", av_err2str(ret)); + return ret; } return 0; } -int filtergraph_is_simple(FilterGraph *fg) +int fg_transcode_step(FilterGraph *graph, InputStream **best_ist) { - return !fg->graph_desc; + FilterGraphPriv *fgp = fgp_from_fg(graph); + int i, ret; + int nb_requests, nb_requests_max = 0; + InputStream *ist; + + if (!graph->graph) { + for (int i = 0; i < graph->nb_inputs; i++) { + InputFilter *ifilter = graph->inputs[i]; + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); + if (ifp->format < 0 && !ifp->eof) { + *best_ist = ifp->ist; + return 0; + } + } + + // graph not configured, but all inputs are either initialized or EOF + for (int i = 0; i < graph->nb_outputs; i++) + graph->outputs[i]->ost->inputs_done = 1; + + return 0; + } + + *best_ist = NULL; + ret = avfilter_graph_request_oldest(graph->graph); + if (ret >= 0) + return reap_filters(graph, 0); + + if (ret == AVERROR_EOF) { + reap_filters(graph, 1); + for (int i = 0; i < graph->nb_outputs; i++) { + OutputFilter *ofilter = graph->outputs[i]; + OutputFilterPriv *ofp = ofp_from_ofilter(ofilter); + + // we are finished and no frames were ever seen at this output, + // at least initialize the encoder with a dummy frame + if (!ofp->got_frame) { + AVFrame *frame = fgp->frame; + + frame->time_base = ofp->time_base; + frame->format = ofp->format; + + frame->width = ofp->width; + frame->height = ofp->height; + frame->sample_aspect_ratio = ofp->sample_aspect_ratio; + + frame->sample_rate = ofp->sample_rate; + if (ofp->ch_layout.nb_channels) { + ret = av_channel_layout_copy(&frame->ch_layout, &ofp->ch_layout); + if (ret < 0) + return ret; + } + + av_assert0(!frame->buf[0]); + + av_log(ofilter->ost, AV_LOG_WARNING, + "No filtered frames for output stream, trying to " + "initialize anyway.\n"); + + enc_open(ofilter->ost, frame); + av_frame_unref(frame); + } + + close_output_stream(ofilter->ost); + } + return 0; + } + if (ret != AVERROR(EAGAIN)) + return ret; + + for (i = 0; i < graph->nb_inputs; i++) { + InputFilter *ifilter = graph->inputs[i]; + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); + + ist = ifp->ist; + if (input_files[ist->file_index]->eagain || ifp->eof) + continue; + nb_requests = av_buffersrc_get_nb_failed_requests(ifp->filter); + if (nb_requests > nb_requests_max) { + nb_requests_max = nb_requests; + *best_ist = ist; + } + } + + if (!*best_ist) + for (i = 0; i < graph->nb_outputs; i++) + graph->outputs[i]->ost->unavailable = 1; + + return 0; } diff --git a/fftools/ffmpeg_hw.c b/fftools/ffmpeg_hw.c index 88fa7824701..46bc7e39c22 100644 --- a/fftools/ffmpeg_hw.c +++ b/fftools/ffmpeg_hw.c @@ -27,7 +27,7 @@ static int nb_hw_devices; static HWDevice **hw_devices; -static HWDevice *hw_device_get_by_type(enum AVHWDeviceType type) +HWDevice *hw_device_get_by_type(enum AVHWDeviceType type) { HWDevice *found = NULL; int i; @@ -242,9 +242,9 @@ int hw_device_init_from_string(const char *arg, HWDevice **dev_out) goto done; } -static int hw_device_init_from_type(enum AVHWDeviceType type, - const char *device, - HWDevice **dev_out) +int hw_device_init_from_type(enum AVHWDeviceType type, + const char *device, + HWDevice **dev_out) { AVBufferRef *device_ref = NULL; HWDevice *dev; @@ -297,207 +297,7 @@ void hw_device_free_all(void) nb_hw_devices = 0; } -static HWDevice *hw_device_match_by_codec(const AVCodec *codec) -{ - const AVCodecHWConfig *config; - HWDevice *dev; - int i; - for (i = 0;; i++) { - config = avcodec_get_hw_config(codec, i); - if (!config) - return NULL; - if (!(config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)) - continue; - dev = hw_device_get_by_type(config->device_type); - if (dev) - return dev; - } -} - -int hw_device_setup_for_decode(InputStream *ist) -{ - const AVCodecHWConfig *config; - enum AVHWDeviceType type; - HWDevice *dev = NULL; - int err, auto_device = 0; - - if (ist->hwaccel_device) { - dev = hw_device_get_by_name(ist->hwaccel_device); - if (!dev) { - if (ist->hwaccel_id == HWACCEL_AUTO) { - auto_device = 1; - } else if (ist->hwaccel_id == HWACCEL_GENERIC) { - type = ist->hwaccel_device_type; - err = hw_device_init_from_type(type, ist->hwaccel_device, - &dev); - } else { - // This will be dealt with by API-specific initialisation - // (using hwaccel_device), so nothing further needed here. - return 0; - } - } else { - if (ist->hwaccel_id == HWACCEL_AUTO) { - ist->hwaccel_device_type = dev->type; - } else if (ist->hwaccel_device_type != dev->type) { - av_log(NULL, AV_LOG_ERROR, "Invalid hwaccel device " - "specified for decoder: device %s of type %s is not " - "usable with hwaccel %s.\n", dev->name, - av_hwdevice_get_type_name(dev->type), - av_hwdevice_get_type_name(ist->hwaccel_device_type)); - return AVERROR(EINVAL); - } - } - } else { - if (ist->hwaccel_id == HWACCEL_AUTO) { - auto_device = 1; - } else if (ist->hwaccel_id == HWACCEL_GENERIC) { - type = ist->hwaccel_device_type; - dev = hw_device_get_by_type(type); - - // When "-qsv_device device" is used, an internal QSV device named - // as "__qsv_device" is created. Another QSV device is created too - // if "-init_hw_device qsv=name:device" is used. There are 2 QSV devices - // if both "-qsv_device device" and "-init_hw_device qsv=name:device" - // are used, hw_device_get_by_type(AV_HWDEVICE_TYPE_QSV) returns NULL. - // To keep back-compatibility with the removed ad-hoc libmfx setup code, - // call hw_device_get_by_name("__qsv_device") to select the internal QSV - // device. - if (!dev && type == AV_HWDEVICE_TYPE_QSV) - dev = hw_device_get_by_name("__qsv_device"); - - if (!dev) - err = hw_device_init_from_type(type, NULL, &dev); - } else { - dev = hw_device_match_by_codec(ist->dec); - if (!dev) { - // No device for this codec, but not using generic hwaccel - // and therefore may well not need one - ignore. - return 0; - } - } - } - - if (auto_device) { - int i; - if (!avcodec_get_hw_config(ist->dec, 0)) { - // Decoder does not support any hardware devices. - return 0; - } - for (i = 0; !dev; i++) { - config = avcodec_get_hw_config(ist->dec, i); - if (!config) - break; - type = config->device_type; - dev = hw_device_get_by_type(type); - if (dev) { - av_log(NULL, AV_LOG_INFO, "Using auto " - "hwaccel type %s with existing device %s.\n", - av_hwdevice_get_type_name(type), dev->name); - } - } - for (i = 0; !dev; i++) { - config = avcodec_get_hw_config(ist->dec, i); - if (!config) - break; - type = config->device_type; - // Try to make a new device of this type. - err = hw_device_init_from_type(type, ist->hwaccel_device, - &dev); - if (err < 0) { - // Can't make a device of this type. - continue; - } - if (ist->hwaccel_device) { - av_log(NULL, AV_LOG_INFO, "Using auto " - "hwaccel type %s with new device created " - "from %s.\n", av_hwdevice_get_type_name(type), - ist->hwaccel_device); - } else { - av_log(NULL, AV_LOG_INFO, "Using auto " - "hwaccel type %s with new default device.\n", - av_hwdevice_get_type_name(type)); - } - } - if (dev) { - ist->hwaccel_device_type = type; - } else { - av_log(NULL, AV_LOG_INFO, "Auto hwaccel " - "disabled: no device found.\n"); - ist->hwaccel_id = HWACCEL_NONE; - return 0; - } - } - - if (!dev) { - av_log(NULL, AV_LOG_ERROR, "No device available " - "for decoder: device type %s needed for codec %s.\n", - av_hwdevice_get_type_name(type), ist->dec->name); - return err; - } - - ist->dec_ctx->hw_device_ctx = av_buffer_ref(dev->device_ref); - if (!ist->dec_ctx->hw_device_ctx) - return AVERROR(ENOMEM); - - return 0; -} - -int hw_device_setup_for_encode(OutputStream *ost) -{ - const AVCodecHWConfig *config; - HWDevice *dev = NULL; - AVBufferRef *frames_ref = NULL; - int i; - - if (ost->filter) { - frames_ref = av_buffersink_get_hw_frames_ctx(ost->filter->filter); - if (frames_ref && - ((AVHWFramesContext*)frames_ref->data)->format == - ost->enc_ctx->pix_fmt) { - // Matching format, will try to use hw_frames_ctx. - } else { - frames_ref = NULL; - } - } - - for (i = 0;; i++) { - config = avcodec_get_hw_config(ost->enc_ctx->codec, i); - if (!config) - break; - - if (frames_ref && - config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX && - (config->pix_fmt == AV_PIX_FMT_NONE || - config->pix_fmt == ost->enc_ctx->pix_fmt)) { - av_log(ost->enc_ctx, AV_LOG_VERBOSE, "Using input " - "frames context (format %s) with %s encoder.\n", - av_get_pix_fmt_name(ost->enc_ctx->pix_fmt), - ost->enc_ctx->codec->name); - ost->enc_ctx->hw_frames_ctx = av_buffer_ref(frames_ref); - if (!ost->enc_ctx->hw_frames_ctx) - return AVERROR(ENOMEM); - return 0; - } - - if (!dev && - config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) - dev = hw_device_get_by_type(config->device_type); - } - - if (dev) { - av_log(ost->enc_ctx, AV_LOG_VERBOSE, "Using device %s " - "(type %s) with %s encoder.\n", dev->name, - av_hwdevice_get_type_name(dev->type), ost->enc_ctx->codec->name); - ost->enc_ctx->hw_device_ctx = av_buffer_ref(dev->device_ref); - if (!ost->enc_ctx->hw_device_ctx) - return AVERROR(ENOMEM); - } else { - // No device required, or no device available. - } - return 0; -} - -static int hwaccel_retrieve_data(AVCodecContext *avctx, AVFrame *input) +int hwaccel_retrieve_data(AVCodecContext *avctx, AVFrame *input) { InputStream *ist = avctx->opaque; AVFrame *output = NULL; @@ -539,26 +339,14 @@ static int hwaccel_retrieve_data(AVCodecContext *avctx, AVFrame *input) return err; } -int hwaccel_decode_init(AVCodecContext *avctx) -{ - InputStream *ist = avctx->opaque; - - ist->hwaccel_retrieve_data = &hwaccel_retrieve_data; - - return 0; -} - -int hw_device_setup_for_filter(FilterGraph *fg) +AVBufferRef *hw_device_for_filter(void) { - HWDevice *dev; - int i; - // Pick the last hardware device if the user doesn't pick the device for // filters explicitly with the filter_hw_device option. if (filter_hw_device) - dev = filter_hw_device; + return filter_hw_device->device_ref; else if (nb_hw_devices > 0) { - dev = hw_devices[nb_hw_devices - 1]; + HWDevice *dev = hw_devices[nb_hw_devices - 1]; if (nb_hw_devices > 1) av_log(NULL, AV_LOG_WARNING, "There are %d hardware devices. device " @@ -567,17 +355,9 @@ int hw_device_setup_for_filter(FilterGraph *fg) "%s is not usable for filters.\n", nb_hw_devices, dev->name, av_hwdevice_get_type_name(dev->type), dev->name); - } else - dev = NULL; - - if (dev) { - for (i = 0; i < fg->graph->nb_filters; i++) { - fg->graph->filters[i]->hw_device_ctx = - av_buffer_ref(dev->device_ref); - if (!fg->graph->filters[i]->hw_device_ctx) - return AVERROR(ENOMEM); - } + + return dev->device_ref; } - return 0; + return NULL; } diff --git a/fftools/ffmpeg_mux.c b/fftools/ffmpeg_mux.c index 20524e5a282..24cdf00469b 100644 --- a/fftools/ffmpeg_mux.c +++ b/fftools/ffmpeg_mux.c @@ -62,8 +62,8 @@ static int write_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt) { MuxStream *ms = ms_from_ost(ost); AVFormatContext *s = mux->fc; - AVStream *st = ost->st; int64_t fs; + uint64_t frame_num; int ret; fs = filesize(s->pb); @@ -73,19 +73,11 @@ static int write_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt) goto fail; } - if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && ost->vsync_method == VSYNC_DROP) + if (ost->type == AVMEDIA_TYPE_VIDEO && ost->vsync_method == VSYNC_DROP) pkt->pts = pkt->dts = AV_NOPTS_VALUE; - if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { - if (ost->frame_rate.num && ost->is_cfr) { - if (pkt->duration > 0) - av_log(NULL, AV_LOG_WARNING, "Overriding packet duration by frame rate, this should not happen\n"); - pkt->duration = av_rescale_q(1, av_inv_q(ost->frame_rate), - ost->mux_timebase); - } - } - - av_packet_rescale_ts(pkt, ost->mux_timebase, ost->st->time_base); + av_packet_rescale_ts(pkt, pkt->time_base, ost->st->time_base); + pkt->time_base = ost->st->time_base; if (!(s->oformat->flags & AVFMT_NOTIMESTAMPS)) { if (pkt->dts != AV_NOPTS_VALUE && @@ -99,12 +91,12 @@ static int write_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt) - FFMIN3(pkt->pts, pkt->dts, ms->last_mux_dts + 1) - FFMAX3(pkt->pts, pkt->dts, ms->last_mux_dts + 1); } - if ((st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) && + if ((ost->type == AVMEDIA_TYPE_AUDIO || ost->type == AVMEDIA_TYPE_VIDEO || ost->type == AVMEDIA_TYPE_SUBTITLE) && pkt->dts != AV_NOPTS_VALUE && ms->last_mux_dts != AV_NOPTS_VALUE) { int64_t max = ms->last_mux_dts + !(s->oformat->flags & AVFMT_TS_NONSTRICT); if (pkt->dts < max) { - int loglevel = max - pkt->dts > 2 || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ? AV_LOG_WARNING : AV_LOG_DEBUG; + int loglevel = max - pkt->dts > 2 || ost->type == AVMEDIA_TYPE_VIDEO ? AV_LOG_WARNING : AV_LOG_DEBUG; if (exit_on_error) loglevel = AV_LOG_ERROR; av_log(s, loglevel, "Non-monotonous DTS in output stream " @@ -126,15 +118,15 @@ static int write_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt) } ms->last_mux_dts = pkt->dts; - ost->data_size_mux += pkt->size; - atomic_fetch_add(&ost->packets_written, 1); + ms->data_size_mux += pkt->size; + frame_num = atomic_fetch_add(&ost->packets_written, 1); pkt->stream_index = ost->index; if (debug_ts) { - av_log(NULL, AV_LOG_INFO, "muxer <- type:%s " + av_log(ost, AV_LOG_INFO, "muxer <- type:%s " "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s duration:%s duration_time:%s size:%d\n", - av_get_media_type_string(st->codecpar->codec_type), + av_get_media_type_string(ost->type), av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &ost->st->time_base), av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &ost->st->time_base), av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, &ost->st->time_base), @@ -142,9 +134,14 @@ static int write_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt) ); } + if (ms->stats.io) + enc_stats_write(ost, &ms->stats, NULL, pkt, frame_num); + ret = av_interleaved_write_frame(s, pkt); if (ret < 0) { - print_error("av_interleaved_write_frame()", ret); + av_log(ost, AV_LOG_ERROR, + "Error submitting a packet to the muxer: %s\n", + av_err2str(ret)); goto fail; } @@ -154,19 +151,29 @@ static int write_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt) return ret; } -static int sync_queue_process(Muxer *mux, OutputStream *ost, AVPacket *pkt) +static int sync_queue_process(Muxer *mux, OutputStream *ost, AVPacket *pkt, int *stream_eof) { OutputFile *of = &mux->of; if (ost->sq_idx_mux >= 0) { int ret = sq_send(mux->sq_mux, ost->sq_idx_mux, SQPKT(pkt)); - if (ret < 0) + if (ret < 0) { + if (ret == AVERROR_EOF) + *stream_eof = 1; + return ret; + } while (1) { ret = sq_receive(mux->sq_mux, -1, SQPKT(mux->sq_pkt)); - if (ret < 0) - return (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) ? 0 : ret; + if (ret < 0) { + /* n.b.: We forward EOF from the sync queue, terminating muxing. + * This assumes that if a muxing sync queue is present, then all + * the streams use it. That is true currently, but may change in + * the future, then this code needs to be revisited. + */ + return ret == AVERROR(EAGAIN) ? 0 : ret; + } ret = write_packet(mux, of->streams[ret], mux->sq_pkt); @@ -203,24 +210,28 @@ static void *muxer_thread(void *arg) while (1) { OutputStream *ost; - int stream_idx; + int stream_idx, stream_eof = 0; ret = tq_receive(mux->tq, &stream_idx, pkt); if (stream_idx < 0) { - av_log(NULL, AV_LOG_VERBOSE, - "All streams finished for output file #%d\n", of->index); + av_log(mux, AV_LOG_VERBOSE, "All streams finished\n"); ret = 0; break; } ost = of->streams[stream_idx]; - ret = sync_queue_process(mux, ost, ret < 0 ? NULL : pkt); + ret = sync_queue_process(mux, ost, ret < 0 ? NULL : pkt, &stream_eof); av_packet_unref(pkt); - if (ret == AVERROR_EOF) - tq_receive_finish(mux->tq, stream_idx); - else if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, - "Error muxing a packet for output file #%d\n", of->index); + if (ret == AVERROR_EOF) { + if (stream_eof) { + tq_receive_finish(mux->tq, stream_idx); + } else { + av_log(mux, AV_LOG_VERBOSE, "Muxer returned EOF\n"); + ret = 0; + break; + } + } else if (ret < 0) { + av_log(mux, AV_LOG_ERROR, "Error muxing a packet\n"); break; } } @@ -231,7 +242,7 @@ static void *muxer_thread(void *arg) for (unsigned int i = 0; i < mux->fc->nb_streams; i++) tq_receive_finish(mux->tq, i); - av_log(NULL, AV_LOG_VERBOSE, "Terminating muxer thread %d\n", of->index); + av_log(mux, AV_LOG_VERBOSE, "Terminating muxer thread\n"); return (void*)(intptr_t)ret; } @@ -258,7 +269,7 @@ static int thread_submit_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt) return ret == AVERROR_EOF ? 0 : ret; } -static int queue_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt) +static int queue_packet(OutputStream *ost, AVPacket *pkt) { MuxStream *ms = ms_from_ost(ost); AVPacket *tmp_pkt = NULL; @@ -273,7 +284,7 @@ static int queue_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt) size_t new_size = FFMIN(2 * cur_size, limit); if (new_size <= cur_size) { - av_log(NULL, AV_LOG_ERROR, + av_log(ost, AV_LOG_ERROR, "Too many packets buffered for output stream %d:%d.\n", ost->file_index, ost->st->index); return AVERROR(ENOSPC); @@ -308,7 +319,7 @@ static int submit_packet(Muxer *mux, AVPacket *pkt, OutputStream *ost) return thread_submit_packet(mux, ost, pkt); } else { /* the muxer is not initialized yet, buffer the packet */ - ret = queue_packet(mux, ost, pkt); + ret = queue_packet(ost, pkt); if (ret < 0) { if (pkt) av_packet_unref(pkt); @@ -319,30 +330,36 @@ static int submit_packet(Muxer *mux, AVPacket *pkt, OutputStream *ost) return 0; } -void of_output_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int eof) +int of_output_packet(OutputFile *of, OutputStream *ost, AVPacket *pkt) { Muxer *mux = mux_from_of(of); MuxStream *ms = ms_from_ost(ost); const char *err_msg; int ret = 0; - if (!eof && pkt->dts != AV_NOPTS_VALUE) - ost->last_mux_dts = av_rescale_q(pkt->dts, ost->mux_timebase, AV_TIME_BASE_Q); + if (pkt && pkt->dts != AV_NOPTS_VALUE) + ost->last_mux_dts = av_rescale_q(pkt->dts, pkt->time_base, AV_TIME_BASE_Q); + + /* rescale timestamps to the muxing timebase */ + if (pkt) { + av_packet_rescale_ts(pkt, pkt->time_base, ost->mux_timebase); + pkt->time_base = ost->mux_timebase; + } /* apply the output bitstream filters */ if (ms->bsf_ctx) { int bsf_eof = 0; - ret = av_bsf_send_packet(ms->bsf_ctx, eof ? NULL : pkt); + ret = av_bsf_send_packet(ms->bsf_ctx, pkt); if (ret < 0) { err_msg = "submitting a packet for bitstream filtering"; goto fail; } while (!bsf_eof) { - ret = av_bsf_receive_packet(ms->bsf_ctx, pkt); + ret = av_bsf_receive_packet(ms->bsf_ctx, ms->bsf_pkt); if (ret == AVERROR(EAGAIN)) - return; + return 0; else if (ret == AVERROR_EOF) bsf_eof = 1; else if (ret < 0) { @@ -350,27 +367,103 @@ void of_output_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int eof) goto fail; } - ret = submit_packet(mux, bsf_eof ? NULL : pkt, ost); + ret = submit_packet(mux, bsf_eof ? NULL : ms->bsf_pkt, ost); if (ret < 0) goto mux_fail; } } else { - ret = submit_packet(mux, eof ? NULL : pkt, ost); + ret = submit_packet(mux, pkt, ost); if (ret < 0) goto mux_fail; } - return; + return 0; mux_fail: err_msg = "submitting a packet to the muxer"; fail: - av_log(NULL, AV_LOG_ERROR, "Error %s for output stream #%d:%d.\n", - err_msg, ost->file_index, ost->index); - if (exit_on_error) - exit_program(1); + av_log(ost, AV_LOG_ERROR, "Error %s\n", err_msg); + return exit_on_error ? ret : 0; +} + +int of_streamcopy(OutputStream *ost, const AVPacket *pkt, int64_t dts) +{ + OutputFile *of = output_files[ost->file_index]; + MuxStream *ms = ms_from_ost(ost); + int64_t start_time = (of->start_time == AV_NOPTS_VALUE) ? 0 : of->start_time; + int64_t ost_tb_start_time = av_rescale_q(start_time, AV_TIME_BASE_Q, ost->mux_timebase); + AVPacket *opkt = ms->pkt; + int ret; + + av_packet_unref(opkt); + + if (of->recording_time != INT64_MAX && + dts >= of->recording_time + start_time) + pkt = NULL; + + // EOF: flush output bitstream filters. + if (!pkt) + return of_output_packet(of, ost, NULL); + + if (!ms->streamcopy_started && !(pkt->flags & AV_PKT_FLAG_KEY) && + !ms->copy_initial_nonkeyframes) + return 0; + if (!ms->streamcopy_started) { + if (!ms->copy_prior_start && + (pkt->pts == AV_NOPTS_VALUE ? + dts < ms->ts_copy_start : + pkt->pts < av_rescale_q(ms->ts_copy_start, AV_TIME_BASE_Q, pkt->time_base))) + return 0; + + if (of->start_time != AV_NOPTS_VALUE && dts < of->start_time) + return 0; + } + + ret = av_packet_ref(opkt, pkt); + if (ret < 0) + return ret; + + opkt->time_base = ost->mux_timebase; + + if (pkt->pts != AV_NOPTS_VALUE) + opkt->pts = av_rescale_q(pkt->pts, pkt->time_base, opkt->time_base) - ost_tb_start_time; + + if (pkt->dts == AV_NOPTS_VALUE) { + opkt->dts = av_rescale_q(dts, AV_TIME_BASE_Q, opkt->time_base); + } else if (ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + int duration = av_get_audio_frame_duration2(ost->par_in, pkt->size); + if(!duration) + duration = ost->par_in->frame_size; + opkt->dts = av_rescale_delta(pkt->time_base, pkt->dts, + (AVRational){1, ost->par_in->sample_rate}, duration, + &ms->ts_rescale_delta_last, opkt->time_base); + /* dts will be set immediately afterwards to what pts is now */ + opkt->pts = opkt->dts - ost_tb_start_time; + } else + opkt->dts = av_rescale_q(pkt->dts, pkt->time_base, opkt->time_base); + opkt->dts -= ost_tb_start_time; + + opkt->duration = av_rescale_q(pkt->duration, pkt->time_base, opkt->time_base); + + { + int ret = trigger_fix_sub_duration_heartbeat(ost, pkt); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, + "Subtitle heartbeat logic failed in %s! (%s)\n", + __func__, av_err2str(ret)); + return ret; + } + } + + ret = of_output_packet(of, ost, opkt); + if (ret < 0) + return ret; + + ms->streamcopy_started = 1; + + return 0; } static int thread_stop(Muxer *mux) @@ -390,11 +483,6 @@ static int thread_stop(Muxer *mux) return (int)(intptr_t)ret; } -static void pkt_move(void *dst, void *src) -{ - av_packet_move_ref(dst, src); -} - static int thread_start(Muxer *mux) { AVFormatContext *fc = mux->fc; @@ -511,10 +599,8 @@ int mux_check_init(Muxer *mux) ret = avformat_write_header(fc, &mux->opts); if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, - "Could not write header for output file #%d " - "(incorrect codec parameters ?): %s\n", - of->index, av_err2str(ret)); + av_log(mux, AV_LOG_ERROR, "Could not write header (incorrect codec " + "parameters ?): %s\n", av_err2str(ret)); return ret; } //assert_avoptions(of->opts); @@ -553,9 +639,9 @@ static int bsf_init(MuxStream *ms) int ret; if (!ctx) - return 0; + return avcodec_parameters_copy(ost->st->codecpar, ost->par_in); - ret = avcodec_parameters_copy(ctx->par_in, ost->st->codecpar); + ret = avcodec_parameters_copy(ctx->par_in, ost->par_in); if (ret < 0) return ret; @@ -563,7 +649,7 @@ static int bsf_init(MuxStream *ms) ret = av_bsf_init(ctx); if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Error initializing bitstream filter: %s\n", + av_log(ms, AV_LOG_ERROR, "Error initializing bitstream filter: %s\n", ctx->filter->name); return ret; } @@ -573,6 +659,10 @@ static int bsf_init(MuxStream *ms) return ret; ost->st->time_base = ctx->time_base_out; + ms->bsf_pkt = av_packet_alloc(); + if (!ms->bsf_pkt) + return AVERROR(ENOMEM); + return 0; } @@ -582,9 +672,6 @@ int of_stream_init(OutputFile *of, OutputStream *ost) MuxStream *ms = ms_from_ost(ost); int ret; - if (ost->sq_idx_mux >= 0) - sq_set_tb(mux->sq_mux, ost->sq_idx_mux, ost->mux_timebase); - /* initialize bitstream filters for the output stream * needs to be done here, because the codec id for streamcopy is not * known until now */ @@ -592,33 +679,140 @@ int of_stream_init(OutputFile *of, OutputStream *ost) if (ret < 0) return ret; + if (ms->stream_duration) { + ost->st->duration = av_rescale_q(ms->stream_duration, ms->stream_duration_tb, + ost->st->time_base); + } + ost->initialized = 1; return mux_check_init(mux); } +static int check_written(OutputFile *of) +{ + int64_t total_packets_written = 0; + int pass1_used = 1; + int ret = 0; + + for (int i = 0; i < of->nb_streams; i++) { + OutputStream *ost = of->streams[i]; + uint64_t packets_written = atomic_load(&ost->packets_written); + + total_packets_written += packets_written; + + if (ost->enc_ctx && + (ost->enc_ctx->flags & (AV_CODEC_FLAG_PASS1 | AV_CODEC_FLAG_PASS2)) + != AV_CODEC_FLAG_PASS1) + pass1_used = 0; + + if (!packets_written && + (abort_on_flags & ABORT_ON_FLAG_EMPTY_OUTPUT_STREAM)) { + av_log(ost, AV_LOG_FATAL, "Empty output stream\n"); + ret = err_merge(ret, AVERROR(EINVAL)); + } + } + + if (!total_packets_written) { + int level = AV_LOG_WARNING; + + if (abort_on_flags & ABORT_ON_FLAG_EMPTY_OUTPUT) { + ret = err_merge(ret, AVERROR(EINVAL)); + level = AV_LOG_FATAL; + } + + av_log(of, level, "Output file is empty, nothing was encoded%s\n", + pass1_used ? "" : "(check -ss / -t / -frames parameters if used)"); + } + + return ret; +} + +static void mux_final_stats(Muxer *mux) +{ + OutputFile *of = &mux->of; + uint64_t total_packets = 0, total_size = 0; + uint64_t video_size = 0, audio_size = 0, subtitle_size = 0, + extra_size = 0, other_size = 0; + + uint8_t overhead[16] = "unknown"; + int64_t file_size = of_filesize(of); + + av_log(of, AV_LOG_VERBOSE, "Output file #%d (%s):\n", + of->index, of->url); + + for (int j = 0; j < of->nb_streams; j++) { + OutputStream *ost = of->streams[j]; + MuxStream *ms = ms_from_ost(ost); + const AVCodecParameters *par = ost->st->codecpar; + const enum AVMediaType type = par->codec_type; + const uint64_t s = ms->data_size_mux; + + switch (type) { + case AVMEDIA_TYPE_VIDEO: video_size += s; break; + case AVMEDIA_TYPE_AUDIO: audio_size += s; break; + case AVMEDIA_TYPE_SUBTITLE: subtitle_size += s; break; + default: other_size += s; break; + } + + extra_size += par->extradata_size; + total_size += s; + total_packets += atomic_load(&ost->packets_written); + + av_log(of, AV_LOG_VERBOSE, " Output stream #%d:%d (%s): ", + of->index, j, av_get_media_type_string(type)); + if (ost->enc) { + av_log(of, AV_LOG_VERBOSE, "%"PRIu64" frames encoded", + ost->frames_encoded); + if (type == AVMEDIA_TYPE_AUDIO) + av_log(of, AV_LOG_VERBOSE, " (%"PRIu64" samples)", ost->samples_encoded); + av_log(of, AV_LOG_VERBOSE, "; "); + } + + av_log(of, AV_LOG_VERBOSE, "%"PRIu64" packets muxed (%"PRIu64" bytes); ", + atomic_load(&ost->packets_written), s); + + av_log(of, AV_LOG_VERBOSE, "\n"); + } + + av_log(of, AV_LOG_VERBOSE, " Total: %"PRIu64" packets (%"PRIu64" bytes) muxed\n", + total_packets, total_size); + + if (total_size && file_size > 0 && file_size >= total_size) { + snprintf(overhead, sizeof(overhead), "%f%%", + 100.0 * (file_size - total_size) / total_size); + } + + av_log(of, AV_LOG_INFO, + "video:%1.0fkB audio:%1.0fkB subtitle:%1.0fkB other streams:%1.0fkB " + "global headers:%1.0fkB muxing overhead: %s\n", + video_size / 1024.0, + audio_size / 1024.0, + subtitle_size / 1024.0, + other_size / 1024.0, + extra_size / 1024.0, + overhead); +} + int of_write_trailer(OutputFile *of) { Muxer *mux = mux_from_of(of); AVFormatContext *fc = mux->fc; - int ret; + int ret, mux_result = 0; if (!mux->tq) { - av_log(NULL, AV_LOG_ERROR, - "Nothing was written into output file %d (%s), because " - "at least one of its streams received no packets.\n", - of->index, fc->url); + av_log(mux, AV_LOG_ERROR, + "Nothing was written into output file, because " + "at least one of its streams received no packets.\n"); return AVERROR(EINVAL); } - ret = thread_stop(mux); - if (ret < 0) - main_return_code = ret; + mux_result = thread_stop(mux); ret = av_write_trailer(fc); if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Error writing trailer of %s: %s\n", fc->url, av_err2str(ret)); - return ret; + av_log(mux, AV_LOG_ERROR, "Error writing trailer: %s\n", av_err2str(ret)); + mux_result = err_merge(mux_result, ret); } mux->last_filesize = filesize(fc->pb); @@ -626,13 +820,18 @@ int of_write_trailer(OutputFile *of) if (!(of->format->flags & AVFMT_NOFILE)) { ret = avio_closep(&fc->pb); if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Error closing file %s: %s\n", - fc->url, av_err2str(ret)); - return ret; + av_log(mux, AV_LOG_ERROR, "Error closing file: %s\n", av_err2str(ret)); + mux_result = err_merge(mux_result, ret); } } - return 0; + mux_final_stats(mux); + + // check whether anything was actually written + ret = check_written(of); + mux_result = err_merge(mux_result, ret); + + return mux_result; } static void ost_free(OutputStream **post) @@ -644,9 +843,11 @@ static void ost_free(OutputStream **post) return; ms = ms_from_ost(ost); + enc_free(&ost->enc); + if (ost->logfile) { if (fclose(ost->logfile)) - av_log(NULL, AV_LOG_ERROR, + av_log(ms, AV_LOG_ERROR, "Error closing logfile, loss of information possible: %s\n", av_err2str(AVERROR(errno))); ost->logfile = NULL; @@ -659,18 +860,17 @@ static void ost_free(OutputStream **post) av_fifo_freep2(&ms->muxing_queue); } + avcodec_parameters_free(&ost->par_in); + av_bsf_free(&ms->bsf_ctx); + av_packet_free(&ms->bsf_pkt); - av_frame_free(&ost->filtered_frame); - av_frame_free(&ost->sq_frame); - av_frame_free(&ost->last_frame); - av_packet_free(&ost->pkt); + av_packet_free(&ms->pkt); av_dict_free(&ost->encoder_opts); av_freep(&ost->kf.pts); av_expr_free(ost->kf.pexpr); - av_freep(&ost->avfilter); av_freep(&ost->logfile_prefix); av_freep(&ost->apad); @@ -686,6 +886,18 @@ static void ost_free(OutputStream **post) av_freep(&ost->enc_ctx->stats_in); avcodec_free_context(&ost->enc_ctx); + for (int i = 0; i < ost->enc_stats_pre.nb_components; i++) + av_freep(&ost->enc_stats_pre.components[i].str); + av_freep(&ost->enc_stats_pre.components); + + for (int i = 0; i < ost->enc_stats_post.nb_components; i++) + av_freep(&ost->enc_stats_post.components[i].str); + av_freep(&ost->enc_stats_post.components); + + for (int i = 0; i < ms->stats.nb_components; i++) + av_freep(&ms->stats.components[i].str); + av_freep(&ms->stats.components); + av_freep(post); } diff --git a/fftools/ffmpeg_mux.h b/fftools/ffmpeg_mux.h index 6a72b9dc910..7f34b86548c 100644 --- a/fftools/ffmpeg_mux.h +++ b/fftools/ffmpeg_mux.h @@ -37,10 +37,18 @@ typedef struct MuxStream { OutputStream ost; + // name used for logging + char log_name[32]; + /* the packets are buffered here until the muxer is ready to be initialized */ AVFifo *muxing_queue; AVBSFContext *bsf_ctx; + AVPacket *bsf_pkt; + + AVPacket *pkt; + + EncStats stats; int64_t max_frames; @@ -55,14 +63,35 @@ typedef struct MuxStream { /* Threshold after which max_muxing_queue_size will be in effect */ size_t muxing_queue_data_threshold; + // timestamp from which the streamcopied streams should start, + // in AV_TIME_BASE_Q; + // everything before it should be discarded + int64_t ts_copy_start; + /* dts of the last packet sent to the muxer, in the stream timebase * used for making up missing dts values */ int64_t last_mux_dts; + + int64_t stream_duration; + AVRational stream_duration_tb; + + // audio streamcopy - state for av_rescale_delta() + int64_t ts_rescale_delta_last; + + // combined size of all the packets sent to the muxer + uint64_t data_size_mux; + + int copy_initial_nonkeyframes; + int copy_prior_start; + int streamcopy_started; } MuxStream; typedef struct Muxer { OutputFile of; + // name used for logging + char log_name[32]; + AVFormatContext *fc; pthread_t thread; diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c index 9eea8639dcf..0289cdabad2 100644 --- a/fftools/ffmpeg_mux_init.c +++ b/fftools/ffmpeg_mux_init.c @@ -37,6 +37,7 @@ #include "libavutil/avutil.h" #include "libavutil/bprint.h" #include "libavutil/dict.h" +#include "libavutil/display.h" #include "libavutil/getenv_utf8.h" #include "libavutil/intreadwrite.h" #include "libavutil/log.h" @@ -55,8 +56,15 @@ static const char *const opt_name_copy_initial_nonkeyframes[] = {"copyinkf", NUL static const char *const opt_name_copy_prior_start[] = {"copypriorss", NULL}; static const char *const opt_name_disposition[] = {"disposition", NULL}; static const char *const opt_name_enc_time_bases[] = {"enc_time_base", NULL}; +static const char *const opt_name_enc_stats_pre[] = {"enc_stats_pre", NULL}; +static const char *const opt_name_enc_stats_post[] = {"enc_stats_post", NULL}; +static const char *const opt_name_mux_stats[] = {"mux_stats", NULL}; +static const char *const opt_name_enc_stats_pre_fmt[] = {"enc_stats_pre_fmt", NULL}; +static const char *const opt_name_enc_stats_post_fmt[] = {"enc_stats_post_fmt", NULL}; +static const char *const opt_name_mux_stats_fmt[] = {"mux_stats_fmt", NULL}; static const char *const opt_name_filters[] = {"filter", "af", "vf", NULL}; static const char *const opt_name_filter_scripts[] = {"filter_script", NULL}; +static const char *const opt_name_fix_sub_duration_heartbeat[] = {"fix_sub_duration_heartbeat", NULL}; static const char *const opt_name_fps_mode[] = {"fps_mode", NULL}; static const char *const opt_name_force_fps[] = {"force_fps", NULL}; static const char *const opt_name_forced_key_frames[] = {"forced_key_frames", NULL}; @@ -100,29 +108,41 @@ static int check_opt_bitexact(void *ctx, const AVDictionary *opts, static int choose_encoder(const OptionsContext *o, AVFormatContext *s, OutputStream *ost, const AVCodec **enc) { - enum AVMediaType type = ost->st->codecpar->codec_type; + enum AVMediaType type = ost->type; char *codec_name = NULL; *enc = NULL; - if (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO || type == AVMEDIA_TYPE_SUBTITLE) { - MATCH_PER_STREAM_OPT(codec_names, str, codec_name, s, ost->st); - if (!codec_name) { - ost->st->codecpar->codec_id = av_guess_codec(s->oformat, NULL, s->url, - NULL, ost->st->codecpar->codec_type); - *enc = avcodec_find_encoder(ost->st->codecpar->codec_id); - if (!*enc) { - av_log(NULL, AV_LOG_FATAL, "Automatic encoder selection failed for " - "output stream #%d:%d. Default encoder for format %s (codec %s) is " - "probably disabled. Please choose an encoder manually.\n", - ost->file_index, ost->index, s->oformat->name, - avcodec_get_name(ost->st->codecpar->codec_id)); - return AVERROR_ENCODER_NOT_FOUND; - } - } else if (strcmp(codec_name, "copy")) { - *enc = find_codec_or_die(codec_name, ost->st->codecpar->codec_type, 1); - ost->st->codecpar->codec_id = (*enc)->id; + MATCH_PER_STREAM_OPT(codec_names, str, codec_name, s, ost->st); + + if (type != AVMEDIA_TYPE_VIDEO && + type != AVMEDIA_TYPE_AUDIO && + type != AVMEDIA_TYPE_SUBTITLE) { + if (codec_name && strcmp(codec_name, "copy")) { + const char *type_str = av_get_media_type_string(type); + av_log(ost, AV_LOG_FATAL, + "Encoder '%s' specified, but only '-codec copy' supported " + "for %s streams\n", codec_name, type_str); + return AVERROR(ENOSYS); + } + return 0; + } + + if (!codec_name) { + ost->par_in->codec_id = av_guess_codec(s->oformat, NULL, s->url, NULL, ost->type); + *enc = avcodec_find_encoder(ost->par_in->codec_id); + if (!*enc) { + av_log(ost, AV_LOG_FATAL, "Automatic encoder selection failed " + "Default encoder for format %s (codec %s) is " + "probably disabled. Please choose an encoder manually.\n", + s->oformat->name, avcodec_get_name(ost->par_in->codec_id)); + return AVERROR_ENCODER_NOT_FOUND; } + } else if (strcmp(codec_name, "copy")) { + int ret = find_codec(ost, codec_name, ost->type, 1, enc); + if (ret < 0) + return ret; + ost->par_in->codec_id = (*enc)->id; } return 0; @@ -136,7 +156,8 @@ static char *get_line(AVIOContext *s, AVBPrint *bprint) av_bprint_chars(bprint, c, 1); if (!av_bprint_is_complete(bprint)) - report_and_exit(AVERROR(ENOMEM)); + return NULL; + return bprint->str; } @@ -170,236 +191,305 @@ static int get_preset_file_2(const char *preset_name, const char *codec_name, AV return ret; } -static OutputStream *new_output_stream(Muxer *mux, const OptionsContext *o, - enum AVMediaType type, InputStream *ist) -{ - AVFormatContext *oc = mux->fc; - MuxStream *ms; - OutputStream *ost; - const AVCodec *enc; - AVStream *st = avformat_new_stream(oc, NULL); - int idx = oc->nb_streams - 1, ret = 0; - const char *bsfs = NULL, *time_base = NULL; - char *next, *codec_tag = NULL; - double qscale = -1; - int i; +typedef struct EncStatsFile { + char *path; + AVIOContext *io; +} EncStatsFile; - if (!st) - report_and_exit(AVERROR(ENOMEM)); +static EncStatsFile *enc_stats_files; +static int nb_enc_stats_files; - if (oc->nb_streams - 1 < o->nb_streamid_map) - st->id = o->streamid_map[oc->nb_streams - 1]; +static int enc_stats_get_file(AVIOContext **io, const char *path) +{ + EncStatsFile *esf; + int ret; - ms = allocate_array_elem(&mux->of.streams, sizeof(MuxStream), - &mux->of.nb_streams); - ost = &ms->ost; + for (int i = 0; i < nb_enc_stats_files; i++) + if (!strcmp(path, enc_stats_files[i].path)) { + *io = enc_stats_files[i].io; + return 0; + } - ms->muxing_queue = av_fifo_alloc2(8, sizeof(AVPacket*), 0); - if (!ms->muxing_queue) - report_and_exit(AVERROR(ENOMEM)); - ms->last_mux_dts = AV_NOPTS_VALUE; + ret = GROW_ARRAY(enc_stats_files, nb_enc_stats_files); + if (ret < 0) + return ret; - ost->file_index = nb_output_files - 1; - ost->index = idx; - ost->st = st; - ost->kf.ref_pts = AV_NOPTS_VALUE; - st->codecpar->codec_type = type; + esf = &enc_stats_files[nb_enc_stats_files - 1]; - ret = choose_encoder(o, oc, ost, &enc); + ret = avio_open2(&esf->io, path, AVIO_FLAG_WRITE, &int_cb, NULL); if (ret < 0) { - av_log(NULL, AV_LOG_FATAL, "Error selecting an encoder for stream " - "%d:%d\n", ost->file_index, ost->index); - exit_program(1); + av_log(NULL, AV_LOG_ERROR, "Error opening stats file '%s': %s\n", + path, av_err2str(ret)); + return ret; } - if (enc) { - ost->enc_ctx = avcodec_alloc_context3(enc); - if (!ost->enc_ctx) - report_and_exit(AVERROR(ENOMEM)); + esf->path = av_strdup(path); + if (!esf->path) + return AVERROR(ENOMEM); + + *io = esf->io; + + return 0; +} + +void of_enc_stats_close(void) +{ + for (int i = 0; i < nb_enc_stats_files; i++) { + av_freep(&enc_stats_files[i].path); + avio_closep(&enc_stats_files[i].io); } + av_freep(&enc_stats_files); + nb_enc_stats_files = 0; +} - ost->filtered_frame = av_frame_alloc(); - if (!ost->filtered_frame) - report_and_exit(AVERROR(ENOMEM)); +static int unescape(char **pdst, size_t *dst_len, + const char **pstr, char delim) +{ + const char *str = *pstr; + char *dst; + size_t len, idx; - ost->pkt = av_packet_alloc(); - if (!ost->pkt) - report_and_exit(AVERROR(ENOMEM)); + *pdst = NULL; - if (ost->enc_ctx) { - AVCodecContext *enc = ost->enc_ctx; - AVIOContext *s = NULL; - char *buf = NULL, *arg = NULL, *preset = NULL; + len = strlen(str); + if (!len) + return 0; - ost->encoder_opts = filter_codec_opts(o->g->codec_opts, enc->codec_id, - oc, st, enc->codec); + dst = av_malloc(len + 1); + if (!dst) + return AVERROR(ENOMEM); - MATCH_PER_STREAM_OPT(presets, str, preset, oc, st); - ost->autoscale = 1; - MATCH_PER_STREAM_OPT(autoscale, i, ost->autoscale, oc, st); - if (preset && (!(ret = get_preset_file_2(preset, enc->codec->name, &s)))) { - AVBPrint bprint; - av_bprint_init(&bprint, 0, AV_BPRINT_SIZE_UNLIMITED); - do { - av_bprint_clear(&bprint); - buf = get_line(s, &bprint); - if (!buf[0] || buf[0] == '#') - continue; - if (!(arg = strchr(buf, '='))) { - av_log(NULL, AV_LOG_FATAL, "Invalid line found in the preset file.\n"); - exit_program(1); - } - *arg++ = 0; - av_dict_set(&ost->encoder_opts, buf, arg, AV_DICT_DONT_OVERWRITE); - } while (!s->eof_reached); - av_bprint_finalize(&bprint, NULL); - avio_closep(&s); - } - if (ret) { - av_log(NULL, AV_LOG_FATAL, - "Preset %s specified for stream %d:%d, but could not be opened.\n", - preset, ost->file_index, ost->index); - exit_program(1); - } - } else { - ost->encoder_opts = filter_codec_opts(o->g->codec_opts, AV_CODEC_ID_NONE, oc, st, NULL); + for (idx = 0; *str; idx++, str++) { + if (str[0] == '\\' && str[1]) + str++; + else if (*str == delim) + break; + + dst[idx] = *str; + } + if (!idx) { + av_freep(&dst); + return 0; } + dst[idx] = 0; - if (o->bitexact) { - ost->bitexact = 1; - } else if (ost->enc_ctx) { - ost->bitexact = check_opt_bitexact(ost->enc_ctx, ost->encoder_opts, "flags", - AV_CODEC_FLAG_BITEXACT); - } + *pdst = dst; + *dst_len = idx; + *pstr = str; - MATCH_PER_STREAM_OPT(time_bases, str, time_base, oc, st); - if (time_base) { - AVRational q; - if (av_parse_ratio(&q, time_base, INT_MAX, 0, NULL) < 0 || - q.num <= 0 || q.den <= 0) { - av_log(NULL, AV_LOG_FATAL, "Invalid time base: %s\n", time_base); - exit_program(1); - } - st->time_base = q; - } + return 0; +} - MATCH_PER_STREAM_OPT(enc_time_bases, str, time_base, oc, st); - if (time_base) { - AVRational q; - if (av_parse_ratio(&q, time_base, INT_MAX, 0, NULL) < 0 || - q.den <= 0) { - av_log(NULL, AV_LOG_FATAL, "Invalid time base: %s\n", time_base); - exit_program(1); +static int enc_stats_init(OutputStream *ost, EncStats *es, int pre, + const char *path, const char *fmt_spec) +{ + static const struct { + enum EncStatsType type; + const char *str; + int pre_only:1; + int post_only:1; + int need_input_data:1; + } fmt_specs[] = { + { ENC_STATS_FILE_IDX, "fidx" }, + { ENC_STATS_STREAM_IDX, "sidx" }, + { ENC_STATS_FRAME_NUM, "n" }, + { ENC_STATS_FRAME_NUM_IN, "ni", 0, 0, 1 }, + { ENC_STATS_TIMEBASE, "tb" }, + { ENC_STATS_TIMEBASE_IN, "tbi", 0, 0, 1 }, + { ENC_STATS_PTS, "pts" }, + { ENC_STATS_PTS_TIME, "t" }, + { ENC_STATS_PTS_IN, "ptsi", 0, 0, 1 }, + { ENC_STATS_PTS_TIME_IN, "ti", 0, 0, 1 }, + { ENC_STATS_DTS, "dts", 0, 1 }, + { ENC_STATS_DTS_TIME, "dt", 0, 1 }, + { ENC_STATS_SAMPLE_NUM, "sn", 1 }, + { ENC_STATS_NB_SAMPLES, "samp", 1 }, + { ENC_STATS_PKT_SIZE, "size", 0, 1 }, + { ENC_STATS_BITRATE, "br", 0, 1 }, + { ENC_STATS_AVG_BITRATE, "abr", 0, 1 }, + }; + const char *next = fmt_spec; + + int ret; + + while (*next) { + EncStatsComponent *c; + char *val; + size_t val_len; + + // get the sequence up until next opening brace + ret = unescape(&val, &val_len, &next, '{'); + if (ret < 0) + return ret; + + if (val) { + ret = GROW_ARRAY(es->components, es->nb_components); + if (ret < 0) { + av_freep(&val); + return ret; + } + + c = &es->components[es->nb_components - 1]; + c->type = ENC_STATS_LITERAL; + c->str = val; + c->str_len = val_len; } - ost->enc_timebase = q; - } - ms->max_frames = INT64_MAX; - MATCH_PER_STREAM_OPT(max_frames, i64, ms->max_frames, oc, st); - for (i = 0; inb_max_frames; i++) { - char *p = o->max_frames[i].specifier; - if (!*p && type != AVMEDIA_TYPE_VIDEO) { - av_log(NULL, AV_LOG_WARNING, "Applying unspecific -frames to non video streams, maybe you meant -vframes ?\n"); + if (!*next) break; - } - } + next++; - ost->copy_prior_start = -1; - MATCH_PER_STREAM_OPT(copy_prior_start, i, ost->copy_prior_start, oc ,st); + // get the part inside braces + ret = unescape(&val, &val_len, &next, '}'); + if (ret < 0) + return ret; - MATCH_PER_STREAM_OPT(bitstream_filters, str, bsfs, oc, st); - if (bsfs && *bsfs) { - ret = av_bsf_list_parse_str(bsfs, &ms->bsf_ctx); - if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Error parsing bitstream filter sequence '%s': %s\n", bsfs, av_err2str(ret)); - exit_program(1); + if (!val) { + av_log(NULL, AV_LOG_ERROR, + "Empty formatting directive in: %s\n", fmt_spec); + return AVERROR(EINVAL); } - } - MATCH_PER_STREAM_OPT(codec_tags, str, codec_tag, oc, st); - if (codec_tag) { - uint32_t tag = strtol(codec_tag, &next, 0); - if (*next) - tag = AV_RL32(codec_tag); - ost->st->codecpar->codec_tag = tag; - if (ost->enc_ctx) - ost->enc_ctx->codec_tag = tag; - } + if (!*next) { + av_log(NULL, AV_LOG_ERROR, + "Missing closing brace in: %s\n", fmt_spec); + ret = AVERROR(EINVAL); + goto fail; + } + next++; - MATCH_PER_STREAM_OPT(qscale, dbl, qscale, oc, st); - if (ost->enc_ctx && qscale >= 0) { - ost->enc_ctx->flags |= AV_CODEC_FLAG_QSCALE; - ost->enc_ctx->global_quality = FF_QP2LAMBDA * qscale; - } + ret = GROW_ARRAY(es->components, es->nb_components); + if (ret < 0) + goto fail; - ms->max_muxing_queue_size = 128; - MATCH_PER_STREAM_OPT(max_muxing_queue_size, i, ms->max_muxing_queue_size, oc, st); + c = &es->components[es->nb_components - 1]; - ms->muxing_queue_data_threshold = 50*1024*1024; - MATCH_PER_STREAM_OPT(muxing_queue_data_threshold, i, ms->muxing_queue_data_threshold, oc, st); + for (size_t i = 0; i < FF_ARRAY_ELEMS(fmt_specs); i++) { + if (!strcmp(val, fmt_specs[i].str)) { + if ((pre && fmt_specs[i].post_only) || (!pre && fmt_specs[i].pre_only)) { + av_log(NULL, AV_LOG_ERROR, + "Format directive '%s' may only be used %s-encoding\n", + val, pre ? "post" : "pre"); + ret = AVERROR(EINVAL); + goto fail; + } - MATCH_PER_STREAM_OPT(bits_per_raw_sample, i, ost->bits_per_raw_sample, - oc, st); + c->type = fmt_specs[i].type; - if (oc->oformat->flags & AVFMT_GLOBALHEADER && ost->enc_ctx) - ost->enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; + if (fmt_specs[i].need_input_data && !ost->ist) { + av_log(ost, AV_LOG_WARNING, + "Format directive '%s' is unavailable, because " + "this output stream has no associated input stream\n", + val); + } - av_dict_copy(&ost->sws_dict, o->g->sws_dict, 0); + break; + } + } - av_dict_copy(&ost->swr_opts, o->g->swr_opts, 0); - if (ost->enc_ctx && av_get_exact_bits_per_sample(ost->enc_ctx->codec_id) == 24) - av_dict_set(&ost->swr_opts, "output_sample_bits", "24", 0); + if (!c->type) { + av_log(NULL, AV_LOG_ERROR, "Invalid format directive: %s\n", val); + ret = AVERROR(EINVAL); + goto fail; + } - if (ist) { - ost->ist = ist; - ost->ist->discard = 0; - ost->ist->st->discard = ost->ist->user_set_discard; +fail: + av_freep(&val); + if (ret < 0) + return ret; } - ost->last_mux_dts = AV_NOPTS_VALUE; - ost->last_filter_pts = AV_NOPTS_VALUE; - MATCH_PER_STREAM_OPT(copy_initial_nonkeyframes, i, - ost->copy_initial_nonkeyframes, oc, st); + ret = enc_stats_get_file(&es->io, path); + if (ret < 0) + return ret; - return ost; + return 0; } -static char *get_ost_filters(const OptionsContext *o, AVFormatContext *oc, - OutputStream *ost) +static const char *output_stream_item_name(void *obj) { - AVStream *st = ost->st; + const MuxStream *ms = obj; - if (ost->filters_script && ost->filters) { - av_log(NULL, AV_LOG_ERROR, "Both -filter and -filter_script set for " - "output stream #%d:%d.\n", nb_output_files, st->index); - exit_program(1); - } + return ms->log_name; +} + +static const AVClass output_stream_class = { + .class_name = "OutputStream", + .version = LIBAVUTIL_VERSION_INT, + .item_name = output_stream_item_name, + .category = AV_CLASS_CATEGORY_MUXER, +}; + +static MuxStream *mux_stream_alloc(Muxer *mux, enum AVMediaType type) +{ + const char *type_str = av_get_media_type_string(type); + MuxStream *ms; + + ms = allocate_array_elem(&mux->of.streams, sizeof(*ms), &mux->of.nb_streams); + if (!ms) + return NULL; - if (ost->filters_script) - return file_read(ost->filters_script); - else if (ost->filters) - return av_strdup(ost->filters); + ms->ost.file_index = mux->of.index; + ms->ost.index = mux->of.nb_streams - 1; + ms->ost.type = type; - return av_strdup(st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ? - "null" : "anull"); + ms->ost.class = &output_stream_class; + + snprintf(ms->log_name, sizeof(ms->log_name), "%cost#%d:%d", + type_str ? *type_str : '?', mux->of.index, ms->ost.index); + + return ms; } -static void check_streamcopy_filters(const OptionsContext *o, AVFormatContext *oc, - const OutputStream *ost, enum AVMediaType type) +static int ost_get_filters(const OptionsContext *o, AVFormatContext *oc, + OutputStream *ost, char **dst) { - if (ost->filters_script || ost->filters) { - av_log(NULL, AV_LOG_ERROR, - "%s '%s' was defined for %s output stream %d:%d but codec copy was selected.\n" - "Filtering and streamcopy cannot be used together.\n", - ost->filters ? "Filtergraph" : "Filtergraph script", - ost->filters ? ost->filters : ost->filters_script, - av_get_media_type_string(type), ost->file_index, ost->index); - exit_program(1); + const char *filters = NULL, *filters_script = NULL; + + MATCH_PER_STREAM_OPT(filter_scripts, str, filters_script, oc, ost->st); + MATCH_PER_STREAM_OPT(filters, str, filters, oc, ost->st); + + if (!ost->enc) { + if (filters_script || filters) { + av_log(ost, AV_LOG_ERROR, + "%s '%s' was specified, but codec copy was selected. " + "Filtering and streamcopy cannot be used together.\n", + filters ? "Filtergraph" : "Filtergraph script", + filters ? filters : filters_script); + return AVERROR(ENOSYS); + } + return 0; + } + + if (!ost->ist) { + if (filters_script || filters) { + av_log(ost, AV_LOG_ERROR, + "%s '%s' was specified for a stream fed from a complex " + "filtergraph. Simple and complex filtering cannot be used " + "together for the same stream.\n", + filters ? "Filtergraph" : "Filtergraph script", + filters ? filters : filters_script); + return AVERROR(EINVAL); + } + return 0; + } + + if (filters_script && filters) { + av_log(ost, AV_LOG_ERROR, "Both -filter and -filter_script set\n"); + return AVERROR(EINVAL); } + + if (filters_script) + *dst = file_read(filters_script); + else if (filters) + *dst = av_strdup(filters); + else + *dst = av_strdup(ost->type == AVMEDIA_TYPE_VIDEO ? "null" : "anull"); + return *dst ? 0 : AVERROR(ENOMEM); } -static void parse_matrix_coeffs(uint16_t *dest, const char *str) +static int parse_matrix_coeffs(void *logctx, uint16_t *dest, const char *str) { int i; const char *p = str; @@ -409,38 +499,118 @@ static void parse_matrix_coeffs(uint16_t *dest, const char *str) break; p = strchr(p, ','); if (!p) { - av_log(NULL, AV_LOG_FATAL, "Syntax error in matrix \"%s\" at coeff %d\n", str, i); - exit_program(1); + av_log(logctx, AV_LOG_FATAL, + "Syntax error in matrix \"%s\" at coeff %d\n", str, i); + return AVERROR(EINVAL); } p++; } + + return 0; +} + +static int fmt_in_list(const int *formats, int format) +{ + for (; *formats != -1; formats++) + if (*formats == format) + return 1; + return 0; +} + +static enum AVPixelFormat +choose_pixel_fmt(const AVCodec *codec, enum AVPixelFormat target) +{ + const enum AVPixelFormat *p = codec->pix_fmts; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(target); + //FIXME: This should check for AV_PIX_FMT_FLAG_ALPHA after PAL8 pixel format without alpha is implemented + int has_alpha = desc ? desc->nb_components % 2 == 0 : 0; + enum AVPixelFormat best= AV_PIX_FMT_NONE; + + for (; *p != AV_PIX_FMT_NONE; p++) { + best = av_find_best_pix_fmt_of_2(best, *p, target, has_alpha, NULL); + if (*p == target) + break; + } + if (*p == AV_PIX_FMT_NONE) { + if (target != AV_PIX_FMT_NONE) + av_log(NULL, AV_LOG_WARNING, + "Incompatible pixel format '%s' for codec '%s', auto-selecting format '%s'\n", + av_get_pix_fmt_name(target), + codec->name, + av_get_pix_fmt_name(best)); + return best; + } + return target; +} + +static enum AVPixelFormat pix_fmt_parse(OutputStream *ost, const char *name) +{ + const enum AVPixelFormat *fmts = ost->enc_ctx->codec->pix_fmts; + enum AVPixelFormat fmt; + + fmt = av_get_pix_fmt(name); + if (fmt == AV_PIX_FMT_NONE) { + av_log(ost, AV_LOG_FATAL, "Unknown pixel format requested: %s.\n", name); + return AV_PIX_FMT_NONE; + } + + /* when the user specified-format is an alias for an endianness-specific + * one (e.g. rgb48 -> rgb48be/le), it gets translated into the native + * endianness by av_get_pix_fmt(); + * the following code handles the case when the native endianness is not + * supported by the encoder, but the other one is */ + if (fmts && !fmt_in_list(fmts, fmt)) { + const char *name_canonical = av_get_pix_fmt_name(fmt); + int len = strlen(name_canonical); + + if (strcmp(name, name_canonical) && + (!strcmp(name_canonical + len - 2, "le") || + !strcmp(name_canonical + len - 2, "be"))) { + char name_other[64]; + enum AVPixelFormat fmt_other; + + snprintf(name_other, sizeof(name_other), "%s%ce", + name, name_canonical[len - 2] == 'l' ? 'b' : 'l'); + fmt_other = av_get_pix_fmt(name_other); + if (fmt_other != AV_PIX_FMT_NONE && fmt_in_list(fmts, fmt_other)) { + av_log(ost, AV_LOG_VERBOSE, "Mapping pixel format %s->%s\n", + name, name_other); + fmt = fmt_other; + } + } + } + + if (fmts && !fmt_in_list(fmts, fmt)) + fmt = choose_pixel_fmt(ost->enc_ctx->codec, fmt); + + return fmt; } -static OutputStream *new_video_stream(Muxer *mux, const OptionsContext *o, InputStream *ist) +static int new_stream_video(Muxer *mux, const OptionsContext *o, + OutputStream *ost) { AVFormatContext *oc = mux->fc; AVStream *st; - OutputStream *ost; char *frame_rate = NULL, *max_frame_rate = NULL, *frame_aspect_ratio = NULL; + int ret = 0; - ost = new_output_stream(mux, o, AVMEDIA_TYPE_VIDEO, ist); st = ost->st; MATCH_PER_STREAM_OPT(frame_rates, str, frame_rate, oc, st); if (frame_rate && av_parse_video_rate(&ost->frame_rate, frame_rate) < 0) { - av_log(NULL, AV_LOG_FATAL, "Invalid framerate value: %s\n", frame_rate); - exit_program(1); + av_log(ost, AV_LOG_FATAL, "Invalid framerate value: %s\n", frame_rate); + return AVERROR(EINVAL); } MATCH_PER_STREAM_OPT(max_frame_rates, str, max_frame_rate, oc, st); if (max_frame_rate && av_parse_video_rate(&ost->max_frame_rate, max_frame_rate) < 0) { - av_log(NULL, AV_LOG_FATAL, "Invalid maximum framerate value: %s\n", max_frame_rate); - exit_program(1); + av_log(ost, AV_LOG_FATAL, "Invalid maximum framerate value: %s\n", max_frame_rate); + return AVERROR(EINVAL); } if (frame_rate && max_frame_rate) { - av_log(NULL, AV_LOG_ERROR, "Only one of -fpsmax and -r can be set for a stream.\n"); - exit_program(1); + av_log(ost, AV_LOG_ERROR, "Only one of -fpsmax and -r can be set for a stream.\n"); + return AVERROR(EINVAL); } MATCH_PER_STREAM_OPT(frame_aspect_ratios, str, frame_aspect_ratio, oc, st); @@ -448,15 +618,12 @@ static OutputStream *new_video_stream(Muxer *mux, const OptionsContext *o, Input AVRational q; if (av_parse_ratio(&q, frame_aspect_ratio, 255, 0, NULL) < 0 || q.num <= 0 || q.den <= 0) { - av_log(NULL, AV_LOG_FATAL, "Invalid aspect ratio: %s\n", frame_aspect_ratio); - exit_program(1); + av_log(ost, AV_LOG_FATAL, "Invalid aspect ratio: %s\n", frame_aspect_ratio); + return AVERROR(EINVAL); } ost->frame_aspect_ratio = q; } - MATCH_PER_STREAM_OPT(filter_scripts, str, ost->filters_script, oc, st); - MATCH_PER_STREAM_OPT(filters, str, ost->filters, oc, st); - if (ost->enc_ctx) { AVCodecContext *video_enc = ost->enc_ctx; const char *p = NULL, *fps_mode = NULL; @@ -468,9 +635,12 @@ static OutputStream *new_video_stream(Muxer *mux, const OptionsContext *o, Input int i; MATCH_PER_STREAM_OPT(frame_sizes, str, frame_size, oc, st); - if (frame_size && av_parse_video_size(&video_enc->width, &video_enc->height, frame_size) < 0) { - av_log(NULL, AV_LOG_FATAL, "Invalid frame size: %s.\n", frame_size); - exit_program(1); + if (frame_size) { + ret = av_parse_video_size(&video_enc->width, &video_enc->height, frame_size); + if (ret < 0) { + av_log(ost, AV_LOG_FATAL, "Invalid frame size: %s.\n", frame_size); + return AVERROR(EINVAL); + } } MATCH_PER_STREAM_OPT(frame_pix_fmts, str, frame_pix_fmt, oc, st); @@ -479,31 +649,38 @@ static OutputStream *new_video_stream(Muxer *mux, const OptionsContext *o, Input if (!*++frame_pix_fmt) frame_pix_fmt = NULL; } - if (frame_pix_fmt && (video_enc->pix_fmt = av_get_pix_fmt(frame_pix_fmt)) == AV_PIX_FMT_NONE) { - av_log(NULL, AV_LOG_FATAL, "Unknown pixel format requested: %s.\n", frame_pix_fmt); - exit_program(1); + if (frame_pix_fmt) { + video_enc->pix_fmt = pix_fmt_parse(ost, frame_pix_fmt); + if (video_enc->pix_fmt == AV_PIX_FMT_NONE) + return AVERROR(EINVAL); } - st->sample_aspect_ratio = video_enc->sample_aspect_ratio; MATCH_PER_STREAM_OPT(intra_matrices, str, intra_matrix, oc, st); if (intra_matrix) { if (!(video_enc->intra_matrix = av_mallocz(sizeof(*video_enc->intra_matrix) * 64))) - report_and_exit(AVERROR(ENOMEM)); - parse_matrix_coeffs(video_enc->intra_matrix, intra_matrix); + return AVERROR(ENOMEM); + + ret = parse_matrix_coeffs(ost, video_enc->intra_matrix, intra_matrix); + if (ret < 0) + return ret; } MATCH_PER_STREAM_OPT(chroma_intra_matrices, str, chroma_intra_matrix, oc, st); if (chroma_intra_matrix) { uint16_t *p = av_mallocz(sizeof(*video_enc->chroma_intra_matrix) * 64); if (!p) - report_and_exit(AVERROR(ENOMEM)); + return AVERROR(ENOMEM); video_enc->chroma_intra_matrix = p; - parse_matrix_coeffs(p, chroma_intra_matrix); + ret = parse_matrix_coeffs(ost, p, chroma_intra_matrix); + if (ret < 0) + return ret; } MATCH_PER_STREAM_OPT(inter_matrices, str, inter_matrix, oc, st); if (inter_matrix) { if (!(video_enc->inter_matrix = av_mallocz(sizeof(*video_enc->inter_matrix) * 64))) - report_and_exit(AVERROR(ENOMEM)); - parse_matrix_coeffs(video_enc->inter_matrix, inter_matrix); + return AVERROR(ENOMEM); + ret = parse_matrix_coeffs(ost, video_enc->inter_matrix, inter_matrix); + if (ret < 0) + return ret; } MATCH_PER_STREAM_OPT(rc_overrides, str, p, oc, st); @@ -511,15 +688,15 @@ static OutputStream *new_video_stream(Muxer *mux, const OptionsContext *o, Input int start, end, q; int e = sscanf(p, "%d,%d,%d", &start, &end, &q); if (e != 3) { - av_log(NULL, AV_LOG_FATAL, "error parsing rc_override\n"); - exit_program(1); + av_log(ost, AV_LOG_FATAL, "error parsing rc_override\n"); + return AVERROR(EINVAL); } video_enc->rc_override = av_realloc_array(video_enc->rc_override, i + 1, sizeof(RcOverride)); if (!video_enc->rc_override) { - av_log(NULL, AV_LOG_FATAL, "Could not (re)allocate memory for rc_override.\n"); - exit_program(1); + av_log(ost, AV_LOG_FATAL, "Could not (re)allocate memory for rc_override.\n"); + return AVERROR(ENOMEM); } video_enc->rc_override[i].start_frame = start; video_enc->rc_override[i].end_frame = end; @@ -538,7 +715,7 @@ static OutputStream *new_video_stream(Muxer *mux, const OptionsContext *o, Input #if FFMPEG_OPT_PSNR if (do_psnr) { - av_log(NULL, AV_LOG_WARNING, "The -psnr option is deprecated, use -flags +psnr\n"); + av_log(ost, AV_LOG_WARNING, "The -psnr option is deprecated, use -flags +psnr\n"); video_enc->flags|= AV_CODEC_FLAG_PSNR; } #endif @@ -559,7 +736,7 @@ static OutputStream *new_video_stream(Muxer *mux, const OptionsContext *o, Input MATCH_PER_STREAM_OPT(passlogfiles, str, ost->logfile_prefix, oc, st); if (ost->logfile_prefix && !(ost->logfile_prefix = av_strdup(ost->logfile_prefix))) - report_and_exit(AVERROR(ENOMEM)); + return AVERROR(ENOMEM); if (do_pass) { int ost_idx = -1; @@ -581,19 +758,19 @@ static OutputStream *new_video_stream(Muxer *mux, const OptionsContext *o, Input char *logbuffer = file_read(logfilename); if (!logbuffer) { - av_log(NULL, AV_LOG_FATAL, "Error reading log file '%s' for pass-2 encoding\n", + av_log(ost, AV_LOG_FATAL, "Error reading log file '%s' for pass-2 encoding\n", logfilename); - exit_program(1); + return AVERROR(EIO); } video_enc->stats_in = logbuffer; } if (video_enc->flags & AV_CODEC_FLAG_PASS1) { f = fopen_utf8(logfilename, "wb"); if (!f) { - av_log(NULL, AV_LOG_FATAL, + av_log(ost, AV_LOG_FATAL, "Cannot write log file '%s' for pass-1 encoding: %s\n", logfilename, strerror(errno)); - exit_program(1); + return AVERROR(errno); } ost->logfile = f; } @@ -607,15 +784,18 @@ static OutputStream *new_video_stream(Muxer *mux, const OptionsContext *o, Input ost->vsync_method = video_sync_method; MATCH_PER_STREAM_OPT(fps_mode, str, fps_mode, oc, st); - if (fps_mode) - parse_and_set_vsync(fps_mode, &ost->vsync_method, ost->file_index, ost->index, 0); + if (fps_mode) { + ret = parse_and_set_vsync(fps_mode, &ost->vsync_method, ost->file_index, ost->index, 0); + if (ret < 0) + return ret; + } if ((ost->frame_rate.num || ost->max_frame_rate.num) && !(ost->vsync_method == VSYNC_AUTO || ost->vsync_method == VSYNC_CFR || ost->vsync_method == VSYNC_VSCFR)) { - av_log(NULL, AV_LOG_FATAL, "One of -r/-fpsmax was specified " + av_log(ost, AV_LOG_FATAL, "One of -r/-fpsmax was specified " "together a non-CFR -vsync/-fps_mode. This is contradictory.\n"); - exit_program(1); + return AVERROR(EINVAL); } if (ost->vsync_method == VSYNC_AUTO) { @@ -642,33 +822,20 @@ static OutputStream *new_video_stream(Muxer *mux, const OptionsContext *o, Input } } ost->is_cfr = (ost->vsync_method == VSYNC_CFR || ost->vsync_method == VSYNC_VSCFR); + } - ost->avfilter = get_ost_filters(o, oc, ost); - if (!ost->avfilter) - exit_program(1); - - ost->last_frame = av_frame_alloc(); - if (!ost->last_frame) - report_and_exit(AVERROR(ENOMEM)); - } else - check_streamcopy_filters(o, oc, ost, AVMEDIA_TYPE_VIDEO); - - return ost; + return 0; } -static OutputStream *new_audio_stream(Muxer *mux, const OptionsContext *o, InputStream *ist) +static int new_stream_audio(Muxer *mux, const OptionsContext *o, + OutputStream *ost) { AVFormatContext *oc = mux->fc; AVStream *st; - OutputStream *ost; + int ret = 0; - ost = new_output_stream(mux, o, AVMEDIA_TYPE_AUDIO, ist); st = ost->st; - - MATCH_PER_STREAM_OPT(filter_scripts, str, ost->filters_script, oc, st); - MATCH_PER_STREAM_OPT(filters, str, ost->filters, oc, st); - if (ost->enc_ctx) { AVCodecContext *audio_enc = ost->enc_ctx; int channels = 0; @@ -691,11 +858,11 @@ static OutputStream *new_audio_stream(Muxer *mux, const OptionsContext *o, Input }) if (!mask) { #endif - av_log(NULL, AV_LOG_FATAL, "Unknown channel layout: %s\n", layout); - exit_program(1); + av_log(ost, AV_LOG_FATAL, "Unknown channel layout: %s\n", layout); + return AVERROR(EINVAL); #if FF_API_OLD_CHANNEL_LAYOUT } - av_log(NULL, AV_LOG_WARNING, "Channel layout '%s' uses a deprecated syntax.\n", + av_log(ost, AV_LOG_WARNING, "Channel layout '%s' uses a deprecated syntax.\n", layout); av_channel_layout_from_mask(&audio_enc->ch_layout, mask); #endif @@ -705,8 +872,8 @@ static OutputStream *new_audio_stream(Muxer *mux, const OptionsContext *o, Input MATCH_PER_STREAM_OPT(sample_fmts, str, sample_fmt, oc, st); if (sample_fmt && (audio_enc->sample_fmt = av_get_sample_fmt(sample_fmt)) == AV_SAMPLE_FMT_NONE) { - av_log(NULL, AV_LOG_FATAL, "Invalid sample format '%s'\n", sample_fmt); - exit_program(1); + av_log(ost, AV_LOG_FATAL, "Invalid sample format '%s'\n", sample_fmt); + return AVERROR(EINVAL); } MATCH_PER_STREAM_OPT(audio_sample_rate, i, audio_enc->sample_rate, oc, st); @@ -714,10 +881,6 @@ static OutputStream *new_audio_stream(Muxer *mux, const OptionsContext *o, Input MATCH_PER_STREAM_OPT(apad, str, ost->apad, oc, st); ost->apad = av_strdup(ost->apad); - ost->avfilter = get_ost_filters(o, oc, ost); - if (!ost->avfilter) - exit_program(1); - #if FFMPEG_OPT_MAP_CHANNEL /* check for channel mapping for this audio stream */ for (int n = 0; n < o->nb_audio_channel_maps; n++) { @@ -729,128 +892,572 @@ static OutputStream *new_audio_stream(Muxer *mux, const OptionsContext *o, Input if (map->channel_idx == -1) { ist = NULL; } else if (!ost->ist) { - av_log(NULL, AV_LOG_FATAL, "Cannot determine input stream for channel mapping %d.%d\n", + av_log(ost, AV_LOG_FATAL, "Cannot determine input stream for channel mapping %d.%d\n", ost->file_index, ost->st->index); continue; } else { ist = ost->ist; } - if (!ist || (ist->file_index == map->file_idx && ist->st->index == map->stream_idx)) { - if (av_reallocp_array(&ost->audio_channels_map, - ost->audio_channels_mapped + 1, - sizeof(*ost->audio_channels_map) - ) < 0 ) - report_and_exit(AVERROR(ENOMEM)); + if (!ist || (ist->file_index == map->file_idx && ist->index == map->stream_idx)) { + ret = av_reallocp_array(&ost->audio_channels_map, + ost->audio_channels_mapped + 1, + sizeof(*ost->audio_channels_map)); + if (ret < 0) + return ret; ost->audio_channels_map[ost->audio_channels_mapped++] = map->channel_idx; } } } #endif - } else - check_streamcopy_filters(o, oc, ost, AVMEDIA_TYPE_AUDIO); + } - return ost; + return 0; } -static OutputStream *new_data_stream(Muxer *mux, const OptionsContext *o, InputStream *ist) +static int new_stream_attachment(Muxer *mux, const OptionsContext *o, + OutputStream *ost) { - OutputStream *ost; + ost->finished = 1; + return 0; +} + +static int new_stream_subtitle(Muxer *mux, const OptionsContext *o, + OutputStream *ost) +{ + AVStream *st; + + st = ost->st; - ost = new_output_stream(mux, o, AVMEDIA_TYPE_DATA, ist); if (ost->enc_ctx) { - av_log(NULL, AV_LOG_FATAL, "Data stream encoding not supported yet (only streamcopy)\n"); - exit_program(1); + AVCodecContext *subtitle_enc = ost->enc_ctx; + + AVCodecDescriptor const *input_descriptor = + avcodec_descriptor_get(ost->ist->par->codec_id); + AVCodecDescriptor const *output_descriptor = + avcodec_descriptor_get(subtitle_enc->codec_id); + int input_props = 0, output_props = 0; + + char *frame_size = NULL; + + MATCH_PER_STREAM_OPT(frame_sizes, str, frame_size, mux->fc, st); + if (frame_size) { + int ret = av_parse_video_size(&subtitle_enc->width, &subtitle_enc->height, frame_size); + if (ret < 0) { + av_log(ost, AV_LOG_FATAL, "Invalid frame size: %s.\n", frame_size); + return ret; + } + } + if (input_descriptor) + input_props = input_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB); + if (output_descriptor) + output_props = output_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB); + if (input_props && output_props && input_props != output_props) { + av_log(ost, AV_LOG_ERROR, + "Subtitle encoding currently only possible from text to text " + "or bitmap to bitmap\n"); + return AVERROR(EINVAL); + } } - return ost; + return 0; } -static OutputStream *new_unknown_stream(Muxer *mux, const OptionsContext *o, InputStream *ist) +static int streamcopy_init(const Muxer *mux, OutputStream *ost) { + MuxStream *ms = ms_from_ost(ost); + + const InputStream *ist = ost->ist; + const InputFile *ifile = input_files[ist->file_index]; + + AVCodecParameters *par = ost->par_in; + uint32_t codec_tag = par->codec_tag; + + AVCodecContext *codec_ctx = NULL; + AVDictionary *codec_opts = NULL; + + AVRational fr = ost->frame_rate; + + int ret = 0; + + codec_ctx = avcodec_alloc_context3(NULL); + if (!codec_ctx) + return AVERROR(ENOMEM); + + ret = avcodec_parameters_to_context(codec_ctx, ist->par); + if (ret >= 0) + ret = av_opt_set_dict(codec_ctx, &ost->encoder_opts); + if (ret < 0) { + av_log(ost, AV_LOG_FATAL, + "Error setting up codec context options.\n"); + goto fail; + } + + ret = avcodec_parameters_from_context(par, codec_ctx); + if (ret < 0) { + av_log(ost, AV_LOG_FATAL, + "Error getting reference codec parameters.\n"); + goto fail; + } + + if (!codec_tag) { + const struct AVCodecTag * const *ct = mux->fc->oformat->codec_tag; + unsigned int codec_tag_tmp; + if (!ct || av_codec_get_id (ct, par->codec_tag) == par->codec_id || + !av_codec_get_tag2(ct, par->codec_id, &codec_tag_tmp)) + codec_tag = par->codec_tag; + } + + par->codec_tag = codec_tag; + + if (!fr.num) + fr = ist->framerate; + + if (fr.num) + ost->st->avg_frame_rate = fr; + else + ost->st->avg_frame_rate = ist->st->avg_frame_rate; + + ret = avformat_transfer_internal_stream_timing_info(mux->fc->oformat, + ost->st, ist->st, copy_tb); + if (ret < 0) + goto fail; + + // copy timebase while removing common factors + if (ost->st->time_base.num <= 0 || ost->st->time_base.den <= 0) { + if (fr.num) + ost->st->time_base = av_inv_q(fr); + else + ost->st->time_base = av_add_q(av_stream_get_codec_timebase(ost->st), (AVRational){0, 1}); + } + + if (!ms->copy_prior_start) { + ms->ts_copy_start = (mux->of.start_time == AV_NOPTS_VALUE) ? + 0 : mux->of.start_time; + if (copy_ts && ifile->start_time != AV_NOPTS_VALUE) { + ms->ts_copy_start = FFMAX(ms->ts_copy_start, + ifile->start_time + ifile->ts_offset); + } + } + + for (int i = 0; i < ist->st->nb_side_data; i++) { + const AVPacketSideData *sd_src = &ist->st->side_data[i]; + uint8_t *dst_data; + + dst_data = av_stream_new_side_data(ost->st, sd_src->type, sd_src->size); + if (!dst_data) { + ret = AVERROR(ENOMEM); + goto fail; + } + memcpy(dst_data, sd_src->data, sd_src->size); + } + +#if FFMPEG_ROTATION_METADATA + if (ost->rotate_overridden) { + uint8_t *sd = av_stream_new_side_data(ost->st, AV_PKT_DATA_DISPLAYMATRIX, + sizeof(int32_t) * 9); + if (sd) + av_display_rotation_set((int32_t *)sd, -ost->rotate_override_value); + } +#endif + + switch (par->codec_type) { + case AVMEDIA_TYPE_AUDIO: + if ((par->block_align == 1 || par->block_align == 1152 || par->block_align == 576) && + par->codec_id == AV_CODEC_ID_MP3) + par->block_align = 0; + if (par->codec_id == AV_CODEC_ID_AC3) + par->block_align = 0; + break; + case AVMEDIA_TYPE_VIDEO: { + AVRational sar; + if (ost->frame_aspect_ratio.num) { // overridden by the -aspect cli option + sar = + av_mul_q(ost->frame_aspect_ratio, + (AVRational){ par->height, par->width }); + av_log(ost, AV_LOG_WARNING, "Overriding aspect ratio " + "with stream copy may produce invalid files\n"); + } + else if (ist->st->sample_aspect_ratio.num) + sar = ist->st->sample_aspect_ratio; + else + sar = par->sample_aspect_ratio; + ost->st->sample_aspect_ratio = par->sample_aspect_ratio = sar; + ost->st->avg_frame_rate = ist->st->avg_frame_rate; + ost->st->r_frame_rate = ist->st->r_frame_rate; + break; + } + } + + ost->mux_timebase = ist->st->time_base; + +fail: + avcodec_free_context(&codec_ctx); + av_dict_free(&codec_opts); + return ret; +} + +static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, + InputStream *ist, OutputFilter *ofilter, + OutputStream **post) +{ + AVFormatContext *oc = mux->fc; + MuxStream *ms; OutputStream *ost; + const AVCodec *enc; + AVStream *st; + int ret = 0; + const char *bsfs = NULL, *time_base = NULL; + char *filters = NULL, *next, *codec_tag = NULL; + double qscale = -1; + int i; + + st = avformat_new_stream(oc, NULL); + if (!st) + return AVERROR(ENOMEM); + + ms = mux_stream_alloc(mux, type); + if (!ms) + return AVERROR(ENOMEM); + + ost = &ms->ost; + + if (o->streamid) { + AVDictionaryEntry *e; + char idx[16], *p; + snprintf(idx, sizeof(idx), "%d", ost->index); + + e = av_dict_get(o->streamid, idx, NULL, 0); + if (e) { + st->id = strtol(e->value, &p, 0); + if (!e->value[0] || *p) { + av_log(ost, AV_LOG_FATAL, "Invalid stream id: %s\n", e->value); + return AVERROR(EINVAL); + } + } + } + + ost->par_in = avcodec_parameters_alloc(); + if (!ost->par_in) + return AVERROR(ENOMEM); + + ms->muxing_queue = av_fifo_alloc2(8, sizeof(AVPacket*), 0); + if (!ms->muxing_queue) + return AVERROR(ENOMEM); + ms->last_mux_dts = AV_NOPTS_VALUE; + + ost->st = st; + ost->ist = ist; + ost->kf.ref_pts = AV_NOPTS_VALUE; + ost->par_in->codec_type = type; + st->codecpar->codec_type = type; + + ret = choose_encoder(o, oc, ost, &enc); + if (ret < 0) { + av_log(ost, AV_LOG_FATAL, "Error selecting an encoder\n"); + return ret; + } + + if (enc) { + ost->enc_ctx = avcodec_alloc_context3(enc); + if (!ost->enc_ctx) + return AVERROR(ENOMEM); + + ret = enc_alloc(&ost->enc, enc); + if (ret < 0) + return ret; + + av_strlcat(ms->log_name, "/", sizeof(ms->log_name)); + av_strlcat(ms->log_name, enc->name, sizeof(ms->log_name)); + } else { + if (ofilter) { + av_log(ost, AV_LOG_ERROR, + "Streamcopy requested for output stream fed " + "from a complex filtergraph. Filtering and streamcopy " + "cannot be used together.\n"); + return AVERROR(EINVAL); + } + + av_strlcat(ms->log_name, "/copy", sizeof(ms->log_name)); + } + + av_log(ost, AV_LOG_VERBOSE, "Created %s stream from ", + av_get_media_type_string(type)); + if (ist) + av_log(ost, AV_LOG_VERBOSE, "input stream %d:%d", + ist->file_index, ist->index); + else if (ofilter) + av_log(ost, AV_LOG_VERBOSE, "complex filtergraph %d:[%s]\n", + ofilter->graph->index, ofilter->name); + else if (type == AVMEDIA_TYPE_ATTACHMENT) + av_log(ost, AV_LOG_VERBOSE, "attached file"); + else av_assert0(0); + av_log(ost, AV_LOG_VERBOSE, "\n"); + + ms->pkt = av_packet_alloc(); + if (!ms->pkt) + return AVERROR(ENOMEM); - ost = new_output_stream(mux, o, AVMEDIA_TYPE_UNKNOWN, ist); if (ost->enc_ctx) { - av_log(NULL, AV_LOG_FATAL, "Unknown stream encoding not supported yet (only streamcopy)\n"); - exit_program(1); + AVCodecContext *enc = ost->enc_ctx; + AVIOContext *s = NULL; + char *buf = NULL, *arg = NULL, *preset = NULL; + const char *enc_stats_pre = NULL, *enc_stats_post = NULL, *mux_stats = NULL; + const char *enc_time_base = NULL; + + ret = filter_codec_opts(o->g->codec_opts, enc->codec_id, + oc, st, enc->codec, &ost->encoder_opts); + if (ret < 0) + return ret; + + MATCH_PER_STREAM_OPT(presets, str, preset, oc, st); + ost->autoscale = 1; + MATCH_PER_STREAM_OPT(autoscale, i, ost->autoscale, oc, st); + if (preset && (!(ret = get_preset_file_2(preset, enc->codec->name, &s)))) { + AVBPrint bprint; + av_bprint_init(&bprint, 0, AV_BPRINT_SIZE_UNLIMITED); + do { + av_bprint_clear(&bprint); + buf = get_line(s, &bprint); + if (!buf) { + ret = AVERROR(ENOMEM); + break; + } + + if (!buf[0] || buf[0] == '#') + continue; + if (!(arg = strchr(buf, '='))) { + av_log(ost, AV_LOG_FATAL, "Invalid line found in the preset file.\n"); + ret = AVERROR(EINVAL); + break; + } + *arg++ = 0; + av_dict_set(&ost->encoder_opts, buf, arg, AV_DICT_DONT_OVERWRITE); + } while (!s->eof_reached); + av_bprint_finalize(&bprint, NULL); + avio_closep(&s); + } + if (ret) { + av_log(ost, AV_LOG_FATAL, + "Preset %s specified, but could not be opened.\n", preset); + return ret; + } + + MATCH_PER_STREAM_OPT(enc_stats_pre, str, enc_stats_pre, oc, st); + if (enc_stats_pre && + (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO)) { + const char *format = "{fidx} {sidx} {n} {t}"; + + MATCH_PER_STREAM_OPT(enc_stats_pre_fmt, str, format, oc, st); + + ret = enc_stats_init(ost, &ost->enc_stats_pre, 1, enc_stats_pre, format); + if (ret < 0) + return ret; + } + + MATCH_PER_STREAM_OPT(enc_stats_post, str, enc_stats_post, oc, st); + if (enc_stats_post && + (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO)) { + const char *format = "{fidx} {sidx} {n} {t}"; + + MATCH_PER_STREAM_OPT(enc_stats_post_fmt, str, format, oc, st); + + ret = enc_stats_init(ost, &ost->enc_stats_post, 0, enc_stats_post, format); + if (ret < 0) + return ret; + } + + MATCH_PER_STREAM_OPT(mux_stats, str, mux_stats, oc, st); + if (mux_stats && + (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO)) { + const char *format = "{fidx} {sidx} {n} {t}"; + + MATCH_PER_STREAM_OPT(mux_stats_fmt, str, format, oc, st); + + ret = enc_stats_init(ost, &ms->stats, 0, mux_stats, format); + if (ret < 0) + return ret; + } + + MATCH_PER_STREAM_OPT(enc_time_bases, str, enc_time_base, oc, st); + if (enc_time_base) { + AVRational q; + if (!strcmp(enc_time_base, "demux")) { + q = (AVRational){ ENC_TIME_BASE_DEMUX, 0 }; + } else if (!strcmp(enc_time_base, "filter")) { + q = (AVRational){ ENC_TIME_BASE_FILTER, 0 }; + } else { + ret = av_parse_ratio(&q, enc_time_base, INT_MAX, 0, NULL); + if (ret < 0 || q.den <= 0 +#if !FFMPEG_OPT_ENC_TIME_BASE_NUM + || q.num < 0 +#endif + ) { + av_log(ost, AV_LOG_FATAL, "Invalid time base: %s\n", enc_time_base); + return ret < 0 ? ret : AVERROR(EINVAL); + } +#if FFMPEG_OPT_ENC_TIME_BASE_NUM + if (q.num < 0) + av_log(ost, AV_LOG_WARNING, "-enc_time_base -1 is deprecated," + " use -enc_timebase demux\n"); +#endif + } + + ost->enc_timebase = q; + } + } else { + ret = filter_codec_opts(o->g->codec_opts, AV_CODEC_ID_NONE, oc, st, + NULL, &ost->encoder_opts); + if (ret < 0) + return ret; + } + + + if (o->bitexact) { + ost->bitexact = 1; + } else if (ost->enc_ctx) { + ost->bitexact = check_opt_bitexact(ost->enc_ctx, ost->encoder_opts, "flags", + AV_CODEC_FLAG_BITEXACT); + } + + MATCH_PER_STREAM_OPT(time_bases, str, time_base, oc, st); + if (time_base) { + AVRational q; + if (av_parse_ratio(&q, time_base, INT_MAX, 0, NULL) < 0 || + q.num <= 0 || q.den <= 0) { + av_log(ost, AV_LOG_FATAL, "Invalid time base: %s\n", time_base); + return AVERROR(EINVAL); + } + st->time_base = q; + } + + ms->max_frames = INT64_MAX; + MATCH_PER_STREAM_OPT(max_frames, i64, ms->max_frames, oc, st); + for (i = 0; inb_max_frames; i++) { + char *p = o->max_frames[i].specifier; + if (!*p && type != AVMEDIA_TYPE_VIDEO) { + av_log(ost, AV_LOG_WARNING, "Applying unspecific -frames to non video streams, maybe you meant -vframes ?\n"); + break; + } + } + + ms->copy_prior_start = -1; + MATCH_PER_STREAM_OPT(copy_prior_start, i, ms->copy_prior_start, oc ,st); + + MATCH_PER_STREAM_OPT(bitstream_filters, str, bsfs, oc, st); + if (bsfs && *bsfs) { + ret = av_bsf_list_parse_str(bsfs, &ms->bsf_ctx); + if (ret < 0) { + av_log(ost, AV_LOG_ERROR, "Error parsing bitstream filter sequence '%s': %s\n", bsfs, av_err2str(ret)); + return ret; + } + } + + MATCH_PER_STREAM_OPT(codec_tags, str, codec_tag, oc, st); + if (codec_tag) { + uint32_t tag = strtol(codec_tag, &next, 0); + if (*next) { + uint8_t buf[4] = { 0 }; + memcpy(buf, codec_tag, FFMIN(sizeof(buf), strlen(codec_tag))); + tag = AV_RL32(buf); + } + ost->st->codecpar->codec_tag = tag; + ost->par_in->codec_tag = tag; + if (ost->enc_ctx) + ost->enc_ctx->codec_tag = tag; } - return ost; -} + MATCH_PER_STREAM_OPT(qscale, dbl, qscale, oc, st); + if (ost->enc_ctx && qscale >= 0) { + ost->enc_ctx->flags |= AV_CODEC_FLAG_QSCALE; + ost->enc_ctx->global_quality = FF_QP2LAMBDA * qscale; + } + + ms->max_muxing_queue_size = 128; + MATCH_PER_STREAM_OPT(max_muxing_queue_size, i, ms->max_muxing_queue_size, oc, st); + + ms->muxing_queue_data_threshold = 50*1024*1024; + MATCH_PER_STREAM_OPT(muxing_queue_data_threshold, i, ms->muxing_queue_data_threshold, oc, st); -static OutputStream *new_attachment_stream(Muxer *mux, const OptionsContext *o, InputStream *ist) -{ - OutputStream *ost = new_output_stream(mux, o, AVMEDIA_TYPE_ATTACHMENT, ist); - ost->finished = 1; - return ost; -} + MATCH_PER_STREAM_OPT(bits_per_raw_sample, i, ost->bits_per_raw_sample, + oc, st); -static OutputStream *new_subtitle_stream(Muxer *mux, const OptionsContext *o, InputStream *ist) -{ - AVStream *st; - OutputStream *ost; + MATCH_PER_STREAM_OPT(fix_sub_duration_heartbeat, i, ost->fix_sub_duration_heartbeat, + oc, st); - ost = new_output_stream(mux, o, AVMEDIA_TYPE_SUBTITLE, ist); - st = ost->st; + if (oc->oformat->flags & AVFMT_GLOBALHEADER && ost->enc_ctx) + ost->enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; - if (ost->enc_ctx) { - AVCodecContext *subtitle_enc = ost->enc_ctx; - char *frame_size = NULL; + av_dict_copy(&ost->sws_dict, o->g->sws_dict, 0); - MATCH_PER_STREAM_OPT(frame_sizes, str, frame_size, mux->fc, st); - if (frame_size && av_parse_video_size(&subtitle_enc->width, &subtitle_enc->height, frame_size) < 0) { - av_log(NULL, AV_LOG_FATAL, "Invalid frame size: %s.\n", frame_size); - exit_program(1); - } - } + av_dict_copy(&ost->swr_opts, o->g->swr_opts, 0); + if (ost->enc_ctx && av_get_exact_bits_per_sample(ost->enc_ctx->codec_id) == 24) + av_dict_set(&ost->swr_opts, "output_sample_bits", "24", 0); - return ost; -} + ost->last_mux_dts = AV_NOPTS_VALUE; -static void init_output_filter(OutputFilter *ofilter, const OptionsContext *o, - Muxer *mux) -{ - OutputStream *ost; + MATCH_PER_STREAM_OPT(copy_initial_nonkeyframes, i, + ms->copy_initial_nonkeyframes, oc, st); - switch (ofilter->type) { - case AVMEDIA_TYPE_VIDEO: ost = new_video_stream(mux, o, NULL); break; - case AVMEDIA_TYPE_AUDIO: ost = new_audio_stream(mux, o, NULL); break; - default: - av_log(NULL, AV_LOG_FATAL, "Only video and audio filters are supported " - "currently.\n"); - exit_program(1); + switch (type) { + case AVMEDIA_TYPE_VIDEO: ret = new_stream_video (mux, o, ost); break; + case AVMEDIA_TYPE_AUDIO: ret = new_stream_audio (mux, o, ost); break; + case AVMEDIA_TYPE_SUBTITLE: ret = new_stream_subtitle (mux, o, ost); break; + case AVMEDIA_TYPE_ATTACHMENT: ret = new_stream_attachment(mux, o, ost); break; } + if (ret < 0) + return ret; - ost->filter = ofilter; + if (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO) { + ret = ost_get_filters(o, oc, ost, &filters); + if (ret < 0) + return ret; + } - ofilter->ost = ost; - ofilter->format = -1; + if (ost->enc && + (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO)) { + if (ofilter) { + ost->filter = ofilter; + ret = ofilter_bind_ost(ofilter, ost); + if (ret < 0) + return ret; + } else { + ret = init_simple_filtergraph(ost->ist, ost, filters); + if (ret < 0) { + av_log(ost, AV_LOG_ERROR, + "Error initializing a simple filtergraph\n"); + return ret; + } + } + } else if (ost->ist) { + ret = ist_output_add(ost->ist, ost); + if (ret < 0) { + av_log(ost, AV_LOG_ERROR, + "Error binding an input stream\n"); + return ret; + } + } - if (!ost->enc_ctx) { - av_log(NULL, AV_LOG_ERROR, "Streamcopy requested for output stream %d:%d, " - "which is fed from a complex filtergraph. Filtering and streamcopy " - "cannot be used together.\n", ost->file_index, ost->index); - exit_program(1); + if (ost->ist && !ost->enc) { + ret = streamcopy_init(mux, ost); + if (ret < 0) + return ret; } - if (ost->avfilter && (ost->filters || ost->filters_script)) { - const char *opt = ost->filters ? "-vf/-af/-filter" : "-filter_script"; - av_log(NULL, AV_LOG_ERROR, - "%s '%s' was specified through the %s option " - "for output stream %d:%d, which is fed from a complex filtergraph.\n" - "%s and -filter_complex cannot be used together for the same stream.\n", - ost->filters ? "Filtergraph" : "Filtergraph script", - ost->filters ? ost->filters : ost->filters_script, - opt, ost->file_index, ost->index, opt); - exit_program(1); + // copy estimated duration as a hint to the muxer + if (ost->ist && ost->ist->st->duration > 0) { + ms->stream_duration = ist->st->duration; + ms->stream_duration_tb = ist->st->time_base; } - avfilter_inout_free(&ofilter->out_tmp); + if (post) + *post = ost; + + return 0; } -static void map_auto_video(Muxer *mux, const OptionsContext *o) +static int map_auto_video(Muxer *mux, const OptionsContext *o) { AVFormatContext *oc = mux->fc; InputStream *best_ist = NULL; @@ -859,7 +1466,7 @@ static void map_auto_video(Muxer *mux, const OptionsContext *o) /* video: highest resolution */ if (av_guess_codec(oc->oformat, NULL, oc->url, NULL, AVMEDIA_TYPE_VIDEO) == AV_CODEC_ID_NONE) - return; + return 0; qcr = avformat_query_codec(oc->oformat, oc->oformat->video_codec, 0); for (int j = 0; j < nb_input_files; j++) { @@ -898,10 +1505,12 @@ static void map_auto_video(Muxer *mux, const OptionsContext *o) } } if (best_ist) - new_video_stream(mux, o, best_ist); + return ost_add(mux, o, AVMEDIA_TYPE_VIDEO, best_ist, NULL, NULL); + + return 0; } -static void map_auto_audio(Muxer *mux, const OptionsContext *o) +static int map_auto_audio(Muxer *mux, const OptionsContext *o) { AVFormatContext *oc = mux->fc; InputStream *best_ist = NULL; @@ -909,7 +1518,7 @@ static void map_auto_audio(Muxer *mux, const OptionsContext *o) /* audio: most channels */ if (av_guess_codec(oc->oformat, NULL, oc->url, NULL, AVMEDIA_TYPE_AUDIO) == AV_CODEC_ID_NONE) - return; + return 0; for (int j = 0; j < nb_input_files; j++) { InputFile *ifile = input_files[j]; @@ -940,10 +1549,12 @@ static void map_auto_audio(Muxer *mux, const OptionsContext *o) } } if (best_ist) - new_audio_stream(mux, o, best_ist); + return ost_add(mux, o, AVMEDIA_TYPE_AUDIO, best_ist, NULL, NULL); + + return 0; } -static void map_auto_subtitle(Muxer *mux, const OptionsContext *o) +static int map_auto_subtitle(Muxer *mux, const OptionsContext *o) { AVFormatContext *oc = mux->fc; char *subtitle_codec_name = NULL; @@ -951,7 +1562,7 @@ static void map_auto_subtitle(Muxer *mux, const OptionsContext *o) /* subtitles: pick first */ MATCH_PER_TYPE_OPT(codec_names, str, subtitle_codec_name, oc, "s"); if (!avcodec_find_encoder(oc->oformat->subtitle_codec) && !subtitle_codec_name) - return; + return 0; for (InputStream *ist = ist_iter(NULL); ist; ist = ist_iter(ist)) if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) { @@ -975,36 +1586,43 @@ static void map_auto_subtitle(Muxer *mux, const OptionsContext *o) input_descriptor && output_descriptor && (!input_descriptor->props || !output_descriptor->props)) { - new_subtitle_stream(mux, o, ist); - break; + return ost_add(mux, o, AVMEDIA_TYPE_SUBTITLE, ist, NULL, NULL); } } + + return 0; } -static void map_auto_data(Muxer *mux, const OptionsContext *o) +static int map_auto_data(Muxer *mux, const OptionsContext *o) { AVFormatContext *oc = mux->fc; /* Data only if codec id match */ enum AVCodecID codec_id = av_guess_codec(oc->oformat, NULL, oc->url, NULL, AVMEDIA_TYPE_DATA); if (codec_id == AV_CODEC_ID_NONE) - return; + return 0; for (InputStream *ist = ist_iter(NULL); ist; ist = ist_iter(ist)) { if (ist->user_set_discard == AVDISCARD_ALL) continue; if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_DATA && - ist->st->codecpar->codec_id == codec_id ) - new_data_stream(mux, o, ist); + ist->st->codecpar->codec_id == codec_id) { + int ret = ost_add(mux, o, AVMEDIA_TYPE_DATA, ist, NULL, NULL); + if (ret < 0) + return ret; + } } + + return 0; } -static void map_manual(Muxer *mux, const OptionsContext *o, const StreamMap *map) +static int map_manual(Muxer *mux, const OptionsContext *o, const StreamMap *map) { InputStream *ist; + int ret; if (map->disabled) - return; + return 0; if (map->linklabel) { FilterGraph *fg; @@ -1014,8 +1632,8 @@ static void map_manual(Muxer *mux, const OptionsContext *o, const StreamMap *map for (j = 0; j < nb_filtergraphs; j++) { fg = filtergraphs[j]; for (k = 0; k < fg->nb_outputs; k++) { - AVFilterInOut *out = fg->outputs[k]->out_tmp; - if (out && !strcmp(out->name, map->linklabel)) { + const char *linklabel = fg->outputs[k]->linklabel; + if (linklabel && !strcmp(linklabel, map->linklabel)) { ofilter = fg->outputs[k]; goto loop_end; } @@ -1023,54 +1641,57 @@ static void map_manual(Muxer *mux, const OptionsContext *o, const StreamMap *map } loop_end: if (!ofilter) { - av_log(NULL, AV_LOG_FATAL, "Output with label '%s' does not exist " + av_log(mux, AV_LOG_FATAL, "Output with label '%s' does not exist " "in any defined filter graph, or was already used elsewhere.\n", map->linklabel); - exit_program(1); + return AVERROR(EINVAL); } - init_output_filter(ofilter, o, mux); + + av_log(mux, AV_LOG_VERBOSE, "Creating output stream from an explicitly " + "mapped complex filtergraph %d, output [%s]\n", fg->index, map->linklabel); + + ret = ost_add(mux, o, ofilter->type, NULL, ofilter, NULL); + if (ret < 0) + return ret; } else { ist = input_files[map->file_index]->streams[map->stream_index]; if (ist->user_set_discard == AVDISCARD_ALL) { - av_log(NULL, AV_LOG_FATAL, "Stream #%d:%d is disabled and cannot be mapped.\n", + av_log(mux, AV_LOG_FATAL, "Stream #%d:%d is disabled and cannot be mapped.\n", map->file_index, map->stream_index); - exit_program(1); + return AVERROR(EINVAL); } if(o->subtitle_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) - return; + return 0; if(o-> audio_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) - return; + return 0; if(o-> video_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) - return; + return 0; if(o-> data_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_DATA) - return; - - switch (ist->st->codecpar->codec_type) { - case AVMEDIA_TYPE_VIDEO: new_video_stream (mux, o, ist); break; - case AVMEDIA_TYPE_AUDIO: new_audio_stream (mux, o, ist); break; - case AVMEDIA_TYPE_SUBTITLE: new_subtitle_stream (mux, o, ist); break; - case AVMEDIA_TYPE_DATA: new_data_stream (mux, o, ist); break; - case AVMEDIA_TYPE_ATTACHMENT: new_attachment_stream(mux, o, ist); break; - case AVMEDIA_TYPE_UNKNOWN: - if (copy_unknown_streams) { - new_unknown_stream (mux, o, ist); - break; - } - default: - av_log(NULL, ignore_unknown_streams ? AV_LOG_WARNING : AV_LOG_FATAL, + return 0; + + if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_UNKNOWN && + !copy_unknown_streams) { + av_log(mux, ignore_unknown_streams ? AV_LOG_WARNING : AV_LOG_FATAL, "Cannot map stream #%d:%d - unsupported type.\n", map->file_index, map->stream_index); if (!ignore_unknown_streams) { - av_log(NULL, AV_LOG_FATAL, + av_log(mux, AV_LOG_FATAL, "If you want unsupported types ignored instead " "of failing, please use the -ignore_unknown option\n" "If you want them copied, please use -copy_unknown\n"); - exit_program(1); + return AVERROR(EINVAL); } + return 0; } + + ret = ost_add(mux, o, ist->st->codecpar->codec_type, ist, NULL, NULL); + if (ret < 0) + return ret; } + + return 0; } -static void of_add_attachments(Muxer *mux, const OptionsContext *o) +static int of_add_attachments(Muxer *mux, const OptionsContext *o) { OutputStream *ost; int err; @@ -1082,42 +1703,84 @@ static void of_add_attachments(Muxer *mux, const OptionsContext *o) int64_t len; if ((err = avio_open2(&pb, o->attachments[i], AVIO_FLAG_READ, &int_cb, NULL)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Could not open attachment file %s.\n", + av_log(mux, AV_LOG_FATAL, "Could not open attachment file %s.\n", o->attachments[i]); - exit_program(1); + return err; } if ((len = avio_size(pb)) <= 0) { - av_log(NULL, AV_LOG_FATAL, "Could not get size of the attachment %s.\n", + av_log(mux, AV_LOG_FATAL, "Could not get size of the attachment %s.\n", o->attachments[i]); - exit_program(1); + err = len ? len : AVERROR_INVALIDDATA; + goto read_fail; } - if (len > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE || - !(attachment = av_malloc(len + AV_INPUT_BUFFER_PADDING_SIZE))) { - av_log(NULL, AV_LOG_FATAL, "Attachment %s too large.\n", + if (len > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE) { + av_log(mux, AV_LOG_FATAL, "Attachment %s too large.\n", o->attachments[i]); - exit_program(1); + err = AVERROR(ERANGE); + goto read_fail; + } + + attachment = av_malloc(len + AV_INPUT_BUFFER_PADDING_SIZE); + if (!attachment) { + err = AVERROR(ENOMEM); + goto read_fail; + } + + err = avio_read(pb, attachment, len); + if (err < 0) + av_log(mux, AV_LOG_FATAL, "Error reading attachment file %s: %s\n", + o->attachments[i], av_err2str(err)); + else if (err != len) { + av_log(mux, AV_LOG_FATAL, "Could not read all %"PRId64" bytes for " + "attachment file %s\n", len, o->attachments[i]); + err = AVERROR(EIO); } - avio_read(pb, attachment, len); + +read_fail: + avio_closep(&pb); + if (err < 0) + return err; + memset(attachment + len, 0, AV_INPUT_BUFFER_PADDING_SIZE); - ost = new_attachment_stream(mux, o, NULL); + av_log(mux, AV_LOG_VERBOSE, "Creating attachment stream from file %s\n", + o->attachments[i]); + + err = ost_add(mux, o, AVMEDIA_TYPE_ATTACHMENT, NULL, NULL, &ost); + if (err < 0) { + av_freep(&attachment); + return err; + } + ost->attachment_filename = o->attachments[i]; - ost->st->codecpar->extradata = attachment; - ost->st->codecpar->extradata_size = len; + ost->par_in->extradata = attachment; + ost->par_in->extradata_size = len; p = strrchr(o->attachments[i], '/'); av_dict_set(&ost->st->metadata, "filename", (p && *p) ? p + 1 : o->attachments[i], AV_DICT_DONT_OVERWRITE); - avio_closep(&pb); } + + return 0; } -static void create_streams(Muxer *mux, const OptionsContext *o) +static int create_streams(Muxer *mux, const OptionsContext *o) { + static int (* const map_func[])(Muxer *mux, const OptionsContext *o) = { + [AVMEDIA_TYPE_VIDEO] = map_auto_video, + [AVMEDIA_TYPE_AUDIO] = map_auto_audio, + [AVMEDIA_TYPE_SUBTITLE] = map_auto_subtitle, + [AVMEDIA_TYPE_DATA] = map_auto_data, + }; + AVFormatContext *oc = mux->fc; - int auto_disable_v = o->video_disable; - int auto_disable_a = o->audio_disable; - int auto_disable_s = o->subtitle_disable; - int auto_disable_d = o->data_disable; + + int auto_disable = + o->video_disable * (1 << AVMEDIA_TYPE_VIDEO) | + o->audio_disable * (1 << AVMEDIA_TYPE_AUDIO) | + o->subtitle_disable * (1 << AVMEDIA_TYPE_SUBTITLE) | + o->data_disable * (1 << AVMEDIA_TYPE_DATA); + + int ret; /* create streams for all unlabeled output pads */ for (int i = 0; i < nb_filtergraphs; i++) { @@ -1125,46 +1788,62 @@ static void create_streams(Muxer *mux, const OptionsContext *o) for (int j = 0; j < fg->nb_outputs; j++) { OutputFilter *ofilter = fg->outputs[j]; - if (!ofilter->out_tmp || ofilter->out_tmp->name) + if (ofilter->linklabel || ofilter->ost) continue; - switch (ofilter->type) { - case AVMEDIA_TYPE_VIDEO: auto_disable_v = 1; break; - case AVMEDIA_TYPE_AUDIO: auto_disable_a = 1; break; - case AVMEDIA_TYPE_SUBTITLE: auto_disable_s = 1; break; - } - init_output_filter(ofilter, o, mux); + auto_disable |= 1 << ofilter->type; + + av_log(mux, AV_LOG_VERBOSE, "Creating output stream from unlabeled " + "output of complex filtergraph %d.", fg->index); + if (!o->nb_stream_maps) + av_log(mux, AV_LOG_VERBOSE, " This overrides automatic %s mapping.", + av_get_media_type_string(ofilter->type)); + av_log(mux, AV_LOG_VERBOSE, "\n"); + + ret = ost_add(mux, o, ofilter->type, NULL, ofilter, NULL); + if (ret < 0) + return ret; } } if (!o->nb_stream_maps) { + av_log(mux, AV_LOG_VERBOSE, "No explicit maps, mapping streams automatically...\n"); + /* pick the "best" stream of each type */ - if (!auto_disable_v) - map_auto_video(mux, o); - if (!auto_disable_a) - map_auto_audio(mux, o); - if (!auto_disable_s) - map_auto_subtitle(mux, o); - if (!auto_disable_d) - map_auto_data(mux, o); + for (int i = 0; i < FF_ARRAY_ELEMS(map_func); i++) { + if (!map_func[i] || auto_disable & (1 << i)) + continue; + ret = map_func[i](mux, o); + if (ret < 0) + return ret; + } } else { - for (int i = 0; i < o->nb_stream_maps; i++) - map_manual(mux, o, &o->stream_maps[i]); + av_log(mux, AV_LOG_VERBOSE, "Adding streams from explicit maps...\n"); + + for (int i = 0; i < o->nb_stream_maps; i++) { + ret = map_manual(mux, o, &o->stream_maps[i]); + if (ret < 0) + return ret; + } } - of_add_attachments(mux, o); + ret = of_add_attachments(mux, o); + if (ret < 0) + return ret; if (!oc->nb_streams && !(oc->oformat->flags & AVFMT_NOSTREAMS)) { av_dump_format(oc, nb_output_files - 1, oc->url, 1); - av_log(NULL, AV_LOG_ERROR, "Output file #%d does not contain any stream\n", nb_output_files - 1); - exit_program(1); + av_log(mux, AV_LOG_ERROR, "Output file does not contain any stream\n"); + return AVERROR(EINVAL); } + + return 0; } static int setup_sync_queues(Muxer *mux, AVFormatContext *oc, int64_t buf_size_us) { OutputFile *of = &mux->of; - int nb_av_enc = 0, nb_interleaved = 0; + int nb_av_enc = 0, nb_audio_fs = 0, nb_interleaved = 0; int limit_frames = 0, limit_frames_av_enc = 0; #define IS_AV_ENC(ost, type) \ @@ -1174,34 +1853,41 @@ static int setup_sync_queues(Muxer *mux, AVFormatContext *oc, int64_t buf_size_u for (int i = 0; i < oc->nb_streams; i++) { OutputStream *ost = of->streams[i]; MuxStream *ms = ms_from_ost(ost); - enum AVMediaType type = ost->st->codecpar->codec_type; + enum AVMediaType type = ost->type; ost->sq_idx_encode = -1; ost->sq_idx_mux = -1; nb_interleaved += IS_INTERLEAVED(type); nb_av_enc += IS_AV_ENC(ost, type); + nb_audio_fs += (ost->enc_ctx && type == AVMEDIA_TYPE_AUDIO && + !(ost->enc_ctx->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)); limit_frames |= ms->max_frames < INT64_MAX; limit_frames_av_enc |= (ms->max_frames < INT64_MAX) && IS_AV_ENC(ost, type); } if (!((nb_interleaved > 1 && of->shortest) || - (nb_interleaved > 0 && limit_frames))) + (nb_interleaved > 0 && limit_frames) || + nb_audio_fs)) return 0; - /* if we have more than one encoded audio/video streams, or at least - * one encoded audio/video stream is frame-limited, then we - * synchronize them before encoding */ - if ((of->shortest && nb_av_enc > 1) || limit_frames_av_enc) { - of->sq_encode = sq_alloc(SYNC_QUEUE_FRAMES, buf_size_us); + /* we use a sync queue before encoding when: + * - 'shortest' is in effect and we have two or more encoded audio/video + * streams + * - at least one encoded audio/video stream is frame-limited, since + * that has similar semantics to 'shortest' + * - at least one audio encoder requires constant frame sizes + */ + if ((of->shortest && nb_av_enc > 1) || limit_frames_av_enc || nb_audio_fs) { + of->sq_encode = sq_alloc(SYNC_QUEUE_FRAMES, buf_size_us, mux); if (!of->sq_encode) return AVERROR(ENOMEM); for (int i = 0; i < oc->nb_streams; i++) { OutputStream *ost = of->streams[i]; MuxStream *ms = ms_from_ost(ost); - enum AVMediaType type = ost->st->codecpar->codec_type; + enum AVMediaType type = ost->type; if (!IS_AV_ENC(ost, type)) continue; @@ -1211,10 +1897,6 @@ static int setup_sync_queues(Muxer *mux, AVFormatContext *oc, int64_t buf_size_u if (ost->sq_idx_encode < 0) return ost->sq_idx_encode; - ost->sq_frame = av_frame_alloc(); - if (!ost->sq_frame) - return AVERROR(ENOMEM); - if (ms->max_frames != INT64_MAX) sq_limit_frames(of->sq_encode, ost->sq_idx_encode, ms->max_frames); } @@ -1223,7 +1905,7 @@ static int setup_sync_queues(Muxer *mux, AVFormatContext *oc, int64_t buf_size_u /* if there are any additional interleaved streams, then ALL the streams * are also synchronized before sending them to the muxer */ if (nb_interleaved > nb_av_enc) { - mux->sq_mux = sq_alloc(SYNC_QUEUE_PACKETS, buf_size_us); + mux->sq_mux = sq_alloc(SYNC_QUEUE_PACKETS, buf_size_us, mux); if (!mux->sq_mux) return AVERROR(ENOMEM); @@ -1234,7 +1916,7 @@ static int setup_sync_queues(Muxer *mux, AVFormatContext *oc, int64_t buf_size_u for (int i = 0; i < oc->nb_streams; i++) { OutputStream *ost = of->streams[i]; MuxStream *ms = ms_from_ost(ost); - enum AVMediaType type = ost->st->codecpar->codec_type; + enum AVMediaType type = ost->type; if (!IS_INTERLEAVED(type)) continue; @@ -1255,75 +1937,66 @@ static int setup_sync_queues(Muxer *mux, AVFormatContext *oc, int64_t buf_size_u return 0; } -static void of_add_programs(AVFormatContext *oc, const OptionsContext *o) +static int of_add_programs(Muxer *mux, const OptionsContext *o) { + AVFormatContext *oc = mux->fc; /* process manually set programs */ for (int i = 0; i < o->nb_program; i++) { - const char *p = o->program[i].u.str; - int progid = i+1; + AVDictionary *dict = NULL; + const AVDictionaryEntry *e; AVProgram *program; + int ret, progid = i + 1; - while(*p) { - const char *p2 = av_get_token(&p, ":"); - const char *to_dealloc = p2; - char *key; - if (!p2) - break; + ret = av_dict_parse_string(&dict, o->program[i].u.str, "=", ":", + AV_DICT_MULTIKEY); + if (ret < 0) { + av_log(mux, AV_LOG_ERROR, "Error parsing program specification %s\n", + o->program[i].u.str); + return ret; + } - if(*p) p++; + e = av_dict_get(dict, "program_num", NULL, 0); + if (e) { + progid = strtol(e->value, NULL, 0); + av_dict_set(&dict, e->key, NULL, 0); + } - key = av_get_token(&p2, "="); - if (!key || !*p2) { - av_freep(&to_dealloc); - av_freep(&key); - break; - } - p2++; + program = av_new_program(oc, progid); + if (!program) { + ret = AVERROR(ENOMEM); + goto fail; + } - if (!strcmp(key, "program_num")) - progid = strtol(p2, NULL, 0); - av_freep(&to_dealloc); - av_freep(&key); + e = av_dict_get(dict, "title", NULL, 0); + if (e) { + av_dict_set(&program->metadata, e->key, e->value, 0); + av_dict_set(&dict, e->key, NULL, 0); } - program = av_new_program(oc, progid); - if (!program) - report_and_exit(AVERROR(ENOMEM)); - - p = o->program[i].u.str; - while(*p) { - const char *p2 = av_get_token(&p, ":"); - const char *to_dealloc = p2; - char *key; - if (!p2) - break; - if(*p) p++; - - key = av_get_token(&p2, "="); - if (!key) { - av_log(NULL, AV_LOG_FATAL, - "No '=' character in program string %s.\n", - p2); - exit_program(1); - } - if (!*p2) - exit_program(1); - p2++; - - if (!strcmp(key, "title")) { - av_dict_set(&program->metadata, "title", p2, 0); - } else if (!strcmp(key, "program_num")) { - } else if (!strcmp(key, "st")) { - int st_num = strtol(p2, NULL, 0); - av_program_add_stream_index(oc, progid, st_num); - } else { - av_log(NULL, AV_LOG_FATAL, "Unknown program key %s.\n", key); - exit_program(1); - } - av_freep(&to_dealloc); - av_freep(&key); + e = NULL; + while (e = av_dict_get(dict, "st", e, 0)) { + int st_num = strtol(e->value, NULL, 0); + av_program_add_stream_index(oc, progid, st_num); + } + + // make sure that nothing but "st" entries are left in the dict + e = NULL; + while (e = av_dict_iterate(dict, e)) { + if (!strcmp(e->key, "st")) + continue; + + av_log(mux, AV_LOG_FATAL, "Unknown program key %s.\n", e->key); + ret = AVERROR(EINVAL); + goto fail; } + +fail: + av_dict_free(&dict); + if (ret < 0) + return ret; } + + return 0; } /** @@ -1333,7 +2006,8 @@ static void of_add_programs(AVFormatContext *oc, const OptionsContext *o) * @param index for type c/p, chapter/program index is written here * @param stream_spec for type s, the stream specifier is written here */ -static void parse_meta_type(const char *arg, char *type, int *index, const char **stream_spec) +static int parse_meta_type(void *logctx, const char *arg, + char *type, int *index, const char **stream_spec) { if (*arg) { *type = *arg; @@ -1342,8 +2016,8 @@ static void parse_meta_type(const char *arg, char *type, int *index, const char break; case 's': if (*(++arg) && *arg != ':') { - av_log(NULL, AV_LOG_FATAL, "Invalid metadata specifier %s.\n", arg); - exit_program(1); + av_log(logctx, AV_LOG_FATAL, "Invalid metadata specifier %s.\n", arg); + return AVERROR(EINVAL); } *stream_spec = *arg == ':' ? arg + 1 : ""; break; @@ -1353,15 +2027,17 @@ static void parse_meta_type(const char *arg, char *type, int *index, const char *index = strtol(++arg, NULL, 0); break; default: - av_log(NULL, AV_LOG_FATAL, "Invalid metadata type %c.\n", *arg); - exit_program(1); + av_log(logctx, AV_LOG_FATAL, "Invalid metadata type %c.\n", *arg); + return AVERROR(EINVAL); } } else *type = 'g'; + + return 0; } -static void of_add_metadata(OutputFile *of, AVFormatContext *oc, - const OptionsContext *o) +static int of_add_metadata(OutputFile *of, AVFormatContext *oc, + const OptionsContext *o) { for (int i = 0; i < o->nb_metadata; i++) { AVDictionary **m; @@ -1371,13 +2047,16 @@ static void of_add_metadata(OutputFile *of, AVFormatContext *oc, val = strchr(o->metadata[i].u.str, '='); if (!val) { - av_log(NULL, AV_LOG_FATAL, "No '=' character in metadata string %s.\n", + av_log(of, AV_LOG_FATAL, "No '=' character in metadata string %s.\n", o->metadata[i].u.str); - exit_program(1); + return AVERROR(EINVAL); } *val++ = 0; - parse_meta_type(o->metadata[i].specifier, &type, &index, &stream_spec); + ret = parse_meta_type(of, o->metadata[i].specifier, &type, &index, &stream_spec); + if (ret < 0) + return ret; + if (type == 's') { for (int j = 0; j < oc->nb_streams; j++) { OutputStream *ost = of->streams[j]; @@ -1391,7 +2070,7 @@ static void of_add_metadata(OutputFile *of, AVFormatContext *oc, ost->rotate_override_value = theta; } - av_log(NULL, AV_LOG_WARNING, + av_log(ost, AV_LOG_WARNING, "Conversion of a 'rotate' metadata key to a " "proper display matrix rotation is deprecated. " "See -display_rotation for setting rotation " @@ -1403,7 +2082,7 @@ static void of_add_metadata(OutputFile *of, AVFormatContext *oc, } #endif } else if (ret < 0) - exit_program(1); + return ret; } } else { switch (type) { @@ -1412,63 +2091,27 @@ static void of_add_metadata(OutputFile *of, AVFormatContext *oc, break; case 'c': if (index < 0 || index >= oc->nb_chapters) { - av_log(NULL, AV_LOG_FATAL, "Invalid chapter index %d in metadata specifier.\n", index); - exit_program(1); + av_log(of, AV_LOG_FATAL, "Invalid chapter index %d in metadata specifier.\n", index); + return AVERROR(EINVAL); } m = &oc->chapters[index]->metadata; break; case 'p': if (index < 0 || index >= oc->nb_programs) { - av_log(NULL, AV_LOG_FATAL, "Invalid program index %d in metadata specifier.\n", index); - exit_program(1); + av_log(of, AV_LOG_FATAL, "Invalid program index %d in metadata specifier.\n", index); + return AVERROR(EINVAL); } m = &oc->programs[index]->metadata; break; default: - av_log(NULL, AV_LOG_FATAL, "Invalid metadata specifier %s.\n", o->metadata[i].specifier); - exit_program(1); + av_log(of, AV_LOG_FATAL, "Invalid metadata specifier %s.\n", o->metadata[i].specifier); + return AVERROR(EINVAL); } av_dict_set(m, o->metadata[i].u.str, *val ? val : NULL, 0); } } -} - -static void set_channel_layout(OutputFilter *f, OutputStream *ost) -{ - const AVCodec *c = ost->enc_ctx->codec; - int i, err; - - if (ost->enc_ctx->ch_layout.order != AV_CHANNEL_ORDER_UNSPEC) { - /* Pass the layout through for all orders but UNSPEC */ - err = av_channel_layout_copy(&f->ch_layout, &ost->enc_ctx->ch_layout); - if (err < 0) - report_and_exit(AVERROR(ENOMEM)); - return; - } - /* Requested layout is of order UNSPEC */ - if (!c->ch_layouts) { - /* Use the default native layout for the requested amount of channels when the - encoder doesn't have a list of supported layouts */ - av_channel_layout_default(&f->ch_layout, ost->enc_ctx->ch_layout.nb_channels); - return; - } - /* Encoder has a list of supported layouts. Pick the first layout in it with the - same amount of channels as the requested layout */ - for (i = 0; c->ch_layouts[i].nb_channels; i++) { - if (c->ch_layouts[i].nb_channels == ost->enc_ctx->ch_layout.nb_channels) - break; - } - if (c->ch_layouts[i].nb_channels) { - /* Use it if one is found */ - err = av_channel_layout_copy(&f->ch_layout, &c->ch_layouts[i]); - if (err < 0) - report_and_exit(AVERROR(ENOMEM)); - return; - } - /* If no layout for the amount of channels requested was found, use the default - native layout for it. */ - av_channel_layout_default(&f->ch_layout, ost->enc_ctx->ch_layout.nb_channels); + return 0; } static int copy_chapters(InputFile *ifile, OutputFile *ofile, AVFormatContext *os, @@ -1514,11 +2157,12 @@ static int copy_chapters(InputFile *ifile, OutputFile *ofile, AVFormatContext *o return 0; } -static int copy_metadata(const char *outspec, const char *inspec, - AVFormatContext *oc, AVFormatContext *ic, +static int copy_metadata(Muxer *mux, AVFormatContext *ic, + const char *outspec, const char *inspec, int *metadata_global_manual, int *metadata_streams_manual, - int *metadata_chapters_manual, const OptionsContext *o) + int *metadata_chapters_manual) { + AVFormatContext *oc = mux->fc; AVDictionary **meta_in = NULL; AVDictionary **meta_out = NULL; int i, ret = 0; @@ -1526,14 +2170,17 @@ static int copy_metadata(const char *outspec, const char *inspec, const char *istream_spec = NULL, *ostream_spec = NULL; int idx_in = 0, idx_out = 0; - parse_meta_type(inspec, &type_in, &idx_in, &istream_spec); - parse_meta_type(outspec, &type_out, &idx_out, &ostream_spec); + ret = parse_meta_type(mux, inspec, &type_in, &idx_in, &istream_spec); + if (ret >= 0) + ret = parse_meta_type(mux, outspec, &type_out, &idx_out, &ostream_spec); + if (ret < 0) + return ret; - if (type_in == 'g' || type_out == 'g') + if (type_in == 'g' || type_out == 'g' || !*outspec) *metadata_global_manual = 1; - if (type_in == 's' || type_out == 's') + if (type_in == 's' || type_out == 's' || !*outspec) *metadata_streams_manual = 1; - if (type_in == 'c' || type_out == 'c') + if (type_in == 'c' || type_out == 'c' || !*outspec) *metadata_chapters_manual = 1; /* ic is NULL when just disabling automatic mappings */ @@ -1542,9 +2189,9 @@ static int copy_metadata(const char *outspec, const char *inspec, #define METADATA_CHECK_INDEX(index, nb_elems, desc)\ if ((index) < 0 || (index) >= (nb_elems)) {\ - av_log(NULL, AV_LOG_FATAL, "Invalid %s index %d while processing metadata maps.\n",\ + av_log(mux, AV_LOG_FATAL, "Invalid %s index %d while processing metadata maps.\n",\ (desc), (index));\ - exit_program(1);\ + return AVERROR(EINVAL);\ } #define SET_DICT(type, meta, context, index)\ @@ -1575,11 +2222,11 @@ static int copy_metadata(const char *outspec, const char *inspec, meta_in = &ic->streams[i]->metadata; break; } else if (ret < 0) - exit_program(1); + return ret; } if (!meta_in) { - av_log(NULL, AV_LOG_FATAL, "Stream specifier %s does not match any streams.\n", istream_spec); - exit_program(1); + av_log(mux, AV_LOG_FATAL, "Stream specifier %s does not match any streams.\n", istream_spec); + return AVERROR(EINVAL); } } @@ -1589,7 +2236,7 @@ static int copy_metadata(const char *outspec, const char *inspec, meta_out = &oc->streams[i]->metadata; av_dict_copy(meta_out, *meta_in, AV_DICT_DONT_OVERWRITE); } else if (ret < 0) - exit_program(1); + return ret; } } else av_dict_copy(meta_out, *meta_in, AV_DICT_DONT_OVERWRITE); @@ -1597,7 +2244,7 @@ static int copy_metadata(const char *outspec, const char *inspec, return 0; } -static void copy_meta(Muxer *mux, const OptionsContext *o) +static int copy_meta(Muxer *mux, const OptionsContext *o) { OutputFile *of = &mux->of; AVFormatContext *oc = mux->fc; @@ -1605,6 +2252,7 @@ static void copy_meta(Muxer *mux, const OptionsContext *o) int metadata_global_manual = 0; int metadata_streams_manual = 0; int metadata_chapters_manual = 0; + int ret; /* copy metadata */ for (int i = 0; i < o->nb_metadata_map; i++) { @@ -1612,14 +2260,17 @@ static void copy_meta(Muxer *mux, const OptionsContext *o) int in_file_index = strtol(o->metadata_map[i].u.str, &p, 0); if (in_file_index >= nb_input_files) { - av_log(NULL, AV_LOG_FATAL, "Invalid input file index %d while processing metadata maps\n", in_file_index); - exit_program(1); + av_log(mux, AV_LOG_FATAL, "Invalid input file index %d while " + "processing metadata maps\n", in_file_index); + return AVERROR(EINVAL); } - copy_metadata(o->metadata_map[i].specifier, *p ? p + 1 : p, oc, - in_file_index >= 0 ? - input_files[in_file_index]->ctx : NULL, - &metadata_global_manual, &metadata_streams_manual, - &metadata_chapters_manual, o); + ret = copy_metadata(mux, + in_file_index >= 0 ? input_files[in_file_index]->ctx : NULL, + o->metadata_map[i].specifier, *p ? p + 1 : p, + &metadata_global_manual, &metadata_streams_manual, + &metadata_chapters_manual); + if (ret < 0) + return ret; } /* copy chapters */ @@ -1633,9 +2284,9 @@ static void copy_meta(Muxer *mux, const OptionsContext *o) break; } } else { - av_log(NULL, AV_LOG_FATAL, "Invalid input file index %d in chapter mapping.\n", + av_log(mux, AV_LOG_FATAL, "Invalid input file index %d in chapter mapping.\n", chapters_input_file); - exit_program(1); + return AVERROR(EINVAL); } } if (chapters_input_file >= 0) @@ -1664,6 +2315,8 @@ static void copy_meta(Muxer *mux, const OptionsContext *o) av_dict_set(&ost->st->metadata, "encoder", NULL, 0); } } + + return 0; } static int set_dispositions(Muxer *mux, const OptionsContext *o) @@ -1671,8 +2324,9 @@ static int set_dispositions(Muxer *mux, const OptionsContext *o) OutputFile *of = &mux->of; AVFormatContext *ctx = mux->fc; - int nb_streams[AVMEDIA_TYPE_NB] = { 0 }; - int have_default[AVMEDIA_TYPE_NB] = { 0 }; + // indexed by type+1, because AVMEDIA_TYPE_UNKNOWN=-1 + int nb_streams[AVMEDIA_TYPE_NB + 1] = { 0 }; + int have_default[AVMEDIA_TYPE_NB + 1] = { 0 }; int have_manual = 0; int ret = 0; @@ -1686,7 +2340,7 @@ static int set_dispositions(Muxer *mux, const OptionsContext *o) for (int i = 0; i < ctx->nb_streams; i++) { OutputStream *ost = of->streams[i]; - nb_streams[ost->st->codecpar->codec_type]++; + nb_streams[ost->type + 1]++; MATCH_PER_STREAM_OPT(disposition, str, dispositions[i], ctx, ost->st); @@ -1696,7 +2350,7 @@ static int set_dispositions(Muxer *mux, const OptionsContext *o) ost->st->disposition = ost->ist->st->disposition; if (ost->st->disposition & AV_DISPOSITION_DEFAULT) - have_default[ost->st->codecpar->codec_type] = 1; + have_default[ost->type + 1] = 1; } } @@ -1709,18 +2363,7 @@ static int set_dispositions(Muxer *mux, const OptionsContext *o) if (!disp) continue; -#if LIBAVFORMAT_VERSION_MAJOR >= 60 ret = av_opt_set(ost->st, "disposition", disp, 0); -#else - { - const AVClass *class = av_stream_get_class(); - const AVOption *o = av_opt_find(&class, "disposition", NULL, 0, AV_OPT_SEARCH_FAKE_OBJ); - - av_assert0(o); - ret = av_opt_eval_flags(&class, o, disp, &ost->st->disposition); - } -#endif - if (ret < 0) goto finish; } @@ -1730,14 +2373,14 @@ static int set_dispositions(Muxer *mux, const OptionsContext *o) // "Suitable" means the first of that type, skipping attached pictures. for (int i = 0; i < ctx->nb_streams; i++) { OutputStream *ost = of->streams[i]; - enum AVMediaType type = ost->st->codecpar->codec_type; + enum AVMediaType type = ost->type; - if (nb_streams[type] < 2 || have_default[type] || + if (nb_streams[type + 1] < 2 || have_default[type + 1] || ost->st->disposition & AV_DISPOSITION_ATTACHED_PIC) continue; ost->st->disposition |= AV_DISPOSITION_DEFAULT; - have_default[type] = 1; + have_default[type + 1] = 1; } } @@ -1761,11 +2404,11 @@ static int compare_int64(const void *a, const void *b) return FFDIFFSIGN(*(const int64_t *)a, *(const int64_t *)b); } -static void parse_forced_key_frames(KeyframeForceCtx *kf, const Muxer *mux, - const char *spec) +static int parse_forced_key_frames(void *log, KeyframeForceCtx *kf, + const Muxer *mux, const char *spec) { const char *p; - int n = 1, i, size, index = 0; + int n = 1, i, ret, size, index = 0; int64_t t, *pts; for (p = spec; *p; p++) @@ -1774,7 +2417,7 @@ static void parse_forced_key_frames(KeyframeForceCtx *kf, const Muxer *mux, size = n; pts = av_malloc_array(size, sizeof(*pts)); if (!pts) - report_and_exit(AVERROR(ENOMEM)); + return AVERROR(ENOMEM); p = spec; for (i = 0; i < n; i++) { @@ -1783,7 +2426,7 @@ static void parse_forced_key_frames(KeyframeForceCtx *kf, const Muxer *mux, if (next) *next++ = 0; - if (!memcmp(p, "chapters", 8)) { + if (strstr(p, "chapters") == p) { AVChapter * const *ch = mux->fc->chapters; unsigned int nb_ch = mux->fc->nb_chapters; int j; @@ -1791,8 +2434,17 @@ static void parse_forced_key_frames(KeyframeForceCtx *kf, const Muxer *mux, if (nb_ch > INT_MAX - size || !(pts = av_realloc_f(pts, size += nb_ch - 1, sizeof(*pts)))) - report_and_exit(AVERROR(ENOMEM)); - t = p[8] ? parse_time_or_die("force_key_frames", p + 8, 1) : 0; + return AVERROR(ENOMEM); + + if (p[8]) { + ret = av_parse_time(&t, p + 8, 1); + if (ret < 0) { + av_log(log, AV_LOG_ERROR, + "Invalid chapter time offset: %s\n", p + 8); + goto fail; + } + } else + t = 0; for (j = 0; j < nb_ch; j++) { const AVChapter *c = ch[j]; @@ -1803,7 +2455,13 @@ static void parse_forced_key_frames(KeyframeForceCtx *kf, const Muxer *mux, } else { av_assert1(index < size); - pts[index++] = parse_time_or_die("force_key_frames", p, 1); + ret = av_parse_time(&t, p, 1); + if (ret < 0) { + av_log(log, AV_LOG_ERROR, "Invalid keyframe time: %s\n", p); + goto fail; + } + + pts[index++] = t; } p = next; @@ -1813,6 +2471,11 @@ static void parse_forced_key_frames(KeyframeForceCtx *kf, const Muxer *mux, qsort(pts, size, sizeof(*pts), compare_int64); kf->nb_pts = size; kf->pts = pts; + + return 0; +fail: + av_freep(&pts); + return ret; } static int process_forced_keyframes(Muxer *mux, const OptionsContext *o) @@ -1823,7 +2486,7 @@ static int process_forced_keyframes(Muxer *mux, const OptionsContext *o) MATCH_PER_STREAM_OPT(forced_key_frames, str, forced_keyframes, mux->fc, ost->st); - if (!(ost->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && + if (!(ost->type == AVMEDIA_TYPE_VIDEO && ost->enc_ctx && forced_keyframes)) continue; @@ -1831,7 +2494,7 @@ static int process_forced_keyframes(Muxer *mux, const OptionsContext *o) int ret = av_expr_parse(&ost->kf.pexpr, forced_keyframes + 5, forced_keyframes_const_names, NULL, NULL, NULL, NULL, 0, NULL); if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, + av_log(ost, AV_LOG_ERROR, "Invalid force_key_frames expression '%s'\n", forced_keyframes + 5); return ret; } @@ -1847,14 +2510,16 @@ static int process_forced_keyframes(Muxer *mux, const OptionsContext *o) } else if (!strcmp(forced_keyframes, "source_no_drop")) { ost->kf.type = KF_FORCE_SOURCE_NO_DROP; } else { - parse_forced_key_frames(&ost->kf, mux, forced_keyframes); + int ret = parse_forced_key_frames(ost, &ost->kf, mux, forced_keyframes); + if (ret < 0) + return ret; } } return 0; } -static void validate_enc_avopt(const Muxer *mux, const AVDictionary *codec_avopt) +static int validate_enc_avopt(Muxer *mux, const AVDictionary *codec_avopt) { const AVClass *class = avcodec_get_class(); const AVClass *fclass = avformat_get_class(); @@ -1880,25 +2545,70 @@ static void validate_enc_avopt(const Muxer *mux, const AVDictionary *codec_avopt continue; if (!(option->flags & AV_OPT_FLAG_ENCODING_PARAM)) { - av_log(NULL, AV_LOG_ERROR, "Codec AVOption %s (%s) specified for " - "output file #%d (%s) is not an encoding option.\n", e->key, - option->help ? option->help : "", nb_output_files - 1, - mux->fc->url); - exit_program(1); + av_log(mux, AV_LOG_ERROR, "Codec AVOption %s (%s) is not an " + "encoding option.\n", e->key, option->help ? option->help : ""); + return AVERROR(EINVAL); } // gop_timecode is injected by generic code but not always used if (!strcmp(e->key, "gop_timecode")) continue; - av_log(NULL, AV_LOG_WARNING, "Codec AVOption %s (%s) specified for " - "output file #%d (%s) has not been used for any stream. The most " - "likely reason is either wrong type (e.g. a video option with " - "no video streams) or that it is a private option of some encoder " - "which was not actually used for any stream.\n", e->key, - option->help ? option->help : "", nb_output_files - 1, mux->fc->url); + av_log(mux, AV_LOG_WARNING, "Codec AVOption %s (%s) has not been used " + "for any stream. The most likely reason is either wrong type " + "(e.g. a video option with no video streams) or that it is a " + "private option of some encoder which was not actually used for " + "any stream.\n", e->key, option->help ? option->help : ""); } av_dict_free(&unused_opts); + + return 0; +} + +static int init_output_stream_nofilter(OutputStream *ost) +{ + int ret = 0; + + if (ost->enc_ctx) { + ret = enc_open(ost, NULL); + if (ret < 0) + return ret; + } else { + ret = of_stream_init(output_files[ost->file_index], ost); + if (ret < 0) + return ret; + } + + return ret; +} + +static const char *output_file_item_name(void *obj) +{ + const Muxer *mux = obj; + + return mux->log_name; +} + +static const AVClass output_file_class = { + .class_name = "OutputFile", + .version = LIBAVUTIL_VERSION_INT, + .item_name = output_file_item_name, + .category = AV_CLASS_CATEGORY_MUXER, +}; + +static Muxer *mux_alloc(void) +{ + Muxer *mux = allocate_array_elem(&output_files, sizeof(*mux), &nb_output_files); + + if (!mux) + return NULL; + + mux->of.class = &output_file_class; + mux->of.index = nb_output_files - 1; + + snprintf(mux->log_name, sizeof(mux->log_name), "out#%d", mux->of.index); + + return mux; } int of_open(const OptionsContext *o, const char *filename) @@ -1911,25 +2621,27 @@ int of_open(const OptionsContext *o, const char *filename) int64_t recording_time = o->recording_time; int64_t stop_time = o->stop_time; + mux = mux_alloc(); + if (!mux) + return AVERROR(ENOMEM); + + of = &mux->of; + if (stop_time != INT64_MAX && recording_time != INT64_MAX) { stop_time = INT64_MAX; - av_log(NULL, AV_LOG_WARNING, "-t and -to cannot be used together; using -t.\n"); + av_log(mux, AV_LOG_WARNING, "-t and -to cannot be used together; using -t.\n"); } if (stop_time != INT64_MAX && recording_time == INT64_MAX) { int64_t start_time = o->start_time == AV_NOPTS_VALUE ? 0 : o->start_time; if (stop_time <= start_time) { - av_log(NULL, AV_LOG_ERROR, "-to value smaller than -ss; aborting.\n"); - exit_program(1); + av_log(mux, AV_LOG_ERROR, "-to value smaller than -ss; aborting.\n"); + return AVERROR(EINVAL); } else { recording_time = stop_time - start_time; } } - mux = allocate_array_elem(&output_files, sizeof(Muxer), &nb_output_files); - of = &mux->of; - - of->index = nb_output_files - 1; of->recording_time = recording_time; of->start_time = o->start_time; of->shortest = o->shortest; @@ -1943,11 +2655,15 @@ int of_open(const OptionsContext *o, const char *filename) err = avformat_alloc_output_context2(&oc, NULL, o->format, filename); if (!oc) { - print_error(filename, err); - exit_program(1); + av_log(mux, AV_LOG_FATAL, "Error initializing the muxer for %s: %s\n", + filename, av_err2str(err)); + return err; } mux->fc = oc; + av_strlcat(mux->log_name, "/", sizeof(mux->log_name)); + av_strlcat(mux->log_name, oc->oformat->name, sizeof(mux->log_name)); + if (strcmp(oc->oformat->name, "rtp")) want_sdp = 0; @@ -1966,92 +2682,43 @@ int of_open(const OptionsContext *o, const char *filename) } /* create all output streams for this file */ - create_streams(mux, o); + err = create_streams(mux, o); + if (err < 0) + return err; /* check if all codec options have been used */ - validate_enc_avopt(mux, o->g->codec_opts); - - /* set the decoding_needed flags and create simple filtergraphs */ - for (int i = 0; i < of->nb_streams; i++) { - OutputStream *ost = of->streams[i]; - - if (ost->enc_ctx && ost->ist) { - InputStream *ist = ost->ist; - ist->decoding_needed |= DECODING_FOR_OST; - ist->processing_needed = 1; - - if (ost->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || - ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { - err = init_simple_filtergraph(ist, ost); - if (err < 0) { - av_log(NULL, AV_LOG_ERROR, - "Error initializing a simple filtergraph between streams " - "%d:%d->%d:%d\n", ist->file_index, ist->st->index, - nb_output_files - 1, ost->st->index); - exit_program(1); - } - } - } else if (ost->ist) { - ost->ist->processing_needed = 1; - } - - /* set the filter output constraints */ - if (ost->filter) { - const AVCodec *c = ost->enc_ctx->codec; - OutputFilter *f = ost->filter; - switch (ost->enc_ctx->codec_type) { - case AVMEDIA_TYPE_VIDEO: - f->frame_rate = ost->frame_rate; - f->width = ost->enc_ctx->width; - f->height = ost->enc_ctx->height; - if (ost->enc_ctx->pix_fmt != AV_PIX_FMT_NONE) { - f->format = ost->enc_ctx->pix_fmt; - } else { - f->formats = c->pix_fmts; - } - break; - case AVMEDIA_TYPE_AUDIO: - if (ost->enc_ctx->sample_fmt != AV_SAMPLE_FMT_NONE) { - f->format = ost->enc_ctx->sample_fmt; - } else { - f->formats = c->sample_fmts; - } - if (ost->enc_ctx->sample_rate) { - f->sample_rate = ost->enc_ctx->sample_rate; - } else { - f->sample_rates = c->supported_samplerates; - } - if (ost->enc_ctx->ch_layout.nb_channels) { - set_channel_layout(f, ost); - } else if (c->ch_layouts) { - f->ch_layouts = c->ch_layouts; - } - break; - } - } - } + err = validate_enc_avopt(mux, o->g->codec_opts); + if (err < 0) + return err; /* check filename in case of an image number is expected */ - if (oc->oformat->flags & AVFMT_NEEDNUMBER) { - if (!av_filename_number_test(oc->url)) { - print_error(oc->url, AVERROR(EINVAL)); - exit_program(1); - } + if (oc->oformat->flags & AVFMT_NEEDNUMBER && !av_filename_number_test(oc->url)) { + av_log(mux, AV_LOG_FATAL, + "Output filename '%s' does not contain a numeric pattern like " + "'%%d', which is required by output format '%s'.\n", + oc->url, oc->oformat->name); + return AVERROR(EINVAL); } if (!(oc->oformat->flags & AVFMT_NOFILE)) { /* test if it already exists to avoid losing precious files */ - assert_file_overwrite(filename); + err = assert_file_overwrite(filename); + if (err < 0) + return err; /* open the file */ if ((err = avio_open2(&oc->pb, filename, AVIO_FLAG_WRITE, &oc->interrupt_callback, &mux->opts)) < 0) { - print_error(filename, err); - exit_program(1); + av_log(mux, AV_LOG_FATAL, "Error opening output %s: %s\n", + filename, av_err2str(err)); + return err; } - } else if (strcmp(oc->oformat->name, "image2")==0 && !av_filename_number_test(filename)) - assert_file_overwrite(filename); + } else if (strcmp(oc->oformat->name, "image2")==0 && !av_filename_number_test(filename)) { + err = assert_file_overwrite(filename); + if (err < 0) + return err; + } if (o->mux_preload) { av_dict_set_int(&mux->opts, "preload", o->mux_preload*AV_TIME_BASE, 0); @@ -2059,33 +2726,55 @@ int of_open(const OptionsContext *o, const char *filename) oc->max_delay = (int)(o->mux_max_delay * AV_TIME_BASE); /* copy metadata and chapters from input files */ - copy_meta(mux, o); + err = copy_meta(mux, o); + if (err < 0) + return err; + + err = of_add_programs(mux, o); + if (err < 0) + return err; - of_add_programs(oc, o); - of_add_metadata(of, oc, o); + err = of_add_metadata(of, oc, o); + if (err < 0) + return err; err = set_dispositions(mux, o); if (err < 0) { - av_log(NULL, AV_LOG_FATAL, "Error setting output stream dispositions\n"); - exit_program(1); + av_log(mux, AV_LOG_FATAL, "Error setting output stream dispositions\n"); + return err; } // parse forced keyframe specifications; // must be done after chapters are created err = process_forced_keyframes(mux, o); if (err < 0) { - av_log(NULL, AV_LOG_FATAL, "Error processing forced keyframes\n"); - exit_program(1); + av_log(mux, AV_LOG_FATAL, "Error processing forced keyframes\n"); + return err; } err = setup_sync_queues(mux, oc, o->shortest_buf_duration * AV_TIME_BASE); if (err < 0) { - av_log(NULL, AV_LOG_FATAL, "Error setting up output sync queues\n"); - exit_program(1); + av_log(mux, AV_LOG_FATAL, "Error setting up output sync queues\n"); + return err; } of->url = filename; + /* initialize stream copy and subtitle/data streams. + * Encoded AVFrame based streams will get initialized when the first AVFrame + * is received in do_video_out + */ + for (int i = 0; i < of->nb_streams; i++) { + OutputStream *ost = of->streams[i]; + + if (ost->filter) + continue; + + err = init_output_stream_nofilter(ost); + if (err < 0) + return err; + } + /* write the header for files with no streams */ if (of->format->flags & AVFMT_NOSTREAMS && oc->nb_streams == 0) { int ret = mux_check_init(mux); diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c index 3df02b7d7fa..dc6044120a0 100644 --- a/fftools/ffmpeg_opt.c +++ b/fftools/ffmpeg_opt.c @@ -80,7 +80,6 @@ int debug_ts = 0; int exit_on_error = 0; int abort_on_flags = 0; int print_stats = -1; -int qp_hist = 0; int stdin_interaction = 1; float max_error_rate = 2.0/3; char *filter_nbthreads; @@ -129,8 +128,9 @@ static void uninit_options(OptionsContext *o) #if FFMPEG_OPT_MAP_CHANNEL av_freep(&o->audio_channel_maps); #endif - av_freep(&o->streamid_map); av_freep(&o->attachments); + + av_dict_free(&o->streamid); } static void init_options(OptionsContext *o) @@ -190,11 +190,18 @@ int parse_and_set_vsync(const char *arg, int *vsync_var, int file_idx, int st_id else if (!is_global && !av_strcasecmp(arg, "auto")) *vsync_var = VSYNC_AUTO; else if (!is_global) { av_log(NULL, AV_LOG_FATAL, "Invalid value %s specified for fps_mode of #%d:%d.\n", arg, file_idx, st_idx); - exit_program(1); + return AVERROR(EINVAL); } if (is_global && *vsync_var == VSYNC_AUTO) { - video_sync_method = parse_number_or_die("vsync", arg, OPT_INT, VSYNC_AUTO, VSYNC_VFR); + int ret; + double num; + + ret = parse_number("vsync", arg, OPT_INT, VSYNC_AUTO, VSYNC_VFR, &num); + if (ret < 0) + return ret; + + video_sync_method = num; av_log(NULL, AV_LOG_WARNING, "Passing a number to -vsync is deprecated," " use a string argument as described in the manual.\n"); } @@ -250,12 +257,12 @@ static int apply_sync_offsets(void) if (self->input_sync_ref == -1 || self->input_sync_ref == i) continue; if (self->input_sync_ref >= nb_input_files || self->input_sync_ref < -1) { av_log(NULL, AV_LOG_FATAL, "-isync for input %d references non-existent input %d.\n", i, self->input_sync_ref); - exit_program(1); + return AVERROR(EINVAL); } if (copy_ts && !start_at_zero) { av_log(NULL, AV_LOG_FATAL, "Use of -isync requires that start_at_zero be set if copyts is set.\n"); - exit_program(1); + return AVERROR(EINVAL); } ref = input_files[self->input_sync_ref]; @@ -319,7 +326,10 @@ static int opt_abort_on(void *optctx, const char *opt, const char *arg) static int opt_stats_period(void *optctx, const char *opt, const char *arg) { - int64_t user_stats_period = parse_time_or_die(opt, arg, 1); + int64_t user_stats_period; + int ret = av_parse_time(&user_stats_period, arg, 1); + if (ret < 0) + return ret; if (user_stats_period <= 0) { av_log(NULL, AV_LOG_ERROR, "stats_period %s must be positive.\n", arg); @@ -361,9 +371,7 @@ static int opt_map(void *optctx, const char *opt, const char *arg) OptionsContext *o = optctx; StreamMap *m = NULL; int i, negative = 0, file_idx, disabled = 0; -#if FFMPEG_OPT_MAP_SYNC - char *sync; -#endif + int ret; char *map, *p; char *allow_unused; @@ -376,10 +384,14 @@ static int opt_map(void *optctx, const char *opt, const char *arg) return AVERROR(ENOMEM); #if FFMPEG_OPT_MAP_SYNC - /* parse sync stream first, just pick first matching stream */ - if (sync = strchr(map, ',')) { - *sync = 0; - av_log(NULL, AV_LOG_WARNING, "Specifying a sync stream is deprecated and has no effect\n"); + { + /* parse sync stream first, just pick first matching stream */ + char *sync = strchr(map, ','); + + if (sync) { + *sync = 0; + av_log(NULL, AV_LOG_WARNING, "Specifying a sync stream is deprecated and has no effect\n"); + } } #endif @@ -387,12 +399,17 @@ static int opt_map(void *optctx, const char *opt, const char *arg) if (map[0] == '[') { /* this mapping refers to lavfi output */ const char *c = map + 1; - GROW_ARRAY(o->stream_maps, o->nb_stream_maps); + + ret = GROW_ARRAY(o->stream_maps, o->nb_stream_maps); + if (ret < 0) + goto fail; + m = &o->stream_maps[o->nb_stream_maps - 1]; m->linklabel = av_get_token(&c, "]"); if (!m->linklabel) { av_log(NULL, AV_LOG_ERROR, "Invalid output link label: %s.\n", map); - exit_program(1); + ret = AVERROR(EINVAL); + goto fail; } } else { if (allow_unused = strchr(map, '?')) @@ -400,7 +417,8 @@ static int opt_map(void *optctx, const char *opt, const char *arg) file_idx = strtol(map, &p, 0); if (file_idx >= nb_input_files || file_idx < 0) { av_log(NULL, AV_LOG_FATAL, "Invalid input file index: %d.\n", file_idx); - exit_program(1); + ret = AVERROR(EINVAL); + goto fail; } if (negative) /* disable some already defined maps */ @@ -421,7 +439,10 @@ static int opt_map(void *optctx, const char *opt, const char *arg) disabled = 1; continue; } - GROW_ARRAY(o->stream_maps, o->nb_stream_maps); + ret = GROW_ARRAY(o->stream_maps, o->nb_stream_maps); + if (ret < 0) + goto fail; + m = &o->stream_maps[o->nb_stream_maps - 1]; m->file_index = file_idx; @@ -435,22 +456,28 @@ static int opt_map(void *optctx, const char *opt, const char *arg) } else if (disabled) { av_log(NULL, AV_LOG_FATAL, "Stream map '%s' matches disabled streams.\n" "To ignore this, add a trailing '?' to the map.\n", arg); - exit_program(1); + ret = AVERROR(EINVAL); + goto fail; } else { av_log(NULL, AV_LOG_FATAL, "Stream map '%s' matches no streams.\n" "To ignore this, add a trailing '?' to the map.\n", arg); - exit_program(1); + ret = AVERROR(EINVAL); + goto fail; } } - + ret = 0; +fail: av_freep(&map); - return 0; + return ret; } static int opt_attach(void *optctx, const char *opt, const char *arg) { OptionsContext *o = optctx; - GROW_ARRAY(o->attachments, o->nb_attachments); + int ret = GROW_ARRAY(o->attachments, o->nb_attachments); + if (ret < 0) + return ret; + o->attachments[o->nb_attachments - 1] = arg; return 0; } @@ -459,7 +486,7 @@ static int opt_attach(void *optctx, const char *opt, const char *arg) static int opt_map_channel(void *optctx, const char *opt, const char *arg) { OptionsContext *o = optctx; - int n; + int n, ret; AVStream *st; AudioChannelMap *m; char *allow_unused; @@ -474,7 +501,10 @@ static int opt_map_channel(void *optctx, const char *opt, const char *arg) if (!mapchan) return AVERROR(ENOMEM); - GROW_ARRAY(o->audio_channel_maps, o->nb_audio_channel_maps); + ret = GROW_ARRAY(o->audio_channel_maps, o->nb_audio_channel_maps); + if (ret < 0) + goto end; + m = &o->audio_channel_maps[o->nb_audio_channel_maps - 1]; /* muted channel syntax */ @@ -495,7 +525,7 @@ static int opt_map_channel(void *optctx, const char *opt, const char *arg) if (n != 3 && n != 5) { av_log(NULL, AV_LOG_FATAL, "Syntax error, mapchan usage: " "[file.stream.channel|-1][:syncfile:syncstream]\n"); - exit_program(1); + goto fail; } if (n != 5) // only file.stream.channel specified @@ -505,19 +535,19 @@ static int opt_map_channel(void *optctx, const char *opt, const char *arg) if (m->file_idx < 0 || m->file_idx >= nb_input_files) { av_log(NULL, AV_LOG_FATAL, "mapchan: invalid input file index: %d\n", m->file_idx); - exit_program(1); + goto fail; } if (m->stream_idx < 0 || m->stream_idx >= input_files[m->file_idx]->nb_streams) { av_log(NULL, AV_LOG_FATAL, "mapchan: invalid input file stream index #%d.%d\n", m->file_idx, m->stream_idx); - exit_program(1); + goto fail; } st = input_files[m->file_idx]->ctx->streams[m->stream_idx]; if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) { av_log(NULL, AV_LOG_FATAL, "mapchan: stream #%d.%d is not an audio stream.\n", m->file_idx, m->stream_idx); - exit_program(1); + goto fail; } /* allow trailing ? to map_channel */ if (allow_unused = strchr(mapchan, '?')) @@ -531,12 +561,17 @@ static int opt_map_channel(void *optctx, const char *opt, const char *arg) av_log(NULL, AV_LOG_FATAL, "mapchan: invalid audio channel #%d.%d.%d\n" "To ignore this, add a trailing '?' to the map_channel.\n", m->file_idx, m->stream_idx, m->channel_idx); - exit_program(1); + goto fail; } } + ret = 0; +end: av_free(mapchan); - return 0; + return ret; +fail: + ret = AVERROR(EINVAL); + goto end; } #endif @@ -588,7 +623,7 @@ static int opt_init_hw_device(void *optctx, const char *opt, const char *arg) AV_HWDEVICE_TYPE_NONE) printf("%s\n", av_hwdevice_get_type_name(type)); printf("\n"); - exit_program(0); + return AVERROR_EXIT; } else { return hw_device_init_from_string(arg, NULL); } @@ -612,8 +647,16 @@ static int opt_recording_timestamp(void *optctx, const char *opt, const char *ar { OptionsContext *o = optctx; char buf[128]; - int64_t recording_timestamp = parse_time_or_die(opt, arg, 0) / 1E6; - struct tm time = *gmtime((time_t*)&recording_timestamp); + int64_t recording_timestamp; + int ret; + struct tm time; + + ret = av_parse_time(&recording_timestamp, arg, 0); + if (ret < 0) + return ret; + + recording_timestamp /= 1e6; + time = *gmtime((time_t*)&recording_timestamp); if (!strftime(buf, sizeof(buf), "creation_time=%Y-%m-%dT%H:%M:%S%z", &time)) return -1; parse_option(o, "metadata", buf, options); @@ -623,7 +666,8 @@ static int opt_recording_timestamp(void *optctx, const char *opt, const char *ar return 0; } -const AVCodec *find_codec_or_die(const char *name, enum AVMediaType type, int encoder) +int find_codec(void *logctx, const char *name, + enum AVMediaType type, int encoder, const AVCodec **pcodec) { const AVCodecDescriptor *desc; const char *codec_string = encoder ? "encoder" : "decoder"; @@ -637,28 +681,31 @@ const AVCodec *find_codec_or_die(const char *name, enum AVMediaType type, int en codec = encoder ? avcodec_find_encoder(desc->id) : avcodec_find_decoder(desc->id); if (codec) - av_log(NULL, AV_LOG_VERBOSE, "Matched %s '%s' for codec '%s'.\n", + av_log(logctx, AV_LOG_VERBOSE, "Matched %s '%s' for codec '%s'.\n", codec_string, codec->name, desc->name); } if (!codec) { - av_log(NULL, AV_LOG_FATAL, "Unknown %s '%s'\n", codec_string, name); - exit_program(1); + av_log(logctx, AV_LOG_FATAL, "Unknown %s '%s'\n", codec_string, name); + return encoder ? AVERROR_ENCODER_NOT_FOUND : + AVERROR_DECODER_NOT_FOUND; } if (codec->type != type && !recast_media) { - av_log(NULL, AV_LOG_FATAL, "Invalid %s type '%s'\n", codec_string, name); - exit_program(1); + av_log(logctx, AV_LOG_FATAL, "Invalid %s type '%s'\n", codec_string, name); + return AVERROR(EINVAL); } - return codec; + + *pcodec = codec; + return 0;; } -void assert_file_overwrite(const char *filename) +int assert_file_overwrite(const char *filename) { const char *proto_name = avio_find_protocol_name(filename); if (file_overwrite && no_file_overwrite) { fprintf(stderr, "Error, both -y and -n supplied. Exiting.\n"); - exit_program(1); + return AVERROR(EINVAL); } if (!file_overwrite) { @@ -670,13 +717,13 @@ void assert_file_overwrite(const char *filename) signal(SIGINT, SIG_DFL); if (!read_yesno()) { av_log(NULL, AV_LOG_FATAL, "Not overwriting - exiting\n"); - exit_program(1); + return AVERROR_EXIT; } term_init(); } else { av_log(NULL, AV_LOG_FATAL, "File '%s' already exists. Exiting.\n", filename); - exit_program(1); + return AVERROR_EXIT; } } } @@ -689,10 +736,12 @@ void assert_file_overwrite(const char *filename) if (!strcmp(filename, file->ctx->url)) { av_log(NULL, AV_LOG_FATAL, "Output %s same as Input #%d - exiting\n", filename, i); av_log(NULL, AV_LOG_WARNING, "FFmpeg cannot edit existing files in-place.\n"); - exit_program(1); + return AVERROR(EINVAL); } } } + + return 0; } /* read file contents into a string */ @@ -725,7 +774,6 @@ char *file_read(const char *filename) static int opt_streamid(void *optctx, const char *opt, const char *arg) { OptionsContext *o = optctx; - int idx; char *p; char idx_str[16]; @@ -735,13 +783,11 @@ static int opt_streamid(void *optctx, const char *opt, const char *arg) av_log(NULL, AV_LOG_FATAL, "Invalid value '%s' for option '%s', required syntax is 'index:value'\n", arg, opt); - exit_program(1); + return AVERROR(EINVAL); } *p++ = '\0'; - idx = parse_number_or_die(opt, idx_str, OPT_INT, 0, MAX_STREAMS-1); - o->streamid_map = grow_array(o->streamid_map, sizeof(*o->streamid_map), &o->nb_streamid_map, idx+1); - o->streamid_map[idx] = parse_number_or_die(opt, p, OPT_INT, 0, INT_MAX); - return 0; + + return av_dict_set(&o->streamid, idx_str, p, 0); } static int init_complex_filters(void) @@ -802,7 +848,7 @@ static int opt_target(void *optctx, const char *opt, const char *arg) av_log(NULL, AV_LOG_FATAL, "Could not determine norm (PAL/NTSC/NTSC-Film) for target.\n"); av_log(NULL, AV_LOG_FATAL, "Please prefix target with \"pal-\", \"ntsc-\" or \"film-\",\n"); av_log(NULL, AV_LOG_FATAL, "or set a framerate with \"-r xxx\".\n"); - exit_program(1); + return AVERROR(EINVAL); } if (!strcmp(arg, "vcd")) { @@ -914,7 +960,7 @@ static int opt_vstats(void *optctx, const char *opt, const char *arg) if (!today) { // maybe tomorrow av_log(NULL, AV_LOG_FATAL, "Unable to get current time: %s\n", strerror(errno)); - exit_program(1); + return AVERROR(errno); } snprintf(filename, sizeof(filename), "vstats_%02d%02d%02d.log", today->tm_hour, today->tm_min, @@ -966,6 +1012,7 @@ static int opt_preset(void *optctx, const char *opt, const char *arg) FILE *f=NULL; char filename[1000], line[1000], tmp_line[1000]; const char *codec_name = NULL; + int ret = 0; tmp_line[0] = *opt; tmp_line[1] = 0; @@ -976,7 +1023,7 @@ static int opt_preset(void *optctx, const char *opt, const char *arg) av_log(NULL, AV_LOG_FATAL, "Please use -preset -qp 0\n"); }else av_log(NULL, AV_LOG_FATAL, "File for preset '%s' not found\n", arg); - exit_program(1); + return AVERROR(ENOENT); } while (fgets(line, sizeof(line), f)) { @@ -988,7 +1035,8 @@ static int opt_preset(void *optctx, const char *opt, const char *arg) if (!av_strtok(key, "=", &value) || !av_strtok(value, "\r\n", &endptr)) { av_log(NULL, AV_LOG_FATAL, "%s: Invalid syntax: '%s'\n", filename, line); - exit_program(1); + ret = AVERROR(EINVAL); + goto fail; } av_log(NULL, AV_LOG_DEBUG, "ffpreset[%s]: set '%s' = '%s'\n", filename, key, value); @@ -999,13 +1047,15 @@ static int opt_preset(void *optctx, const char *opt, const char *arg) else if (opt_default_new(o, key, value) < 0) { av_log(NULL, AV_LOG_FATAL, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n", filename, line, key, value); - exit_program(1); + ret = AVERROR(EINVAL); + goto fail; } } +fail: fclose(f); - return 0; + return ret; } static int opt_old2new(void *optctx, const char *opt, const char *arg) @@ -1080,8 +1130,7 @@ static int opt_audio_filters(void *optctx, const char *opt, const char *arg) static int opt_vsync(void *optctx, const char *opt, const char *arg) { av_log(NULL, AV_LOG_WARNING, "-vsync is deprecated. Use -fps_mode\n"); - parse_and_set_vsync(arg, &video_sync_method, -1, -1, 1); - return 0; + return parse_and_set_vsync(arg, &video_sync_method, -1, -1, 1); } static int opt_timecode(void *optctx, const char *opt, const char *arg) @@ -1106,28 +1155,20 @@ static int opt_audio_qscale(void *optctx, const char *opt, const char *arg) static int opt_filter_complex(void *optctx, const char *opt, const char *arg) { - FilterGraph *fg = ALLOC_ARRAY_ELEM(filtergraphs, nb_filtergraphs); - - fg->index = nb_filtergraphs - 1; - fg->graph_desc = av_strdup(arg); - if (!fg->graph_desc) + char *graph_desc = av_strdup(arg); + if (!graph_desc) return AVERROR(ENOMEM); - return 0; + return fg_create(NULL, graph_desc); } static int opt_filter_complex_script(void *optctx, const char *opt, const char *arg) { - FilterGraph *fg; char *graph_desc = file_read(arg); if (!graph_desc) return AVERROR(EINVAL); - fg = ALLOC_ARRAY_ELEM(filtergraphs, nb_filtergraphs); - fg->index = nb_filtergraphs - 1; - fg->graph_desc = graph_desc; - - return 0; + return fg_create(NULL, graph_desc); } void show_help_default(const char *opt, const char *arg) @@ -1256,6 +1297,7 @@ static int open_files(OptionGroupList *l, const char *inout, int ffmpeg_parse_options(int argc, char **argv) { OptionParseContext octx; + const char *errmsg = NULL; int ret; memset(&octx, 0, sizeof(octx)); @@ -1264,14 +1306,14 @@ int ffmpeg_parse_options(int argc, char **argv) ret = split_commandline(&octx, argc, argv, options, groups, FF_ARRAY_ELEMS(groups)); if (ret < 0) { - av_log(NULL, AV_LOG_FATAL, "Error splitting the argument list: "); + errmsg = "splitting the argument list"; goto fail; } /* apply global options */ ret = parse_optgroup(NULL, &octx.global_opts); if (ret < 0) { - av_log(NULL, AV_LOG_FATAL, "Error parsing global options: "); + errmsg = "parsing global options"; goto fail; } @@ -1281,34 +1323,39 @@ int ffmpeg_parse_options(int argc, char **argv) /* open input files */ ret = open_files(&octx.groups[GROUP_INFILE], "input", ifile_open); if (ret < 0) { - av_log(NULL, AV_LOG_FATAL, "Error opening input files: "); + errmsg = "opening input files"; goto fail; } /* create the complex filtergraphs */ ret = init_complex_filters(); if (ret < 0) { - av_log(NULL, AV_LOG_FATAL, "Error initializing complex filters.\n"); + errmsg = "initializing complex filters"; goto fail; } /* open output files */ ret = open_files(&octx.groups[GROUP_OUTFILE], "output", of_open); if (ret < 0) { - av_log(NULL, AV_LOG_FATAL, "Error opening output files: "); + errmsg = "opening output files"; goto fail; } correct_input_start_times(); - apply_sync_offsets(); + ret = apply_sync_offsets(); + if (ret < 0) + goto fail; - check_filter_outputs(); + ret = check_filter_outputs(); + if (ret < 0) + goto fail; fail: uninit_parse_context(&octx); - if (ret < 0) { - av_log(NULL, AV_LOG_FATAL, "%s\n", av_err2str(ret)); + if (ret < 0 && ret != AVERROR_EXIT) { + av_log(NULL, AV_LOG_FATAL, "Error %s: %s\n", + errmsg ? errmsg : "", av_err2str(ret)); } return ret; } @@ -1333,8 +1380,15 @@ static int opt_progress(void *optctx, const char *opt, const char *arg) int opt_timelimit(void *optctx, const char *opt, const char *arg) { #if HAVE_SETRLIMIT - int lim = parse_number_or_die(opt, arg, OPT_INT64, 0, INT_MAX); - struct rlimit rl = { lim, lim + 1 }; + int ret; + double lim; + struct rlimit rl; + + ret = parse_number(opt, arg, OPT_INT64, 0, INT_MAX, &lim); + if (ret < 0) + return ret; + + rl = (struct rlimit){ lim, lim + 1 }; if (setrlimit(RLIMIT_CPU, &rl)) perror("setrlimit"); #else @@ -1343,6 +1397,22 @@ int opt_timelimit(void *optctx, const char *opt, const char *arg) return 0; } +#if FFMPEG_OPT_QPHIST +static int opt_qphist(void *optctx, const char *opt, const char *arg) +{ + av_log(NULL, AV_LOG_WARNING, "Option -%s is deprecated and has no effect\n", opt); + return 0; +} +#endif + +#if FFMPEG_OPT_ADRIFT_THRESHOLD +static int opt_adrift_threshold(void *optctx, const char *opt, const char *arg) +{ + av_log(NULL, AV_LOG_WARNING, "Option -%s is deprecated and has no effect\n", opt); + return 0; +} +#endif + #define OFFSET(x) offsetof(OptionsContext, x) const OptionDef options[] = { /* main options */ @@ -1442,6 +1512,9 @@ const OptionDef options[] = { { "readrate", HAS_ARG | OPT_FLOAT | OPT_OFFSET | OPT_EXPERT | OPT_INPUT, { .off = OFFSET(readrate) }, "read input at specified rate", "speed" }, + { "readrate_initial_burst", HAS_ARG | OPT_DOUBLE | OPT_OFFSET | + OPT_EXPERT | OPT_INPUT, { .off = OFFSET(readrate_initial_burst) }, + "The initial amount of input to burst read before imposing any readrate", "seconds" }, { "target", HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_target }, "specify target file type (\"vcd\", \"svcd\", \"dvd\", \"dv\" or \"dv50\" " "with optional prefixes \"pal-\", \"ntsc-\" or \"film-\")", "type" }, @@ -1449,8 +1522,10 @@ const OptionDef options[] = { "set video sync method globally; deprecated, use -fps_mode", "" }, { "frame_drop_threshold", HAS_ARG | OPT_FLOAT | OPT_EXPERT, { &frame_drop_threshold }, "frame drop threshold", "" }, - { "adrift_threshold", HAS_ARG | OPT_FLOAT | OPT_EXPERT, { &audio_drift_threshold }, - "audio drift threshold", "threshold" }, +#if FFMPEG_OPT_ADRIFT_THRESHOLD + { "adrift_threshold", HAS_ARG | OPT_EXPERT, { .func_arg = opt_adrift_threshold }, + "deprecated, does nothing", "threshold" }, +#endif { "copyts", OPT_BOOL | OPT_EXPERT, { ©_ts }, "copy timestamps" }, { "start_at_zero", OPT_BOOL | OPT_EXPERT, { &start_at_zero }, @@ -1543,6 +1618,19 @@ const OptionDef options[] = { { .off = OFFSET(bits_per_raw_sample) }, "set the number of bits per raw sample", "number" }, + { "stats_enc_pre", HAS_ARG | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT | OPT_STRING, { .off = OFFSET(enc_stats_pre) }, + "write encoding stats before encoding" }, + { "stats_enc_post", HAS_ARG | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT | OPT_STRING, { .off = OFFSET(enc_stats_post) }, + "write encoding stats after encoding" }, + { "stats_mux_pre", HAS_ARG | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT | OPT_STRING, { .off = OFFSET(mux_stats) }, + "write packets stats before muxing" }, + { "stats_enc_pre_fmt", HAS_ARG | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT | OPT_STRING, { .off = OFFSET(enc_stats_pre_fmt) }, + "format of the stats written with -stats_enc_pre" }, + { "stats_enc_post_fmt", HAS_ARG | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT | OPT_STRING, { .off = OFFSET(enc_stats_post_fmt) }, + "format of the stats written with -stats_enc_post" }, + { "stats_mux_pre_fmt", HAS_ARG | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT | OPT_STRING, { .off = OFFSET(mux_stats_fmt) }, + "format of the stats written with -stats_mux_pre" }, + /* video options */ { "vframes", OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_video_frames }, "set the number of video frames to output", "number" }, @@ -1613,8 +1701,10 @@ const OptionDef options[] = { { "vtag", OPT_VIDEO | HAS_ARG | OPT_EXPERT | OPT_PERFILE | OPT_INPUT | OPT_OUTPUT, { .func_arg = opt_old2new }, "force video tag/fourcc", "fourcc/tag" }, - { "qphist", OPT_VIDEO | OPT_BOOL | OPT_EXPERT , { &qp_hist }, - "show QP histogram" }, +#if FFMPEG_OPT_QPHIST + { "qphist", OPT_VIDEO | OPT_EXPERT , { .func_arg = opt_qphist }, + "deprecated, does nothing" }, +#endif { "fps_mode", OPT_VIDEO | HAS_ARG | OPT_STRING | OPT_EXPERT | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(fps_mode) }, "set framerate mode for matching video streams; overrides vsync" }, @@ -1627,8 +1717,6 @@ const OptionDef options[] = { { "force_key_frames", OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(forced_key_frames) }, "force key frames at specified timestamps", "timestamps" }, - { "ab", OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_bitrate }, - "audio bitrate (please use -b:a)", "bitrate" }, { "b", OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_bitrate }, "video bitrate (please use -b:v)", "bitrate" }, { "hwaccel", OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT | @@ -1648,6 +1736,11 @@ const OptionDef options[] = { { "autoscale", HAS_ARG | OPT_BOOL | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT, { .off = OFFSET(autoscale) }, "automatically insert a scale filter at the end of the filter graph" }, + { "fix_sub_duration_heartbeat", OPT_VIDEO | OPT_BOOL | OPT_EXPERT | + OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(fix_sub_duration_heartbeat) }, + "set this video output stream to be a heartbeat stream for " + "fix_sub_duration, according to which subtitles should be split at " + "random access points" }, /* audio options */ { "aframes", OPT_AUDIO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_audio_frames }, @@ -1665,6 +1758,8 @@ const OptionDef options[] = { { "acodec", OPT_AUDIO | HAS_ARG | OPT_PERFILE | OPT_INPUT | OPT_OUTPUT, { .func_arg = opt_audio_codec }, "force audio codec ('copy' to copy stream)", "codec" }, + { "ab", OPT_AUDIO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_bitrate }, + "audio bitrate (please use -b:a)", "bitrate" }, { "atag", OPT_AUDIO | HAS_ARG | OPT_EXPERT | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_old2new }, "force audio tag/fourcc", "fourcc/tag" }, @@ -1739,7 +1834,7 @@ const OptionDef options[] = { #if CONFIG_VAAPI { "vaapi_device", HAS_ARG | OPT_EXPERT, { .func_arg = opt_vaapi_device }, - "set VAAPI hardware device (DRM path or X11 display name)", "device" }, + "set VAAPI hardware device (DirectX adapter index, DRM path or X11 display name)", "device" }, #endif #if CONFIG_QSV diff --git a/fftools/ffplay.c b/fftools/ffplay.c index d6479aef5f7..5212ad053e8 100644 --- a/fftools/ffplay.c +++ b/fftools/ffplay.c @@ -50,11 +50,9 @@ #include "libavcodec/avfft.h" #include "libswresample/swresample.h" -#if CONFIG_AVFILTER -# include "libavfilter/avfilter.h" -# include "libavfilter/buffersink.h" -# include "libavfilter/buffersrc.h" -#endif +#include "libavfilter/avfilter.h" +#include "libavfilter/buffersink.h" +#include "libavfilter/buffersrc.h" #include #include @@ -109,8 +107,6 @@ const int program_birth_year = 2003; #define USE_ONEPASS_SUBTITLE_RENDER 1 -static unsigned sws_flags = SWS_BICUBIC; - typedef struct MyAVPacketList { AVPacket *pkt; int serial; @@ -150,6 +146,10 @@ typedef struct Clock { int *queue_serial; /* pointer to the current packet queue serial, used for obsolete clock detection */ } Clock; +typedef struct FrameData { + int64_t pkt_pos; +} FrameData; + /* Common struct for handling all types of decoded data and allocated render buffers. */ typedef struct Frame { AVFrame *frame; @@ -250,9 +250,7 @@ typedef struct VideoState { int audio_volume; int muted; struct AudioParams audio_src; -#if CONFIG_AVFILTER struct AudioParams audio_filter_src; -#endif struct AudioParams audio_tgt; struct SwrContext *swr_ctx; int frame_drops_early; @@ -284,7 +282,6 @@ typedef struct VideoState { AVStream *video_st; PacketQueue videoq; double max_frame_duration; // maximum duration of a frame - above this, we consider the jump a timestamp discontinuity - struct SwsContext *img_convert_ctx; struct SwsContext *sub_convert_ctx; int eof; @@ -292,14 +289,12 @@ typedef struct VideoState { int width, height, xleft, ytop; int step; -#if CONFIG_AVFILTER int vfilter_idx; AVFilterContext *in_video_filter; // the first filter in the video chain AVFilterContext *out_video_filter; // the last filter in the video chain AVFilterContext *in_audio_filter; // the first filter in the audio chain AVFilterContext *out_audio_filter; // the last filter in the audio chain AVFilterGraph *agraph; // audio filter graph -#endif int last_video_stream, last_audio_stream, last_subtitle_stream; @@ -347,11 +342,9 @@ static const char *video_codec_name; double rdftspeed = 0.02; static int64_t cursor_last_shown; static int cursor_hidden = 0; -#if CONFIG_AVFILTER static const char **vfilters_list = NULL; static int nb_vfilters = 0; static char *afilters = NULL; -#endif static int autorotate = 1; static int find_stream_info = 1; static int filter_nbthreads = 0; @@ -393,14 +386,15 @@ static const struct TextureFormatEntry { { AV_PIX_FMT_NONE, SDL_PIXELFORMAT_UNKNOWN }, }; -#if CONFIG_AVFILTER static int opt_add_vfilter(void *optctx, const char *opt, const char *arg) { - GROW_ARRAY(vfilters_list, nb_vfilters); + int ret = GROW_ARRAY(vfilters_list, nb_vfilters); + if (ret < 0) + return ret; + vfilters_list[nb_vfilters - 1] = arg; return 0; } -#endif static inline int cmp_audio_fmts(enum AVSampleFormat fmt1, int64_t channel_count1, @@ -653,6 +647,16 @@ static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) { } av_packet_unref(d->pkt); } else { + if (d->pkt->buf && !d->pkt->opaque_ref) { + FrameData *fd; + + d->pkt->opaque_ref = av_buffer_allocz(sizeof(*fd)); + if (!d->pkt->opaque_ref) + return AVERROR(ENOMEM); + fd = (FrameData*)d->pkt->opaque_ref->data; + fd->pkt_pos = d->pkt->pos; + } + if (avcodec_send_packet(d->avctx, d->pkt) == AVERROR(EAGAIN)) { av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n"); d->packet_pending = 1; @@ -695,7 +699,7 @@ static int frame_queue_init(FrameQueue *f, PacketQueue *pktq, int max_size, int return 0; } -static void frame_queue_destory(FrameQueue *f) +static void frame_queue_destroy(FrameQueue *f) { int i; for (i = 0; i < f->max_size; i++) { @@ -891,7 +895,8 @@ static void get_sdl_pix_fmt_and_blendmode(int format, Uint32 *sdl_pix_fmt, SDL_B } } -static int upload_texture(SDL_Texture **tex, AVFrame *frame, struct SwsContext **img_convert_ctx) { +static int upload_texture(SDL_Texture **tex, AVFrame *frame) +{ int ret = 0; Uint32 sdl_pix_fmt; SDL_BlendMode sdl_blendmode; @@ -899,24 +904,6 @@ static int upload_texture(SDL_Texture **tex, AVFrame *frame, struct SwsContext * if (realloc_texture(tex, sdl_pix_fmt == SDL_PIXELFORMAT_UNKNOWN ? SDL_PIXELFORMAT_ARGB8888 : sdl_pix_fmt, frame->width, frame->height, sdl_blendmode, 0) < 0) return -1; switch (sdl_pix_fmt) { - case SDL_PIXELFORMAT_UNKNOWN: - /* This should only happen if we are not using avfilter... */ - *img_convert_ctx = sws_getCachedContext(*img_convert_ctx, - frame->width, frame->height, frame->format, frame->width, frame->height, - AV_PIX_FMT_BGRA, sws_flags, NULL, NULL, NULL); - if (*img_convert_ctx != NULL) { - uint8_t *pixels[4]; - int pitch[4]; - if (!SDL_LockTexture(*tex, NULL, (void **)pixels, pitch)) { - sws_scale(*img_convert_ctx, (const uint8_t * const *)frame->data, frame->linesize, - 0, frame->height, pixels, pitch); - SDL_UnlockTexture(*tex); - } - } else { - av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context\n"); - ret = -1; - } - break; case SDL_PIXELFORMAT_IYUV: if (frame->linesize[0] > 0 && frame->linesize[1] > 0 && frame->linesize[2] > 0) { ret = SDL_UpdateYUVTexture(*tex, NULL, frame->data[0], frame->linesize[0], @@ -1014,7 +1001,7 @@ static void video_image_display(VideoState *is) set_sdl_yuv_conversion_mode(vp->frame); if (!vp->uploaded) { - if (upload_texture(&is->vid_texture, vp->frame, &is->img_convert_ctx) < 0) { + if (upload_texture(&is->vid_texture, vp->frame) < 0) { set_sdl_yuv_conversion_mode(NULL); return; } @@ -1268,11 +1255,10 @@ static void stream_close(VideoState *is) packet_queue_destroy(&is->subtitleq); /* free all pictures */ - frame_queue_destory(&is->pictq); - frame_queue_destory(&is->sampq); - frame_queue_destory(&is->subpq); + frame_queue_destroy(&is->pictq); + frame_queue_destroy(&is->sampq); + frame_queue_destroy(&is->subpq); SDL_DestroyCond(is->continue_read_thread); - sws_freeContext(is->img_convert_ctx); sws_freeContext(is->sub_convert_ctx); av_free(is->filename); if (is->vis_texture) @@ -1294,9 +1280,7 @@ static void do_exit(VideoState *is) if (window) SDL_DestroyWindow(window); uninit_opts(); -#if CONFIG_AVFILTER av_freep(&vfilters_list); -#endif avformat_network_deinit(); if (show_status) printf("\n"); @@ -1553,7 +1537,8 @@ static double vp_duration(VideoState *is, Frame *vp, Frame *nextvp) { } } -static void update_video_pts(VideoState *is, double pts, int64_t pos, int serial) { +static void update_video_pts(VideoState *is, double pts, int serial) +{ /* update current video pts */ set_clock(&is->vidclk, pts, serial); sync_clock_to_slave(&is->extclk, &is->vidclk); @@ -1618,7 +1603,7 @@ static void video_refresh(void *opaque, double *remaining_time) SDL_LockMutex(is->pictq.mutex); if (!isnan(vp->pts)) - update_video_pts(is, vp->pts, vp->pos, vp->serial); + update_video_pts(is, vp->pts, vp->serial); SDL_UnlockMutex(is->pictq.mutex); if (frame_queue_nb_remaining(&is->pictq) > 1) { @@ -1793,7 +1778,6 @@ static int get_video_frame(VideoState *is, AVFrame *frame) return got_picture; } -#if CONFIG_AVFILTER static int configure_filtergraph(AVFilterGraph *graph, const char *filtergraph, AVFilterContext *source_ctx, AVFilterContext *sink_ctx) { @@ -2021,17 +2005,14 @@ static int configure_audio_filters(VideoState *is, const char *afilters, int for return ret; } -#endif /* CONFIG_AVFILTER */ static int audio_thread(void *arg) { VideoState *is = arg; AVFrame *frame = av_frame_alloc(); Frame *af; -#if CONFIG_AVFILTER int last_serial = -1; int reconfigure; -#endif int got_frame = 0; AVRational tb; int ret = 0; @@ -2046,7 +2027,6 @@ static int audio_thread(void *arg) if (got_frame) { tb = (AVRational){1, frame->sample_rate}; -#if CONFIG_AVFILTER reconfigure = cmp_audio_fmts(is->audio_filter_src.fmt, is->audio_filter_src.ch_layout.nb_channels, frame->format, frame->ch_layout.nb_channels) || @@ -2078,32 +2058,28 @@ static int audio_thread(void *arg) goto the_end; while ((ret = av_buffersink_get_frame_flags(is->out_audio_filter, frame, 0)) >= 0) { + FrameData *fd = frame->opaque_ref ? (FrameData*)frame->opaque_ref->data : NULL; tb = av_buffersink_get_time_base(is->out_audio_filter); -#endif if (!(af = frame_queue_peek_writable(&is->sampq))) goto the_end; af->pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb); - af->pos = frame->pkt_pos; + af->pos = fd ? fd->pkt_pos : -1; af->serial = is->auddec.pkt_serial; af->duration = av_q2d((AVRational){frame->nb_samples, frame->sample_rate}); av_frame_move_ref(af->frame, frame); frame_queue_push(&is->sampq); -#if CONFIG_AVFILTER if (is->audioq.serial != is->auddec.pkt_serial) break; } if (ret == AVERROR_EOF) is->auddec.finished = is->auddec.pkt_serial; -#endif } } while (ret >= 0 || ret == AVERROR(EAGAIN) || ret == AVERROR_EOF); the_end: -#if CONFIG_AVFILTER avfilter_graph_free(&is->agraph); -#endif av_frame_free(&frame); return ret; } @@ -2129,7 +2105,6 @@ static int video_thread(void *arg) AVRational tb = is->video_st->time_base; AVRational frame_rate = av_guess_frame_rate(is->ic, is->video_st, NULL); -#if CONFIG_AVFILTER AVFilterGraph *graph = NULL; AVFilterContext *filt_out = NULL, *filt_in = NULL; int last_w = 0; @@ -2137,7 +2112,6 @@ static int video_thread(void *arg) enum AVPixelFormat last_format = -2; int last_serial = -1; int last_vfilter_idx = 0; -#endif if (!frame) return AVERROR(ENOMEM); @@ -2149,7 +2123,6 @@ static int video_thread(void *arg) if (!ret) continue; -#if CONFIG_AVFILTER if ( last_w != frame->width || last_h != frame->height || last_format != frame->format @@ -2190,6 +2163,8 @@ static int video_thread(void *arg) goto the_end; while (ret >= 0) { + FrameData *fd; + is->frame_last_returned_time = av_gettime_relative() / 1000000.0; ret = av_buffersink_get_frame_flags(filt_out, frame, 0); @@ -2200,28 +2175,25 @@ static int video_thread(void *arg) break; } + fd = frame->opaque_ref ? (FrameData*)frame->opaque_ref->data : NULL; + is->frame_last_filter_delay = av_gettime_relative() / 1000000.0 - is->frame_last_returned_time; if (fabs(is->frame_last_filter_delay) > AV_NOSYNC_THRESHOLD / 10.0) is->frame_last_filter_delay = 0; tb = av_buffersink_get_time_base(filt_out); -#endif duration = (frame_rate.num && frame_rate.den ? av_q2d((AVRational){frame_rate.den, frame_rate.num}) : 0); pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb); - ret = queue_picture(is, frame, pts, duration, frame->pkt_pos, is->viddec.pkt_serial); + ret = queue_picture(is, frame, pts, duration, fd ? fd->pkt_pos : -1, is->viddec.pkt_serial); av_frame_unref(frame); -#if CONFIG_AVFILTER if (is->videoq.serial != is->viddec.pkt_serial) break; } -#endif if (ret < 0) goto the_end; } the_end: -#if CONFIG_AVFILTER avfilter_graph_free(&graph); -#endif av_frame_free(&frame); return 0; } @@ -2612,11 +2584,18 @@ static int stream_component_open(VideoState *is, int stream_index) if (fast) avctx->flags2 |= AV_CODEC_FLAG2_FAST; - opts = filter_codec_opts(codec_opts, avctx->codec_id, ic, ic->streams[stream_index], codec); + ret = filter_codec_opts(codec_opts, avctx->codec_id, ic, + ic->streams[stream_index], codec, &opts); + if (ret < 0) + goto fail; + if (!av_dict_get(opts, "threads", NULL, 0)) av_dict_set(&opts, "threads", "auto", 0); if (stream_lowres) av_dict_set_int(&opts, "lowres", stream_lowres, 0); + + av_dict_set(&opts, "flags", "+copy_opaque", AV_DICT_MULTIKEY); + if ((ret = avcodec_open2(avctx, codec, &opts)) < 0) { goto fail; } @@ -2630,7 +2609,6 @@ static int stream_component_open(VideoState *is, int stream_index) ic->streams[stream_index]->discard = AVDISCARD_DEFAULT; switch (avctx->codec_type) { case AVMEDIA_TYPE_AUDIO: -#if CONFIG_AVFILTER { AVFilterContext *sink; @@ -2647,12 +2625,6 @@ static int stream_component_open(VideoState *is, int stream_index) if (ret < 0) goto fail; } -#else - sample_rate = avctx->sample_rate; - ret = av_channel_layout_copy(&ch_layout, &avctx->ch_layout); - if (ret < 0) - goto fail; -#endif /* prepare audio output */ if ((ret = audio_open(is, &ch_layout, sample_rate, &is->audio_tgt)) < 0) @@ -2674,7 +2646,7 @@ static int stream_component_open(VideoState *is, int stream_index) if ((ret = decoder_init(&is->auddec, avctx, &is->audioq, is->continue_read_thread)) < 0) goto fail; - if ((is->ic->iformat->flags & (AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK)) && !is->ic->iformat->read_seek) { + if (is->ic->iformat->flags & AVFMT_NOTIMESTAMPS) { is->auddec.start_pts = is->audio_st->start_time; is->auddec.start_pts_tb = is->audio_st->time_base; } @@ -2808,9 +2780,17 @@ static int read_thread(void *arg) av_format_inject_global_side_data(ic); if (find_stream_info) { - AVDictionary **opts = setup_find_stream_info_opts(ic, codec_opts); + AVDictionary **opts; int orig_nb_streams = ic->nb_streams; + err = setup_find_stream_info_opts(ic, codec_opts, &opts); + if (err < 0) { + av_log(NULL, AV_LOG_ERROR, + "Error setting up avformat_find_stream_info() options\n"); + ret = err; + goto fail; + } + err = avformat_find_stream_info(ic, opts); for (i = 0; i < orig_nb_streams; i++) @@ -3326,7 +3306,6 @@ static void event_loop(VideoState *cur_stream) stream_cycle_channel(cur_stream, AVMEDIA_TYPE_SUBTITLE); break; case SDLK_w: -#if CONFIG_AVFILTER if (cur_stream->show_mode == SHOW_MODE_VIDEO && cur_stream->vfilter_idx < nb_vfilters - 1) { if (++cur_stream->vfilter_idx >= nb_vfilters) cur_stream->vfilter_idx = 0; @@ -3334,9 +3313,6 @@ static void event_loop(VideoState *cur_stream) cur_stream->vfilter_idx = 0; toggle_audio_display(cur_stream); } -#else - toggle_audio_display(cur_stream); -#endif break; case SDLK_PAGEUP: if (cur_stream->ic->nb_chapters <= 1) { @@ -3472,13 +3448,23 @@ static void event_loop(VideoState *cur_stream) static int opt_width(void *optctx, const char *opt, const char *arg) { - screen_width = parse_number_or_die(opt, arg, OPT_INT64, 1, INT_MAX); + double num; + int ret = parse_number(opt, arg, OPT_INT64, 1, INT_MAX, &num); + if (ret < 0) + return ret; + + screen_width = num; return 0; } static int opt_height(void *optctx, const char *opt, const char *arg) { - screen_height = parse_number_or_die(opt, arg, OPT_INT64, 1, INT_MAX); + double num; + int ret = parse_number(opt, arg, OPT_INT64, 1, INT_MAX, &num); + if (ret < 0) + return ret; + + screen_height = num; return 0; } @@ -3507,38 +3493,35 @@ static int opt_sync(void *optctx, const char *opt, const char *arg) return 0; } -static int opt_seek(void *optctx, const char *opt, const char *arg) -{ - start_time = parse_time_or_die(opt, arg, 1); - return 0; -} - -static int opt_duration(void *optctx, const char *opt, const char *arg) -{ - duration = parse_time_or_die(opt, arg, 1); - return 0; -} - static int opt_show_mode(void *optctx, const char *opt, const char *arg) { show_mode = !strcmp(arg, "video") ? SHOW_MODE_VIDEO : !strcmp(arg, "waves") ? SHOW_MODE_WAVES : - !strcmp(arg, "rdft" ) ? SHOW_MODE_RDFT : - parse_number_or_die(opt, arg, OPT_INT, 0, SHOW_MODE_NB-1); + !strcmp(arg, "rdft" ) ? SHOW_MODE_RDFT : SHOW_MODE_NONE; + + if (show_mode == SHOW_MODE_NONE) { + double num; + int ret = parse_number(opt, arg, OPT_INT, 0, SHOW_MODE_NB-1, &num); + if (ret < 0) + return ret; + show_mode = num; + } return 0; } -static void opt_input_file(void *optctx, const char *filename) +static int opt_input_file(void *optctx, const char *filename) { if (input_filename) { av_log(NULL, AV_LOG_FATAL, "Argument '%s' provided as input filename, but '%s' was already specified.\n", filename, input_filename); - exit(1); + return AVERROR(EINVAL); } if (!strcmp(filename, "-")) filename = "fd:"; input_filename = filename; + + return 0; } static int opt_codec(void *optctx, const char *opt, const char *arg) @@ -3576,8 +3559,8 @@ static const OptionDef options[] = { { "ast", OPT_STRING | HAS_ARG | OPT_EXPERT, { &wanted_stream_spec[AVMEDIA_TYPE_AUDIO] }, "select desired audio stream", "stream_specifier" }, { "vst", OPT_STRING | HAS_ARG | OPT_EXPERT, { &wanted_stream_spec[AVMEDIA_TYPE_VIDEO] }, "select desired video stream", "stream_specifier" }, { "sst", OPT_STRING | HAS_ARG | OPT_EXPERT, { &wanted_stream_spec[AVMEDIA_TYPE_SUBTITLE] }, "select desired subtitle stream", "stream_specifier" }, - { "ss", HAS_ARG, { .func_arg = opt_seek }, "seek to a given position in seconds", "pos" }, - { "t", HAS_ARG, { .func_arg = opt_duration }, "play \"duration\" seconds of audio/video", "duration" }, + { "ss", HAS_ARG | OPT_TIME, { &start_time }, "seek to a given position in seconds", "pos" }, + { "t", HAS_ARG | OPT_TIME, { &duration }, "play \"duration\" seconds of audio/video", "duration" }, { "bytes", OPT_INT | HAS_ARG, { &seek_by_bytes }, "seek by bytes 0=off 1=on -1=auto", "val" }, { "seek_interval", OPT_FLOAT | HAS_ARG, { &seek_interval }, "set seek interval for left/right keys, in seconds", "seconds" }, { "nodisp", OPT_BOOL, { &display_disable }, "disable graphical display" }, @@ -3600,10 +3583,8 @@ static const OptionDef options[] = { { "window_title", OPT_STRING | HAS_ARG, { &window_title }, "set window title", "window title" }, { "left", OPT_INT | HAS_ARG | OPT_EXPERT, { &screen_left }, "set the x position for the left of the window", "x pos" }, { "top", OPT_INT | HAS_ARG | OPT_EXPERT, { &screen_top }, "set the y position for the top of the window", "y pos" }, -#if CONFIG_AVFILTER { "vf", OPT_EXPERT | HAS_ARG, { .func_arg = opt_add_vfilter }, "set video filters", "filter_graph" }, { "af", OPT_STRING | HAS_ARG, { &afilters }, "set audio filters", "filter_graph" }, -#endif { "rdftspeed", OPT_INT | HAS_ARG| OPT_AUDIO | OPT_EXPERT, { &rdftspeed }, "rdft speed", "msecs" }, { "showmode", HAS_ARG, { .func_arg = opt_show_mode}, "select show mode (0 = video, 1 = waves, 2 = RDFT)", "mode" }, { "i", OPT_BOOL, { &dummy}, "read specified file", "input_file"}, @@ -3634,11 +3615,7 @@ void show_help_default(const char *opt, const char *arg) printf("\n"); show_help_children(avcodec_get_class(), AV_OPT_FLAG_DECODING_PARAM); show_help_children(avformat_get_class(), AV_OPT_FLAG_DECODING_PARAM); -#if !CONFIG_AVFILTER - show_help_children(sws_get_class(), AV_OPT_FLAG_ENCODING_PARAM); -#else show_help_children(avfilter_get_class(), AV_OPT_FLAG_FILTERING_PARAM); -#endif printf("\nWhile playing:\n" "q, ESC quit\n" "f toggle full screen\n" @@ -3663,7 +3640,7 @@ void show_help_default(const char *opt, const char *arg) /* Called from the main */ int main(int argc, char **argv) { - int flags; + int flags, ret; VideoState *is; init_dynload(); @@ -3682,7 +3659,9 @@ int main(int argc, char **argv) show_banner(argc, argv, options); - parse_options(NULL, argc, argv, options, opt_input_file); + ret = parse_options(NULL, argc, argv, options, opt_input_file); + if (ret < 0) + exit(ret == AVERROR_EXIT ? 0 : 1); if (!input_filename) { show_usage(); diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c index b9310a2d251..4fcfe1164bc 100644 --- a/fftools/ffprobe.c +++ b/fftools/ffprobe.c @@ -79,6 +79,12 @@ # define pthread_mutex_unlock(a) do{}while(0) #endif +// attached as opaque_ref to packets/frames +typedef struct FrameData { + int64_t pkt_pos; + int pkt_size; +} FrameData; + typedef struct InputStream { AVStream *st; @@ -155,22 +161,6 @@ static int find_stream_info = 1; #define SECTION_MAX_NB_CHILDREN 10 -struct section { - int id; ///< unique id identifying a section - const char *name; - -#define SECTION_FLAG_IS_WRAPPER 1 ///< the section only contains other sections, but has no data at its own level -#define SECTION_FLAG_IS_ARRAY 2 ///< the section contains an array of elements of the same type -#define SECTION_FLAG_HAS_VARIABLE_FIELDS 4 ///< the section may contain a variable number of fields with variable keys. - /// For these sections the element_name field is mandatory. - int flags; - int children_ids[SECTION_MAX_NB_CHILDREN+1]; ///< list of children section IDS, terminated by -1 - const char *element_name; ///< name of the contained element, if provided - const char *unique_name; ///< unique section name, in case the name is ambiguous - AVDictionary *entries_to_show; - int show_all_entries; -}; - typedef enum { SECTION_ID_NONE = -1, SECTION_ID_CHAPTER, @@ -223,6 +213,22 @@ typedef enum { SECTION_ID_SUBTITLE, } SectionID; +struct section { + int id; ///< unique id identifying a section + const char *name; + +#define SECTION_FLAG_IS_WRAPPER 1 ///< the section only contains other sections, but has no data at its own level +#define SECTION_FLAG_IS_ARRAY 2 ///< the section contains an array of elements of the same type +#define SECTION_FLAG_HAS_VARIABLE_FIELDS 4 ///< the section may contain a variable number of fields with variable keys. + /// For these sections the element_name field is mandatory. + int flags; + const SectionID children_ids[SECTION_MAX_NB_CHILDREN+1]; ///< list of children section IDS, terminated by -1 + const char *element_name; ///< name of the contained element, if provided + const char *unique_name; ///< unique section name, in case the name is ambiguous + AVDictionary *entries_to_show; + int show_all_entries; +}; + static struct section sections[] = { [SECTION_ID_CHAPTERS] = { SECTION_ID_CHAPTERS, "chapters", SECTION_FLAG_IS_ARRAY, { SECTION_ID_CHAPTER, -1 } }, [SECTION_ID_CHAPTER] = { SECTION_ID_CHAPTER, "chapter", 0, { SECTION_ID_CHAPTER_TAGS, -1 } }, @@ -375,17 +381,6 @@ static void log_callback(void *ptr, int level, const char *fmt, va_list vl) #endif } -static void ffprobe_cleanup(int ret) -{ - int i; - for (i = 0; i < FF_ARRAY_ELEMS(sections); i++) - av_dict_free(&(sections[i].entries_to_show)); - -#if HAVE_THREADS - pthread_mutex_destroy(&log_mutex); -#endif -} - struct unit_value { union { double d; long long int i; } val; const char *unit; @@ -2221,8 +2216,8 @@ static void print_dynamic_hdr_vivid(WriterContext *w, const AVDynamicHDRVivid *m const AVHDRVividColorTransformParams *params = &metadata->params[n]; print_int("tone_mapping_mode_flag", params->tone_mapping_mode_flag); - print_int("tone_mapping_param_num", params->tone_mapping_param_num); if (params->tone_mapping_mode_flag) { + print_int("tone_mapping_param_num", params->tone_mapping_param_num); for (int i = 0; i < params->tone_mapping_param_num; i++) { const AVHDRVividColorToneMappingParams *tm_params = ¶ms->tm_params[i]; @@ -2246,14 +2241,16 @@ static void print_dynamic_hdr_vivid(WriterContext *w, const AVDynamicHDRVivid *m print_int("3Spline_enable_flag", tm_params->three_Spline_enable_flag); if (tm_params->three_Spline_enable_flag) { print_int("3Spline_num", tm_params->three_Spline_num); - print_int("3Spline_TH_mode", tm_params->three_Spline_TH_mode); for (int j = 0; j < tm_params->three_Spline_num; j++) { - print_q("3Spline_TH_enable_MB", tm_params->three_Spline_TH_enable_MB, '/'); - print_q("3Spline_TH_enable", tm_params->three_Spline_TH_enable, '/'); - print_q("3Spline_TH_Delta1", tm_params->three_Spline_TH_Delta1, '/'); - print_q("3Spline_TH_Delta2", tm_params->three_Spline_TH_Delta2, '/'); - print_q("3Spline_enable_Strength", tm_params->three_Spline_enable_Strength, '/'); + const AVHDRVivid3SplineParams *three_spline = &tm_params->three_spline[j]; + print_int("3Spline_TH_mode", three_spline->th_mode); + if (three_spline->th_mode == 0 || three_spline->th_mode == 2) + print_q("3Spline_TH_enable_MB", three_spline->th_enable_mb, '/'); + print_q("3Spline_TH_enable", three_spline->th_enable, '/'); + print_q("3Spline_TH_Delta1", three_spline->th_delta1, '/'); + print_q("3Spline_TH_Delta2", three_spline->th_delta2, '/'); + print_q("3Spline_enable_Strength", three_spline->enable_strength, '/'); } } } @@ -2352,6 +2349,9 @@ static void print_pkt_side_data(WriterContext *w, AVContentLightMetadata *metadata = (AVContentLightMetadata *)sd->data; print_int("max_content", metadata->MaxCLL); print_int("max_average", metadata->MaxFALL); + } else if (sd->type == AV_PKT_DATA_DYNAMIC_HDR10_PLUS) { + AVDynamicHDRPlus *metadata = (AVDynamicHDRPlus *)sd->data; + print_dynamic_hdr10_plus(w, metadata); } else if (sd->type == AV_PKT_DATA_DOVI_CONF) { AVDOVIDecoderConfigurationRecord *dovi = (AVDOVIDecoderConfigurationRecord *)sd->data; print_int("dv_version_major", dovi->dv_version_major); @@ -2387,6 +2387,19 @@ static void print_pkt_side_data(WriterContext *w, writer_print_section_footer(w); } +static void print_private_data(WriterContext *w, void *priv_data) +{ + const AVOption *opt = NULL; + while (opt = av_opt_next(priv_data, opt)) { + uint8_t *str; + if (!(opt->flags & AV_OPT_FLAG_EXPORT)) continue; + if (av_opt_get(priv_data, opt->name, 0, &str) >= 0) { + print_str(opt->name, str); + av_free(str); + } + } +} + static void print_color_range(WriterContext *w, enum AVColorRange color_range) { const char *val = av_color_range_name(color_range); @@ -2437,7 +2450,6 @@ static void print_chroma_location(WriterContext *w, enum AVChromaLocation chroma } } - static void clear_log(int need_lock) { int i; @@ -2513,8 +2525,12 @@ static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int p print_val("size", pkt->size, unit_byte_str); if (pkt->pos != -1) print_fmt ("pos", "%"PRId64, pkt->pos); else print_str_opt("pos", "N/A"); - print_fmt("flags", "%c%c", pkt->flags & AV_PKT_FLAG_KEY ? 'K' : '_', - pkt->flags & AV_PKT_FLAG_DISCARD ? 'D' : '_'); + print_fmt("flags", "%c%c%c", pkt->flags & AV_PKT_FLAG_KEY ? 'K' : '_', + pkt->flags & AV_PKT_FLAG_DISCARD ? 'D' : '_', + pkt->flags & AV_PKT_FLAG_CORRUPT ? 'C' : '_'); + if (do_show_data) + writer_print_data(w, "data", pkt->data, pkt->size); + writer_print_data_hash(w, "data_hash", pkt->data, pkt->size); if (pkt->side_data_elems) { size_t size; @@ -2533,9 +2549,6 @@ static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int p SECTION_ID_PACKET_SIDE_DATA); } - if (do_show_data) - writer_print_data(w, "data", pkt->data, pkt->size); - writer_print_data_hash(w, "data_hash", pkt->data, pkt->size); writer_print_section_footer(w); av_bprint_finalize(&pbuf, NULL); @@ -2568,6 +2581,7 @@ static void show_subtitle(WriterContext *w, AVSubtitle *sub, AVStream *stream, static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream, AVFormatContext *fmt_ctx) { + FrameData *fd = frame->opaque_ref ? (FrameData*)frame->opaque_ref->data : NULL; AVBPrint pbuf; char val_str[128]; const char *s; @@ -2581,14 +2595,14 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream, if (s) print_str ("media_type", s); else print_str_opt("media_type", "unknown"); print_int("stream_index", stream->index); - print_int("key_frame", frame->key_frame); + print_int("key_frame", !!(frame->flags & AV_FRAME_FLAG_KEY)); print_ts ("pts", frame->pts); print_time("pts_time", frame->pts, &stream->time_base); print_ts ("pkt_dts", frame->pkt_dts); print_time("pkt_dts_time", frame->pkt_dts, &stream->time_base); print_ts ("best_effort_timestamp", frame->best_effort_timestamp); print_time("best_effort_timestamp_time", frame->best_effort_timestamp, &stream->time_base); -#if LIBAVUTIL_VERSION_MAJOR < 58 +#if LIBAVUTIL_VERSION_MAJOR < 59 AV_NOWARN_DEPRECATED( print_duration_ts ("pkt_duration", frame->pkt_duration); print_duration_time("pkt_duration_time", frame->pkt_duration, &stream->time_base); @@ -2596,10 +2610,10 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream, #endif print_duration_ts ("duration", frame->duration); print_duration_time("duration_time", frame->duration, &stream->time_base); - if (frame->pkt_pos != -1) print_fmt ("pkt_pos", "%"PRId64, frame->pkt_pos); - else print_str_opt("pkt_pos", "N/A"); - if (frame->pkt_size != -1) print_val ("pkt_size", frame->pkt_size, unit_byte_str); - else print_str_opt("pkt_size", "N/A"); + if (fd && fd->pkt_pos != -1) print_fmt ("pkt_pos", "%"PRId64, fd->pkt_pos); + else print_str_opt("pkt_pos", "N/A"); + if (fd && fd->pkt_size != -1) print_val ("pkt_size", fd->pkt_size, unit_byte_str); + else print_str_opt("pkt_size", "N/A"); switch (stream->codecpar->codec_type) { AVRational sar; @@ -2607,6 +2621,10 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream, case AVMEDIA_TYPE_VIDEO: print_int("width", frame->width); print_int("height", frame->height); + print_int("crop_top", frame->crop_top); + print_int("crop_bottom", frame->crop_bottom); + print_int("crop_left", frame->crop_left); + print_int("crop_right", frame->crop_right); s = av_get_pix_fmt_name(frame->format); if (s) print_str ("pix_fmt", s); else print_str_opt("pix_fmt", "unknown"); @@ -2617,10 +2635,14 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream, print_str_opt("sample_aspect_ratio", "N/A"); } print_fmt("pict_type", "%c", av_get_picture_type_char(frame->pict_type)); +#if LIBAVUTIL_VERSION_MAJOR < 59 + AV_NOWARN_DEPRECATED( print_int("coded_picture_number", frame->coded_picture_number); print_int("display_picture_number", frame->display_picture_number); - print_int("interlaced_frame", frame->interlaced_frame); - print_int("top_field_first", frame->top_field_first); + ) +#endif + print_int("interlaced_frame", !!(frame->flags & AV_FRAME_FLAG_INTERLACED)); + print_int("top_field_first", !!(frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)); print_int("repeat_pict", frame->repeat_pict); print_color_range(w, frame->color_range); @@ -2733,7 +2755,7 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream, static av_always_inline int process_frame(WriterContext *w, InputFile *ifile, - AVFrame *frame, AVPacket *pkt, + AVFrame *frame, const AVPacket *pkt, int *packet_new) { AVFormatContext *fmt_ctx = ifile->fmt_ctx; @@ -2874,9 +2896,10 @@ static int read_interval_packets(WriterContext *w, InputFile *ifile, } if (selected_streams[pkt->stream_index]) { AVRational tb = ifile->streams[pkt->stream_index].st->time_base; + int64_t pts = pkt->pts != AV_NOPTS_VALUE ? pkt->pts : pkt->dts; - if (pkt->pts != AV_NOPTS_VALUE) - *cur_ts = av_rescale_q(pkt->pts, tb, AV_TIME_BASE_Q); + if (pts != AV_NOPTS_VALUE) + *cur_ts = av_rescale_q(pts, tb, AV_TIME_BASE_Q); if (!has_start && *cur_ts != AV_NOPTS_VALUE) { start = *cur_ts; @@ -2903,6 +2926,17 @@ static int read_interval_packets(WriterContext *w, InputFile *ifile, } if (do_read_frames) { int packet_new = 1; + FrameData *fd; + + pkt->opaque_ref = av_buffer_allocz(sizeof(*fd)); + if (!pkt->opaque_ref) { + ret = AVERROR(ENOMEM); + goto end; + } + fd = (FrameData*)pkt->opaque_ref->data; + fd->pkt_pos = pkt->pos; + fd->pkt_size = pkt->size; + while (process_frame(w, ifile, frame, pkt, &packet_new) > 0); } } @@ -3084,16 +3118,11 @@ static int show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_id break; } - if (dec_ctx && dec_ctx->codec->priv_class && show_private_data) { - const AVOption *opt = NULL; - while (opt = av_opt_next(dec_ctx->priv_data,opt)) { - uint8_t *str; - if (!(opt->flags & AV_OPT_FLAG_EXPORT)) continue; - if (av_opt_get(dec_ctx->priv_data, opt->name, 0, &str) >= 0) { - print_str(opt->name, str); - av_free(str); - } - } + if (show_private_data) { + if (dec_ctx && dec_ctx->codec->priv_class) + print_private_data(w, dec_ctx->priv_data); + if (fmt_ctx->iformat->priv_class) + print_private_data(w, fmt_ctx->priv_data); } if (fmt_ctx->iformat->flags & AVFMT_SHOW_IDS) print_fmt ("id", "0x%x", stream->id); @@ -3312,7 +3341,7 @@ static int open_input_file(InputFile *ifile, const char *filename, fmt_ctx = avformat_alloc_context(); if (!fmt_ctx) - report_and_exit(AVERROR(ENOMEM)); + return AVERROR(ENOMEM); if (!av_dict_get(format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE)) { av_dict_set(&format_opts, "scan_all_pmts", "1", AV_DICT_DONT_OVERWRITE); @@ -3334,9 +3363,13 @@ static int open_input_file(InputFile *ifile, const char *filename, av_log(NULL, AV_LOG_WARNING, "Option %s skipped - not known to demuxer.\n", t->key); if (find_stream_info) { - AVDictionary **opts = setup_find_stream_info_opts(fmt_ctx, codec_opts); + AVDictionary **opts; int orig_nb_streams = fmt_ctx->nb_streams; + err = setup_find_stream_info_opts(fmt_ctx, codec_opts, &opts); + if (err < 0) + return err; + err = avformat_find_stream_info(fmt_ctx, opts); for (i = 0; i < orig_nb_streams; i++) @@ -3379,8 +3412,12 @@ static int open_input_file(InputFile *ifile, const char *filename, continue; } { - AVDictionary *opts = filter_codec_opts(codec_opts, stream->codecpar->codec_id, - fmt_ctx, stream, codec); + AVDictionary *opts; + + err = filter_codec_opts(codec_opts, stream->codecpar->codec_id, + fmt_ctx, stream, codec, &opts); + if (err < 0) + exit(1); ist->dec_ctx = avcodec_alloc_context3(codec); if (!ist->dec_ctx) @@ -3397,6 +3434,8 @@ static int open_input_file(InputFile *ifile, const char *filename, av_dict_set(&codec_opts, "threads", "1", 0); } + av_dict_set(&opts, "flags", "+copy_opaque", AV_DICT_MULTIKEY); + ist->dec_ctx->pkt_timebase = stream->time_base; if (avcodec_open2(ist->dec_ctx, codec, &opts) < 0) { @@ -3621,8 +3660,14 @@ static int opt_show_optional_fields(void *optctx, const char *opt, const char *a else if (!av_strcasecmp(arg, "never")) show_optional_fields = SHOW_OPTIONAL_FIELDS_NEVER; else if (!av_strcasecmp(arg, "auto")) show_optional_fields = SHOW_OPTIONAL_FIELDS_AUTO; - if (show_optional_fields == SHOW_OPTIONAL_FIELDS_AUTO && av_strcasecmp(arg, "auto")) - show_optional_fields = parse_number_or_die("show_optional_fields", arg, OPT_INT, SHOW_OPTIONAL_FIELDS_AUTO, SHOW_OPTIONAL_FIELDS_ALWAYS); + if (show_optional_fields == SHOW_OPTIONAL_FIELDS_AUTO && av_strcasecmp(arg, "auto")) { + double num; + int ret = parse_number("show_optional_fields", arg, OPT_INT, + SHOW_OPTIONAL_FIELDS_AUTO, SHOW_OPTIONAL_FIELDS_ALWAYS, &num); + if (ret < 0) + return ret; + show_optional_fields = num; + } return 0; } @@ -3643,8 +3688,7 @@ static inline void mark_section_show_entries(SectionID section_id, section->show_all_entries = show_all_entries; if (show_all_entries) { - SectionID *id; - for (id = section->children_ids; *id != -1; id++) + for (const SectionID *id = section->children_ids; *id != -1; id++) mark_section_show_entries(*id, show_all_entries, entries); } else { av_dict_copy(§ion->entries_to_show, entries, 0); @@ -3720,17 +3764,19 @@ static int opt_show_entries(void *optctx, const char *opt, const char *arg) return ret; } -static void opt_input_file(void *optctx, const char *arg) +static int opt_input_file(void *optctx, const char *arg) { if (input_filename) { av_log(NULL, AV_LOG_ERROR, "Argument '%s' provided as input filename, but '%s' was already specified.\n", arg, input_filename); - exit_program(1); + return AVERROR(EINVAL); } if (!strcmp(arg, "-")) arg = "fd:"; input_filename = arg; + + return 0; } static int opt_input_file_i(void *optctx, const char *opt, const char *arg) @@ -3739,22 +3785,18 @@ static int opt_input_file_i(void *optctx, const char *opt, const char *arg) return 0; } -static void opt_output_file(void *optctx, const char *arg) +static int opt_output_file_o(void *optctx, const char *opt, const char *arg) { if (output_filename) { av_log(NULL, AV_LOG_ERROR, "Argument '%s' provided as output filename, but '%s' was already specified.\n", arg, output_filename); - exit_program(1); + return AVERROR(EINVAL); } if (!strcmp(arg, "-")) arg = "fd:"; output_filename = arg; -} -static int opt_output_file_o(void *optctx, const char *opt, const char *arg) -{ - opt_output_file(optctx, arg); return 0; } @@ -4029,11 +4071,10 @@ static const OptionDef real_options[] = { static inline int check_section_show_entries(int section_id) { - int *id; struct section *section = §ions[section_id]; if (sections[section_id].show_all_entries || sections[section_id].entries_to_show) return 1; - for (id = section->children_ids; *id != -1; id++) + for (const SectionID *id = section->children_ids; *id != -1; id++) if (check_section_show_entries(*id)) return 1; return 0; @@ -4061,7 +4102,6 @@ int main(int argc, char **argv) } #endif av_log_set_flags(AV_LOG_SKIP_REPEATED); - register_exit(ffprobe_cleanup); options = real_options; parse_loglevel(argc, argv, options); @@ -4071,7 +4111,11 @@ int main(int argc, char **argv) #endif show_banner(argc, argv, options); - parse_options(NULL, argc, argv, options, opt_input_file); + ret = parse_options(NULL, argc, argv, options, opt_input_file); + if (ret < 0) { + ret = (ret == AVERROR_EXIT) ? 0 : ret; + goto end; + } if (do_show_log) av_log_set_callback(log_callback); @@ -4195,5 +4239,9 @@ int main(int argc, char **argv) avformat_network_deinit(); +#if HAVE_THREADS + pthread_mutex_destroy(&log_mutex); +#endif + return ret < 0; } diff --git a/fftools/opt_common.c b/fftools/opt_common.c index 8a06df82df2..02d7048c42e 100644 --- a/fftools/opt_common.c +++ b/fftools/opt_common.c @@ -291,8 +291,6 @@ static void print_codec(const AVCodec *c) printf("delay "); if (c->capabilities & AV_CODEC_CAP_SMALL_LAST_FRAME) printf("small "); - if (c->capabilities & AV_CODEC_CAP_SUBFRAMES) - printf("subframes "); if (c->capabilities & AV_CODEC_CAP_EXPERIMENTAL) printf("exp "); if (c->capabilities & AV_CODEC_CAP_CHANNEL_CONF) @@ -617,10 +615,10 @@ static void print_codecs_for_id(enum AVCodecID id, int encoder) void *iter = NULL; const AVCodec *codec; - printf(" (%s: ", encoder ? "encoders" : "decoders"); + printf(" (%s:", encoder ? "encoders" : "decoders"); while ((codec = next_codec_for_id(id, &iter, encoder))) - printf("%s ", codec->name); + printf(" %s", codec->name); printf(")"); } @@ -634,7 +632,7 @@ static int compare_codec_desc(const void *a, const void *b) strcmp((*da)->name, (*db)->name); } -static unsigned get_codecs_sorted(const AVCodecDescriptor ***rcodecs) +static int get_codecs_sorted(const AVCodecDescriptor ***rcodecs) { const AVCodecDescriptor *desc = NULL; const AVCodecDescriptor **codecs; @@ -643,7 +641,7 @@ static unsigned get_codecs_sorted(const AVCodecDescriptor ***rcodecs) while ((desc = avcodec_descriptor_next(desc))) nb_codecs++; if (!(codecs = av_calloc(nb_codecs, sizeof(*codecs)))) - report_and_exit(AVERROR(ENOMEM)); + return AVERROR(ENOMEM); desc = NULL; while ((desc = avcodec_descriptor_next(desc))) codecs[i++] = desc; @@ -668,7 +666,11 @@ static char get_media_type_char(enum AVMediaType type) int show_codecs(void *optctx, const char *opt, const char *arg) { const AVCodecDescriptor **codecs; - unsigned i, nb_codecs = get_codecs_sorted(&codecs); + unsigned i; + int nb_codecs = get_codecs_sorted(&codecs); + + if (nb_codecs < 0) + return nb_codecs; printf("Codecs:\n" " D..... = Decoding supported\n" @@ -690,14 +692,13 @@ int show_codecs(void *optctx, const char *opt, const char *arg) if (strstr(desc->name, "_deprecated")) continue; - printf(" "); - printf(avcodec_find_decoder(desc->id) ? "D" : "."); - printf(avcodec_find_encoder(desc->id) ? "E" : "."); - - printf("%c", get_media_type_char(desc->type)); - printf((desc->props & AV_CODEC_PROP_INTRA_ONLY) ? "I" : "."); - printf((desc->props & AV_CODEC_PROP_LOSSY) ? "L" : "."); - printf((desc->props & AV_CODEC_PROP_LOSSLESS) ? "S" : "."); + printf(" %c%c%c%c%c%c", + avcodec_find_decoder(desc->id) ? 'D' : '.', + avcodec_find_encoder(desc->id) ? 'E' : '.', + get_media_type_char(desc->type), + (desc->props & AV_CODEC_PROP_INTRA_ONLY) ? 'I' : '.', + (desc->props & AV_CODEC_PROP_LOSSY) ? 'L' : '.', + (desc->props & AV_CODEC_PROP_LOSSLESS) ? 'S' : '.'); printf(" %-20s %s", desc->name, desc->long_name ? desc->long_name : ""); @@ -745,12 +746,13 @@ static void print_codecs(int encoder) void *iter = NULL; while ((codec = next_codec_for_id(desc->id, &iter, encoder))) { - printf(" %c", get_media_type_char(desc->type)); - printf((codec->capabilities & AV_CODEC_CAP_FRAME_THREADS) ? "F" : "."); - printf((codec->capabilities & AV_CODEC_CAP_SLICE_THREADS) ? "S" : "."); - printf((codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL) ? "X" : "."); - printf((codec->capabilities & AV_CODEC_CAP_DRAW_HORIZ_BAND)?"B" : "."); - printf((codec->capabilities & AV_CODEC_CAP_DR1) ? "D" : "."); + printf(" %c%c%c%c%c%c", + get_media_type_char(desc->type), + (codec->capabilities & AV_CODEC_CAP_FRAME_THREADS) ? 'F' : '.', + (codec->capabilities & AV_CODEC_CAP_SLICE_THREADS) ? 'S' : '.', + (codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL) ? 'X' : '.', + (codec->capabilities & AV_CODEC_CAP_DRAW_HORIZ_BAND) ? 'B' : '.', + (codec->capabilities & AV_CODEC_CAP_DR1) ? 'D' : '.'); printf(" %-20s %s", codec->name, codec->long_name ? codec->long_name : ""); if (strcmp(codec->name, desc->name)) @@ -1161,7 +1163,10 @@ int init_report(const char *env, FILE **file) report_file_level = strtol(val, &tail, 10); if (*tail) { av_log(NULL, AV_LOG_FATAL, "Invalid report file level\n"); - exit_program(1); + av_free(key); + av_free(val); + av_free(filename_template); + return AVERROR(EINVAL); } envlevel = 1; } else { @@ -1221,7 +1226,7 @@ int opt_max_alloc(void *optctx, const char *opt, const char *arg) max = strtol(arg, &tail, 10); if (*tail) { av_log(NULL, AV_LOG_FATAL, "Invalid max_alloc \"%s\".\n", arg); - exit_program(1); + return AVERROR(EINVAL); } av_max_alloc(max); return 0; @@ -1295,7 +1300,7 @@ int opt_loglevel(void *optctx, const char *opt, const char *arg) "Possible levels are numbers or:\n", arg); for (i = 0; i < FF_ARRAY_ELEMS(log_levels); i++) av_log(NULL, AV_LOG_FATAL, "\"%s\"\n", log_levels[i].name); - exit_program(1); + return AVERROR(EINVAL); } end: diff --git a/fftools/sync_queue.c b/fftools/sync_queue.c index c2b23ee4f5a..bc107ba4fe7 100644 --- a/fftools/sync_queue.c +++ b/fftools/sync_queue.c @@ -20,18 +20,59 @@ #include #include "libavutil/avassert.h" +#include "libavutil/channel_layout.h" +#include "libavutil/cpu.h" #include "libavutil/error.h" #include "libavutil/fifo.h" #include "libavutil/mathematics.h" #include "libavutil/mem.h" +#include "libavutil/samplefmt.h" +#include "libavutil/timestamp.h" #include "objpool.h" #include "sync_queue.h" +/* + * How this works: + * -------------- + * time: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 + * ------------------------------------------------------------------- + * | | | | | | | | | | | | | | + * | ┌───┐┌────────┐┌───┐┌─────────────┐ + * stream 0| │d=1││ d=2 ││d=1││ d=3 │ + * | └───┘└────────┘└───┘└─────────────┘ + * ┌───┐ ┌───────────────────────┐ + * stream 1│d=1│ │ d=5 │ + * └───┘ └───────────────────────┘ + * | ┌───┐┌───┐┌───┐┌───┐ + * stream 2| │d=1││d=1││d=1││d=1│ <- stream 2 is the head stream of the queue + * | └───┘└───┘└───┘└───┘ + * ^ ^ + * [stream 2 tail] [stream 2 head] + * + * We have N streams (N=3 in the diagram), each stream is a FIFO. The *tail* of + * each FIFO is the frame with smallest end time, the *head* is the frame with + * the largest end time. Frames submitted to the queue with sq_send() are placed + * after the head, frames returned to the caller with sq_receive() are taken + * from the tail. + * + * The head stream of the whole queue (SyncQueue.head_stream) is the limiting + * stream with the *smallest* head timestamp, i.e. the stream whose source lags + * furthest behind all other streams. It determines which frames can be output + * from the queue. + * + * In the diagram, the head stream is 2, because it head time is t=5, while + * streams 0 and 1 end at t=8 and t=9 respectively. All frames that _end_ at + * or before t=5 can be output, i.e. the first 3 frames from stream 0, first + * frame from stream 1, and all 4 frames from stream 2. + */ + typedef struct SyncQueueStream { AVFifo *fifo; AVRational tb; + /* number of audio samples in fifo */ + uint64_t samples_queued; /* stream head: largest timestamp seen */ int64_t head_ts; int limiting; @@ -39,12 +80,16 @@ typedef struct SyncQueueStream { int finished; uint64_t frames_sent; + uint64_t samples_sent; uint64_t frames_max; + int frame_samples; } SyncQueueStream; struct SyncQueue { enum SyncQueueType type; + void *logctx; + /* no more frames will be sent for any stream */ int finished; /* sync head: the stream with the _smallest_ head timestamp @@ -61,6 +106,10 @@ struct SyncQueue { // pool of preallocated frames to avoid constant allocations ObjPool *pool; + + int have_limiting; + + uintptr_t align_mask; }; static void frame_move(const SyncQueue *sq, SyncQueueFrame dst, @@ -72,22 +121,62 @@ static void frame_move(const SyncQueue *sq, SyncQueueFrame dst, av_frame_move_ref(dst.f, src.f); } -static int64_t frame_ts(const SyncQueue *sq, SyncQueueFrame frame) +/** + * Compute the end timestamp of a frame. If nb_samples is provided, consider + * the frame to have this number of audio samples, otherwise use frame duration. + */ +static int64_t frame_end(const SyncQueue *sq, SyncQueueFrame frame, int nb_samples) { + if (nb_samples) { + int64_t d = av_rescale_q(nb_samples, (AVRational){ 1, frame.f->sample_rate}, + frame.f->time_base); + return frame.f->pts + d; + } + return (sq->type == SYNC_QUEUE_PACKETS) ? frame.p->pts + frame.p->duration : frame.f->pts + frame.f->duration; } +static int frame_samples(const SyncQueue *sq, SyncQueueFrame frame) +{ + return (sq->type == SYNC_QUEUE_PACKETS) ? 0 : frame.f->nb_samples; +} + static int frame_null(const SyncQueue *sq, SyncQueueFrame frame) { return (sq->type == SYNC_QUEUE_PACKETS) ? (frame.p == NULL) : (frame.f == NULL); } +static void tb_update(const SyncQueue *sq, SyncQueueStream *st, + const SyncQueueFrame frame) +{ + AVRational tb = (sq->type == SYNC_QUEUE_PACKETS) ? + frame.p->time_base : frame.f->time_base; + + av_assert0(tb.num > 0 && tb.den > 0); + + if (tb.num == st->tb.num && tb.den == st->tb.den) + return; + + // timebase should not change after the first frame + av_assert0(!av_fifo_can_read(st->fifo)); + + if (st->head_ts != AV_NOPTS_VALUE) + st->head_ts = av_rescale_q(st->head_ts, st->tb, tb); + + st->tb = tb; +} + static void finish_stream(SyncQueue *sq, unsigned int stream_idx) { SyncQueueStream *st = &sq->streams[stream_idx]; + if (!st->finished) + av_log(sq->logctx, AV_LOG_DEBUG, + "sq: finish %u; head ts %s\n", stream_idx, + av_ts2timestr(st->head_ts, &st->tb)); + st->finished = 1; if (st->limiting && st->head_ts != AV_NOPTS_VALUE) { @@ -105,8 +194,14 @@ static void finish_stream(SyncQueue *sq, unsigned int stream_idx) for (unsigned int i = 0; i < sq->nb_streams; i++) { SyncQueueStream *st1 = &sq->streams[i]; if (st != st1 && st1->head_ts != AV_NOPTS_VALUE && - av_compare_ts(st->head_ts, st->tb, st1->head_ts, st1->tb) <= 0) + av_compare_ts(st->head_ts, st->tb, st1->head_ts, st1->tb) <= 0) { + if (!st1->finished) + av_log(sq->logctx, AV_LOG_DEBUG, + "sq: finish secondary %u; head ts %s\n", i, + av_ts2timestr(st1->head_ts, &st1->tb)); + st1->finished = 1; + } } } @@ -116,21 +211,32 @@ static void finish_stream(SyncQueue *sq, unsigned int stream_idx) return; } sq->finished = 1; + + av_log(sq->logctx, AV_LOG_DEBUG, "sq: finish queue\n"); } static void queue_head_update(SyncQueue *sq) { + av_assert0(sq->have_limiting); + if (sq->head_stream < 0) { + unsigned first_limiting = UINT_MAX; + /* wait for one timestamp in each stream before determining * the queue head */ for (unsigned int i = 0; i < sq->nb_streams; i++) { SyncQueueStream *st = &sq->streams[i]; - if (st->limiting && st->head_ts == AV_NOPTS_VALUE) + if (!st->limiting) + continue; + if (st->head_ts == AV_NOPTS_VALUE) return; + if (first_limiting == UINT_MAX) + first_limiting = i; } // placeholder value, correct one will be found below - sq->head_stream = 0; + av_assert0(first_limiting < UINT_MAX); + sq->head_stream = first_limiting; } for (unsigned int i = 0; i < sq->nb_streams; i++) { @@ -203,7 +309,7 @@ static int overflow_heartbeat(SyncQueue *sq, int stream_idx) /* get the chosen stream's tail timestamp */ for (size_t i = 0; tail_ts == AV_NOPTS_VALUE && av_fifo_peek(st->fifo, &frame, 1, i) >= 0; i++) - tail_ts = frame_ts(sq, frame); + tail_ts = frame_end(sq, frame, 0); /* overflow triggers when the tail is over specified duration behind the head */ if (tail_ts == AV_NOPTS_VALUE || tail_ts >= st->head_ts || @@ -225,6 +331,9 @@ static int overflow_heartbeat(SyncQueue *sq, int stream_idx) if (st1->head_ts != AV_NOPTS_VALUE) ts = FFMAX(st1->head_ts + 1, ts); + av_log(sq->logctx, AV_LOG_DEBUG, "sq: %u overflow heardbeat %s -> %s\n", + i, av_ts2timestr(st1->head_ts, &st1->tb), av_ts2timestr(ts, &st1->tb)); + stream_update_ts(sq, i, ts); } @@ -236,27 +345,39 @@ int sq_send(SyncQueue *sq, unsigned int stream_idx, SyncQueueFrame frame) SyncQueueStream *st; SyncQueueFrame dst; int64_t ts; - int ret; + int ret, nb_samples; av_assert0(stream_idx < sq->nb_streams); st = &sq->streams[stream_idx]; - av_assert0(st->tb.num > 0 && st->tb.den > 0); - if (frame_null(sq, frame)) { + av_log(sq->logctx, AV_LOG_DEBUG, "sq: %u EOF\n", stream_idx); finish_stream(sq, stream_idx); return 0; } if (st->finished) return AVERROR_EOF; + tb_update(sq, st, frame); + ret = objpool_get(sq->pool, (void**)&dst); if (ret < 0) return ret; frame_move(sq, dst, frame); - ts = frame_ts(sq, dst); + nb_samples = frame_samples(sq, dst); + // make sure frame duration is consistent with sample count + if (nb_samples) { + av_assert0(dst.f->sample_rate > 0); + dst.f->duration = av_rescale_q(nb_samples, (AVRational){ 1, dst.f->sample_rate }, + dst.f->time_base); + } + + ts = frame_end(sq, dst, 0); + + av_log(sq->logctx, AV_LOG_DEBUG, "sq: send %u ts %s\n", stream_idx, + av_ts2timestr(ts, &st->tb)); ret = av_fifo_write(st->fifo, &dst, 1); if (ret < 0) { @@ -267,13 +388,139 @@ int sq_send(SyncQueue *sq, unsigned int stream_idx, SyncQueueFrame frame) stream_update_ts(sq, stream_idx, ts); - st->frames_sent++; - if (st->frames_sent >= st->frames_max) + st->samples_queued += nb_samples; + st->samples_sent += nb_samples; + + if (st->frame_samples) + st->frames_sent = st->samples_sent / st->frame_samples; + else + st->frames_sent++; + + if (st->frames_sent >= st->frames_max) { + av_log(sq->logctx, AV_LOG_DEBUG, "sq: %u frames_max %"PRIu64" reached\n", + stream_idx, st->frames_max); + finish_stream(sq, stream_idx); + } + + return 0; +} + +static void offset_audio(AVFrame *f, int nb_samples) +{ + const int planar = av_sample_fmt_is_planar(f->format); + const int planes = planar ? f->ch_layout.nb_channels : 1; + const int bps = av_get_bytes_per_sample(f->format); + const int offset = nb_samples * bps * (planar ? 1 : f->ch_layout.nb_channels); + + av_assert0(bps > 0); + av_assert0(nb_samples < f->nb_samples); + + for (int i = 0; i < planes; i++) { + f->extended_data[i] += offset; + if (i < FF_ARRAY_ELEMS(f->data)) + f->data[i] = f->extended_data[i]; + } + f->linesize[0] -= offset; + f->nb_samples -= nb_samples; + f->duration = av_rescale_q(f->nb_samples, (AVRational){ 1, f->sample_rate }, + f->time_base); + f->pts += av_rescale_q(nb_samples, (AVRational){ 1, f->sample_rate }, + f->time_base); +} + +static int frame_is_aligned(const SyncQueue *sq, const AVFrame *frame) +{ + // only checks linesize[0], so only works for audio + av_assert0(frame->nb_samples > 0); + av_assert0(sq->align_mask); + + // only check data[0], because we always offset all data pointers + // by the same offset, so if one is aligned, all are + if (!((uintptr_t)frame->data[0] & sq->align_mask) && + !(frame->linesize[0] & sq->align_mask) && + frame->linesize[0] > sq->align_mask) + return 1; return 0; } +static int receive_samples(SyncQueue *sq, SyncQueueStream *st, + AVFrame *dst, int nb_samples) +{ + SyncQueueFrame src; + int ret; + + av_assert0(st->samples_queued >= nb_samples); + + ret = av_fifo_peek(st->fifo, &src, 1, 0); + av_assert0(ret >= 0); + + // peeked frame has enough samples and its data is aligned + // -> we can just make a reference and limit its sample count + if (src.f->nb_samples > nb_samples && frame_is_aligned(sq, src.f)) { + ret = av_frame_ref(dst, src.f); + if (ret < 0) + return ret; + + dst->nb_samples = nb_samples; + offset_audio(src.f, nb_samples); + st->samples_queued -= nb_samples; + + goto finish; + } + + // otherwise allocate a new frame and copy the data + ret = av_channel_layout_copy(&dst->ch_layout, &src.f->ch_layout); + if (ret < 0) + return ret; + + dst->format = src.f->format; + dst->nb_samples = nb_samples; + + ret = av_frame_get_buffer(dst, 0); + if (ret < 0) + goto fail; + + ret = av_frame_copy_props(dst, src.f); + if (ret < 0) + goto fail; + + dst->nb_samples = 0; + while (dst->nb_samples < nb_samples) { + int to_copy; + + ret = av_fifo_peek(st->fifo, &src, 1, 0); + av_assert0(ret >= 0); + + to_copy = FFMIN(nb_samples - dst->nb_samples, src.f->nb_samples); + + av_samples_copy(dst->extended_data, src.f->extended_data, dst->nb_samples, + 0, to_copy, dst->ch_layout.nb_channels, dst->format); + + if (to_copy < src.f->nb_samples) + offset_audio(src.f, to_copy); + else { + av_frame_unref(src.f); + objpool_release(sq->pool, (void**)&src); + av_fifo_drain2(st->fifo, 1); + } + st->samples_queued -= to_copy; + + dst->nb_samples += to_copy; + } + +finish: + dst->duration = av_rescale_q(nb_samples, (AVRational){ 1, dst->sample_rate }, + dst->time_base); + + return 0; + +fail: + av_frame_unref(dst); + return ret; +} + static int receive_for_stream(SyncQueue *sq, unsigned int stream_idx, SyncQueueFrame frame) { @@ -284,13 +531,18 @@ static int receive_for_stream(SyncQueue *sq, unsigned int stream_idx, av_assert0(stream_idx < sq->nb_streams); st = &sq->streams[stream_idx]; - if (av_fifo_can_read(st->fifo)) { + if (av_fifo_can_read(st->fifo) && + (st->frame_samples <= st->samples_queued || st->finished)) { + int nb_samples = st->frame_samples; SyncQueueFrame peek; int64_t ts; int cmp = 1; + if (st->finished) + nb_samples = FFMIN(nb_samples, st->samples_queued); + av_fifo_peek(st->fifo, &peek, 1, 0); - ts = frame_ts(sq, peek); + ts = frame_end(sq, peek, nb_samples); /* check if this stream's tail timestamp does not overtake * the overall queue head */ @@ -299,11 +551,28 @@ static int receive_for_stream(SyncQueue *sq, unsigned int stream_idx, /* We can release frames that do not end after the queue head. * Frames with no timestamps are just passed through with no conditions. + * Frames are also passed through when there are no limiting streams. */ - if (cmp <= 0 || ts == AV_NOPTS_VALUE) { - frame_move(sq, frame, peek); - objpool_release(sq->pool, (void**)&peek); - av_fifo_drain2(st->fifo, 1); + if (cmp <= 0 || ts == AV_NOPTS_VALUE || !sq->have_limiting) { + if (nb_samples && + (nb_samples != peek.f->nb_samples || !frame_is_aligned(sq, peek.f))) { + int ret = receive_samples(sq, st, frame.f, nb_samples); + if (ret < 0) + return ret; + } else { + frame_move(sq, frame, peek); + objpool_release(sq->pool, (void**)&peek); + av_fifo_drain2(st->fifo, 1); + av_assert0(st->samples_queued >= frame_samples(sq, frame)); + st->samples_queued -= frame_samples(sq, frame); + } + + av_log(sq->logctx, AV_LOG_DEBUG, + "sq: receive %u ts %s queue head %d ts %s\n", stream_idx, + av_ts2timestr(frame_end(sq, frame, 0), &st->tb), + sq->head_stream, + st_head ? av_ts2timestr(st_head->head_ts, &st_head->tb) : "N/A"); + return 0; } } @@ -372,37 +641,38 @@ int sq_add_stream(SyncQueue *sq, int limiting) st->frames_max = UINT64_MAX; st->limiting = limiting; + sq->have_limiting |= limiting; + return sq->nb_streams++; } -void sq_set_tb(SyncQueue *sq, unsigned int stream_idx, AVRational tb) +void sq_limit_frames(SyncQueue *sq, unsigned int stream_idx, uint64_t frames) { SyncQueueStream *st; av_assert0(stream_idx < sq->nb_streams); st = &sq->streams[stream_idx]; - av_assert0(!av_fifo_can_read(st->fifo)); - - if (st->head_ts != AV_NOPTS_VALUE) - st->head_ts = av_rescale_q(st->head_ts, st->tb, tb); - - st->tb = tb; + st->frames_max = frames; + if (st->frames_sent >= st->frames_max) + finish_stream(sq, stream_idx); } -void sq_limit_frames(SyncQueue *sq, unsigned int stream_idx, uint64_t frames) +void sq_frame_samples(SyncQueue *sq, unsigned int stream_idx, + int frame_samples) { SyncQueueStream *st; + av_assert0(sq->type == SYNC_QUEUE_FRAMES); av_assert0(stream_idx < sq->nb_streams); st = &sq->streams[stream_idx]; - st->frames_max = frames; - if (st->frames_sent >= st->frames_max) - finish_stream(sq, stream_idx); + st->frame_samples = frame_samples; + + sq->align_mask = av_cpu_max_align() - 1; } -SyncQueue *sq_alloc(enum SyncQueueType type, int64_t buf_size_us) +SyncQueue *sq_alloc(enum SyncQueueType type, int64_t buf_size_us, void *logctx) { SyncQueue *sq = av_mallocz(sizeof(*sq)); @@ -411,6 +681,7 @@ SyncQueue *sq_alloc(enum SyncQueueType type, int64_t buf_size_us) sq->type = type; sq->buf_size_us = buf_size_us; + sq->logctx = logctx; sq->head_stream = -1; sq->head_finished_stream = -1; diff --git a/fftools/sync_queue.h b/fftools/sync_queue.h index 3f823ff0d91..dc5acfd4991 100644 --- a/fftools/sync_queue.h +++ b/fftools/sync_queue.h @@ -38,6 +38,11 @@ typedef union SyncQueueFrame { #define SQFRAME(frame) ((SyncQueueFrame){ .f = (frame) }) #define SQPKT(pkt) ((SyncQueueFrame){ .p = (pkt) }) +/** + * A sync queue provides timestamp synchronization between multiple streams. + * Some of these streams are marked as "limiting", then the queue ensures no + * stream gets ahead of any of the limiting streams. + */ typedef struct SyncQueue SyncQueue; /** @@ -45,7 +50,7 @@ typedef struct SyncQueue SyncQueue; * * @param buf_size_us maximum duration that will be buffered in microseconds */ -SyncQueue *sq_alloc(enum SyncQueueType type, int64_t buf_size_us); +SyncQueue *sq_alloc(enum SyncQueueType type, int64_t buf_size_us, void *logctx); void sq_free(SyncQueue **sq); /** @@ -59,12 +64,6 @@ void sq_free(SyncQueue **sq); */ int sq_add_stream(SyncQueue *sq, int limiting); -/** - * Set the timebase for the stream with index stream_idx. Should be called - * before sending any frames for this stream. - */ -void sq_set_tb(SyncQueue *sq, unsigned int stream_idx, AVRational tb); - /** * Limit the number of output frames for stream with index stream_idx * to max_frames. @@ -72,6 +71,16 @@ void sq_set_tb(SyncQueue *sq, unsigned int stream_idx, AVRational tb); void sq_limit_frames(SyncQueue *sq, unsigned int stream_idx, uint64_t max_frames); +/** + * Set a constant output audio frame size, in samples. Can only be used with + * SYNC_QUEUE_FRAMES queues and audio streams. + * + * All output frames will have exactly frame_samples audio samples, except + * possibly for the last one, which may have fewer. + */ +void sq_frame_samples(SyncQueue *sq, unsigned int stream_idx, + int frame_samples); + /** * Submit a frame for the stream with index stream_idx. * diff --git a/libavcodec/012v.c b/libavcodec/012v.c index 2d89a86b98b..fa5eb0f95ef 100644 --- a/libavcodec/012v.c +++ b/libavcodec/012v.c @@ -65,7 +65,7 @@ static int zero12v_decode_frame(AVCodecContext *avctx, AVFrame *pic, return ret; pic->pict_type = AV_PICTURE_TYPE_I; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; line_end = avpkt->data + stride; for (line = 0; line < avctx->height; line++) { @@ -131,8 +131,8 @@ static int zero12v_decode_frame(AVCodecContext *avctx, AVFrame *pic, u = x/2 + (uint16_t *)(pic->data[1] + line * pic->linesize[1]); v = x/2 + (uint16_t *)(pic->data[2] + line * pic->linesize[2]); memcpy(y, y_temp, sizeof(*y) * (width - x)); - memcpy(u, u_temp, sizeof(*u) * (width - x + 1) / 2); - memcpy(v, v_temp, sizeof(*v) * (width - x + 1) / 2); + memcpy(u, u_temp, sizeof(*u) * ((width - x + 1) / 2)); + memcpy(v, v_temp, sizeof(*v) * ((width - x + 1) / 2)); } line_end += stride; diff --git a/libavcodec/4xm.c b/libavcodec/4xm.c index 5636fdef2df..411e50da7c0 100644 --- a/libavcodec/4xm.c +++ b/libavcodec/4xm.c @@ -875,7 +875,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *picture, } for (i = 0; i < CFRAME_BUFFER_COUNT; i++) - if (f->cfrm[i].id && f->cfrm[i].id < avctx->frame_number) + if (f->cfrm[i].id && f->cfrm[i].id < avctx->frame_num) av_log(f->avctx, AV_LOG_ERROR, "lost c frame %d\n", f->cfrm[i].id); @@ -910,9 +910,9 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *picture, buf = cfrm->data; frame_size = cfrm->size; - if (id != avctx->frame_number) - av_log(f->avctx, AV_LOG_ERROR, "cframe id mismatch %d %d\n", - id, avctx->frame_number); + if (id != avctx->frame_num) + av_log(f->avctx, AV_LOG_ERROR, "cframe id mismatch %d %"PRId64"\n", + id, avctx->frame_num); if (f->version <= 1) return AVERROR_INVALIDDATA; @@ -957,7 +957,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *picture, return AVERROR_INVALIDDATA; } - picture->key_frame = picture->pict_type == AV_PICTURE_TYPE_I; + if (picture->pict_type == AV_PICTURE_TYPE_I) + picture->flags |= AV_FRAME_FLAG_KEY; + else + picture->flags &= ~AV_FRAME_FLAG_KEY; av_image_copy_plane(picture->data[0], picture->linesize[0], (const uint8_t*)f->frame_buffer, avctx->width * 2, diff --git a/libavcodec/8bps.c b/libavcodec/8bps.c index 90d6c96fd1b..af98f62fad7 100644 --- a/libavcodec/8bps.c +++ b/libavcodec/8bps.c @@ -47,8 +47,6 @@ typedef struct EightBpsContext { unsigned char planes; unsigned char planemap[4]; - - uint32_t pal[256]; } EightBpsContext; static int decode_frame(AVCodecContext *avctx, AVFrame *frame, @@ -123,9 +121,14 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, } if (avctx->bits_per_coded_sample <= 8) { - frame->palette_has_changed = ff_copy_palette(c->pal, avpkt, avctx); - - memcpy (frame->data[1], c->pal, AVPALETTE_SIZE); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS + frame->palette_has_changed = +#endif + ff_copy_palette(frame->data[1], avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_ENABLE_DEPRECATION_WARNINGS +#endif } *got_frame = 1; diff --git a/libavcodec/8svx.c b/libavcodec/8svx.c index ed635f9ede3..0a6d311cf1f 100644 --- a/libavcodec/8svx.c +++ b/libavcodec/8svx.c @@ -151,7 +151,7 @@ static int eightsvx_decode_frame(AVCodecContext *avctx, AVFrame *frame, *got_frame_ptr = 1; - return ((avctx->frame_number == 0) * hdr_size + buf_size) * channels; + return ((avctx->frame_num == 0) * hdr_size + buf_size) * channels; } static av_cold int eightsvx_decode_init(AVCodecContext *avctx) diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 651d3024424..8c603c2d6dd 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -62,6 +62,7 @@ OBJS = ac3_parser.o \ xiph.o \ # subsystems +include $(SRC_PATH)/libavcodec/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 @@ -76,6 +77,7 @@ OBJS-$(CONFIG_CBS) += cbs.o cbs_bsf.o OBJS-$(CONFIG_CBS_AV1) += cbs_av1.o OBJS-$(CONFIG_CBS_H264) += cbs_h2645.o cbs_sei.o h2645_parse.o OBJS-$(CONFIG_CBS_H265) += cbs_h2645.o cbs_sei.o h2645_parse.o +OBJS-$(CONFIG_CBS_H266) += 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_VP9) += cbs_vp9.o @@ -84,6 +86,7 @@ OBJS-$(CONFIG_DCT) += dct.o dct32_fixed.o dct32_float.o OBJS-$(CONFIG_DEFLATE_WRAPPER) += zlib_wrapper.o OBJS-$(CONFIG_DOVI_RPU) += dovi_rpu.o OBJS-$(CONFIG_ERROR_RESILIENCE) += error_resilience.o +OBJS-$(CONFIG_EVCPARSE) += evc_parse.o evc_ps.o OBJS-$(CONFIG_EXIF) += exif.o tiff_common.o OBJS-$(CONFIG_FAANDCT) += faandct.o OBJS-$(CONFIG_FAANIDCT) += faanidct.o @@ -104,7 +107,7 @@ OBJS-$(CONFIG_H264_SEI) += h264_sei.o h2645_sei.o OBJS-$(CONFIG_HEVCPARSE) += hevc_parse.o hevc_ps.o hevc_data.o \ h2645data.o h2645_parse.o h2645_vui.o OBJS-$(CONFIG_HEVC_SEI) += hevc_sei.o h2645_sei.o \ - dynamic_hdr10_plus.o dynamic_hdr_vivid.o + dynamic_hdr_vivid.o OBJS-$(CONFIG_HPELDSP) += hpeldsp.o OBJS-$(CONFIG_HUFFMAN) += huffman.o OBJS-$(CONFIG_HUFFYUVDSP) += huffyuvdsp.o @@ -163,6 +166,7 @@ OBJS-$(CONFIG_TEXTUREDSP) += texturedsp.o OBJS-$(CONFIG_TEXTUREDSPENC) += texturedspenc.o OBJS-$(CONFIG_TPELDSP) += tpeldsp.o OBJS-$(CONFIG_VAAPI_ENCODE) += vaapi_encode.o +OBJS-$(CONFIG_AV1_AMF_ENCODER) += amfenc_av1.o OBJS-$(CONFIG_VC1DSP) += vc1dsp.o OBJS-$(CONFIG_VIDEODSP) += videodsp.o OBJS-$(CONFIG_VP3DSP) += vp3dsp.o @@ -218,6 +222,8 @@ OBJS-$(CONFIG_AMRWB_DECODER) += amrwbdec.o celp_filters.o \ acelp_pitch_delay.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 @@ -250,6 +256,7 @@ OBJS-$(CONFIG_AURA2_DECODER) += aura.o OBJS-$(CONFIG_AV1_DECODER) += av1dec.o OBJS-$(CONFIG_AV1_CUVID_DECODER) += cuviddec.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_AVRN_DECODER) += avrndec.o @@ -277,6 +284,7 @@ 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_DECODER) += dpcm.o OBJS-$(CONFIG_CCAPTION_DECODER) += ccaption_dec.o ass.o OBJS-$(CONFIG_CDGRAPHICS_DECODER) += cdgraphics.o OBJS-$(CONFIG_CDTOONS_DECODER) += cdtoons.o @@ -465,7 +473,7 @@ 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 + 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 @@ -481,7 +489,7 @@ 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) += media100.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 @@ -538,6 +546,7 @@ OBJS-$(CONFIG_MPEG4_DECODER) += 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_OMX_ENCODER) += omx.o OBJS-$(CONFIG_MPEG4_V4L2M2M_DECODER) += v4l2_m2m_dec.o OBJS-$(CONFIG_MPEG4_V4L2M2M_ENCODER) += v4l2_m2m_enc.o @@ -546,6 +555,7 @@ 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 @@ -578,6 +588,7 @@ 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_PFM_DECODER) += pnmdec.o pnm.o OBJS-$(CONFIG_PFM_ENCODER) += pnmenc.o OBJS-$(CONFIG_PGM_DECODER) += pnmdec.o pnm.o @@ -627,6 +638,7 @@ 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 @@ -635,6 +647,7 @@ 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 @@ -744,7 +757,10 @@ 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 \ @@ -753,10 +769,11 @@ OBJS-$(CONFIG_VP3_DECODER) += vp3.o jpegquanttables.o OBJS-$(CONFIG_VP5_DECODER) += vp5.o vp56.o vp56data.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 vpx_rac.o -OBJS-$(CONFIG_VP8_DECODER) += vp8.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 @@ -767,6 +784,7 @@ OBJS-$(CONFIG_VP9_DECODER) += vp9.o vp9data.o vp9dsp.o vp9lpf.o vp9r vp9dsp_8bpp.o vp9dsp_10bpp.o vp9dsp_12bpp.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 @@ -774,6 +792,8 @@ 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_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 @@ -956,6 +976,7 @@ 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_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 @@ -967,12 +988,14 @@ 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_AV1_D3D11VA_HWACCEL) += dxva2_av1.o OBJS-$(CONFIG_AV1_DXVA2_HWACCEL) += dxva2_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_VULKAN_HWACCEL) += vulkan_av1.o OBJS-$(CONFIG_H263_VAAPI_HWACCEL) += vaapi_mpeg4.o OBJS-$(CONFIG_H263_VIDEOTOOLBOX_HWACCEL) += videotoolbox.o OBJS-$(CONFIG_H264_D3D11VA_HWACCEL) += dxva2_h264.o @@ -982,12 +1005,14 @@ 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_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 @@ -1069,10 +1094,11 @@ 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_LIBCELT_DECODER) += libcelt_dec.o OBJS-$(CONFIG_LIBCODEC2_DECODER) += libcodec2.o OBJS-$(CONFIG_LIBCODEC2_ENCODER) += libcodec2.o -OBJS-$(CONFIG_LIBDAV1D_DECODER) += libdav1d.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 @@ -1091,7 +1117,6 @@ 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_DECODER) += libopenjpegdec.o OBJS-$(CONFIG_LIBOPENJPEG_ENCODER) += libopenjpegenc.o OBJS-$(CONFIG_LIBOPUS_DECODER) += libopusdec.o libopus.o \ vorbis_data.o @@ -1111,8 +1136,8 @@ 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 libvpx.o -OBJS-$(CONFIG_LIBVPX_VP9_ENCODER) += libvpxenc.o libvpx.o +OBJS-$(CONFIG_LIBVPX_VP9_DECODER) += libvpxdec.o +OBJS-$(CONFIG_LIBVPX_VP9_ENCODER) += libvpxenc.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 @@ -1130,7 +1155,7 @@ OBJS-$(CONFIG_AC3_PARSER) += aac_ac3_parser.o ac3tab.o \ ac3_channel_layout_tab.o OBJS-$(CONFIG_ADX_PARSER) += adx_parser.o OBJS-$(CONFIG_AMR_PARSER) += amr_parser.o -OBJS-$(CONFIG_AV1_PARSER) += av1_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 @@ -1147,6 +1172,7 @@ 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_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 @@ -1184,6 +1210,7 @@ OBJS-$(CONFIG_VC1_PARSER) += vc1_parser.o vc1.o vc1data.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 @@ -1212,6 +1239,7 @@ OBJS-$(CONFIG_HEVC_METADATA_BSF) += h265_metadata_bsf.o h265_profile_le h2645data.o OBJS-$(CONFIG_HEVC_MP4TOANNEXB_BSF) += hevc_mp4toannexb_bsf.o OBJS-$(CONFIG_IMX_DUMP_HEADER_BSF) += imx_dump_header_bsf.o +OBJS-$(CONFIG_MEDIA100_TO_MJPEGB_BSF) += media100_to_mjpegb_bsf.o OBJS-$(CONFIG_MJPEG2JPEG_BSF) += mjpeg2jpeg_bsf.o OBJS-$(CONFIG_MJPEGA_DUMP_HEADER_BSF) += mjpega_dump_header_bsf.o OBJS-$(CONFIG_MPEG4_UNPACK_BFRAMES_BSF) += mpeg4_unpack_bframes_bsf.o @@ -1234,6 +1262,9 @@ OBJS-$(CONFIG_VP9_METADATA_BSF) += vp9_metadata_bsf.o OBJS-$(CONFIG_VP9_RAW_REORDER_BSF) += vp9_raw_reorder_bsf.o OBJS-$(CONFIG_VP9_SUPERFRAME_BSF) += vp9_superframe_bsf.o OBJS-$(CONFIG_VP9_SUPERFRAME_SPLIT_BSF) += vp9_superframe_split_bsf.o +OBJS-$(CONFIG_VVC_METADATA_BSF) += h266_metadata_bsf.o +OBJS-$(CONFIG_VVC_MP4TOANNEXB_BSF) += vvc_mp4toannexb_bsf.o +OBJS-$(CONFIG_EVC_FRAME_MERGE_BSF) += evc_frame_merge_bsf.o # thread libraries OBJS-$(HAVE_LIBC_MSVCRT) += file_open.o @@ -1253,6 +1284,7 @@ SKIPHEADERS += %_tablegen.h \ aacenc_quantization.h \ aacenc_quantization_misc.h \ bitstream_template.h \ + vulkan_video_codec_av1std.h \ $(ARCH)/vpx_arith.h \ SKIPHEADERS-$(CONFIG_AMF) += amfenc.h @@ -1274,6 +1306,7 @@ SKIPHEADERS-$(CONFIG_XVMC) += xvmc.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) += vulkan.h vulkan_video.h vulkan_decode.h vulkan_video_codec_av1std_decode.h SKIPHEADERS-$(CONFIG_V4L2_M2M) += v4l2_buffers.h v4l2_context.h v4l2_m2m.h SKIPHEADERS-$(CONFIG_ZLIB) += zlib_wrapper.h diff --git a/libavcodec/aac_ac3_parser.c b/libavcodec/aac_ac3_parser.c index 9ab979632dc..83d515b5a83 100644 --- a/libavcodec/aac_ac3_parser.c +++ b/libavcodec/aac_ac3_parser.c @@ -95,6 +95,7 @@ int ff_aac_ac3_parse(AVCodecParserContext *s1, duration in seconds is still correct (as is the number of bits in the frame). */ if (avctx->codec_id != AV_CODEC_ID_AAC) { +#if CONFIG_AC3_PARSER AC3HeaderInfo hdr, *phrd = &hdr; int offset = ff_ac3_find_syncword(buf, buf_size); @@ -146,7 +147,9 @@ FF_ENABLE_DEPRECATION_WARNINGS if (hdr.bitstream_mode == 0x7 && hdr.channels > 1) avctx->audio_service_type = AV_AUDIO_SERVICE_TYPE_KARAOKE; bit_rate = hdr.bit_rate; +#endif } else { +#if CONFIG_AAC_PARSER AACADTSHeaderInfo hdr, *phrd = &hdr; int ret = avpriv_adts_header_parse(&phrd, buf, buf_size); @@ -154,6 +157,7 @@ FF_ENABLE_DEPRECATION_WARNINGS return i; bit_rate = hdr.bit_rate; +#endif } /* Calculate the average bit rate */ diff --git a/libavcodec/aacdec_template.c b/libavcodec/aacdec_template.c index 444dc4fa9d5..e8dcd460a07 100644 --- a/libavcodec/aacdec_template.c +++ b/libavcodec/aacdec_template.c @@ -1159,8 +1159,8 @@ static av_cold void aac_static_table_init(void) 352); // window initialization - AAC_RENAME(ff_kbd_window_init)(AAC_RENAME(aac_kbd_long_960), 4.0, 960); - AAC_RENAME(ff_kbd_window_init)(AAC_RENAME(aac_kbd_short_120), 6.0, 120); + AAC_RENAME(avpriv_kbd_window_init)(AAC_RENAME(aac_kbd_long_960), 4.0, 960); + AAC_RENAME(avpriv_kbd_window_init)(AAC_RENAME(aac_kbd_short_120), 6.0, 120); #if !USE_FIXED AAC_RENAME(ff_sine_window_init)(AAC_RENAME(sine_960), 960); @@ -1168,8 +1168,8 @@ static av_cold void aac_static_table_init(void) AAC_RENAME(ff_init_ff_sine_windows)(9); ff_aac_float_common_init(); #else - AAC_RENAME(ff_kbd_window_init)(AAC_RENAME2(aac_kbd_long_1024), 4.0, 1024); - AAC_RENAME(ff_kbd_window_init)(AAC_RENAME2(aac_kbd_short_128), 6.0, 128); + AAC_RENAME(avpriv_kbd_window_init)(AAC_RENAME2(aac_kbd_long_1024), 4.0, 1024); + AAC_RENAME(avpriv_kbd_window_init)(AAC_RENAME2(aac_kbd_short_128), 6.0, 128); init_sine_windows_fixed(); #endif @@ -2856,8 +2856,8 @@ static void imdct_and_windowing_eld(AACContext *ac, SingleChannelElement *sce) ac->mdct512_fn(ac->mdct512, buf, in, sizeof(INTFLOAT)); for (i = 0; i < n; i+=2) { - buf[i + 0] = -(USE_FIXED + 1)*buf[i + 0]; - buf[i + 1] = (USE_FIXED + 1)*buf[i + 1]; + buf[i + 0] = -(int)(USE_FIXED + 1U)*buf[i + 0]; + buf[i + 1] = (int)(USE_FIXED + 1U)*buf[i + 1]; } // Like with the regular IMDCT at this point we still have the middle half // of a transform but with even symmetry on the left and odd symmetry on diff --git a/libavcodec/aacenc.c b/libavcodec/aacenc.c index 5bc60c7390a..f48f0570221 100644 --- a/libavcodec/aacenc.c +++ b/libavcodec/aacenc.c @@ -854,7 +854,7 @@ static int aac_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, if (s->psypp) ff_psy_preprocess(s->psypp, s->planar_samples, s->channels); - if (!avctx->frame_number) + if (!avctx->frame_num) return 0; start_ch = 0; @@ -958,7 +958,7 @@ static int aac_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, do { init_put_bits(&s->pb, avpkt->data, avpkt->size); - if ((avctx->frame_number & 0xFF)==1 && !(avctx->flags & AV_CODEC_FLAG_BITEXACT)) + if ((avctx->frame_num & 0xFF)==1 && !(avctx->flags & AV_CODEC_FLAG_BITEXACT)) put_bitstream_info(s, LIBAVCODEC_IDENT); start_ch = 0; target_bits = 0; @@ -1106,6 +1106,18 @@ static int aac_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, too_many_bits = FFMIN(too_many_bits, 6144 * s->channels - 3); too_few_bits = FFMIN(FFMAX(rate_bits - rate_bits/4, target_bits), too_many_bits); + /* When strict bit-rate control is demanded */ + if (avctx->bit_rate_tolerance == 0) { + if (rate_bits < frame_bits) { + float ratio = ((float)rate_bits) / frame_bits; + s->lambda *= FFMIN(0.9f, ratio); + continue; + } + /* reset lambda when solution is found */ + s->lambda = avctx->global_quality > 0 ? avctx->global_quality : 120; + break; + } + /* When using ABR, be strict (but only for increasing) */ too_few_bits = too_few_bits - too_few_bits/8; too_many_bits = too_many_bits + too_many_bits/2; diff --git a/libavcodec/aacps_tablegen.h b/libavcodec/aacps_tablegen.h index 0ac4f68d68d..5fdd7f0a9dd 100644 --- a/libavcodec/aacps_tablegen.h +++ b/libavcodec/aacps_tablegen.h @@ -34,7 +34,7 @@ #include "libavutil/common.h" #include "libavutil/libm.h" #include "libavutil/mathematics.h" -#include "libavutil/mem.h" +#include "libavutil/mem_internal.h" #define NR_ALLPASS_BANDS20 30 #define NR_ALLPASS_BANDS34 50 #define PS_AP_LINKS 3 diff --git a/libavcodec/aacpsdsp_template.c b/libavcodec/aacpsdsp_template.c index c063788b897..7b3eb78db1b 100644 --- a/libavcodec/aacpsdsp_template.c +++ b/libavcodec/aacpsdsp_template.c @@ -26,24 +26,25 @@ #include "libavutil/attributes.h" #include "aacpsdsp.h" -static void ps_add_squares_c(INTFLOAT *dst, const INTFLOAT (*src)[2], int n) +static void ps_add_squares_c(INTFLOAT *av_restrict dst, + const INTFLOAT (*src)[2], int n) { - int i; - for (i = 0; i < n; i++) + for (int i = 0; i < n; i++) dst[i] += (UINTFLOAT)AAC_MADD28(src[i][0], src[i][0], src[i][1], src[i][1]); } -static void ps_mul_pair_single_c(INTFLOAT (*dst)[2], INTFLOAT (*src0)[2], INTFLOAT *src1, +static void ps_mul_pair_single_c(INTFLOAT (*av_restrict dst)[2], + INTFLOAT (*src0)[2], INTFLOAT *src1, int n) { - int i; - for (i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { dst[i][0] = AAC_MUL16(src0[i][0], src1[i]); dst[i][1] = AAC_MUL16(src0[i][1], src1[i]); } } -static void ps_hybrid_analysis_c(INTFLOAT (*out)[2], INTFLOAT (*in)[2], +static void ps_hybrid_analysis_c(INTFLOAT (*av_restrict out)[2], + INTFLOAT (*in)[2], const INTFLOAT (*filter)[8][2], ptrdiff_t stride, int n) { @@ -76,13 +77,12 @@ static void ps_hybrid_analysis_c(INTFLOAT (*out)[2], INTFLOAT (*in)[2], } } -static void ps_hybrid_analysis_ileave_c(INTFLOAT (*out)[32][2], INTFLOAT L[2][38][64], - int i, int len) +static void ps_hybrid_analysis_ileave_c(INTFLOAT (*av_restrict out)[32][2], + INTFLOAT L[2][38][64], + int i, int len) { - int j; - for (; i < 64; i++) { - for (j = 0; j < len; j++) { + for (int j = 0; j < len; j++) { out[i][j][0] = L[0][j][i]; out[i][j][1] = L[1][j][i]; } @@ -90,13 +90,11 @@ static void ps_hybrid_analysis_ileave_c(INTFLOAT (*out)[32][2], INTFLOAT L[2][38 } static void ps_hybrid_synthesis_deint_c(INTFLOAT out[2][38][64], - INTFLOAT (*in)[32][2], - int i, int len) + INTFLOAT (*av_restrict in)[32][2], + int i, int len) { - int n; - for (; i < 64; i++) { - for (n = 0; n < len; n++) { + for (int n = 0; n < len; n++) { out[0][n][i] = in[i][n][0]; out[1][n][i] = in[i][n][1]; } diff --git a/libavcodec/aacpsy.c b/libavcodec/aacpsy.c index 4c5ab2c9d5d..933369e445a 100644 --- a/libavcodec/aacpsy.c +++ b/libavcodec/aacpsy.c @@ -267,7 +267,7 @@ static av_cold void lame_window_init(AacPsyContext *ctx, AVCodecContext *avctx) AacPsyChannel *pch = &ctx->ch[i]; if (avctx->flags & AV_CODEC_FLAG_QSCALE) - pch->attack_threshold = psy_vbr_map[avctx->global_quality / FF_QP2LAMBDA].st_lrm; + pch->attack_threshold = psy_vbr_map[av_clip(avctx->global_quality / FF_QP2LAMBDA, 0, 10)].st_lrm; else pch->attack_threshold = lame_calc_attack_threshold(avctx->bit_rate / avctx->ch_layout.nb_channels / 1000); diff --git a/libavcodec/aactab.c b/libavcodec/aactab.c index 0f4941d5dff..d0006eac35e 100644 --- a/libavcodec/aactab.c +++ b/libavcodec/aactab.c @@ -48,8 +48,8 @@ DECLARE_ALIGNED(32, float, ff_aac_kbd_short_128)[128]; static av_cold void aac_float_common_init(void) { - ff_kbd_window_init(ff_aac_kbd_long_1024, 4.0, 1024); - ff_kbd_window_init(ff_aac_kbd_short_128, 6.0, 128); + avpriv_kbd_window_init(ff_aac_kbd_long_1024, 4.0, 1024); + avpriv_kbd_window_init(ff_aac_kbd_short_128, 6.0, 128); ff_init_ff_sine_windows(10); ff_init_ff_sine_windows(7); } diff --git a/libavcodec/aarch64/Makefile b/libavcodec/aarch64/Makefile index 02fb51c3ab9..cb428b49e0f 100644 --- a/libavcodec/aarch64/Makefile +++ b/libavcodec/aarch64/Makefile @@ -65,7 +65,9 @@ NEON-OBJS-$(CONFIG_VP9_DECODER) += aarch64/vp9itxfm_16bpp_neon.o \ aarch64/vp9lpf_neon.o \ aarch64/vp9mc_16bpp_neon.o \ aarch64/vp9mc_neon.o -NEON-OBJS-$(CONFIG_HEVC_DECODER) += aarch64/hevcdsp_idct_neon.o \ +NEON-OBJS-$(CONFIG_HEVC_DECODER) += aarch64/hevcdsp_deblock_neon.o \ + aarch64/hevcdsp_idct_neon.o \ aarch64/hevcdsp_init_aarch64.o \ aarch64/hevcdsp_qpel_neon.o \ + aarch64/hevcdsp_epel_neon.o \ aarch64/hevcdsp_sao_neon.o diff --git a/libavcodec/aarch64/hevcdsp_deblock_neon.S b/libavcodec/aarch64/hevcdsp_deblock_neon.S new file mode 100644 index 00000000000..8227f656496 --- /dev/null +++ b/libavcodec/aarch64/hevcdsp_deblock_neon.S @@ -0,0 +1,183 @@ +/* -*-arm64-*- + * vim: syntax=arm64asm + * + * Copyright (c) 2014 Seppo Tomperi + * Copyright (c) 2023 J. Dekker + * + * 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 "libavutil/aarch64/asm.S" +#include "neon.S" + +.macro hevc_loop_filter_chroma_start bitdepth + mov x4, x30 + ldr w14, [x2] + ldr w15, [x2, #4] +.if \bitdepth > 8 + lsl w14, w14, #(\bitdepth - 8) + lsl w15, w15, #(\bitdepth - 8) +.endif + adds w2, w14, w15 + b.eq 1f + dup v16.4h, w14 + dup v17.4h, w15 + trn1 v16.2d, v16.2d, v17.2d +.if \bitdepth > 8 + mvni v19.8h, #((0xff << (\bitdepth - 8)) & 0xff), lsl #8 + movi v18.8h, #0 +.endif + neg v17.8h, v16.8h +.endm + +.macro hevc_loop_filter_chroma_body bitdepth +.if \bitdepth <= 8 + uxtl v20.8h, v0.8b // p1 + uxtl v1.8h, v1.8b // p0 + uxtl v2.8h, v2.8b // q0 + uxtl v23.8h, v3.8b // q1 + va .req v20 + vb .req v23 +.else // required to specify both cases as we are unable to do: v0 .req v20 + va .req v0 + vb .req v3 +.endif + sub v5.8h, v2.8h, v1.8h // q0 - p0 + sub v6.8h, va.8h, vb.8h // p1 - q1 + shl v5.8h, v5.8h, #2 + add v5.8h, v6.8h, v5.8h + srshr v5.8h, v5.8h, #3 + clip v17.8h, v16.8h, v5.8h + sqadd v1.8h, v1.8h, v5.8h // p0 + delta + sqsub v2.8h, v2.8h, v5.8h // q0 - delta +.if \bitdepth <= 8 + sqxtun v1.8b, v1.8h + sqxtun v2.8b, v2.8h +.else + clip v18.8h, v19.8h, v1.8h, v2.8h +.endif +.unreq va +.unreq vb +.endm + +function hevc_loop_filter_chroma_body_8_neon, export=0 + hevc_loop_filter_chroma_body 8 + ret +endfunc + +function hevc_loop_filter_chroma_body_10_neon, export=0 +hevc_loop_filter_chroma_body_12_neon: + hevc_loop_filter_chroma_body 10 + ret +endfunc + +// void ff_hevc_h_loop_filter_chroma_8_neon(uint8_t *_pix, ptrdiff_t _stride, int *_tc, uint8_t *_no_p, uint8_t *_no_q); + +.macro hevc_h_loop_filter_chroma bitdepth +function ff_hevc_h_loop_filter_chroma_\bitdepth\()_neon, export=1 + hevc_loop_filter_chroma_start \bitdepth + sub x0, x0, x1, lsl #1 +.if \bitdepth > 8 + ld1 {v0.8h}, [x0], x1 + ld1 {v1.8h}, [x0], x1 + ld1 {v2.8h}, [x0], x1 + ld1 {v3.8h}, [x0] +.else + ld1 {v0.8b}, [x0], x1 + ld1 {v1.8b}, [x0], x1 + ld1 {v2.8b}, [x0], x1 + ld1 {v3.8b}, [x0] +.endif + sub x0, x0, x1, lsl #1 + bl hevc_loop_filter_chroma_body_\bitdepth\()_neon +.if \bitdepth > 8 + st1 {v1.8h}, [x0], x1 + st1 {v2.8h}, [x0] +.else + st1 {v1.8b}, [x0], x1 + st1 {v2.8b}, [x0] +.endif +1: ret x4 +endfunc +.endm + +.macro hevc_v_loop_filter_chroma bitdepth +function ff_hevc_v_loop_filter_chroma_\bitdepth\()_neon, export=1 + hevc_loop_filter_chroma_start \bitdepth +.if \bitdepth > 8 + sub x0, x0, #4 + add x3, x0, x1 + lsl x1, x1, #1 + ld1 {v0.d}[0], [x0], x1 + ld1 {v1.d}[0], [x3], x1 + ld1 {v2.d}[0], [x0], x1 + ld1 {v3.d}[0], [x3], x1 + ld1 {v0.d}[1], [x0], x1 + ld1 {v1.d}[1], [x3], x1 + ld1 {v2.d}[1], [x0], x1 + ld1 {v3.d}[1], [x3], x1 + transpose_4x8H v0, v1, v2, v3, v28, v29, v30, v31 +.else + sub x0, x0, #2 + add x3, x0, x1 + lsl x1, x1, #1 + ld1 {v0.s}[0], [x0], x1 + ld1 {v1.s}[0], [x3], x1 + ld1 {v2.s}[0], [x0], x1 + ld1 {v3.s}[0], [x3], x1 + ld1 {v0.s}[1], [x0], x1 + ld1 {v1.s}[1], [x3], x1 + ld1 {v2.s}[1], [x0], x1 + ld1 {v3.s}[1], [x3], x1 + transpose_4x8B v0, v1, v2, v3, v28, v29, v30, v31 +.endif + sub x0, x0, x1, lsl #2 + sub x3, x3, x1, lsl #2 + bl hevc_loop_filter_chroma_body_\bitdepth\()_neon +.if \bitdepth > 8 + transpose_4x8H v0, v1, v2, v3, v28, v29, v30, v31 + st1 {v0.d}[0], [x0], x1 + st1 {v1.d}[0], [x3], x1 + st1 {v2.d}[0], [x0], x1 + st1 {v3.d}[0], [x3], x1 + st1 {v0.d}[1], [x0], x1 + st1 {v1.d}[1], [x3], x1 + st1 {v2.d}[1], [x0], x1 + st1 {v3.d}[1], [x3] +.else + transpose_4x8B v0, v1, v2, v3, v28, v29, v30, v31 + st1 {v0.s}[0], [x0], x1 + st1 {v1.s}[0], [x3], x1 + st1 {v2.s}[0], [x0], x1 + st1 {v3.s}[0], [x3], x1 + st1 {v0.s}[1], [x0], x1 + st1 {v1.s}[1], [x3], x1 + st1 {v2.s}[1], [x0], x1 + st1 {v3.s}[1], [x3] +.endif +1: ret x4 +endfunc +.endm + +hevc_h_loop_filter_chroma 8 +hevc_h_loop_filter_chroma 10 +hevc_h_loop_filter_chroma 12 + +hevc_v_loop_filter_chroma 8 +hevc_v_loop_filter_chroma 10 +hevc_v_loop_filter_chroma 12 diff --git a/libavcodec/aarch64/hevcdsp_epel_neon.S b/libavcodec/aarch64/hevcdsp_epel_neon.S new file mode 100644 index 00000000000..a8d694639bd --- /dev/null +++ b/libavcodec/aarch64/hevcdsp_epel_neon.S @@ -0,0 +1,1891 @@ +/* -*-arm64-*- + * vim: syntax=arm64asm + * + * 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 "libavutil/aarch64/asm.S" +#define MAX_PB_SIZE 64 + +const epel_filters, align=4 + .byte 0, 0, 0, 0 + .byte -2, 58, 10, -2 + .byte -4, 54, 16, -2 + .byte -6, 46, 28, -4 + .byte -4, 36, 36, -4 + .byte -4, 28, 46, -6 + .byte -2, 16, 54, -4 + .byte -2, 10, 58, -2 +endconst + +#if HAVE_I8MM + +.macro EPEL_H_HEADER + movrel x5, epel_filters + add x5, x5, x4, lsl #2 + ld1r {v30.4s}, [x5] + sub x1, x1, #1 + mov x10, #(MAX_PB_SIZE * 2) +.endm + +function ff_hevc_put_hevc_epel_h4_8_neon_i8mm, export=1 + EPEL_H_HEADER +1: ld1 {v4.8b}, [x1], x2 + subs w3, w3, #1 // height + ext v5.8b, v4.8b, v4.8b, #1 + ext v6.8b, v4.8b, v4.8b, #2 + ext v7.8b, v4.8b, v4.8b, #3 + trn1 v4.2s, v4.2s, v5.2s + trn1 v6.2s, v6.2s, v7.2s + trn1 v4.2d, v4.2d, v6.2d + movi v16.2d, #0 + usdot v16.4s, v4.16b, v30.16b + xtn v16.4h, v16.4s + st1 {v16.4h}, [x0], x10 + b.ne 1b + ret +endfunc + + +function ff_hevc_put_hevc_epel_h6_8_neon_i8mm, export=1 + EPEL_H_HEADER +1: ld1 {v4.16b}, [x1], x2 + subs w3, w3, #1 // height + ext v5.16b, v4.16b, v4.16b, #1 + ext v6.8b, v4.8b, v4.8b, #2 + ext v7.8b, v4.8b, v4.8b, #3 + trn1 v16.2s, v4.2s, v5.2s + trn2 v17.2s, v4.2s, v5.2s + trn1 v6.2s, v6.2s, v7.2s + trn1 v16.2d, v16.2d, v6.2d + movi v18.2d, #0 + movi v19.2d, #0 + usdot v18.4s, v16.16b, v30.16b + usdot v19.2s, v17.8b, v30.8b + xtn v18.4h, v18.4s + xtn v19.4h, v19.4s + str d18, [x0] + str s19, [x0, #8] + add x0, x0, x10 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_h8_8_neon_i8mm, export=1 + EPEL_H_HEADER +1: ld1 {v4.16b}, [x1], x2 + subs w3, w3, #1 // height + ext v5.16b, v4.16b, v4.16b, #1 + ext v6.16b, v4.16b, v4.16b, #2 + ext v7.16b, v4.16b, v4.16b, #3 + zip1 v20.4s, v4.4s, v6.4s + zip1 v21.4s, v5.4s, v7.4s + movi v16.2d, #0 + movi v17.2d, #0 + usdot v16.4s, v20.16b, v30.16b + usdot v17.4s, v21.16b, v30.16b + xtn v16.4h, v16.4s + xtn v17.4h, v17.4s + st2 {v16.4h, v17.4h}, [x0], x10 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_h12_8_neon_i8mm, export=1 + EPEL_H_HEADER +1: ld1 {v4.16b}, [x1], x2 + subs w3, w3, #1 // height + ext v5.16b, v4.16b, v4.16b, #1 + ext v6.16b, v4.16b, v4.16b, #2 + ext v7.16b, v4.16b, v4.16b, #3 + trn1 v20.2d, v4.2d, v6.2d + trn2 v22.2d, v4.2d, v6.2d + trn1 v21.2d, v5.2d, v7.2d + trn2 v23.2d, v5.2d, v7.2d + trn1 v4.4s, v20.4s, v21.4s + trn2 v5.4s, v20.4s, v21.4s + trn1 v6.4s, v22.4s, v23.4s + movi v16.2d, #0 + movi v17.2d, #0 + movi v18.2d, #0 + usdot v16.4s, v4.16b, v30.16b + usdot v17.4s, v5.16b, v30.16b + usdot v18.4s, v6.16b, v30.16b + xtn v16.4h, v16.4s + xtn2 v16.8h, v17.4s + xtn v18.4h, v18.4s + str q16, [x0] + str d18, [x0, #16] + add x0, x0, x10 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_h16_8_neon_i8mm, export=1 + EPEL_H_HEADER +1: ld1 {v0.16b, v1.16b}, [x1], x2 + subs w3, w3, #1 // height + ext v5.16b, v0.16b, v1.16b, #1 + ext v6.16b, v0.16b, v1.16b, #2 + ext v7.16b, v0.16b, v1.16b, #3 + zip1 v20.4s, v0.4s, v6.4s + zip2 v22.4s, v0.4s, v6.4s + zip1 v21.4s, v5.4s, v7.4s + zip2 v23.4s, v5.4s, v7.4s + movi v16.2d, #0 + movi v17.2d, #0 + movi v18.2d, #0 + movi v19.2d, #0 + usdot v16.4s, v20.16b, v30.16b + usdot v17.4s, v21.16b, v30.16b + usdot v18.4s, v22.16b, v30.16b + usdot v19.4s, v23.16b, v30.16b + xtn v16.4h, v16.4s + xtn2 v16.8h, v18.4s + xtn v17.4h, v17.4s + xtn2 v17.8h, v19.4s + st2 {v16.8h, v17.8h}, [x0], x10 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_h24_8_neon_i8mm, export=1 + EPEL_H_HEADER +1: ld1 {v0.16b, v1.16b}, [x1], x2 + subs w3, w3, #1 // height + ext v5.16b, v0.16b, v1.16b, #1 + ext v6.16b, v0.16b, v1.16b, #2 + ext v7.16b, v0.16b, v1.16b, #3 + ext v26.16b, v1.16b, v1.16b, #1 + ext v27.16b, v1.16b, v1.16b, #2 + ext v28.16b, v1.16b, v1.16b, #3 + movi v16.2d, #0 + movi v17.2d, #0 + movi v18.2d, #0 + movi v19.2d, #0 + movi v20.2d, #0 + movi v21.2d, #0 + movi v22.2d, #0 + movi v23.2d, #0 + usdot v16.4s, v0.16b, v30.16b + usdot v17.4s, v5.16b, v30.16b + usdot v18.4s, v6.16b, v30.16b + usdot v19.4s, v7.16b, v30.16b + usdot v20.4s, v1.16b, v30.16b + usdot v21.4s, v26.16b, v30.16b + usdot v22.4s, v27.16b, v30.16b + usdot v23.4s, v28.16b, v30.16b + xtn v16.4h, v16.4s + xtn2 v16.8h, v20.4s + xtn v17.4h, v17.4s + xtn2 v17.8h, v21.4s + xtn v18.4h, v18.4s + xtn2 v18.8h, v22.4s + xtn v19.4h, v19.4s + xtn2 v19.8h, v23.4s + zip1 v20.8h, v16.8h, v18.8h + zip1 v21.8h, v17.8h, v19.8h + zip2 v22.8h, v16.8h, v18.8h + zip2 v23.8h, v17.8h, v19.8h + zip1 v22.8h, v22.8h, v23.8h + add x7, x0, #32 + st2 {v20.8h, v21.8h}, [x0], x10 + st1 {v22.8h}, [x7] + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_h32_8_neon_i8mm, export=1 + EPEL_H_HEADER +1: ld1 {v0.16b, v1.16b, v2.16b}, [x1], x2 + subs w3, w3, #1 // height + ext v5.16b, v0.16b, v1.16b, #1 + ext v6.16b, v0.16b, v1.16b, #2 + ext v7.16b, v0.16b, v1.16b, #3 + ext v26.16b, v1.16b, v2.16b, #1 + ext v27.16b, v1.16b, v2.16b, #2 + ext v28.16b, v1.16b, v2.16b, #3 + movi v16.2d, #0 + movi v17.2d, #0 + movi v18.2d, #0 + movi v19.2d, #0 + movi v20.2d, #0 + movi v21.2d, #0 + movi v22.2d, #0 + movi v23.2d, #0 + usdot v16.4s, v0.16b, v30.16b + usdot v17.4s, v5.16b, v30.16b + usdot v18.4s, v6.16b, v30.16b + usdot v19.4s, v7.16b, v30.16b + usdot v20.4s, v1.16b, v30.16b + usdot v21.4s, v26.16b, v30.16b + usdot v22.4s, v27.16b, v30.16b + usdot v23.4s, v28.16b, v30.16b + xtn v16.4h, v16.4s + xtn2 v16.8h, v20.4s + xtn v17.4h, v17.4s + xtn2 v17.8h, v21.4s + xtn v18.4h, v18.4s + xtn2 v18.8h, v22.4s + xtn v19.4h, v19.4s + xtn2 v19.8h, v23.4s + st4 {v16.8h, v17.8h, v18.8h, v19.8h}, [x0], x10 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_h48_8_neon_i8mm, export=1 + EPEL_H_HEADER +1: ld1 {v0.16b, v1.16b, v2.16b, v3.16b}, [x1], x2 + subs w3, w3, #1 // height + ext v4.16b, v0.16b, v1.16b, #1 + ext v5.16b, v0.16b, v1.16b, #2 + ext v6.16b, v0.16b, v1.16b, #3 + ext v16.16b, v1.16b, v2.16b, #1 + ext v17.16b, v1.16b, v2.16b, #2 + ext v18.16b, v1.16b, v2.16b, #3 + movi v20.2d, #0 + movi v21.2d, #0 + movi v22.2d, #0 + movi v23.2d, #0 + usdot v20.4s, v0.16b, v30.16b + usdot v21.4s, v4.16b, v30.16b + usdot v22.4s, v5.16b, v30.16b + usdot v23.4s, v6.16b, v30.16b + movi v24.2d, #0 + movi v25.2d, #0 + movi v26.2d, #0 + movi v27.2d, #0 + usdot v24.4s, v1.16b, v30.16b + usdot v25.4s, v16.16b, v30.16b + usdot v26.4s, v17.16b, v30.16b + usdot v27.4s, v18.16b, v30.16b + xtn v20.4h, v20.4s + xtn2 v20.8h, v24.4s + xtn v21.4h, v21.4s + xtn2 v21.8h, v25.4s + xtn v22.4h, v22.4s + xtn2 v22.8h, v26.4s + xtn v23.4h, v23.4s + xtn2 v23.8h, v27.4s + st4 {v20.8h, v21.8h, v22.8h, v23.8h}, [x0], x10 + ext v4.16b, v2.16b, v3.16b, #1 + ext v5.16b, v2.16b, v3.16b, #2 + ext v6.16b, v2.16b, v3.16b, #3 + movi v20.2d, #0 + movi v21.2d, #0 + movi v22.2d, #0 + movi v23.2d, #0 + usdot v20.4s, v2.16b, v30.16b + usdot v21.4s, v4.16b, v30.16b + usdot v22.4s, v5.16b, v30.16b + usdot v23.4s, v6.16b, v30.16b + xtn v20.4h, v20.4s + xtn2 v20.8h, v22.4s + xtn v21.4h, v21.4s + xtn2 v21.8h, v23.4s + add x7, x0, #64 + st2 {v20.8h, v21.8h}, [x7] + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_h64_8_neon_i8mm, export=1 + EPEL_H_HEADER + sub x2, x2, #64 +1: ld1 {v0.16b, v1.16b, v2.16b, v3.16b}, [x1], #64 + subs w3, w3, #1 // height + ext v4.16b, v0.16b, v1.16b, #1 + ext v5.16b, v0.16b, v1.16b, #2 + ext v6.16b, v0.16b, v1.16b, #3 + ext v16.16b, v1.16b, v2.16b, #1 + ext v17.16b, v1.16b, v2.16b, #2 + ext v18.16b, v1.16b, v2.16b, #3 + movi v20.2d, #0 + movi v21.2d, #0 + movi v22.2d, #0 + movi v23.2d, #0 + usdot v20.4s, v0.16b, v30.16b + usdot v21.4s, v4.16b, v30.16b + usdot v22.4s, v5.16b, v30.16b + usdot v23.4s, v6.16b, v30.16b + movi v24.2d, #0 + movi v25.2d, #0 + movi v26.2d, #0 + movi v27.2d, #0 + usdot v24.4s, v1.16b, v30.16b + usdot v25.4s, v16.16b, v30.16b + usdot v26.4s, v17.16b, v30.16b + usdot v27.4s, v18.16b, v30.16b + xtn v20.4h, v20.4s + xtn2 v20.8h, v24.4s + xtn v21.4h, v21.4s + xtn2 v21.8h, v25.4s + xtn v22.4h, v22.4s + xtn2 v22.8h, v26.4s + xtn v23.4h, v23.4s + xtn2 v23.8h, v27.4s + st4 {v20.8h, v21.8h, v22.8h, v23.8h}, [x0], #64 + ld1 {v7.8b}, [x1], x2 + ext v4.16b, v2.16b, v3.16b, #1 + ext v5.16b, v2.16b, v3.16b, #2 + ext v6.16b, v2.16b, v3.16b, #3 + ext v16.16b, v3.16b, v7.16b, #1 + ext v17.16b, v3.16b, v7.16b, #2 + ext v18.16b, v3.16b, v7.16b, #3 + movi v20.2d, #0 + movi v21.2d, #0 + movi v22.2d, #0 + movi v23.2d, #0 + usdot v20.4s, v2.16b, v30.16b + usdot v21.4s, v4.16b, v30.16b + usdot v22.4s, v5.16b, v30.16b + usdot v23.4s, v6.16b, v30.16b + movi v24.2d, #0 + movi v25.2d, #0 + movi v26.2d, #0 + movi v27.2d, #0 + usdot v24.4s, v3.16b, v30.16b + usdot v25.4s, v16.16b, v30.16b + usdot v26.4s, v17.16b, v30.16b + usdot v27.4s, v18.16b, v30.16b + xtn v20.4h, v20.4s + xtn2 v20.8h, v24.4s + xtn v21.4h, v21.4s + xtn2 v21.8h, v25.4s + xtn v22.4h, v22.4s + xtn2 v22.8h, v26.4s + xtn v23.4h, v23.4s + xtn2 v23.8h, v27.4s + st4 {v20.8h, v21.8h, v22.8h, v23.8h}, [x0], #64 + b.ne 1b + ret +endfunc + +.macro EPEL_UNI_W_H_HEADER + ldr x12, [sp] + sub x2, x2, #1 + movrel x9, epel_filters + add x9, x9, x12, lsl #2 + ld1r {v28.4s}, [x9] + mov w10, #-6 + sub w10, w10, w5 + dup v30.4s, w6 + dup v31.4s, w10 + dup v29.4s, w7 +.endm + + +function ff_hevc_put_hevc_epel_uni_w_h4_8_neon_i8mm, export=1 + EPEL_UNI_W_H_HEADER +1: + ld1 {v0.8b}, [x2], x3 + subs w4, w4, #1 + ext v1.8b, v0.8b, v0.8b, #1 + ext v2.8b, v0.8b, v0.8b, #2 + ext v3.8b, v0.8b, v0.8b, #3 + trn1 v0.2s, v0.2s, v2.2s + trn1 v1.2s, v1.2s, v3.2s + zip1 v0.4s, v0.4s, v1.4s + movi v16.2d, #0 + usdot v16.4s, v0.16b, v28.16b + mul v16.4s, v16.4s, v30.4s + sqrshl v16.4s, v16.4s, v31.4s + sqadd v16.4s, v16.4s, v29.4s + sqxtn v16.4h, v16.4s + sqxtun v16.8b, v16.8h + str s16, [x0] + add x0, x0, x1 + b.hi 1b + ret +endfunc + + +function ff_hevc_put_hevc_epel_uni_w_h6_8_neon_i8mm, export=1 + EPEL_UNI_W_H_HEADER + sub x1, x1, #4 +1: + ld1 {v0.16b}, [x2], x3 + subs w4, w4, #1 + ext v1.16b, v0.16b, v0.16b, #1 + ext v2.16b, v0.16b, v0.16b, #2 + ext v3.16b, v0.16b, v0.16b, #3 + trn1 v4.2s, v0.2s, v1.2s + trn2 v6.2s, v0.2s, v1.2s + trn1 v5.2s, v2.2s, v3.2s + zip1 v4.2d, v4.2d, v5.2d + movi v16.2d, #0 + movi v17.2d, #0 + usdot v16.4s, v4.16b, v28.16b + usdot v17.2s, v6.8b, v28.8b + mul v16.4s, v16.4s, v30.4s + mul v17.2s, v17.2s, v30.2s + sqrshl v16.4s, v16.4s, v31.4s + sqrshl v17.2s, v17.2s, v31.2s + sqadd v16.4s, v16.4s, v29.4s + sqadd v17.2s, v17.2s, v29.2s + sqxtn v16.4h, v16.4s + sqxtn2 v16.8h, v17.4s + sqxtun v16.8b, v16.8h + str s16, [x0], #4 + st1 {v16.h}[2], [x0], x1 + b.hi 1b + ret +endfunc + +.macro EPEL_UNI_W_H_CALC s0, s1, d0, d1 + movi \d0\().2d, #0 + movi \d1\().2d, #0 + usdot \d0\().4s, \s0\().16b, v28.16b + usdot \d1\().4s, \s1\().16b, v28.16b + mul \d0\().4s, \d0\().4s, v30.4s + mul \d1\().4s, \d1\().4s, v30.4s + sqrshl \d0\().4s, \d0\().4s, v31.4s + sqrshl \d1\().4s, \d1\().4s, v31.4s + sqadd \d0\().4s, \d0\().4s, v29.4s + sqadd \d1\().4s, \d1\().4s, v29.4s +.endm + +function ff_hevc_put_hevc_epel_uni_w_h8_8_neon_i8mm, export=1 + EPEL_UNI_W_H_HEADER +1: + ld1 {v0.16b}, [x2], x3 + subs w4, w4, #1 + ext v1.16b, v0.16b, v0.16b, #1 + ext v2.16b, v0.16b, v0.16b, #2 + ext v3.16b, v0.16b, v0.16b, #3 + zip1 v4.4s, v0.4s, v2.4s + zip1 v5.4s, v1.4s, v3.4s + EPEL_UNI_W_H_CALC v4, v5, v16, v17 + sqxtn v16.4h, v16.4s + sqxtn v17.4h, v17.4s + zip1 v16.8h, v16.8h, v17.8h + sqxtun v16.8b, v16.8h + str d16, [x0] + add x0, x0, x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h12_8_neon_i8mm, export=1 + EPEL_UNI_W_H_HEADER +1: + ld1 {v0.16b}, [x2], x3 + subs w4, w4, #1 + ext v1.16b, v0.16b, v0.16b, #1 + ext v2.16b, v0.16b, v0.16b, #2 + ext v3.16b, v0.16b, v0.16b, #3 + zip1 v4.4s, v0.4s, v2.4s + zip1 v5.4s, v1.4s, v3.4s + zip2 v6.4s, v0.4s, v2.4s + zip2 v7.4s, v1.4s, v3.4s + zip1 v6.4s, v6.4s, v7.4s + EPEL_UNI_W_H_CALC v4, v5, v16, v17 + movi v18.2d, #0 + usdot v18.4s, v6.16b, v28.16b + mul v18.4s, v18.4s, v30.4s + sqrshl v18.4s, v18.4s, v31.4s + sqadd v18.4s, v18.4s, v29.4s + sqxtn v16.4h, v16.4s + sqxtn v17.4h, v17.4s + sqxtn v18.4h, v18.4s + zip1 v16.8h, v16.8h, v17.8h + sqxtun v16.8b, v16.8h + sqxtun v18.8b, v18.8h + str d16, [x0] + str s18, [x0, #8] + add x0, x0, x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h16_8_neon_i8mm, export=1 + EPEL_UNI_W_H_HEADER +1: + ld1 {v0.16b, v1.16b}, [x2], x3 + subs w4, w4, #1 + ext v4.16b, v0.16b, v1.16b, #1 + ext v5.16b, v0.16b, v1.16b, #2 + ext v6.16b, v0.16b, v1.16b, #3 + zip1 v20.4s, v0.4s, v5.4s + zip1 v21.4s, v4.4s, v6.4s + zip2 v22.4s, v0.4s, v5.4s + zip2 v23.4s, v4.4s, v6.4s + EPEL_UNI_W_H_CALC v20, v21, v16, v17 + EPEL_UNI_W_H_CALC v22, v23, v18, v19 + sqxtn v16.4h, v16.4s + sqxtn v17.4h, v17.4s + sqxtn2 v16.8h, v18.4s + sqxtn2 v17.8h, v19.4s + sqxtun v16.8b, v16.8h + sqxtun v17.8b, v17.8h + st2 {v16.8b, v17.8b}, [x0], x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h24_8_neon_i8mm, export=1 + EPEL_UNI_W_H_HEADER +1: + ld1 {v0.16b, v1.16b}, [x2], x3 + subs w4, w4, #1 + ext v2.16b, v0.16b, v1.16b, #1 + ext v3.16b, v0.16b, v1.16b, #2 + ext v4.16b, v0.16b, v1.16b, #3 + ext v5.16b, v1.16b, v1.16b, #1 + ext v6.16b, v1.16b, v1.16b, #2 + ext v7.16b, v1.16b, v1.16b, #3 + zip1 v20.4s, v0.4s, v3.4s + zip1 v21.4s, v2.4s, v4.4s + zip2 v22.4s, v0.4s, v3.4s + zip2 v23.4s, v2.4s, v4.4s + zip1 v24.4s, v1.4s, v6.4s + zip1 v25.4s, v5.4s, v7.4s + EPEL_UNI_W_H_CALC v20, v21, v16, v17 + EPEL_UNI_W_H_CALC v22, v23, v18, v19 + EPEL_UNI_W_H_CALC v24, v25, v26, v27 + sqxtn v16.4h, v16.4s + sqxtn v17.4h, v17.4s + sqxtn v18.4h, v18.4s + sqxtn v19.4h, v19.4s + sqxtn v26.4h, v26.4s + sqxtn v27.4h, v27.4s + zip1 v16.8h, v16.8h, v17.8h + zip1 v18.8h, v18.8h, v19.8h + zip1 v26.8h, v26.8h, v27.8h + sqxtun v16.8b, v16.8h + sqxtun2 v16.16b, v18.8h + sqxtun v26.8b, v26.8h + str q16, [x0] + str d26, [x0, #16] + add x0, x0, x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h32_8_neon_i8mm, export=1 + EPEL_UNI_W_H_HEADER +1: + ld1 {v0.16b, v1.16b, v2.16b}, [x2], x3 + subs w4, w4, #1 + ext v3.16b, v0.16b, v1.16b, #1 + ext v4.16b, v0.16b, v1.16b, #2 + ext v5.16b, v0.16b, v1.16b, #3 + ext v16.16b, v1.16b, v2.16b, #1 + ext v17.16b, v1.16b, v2.16b, #2 + ext v18.16b, v1.16b, v2.16b, #3 + EPEL_UNI_W_H_CALC v0, v3, v6, v7 + EPEL_UNI_W_H_CALC v4, v5, v19, v20 + EPEL_UNI_W_H_CALC v1, v16, v21, v22 + EPEL_UNI_W_H_CALC v17, v18, v23, v24 + sqxtn v6.4h, v6.4s + sqxtn2 v6.8h, v21.4s + sqxtn v7.4h, v7.4s + sqxtn2 v7.8h, v22.4s + sqxtn v19.4h, v19.4s + sqxtn2 v19.8h, v23.4s + sqxtn v20.4h, v20.4s + sqxtn2 v20.8h, v24.4s + sqxtun v0.8b, v6.8h + sqxtun v1.8b, v7.8h + sqxtun v2.8b, v19.8h + sqxtun v3.8b, v20.8h + st4 {v0.8b, v1.8b, v2.8b, v3.8b}, [x0], x1 + b.hi 1b + ret +endfunc + + + +function ff_hevc_put_hevc_epel_uni_w_h48_8_neon_i8mm, export=1 + EPEL_UNI_W_H_HEADER + sub x1, x1, #32 +1: + ld1 {v0.16b, v1.16b, v2.16b, v3.16b}, [x2], x3 + subs w4, w4, #1 + ext v4.16b, v0.16b, v1.16b, #1 + ext v5.16b, v0.16b, v1.16b, #2 + ext v6.16b, v0.16b, v1.16b, #3 + ext v16.16b, v1.16b, v2.16b, #1 + ext v17.16b, v1.16b, v2.16b, #2 + ext v18.16b, v1.16b, v2.16b, #3 + EPEL_UNI_W_H_CALC v0, v4, v19, v20 + EPEL_UNI_W_H_CALC v5, v6, v21, v22 + EPEL_UNI_W_H_CALC v1, v16, v23, v24 + EPEL_UNI_W_H_CALC v17, v18, v25, v26 + sqxtn v19.4h, v19.4s + sqxtn2 v19.8h, v23.4s + sqxtn v20.4h, v20.4s + sqxtn2 v20.8h, v24.4s + sqxtn v21.4h, v21.4s + sqxtn2 v21.8h, v25.4s + sqxtn v22.4h, v22.4s + sqxtn2 v22.8h, v26.4s + sqxtun v19.8b, v19.8h + sqxtun v20.8b, v20.8h + sqxtun v21.8b, v21.8h + sqxtun v22.8b, v22.8h + st4 {v19.8b, v20.8b, v21.8b, v22.8b}, [x0], #32 + ext v5.16b, v2.16b, v3.16b, #1 + ext v6.16b, v2.16b, v3.16b, #2 + ext v7.16b, v2.16b, v3.16b, #3 + EPEL_UNI_W_H_CALC v2, v5, v19, v20 + EPEL_UNI_W_H_CALC v6, v7, v21, v22 + sqxtn v19.4h, v19.4s + sqxtn v20.4h, v20.4s + sqxtn v21.4h, v21.4s + sqxtn v22.4h, v22.4s + zip1 v4.8h, v19.8h, v21.8h + zip1 v5.8h, v20.8h, v22.8h + sqxtun v4.8b, v4.8h + sqxtun v5.8b, v5.8h + st2 {v4.8b, v5.8b}, [x0], x1 + b.hi 1b + ret +endfunc + + +function ff_hevc_put_hevc_epel_uni_w_h64_8_neon_i8mm, export=1 + EPEL_UNI_W_H_HEADER + sub x1, x1, #32 + sub x3, x3, #64 +1: + ld1 {v0.16b, v1.16b, v2.16b, v3.16b}, [x2], #64 + subs w4, w4, #1 + ext v4.16b, v0.16b, v1.16b, #1 + ext v5.16b, v0.16b, v1.16b, #2 + ext v6.16b, v0.16b, v1.16b, #3 + ext v16.16b, v1.16b, v2.16b, #1 + ext v17.16b, v1.16b, v2.16b, #2 + ext v18.16b, v1.16b, v2.16b, #3 + EPEL_UNI_W_H_CALC v0, v4, v19, v20 + EPEL_UNI_W_H_CALC v5, v6, v21, v22 + EPEL_UNI_W_H_CALC v1, v16, v23, v24 + EPEL_UNI_W_H_CALC v17, v18, v25, v26 + sqxtn v19.4h, v19.4s + sqxtn2 v19.8h, v23.4s + sqxtn v20.4h, v20.4s + sqxtn2 v20.8h, v24.4s + sqxtn v21.4h, v21.4s + sqxtn2 v21.8h, v25.4s + sqxtn v22.4h, v22.4s + sqxtn2 v22.8h, v26.4s + sqxtun v19.8b, v19.8h + sqxtun v20.8b, v20.8h + sqxtun v21.8b, v21.8h + sqxtun v22.8b, v22.8h + st4 {v19.8b, v20.8b, v21.8b, v22.8b}, [x0], #32 + ld1 {v7.8b}, [x2], x3 + ext v4.16b, v2.16b, v3.16b, #1 + ext v5.16b, v2.16b, v3.16b, #2 + ext v6.16b, v2.16b, v3.16b, #3 + ext v16.16b, v3.16b, v7.16b, #1 + ext v17.16b, v3.16b, v7.16b, #2 + ext v18.16b, v3.16b, v7.16b, #3 + EPEL_UNI_W_H_CALC v2, v4, v19, v20 + EPEL_UNI_W_H_CALC v5, v6, v21, v22 + EPEL_UNI_W_H_CALC v3, v16, v23, v24 + EPEL_UNI_W_H_CALC v17, v18, v25, v26 + sqxtn v19.4h, v19.4s + sqxtn2 v19.8h, v23.4s + sqxtn v20.4h, v20.4s + sqxtn2 v20.8h, v24.4s + sqxtn v21.4h, v21.4s + sqxtn2 v21.8h, v25.4s + sqxtn v22.4h, v22.4s + sqxtn2 v22.8h, v26.4s + sqxtun v19.8b, v19.8h + sqxtun v20.8b, v20.8h + sqxtun v21.8b, v21.8h + sqxtun v22.8b, v22.8h + st4 {v19.8b, v20.8b, v21.8b, v22.8b}, [x0], x1 + b.hi 1b + ret +endfunc + +.macro epel_uni_w_hv_start + mov x15, x5 //denom + mov x16, x6 //wx + mov x17, x7 //ox + add w15, w15, #6 //shift = denom+6 + + + ldp x5, x6, [sp] + ldr x7, [sp, #16] + + stp d14, d15, [sp, #-64]! + stp d8, d9, [sp, #16] + stp d10, d11, [sp, #32] + stp d12, d13, [sp, #48] + + dup v13.8h, w16 //wx + dup v14.4s, w17 //ox + + mov w17, #1 + lsl w17, w17, w15 + lsr w17, w17, #1 + dup v15.4s, w17 + + neg w15, w15 // -shift + dup v12.4s, w15 //shift +.endm + +.macro epel_uni_w_hv_end + smull v28.4s, v4.4h, v13.4h + smull2 v29.4s, v4.8h, v13.8h + add v28.4s, v28.4s, v15.4s + add v29.4s, v29.4s, v15.4s + sshl v28.4s, v28.4s, v12.4s + sshl v29.4s, v29.4s, v12.4s + add v28.4s, v28.4s, v14.4s + add v29.4s, v29.4s, v14.4s + sqxtn v4.4h, v28.4s + sqxtn2 v4.8h, v29.4s +.endm + +.macro epel_uni_w_hv_end2 + smull v28.4s, v4.4h, v13.4h + smull2 v29.4s, v4.8h, v13.8h + smull v30.4s, v5.4h, v13.4h + smull2 v31.4s, v5.8h, v13.8h + add v28.4s, v28.4s, v15.4s + add v29.4s, v29.4s, v15.4s + add v30.4s, v30.4s, v15.4s + add v31.4s, v31.4s, v15.4s + + sshl v28.4s, v28.4s, v12.4s + sshl v29.4s, v29.4s, v12.4s + sshl v30.4s, v30.4s, v12.4s + sshl v31.4s, v31.4s, v12.4s + + add v28.4s, v28.4s, v14.4s + add v29.4s, v29.4s, v14.4s + add v30.4s, v30.4s, v14.4s + add v31.4s, v31.4s, v14.4s + + sqxtn v4.4h, v28.4s + sqxtn2 v4.8h, v29.4s + sqxtn v5.4h, v30.4s + sqxtn2 v5.8h, v31.4s +.endm + +.macro epel_uni_w_hv_end3 + smull v1.4s, v4.4h, v13.4h + smull2 v2.4s, v4.8h, v13.8h + smull v28.4s, v5.4h, v13.4h + smull2 v29.4s, v5.8h, v13.8h + smull v30.4s, v6.4h, v13.4h + smull2 v31.4s, v6.8h, v13.8h + add v1.4s, v1.4s, v15.4s + add v2.4s, v2.4s, v15.4s + add v28.4s, v28.4s, v15.4s + add v29.4s, v29.4s, v15.4s + add v30.4s, v30.4s, v15.4s + add v31.4s, v31.4s, v15.4s + + sshl v1.4s, v1.4s, v12.4s + sshl v2.4s, v2.4s, v12.4s + sshl v28.4s, v28.4s, v12.4s + sshl v29.4s, v29.4s, v12.4s + sshl v30.4s, v30.4s, v12.4s + sshl v31.4s, v31.4s, v12.4s + add v1.4s, v1.4s, v14.4s + add v2.4s, v2.4s, v14.4s + add v28.4s, v28.4s, v14.4s + add v29.4s, v29.4s, v14.4s + add v30.4s, v30.4s, v14.4s + add v31.4s, v31.4s, v14.4s + + sqxtn v4.4h, v1.4s + sqxtn2 v4.8h, v2.4s + sqxtn v5.4h, v28.4s + sqxtn2 v5.8h, v29.4s + sqxtn v6.4h, v30.4s + sqxtn2 v6.8h, v31.4s +.endm + +.macro calc_epelh dst, src0, src1, src2, src3 + smull \dst\().4s, \src0\().4h, v0.h[0] + smlal \dst\().4s, \src1\().4h, v0.h[1] + smlal \dst\().4s, \src2\().4h, v0.h[2] + smlal \dst\().4s, \src3\().4h, v0.h[3] + sqshrn \dst\().4h, \dst\().4s, #6 +.endm + +.macro calc_epelh2 dst, tmp, src0, src1, src2, src3 + smull2 \tmp\().4s, \src0\().8h, v0.h[0] + smlal2 \tmp\().4s, \src1\().8h, v0.h[1] + smlal2 \tmp\().4s, \src2\().8h, v0.h[2] + smlal2 \tmp\().4s, \src3\().8h, v0.h[3] + sqshrn2 \dst\().8h, \tmp\().4s, #6 +.endm + +.macro load_epel_filterh freg, xreg + movrel \xreg, epel_filters + add \xreg, \xreg, \freg, lsl #2 + ld1 {v0.8b}, [\xreg] + sxtl v0.8h, v0.8b +.endm + +function ff_hevc_put_hevc_epel_uni_w_hv4_8_neon_i8mm, export=1 + epel_uni_w_hv_start + sxtw x4, w4 + + add x10, x4, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + str x30, [sp, #-48]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + add x0, sp, #48 + sub x1, x2, x3 + mov x2, x3 + add x3, x4, #3 + mov x4, x5 + bl X(ff_hevc_put_hevc_epel_h4_8_neon_i8mm) + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x30, [sp], #48 + load_epel_filterh x6, x5 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.4h}, [sp], x10 + ld1 {v17.4h}, [sp], x10 + ld1 {v18.4h}, [sp], x10 +1: ld1 {v19.4h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v16, v17, v18, v19 + epel_uni_w_hv_end + sqxtun v4.8b, v4.8h + str s4, [x0] + add x0, x0, x1 + b.eq 2f + + ld1 {v16.4h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v17, v18, v19, v16 + epel_uni_w_hv_end + sqxtun v4.8b, v4.8h + str s4, [x0] + add x0, x0, x1 + b.eq 2f + + ld1 {v17.4h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v18, v19, v16, v17 + epel_uni_w_hv_end + sqxtun v4.8b, v4.8h + str s4, [x0] + add x0, x0, x1 + b.eq 2f + + ld1 {v18.4h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v19, v16, v17, v18 + epel_uni_w_hv_end + sqxtun v4.8b, v4.8h + str s4, [x0] + add x0, x0, x1 + b.ne 1b +2: + ldp d8, d9, [sp, #16] + ldp d10, d11, [sp, #32] + ldp d12, d13, [sp, #48] + ldp d14, d15, [sp], #64 + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv6_8_neon_i8mm, export=1 + epel_uni_w_hv_start + sxtw x4, w4 + + add x10, x4, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + str x30, [sp, #-48]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + add x0, sp, #48 + sub x1, x2, x3 + mov x2, x3 + add x3, x4, #3 + mov x4, x5 + bl X(ff_hevc_put_hevc_epel_h6_8_neon_i8mm) + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x30, [sp], #48 + load_epel_filterh x6, x5 + sub x1, x1, #4 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8h}, [sp], x10 + ld1 {v17.8h}, [sp], x10 + ld1 {v18.8h}, [sp], x10 +1: ld1 {v19.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v16, v17, v18, v19 + calc_epelh2 v4, v5, v16, v17, v18, v19 + epel_uni_w_hv_end + sqxtun v4.8b, v4.8h + st1 {v4.s}[0], [x0], #4 + st1 {v4.h}[2], [x0], x1 + b.eq 2f + + ld1 {v16.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v17, v18, v19, v16 + calc_epelh2 v4, v5, v17, v18, v19, v16 + epel_uni_w_hv_end + sqxtun v4.8b, v4.8h + st1 {v4.s}[0], [x0], #4 + st1 {v4.h}[2], [x0], x1 + b.eq 2f + + ld1 {v17.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v18, v19, v16, v17 + calc_epelh2 v4, v5, v18, v19, v16, v17 + epel_uni_w_hv_end + sqxtun v4.8b, v4.8h + st1 {v4.s}[0], [x0], #4 + st1 {v4.h}[2], [x0], x1 + b.eq 2f + + ld1 {v18.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v19, v16, v17, v18 + calc_epelh2 v4, v5, v19, v16, v17, v18 + epel_uni_w_hv_end + sqxtun v4.8b, v4.8h + st1 {v4.s}[0], [x0], #4 + st1 {v4.h}[2], [x0], x1 + b.ne 1b +2: + ldp d8, d9, [sp, #16] + ldp d10, d11, [sp, #32] + ldp d12, d13, [sp, #48] + ldp d14, d15, [sp], #64 + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv8_8_neon_i8mm, export=1 + epel_uni_w_hv_start + sxtw x4, w4 + + add x10, x4, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + str x30, [sp, #-48]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + add x0, sp, #48 + sub x1, x2, x3 + mov x2, x3 + add x3, x4, #3 + mov x4, x5 + bl X(ff_hevc_put_hevc_epel_h8_8_neon_i8mm) + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x30, [sp], #48 + load_epel_filterh x6, x5 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8h}, [sp], x10 + ld1 {v17.8h}, [sp], x10 + ld1 {v18.8h}, [sp], x10 +1: ld1 {v19.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v16, v17, v18, v19 + calc_epelh2 v4, v5, v16, v17, v18, v19 + epel_uni_w_hv_end + sqxtun v4.8b, v4.8h + st1 {v4.8b}, [x0], x1 + b.eq 2f + + ld1 {v16.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v17, v18, v19, v16 + calc_epelh2 v4, v5, v17, v18, v19, v16 + epel_uni_w_hv_end + sqxtun v4.8b, v4.8h + st1 {v4.8b}, [x0], x1 + b.eq 2f + + ld1 {v17.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v18, v19, v16, v17 + calc_epelh2 v4, v5, v18, v19, v16, v17 + epel_uni_w_hv_end + sqxtun v4.8b, v4.8h + st1 {v4.8b}, [x0], x1 + b.eq 2f + + ld1 {v18.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v19, v16, v17, v18 + calc_epelh2 v4, v5, v19, v16, v17, v18 + epel_uni_w_hv_end + sqxtun v4.8b, v4.8h + st1 {v4.8b}, [x0], x1 + b.ne 1b +2: + ldp d8, d9, [sp, #16] + ldp d10, d11, [sp, #32] + ldp d12, d13, [sp, #48] + ldp d14, d15, [sp], #64 + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv12_8_neon_i8mm, export=1 + epel_uni_w_hv_start + sxtw x4, w4 + + add x10, x4, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + str x30, [sp, #-48]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + add x0, sp, #48 + sub x1, x2, x3 + mov x2, x3 + add x3, x4, #3 + mov x4, x5 + bl X(ff_hevc_put_hevc_epel_h12_8_neon_i8mm) + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x30, [sp], #48 + load_epel_filterh x6, x5 + sub x1, x1, #8 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8h, v17.8h}, [sp], x10 + ld1 {v18.8h, v19.8h}, [sp], x10 + ld1 {v20.8h, v21.8h}, [sp], x10 +1: ld1 {v22.8h, v23.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v16, v18, v20, v22 + calc_epelh2 v4, v5, v16, v18, v20, v22 + calc_epelh v5, v17, v19, v21, v23 + epel_uni_w_hv_end2 + sqxtun v4.8b, v4.8h + sqxtun2 v4.16b, v5.8h + st1 {v4.8b}, [x0], #8 + st1 {v4.s}[2], [x0], x1 + b.eq 2f + + ld1 {v16.8h, v17.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v18, v20, v22, v16 + calc_epelh2 v4, v5, v18, v20, v22, v16 + calc_epelh v5, v19, v21, v23, v17 + epel_uni_w_hv_end2 + sqxtun v4.8b, v4.8h + sqxtun2 v4.16b, v5.8h + st1 {v4.8b}, [x0], #8 + st1 {v4.s}[2], [x0], x1 + b.eq 2f + + ld1 {v18.8h, v19.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v20, v22, v16, v18 + calc_epelh2 v4, v5, v20, v22, v16, v18 + calc_epelh v5, v21, v23, v17, v19 + epel_uni_w_hv_end2 + sqxtun v4.8b, v4.8h + sqxtun2 v4.16b, v5.8h + st1 {v4.8b}, [x0], #8 + st1 {v4.s}[2], [x0], x1 + b.eq 2f + + ld1 {v20.8h, v21.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v22, v16, v18, v20 + calc_epelh2 v4, v5, v22, v16, v18, v20 + calc_epelh v5, v23, v17, v19, v21 + epel_uni_w_hv_end2 + sqxtun v4.8b, v4.8h + sqxtun2 v4.16b, v5.8h + st1 {v4.8b}, [x0], #8 + st1 {v4.s}[2], [x0], x1 + b.ne 1b +2: + ldp d8, d9, [sp, #16] + ldp d10, d11, [sp, #32] + ldp d12, d13, [sp, #48] + ldp d14, d15, [sp], #64 + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv16_8_neon_i8mm, export=1 + epel_uni_w_hv_start + sxtw x4, w4 + + add x10, x4, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + str x30, [sp, #-48]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + add x0, sp, #48 + sub x1, x2, x3 + mov x2, x3 + add x3, x4, #3 + mov x4, x5 + bl X(ff_hevc_put_hevc_epel_h16_8_neon_i8mm) + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x30, [sp], #48 + load_epel_filterh x6, x5 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8h, v17.8h}, [sp], x10 + ld1 {v18.8h, v19.8h}, [sp], x10 + ld1 {v20.8h, v21.8h}, [sp], x10 +1: ld1 {v22.8h, v23.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v16, v18, v20, v22 + calc_epelh2 v4, v5, v16, v18, v20, v22 + calc_epelh v5, v17, v19, v21, v23 + calc_epelh2 v5, v6, v17, v19, v21, v23 + epel_uni_w_hv_end2 + sqxtun v4.8b, v4.8h + sqxtun2 v4.16b, v5.8h + st1 {v4.16b}, [x0], x1 + b.eq 2f + + ld1 {v16.8h, v17.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v18, v20, v22, v16 + calc_epelh2 v4, v5, v18, v20, v22, v16 + calc_epelh v5, v19, v21, v23, v17 + calc_epelh2 v5, v6, v19, v21, v23, v17 + epel_uni_w_hv_end2 + sqxtun v4.8b, v4.8h + sqxtun2 v4.16b, v5.8h + st1 {v4.16b}, [x0], x1 + b.eq 2f + + ld1 {v18.8h, v19.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v20, v22, v16, v18 + calc_epelh2 v4, v5, v20, v22, v16, v18 + calc_epelh v5, v21, v23, v17, v19 + calc_epelh2 v5, v6, v21, v23, v17, v19 + epel_uni_w_hv_end2 + sqxtun v4.8b, v4.8h + sqxtun2 v4.16b, v5.8h + st1 {v4.16b}, [x0], x1 + b.eq 2f + + ld1 {v20.8h, v21.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v22, v16, v18, v20 + calc_epelh2 v4, v5, v22, v16, v18, v20 + calc_epelh v5, v23, v17, v19, v21 + calc_epelh2 v5, v6, v23, v17, v19, v21 + epel_uni_w_hv_end2 + sqxtun v4.8b, v4.8h + sqxtun2 v4.16b, v5.8h + st1 {v4.16b}, [x0], x1 + b.ne 1b +2: + ldp d8, d9, [sp, #16] + ldp d10, d11, [sp, #32] + ldp d12, d13, [sp, #48] + ldp d14, d15, [sp], #64 + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv24_8_neon_i8mm, export=1 + epel_uni_w_hv_start + sxtw x4, w4 + + add x10, x4, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + str x30, [sp, #-48]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + add x0, sp, #48 + sub x1, x2, x3 + mov x2, x3 + add x3, x4, #3 + mov x4, x5 + bl X(ff_hevc_put_hevc_epel_h24_8_neon_i8mm) + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x30, [sp], #48 + load_epel_filterh x6, x5 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8h, v17.8h, v18.8h}, [sp], x10 + ld1 {v19.8h, v20.8h, v21.8h}, [sp], x10 + ld1 {v22.8h, v23.8h, v24.8h}, [sp], x10 +1: ld1 {v25.8h, v26.8h, v27.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v16, v19, v22, v25 + calc_epelh2 v4, v5, v16, v19, v22, v25 + calc_epelh v5, v17, v20, v23, v26 + calc_epelh2 v5, v6, v17, v20, v23, v26 + calc_epelh v6, v18, v21, v24, v27 + calc_epelh2 v6, v7, v18, v21, v24, v27 + + epel_uni_w_hv_end3 + sqxtun v4.8b, v4.8h + sqxtun v5.8b, v5.8h + sqxtun v6.8b, v6.8h + st1 {v4.8b, v5.8b, v6.8b}, [x0], x1 + b.eq 2f + + ld1 {v16.8h, v17.8h, v18.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v19, v22, v25, v16 + calc_epelh2 v4, v5, v19, v22, v25, v16 + calc_epelh v5, v20, v23, v26, v17 + calc_epelh2 v5, v6, v20, v23, v26, v17 + calc_epelh v6, v21, v24, v27, v18 + calc_epelh2 v6, v7, v21, v24, v27, v18 + epel_uni_w_hv_end3 + + sqxtun v4.8b, v4.8h + sqxtun v5.8b, v5.8h + sqxtun v6.8b, v6.8h + st1 {v4.8b, v5.8b, v6.8b}, [x0], x1 + b.eq 2f + + ld1 {v19.8h, v20.8h, v21.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v22, v25, v16, v19 + calc_epelh2 v4, v5, v22, v25, v16, v19 + calc_epelh v5, v23, v26, v17, v20 + calc_epelh2 v5, v6, v23, v26, v17, v20 + calc_epelh v6, v24, v27, v18, v21 + calc_epelh2 v6, v7, v24, v27, v18, v21 + epel_uni_w_hv_end3 + + sqxtun v4.8b, v4.8h + sqxtun v5.8b, v5.8h + sqxtun v6.8b, v6.8h + st1 {v4.8b, v5.8b, v6.8b}, [x0], x1 + b.eq 2f + + ld1 {v22.8h, v23.8h, v24.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v25, v16, v19, v22 + calc_epelh2 v4, v5, v25, v16, v19, v22 + calc_epelh v5, v26, v17, v20, v23 + calc_epelh2 v5, v6, v26, v17, v20, v23 + calc_epelh v6, v27, v18, v21, v24 + calc_epelh2 v6, v7, v27, v18, v21, v24 + epel_uni_w_hv_end3 + + sqxtun v4.8b, v4.8h + sqxtun v5.8b, v5.8h + sqxtun v6.8b, v6.8h + st1 {v4.8b, v5.8b, v6.8b}, [x0], x1 + b.ne 1b +2: + ldp d8, d9, [sp, #16] + ldp d10, d11, [sp, #32] + ldp d12, d13, [sp, #48] + ldp d14, d15, [sp], #64 + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv32_8_neon_i8mm, export=1 + ldp x15, x16, [sp] + mov x17, #16 + stp x15, x16, [sp, #-96]! + stp x0, x30, [sp, #16] + stp x1, x2, [sp, #32] + stp x3, x4, [sp, #48] + stp x5, x6, [sp, #64] + stp x17, x7, [sp, #80] + + bl X(ff_hevc_put_hevc_epel_uni_w_hv16_8_neon_i8mm) + ldp x0, x30, [sp, #16] + ldp x1, x2, [sp, #32] + ldp x3, x4, [sp, #48] + ldp x5, x6, [sp, #64] + ldp x17, x7, [sp, #80] + ldp x15, x16, [sp], #96 + add x0, x0, #16 + add x2, x2, #16 + mov x17, #16 + stp x15, x16, [sp, #-32]! + stp x17, x30, [sp, #16] + bl X(ff_hevc_put_hevc_epel_uni_w_hv16_8_neon_i8mm) + ldp x17, x30, [sp, #16] + ldp x15, x16, [sp], #32 + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv48_8_neon_i8mm, export=1 + ldp x15, x16, [sp] + mov x17, #24 + stp x15, x16, [sp, #-96]! + stp x0, x30, [sp, #16] + stp x1, x2, [sp, #32] + stp x3, x4, [sp, #48] + stp x5, x6, [sp, #64] + stp x17, x7, [sp, #80] + bl X(ff_hevc_put_hevc_epel_uni_w_hv24_8_neon_i8mm) + ldp x0, x30, [sp, #16] + ldp x1, x2, [sp, #32] + ldp x3, x4, [sp, #48] + ldp x5, x6, [sp, #64] + ldp x17, x7, [sp, #80] + ldp x15, x16, [sp], #96 + add x0, x0, #24 + add x2, x2, #24 + mov x17, #24 + stp x15, x16, [sp, #-32]! + stp x17, x30, [sp, #16] + bl X(ff_hevc_put_hevc_epel_uni_w_hv24_8_neon_i8mm) + ldp x17, x30, [sp, #16] + ldp x15, x16, [sp], #32 + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv64_8_neon_i8mm, export=1 + ldp x15, x16, [sp] + mov x17, #32 + stp x15, x16, [sp, #-96]! + stp x0, x30, [sp, #16] + stp x1, x2, [sp, #32] + stp x3, x4, [sp, #48] + stp x5, x6, [sp, #64] + stp x17, x7, [sp, #80] + + bl X(ff_hevc_put_hevc_epel_uni_w_hv32_8_neon_i8mm) + ldp x0, x30, [sp, #16] + ldp x1, x2, [sp, #32] + ldp x3, x4, [sp, #48] + ldp x5, x6, [sp, #64] + ldp x17, x7, [sp, #80] + ldp x15, x16, [sp], #96 + add x0, x0, #32 + add x2, x2, #32 + mov x17, #32 + stp x15, x16, [sp, #-32]! + stp x17, x30, [sp, #16] + bl X(ff_hevc_put_hevc_epel_uni_w_hv32_8_neon_i8mm) + ldp x17, x30, [sp, #16] + ldp x15, x16, [sp], #32 + ret +endfunc + + +#endif + + +.macro EPEL_UNI_W_V_HEADER + ldr x12, [sp, #8] + movrel x9, epel_filters + add x9, x9, x12, lsl #2 + ld4r {v0.16b, v1.16b, v2.16b, v3.16b}, [x9] // filter + neg v0.16b, v0.16b + neg v3.16b, v3.16b + mov w10, #-6 + sub w10, w10, w5 + dup v30.8h, w6 + dup v31.4s, w10 + dup v29.4s, w7 + sub x2, x2, x3 +.endm + +.macro EPEL_UNI_W_V4_CALC d0, s0, s1, s2, s3 + movi \d0\().2d, #0 + umlsl \d0\().8h, \s0\().8b, v0.8b + umlal \d0\().8h, \s1\().8b, v1.8b + umlal \d0\().8h, \s2\().8b, v2.8b + umlsl \d0\().8h, \s3\().8b, v3.8b + smull \d0\().4s, \d0\().4h, v30.4h + sqrshl \d0\().4s, \d0\().4s, v31.4s + sqadd \d0\().4s, \d0\().4s, v29.4s + sqxtn \d0\().4h, \d0\().4s + sqxtun \d0\().8b, \d0\().8h +.endm + +function ff_hevc_put_hevc_epel_uni_w_v4_8_neon, export=1 + EPEL_UNI_W_V_HEADER + + ldr s4, [x2] + ldr s5, [x2, x3] + add x2, x2, x3, lsl #1 + ldr s6, [x2] +1: + ldr s7, [x2, x3] + subs w4, w4, #1 + add x2, x2, x3, lsl #1 + EPEL_UNI_W_V4_CALC v16, v4, v5, v6, v7 + str s16, [x0] + b.eq 2f + add x0, x0, x1 + ldr s4, [x2] + subs w4, w4, #1 + EPEL_UNI_W_V4_CALC v17, v5, v6, v7, v4 + str s17, [x0] + add x0, x0, x1 + b.eq 2f + ldr s5, [x2, x3] + subs w4, w4, #1 + add x2, x2, x3, lsl #1 + EPEL_UNI_W_V4_CALC v18, v6, v7, v4, v5 + str s18, [x0] + add x0, x0, x1 + b.eq 2f + ldr s6, [x2] + subs w4, w4, #1 + EPEL_UNI_W_V4_CALC v19, v7, v4, v5, v6 + str s19, [x0] + add x0, x0, x1 + b.hi 1b +2: + ret +endfunc + +.macro EPEL_UNI_W_V8_CALC d0, s0, s1, s2, s3, t0, t1 + movi \d0\().2d, #0 + umlsl \d0\().8h, \s0\().8b, v0.8b + umlal \d0\().8h, \s1\().8b, v1.8b + umlal \d0\().8h, \s2\().8b, v2.8b + umlsl \d0\().8h, \s3\().8b, v3.8b + smull \t0\().4s, \d0\().4h, v30.4h + smull2 \t1\().4s, \d0\().8h, v30.8h + sqrshl \t0\().4s, \t0\().4s, v31.4s + sqrshl \t1\().4s, \t1\().4s, v31.4s + sqadd \t0\().4s, \t0\().4s, v29.4s + sqadd \t1\().4s, \t1\().4s, v29.4s + sqxtn \d0\().4h, \t0\().4s + sqxtn2 \d0\().8h, \t1\().4s + sqxtun \d0\().8b, \d0\().8h +.endm + +function ff_hevc_put_hevc_epel_uni_w_v6_8_neon, export=1 + EPEL_UNI_W_V_HEADER + + sub x1, x1, #4 + ldr d4, [x2] + ldr d5, [x2, x3] + add x2, x2, x3, lsl #1 + ldr d6, [x2] +1: + ldr d7, [x2, x3] + subs w4, w4, #1 + add x2, x2, x3, lsl #1 + EPEL_UNI_W_V8_CALC v16, v4, v5, v6, v7, v20, v21 + str s16, [x0], #4 + st1 {v16.h}[2], [x0], x1 + b.eq 2f + ldr d4, [x2] + subs w4, w4, #1 + EPEL_UNI_W_V8_CALC v17, v5, v6, v7, v4, v20, v21 + str s17, [x0], #4 + st1 {v17.h}[2], [x0], x1 + b.eq 2f + ldr d5, [x2, x3] + subs w4, w4, #1 + add x2, x2, x3, lsl #1 + EPEL_UNI_W_V8_CALC v18, v6, v7, v4, v5, v20, v21 + str s18, [x0], #4 + st1 {v18.h}[2], [x0], x1 + b.eq 2f + ldr d6, [x2] + subs w4, w4, #1 + EPEL_UNI_W_V8_CALC v19, v7, v4, v5, v6, v20, v21 + str s19, [x0], #4 + st1 {v19.h}[2], [x0], x1 + b.hi 1b +2: + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_v8_8_neon, export=1 + EPEL_UNI_W_V_HEADER + + ldr d4, [x2] + ldr d5, [x2, x3] + add x2, x2, x3, lsl #1 + ldr d6, [x2] +1: + ldr d7, [x2, x3] + subs w4, w4, #1 + add x2, x2, x3, lsl #1 + EPEL_UNI_W_V8_CALC v16, v4, v5, v6, v7, v20, v21 + str d16, [x0] + add x0, x0, x1 + b.eq 2f + ldr d4, [x2] + subs w4, w4, #1 + EPEL_UNI_W_V8_CALC v17, v5, v6, v7, v4, v20, v21 + str d17, [x0] + add x0, x0, x1 + b.eq 2f + ldr d5, [x2, x3] + subs w4, w4, #1 + add x2, x2, x3, lsl #1 + EPEL_UNI_W_V8_CALC v18, v6, v7, v4, v5, v20, v21 + str d18, [x0] + add x0, x0, x1 + b.eq 2f + ldr d6, [x2] + subs w4, w4, #1 + EPEL_UNI_W_V8_CALC v19, v7, v4, v5, v6, v20, v21 + str d19, [x0] + add x0, x0, x1 + b.hi 1b +2: + ret +endfunc + +.macro EPEL_UNI_W_V12_CALC d0, d1, s0, s1, s2, s3, t0, t1, t2, t3 + movi \d0\().2d, #0 + movi \d1\().2d, #0 + umlsl \d0\().8h, \s0\().8b, v0.8b + umlsl2 \d1\().8h, \s0\().16b, v0.16b + umlal \d0\().8h, \s1\().8b, v1.8b + umlal2 \d1\().8h, \s1\().16b, v1.16b + umlal \d0\().8h, \s2\().8b, v2.8b + umlal2 \d1\().8h, \s2\().16b, v2.16b + umlsl \d0\().8h, \s3\().8b, v3.8b + umlsl2 \d1\().8h, \s3\().16b, v3.16b + + smull \t0\().4s, \d0\().4h, v30.4h + smull2 \t1\().4s, \d0\().8h, v30.8h + smull \t2\().4s, \d1\().4h, v30.4h + + sqrshl \t0\().4s, \t0\().4s, v31.4s + sqrshl \t1\().4s, \t1\().4s, v31.4s + sqrshl \t2\().4s, \t2\().4s, v31.4s + sqadd \t0\().4s, \t0\().4s, v29.4s + sqadd \t1\().4s, \t1\().4s, v29.4s + sqadd \t2\().4s, \t2\().4s, v29.4s + + sqxtn \d0\().4h, \t0\().4s + sqxtn2 \d0\().8h, \t1\().4s + sqxtn \d1\().4h, \t2\().4s + sqxtun \d0\().8b, \d0\().8h + sqxtun2 \d0\().16b, \d1\().8h +.endm + +function ff_hevc_put_hevc_epel_uni_w_v12_8_neon, export=1 + EPEL_UNI_W_V_HEADER + + ldr q4, [x2] + ldr q5, [x2, x3] + add x2, x2, x3, lsl #1 + ldr q6, [x2] + sub x1, x1, #8 +1: + ldr q7, [x2, x3] + subs w4, w4, #1 + add x2, x2, x3, lsl #1 + EPEL_UNI_W_V12_CALC v16, v17, v4, v5, v6, v7, v24, v25, v26, v27 + str d16, [x0], #8 + st1 {v16.s}[2], [x0] + add x0, x0, x1 + b.eq 2f + ldr q4, [x2] + subs w4, w4, #1 + EPEL_UNI_W_V12_CALC v18, v19, v5, v6, v7, v4, v24, v25, v26, v27 + str d18, [x0], #8 + st1 {v18.s}[2], [x0] + add x0, x0, x1 + b.eq 2f + ldr q5, [x2, x3] + subs w4, w4, #1 + add x2, x2, x3, lsl #1 + EPEL_UNI_W_V12_CALC v20, v21, v6, v7, v4, v5, v24, v25, v26, v27 + str d20, [x0], #8 + st1 {v20.s}[2], [x0] + add x0, x0, x1 + b.eq 2f + ldr q6, [x2] + subs w4, w4, #1 + EPEL_UNI_W_V12_CALC v22, v23, v7, v4, v5, v6, v24, v25, v26, v27 + str d22, [x0], #8 + st1 {v22.s}[2], [x0] + add x0, x0, x1 + b.hi 1b +2: + ret +endfunc + +.macro EPEL_UNI_W_V16_CALC d0, d1, s0, s1, s2, s3, t0, t1, t2, t3 + movi \d0\().2d, #0 + movi \d1\().2d, #0 + umlsl \d0\().8h, \s0\().8b, v0.8b + umlsl2 \d1\().8h, \s0\().16b, v0.16b + umlal \d0\().8h, \s1\().8b, v1.8b + umlal2 \d1\().8h, \s1\().16b, v1.16b + umlal \d0\().8h, \s2\().8b, v2.8b + umlal2 \d1\().8h, \s2\().16b, v2.16b + umlsl \d0\().8h, \s3\().8b, v3.8b + umlsl2 \d1\().8h, \s3\().16b, v3.16b + + smull \t0\().4s, \d0\().4h, v30.4h + smull2 \t1\().4s, \d0\().8h, v30.8h + smull \t2\().4s, \d1\().4h, v30.4h + smull2 \t3\().4s, \d1\().8h, v30.8h + + sqrshl \t0\().4s, \t0\().4s, v31.4s + sqrshl \t1\().4s, \t1\().4s, v31.4s + sqrshl \t2\().4s, \t2\().4s, v31.4s + sqrshl \t3\().4s, \t3\().4s, v31.4s + sqadd \t0\().4s, \t0\().4s, v29.4s + sqadd \t1\().4s, \t1\().4s, v29.4s + sqadd \t2\().4s, \t2\().4s, v29.4s + sqadd \t3\().4s, \t3\().4s, v29.4s + + sqxtn \d0\().4h, \t0\().4s + sqxtn2 \d0\().8h, \t1\().4s + sqxtn \d1\().4h, \t2\().4s + sqxtn2 \d1\().8h, \t3\().4s + sqxtun \d0\().8b, \d0\().8h + sqxtun2 \d0\().16b, \d1\().8h +.endm + + +function ff_hevc_put_hevc_epel_uni_w_v16_8_neon, export=1 + EPEL_UNI_W_V_HEADER + + ldr q4, [x2] + ldr q5, [x2, x3] + add x2, x2, x3, lsl #1 + ldr q6, [x2] +1: + ldr q7, [x2, x3] + subs w4, w4, #1 + add x2, x2, x3, lsl #1 + EPEL_UNI_W_V16_CALC v16, v17, v4, v5, v6, v7, v24, v25, v26, v27 + str q16, [x0] + add x0, x0, x1 + b.eq 2f + ldr q4, [x2] + subs w4, w4, #1 + EPEL_UNI_W_V16_CALC v18, v19, v5, v6, v7, v4, v24, v25, v26, v27 + str q18, [x0] + add x0, x0, x1 + b.eq 2f + ldr q5, [x2, x3] + subs w4, w4, #1 + add x2, x2, x3, lsl #1 + EPEL_UNI_W_V16_CALC v20, v21, v6, v7, v4, v5, v24, v25, v26, v27 + str q20, [x0] + add x0, x0, x1 + b.eq 2f + ldr q6, [x2] + subs w4, w4, #1 + EPEL_UNI_W_V16_CALC v22, v23, v7, v4, v5, v6, v24, v25, v26, v27 + str q22, [x0] + add x0, x0, x1 + b.hi 1b +2: + ret +endfunc + + + +function ff_hevc_put_hevc_epel_uni_w_v24_8_neon, export=1 + EPEL_UNI_W_V_HEADER + + ldp q16, q17, [x2] + add x2, x2, x3 + ldp q18, q19, [x2] + add x2, x2, x3 + ldp q20, q21, [x2] + add x2, x2, x3 +1: + ldp q22, q23, [x2] + subs w4, w4, #1 + add x2, x2, x3 + EPEL_UNI_W_V16_CALC v4, v5, v16, v18, v20, v22, v24, v25, v26, v27 + EPEL_UNI_W_V8_CALC v6, v17, v19, v21, v23, v24, v25 + str q4, [x0] + str d6, [x0, #16] + add x0, x0, x1 + b.eq 2f + ldp q16, q17, [x2] + subs w4, w4, #1 + add x2, x2, x3 + EPEL_UNI_W_V16_CALC v4, v5, v18, v20, v22, v16, v24, v25, v26, v27 + EPEL_UNI_W_V8_CALC v6, v19, v21, v23, v17, v24, v25 + str q4, [x0] + str d6, [x0, #16] + add x0, x0, x1 + b.eq 2f + ldp q18, q19, [x2] + subs w4, w4, #1 + add x2, x2, x3 + EPEL_UNI_W_V16_CALC v4, v5, v20, v22, v16, v18, v24, v25, v26, v27 + EPEL_UNI_W_V8_CALC v6, v21, v23, v17, v19, v24, v25 + str q4, [x0] + str d6, [x0, #16] + add x0, x0, x1 + b.eq 2f + ldp q20, q21, [x2] + subs w4, w4, #1 + add x2, x2, x3 + EPEL_UNI_W_V16_CALC v4, v5, v22, v16, v18, v20, v24, v25, v26, v27 + EPEL_UNI_W_V8_CALC v6, v23, v17, v19, v21, v24, v25 + str q4, [x0] + str d6, [x0, #16] + add x0, x0, x1 + b.hi 1b +2: + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_v32_8_neon, export=1 + EPEL_UNI_W_V_HEADER + + ldp q16, q17, [x2] + add x2, x2, x3 + ldp q18, q19, [x2] + add x2, x2, x3 + ldp q20, q21, [x2] + add x2, x2, x3 +1: + ldp q22, q23, [x2] + subs w4, w4, #1 + add x2, x2, x3 + EPEL_UNI_W_V16_CALC v4, v5, v16, v18, v20, v22, v24, v25, v26, v27 + EPEL_UNI_W_V16_CALC v6, v7, v17, v19, v21, v23, v24, v25, v26, v27 + str q4, [x0] + str q6, [x0, #16] + add x0, x0, x1 + b.eq 2f + ldp q16, q17, [x2] + subs w4, w4, #1 + add x2, x2, x3 + EPEL_UNI_W_V16_CALC v4, v5, v18, v20, v22, v16, v24, v25, v26, v27 + EPEL_UNI_W_V16_CALC v6, v7, v19, v21, v23, v17, v24, v25, v26, v27 + str q4, [x0] + str q6, [x0, #16] + add x0, x0, x1 + b.eq 2f + ldp q18, q19, [x2] + subs w4, w4, #1 + add x2, x2, x3 + EPEL_UNI_W_V16_CALC v4, v5, v20, v22, v16, v18, v24, v25, v26, v27 + EPEL_UNI_W_V16_CALC v6, v7, v21, v23, v17, v19, v24, v25, v26, v27 + str q4, [x0] + str q6, [x0, #16] + add x0, x0, x1 + b.eq 2f + ldp q20, q21, [x2] + subs w4, w4, #1 + add x2, x2, x3 + EPEL_UNI_W_V16_CALC v4, v5, v22, v16, v18, v20, v24, v25, v26, v27 + EPEL_UNI_W_V16_CALC v6, v7, v23, v17, v19, v21, v24, v25, v26, v27 + str q4, [x0] + str q6, [x0, #16] + add x0, x0, x1 + b.hi 1b +2: + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_v48_8_neon, export=1 + EPEL_UNI_W_V_HEADER + stp d8, d9, [sp, #-32]! + stp d10, d11, [sp, #16] + + ld1 {v16.16b, v17.16b, v18.16b}, [x2], x3 + ld1 {v19.16b, v20.16b, v21.16b}, [x2], x3 + ld1 {v22.16b, v23.16b, v24.16b}, [x2], x3 +1: + ld1 {v25.16b, v26.16b, v27.16b}, [x2], x3 + subs w4, w4, #1 + EPEL_UNI_W_V16_CALC v4, v6, v16, v19, v22, v25, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v5, v7, v17, v20, v23, v26, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v6, v7, v18, v21, v24, v27, v8, v9, v10, v11 + st1 {v4.16b, v5.16b, v6.16b}, [x0], x1 + b.eq 2f + ld1 {v16.16b, v17.16b, v18.16b}, [x2], x3 + subs w4, w4, #1 + EPEL_UNI_W_V16_CALC v4, v6, v19, v22, v25, v16, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v5, v7, v20, v23, v26, v17, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v6, v7, v21, v24, v27, v18, v8, v9, v10, v11 + st1 {v4.16b, v5.16b, v6.16b}, [x0], x1 + b.eq 2f + ld1 {v19.16b, v20.16b, v21.16b}, [x2], x3 + subs w4, w4, #1 + EPEL_UNI_W_V16_CALC v4, v6, v22, v25, v16, v19, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v5, v7, v23, v26, v17, v20, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v6, v7, v24, v27, v18, v21, v8, v9, v10, v11 + st1 {v4.16b, v5.16b, v6.16b}, [x0], x1 + b.eq 2f + ld1 {v22.16b, v23.16b, v24.16b}, [x2], x3 + subs w4, w4, #1 + EPEL_UNI_W_V16_CALC v4, v6, v25, v16, v19, v22, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v5, v7, v26, v17, v20, v23, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v6, v7, v27, v18, v21, v24, v8, v9, v10, v11 + st1 {v4.16b, v5.16b, v6.16b}, [x0], x1 + b.hi 1b +2: + ldp d10, d11, [sp, #16] + ldp d8, d9, [sp], #32 + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_v64_8_neon, export=1 + EPEL_UNI_W_V_HEADER + stp d8, d9, [sp, #-64]! + stp d10, d11, [sp, #16] + stp d12, d13, [sp, #32] + stp d14, d15, [sp, #48] + + ld1 {v16.16b, v17.16b, v18.16b, v19.16b}, [x2], x3 + ld1 {v20.16b, v21.16b, v22.16b, v23.16b}, [x2], x3 + ld1 {v24.16b, v25.16b, v26.16b, v27.16b}, [x2], x3 +1: + ld1 {v12.16b, v13.16b, v14.16b, v15.16b}, [x2], x3 + subs w4, w4, #1 + EPEL_UNI_W_V16_CALC v4, v6, v16, v20, v24, v12, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v5, v7, v17, v21, v25, v13, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v6, v7, v18, v22, v26, v14, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v7,v28, v19, v23, v27, v15, v8, v9, v10, v11 + st1 {v4.16b, v5.16b, v6.16b, v7.16b}, [x0], x1 + b.eq 2f + ld1 {v16.16b, v17.16b, v18.16b, v19.16b}, [x2], x3 + subs w4, w4, #1 + EPEL_UNI_W_V16_CALC v4, v6, v20, v24, v12, v16, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v5, v7, v21, v25, v13, v17, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v6, v7, v22, v26, v14, v18, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v7,v28, v23, v27, v15, v19, v8, v9, v10, v11 + st1 {v4.16b, v5.16b, v6.16b, v7.16b}, [x0], x1 + b.eq 2f + ld1 {v20.16b, v21.16b, v22.16b, v23.16b}, [x2], x3 + subs w4, w4, #1 + EPEL_UNI_W_V16_CALC v4, v6, v24, v12, v16, v20, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v5, v7, v25, v13, v17, v21, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v6, v7, v26, v14, v18, v22, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v7,v28, v27, v15, v19, v23, v8, v9, v10, v11 + st1 {v4.16b, v5.16b, v6.16b, v7.16b}, [x0], x1 + b.eq 2f + ld1 {v24.16b, v25.16b, v26.16b, v27.16b}, [x2], x3 + subs w4, w4, #1 + EPEL_UNI_W_V16_CALC v4, v6, v12, v16, v20, v24, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v5, v7, v13, v17, v21, v25, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v6, v7, v14, v18, v22, v26, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v7,v28, v15, v19, v23, v27, v8, v9, v10, v11 + st1 {v4.16b, v5.16b, v6.16b, v7.16b}, [x0], x1 + b.hi 1b +2: + ldp d10, d11, [sp, #16] + ldp d12, d13, [sp, #32] + ldp d14, d15, [sp, #48] + ldp d8, d9, [sp], #64 + ret +endfunc diff --git a/libavcodec/aarch64/hevcdsp_idct_neon.S b/libavcodec/aarch64/hevcdsp_idct_neon.S index 124c50998a7..ba8a1ebaed8 100644 --- a/libavcodec/aarch64/hevcdsp_idct_neon.S +++ b/libavcodec/aarch64/hevcdsp_idct_neon.S @@ -5,7 +5,8 @@ * * Ported from arm/hevcdsp_idct_neon.S by * Copyright (c) 2020 Reimar Döffinger - * Copyright (c) 2020 J. Dekker + * Copyright (c) 2023 J. Dekker + * Copyright (c) 2023 xu fulong <839789740@qq.com> * * This file is part of FFmpeg. * @@ -25,6 +26,7 @@ */ #include "libavutil/aarch64/asm.S" +#include "neon.S" const trans, align=4 .short 64, 83, 64, 36 @@ -37,13 +39,6 @@ const trans, align=4 .short 31, 22, 13, 4 endconst -.macro clip2 in1, in2, min, max - smax \in1, \in1, \min - smax \in2, \in2, \min - smin \in1, \in1, \max - smin \in2, \in2, \max -.endm - function ff_hevc_add_residual_4x4_8_neon, export=1 ld1 {v0.8h-v1.8h}, [x1] ld1 {v2.s}[0], [x0], x2 @@ -181,7 +176,7 @@ function hevc_add_residual_4x4_16_neon, export=0 ld1 {v3.d}[1], [x12], x2 movi v4.8h, #0 sqadd v1.8h, v1.8h, v3.8h - clip2 v0.8h, v1.8h, v4.8h, v21.8h + clip v4.8h, v21.8h, v0.8h, v1.8h st1 {v0.d}[0], [x0], x2 st1 {v0.d}[1], [x0], x2 st1 {v1.d}[0], [x0], x2 @@ -200,7 +195,7 @@ function hevc_add_residual_8x8_16_neon, export=0 sqadd v0.8h, v0.8h, v2.8h ld1 {v3.8h}, [x12] sqadd v1.8h, v1.8h, v3.8h - clip2 v0.8h, v1.8h, v4.8h, v21.8h + clip v4.8h, v21.8h, v0.8h, v1.8h st1 {v0.8h}, [x0], x2 st1 {v1.8h}, [x12], x2 bne 1b @@ -220,8 +215,7 @@ function hevc_add_residual_16x16_16_neon, export=0 sqadd v1.8h, v1.8h, v17.8h sqadd v2.8h, v2.8h, v18.8h sqadd v3.8h, v3.8h, v19.8h - clip2 v0.8h, v1.8h, v20.8h, v21.8h - clip2 v2.8h, v3.8h, v20.8h, v21.8h + clip v20.8h, v21.8h, v0.8h, v1.8h, v2.8h, v3.8h st1 {v0.8h-v1.8h}, [x0], x2 st1 {v2.8h-v3.8h}, [x12], x2 bne 1b @@ -238,13 +232,49 @@ function hevc_add_residual_32x32_16_neon, export=0 sqadd v1.8h, v1.8h, v17.8h sqadd v2.8h, v2.8h, v18.8h sqadd v3.8h, v3.8h, v19.8h - clip2 v0.8h, v1.8h, v20.8h, v21.8h - clip2 v2.8h, v3.8h, v20.8h, v21.8h + clip v20.8h, v21.8h, v0.8h, v1.8h, v2.8h, v3.8h st1 {v0.8h-v3.8h}, [x0], x2 bne 1b ret endfunc +.macro tr_4x4 in0, in1, in2, in3, out0, out1, out2, out3, shift + sshll v20.4s, \in0, #6 + sshll v21.4s, \in0, #6 + smull v22.4s, \in1, v4.h[1] + smull v23.4s, \in1, v4.h[3] + smlal v20.4s, \in2, v4.h[0] //e0 + smlsl v21.4s, \in2, v4.h[0] //e1 + smlal v22.4s, \in3, v4.h[3] //o0 + smlsl v23.4s, \in3, v4.h[1] //o1 + + add v24.4s, v20.4s, v22.4s + sub v20.4s, v20.4s, v22.4s + add v22.4s, v21.4s, v23.4s + sub v21.4s, v21.4s, v23.4s + sqrshrn \out0, v24.4s, #\shift + sqrshrn \out3, v20.4s, #\shift + sqrshrn \out1, v22.4s, #\shift + sqrshrn \out2, v21.4s, #\shift +.endm + +.macro idct_4x4 bitdepth +function ff_hevc_idct_4x4_\bitdepth\()_neon, export=1 + ld1 {v0.4h-v3.4h}, [x0] + + movrel x1, trans + ld1 {v4.4h}, [x1] + + tr_4x4 v0.4h, v1.4h, v2.4h, v3.4h, v16.4h, v17.4h, v18.4h, v19.4h, 7 + transpose_4x8H v16, v17, v18, v19, v26, v27, v28, v29 + + tr_4x4 v16.4h, v17.4h, v18.4h, v19.4h, v0.4h, v1.4h, v2.4h, v3.4h, 20 - \bitdepth + transpose_4x8H v0, v1, v2, v3, v26, v27, v28, v29 + st1 {v0.4h-v3.4h}, [x0] + ret +endfunc +.endm + .macro sum_sub out, in, c, op, p .ifc \op, + smlal\p \out, \in, \c @@ -279,20 +309,9 @@ endfunc sub \out3, v28.4s, v30.4s .endm -.macro transpose8_4x4 r0, r1, r2, r3 - trn1 v2.8h, \r0\().8h, \r1\().8h - trn2 v3.8h, \r0\().8h, \r1\().8h - trn1 v4.8h, \r2\().8h, \r3\().8h - trn2 v5.8h, \r2\().8h, \r3\().8h - trn1 \r0\().4s, v2.4s, v4.4s - trn2 \r2\().4s, v2.4s, v4.4s - trn1 \r1\().4s, v3.4s, v5.4s - trn2 \r3\().4s, v3.4s, v5.4s -.endm - .macro transpose_8x8 r0, r1, r2, r3, r4, r5, r6, r7 - transpose8_4x4 \r0, \r1, \r2, \r3 - transpose8_4x4 \r4, \r5, \r6, \r7 + transpose_4x8H \r0, \r1, \r2, \r3, v2, v3, v4, v5 + transpose_4x8H \r4, \r5, \r6, \r7, v2, v3, v4, v5 .endm .macro tr_8x4 shift, in0,in0t, in1,in1t, in2,in2t, in3,in3t, in4,in4t, in5,in5t, in6,in6t, in7,in7t, p1, p2 @@ -459,34 +478,52 @@ endfunc sqrshrn2 \out3\().8h, \in7, \shift .endm -.macro transpose16_4x4_2 r0, r1, r2, r3 +// use temp register to transpose, then we can reuse it +.macro transpose16_4x4_2 r0, r1, r2, r3, tmp0, tmp1, tmp2, tmp3, tmp4, tmp5 // lower halves - trn1 v2.4h, \r0\().4h, \r1\().4h - trn2 v3.4h, \r0\().4h, \r1\().4h - trn1 v4.4h, \r2\().4h, \r3\().4h - trn2 v5.4h, \r2\().4h, \r3\().4h - trn1 v6.2s, v2.2s, v4.2s - trn2 v7.2s, v2.2s, v4.2s - trn1 v2.2s, v3.2s, v5.2s - trn2 v4.2s, v3.2s, v5.2s - mov \r0\().d[0], v6.d[0] - mov \r2\().d[0], v7.d[0] - mov \r1\().d[0], v2.d[0] - mov \r3\().d[0], v4.d[0] + trn1 \tmp0\().4h, \r0\().4h, \r1\().4h + trn2 \tmp1\().4h, \r0\().4h, \r1\().4h + trn1 \tmp2\().4h, \r2\().4h, \r3\().4h + trn2 \tmp3\().4h, \r2\().4h, \r3\().4h + trn1 \tmp4\().2s, \tmp0\().2s, \tmp2\().2s + trn2 \tmp5\().2s, \tmp0\().2s, \tmp2\().2s + trn1 \tmp0\().2s, \tmp1\().2s, \tmp3\().2s + trn2 \tmp2\().2s, \tmp1\().2s, \tmp3\().2s + mov \r0\().d[0], \tmp4\().d[0] + mov \r2\().d[0], \tmp5\().d[0] + mov \r1\().d[0], \tmp0\().d[0] + mov \r3\().d[0], \tmp2\().d[0] // upper halves in reverse order - trn1 v2.8h, \r3\().8h, \r2\().8h - trn2 v3.8h, \r3\().8h, \r2\().8h - trn1 v4.8h, \r1\().8h, \r0\().8h - trn2 v5.8h, \r1\().8h, \r0\().8h - trn1 v6.4s, v2.4s, v4.4s - trn2 v7.4s, v2.4s, v4.4s - trn1 v2.4s, v3.4s, v5.4s - trn2 v4.4s, v3.4s, v5.4s - mov \r3\().d[1], v6.d[1] - mov \r1\().d[1], v7.d[1] - mov \r2\().d[1], v2.d[1] - mov \r0\().d[1], v4.d[1] + trn1 \tmp0\().8h, \r3\().8h, \r2\().8h + trn2 \tmp1\().8h, \r3\().8h, \r2\().8h + trn1 \tmp2\().8h, \r1\().8h, \r0\().8h + trn2 \tmp3\().8h, \r1\().8h, \r0\().8h + trn1 \tmp4\().4s, \tmp0\().4s, \tmp2\().4s + trn2 \tmp5\().4s, \tmp0\().4s, \tmp2\().4s + trn1 \tmp0\().4s, \tmp1\().4s, \tmp3\().4s + trn2 \tmp2\().4s, \tmp1\().4s, \tmp3\().4s + mov \r3\().d[1], \tmp4\().d[1] + mov \r1\().d[1], \tmp5\().d[1] + mov \r2\().d[1], \tmp0\().d[1] + mov \r0\().d[1], \tmp2\().d[1] +.endm + +// stores in0, in2, in4, in6 ascending from off1 and +// stores in1, in3, in5, in7 descending from off2 +.macro store_to_stack off1, off2, in0, in2, in4, in6, in7, in5, in3, in1 + add x1, sp, #\off1 + add x3, sp, #\off2 + mov x2, #-16 + mov x4, #16 + st1 {\in0}, [x1], x4 + st1 {\in1}, [x3], x2 + st1 {\in2}, [x1], x4 + st1 {\in3}, [x3], x2 + st1 {\in4}, [x1], x4 + st1 {\in5}, [x3], x2 + st1 {\in6}, [x1] + st1 {\in7}, [x3] .endm .macro tr_16x4 name, shift, offset, step @@ -525,27 +562,34 @@ function func_tr_16x4_\name add x4, sp, #\offset ld1 {v16.4s-v19.4s}, [x4], #64 - butterfly16 v16.4s, v21.4s, v17.4s, v22.4s, v18.4s, v23.4s, v19.4s, v24.4s + .if \shift > 0 scale v29, v30, v31, v24, v20.4s, v16.4s, v21.4s, v17.4s, v22.4s, v18.4s, v23.4s, v19.4s, \shift - transpose16_4x4_2 v29, v30, v31, v24 + transpose16_4x4_2 v29, v30, v31, v24, v2, v3, v4, v5, v6, v7 mov x1, x6 add x3, x6, #(24 +3*32) mov x2, #32 mov x4, #-32 store16 v29.d, v30.d, v31.d, v24.d, x4 + .else + store_to_stack \offset, (\offset + 240), v20.4s, v21.4s, v22.4s, v23.4s, v19.4s, v18.4s, v17.4s, v16.4s + .endif add x4, sp, #(\offset + 64) ld1 {v16.4s-v19.4s}, [x4] butterfly16 v16.4s, v25.4s, v17.4s, v26.4s, v18.4s, v27.4s, v19.4s, v28.4s + .if \shift > 0 scale v29, v30, v31, v20, v20.4s, v16.4s, v25.4s, v17.4s, v26.4s, v18.4s, v27.4s, v19.4s, \shift - transpose16_4x4_2 v29, v30, v31, v20 + transpose16_4x4_2 v29, v30, v31, v20, v2, v3, v4, v5, v6, v7 add x1, x6, #8 add x3, x6, #(16 + 3 * 32) mov x2, #32 mov x4, #-32 store16 v29.d, v30.d, v31.d, v20.d, x4 + .else + store_to_stack (\offset + 64), (\offset + 176), v20.4s, v25.4s, v26.4s, v27.4s, v19.4s, v18.4s, v17.4s, v16.4s + .endif ret endfunc @@ -573,21 +617,277 @@ function ff_hevc_idct_16x16_\bitdepth\()_neon, export=1 add sp, sp, #640 - mov x30, x15 + ret x15 +endfunc +.endm + +.macro load32 + add x1, x5, #64 + add x3, x1, #128 + mov x2, #256 + ld1 {v4.d}[0], [x1], x2 + ld1 {v4.d}[1], [x3], x2 + ld1 {v5.d}[0], [x1], x2 + ld1 {v5.d}[1], [x3], x2 + ld1 {v6.d}[0], [x1], x2 + ld1 {v6.d}[1], [x3], x2 + ld1 {v7.d}[0], [x1], x2 + ld1 {v7.d}[1], [x3], x2 + ld1 {v16.d}[0], [x1], x2 + ld1 {v16.d}[1], [x3], x2 + ld1 {v17.d}[0], [x1], x2 + ld1 {v17.d}[1], [x3], x2 + ld1 {v18.d}[0], [x1], x2 + ld1 {v18.d}[1], [x3], x2 + ld1 {v19.d}[0], [x1], x2 + ld1 {v19.d}[1], [x3], x2 +.endm + +.macro add_member32 in, t0, t1, t2, t3, op0, op1, op2, op3, p + sum_sub v24.4s, \in, \t0, \op0, \p + sum_sub v25.4s, \in, \t1, \op1, \p + sum_sub v26.4s, \in, \t2, \op2, \p + sum_sub v27.4s, \in, \t3, \op3, \p +.endm + +.macro butterfly32 in0, in1, in2, in3, out + add \out, \in0, \in1 + sub \in0, \in0, \in1 + add \in1, \in2, \in3 + sub \in2, \in2, \in3 +.endm + +.macro multiply in + smull v24.4s, v4.4h, \in\().h[0] + smull v25.4s, v4.4h, \in\().h[1] + smull v26.4s, v4.4h, \in\().h[2] + smull v27.4s, v4.4h, \in\().h[3] +.endm + +.macro scale_store shift + ld1 {v28.8h-v31.8h}, [x4], #64 + butterfly32 v28.4s, v24.4s, v29.4s, v25.4s, v2.4s + butterfly32 v30.4s, v26.4s, v31.4s, v27.4s, v3.4s + scale v20, v21, v22, v23, v2.4s, v28.4s, v24.4s, v29.4s, v3.4s, v30.4s, v26.4s, v31.4s, \shift + + transpose16_4x4_2 v20, v21, v22, v23, v24, v25, v26, v27, v28, v29 + store16 v20.d, v21.d, v22.d, v23.d, x8 + + // reload coefficients + ld1 {v2.4h-v3.4h}, [x9] +.endm + +function tr_block1 + multiply v0 + add_member32 v4.8h, v0.h[1], v1.h[0], v1.h[3], v2.h[2], +, +, +, +, 2 + add_member32 v5.4h, v0.h[2], v1.h[3], v3.h[0], v3.h[2], +, +, +, - + add_member32 v5.8h, v0.h[3], v2.h[2], v3.h[2], v1.h[3], +, +, -, -, 2 + add_member32 v6.4h, v1.h[0], v3.h[1], v2.h[1], v0.h[0], +, +, -, - + add_member32 v6.8h, v1.h[1], v3.h[3], v1.h[0], v1.h[2], +, -, -, -, 2 + add_member32 v7.4h, v1.h[2], v3.h[0], v0.h[0], v3.h[1], +, -, -, - + add_member32 v7.8h, v1.h[3], v2.h[1], v1.h[1], v2.h[3], +, -, -, +, 2 + add_member32 v16.4h, v2.h[0], v1.h[2], v2.h[2], v1.h[0], +, -, -, + + add_member32 v16.8h, v2.h[1], v0.h[3], v3.h[3], v0.h[2], +, -, -, +, 2 + add_member32 v17.4h, v2.h[2], v0.h[1], v2.h[3], v2.h[1], +, -, +, + + add_member32 v17.8h, v2.h[3], v0.h[2], v1.h[2], v3.h[3], +, -, +, -, 2 + add_member32 v18.4h, v3.h[0], v1.h[1], v0.h[1], v2.h[0], +, -, +, - + add_member32 v18.8h, v3.h[1], v2.h[0], v0.h[3], v0.h[1], +, -, +, -, 2 + add_member32 v19.4h, v3.h[2], v2.h[3], v2.h[0], v1.h[1], +, -, +, - + add_member32 v19.8h, v3.h[3], v3.h[2], v3.h[1], v3.h[0], +, -, +, -, 2 + ret +endfunc + +function tr_block2 + multiply v1 + add_member32 v4.8h, v3.h[1], v3.h[3], v3.h[0], v2.h[1], +, -, -, -, 2 + add_member32 v5.4h, v2.h[1], v1.h[0], v0.h[0], v1.h[1], -, -, -, - + add_member32 v5.8h, v0.h[0], v1.h[2], v3.h[1], v2.h[3], -, -, -, +, 2 + add_member32 v6.4h, v2.h[0], v3.h[2], v1.h[1], v0.h[3], -, +, +, + + add_member32 v6.8h, v3.h[2], v0.h[3], v1.h[3], v3.h[1], +, +, +, -, 2 + add_member32 v7.4h, v1.h[1], v1.h[3], v2.h[3], v0.h[0], +, +, -, - + add_member32 v7.8h, v0.h[3], v3.h[1], v0.h[1], v3.h[3], +, -, -, +, 2 + add_member32 v16.4h, v3.h[0], v0.h[2], v3.h[2], v0.h[1], +, -, -, + + add_member32 v16.8h, v2.h[2], v2.h[0], v1.h[0], v3.h[2], -, -, +, +, 2 + add_member32 v17.4h, v0.h[1], v3.h[0], v2.h[0], v0.h[2], -, +, +, - + add_member32 v17.8h, v1.h[3], v0.h[1], v2.h[2], v3.h[0], -, +, -, -, 2 + add_member32 v18.4h, v3.h[3], v2.h[1], v0.h[2], v1.h[0], +, +, -, + + add_member32 v18.8h, v1.h[2], v2.h[3], v3.h[3], v2.h[2], +, -, -, +, 2 + add_member32 v19.4h, v0.h[2], v0.h[1], v0.h[3], v1.h[2], +, -, +, - + add_member32 v19.8h, v2.h[3], v2.h[2], v2.h[1], v2.h[0], +, -, +, -, 2 + ret +endfunc + +function tr_block3 + multiply v2 + add_member32 v4.8h, v1.h[2], v0.h[3], v0.h[0], v0.h[2], -, -, -, -, 2 + add_member32 v5.4h, v2.h[2], v3.h[3], v2.h[3], v1.h[2], -, -, +, + + add_member32 v5.8h, v1.h[0], v0.h[2], v2.h[1], v3.h[3], +, +, +, -, 2 + add_member32 v6.4h, v3.h[0], v2.h[2], v0.h[1], v1.h[3], +, -, -, - + add_member32 v6.8h, v0.h[2], v2.h[0], v3.h[0], v0.h[0], -, -, +, +, 2 + add_member32 v7.4h, v3.h[2], v1.h[0], v2.h[0], v2.h[2], -, +, +, - + add_member32 v7.8h, v0.h[0], v3.h[2], v0.h[2], v3.h[0], +, +, -, -, 2 + add_member32 v16.4h, v3.h[3], v0.h[1], v3.h[1], v0.h[3], -, -, +, + + add_member32 v16.8h, v0.h[1], v2.h[3], v1.h[3], v1.h[1], -, +, +, -, 2 + add_member32 v17.4h, v3.h[1], v1.h[3], v0.h[3], v3.h[2], +, +, -, + + add_member32 v17.8h, v0.h[3], v1.h[1], v3.h[2], v2.h[0], +, -, +, +, 2 + add_member32 v18.4h, v2.h[3], v3.h[1], v1.h[2], v0.h[1], -, -, +, - + add_member32 v18.8h, v1.h[1], v0.h[0], v1.h[0], v2.h[1], -, +, -, +, 2 + add_member32 v19.4h, v2.h[1], v3.h[0], v3.h[3], v3.h[1], +, -, +, + + add_member32 v19.8h, v1.h[3], v1.h[2], v1.h[1], v1.h[0], +, -, +, -, 2 ret endfunc + +function tr_block4 + multiply v3 + add_member32 v4.8h, v1.h[1], v2.h[0], v2.h[3], v3.h[2], -, -, -, -, 2 + add_member32 v5.4h, v0.h[0], v0.h[3], v2.h[0], v3.h[1], +, +, +, + + add_member32 v5.8h, v2.h[0], v0.h[0], v1.h[1], v3.h[0], -, -, -, -, 2 + add_member32 v6.4h, v3.h[3], v1.h[2], v0.h[2], v2.h[3], +, +, +, + + add_member32 v6.8h, v2.h[1], v2.h[3], v0.h[0], v2.h[2], +, -, -, -, 2 + add_member32 v7.4h, v0.h[2], v3.h[3], v0.h[3], v2.h[1], -, -, +, + + add_member32 v7.8h, v1.h[0], v2.h[2], v1.h[2], v2.h[0], +, +, -, -, 2 + add_member32 v16.4h, v2.h[3], v1.h[1], v2.h[1], v1.h[3], -, -, +, + + add_member32 v16.8h, v3.h[1], v0.h[1], v3.h[0], v1.h[2], -, +, -, -, 2 + add_member32 v17.4h, v1.h[2], v1.h[0], v3.h[3], v1.h[1], +, -, +, + + add_member32 v17.8h, v0.h[1], v2.h[1], v3.h[1], v1.h[0], -, +, +, -, 2 + add_member32 v18.4h, v1.h[3], v3.h[2], v2.h[2], v0.h[3], +, -, -, + + add_member32 v18.8h, v3.h[2], v3.h[0], v1.h[3], v0.h[2], -, -, +, -, 2 + add_member32 v19.4h, v2.h[2], v1.h[3], v1.h[0], v0.h[1], -, +, -, + + add_member32 v19.8h, v0.h[3], v0.h[2], v0.h[1], v0.h[0], +, -, +, -, 2 + ret +endfunc + +.macro tr_32x4 name, shift +function func_tr_32x4_\name + mov x10, x30 + bl func_tr_16x4_noscale + + load32 + movrel x9, trans, 32 + ld1 {v0.4h-v1.4h}, [x9], #16 + ld1 {v2.4h-v3.4h}, [x9] + add x4, sp, #2048 + mov x2, #64 + mov x8, #-64 + + bl tr_block1 + mov x1, x11 + add x3, x11, #(56 + 3 * 64) + scale_store \shift + + bl tr_block2 + add x1, x11, #8 + add x3, x11, #(48 + 3 * 64) + scale_store \shift + + bl tr_block3 + add x1, x11, #16 + add x3, x11, #(40 + 3 * 64) + scale_store \shift + + bl tr_block4 + add x1, x11, #24 + add x3, x11, #(32 + 3 * 64) + scale_store \shift + + ret x10 +endfunc +.endm + +.macro idct_32x32 bitdepth +function ff_hevc_idct_32x32_\bitdepth\()_neon, export=1 + mov x15, x30 + // allocate a temp buffer + sub sp, sp, #2432 + +.irp i, 0, 1, 2, 3, 4, 5, 6, 7 + add x5, x0, #(8 * \i) + add x11, sp, #(8 * \i * 32) + bl func_tr_32x4_firstpass +.endr + +.irp i, 0, 1, 2, 3, 4, 5, 6, 7 + add x5, sp, #(8 * \i) + add x11, x0, #(8 * \i * 32) + bl func_tr_32x4_secondpass_\bitdepth +.endr + + add sp, sp, #2432 + ret x15 +endfunc .endm +idct_4x4 8 +idct_4x4 10 + idct_8x8 8 idct_8x8 10 tr_16x4 firstpass, 7, 512, 1 tr_16x4 secondpass_8, 20 - 8, 512, 1 tr_16x4 secondpass_10, 20 - 10, 512, 1 +tr_16x4 noscale, 0, 2048, 4 idct_16x16 8 idct_16x16 10 +.ltorg +tr_32x4 firstpass, 7 +tr_32x4 secondpass_8, 20 - 8 +tr_32x4 secondpass_10, 20 - 10 +.ltorg + +idct_32x32 8 +idct_32x32 10 + +.macro tr4_luma_shift r0, r1, r2, r3, shift + saddl v0.4s, \r0, \r2 // c0 = src0 + src2 + saddl v1.4s, \r2, \r3 // c1 = src2 + src3 + ssubl v2.4s, \r0, \r3 // c2 = src0 - src3 + smull v3.4s, \r1, v21.4h // c3 = 74 * src1 + + saddl v7.4s, \r0, \r3 // src0 + src3 + ssubw v7.4s, v7.4s, \r2 // src0 - src2 + src3 + mul v7.4s, v7.4s, v18.4s // dst2 = 74 * (src0 - src2 + src3) + + mul v5.4s, v0.4s, v19.4s // 29 * c0 + mul v6.4s, v1.4s, v20.4s // 55 * c1 + add v5.4s, v5.4s, v6.4s // 29 * c0 + 55 * c1 + add v5.4s, v5.4s, v3.4s // dst0 = 29 * c0 + 55 * c1 + c3 + + mul v1.4s, v1.4s, v19.4s // 29 * c1 + mul v6.4s, v2.4s, v20.4s // 55 * c2 + sub v6.4s, v6.4s, v1.4s // 55 * c2 - 29 * c1 + add v6.4s, v6.4s, v3.4s // dst1 = 55 * c2 - 29 * c1 + c3 + + mul v0.4s, v0.4s, v20.4s // 55 * c0 + mul v2.4s, v2.4s, v19.4s // 29 * c2 + add v0.4s, v0.4s, v2.4s // 55 * c0 + 29 * c2 + sub v0.4s, v0.4s, v3.4s // dst3 = 55 * c0 + 29 * c2 - c3 + + sqrshrn \r0, v5.4s, \shift + sqrshrn \r1, v6.4s, \shift + sqrshrn \r2, v7.4s, \shift + sqrshrn \r3, v0.4s, \shift +.endm + +function ff_hevc_transform_luma_4x4_neon_8, export=1 + ld1 {v28.4h-v31.4h}, [x0] + movi v18.4s, #74 + movi v19.4s, #29 + movi v20.4s, #55 + movi v21.4h, #74 + + tr4_luma_shift v28.4h, v29.4h, v30.4h, v31.4h, #7 + transpose_4x4H v28, v29, v30, v31, v22, v23, v24, v25 + + tr4_luma_shift v28.4h, v29.4h, v30.4h, v31.4h, #12 + transpose_4x4H v28, v29, v30, v31, v22, v23, v24, v25 + + st1 {v28.4h-v31.4h}, [x0] + ret +endfunc + // void ff_hevc_idct_NxN_dc_DEPTH_neon(int16_t *coeffs) .macro idct_dc size, bitdepth function ff_hevc_idct_\size\()x\size\()_dc_\bitdepth\()_neon, export=1 diff --git a/libavcodec/aarch64/hevcdsp_init_aarch64.c b/libavcodec/aarch64/hevcdsp_init_aarch64.c index 88a797f3931..e125b0cfb28 100644 --- a/libavcodec/aarch64/hevcdsp_init_aarch64.c +++ b/libavcodec/aarch64/hevcdsp_init_aarch64.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2020 Reimar Döffinger + * Copyright (c) 2023 xu fulong <839789740@qq.com> * * This file is part of FFmpeg. * @@ -25,6 +26,18 @@ #include "libavutil/aarch64/cpu.h" #include "libavcodec/hevcdsp.h" +void ff_hevc_v_loop_filter_chroma_8_neon(uint8_t *_pix, ptrdiff_t _stride, + const int *_tc, const uint8_t *_no_p, const uint8_t *_no_q); +void ff_hevc_v_loop_filter_chroma_10_neon(uint8_t *_pix, ptrdiff_t _stride, + const int *_tc, const uint8_t *_no_p, const uint8_t *_no_q); +void ff_hevc_v_loop_filter_chroma_12_neon(uint8_t *_pix, ptrdiff_t _stride, + const int *_tc, const uint8_t *_no_p, const uint8_t *_no_q); +void ff_hevc_h_loop_filter_chroma_8_neon(uint8_t *_pix, ptrdiff_t _stride, + const int *_tc, const uint8_t *_no_p, const uint8_t *_no_q); +void ff_hevc_h_loop_filter_chroma_10_neon(uint8_t *_pix, ptrdiff_t _stride, + const int *_tc, const uint8_t *_no_p, const uint8_t *_no_q); +void ff_hevc_h_loop_filter_chroma_12_neon(uint8_t *_pix, ptrdiff_t _stride, + const int *_tc, const uint8_t *_no_p, const uint8_t *_no_q); void ff_hevc_add_residual_4x4_8_neon(uint8_t *_dst, const int16_t *coeffs, ptrdiff_t stride); void ff_hevc_add_residual_4x4_10_neon(uint8_t *_dst, const int16_t *coeffs, @@ -49,10 +62,14 @@ void ff_hevc_add_residual_32x32_10_neon(uint8_t *_dst, const int16_t *coeffs, ptrdiff_t stride); void ff_hevc_add_residual_32x32_12_neon(uint8_t *_dst, const int16_t *coeffs, ptrdiff_t stride); +void ff_hevc_idct_4x4_8_neon(int16_t *coeffs, int col_limit); +void ff_hevc_idct_4x4_10_neon(int16_t *coeffs, int col_limit); void ff_hevc_idct_8x8_8_neon(int16_t *coeffs, int col_limit); void ff_hevc_idct_8x8_10_neon(int16_t *coeffs, int col_limit); void ff_hevc_idct_16x16_8_neon(int16_t *coeffs, int col_limit); void ff_hevc_idct_16x16_10_neon(int16_t *coeffs, int col_limit); +void ff_hevc_idct_32x32_8_neon(int16_t *coeffs, int col_limit); +void ff_hevc_idct_32x32_10_neon(int16_t *coeffs, int col_limit); void ff_hevc_idct_4x4_dc_8_neon(int16_t *coeffs); void ff_hevc_idct_8x8_dc_8_neon(int16_t *coeffs); void ff_hevc_idct_16x16_dc_8_neon(int16_t *coeffs); @@ -61,6 +78,7 @@ void ff_hevc_idct_4x4_dc_10_neon(int16_t *coeffs); void ff_hevc_idct_8x8_dc_10_neon(int16_t *coeffs); void ff_hevc_idct_16x16_dc_10_neon(int16_t *coeffs); void ff_hevc_idct_32x32_dc_10_neon(int16_t *coeffs); +void ff_hevc_transform_luma_4x4_neon_8(int16_t *coeffs); void ff_hevc_sao_band_filter_8x8_8_neon(uint8_t *_dst, const uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, const int16_t *sao_offset_val, int sao_left_class, @@ -110,21 +128,124 @@ void ff_hevc_put_hevc_qpel_bi_h16_8_neon(uint8_t *_dst, ptrdiff_t _dststride, co ptrdiff_t _srcstride, const int16_t *src2, int height, intptr_t mx, intptr_t my, int width); +#define NEON8_FNPROTO(fn, args, ext) \ + void ff_hevc_put_hevc_##fn##4_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##6_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##8_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##12_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##16_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##24_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##32_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##48_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##64_8_neon##ext args; \ + +#define NEON8_FNPROTO_PARTIAL_4(fn, args, ext) \ + void ff_hevc_put_hevc_##fn##4_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##8_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##16_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##64_8_neon##ext args; \ + +#define NEON8_FNPROTO_PARTIAL_5(fn, args, ext) \ + void ff_hevc_put_hevc_##fn##4_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##8_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##16_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##32_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##64_8_neon##ext args; \ + +NEON8_FNPROTO(pel_uni_pixels, (uint8_t *_dst, ptrdiff_t _dststride, + const uint8_t *_src, ptrdiff_t _srcstride, + int height, intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO(pel_uni_w_pixels, (uint8_t *_dst, ptrdiff_t _dststride, + const uint8_t *_src, ptrdiff_t _srcstride, + int height, int denom, int wx, int ox, + intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO(epel_uni_w_v, (uint8_t *_dst, ptrdiff_t _dststride, + const uint8_t *_src, ptrdiff_t _srcstride, + int height, int denom, int wx, int ox, + intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO_PARTIAL_4(qpel_uni_w_v, (uint8_t *_dst, ptrdiff_t _dststride, + const uint8_t *_src, ptrdiff_t _srcstride, + int height, int denom, int wx, int ox, + intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO(epel_h, (int16_t *dst, + const uint8_t *_src, ptrdiff_t _srcstride, + int height, intptr_t mx, intptr_t my, int width), _i8mm); + +NEON8_FNPROTO(epel_uni_w_h, (uint8_t *_dst, ptrdiff_t _dststride, + const uint8_t *_src, ptrdiff_t _srcstride, + int height, int denom, int wx, int ox, + intptr_t mx, intptr_t my, int width), _i8mm); + +NEON8_FNPROTO(qpel_h, (int16_t *dst, + const uint8_t *_src, ptrdiff_t _srcstride, + int height, intptr_t mx, intptr_t my, int width), _i8mm); + +NEON8_FNPROTO(qpel_uni_w_h, (uint8_t *_dst, ptrdiff_t _dststride, + const uint8_t *_src, ptrdiff_t _srcstride, + int height, int denom, int wx, int ox, + intptr_t mx, intptr_t my, int width), _i8mm); + +NEON8_FNPROTO(epel_uni_w_hv, (uint8_t *_dst, ptrdiff_t _dststride, + const uint8_t *_src, ptrdiff_t _srcstride, + int height, int denom, int wx, int ox, + intptr_t mx, intptr_t my, int width), _i8mm); + +NEON8_FNPROTO_PARTIAL_5(qpel_uni_w_hv, (uint8_t *_dst, ptrdiff_t _dststride, + const uint8_t *_src, ptrdiff_t _srcstride, + int height, int denom, int wx, int ox, + intptr_t mx, intptr_t my, int width), _i8mm); + +#define NEON8_FNASSIGN(member, v, h, fn, ext) \ + member[1][v][h] = ff_hevc_put_hevc_##fn##4_8_neon##ext; \ + member[2][v][h] = ff_hevc_put_hevc_##fn##6_8_neon##ext; \ + member[3][v][h] = ff_hevc_put_hevc_##fn##8_8_neon##ext; \ + member[4][v][h] = ff_hevc_put_hevc_##fn##12_8_neon##ext; \ + member[5][v][h] = ff_hevc_put_hevc_##fn##16_8_neon##ext; \ + member[6][v][h] = ff_hevc_put_hevc_##fn##24_8_neon##ext; \ + member[7][v][h] = ff_hevc_put_hevc_##fn##32_8_neon##ext; \ + member[8][v][h] = ff_hevc_put_hevc_##fn##48_8_neon##ext; \ + member[9][v][h] = ff_hevc_put_hevc_##fn##64_8_neon##ext; + +#define NEON8_FNASSIGN_PARTIAL_4(member, v, h, fn, ext) \ + member[1][v][h] = ff_hevc_put_hevc_##fn##4_8_neon##ext; \ + member[3][v][h] = ff_hevc_put_hevc_##fn##8_8_neon##ext; \ + member[5][v][h] = ff_hevc_put_hevc_##fn##16_8_neon##ext; \ + member[7][v][h] = ff_hevc_put_hevc_##fn##64_8_neon##ext; \ + member[8][v][h] = ff_hevc_put_hevc_##fn##64_8_neon##ext; \ + member[9][v][h] = ff_hevc_put_hevc_##fn##64_8_neon##ext; + +#define NEON8_FNASSIGN_PARTIAL_5(member, v, h, fn, ext) \ + member[1][v][h] = ff_hevc_put_hevc_##fn##4_8_neon##ext; \ + member[3][v][h] = ff_hevc_put_hevc_##fn##8_8_neon##ext; \ + member[5][v][h] = ff_hevc_put_hevc_##fn##16_8_neon##ext; \ + member[7][v][h] = ff_hevc_put_hevc_##fn##32_8_neon##ext; \ + member[9][v][h] = ff_hevc_put_hevc_##fn##64_8_neon##ext; + av_cold void ff_hevc_dsp_init_aarch64(HEVCDSPContext *c, const int bit_depth) { - if (!have_neon(av_get_cpu_flags())) return; + int cpu_flags = av_get_cpu_flags(); + if (!have_neon(cpu_flags)) return; if (bit_depth == 8) { + c->hevc_h_loop_filter_chroma = ff_hevc_h_loop_filter_chroma_8_neon; + c->hevc_v_loop_filter_chroma = ff_hevc_v_loop_filter_chroma_8_neon; c->add_residual[0] = ff_hevc_add_residual_4x4_8_neon; c->add_residual[1] = ff_hevc_add_residual_8x8_8_neon; c->add_residual[2] = ff_hevc_add_residual_16x16_8_neon; c->add_residual[3] = ff_hevc_add_residual_32x32_8_neon; + c->idct[0] = ff_hevc_idct_4x4_8_neon; c->idct[1] = ff_hevc_idct_8x8_8_neon; c->idct[2] = ff_hevc_idct_16x16_8_neon; + c->idct[3] = ff_hevc_idct_32x32_8_neon; c->idct_dc[0] = ff_hevc_idct_4x4_dc_8_neon; c->idct_dc[1] = ff_hevc_idct_8x8_dc_8_neon; c->idct_dc[2] = ff_hevc_idct_16x16_dc_8_neon; c->idct_dc[3] = ff_hevc_idct_32x32_dc_8_neon; + c->transform_4x4_luma = ff_hevc_transform_luma_4x4_neon_8; c->sao_band_filter[0] = c->sao_band_filter[1] = c->sao_band_filter[2] = @@ -162,20 +283,43 @@ av_cold void ff_hevc_dsp_init_aarch64(HEVCDSPContext *c, const int bit_depth) c->put_hevc_qpel_bi[7][0][1] = c->put_hevc_qpel_bi[8][0][1] = c->put_hevc_qpel_bi[9][0][1] = ff_hevc_put_hevc_qpel_bi_h16_8_neon; + + NEON8_FNASSIGN(c->put_hevc_epel_uni, 0, 0, pel_uni_pixels,); + NEON8_FNASSIGN(c->put_hevc_qpel_uni, 0, 0, pel_uni_pixels,); + NEON8_FNASSIGN(c->put_hevc_epel_uni_w, 0, 0, pel_uni_w_pixels,); + NEON8_FNASSIGN(c->put_hevc_qpel_uni_w, 0, 0, pel_uni_w_pixels,); + NEON8_FNASSIGN(c->put_hevc_epel_uni_w, 1, 0, epel_uni_w_v,); + NEON8_FNASSIGN_PARTIAL_4(c->put_hevc_qpel_uni_w, 1, 0, qpel_uni_w_v,); + + if (have_i8mm(cpu_flags)) { + NEON8_FNASSIGN(c->put_hevc_epel, 0, 1, epel_h, _i8mm); + NEON8_FNASSIGN(c->put_hevc_epel_uni_w, 0, 1, epel_uni_w_h ,_i8mm); + NEON8_FNASSIGN(c->put_hevc_qpel, 0, 1, qpel_h, _i8mm); + NEON8_FNASSIGN(c->put_hevc_qpel_uni_w, 0, 1, qpel_uni_w_h, _i8mm); + NEON8_FNASSIGN(c->put_hevc_epel_uni_w, 1, 1, epel_uni_w_hv, _i8mm); + NEON8_FNASSIGN_PARTIAL_5(c->put_hevc_qpel_uni_w, 1, 1, qpel_uni_w_hv, _i8mm); + } + } if (bit_depth == 10) { + c->hevc_h_loop_filter_chroma = ff_hevc_h_loop_filter_chroma_10_neon; + c->hevc_v_loop_filter_chroma = ff_hevc_v_loop_filter_chroma_10_neon; c->add_residual[0] = ff_hevc_add_residual_4x4_10_neon; c->add_residual[1] = ff_hevc_add_residual_8x8_10_neon; c->add_residual[2] = ff_hevc_add_residual_16x16_10_neon; c->add_residual[3] = ff_hevc_add_residual_32x32_10_neon; + c->idct[0] = ff_hevc_idct_4x4_10_neon; c->idct[1] = ff_hevc_idct_8x8_10_neon; c->idct[2] = ff_hevc_idct_16x16_10_neon; + c->idct[3] = ff_hevc_idct_32x32_10_neon; c->idct_dc[0] = ff_hevc_idct_4x4_dc_10_neon; c->idct_dc[1] = ff_hevc_idct_8x8_dc_10_neon; c->idct_dc[2] = ff_hevc_idct_16x16_dc_10_neon; c->idct_dc[3] = ff_hevc_idct_32x32_dc_10_neon; } if (bit_depth == 12) { + c->hevc_h_loop_filter_chroma = ff_hevc_h_loop_filter_chroma_12_neon; + c->hevc_v_loop_filter_chroma = ff_hevc_v_loop_filter_chroma_12_neon; c->add_residual[0] = ff_hevc_add_residual_4x4_12_neon; c->add_residual[1] = ff_hevc_add_residual_8x8_12_neon; c->add_residual[2] = ff_hevc_add_residual_16x16_12_neon; diff --git a/libavcodec/aarch64/hevcdsp_qpel_neon.S b/libavcodec/aarch64/hevcdsp_qpel_neon.S index 0e7b912678d..e38dff9645d 100644 --- a/libavcodec/aarch64/hevcdsp_qpel_neon.S +++ b/libavcodec/aarch64/hevcdsp_qpel_neon.S @@ -30,6 +30,13 @@ const qpel_filters, align=4 .byte 0, 1, -5, 17, 58,-10, 4, -1 endconst +const qpel_filters_abs, align=4 + .byte 0, 0, 0, 0, 0, 0, 0, 0 + .byte 1, 4, 10, 58, 17, 5, 1, 0 + .byte 1, 4, 11, 40, 40, 11, 4, 1 + .byte 0, 1, 5, 17, 58, 10, 4, 1 +endconst + .macro load_filter m movrel x15, qpel_filters add x15, x15, \m, lsl #3 @@ -482,3 +489,2322 @@ endfunc put_hevc qpel put_hevc qpel_uni put_hevc qpel_bi + +function ff_hevc_put_hevc_pel_uni_pixels4_8_neon, export=1 +1: + ldr s0, [x2] + ldr s1, [x2, x3] + subs w4, w4, #2 + add x2, x2, x3, lsl #1 + str s0, [x0] + str s1, [x0, x1] + add x0, x0, x1, lsl #1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_pixels6_8_neon, export=1 + sub x1, x1, #4 +1: + ldr d0, [x2] + ldr d1, [x2, x3] + subs w4, w4, #2 + add x2, x2, x3, lsl #1 + str s0, [x0], #4 + st1 {v0.h}[2], [x0], x1 + str s1, [x0], #4 + st1 {v1.h}[2], [x0], x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_pixels8_8_neon, export=1 +1: + ldr d0, [x2] + ldr d1, [x2, x3] + subs w4, w4, #2 + add x2, x2, x3, lsl #1 + str d0, [x0] + str d1, [x0, x1] + add x0, x0, x1, lsl #1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_pixels12_8_neon, export=1 + sub x1, x1, #8 +1: + ldr q0, [x2] + ldr q1, [x2, x3] + subs w4, w4, #2 + add x2, x2, x3, lsl #1 + str d0, [x0], #8 + st1 {v0.s}[2], [x0], x1 + str d1, [x0], #8 + st1 {v1.s}[2], [x0], x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_pixels16_8_neon, export=1 +1: + ldr q0, [x2] + ldr q1, [x2, x3] + subs w4, w4, #2 + add x2, x2, x3, lsl #1 + str q0, [x0] + str q1, [x0, x1] + add x0, x0, x1, lsl #1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_pixels24_8_neon, export=1 +1: + ld1 {v0.8b, v1.8b, v2.8b}, [x2], x3 + subs w4, w4, #1 + st1 {v0.8b, v1.8b, v2.8b}, [x0], x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_pixels32_8_neon, export=1 +1: + ld1 {v0.16b, v1.16b}, [x2], x3 + subs w4, w4, #1 + st1 {v0.16b, v1.16b}, [x0], x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_pixels48_8_neon, export=1 +1: + ld1 {v0.16b, v1.16b, v2.16b}, [x2], x3 + subs w4, w4, #1 + st1 {v0.16b, v1.16b, v2.16b}, [x0], x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_pixels64_8_neon, export=1 +1: + ld1 {v0.16b, v1.16b, v2.16b, v3.16b}, [x2], x3 + subs w4, w4, #1 + st1 {v0.16b, v1.16b, v2.16b, v3.16b}, [x0], x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels4_8_neon, export=1 + mov w10, #-6 + sub w10, w10, w5 + dup v30.8h, w6 + dup v31.4s, w10 + dup v29.4s, w7 +1: + ldr s0, [x2] + ldr s1, [x2, x3] + add x2, x2, x3, lsl #1 + ushll v0.8h, v0.8b, #6 + ushll v1.8h, v1.8b, #6 + smull v0.4s, v0.4h, v30.4h + smull v1.4s, v1.4h, v30.4h + sqrshl v0.4s, v0.4s, v31.4s + sqrshl v1.4s, v1.4s, v31.4s + sqadd v0.4s, v0.4s, v29.4s + sqadd v1.4s, v1.4s, v29.4s + sqxtn v0.4h, v0.4s + sqxtn v1.4h, v1.4s + sqxtun v0.8b, v0.8h + sqxtun v1.8b, v1.8h + str s0, [x0] + str s1, [x0, x1] + add x0, x0, x1, lsl #1 + subs w4, w4, #2 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels6_8_neon, export=1 + mov w10, #-6 + sub w10, w10, w5 + dup v30.8h, w6 + dup v31.4s, w10 + dup v29.4s, w7 + sub x1, x1, #4 +1: + ldr d0, [x2] + ldr d1, [x2, x3] + add x2, x2, x3, lsl #1 + ushll v0.8h, v0.8b, #6 + ushll v1.8h, v1.8b, #6 + smull v4.4s, v0.4h, v30.4h + smull2 v5.4s, v0.8h, v30.8h + smull v6.4s, v1.4h, v30.4h + smull2 v7.4s, v1.8h, v30.8h + sqrshl v4.4s, v4.4s, v31.4s + sqrshl v5.4s, v5.4s, v31.4s + sqrshl v6.4s, v6.4s, v31.4s + sqrshl v7.4s, v7.4s, v31.4s + sqadd v4.4s, v4.4s, v29.4s + sqadd v5.4s, v5.4s, v29.4s + sqadd v6.4s, v6.4s, v29.4s + sqadd v7.4s, v7.4s, v29.4s + sqxtn v0.4h, v4.4s + sqxtn2 v0.8h, v5.4s + sqxtn v1.4h, v6.4s + sqxtn2 v1.8h, v7.4s + sqxtun v0.8b, v0.8h + sqxtun v1.8b, v1.8h + str s0, [x0], #4 + st1 {v0.h}[2], [x0], x1 + str s1, [x0], #4 + st1 {v1.h}[2], [x0], x1 + subs w4, w4, #2 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels8_8_neon, export=1 + mov w10, #-6 + sub w10, w10, w5 + dup v30.8h, w6 + dup v31.4s, w10 + dup v29.4s, w7 +1: + ldr d0, [x2] + ldr d1, [x2, x3] + add x2, x2, x3, lsl #1 + ushll v0.8h, v0.8b, #6 + ushll v1.8h, v1.8b, #6 + smull v4.4s, v0.4h, v30.4h + smull2 v5.4s, v0.8h, v30.8h + smull v6.4s, v1.4h, v30.4h + smull2 v7.4s, v1.8h, v30.8h + sqrshl v4.4s, v4.4s, v31.4s + sqrshl v5.4s, v5.4s, v31.4s + sqrshl v6.4s, v6.4s, v31.4s + sqrshl v7.4s, v7.4s, v31.4s + sqadd v4.4s, v4.4s, v29.4s + sqadd v5.4s, v5.4s, v29.4s + sqadd v6.4s, v6.4s, v29.4s + sqadd v7.4s, v7.4s, v29.4s + sqxtn v0.4h, v4.4s + sqxtn2 v0.8h, v5.4s + sqxtn v1.4h, v6.4s + sqxtn2 v1.8h, v7.4s + sqxtun v0.8b, v0.8h + sqxtun v1.8b, v1.8h + str d0, [x0] + str d1, [x0, x1] + add x0, x0, x1, lsl #1 + subs w4, w4, #2 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels12_8_neon, export=1 + mov w10, #-6 + sub w10, w10, w5 + dup v30.8h, w6 + dup v31.4s, w10 + dup v29.4s, w7 + sub x1, x1, #8 +1: + ldr q0, [x2] + ldr q1, [x2, x3] + add x2, x2, x3, lsl #1 + ushll v4.8h, v0.8b, #6 + ushll2 v5.8h, v0.16b, #6 + ushll v6.8h, v1.8b, #6 + ushll2 v7.8h, v1.16b, #6 + smull v16.4s, v4.4h, v30.4h + smull2 v17.4s, v4.8h, v30.8h + smull v18.4s, v5.4h, v30.4h + smull2 v19.4s, v5.8h, v30.8h + smull v20.4s, v6.4h, v30.4h + smull2 v21.4s, v6.8h, v30.8h + smull v22.4s, v7.4h, v30.4h + smull2 v23.4s, v7.8h, v30.8h + + sqrshl v16.4s, v16.4s, v31.4s + sqrshl v17.4s, v17.4s, v31.4s + sqrshl v18.4s, v18.4s, v31.4s + sqrshl v19.4s, v19.4s, v31.4s + sqrshl v20.4s, v20.4s, v31.4s + sqrshl v21.4s, v21.4s, v31.4s + sqrshl v22.4s, v22.4s, v31.4s + sqrshl v23.4s, v23.4s, v31.4s + sqadd v16.4s, v16.4s, v29.4s + sqadd v17.4s, v17.4s, v29.4s + sqadd v18.4s, v18.4s, v29.4s + sqadd v19.4s, v19.4s, v29.4s + sqadd v20.4s, v20.4s, v29.4s + sqadd v21.4s, v21.4s, v29.4s + sqadd v22.4s, v22.4s, v29.4s + sqadd v23.4s, v23.4s, v29.4s + sqxtn v0.4h, v16.4s + sqxtn2 v0.8h, v17.4s + sqxtn v1.4h, v18.4s + sqxtn2 v1.8h, v19.4s + sqxtn v2.4h, v20.4s + sqxtn2 v2.8h, v21.4s + sqxtn v3.4h, v22.4s + sqxtn2 v3.8h, v23.4s + sqxtun v0.8b, v0.8h + sqxtun2 v0.16b, v1.8h + sqxtun v2.8b, v2.8h + sqxtun2 v2.16b, v3.8h + str d0, [x0], #8 + st1 {v0.s}[2], [x0], x1 + str d2, [x0], #8 + st1 {v2.s}[2], [x0], x1 + subs w4, w4, #2 + b.ne 1b + ret +endfunc + +.macro PEL_UNI_W_PIXEL_CALC s0, t0, t1, d0, d1, d2, d3 + ushll \t0\().8h, \s0\().8b, #6 + ushll2 \t1\().8h, \s0\().16b, #6 + smull \d0\().4s, \t0\().4h, v30.4h + smull2 \d1\().4s, \t0\().8h, v30.8h + smull \d2\().4s, \t1\().4h, v30.4h + smull2 \d3\().4s, \t1\().8h, v30.8h + sqrshl \d0\().4s, \d0\().4s, v31.4s + sqrshl \d1\().4s, \d1\().4s, v31.4s + sqrshl \d2\().4s, \d2\().4s, v31.4s + sqrshl \d3\().4s, \d3\().4s, v31.4s + sqadd \d0\().4s, \d0\().4s, v29.4s + sqadd \d1\().4s, \d1\().4s, v29.4s + sqadd \d2\().4s, \d2\().4s, v29.4s + sqadd \d3\().4s, \d3\().4s, v29.4s + sqxtn \t0\().4h, \d0\().4s + sqxtn2 \t0\().8h, \d1\().4s + sqxtn \t1\().4h, \d2\().4s + sqxtn2 \t1\().8h, \d3\().4s + sqxtun \s0\().8b, \t0\().8h + sqxtun2 \s0\().16b, \t1\().8h +.endm + + +function ff_hevc_put_hevc_pel_uni_w_pixels16_8_neon, export=1 + mov w10, #-6 + sub w10, w10, w5 + dup v30.8h, w6 + dup v31.4s, w10 + dup v29.4s, w7 +1: + ldr q0, [x2] + ldr q1, [x2, x3] + add x2, x2, x3, lsl #1 + PEL_UNI_W_PIXEL_CALC v0, v4, v5, v16, v17, v18, v19 + PEL_UNI_W_PIXEL_CALC v1, v6, v7, v20, v21, v22, v23 + str q0, [x0] + str q1, [x0, x1] + add x0, x0, x1, lsl #1 + subs w4, w4, #2 + b.ne 1b + ret +endfunc + + + +function ff_hevc_put_hevc_pel_uni_w_pixels24_8_neon, export=1 + mov w10, #-6 + sub w10, w10, w5 + dup v30.8h, w6 + dup v31.4s, w10 + dup v29.4s, w7 +1: + ld1 {v0.16b, v1.16b}, [x2], x3 + ushll v4.8h, v0.8b, #6 + ushll2 v5.8h, v0.16b, #6 + ushll v6.8h, v1.8b, #6 + smull v16.4s, v4.4h, v30.4h + smull2 v17.4s, v4.8h, v30.8h + smull v18.4s, v5.4h, v30.4h + smull2 v19.4s, v5.8h, v30.8h + smull v20.4s, v6.4h, v30.4h + smull2 v21.4s, v6.8h, v30.8h + sqrshl v16.4s, v16.4s, v31.4s + sqrshl v17.4s, v17.4s, v31.4s + sqrshl v18.4s, v18.4s, v31.4s + sqrshl v19.4s, v19.4s, v31.4s + sqrshl v20.4s, v20.4s, v31.4s + sqrshl v21.4s, v21.4s, v31.4s + sqadd v16.4s, v16.4s, v29.4s + sqadd v17.4s, v17.4s, v29.4s + sqadd v18.4s, v18.4s, v29.4s + sqadd v19.4s, v19.4s, v29.4s + sqadd v20.4s, v20.4s, v29.4s + sqadd v21.4s, v21.4s, v29.4s + sqxtn v0.4h, v16.4s + sqxtn2 v0.8h, v17.4s + sqxtn v1.4h, v18.4s + sqxtn2 v1.8h, v19.4s + sqxtn v2.4h, v20.4s + sqxtn2 v2.8h, v21.4s + sqxtun v0.8b, v0.8h + sqxtun v1.8b, v1.8h + sqxtun v2.8b, v2.8h + st1 {v0.8b, v1.8b, v2.8b}, [x0], x1 + subs w4, w4, #1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels32_8_neon, export=1 + mov w10, #-6 + sub w10, w10, w5 + dup v30.8h, w6 + dup v31.4s, w10 + dup v29.4s, w7 +1: + ld1 {v0.16b, v1.16b}, [x2], x3 + PEL_UNI_W_PIXEL_CALC v0, v4, v5, v16, v17, v18, v19 + PEL_UNI_W_PIXEL_CALC v1, v6, v7, v20, v21, v22, v23 + st1 {v0.16b, v1.16b}, [x0], x1 + subs w4, w4, #1 + b.ne 1b + ret +endfunc + + +function ff_hevc_put_hevc_pel_uni_w_pixels48_8_neon, export=1 + mov w10, #-6 + sub w10, w10, w5 + dup v30.8h, w6 + dup v31.4s, w10 + dup v29.4s, w7 +1: + ld1 {v0.16b, v1.16b, v2.16b}, [x2], x3 + PEL_UNI_W_PIXEL_CALC v0, v4, v5, v16, v17, v18, v19 + PEL_UNI_W_PIXEL_CALC v1, v6, v7, v20, v21, v22, v23 + PEL_UNI_W_PIXEL_CALC v2, v4, v5, v16, v17, v18, v19 + st1 {v0.16b, v1.16b, v2.16b}, [x0], x1 + subs w4, w4, #1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels64_8_neon, export=1 + mov w10, #-6 + sub w10, w10, w5 + dup v30.8h, w6 + dup v31.4s, w10 + dup v29.4s, w7 +1: + ld1 {v0.16b, v1.16b, v2.16b, v3.16b}, [x2], x3 + PEL_UNI_W_PIXEL_CALC v0, v4, v5, v16, v17, v18, v19 + PEL_UNI_W_PIXEL_CALC v1, v6, v7, v20, v21, v22, v23 + PEL_UNI_W_PIXEL_CALC v2, v4, v5, v16, v17, v18, v19 + PEL_UNI_W_PIXEL_CALC v3, v6, v7, v20, v21, v22, v23 + st1 {v0.16b, v1.16b, v2.16b, v3.16b}, [x0], x1 + subs w4, w4, #1 + b.ne 1b + ret +endfunc + +.macro QPEL_UNI_W_V_HEADER + ldur x12, [sp, #8] // my + sub x2, x2, x3, lsl #1 + sub x2, x2, x3 + movrel x9, qpel_filters_abs + add x9, x9, x12, lsl #3 + ldr d28, [x9] + dup v0.16b, v28.b[0] + dup v1.16b, v28.b[1] + dup v2.16b, v28.b[2] + dup v3.16b, v28.b[3] + dup v4.16b, v28.b[4] + dup v5.16b, v28.b[5] + dup v6.16b, v28.b[6] + dup v7.16b, v28.b[7] + + mov w10, #-6 + sub w10, w10, w5 + dup v30.8h, w6 // wx + dup v31.4s, w10 // shift + dup v29.4s, w7 // ox +.endm + +.macro QPEL_FILTER_B dst, src0, src1, src2, src3, src4, src5, src6, src7 + umull \dst\().8h, \src1\().8b, v1.8b + umlsl \dst\().8h, \src0\().8b, v0.8b + umlsl \dst\().8h, \src2\().8b, v2.8b + umlal \dst\().8h, \src3\().8b, v3.8b + umlal \dst\().8h, \src4\().8b, v4.8b + umlsl \dst\().8h, \src5\().8b, v5.8b + umlal \dst\().8h, \src6\().8b, v6.8b + umlsl \dst\().8h, \src7\().8b, v7.8b +.endm + +.macro QPEL_FILTER_B2 dst, src0, src1, src2, src3, src4, src5, src6, src7 + umull2 \dst\().8h, \src1\().16b, v1.16b + umlsl2 \dst\().8h, \src0\().16b, v0.16b + umlsl2 \dst\().8h, \src2\().16b, v2.16b + umlal2 \dst\().8h, \src3\().16b, v3.16b + umlal2 \dst\().8h, \src4\().16b, v4.16b + umlsl2 \dst\().8h, \src5\().16b, v5.16b + umlal2 \dst\().8h, \src6\().16b, v6.16b + umlsl2 \dst\().8h, \src7\().16b, v7.16b +.endm + +.macro QPEL_UNI_W_V_4 + smull v24.4s, v24.4h, v30.4h + sqrshl v24.4s, v24.4s, v31.4s + sqadd v24.4s, v24.4s, v29.4s + sqxtn v24.4h, v24.4s + sqxtun v24.8b, v24.8h + st1 {v24.s}[0], [x0], x1 +.endm + +function ff_hevc_put_hevc_qpel_uni_w_v4_8_neon, export=1 + QPEL_UNI_W_V_HEADER + ldr s16, [x2] + ldr s17, [x2, x3] + add x2, x2, x3, lsl #1 + ldr s18, [x2] + ldr s19, [x2, x3] + add x2, x2, x3, lsl #1 + ldr s20, [x2] + ldr s21, [x2, x3] + add x2, x2, x3, lsl #1 + ldr s22, [x2] + +1: ldr s23, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v24, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_UNI_W_V_4 + subs w4, w4, #1 + b.eq 2f + + ldr s16, [x2] + QPEL_FILTER_B v24, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_UNI_W_V_4 + subs w4, w4, #1 + b.eq 2f + + ldr s17, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v24, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_UNI_W_V_4 + subs w4, w4, #1 + b.eq 2f + + ldr s18, [x2] + QPEL_FILTER_B v24, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_UNI_W_V_4 + subs w4, w4, #1 + b.eq 2f + + ldr s19, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v24, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_UNI_W_V_4 + subs w4, w4, #1 + b.eq 2f + + ldr s20, [x2] + QPEL_FILTER_B v24, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_UNI_W_V_4 + subs w4, w4, #1 + b.eq 2f + + ldr s21, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v24, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_UNI_W_V_4 + subs w4, w4, #1 + b.eq 2f + + ldr s22, [x2] + QPEL_FILTER_B v24, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_UNI_W_V_4 + subs w4, w4, #1 + b.ne 1b +2: + ret +endfunc + +.macro QPEL_UNI_W_V_8 + smull v24.4s, v26.4h, v30.4h + smull2 v25.4s, v26.8h, v30.8h + sqrshl v24.4s, v24.4s, v31.4s + sqrshl v25.4s, v25.4s, v31.4s + sqadd v24.4s, v24.4s, v29.4s + sqadd v25.4s, v25.4s, v29.4s + sqxtn v24.4h, v24.4s + sqxtn2 v24.8h, v25.4s + sqxtun v24.8b, v24.8h + st1 {v24.d}[0], [x0], x1 +.endm + +function ff_hevc_put_hevc_qpel_uni_w_v8_8_neon, export=1 + QPEL_UNI_W_V_HEADER + ldr d16, [x2] + ldr d17, [x2, x3] + add x2, x2, x3, lsl #1 + ldr d18, [x2] + ldr d19, [x2, x3] + add x2, x2, x3, lsl #1 + ldr d20, [x2] + ldr d21, [x2, x3] + add x2, x2, x3, lsl #1 + ldr d22, [x2] + +1: ldr d23, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v26, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_UNI_W_V_8 + subs w4, w4, #1 + b.eq 2f + + ldr d16, [x2] + QPEL_FILTER_B v26, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_UNI_W_V_8 + subs w4, w4, #1 + b.eq 2f + + ldr d17, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v26, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_UNI_W_V_8 + subs w4, w4, #1 + b.eq 2f + + ldr d18, [x2] + QPEL_FILTER_B v26, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_UNI_W_V_8 + subs w4, w4, #1 + b.eq 2f + + ldr d19, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v26, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_UNI_W_V_8 + subs w4, w4, #1 + b.eq 2f + + ldr d20, [x2] + QPEL_FILTER_B v26, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_UNI_W_V_8 + subs w4, w4, #1 + b.eq 2f + + ldr d21, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v26, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_UNI_W_V_8 + subs w4, w4, #1 + b.eq 2f + + ldr d22, [x2] + QPEL_FILTER_B v26, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_UNI_W_V_8 + subs w4, w4, #1 + b.ne 1b +2: + ret +endfunc + +.macro QPEL_UNI_W_V_16 + smull v24.4s, v26.4h, v30.4h + smull2 v25.4s, v26.8h, v30.8h + smull v26.4s, v27.4h, v30.4h + smull2 v27.4s, v27.8h, v30.8h + sqrshl v24.4s, v24.4s, v31.4s + sqrshl v25.4s, v25.4s, v31.4s + sqrshl v26.4s, v26.4s, v31.4s + sqrshl v27.4s, v27.4s, v31.4s + sqadd v24.4s, v24.4s, v29.4s + sqadd v25.4s, v25.4s, v29.4s + sqadd v26.4s, v26.4s, v29.4s + sqadd v27.4s, v27.4s, v29.4s + sqxtn v24.4h, v24.4s + sqxtn2 v24.8h, v25.4s + sqxtn v26.4h, v26.4s + sqxtn2 v26.8h, v27.4s + sqxtun v24.8b, v24.8h + sqxtun2 v24.16b, v26.8h + st1 {v24.16b}, [x0], x1 +.endm + +function ff_hevc_put_hevc_qpel_uni_w_v16_8_neon, export=1 + QPEL_UNI_W_V_HEADER + ldr q16, [x2] + ldr q17, [x2, x3] + add x2, x2, x3, lsl #1 + ldr q18, [x2] + ldr q19, [x2, x3] + add x2, x2, x3, lsl #1 + ldr q20, [x2] + ldr q21, [x2, x3] + add x2, x2, x3, lsl #1 + ldr q22, [x2] + +1: ldr q23, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v26, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_FILTER_B2 v27, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q16, [x2] + QPEL_FILTER_B v26, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_FILTER_B2 v27, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q17, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v26, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_FILTER_B2 v27, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q18, [x2] + QPEL_FILTER_B v26, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_FILTER_B2 v27, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q19, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v26, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_FILTER_B2 v27, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q20, [x2] + QPEL_FILTER_B v26, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_FILTER_B2 v27, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q21, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v26, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_FILTER_B2 v27, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q22, [x2] + QPEL_FILTER_B v26, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_FILTER_B2 v27, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.ne 1b +2: + ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_v64_8_neon, export=1 + QPEL_UNI_W_V_HEADER + ldur w13, [sp, #16] + mov x14, x0 + mov x15, x2 + mov w11, w4 + +3: + ldr q16, [x2] + ldr q17, [x2, x3] + add x2, x2, x3, lsl #1 + ldr q18, [x2] + ldr q19, [x2, x3] + add x2, x2, x3, lsl #1 + ldr q20, [x2] + ldr q21, [x2, x3] + add x2, x2, x3, lsl #1 + ldr q22, [x2] + + +1: ldr q23, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v26, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_FILTER_B2 v27, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q16, [x2] + QPEL_FILTER_B v26, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_FILTER_B2 v27, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q17, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v26, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_FILTER_B2 v27, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q18, [x2] + QPEL_FILTER_B v26, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_FILTER_B2 v27, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q19, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v26, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_FILTER_B2 v27, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q20, [x2] + QPEL_FILTER_B v26, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_FILTER_B2 v27, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q21, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v26, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_FILTER_B2 v27, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q22, [x2] + QPEL_FILTER_B v26, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_FILTER_B2 v27, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.ne 1b +2: + subs w13, w13, #16 + add x14, x14, #16 + add x15, x15, #16 + mov x0, x14 + mov x2, x15 + mov w4, w11 + b.hi 3b + ret +endfunc + +#if HAVE_I8MM +.macro QPEL_UNI_W_H_HEADER + ldr x12, [sp] + sub x2, x2, #3 + movrel x9, qpel_filters + add x9, x9, x12, lsl #3 + ldr x11, [x9] + dup v28.2d, x11 + mov w10, #-6 + sub w10, w10, w5 + dup v30.4s, w6 // wx + dup v31.4s, w10 // shift + dup v29.4s, w7 // ox +.endm + +function ff_hevc_put_hevc_qpel_uni_w_h4_8_neon_i8mm, export=1 + QPEL_UNI_W_H_HEADER +1: + ld1 {v0.16b}, [x2], x3 + ext v1.16b, v0.16b, v0.16b, #1 + ext v2.16b, v0.16b, v0.16b, #2 + ext v3.16b, v0.16b, v0.16b, #3 + zip1 v0.2d, v0.2d, v1.2d + zip1 v2.2d, v2.2d, v3.2d + movi v16.2d, #0 + movi v17.2d, #0 + usdot v16.4s, v0.16b, v28.16b + usdot v17.4s, v2.16b, v28.16b + addp v16.4s, v16.4s, v17.4s + mul v16.4s, v16.4s, v30.4s + sqrshl v16.4s, v16.4s, v31.4s + sqadd v16.4s, v16.4s, v29.4s + sqxtn v16.4h, v16.4s + sqxtun v16.8b, v16.8h + str s16, [x0] + add x0, x0, x1 + subs w4, w4, #1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h6_8_neon_i8mm, export=1 + QPEL_UNI_W_H_HEADER + sub x1, x1, #4 +1: + ld1 {v0.16b}, [x2], x3 + ext v1.16b, v0.16b, v0.16b, #1 + ext v2.16b, v0.16b, v0.16b, #2 + ext v3.16b, v0.16b, v0.16b, #3 + ext v4.16b, v0.16b, v0.16b, #4 + ext v5.16b, v0.16b, v0.16b, #5 + zip1 v0.2d, v0.2d, v1.2d + zip1 v2.2d, v2.2d, v3.2d + zip1 v4.2d, v4.2d, v5.2d + movi v16.2d, #0 + movi v17.2d, #0 + movi v18.2d, #0 + usdot v16.4s, v0.16b, v28.16b + usdot v17.4s, v2.16b, v28.16b + usdot v18.4s, v4.16b, v28.16b + addp v16.4s, v16.4s, v17.4s + addp v18.4s, v18.4s, v18.4s + mul v16.4s, v16.4s, v30.4s + mul v18.2s, v18.2s, v30.2s + sqrshl v16.4s, v16.4s, v31.4s + sqrshl v18.2s, v18.2s, v31.2s + sqadd v16.4s, v16.4s, v29.4s + sqadd v18.2s, v18.2s, v29.2s + sqxtn v16.4h, v16.4s + sqxtn2 v16.8h, v18.4s + sqxtun v16.8b, v16.8h + str s16, [x0], #4 + st1 {v16.h}[2], [x0], x1 + subs w4, w4, #1 + b.hi 1b + ret +endfunc + + +.macro QPEL_UNI_W_H_CALC s0, s1, s2, s3, d0, d1, d2, d3 + movi \d0\().2d, #0 + movi \d1\().2d, #0 + movi \d2\().2d, #0 + movi \d3\().2d, #0 + usdot \d0\().4s, \s0\().16b, v28.16b + usdot \d1\().4s, \s1\().16b, v28.16b + usdot \d2\().4s, \s2\().16b, v28.16b + usdot \d3\().4s, \s3\().16b, v28.16b + addp \d0\().4s, \d0\().4s, \d1\().4s + addp \d2\().4s, \d2\().4s, \d3\().4s + mul \d0\().4s, \d0\().4s, v30.4s + mul \d2\().4s, \d2\().4s, v30.4s + sqrshl \d0\().4s, \d0\().4s, v31.4s + sqrshl \d2\().4s, \d2\().4s, v31.4s + sqadd \d0\().4s, \d0\().4s, v29.4s + sqadd \d2\().4s, \d2\().4s, v29.4s +.endm + +.macro QPEL_UNI_W_H_CALC_HALF s0, s1, d0, d1 + movi \d0\().2d, #0 + movi \d1\().2d, #0 + usdot \d0\().4s, \s0\().16b, v28.16b + usdot \d1\().4s, \s1\().16b, v28.16b + addp \d0\().4s, \d0\().4s, \d1\().4s + mul \d0\().4s, \d0\().4s, v30.4s + sqrshl \d0\().4s, \d0\().4s, v31.4s + sqadd \d0\().4s, \d0\().4s, v29.4s +.endm + + +function ff_hevc_put_hevc_qpel_uni_w_h8_8_neon_i8mm, export=1 + QPEL_UNI_W_H_HEADER +1: + ld1 {v16.16b, v17.16b}, [x2], x3 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + zip1 v0.2d, v16.2d, v1.2d + zip1 v2.2d, v2.2d, v3.2d + zip1 v4.2d, v4.2d, v5.2d + zip1 v6.2d, v6.2d, v7.2d + QPEL_UNI_W_H_CALC v0, v2, v4, v6, v18, v19, v20, v21 + sqxtn v18.4h, v18.4s + sqxtn2 v18.8h, v20.4s + sqxtun v18.8b, v18.8h + str d18, [x0] + add x0, x0, x1 + subs w4, w4, #1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h12_8_neon_i8mm, export=1 + QPEL_UNI_W_H_HEADER + add x13, x0, #8 +1: + ld1 {v16.16b, v17.16b}, [x2], x3 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + zip1 v18.2d, v16.2d, v1.2d + zip1 v19.2d, v2.2d, v3.2d + zip1 v20.2d, v4.2d, v5.2d + zip1 v21.2d, v6.2d, v7.2d + zip2 v22.2d, v16.2d, v1.2d + zip2 v23.2d, v2.2d, v3.2d + QPEL_UNI_W_H_CALC v18, v19, v20, v21, v0, v2, v4, v6 + QPEL_UNI_W_H_CALC_HALF v22, v23, v24, v25 + sqxtn v0.4h, v0.4s + sqxtn2 v0.8h, v4.4s + sqxtn v1.4h, v24.4s + sqxtun v0.8b, v0.8h + sqxtun v1.8b, v1.8h + + str d0, [x0] + str s1, [x13] + add x0, x0, x1 + add x13, x13, x1 + subs w4, w4, #1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h16_8_neon_i8mm, export=1 + QPEL_UNI_W_H_HEADER +1: + ld1 {v16.16b, v17.16b}, [x2], x3 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + QPEL_UNI_W_H_CALC v16, v2, v1, v3, v18, v19, v20, v21 // v18: 0, 8, 2, 10 v20: 1, 9, 3, 11 + QPEL_UNI_W_H_CALC v4, v6, v5, v7, v22, v23, v24, v25 // v22: 4, 12, 6, 14 v24: 5, 13, 7, 15 + sqxtn v0.4h, v18.4s + sqxtn2 v0.8h, v22.4s + sqxtn v1.4h, v20.4s + sqxtn2 v1.8h, v24.4s + trn1 v2.8h, v0.8h, v1.8h + trn2 v3.8h, v0.8h, v1.8h + sqxtun v0.8b, v2.8h + sqxtun2 v0.16b, v3.8h + st1 {v0.16b}, [x0], x1 + subs w4, w4, #1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h24_8_neon_i8mm, export=1 + QPEL_UNI_W_H_HEADER + sub x1, x1, #16 +1: + ld1 {v16.16b, v17.16b}, [x2], x3 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + QPEL_UNI_W_H_CALC v16, v2, v1, v3, v18, v19, v20, v21 + QPEL_UNI_W_H_CALC v4, v6, v5, v7, v22, v23, v24, v25 + sqxtn v18.4h, v18.4s + sqxtn2 v18.8h, v22.4s + sqxtn v19.4h, v20.4s + sqxtn2 v19.8h, v24.4s + trn1 v20.8h, v18.8h, v19.8h + trn2 v21.8h, v18.8h, v19.8h + sqxtun v26.8b, v20.8h + sqxtun2 v26.16b, v21.8h // 0-15 + ext v1.16b, v17.16b, v17.16b, #1 + ext v2.16b, v17.16b, v17.16b, #2 + ext v3.16b, v17.16b, v17.16b, #3 + ext v4.16b, v17.16b, v17.16b, #4 + ext v5.16b, v17.16b, v17.16b, #5 + ext v6.16b, v17.16b, v17.16b, #6 + ext v7.16b, v17.16b, v17.16b, #7 + zip1 v0.2d, v17.2d, v1.2d + zip1 v2.2d, v2.2d, v3.2d + zip1 v4.2d, v4.2d, v5.2d + zip1 v6.2d, v6.2d, v7.2d + QPEL_UNI_W_H_CALC v0, v2, v4, v6, v18, v19, v20, v21 + sqxtn v18.4h, v18.4s + sqxtn2 v18.8h, v20.4s + sqxtun v27.8b, v18.8h + + st1 {v26.16b}, [x0], #16 + st1 {v27.8b}, [x0], x1 + subs w4, w4, #1 + b.hi 1b + ret +endfunc + + +function ff_hevc_put_hevc_qpel_uni_w_h32_8_neon_i8mm, export=1 + QPEL_UNI_W_H_HEADER +1: + ld1 {v16.16b, v17.16b, v18.16b}, [x2], x3 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + QPEL_UNI_W_H_CALC v16, v2, v1, v3, v0, v19, v20, v21 + QPEL_UNI_W_H_CALC v4, v6, v5, v7, v22, v23, v24, v25 + sqxtn v0.4h, v0.4s + sqxtn2 v0.8h, v22.4s + sqxtn v19.4h, v20.4s + sqxtn2 v19.8h, v24.4s + trn1 v20.8h, v0.8h, v19.8h + trn2 v21.8h, v0.8h, v19.8h + sqxtun v26.8b, v20.8h + sqxtun2 v26.16b, v21.8h // 0-15 + ext v1.16b, v17.16b, v18.16b, #1 + ext v2.16b, v17.16b, v18.16b, #2 + ext v3.16b, v17.16b, v18.16b, #3 + ext v4.16b, v17.16b, v18.16b, #4 + ext v5.16b, v17.16b, v18.16b, #5 + ext v6.16b, v17.16b, v18.16b, #6 + ext v7.16b, v17.16b, v18.16b, #7 + QPEL_UNI_W_H_CALC v17, v2, v1, v3, v0, v19, v20, v21 + QPEL_UNI_W_H_CALC v4, v6, v5, v7, v22, v23, v24, v25 + sqxtn v0.4h, v0.4s + sqxtn2 v0.8h, v22.4s + sqxtn v19.4h, v20.4s + sqxtn2 v19.8h, v24.4s + trn1 v20.8h, v0.8h, v19.8h + trn2 v21.8h, v0.8h, v19.8h + sqxtun v27.8b, v20.8h + sqxtun2 v27.16b, v21.8h // 16-31 + st1 {v26.16b, v27.16b}, [x0], x1 + subs w4, w4, #1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h48_8_neon_i8mm, export=1 + QPEL_UNI_W_H_HEADER +1: + ld1 {v16.16b, v17.16b, v18.16b, v19.16b}, [x2], x3 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + QPEL_UNI_W_H_CALC v16, v2, v1, v3, v20, v24, v21, v0 + QPEL_UNI_W_H_CALC v4, v6, v5, v7, v22, v24, v23, v0 + sqxtn v20.4h, v20.4s + sqxtn2 v20.8h, v22.4s + sqxtn v21.4h, v21.4s + sqxtn2 v21.8h, v23.4s + trn1 v22.8h, v20.8h, v21.8h + trn2 v23.8h, v20.8h, v21.8h + sqxtun v25.8b, v22.8h + sqxtun2 v25.16b, v23.8h // 0-15 + ext v1.16b, v17.16b, v18.16b, #1 + ext v2.16b, v17.16b, v18.16b, #2 + ext v3.16b, v17.16b, v18.16b, #3 + ext v4.16b, v17.16b, v18.16b, #4 + ext v5.16b, v17.16b, v18.16b, #5 + ext v6.16b, v17.16b, v18.16b, #6 + ext v7.16b, v17.16b, v18.16b, #7 + QPEL_UNI_W_H_CALC v17, v2, v1, v3, v20, v24, v21, v0 + QPEL_UNI_W_H_CALC v4, v6, v5, v7, v22, v24, v23, v0 + sqxtn v20.4h, v20.4s + sqxtn2 v20.8h, v22.4s + sqxtn v21.4h, v21.4s + sqxtn2 v21.8h, v23.4s + trn1 v22.8h, v20.8h, v21.8h + trn2 v23.8h, v20.8h, v21.8h + sqxtun v26.8b, v22.8h + sqxtun2 v26.16b, v23.8h // 16-31 + ext v1.16b, v18.16b, v19.16b, #1 + ext v2.16b, v18.16b, v19.16b, #2 + ext v3.16b, v18.16b, v19.16b, #3 + ext v4.16b, v18.16b, v19.16b, #4 + ext v5.16b, v18.16b, v19.16b, #5 + ext v6.16b, v18.16b, v19.16b, #6 + ext v7.16b, v18.16b, v19.16b, #7 + QPEL_UNI_W_H_CALC v18, v2, v1, v3, v20, v24, v21, v0 + QPEL_UNI_W_H_CALC v4, v6, v5, v7, v22, v24, v23, v0 + sqxtn v20.4h, v20.4s + sqxtn2 v20.8h, v22.4s + sqxtn v21.4h, v21.4s + sqxtn2 v21.8h, v23.4s + trn1 v22.8h, v20.8h, v21.8h + trn2 v23.8h, v20.8h, v21.8h + sqxtun v27.8b, v22.8h + sqxtun2 v27.16b, v23.8h // 32-47 + st1 {v25.16b, v26.16b, v27.16b}, [x0], x1 + subs w4, w4, #1 + b.hi 1b + ret +endfunc + + + +function ff_hevc_put_hevc_qpel_uni_w_h64_8_neon_i8mm, export=1 + QPEL_UNI_W_H_HEADER + sub x3, x3, #64 +1: + ld1 {v16.16b, v17.16b, v18.16b, v19.16b}, [x2], #64 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + QPEL_UNI_W_H_CALC v16, v2, v1, v3, v20, v24, v21, v0 + QPEL_UNI_W_H_CALC v4, v6, v5, v7, v22, v24, v23, v0 + sqxtn v20.4h, v20.4s + sqxtn2 v20.8h, v22.4s + sqxtn v21.4h, v21.4s + sqxtn2 v21.8h, v23.4s + trn1 v22.8h, v20.8h, v21.8h + trn2 v23.8h, v20.8h, v21.8h + sqxtun v16.8b, v22.8h + sqxtun2 v16.16b, v23.8h // 0-15 + ext v1.16b, v17.16b, v18.16b, #1 + ext v2.16b, v17.16b, v18.16b, #2 + ext v3.16b, v17.16b, v18.16b, #3 + ext v4.16b, v17.16b, v18.16b, #4 + ext v5.16b, v17.16b, v18.16b, #5 + ext v6.16b, v17.16b, v18.16b, #6 + ext v7.16b, v17.16b, v18.16b, #7 + QPEL_UNI_W_H_CALC v17, v2, v1, v3, v20, v24, v21, v0 + QPEL_UNI_W_H_CALC v4, v6, v5, v7, v22, v24, v23, v0 + sqxtn v20.4h, v20.4s + sqxtn2 v20.8h, v22.4s + sqxtn v21.4h, v21.4s + sqxtn2 v21.8h, v23.4s + trn1 v22.8h, v20.8h, v21.8h + trn2 v23.8h, v20.8h, v21.8h + sqxtun v17.8b, v22.8h + sqxtun2 v17.16b, v23.8h // 16-31 + ext v1.16b, v18.16b, v19.16b, #1 + ext v2.16b, v18.16b, v19.16b, #2 + ext v3.16b, v18.16b, v19.16b, #3 + ext v4.16b, v18.16b, v19.16b, #4 + ext v5.16b, v18.16b, v19.16b, #5 + ext v6.16b, v18.16b, v19.16b, #6 + ext v7.16b, v18.16b, v19.16b, #7 + QPEL_UNI_W_H_CALC v18, v2, v1, v3, v20, v24, v21, v0 + QPEL_UNI_W_H_CALC v4, v6, v5, v7, v22, v24, v23, v0 + ld1 {v0.16b}, [x2], x3 + sqxtn v20.4h, v20.4s + sqxtn2 v20.8h, v22.4s + sqxtn v21.4h, v21.4s + sqxtn2 v21.8h, v23.4s + trn1 v22.8h, v20.8h, v21.8h + trn2 v23.8h, v20.8h, v21.8h + sqxtun v18.8b, v22.8h + sqxtun2 v18.16b, v23.8h // 32-47 + ext v1.16b, v19.16b, v0.16b, #1 + ext v2.16b, v19.16b, v0.16b, #2 + ext v3.16b, v19.16b, v0.16b, #3 + ext v4.16b, v19.16b, v0.16b, #4 + ext v5.16b, v19.16b, v0.16b, #5 + ext v6.16b, v19.16b, v0.16b, #6 + ext v7.16b, v19.16b, v0.16b, #7 + QPEL_UNI_W_H_CALC v19, v2, v1, v3, v20, v24, v21, v0 + QPEL_UNI_W_H_CALC v4, v6, v5, v7, v22, v24, v23, v0 + sqxtn v20.4h, v20.4s + sqxtn2 v20.8h, v22.4s + sqxtn v21.4h, v21.4s + sqxtn2 v21.8h, v23.4s + trn1 v22.8h, v20.8h, v21.8h + trn2 v23.8h, v20.8h, v21.8h + sqxtun v19.8b, v22.8h + sqxtun2 v19.16b, v23.8h // 48-63 + + st1 {v16.16b, v17.16b, v18.16b, v19.16b}, [x0], x1 + subs w4, w4, #1 + b.hi 1b + ret +endfunc + +.macro QPEL_H_HEADER + movrel x9, qpel_filters + add x9, x9, x4, lsl #3 + ldr x11, [x9] + dup v31.2d, x11 + sub x1, x1, #3 +.endm + +function ff_hevc_put_hevc_qpel_h4_8_neon_i8mm, export=1 + QPEL_H_HEADER + mov x10, #MAX_PB_SIZE * 2 +1: + ld1 {v0.16b}, [x1], x2 + ext v1.16b, v0.16b, v0.16b, #1 + ext v2.16b, v0.16b, v0.16b, #2 + ext v3.16b, v0.16b, v0.16b, #3 + zip1 v0.2d, v0.2d, v1.2d + zip1 v2.2d, v2.2d, v3.2d + movi v16.2d, #0 + movi v17.2d, #0 + usdot v16.4s, v0.16b, v31.16b + usdot v17.4s, v2.16b, v31.16b + addp v16.4s, v16.4s, v17.4s + sqxtn v16.4h, v16.4s + str d16, [x0] + add x0, x0, x10 + subs w3, w3, #1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_h6_8_neon_i8mm, export=1 + QPEL_H_HEADER + mov x10, #MAX_PB_SIZE * 2 + add x15, x0, #8 +1: + ld1 {v0.16b}, [x1], x2 + ext v1.16b, v0.16b, v0.16b, #1 + ext v2.16b, v0.16b, v0.16b, #2 + ext v3.16b, v0.16b, v0.16b, #3 + ext v4.16b, v0.16b, v0.16b, #4 + ext v5.16b, v0.16b, v0.16b, #5 + zip1 v0.2d, v0.2d, v1.2d + zip1 v2.2d, v2.2d, v3.2d + zip1 v4.2d, v4.2d, v5.2d + movi v16.2d, #0 + movi v17.2d, #0 + movi v18.2d, #0 + usdot v16.4s, v0.16b, v31.16b + usdot v17.4s, v2.16b, v31.16b + usdot v18.4s, v4.16b, v31.16b + addp v16.4s, v16.4s, v17.4s + addp v18.4s, v18.4s, v18.4s + sqxtn v16.4h, v16.4s + sqxtn v18.4h, v18.4s + str d16, [x0] + str s18, [x15] + add x0, x0, x10 + add x15, x15, x10 + subs w3, w3, #1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_h8_8_neon_i8mm, export=1 + QPEL_H_HEADER + mov x10, #MAX_PB_SIZE * 2 +1: + ld1 {v0.16b}, [x1], x2 + ext v1.16b, v0.16b, v0.16b, #1 + ext v2.16b, v0.16b, v0.16b, #2 + ext v3.16b, v0.16b, v0.16b, #3 + ext v4.16b, v0.16b, v0.16b, #4 + ext v5.16b, v0.16b, v0.16b, #5 + ext v6.16b, v0.16b, v0.16b, #6 + ext v7.16b, v0.16b, v0.16b, #7 + zip1 v0.2d, v0.2d, v1.2d + zip1 v2.2d, v2.2d, v3.2d + zip1 v4.2d, v4.2d, v5.2d + zip1 v6.2d, v6.2d, v7.2d + movi v16.2d, #0 + movi v17.2d, #0 + movi v18.2d, #0 + movi v19.2d, #0 + usdot v16.4s, v0.16b, v31.16b + usdot v17.4s, v2.16b, v31.16b + usdot v18.4s, v4.16b, v31.16b + usdot v19.4s, v6.16b, v31.16b + addp v16.4s, v16.4s, v17.4s + addp v18.4s, v18.4s, v19.4s + sqxtn v16.4h, v16.4s + sqxtn2 v16.8h, v18.4s + str q16, [x0] + add x0, x0, x10 + subs w3, w3, #1 + b.ne 1b + ret +endfunc + +.macro QPEL_H_CALC s0, s1, s2, s3, d0, d1, d2, d3 + movi \d0\().2d, #0 + movi \d1\().2d, #0 + movi \d2\().2d, #0 + movi \d3\().2d, #0 + usdot \d0\().4s, \s0\().16b, v31.16b + usdot \d1\().4s, \s1\().16b, v31.16b + usdot \d2\().4s, \s2\().16b, v31.16b + usdot \d3\().4s, \s3\().16b, v31.16b +.endm + +function ff_hevc_put_hevc_qpel_h12_8_neon_i8mm, export=1 + QPEL_H_HEADER + mov x10, #MAX_PB_SIZE * 2 + add x15, x0, #16 +1: + ld1 {v16.16b, v17.16b}, [x1], x2 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + zip1 v18.2d, v4.2d, v5.2d + zip1 v19.2d, v6.2d, v7.2d + QPEL_H_CALC v16, v1, v2, v3, v20, v21, v22, v23 + addp v20.4s, v20.4s, v22.4s + addp v21.4s, v21.4s, v23.4s + movi v24.2d, #0 + movi v25.2d, #0 + usdot v24.4s, v18.16b, v31.16b + usdot v25.4s, v19.16b, v31.16b + addp v24.4s, v24.4s, v25.4s + trn1 v26.4s, v20.4s, v21.4s + trn2 v27.4s, v20.4s, v21.4s + sqxtn v26.4h, v26.4s + sqxtn v27.4h, v27.4s + sqxtn2 v26.8h, v24.4s + + str q26, [x0] + str d27, [x15] + add x0, x0, x10 + add x15, x15, x10 + subs w3, w3, #1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_h16_8_neon_i8mm, export=1 + QPEL_H_HEADER + mov x10, #MAX_PB_SIZE * 2 +1: + ld1 {v16.16b, v17.16b}, [x1], x2 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + + QPEL_H_CALC v16, v1, v2, v3, v20, v21, v22, v23 + QPEL_H_CALC v4, v5, v6, v7, v24, v25, v26, v27 + + addp v20.4s, v20.4s, v22.4s + addp v21.4s, v21.4s, v23.4s + addp v24.4s, v24.4s, v26.4s + addp v25.4s, v25.4s, v27.4s + + trn1 v22.4s, v20.4s, v21.4s + trn2 v23.4s, v20.4s, v21.4s + trn1 v26.4s, v24.4s, v25.4s + trn2 v27.4s, v24.4s, v25.4s + + sqxtn v18.4h, v22.4s + sqxtn2 v18.8h, v26.4s + sqxtn v19.4h, v23.4s + sqxtn2 v19.8h, v27.4s + + stp q18, q19, [x0] + add x0, x0, x10 + subs w3, w3, #1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_h24_8_neon_i8mm, export=1 + QPEL_H_HEADER + mov x10, #MAX_PB_SIZE * 2 + add x15, x0, #32 +1: + ld1 {v16.16b, v17.16b}, [x1], x2 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + QPEL_H_CALC v16, v1, v2, v3, v20, v21, v22, v23 + QPEL_H_CALC v4, v5, v6, v7, v24, v25, v26, v27 + addp v20.4s, v20.4s, v22.4s + addp v21.4s, v21.4s, v23.4s + addp v24.4s, v24.4s, v26.4s + addp v25.4s, v25.4s, v27.4s + trn1 v22.4s, v20.4s, v21.4s + trn2 v23.4s, v20.4s, v21.4s + trn1 v26.4s, v24.4s, v25.4s + trn2 v27.4s, v24.4s, v25.4s + sqxtn v18.4h, v22.4s + sqxtn2 v18.8h, v26.4s + sqxtn v19.4h, v23.4s + sqxtn2 v19.8h, v27.4s + stp q18, q19, [x0] + add x0, x0, x10 + ext v1.16b, v17.16b, v17.16b, #1 + ext v2.16b, v17.16b, v17.16b, #2 + ext v3.16b, v17.16b, v17.16b, #3 + ext v4.16b, v17.16b, v17.16b, #4 + ext v5.16b, v17.16b, v17.16b, #5 + ext v6.16b, v17.16b, v17.16b, #6 + ext v7.16b, v17.16b, v17.16b, #7 + zip1 v0.2d, v17.2d, v1.2d + zip1 v2.2d, v2.2d, v3.2d + zip1 v4.2d, v4.2d, v5.2d + zip1 v6.2d, v6.2d, v7.2d + QPEL_H_CALC v0, v2, v4, v6, v20, v21, v22, v23 + addp v20.4s, v20.4s, v21.4s + addp v22.4s, v22.4s, v23.4s + sqxtn v20.4h, v20.4s + sqxtn2 v20.8h, v22.4s + str q20, [x15] + add x15, x15, x10 + subs w3, w3, #1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_h32_8_neon_i8mm, export=1 + QPEL_H_HEADER + mov x10, #MAX_PB_SIZE * 2 + add x15, x0, #32 +1: + ld1 {v16.16b, v17.16b, v18.16b}, [x1], x2 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + QPEL_H_CALC v16, v1, v2, v3, v20, v21, v22, v23 + QPEL_H_CALC v4, v5, v6, v7, v24, v25, v26, v27 + addp v20.4s, v20.4s, v22.4s + addp v21.4s, v21.4s, v23.4s + addp v24.4s, v24.4s, v26.4s + addp v25.4s, v25.4s, v27.4s + trn1 v22.4s, v20.4s, v21.4s + trn2 v23.4s, v20.4s, v21.4s + trn1 v26.4s, v24.4s, v25.4s + trn2 v27.4s, v24.4s, v25.4s + sqxtn v20.4h, v22.4s + sqxtn2 v20.8h, v26.4s + sqxtn v21.4h, v23.4s + sqxtn2 v21.8h, v27.4s + stp q20, q21, [x0] + add x0, x0, x10 + ext v1.16b, v17.16b, v18.16b, #1 + ext v2.16b, v17.16b, v18.16b, #2 + ext v3.16b, v17.16b, v18.16b, #3 + ext v4.16b, v17.16b, v18.16b, #4 + ext v5.16b, v17.16b, v18.16b, #5 + ext v6.16b, v17.16b, v18.16b, #6 + ext v7.16b, v17.16b, v18.16b, #7 + QPEL_H_CALC v17, v1, v2, v3, v20, v21, v22, v23 + QPEL_H_CALC v4, v5, v6, v7, v24, v25, v26, v27 + addp v20.4s, v20.4s, v22.4s + addp v21.4s, v21.4s, v23.4s + addp v24.4s, v24.4s, v26.4s + addp v25.4s, v25.4s, v27.4s + trn1 v22.4s, v20.4s, v21.4s + trn2 v23.4s, v20.4s, v21.4s + trn1 v26.4s, v24.4s, v25.4s + trn2 v27.4s, v24.4s, v25.4s + sqxtn v20.4h, v22.4s + sqxtn2 v20.8h, v26.4s + sqxtn v21.4h, v23.4s + sqxtn2 v21.8h, v27.4s + stp q20, q21, [x15] + add x15, x15, x10 + subs w3, w3, #1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_h48_8_neon_i8mm, export=1 + QPEL_H_HEADER + mov x10, #MAX_PB_SIZE * 2 - 64 +1: + ld1 {v16.16b, v17.16b, v18.16b, v19.16b}, [x1], x2 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + QPEL_H_CALC v16, v1, v2, v3, v20, v21, v22, v23 + QPEL_H_CALC v4, v5, v6, v7, v24, v25, v26, v27 + addp v20.4s, v20.4s, v22.4s + addp v21.4s, v21.4s, v23.4s + addp v24.4s, v24.4s, v26.4s + addp v25.4s, v25.4s, v27.4s + trn1 v22.4s, v20.4s, v21.4s + trn2 v23.4s, v20.4s, v21.4s + trn1 v26.4s, v24.4s, v25.4s + trn2 v27.4s, v24.4s, v25.4s + sqxtn v20.4h, v22.4s + sqxtn2 v20.8h, v26.4s + sqxtn v21.4h, v23.4s + sqxtn2 v21.8h, v27.4s + stp q20, q21, [x0], #32 + + ext v1.16b, v17.16b, v18.16b, #1 + ext v2.16b, v17.16b, v18.16b, #2 + ext v3.16b, v17.16b, v18.16b, #3 + ext v4.16b, v17.16b, v18.16b, #4 + ext v5.16b, v17.16b, v18.16b, #5 + ext v6.16b, v17.16b, v18.16b, #6 + ext v7.16b, v17.16b, v18.16b, #7 + QPEL_H_CALC v17, v1, v2, v3, v20, v21, v22, v23 + QPEL_H_CALC v4, v5, v6, v7, v24, v25, v26, v27 + addp v20.4s, v20.4s, v22.4s + addp v21.4s, v21.4s, v23.4s + addp v24.4s, v24.4s, v26.4s + addp v25.4s, v25.4s, v27.4s + trn1 v22.4s, v20.4s, v21.4s + trn2 v23.4s, v20.4s, v21.4s + trn1 v26.4s, v24.4s, v25.4s + trn2 v27.4s, v24.4s, v25.4s + sqxtn v20.4h, v22.4s + sqxtn2 v20.8h, v26.4s + sqxtn v21.4h, v23.4s + sqxtn2 v21.8h, v27.4s + stp q20, q21, [x0], #32 + ext v1.16b, v18.16b, v19.16b, #1 + ext v2.16b, v18.16b, v19.16b, #2 + ext v3.16b, v18.16b, v19.16b, #3 + ext v4.16b, v18.16b, v19.16b, #4 + ext v5.16b, v18.16b, v19.16b, #5 + ext v6.16b, v18.16b, v19.16b, #6 + ext v7.16b, v18.16b, v19.16b, #7 + QPEL_H_CALC v18, v1, v2, v3, v20, v21, v22, v23 + QPEL_H_CALC v4, v5, v6, v7, v24, v25, v26, v27 + addp v20.4s, v20.4s, v22.4s + addp v21.4s, v21.4s, v23.4s + addp v24.4s, v24.4s, v26.4s + addp v25.4s, v25.4s, v27.4s + trn1 v22.4s, v20.4s, v21.4s + trn2 v23.4s, v20.4s, v21.4s + trn1 v26.4s, v24.4s, v25.4s + trn2 v27.4s, v24.4s, v25.4s + sqxtn v20.4h, v22.4s + sqxtn2 v20.8h, v26.4s + sqxtn v21.4h, v23.4s + sqxtn2 v21.8h, v27.4s + stp q20, q21, [x0] + add x0, x0, x10 + subs w3, w3, #1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_h64_8_neon_i8mm, export=1 + QPEL_H_HEADER + sub x2, x2, #64 +1: + ld1 {v16.16b, v17.16b, v18.16b, v19.16b}, [x1], #64 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + QPEL_H_CALC v16, v1, v2, v3, v20, v21, v22, v23 + QPEL_H_CALC v4, v5, v6, v7, v24, v25, v26, v27 + addp v20.4s, v20.4s, v22.4s + addp v21.4s, v21.4s, v23.4s + addp v24.4s, v24.4s, v26.4s + addp v25.4s, v25.4s, v27.4s + trn1 v22.4s, v20.4s, v21.4s + trn2 v23.4s, v20.4s, v21.4s + trn1 v26.4s, v24.4s, v25.4s + trn2 v27.4s, v24.4s, v25.4s + sqxtn v20.4h, v22.4s + sqxtn2 v20.8h, v26.4s + sqxtn v21.4h, v23.4s + sqxtn2 v21.8h, v27.4s + stp q20, q21, [x0], #32 + + ext v1.16b, v17.16b, v18.16b, #1 + ext v2.16b, v17.16b, v18.16b, #2 + ext v3.16b, v17.16b, v18.16b, #3 + ext v4.16b, v17.16b, v18.16b, #4 + ext v5.16b, v17.16b, v18.16b, #5 + ext v6.16b, v17.16b, v18.16b, #6 + ext v7.16b, v17.16b, v18.16b, #7 + QPEL_H_CALC v17, v1, v2, v3, v20, v21, v22, v23 + QPEL_H_CALC v4, v5, v6, v7, v24, v25, v26, v27 + addp v20.4s, v20.4s, v22.4s + addp v21.4s, v21.4s, v23.4s + addp v24.4s, v24.4s, v26.4s + addp v25.4s, v25.4s, v27.4s + trn1 v22.4s, v20.4s, v21.4s + trn2 v23.4s, v20.4s, v21.4s + trn1 v26.4s, v24.4s, v25.4s + trn2 v27.4s, v24.4s, v25.4s + sqxtn v20.4h, v22.4s + sqxtn2 v20.8h, v26.4s + sqxtn v21.4h, v23.4s + sqxtn2 v21.8h, v27.4s + stp q20, q21, [x0], #32 + ext v1.16b, v18.16b, v19.16b, #1 + ext v2.16b, v18.16b, v19.16b, #2 + ext v3.16b, v18.16b, v19.16b, #3 + ext v4.16b, v18.16b, v19.16b, #4 + ext v5.16b, v18.16b, v19.16b, #5 + ext v6.16b, v18.16b, v19.16b, #6 + ext v7.16b, v18.16b, v19.16b, #7 + QPEL_H_CALC v18, v1, v2, v3, v20, v21, v22, v23 + QPEL_H_CALC v4, v5, v6, v7, v24, v25, v26, v27 + addp v20.4s, v20.4s, v22.4s + addp v21.4s, v21.4s, v23.4s + addp v24.4s, v24.4s, v26.4s + addp v25.4s, v25.4s, v27.4s + trn1 v22.4s, v20.4s, v21.4s + trn2 v23.4s, v20.4s, v21.4s + trn1 v26.4s, v24.4s, v25.4s + trn2 v27.4s, v24.4s, v25.4s + sqxtn v20.4h, v22.4s + sqxtn2 v20.8h, v26.4s + sqxtn v21.4h, v23.4s + sqxtn2 v21.8h, v27.4s + stp q20, q21, [x0], #32 + ld1 {v28.8b}, [x1], x2 + ext v1.16b, v19.16b, v28.16b, #1 + ext v2.16b, v19.16b, v28.16b, #2 + ext v3.16b, v19.16b, v28.16b, #3 + ext v4.16b, v19.16b, v28.16b, #4 + ext v5.16b, v19.16b, v28.16b, #5 + ext v6.16b, v19.16b, v28.16b, #6 + ext v7.16b, v19.16b, v28.16b, #7 + QPEL_H_CALC v19, v1, v2, v3, v20, v21, v22, v23 + QPEL_H_CALC v4, v5, v6, v7, v24, v25, v26, v27 + addp v20.4s, v20.4s, v22.4s + addp v21.4s, v21.4s, v23.4s + addp v24.4s, v24.4s, v26.4s + addp v25.4s, v25.4s, v27.4s + trn1 v22.4s, v20.4s, v21.4s + trn2 v23.4s, v20.4s, v21.4s + trn1 v26.4s, v24.4s, v25.4s + trn2 v27.4s, v24.4s, v25.4s + sqxtn v20.4h, v22.4s + sqxtn2 v20.8h, v26.4s + sqxtn v21.4h, v23.4s + sqxtn2 v21.8h, v27.4s + stp q20, q21, [x0], #32 + subs w3, w3, #1 + b.ne 1b + ret +endfunc + +.macro QPEL_UNI_W_HV_HEADER width + ldp x14, x15, [sp] // mx, my + ldr w13, [sp, #16] // width + stp x19, x30, [sp, #-80]! + stp x20, x21, [sp, #16] + stp x22, x23, [sp, #32] + stp x24, x25, [sp, #48] + stp x26, x27, [sp, #64] + mov x19, sp + mov x11, #9088 + sub sp, sp, x11 + mov x20, x0 + mov x21, x1 + mov x0, sp + sub x1, x2, x3, lsl #1 + sub x1, x1, x3 + mov x2, x3 + add w3, w4, #7 + mov w22, w4 // height + mov x4, x14 // mx + mov x23, x15 // my + mov w24, w6 // wx + mov w25, w7 // ox + mov w26, #-6 + sub w26, w26, w5 // -shift + mov w27, w13 // width + bl X(ff_hevc_put_hevc_qpel_h\width\()_8_neon_i8mm) + movrel x9, qpel_filters + add x9, x9, x23, lsl #3 + ld1 {v0.8b}, [x9] + sxtl v0.8h, v0.8b + mov x10, #(MAX_PB_SIZE * 2) + dup v28.4s, w24 + dup v29.4s, w25 + dup v30.4s, w26 +.endm + +.macro QPEL_UNI_W_HV_END + mov sp, x19 + ldp x20, x21, [sp, #16] + ldp x22, x23, [sp, #32] + ldp x24, x25, [sp, #48] + ldp x26, x27, [sp, #64] + ldp x19, x30, [sp], #80 +.endm + +.macro QPEL_UNI_W_HV_4 + sshr v26.4s, v26.4s, #6 + mul v24.4s, v26.4s, v28.4s + sqrshl v24.4s, v24.4s, v30.4s + sqadd v24.4s, v24.4s, v29.4s + sqxtn v24.4h, v24.4s + sqxtun v24.8b, v24.8h + st1 {v24.s}[0], [x20], x21 +.endm + +.macro QPEL_FILTER_H dst, src0, src1, src2, src3, src4, src5, src6, src7 + smull \dst\().4s, \src0\().4h, v0.h[0] + smlal \dst\().4s, \src1\().4h, v0.h[1] + smlal \dst\().4s, \src2\().4h, v0.h[2] + smlal \dst\().4s, \src3\().4h, v0.h[3] + smlal \dst\().4s, \src4\().4h, v0.h[4] + smlal \dst\().4s, \src5\().4h, v0.h[5] + smlal \dst\().4s, \src6\().4h, v0.h[6] + smlal \dst\().4s, \src7\().4h, v0.h[7] +.endm + +.macro QPEL_FILTER_H2 dst, src0, src1, src2, src3, src4, src5, src6, src7 + smull2 \dst\().4s, \src0\().8h, v0.h[0] + smlal2 \dst\().4s, \src1\().8h, v0.h[1] + smlal2 \dst\().4s, \src2\().8h, v0.h[2] + smlal2 \dst\().4s, \src3\().8h, v0.h[3] + smlal2 \dst\().4s, \src4\().8h, v0.h[4] + smlal2 \dst\().4s, \src5\().8h, v0.h[5] + smlal2 \dst\().4s, \src6\().8h, v0.h[6] + smlal2 \dst\().4s, \src7\().8h, v0.h[7] +.endm + +function ff_hevc_put_hevc_qpel_uni_w_hv4_8_neon_i8mm, export=1 + QPEL_UNI_W_HV_HEADER 4 + ldr d16, [sp] + ldr d17, [sp, x10] + add sp, sp, x10, lsl #1 + ldr d18, [sp] + ldr d19, [sp, x10] + add sp, sp, x10, lsl #1 + ldr d20, [sp] + ldr d21, [sp, x10] + add sp, sp, x10, lsl #1 + ldr d22, [sp] + add sp, sp, x10 +1: + ldr d23, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_UNI_W_HV_4 + subs w22, w22, #1 + b.eq 2f + + ldr d16, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_UNI_W_HV_4 + subs w22, w22, #1 + b.eq 2f + + ldr d17, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_UNI_W_HV_4 + subs w22, w22, #1 + b.eq 2f + + ldr d18, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_UNI_W_HV_4 + subs w22, w22, #1 + b.eq 2f + + ldr d19, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_UNI_W_HV_4 + subs w22, w22, #1 + b.eq 2f + + ldr d20, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_UNI_W_HV_4 + subs w22, w22, #1 + b.eq 2f + + ldr d21, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_UNI_W_HV_4 + subs w22, w22, #1 + b.eq 2f + + ldr d22, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_UNI_W_HV_4 + subs w22, w22, #1 + b.hi 1b + +2: + QPEL_UNI_W_HV_END + ret +endfunc + +.macro QPEL_UNI_W_HV_8 + sshr v26.4s, v26.4s, #6 + sshr v27.4s, v27.4s, #6 + mul v24.4s, v26.4s, v28.4s + mul v25.4s, v27.4s, v28.4s + sqrshl v24.4s, v24.4s, v30.4s + sqrshl v25.4s, v25.4s, v30.4s + sqadd v24.4s, v24.4s, v29.4s + sqadd v25.4s, v25.4s, v29.4s + sqxtn v24.4h, v24.4s + sqxtn2 v24.8h, v25.4s + sqxtun v24.8b, v24.8h + st1 {v24.d}[0], [x20], x21 +.endm + +function ff_hevc_put_hevc_qpel_uni_w_hv8_8_neon_i8mm, export=1 + QPEL_UNI_W_HV_HEADER 8 + ldr q16, [sp] + ldr q17, [sp, x10] + add sp, sp, x10, lsl #1 + ldr q18, [sp] + ldr q19, [sp, x10] + add sp, sp, x10, lsl #1 + ldr q20, [sp] + ldr q21, [sp, x10] + add sp, sp, x10, lsl #1 + ldr q22, [sp] + add sp, sp, x10 +1: + ldr q23, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_FILTER_H2 v27, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_UNI_W_HV_8 + subs w22, w22, #1 + b.eq 2f + + ldr q16, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_FILTER_H2 v27, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_UNI_W_HV_8 + subs w22, w22, #1 + b.eq 2f + + ldr q17, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_FILTER_H2 v27, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_UNI_W_HV_8 + subs w22, w22, #1 + b.eq 2f + + ldr q18, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_FILTER_H2 v27, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_UNI_W_HV_8 + subs w22, w22, #1 + b.eq 2f + + ldr q19, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_FILTER_H2 v27, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_UNI_W_HV_8 + subs w22, w22, #1 + b.eq 2f + + ldr q20, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_FILTER_H2 v27, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_UNI_W_HV_8 + subs w22, w22, #1 + b.eq 2f + + ldr q21, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_FILTER_H2 v27, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_UNI_W_HV_8 + subs w22, w22, #1 + b.eq 2f + + ldr q22, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_FILTER_H2 v27, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_UNI_W_HV_8 + subs w22, w22, #1 + b.hi 1b + +2: + QPEL_UNI_W_HV_END + ret +endfunc + +.macro QPEL_UNI_W_HV_16 + sshr v24.4s, v24.4s, #6 + sshr v25.4s, v25.4s, #6 + sshr v26.4s, v26.4s, #6 + sshr v27.4s, v27.4s, #6 + mul v24.4s, v24.4s, v28.4s + mul v25.4s, v25.4s, v28.4s + mul v26.4s, v26.4s, v28.4s + mul v27.4s, v27.4s, v28.4s + sqrshl v24.4s, v24.4s, v30.4s + sqrshl v25.4s, v25.4s, v30.4s + sqrshl v26.4s, v26.4s, v30.4s + sqrshl v27.4s, v27.4s, v30.4s + sqadd v24.4s, v24.4s, v29.4s + sqadd v25.4s, v25.4s, v29.4s + sqadd v26.4s, v26.4s, v29.4s + sqadd v27.4s, v27.4s, v29.4s + sqxtn v24.4h, v24.4s + sqxtn2 v24.8h, v25.4s + sqxtn v26.4h, v26.4s + sqxtn2 v26.8h, v27.4s + sqxtun v24.8b, v24.8h + sqxtun2 v24.16b, v26.8h + + st1 {v24.16b}, [x20], x21 +.endm + +function ff_hevc_put_hevc_qpel_uni_w_hv16_8_neon_i8mm, export=1 + QPEL_UNI_W_HV_HEADER 16 + ldp q16, q1, [sp] + add sp, sp, x10 + ldp q17, q2, [sp] + add sp, sp, x10 + ldp q18, q3, [sp] + add sp, sp, x10 + ldp q19, q4, [sp] + add sp, sp, x10 + ldp q20, q5, [sp] + add sp, sp, x10 + ldp q21, q6, [sp] + add sp, sp, x10 + ldp q22, q7, [sp] + add sp, sp, x10 +1: + ldp q23, q31, [sp] + add sp, sp, x10 + QPEL_FILTER_H v24, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_FILTER_H2 v25, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_FILTER_H v26, v1, v2, v3, v4, v5, v6, v7, v31 + QPEL_FILTER_H2 v27, v1, v2, v3, v4, v5, v6, v7, v31 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q16, q1, [sp] + add sp, sp, x10 + QPEL_FILTER_H v24, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_FILTER_H2 v25, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_FILTER_H v26, v2, v3, v4, v5, v6, v7, v31, v1 + QPEL_FILTER_H2 v27, v2, v3, v4, v5, v6, v7, v31, v1 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q17, q2, [sp] + add sp, sp, x10 + QPEL_FILTER_H v24, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_FILTER_H2 v25, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_FILTER_H v26, v3, v4, v5, v6, v7, v31, v1, v2 + QPEL_FILTER_H2 v27, v3, v4, v5, v6, v7, v31, v1, v2 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q18, q3, [sp] + add sp, sp, x10 + QPEL_FILTER_H v24, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_FILTER_H2 v25, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_FILTER_H v26, v4, v5, v6, v7, v31, v1, v2, v3 + QPEL_FILTER_H2 v27, v4, v5, v6, v7, v31, v1, v2, v3 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q19, q4, [sp] + add sp, sp, x10 + QPEL_FILTER_H v24, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_FILTER_H2 v25, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_FILTER_H v26, v5, v6, v7, v31, v1, v2, v3, v4 + QPEL_FILTER_H2 v27, v5, v6, v7, v31, v1, v2, v3, v4 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q20, q5, [sp] + add sp, sp, x10 + QPEL_FILTER_H v24, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_FILTER_H2 v25, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_FILTER_H v26, v6, v7, v31, v1, v2, v3, v4, v5 + QPEL_FILTER_H2 v27, v6, v7, v31, v1, v2, v3, v4, v5 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q21, q6, [sp] + add sp, sp, x10 + QPEL_FILTER_H v24, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_FILTER_H2 v25, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_FILTER_H v26, v7, v31, v1, v2, v3, v4, v5, v6 + QPEL_FILTER_H2 v27, v7, v31, v1, v2, v3, v4, v5, v6 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q22, q7, [sp] + add sp, sp, x10 + QPEL_FILTER_H v24, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_FILTER_H2 v25, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_FILTER_H v26, v31, v1, v2, v3, v4, v5, v6, v7 + QPEL_FILTER_H2 v27, v31, v1, v2, v3, v4, v5, v6, v7 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.hi 1b + +2: + QPEL_UNI_W_HV_END + ret +endfunc + + +function ff_hevc_put_hevc_qpel_uni_w_hv32_8_neon_i8mm, export=1 + QPEL_UNI_W_HV_HEADER 32 + mov x11, sp + mov w12, w22 + mov x13, x20 +3: + ldp q16, q1, [sp] + add sp, sp, x10 + ldp q17, q2, [sp] + add sp, sp, x10 + ldp q18, q3, [sp] + add sp, sp, x10 + ldp q19, q4, [sp] + add sp, sp, x10 + ldp q20, q5, [sp] + add sp, sp, x10 + ldp q21, q6, [sp] + add sp, sp, x10 + ldp q22, q7, [sp] + add sp, sp, x10 +1: + ldp q23, q31, [sp] + add sp, sp, x10 + QPEL_FILTER_H v24, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_FILTER_H2 v25, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_FILTER_H v26, v1, v2, v3, v4, v5, v6, v7, v31 + QPEL_FILTER_H2 v27, v1, v2, v3, v4, v5, v6, v7, v31 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q16, q1, [sp] + add sp, sp, x10 + QPEL_FILTER_H v24, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_FILTER_H2 v25, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_FILTER_H v26, v2, v3, v4, v5, v6, v7, v31, v1 + QPEL_FILTER_H2 v27, v2, v3, v4, v5, v6, v7, v31, v1 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q17, q2, [sp] + add sp, sp, x10 + QPEL_FILTER_H v24, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_FILTER_H2 v25, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_FILTER_H v26, v3, v4, v5, v6, v7, v31, v1, v2 + QPEL_FILTER_H2 v27, v3, v4, v5, v6, v7, v31, v1, v2 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q18, q3, [sp] + add sp, sp, x10 + QPEL_FILTER_H v24, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_FILTER_H2 v25, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_FILTER_H v26, v4, v5, v6, v7, v31, v1, v2, v3 + QPEL_FILTER_H2 v27, v4, v5, v6, v7, v31, v1, v2, v3 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q19, q4, [sp] + add sp, sp, x10 + QPEL_FILTER_H v24, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_FILTER_H2 v25, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_FILTER_H v26, v5, v6, v7, v31, v1, v2, v3, v4 + QPEL_FILTER_H2 v27, v5, v6, v7, v31, v1, v2, v3, v4 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q20, q5, [sp] + add sp, sp, x10 + QPEL_FILTER_H v24, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_FILTER_H2 v25, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_FILTER_H v26, v6, v7, v31, v1, v2, v3, v4, v5 + QPEL_FILTER_H2 v27, v6, v7, v31, v1, v2, v3, v4, v5 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q21, q6, [sp] + add sp, sp, x10 + QPEL_FILTER_H v24, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_FILTER_H2 v25, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_FILTER_H v26, v7, v31, v1, v2, v3, v4, v5, v6 + QPEL_FILTER_H2 v27, v7, v31, v1, v2, v3, v4, v5, v6 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q22, q7, [sp] + add sp, sp, x10 + QPEL_FILTER_H v24, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_FILTER_H2 v25, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_FILTER_H v26, v31, v1, v2, v3, v4, v5, v6, v7 + QPEL_FILTER_H2 v27, v31, v1, v2, v3, v4, v5, v6, v7 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.hi 1b +2: + subs w27, w27, #16 + add sp, x11, #32 + add x20, x13, #16 + mov w22, w12 + mov x11, sp + mov x13, x20 + b.hi 3b + QPEL_UNI_W_HV_END + ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_hv64_8_neon_i8mm, export=1 + QPEL_UNI_W_HV_HEADER 64 + mov x11, sp + mov w12, w22 + mov x13, x20 +3: + ldp q16, q1, [sp] + add sp, sp, x10 + ldp q17, q2, [sp] + add sp, sp, x10 + ldp q18, q3, [sp] + add sp, sp, x10 + ldp q19, q4, [sp] + add sp, sp, x10 + ldp q20, q5, [sp] + add sp, sp, x10 + ldp q21, q6, [sp] + add sp, sp, x10 + ldp q22, q7, [sp] + add sp, sp, x10 +1: + ldp q23, q31, [sp] + add sp, sp, x10 + QPEL_FILTER_H v24, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_FILTER_H2 v25, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_FILTER_H v26, v1, v2, v3, v4, v5, v6, v7, v31 + QPEL_FILTER_H2 v27, v1, v2, v3, v4, v5, v6, v7, v31 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q16, q1, [sp] + add sp, sp, x10 + QPEL_FILTER_H v24, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_FILTER_H2 v25, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_FILTER_H v26, v2, v3, v4, v5, v6, v7, v31, v1 + QPEL_FILTER_H2 v27, v2, v3, v4, v5, v6, v7, v31, v1 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q17, q2, [sp] + add sp, sp, x10 + QPEL_FILTER_H v24, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_FILTER_H2 v25, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_FILTER_H v26, v3, v4, v5, v6, v7, v31, v1, v2 + QPEL_FILTER_H2 v27, v3, v4, v5, v6, v7, v31, v1, v2 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q18, q3, [sp] + add sp, sp, x10 + QPEL_FILTER_H v24, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_FILTER_H2 v25, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_FILTER_H v26, v4, v5, v6, v7, v31, v1, v2, v3 + QPEL_FILTER_H2 v27, v4, v5, v6, v7, v31, v1, v2, v3 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q19, q4, [sp] + add sp, sp, x10 + QPEL_FILTER_H v24, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_FILTER_H2 v25, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_FILTER_H v26, v5, v6, v7, v31, v1, v2, v3, v4 + QPEL_FILTER_H2 v27, v5, v6, v7, v31, v1, v2, v3, v4 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q20, q5, [sp] + add sp, sp, x10 + QPEL_FILTER_H v24, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_FILTER_H2 v25, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_FILTER_H v26, v6, v7, v31, v1, v2, v3, v4, v5 + QPEL_FILTER_H2 v27, v6, v7, v31, v1, v2, v3, v4, v5 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q21, q6, [sp] + add sp, sp, x10 + QPEL_FILTER_H v24, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_FILTER_H2 v25, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_FILTER_H v26, v7, v31, v1, v2, v3, v4, v5, v6 + QPEL_FILTER_H2 v27, v7, v31, v1, v2, v3, v4, v5, v6 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q22, q7, [sp] + add sp, sp, x10 + QPEL_FILTER_H v24, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_FILTER_H2 v25, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_FILTER_H v26, v31, v1, v2, v3, v4, v5, v6, v7 + QPEL_FILTER_H2 v27, v31, v1, v2, v3, v4, v5, v6, v7 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.hi 1b +2: + subs w27, w27, #16 + add sp, x11, #32 + add x20, x13, #16 + mov w22, w12 + mov x11, sp + mov x13, x20 + b.hi 3b + QPEL_UNI_W_HV_END + ret +endfunc + +#endif // HAVE_I8MM diff --git a/libavcodec/aarch64/neon.S b/libavcodec/aarch64/neon.S index 1ad32c359d4..bc105e48619 100644 --- a/libavcodec/aarch64/neon.S +++ b/libavcodec/aarch64/neon.S @@ -1,6 +1,8 @@ /* * This file is part of FFmpeg. * + * Copyright (c) 2023 J. Dekker + * * 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 @@ -16,6 +18,15 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +.macro clip min, max, regs:vararg +.irp x, \regs + smax \x, \x, \min +.endr +.irp x, \regs + smin \x, \x, \max +.endr +.endm + .macro transpose_8x8B r0, r1, r2, r3, r4, r5, r6, r7, r8, r9 trn1 \r8\().8B, \r0\().8B, \r1\().8B trn2 \r9\().8B, \r0\().8B, \r1\().8B diff --git a/libavcodec/aarch64/synth_filter_init.c b/libavcodec/aarch64/synth_filter_init.c index 801b46e2176..6b6da35b54e 100644 --- a/libavcodec/aarch64/synth_filter_init.c +++ b/libavcodec/aarch64/synth_filter_init.c @@ -28,7 +28,7 @@ #include "asm-offsets.h" -#if HAVE_NEON || HAVE_VFP +#if HAVE_NEON AV_CHECK_OFFSET(FFTContext, imdct_half, IMDCT_HALF); #endif diff --git a/libavcodec/ac3dec.c b/libavcodec/ac3dec.c index 0b120e6140a..8c63f015f44 100644 --- a/libavcodec/ac3dec.c +++ b/libavcodec/ac3dec.c @@ -228,7 +228,7 @@ static av_cold int ac3_decode_init(AVCodecContext *avctx) if ((ret = av_tx_init(&s->tx_256, &s->tx_fn_256, IMDCT_TYPE, 1, 256, &scale, 0))) return ret; - AC3_RENAME(ff_kbd_window_init)(s->window, 5.0, 256); + AC3_RENAME(avpriv_kbd_window_init)(s->window, 5.0, 256); ff_bswapdsp_init(&s->bdsp); #if (USE_FIXED) @@ -1714,6 +1714,7 @@ static int ac3_decode_frame(AVCodecContext *avctx, AVFrame *frame, if (!err) { avctx->sample_rate = s->sample_rate; avctx->bit_rate = s->bit_rate + s->prev_bit_rate; + avctx->profile = s->eac3_extension_type_a == 1 ? FF_PROFILE_EAC3_DDP_ATMOS : FF_PROFILE_UNKNOWN; } if (!avctx->sample_rate) { diff --git a/libavcodec/ac3dec.h b/libavcodec/ac3dec.h index 138b462abb5..98de7b5abff 100644 --- a/libavcodec/ac3dec.h +++ b/libavcodec/ac3dec.h @@ -90,7 +90,6 @@ typedef struct AC3DecodeContext { int lfe_on; ///< lfe channel in use int dialog_normalization[2]; ///< dialog level in dBFS (dialnorm) int compression_exists[2]; ///< compression field is valid for frame (compre) - int compression_gain[2]; ///< gain to apply for heavy compression (compr) int channel_map; ///< custom channel map (chanmap) int preferred_downmix; ///< Preferred 2-channel downmix mode (dmixmod) int center_mix_level; ///< Center mix level index @@ -100,8 +99,8 @@ typedef struct AC3DecodeContext { int lfe_mix_level_exists; ///< indicates if lfemixlevcod is specified (lfemixlevcode) int lfe_mix_level; ///< LFE mix level index (lfemixlevcod) int eac3; ///< indicates if current frame is E-AC-3 - int eac3_frame_dependent_found; ///< bitstream has E-AC-3 dependent frame(s) int eac3_subsbtreamid_found; ///< bitstream has E-AC-3 additional substream(s) + int eac3_extension_type_a; ///< bitstream has E-AC-3 extension type A enabled frame(s) int dolby_surround_mode; ///< dolby surround mode (dsurmod) int dolby_surround_ex_mode; ///< dolby surround ex mode (dsurexmod) int dolby_headphone_mode; ///< dolby headphone mode (dheadphonmod) diff --git a/libavcodec/ac3dec_float.c b/libavcodec/ac3dec_float.c index b8868d8ee15..39d3cbd282b 100644 --- a/libavcodec/ac3dec_float.c +++ b/libavcodec/ac3dec_float.c @@ -33,6 +33,7 @@ #include "ac3dec.h" #include "codec_internal.h" +#include "profiles.h" #include "eac3dec.c" #include "ac3dec.c" @@ -92,6 +93,7 @@ const FFCodec ff_eac3_decoder = { .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, .p.priv_class = &ac3_eac3_decoder_class, + .p.profiles = NULL_IF_CONFIG_SMALL(ff_eac3_profiles), .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, }; #endif diff --git a/libavcodec/ac3enc_fixed.c b/libavcodec/ac3enc_fixed.c index 5a5f1cc2e95..079a43dc39a 100644 --- a/libavcodec/ac3enc_fixed.c +++ b/libavcodec/ac3enc_fixed.c @@ -82,7 +82,7 @@ static av_cold int ac3_fixed_mdct_init(AC3EncodeContext *s) if (!iwin) return AVERROR(ENOMEM); - ff_kbd_window_init(fwin, 5.0, AC3_BLOCK_SIZE); + avpriv_kbd_window_init(fwin, 5.0, AC3_BLOCK_SIZE); for (int i = 0; i < AC3_BLOCK_SIZE; i++) iwin[i] = lrintf(fwin[i] * (1 << 22)); @@ -112,7 +112,7 @@ const FFCodec ff_ac3_fixed_encoder = { CODEC_LONG_NAME("ATSC A/52A (AC-3)"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_AC3, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(AC3EncodeContext), .init = ac3_fixed_encode_init, FF_CODEC_ENCODE_CB(ff_ac3_fixed_encode_frame), diff --git a/libavcodec/ac3enc_float.c b/libavcodec/ac3enc_float.c index 4c4d18ce567..9664adbf638 100644 --- a/libavcodec/ac3enc_float.c +++ b/libavcodec/ac3enc_float.c @@ -92,7 +92,7 @@ static av_cold int ac3_float_mdct_init(AC3EncodeContext *s) return AVERROR(ENOMEM); } - ff_kbd_window_init(window, 5.0, AC3_BLOCK_SIZE); + avpriv_kbd_window_init(window, 5.0, AC3_BLOCK_SIZE); s->mdct_window = window; return av_tx_init(&s->tx, &s->tx_fn, AV_TX_FLOAT_MDCT, 0, @@ -116,7 +116,7 @@ const FFCodec ff_ac3_encoder = { CODEC_LONG_NAME("ATSC A/52A (AC-3)"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_AC3, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(AC3EncodeContext), .init = ff_ac3_float_encode_init, FF_CODEC_ENCODE_CB(ff_ac3_float_encode_frame), diff --git a/libavcodec/adpcm.c b/libavcodec/adpcm.c index 841538b1389..59b9ef34971 100644 --- a/libavcodec/adpcm.c +++ b/libavcodec/adpcm.c @@ -324,6 +324,7 @@ static av_cold int adpcm_decode_init(AVCodecContext * avctx) case AV_CODEC_ID_ADPCM_IMA_WAV: case AV_CODEC_ID_ADPCM_4XM: case AV_CODEC_ID_ADPCM_XA: + case AV_CODEC_ID_ADPCM_XMD: case AV_CODEC_ID_ADPCM_EA_R1: case AV_CODEC_ID_ADPCM_EA_R2: case AV_CODEC_ID_ADPCM_EA_R3: @@ -1043,6 +1044,9 @@ static int get_nb_samples(AVCodecContext *avctx, GetByteContext *gb, case AV_CODEC_ID_ADPCM_XA: nb_samples = (buf_size / 128) * 224 / ch; break; + case AV_CODEC_ID_ADPCM_XMD: + nb_samples = buf_size / (21 * ch) * 32; + break; case AV_CODEC_ID_ADPCM_DTK: case AV_CODEC_ID_ADPCM_PSX: nb_samples = buf_size / (16 * ch) * 28; @@ -1553,6 +1557,45 @@ static int adpcm_decode_frame(AVCodecContext *avctx, AVFrame *frame, } bytestream2_seek(&gb, 0, SEEK_END); ) /* End of CASE */ + CASE(ADPCM_XMD, + int bytes_remaining, block = 0; + while (bytestream2_get_bytes_left(&gb) >= 21 * channels) { + for (int channel = 0; channel < channels; channel++) { + int16_t *out = samples_p[channel] + block * 32; + int16_t history[2]; + uint16_t scale; + + history[1] = sign_extend(bytestream2_get_le16(&gb), 16); + history[0] = sign_extend(bytestream2_get_le16(&gb), 16); + scale = bytestream2_get_le16(&gb); + + out[0] = history[1]; + out[1] = history[0]; + + for (int n = 0; n < 15; n++) { + unsigned byte = bytestream2_get_byte(&gb); + int32_t nibble[2]; + + nibble[0] = sign_extend(byte & 15, 4); + nibble[1] = sign_extend(byte >> 4, 4); + + out[2+n*2] = nibble[0]*scale + ((history[0]*3667 - history[1]*1642) >> 11); + history[1] = history[0]; + history[0] = out[2+n*2]; + + out[2+n*2+1] = nibble[1]*scale + ((history[0]*3667 - history[1]*1642) >> 11); + history[1] = history[0]; + history[0] = out[2+n*2+1]; + } + } + + block++; + } + bytes_remaining = bytestream2_get_bytes_left(&gb); + if (bytes_remaining > 0) { + bytestream2_skip(&gb, bytes_remaining); + } + ) /* End of CASE */ CASE(ADPCM_XA, int16_t *out0 = samples_p[0]; int16_t *out1 = samples_p[1]; @@ -2350,5 +2393,6 @@ ADPCM_DECODER(ADPCM_SWF, sample_fmts_s16, adpcm_swf, "ADPCM Sho ADPCM_DECODER(ADPCM_THP_LE, sample_fmts_s16p, adpcm_thp_le, "ADPCM Nintendo THP (little-endian)") ADPCM_DECODER(ADPCM_THP, sample_fmts_s16p, adpcm_thp, "ADPCM Nintendo THP") ADPCM_DECODER(ADPCM_XA, sample_fmts_s16p, adpcm_xa, "ADPCM CDROM XA") +ADPCM_DECODER(ADPCM_XMD, sample_fmts_s16p, adpcm_xmd, "ADPCM Konami XMD") ADPCM_DECODER(ADPCM_YAMAHA, sample_fmts_s16, adpcm_yamaha, "ADPCM Yamaha") ADPCM_DECODER(ADPCM_ZORK, sample_fmts_s16, adpcm_zork, "ADPCM Zork") diff --git a/libavcodec/adpcmenc.c b/libavcodec/adpcmenc.c index 57709b19a10..63afffc58f7 100644 --- a/libavcodec/adpcmenc.c +++ b/libavcodec/adpcmenc.c @@ -1003,7 +1003,8 @@ const FFCodec ff_ ## name_ ## _encoder = { \ .p.id = id_, \ .p.sample_fmts = sample_fmts_, \ .p.ch_layouts = ch_layouts, \ - .p.capabilities = capabilities_ | AV_CODEC_CAP_DR1, \ + .p.capabilities = capabilities_ | AV_CODEC_CAP_DR1 | \ + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, \ .p.priv_class = &adpcm_encoder_class, \ .priv_data_size = sizeof(ADPCMEncodeContext), \ .init = adpcm_encode_init, \ diff --git a/libavcodec/adxenc.c b/libavcodec/adxenc.c index 153c91b852f..796efdab63b 100644 --- a/libavcodec/adxenc.c +++ b/libavcodec/adxenc.c @@ -183,8 +183,6 @@ static int adx_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, dst += BLOCK_SIZE; } - avpkt->pts = frame->pts; - avpkt->duration = frame->nb_samples; *got_packet_ptr = 1; return 0; } @@ -194,10 +192,12 @@ const FFCodec ff_adpcm_adx_encoder = { CODEC_LONG_NAME("SEGA CRI ADX ADPCM"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_ADPCM_ADX, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(ADXContext), .init = adx_encode_init, FF_CODEC_ENCODE_CB(adx_encode_frame), .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE }, + .caps_internal = FF_CODEC_CAP_EOF_FLUSH, }; diff --git a/libavcodec/agm.c b/libavcodec/agm.c index b37f1a42c96..55cf0b47c83 100644 --- a/libavcodec/agm.c +++ b/libavcodec/agm.c @@ -1100,7 +1100,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, return AVERROR_INVALIDDATA; s->key_frame = (avpkt->flags & AV_PKT_FLAG_KEY); - frame->key_frame = s->key_frame; + if (s->key_frame) + frame->flags |= AV_FRAME_FLAG_KEY; + else + frame->flags &= ~AV_FRAME_FLAG_KEY; frame->pict_type = s->key_frame ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; if (!s->key_frame) { @@ -1171,7 +1174,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if ((ret = ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF)) < 0) return ret; - if (frame->key_frame) { + if (frame->flags & AV_FRAME_FLAG_KEY) { if (!s->dct && !s->rgb) ret = decode_raw_intra(avctx, gbyte, frame); else if (!s->dct && s->rgb) diff --git a/libavcodec/aic.c b/libavcodec/aic.c index 7ba1c02fdd4..f8b0f60354c 100644 --- a/libavcodec/aic.c +++ b/libavcodec/aic.c @@ -393,7 +393,7 @@ static int aic_decode_frame(AVCodecContext *avctx, AVFrame *frame, ctx->frame = frame; ctx->frame->pict_type = AV_PICTURE_TYPE_I; - ctx->frame->key_frame = 1; + ctx->frame->flags |= AV_FRAME_FLAG_KEY; off = FFALIGN(AIC_HDR_SIZE + ctx->num_x_slices * ctx->mb_height * 2, 4); diff --git a/libavcodec/alacenc.c b/libavcodec/alacenc.c index 0f685d71d62..9598e5861e5 100644 --- a/libavcodec/alacenc.c +++ b/libavcodec/alacenc.c @@ -653,7 +653,8 @@ const FFCodec ff_alac_encoder = { CODEC_LONG_NAME("ALAC (Apple Lossless Audio Codec)"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_ALAC, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SMALL_LAST_FRAME, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SMALL_LAST_FRAME | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(AlacEncodeContext), .p.priv_class = &alacenc_class, .init = alac_encode_init, diff --git a/libavcodec/aliaspixdec.c b/libavcodec/aliaspixdec.c index 45155d79cde..72f810d408e 100644 --- a/libavcodec/aliaspixdec.c +++ b/libavcodec/aliaspixdec.c @@ -70,7 +70,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *f, return ret; f->pict_type = AV_PICTURE_TYPE_I; - f->key_frame = 1; + f->flags |= AV_FRAME_FLAG_KEY; x = 0; y = 1; diff --git a/libavcodec/aliaspixenc.c b/libavcodec/aliaspixenc.c index 6593d3f0473..90d2a633193 100644 --- a/libavcodec/aliaspixenc.c +++ b/libavcodec/aliaspixenc.c @@ -106,7 +106,7 @@ const FFCodec ff_alias_pix_encoder = { CODEC_LONG_NAME("Alias/Wavefront PIX image"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_ALIAS_PIX, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, FF_CODEC_ENCODE_CB(encode_frame), .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_BGR24, AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index b009848a44b..f5ac324cd9c 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -228,6 +228,7 @@ extern const FFCodec ff_msmpeg4v3_encoder; extern const FFCodec ff_msmpeg4v3_decoder; extern const FFCodec ff_msmpeg4_crystalhd_decoder; extern const FFCodec ff_msp2_decoder; +extern const FFCodec ff_msrle_encoder; extern const FFCodec ff_msrle_decoder; extern const FFCodec ff_mss1_decoder; extern const FFCodec ff_mss2_decoder; @@ -251,6 +252,7 @@ extern const FFCodec ff_pbm_encoder; extern const FFCodec ff_pbm_decoder; extern const FFCodec ff_pcx_encoder; extern const FFCodec ff_pcx_decoder; +extern const FFCodec ff_pdv_decoder; extern const FFCodec ff_pfm_encoder; extern const FFCodec ff_pfm_decoder; extern const FFCodec ff_pgm_encoder; @@ -287,12 +289,14 @@ extern const FFCodec ff_r210_decoder; extern const FFCodec ff_rasc_decoder; extern const FFCodec ff_rawvideo_encoder; extern const FFCodec ff_rawvideo_decoder; +extern const FFCodec ff_rka_decoder; extern const FFCodec ff_rl2_decoder; extern const FFCodec ff_roq_encoder; extern const FFCodec ff_roq_decoder; extern const FFCodec ff_rpza_encoder; extern const FFCodec ff_rpza_decoder; extern const FFCodec ff_rscc_decoder; +extern const FFCodec ff_rtv1_decoder; extern const FFCodec ff_rv10_encoder; extern const FFCodec ff_rv10_decoder; extern const FFCodec ff_rv20_encoder; @@ -367,6 +371,7 @@ extern const FFCodec ff_vc1_v4l2m2m_decoder; extern const FFCodec ff_vc2_encoder; extern const FFCodec ff_vcr1_decoder; extern const FFCodec ff_vmdvideo_decoder; +extern const FFCodec ff_vmix_decoder; extern const FFCodec ff_vmnc_decoder; extern const FFCodec ff_vp3_decoder; extern const FFCodec ff_vp4_decoder; @@ -383,6 +388,7 @@ extern const FFCodec ff_vp9_rkmpp_decoder; extern const FFCodec ff_vp9_v4l2m2m_decoder; extern const FFCodec ff_vqa_decoder; extern const FFCodec ff_vqc_decoder; +extern const FFCodec ff_vvc_decoder; extern const FFCodec ff_wbmp_decoder; extern const FFCodec ff_wbmp_encoder; extern const FFCodec ff_webp_decoder; @@ -538,6 +544,7 @@ extern const FFCodec ff_twinvq_decoder; extern const FFCodec ff_vmdaudio_decoder; extern const FFCodec ff_vorbis_encoder; extern const FFCodec ff_vorbis_decoder; +extern const FFCodec ff_wavarc_decoder; extern const FFCodec ff_wavpack_encoder; extern const FFCodec ff_wavpack_decoder; extern const FFCodec ff_wmalossless_decoder; @@ -620,6 +627,7 @@ extern const FFCodec ff_pcm_vidc_encoder; extern const FFCodec ff_pcm_vidc_decoder; /* DPCM codecs */ +extern const FFCodec ff_cbd2_dpcm_decoder; extern const FFCodec ff_derf_dpcm_decoder; extern const FFCodec ff_gremlin_dpcm_decoder; extern const FFCodec ff_interplay_dpcm_decoder; @@ -628,6 +636,7 @@ extern const FFCodec ff_roq_dpcm_decoder; extern const FFCodec ff_sdx2_dpcm_decoder; extern const FFCodec ff_sol_dpcm_decoder; extern const FFCodec ff_xan_dpcm_decoder; +extern const FFCodec ff_wady_dpcm_decoder; /* ADPCM codecs */ extern const FFCodec ff_adpcm_4xm_decoder; @@ -693,6 +702,7 @@ extern const FFCodec ff_adpcm_thp_decoder; extern const FFCodec ff_adpcm_thp_le_decoder; extern const FFCodec ff_adpcm_vima_decoder; extern const FFCodec ff_adpcm_xa_decoder; +extern const FFCodec ff_adpcm_xmd_decoder; extern const FFCodec ff_adpcm_yamaha_encoder; extern const FFCodec ff_adpcm_yamaha_decoder; extern const FFCodec ff_adpcm_zork_decoder; @@ -754,6 +764,8 @@ extern const FFCodec ff_pcm_mulaw_at_decoder; extern const FFCodec ff_qdmc_at_decoder; extern const FFCodec ff_qdm2_at_decoder; extern FFCodec ff_libaom_av1_encoder; +/* preferred over libaribb24 */ +extern const FFCodec ff_libaribcaption_decoder; extern const FFCodec ff_libaribb24_decoder; extern const FFCodec ff_libcelt_decoder; extern const FFCodec ff_libcodec2_encoder; @@ -775,7 +787,6 @@ extern const FFCodec ff_libopencore_amrnb_encoder; extern const FFCodec ff_libopencore_amrnb_decoder; extern const FFCodec ff_libopencore_amrwb_decoder; extern const FFCodec ff_libopenjpeg_encoder; -extern const FFCodec ff_libopenjpeg_decoder; extern const FFCodec ff_libopus_encoder; extern const FFCodec ff_libopus_decoder; extern const FFCodec ff_librav1e_encoder; @@ -793,7 +804,7 @@ extern const FFCodec ff_libvorbis_decoder; extern const FFCodec ff_libvpx_vp8_encoder; extern const FFCodec ff_libvpx_vp8_decoder; extern FFCodec ff_libvpx_vp9_encoder; -extern FFCodec ff_libvpx_vp9_decoder; +extern const FFCodec ff_libvpx_vp9_decoder; /* preferred over libwebp */ extern const FFCodec ff_libwebp_anim_encoder; extern const FFCodec ff_libwebp_encoder; @@ -829,9 +840,11 @@ extern const FFCodec ff_libaom_av1_decoder; extern const FFCodec ff_av1_decoder; extern const FFCodec ff_av1_cuvid_decoder; extern const FFCodec ff_av1_mediacodec_decoder; +extern const FFCodec ff_av1_mediacodec_encoder; extern const FFCodec ff_av1_nvenc_encoder; extern const FFCodec ff_av1_qsv_decoder; extern const FFCodec ff_av1_qsv_encoder; +extern const FFCodec ff_av1_amf_encoder; extern const FFCodec ff_libopenh264_encoder; extern const FFCodec ff_libopenh264_decoder; extern const FFCodec ff_h264_amf_encoder; @@ -865,21 +878,30 @@ extern const FFCodec ff_mpeg2_qsv_encoder; extern const FFCodec ff_mpeg2_vaapi_encoder; extern const FFCodec ff_mpeg4_cuvid_decoder; extern const FFCodec ff_mpeg4_mediacodec_decoder; +extern const FFCodec ff_mpeg4_mediacodec_encoder; extern const FFCodec ff_mpeg4_omx_encoder; extern const FFCodec ff_mpeg4_v4l2m2m_encoder; extern const FFCodec ff_prores_videotoolbox_encoder; extern const FFCodec ff_vc1_cuvid_decoder; extern const FFCodec ff_vp8_cuvid_decoder; extern const FFCodec ff_vp8_mediacodec_decoder; +extern const FFCodec ff_vp8_mediacodec_encoder; extern const FFCodec ff_vp8_qsv_decoder; extern const FFCodec ff_vp8_v4l2m2m_encoder; extern const FFCodec ff_vp8_vaapi_encoder; extern const FFCodec ff_vp9_cuvid_decoder; extern const FFCodec ff_vp9_mediacodec_decoder; +extern const FFCodec ff_vp9_mediacodec_encoder; extern const FFCodec ff_vp9_qsv_decoder; extern const FFCodec ff_vp9_vaapi_encoder; extern const FFCodec ff_vp9_qsv_encoder; +// null codecs +extern const FFCodec ff_vnull_decoder; +extern const FFCodec ff_vnull_encoder; +extern const FFCodec ff_anull_decoder; +extern const FFCodec ff_anull_encoder; + // The iterate API is not usable with ossfuzz due to the excessive size of binaries created #if CONFIG_OSSFUZZ const FFCodec * codec_list[] = { diff --git a/libavcodec/alsdec.c b/libavcodec/alsdec.c index 4605b2248fa..c64d1032a41 100644 --- a/libavcodec/alsdec.c +++ b/libavcodec/alsdec.c @@ -2190,6 +2190,10 @@ const FFCodec ff_als_decoder = { .close = decode_end, FF_CODEC_DECODE_CB(decode_frame), .flush = flush, - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_CHANNEL_CONF, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1 | AV_CODEC_CAP_CHANNEL_CONF, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, }; diff --git a/libavcodec/amfenc.c b/libavcodec/amfenc.c index a033e1220ed..518b8396e7b 100644 --- a/libavcodec/amfenc.c +++ b/libavcodec/amfenc.c @@ -349,6 +349,9 @@ static int amf_init_encoder(AVCodecContext *avctx) case AV_CODEC_ID_HEVC: codec_id = AMFVideoEncoder_HEVC; break; + case AV_CODEC_ID_AV1 : + codec_id = AMFVideoEncoder_AV1; + break; default: break; } @@ -460,6 +463,11 @@ static int amf_copy_buffer(AVCodecContext *avctx, AVPacket *pkt, AMFBuffer *buff pkt->flags = AV_PKT_FLAG_KEY; } break; + case AV_CODEC_ID_AV1: + buffer->pVtbl->GetProperty(buffer, AMF_VIDEO_ENCODER_AV1_OUTPUT_FRAME_TYPE, &var); + if (var.int64Value == AMF_VIDEO_ENCODER_AV1_OUTPUT_FRAME_TYPE_KEY) { + pkt->flags = AV_PKT_FLAG_KEY; + } default: break; } @@ -473,7 +481,7 @@ static int amf_copy_buffer(AVCodecContext *avctx, AVPacket *pkt, AMFBuffer *buff AVERROR_UNKNOWN, "timestamp_list is empty\n"); // calc dts shift if max_b_frames > 0 - if (avctx->max_b_frames > 0 && ctx->dts_delay == 0) { + if ((ctx->max_b_frames > 0 || ((ctx->pa_adaptive_mini_gop == 1) ? true : false)) && ctx->dts_delay == 0) { int64_t timestamp_last = AV_NOPTS_VALUE; size_t can_read = av_fifo_can_read(ctx->timestamp_list); @@ -585,6 +593,8 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) AMFData *data = NULL; AVFrame *frame = ctx->delayed_frame; int block_and_wait; + int query_output_data_flag = 0; + AMF_RESULT res_resubmit; if (!ctx->encoder) return AVERROR(EINVAL); @@ -681,6 +691,7 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) case AV_CODEC_ID_HEVC: AMF_ASSIGN_PROPERTY_INT64(res, surface, AMF_VIDEO_ENCODER_HEVC_INSERT_AUD, !!ctx->aud); break; + //case AV_CODEC_ID_AV1 not supported default: break; } @@ -706,56 +717,61 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) do { block_and_wait = 0; // poll data - res_query = ctx->encoder->pVtbl->QueryOutput(ctx->encoder, &data); - if (data) { - // copy data to packet - AMFBuffer* buffer; - AMFGuid guid = IID_AMFBuffer(); - data->pVtbl->QueryInterface(data, &guid, (void**)&buffer); // query for buffer interface - ret = amf_copy_buffer(avctx, avpkt, buffer); - - buffer->pVtbl->Release(buffer); - - if (data->pVtbl->HasProperty(data, L"av_frame_ref")) { - AMFBuffer *frame_ref_storage_buffer; - res = amf_get_property_buffer(data, L"av_frame_ref", &frame_ref_storage_buffer); - AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "GetProperty failed for \"av_frame_ref\" with error %d\n", res); - amf_release_buffer_with_frame_ref(frame_ref_storage_buffer); - ctx->hwsurfaces_in_queue--; - } - - data->pVtbl->Release(data); + if (!avpkt->data && !avpkt->buf) { + res_query = ctx->encoder->pVtbl->QueryOutput(ctx->encoder, &data); + if (data) { + // copy data to packet + AMFBuffer *buffer; + AMFGuid guid = IID_AMFBuffer(); + query_output_data_flag = 1; + data->pVtbl->QueryInterface(data, &guid, (void**)&buffer); // query for buffer interface + ret = amf_copy_buffer(avctx, avpkt, buffer); + + buffer->pVtbl->Release(buffer); + + if (data->pVtbl->HasProperty(data, L"av_frame_ref")) { + AMFBuffer* frame_ref_storage_buffer; + res = amf_get_property_buffer(data, L"av_frame_ref", &frame_ref_storage_buffer); + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "GetProperty failed for \"av_frame_ref\" with error %d\n", res); + amf_release_buffer_with_frame_ref(frame_ref_storage_buffer); + ctx->hwsurfaces_in_queue--; + } - AMF_RETURN_IF_FALSE(ctx, ret >= 0, ret, "amf_copy_buffer() failed with error %d\n", ret); + data->pVtbl->Release(data); - if (ctx->delayed_surface != NULL) { // try to resubmit frame - res = ctx->encoder->pVtbl->SubmitInput(ctx->encoder, (AMFData*)ctx->delayed_surface); - if (res != AMF_INPUT_FULL) { - int64_t pts = ctx->delayed_surface->pVtbl->GetPts(ctx->delayed_surface); - ctx->delayed_surface->pVtbl->Release(ctx->delayed_surface); - ctx->delayed_surface = NULL; - av_frame_unref(ctx->delayed_frame); - AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "Repeated SubmitInput() failed with error %d\n", res); + AMF_RETURN_IF_FALSE(ctx, ret >= 0, ret, "amf_copy_buffer() failed with error %d\n", ret); + } + } + res_resubmit = AMF_OK; + if (ctx->delayed_surface != NULL) { // try to resubmit frame + res_resubmit = ctx->encoder->pVtbl->SubmitInput(ctx->encoder, (AMFData*)ctx->delayed_surface); + if (res_resubmit != AMF_INPUT_FULL) { + int64_t pts = ctx->delayed_surface->pVtbl->GetPts(ctx->delayed_surface); + ctx->delayed_surface->pVtbl->Release(ctx->delayed_surface); + ctx->delayed_surface = NULL; + av_frame_unref(ctx->delayed_frame); + AMF_RETURN_IF_FALSE(ctx, res_resubmit == AMF_OK, AVERROR_UNKNOWN, "Repeated SubmitInput() failed with error %d\n", res_resubmit); + + ret = av_fifo_write(ctx->timestamp_list, &pts, 1); + if (ret < 0) + return ret; + } + } else if (ctx->delayed_drain) { // try to resubmit drain + res = ctx->encoder->pVtbl->Drain(ctx->encoder); + if (res != AMF_INPUT_FULL) { + ctx->delayed_drain = 0; + ctx->eof = 1; // drain started + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "Repeated Drain() failed with error %d\n", res); + } else { + av_log(avctx, AV_LOG_WARNING, "Data acquired but delayed drain submission got AMF_INPUT_FULL- should not happen\n"); + } + } - ret = av_fifo_write(ctx->timestamp_list, &pts, 1); - if (ret < 0) - return ret; - } else { - av_log(avctx, AV_LOG_WARNING, "Data acquired but delayed frame submission got AMF_INPUT_FULL- should not happen\n"); - } - } else if (ctx->delayed_drain) { // try to resubmit drain - res = ctx->encoder->pVtbl->Drain(ctx->encoder); - if (res != AMF_INPUT_FULL) { - ctx->delayed_drain = 0; - ctx->eof = 1; // drain started - AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "Repeated Drain() failed with error %d\n", res); - } else { - av_log(avctx, AV_LOG_WARNING, "Data acquired but delayed drain submission got AMF_INPUT_FULL- should not happen\n"); - } + if (query_output_data_flag == 0) { + if (res_resubmit == AMF_INPUT_FULL || ctx->delayed_drain || (ctx->eof && res_query != AMF_EOF) || (ctx->hwsurfaces_in_queue >= ctx->hwsurfaces_in_queue_max)) { + block_and_wait = 1; + av_usleep(1000); } - } else if (ctx->delayed_surface != NULL || ctx->delayed_drain || (ctx->eof && res_query != AMF_EOF) || (ctx->hwsurfaces_in_queue >= ctx->hwsurfaces_in_queue_max)) { - block_and_wait = 1; - av_usleep(1000); // wait and poll again } } while (block_and_wait); diff --git a/libavcodec/amfenc.h b/libavcodec/amfenc.h index 1ab98d2f784..2dbd378ef87 100644 --- a/libavcodec/amfenc.h +++ b/libavcodec/amfenc.h @@ -23,12 +23,14 @@ #include #include +#include #include "libavutil/fifo.h" #include "avcodec.h" #include "hwconfig.h" +#define MAX_LOOKAHEAD_DEPTH 41 /** * AMF trace writer callback class @@ -83,7 +85,7 @@ typedef struct AmfContext { int usage; int profile; int level; - int preanalysis; + int preencode; int quality; int b_frame_delta_qp; int ref_b_frame_delta_qp; @@ -106,6 +108,10 @@ typedef struct AmfContext { int me_half_pel; int me_quarter_pel; int aud; + int max_consecutive_b_frames; + int max_b_frames; + int qvbr_quality_level; + int hw_high_motion_quality_boost; // HEVC - specific options @@ -116,6 +122,31 @@ typedef struct AmfContext { int min_qp_p; int max_qp_p; int tier; + + // AV1 - specific options + + enum AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_ENUM align; + + // Preanalysis - specific options + + int preanalysis; + int pa_activity_type; + int pa_scene_change_detection; + int pa_scene_change_detection_sensitivity; + int pa_static_scene_detection; + int pa_static_scene_detection_sensitivity; + int pa_initial_qp; + int pa_max_qp; + int pa_caq_strength; + int pa_frame_sad; + int pa_ltr; + int pa_lookahead_buffer_depth; + int pa_paq_mode; + int pa_taq_mode; + int pa_high_motion_quality_boost_mode; + int pa_adaptive_mini_gop; + + } AmfContext; extern const AVCodecHWConfigInternal *const ff_amfenc_hw_configs[]; diff --git a/libavcodec/amfenc_av1.c b/libavcodec/amfenc_av1.c new file mode 100644 index 00000000000..30c0a9fad25 --- /dev/null +++ b/libavcodec/amfenc_av1.c @@ -0,0 +1,488 @@ +/* + * 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 "libavutil/internal.h" +#include "libavutil/opt.h" +#include "amfenc.h" +#include "codec_internal.h" +#include "internal.h" + +#define OFFSET(x) offsetof(AmfContext, x) +#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM +static const AVOption options[] = { + { "usage", "Set the encoding usage", OFFSET(usage), AV_OPT_TYPE_INT, {.i64 = AMF_VIDEO_ENCODER_AV1_USAGE_TRANSCODING }, AMF_VIDEO_ENCODER_AV1_USAGE_TRANSCODING, AMF_VIDEO_ENCODER_AV1_USAGE_LOW_LATENCY, VE, "usage" }, + { "transcoding", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_USAGE_TRANSCODING }, 0, 0, VE, "usage" }, + { "lowlatency", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_USAGE_LOW_LATENCY }, 0, 0, VE, "usage" }, + + { "profile", "Set the profile (default main)", OFFSET(profile), AV_OPT_TYPE_INT,{.i64 = AMF_VIDEO_ENCODER_AV1_PROFILE_MAIN }, AMF_VIDEO_ENCODER_AV1_PROFILE_MAIN, AMF_VIDEO_ENCODER_AV1_PROFILE_MAIN, VE, "profile" }, + { "main", "", 0, AV_OPT_TYPE_CONST,{.i64 = AMF_VIDEO_ENCODER_AV1_PROFILE_MAIN }, 0, 0, VE, "profile" }, + + { "level", "Set the encoding level (default auto)", OFFSET(level), AV_OPT_TYPE_INT,{.i64 = 0 }, 0, AMF_VIDEO_ENCODER_AV1_LEVEL_7_3, VE, "level" }, + { "auto", "", 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, VE, "level" }, + { "2.0", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_2_0 }, 0, 0, VE, "level" }, + { "2.1", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_2_1 }, 0, 0, VE, "level" }, + { "2.2", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_2_2 }, 0, 0, VE, "level" }, + { "2.3", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_2_3 }, 0, 0, VE, "level" }, + { "3.0", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_3_0 }, 0, 0, VE, "level" }, + { "3.1", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_3_1 }, 0, 0, VE, "level" }, + { "3.2", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_3_2 }, 0, 0, VE, "level" }, + { "3.3", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_3_3 }, 0, 0, VE, "level" }, + { "4.0", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_4_0 }, 0, 0, VE, "level" }, + { "4.1", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_4_1 }, 0, 0, VE, "level" }, + { "4.2", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_4_2 }, 0, 0, VE, "level" }, + { "4.3", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_4_3 }, 0, 0, VE, "level" }, + { "5.0", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_5_0 }, 0, 0, VE, "level" }, + { "5.1", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_5_1 }, 0, 0, VE, "level" }, + { "5.2", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_5_2 }, 0, 0, VE, "level" }, + { "5.3", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_5_3 }, 0, 0, VE, "level" }, + { "6.0", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_6_0 }, 0, 0, VE, "level" }, + { "6.1", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_6_1 }, 0, 0, VE, "level" }, + { "6.2", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_6_2 }, 0, 0, VE, "level" }, + { "6.3", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_6_3 }, 0, 0, VE, "level" }, + { "7.0", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_7_0 }, 0, 0, VE, "level" }, + { "7.1", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_7_1 }, 0, 0, VE, "level" }, + { "7.2", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_7_2 }, 0, 0, VE, "level" }, + { "7.3", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_7_3 }, 0, 0, VE, "level" }, + + { "quality", "Set the encoding quality", OFFSET(quality), AV_OPT_TYPE_INT, {.i64 = AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_SPEED }, AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_HIGH_QUALITY, AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_SPEED, VE, "quality" }, + { "balanced", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_BALANCED }, 0, 0, VE, "quality" }, + { "speed", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_SPEED }, 0, 0, VE, "quality" }, + { "quality", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_QUALITY }, 0, 0, VE, "quality" }, + { "high_quality", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_HIGH_QUALITY }, 0, 0, VE, "quality" }, + + { "rc", "Set the rate control mode", OFFSET(rate_control_mode), AV_OPT_TYPE_INT, {.i64 = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_UNKNOWN }, AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_UNKNOWN, AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_HIGH_QUALITY_CBR, VE, "rc" }, + { "cqp", "Constant Quantization Parameter", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CONSTANT_QP }, 0, 0, VE, "rc" }, + { "vbr_latency", "Latency Constrained Variable Bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR }, 0, 0, VE, "rc" }, + { "vbr_peak", "Peak Contrained Variable Bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR }, 0, 0, VE, "rc" }, + { "cbr", "Constant Bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CBR }, 0, 0, VE, "rc" }, + { "qvbr", "Quality Variable Bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_QUALITY_VBR }, 0, 0, VE, "rc" }, + { "hqvbr", "High Quality Variable Bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_HIGH_QUALITY_VBR }, 0, 0, VE, "rc" }, + { "hqcbr", "High Quality Constant Bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_HIGH_QUALITY_CBR }, 0, 0, VE, "rc" }, + + { "qvbr_quality_level", "Sets the QVBR quality level", OFFSET(qvbr_quality_level), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 51, VE }, + + + { "header_insertion_mode", "Set header insertion mode", OFFSET(header_insertion_mode), AV_OPT_TYPE_INT,{.i64 = AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE_NONE }, AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE_NONE, AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE_KEY_FRAME_ALIGNED, VE, "hdrmode" }, + { "none", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE_NONE }, 0, 0, VE, "hdrmode" }, + { "gop", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE_GOP_ALIGNED }, 0, 0, VE, "hdrmode" }, + { "frame", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE_KEY_FRAME_ALIGNED }, 0, 0, VE, "hdrmode" }, + + { "preencode", "Enable preencode", OFFSET(preencode), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, VE}, + { "enforce_hrd", "Enforce HRD", OFFSET(enforce_hrd), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, VE}, + { "filler_data", "Filler Data Enable", OFFSET(filler_data), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, VE}, + + { "high_motion_quality_boost_enable", "Enable High motion quality boost mode", OFFSET(hw_high_motion_quality_boost), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + + // min_qp_i -> min_qp_intra, min_qp_p -> min_qp_inter + { "min_qp_i", "min quantization parameter for I-frame", OFFSET(min_qp_i), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 255, VE }, + { "max_qp_i", "max quantization parameter for I-frame", OFFSET(max_qp_i), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 255, VE }, + { "min_qp_p", "min quantization parameter for P-frame", OFFSET(min_qp_p), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 255, VE }, + { "max_qp_p", "max quantization parameter for P-frame", OFFSET(max_qp_p), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 255, VE }, + { "qp_p", "quantization parameter for P-frame", OFFSET(qp_p), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 255, VE }, + { "qp_i", "quantization parameter for I-frame", OFFSET(qp_i), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 255, VE }, + { "skip_frame", "Rate Control Based Frame Skip", OFFSET(skip_frame), AV_OPT_TYPE_BOOL,{.i64 = 0 }, 0, 1, VE }, + + { "align", "alignment mode", OFFSET(align), AV_OPT_TYPE_INT, {.i64 = AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_NO_RESTRICTIONS }, AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_64X16_ONLY, AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_NO_RESTRICTIONS, VE, "align" }, + { "64x16", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_64X16_ONLY }, 0, 0, VE, "align" }, + { "1080p", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_64X16_1080P_CODED_1082 }, 0, 0, VE, "align" }, + { "none", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_NO_RESTRICTIONS }, 0, 0, VE, "align" }, + + { "log_to_dbg", "Enable AMF logging to debug output", OFFSET(log_to_dbg), AV_OPT_TYPE_BOOL,{.i64 = 0 }, 0, 1, VE }, + + //Pre Analysis options + { "preanalysis", "Enable preanalysis", OFFSET(preanalysis), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + + { "pa_activity_type", "Set the type of activity analysis", OFFSET(pa_activity_type), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_ACTIVITY_YUV, VE, "activity_type" }, + { "y", "activity y", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_ACTIVITY_Y }, 0, 0, VE, "activity_type" }, + { "yuv", "activity yuv", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_ACTIVITY_YUV }, 0, 0, VE, "activity_type" }, + + { "pa_scene_change_detection_enable", "Enable scene change detection", OFFSET(pa_scene_change_detection), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + + { "pa_scene_change_detection_sensitivity", "Set the sensitivity of scene change detection", OFFSET(pa_scene_change_detection_sensitivity), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_HIGH, VE, "scene_change_sensitivity" }, + { "low", "low scene change dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_LOW }, 0, 0, VE, "scene_change_sensitivity" }, + { "medium", "medium scene change dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_MEDIUM }, 0, 0, VE, "scene_change_sensitivity" }, + { "high", "high scene change dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_HIGH }, 0, 0, VE, "scene_change_sensitivity" }, + + { "pa_static_scene_detection_enable", "Enable static scene detection", OFFSET(pa_static_scene_detection), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + + { "pa_static_scene_detection_sensitivity", "Set the sensitivity of static scene detection", OFFSET(pa_static_scene_detection_sensitivity), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_HIGH, VE , "static_scene_sensitivity" }, + { "low", "low static scene dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_LOW }, 0, 0, VE, "static_scene_sensitivity" }, + { "medium", "medium static scene dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_MEDIUM }, 0, 0, VE, "static_scene_sensitivity" }, + { "high", "high static scene dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_HIGH }, 0, 0, VE, "static_scene_sensitivity" }, + + { "pa_initial_qp_after_scene_change", "The QP value that is used immediately after a scene change", OFFSET(pa_initial_qp), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 51, VE }, + { "pa_max_qp_before_force_skip", "The QP threshold to allow a skip frame", OFFSET(pa_max_qp), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 51, VE }, + + { "pa_caq_strength", "Content Adaptive Quantization strength", OFFSET(pa_caq_strength), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_CAQ_STRENGTH_HIGH, VE , "caq_strength" }, + { "low", "low Content Adaptive Quantization strength", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_CAQ_STRENGTH_LOW }, 0, 0, VE, "caq_strength" }, + { "medium", "medium Content Adaptive Quantization strength", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_CAQ_STRENGTH_MEDIUM }, 0, 0, VE, "caq_strength" }, + { "high", "high Content Adaptive Quantization strength", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_CAQ_STRENGTH_HIGH }, 0, 0, VE, "caq_strength" }, + + { "pa_frame_sad_enable", "Enable Frame SAD algorithm", OFFSET(pa_frame_sad), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + { "pa_ltr_enable", "Enable long term reference frame management", OFFSET(pa_ltr), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + { "pa_lookahead_buffer_depth", "Sets the PA lookahead buffer size", OFFSET(pa_lookahead_buffer_depth), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, MAX_LOOKAHEAD_DEPTH, VE }, + + { "pa_paq_mode", "Sets the perceptual adaptive quantization mode", OFFSET(pa_paq_mode), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_PAQ_MODE_CAQ, VE , "paq_mode" }, + { "none", "no perceptual adaptive quantization", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_PAQ_MODE_NONE }, 0, 0, VE, "paq_mode" }, + { "caq", "caq perceptual adaptive quantization", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_PAQ_MODE_CAQ }, 0, 0, VE, "paq_mode" }, + + { "pa_taq_mode", "Sets the temporal adaptive quantization mode", OFFSET(pa_taq_mode), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_TAQ_MODE_2, VE , "taq_mode" }, + { "none", "no temporal adaptive quantization", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_TAQ_MODE_NONE }, 0, 0, VE, "taq_mode" }, + { "1", "temporal adaptive quantization mode 1", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_TAQ_MODE_1 }, 0, 0, VE, "taq_mode" }, + { "2", "temporal adaptive quantization mode 2", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_TAQ_MODE_2 }, 0, 0, VE, "taq_mode" }, + + { "pa_high_motion_quality_boost_mode", "Sets the PA high motion quality boost mode", OFFSET(pa_high_motion_quality_boost_mode), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE_AUTO, VE , "high_motion_quality_boost_mode" }, + { "none", "no high motion quality boost", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE_NONE }, 0, 0, VE, "high_motion_quality_boost_mode" }, + { "auto", "auto high motion quality boost", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE_AUTO }, 0, 0, VE, "high_motion_quality_boost_mode" }, + { NULL } + +}; + +static av_cold int amf_encode_init_av1(AVCodecContext* avctx) +{ + int ret = 0; + AMF_RESULT res = AMF_OK; + AmfContext* ctx = avctx->priv_data; + AMFVariantStruct var = { 0 }; + amf_int64 profile = 0; + amf_int64 profile_level = 0; + AMFBuffer* buffer; + AMFGuid guid; + AMFRate framerate; + AMFSize framesize = AMFConstructSize(avctx->width, avctx->height); + + + + if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { + framerate = AMFConstructRate(avctx->framerate.num, avctx->framerate.den); + } + else { +FF_DISABLE_DEPRECATION_WARNINGS + framerate = AMFConstructRate(avctx->time_base.den, avctx->time_base.num +#if FF_API_TICKS_PER_FRAME + * avctx->ticks_per_frame +#endif + ); +FF_ENABLE_DEPRECATION_WARNINGS + } + + if ((ret = ff_amf_encode_init(avctx)) < 0) + return ret; + + // init static parameters + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_USAGE, ctx->usage); + + AMF_ASSIGN_PROPERTY_SIZE(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_FRAMESIZE, framesize); + + AMF_ASSIGN_PROPERTY_RATE(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_FRAMERATE, framerate); + + switch (avctx->profile) { + case FF_PROFILE_AV1_MAIN: + profile = AMF_VIDEO_ENCODER_AV1_PROFILE_MAIN; + break; + default: + break; + } + if (profile == 0) { + profile = ctx->profile; + } + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_PROFILE, profile); + + profile_level = avctx->level; + if (profile_level == FF_LEVEL_UNKNOWN) { + profile_level = ctx->level; + } + if (profile_level != 0) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_LEVEL, profile_level); + } + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET, ctx->quality); + + // Maximum Reference Frames + if (avctx->refs != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_MAX_NUM_REFRAMES, avctx->refs); + } + + // Picture control properties + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_GOP_SIZE, avctx->gop_size); + + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE, ctx->header_insertion_mode); + + // Rate control + // autodetect rate control method + if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_UNKNOWN) { + if (ctx->min_qp_i != -1 || ctx->max_qp_i != -1 || + ctx->min_qp_p != -1 || ctx->max_qp_p != -1 || + ctx->qp_i != -1 || ctx->qp_p != -1) { + ctx->rate_control_mode = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CONSTANT_QP; + av_log(ctx, AV_LOG_DEBUG, "Rate control turned to CQP\n"); + } + else if (avctx->rc_max_rate > 0) { + ctx->rate_control_mode = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR; + av_log(ctx, AV_LOG_DEBUG, "Rate control turned to Peak VBR\n"); + } + else { + ctx->rate_control_mode = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CBR; + av_log(ctx, AV_LOG_DEBUG, "Rate control turned to CBR\n"); + } + } + + // Pre-Pass, Pre-Analysis, Two-Pass + if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CONSTANT_QP) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_PREENCODE, 0); + if (ctx->preencode) + av_log(ctx, AV_LOG_WARNING, "Preencode is not supported by cqp Rate Control Method, automatically disabled\n"); + } + else { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_PREENCODE, ctx->preencode); + } + + if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_QUALITY_VBR) { + if (ctx->qvbr_quality_level != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_QVBR_QUALITY_LEVEL, ctx->qvbr_quality_level); + } + } + + if (ctx->hw_high_motion_quality_boost != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_HIGH_MOTION_QUALITY_BOOST, ((ctx->hw_high_motion_quality_boost == 0) ? false : true)); + } + + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD, ctx->rate_control_mode); + if (avctx->rc_buffer_size) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_VBV_BUFFER_SIZE, avctx->rc_buffer_size); + + if (avctx->rc_initial_buffer_occupancy != 0) { + int amf_buffer_fullness = avctx->rc_initial_buffer_occupancy * 64 / avctx->rc_buffer_size; + if (amf_buffer_fullness > 64) + amf_buffer_fullness = 64; + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_INITIAL_VBV_BUFFER_FULLNESS, amf_buffer_fullness); + } + } + + // init dynamic rate control params + if (ctx->max_au_size) + ctx->enforce_hrd = 1; + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_ENFORCE_HRD, ctx->enforce_hrd); + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_FILLER_DATA, ctx->filler_data); + + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_TARGET_BITRATE, avctx->bit_rate); + + if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CBR) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_PEAK_BITRATE, avctx->bit_rate); + } + if (avctx->rc_max_rate) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_PEAK_BITRATE, avctx->rc_max_rate); + } + else if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR) { + av_log(ctx, AV_LOG_WARNING, "rate control mode is PEAK_CONSTRAINED_VBR but rc_max_rate is not set\n"); + } + if (avctx->bit_rate > 0) { + ctx->rate_control_mode = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CBR; + av_log(ctx, AV_LOG_DEBUG, "Rate control turned to CBR\n"); + } + + switch (ctx->align) + { + case AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_64X16_ONLY: + if (avctx->width / 64 * 64 != avctx->width || avctx->height / 16 * 16 != avctx->height) + { + res = AMF_NOT_SUPPORTED; + av_log(ctx, AV_LOG_ERROR, "Resolution incorrect for alignment mode\n"); + return AVERROR_EXIT; + } + break; + case AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_64X16_1080P_CODED_1082: + if ((avctx->width / 64 * 64 == avctx->width && avctx->height / 16 * 16 == avctx->height) || (avctx->width == 1920 && avctx->height == 1080)) + { + res = AMF_OK; + } + else + { + res = AMF_NOT_SUPPORTED; + av_log(ctx, AV_LOG_ERROR, "Resolution incorrect for alignment mode\n"); + return AVERROR_EXIT; + } + break; + case AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_NO_RESTRICTIONS: + res = AMF_OK; + break; + default: + res = AMF_NOT_SUPPORTED; + av_log(ctx, AV_LOG_ERROR, "Invalid alignment mode\n"); + return AVERROR_EXIT; + } + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE, ctx->align); + + if (ctx->preanalysis != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_PRE_ANALYSIS_ENABLE, !!((ctx->preanalysis == 0) ? false : true)); + } + + res = ctx->encoder->pVtbl->GetProperty(ctx->encoder, AMF_VIDEO_ENCODER_AV1_PRE_ANALYSIS_ENABLE, &var); + if ((int)var.int64Value) + { + if (ctx->pa_activity_type != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_ACTIVITY_TYPE, ctx->pa_activity_type); + } + if (ctx->pa_scene_change_detection != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_SCENE_CHANGE_DETECTION_ENABLE, ((ctx->pa_scene_change_detection == 0) ? false : true)); + } + if (ctx->pa_scene_change_detection_sensitivity != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY, ctx->pa_scene_change_detection_sensitivity); + } + if (ctx->pa_static_scene_detection != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_STATIC_SCENE_DETECTION_ENABLE, ((ctx->pa_static_scene_detection == 0) ? false : true)); + } + if (ctx->pa_static_scene_detection_sensitivity != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY, ctx->pa_static_scene_detection_sensitivity); + } + if (ctx->pa_initial_qp != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_INITIAL_QP_AFTER_SCENE_CHANGE, ctx->pa_initial_qp); + } + if (ctx->pa_max_qp != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_MAX_QP_BEFORE_FORCE_SKIP, ctx->pa_max_qp); + } + if (ctx->pa_caq_strength != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_CAQ_STRENGTH, ctx->pa_caq_strength); + } + if (ctx->pa_frame_sad != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_FRAME_SAD_ENABLE, ((ctx->pa_frame_sad == 0) ? false : true)); + } + if (ctx->pa_paq_mode != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_PAQ_MODE, ctx->pa_paq_mode); + } + if (ctx->pa_taq_mode != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_TAQ_MODE, ctx->pa_taq_mode); + } + if (ctx->pa_ltr != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_LTR_ENABLE, ((ctx->pa_ltr == 0) ? false : true)); + } + if (ctx->pa_lookahead_buffer_depth != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_LOOKAHEAD_BUFFER_DEPTH, ctx->pa_lookahead_buffer_depth); + } + if (ctx->pa_high_motion_quality_boost_mode != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE, ctx->pa_high_motion_quality_boost_mode); + } + } + + // init encoder + res = ctx->encoder->pVtbl->Init(ctx->encoder, ctx->format, avctx->width, avctx->height); + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_BUG, "encoder->Init() failed with error %d\n", res); + + // init dynamic picture control params + if (ctx->min_qp_i != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_MIN_Q_INDEX_INTRA, ctx->min_qp_i); + } + else if (avctx->qmin != -1) { + int qval = avctx->qmin > 255 ? 255 : avctx->qmin; + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_MIN_Q_INDEX_INTRA, qval); + } + if (ctx->max_qp_i != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_MAX_Q_INDEX_INTRA, ctx->max_qp_i); + } + else if (avctx->qmax != -1) { + int qval = avctx->qmax > 255 ? 255 : avctx->qmax; + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_MAX_Q_INDEX_INTRA, qval); + } + if (ctx->min_qp_p != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_MIN_Q_INDEX_INTER, ctx->min_qp_p); + } + else if (avctx->qmin != -1) { + int qval = avctx->qmin > 255 ? 255 : avctx->qmin; + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_MIN_Q_INDEX_INTER, qval); + } + if (ctx->max_qp_p != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_MAX_Q_INDEX_INTER, ctx->max_qp_p); + } + else if (avctx->qmax != -1) { + int qval = avctx->qmax > 255 ? 255 : avctx->qmax; + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_MAX_Q_INDEX_INTER, qval); + } + + if (ctx->qp_p != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_Q_INDEX_INTER, ctx->qp_p); + } + if (ctx->qp_i != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_Q_INDEX_INTRA, ctx->qp_i); + } + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_SKIP_FRAME, ctx->skip_frame); + + // fill extradata + res = AMFVariantInit(&var); + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_BUG, "AMFVariantInit() failed with error %d\n", res); + + res = ctx->encoder->pVtbl->GetProperty(ctx->encoder, AMF_VIDEO_ENCODER_AV1_EXTRA_DATA, &var); + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_BUG, "GetProperty(AMF_VIDEO_ENCODER_EXTRADATA) failed with error %d\n", res); + AMF_RETURN_IF_FALSE(ctx, var.pInterface != NULL, AVERROR_BUG, "GetProperty(AMF_VIDEO_ENCODER_EXTRADATA) returned NULL\n"); + + guid = IID_AMFBuffer(); + + res = var.pInterface->pVtbl->QueryInterface(var.pInterface, &guid, (void**)&buffer); // query for buffer interface + if (res != AMF_OK) { + var.pInterface->pVtbl->Release(var.pInterface); + } + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_BUG, "QueryInterface(IID_AMFBuffer) failed with error %d\n", res); + + avctx->extradata_size = (int)buffer->pVtbl->GetSize(buffer); + avctx->extradata = av_mallocz(avctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); + if (!avctx->extradata) { + buffer->pVtbl->Release(buffer); + var.pInterface->pVtbl->Release(var.pInterface); + return AVERROR(ENOMEM); + } + memcpy(avctx->extradata, buffer->pVtbl->GetNative(buffer), avctx->extradata_size); + + buffer->pVtbl->Release(buffer); + var.pInterface->pVtbl->Release(var.pInterface); + + return 0; +} + +static const FFCodecDefault defaults[] = { + { "refs", "-1" }, + { "aspect", "0" }, + { "b", "2M" }, + { "g", "250" }, + { "qmin", "-1" }, + { "qmax", "-1" }, + { NULL }, +}; + +static const AVClass av1_amf_class = { + .class_name = "av1_amf", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const FFCodec ff_av1_amf_encoder = { + .p.name = "av1_amf", + CODEC_LONG_NAME("AMD AMF AV1 encoder"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_AV1, + .init = amf_encode_init_av1, + FF_CODEC_RECEIVE_PACKET_CB(ff_amf_receive_packet), + .close = ff_amf_encode_close, + .priv_data_size = sizeof(AmfContext), + .p.priv_class = &av1_amf_class, + .defaults = defaults, + .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE | + AV_CODEC_CAP_DR1, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, + .p.pix_fmts = ff_amf_pix_fmts, + .p.wrapper_name = "amf", + .hw_configs = ff_amfenc_hw_configs, +}; diff --git a/libavcodec/amfenc_h264.c b/libavcodec/amfenc_h264.c index eaf7f974f3c..2380aa4e906 100644 --- a/libavcodec/amfenc_h264.c +++ b/libavcodec/amfenc_h264.c @@ -22,6 +22,7 @@ #include "amfenc.h" #include "codec_internal.h" #include "internal.h" +#include #define OFFSET(x) offsetof(AmfContext, x) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM @@ -29,11 +30,14 @@ static const AVOption options[] = { // Static /// Usage - { "usage", "Encoder Usage", OFFSET(usage), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_USAGE_TRANSCONDING }, AMF_VIDEO_ENCODER_USAGE_TRANSCONDING, AMF_VIDEO_ENCODER_USAGE_WEBCAM, VE, "usage" }, - { "transcoding", "Generic Transcoding", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_USAGE_TRANSCONDING }, 0, 0, VE, "usage" }, - { "ultralowlatency","", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_USAGE_ULTRA_LOW_LATENCY }, 0, 0, VE, "usage" }, - { "lowlatency", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY }, 0, 0, VE, "usage" }, + { "usage", "Encoder Usage", OFFSET(usage), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_USAGE_TRANSCODING }, AMF_VIDEO_ENCODER_USAGE_TRANSCODING, AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY_HIGH_QUALITY, VE, "usage" }, + { "transcoding", "Generic Transcoding", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_USAGE_TRANSCODING }, 0, 0, VE, "usage" }, + { "ultralowlatency","ultra low latency trancoding", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_USAGE_ULTRA_LOW_LATENCY }, 0, 0, VE, "usage" }, + { "lowlatency", "low latency trancoding", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY }, 0, 0, VE, "usage" }, { "webcam", "Webcam", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_USAGE_WEBCAM }, 0, 0, VE, "usage" }, + { "high_quality", "high quality trancoding", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_USAGE_HIGH_QUALITY }, 0, 0, VE, "usage" }, + { "lowlatency_high_quality", "low latency yet high quality trancoding", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY_HIGH_QUALITY }, 0, 0, VE, "usage" }, + /// Profile, { "profile", "Profile", OFFSET(profile),AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_PROFILE_MAIN }, AMF_VIDEO_ENCODER_PROFILE_BASELINE, AMF_VIDEO_ENCODER_PROFILE_CONSTRAINED_HIGH, VE, "profile" }, @@ -74,11 +78,16 @@ static const AVOption options[] = { // Dynamic /// Rate Control Method - { "rc", "Rate Control Method", OFFSET(rate_control_mode), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_UNKNOWN }, AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_UNKNOWN, AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR, VE, "rc" }, + { "rc", "Rate Control Method", OFFSET(rate_control_mode), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_UNKNOWN }, AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_UNKNOWN, AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_HIGH_QUALITY_CBR, VE, "rc" }, { "cqp", "Constant Quantization Parameter", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CONSTANT_QP }, 0, 0, VE, "rc" }, { "cbr", "Constant Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR }, 0, 0, VE, "rc" }, { "vbr_peak", "Peak Contrained Variable Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR }, 0, 0, VE, "rc" }, { "vbr_latency", "Latency Constrained Variable Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR }, 0, 0, VE, "rc" }, + { "qvbr", "Quality Variable Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_QUALITY_VBR }, 0, 0, VE, "rc" }, + { "hqvbr", "High Quality Variable Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_HIGH_QUALITY_VBR }, 0, 0, VE, "rc" }, + { "hqcbr", "High Quality Constant Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_HIGH_QUALITY_CBR }, 0, 0, VE, "rc" }, + + { "qvbr_quality_level", "Sets the QVBR quality level", OFFSET(qvbr_quality_level),AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 51, VE }, /// Enforce HRD, Filler Data, VBAQ, Frame Skipping { "enforce_hrd", "Enforce HRD", OFFSET(enforce_hrd), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, @@ -92,7 +101,7 @@ static const AVOption options[] = { { "qp_b", "Quantization Parameter for B-Frame", OFFSET(qp_b), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 51, VE }, /// Pre-Pass, Pre-Analysis, Two-Pass - { "preanalysis", "Pre-Analysis Mode", OFFSET(preanalysis), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE, NULL }, + { "preencode", "Pre-encode assisted rate control", OFFSET(preencode), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE, NULL }, /// Maximum Access Unit Size { "max_au_size", "Maximum Access Unit Size for rate control (in bits)", OFFSET(max_au_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, @@ -106,6 +115,9 @@ static const AVOption options[] = { { "bf_ref", "Enable Reference to B-Frames", OFFSET(b_frame_ref), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE }, { "bf_ref_delta_qp","Reference B-Picture Delta QP", OFFSET(ref_b_frame_delta_qp), AV_OPT_TYPE_INT, { .i64 = 4 }, -10, 10, VE }, + { "max_b_frames", "Maximum number of consecutive B Pictures", OFFSET(max_consecutive_b_frames), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 3, VE }, + { "bf", "B Picture Pattern", OFFSET(max_b_frames), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 3, VE }, + /// Intra-Refresh { "intra_refresh_mb","Intra Refresh MBs Number Per Slot in Macroblocks", OFFSET(intra_refresh_mb), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, @@ -115,6 +127,8 @@ static const AVOption options[] = { { "cavlc", "Context Adaptive Variable-Length Coding", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_CALV }, 0, 0, VE, "coder" }, { "cabac", "Context Adaptive Binary Arithmetic Coding", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_CABAC }, 0, 0, VE, "coder" }, + { "high_motion_quality_boost_enable", "Enable High motion quality boost mode", OFFSET(hw_high_motion_quality_boost), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + { "me_half_pel", "Enable ME Half Pixel", OFFSET(me_half_pel), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE }, { "me_quarter_pel", "Enable ME Quarter Pixel", OFFSET(me_quarter_pel),AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE }, @@ -122,6 +136,53 @@ static const AVOption options[] = { { "log_to_dbg", "Enable AMF logging to debug output", OFFSET(log_to_dbg) , AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, + //Pre Analysis options + { "preanalysis", "Enable preanalysis", OFFSET(preanalysis), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + + { "pa_activity_type", "Set the type of activity analysis", OFFSET(pa_activity_type), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_ACTIVITY_YUV, VE, "activity_type" }, + { "y", "activity y", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_ACTIVITY_Y }, 0, 0, VE, "activity_type" }, + { "yuv", "activity yuv", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_ACTIVITY_YUV }, 0, 0, VE, "activity_type" }, + + { "pa_scene_change_detection_enable", "Enable scene change detection", OFFSET(pa_scene_change_detection), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + + { "pa_scene_change_detection_sensitivity", "Set the sensitivity of scene change detection", OFFSET(pa_scene_change_detection_sensitivity), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_HIGH, VE, "scene_change_sensitivity" }, + { "low", "low scene change dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_LOW }, 0, 0, VE, "scene_change_sensitivity" }, + { "medium", "medium scene change dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_MEDIUM }, 0, 0, VE, "scene_change_sensitivity" }, + { "high", "high scene change dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_HIGH }, 0, 0, VE, "scene_change_sensitivity" }, + + { "pa_static_scene_detection_enable", "Enable static scene detection", OFFSET(pa_static_scene_detection), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + + { "pa_static_scene_detection_sensitivity", "Set the sensitivity of static scene detection", OFFSET(pa_static_scene_detection_sensitivity), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_HIGH, VE , "static_scene_sensitivity" }, + { "low", "low static scene dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_LOW }, 0, 0, VE, "static_scene_sensitivity" }, + { "medium", "medium static scene dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_MEDIUM }, 0, 0, VE, "static_scene_sensitivity" }, + { "high", "high static scene dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_HIGH }, 0, 0, VE, "static_scene_sensitivity" }, + + { "pa_initial_qp_after_scene_change", "The QP value that is used immediately after a scene change", OFFSET(pa_initial_qp), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 51, VE }, + { "pa_max_qp_before_force_skip", "The QP threshold to allow a skip frame", OFFSET(pa_max_qp), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 51, VE }, + + { "pa_caq_strength", "Content Adaptive Quantization strength", OFFSET(pa_caq_strength), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_CAQ_STRENGTH_HIGH, VE , "caq_strength" }, + { "low", "low Content Adaptive Quantization strength", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_CAQ_STRENGTH_LOW }, 0, 0, VE, "caq_strength" }, + { "medium", "medium Content Adaptive Quantization strength", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_CAQ_STRENGTH_MEDIUM }, 0, 0, VE, "caq_strength" }, + { "high", "high Content Adaptive Quantization strength", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_CAQ_STRENGTH_HIGH }, 0, 0, VE, "caq_strength" }, + + { "pa_frame_sad_enable", "Enable Frame SAD algorithm", OFFSET(pa_frame_sad), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + { "pa_ltr_enable", "Enable long term reference frame management", OFFSET(pa_ltr), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + { "pa_lookahead_buffer_depth", "Sets the PA lookahead buffer size", OFFSET(pa_lookahead_buffer_depth), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, MAX_LOOKAHEAD_DEPTH, VE }, + + { "pa_paq_mode", "Sets the perceptual adaptive quantization mode", OFFSET(pa_paq_mode), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_PAQ_MODE_CAQ, VE , "paq_mode" }, + { "none", "no perceptual adaptive quantization", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_PAQ_MODE_NONE }, 0, 0, VE, "paq_mode" }, + { "caq", "caq perceptual adaptive quantization", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_PAQ_MODE_CAQ }, 0, 0, VE, "paq_mode" }, + + { "pa_taq_mode", "Sets the temporal adaptive quantization mode", OFFSET(pa_taq_mode), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_TAQ_MODE_2, VE , "taq_mode" }, + { "none", "no temporal adaptive quantization", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_TAQ_MODE_NONE }, 0, 0, VE, "taq_mode" }, + { "1", "temporal adaptive quantization mode 1", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_TAQ_MODE_1 }, 0, 0, VE, "taq_mode" }, + { "2", "temporal adaptive quantization mode 2", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_TAQ_MODE_2 }, 0, 0, VE, "taq_mode" }, + + { "pa_high_motion_quality_boost_mode", "Sets the PA high motion quality boost mode", OFFSET(pa_high_motion_quality_boost_mode), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE_AUTO, VE , "high_motion_quality_boost_mode" }, + { "none", "no high motion quality boost", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE_NONE }, 0, 0, VE, "high_motion_quality_boost_mode" }, + { "auto", "auto high motion quality boost", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE_AUTO }, 0, 0, VE, "high_motion_quality_boost_mode" }, + + { "pa_adaptive_mini_gop", "Enable Adaptive MiniGOP", OFFSET(pa_adaptive_mini_gop), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VE }, { NULL } }; @@ -142,7 +203,13 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx) if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { framerate = AMFConstructRate(avctx->framerate.num, avctx->framerate.den); } else { - framerate = AMFConstructRate(avctx->time_base.den, avctx->time_base.num * avctx->ticks_per_frame); +FF_DISABLE_DEPRECATION_WARNINGS + framerate = AMFConstructRate(avctx->time_base.den, avctx->time_base.num +#if FF_API_TICKS_PER_FRAME + * avctx->ticks_per_frame +#endif + ); +FF_ENABLE_DEPRECATION_WARNINGS } if ((ret = ff_amf_encode_init(avctx)) != 0) @@ -215,11 +282,21 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx) } if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CONSTANT_QP) { - AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_RATE_CONTROL_PREANALYSIS_ENABLE, AMF_VIDEO_ENCODER_PREENCODE_DISABLED); - if (ctx->preanalysis) - av_log(ctx, AV_LOG_WARNING, "Pre-Analysis is not supported by cqp Rate Control Method, automatically disabled\n"); + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_PREENCODE_ENABLE, AMF_VIDEO_ENCODER_PREENCODE_DISABLED); + if (ctx->preencode) + av_log(ctx, AV_LOG_WARNING, "Preencode is not supported by cqp Rate Control Method, automatically disabled\n"); } else { - AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_RATE_CONTROL_PREANALYSIS_ENABLE, ctx->preanalysis); + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_PREENCODE_ENABLE, ctx->preencode); + } + + if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_QUALITY_VBR) { + if (ctx->qvbr_quality_level != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_QVBR_QUALITY_LEVEL, ctx->qvbr_quality_level); + } + } + + if (ctx->hw_high_motion_quality_boost != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HIGH_MOTION_QUALITY_BOOST_ENABLE, ((ctx->hw_high_motion_quality_boost == 0) ? false : true)); } AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_QUALITY_PRESET, ctx->quality); @@ -276,6 +353,88 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx) av_log(ctx, AV_LOG_WARNING, "rate control mode is PEAK_CONSTRAINED_VBR but rc_max_rate is not set\n"); } + if (ctx->preanalysis != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_PRE_ANALYSIS_ENABLE, !!((ctx->preanalysis == 0) ? false : true)); + } + + res = ctx->encoder->pVtbl->GetProperty(ctx->encoder, AMF_VIDEO_ENCODER_PRE_ANALYSIS_ENABLE, &var); + if ((int)var.int64Value) + { + if (ctx->pa_activity_type != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_ACTIVITY_TYPE, ctx->pa_activity_type); + } + if (ctx->pa_scene_change_detection != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_SCENE_CHANGE_DETECTION_ENABLE, ((ctx->pa_scene_change_detection == 0) ? false : true)); + } + if (ctx->pa_scene_change_detection_sensitivity != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY, ctx->pa_scene_change_detection_sensitivity); + } + if (ctx->pa_static_scene_detection != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_STATIC_SCENE_DETECTION_ENABLE, ((ctx->pa_static_scene_detection == 0) ? false : true)); + } + if (ctx->pa_static_scene_detection_sensitivity != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY, ctx->pa_static_scene_detection_sensitivity); + } + if (ctx->pa_initial_qp != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_INITIAL_QP_AFTER_SCENE_CHANGE, ctx->pa_initial_qp); + } + if (ctx->pa_max_qp != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_MAX_QP_BEFORE_FORCE_SKIP, ctx->pa_max_qp); + } + if (ctx->pa_caq_strength != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_CAQ_STRENGTH, ctx->pa_caq_strength); + } + if (ctx->pa_frame_sad != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_FRAME_SAD_ENABLE, ((ctx->pa_frame_sad == 0) ? false : true)); + } + if (ctx->pa_paq_mode != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_PAQ_MODE, ctx->pa_paq_mode); + } + if (ctx->pa_taq_mode != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_TAQ_MODE, ctx->pa_taq_mode); + } + if (ctx->pa_adaptive_mini_gop != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_ADAPTIVE_MINIGOP, ((ctx->pa_adaptive_mini_gop == 0) ? false : true)); + } + if (ctx->pa_ltr != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_LTR_ENABLE, ((ctx->pa_ltr == 0) ? false : true)); + } + if (ctx->pa_lookahead_buffer_depth != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_LOOKAHEAD_BUFFER_DEPTH, ctx->pa_lookahead_buffer_depth); + } + if (ctx->pa_high_motion_quality_boost_mode != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE, ctx->pa_high_motion_quality_boost_mode); + } + } + + // B-Frames + if (ctx->max_consecutive_b_frames != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_MAX_CONSECUTIVE_BPICTURES, ctx->max_consecutive_b_frames); + if (ctx->max_b_frames != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_B_PIC_PATTERN, ctx->max_b_frames); + if (res != AMF_OK) { + res = ctx->encoder->pVtbl->GetProperty(ctx->encoder, AMF_VIDEO_ENCODER_B_PIC_PATTERN, &var); + av_log(ctx, AV_LOG_WARNING, "B-frames=%d is not supported by this GPU, switched to %d\n", + ctx->max_b_frames, (int)var.int64Value); + ctx->max_b_frames = (int)var.int64Value; + } + if (ctx->max_consecutive_b_frames < ctx->max_b_frames) { + av_log(ctx, AVERROR_BUG, "Maxium B frames needs to be greater than the specified B frame count.\n"); + } + } + } + else { + if (ctx->max_b_frames != -1) { + av_log(ctx, AVERROR_BUG, "Maxium number of B frames needs to be specified.\n"); + } + } + res = ctx->encoder->pVtbl->GetProperty(ctx->encoder, AMF_VIDEO_ENCODER_B_PIC_PATTERN, &var); + if ((int)var.int64Value) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_B_PIC_DELTA_QP, ctx->b_frame_delta_qp); + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_B_REFERENCE_ENABLE, !!ctx->b_frame_ref); + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_REF_B_PIC_DELTA_QP, ctx->ref_b_frame_delta_qp); + } + // Initialize Encoder res = ctx->encoder->pVtbl->Init(ctx->encoder, ctx->format, avctx->width, avctx->height); AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_BUG, "encoder->Init() failed with error %d\n", res); @@ -293,20 +452,6 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx) } AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_DE_BLOCKING_FILTER, !!deblocking_filter); - // B-Frames - AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_B_PIC_PATTERN, avctx->max_b_frames); - if (res != AMF_OK) { - res = ctx->encoder->pVtbl->GetProperty(ctx->encoder, AMF_VIDEO_ENCODER_B_PIC_PATTERN, &var); - av_log(ctx, AV_LOG_WARNING, "B-frames=%d is not supported by this GPU, switched to %d\n", - avctx->max_b_frames, (int)var.int64Value); - avctx->max_b_frames = (int)var.int64Value; - } - if (avctx->max_b_frames) { - AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_B_PIC_DELTA_QP, ctx->b_frame_delta_qp); - AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_B_REFERENCE_ENABLE, !!ctx->b_frame_ref); - AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_REF_B_PIC_DELTA_QP, ctx->ref_b_frame_delta_qp); - } - // Keyframe Interval AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_IDR_PERIOD, avctx->gop_size); diff --git a/libavcodec/amfenc_hevc.c b/libavcodec/amfenc_hevc.c index 9b46946f1ec..dd232cc8ac8 100644 --- a/libavcodec/amfenc_hevc.c +++ b/libavcodec/amfenc_hevc.c @@ -21,15 +21,18 @@ #include "amfenc.h" #include "codec_internal.h" #include "internal.h" +#include #define OFFSET(x) offsetof(AmfContext, x) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - { "usage", "Set the encoding usage", OFFSET(usage), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCONDING }, AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCONDING, AMF_VIDEO_ENCODER_HEVC_USAGE_WEBCAM, VE, "usage" }, - { "transcoding", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCONDING }, 0, 0, VE, "usage" }, - { "ultralowlatency","", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_ULTRA_LOW_LATENCY }, 0, 0, VE, "usage" }, - { "lowlatency", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_LOW_LATENCY }, 0, 0, VE, "usage" }, - { "webcam", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_WEBCAM }, 0, 0, VE, "usage" }, + { "usage", "Set the encoding usage", OFFSET(usage), AV_OPT_TYPE_INT, {.i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCODING }, AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCODING, AMF_VIDEO_ENCODER_HEVC_USAGE_LOW_LATENCY_HIGH_QUALITY, VE, "usage" }, + { "transcoding", "Generic Transcoding", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCODING }, 0, 0, VE, "usage" }, + { "ultralowlatency", "ultra low latency trancoding", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_ULTRA_LOW_LATENCY }, 0, 0, VE, "usage" }, + { "lowlatency", "low latency trancoding", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_LOW_LATENCY }, 0, 0, VE, "usage" }, + { "webcam", "Webcam", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_WEBCAM }, 0, 0, VE, "usage" }, + { "high_quality", "high quality trancoding", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_HIGH_QUALITY }, 0, 0, VE, "usage" }, + { "lowlatency_high_quality","low latency yet high quality trancoding", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_LOW_LATENCY_HIGH_QUALITY }, 0, 0, VE, "usage" }, { "profile", "Set the profile (default main)", OFFSET(profile), AV_OPT_TYPE_INT,{ .i64 = AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN }, AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN, AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN, VE, "profile" }, { "main", "", 0, AV_OPT_TYPE_CONST,{ .i64 = AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN }, 0, 0, VE, "profile" }, @@ -59,19 +62,25 @@ static const AVOption options[] = { { "speed", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_SPEED }, 0, 0, VE, "quality" }, { "quality", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_QUALITY }, 0, 0, VE, "quality" }, - { "rc", "Set the rate control mode", OFFSET(rate_control_mode), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_UNKNOWN }, AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_UNKNOWN, AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR, VE, "rc" }, + { "rc", "Set the rate control mode", OFFSET(rate_control_mode), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_UNKNOWN }, AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_UNKNOWN, AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_HIGH_QUALITY_CBR, VE, "rc" }, { "cqp", "Constant Quantization Parameter", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CONSTANT_QP }, 0, 0, VE, "rc" }, { "cbr", "Constant Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR }, 0, 0, VE, "rc" }, { "vbr_peak", "Peak Contrained Variable Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR }, 0, 0, VE, "rc" }, { "vbr_latency", "Latency Constrained Variable Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR }, 0, 0, VE, "rc" }, + { "qvbr", "Quality Variable Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_QUALITY_VBR }, 0, 0, VE, "rc" }, + { "hqvbr", "High Quality Variable Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_HIGH_QUALITY_VBR }, 0, 0, VE, "rc" }, + { "hqcbr", "High Quality Constant Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_HIGH_QUALITY_CBR }, 0, 0, VE, "rc" }, + + { "qvbr_quality_level", "Sets the QVBR quality level", OFFSET(qvbr_quality_level), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 51, VE }, { "header_insertion_mode", "Set header insertion mode", OFFSET(header_insertion_mode), AV_OPT_TYPE_INT,{ .i64 = AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_NONE }, AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_NONE, AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_IDR_ALIGNED, VE, "hdrmode" }, { "none", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_NONE }, 0, 0, VE, "hdrmode" }, { "gop", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_GOP_ALIGNED }, 0, 0, VE, "hdrmode" }, { "idr", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_IDR_ALIGNED }, 0, 0, VE, "hdrmode" }, - { "gops_per_idr", "GOPs per IDR 0-no IDR will be inserted", OFFSET(gops_per_idr), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, INT_MAX, VE }, - { "preanalysis", "Enable preanalysis", OFFSET(preanalysis), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE}, + { "high_motion_quality_boost_enable", "Enable High motion quality boost mode", OFFSET(hw_high_motion_quality_boost), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + { "gops_per_idr", "GOPs per IDR 0-no IDR will be inserted", OFFSET(gops_per_idr), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, INT_MAX, VE }, + { "preencode", "Enable preencode", OFFSET(preencode), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE}, { "vbaq", "Enable VBAQ", OFFSET(enable_vbaq), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE}, { "enforce_hrd", "Enforce HRD", OFFSET(enforce_hrd), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE}, { "filler_data", "Filler Data Enable", OFFSET(filler_data), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE}, @@ -89,6 +98,52 @@ static const AVOption options[] = { { "aud", "Inserts AU Delimiter NAL unit", OFFSET(aud) ,AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE }, { "log_to_dbg", "Enable AMF logging to debug output", OFFSET(log_to_dbg), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE }, + + //Pre Analysis options + { "preanalysis", "Enable preanalysis", OFFSET(preanalysis), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + + { "pa_activity_type", "Set the type of activity analysis", OFFSET(pa_activity_type), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_ACTIVITY_YUV, VE, "activity_type" }, + { "y", "activity y", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_ACTIVITY_Y }, 0, 0, VE, "activity_type" }, + { "yuv", "activity yuv", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_ACTIVITY_YUV }, 0, 0, VE, "activity_type" }, + + { "pa_scene_change_detection_enable", "Enable scene change detection", OFFSET(pa_scene_change_detection), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + + { "pa_scene_change_detection_sensitivity", "Set the sensitivity of scene change detection", OFFSET(pa_scene_change_detection_sensitivity), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_HIGH, VE, "scene_change_sensitivity" }, + { "low", "low scene change dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_LOW }, 0, 0, VE, "scene_change_sensitivity" }, + { "medium", "medium scene change dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_MEDIUM }, 0, 0, VE, "scene_change_sensitivity" }, + { "high", "high scene change dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_HIGH }, 0, 0, VE, "scene_change_sensitivity" }, + + { "pa_static_scene_detection_enable", "Enable static scene detection", OFFSET(pa_static_scene_detection), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + + { "pa_static_scene_detection_sensitivity", "Set the sensitivity of static scene detection", OFFSET(pa_static_scene_detection_sensitivity), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_HIGH, VE , "static_scene_sensitivity" }, + { "low", "low static scene dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_LOW }, 0, 0, VE, "static_scene_sensitivity" }, + { "medium", "medium static scene dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_MEDIUM }, 0, 0, VE, "static_scene_sensitivity" }, + { "high", "high static scene dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_HIGH }, 0, 0, VE, "static_scene_sensitivity" }, + + { "pa_initial_qp_after_scene_change", "The QP value that is used immediately after a scene change", OFFSET(pa_initial_qp), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 51, VE }, + { "pa_max_qp_before_force_skip", "The QP threshold to allow a skip frame", OFFSET(pa_max_qp), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 51, VE }, + + { "pa_caq_strength", "Content Adaptive Quantization strength", OFFSET(pa_caq_strength), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_CAQ_STRENGTH_HIGH, VE , "caq_strength" }, + { "low", "low Content Adaptive Quantization strength", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_CAQ_STRENGTH_LOW }, 0, 0, VE, "caq_strength" }, + { "medium", "medium Content Adaptive Quantization strength", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_CAQ_STRENGTH_MEDIUM }, 0, 0, VE, "caq_strength" }, + { "high", "high Content Adaptive Quantization strength", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_CAQ_STRENGTH_HIGH }, 0, 0, VE, "caq_strength" }, + + { "pa_frame_sad_enable", "Enable Frame SAD algorithm", OFFSET(pa_frame_sad), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + { "pa_ltr_enable", "Enable long term reference frame management", OFFSET(pa_ltr), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + { "pa_lookahead_buffer_depth", "Sets the PA lookahead buffer size", OFFSET(pa_lookahead_buffer_depth), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, MAX_LOOKAHEAD_DEPTH, VE }, + + { "pa_paq_mode", "Sets the perceptual adaptive quantization mode", OFFSET(pa_paq_mode), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_PAQ_MODE_CAQ, VE , "paq_mode" }, + { "none", "no perceptual adaptive quantization", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_PAQ_MODE_NONE }, 0, 0, VE, "paq_mode" }, + { "caq", "caq perceptual adaptive quantization", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_PAQ_MODE_CAQ }, 0, 0, VE, "paq_mode" }, + + { "pa_taq_mode", "Sets the temporal adaptive quantization mode", OFFSET(pa_taq_mode), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_TAQ_MODE_2, VE , "taq_mode" }, + { "none", "no temporal adaptive quantization", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_TAQ_MODE_NONE }, 0, 0, VE, "taq_mode" }, + { "1", "temporal adaptive quantization mode 1", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_TAQ_MODE_1 }, 0, 0, VE, "taq_mode" }, + { "2", "temporal adaptive quantization mode 2", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_TAQ_MODE_2 }, 0, 0, VE, "taq_mode" }, + + { "pa_high_motion_quality_boost_mode", "Sets the PA high motion quality boost mode", OFFSET(pa_high_motion_quality_boost_mode), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE_AUTO, VE , "high_motion_quality_boost_mode" }, + { "none", "no high motion quality boost", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE_NONE }, 0, 0, VE, "high_motion_quality_boost_mode" }, + { "auto", "auto high motion quality boost", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE_AUTO }, 0, 0, VE, "high_motion_quality_boost_mode" }, { NULL } }; @@ -109,7 +164,13 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { framerate = AMFConstructRate(avctx->framerate.num, avctx->framerate.den); } else { - framerate = AMFConstructRate(avctx->time_base.den, avctx->time_base.num * avctx->ticks_per_frame); +FF_DISABLE_DEPRECATION_WARNINGS + framerate = AMFConstructRate(avctx->time_base.den, avctx->time_base.num +#if FF_API_TICKS_PER_FRAME + * avctx->ticks_per_frame +#endif + ); +FF_ENABLE_DEPRECATION_WARNINGS } if ((ret = ff_amf_encode_init(avctx)) < 0) @@ -180,6 +241,25 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) } } + // Pre-Pass, Pre-Analysis, Two-Pass + if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CONSTANT_QP) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_PREENCODE_ENABLE, 0); + if (ctx->preencode) + av_log(ctx, AV_LOG_WARNING, "Preencode is not supported by cqp Rate Control Method, automatically disabled\n"); + } + else { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_PREENCODE_ENABLE, ctx->preencode); + } + + if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_QUALITY_VBR) { + if (ctx->qvbr_quality_level != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_QVBR_QUALITY_LEVEL, ctx->qvbr_quality_level); + } + } + + if (ctx->hw_high_motion_quality_boost != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_HIGH_MOTION_QUALITY_BOOST_ENABLE, ((ctx->hw_high_motion_quality_boost == 0) ? false : true)); + } AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD, ctx->rate_control_mode); if (avctx->rc_buffer_size) { @@ -192,8 +272,6 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_INITIAL_VBV_BUFFER_FULLNESS, amf_buffer_fullness); } } - // Pre-Pass, Pre-Analysis, Two-Pass - AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_PREANALYSIS_ENABLE, ctx->preanalysis); if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CONSTANT_QP) { AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_ENABLE_VBAQ, false); @@ -222,6 +300,57 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) av_log(ctx, AV_LOG_WARNING, "rate control mode is PEAK_CONSTRAINED_VBR but rc_max_rate is not set\n"); } + if (ctx->preanalysis != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_PRE_ANALYSIS_ENABLE, !!((ctx->preanalysis == 0) ? false : true)); + } + + res = ctx->encoder->pVtbl->GetProperty(ctx->encoder, AMF_VIDEO_ENCODER_HEVC_PRE_ANALYSIS_ENABLE, &var); + if ((int)var.int64Value) + { + if (ctx->pa_activity_type != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_ACTIVITY_TYPE, ctx->pa_activity_type); + } + if (ctx->pa_scene_change_detection != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_SCENE_CHANGE_DETECTION_ENABLE, ((ctx->pa_scene_change_detection == 0) ? false : true)); + } + if (ctx->pa_scene_change_detection_sensitivity != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY, ctx->pa_scene_change_detection_sensitivity); + } + if (ctx->pa_static_scene_detection != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_STATIC_SCENE_DETECTION_ENABLE, ((ctx->pa_static_scene_detection == 0) ? false : true)); + } + if (ctx->pa_static_scene_detection_sensitivity != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY, ctx->pa_static_scene_detection_sensitivity); + } + if (ctx->pa_initial_qp != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_INITIAL_QP_AFTER_SCENE_CHANGE, ctx->pa_initial_qp); + } + if (ctx->pa_max_qp != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_MAX_QP_BEFORE_FORCE_SKIP, ctx->pa_max_qp); + } + if (ctx->pa_caq_strength != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_CAQ_STRENGTH, ctx->pa_caq_strength); + } + if (ctx->pa_frame_sad != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_FRAME_SAD_ENABLE, ((ctx->pa_frame_sad == 0) ? false : true)); + } + if (ctx->pa_paq_mode != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_PAQ_MODE, ctx->pa_paq_mode); + } + if (ctx->pa_taq_mode != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_TAQ_MODE, ctx->pa_taq_mode); + } + if (ctx->pa_ltr != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_LTR_ENABLE, ((ctx->pa_ltr == 0) ? false : true)); + } + if (ctx->pa_lookahead_buffer_depth != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_LOOKAHEAD_BUFFER_DEPTH, ctx->pa_lookahead_buffer_depth); + } + if (ctx->pa_high_motion_quality_boost_mode != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE, ctx->pa_high_motion_quality_boost_mode); + } + } + // init encoder res = ctx->encoder->pVtbl->Init(ctx->encoder, ctx->format, avctx->width, avctx->height); AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_BUG, "encoder->Init() failed with error %d\n", res); @@ -262,7 +391,6 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) } AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_SKIP_FRAME_ENABLE, ctx->skip_frame); - // fill extradata res = AMFVariantInit(&var); AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_BUG, "AMFVariantInit() failed with error %d\n", res); diff --git a/libavcodec/ansi.c b/libavcodec/ansi.c index e15c1bb0977..d8d32bafbdc 100644 --- a/libavcodec/ansi.c +++ b/libavcodec/ansi.c @@ -262,7 +262,11 @@ static int execute_code(AVCodecContext * avctx, int c) AV_GET_BUFFER_FLAG_REF)) < 0) return ret; s->frame->pict_type = AV_PICTURE_TYPE_I; +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS s->frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif set_palette((uint32_t *)s->frame->data[1]); erase_screen(avctx); } else if (c == 'l') { @@ -326,7 +330,7 @@ static int execute_code(AVCodecContext * avctx, int c) s->bg = index < 16 ? ansi_to_cga[index] : index; i += 2; } else if (m == 49) { - s->fg = ansi_to_cga[DEFAULT_BG_COLOR]; + s->bg = ansi_to_cga[DEFAULT_BG_COLOR]; } else { avpriv_request_sample(avctx, "Unsupported rendition parameter"); } @@ -364,14 +368,18 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) return ret; - if (!avctx->frame_number) { + if (!avctx->frame_num) { for (i=0; iheight; i++) memset(s->frame->data[0]+ i*s->frame->linesize[0], 0, avctx->width); memset(s->frame->data[1], 0, AVPALETTE_SIZE); } s->frame->pict_type = AV_PICTURE_TYPE_I; +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS s->frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif set_palette((uint32_t *)s->frame->data[1]); if (!s->first_frame) { erase_screen(avctx); diff --git a/libavcodec/apac.c b/libavcodec/apac.c index 3408f75292d..b6cb6c669ec 100644 --- a/libavcodec/apac.c +++ b/libavcodec/apac.c @@ -269,8 +269,10 @@ const FFCodec ff_apac_decoder = { FF_CODEC_DECODE_CB(apac_decode), .close = apac_close, .p.capabilities = AV_CODEC_CAP_DELAY | - AV_CODEC_CAP_DR1 | - AV_CODEC_CAP_SUBFRAMES, +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_U8P, AV_SAMPLE_FMT_S16P, diff --git a/libavcodec/apedec.c b/libavcodec/apedec.c index c08d13d6c23..613c76df0b8 100644 --- a/libavcodec/apedec.c +++ b/libavcodec/apedec.c @@ -944,7 +944,7 @@ static void long_filter_high_3800(int32_t *buffer, int order, int shift, int len { int i, j; int32_t dotprod, sign; - int32_t coeffs[256], delay[256]; + int32_t coeffs[256], delay[256+256], *delayp = delay; if (order >= length) return; @@ -955,14 +955,28 @@ static void long_filter_high_3800(int32_t *buffer, int order, int shift, int len for (i = order; i < length; i++) { dotprod = 0; sign = APESIGN(buffer[i]); - for (j = 0; j < order; j++) { - dotprod += delay[j] * (unsigned)coeffs[j]; - coeffs[j] += ((delay[j] >> 31) | 1) * sign; + if (sign == 1) { + for (j = 0; j < order; j++) { + dotprod += delayp[j] * (unsigned)coeffs[j]; + coeffs[j] += (delayp[j] >> 31) | 1; + } + } else if (sign == -1) { + for (j = 0; j < order; j++) { + dotprod += delayp[j] * (unsigned)coeffs[j]; + coeffs[j] -= (delayp[j] >> 31) | 1; + } + } else { + for (j = 0; j < order; j++) { + dotprod += delayp[j] * (unsigned)coeffs[j]; + } } buffer[i] -= (unsigned)(dotprod >> shift); - for (j = 0; j < order - 1; j++) - delay[j] = delay[j + 1]; - delay[order - 1] = buffer[i]; + delayp ++; + delayp[order - 1] = buffer[i]; + if (delayp - delay == 256) { + memcpy(delay, delayp, sizeof(*delay)*256); + delayp = delay; + } } } @@ -1666,7 +1680,11 @@ const FFCodec ff_ape_decoder = { .init = ape_decode_init, .close = ape_decode_close, FF_CODEC_DECODE_CB(ape_decode_frame), - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DELAY | + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DELAY | AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .flush = ape_flush, diff --git a/libavcodec/aptxenc.c b/libavcodec/aptxenc.c index 5fc0378f5dc..6deebaf2cbd 100644 --- a/libavcodec/aptxenc.c +++ b/libavcodec/aptxenc.c @@ -271,7 +271,7 @@ const FFCodec ff_aptx_encoder = { CODEC_LONG_NAME("aptX (Audio Processing Technology for Bluetooth)"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_APTX, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(AptXEncContext), .init = aptx_encode_init, FF_CODEC_ENCODE_CB(aptx_encode_frame), @@ -290,7 +290,7 @@ const FFCodec ff_aptx_hd_encoder = { CODEC_LONG_NAME("aptX HD (Audio Processing Technology for Bluetooth)"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_APTX_HD, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(AptXEncContext), .init = aptx_encode_init, FF_CODEC_ENCODE_CB(aptx_encode_frame), diff --git a/libavcodec/arbc.c b/libavcodec/arbc.c index 343c56695ea..1b349f4dd61 100644 --- a/libavcodec/arbc.c +++ b/libavcodec/arbc.c @@ -171,7 +171,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, return ret; frame->pict_type = prev_pixels <= 0 ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; - frame->key_frame = prev_pixels <= 0; + if (prev_pixels <= 0) + frame->flags |= AV_FRAME_FLAG_KEY; + else + frame->flags &= ~AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; diff --git a/libavcodec/argo.c b/libavcodec/argo.c index 9bedb1394d0..589feed4101 100644 --- a/libavcodec/argo.c +++ b/libavcodec/argo.c @@ -666,7 +666,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, return ret; frame->pict_type = s->key ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; - frame->key_frame = s->key; + if (s->key) + frame->flags |= AV_FRAME_FLAG_KEY; + else + frame->flags &= ~AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; diff --git a/libavcodec/arm/hevcdsp_idct_neon.S b/libavcodec/arm/hevcdsp_idct_neon.S index 75795e6a6ae..66ed1c67852 100644 --- a/libavcodec/arm/hevcdsp_idct_neon.S +++ b/libavcodec/arm/hevcdsp_idct_neon.S @@ -876,48 +876,28 @@ function func_tr_32x4_\name movrel r9, trans + 32 vld1.s16 {q0}, [r9, :128]! vld1.s16 {q1}, [r9, :128] - - bl tr_block1 - add r4, sp, #2048 - vld1.s16 {q14-q15}, [r4, :128]! - butterfly32 q14, q10, q15, q11 - scale32 d22, d23, d20, d21, q1, q14, q10, q15, \shift - - vld1.s16 {q14-q15}, [r4, :128]! - butterfly32 q14, q12, q15, q13 - scale32 d2, d3, d28, d29, q1, q14, q12, q15, \shift - - transpose8_4x4 d22, d20, d2, d28 - transpose8_4x4 d29, d3, d21, d23 - mov r1, r11 mov r2, #64 mov r8, #-64 - add r3, r11, #(56 + 3 * 64) - store16 d22, d23, d20, d21, d2, d3, d28, d29, r8 - @ reload multiplication coefficiens to q1 - vld1.s16 {q1}, [r9, :128] + bl tr_block1 + mov r1, r11 + add r3, r11, #(56 + 3 * 64) + scale_store \shift bl tr_block2 add r1, r11, #8 add r3, r11, #(48 + 3 * 64) - mov r2, #64 - mov r8, #-64 scale_store \shift bl tr_block3 add r1, r11, #16 add r3, r11, #(40 + 3 * 64) - mov r2, #64 - mov r8, #-64 scale_store \shift bl tr_block4 add r1, r11, #24 add r3, r11, #(32 + 3 * 64) - mov r2, #64 - mov r8, #-64 scale_store \shift bx r10 diff --git a/libavcodec/arm/hevcdsp_init_neon.c b/libavcodec/arm/hevcdsp_init_neon.c index 8094e6c62e2..1f26fc64541 100644 --- a/libavcodec/arm/hevcdsp_init_neon.c +++ b/libavcodec/arm/hevcdsp_init_neon.c @@ -119,7 +119,7 @@ QPEL_FUNC(ff_hevc_put_qpel_h3v3_neon_8); #define QPEL_FUNC_UW_PIX(name) \ void name(uint8_t *dst, ptrdiff_t dststride, const uint8_t *_src, ptrdiff_t _srcstride, \ - int height, intptr_t mx, intptr_t my, int width); + int height, intptr_t mx, intptr_t my, int width) QPEL_FUNC_UW_PIX(ff_hevc_put_qpel_uw_pixels_w4_neon_8); QPEL_FUNC_UW_PIX(ff_hevc_put_qpel_uw_pixels_w8_neon_8); QPEL_FUNC_UW_PIX(ff_hevc_put_qpel_uw_pixels_w16_neon_8); @@ -131,7 +131,7 @@ QPEL_FUNC_UW_PIX(ff_hevc_put_qpel_uw_pixels_w64_neon_8); #define QPEL_FUNC_UW(name) \ void name(uint8_t *dst, ptrdiff_t dststride, const uint8_t *_src, ptrdiff_t _srcstride, \ - int width, int height, const int16_t* src2, ptrdiff_t src2stride); + int width, int height, const int16_t* src2, ptrdiff_t src2stride) QPEL_FUNC_UW(ff_hevc_put_qpel_uw_pixels_neon_8); QPEL_FUNC_UW(ff_hevc_put_qpel_uw_v1_neon_8); QPEL_FUNC_UW(ff_hevc_put_qpel_uw_v2_neon_8); diff --git a/libavcodec/assenc.c b/libavcodec/assenc.c index db6fd25dd7c..e548b9fd3bd 100644 --- a/libavcodec/assenc.c +++ b/libavcodec/assenc.c @@ -45,27 +45,26 @@ static int ass_encode_frame(AVCodecContext *avctx, unsigned char *buf, int bufsize, const AVSubtitle *sub) { - int i, len, total_len = 0; + int len; - for (i=0; inum_rects; i++) { - const char *ass = sub->rects[i]->ass; - - if (sub->rects[i]->type != SUBTITLE_ASS) { - av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); - return AVERROR(EINVAL); - } + if (sub->num_rects != 1) { + av_log(avctx, AV_LOG_ERROR, "Only one rect per AVSubtitle is supported in ASS.\n"); + return AVERROR_INVALIDDATA; + } - len = av_strlcpy(buf+total_len, ass, bufsize-total_len); + if (sub->rects[0]->type != SUBTITLE_ASS) { + av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); + return AVERROR(EINVAL); + } - if (len > bufsize-total_len-1) { - av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n"); - return AVERROR_BUFFER_TOO_SMALL; - } + len = av_strlcpy(buf, sub->rects[0]->ass, bufsize); - total_len += len; + if (len > bufsize - 1) { + av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n"); + return AVERROR_BUFFER_TOO_SMALL; } - return total_len; + return len; } #if CONFIG_SSA_ENCODER diff --git a/libavcodec/asvdec.c b/libavcodec/asvdec.c index 699aab9f8f9..62295b2dbb4 100644 --- a/libavcodec/asvdec.c +++ b/libavcodec/asvdec.c @@ -245,7 +245,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; if (avctx->codec_id == AV_CODEC_ID_ASV1) { av_fast_padded_malloc(&a->bitstream_buffer, &a->bitstream_buffer_size, diff --git a/libavcodec/asvenc.c b/libavcodec/asvenc.c index 9da7cbb986a..50da46738cf 100644 --- a/libavcodec/asvenc.c +++ b/libavcodec/asvenc.c @@ -33,7 +33,6 @@ #include "asv.h" #include "avcodec.h" #include "codec_internal.h" -#include "dct.h" #include "encode.h" #include "fdctdsp.h" #include "mpeg12data.h" @@ -362,7 +361,7 @@ const FFCodec ff_asv1_encoder = { CODEC_LONG_NAME("ASUS V1"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_ASV1, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(ASVEncContext), .init = encode_init, FF_CODEC_ENCODE_CB(encode_frame), @@ -377,7 +376,7 @@ const FFCodec ff_asv2_encoder = { CODEC_LONG_NAME("ASUS V2"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_ASV2, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(ASVEncContext), .init = encode_init, FF_CODEC_ENCODE_CB(encode_frame), diff --git a/libavcodec/atrac3.c b/libavcodec/atrac3.c index 7415da590bc..5851ee027ca 100644 --- a/libavcodec/atrac3.c +++ b/libavcodec/atrac3.c @@ -1026,7 +1026,11 @@ const FFCodec ff_atrac3_decoder = { .init = atrac3_decode_init, .close = atrac3_decode_close, FF_CODEC_DECODE_CB(atrac3_decode_frame), - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, @@ -1041,7 +1045,11 @@ const FFCodec ff_atrac3al_decoder = { .init = atrac3_decode_init, .close = atrac3_decode_close, FF_CODEC_DECODE_CB(atrac3al_decode_frame), - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, diff --git a/libavcodec/atrac3plus.c b/libavcodec/atrac3plus.c index a0836f11784..5661654ce31 100644 --- a/libavcodec/atrac3plus.c +++ b/libavcodec/atrac3plus.c @@ -1391,9 +1391,9 @@ static int decode_band_numwavs(GetBitContext *gb, Atrac3pChanUnitCtx *ctx, if (band_has_tones[sb]) { if (ctx->waves_info->tones_index + dst[sb].num_wavs > 48) { av_log(avctx, AV_LOG_ERROR, - "Too many tones: %d (max. 48), frame: %d!\n", + "Too many tones: %d (max. 48), frame: %"PRId64"!\n", ctx->waves_info->tones_index + dst[sb].num_wavs, - avctx->frame_number); + avctx->frame_num); return AVERROR_INVALIDDATA; } dst[sb].start_index = ctx->waves_info->tones_index; diff --git a/libavcodec/atrac9dec.c b/libavcodec/atrac9dec.c index 60962b16762..8a6940455d4 100644 --- a/libavcodec/atrac9dec.c +++ b/libavcodec/atrac9dec.c @@ -1003,5 +1003,9 @@ const FFCodec ff_atrac9_decoder = { FF_CODEC_DECODE_CB(atrac9_decode_frame), .flush = atrac9_decode_flush, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_CHANNEL_CONF, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1 | AV_CODEC_CAP_CHANNEL_CONF, }; diff --git a/libavcodec/audiodsp.c b/libavcodec/audiodsp.c index eba6e809fd6..c5427d3535d 100644 --- a/libavcodec/audiodsp.c +++ b/libavcodec/audiodsp.c @@ -22,36 +22,36 @@ #include "libavutil/common.h" #include "audiodsp.h" -static inline uint32_t clipf_c_one(uint32_t a, uint32_t mini, - uint32_t maxi, uint32_t maxisign) +static inline float clipf_c_one(float a, uint32_t mini, + uint32_t maxi, uint32_t maxisign) { - if (a > mini) - return mini; - else if ((a ^ (1U << 31)) > maxisign) - return maxi; + uint32_t ai = av_float2int(a); + + if (ai > mini) + return av_int2float(mini); + else if ((ai ^ (1U << 31)) > maxisign) + return av_int2float(maxi); else return a; } static void vector_clipf_c_opposite_sign(float *dst, const float *src, - float *min, float *max, int len) + float min, float max, int len) { int i; - uint32_t mini = *(uint32_t *) min; - uint32_t maxi = *(uint32_t *) max; + uint32_t mini = av_float2int(min); + uint32_t maxi = av_float2int(max); uint32_t maxisign = maxi ^ (1U << 31); - uint32_t *dsti = (uint32_t *) dst; - const uint32_t *srci = (const uint32_t *) src; for (i = 0; i < len; i += 8) { - dsti[i + 0] = clipf_c_one(srci[i + 0], mini, maxi, maxisign); - dsti[i + 1] = clipf_c_one(srci[i + 1], mini, maxi, maxisign); - dsti[i + 2] = clipf_c_one(srci[i + 2], mini, maxi, maxisign); - dsti[i + 3] = clipf_c_one(srci[i + 3], mini, maxi, maxisign); - dsti[i + 4] = clipf_c_one(srci[i + 4], mini, maxi, maxisign); - dsti[i + 5] = clipf_c_one(srci[i + 5], mini, maxi, maxisign); - dsti[i + 6] = clipf_c_one(srci[i + 6], mini, maxi, maxisign); - dsti[i + 7] = clipf_c_one(srci[i + 7], mini, maxi, maxisign); + dst[i + 0] = clipf_c_one(src[i + 0], mini, maxi, maxisign); + dst[i + 1] = clipf_c_one(src[i + 1], mini, maxi, maxisign); + dst[i + 2] = clipf_c_one(src[i + 2], mini, maxi, maxisign); + dst[i + 3] = clipf_c_one(src[i + 3], mini, maxi, maxisign); + dst[i + 4] = clipf_c_one(src[i + 4], mini, maxi, maxisign); + dst[i + 5] = clipf_c_one(src[i + 5], mini, maxi, maxisign); + dst[i + 6] = clipf_c_one(src[i + 6], mini, maxi, maxisign); + dst[i + 7] = clipf_c_one(src[i + 7], mini, maxi, maxisign); } } @@ -61,7 +61,7 @@ static void vector_clipf_c(float *dst, const float *src, int len, int i; if (min < 0 && max > 0) { - vector_clipf_c_opposite_sign(dst, src, &min, &max, len); + vector_clipf_c_opposite_sign(dst, src, min, max, len); } else { for (i = 0; i < len; i += 8) { dst[i] = av_clipf(src[i], min, max); diff --git a/libavcodec/audiotoolboxdec.c b/libavcodec/audiotoolboxdec.c index 82babe3d317..5456f916cf4 100644 --- a/libavcodec/audiotoolboxdec.c +++ b/libavcodec/audiotoolboxdec.c @@ -71,10 +71,12 @@ static UInt32 ffat_get_format_id(enum AVCodecID codec, int profile) return kAudioFormatAMR; case AV_CODEC_ID_EAC3: return kAudioFormatEnhancedAC3; +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 case AV_CODEC_ID_GSM_MS: return kAudioFormatMicrosoftGSM; case AV_CODEC_ID_ILBC: return kAudioFormatiLBC; +#endif case AV_CODEC_ID_MP1: return kAudioFormatMPEGLayer1; case AV_CODEC_ID_MP2: diff --git a/libavcodec/audiotoolboxenc.c b/libavcodec/audiotoolboxenc.c index 1ccfda4d207..46aca4c7ff9 100644 --- a/libavcodec/audiotoolboxenc.c +++ b/libavcodec/audiotoolboxenc.c @@ -69,15 +69,19 @@ static UInt32 ffat_get_format_id(enum AVCodecID codec, int profile) return kAudioFormatMPEG4AAC_HE_V2; case FF_PROFILE_AAC_LD: return kAudioFormatMPEG4AAC_LD; +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 case FF_PROFILE_AAC_ELD: return kAudioFormatMPEG4AAC_ELD; +#endif } case AV_CODEC_ID_ADPCM_IMA_QT: return kAudioFormatAppleIMA4; case AV_CODEC_ID_ALAC: return kAudioFormatAppleLossless; +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 case AV_CODEC_ID_ILBC: return kAudioFormatiLBC; +#endif case AV_CODEC_ID_PCM_ALAW: return kAudioFormatALaw; case AV_CODEC_ID_PCM_MULAW: diff --git a/libavcodec/av1_parse.c b/libavcodec/av1_parse.c index 59ea0bc6e75..061636815fd 100644 --- a/libavcodec/av1_parse.c +++ b/libavcodec/av1_parse.c @@ -56,7 +56,7 @@ int ff_av1_extract_obu(AV1OBU *obu, const uint8_t *buf, int length, void *logctx int ff_av1_packet_split(AV1Packet *pkt, const uint8_t *buf, int length, void *logctx) { GetByteContext bc; - int ret, consumed; + int consumed; bytestream2_init(&bc, buf, length); pkt->nb_obus = 0; @@ -88,16 +88,14 @@ int ff_av1_packet_split(AV1Packet *pkt, const uint8_t *buf, int length, void *lo obu->size_bits = get_obu_bit_length(obu->data, obu->size, obu->type); - if (obu->size_bits < 0 || (!obu->size_bits && obu->type != AV1_OBU_TEMPORAL_DELIMITER)) { + if (obu->size_bits < 0 || + (obu->size_bits == 0 && (obu->type != AV1_OBU_TEMPORAL_DELIMITER && + obu->type != AV1_OBU_PADDING))) { av_log(logctx, AV_LOG_ERROR, "Invalid OBU of type %d, skipping.\n", obu->type); continue; } pkt->nb_obus++; - - ret = init_get_bits(&obu->gb, obu->data, obu->size_bits); - if (ret < 0) - return ret; } return 0; @@ -108,3 +106,17 @@ void ff_av1_packet_uninit(AV1Packet *pkt) av_freep(&pkt->obus); pkt->obus_allocated = pkt->obus_allocated_size = 0; } + +AVRational ff_av1_framerate(int64_t ticks_per_frame, int64_t units_per_tick, + int64_t time_scale) +{ + AVRational fr; + + if (ticks_per_frame && units_per_tick && time_scale && + ticks_per_frame < INT64_MAX / units_per_tick && + av_reduce(&fr.den, &fr.num, units_per_tick * ticks_per_frame, + time_scale, INT_MAX)) + return fr; + + return (AVRational){ 0, 1 }; +} diff --git a/libavcodec/av1_parse.h b/libavcodec/av1_parse.h index f4a5d2830ec..d0abd7ac7c3 100644 --- a/libavcodec/av1_parse.h +++ b/libavcodec/av1_parse.h @@ -49,9 +49,6 @@ typedef struct AV1OBU { int raw_size; const uint8_t *raw_data; - /** GetBitContext initialized to the start of the payload */ - GetBitContext gb; - int type; int temporal_id; @@ -181,4 +178,7 @@ static inline int get_obu_bit_length(const uint8_t *buf, int size, int type) return size; } +AVRational ff_av1_framerate(int64_t ticks_per_frame, int64_t units_per_tick, + int64_t time_scale); + #endif /* AVCODEC_AV1_PARSE_H */ diff --git a/libavcodec/av1_parser.c b/libavcodec/av1_parser.c index e57e382757e..2b79493bf8f 100644 --- a/libavcodec/av1_parser.c +++ b/libavcodec/av1_parser.c @@ -21,6 +21,8 @@ */ #include "libavutil/avassert.h" + +#include "av1_parse.h" #include "cbs.h" #include "cbs_av1.h" #include "parser.h" @@ -162,14 +164,10 @@ static int av1_parser_parse(AVCodecParserContext *ctx, avctx->color_trc = (enum AVColorTransferCharacteristic) color->transfer_characteristics; avctx->color_range = color->color_range ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; - if (seq->timing_info_present_flag) { - const AV1RawTimingInfo *timing = &seq->timing_info; - av_reduce(&avctx->framerate.den, &avctx->framerate.num, - timing->num_units_in_display_tick, timing->time_scale, INT_MAX); - } - - if (avctx->framerate.num) - avctx->time_base = av_inv_q(av_mul_q(avctx->framerate, (AVRational){avctx->ticks_per_frame, 1})); + 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); end: ff_cbs_fragment_reset(td); diff --git a/libavcodec/av1dec.c b/libavcodec/av1dec.c index d83c902f1f8..03283ab0643 100644 --- a/libavcodec/av1dec.c +++ b/libavcodec/av1dec.c @@ -20,14 +20,21 @@ #include "config_components.h" +#include "libavutil/hdr_dynamic_metadata.h" #include "libavutil/film_grain_params.h" +#include "libavutil/mastering_display_metadata.h" #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avcodec.h" +#include "av1_parse.h" +#include "decode.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 "hwconfig.h" #include "profiles.h" #include "thread.h" @@ -264,7 +271,9 @@ static void skip_mode_params(AV1DecContext *s) int second_forward_idx, second_forward_hint; int ref_hint, dist, i; - if (!header->skip_mode_present) + 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; @@ -442,7 +451,8 @@ static int get_pixel_format(AVCodecContext *avctx) CONFIG_AV1_D3D11VA_HWACCEL * 2 + \ CONFIG_AV1_NVDEC_HWACCEL + \ CONFIG_AV1_VAAPI_HWACCEL + \ - CONFIG_AV1_VDPAU_HWACCEL) + CONFIG_AV1_VDPAU_HWACCEL + \ + CONFIG_AV1_VULKAN_HWACCEL) enum AVPixelFormat pix_fmts[HWACCEL_MAX + 2], *fmtp = pix_fmts; if (seq->seq_profile == 2 && seq->color_config.high_bitdepth) @@ -522,6 +532,9 @@ static int get_pixel_format(AVCodecContext *avctx) #endif #if CONFIG_AV1_VDPAU_HWACCEL *fmtp++ = AV_PIX_FMT_VDPAU; +#endif +#if CONFIG_AV1_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; #endif break; case AV_PIX_FMT_YUV420P10: @@ -540,6 +553,44 @@ static int get_pixel_format(AVCodecContext *avctx) #endif #if CONFIG_AV1_VDPAU_HWACCEL *fmtp++ = AV_PIX_FMT_VDPAU; +#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: @@ -645,6 +696,7 @@ static int av1_frame_ref(AVCodecContext *avctx, AV1Frame *dst, const AV1Frame *s 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(avctx, &s->ref[i]); @@ -655,8 +707,14 @@ static av_cold int av1_decode_free(AVCodecContext *avctx) av_buffer_unref(&s->seq_ref); av_buffer_unref(&s->header_ref); + av_buffer_unref(&s->cll_ref); + av_buffer_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); @@ -699,15 +757,10 @@ static int set_context_with_sequence(AVCodecContext *avctx, } avctx->sample_aspect_ratio = (AVRational) { 1, 1 }; - if (seq->timing_info.num_units_in_display_tick && - seq->timing_info.time_scale) { - av_reduce(&avctx->framerate.den, &avctx->framerate.num, - seq->timing_info.num_units_in_display_tick, - seq->timing_info.time_scale, - INT_MAX); - if (seq->timing_info.equal_picture_interval) - avctx->ticks_per_frame = seq->timing_info.num_ticks_per_picture_minus_1 + 1; - } + 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); return 0; } @@ -742,6 +795,16 @@ static int update_context_with_frame_header(AVCodecContext *avctx, 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; @@ -749,6 +812,7 @@ static av_cold int av1_decode_init(AVCodecContext *avctx) int ret; s->avctx = avctx; + s->pkt = avctx->internal->in_pkt; s->pix_fmt = AV_PIX_FMT_NONE; for (int i = 0; i < FF_ARRAY_ELEMS(s->ref); i++) { @@ -771,6 +835,14 @@ static av_cold int av1_decode_init(AVCodecContext *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) { @@ -779,7 +851,7 @@ static av_cold int av1_decode_init(AVCodecContext *avctx) avctx); if (ret < 0) { av_log(avctx, AV_LOG_WARNING, "Failed to read extradata.\n"); - return ret; + goto end; } seq = ((CodedBitstreamAV1Context *)(s->cbc->priv_data))->sequence_header; @@ -818,7 +890,10 @@ static int av1_frame_alloc(AVCodecContext *avctx, AV1Frame *f) goto fail; frame = f->f; - frame->key_frame = header->frame_type == AV1_FRAME_KEY; + 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: @@ -833,18 +908,11 @@ static int av1_frame_alloc(AVCodecContext *avctx, AV1Frame *f) break; } - if (avctx->hwaccel) { - const AVHWAccel *hwaccel = avctx->hwaccel; - if (hwaccel->frame_priv_data_size) { - f->hwaccel_priv_buf = - av_buffer_allocz(hwaccel->frame_priv_data_size); - if (!f->hwaccel_priv_buf) { - ret = AVERROR(ENOMEM); - goto fail; - } - f->hwaccel_picture_private = f->hwaccel_priv_buf->data; - } - } + ret = ff_hwaccel_frame_priv_alloc(avctx, &f->hwaccel_picture_private, + &f->hwaccel_priv_buf); + if (ret < 0) + goto fail; + return 0; fail: @@ -852,6 +920,108 @@ static int av1_frame_alloc(AVCodecContext *avctx, AV1Frame *f) return ret; } +static int export_itut_t35(AVCodecContext *avctx, AVFrame *frame, + const AV1RawMetadataITUTT35 *itut_t35) +{ + GetByteContext gb; + int ret, provider_code; + + bytestream2_init(&gb, itut_t35->payload, itut_t35->payload_size); + + provider_code = bytestream2_get_be16(&gb); + switch (provider_code) { + case 0x31: { // atsc_provider_code + uint32_t user_identifier = bytestream2_get_be32(&gb); + switch (user_identifier) { + case MKBETAG('G', 'A', '9', '4'): { // closed captions + AVBufferRef *buf = NULL; + + ret = ff_parse_a53_cc(&buf, gb.buffer, bytestream2_get_bytes_left(&gb)); + if (ret < 0) + return ret; + if (!ret) + break; + + if (!av_frame_new_side_data_from_buf(frame, AV_FRAME_DATA_A53_CC, buf)) + av_buffer_unref(&buf); + + avctx->properties |= FF_CODEC_PROPERTY_CLOSED_CAPTIONS; + break; + } + default: // ignore unsupported identifiers + break; + } + break; + } + case 0x3C: { // smpte_provider_code + AVDynamicHDRPlus *hdrplus; + int provider_oriented_code = bytestream2_get_be16(&gb); + int application_identifier = bytestream2_get_byte(&gb); + + if (itut_t35->itu_t_t35_country_code != 0xB5 || + provider_oriented_code != 1 || application_identifier != 4) + break; + + hdrplus = av_dynamic_hdr_plus_create_side_data(frame); + if (!hdrplus) + return AVERROR(ENOMEM); + + ret = av_dynamic_hdr_plus_from_t35(hdrplus, gb.buffer, + bytestream2_get_bytes_left(&gb)); + if (ret < 0) + return ret; + break; + } + default: // ignore unsupported provider codes + break; + } + + 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 = av_mastering_display_metadata_create_side_data(frame); + if (!mastering) + return AVERROR(ENOMEM); + + 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 = av_content_light_metadata_create_side_data(frame); + if (!light) + return AVERROR(ENOMEM); + + 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; @@ -912,11 +1082,11 @@ static int export_film_grain(AVCodecContext *avctx, AVFrame *frame) return 0; } -static int set_output_frame(AVCodecContext *avctx, AVFrame *frame, - const AVPacket *pkt, int *got_frame) +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 @@ -928,6 +1098,12 @@ static int set_output_frame(AVCodecContext *avctx, AVFrame *frame, 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) { @@ -938,9 +1114,14 @@ static int set_output_frame(AVCodecContext *avctx, AVFrame *frame, frame->pts = pkt->pts; frame->pkt_dts = pkt->dts; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS frame->pkt_size = pkt->size; + frame->pkt_pos = pkt->pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif - *got_frame = 1; + av_packet_unref(pkt); return 0; } @@ -1006,22 +1187,13 @@ static int get_current_frame(AVCodecContext *avctx) return ret; } -static int av1_decode_frame(AVCodecContext *avctx, AVFrame *frame, - int *got_frame, AVPacket *pkt) +static int av1_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame) { AV1DecContext *s = avctx->priv_data; AV1RawTileGroup *raw_tile_group = NULL; - int ret; + int i = 0, ret; - ret = ff_cbs_read_packet(s->cbc, &s->current_obu, pkt); - if (ret < 0) { - av_log(avctx, AV_LOG_ERROR, "Failed to read packet.\n"); - goto end; - } - av_log(avctx, AV_LOG_DEBUG, "Total obu for this frame:%d.\n", - s->current_obu.nb_units); - - for (int i = 0; i < s->current_obu.nb_units; i++) { + 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; @@ -1062,9 +1234,9 @@ static int av1_decode_frame(AVCodecContext *avctx, AVFrame *frame, } } - if (avctx->hwaccel && avctx->hwaccel->decode_params) { - ret = avctx->hwaccel->decode_params(avctx, unit->type, unit->data, - unit->data_size); + if (FF_HW_HAS_CB(avctx, decode_params)) { + ret = FF_HW_CALL(avctx, decode_params, unit->type, + unit->data, unit->data_size); if (ret < 0) { av_log(avctx, AV_LOG_ERROR, "HW accel decode params fail.\n"); s->raw_seq = NULL; @@ -1113,12 +1285,13 @@ static int av1_decode_frame(AVCodecContext *avctx, AVFrame *frame, } if (s->cur_frame.f->buf[0]) { - ret = set_output_frame(avctx, frame, pkt, got_frame); + ret = set_output_frame(avctx, frame); if (ret < 0) av_log(avctx, AV_LOG_ERROR, "Set output frame error.\n"); } s->raw_frame_header = NULL; + i++; goto end; } @@ -1133,8 +1306,7 @@ static int av1_decode_frame(AVCodecContext *avctx, AVFrame *frame, s->cur_frame.temporal_id = header->temporal_id; if (avctx->hwaccel && s->cur_frame.f->buf[0]) { - ret = avctx->hwaccel->start_frame(avctx, unit->data, - unit->data_size); + ret = FF_HW_CALL(avctx, start_frame, unit->data, unit->data_size); if (ret < 0) { av_log(avctx, AV_LOG_ERROR, "HW accel start frame fail.\n"); goto end; @@ -1160,9 +1332,8 @@ static int av1_decode_frame(AVCodecContext *avctx, AVFrame *frame, goto end; if (avctx->hwaccel && s->cur_frame.f->buf[0]) { - ret = avctx->hwaccel->decode_slice(avctx, - raw_tile_group->tile_data.data, - raw_tile_group->tile_data.data_size); + 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"); @@ -1173,7 +1344,47 @@ static int av1_decode_frame(AVCodecContext *avctx, AVFrame *frame, case AV1_OBU_TILE_LIST: case AV1_OBU_TEMPORAL_DELIMITER: case AV1_OBU_PADDING: + break; case AV1_OBU_METADATA: + switch (obu->obu.metadata.metadata_type) { + case AV1_METADATA_TYPE_HDR_CLL: + av_buffer_unref(&s->cll_ref); + s->cll_ref = av_buffer_ref(unit->content_ref); + if (!s->cll_ref) { + s->cll = NULL; + ret = AVERROR(ENOMEM); + goto end; + } + s->cll = &obu->obu.metadata.metadata.hdr_cll; + break; + case AV1_METADATA_TYPE_HDR_MDCV: + av_buffer_unref(&s->mdcv_ref); + s->mdcv_ref = av_buffer_ref(unit->content_ref); + if (!s->mdcv_ref) { + s->mdcv = NULL; + ret = AVERROR(ENOMEM); + goto end; + } + 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, @@ -1182,8 +1393,9 @@ static int av1_decode_frame(AVCodecContext *avctx, AVFrame *frame, } if (raw_tile_group && (s->tile_num == raw_tile_group->tg_end + 1)) { + int show_frame = s->raw_frame_header->show_frame; if (avctx->hwaccel && s->cur_frame.f->buf[0]) { - ret = avctx->hwaccel->end_frame(avctx); + 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; @@ -1197,7 +1409,7 @@ static int av1_decode_frame(AVCodecContext *avctx, AVFrame *frame, } if (s->raw_frame_header->show_frame && s->cur_frame.f->buf[0]) { - ret = set_output_frame(avctx, frame, pkt, got_frame); + ret = set_output_frame(avctx, frame); if (ret < 0) { av_log(avctx, AV_LOG_ERROR, "Set output frame error\n"); goto end; @@ -1205,29 +1417,82 @@ static int av1_decode_frame(AVCodecContext *avctx, AVFrame *frame, } raw_tile_group = NULL; s->raw_frame_header = NULL; + if (show_frame) { + i++; + goto end; + } } } + ret = AVERROR(EAGAIN); end: - ff_cbs_fragment_reset(&s->current_obu); - if (ret < 0) - s->raw_frame_header = NULL; + 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 = 0; + } + + 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 = 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 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(avctx, &s->ref[i]); av1_frame_unref(avctx, &s->cur_frame); s->operating_point_idc = 0; + s->nb_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) @@ -1253,14 +1518,12 @@ const FFCodec ff_av1_decoder = { .priv_data_size = sizeof(AV1DecContext), .init = av1_decode_init, .close = av1_decode_free, - FF_CODEC_DECODE_CB(av1_decode_frame), + FF_CODEC_RECEIVE_FRAME_CB(av1_receive_frame), .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_AVOID_PROBING, - .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | - FF_CODEC_CAP_SETS_PKT_DTS, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .flush = av1_decode_flush, .p.profiles = NULL_IF_CONFIG_SMALL(ff_av1_profiles), .p.priv_class = &av1_class, - .bsfs = "av1_frame_split", .hw_configs = (const AVCodecHWConfigInternal *const []) { #if CONFIG_AV1_DXVA2_HWACCEL HWACCEL_DXVA2(av1), @@ -1280,6 +1543,9 @@ const FFCodec ff_av1_decoder = { #if CONFIG_AV1_VDPAU_HWACCEL HWACCEL_VDPAU(av1), #endif +#if CONFIG_AV1_VULKAN_HWACCEL + HWACCEL_VULKAN(av1), +#endif NULL }, diff --git a/libavcodec/av1dec.h b/libavcodec/av1dec.h index 82c7084e99f..59ffed1d9ba 100644 --- a/libavcodec/av1dec.h +++ b/libavcodec/av1dec.h @@ -23,10 +23,12 @@ #include +#include "libavutil/fifo.h" #include "libavutil/buffer.h" #include "libavutil/frame.h" #include "libavutil/pixfmt.h" #include "avcodec.h" +#include "packet.h" #include "cbs.h" #include "cbs_av1.h" @@ -67,12 +69,20 @@ typedef struct AV1DecContext { enum AVPixelFormat pix_fmt; CodedBitstreamContext *cbc; CodedBitstreamFragment current_obu; + AVPacket *pkt; AVBufferRef *seq_ref; AV1RawSequenceHeader *raw_seq; AVBufferRef *header_ref; AV1RawFrameHeader *raw_frame_header; TileGroupInfo *tile_group_info; + + AVBufferRef *cll_ref; + AV1RawMetadataHDRCLL *cll; + AVBufferRef *mdcv_ref; + AV1RawMetadataHDRMDCV *mdcv; + AVFifo *itut_t35_fifo; + uint16_t tile_num; uint16_t tg_start; uint16_t tg_end; @@ -82,6 +92,8 @@ typedef struct AV1DecContext { AV1Frame ref[AV1_NUM_REF_FRAMES]; AV1Frame cur_frame; + int nb_unit; + // AVOptions int operating_point; } AV1DecContext; diff --git a/libavcodec/avcodec.c b/libavcodec/avcodec.c index efa76d2740a..0700a53b5c0 100644 --- a/libavcodec/avcodec.c +++ b/libavcodec/avcodec.c @@ -34,20 +34,30 @@ #include "libavutil/opt.h" #include "libavutil/thread.h" #include "avcodec.h" +#include "avcodec_internal.h" #include "bsf.h" #include "codec_internal.h" #include "decode.h" #include "encode.h" #include "frame_thread_encoder.h" +#include "hwconfig.h" #include "internal.h" #include "thread.h" +/** + * Maximum size in bytes of extradata. + * This value was chosen such that every bit of the buffer is + * addressable by a 32-bit signed integer as used by get_bits. + */ +#define FF_MAX_EXTRADATA_SIZE ((1 << 28) - AV_INPUT_BUFFER_PADDING_SIZE) + int avcodec_default_execute(AVCodecContext *c, int (*func)(AVCodecContext *c2, void *arg2), void *arg, int *ret, int count, int size) { - int i; + size_t i; for (i = 0; i < count; i++) { - int r = func(c, (char *)arg + i * size); + size_t offset = i * size; + int r = func(c, FF_PTR_ADD((char *)arg, offset)); if (ret) ret[i] = r; } @@ -147,7 +157,9 @@ int attribute_align_arg avcodec_open2(AVCodecContext *avctx, const AVCodec *code if (avctx->extradata_size < 0 || avctx->extradata_size >= FF_MAX_EXTRADATA_SIZE) return AVERROR(EINVAL); - avci = av_mallocz(sizeof(*avci)); + avci = av_codec_is_decoder(codec) ? + ff_decode_internal_alloc() : + ff_encode_internal_alloc(); if (!avci) { ret = AVERROR(ENOMEM); goto end; @@ -266,7 +278,12 @@ FF_ENABLE_DEPRECATION_WARNINGS goto free_and_end; } - avctx->frame_number = 0; + avctx->frame_num = 0; +#if FF_API_AVCTX_FRAME_NUMBER +FF_DISABLE_DEPRECATION_WARNINGS + avctx->frame_number = avctx->frame_num; +FF_ENABLE_DEPRECATION_WARNINGS +#endif avctx->codec_descriptor = avcodec_descriptor_get(avctx->codec_id); if ((avctx->codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL) && @@ -349,11 +366,6 @@ FF_ENABLE_DEPRECATION_WARNINGS ret = AVERROR(EINVAL); goto free_and_end; } - -#if FF_API_AVCTX_TIMEBASE - if (avctx->framerate.num > 0 && avctx->framerate.den > 0) - avctx->time_base = av_inv_q(av_mul_q(avctx->framerate, (AVRational){avctx->ticks_per_frame, 1})); -#endif } if (codec->priv_class) av_assert0(*(const AVClass **)avctx->priv_data == codec->priv_class); @@ -380,23 +392,12 @@ void avcodec_flush_buffers(AVCodecContext *avctx) "that doesn't support it\n"); return; } - if (avci->in_frame) - av_frame_unref(avci->in_frame); - if (avci->recon_frame) - av_frame_unref(avci->recon_frame); - } else { - av_packet_unref(avci->last_pkt_props); - av_packet_unref(avci->in_pkt); - - avctx->pts_correction_last_pts = - avctx->pts_correction_last_dts = INT64_MIN; - - av_bsf_flush(avci->bsf); - } + ff_encode_flush_buffers(avctx); + } else + ff_decode_flush_buffers(avctx); avci->draining = 0; avci->draining_done = 0; - avci->nb_draining_errors = 0; av_frame_unref(avci->buffer_frame); av_packet_unref(avci->buffer_pkt); @@ -458,13 +459,13 @@ av_cold int avcodec_close(AVCodecContext *avctx) av_buffer_unref(&avci->pool); - if (avctx->hwaccel && avctx->hwaccel->uninit) - avctx->hwaccel->uninit(avctx); - av_freep(&avci->hwaccel_priv_data); + ff_hwaccel_uninit(avctx); av_bsf_free(&avci->bsf); +#if FF_API_DROPCHANGED av_channel_layout_uninit(&avci->initial_ch_layout); +#endif #if CONFIG_LCMS2 ff_icc_context_uninit(&avci->icc); diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 0ac581d6604..649411ac79b 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -226,21 +226,76 @@ typedef struct RcOverride{ * Use qpel MC. */ #define AV_CODEC_FLAG_QPEL (1 << 4) +#if FF_API_DROPCHANGED /** * Don't output frames whose parameters differ from first * decoded frame in stream. + * + * @deprecated callers should implement this functionality in their own code */ #define AV_CODEC_FLAG_DROPCHANGED (1 << 5) +#endif /** - * Request the encoder to output reconstructed frames, i.e. frames that would be - * produced by decoding the encoded bistream. These frames may be retrieved by - * calling avcodec_receive_frame() immediately after a successful call to + * Request the encoder to output reconstructed frames, i.e.\ frames that would + * be produced by decoding the encoded bistream. These frames may be retrieved + * by calling avcodec_receive_frame() immediately after a successful call to * avcodec_receive_packet(). * * Should only be used with encoders flagged with the - * AV_CODEC_CAP_ENCODER_RECON_FRAME capability. + * @ref AV_CODEC_CAP_ENCODER_RECON_FRAME capability. + * + * @note + * Each reconstructed frame returned by the encoder corresponds to the last + * encoded packet, i.e. the frames are returned in coded order rather than + * presentation order. + * + * @note + * Frame parameters (like pixel format or dimensions) do not have to match the + * AVCodecContext values. Make sure to use the values from the returned frame. */ #define AV_CODEC_FLAG_RECON_FRAME (1 << 6) +/** + * @par decoding + * Request the decoder to propagate each packet's AVPacket.opaque and + * AVPacket.opaque_ref to its corresponding output AVFrame. + * + * @par encoding: + * Request the encoder to propagate each frame's AVFrame.opaque and + * AVFrame.opaque_ref values to its corresponding output AVPacket. + * + * @par + * May only be set on encoders that have the + * @ref AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE capability flag. + * + * @note + * While in typical cases one input frame produces exactly one output packet + * (perhaps after a delay), in general the mapping of frames to packets is + * M-to-N, so + * - Any number of input frames may be associated with any given output packet. + * This includes zero - e.g. some encoders may output packets that carry only + * metadata about the whole stream. + * - A given input frame may be associated with any number of output packets. + * Again this includes zero - e.g. some encoders may drop frames under certain + * conditions. + * . + * This implies that when using this flag, the caller must NOT assume that + * - a given input frame's opaques will necessarily appear on some output packet; + * - every output packet will have some non-NULL opaque value. + * . + * When an output packet contains multiple frames, the opaque values will be + * taken from the first of those. + * + * @note + * The converse holds for decoders, with frames and packets switched. + */ +#define AV_CODEC_FLAG_COPY_OPAQUE (1 << 7) +/** + * Signal to the encoder that the values of AVFrame.duration are valid and + * should be used (typically for transferring them to output packets). + * + * If this flag is not set, frame durations are ignored. + */ +#define AV_CODEC_FLAG_FRAME_DURATION (1 << 8) /** * Use internal 2pass ratecontrol in first pass mode. */ @@ -261,15 +316,6 @@ typedef struct RcOverride{ * error[?] variables will be set during encoding. */ #define AV_CODEC_FLAG_PSNR (1 << 15) -#if FF_API_FLAG_TRUNCATED -/** - * Input bitstream might be truncated at a random location - * instead of only at frame boundaries. - * - * @deprecated use codec parsers for packetizing input - */ -#define AV_CODEC_FLAG_TRUNCATED (1 << 16) -#endif /** * Use interlaced DCT. */ @@ -310,11 +356,6 @@ typedef struct RcOverride{ */ #define AV_CODEC_FLAG2_LOCAL_HEADER (1 << 3) -/** - * timecode is in drop frame format. DEPRECATED!!!! - */ -#define AV_CODEC_FLAG2_DROP_FRAME_TIMECODE (1 << 13) - /** * Input bitstream might be truncated at a packet boundaries * instead of only at frame boundaries. @@ -380,8 +421,6 @@ typedef struct RcOverride{ */ #define AV_GET_ENCODE_BUFFER_FLAG_REF (1 << 0) -struct AVCodecInternal; - /** * main external API structure. * New fields can be added to the end with minor version bumps. @@ -515,19 +554,26 @@ typedef struct AVCodecContext { * (fixed_vop_rate == 0 implies that it is different from the framerate) * * - encoding: MUST be set by user. - * - decoding: the use of this field for decoding is deprecated. - * Use framerate instead. + * - decoding: unused. */ AVRational time_base; +#if FF_API_TICKS_PER_FRAME /** * For some codecs, the time base is closer to the field rate than the frame rate. * Most notably, H.264 and MPEG-2 specify time_base as half of frame duration * if no telecine is used ... * * Set to time_base ticks per frame. Default 1, e.g., H.264/MPEG-2 set it to 2. + * + * @deprecated + * - decoding: Use AVCodecDescriptor.props & AV_CODEC_PROP_FIELDS + * - encoding: Set AVCodecContext.framerate instead + * */ + attribute_deprecated int ticks_per_frame; +#endif /** * Codec delay. @@ -744,11 +790,13 @@ typedef struct AVCodecContext { */ float dark_masking; +#if FF_API_SLICE_OFFSET /** * slice count * - encoding: Set by libavcodec. * - decoding: Set by user (or 0). */ + attribute_deprecated int slice_count; /** @@ -756,7 +804,9 @@ typedef struct AVCodecContext { * - encoding: Set/allocated by libavcodec. * - decoding: Set/allocated by user (or NULL). */ + attribute_deprecated int *slice_offset; +#endif /** * sample aspect ratio (0 if unknown) @@ -976,8 +1026,11 @@ typedef struct AVCodecContext { /** * MPEG vs JPEG YUV range. - * - encoding: Set by user - * - decoding: Set by libavcodec + * - encoding: Set by user to override the default output color range value, + * If not specified, libavcodec sets the color range depending on the + * output format. + * - decoding: Set by libavcodec, can be set by the user to propagate the + * color range to components reading from the decoder context. */ enum AVColorRange color_range; @@ -1034,6 +1087,7 @@ typedef struct AVCodecContext { */ int frame_size; +#if FF_API_AVCTX_FRAME_NUMBER /** * Frame counter, set by libavcodec. * @@ -1042,8 +1096,11 @@ typedef struct AVCodecContext { * * @note the counter is not incremented if encoding/decoding resulted in * an error. + * @deprecated use frame_num instead */ + attribute_deprecated int frame_number; +#endif /** * number of bytes per packet if constant and known or 0 @@ -1350,6 +1407,7 @@ typedef struct AVCodecContext { */ int err_recognition; +#if FF_API_REORDERED_OPAQUE /** * opaque 64-bit number (generally a PTS) that will be reordered and * output in AVFrame.reordered_opaque @@ -1358,8 +1416,12 @@ typedef struct AVCodecContext { * supported by encoders with the * AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE capability. * - decoding: Set by user. + * + * @deprecated Use AV_CODEC_FLAG_COPY_OPAQUE instead */ + attribute_deprecated int64_t reordered_opaque; +#endif /** * Hardware accelerator in use @@ -1484,27 +1546,6 @@ typedef struct AVCodecContext { */ int active_thread_type; -#if FF_API_THREAD_SAFE_CALLBACKS - /** - * Set by the client if its custom get_buffer() callback can be called - * synchronously from another thread, which allows faster multithreaded decoding. - * draw_horiz_band() will be called from other threads regardless of this setting. - * Ignored if the default get_buffer() is used. - * - encoding: Set by user. - * - decoding: Set by user. - * - * @deprecated the custom get_buffer2() callback should always be - * thread-safe. Thread-unsafe get_buffer2() implementations will be - * invalid starting with LIBAVCODEC_VERSION_MAJOR=60; in other words, - * libavcodec will behave as if this field was always set to 1. - * Callers that want to be forward compatible with future libavcodec - * versions should wrap access to this field in - * `#if LIBAVCODEC_VERSION_MAJOR < 60` - */ - attribute_deprecated - int thread_safe_callbacks; -#endif - /** * The codec may call this to execute several independent things. * It will return only after finishing all tasks. @@ -1569,12 +1610,19 @@ typedef struct AVCodecContext { #define FF_PROFILE_DNXHR_HQX 4 #define FF_PROFILE_DNXHR_444 5 -#define FF_PROFILE_DTS 20 -#define FF_PROFILE_DTS_ES 30 -#define FF_PROFILE_DTS_96_24 40 -#define FF_PROFILE_DTS_HD_HRA 50 -#define FF_PROFILE_DTS_HD_MA 60 -#define FF_PROFILE_DTS_EXPRESS 70 +#define FF_PROFILE_DTS 20 +#define FF_PROFILE_DTS_ES 30 +#define FF_PROFILE_DTS_96_24 40 +#define FF_PROFILE_DTS_HD_HRA 50 +#define FF_PROFILE_DTS_HD_MA 60 +#define FF_PROFILE_DTS_EXPRESS 70 +#define FF_PROFILE_DTS_HD_MA_X 61 +#define FF_PROFILE_DTS_HD_MA_X_IMAX 62 + + +#define FF_PROFILE_EAC3_DDP_ATMOS 30 + +#define FF_PROFILE_TRUEHD_ATMOS 30 #define FF_PROFILE_MPEG2_422 0 #define FF_PROFILE_MPEG2_HIGH 1 @@ -1639,6 +1687,7 @@ typedef struct AVCodecContext { #define FF_PROFILE_HEVC_MAIN_10 2 #define FF_PROFILE_HEVC_MAIN_STILL_PICTURE 3 #define FF_PROFILE_HEVC_REXT 4 +#define FF_PROFILE_HEVC_SCC 9 #define FF_PROFILE_VVC_MAIN_10 1 #define FF_PROFILE_VVC_MAIN_10_444 33 @@ -1668,6 +1717,9 @@ typedef struct AVCodecContext { #define FF_PROFILE_KLVA_SYNC 0 #define FF_PROFILE_KLVA_ASYNC 1 +#define FF_PROFILE_EVC_BASELINE 0 +#define FF_PROFILE_EVC_MAIN 1 + /** * level * - encoding: Set by user. @@ -1805,17 +1857,6 @@ typedef struct AVCodecContext { */ int seek_preroll; -#if FF_API_DEBUG_MV - /** - * @deprecated unused - */ - attribute_deprecated - int debug_mv; -#define FF_DEBUG_VIS_MV_P_FOR 0x00000001 //visualize forward predicted MVs of P frames -#define FF_DEBUG_VIS_MV_B_FOR 0x00000002 //visualize forward predicted MVs of B frames -#define FF_DEBUG_VIS_MV_B_BACK 0x00000004 //visualize backward predicted MVs of B frames -#endif - /** * custom intra quantization matrix * - encoding: Set by user, can be NULL. @@ -1882,15 +1923,6 @@ typedef struct AVCodecContext { */ AVBufferRef *hw_frames_ctx; -#if FF_API_SUB_TEXT_FORMAT - /** - * @deprecated unused - */ - attribute_deprecated - int sub_text_format; -#define FF_SUB_TEXT_FMT_ASS 0 -#endif - /** * Audio only. The amount of padding (in samples) appended by the encoder to * the end of the audio. I.e. this number of decoded samples must be @@ -2057,6 +2089,17 @@ typedef struct AVCodecContext { * The decoder can then override during decoding as needed. */ AVChannelLayout ch_layout; + + /** + * Frame counter, set by libavcodec. + * + * - decoding: total number of frames returned from the decoder so far. + * - encoding: total number of frames passed to the encoder so far. + * + * @note the counter is not incremented if encoding/decoding resulted in + * an error. + */ + int64_t frame_num; } AVCodecContext; /** @@ -2101,120 +2144,6 @@ typedef struct AVHWAccel { * see AV_HWACCEL_CODEC_CAP_* */ int capabilities; - - /***************************************************************** - * No fields below this line are part of the public API. They - * may not be used outside of libavcodec and can be changed and - * removed at will. - * New public fields should be added right above. - ***************************************************************** - */ - - /** - * Allocate a custom buffer - */ - int (*alloc_frame)(AVCodecContext *avctx, AVFrame *frame); - - /** - * Called at the beginning of each frame or field picture. - * - * Meaningful frame information (codec specific) is guaranteed to - * be parsed at this point. This function is mandatory. - * - * Note that buf can be NULL along with buf_size set to 0. - * Otherwise, this means the whole frame is available at this point. - * - * @param avctx the codec context - * @param buf the frame data buffer base - * @param buf_size the size of the frame in bytes - * @return zero if successful, a negative value otherwise - */ - int (*start_frame)(AVCodecContext *avctx, const uint8_t *buf, uint32_t buf_size); - - /** - * Callback for parameter data (SPS/PPS/VPS etc). - * - * Useful for hardware decoders which keep persistent state about the - * video parameters, and need to receive any changes to update that state. - * - * @param avctx the codec context - * @param type the nal unit type - * @param buf the nal unit data buffer - * @param buf_size the size of the nal unit in bytes - * @return zero if successful, a negative value otherwise - */ - int (*decode_params)(AVCodecContext *avctx, int type, const uint8_t *buf, uint32_t buf_size); - - /** - * Callback for each slice. - * - * Meaningful slice information (codec specific) is guaranteed to - * be parsed at this point. This function is mandatory. - * - * @param avctx the codec context - * @param buf the slice data buffer base - * @param buf_size the size of the slice in bytes - * @return zero if successful, a negative value otherwise - */ - int (*decode_slice)(AVCodecContext *avctx, const uint8_t *buf, uint32_t buf_size); - - /** - * Called at the end of each frame or field picture. - * - * The whole picture is parsed at this point and can now be sent - * to the hardware accelerator. This function is mandatory. - * - * @param avctx the codec context - * @return zero if successful, a negative value otherwise - */ - int (*end_frame)(AVCodecContext *avctx); - - /** - * Size of per-frame hardware accelerator private data. - * - * Private data is allocated with av_mallocz() before - * AVCodecContext.get_buffer() and deallocated after - * AVCodecContext.release_buffer(). - */ - int frame_priv_data_size; - - /** - * Initialize the hwaccel private data. - * - * This will be called from ff_get_format(), after hwaccel and - * hwaccel_context are set and the hwaccel private data in AVCodecInternal - * is allocated. - */ - int (*init)(AVCodecContext *avctx); - - /** - * Uninitialize the hwaccel private data. - * - * This will be called from get_format() or avcodec_close(), after hwaccel - * and hwaccel_context are already uninitialized. - */ - int (*uninit)(AVCodecContext *avctx); - - /** - * Size of the private data to allocate in - * AVCodecInternal.hwaccel_priv_data. - */ - int priv_data_size; - - /** - * Internal hwaccel capabilities. - */ - int caps_internal; - - /** - * Fill the given hw_frames context with current codec parameters. Called - * from get_format. Refer to avcodec_get_hw_frames_parameters() for - * details. - * - * This CAN be called before AVHWAccel.init is called, and you must assume - * that avctx->hwaccel_priv_data is invalid. - */ - int (*frame_params)(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx); } AVHWAccel; /** @@ -2374,14 +2303,6 @@ void avcodec_free_context(AVCodecContext **avctx); */ const AVClass *avcodec_get_class(void); -#if FF_API_GET_FRAME_CLASS -/** - * @deprecated This function should not be used. - */ -attribute_deprecated -const AVClass *avcodec_get_frame_class(void); -#endif - /** * Get the AVClass for AVSubtitleRect. It can be used in combination with * AV_OPT_SEARCH_FAKE_OBJ for examining options. @@ -2419,9 +2340,16 @@ int avcodec_parameters_to_context(AVCodecContext *codec, * avcodec_find_decoder() and avcodec_find_encoder() provide an easy way for * retrieving a codec. * - * @note Always call this function before using decoding routines (such as - * @ref avcodec_receive_frame()). + * Depending on the codec, you might need to set options in the codec context + * also for decoding (e.g. width, height, or the pixel or audio sample format in + * the case the information is not available in the bitstream, as when decoding + * raw audio or video). * + * Options in the codec context can be set either by setting them in the options + * AVDictionary, or by setting the values in the context itself, directly or by + * using the av_opt_set() API before calling this function. + * + * Example: * @code * av_dict_set(&opts, "b", "2.5M", 0); * codec = avcodec_find_decoder(AV_CODEC_ID_H264); @@ -2434,17 +2362,36 @@ int avcodec_parameters_to_context(AVCodecContext *codec, * exit(1); * @endcode * + * In the case AVCodecParameters are available (e.g. when demuxing a stream + * using libavformat, and accessing the AVStream contained in the demuxer), the + * codec parameters can be copied to the codec context using + * avcodec_parameters_to_context(), as in the following example: + * + * @code + * AVStream *stream = ...; + * context = avcodec_alloc_context3(codec); + * if (avcodec_parameters_to_context(context, stream->codecpar) < 0) + * exit(1); + * if (avcodec_open2(context, codec, NULL) < 0) + * exit(1); + * @endcode + * + * @note Always call this function before using decoding routines (such as + * @ref avcodec_receive_frame()). + * * @param avctx The context to initialize. * @param codec The codec to open this context for. If a non-NULL codec has been * previously passed to avcodec_alloc_context3() or * for this context, then this parameter MUST be either NULL or * equal to the previously passed codec. - * @param options A dictionary filled with AVCodecContext and codec-private options. - * On return this object will be filled with options that were not found. + * @param options A dictionary filled with AVCodecContext and codec-private + * options, which are set on top of the options already set in + * avctx, can be NULL. On return this object will be filled with + * options that were not found in the avctx codec context. * * @return zero on success, a negative value on error * @see avcodec_alloc_context3(), avcodec_find_decoder(), avcodec_find_encoder(), - * av_dict_set(), av_opt_find(). + * av_dict_set(), av_opt_set(), av_opt_find(), avcodec_parameters_to_context() */ int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options); @@ -2568,8 +2515,7 @@ enum AVChromaLocation avcodec_chroma_pos_to_enum(int xpos, int ypos); * @param[in] avpkt The input AVPacket containing the input buffer. */ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub, - int *got_sub_ptr, - AVPacket *avpkt); + int *got_sub_ptr, const AVPacket *avpkt); /** * Supply raw packet data as input to a decoder. @@ -2605,23 +2551,23 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub, * still has frames buffered, it will return them after sending * a flush packet. * - * @return 0 on success, otherwise negative error code: - * AVERROR(EAGAIN): input is not accepted in the current state - user - * must read output with avcodec_receive_frame() (once - * all output is read, the packet should be resent, and - * the call will not fail with EAGAIN). - * AVERROR_EOF: the decoder has been flushed, and no new packets can - * be sent to it (also returned if more than 1 flush - * packet is sent) - * AVERROR(EINVAL): codec not opened, it is an encoder, or requires flush - * AVERROR(ENOMEM): failed to add packet to internal queue, or similar - * other errors: legitimate decoding errors + * @retval 0 success + * @retval AVERROR(EAGAIN) input is not accepted in the current state - user + * must read output with avcodec_receive_frame() (once + * all output is read, the packet should be resent, + * and the call will not fail with EAGAIN). + * @retval AVERROR_EOF the decoder has been flushed, and no new packets can be + * sent to it (also returned if more than 1 flush + * packet is sent) + * @retval AVERROR(EINVAL) codec not opened, it is an encoder, or requires flush + * @retval AVERROR(ENOMEM) failed to add packet to internal queue, or similar + * @retval "another negative error code" legitimate decoding errors */ int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt); /** * Return decoded output data from a decoder or encoder (when the - * AV_CODEC_FLAG_RECON_FRAME flag is used). + * @ref AV_CODEC_FLAG_RECON_FRAME flag is used). * * @param avctx codec context * @param frame This will be set to a reference-counted video or audio @@ -2629,18 +2575,14 @@ int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt); * codec. Note that the function will always call * av_frame_unref(frame) before doing anything else. * - * @return - * 0: success, a frame was returned - * AVERROR(EAGAIN): output is not available in this state - user must try - * to send new input - * AVERROR_EOF: the codec has been fully flushed, and there will be - * no more output frames - * AVERROR(EINVAL): codec not opened, or it is an encoder without - * the AV_CODEC_FLAG_RECON_FRAME flag enabled - * AVERROR_INPUT_CHANGED: current decoded frame has changed parameters - * with respect to first decoded frame. Applicable - * when flag AV_CODEC_FLAG_DROPCHANGED is set. - * other negative values: legitimate decoding errors + * @retval 0 success, a frame was returned + * @retval AVERROR(EAGAIN) output is not available in this state - user must + * try to send new input + * @retval AVERROR_EOF the codec has been fully flushed, and there will be + * no more output frames + * @retval AVERROR(EINVAL) codec not opened, or it is an encoder without the + * @ref AV_CODEC_FLAG_RECON_FRAME flag enabled + * @retval "other negative error code" legitimate decoding errors */ int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame); @@ -2667,16 +2609,16 @@ int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame); * If it is not set, frame->nb_samples must be equal to * avctx->frame_size for all frames except the last. * The final frame may be smaller than avctx->frame_size. - * @return 0 on success, otherwise negative error code: - * AVERROR(EAGAIN): input is not accepted in the current state - user - * must read output with avcodec_receive_packet() (once - * all output is read, the packet should be resent, and - * the call will not fail with EAGAIN). - * AVERROR_EOF: the encoder has been flushed, and no new frames can - * be sent to it - * AVERROR(EINVAL): codec not opened, it is a decoder, or requires flush - * AVERROR(ENOMEM): failed to add packet to internal queue, or similar - * other errors: legitimate encoding errors + * @retval 0 success + * @retval AVERROR(EAGAIN) input is not accepted in the current state - user must + * read output with avcodec_receive_packet() (once all + * output is read, the packet should be resent, and the + * call will not fail with EAGAIN). + * @retval AVERROR_EOF the encoder has been flushed, and no new frames can + * be sent to it + * @retval AVERROR(EINVAL) codec not opened, it is a decoder, or requires flush + * @retval AVERROR(ENOMEM) failed to add packet to internal queue, or similar + * @retval "another negative error code" legitimate encoding errors */ int avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame); @@ -2687,13 +2629,13 @@ int avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame); * @param avpkt This will be set to a reference-counted packet allocated by the * encoder. Note that the function will always call * av_packet_unref(avpkt) before doing anything else. - * @return 0 on success, otherwise negative error code: - * AVERROR(EAGAIN): output is not available in the current state - user - * must try to send input - * AVERROR_EOF: the encoder has been fully flushed, and there will be - * no more output packets - * AVERROR(EINVAL): codec not opened, or it is a decoder - * other errors: legitimate encoding errors + * @retval 0 success + * @retval AVERROR(EAGAIN) output is not available in the current state - user must + * try to send input + * @retval AVERROR_EOF the encoder has been fully flushed, and there will be no + * more output packets + * @retval AVERROR(EINVAL) codec not opened, or it is a decoder + * @retval "another negative error code" legitimate encoding errors */ int avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt); diff --git a/libavcodec/avcodec_internal.h b/libavcodec/avcodec_internal.h new file mode 100644 index 00000000000..9b93ff3d819 --- /dev/null +++ b/libavcodec/avcodec_internal.h @@ -0,0 +1,59 @@ +/* + * 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 + */ + +/* + * APIs internal to the generic codec layer. + * + * MUST NOT be included by individual encoders or decoders. + */ + +#ifndef AVCODEC_AVCODEC_INTERNAL_H +#define AVCODEC_AVCODEC_INTERNAL_H + +struct AVCodecContext; +struct AVFrame; + +/** + * avcodec_receive_frame() implementation for decoders. + */ +int ff_decode_receive_frame(struct AVCodecContext *avctx, struct AVFrame *frame); + +/** + * avcodec_receive_frame() implementation for encoders. + */ +int ff_encode_receive_frame(struct AVCodecContext *avctx, struct AVFrame *frame); + +/* + * Perform encoder initialization and validation. + * Called when opening the encoder, before the FFCodec.init() call. + */ +int ff_encode_preinit(struct AVCodecContext *avctx); + +/** + * Perform decoder initialization and validation. + * Called when opening the decoder, before the FFCodec.init() call. + */ +int ff_decode_preinit(struct AVCodecContext *avctx); + +void ff_decode_flush_buffers(struct AVCodecContext *avctx); +void ff_encode_flush_buffers(struct AVCodecContext *avctx); + +struct AVCodecInternal *ff_decode_internal_alloc(void); +struct AVCodecInternal *ff_encode_internal_alloc(void); + +#endif // AVCODEC_AVCODEC_INTERNAL_H diff --git a/libavcodec/avrndec.c b/libavcodec/avrndec.c index ef194058fc5..97d28246255 100644 --- a/libavcodec/avrndec.c +++ b/libavcodec/avrndec.c @@ -68,7 +68,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; p->pict_type= AV_PICTURE_TYPE_I; - p->key_frame= 1; + p->flags |= AV_FRAME_FLAG_KEY; if(a->interlace) { buf += (true_height - avctx->height)*avctx->width; diff --git a/libavcodec/avs.c b/libavcodec/avs.c index a1c9d3c436f..b53175f6400 100644 --- a/libavcodec/avs.c +++ b/libavcodec/avs.c @@ -61,7 +61,7 @@ static int avs_decode_frame(AVCodecContext * avctx, AVFrame *picture, if ((ret = ff_reget_buffer(avctx, p, 0)) < 0) return ret; p->pict_type = AV_PICTURE_TYPE_P; - p->key_frame = 0; + p->flags &= ~AV_FRAME_FLAG_KEY; out = p->data[0]; stride = p->linesize[0]; @@ -97,7 +97,7 @@ static int avs_decode_frame(AVCodecContext * avctx, AVFrame *picture, switch (sub_type) { case AVS_I_FRAME: p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; case AVS_P_FRAME_3X3: vect_w = 3; vect_h = 3; diff --git a/libavcodec/avs2_parser.c b/libavcodec/avs2_parser.c index 0350517493a..200134f91db 100644 --- a/libavcodec/avs2_parser.c +++ b/libavcodec/avs2_parser.c @@ -112,9 +112,9 @@ static void parse_avs2_seq_header(AVCodecParserContext *s, const uint8_t *buf, s->height = height; s->coded_width = FFALIGN(width, 8); s->coded_height = FFALIGN(height, 8); - avctx->framerate.num = avctx->time_base.den = + avctx->framerate.num = ff_avs2_frame_rate_tab[frame_rate_code].num; - avctx->framerate.den = avctx->time_base.num = + avctx->framerate.den = ff_avs2_frame_rate_tab[frame_rate_code].den; avctx->has_b_frames = FFMAX(avctx->has_b_frames, !low_delay); diff --git a/libavcodec/avs3_parser.c b/libavcodec/avs3_parser.c index a9fd879e9de..a819b5783d6 100644 --- a/libavcodec/avs3_parser.c +++ b/libavcodec/avs3_parser.c @@ -117,8 +117,8 @@ static void parse_avs3_nal_units(AVCodecParserContext *s, const uint8_t *buf, low_delay = get_bits(&gb, 1); avctx->has_b_frames = FFMAX(avctx->has_b_frames, !low_delay); - avctx->framerate.num = avctx->time_base.den = ff_avs3_frame_rate_tab[ratecode].num; - avctx->framerate.den = avctx->time_base.num = ff_avs3_frame_rate_tab[ratecode].den; + avctx->framerate.num = ff_avs3_frame_rate_tab[ratecode].num; + avctx->framerate.den = ff_avs3_frame_rate_tab[ratecode].den; s->width = s->coded_width = avctx->width; s->height = s->coded_height = avctx->height; diff --git a/libavcodec/avuidec.c b/libavcodec/avuidec.c index ba157e167cd..48b23d4875f 100644 --- a/libavcodec/avuidec.c +++ b/libavcodec/avuidec.c @@ -71,7 +71,7 @@ static int avui_decode_frame(AVCodecContext *avctx, AVFrame *pic, if ((ret = ff_get_buffer(avctx, pic, 0)) < 0) return ret; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; pic->pict_type = AV_PICTURE_TYPE_I; if (!interlaced) { diff --git a/libavcodec/avuienc.c b/libavcodec/avuienc.c index 0b82848cb36..8a093d3d193 100644 --- a/libavcodec/avuienc.c +++ b/libavcodec/avuienc.c @@ -96,7 +96,8 @@ const FFCodec ff_avui_encoder = { CODEC_LONG_NAME("Avid Meridien Uncompressed"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_AVUI, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_EXPERIMENTAL, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_EXPERIMENTAL | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_UYVY422, AV_PIX_FMT_NONE }, .init = avui_encode_init, FF_CODEC_ENCODE_CB(avui_encode_frame), diff --git a/libavcodec/bethsoftvideo.c b/libavcodec/bethsoftvideo.c index e095d04fa56..6de502822b1 100644 --- a/libavcodec/bethsoftvideo.c +++ b/libavcodec/bethsoftvideo.c @@ -63,7 +63,11 @@ static int set_palette(BethsoftvidContext *ctx, GetByteContext *g) palette[a] = 0xFFU << 24 | bytestream2_get_be24u(g) * 4; palette[a] |= palette[a] >> 6 & 0x30303; } +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS ctx->frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif return 0; } diff --git a/libavcodec/bfi.c b/libavcodec/bfi.c index 2b647419c79..901669a3a99 100644 --- a/libavcodec/bfi.c +++ b/libavcodec/bfi.c @@ -66,9 +66,9 @@ static int bfi_decode_frame(AVCodecContext *avctx, AVFrame *frame, bytestream2_init(&g, avpkt->data, buf_size); /* Set frame parameters and palette, if necessary */ - if (!avctx->frame_number) { + if (!avctx->frame_num) { frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; /* Setting the palette */ if (avctx->extradata_size > 768) { av_log(avctx, AV_LOG_ERROR, "Palette is too large.\n"); @@ -84,11 +84,19 @@ static int bfi_decode_frame(AVCodecContext *avctx, AVFrame *frame, pal++; } memcpy(bfi->pal, frame->data[1], sizeof(bfi->pal)); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } else { frame->pict_type = AV_PICTURE_TYPE_P; - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif memcpy(frame->data[1], bfi->pal, sizeof(bfi->pal)); } diff --git a/libavcodec/bink.c b/libavcodec/bink.c index e3971e557aa..e3822d689c0 100644 --- a/libavcodec/bink.c +++ b/libavcodec/bink.c @@ -59,11 +59,11 @@ enum OldSources { BINKB_NB_SRC }; -static const int binkb_bundle_sizes[BINKB_NB_SRC] = { +static const uint8_t binkb_bundle_sizes[BINKB_NB_SRC] = { 4, 8, 8, 5, 5, 11, 11, 4, 4, 7 }; -static const int binkb_bundle_signed[BINKB_NB_SRC] = { +static const uint8_t binkb_bundle_signed[BINKB_NB_SRC] = { 0, 0, 0, 1, 1, 0, 1, 0, 0, 0 }; @@ -870,7 +870,7 @@ static int binkb_decode_plane(BinkContext *c, AVFrame *frame, GetBitContext *gb, binkb_init_bundles(c); ref_start = frame->data[plane_idx]; - ref_end = frame->data[plane_idx] + (bh * frame->linesize[plane_idx] + bw) * 8; + ref_end = frame->data[plane_idx] + ((bh - 1) * frame->linesize[plane_idx] + bw - 1) * 8; for (i = 0; i < 64; i++) coordmap[i] = (i & 7) + (i >> 3) * stride; @@ -926,7 +926,7 @@ static int binkb_decode_plane(BinkContext *c, AVFrame *frame, GetBitContext *gb, xoff = binkb_get_value(c, BINKB_SRC_X_OFF); yoff = binkb_get_value(c, BINKB_SRC_Y_OFF) + ybias; ref = dst + xoff + yoff * stride; - if (ref < ref_start || ref + 8*stride > ref_end) { + if (ref < ref_start || ref > ref_end) { av_log(c->avctx, AV_LOG_WARNING, "Reference block is out of bounds\n"); } else if (ref + 8*stride < dst || ref >= dst + 8*stride) { c->put_pixels_tab(dst, ref, stride, 8); @@ -942,7 +942,7 @@ static int binkb_decode_plane(BinkContext *c, AVFrame *frame, GetBitContext *gb, xoff = binkb_get_value(c, BINKB_SRC_X_OFF); yoff = binkb_get_value(c, BINKB_SRC_Y_OFF) + ybias; ref = dst + xoff + yoff * stride; - if (ref < ref_start || ref + 8 * stride > ref_end) { + if (ref < ref_start || ref > ref_end) { av_log(c->avctx, AV_LOG_WARNING, "Reference block is out of bounds\n"); } else if (ref + 8*stride < dst || ref >= dst + 8*stride) { c->put_pixels_tab(dst, ref, stride, 8); @@ -974,7 +974,7 @@ static int binkb_decode_plane(BinkContext *c, AVFrame *frame, GetBitContext *gb, xoff = binkb_get_value(c, BINKB_SRC_X_OFF); yoff = binkb_get_value(c, BINKB_SRC_Y_OFF) + ybias; ref = dst + xoff + yoff * stride; - if (ref < ref_start || ref + 8 * stride > ref_end) { + if (ref < ref_start || ref > ref_end) { av_log(c->avctx, AV_LOG_WARNING, "Reference block is out of bounds\n"); } else if (ref + 8*stride < dst || ref >= dst + 8*stride) { c->put_pixels_tab(dst, ref, stride, 8); diff --git a/libavcodec/binkaudio.c b/libavcodec/binkaudio.c index f28ecba7603..265f93a8222 100644 --- a/libavcodec/binkaudio.c +++ b/libavcodec/binkaudio.c @@ -325,7 +325,7 @@ static int binkaudio_receive_frame(AVCodecContext *avctx, AVFrame *frame) if (s->ch_offset == 0) { frame->nb_samples = s->frame_len; if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) - return ret; + goto fail; if (!new_pkt) frame->pts = AV_NOPTS_VALUE; } @@ -334,8 +334,8 @@ static int binkaudio_receive_frame(AVCodecContext *avctx, AVFrame *frame) avctx->codec->id == AV_CODEC_ID_BINKAUDIO_DCT, FFMIN(MAX_CHANNELS, s->channels - s->ch_offset), s->ch_offset)) { av_log(avctx, AV_LOG_ERROR, "Incomplete packet\n"); - s->ch_offset = 0; - return AVERROR_INVALIDDATA; + ret = AVERROR_INVALIDDATA; + goto fail; } s->ch_offset += MAX_CHANNELS; get_bits_align32(gb); diff --git a/libavcodec/bintext.c b/libavcodec/bintext.c index ce814f76939..b20d6ce176e 100644 --- a/libavcodec/bintext.c +++ b/libavcodec/bintext.c @@ -157,7 +157,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if ((ret = ff_get_buffer(avctx, s->frame, 0)) < 0) return ret; s->frame->pict_type = AV_PICTURE_TYPE_I; +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS s->frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif memcpy(s->frame->data[1], s->palette, 16 * 4); if (avctx->codec_id == AV_CODEC_ID_XBIN) { diff --git a/libavcodec/bitpacked_dec.c b/libavcodec/bitpacked_dec.c index a1ffef185ce..c88f8619938 100644 --- a/libavcodec/bitpacked_dec.c +++ b/libavcodec/bitpacked_dec.c @@ -134,7 +134,7 @@ static int bitpacked_decode(AVCodecContext *avctx, AVFrame *frame, return res; frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; return buf_size; diff --git a/libavcodec/bitpacked_enc.c b/libavcodec/bitpacked_enc.c index ca4d5c887d6..3c4e11293db 100644 --- a/libavcodec/bitpacked_enc.c +++ b/libavcodec/bitpacked_enc.c @@ -110,7 +110,8 @@ const FFCodec ff_bitpacked_encoder = { .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_BITPACKED, .priv_data_size = sizeof(struct BitpackedContext), - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .init = encode_init, FF_CODEC_ENCODE_CB(encode_frame), .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV422P10, diff --git a/libavcodec/bitstream.h b/libavcodec/bitstream.h index ef7d8d55c24..b60f0c296d1 100644 --- a/libavcodec/bitstream.h +++ b/libavcodec/bitstream.h @@ -89,9 +89,11 @@ # define bits_read_63 bits_read_63_le # define bits_read_64 bits_read_64_le # define bits_read_signed bits_read_signed_le +# define bits_read_signed_nz bits_read_signed_nz_le # define bits_peek_nz bits_peek_nz_le # define bits_peek bits_peek_le # define bits_peek_signed bits_peek_signed_le +# define bits_peek_signed_nz bits_peek_signed_nz_le # define bits_skip bits_skip_le # define bits_seek bits_seek_le # define bits_align bits_align_le @@ -115,9 +117,11 @@ # define bits_read_63 bits_read_63_be # define bits_read_64 bits_read_64_be # define bits_read_signed bits_read_signed_be +# define bits_read_signed_nz bits_read_signed_nz_be # define bits_peek_nz bits_peek_nz_be # define bits_peek bits_peek_be # define bits_peek_signed bits_peek_signed_be +# define bits_peek_signed_nz bits_peek_signed_nz_be # define bits_skip bits_skip_be # define bits_seek bits_seek_be # define bits_align bits_align_be diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c index a3bebefe5f9..1e9a676a3d2 100644 --- a/libavcodec/bitstream_filters.c +++ b/libavcodec/bitstream_filters.c @@ -43,6 +43,7 @@ extern const FFBitStreamFilter ff_hapqa_extract_bsf; extern const FFBitStreamFilter ff_hevc_metadata_bsf; extern const FFBitStreamFilter ff_hevc_mp4toannexb_bsf; extern const FFBitStreamFilter ff_imx_dump_header_bsf; +extern const FFBitStreamFilter ff_media100_to_mjpegb_bsf; extern const FFBitStreamFilter ff_mjpeg2jpeg_bsf; extern const FFBitStreamFilter ff_mjpega_dump_header_bsf; extern const FFBitStreamFilter ff_mp3_header_decompress_bsf; @@ -64,16 +65,19 @@ extern const FFBitStreamFilter ff_vp9_metadata_bsf; extern const FFBitStreamFilter ff_vp9_raw_reorder_bsf; extern const FFBitStreamFilter ff_vp9_superframe_bsf; extern const FFBitStreamFilter ff_vp9_superframe_split_bsf; +extern const FFBitStreamFilter ff_vvc_metadata_bsf; +extern const FFBitStreamFilter ff_vvc_mp4toannexb_bsf; +extern const FFBitStreamFilter ff_evc_frame_merge_bsf; #include "libavcodec/bsf_list.c" const AVBitStreamFilter *av_bsf_iterate(void **opaque) { - uintptr_t i = (uintptr_t)*opaque; + uintptr_t i = (uintptr_t) * opaque; const FFBitStreamFilter *f = bitstream_filters[i]; if (f) { - *opaque = (void*)(i + 1); + *opaque = (void *)(i + 1); return &f->p; } return NULL; diff --git a/libavcodec/bitstream_template.h b/libavcodec/bitstream_template.h index f2c14fc4d37..30bea84add6 100644 --- a/libavcodec/bitstream_template.h +++ b/libavcodec/bitstream_template.h @@ -290,13 +290,29 @@ static inline uint64_t BS_FUNC(read_64)(BSCTX *bc, unsigned int n) return BS_FUNC(read_63)(bc, n); } +/** + * Return n bits from the buffer as a signed integer, n has to be in the 1-32 + * range. May be faster than bits_read_signed() when n is not a compile-time + * constant and is known to be non-zero; + */ +static inline int32_t BS_FUNC(read_signed_nz)(BSCTX *bc, unsigned int n) +{ + av_assert2(n > 0 && n <= 32); + return sign_extend(BS_FUNC(read_nz)(bc, n), n); +} + /** * Return n bits from the buffer as a signed integer. * n has to be in the 0-32 range. */ static inline int32_t BS_FUNC(read_signed)(BSCTX *bc, unsigned int n) { - return sign_extend(BS_FUNC(read)(bc, n), n); + av_assert2(n <= 32); + + if (!n) + return 0; + + return BS_FUNC(read_signed_nz)(bc, n); } /** @@ -327,6 +343,17 @@ static inline uint32_t BS_FUNC(peek)(BSCTX *bc, unsigned int n) return BS_FUNC(peek_nz)(bc, n); } +/** + * Return n bits from the buffer as a signed integer, do not change the buffer + * state. n has to be in the 1-32 range. May be faster than bits_peek_signed() + * when n is not a compile-time constant and is known to be non-zero; + */ +static inline int BS_FUNC(peek_signed_nz)(BSCTX *bc, unsigned int n) +{ + av_assert2(n > 0 && n <= 32); + return sign_extend(BS_FUNC(peek_nz)(bc, n), n); +} + /** * Return n bits from the buffer as a signed integer, * do not change the buffer state. @@ -334,7 +361,12 @@ static inline uint32_t BS_FUNC(peek)(BSCTX *bc, unsigned int n) */ static inline int BS_FUNC(peek_signed)(BSCTX *bc, unsigned int n) { - return sign_extend(BS_FUNC(peek)(bc, n), n); + av_assert2(n <= 32); + + if (!n) + return 0; + + return BS_FUNC(peek_signed_nz)(bc, n); } /** diff --git a/libavcodec/bmp.c b/libavcodec/bmp.c index d7e01f07256..d117c06cf41 100644 --- a/libavcodec/bmp.c +++ b/libavcodec/bmp.c @@ -210,7 +210,7 @@ static int bmp_decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; buf = buf0 + hsize; dsize = buf_size - hsize; diff --git a/libavcodec/bmpenc.c b/libavcodec/bmpenc.c index abf644bd998..3e3ca324eae 100644 --- a/libavcodec/bmpenc.c +++ b/libavcodec/bmpenc.c @@ -161,7 +161,7 @@ const FFCodec ff_bmp_encoder = { CODEC_LONG_NAME("BMP (Windows and OS/2 bitmap)"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_BMP, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .init = bmp_encode_init, FF_CODEC_ENCODE_CB(bmp_encode_frame), .p.pix_fmts = (const enum AVPixelFormat[]){ diff --git a/libavcodec/bmvvideo.c b/libavcodec/bmvvideo.c index 92ce41c8363..20f07ca556f 100644 --- a/libavcodec/bmvvideo.c +++ b/libavcodec/bmvvideo.c @@ -251,7 +251,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, } memcpy(frame->data[1], c->pal, AVPALETTE_SIZE); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = type & BMV_PALETTE; +FF_ENABLE_DEPRECATION_WARNINGS +#endif outptr = frame->data[0]; srcptr = c->frame; diff --git a/libavcodec/bonk.c b/libavcodec/bonk.c index 061cc69a584..fbea91c750f 100644 --- a/libavcodec/bonk.c +++ b/libavcodec/bonk.c @@ -155,6 +155,7 @@ static int intlist_read(BonkContext *s, int *buf, int entries, int base_2_part) int n_zeros = 0, step = 256, dominant = 0; int pos = 0, level = 0; BitCount *bits = s->bits; + int passes = 1; memset(buf, 0, entries * sizeof(*buf)); if (base_2_part) { @@ -216,24 +217,28 @@ static int intlist_read(BonkContext *s, int *buf, int entries, int base_2_part) x = 0; n_zeros = 0; for (i = 0; n_zeros < entries; i++) { + if (x >= max_x) + return AVERROR_INVALIDDATA; + if (pos >= entries) { pos = 0; - level += 1 << low_bits; + level += passes << low_bits; + passes = 1; + if (bits[x].bit && bits[x].count > entries - n_zeros) + passes = bits[x].count / (entries - n_zeros); } if (level > 1 << 16) return AVERROR_INVALIDDATA; - if (x >= max_x) - return AVERROR_INVALIDDATA; - if (buf[pos] >= level) { if (bits[x].bit) - buf[pos] += 1 << low_bits; + buf[pos] += passes << low_bits; else n_zeros++; - bits[x].count--; + av_assert1(bits[x].count >= passes); + bits[x].count -= passes; x += bits[x].count == 0; } @@ -265,14 +270,14 @@ static inline int shift(int a, int b) static int predictor_calc_error(int *k, int *state, int order, int error) { - int i, x = error - shift_down(k[order-1] * state[order-1], LATTICE_SHIFT); + int i, x = error - shift_down(k[order-1] * (unsigned)state[order-1], LATTICE_SHIFT); int *k_ptr = &(k[order-2]), *state_ptr = &(state[order-2]); for (i = order-2; i >= 0; i--, k_ptr--, state_ptr--) { unsigned k_value = *k_ptr, state_value = *state_ptr; - x -= shift_down(k_value * state_value, LATTICE_SHIFT); + x -= shift_down(k_value * (unsigned)state_value, LATTICE_SHIFT); state_ptr[1] = state_value + shift_down(k_value * x, LATTICE_SHIFT); } @@ -330,10 +335,10 @@ static int bonk_decode(AVCodecContext *avctx, AVFrame *frame, frame->nb_samples = FFMIN(s->samples_per_packet * s->down_sampling, s->nb_samples); if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) - return ret; + goto fail; if ((ret = init_get_bits8(gb, buf, buf_size)) < 0) - return ret; + goto fail; skip_bits(gb, s->skip); if ((ret = intlist_read(s, s->k, s->n_taps, 0)) < 0) @@ -423,8 +428,10 @@ const FFCodec ff_bonk_decoder = { FF_CODEC_DECODE_CB(bonk_decode), .close = bonk_close, .p.capabilities = AV_CODEC_CAP_DELAY | - AV_CODEC_CAP_DR1 | - AV_CODEC_CAP_SUBFRAMES, +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_NONE }, diff --git a/libavcodec/brenderpix.c b/libavcodec/brenderpix.c index e95ab3d4afd..70a3e6be2a3 100644 --- a/libavcodec/brenderpix.c +++ b/libavcodec/brenderpix.c @@ -245,7 +245,11 @@ static int pix_decode_frame(AVCodecContext *avctx, AVFrame *frame, *pal_out++ = (0xFFU << 24) | bytestream2_get_be32u(&gb); bytestream2_skip(&gb, 8); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif chunk_type = bytestream2_get_be32(&gb); } else if (avctx->pix_fmt == AV_PIX_FMT_PAL8) { @@ -257,7 +261,11 @@ static int pix_decode_frame(AVCodecContext *avctx, AVFrame *frame, "Using default palette, colors might be off.\n"); memcpy(pal_out, std_pal_table, sizeof(uint32_t) * 256); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } data_len = bytestream2_get_be32(&gb); @@ -278,7 +286,7 @@ static int pix_decode_frame(AVCodecContext *avctx, AVFrame *frame, bytes_per_scanline, hdr.height); frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; diff --git a/libavcodec/bsf.c b/libavcodec/bsf.c index 42cc1b5ab0c..1e710f7d4af 100644 --- a/libavcodec/bsf.c +++ b/libavcodec/bsf.c @@ -31,8 +31,7 @@ #include "bsf_internal.h" #include "codec_desc.h" #include "codec_par.h" - -#define IS_EMPTY(pkt) (!(pkt)->data && !(pkt)->side_data_elems) +#include "packet_internal.h" static av_always_inline const FFBitStreamFilter *ff_bsf(const AVBitStreamFilter *bsf) { @@ -205,7 +204,7 @@ int av_bsf_send_packet(AVBSFContext *ctx, AVPacket *pkt) FFBSFContext *const bsfi = ffbsfcontext(ctx); int ret; - if (!pkt || IS_EMPTY(pkt)) { + if (!pkt || AVPACKET_IS_EMPTY(pkt)) { if (pkt) av_packet_unref(pkt); bsfi->eof = 1; @@ -217,7 +216,7 @@ int av_bsf_send_packet(AVBSFContext *ctx, AVPacket *pkt) return AVERROR(EINVAL); } - if (!IS_EMPTY(bsfi->buffer_pkt)) + if (!AVPACKET_IS_EMPTY(bsfi->buffer_pkt)) return AVERROR(EAGAIN); ret = av_packet_make_refcounted(pkt); @@ -241,7 +240,7 @@ int ff_bsf_get_packet(AVBSFContext *ctx, AVPacket **pkt) if (bsfi->eof) return AVERROR_EOF; - if (IS_EMPTY(bsfi->buffer_pkt)) + if (AVPACKET_IS_EMPTY(bsfi->buffer_pkt)) return AVERROR(EAGAIN); tmp_pkt = av_packet_alloc(); @@ -261,7 +260,7 @@ int ff_bsf_get_packet_ref(AVBSFContext *ctx, AVPacket *pkt) if (bsfi->eof) return AVERROR_EOF; - if (IS_EMPTY(bsfi->buffer_pkt)) + if (AVPACKET_IS_EMPTY(bsfi->buffer_pkt)) return AVERROR(EAGAIN); av_packet_move_ref(pkt, bsfi->buffer_pkt); diff --git a/libavcodec/bytestream.h b/libavcodec/bytestream.h index d0033f14f36..67080604b92 100644 --- a/libavcodec/bytestream.h +++ b/libavcodec/bytestream.h @@ -180,7 +180,7 @@ static av_always_inline void bytestream2_skipu(GetByteContext *g, static av_always_inline void bytestream2_skip_p(PutByteContext *p, unsigned int size) { - int size2; + unsigned int size2; if (p->eof) return; size2 = FFMIN(p->buffer_end - p->buffer, size); @@ -268,7 +268,7 @@ static av_always_inline unsigned int bytestream2_get_buffer(GetByteContext *g, uint8_t *dst, unsigned int size) { - int size2 = FFMIN(g->buffer_end - g->buffer, size); + unsigned int size2 = FFMIN(g->buffer_end - g->buffer, size); memcpy(dst, g->buffer, size2); g->buffer += size2; return size2; @@ -287,7 +287,7 @@ static av_always_inline unsigned int bytestream2_put_buffer(PutByteContext *p, const uint8_t *src, unsigned int size) { - int size2; + unsigned int size2; if (p->eof) return 0; size2 = FFMIN(p->buffer_end - p->buffer, size); @@ -311,7 +311,7 @@ static av_always_inline void bytestream2_set_buffer(PutByteContext *p, const uint8_t c, unsigned int size) { - int size2; + unsigned int size2; if (p->eof) return; size2 = FFMIN(p->buffer_end - p->buffer, size); @@ -348,7 +348,7 @@ static av_always_inline unsigned int bytestream2_copy_buffer(PutByteContext *p, GetByteContext *g, unsigned int size) { - int size2; + unsigned int size2; if (p->eof) return 0; diff --git a/libavcodec/c93.c b/libavcodec/c93.c index bfcbc7c1504..2a4fe459580 100644 --- a/libavcodec/c93.c +++ b/libavcodec/c93.c @@ -147,10 +147,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, b = bytestream2_get_byte(&gb); if (b & C93_FIRST_FRAME) { newpic->pict_type = AV_PICTURE_TYPE_I; - newpic->key_frame = 1; + newpic->flags |= AV_FRAME_FLAG_KEY; } else { newpic->pict_type = AV_PICTURE_TYPE_P; - newpic->key_frame = 0; + newpic->flags &= ~AV_FRAME_FLAG_KEY; } for (y = 0; y < HEIGHT; y += 8) { @@ -246,7 +246,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, for (i = 0; i < 256; i++) { palette[i] = 0xFFU << 24 | bytestream2_get_be24(&gb); } +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS newpic->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } else { if (oldpic->data[1]) memcpy(newpic->data[1], oldpic->data[1], 256 * 4); diff --git a/libavcodec/cavs_parser.c b/libavcodec/cavs_parser.c index 03f392c2e5a..4a03effd0f7 100644 --- a/libavcodec/cavs_parser.c +++ b/libavcodec/cavs_parser.c @@ -59,12 +59,11 @@ static int cavs_find_frame_end(ParseContext *pc, const uint8_t *buf, return 0; for(; i SLICE_MAX_START_CODE){ - pc->frame_start_found=0; - pc->state=-1; - return i-3; - } + if (state == PIC_I_START_CODE || state == PIC_PB_START_CODE || + state == CAVS_START_CODE) { + pc->frame_start_found=0; + pc->state=-1; + return i-3; } } } diff --git a/libavcodec/cavsdec.c b/libavcodec/cavsdec.c index b1fa9a981d4..37071dfbc74 100644 --- a/libavcodec/cavsdec.c +++ b/libavcodec/cavsdec.c @@ -1020,6 +1020,9 @@ static int decode_pic(AVSContext *h) skip_bits(&h->gb, 1); //marker_bit } + if (get_bits_left(&h->gb) < 23) + return AVERROR_INVALIDDATA; + ret = ff_get_buffer(h->avctx, h->cur.f, h->cur.f->pict_type == AV_PICTURE_TYPE_B ? 0 : AV_GET_BUFFER_FLAG_REF); if (ret < 0) diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c index 504197e06d4..3ec8285e21e 100644 --- a/libavcodec/cbs.c +++ b/libavcodec/cbs.c @@ -40,6 +40,9 @@ static const CodedBitstreamType *const cbs_type_table[] = { #if CONFIG_CBS_H265 &ff_cbs_type_h265, #endif +#if CONFIG_CBS_H266 + &ff_cbs_type_h266, +#endif #if CONFIG_CBS_JPEG &ff_cbs_type_jpeg, #endif @@ -61,6 +64,9 @@ const enum AVCodecID ff_cbs_all_codec_ids[] = { #if CONFIG_CBS_H265 AV_CODEC_ID_H265, #endif +#if CONFIG_CBS_H266 + AV_CODEC_ID_H266, +#endif #if CONFIG_CBS_JPEG AV_CODEC_ID_MJPEG, #endif @@ -540,10 +546,13 @@ void ff_cbs_trace_syntax_element(CodedBitstreamContext *ctx, int position, position, name, pad, bits, value); } -int ff_cbs_read_unsigned(CodedBitstreamContext *ctx, GetBitContext *gbc, - int width, const char *name, - const int *subscripts, uint32_t *write_to, - uint32_t range_min, uint32_t range_max) +static av_always_inline int cbs_read_unsigned(CodedBitstreamContext *ctx, + GetBitContext *gbc, + int width, const char *name, + const int *subscripts, + uint32_t *write_to, + uint32_t range_min, + uint32_t range_max) { uint32_t value; int position; @@ -583,6 +592,22 @@ int ff_cbs_read_unsigned(CodedBitstreamContext *ctx, GetBitContext *gbc, return 0; } +int ff_cbs_read_unsigned(CodedBitstreamContext *ctx, GetBitContext *gbc, + int width, const char *name, + const int *subscripts, uint32_t *write_to, + uint32_t range_min, uint32_t range_max) +{ + return cbs_read_unsigned(ctx, gbc, width, name, subscripts, + write_to, range_min, range_max); +} + +int ff_cbs_read_simple_unsigned(CodedBitstreamContext *ctx, GetBitContext *gbc, + int width, const char *name, uint32_t *write_to) +{ + return cbs_read_unsigned(ctx, gbc, width, name, NULL, + write_to, 0, UINT32_MAX); +} + int ff_cbs_write_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc, int width, const char *name, const int *subscripts, uint32_t value, @@ -619,6 +644,13 @@ int ff_cbs_write_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc, return 0; } +int ff_cbs_write_simple_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc, + int width, const char *name, uint32_t value) +{ + return ff_cbs_write_unsigned(ctx, pbc, width, name, NULL, + value, 0, MAX_UINT_BITS(width)); +} + int ff_cbs_read_signed(CodedBitstreamContext *ctx, GetBitContext *gbc, int width, const char *name, const int *subscripts, int32_t *write_to, @@ -1026,3 +1058,24 @@ int ff_cbs_make_unit_writable(CodedBitstreamContext *ctx, av_buffer_unref(&ref); return 0; } + +void ff_cbs_discard_units(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + enum AVDiscard skip, + int flags) +{ + if (!ctx->codec->discarded_unit) + return; + + for (int i = frag->nb_units - 1; i >= 0; i--) { + if (ctx->codec->discarded_unit(ctx, &frag->units[i], skip)) { + // discard all units + if (!(flags & DISCARD_FLAG_KEEP_NON_VCL)) { + ff_cbs_fragment_free(frag); + return; + } + + ff_cbs_delete_unit(frag, i); + } + } +} diff --git a/libavcodec/cbs.h b/libavcodec/cbs.h index ee21623dacb..b4131db5fe2 100644 --- a/libavcodec/cbs.h +++ b/libavcodec/cbs.h @@ -26,6 +26,7 @@ #include "codec_id.h" #include "codec_par.h" +#include "defs.h" #include "packet.h" @@ -432,5 +433,21 @@ int ff_cbs_make_unit_refcounted(CodedBitstreamContext *ctx, int ff_cbs_make_unit_writable(CodedBitstreamContext *ctx, CodedBitstreamUnit *unit); +enum CbsDiscardFlags { + DISCARD_FLAG_NONE = 0, + + /** + * keep non-vcl units even if the picture has been dropped. + */ + DISCARD_FLAG_KEEP_NON_VCL = 0x01, +}; + +/** + * Discard units accroding to 'skip'. + */ +void ff_cbs_discard_units(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + enum AVDiscard skip, + int flags); #endif /* AVCODEC_CBS_H */ diff --git a/libavcodec/cbs_av1.c b/libavcodec/cbs_av1.c index 45e1288a519..452e022b368 100644 --- a/libavcodec/cbs_av1.c +++ b/libavcodec/cbs_av1.c @@ -412,9 +412,8 @@ static int cbs_av1_read_subexp(CodedBitstreamContext *ctx, GetBitContext *gbc, } if (len < max_len) { - err = ff_cbs_read_unsigned(ctx, gbc, range_bits, - "subexp_bits", NULL, &value, - 0, MAX_UINT_BITS(range_bits)); + err = ff_cbs_read_simple_unsigned(ctx, gbc, range_bits, + "subexp_bits", &value); if (err < 0) return err; @@ -476,10 +475,9 @@ static int cbs_av1_write_subexp(CodedBitstreamContext *ctx, PutBitContext *pbc, return err; if (len < max_len) { - err = ff_cbs_write_unsigned(ctx, pbc, range_bits, - "subexp_bits", NULL, - value - range_offset, - 0, MAX_UINT_BITS(range_bits)); + err = ff_cbs_write_simple_unsigned(ctx, pbc, range_bits, + "subexp_bits", + value - range_offset); if (err < 0) return err; @@ -546,8 +544,6 @@ static size_t cbs_av1_get_payload_bytes_left(GetBitContext *gbc) #define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]){ subs, __VA_ARGS__ }) : NULL) -#define fb(width, name) \ - xf(width, name, current->name, 0, MAX_UINT_BITS(width), 0, ) #define fc(width, name, range_min, range_max) \ xf(width, name, current->name, range_min, range_max, 0, ) #define flag(name) fb(1, name) @@ -573,6 +569,13 @@ static size_t cbs_av1_get_payload_bytes_left(GetBitContext *gbc) #define READWRITE read #define RWContext GetBitContext +#define fb(width, name) do { \ + uint32_t value; \ + CHECK(ff_cbs_read_simple_unsigned(ctx, rw, width, \ + #name, &value)); \ + current->name = value; \ + } while (0) + #define xf(width, name, var, range_min, range_max, subs, ...) do { \ uint32_t value; \ CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \ @@ -645,6 +648,7 @@ static size_t cbs_av1_get_payload_bytes_left(GetBitContext *gbc) #undef READ #undef READWRITE #undef RWContext +#undef fb #undef xf #undef xsu #undef uvlc @@ -661,6 +665,11 @@ static size_t cbs_av1_get_payload_bytes_left(GetBitContext *gbc) #define READWRITE write #define RWContext PutBitContext +#define fb(width, name) do { \ + CHECK(ff_cbs_write_simple_unsigned(ctx, rw, width, #name, \ + current->name)); \ + } while (0) + #define xf(width, name, var, range_min, range_max, subs, ...) do { \ CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \ SUBSCRIPTS(subs, __VA_ARGS__), \ @@ -723,6 +732,7 @@ static size_t cbs_av1_get_payload_bytes_left(GetBitContext *gbc) #undef WRITE #undef READWRITE #undef RWContext +#undef fb #undef xf #undef xsu #undef uvlc @@ -1306,9 +1316,16 @@ static void cbs_av1_free_metadata(void *unit, uint8_t *content) md = &obu->obu.metadata; switch (md->metadata_type) { + case AV1_METADATA_TYPE_HDR_CLL: + case AV1_METADATA_TYPE_HDR_MDCV: + case AV1_METADATA_TYPE_SCALABILITY: + case AV1_METADATA_TYPE_TIMECODE: + break; case AV1_METADATA_TYPE_ITUT_T35: av_buffer_unref(&md->metadata.itut_t35.payload_ref); break; + default: + av_buffer_unref(&md->metadata.unknown.payload_ref); } av_free(content); } diff --git a/libavcodec/cbs_av1.h b/libavcodec/cbs_av1.h index 1fc80dcfa05..64dfdce9c4e 100644 --- a/libavcodec/cbs_av1.h +++ b/libavcodec/cbs_av1.h @@ -215,6 +215,8 @@ typedef struct AV1RawFrameHeader { uint8_t uniform_tile_spacing_flag; uint8_t tile_cols_log2; uint8_t tile_rows_log2; + uint8_t tile_start_col_sb[AV1_MAX_TILE_COLS]; + uint8_t tile_start_row_sb[AV1_MAX_TILE_COLS]; uint8_t width_in_sbs_minus_1[AV1_MAX_TILE_COLS]; uint8_t height_in_sbs_minus_1[AV1_MAX_TILE_ROWS]; uint16_t context_update_tile_id; @@ -370,6 +372,12 @@ typedef struct AV1RawMetadataTimecode { uint32_t time_offset_value; } AV1RawMetadataTimecode; +typedef struct AV1RawMetadataUnknown { + uint8_t *payload; + AVBufferRef *payload_ref; + size_t payload_size; +} AV1RawMetadataUnknown; + typedef struct AV1RawMetadata { uint64_t metadata_type; union { @@ -378,6 +386,7 @@ typedef struct AV1RawMetadata { AV1RawMetadataScalability scalability; AV1RawMetadataITUTT35 itut_t35; AV1RawMetadataTimecode timecode; + AV1RawMetadataUnknown unknown; } metadata; } AV1RawMetadata; diff --git a/libavcodec/cbs_av1_syntax_template.c b/libavcodec/cbs_av1_syntax_template.c index d98d3d42dea..a747e17784f 100644 --- a/libavcodec/cbs_av1_syntax_template.c +++ b/libavcodec/cbs_av1_syntax_template.c @@ -176,7 +176,7 @@ static int FUNC(decoder_model_info)(CodedBitstreamContext *ctx, RWContext *rw, int err; fb(5, buffer_delay_length_minus_1); - fb(32, num_units_in_decoding_tick); + fc(32, num_units_in_decoding_tick, 1, MAX_UINT_BITS(32)); fb(5, buffer_removal_time_length_minus_1); fb(5, frame_presentation_time_length_minus_1); @@ -626,6 +626,10 @@ static int FUNC(tile_info)(CodedBitstreamContext *ctx, RWContext *rw, tile_width_sb = (sb_cols + (1 << current->tile_cols_log2) - 1) >> current->tile_cols_log2; + + for (int off = 0, i = 0; off < sb_cols; off += tile_width_sb) + current->tile_start_col_sb[i++] = off; + current->tile_cols = (sb_cols + tile_width_sb - 1) / tile_width_sb; min_log2_tile_rows = FFMAX(min_log2_tiles - current->tile_cols_log2, 0); @@ -634,6 +638,10 @@ static int FUNC(tile_info)(CodedBitstreamContext *ctx, RWContext *rw, tile_height_sb = (sb_rows + (1 << current->tile_rows_log2) - 1) >> current->tile_rows_log2; + + for (int off = 0, i = 0; off < sb_rows; off += tile_height_sb) + current->tile_start_row_sb[i++] = off; + current->tile_rows = (sb_rows + tile_height_sb - 1) / tile_height_sb; for (i = 0; i < current->tile_cols - 1; i++) @@ -652,6 +660,7 @@ static int FUNC(tile_info)(CodedBitstreamContext *ctx, RWContext *rw, start_sb = 0; for (i = 0; start_sb < sb_cols && i < AV1_MAX_TILE_COLS; i++) { + current->tile_start_col_sb[i] = start_sb; max_width = FFMIN(sb_cols - start_sb, max_tile_width_sb); ns(max_width, width_in_sbs_minus_1[i], 1, i); size_sb = current->width_in_sbs_minus_1[i] + 1; @@ -669,6 +678,7 @@ static int FUNC(tile_info)(CodedBitstreamContext *ctx, RWContext *rw, start_sb = 0; for (i = 0; start_sb < sb_rows && i < AV1_MAX_TILE_ROWS; i++) { + current->tile_start_row_sb[i] = start_sb; max_height = FFMIN(sb_rows - start_sb, max_tile_height_sb); ns(max_height, height_in_sbs_minus_1[i], 1, i); size_sb = current->height_in_sbs_minus_1[i] + 1; @@ -1843,6 +1853,8 @@ static int FUNC(metadata_hdr_cll)(CodedBitstreamContext *ctx, RWContext *rw, { int err; + HEADER("HDR CLL Metadata"); + fb(16, max_cll); fb(16, max_fall); @@ -1854,6 +1866,8 @@ static int FUNC(metadata_hdr_mdcv)(CodedBitstreamContext *ctx, RWContext *rw, { int err, i; + HEADER("HDR MDCV Metadata"); + for (i = 0; i < 3; i++) { fbs(16, primary_chromaticity_x[i], 1, i); fbs(16, primary_chromaticity_y[i], 1, i); @@ -1862,11 +1876,8 @@ static int FUNC(metadata_hdr_mdcv)(CodedBitstreamContext *ctx, RWContext *rw, fb(16, white_point_chromaticity_x); fb(16, white_point_chromaticity_y); - fc(32, luminance_max, 1, MAX_UINT_BITS(32)); - // luminance_min must be lower than luminance_max. Convert luminance_max from - // 24.8 fixed point to 18.14 fixed point in order to compare them. - fc(32, luminance_min, 0, FFMIN(((uint64_t)current->luminance_max << 6) - 1, - MAX_UINT_BITS(32))); + fb(32, luminance_max); + fb(32, luminance_min); return 0; } @@ -1923,6 +1934,8 @@ static int FUNC(metadata_scalability)(CodedBitstreamContext *ctx, RWContext *rw, { int err; + HEADER("Scalability Metadata"); + fb(8, scalability_mode_idc); if (current->scalability_mode_idc == AV1_SCALABILITY_SS) @@ -1937,6 +1950,8 @@ static int FUNC(metadata_itut_t35)(CodedBitstreamContext *ctx, RWContext *rw, int err; size_t i; + HEADER("ITU-T T.35 Metadata"); + fb(8, itu_t_t35_country_code); if (current->itu_t_t35_country_code == 0xff) fb(8, itu_t_t35_country_code_extension_byte); @@ -1964,6 +1979,8 @@ static int FUNC(metadata_timecode)(CodedBitstreamContext *ctx, RWContext *rw, { int err; + HEADER("Timecode Metadata"); + fb(5, counting_type); flag(full_timestamp_flag); flag(discontinuity_flag); @@ -1997,6 +2014,29 @@ static int FUNC(metadata_timecode)(CodedBitstreamContext *ctx, RWContext *rw, return 0; } +static int FUNC(metadata_unknown)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawMetadataUnknown *current) +{ + int err; + size_t i; + + HEADER("Unknown Metadata"); + +#ifdef READ + current->payload_size = cbs_av1_get_payload_bytes_left(rw); + + current->payload_ref = av_buffer_alloc(current->payload_size); + if (!current->payload_ref) + return AVERROR(ENOMEM); + current->payload = current->payload_ref->data; +#endif + + for (i = 0; i < current->payload_size; i++) + fbs(8, payload[i], 1, i); + + return 0; +} + static int FUNC(metadata_obu)(CodedBitstreamContext *ctx, RWContext *rw, AV1RawMetadata *current) { @@ -2021,8 +2061,7 @@ static int FUNC(metadata_obu)(CodedBitstreamContext *ctx, RWContext *rw, CHECK(FUNC(metadata_timecode)(ctx, rw, ¤t->metadata.timecode)); break; default: - // Unknown metadata type. - return AVERROR_PATCHWELCOME; + CHECK(FUNC(metadata_unknown)(ctx, rw, ¤t->metadata.unknown)); } return 0; diff --git a/libavcodec/cbs_h2645.c b/libavcodec/cbs_h2645.c index 80e48829af9..318c997d943 100644 --- a/libavcodec/cbs_h2645.c +++ b/libavcodec/cbs_h2645.c @@ -24,9 +24,11 @@ #include "cbs_internal.h" #include "cbs_h264.h" #include "cbs_h265.h" +#include "cbs_h266.h" #include "h264.h" #include "h2645_parse.h" #include "hevc.h" +#include "vvc.h" static int cbs_read_ue_golomb(CodedBitstreamContext *ctx, GetBitContext *gbc, @@ -255,14 +257,13 @@ static int cbs_h265_payload_extension_present(GetBitContext *gbc, uint32_t paylo #define FUNC_NAME1(rw, codec, name) FUNC_NAME2(rw, codec, name) #define FUNC_H264(name) FUNC_NAME1(READWRITE, h264, name) #define FUNC_H265(name) FUNC_NAME1(READWRITE, h265, name) +#define FUNC_H266(name) FUNC_NAME1(READWRITE, h266, name) #define FUNC_SEI(name) FUNC_NAME1(READWRITE, sei, name) #define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]){ subs, __VA_ARGS__ }) : NULL) #define u(width, name, range_min, range_max) \ xu(width, name, current->name, range_min, range_max, 0, ) -#define ub(width, name) \ - xu(width, name, current->name, 0, MAX_UINT_BITS(width), 0, ) #define flag(name) ub(1, name) #define ue(name, range_min, range_max) \ xue(name, current->name, range_min, range_max, 0, ) @@ -298,6 +299,12 @@ static int cbs_h265_payload_extension_present(GetBitContext *gbc, uint32_t paylo #define READWRITE read #define RWContext GetBitContext +#define ub(width, name) do { \ + uint32_t value; \ + CHECK(ff_cbs_read_simple_unsigned(ctx, rw, width, #name, \ + &value)); \ + current->name = value; \ + } while (0) #define xu(width, name, var, range_min, range_max, subs, ...) do { \ uint32_t value; \ CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \ @@ -369,9 +376,14 @@ static int cbs_h2645_read_more_rbsp_data(GetBitContext *gbc) #include "cbs_h265_syntax_template.c" #undef FUNC +#define FUNC(name) FUNC_H266(name) +#include "cbs_h266_syntax_template.c" +#undef FUNC + #undef READ #undef READWRITE #undef RWContext +#undef ub #undef xu #undef xi #undef xue @@ -387,6 +399,11 @@ static int cbs_h2645_read_more_rbsp_data(GetBitContext *gbc) #define READWRITE write #define RWContext PutBitContext +#define ub(width, name) do { \ + uint32_t value = current->name; \ + CHECK(ff_cbs_write_simple_unsigned(ctx, rw, width, #name, \ + value)); \ + } while (0) #define xu(width, name, var, range_min, range_max, subs, ...) do { \ uint32_t value = var; \ CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \ @@ -447,9 +464,14 @@ static int cbs_h2645_read_more_rbsp_data(GetBitContext *gbc) #include "cbs_h265_syntax_template.c" #undef FUNC +#define FUNC(name) FUNC_H266(name) +#include "cbs_h266_syntax_template.c" +#undef FUNC + #undef WRITE #undef READWRITE #undef RWContext +#undef ub #undef xu #undef xi #undef xue @@ -476,8 +498,9 @@ static int cbs_h2645_fragment_add_nals(CodedBitstreamContext *ctx, const H2645NAL *nal = &packet->nals[i]; AVBufferRef *ref; size_t size = nal->size; + enum AVCodecID codec_id = ctx->codec->codec_id; - if (nal->nuh_layer_id > 0) + if (codec_id != AV_CODEC_ID_VVC && nal->nuh_layer_id > 0) continue; // Remove trailing zeroes. @@ -640,6 +663,71 @@ static int cbs_h2645_split_fragment(CodedBitstreamContext *ctx, return err; } + } else if(header && frag->data[0] && codec_id == AV_CODEC_ID_VVC) { + // VVCC header. + int ptl_present_flag, num_arrays; + int b, i, j; + + priv->mp4 = 1; + + bytestream2_init(&gbc, frag->data, frag->data_size); + + b = bytestream2_get_byte(&gbc); + priv->nal_length_size = ((b >> 1) & 3) + 1; + ptl_present_flag = b & 1; + + if(ptl_present_flag) { + int num_sublayers, num_bytes_constraint_info, num_sub_profiles; + num_sublayers = (bytestream2_get_be16u(&gbc) >> 4) & 7; + bytestream2_skip(&gbc, 1); + + // begin VvcPTLRecord(num_sublayers); + num_bytes_constraint_info = bytestream2_get_byte(&gbc) & 0x3f; + bytestream2_skip(&gbc, 2 + num_bytes_constraint_info); + if(num_sublayers > 1) { + int count_present_flags = 0; + b = bytestream2_get_byte(&gbc); + for(i = num_sublayers - 2; i >= 0; i--) { + if((b >> (7 - (num_sublayers - 2 - i))) & 0x01) + count_present_flags++; + } + bytestream2_skip(&gbc, count_present_flags); + } + num_sub_profiles = bytestream2_get_byte(&gbc); + bytestream2_skip(&gbc, num_sub_profiles * 4); + // end VvcPTLRecord(num_sublayers); + + bytestream2_skip(&gbc, 3 * 2); + } + + num_arrays = bytestream2_get_byte(&gbc); + for(j = 0; j < num_arrays; j++) { + size_t start, end, size; + int nal_unit_type = bytestream2_get_byte(&gbc) & 0x1f; + unsigned int num_nalus = 1; + if(nal_unit_type != VVC_DCI_NUT && nal_unit_type != VVC_OPI_NUT) + num_nalus = bytestream2_get_be16(&gbc); + + start = bytestream2_tell(&gbc); + for(i = 0; i < num_nalus; i++) { + size = bytestream2_get_be16(&gbc); + bytestream2_skip(&gbc, size); + } + end = bytestream2_tell(&gbc); + + err = ff_h2645_packet_split(&priv->read_packet, + frag->data + start, end - start, + ctx->log_ctx, 1, 2, AV_CODEC_ID_VVC, 1, 1); + if (err < 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Failed to split " + "VVCC array %d (%d NAL units of type %d).\n", + i, num_nalus, nal_unit_type); + return err; + } + err = cbs_h2645_fragment_add_nals(ctx, frag, &priv->read_packet); + if (err < 0) + return err; + } } else { // Annex B, or later MP4 with already-known parameters. @@ -686,6 +774,47 @@ cbs_h2645_replace_ps(5, VPS, vps, vps_video_parameter_set_id) cbs_h2645_replace_ps(5, SPS, sps, sps_seq_parameter_set_id) cbs_h2645_replace_ps(5, PPS, pps, pps_pic_parameter_set_id) +#define cbs_h266_replace_ps(h26n, ps_name, ps_var, id_element) \ +static int cbs_h26 ## h26n ## _replace_ ## ps_var(CodedBitstreamContext *ctx, \ + CodedBitstreamUnit *unit) \ +{ \ + CodedBitstreamH26 ## h26n ## Context *priv = ctx->priv_data; \ + H26 ## h26n ## Raw ## ps_name *ps_var = unit->content; \ + unsigned int id = ps_var->id_element; \ + int err = ff_cbs_make_unit_refcounted(ctx, unit); \ + if (err < 0) \ + return err; \ + av_buffer_unref(&priv->ps_var ## _ref[id]); \ + av_assert0(unit->content_ref); \ + priv->ps_var ## _ref[id] = av_buffer_ref(unit->content_ref); \ + if (!priv->ps_var ## _ref[id]) \ + return AVERROR(ENOMEM); \ + priv->ps_var[id] = (H26 ## h26n ## Raw ## ps_name *)priv->ps_var ## _ref[id]->data; \ + return 0; \ +} + +cbs_h266_replace_ps(6, VPS, vps, vps_video_parameter_set_id) +cbs_h266_replace_ps(6, SPS, sps, sps_seq_parameter_set_id) +cbs_h266_replace_ps(6, PPS, pps, pps_pic_parameter_set_id) + +static int cbs_h266_replace_ph(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit, + H266RawPictureHeader *ph) +{ + CodedBitstreamH266Context *h266 = ctx->priv_data; + int err; + + err = ff_cbs_make_unit_refcounted(ctx, unit); + if (err < 0) + return err; + av_assert0(unit->content_ref); + err = av_buffer_replace(&h266->ph_ref, unit->content_ref); + if (err < 0) + return err; + h266->ph = ph; + return 0; +} + static int cbs_h264_read_nal_unit(CodedBitstreamContext *ctx, CodedBitstreamUnit *unit) { @@ -926,6 +1055,162 @@ static int cbs_h265_read_nal_unit(CodedBitstreamContext *ctx, return 0; } +static int cbs_h266_read_nal_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit) +{ + GetBitContext gbc; + int err; + + err = init_get_bits8(&gbc, unit->data, unit->data_size); + if (err < 0) + return err; + + err = ff_cbs_alloc_unit_content(ctx, unit); + if (err < 0) + return err; + + switch (unit->type) { + case VVC_DCI_NUT: + { + err = cbs_h266_read_dci(ctx, &gbc, unit->content); + + if (err < 0) + return err; + } + break; + case VVC_OPI_NUT: + { + err = cbs_h266_read_opi(ctx, &gbc, unit->content); + + if (err < 0) + return err; + } + break; + case VVC_VPS_NUT: + { + H266RawVPS *vps = unit->content; + + err = cbs_h266_read_vps(ctx, &gbc, vps); + if (err < 0) + return err; + + err = cbs_h266_replace_vps(ctx, unit); + if (err < 0) + return err; + } + break; + case VVC_SPS_NUT: + { + H266RawSPS *sps = unit->content; + + err = cbs_h266_read_sps(ctx, &gbc, sps); + if (err < 0) + return err; + + err = cbs_h266_replace_sps(ctx, unit); + if (err < 0) + return err; + } + break; + + case VVC_PPS_NUT: + { + H266RawPPS *pps = unit->content; + + err = cbs_h266_read_pps(ctx, &gbc, pps); + if (err < 0) + return err; + + err = cbs_h266_replace_pps(ctx, unit); + if (err < 0) + return err; + } + break; + + case VVC_PREFIX_APS_NUT: + case VVC_SUFFIX_APS_NUT: + { + err = cbs_h266_read_aps(ctx, &gbc, unit->content, + unit->type == VVC_PREFIX_APS_NUT); + + if (err < 0) + return err; + } + break; + case VVC_PH_NUT: + { + H266RawPH *ph = unit->content; + err = cbs_h266_read_ph(ctx, &gbc, ph); + if (err < 0) + return err; + err = cbs_h266_replace_ph(ctx, unit, &ph->ph_picture_header); + if (err < 0) + return err; + } + break; + + case VVC_TRAIL_NUT: + case VVC_STSA_NUT: + case VVC_RADL_NUT: + case VVC_RASL_NUT: + case VVC_IDR_W_RADL: + case VVC_IDR_N_LP: + case VVC_CRA_NUT: + case VVC_GDR_NUT: + { + H266RawSlice *slice = unit->content; + int pos, len; + + err = cbs_h266_read_slice_header(ctx, &gbc, &slice->header); + if (err < 0) + return err; + + if (!cbs_h2645_read_more_rbsp_data(&gbc)) + return AVERROR_INVALIDDATA; + + pos = get_bits_count(&gbc); + len = unit->data_size; + + if (slice->header.sh_picture_header_in_slice_header_flag) { + err = cbs_h266_replace_ph(ctx, unit, &slice->header.sh_picture_header); + if (err < 0) + return err; + } + + slice->data_size = len - pos / 8; + slice->data_ref = av_buffer_ref(unit->data_ref); + if (!slice->data_ref) + return AVERROR(ENOMEM); + slice->data = unit->data + pos / 8; + slice->data_bit_start = pos % 8; + } + break; + + case VVC_AUD_NUT: + { + err = cbs_h266_read_aud(ctx, &gbc, unit->content); + if (err < 0) + return err; + } + break; + + case VVC_PREFIX_SEI_NUT: + case VVC_SUFFIX_SEI_NUT: + { + err = cbs_h266_read_sei(ctx, &gbc, unit->content, + unit->type == VVC_PREFIX_SEI_NUT); + + if (err < 0) + return err; + } + break; + + default: + return AVERROR(ENOSYS); + } + return 0; +} + static int cbs_h2645_write_slice_data(CodedBitstreamContext *ctx, PutBitContext *pbc, const uint8_t *data, size_t data_size, int data_bit_start) @@ -1213,11 +1498,285 @@ static int cbs_h265_write_nal_unit(CodedBitstreamContext *ctx, return 0; } +static int cbs_h264_discarded_nal_unit(CodedBitstreamContext *ctx, + const CodedBitstreamUnit *unit, + enum AVDiscard skip) +{ + H264RawNALUnitHeader *header; + H264RawSliceHeader *slice; + int slice_type_i, slice_type_b, slice_type_si; + + if (skip <= AVDISCARD_DEFAULT) + return 0; + + // keep non-VCL + if (unit->type != H264_NAL_SLICE && + unit->type != H264_NAL_IDR_SLICE && + unit->type != H264_NAL_AUXILIARY_SLICE) + return 0; + + if (skip >= AVDISCARD_ALL) + return 1; + + if (skip >= AVDISCARD_NONKEY && unit->type != H264_NAL_IDR_SLICE) + return 1; + + header = (H264RawNALUnitHeader *)unit->content; + if (!header) { + av_log(ctx->log_ctx, AV_LOG_WARNING, + "h264 nal unit header is null, missing decompose?\n"); + return 0; + } + + if (skip >= AVDISCARD_NONREF && !header->nal_ref_idc) + return 1; + + slice = (H264RawSliceHeader *)unit->content; + if (!slice) { + av_log(ctx->log_ctx, AV_LOG_WARNING, + "h264 slice header is null, missing decompose?\n"); + return 0; + } + + slice_type_i = slice->slice_type % 5 == 2; + slice_type_b = slice->slice_type % 5 == 1; + slice_type_si = slice->slice_type % 5 == 4; + + if (skip >= AVDISCARD_BIDIR && slice_type_b) + return 1; + if (skip >= AVDISCARD_NONINTRA && !slice_type_i && !slice_type_si) + return 1; + + return 0; +} + +static int cbs_h265_discarded_nal_unit(CodedBitstreamContext *ctx, + const CodedBitstreamUnit *unit, + enum AVDiscard skip) +{ + H265RawSliceHeader *slice; + + if (skip <= AVDISCARD_DEFAULT) + return 0; + + switch (unit->type) { + 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: + // IRAP slice + if (skip < AVDISCARD_ALL) + return 0; + 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_RADL_N: + case HEVC_NAL_RADL_R: + case HEVC_NAL_RASL_N: + case HEVC_NAL_RASL_R: + // Slice + break; + default: + // Don't discard non-slice nal. + return 0; + } + + if (skip >= AVDISCARD_NONKEY) + return 1; + + slice = (H265RawSliceHeader *)unit->content; + if (!slice) { + av_log(ctx->log_ctx, AV_LOG_WARNING, + "h265 slice header is null, missing decompose?\n"); + return 0; + } + + if (skip >= AVDISCARD_NONINTRA && slice->slice_type != HEVC_SLICE_I) + return 1; + if (skip >= AVDISCARD_BIDIR && slice->slice_type == HEVC_SLICE_B) + return 1; + + if (skip >= AVDISCARD_NONREF) { + switch (unit->type) { + case HEVC_NAL_TRAIL_N: + case HEVC_NAL_TSA_N: + case HEVC_NAL_STSA_N: + case HEVC_NAL_RADL_N: + case HEVC_NAL_RASL_N: + case HEVC_NAL_VCL_N10: + case HEVC_NAL_VCL_N12: + case HEVC_NAL_VCL_N14: + // non-ref + return 1; + default: + break; + } + } + + return 0; +} + +static int cbs_h266_write_nal_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit, + PutBitContext *pbc) +{ + int err; + + switch (unit->type) { + case VVC_DCI_NUT: + { + H266RawDCI *dci = unit->content; + + err = cbs_h266_write_dci(ctx, pbc, dci); + if (err < 0) + return err; + } + break; + case VVC_OPI_NUT: + { + H266RawOPI *opi = unit->content; + + err = cbs_h266_write_opi(ctx, pbc, opi); + if (err < 0) + return err; + } + break; + case VVC_VPS_NUT: + { + H266RawVPS *vps = unit->content; + + err = cbs_h266_write_vps(ctx, pbc, vps); + if (err < 0) + return err; + + err = cbs_h266_replace_vps(ctx, unit); + if (err < 0) + return err; + } + break; + case VVC_SPS_NUT: + { + H266RawSPS *sps = unit->content; + + err = cbs_h266_write_sps(ctx, pbc, sps); + if (err < 0) + return err; + + err = cbs_h266_replace_sps(ctx, unit); + if (err < 0) + return err; + } + break; + + case VVC_PPS_NUT: + { + H266RawPPS *pps = unit->content; + + err = cbs_h266_write_pps(ctx, pbc, pps); + if (err < 0) + return err; + + err = cbs_h266_replace_pps(ctx, unit); + if (err < 0) + return err; + } + break; + + case VVC_PREFIX_APS_NUT: + case VVC_SUFFIX_APS_NUT: + { + err = cbs_h266_write_aps(ctx, pbc, unit->content, + unit->type == VVC_PREFIX_APS_NUT); + if (err < 0) + return err; + } + break; + case VVC_PH_NUT: + { + H266RawPH *ph = unit->content; + err = cbs_h266_write_ph(ctx, pbc, ph); + if (err < 0) + return err; + + err = cbs_h266_replace_ph(ctx, unit, &ph->ph_picture_header); + if (err < 0) + return err; + } + break; + + case VVC_TRAIL_NUT: + case VVC_STSA_NUT: + case VVC_RADL_NUT: + case VVC_RASL_NUT: + case VVC_IDR_W_RADL: + case VVC_IDR_N_LP: + case VVC_CRA_NUT: + case VVC_GDR_NUT: + { + H266RawSlice *slice = unit->content; + + err = cbs_h266_write_slice_header(ctx, pbc, &slice->header); + if (err < 0) + return err; + + if (slice->header.sh_picture_header_in_slice_header_flag) { + err = cbs_h266_replace_ph(ctx, unit, &slice->header.sh_picture_header); + if (err < 0) + return err; + } + + if (slice->data) { + err = cbs_h2645_write_slice_data(ctx, pbc, slice->data, + slice->data_size, + slice->data_bit_start); + if (err < 0) + return err; + } else { + // No slice data - that was just the header. + } + } + break; + + case VVC_AUD_NUT: + { + err = cbs_h266_write_aud(ctx, pbc, unit->content); + if (err < 0) + return err; + } + break; + + case VVC_PREFIX_SEI_NUT: + case VVC_SUFFIX_SEI_NUT: + { + err = cbs_h266_write_sei(ctx, pbc, unit->content, + unit->type == VVC_PREFIX_SEI_NUT); + + if (err < 0) + return err; + } + break; + + default: + av_log(ctx->log_ctx, AV_LOG_ERROR, "Write unimplemented for " + "NAL unit type %"PRIu32".\n", unit->type); + return AVERROR_PATCHWELCOME; + } + + return 0; +} + static int cbs_h2645_unit_requires_zero_byte(enum AVCodecID codec_id, CodedBitstreamUnitType type, int nal_unit_index) { - // Section B.1.2 in H.264, section B.2.2 in H.265. + // Section B.1.2 in H.264, section B.2.2 in H.265, H.266. if (nal_unit_index == 0) { // Assume that this is the first NAL unit in an access unit. return 1; @@ -1226,6 +1785,8 @@ static int cbs_h2645_unit_requires_zero_byte(enum AVCodecID codec_id, return type == H264_NAL_SPS || type == H264_NAL_PPS; if (codec_id == AV_CODEC_ID_HEVC) return type == HEVC_NAL_VPS || type == HEVC_NAL_SPS || type == HEVC_NAL_PPS; + if (codec_id == AV_CODEC_ID_VVC) + return type >= VVC_OPI_NUT && type <= VVC_SUFFIX_APS_NUT; return 0; } @@ -1377,6 +1938,35 @@ static void cbs_h265_close(CodedBitstreamContext *ctx) av_buffer_unref(&h265->pps_ref[i]); } +static void cbs_h266_flush(CodedBitstreamContext *ctx) +{ + CodedBitstreamH266Context *h266 = ctx->priv_data; + + for (int i = 0; i < FF_ARRAY_ELEMS(h266->vps); i++) { + av_buffer_unref(&h266->vps_ref[i]); + h266->vps[i] = NULL; + } + + for (int i = 0; i < FF_ARRAY_ELEMS(h266->sps); i++) { + av_buffer_unref(&h266->sps_ref[i]); + h266->sps[i] = NULL; + } + for (int i = 0; i < FF_ARRAY_ELEMS(h266->pps); i++) { + av_buffer_unref(&h266->pps_ref[i]); + h266->pps[i] = NULL; + } + av_buffer_unref(&h266->ph_ref); + h266->ph = NULL; +} + +static void cbs_h266_close(CodedBitstreamContext *ctx) +{ + CodedBitstreamH266Context *h266 = ctx->priv_data; + + cbs_h266_flush(ctx); + ff_h2645_packet_uninit(&h266->common.read_packet); + } + static void cbs_h264_free_sei(void *opaque, uint8_t *content) { H264RawSEI *sei = (H264RawSEI*)content; @@ -1431,6 +2021,37 @@ static const CodedBitstreamUnitTypeDescriptor cbs_h265_unit_types[] = { CBS_UNIT_TYPE_END_OF_LIST }; +static void cbs_h266_free_sei(void *opaque, uint8_t *content) +{ + H266RawSEI *sei = (H266RawSEI*)content; + ff_cbs_sei_free_message_list(&sei->message_list); + av_free(content); +} + +static const CodedBitstreamUnitTypeDescriptor cbs_h266_unit_types[] = { + CBS_UNIT_TYPE_INTERNAL_REF(VVC_DCI_NUT, H266RawDCI, extension_data.data), + CBS_UNIT_TYPE_INTERNAL_REF(VVC_OPI_NUT, H266RawOPI, extension_data.data), + CBS_UNIT_TYPE_INTERNAL_REF(VVC_VPS_NUT, H266RawVPS, extension_data.data), + CBS_UNIT_TYPE_INTERNAL_REF(VVC_SPS_NUT, H266RawSPS, extension_data.data), + CBS_UNIT_TYPE_INTERNAL_REF(VVC_PPS_NUT, H266RawPPS, extension_data.data), + CBS_UNIT_TYPE_INTERNAL_REF(VVC_PREFIX_APS_NUT, H266RawAPS, extension_data.data), + CBS_UNIT_TYPE_INTERNAL_REF(VVC_SUFFIX_APS_NUT, H266RawAPS, extension_data.data), + + CBS_UNIT_TYPE_POD(VVC_PH_NUT , H266RawPH), + CBS_UNIT_TYPE_POD(VVC_AUD_NUT, H266RawAUD), + + CBS_UNIT_RANGE_INTERNAL_REF(VVC_TRAIL_NUT, VVC_RASL_NUT, + H266RawSlice, data), + + CBS_UNIT_RANGE_INTERNAL_REF(VVC_IDR_W_RADL, VVC_GDR_NUT, + H266RawSlice, data), + + CBS_UNIT_TYPES_COMPLEX((VVC_PREFIX_SEI_NUT, VVC_SUFFIX_SEI_NUT), + H266RawSEI, cbs_h266_free_sei), + + CBS_UNIT_TYPE_END_OF_LIST +}; + const CodedBitstreamType ff_cbs_type_h264 = { .codec_id = AV_CODEC_ID_H264, @@ -1441,6 +2062,7 @@ const CodedBitstreamType ff_cbs_type_h264 = { .split_fragment = &cbs_h2645_split_fragment, .read_unit = &cbs_h264_read_nal_unit, .write_unit = &cbs_h264_write_nal_unit, + .discarded_unit = &cbs_h264_discarded_nal_unit, .assemble_fragment = &cbs_h2645_assemble_fragment, .flush = &cbs_h264_flush, @@ -1457,12 +2079,29 @@ const CodedBitstreamType ff_cbs_type_h265 = { .split_fragment = &cbs_h2645_split_fragment, .read_unit = &cbs_h265_read_nal_unit, .write_unit = &cbs_h265_write_nal_unit, + .discarded_unit = &cbs_h265_discarded_nal_unit, .assemble_fragment = &cbs_h2645_assemble_fragment, .flush = &cbs_h265_flush, .close = &cbs_h265_close, }; +const CodedBitstreamType ff_cbs_type_h266 = { + .codec_id = AV_CODEC_ID_VVC, + + .priv_data_size = sizeof(CodedBitstreamH266Context), + + .unit_types = cbs_h266_unit_types, + + .split_fragment = &cbs_h2645_split_fragment, + .read_unit = &cbs_h266_read_nal_unit, + .write_unit = &cbs_h266_write_nal_unit, + .assemble_fragment = &cbs_h2645_assemble_fragment, + + .flush = &cbs_h266_flush, + .close = &cbs_h266_close, +}; + static const SEIMessageTypeDescriptor cbs_sei_common_types[] = { { SEI_TYPE_FILLER_PAYLOAD, @@ -1613,6 +2252,16 @@ static const SEIMessageTypeDescriptor cbs_sei_h265_types[] = { SEI_MESSAGE_TYPE_END }; +static const SEIMessageTypeDescriptor cbs_sei_h266_types[] = { + { + SEI_TYPE_DECODED_PICTURE_HASH, + 0, 1, + sizeof(H266RawSEIDecodedPictureHash), + SEI_MESSAGE_RW(h266, sei_decoded_picture_hash), + }, + SEI_MESSAGE_TYPE_END +}; + const SEIMessageTypeDescriptor *ff_cbs_sei_find_type(CodedBitstreamContext *ctx, int payload_type) { @@ -1631,6 +2280,9 @@ const SEIMessageTypeDescriptor *ff_cbs_sei_find_type(CodedBitstreamContext *ctx, case AV_CODEC_ID_H265: codec_list = cbs_sei_h265_types; break; + case AV_CODEC_ID_H266: + codec_list = cbs_sei_h266_types; + break; default: return NULL; } diff --git a/libavcodec/cbs_h266.h b/libavcodec/cbs_h266.h new file mode 100644 index 00000000000..3a6f6d96b59 --- /dev/null +++ b/libavcodec/cbs_h266.h @@ -0,0 +1,880 @@ +/* + * 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_CBS_H266_H +#define AVCODEC_CBS_H266_H + +#include +#include + +#include "cbs_h2645.h" +#include "cbs_sei.h" +#include "vvc.h" + +typedef struct H266RawNALUnitHeader { + uint8_t nuh_layer_id; + uint8_t nal_unit_type; + uint8_t nuh_temporal_id_plus1; + uint8_t nuh_reserved_zero_bit; +} H266RawNALUnitHeader; + +typedef struct H266GeneralConstraintsInfo { + uint8_t gci_present_flag; + /* general */ + uint8_t gci_intra_only_constraint_flag; + uint8_t gci_all_layers_independent_constraint_flag; + uint8_t gci_one_au_only_constraint_flag; + + /* picture format */ + uint8_t gci_sixteen_minus_max_bitdepth_constraint_idc; + uint8_t gci_three_minus_max_chroma_format_constraint_idc; + + /* NAL unit type related */ + uint8_t gci_no_mixed_nalu_types_in_pic_constraint_flag; + uint8_t gci_no_trail_constraint_flag; + uint8_t gci_no_stsa_constraint_flag; + uint8_t gci_no_rasl_constraint_flag; + uint8_t gci_no_radl_constraint_flag; + uint8_t gci_no_idr_constraint_flag; + uint8_t gci_no_cra_constraint_flag; + uint8_t gci_no_gdr_constraint_flag; + uint8_t gci_no_aps_constraint_flag; + uint8_t gci_no_idr_rpl_constraint_flag; + + /* tile, slice, subpicture partitioning */ + uint8_t gci_one_tile_per_pic_constraint_flag; + uint8_t gci_pic_header_in_slice_header_constraint_flag; + uint8_t gci_one_slice_per_pic_constraint_flag; + uint8_t gci_no_rectangular_slice_constraint_flag; + uint8_t gci_one_slice_per_subpic_constraint_flag; + uint8_t gci_no_subpic_info_constraint_flag; + + /* CTU and block partitioning */ + uint8_t gci_three_minus_max_log2_ctu_size_constraint_idc; + uint8_t gci_no_partition_constraints_override_constraint_flag; + uint8_t gci_no_mtt_constraint_flag; + uint8_t gci_no_qtbtt_dual_tree_intra_constraint_flag; + + /* intra */ + uint8_t gci_no_palette_constraint_flag; + uint8_t gci_no_ibc_constraint_flag; + uint8_t gci_no_isp_constraint_flag; + uint8_t gci_no_mrl_constraint_flag; + uint8_t gci_no_mip_constraint_flag; + uint8_t gci_no_cclm_constraint_flag; + + /* inter */ + uint8_t gci_no_ref_pic_resampling_constraint_flag; + uint8_t gci_no_res_change_in_clvs_constraint_flag; + uint8_t gci_no_weighted_prediction_constraint_flag; + uint8_t gci_no_ref_wraparound_constraint_flag; + uint8_t gci_no_temporal_mvp_constraint_flag; + uint8_t gci_no_sbtmvp_constraint_flag; + uint8_t gci_no_amvr_constraint_flag; + uint8_t gci_no_bdof_constraint_flag; + uint8_t gci_no_smvd_constraint_flag; + uint8_t gci_no_dmvr_constraint_flag; + uint8_t gci_no_mmvd_constraint_flag; + uint8_t gci_no_affine_motion_constraint_flag; + uint8_t gci_no_prof_constraint_flag; + uint8_t gci_no_bcw_constraint_flag; + uint8_t gci_no_ciip_constraint_flag; + uint8_t gci_no_gpm_constraint_flag; + + /* transform, quantization, residual */ + uint8_t gci_no_luma_transform_size_64_constraint_flag; + uint8_t gci_no_transform_skip_constraint_flag; + uint8_t gci_no_bdpcm_constraint_flag; + uint8_t gci_no_mts_constraint_flag; + uint8_t gci_no_lfnst_constraint_flag; + uint8_t gci_no_joint_cbcr_constraint_flag; + uint8_t gci_no_sbt_constraint_flag; + uint8_t gci_no_act_constraint_flag; + uint8_t gci_no_explicit_scaling_list_constraint_flag; + uint8_t gci_no_dep_quant_constraint_flag; + uint8_t gci_no_sign_data_hiding_constraint_flag; + uint8_t gci_no_cu_qp_delta_constraint_flag; + uint8_t gci_no_chroma_qp_offset_constraint_flag; + + /* loop filter */ + uint8_t gci_no_sao_constraint_flag; + uint8_t gci_no_alf_constraint_flag; + uint8_t gci_no_ccalf_constraint_flag; + uint8_t gci_no_lmcs_constraint_flag; + uint8_t gci_no_ladf_constraint_flag; + uint8_t gci_no_virtual_boundaries_constraint_flag; + + uint8_t gci_num_additional_bits; + uint8_t gci_reserved_bit[255]; + + uint8_t gci_all_rap_pictures_constraint_flag; + uint8_t gci_no_extended_precision_processing_constraint_flag; + uint8_t gci_no_ts_residual_coding_rice_constraint_flag; + uint8_t gci_no_rrc_rice_extension_constraint_flag; + uint8_t gci_no_persistent_rice_adaptation_constraint_flag; + uint8_t gci_no_reverse_last_sig_coeff_constraint_flag; +} H266GeneralConstraintsInfo; + +typedef struct H266RawProfileTierLevel { + uint8_t general_profile_idc; + uint8_t general_tier_flag; + uint8_t general_level_idc; + uint8_t ptl_frame_only_constraint_flag; + uint8_t ptl_multilayer_enabled_flag; + H266GeneralConstraintsInfo general_constraints_info; + uint8_t ptl_sublayer_level_present_flag[VVC_MAX_SUBLAYERS - 1]; + uint8_t sublayer_level_idc[VVC_MAX_SUBLAYERS - 1]; + uint8_t ptl_num_sub_profiles; + uint32_t general_sub_profile_idc[VVC_MAX_SUB_PROFILES]; + + uint8_t ptl_reserved_zero_bit; +} H266RawProfileTierLevel; + +typedef struct H266RawExtensionData { + uint8_t *data; + AVBufferRef *data_ref; + size_t bit_length; +} H266RawExtensionData; + +typedef struct H266DpbParameters { + uint8_t dpb_max_dec_pic_buffering_minus1[VVC_MAX_SUBLAYERS]; + uint8_t dpb_max_num_reorder_pics[VVC_MAX_SUBLAYERS]; + uint8_t dpb_max_latency_increase_plus1[VVC_MAX_SUBLAYERS]; +} H266DpbParameters; + +typedef struct H266RefPicListStruct { + uint8_t num_ref_entries; + uint8_t ltrp_in_header_flag; + uint8_t inter_layer_ref_pic_flag[VVC_MAX_REF_ENTRIES]; + uint8_t st_ref_pic_flag[VVC_MAX_REF_ENTRIES]; + uint8_t abs_delta_poc_st[VVC_MAX_REF_ENTRIES]; + uint8_t strp_entry_sign_flag[VVC_MAX_REF_ENTRIES]; + uint8_t rpls_poc_lsb_lt[VVC_MAX_REF_ENTRIES]; + uint8_t ilrp_idx[VVC_MAX_REF_ENTRIES]; +} H266RefPicListStruct; + +typedef struct H266RefPicLists { + uint8_t rpl_sps_flag[2]; + uint8_t rpl_idx[2]; + H266RefPicListStruct rpl_ref_list[2]; + uint16_t poc_lsb_lt[2][VVC_MAX_REF_ENTRIES]; + uint8_t delta_poc_msb_cycle_present_flag[2][VVC_MAX_REF_ENTRIES]; + uint16_t delta_poc_msb_cycle_lt[2][VVC_MAX_REF_ENTRIES]; +} H266RefPicLists; + +typedef struct H266RawGeneralTimingHrdParameters { + uint32_t num_units_in_tick; + uint32_t time_scale; + uint8_t general_nal_hrd_params_present_flag; + uint8_t general_vcl_hrd_params_present_flag; + uint8_t general_same_pic_timing_in_all_ols_flag; + uint8_t general_du_hrd_params_present_flag; + uint8_t tick_divisor_minus2; + uint8_t bit_rate_scale; + uint8_t cpb_size_scale; + uint8_t cpb_size_du_scale; + uint8_t hrd_cpb_cnt_minus1; +} H266RawGeneralTimingHrdParameters; + +typedef struct H266RawSubLayerHRDParameters { + uint32_t bit_rate_value_minus1[VVC_MAX_SUBLAYERS][VVC_MAX_CPB_CNT]; + uint32_t cpb_size_value_minus1[VVC_MAX_SUBLAYERS][VVC_MAX_CPB_CNT]; + uint32_t cpb_size_du_value_minus1[VVC_MAX_SUBLAYERS][VVC_MAX_CPB_CNT]; + uint32_t bit_rate_du_value_minus1[VVC_MAX_SUBLAYERS][VVC_MAX_CPB_CNT]; + uint8_t cbr_flag[VVC_MAX_SUBLAYERS][VVC_MAX_CPB_CNT]; +} H266RawSubLayerHRDParameters; + +typedef struct H266RawOlsTimingHrdParameters { + uint8_t fixed_pic_rate_general_flag[VVC_MAX_SUBLAYERS]; + uint8_t fixed_pic_rate_within_cvs_flag[VVC_MAX_SUBLAYERS]; + uint16_t elemental_duration_in_tc_minus1[VVC_MAX_SUBLAYERS]; + uint8_t low_delay_hrd_flag[VVC_MAX_SUBLAYERS]; + H266RawSubLayerHRDParameters nal_sub_layer_hrd_parameters; + H266RawSubLayerHRDParameters vcl_sub_layer_hrd_parameters; +} H266RawOlsTimingHrdParameters; + +typedef struct H266RawVUI { + uint8_t vui_progressive_source_flag; + uint8_t vui_interlaced_source_flag; + uint8_t vui_non_packed_constraint_flag; + uint8_t vui_non_projected_constraint_flag; + + uint8_t vui_aspect_ratio_info_present_flag; + uint8_t vui_aspect_ratio_constant_flag; + uint8_t vui_aspect_ratio_idc; + + uint16_t vui_sar_width; + uint16_t vui_sar_height; + + uint8_t vui_overscan_info_present_flag; + uint8_t vui_overscan_appropriate_flag; + + uint8_t vui_colour_description_present_flag; + uint8_t vui_colour_primaries; + + uint8_t vui_transfer_characteristics; + uint8_t vui_matrix_coeffs; + uint8_t vui_full_range_flag; + + uint8_t vui_chroma_loc_info_present_flag; + uint8_t vui_chroma_sample_loc_type_frame; + uint8_t vui_chroma_sample_loc_type_top_field; + uint8_t vui_chroma_sample_loc_type_bottom_field; + H266RawExtensionData extension_data; +} H266RawVUI; + +typedef struct H266RawOPI { + H266RawNALUnitHeader nal_unit_header; + + uint8_t opi_ols_info_present_flag; + uint8_t opi_htid_info_present_flag; + uint16_t opi_ols_idx; + uint8_t opi_htid_plus1; + uint8_t opi_extension_flag; + H266RawExtensionData extension_data; +} H266RawOPI; + +typedef struct H266RawDCI { + H266RawNALUnitHeader nal_unit_header; + + uint8_t dci_reserved_zero_4bits; + uint8_t dci_num_ptls_minus1; + H266RawProfileTierLevel dci_profile_tier_level[VVC_MAX_DCI_PTLS]; + uint8_t dci_extension_flag; + H266RawExtensionData extension_data; +} H266RawDCI; + +typedef struct H266RawVPS { + H266RawNALUnitHeader nal_unit_header; + + uint8_t vps_video_parameter_set_id; + uint8_t vps_max_layers_minus1; + uint8_t vps_max_sublayers_minus1; + uint8_t vps_default_ptl_dpb_hrd_max_tid_flag; + uint8_t vps_all_independent_layers_flag; + uint8_t vps_layer_id[VVC_MAX_LAYERS]; + uint8_t vps_independent_layer_flag[VVC_MAX_LAYERS]; + uint8_t vps_max_tid_ref_present_flag[VVC_MAX_LAYERS]; + uint8_t vps_direct_ref_layer_flag[VVC_MAX_LAYERS][VVC_MAX_LAYERS - 1]; + uint8_t vps_max_tid_il_ref_pics_plus1[VVC_MAX_LAYERS][VVC_MAX_LAYERS - 1]; + uint8_t vps_each_layer_is_an_ols_flag; + uint8_t vps_ols_mode_idc; + uint8_t vps_num_output_layer_sets_minus2; + uint8_t vps_ols_output_layer_flag[VVC_MAX_TOTAL_NUM_OLSS][VVC_MAX_LAYERS]; + + uint8_t vps_num_ptls_minus1; + uint8_t vps_pt_present_flag[VVC_MAX_PTLS]; + uint8_t vps_ptl_max_tid[VVC_MAX_PTLS]; + H266RawProfileTierLevel vps_profile_tier_level[VVC_MAX_PTLS]; + uint8_t vps_ols_ptl_idx[VVC_MAX_TOTAL_NUM_OLSS]; + + uint16_t vps_num_dpb_params_minus1; + uint8_t vps_sublayer_dpb_params_present_flag; + uint8_t vps_dpb_max_tid[VVC_MAX_TOTAL_NUM_OLSS]; + H266DpbParameters vps_dpb_params[VVC_MAX_TOTAL_NUM_OLSS]; + uint16_t vps_ols_dpb_pic_width[VVC_MAX_TOTAL_NUM_OLSS]; + uint16_t vps_ols_dpb_pic_height[VVC_MAX_TOTAL_NUM_OLSS]; + uint8_t vps_ols_dpb_chroma_format[VVC_MAX_TOTAL_NUM_OLSS]; + uint8_t vps_ols_dpb_bitdepth_minus8[VVC_MAX_TOTAL_NUM_OLSS]; + uint16_t vps_ols_dpb_params_idx[VVC_MAX_TOTAL_NUM_OLSS]; + + uint8_t vps_timing_hrd_params_present_flag; + H266RawGeneralTimingHrdParameters vps_general_timing_hrd_parameters; + uint8_t vps_sublayer_cpb_params_present_flag; + uint16_t vps_num_ols_timing_hrd_params_minus1; + uint8_t vps_hrd_max_tid[VVC_MAX_TOTAL_NUM_OLSS]; + H266RawOlsTimingHrdParameters vps_ols_timing_hrd_parameters; + uint8_t vps_ols_timing_hrd_idx[VVC_MAX_TOTAL_NUM_OLSS]; + + uint8_t vps_extension_flag; + H266RawExtensionData extension_data; +} H266RawVPS; + +typedef struct H266RawSPS { + H266RawNALUnitHeader nal_unit_header; + + uint8_t sps_seq_parameter_set_id; + uint8_t sps_video_parameter_set_id; + uint8_t sps_max_sublayers_minus1; + uint8_t sps_chroma_format_idc; + uint8_t sps_log2_ctu_size_minus5; + uint8_t sps_ptl_dpb_hrd_params_present_flag; + H266RawProfileTierLevel profile_tier_level; + uint8_t sps_gdr_enabled_flag; + uint8_t sps_ref_pic_resampling_enabled_flag; + uint8_t sps_res_change_in_clvs_allowed_flag; + + uint16_t sps_pic_width_max_in_luma_samples; + uint16_t sps_pic_height_max_in_luma_samples; + + uint8_t sps_conformance_window_flag; + uint16_t sps_conf_win_left_offset; + uint16_t sps_conf_win_right_offset; + uint16_t sps_conf_win_top_offset; + uint16_t sps_conf_win_bottom_offset; + + uint8_t sps_subpic_info_present_flag; + uint16_t sps_num_subpics_minus1; + uint8_t sps_independent_subpics_flag; + uint8_t sps_subpic_same_size_flag; + uint16_t sps_subpic_ctu_top_left_x[VVC_MAX_SLICES]; + uint16_t sps_subpic_ctu_top_left_y[VVC_MAX_SLICES]; + uint16_t sps_subpic_width_minus1[VVC_MAX_SLICES]; + uint16_t sps_subpic_height_minus1[VVC_MAX_SLICES]; + uint8_t sps_subpic_treated_as_pic_flag[VVC_MAX_SLICES]; + uint8_t sps_loop_filter_across_subpic_enabled_flag[VVC_MAX_SLICES]; + uint8_t sps_subpic_id_len_minus1; + uint8_t sps_subpic_id_mapping_explicitly_signalled_flag; + uint8_t sps_subpic_id_mapping_present_flag; + uint32_t sps_subpic_id[VVC_MAX_SLICES]; + + + uint8_t sps_bitdepth_minus8; + uint8_t sps_entropy_coding_sync_enabled_flag; + uint8_t sps_entry_point_offsets_present_flag; + + uint8_t sps_log2_max_pic_order_cnt_lsb_minus4; + uint8_t sps_poc_msb_cycle_flag; + uint8_t sps_poc_msb_cycle_len_minus1; + + uint8_t sps_num_extra_ph_bytes; + uint8_t sps_extra_ph_bit_present_flag[16]; + + uint8_t sps_num_extra_sh_bytes; + uint8_t sps_extra_sh_bit_present_flag[16]; + + uint8_t sps_sublayer_dpb_params_flag; + H266DpbParameters sps_dpb_params; + + uint8_t sps_log2_min_luma_coding_block_size_minus2; + uint8_t sps_partition_constraints_override_enabled_flag; + uint8_t sps_log2_diff_min_qt_min_cb_intra_slice_luma; + uint8_t sps_max_mtt_hierarchy_depth_intra_slice_luma; + uint8_t sps_log2_diff_max_bt_min_qt_intra_slice_luma; + uint8_t sps_log2_diff_max_tt_min_qt_intra_slice_luma; + + uint8_t sps_qtbtt_dual_tree_intra_flag; + uint8_t sps_log2_diff_min_qt_min_cb_intra_slice_chroma; + uint8_t sps_max_mtt_hierarchy_depth_intra_slice_chroma; + uint8_t sps_log2_diff_max_bt_min_qt_intra_slice_chroma; + uint8_t sps_log2_diff_max_tt_min_qt_intra_slice_chroma; + + uint8_t sps_log2_diff_min_qt_min_cb_inter_slice; + uint8_t sps_max_mtt_hierarchy_depth_inter_slice; + uint8_t sps_log2_diff_max_bt_min_qt_inter_slice; + uint8_t sps_log2_diff_max_tt_min_qt_inter_slice; + + uint8_t sps_max_luma_transform_size_64_flag; + + uint8_t sps_transform_skip_enabled_flag; + uint8_t sps_log2_transform_skip_max_size_minus2; + uint8_t sps_bdpcm_enabled_flag; + + uint8_t sps_mts_enabled_flag; + uint8_t sps_explicit_mts_intra_enabled_flag; + uint8_t sps_explicit_mts_inter_enabled_flag; + + uint8_t sps_lfnst_enabled_flag; + + uint8_t sps_joint_cbcr_enabled_flag; + uint8_t sps_same_qp_table_for_chroma_flag; + + int8_t sps_qp_table_start_minus26[VVC_MAX_SAMPLE_ARRAYS]; + uint8_t sps_num_points_in_qp_table_minus1[VVC_MAX_SAMPLE_ARRAYS]; + uint8_t sps_delta_qp_in_val_minus1[VVC_MAX_SAMPLE_ARRAYS][VVC_MAX_POINTS_IN_QP_TABLE]; + uint8_t sps_delta_qp_diff_val[VVC_MAX_SAMPLE_ARRAYS][VVC_MAX_POINTS_IN_QP_TABLE]; + + uint8_t sps_sao_enabled_flag; + uint8_t sps_alf_enabled_flag; + uint8_t sps_ccalf_enabled_flag; + uint8_t sps_lmcs_enabled_flag; + uint8_t sps_weighted_pred_flag; + uint8_t sps_weighted_bipred_flag; + uint8_t sps_long_term_ref_pics_flag; + uint8_t sps_inter_layer_prediction_enabled_flag; + uint8_t sps_idr_rpl_present_flag; + uint8_t sps_rpl1_same_as_rpl0_flag; + + uint8_t sps_num_ref_pic_lists[2]; + H266RefPicListStruct sps_ref_pic_list_struct[2][VVC_MAX_REF_PIC_LISTS]; + + uint8_t sps_ref_wraparound_enabled_flag; + uint8_t sps_temporal_mvp_enabled_flag; + uint8_t sps_sbtmvp_enabled_flag; + uint8_t sps_amvr_enabled_flag; + uint8_t sps_bdof_enabled_flag; + uint8_t sps_bdof_control_present_in_ph_flag; + uint8_t sps_smvd_enabled_flag; + uint8_t sps_dmvr_enabled_flag; + uint8_t sps_dmvr_control_present_in_ph_flag; + uint8_t sps_mmvd_enabled_flag; + uint8_t sps_mmvd_fullpel_only_enabled_flag; + uint8_t sps_six_minus_max_num_merge_cand; + uint8_t sps_sbt_enabled_flag; + uint8_t sps_affine_enabled_flag; + uint8_t sps_five_minus_max_num_subblock_merge_cand; + uint8_t sps_6param_affine_enabled_flag; + uint8_t sps_affine_amvr_enabled_flag; + uint8_t sps_affine_prof_enabled_flag; + uint8_t sps_prof_control_present_in_ph_flag; + uint8_t sps_bcw_enabled_flag; + uint8_t sps_ciip_enabled_flag; + uint8_t sps_gpm_enabled_flag; + uint8_t sps_max_num_merge_cand_minus_max_num_gpm_cand; + uint8_t sps_log2_parallel_merge_level_minus2; + uint8_t sps_isp_enabled_flag; + uint8_t sps_mrl_enabled_flag; + uint8_t sps_mip_enabled_flag; + uint8_t sps_cclm_enabled_flag; + uint8_t sps_chroma_horizontal_collocated_flag; + uint8_t sps_chroma_vertical_collocated_flag; + uint8_t sps_palette_enabled_flag; + uint8_t sps_act_enabled_flag; + uint8_t sps_min_qp_prime_ts; + uint8_t sps_ibc_enabled_flag; + uint8_t sps_six_minus_max_num_ibc_merge_cand; + uint8_t sps_ladf_enabled_flag; + uint8_t sps_num_ladf_intervals_minus2; + int8_t sps_ladf_lowest_interval_qp_offset; + int8_t sps_ladf_qp_offset[4]; + uint16_t sps_ladf_delta_threshold_minus1[4]; + + uint8_t sps_explicit_scaling_list_enabled_flag; + uint8_t sps_scaling_matrix_for_lfnst_disabled_flag; + uint8_t sps_scaling_matrix_for_alternative_colour_space_disabled_flag; + uint8_t sps_scaling_matrix_designated_colour_space_flag; + uint8_t sps_dep_quant_enabled_flag; + uint8_t sps_sign_data_hiding_enabled_flag; + + uint8_t sps_virtual_boundaries_enabled_flag; + uint8_t sps_virtual_boundaries_present_flag; + uint8_t sps_num_ver_virtual_boundaries; + uint16_t sps_virtual_boundary_pos_x_minus1[3]; + uint8_t sps_num_hor_virtual_boundaries; + uint16_t sps_virtual_boundary_pos_y_minus1[3]; + + uint8_t sps_timing_hrd_params_present_flag; + uint8_t sps_sublayer_cpb_params_present_flag; + H266RawGeneralTimingHrdParameters sps_general_timing_hrd_parameters; + H266RawOlsTimingHrdParameters sps_ols_timing_hrd_parameters; + + uint8_t sps_field_seq_flag; + uint8_t sps_vui_parameters_present_flag; + uint16_t sps_vui_payload_size_minus1; + H266RawVUI vui; + + uint8_t sps_extension_flag; + + uint8_t sps_range_extension_flag; + uint8_t sps_extension_7bits; + + uint8_t sps_extended_precision_flag; + uint8_t sps_ts_residual_coding_rice_present_in_sh_flag; + uint8_t sps_rrc_rice_extension_flag; + uint8_t sps_persistent_rice_adaptation_enabled_flag; + uint8_t sps_reverse_last_sig_coeff_enabled_flag; + + H266RawExtensionData extension_data; + +} H266RawSPS; + +typedef struct H266RawPPS { + H266RawNALUnitHeader nal_unit_header; + + uint8_t pps_pic_parameter_set_id; + uint8_t pps_seq_parameter_set_id; + uint8_t pps_mixed_nalu_types_in_pic_flag; + uint16_t pps_pic_width_in_luma_samples; + uint16_t pps_pic_height_in_luma_samples; + + uint8_t pps_conformance_window_flag; + uint16_t pps_conf_win_left_offset; + uint16_t pps_conf_win_right_offset; + uint16_t pps_conf_win_top_offset; + uint16_t pps_conf_win_bottom_offset; + + uint8_t pps_scaling_window_explicit_signalling_flag; + int16_t pps_scaling_win_left_offset; + int16_t pps_scaling_win_right_offset; + int16_t pps_scaling_win_top_offset; + int16_t pps_scaling_win_bottom_offset; + + uint8_t pps_output_flag_present_flag; + uint8_t pps_no_pic_partition_flag; + + uint8_t pps_subpic_id_mapping_present_flag; + uint16_t pps_num_subpics_minus1; + uint8_t pps_subpic_id_len_minus1; + uint16_t pps_subpic_id[VVC_MAX_SLICES]; + + uint8_t pps_log2_ctu_size_minus5; + uint8_t pps_num_exp_tile_columns_minus1; + uint8_t pps_num_exp_tile_rows_minus1; + uint16_t pps_tile_column_width_minus1[VVC_MAX_TILE_COLUMNS]; + uint16_t pps_tile_row_height_minus1[VVC_MAX_TILE_ROWS]; + + uint8_t pps_loop_filter_across_tiles_enabled_flag; + uint8_t pps_rect_slice_flag; + uint8_t pps_single_slice_per_subpic_flag; + + uint16_t pps_num_slices_in_pic_minus1; + uint8_t pps_tile_idx_delta_present_flag; + uint16_t pps_slice_width_in_tiles_minus1[VVC_MAX_SLICES]; + uint16_t pps_slice_height_in_tiles_minus1[VVC_MAX_SLICES]; + uint16_t pps_num_exp_slices_in_tile[VVC_MAX_SLICES]; + uint16_t pps_exp_slice_height_in_ctus_minus1[VVC_MAX_SLICES][VVC_MAX_TILE_ROWS]; + int16_t pps_tile_idx_delta_val[VVC_MAX_SLICES]; + + uint8_t pps_loop_filter_across_slices_enabled_flag; + uint8_t pps_cabac_init_present_flag; + uint8_t pps_num_ref_idx_default_active_minus1[2]; + uint8_t pps_rpl1_idx_present_flag; + uint8_t pps_weighted_pred_flag; + uint8_t pps_weighted_bipred_flag; + uint8_t pps_ref_wraparound_enabled_flag; + uint16_t pps_pic_width_minus_wraparound_offset; + int8_t pps_init_qp_minus26; + uint8_t pps_cu_qp_delta_enabled_flag; + uint8_t pps_chroma_tool_offsets_present_flag; + int8_t pps_cb_qp_offset; + int8_t pps_cr_qp_offset; + uint8_t pps_joint_cbcr_qp_offset_present_flag; + int8_t pps_joint_cbcr_qp_offset_value; + uint8_t pps_slice_chroma_qp_offsets_present_flag; + uint8_t pps_cu_chroma_qp_offset_list_enabled_flag; + uint8_t pps_chroma_qp_offset_list_len_minus1; + int8_t pps_cb_qp_offset_list[6]; + int8_t pps_cr_qp_offset_list[6]; + int8_t pps_joint_cbcr_qp_offset_list[6]; + uint8_t pps_deblocking_filter_control_present_flag; + uint8_t pps_deblocking_filter_override_enabled_flag; + uint8_t pps_deblocking_filter_disabled_flag; + uint8_t pps_dbf_info_in_ph_flag; + + int8_t pps_luma_beta_offset_div2; + int8_t pps_luma_tc_offset_div2; + int8_t pps_cb_beta_offset_div2; + int8_t pps_cb_tc_offset_div2; + int8_t pps_cr_beta_offset_div2; + int8_t pps_cr_tc_offset_div2; + + uint8_t pps_rpl_info_in_ph_flag; + uint8_t pps_sao_info_in_ph_flag; + uint8_t pps_alf_info_in_ph_flag; + uint8_t pps_wp_info_in_ph_flag; + uint8_t pps_qp_delta_info_in_ph_flag; + + uint8_t pps_picture_header_extension_present_flag; + uint8_t pps_slice_header_extension_present_flag; + uint8_t pps_extension_flag; + H266RawExtensionData extension_data; + + //calculated value; + uint16_t num_tile_columns; + uint16_t num_tile_rows; + uint16_t num_tiles_in_pic; + uint16_t slice_height_in_ctus[VVC_MAX_SLICES]; + uint16_t num_slices_in_subpic[VVC_MAX_SLICES]; + uint16_t sub_pic_id_val[VVC_MAX_SLICES]; + uint16_t col_width_val[VVC_MAX_TILE_COLUMNS]; + uint16_t row_height_val[VVC_MAX_TILE_ROWS]; +} H266RawPPS; + +typedef struct H266RawAPS { + H266RawNALUnitHeader nal_unit_header; + uint8_t aps_params_type; + uint8_t aps_adaptation_parameter_set_id; + uint8_t aps_chroma_present_flag; + + uint8_t alf_luma_filter_signal_flag; + uint8_t alf_chroma_filter_signal_flag; + uint8_t alf_cc_cb_filter_signal_flag; + uint8_t alf_cc_cr_filter_signal_flag; + uint8_t alf_luma_clip_flag; + uint8_t alf_luma_num_filters_signalled_minus1; + uint8_t alf_luma_coeff_delta_idx[VVC_NUM_ALF_FILTERS]; + uint8_t alf_luma_coeff_abs[VVC_NUM_ALF_FILTERS][12]; + uint8_t alf_luma_coeff_sign[VVC_NUM_ALF_FILTERS][12]; + uint8_t alf_luma_clip_idx[VVC_NUM_ALF_FILTERS][12]; + uint8_t alf_chroma_clip_flag; + uint8_t alf_chroma_num_alt_filters_minus1; + uint8_t alf_chroma_coeff_abs[8][6]; + uint8_t alf_chroma_coeff_sign[8][6]; + uint8_t alf_chroma_clip_idx[8][6]; + uint8_t alf_cc_cb_filters_signalled_minus1; + uint8_t alf_cc_cb_mapped_coeff_abs[4][7]; + uint8_t alf_cc_cb_coeff_sign[4][7]; + uint8_t alf_cc_cr_filters_signalled_minus1; + uint8_t alf_cc_cr_mapped_coeff_abs[4][7]; + uint8_t alf_cc_cr_coeff_sign[4][7]; + + uint8_t scaling_list_copy_mode_flag[28]; + uint8_t scaling_list_pred_mode_flag[28]; + uint8_t scaling_list_pred_id_delta[28]; + int8_t scaling_list_dc_coef[14]; + int8_t scaling_list_delta_coef[28][64]; + + uint8_t lmcs_min_bin_idx; + uint8_t lmcs_delta_max_bin_idx; + uint8_t lmcs_delta_cw_prec_minus1; + uint16_t lmcs_delta_abs_cw[16]; + uint8_t lmcs_delta_sign_cw_flag[16]; + uint8_t lmcs_delta_abs_crs; + uint8_t lmcs_delta_sign_crs_flag; + + uint8_t aps_extension_flag; + H266RawExtensionData extension_data; +} H266RawAPS; + +typedef struct H266RawAUD { + H266RawNALUnitHeader nal_unit_header; + uint8_t aud_irap_or_gdr_flag; + uint8_t aud_pic_type; +} H266RawAUD; + +typedef struct H266RawPredWeightTable { + uint8_t luma_log2_weight_denom; + int8_t delta_chroma_log2_weight_denom; + + uint8_t num_l0_weights; + uint8_t luma_weight_l0_flag[15]; + uint8_t chroma_weight_l0_flag[15]; + int8_t delta_luma_weight_l0[15]; + int8_t luma_offset_l0[15]; + int8_t delta_chroma_weight_l0[15][2]; + int16_t delta_chroma_offset_l0[15][2]; + + uint8_t num_l1_weights; + uint8_t luma_weight_l1_flag[15]; + uint8_t chroma_weight_l1_flag[15]; + int8_t delta_luma_weight_l1[15]; + int8_t luma_offset_l1[15]; + int8_t delta_chroma_weight_l1[15][2]; + int16_t delta_chroma_offset_l1[15][2]; + + uint8_t num_weights_l0; ///< NumWeightsL0 + uint8_t num_weights_l1; ///< NumWeightsL1 +} H266RawPredWeightTable; + +typedef struct H266RawPictureHeader { + uint8_t ph_gdr_or_irap_pic_flag; + uint8_t ph_non_ref_pic_flag; + uint8_t ph_gdr_pic_flag; + uint8_t ph_inter_slice_allowed_flag; + uint8_t ph_intra_slice_allowed_flag; + uint8_t ph_pic_parameter_set_id; + uint16_t ph_pic_order_cnt_lsb; + uint8_t ph_recovery_poc_cnt; + uint8_t ph_extra_bit[16]; + uint8_t ph_poc_msb_cycle_present_flag; + uint8_t ph_poc_msb_cycle_val; + + uint8_t ph_alf_enabled_flag; + uint8_t ph_num_alf_aps_ids_luma; + uint8_t ph_alf_aps_id_luma[8]; + uint8_t ph_alf_cb_enabled_flag; + uint8_t ph_alf_cr_enabled_flag; + uint8_t ph_alf_aps_id_chroma; + uint8_t ph_alf_cc_cb_enabled_flag; + uint8_t ph_alf_cc_cb_aps_id; + uint8_t ph_alf_cc_cr_enabled_flag; + uint8_t ph_alf_cc_cr_aps_id; + + uint8_t ph_lmcs_enabled_flag; + uint8_t ph_lmcs_aps_id; + uint8_t ph_chroma_residual_scale_flag; + uint8_t ph_explicit_scaling_list_enabled_flag; + uint8_t ph_scaling_list_aps_id; + + uint8_t ph_virtual_boundaries_present_flag; + uint8_t ph_num_ver_virtual_boundaries; + uint16_t ph_virtual_boundary_pos_x_minus1[3]; + uint8_t ph_num_hor_virtual_boundaries; + uint16_t ph_virtual_boundary_pos_y_minus1[3]; + + uint8_t ph_pic_output_flag; + H266RefPicLists ph_ref_pic_lists; + + uint8_t ph_partition_constraints_override_flag; + + uint8_t ph_log2_diff_min_qt_min_cb_intra_slice_luma; + uint8_t ph_max_mtt_hierarchy_depth_intra_slice_luma; + uint8_t ph_log2_diff_max_bt_min_qt_intra_slice_luma; + uint8_t ph_log2_diff_max_tt_min_qt_intra_slice_luma; + uint8_t ph_log2_diff_min_qt_min_cb_intra_slice_chroma; + + uint8_t ph_max_mtt_hierarchy_depth_intra_slice_chroma; + uint8_t ph_log2_diff_max_bt_min_qt_intra_slice_chroma; + uint8_t ph_log2_diff_max_tt_min_qt_intra_slice_chroma; + + uint8_t ph_cu_qp_delta_subdiv_intra_slice; + uint8_t ph_cu_chroma_qp_offset_subdiv_intra_slice; + + uint8_t ph_log2_diff_min_qt_min_cb_inter_slice; + uint8_t ph_max_mtt_hierarchy_depth_inter_slice; + uint8_t ph_log2_diff_max_bt_min_qt_inter_slice; + uint8_t ph_log2_diff_max_tt_min_qt_inter_slice; + uint8_t ph_cu_qp_delta_subdiv_inter_slice; + uint8_t ph_cu_chroma_qp_offset_subdiv_inter_slice; + + uint8_t ph_temporal_mvp_enabled_flag; + uint8_t ph_collocated_from_l0_flag; + uint8_t ph_collocated_ref_idx; + uint8_t ph_mmvd_fullpel_only_flag; + uint8_t ph_mvd_l1_zero_flag; + uint8_t ph_bdof_disabled_flag; + uint8_t ph_dmvr_disabled_flag; + uint8_t ph_prof_disabled_flag; + + H266RawPredWeightTable ph_pred_weight_table; + + int8_t ph_qp_delta; + uint8_t ph_joint_cbcr_sign_flag; + uint8_t ph_sao_luma_enabled_flag; + uint8_t ph_sao_chroma_enabled_flag; + + uint8_t ph_deblocking_params_present_flag; + uint8_t ph_deblocking_filter_disabled_flag; + int8_t ph_luma_beta_offset_div2; + int8_t ph_luma_tc_offset_div2; + int8_t ph_cb_beta_offset_div2; + int8_t ph_cb_tc_offset_div2; + int8_t ph_cr_beta_offset_div2; + int8_t ph_cr_tc_offset_div2; + + uint8_t ph_extension_length; + uint8_t ph_extension_data_byte[256]; +} H266RawPictureHeader; + +typedef struct H266RawPH { + H266RawNALUnitHeader nal_unit_header; + H266RawPictureHeader ph_picture_header; +} H266RawPH; + +typedef struct H266RawSliceHeader { + H266RawNALUnitHeader nal_unit_header; + uint8_t sh_picture_header_in_slice_header_flag; + H266RawPictureHeader sh_picture_header; + + uint16_t sh_subpic_id; + uint16_t sh_slice_address; + uint8_t sh_extra_bit[16]; + uint8_t sh_num_tiles_in_slice_minus1; + uint8_t sh_slice_type; + uint8_t sh_no_output_of_prior_pics_flag; + + uint8_t sh_alf_enabled_flag; + uint8_t sh_num_alf_aps_ids_luma; + uint8_t sh_alf_aps_id_luma[8]; + uint8_t sh_alf_cb_enabled_flag; + uint8_t sh_alf_cr_enabled_flag; + uint8_t sh_alf_aps_id_chroma; + uint8_t sh_alf_cc_cb_enabled_flag; + uint8_t sh_alf_cc_cb_aps_id; + uint8_t sh_alf_cc_cr_enabled_flag; + uint8_t sh_alf_cc_cr_aps_id; + + uint8_t sh_lmcs_used_flag; + uint8_t sh_explicit_scaling_list_used_flag; + + H266RefPicLists sh_ref_pic_lists; + + uint8_t sh_num_ref_idx_active_override_flag; + uint8_t sh_num_ref_idx_active_minus1[2]; + uint8_t sh_cabac_init_flag; + uint8_t sh_collocated_from_l0_flag; + uint8_t sh_collocated_ref_idx; + + H266RawPredWeightTable sh_pred_weight_table; + + int8_t sh_qp_delta; + int8_t sh_cb_qp_offset; + int8_t sh_cr_qp_offset; + int8_t sh_joint_cbcr_qp_offset; + uint8_t sh_cu_chroma_qp_offset_enabled_flag; + + uint8_t sh_sao_luma_used_flag; + uint8_t sh_sao_chroma_used_flag; + + uint8_t sh_deblocking_params_present_flag; + uint8_t sh_deblocking_filter_disabled_flag; + int8_t sh_luma_beta_offset_div2; + int8_t sh_luma_tc_offset_div2; + int8_t sh_cb_beta_offset_div2; + int8_t sh_cb_tc_offset_div2; + int8_t sh_cr_beta_offset_div2; + int8_t sh_cr_tc_offset_div2; + uint8_t sh_dep_quant_used_flag; + + uint8_t sh_sign_data_hiding_used_flag; + uint8_t sh_ts_residual_coding_disabled_flag; + uint8_t sh_ts_residual_coding_rice_idx_minus1; + uint8_t sh_reverse_last_sig_coeff_flag; + uint16_t sh_slice_header_extension_length; + uint8_t sh_slice_header_extension_data_byte[256]; + + uint8_t sh_entry_offset_len_minus1; + uint32_t sh_entry_point_offset_minus1[VVC_MAX_ENTRY_POINTS]; + + // derived values + uint32_t num_entry_points; ///< NumEntryPoints + uint8_t num_ref_idx_active[2]; ///< NumRefIdxActive[] + +} H266RawSliceHeader; + +typedef struct H266RawSlice { + H266RawSliceHeader header; + + uint8_t *data; + AVBufferRef *data_ref; + size_t data_size; + int data_bit_start; +} H266RawSlice; + +typedef struct H266RawSEIDecodedPictureHash { + uint8_t dph_sei_hash_type; + uint8_t dph_sei_single_component_flag; + uint8_t dph_sei_picture_md5[3][16]; + uint16_t dph_sei_picture_crc[3]; + uint32_t dph_sei_picture_checksum[3]; + + uint8_t dph_sei_reserved_zero_7bits; +} H266RawSEIDecodedPictureHash; + +typedef struct H266RawSEI { + H266RawNALUnitHeader nal_unit_header; + SEIRawMessageList message_list; +} H266RawSEI; + +typedef struct CodedBitstreamH266Context { + // Reader/writer context in common with the H.264 implementation. + CodedBitstreamH2645Context common; + + // All currently available parameter sets. These are updated when + // any parameter set NAL unit is read/written with this context. + AVBufferRef *vps_ref[VVC_MAX_VPS_COUNT]; + AVBufferRef *sps_ref[VVC_MAX_SPS_COUNT]; + AVBufferRef *pps_ref[VVC_MAX_PPS_COUNT]; + AVBufferRef *ph_ref; + H266RawVPS *vps[VVC_MAX_VPS_COUNT]; + H266RawSPS *sps[VVC_MAX_SPS_COUNT]; + H266RawPPS *pps[VVC_MAX_PPS_COUNT]; + H266RawPictureHeader *ph; +} CodedBitstreamH266Context; + +#endif /* AVCODEC_CBS_H266_H */ diff --git a/libavcodec/cbs_h266_syntax_template.c b/libavcodec/cbs_h266_syntax_template.c new file mode 100644 index 00000000000..4075897b9ac --- /dev/null +++ b/libavcodec/cbs_h266_syntax_template.c @@ -0,0 +1,3470 @@ +/* + * 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 + */ + +static int FUNC(rbsp_trailing_bits) (CodedBitstreamContext *ctx, + RWContext *rw) +{ + int err; + + fixed(1, rbsp_stop_one_bit, 1); + while (byte_alignment(rw) != 0) + fixed(1, rbsp_alignment_zero_bit, 0); + return 0; +} + +static int FUNC(nal_unit_header) (CodedBitstreamContext *ctx, RWContext *rw, + H266RawNALUnitHeader *current, + int expected_nal_unit_type) +{ + int err; + + fixed(1, forbidden_zero_bit, 0); + flag(nuh_reserved_zero_bit); + + u(6, nuh_layer_id, 0, 55); + + if (expected_nal_unit_type >= 0) + u(5, nal_unit_type, expected_nal_unit_type, expected_nal_unit_type); + else + ub(5, nal_unit_type); + + u(3, nuh_temporal_id_plus1, 1, 7); + return 0; +} + +static int FUNC(byte_alignment) (CodedBitstreamContext *ctx, RWContext *rw) +{ + int err; + + fixed(1, byte_alignment_bit_equal_to_one, 1); + while (byte_alignment(rw) != 0) + fixed(1, byte_alignment_bit_equal_to_zero, 0); + return 0; +} + +static int FUNC(general_constraints_info) (CodedBitstreamContext *ctx, + RWContext *rw, + H266GeneralConstraintsInfo *current) +{ + int err, i, num_additional_bits_used; + + flag(gci_present_flag); + if (current->gci_present_flag) { + /* general */ + flag(gci_intra_only_constraint_flag); + flag(gci_all_layers_independent_constraint_flag); + flag(gci_one_au_only_constraint_flag); + + /* picture format */ + u(4, gci_sixteen_minus_max_bitdepth_constraint_idc, 0, 8); + ub(2, gci_three_minus_max_chroma_format_constraint_idc); + + /* NAL unit type related */ + flag(gci_no_mixed_nalu_types_in_pic_constraint_flag); + flag(gci_no_trail_constraint_flag); + flag(gci_no_stsa_constraint_flag); + flag(gci_no_rasl_constraint_flag); + flag(gci_no_radl_constraint_flag); + flag(gci_no_idr_constraint_flag); + flag(gci_no_cra_constraint_flag); + flag(gci_no_gdr_constraint_flag); + flag(gci_no_aps_constraint_flag); + flag(gci_no_idr_rpl_constraint_flag); + + /* tile, slice, subpicture partitioning */ + flag(gci_one_tile_per_pic_constraint_flag); + flag(gci_pic_header_in_slice_header_constraint_flag); + flag(gci_one_slice_per_pic_constraint_flag); + flag(gci_no_rectangular_slice_constraint_flag); + flag(gci_one_slice_per_subpic_constraint_flag); + flag(gci_no_subpic_info_constraint_flag); + + /* CTU and block partitioning */ + ub(2, gci_three_minus_max_log2_ctu_size_constraint_idc); + flag(gci_no_partition_constraints_override_constraint_flag); + flag(gci_no_mtt_constraint_flag); + flag(gci_no_qtbtt_dual_tree_intra_constraint_flag); + + /* intra */ + flag(gci_no_palette_constraint_flag); + flag(gci_no_ibc_constraint_flag); + flag(gci_no_isp_constraint_flag); + flag(gci_no_mrl_constraint_flag); + flag(gci_no_mip_constraint_flag); + flag(gci_no_cclm_constraint_flag); + + /* inter */ + flag(gci_no_ref_pic_resampling_constraint_flag); + flag(gci_no_res_change_in_clvs_constraint_flag); + flag(gci_no_weighted_prediction_constraint_flag); + flag(gci_no_ref_wraparound_constraint_flag); + flag(gci_no_temporal_mvp_constraint_flag); + flag(gci_no_sbtmvp_constraint_flag); + flag(gci_no_amvr_constraint_flag); + flag(gci_no_bdof_constraint_flag); + flag(gci_no_smvd_constraint_flag); + flag(gci_no_dmvr_constraint_flag); + flag(gci_no_mmvd_constraint_flag); + flag(gci_no_affine_motion_constraint_flag); + flag(gci_no_prof_constraint_flag); + flag(gci_no_bcw_constraint_flag); + flag(gci_no_ciip_constraint_flag); + flag(gci_no_gpm_constraint_flag); + + /* transform, quantization, residual */ + flag(gci_no_luma_transform_size_64_constraint_flag); + flag(gci_no_transform_skip_constraint_flag); + flag(gci_no_bdpcm_constraint_flag); + flag(gci_no_mts_constraint_flag); + flag(gci_no_lfnst_constraint_flag); + flag(gci_no_joint_cbcr_constraint_flag); + flag(gci_no_sbt_constraint_flag); + flag(gci_no_act_constraint_flag); + flag(gci_no_explicit_scaling_list_constraint_flag); + flag(gci_no_dep_quant_constraint_flag); + flag(gci_no_sign_data_hiding_constraint_flag); + flag(gci_no_cu_qp_delta_constraint_flag); + flag(gci_no_chroma_qp_offset_constraint_flag); + + /* loop filter */ + flag(gci_no_sao_constraint_flag); + flag(gci_no_alf_constraint_flag); + flag(gci_no_ccalf_constraint_flag); + flag(gci_no_lmcs_constraint_flag); + flag(gci_no_ladf_constraint_flag); + flag(gci_no_virtual_boundaries_constraint_flag); + ub(8, gci_num_additional_bits); + if (current->gci_num_additional_bits > 5) { + flag(gci_all_rap_pictures_constraint_flag); + flag(gci_no_extended_precision_processing_constraint_flag); + flag(gci_no_ts_residual_coding_rice_constraint_flag); + flag(gci_no_rrc_rice_extension_constraint_flag); + flag(gci_no_persistent_rice_adaptation_constraint_flag); + flag(gci_no_reverse_last_sig_coeff_constraint_flag); + num_additional_bits_used = 6; + } else { + infer(gci_all_rap_pictures_constraint_flag, 0); + infer(gci_no_extended_precision_processing_constraint_flag, 0); + infer(gci_no_ts_residual_coding_rice_constraint_flag, 0); + infer(gci_no_rrc_rice_extension_constraint_flag, 0); + infer(gci_no_persistent_rice_adaptation_constraint_flag, 0); + infer(gci_no_reverse_last_sig_coeff_constraint_flag, 0); + num_additional_bits_used = 0; + } + + for (i = 0; i < current->gci_num_additional_bits - num_additional_bits_used; i++) + flags(gci_reserved_bit[i], 1, i); + } + while (byte_alignment(rw) != 0) + fixed(1, gci_alignment_zero_bit, 0); + return 0; +} + +static int FUNC(profile_tier_level) (CodedBitstreamContext *ctx, + RWContext *rw, + H266RawProfileTierLevel *current, + int profile_tier_present_flag, + int max_num_sub_layers_minus1) +{ + int err, i; + + if (profile_tier_present_flag) { + ub(7, general_profile_idc); + flag(general_tier_flag); + } + ub(8, general_level_idc); + flag(ptl_frame_only_constraint_flag); + flag(ptl_multilayer_enabled_flag); + if (profile_tier_present_flag) { + CHECK(FUNC(general_constraints_info) (ctx, rw, + ¤t-> + general_constraints_info)); + } + for (i = max_num_sub_layers_minus1 - 1; i >= 0; i--) + flags(ptl_sublayer_level_present_flag[i], 1, i); + while (byte_alignment(rw) != 0) + flag(ptl_reserved_zero_bit); + for (i = max_num_sub_layers_minus1 - 1; i >= 0; i--) + if (current->ptl_sublayer_level_present_flag[i]) + ubs(8, sublayer_level_idc[i], 1, i); + if (profile_tier_present_flag) { + ub(8, ptl_num_sub_profiles); + for (i = 0; i < current->ptl_num_sub_profiles; i++) + ubs(32, general_sub_profile_idc[i], 1, i); + } + return 0; +} + +static int FUNC(vui_parameters_default) (CodedBitstreamContext *ctx, + RWContext *rw, H266RawVUI *current) +{ + //defined in D.8 + infer(vui_progressive_source_flag, 0); + infer(vui_interlaced_source_flag, 0); + + infer(vui_non_packed_constraint_flag, 0); + infer(vui_non_projected_constraint_flag, 0); + + infer(vui_aspect_ratio_constant_flag, 0); + infer(vui_aspect_ratio_idc, 0); + + infer(vui_overscan_info_present_flag, 0); + + infer(vui_colour_primaries, 2); + infer(vui_transfer_characteristics, 2); + infer(vui_matrix_coeffs, 2); + infer(vui_full_range_flag, 0); + + infer(vui_chroma_sample_loc_type_frame, 6); + infer(vui_chroma_sample_loc_type_top_field, 6); + infer(vui_chroma_sample_loc_type_bottom_field, 6); + return 0; +} + +static int FUNC(vui_parameters) (CodedBitstreamContext *ctx, RWContext *rw, + H266RawVUI *current, + uint8_t chroma_format_idc) +{ + int err; + + flag(vui_progressive_source_flag); + flag(vui_interlaced_source_flag); + flag(vui_non_packed_constraint_flag); + flag(vui_non_projected_constraint_flag); + flag(vui_aspect_ratio_info_present_flag); + if (current->vui_aspect_ratio_info_present_flag) { + flag(vui_aspect_ratio_constant_flag); + ub(8, vui_aspect_ratio_idc); + if (current->vui_aspect_ratio_idc == 255) { + ub(16, vui_sar_width); + ub(16, vui_sar_height); + } + } else { + infer(vui_aspect_ratio_constant_flag, 0); + infer(vui_aspect_ratio_idc, 0); + } + flag(vui_overscan_info_present_flag); + if (current->vui_overscan_info_present_flag) + flag(vui_overscan_appropriate_flag); + flag(vui_colour_description_present_flag); + if (current->vui_colour_description_present_flag) { + ub(8, vui_colour_primaries); + av_log(ctx->log_ctx, AV_LOG_DEBUG, "vui_colour_primaries == %d \n", + current->vui_colour_primaries); + ub(8, vui_transfer_characteristics); + av_log(ctx->log_ctx, AV_LOG_DEBUG, + "vui_transfer_characteristics == %d \n", + current->vui_transfer_characteristics); + ub(8, vui_matrix_coeffs); + av_log(ctx->log_ctx, AV_LOG_DEBUG, "vui_matrix_coeffs == %d \n", + current->vui_matrix_coeffs); + flag(vui_full_range_flag); + } else { + infer(vui_colour_primaries, 2); + infer(vui_transfer_characteristics, 2); + infer(vui_matrix_coeffs, 2); + infer(vui_full_range_flag, 0); + } + flag(vui_chroma_loc_info_present_flag); + if (chroma_format_idc != 1 && current->vui_chroma_loc_info_present_flag) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "chroma_format_idc == %d," + "vui_chroma_loc_info_present_flag can't not be true", + chroma_format_idc); + return AVERROR_INVALIDDATA; + } + if (current->vui_chroma_loc_info_present_flag) { + if (current->vui_progressive_source_flag && + !current->vui_interlaced_source_flag) { + ue(vui_chroma_sample_loc_type_frame, 0, 6); + } else { + ue(vui_chroma_sample_loc_type_top_field, 0, 6); + ue(vui_chroma_sample_loc_type_bottom_field, 0, 6); + } + } else { + if (chroma_format_idc == 1) { + infer(vui_chroma_sample_loc_type_frame, 6); + infer(vui_chroma_sample_loc_type_top_field, + current->vui_chroma_sample_loc_type_frame); + infer(vui_chroma_sample_loc_type_bottom_field, + current->vui_chroma_sample_loc_type_frame); + } + } + return 0; +} + +static int FUNC(payload_extension) (CodedBitstreamContext *ctx, RWContext *rw, + H266RawExtensionData *current, + uint32_t payload_size, int cur_pos) +{ + int err; + size_t byte_length, k; + +#ifdef READ + GetBitContext tmp; + int bits_left, payload_zero_bits; + + if (!cbs_h265_payload_extension_present(rw, payload_size, cur_pos)) + return 0; + + bits_left = 8 * payload_size - cur_pos; + tmp = *rw; + if (bits_left > 8) + skip_bits_long(&tmp, bits_left - 8); + payload_zero_bits = get_bits(&tmp, FFMIN(bits_left, 8)); + if (!payload_zero_bits) + return AVERROR_INVALIDDATA; + payload_zero_bits = ff_ctz(payload_zero_bits); + current->bit_length = bits_left - payload_zero_bits - 1; + allocate(current->data, (current->bit_length + 7) / 8); +#endif + + byte_length = (current->bit_length + 7) / 8; + for (k = 0; k < byte_length; k++) { + int length = FFMIN(current->bit_length - k * 8, 8); + xu(length, reserved_payload_extension_data, current->data[k], + 0, MAX_UINT_BITS(length), 0); + } + + return 0; +} + +static int FUNC(vui_payload) (CodedBitstreamContext *ctx, RWContext *rw, + H266RawVUI *current, uint16_t vui_payload_size, + uint8_t chroma_format_idc) +{ + int err; + int start_position, current_position; + + start_position = bit_position(rw); + CHECK(FUNC(vui_parameters) (ctx, rw, current, chroma_format_idc)); + current_position = bit_position(rw) - start_position; + + if (current_position < 8 * vui_payload_size) { + CHECK(FUNC(payload_extension) (ctx, rw, ¤t->extension_data, + vui_payload_size, current_position)); + fixed(1, vui_payload_bit_equal_to_one, 1); + while (byte_alignment(rw) != 0) + fixed(1, vui_payload_bit_equal_to_zero, 0); + } + return 0; +} + +static int FUNC(extension_data) (CodedBitstreamContext *ctx, RWContext *rw, + H266RawExtensionData *current) +{ + int err; + size_t k; +#ifdef READ + GetBitContext start; + uint8_t bit; + start = *rw; + for (k = 0; cbs_h2645_read_more_rbsp_data(rw); k++) + skip_bits(rw, 1); + current->bit_length = k; + if (k > 0) { + *rw = start; + allocate(current->data, (current->bit_length + 7) / 8); + for (k = 0; k < current->bit_length; k++) { + xu(1, extension_data, bit, 0, 1, 0); + current->data[k / 8] |= bit << (7 - k % 8); + } + } +#else + for (k = 0; k < current->bit_length; k++) + xu(1, extension_data, current->data[k / 8] >> (7 - k % 8) & 1, 0, 1, 0); +#endif + return 0; +} + +static int FUNC(dpb_parameters) (CodedBitstreamContext *ctx, RWContext *rw, + H266DpbParameters *current, + uint8_t max_sublayers_minus1, + uint8_t sublayer_info_flag) +{ + int err, i; + for (i = (sublayer_info_flag ? 0 : max_sublayers_minus1); + i <= max_sublayers_minus1; i++) { + ues(dpb_max_dec_pic_buffering_minus1[i], 0, VVC_MAX_DPB_SIZE - 1, 1, i); + ues(dpb_max_num_reorder_pics[i], + 0, current->dpb_max_dec_pic_buffering_minus1[i], 1, i); + ues(dpb_max_latency_increase_plus1[i], 0, UINT32_MAX - 1, 1, i); + } + return 0; +} + +static int FUNC(ref_pic_list_struct) (CodedBitstreamContext *ctx, + RWContext *rw, + H266RefPicListStruct *current, + uint8_t list_idx, uint8_t rpls_idx, + const H266RawSPS *sps) +{ + CodedBitstreamH266Context *h266 = ctx->priv_data; + int err, i, j, general_layer_idx = -1, num_direct_ref_layers = 0; + const H266RawVPS *vps = h266->vps[sps->sps_video_parameter_set_id]; + + if (!vps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "VPS id %d not available.\n", sps->sps_video_parameter_set_id); + return AVERROR_INVALIDDATA; + } + //7.4.3.3 (29) + for (i = 0; i <= vps->vps_max_layers_minus1; i++) { + if (sps->nal_unit_header.nuh_layer_id == vps->vps_layer_id[i]) { + general_layer_idx = i; + break; + } + } + if (general_layer_idx < 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "vps_layer_id %d not available.\n", + sps->nal_unit_header.nuh_layer_id); + return AVERROR_INVALIDDATA; + } + //7.4.3.3 (28) + for (j = 0; j <= vps->vps_max_layers_minus1; j++) { + if (vps->vps_direct_ref_layer_flag[general_layer_idx][j]) + num_direct_ref_layers++; + } + + ue(num_ref_entries, 0, VVC_MAX_REF_ENTRIES); + if (sps->sps_long_term_ref_pics_flag && + rpls_idx < sps->sps_num_ref_pic_lists[list_idx] && + current->num_ref_entries > 0) + flag(ltrp_in_header_flag); + if (sps->sps_long_term_ref_pics_flag && + rpls_idx == sps->sps_num_ref_pic_lists[list_idx]) + infer(ltrp_in_header_flag, 1); + for (i = 0, j = 0; i < current->num_ref_entries; i++) { + if (sps->sps_inter_layer_prediction_enabled_flag) + flags(inter_layer_ref_pic_flag[i], 1, i); + else + infer(inter_layer_ref_pic_flag[i], 0); + + if (!current->inter_layer_ref_pic_flag[i]) { + if (sps->sps_long_term_ref_pics_flag) + flags(st_ref_pic_flag[i], 1, i); + else + infer(st_ref_pic_flag[i], 1); + if (current->st_ref_pic_flag[i]) { + int abs_delta_poc_st; + ues(abs_delta_poc_st[i], 0, MAX_UINT_BITS(15), 1, i); + if ((sps->sps_weighted_pred_flag || + sps->sps_weighted_bipred_flag) && i != 0) + abs_delta_poc_st = current->abs_delta_poc_st[i]; + else + abs_delta_poc_st = current->abs_delta_poc_st[i] + 1; + if (abs_delta_poc_st > 0) + flags(strp_entry_sign_flag[i], 1, i); + } else { + if (!current->ltrp_in_header_flag) { + uint8_t bits = sps->sps_log2_max_pic_order_cnt_lsb_minus4 + 4; + ubs(bits, rpls_poc_lsb_lt[j], 1, j); + j++; + } + } + } else { + if (num_direct_ref_layers == 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "num_direct_ref_layers needs > 0.\n"); + return AVERROR_INVALIDDATA; + } + ues(ilrp_idx[i], 0, num_direct_ref_layers - 1, 1, i); + } + } + return 0; +} + +static int FUNC(ref_pic_lists) (CodedBitstreamContext *ctx, RWContext *rw, + const H266RawSPS *sps, const H266RawPPS *pps, + H266RefPicLists *current) { + const H266RefPicListStruct * ref_list; + int err, i, j, num_ltrp_entries; + for (i = 0; i < 2; i++) { + if (sps->sps_num_ref_pic_lists[i] > 0 && + (i == 0 || (i == 1 && pps->pps_rpl1_idx_present_flag))) { + flags(rpl_sps_flag[i], 1, i); + } else { + if (sps->sps_num_ref_pic_lists[i] == 0) { + infer(rpl_sps_flag[i], 0); + } else { + if (!pps->pps_rpl1_idx_present_flag && i == 1) + infer(rpl_sps_flag[1], current->rpl_sps_flag[0]); + } + } + if (current->rpl_sps_flag[i]) { + if (sps->sps_num_ref_pic_lists[i] > 1 && + (i == 0 || (i == 1 && pps->pps_rpl1_idx_present_flag))) { + uint8_t bits = av_ceil_log2(sps->sps_num_ref_pic_lists[i]); + us(bits, rpl_idx[i], 0, sps->sps_num_ref_pic_lists[i] - 1, 1, i); + } else if (sps->sps_num_ref_pic_lists[i] == 1) { + infer(rpl_idx[i], 0); + } else if (i == 1 && !pps->pps_rpl1_idx_present_flag) { + infer(rpl_idx[1], current->rpl_idx[0]); + } else { + //how to handle this? or never happpend? + av_log(ctx->log_ctx, AV_LOG_ERROR, + "can't infer the rpl_idx[i]\n"); + return AVERROR_PATCHWELCOME; + } + memcpy(¤t->rpl_ref_list[i], + &sps->sps_ref_pic_list_struct[i][current->rpl_idx[i]], + sizeof(current->rpl_ref_list[i])); + } else { + CHECK(FUNC(ref_pic_list_struct) (ctx, rw, ¤t->rpl_ref_list[i], + i, sps->sps_num_ref_pic_lists[i], + sps)); + } + ref_list = ¤t->rpl_ref_list[i]; + + num_ltrp_entries = 0; + for (int k = 0; k < ref_list->num_ref_entries; k++) { + if (!ref_list->inter_layer_ref_pic_flag[k]) { + if (!ref_list->st_ref_pic_flag[k]) { + num_ltrp_entries++; + } + } + } + + for (j = 0; j < num_ltrp_entries; j++) { + if (ref_list->ltrp_in_header_flag) { + ubs(sps->sps_log2_max_pic_order_cnt_lsb_minus4 + 4, + poc_lsb_lt[i][j], 2, i, j); + } + flags(delta_poc_msb_cycle_present_flag[i][j], 2, i, j); + if (current->delta_poc_msb_cycle_present_flag[i][j]) { + uint32_t max = + 1 << (32 - sps->sps_log2_max_pic_order_cnt_lsb_minus4 - 4); + ues(delta_poc_msb_cycle_lt[i][j], 0, max, 2, i, j); + } + } + } + return 0; +} + +static int FUNC(general_timing_hrd_parameters)(CodedBitstreamContext *ctx, + RWContext *rw, + H266RawGeneralTimingHrdParameters *current) +{ + int err; + ub(32, num_units_in_tick); + u(32, time_scale, 1, MAX_UINT_BITS(32)); + flag(general_nal_hrd_params_present_flag); + flag(general_vcl_hrd_params_present_flag); + + if (current->general_nal_hrd_params_present_flag || + current->general_vcl_hrd_params_present_flag) { + flag(general_same_pic_timing_in_all_ols_flag); + flag(general_du_hrd_params_present_flag); + if (current->general_du_hrd_params_present_flag) + ub(8, tick_divisor_minus2); + ub(4, bit_rate_scale); + ub(4, cpb_size_scale); + if (current->general_du_hrd_params_present_flag) + ub(4, cpb_size_du_scale); + ue(hrd_cpb_cnt_minus1, 0, 31); + } else { + //infer general_same_pic_timing_in_all_ols_flag? + infer(general_du_hrd_params_present_flag, 0); + } + return 0; +} + +static int FUNC(sublayer_hrd_parameters) (CodedBitstreamContext *ctx, + RWContext *rw, + H266RawSubLayerHRDParameters *current, + int sublayer_id, + const H266RawGeneralTimingHrdParameters *general) +{ + int err, i; + for (i = 0; i <= general->hrd_cpb_cnt_minus1; i++) { + ues(bit_rate_value_minus1[sublayer_id][i], 0, UINT32_MAX - 1, 2, + sublayer_id, i); + ues(cpb_size_value_minus1[sublayer_id][i], 0, UINT32_MAX - 1, 2, + sublayer_id, i); + if (general->general_du_hrd_params_present_flag) { + ues(cpb_size_du_value_minus1[sublayer_id][i], + 0, UINT32_MAX - 1, 2, sublayer_id, i); + ues(bit_rate_du_value_minus1[sublayer_id][i], + 0, UINT32_MAX - 1, 2, sublayer_id, i); + } + flags(cbr_flag[sublayer_id][i], 2, sublayer_id, i); + } + return 0; +} + +static int FUNC(ols_timing_hrd_parameters) (CodedBitstreamContext *ctx, + RWContext *rw, H266RawOlsTimingHrdParameters *current, + uint8_t first_sublayer, uint8_t max_sublayers_minus1, + const H266RawGeneralTimingHrdParameters *general) +{ + int err, i; + for (i = first_sublayer; i <= max_sublayers_minus1; i++) { + flags(fixed_pic_rate_general_flag[i], 1, i); + if (!current->fixed_pic_rate_general_flag[i]) + flags(fixed_pic_rate_within_cvs_flag[i], 1, i); + else + infer(fixed_pic_rate_within_cvs_flag[i], 1); + if (current->fixed_pic_rate_within_cvs_flag[i]) { + ues(elemental_duration_in_tc_minus1[i], 0, 2047, 1, i); + infer(low_delay_hrd_flag[i], 0); + } else if ((general->general_nal_hrd_params_present_flag || + general->general_vcl_hrd_params_present_flag) && + general->hrd_cpb_cnt_minus1 == 0) { + flags(low_delay_hrd_flag[i], 1, i); + } else { + infer(low_delay_hrd_flag[i], 0); + } + if (general->general_nal_hrd_params_present_flag) + CHECK(FUNC(sublayer_hrd_parameters) (ctx, rw, + ¤t->nal_sub_layer_hrd_parameters, + i, general)); + if (general->general_vcl_hrd_params_present_flag) + CHECK(FUNC(sublayer_hrd_parameters) (ctx, rw, + ¤t->nal_sub_layer_hrd_parameters, + i, general)); + } + return 0; +} + +static int FUNC(opi)(CodedBitstreamContext *ctx, RWContext *rw, + H266RawOPI *current) +{ + int err; + + HEADER("Operating point information"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, + ¤t->nal_unit_header, VVC_OPI_NUT)); + + flag(opi_ols_info_present_flag); + flag(opi_htid_info_present_flag); + + if(current->opi_ols_info_present_flag) + ue(opi_ols_idx, 0, VVC_MAX_TOTAL_NUM_OLSS - 1); + + if(current->opi_htid_info_present_flag) + ub(3, opi_htid_plus1); + + flag(opi_extension_flag); + if (current->opi_extension_flag) + CHECK(FUNC(extension_data) (ctx, rw, ¤t->extension_data)); + CHECK(FUNC(rbsp_trailing_bits) (ctx, rw)); + + return 0; +} + +static int FUNC(dci)(CodedBitstreamContext *ctx, RWContext *rw, + H266RawDCI *current) +{ + int err, i; + + HEADER("Decoding capability information"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, + ¤t->nal_unit_header, VVC_DCI_NUT)); + + ub(4, dci_reserved_zero_4bits); + ub(4, dci_num_ptls_minus1); + for (i = 0; i <= current->dci_num_ptls_minus1; i++) + CHECK(FUNC(profile_tier_level)(ctx, rw, + current->dci_profile_tier_level + i, 1, 0)); + + flag(dci_extension_flag); + if (current->dci_extension_flag) + CHECK(FUNC(extension_data)(ctx, rw, ¤t->extension_data)); + CHECK(FUNC(rbsp_trailing_bits)(ctx, rw)); + + return 0; +} + +static int FUNC(vps) (CodedBitstreamContext *ctx, RWContext *rw, + H266RawVPS *current) +{ + int err, i, j, k; + uint16_t total_num_olss = 0; + uint8_t ols_mode_idc = 0; + uint16_t num_multi_layer_olss = 0; + uint8_t layer_included_in_ols_flag[VVC_MAX_TOTAL_NUM_OLSS][VVC_MAX_LAYERS]; + uint8_t num_ref_layers[VVC_MAX_LAYERS]; + uint8_t reference_layer_idx[VVC_MAX_LAYERS][VVC_MAX_LAYERS]; + + HEADER("Video Parameter Set"); + + CHECK(FUNC(nal_unit_header) (ctx, rw, + ¤t->nal_unit_header, VVC_VPS_NUT)); + + u(4, vps_video_parameter_set_id, 1, VVC_MAX_VPS_COUNT - 1); + ub(6, vps_max_layers_minus1); + u(3, vps_max_sublayers_minus1, 0, 6); + if (current->vps_max_layers_minus1 > 0 + && current->vps_max_sublayers_minus1 > 0) + flag(vps_default_ptl_dpb_hrd_max_tid_flag); + else + infer(vps_default_ptl_dpb_hrd_max_tid_flag, 1); + + if (current->vps_max_layers_minus1 > 0) + flag(vps_all_independent_layers_flag); + else + infer(vps_all_independent_layers_flag, 1); + + for (i = 0; i <= current->vps_max_layers_minus1; i++) { + ubs(6, vps_layer_id[i], 1, i); + if (i > 0 && current->vps_layer_id[i] <= current->vps_layer_id[i - 1]) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "vps_layer_id[%d](%d) should > vps_layer_id[%d](%d).\n", + i, current->vps_layer_id[i], i - 1, + current->vps_layer_id[i - 1]); + return AVERROR_INVALIDDATA; + } + if (i > 0 && !current->vps_all_independent_layers_flag) { + flags(vps_independent_layer_flag[i], 1, i); + if (!current->vps_independent_layer_flag[i]) { + flags(vps_max_tid_ref_present_flag[i], 1, i); + for (j = 0; j < i; j++) { + flags(vps_direct_ref_layer_flag[i][j], 2, i, j); + if (current->vps_max_tid_ref_present_flag[i] && + current->vps_direct_ref_layer_flag[i][j]) { + ubs(3, vps_max_tid_il_ref_pics_plus1[i][j], 2, i, j); + } else { + infer(vps_max_tid_il_ref_pics_plus1[i][j], + current->vps_max_sublayers_minus1 + 1); + } + } + } else { + for (j = 0; j < i; j++) { + infer(vps_direct_ref_layer_flag[i][j], 0); + } + } + } else { + infer(vps_independent_layer_flag[i], 1); + for (j = 0; j < i; j++) { + infer(vps_direct_ref_layer_flag[i][j], 0); + } + } + } + + if (current->vps_max_layers_minus1 > 0) { + if (current->vps_all_independent_layers_flag) + flag(vps_each_layer_is_an_ols_flag); + else + infer(vps_each_layer_is_an_ols_flag, 0); + if (!current->vps_each_layer_is_an_ols_flag) { + if (!current->vps_all_independent_layers_flag) + ub(2, vps_ols_mode_idc); + else + infer(vps_ols_mode_idc, 2); + if (current->vps_ols_mode_idc == 2) { + ub(8, vps_num_output_layer_sets_minus2); + for (i = 1; i <= current->vps_num_output_layer_sets_minus2 + 1; + i++) + for (j = 0; j <= current->vps_max_layers_minus1; j++) + flags(vps_ols_output_layer_flag[i][j], 2, i, j); + } + ols_mode_idc = current->vps_ols_mode_idc; + } else { + ols_mode_idc = 4; + } + if (ols_mode_idc == 4 || ols_mode_idc == 0 || ols_mode_idc == 1) + total_num_olss = current->vps_max_layers_minus1 + 1; + else if (ols_mode_idc == 2) + total_num_olss = current->vps_num_output_layer_sets_minus2 + 2; + else + av_log(ctx->log_ctx, AV_LOG_ERROR, + "ols_mode_idc == 3, patch welcome"); + u(8, vps_num_ptls_minus1, 0, total_num_olss - 1); + } else { + infer(vps_each_layer_is_an_ols_flag, 1); + infer(vps_num_ptls_minus1, 0); + } + { + //calc NumMultiLayerOlss + int m; + uint8_t dependency_flag[VVC_MAX_LAYERS][VVC_MAX_LAYERS]; + uint16_t num_output_layers_in_ols[VVC_MAX_TOTAL_NUM_OLSS]; + uint8_t num_sub_layers_in_layer_in_ols[VVC_MAX_TOTAL_NUM_OLSS][VVC_MAX_TOTAL_NUM_OLSS]; + uint8_t output_layer_idx[VVC_MAX_TOTAL_NUM_OLSS][VVC_MAX_LAYERS]; + + //7.4.3.3 vps_direct_ref_layer_flag section + for (i = 0; i <= current->vps_max_layers_minus1; i++) { + for (j = 0; j <= current->vps_max_layers_minus1; j++) { + dependency_flag[i][j] = current->vps_direct_ref_layer_flag[i][j]; + for (k = 0; k < i; k++) { + if (current->vps_direct_ref_layer_flag[i][k] && + dependency_flag[k][j]) + dependency_flag[i][j] = 1; + } + } + } + for (i = 0; i <= current->vps_max_layers_minus1; i++) { + int r; + for (j = 0, r = 0; j <= current->vps_max_layers_minus1; j++) { + if (dependency_flag[i][j]) + reference_layer_idx[i][r++] = j; + } + num_ref_layers[i] = r; + } + + //7.4.3.3 vps_ols_output_layer_flag section + num_output_layers_in_ols[0] = 1; + num_sub_layers_in_layer_in_ols[0][0] = + current->vps_ptl_max_tid[current->vps_ols_ptl_idx[0]] + 1; + for (i = 1; i < total_num_olss; i++) { + if (ols_mode_idc == 4 || ols_mode_idc == 0) { + num_output_layers_in_ols[i] = 1; + if (current->vps_each_layer_is_an_ols_flag) { + num_sub_layers_in_layer_in_ols[i][0] = + current->vps_ptl_max_tid[current->vps_ols_ptl_idx[i]] + 1; + } else { + num_sub_layers_in_layer_in_ols[i][i] = + current->vps_ptl_max_tid[current->vps_ols_ptl_idx[i]] + 1; + for (k = i - 1; k >= 0; k--) { + num_sub_layers_in_layer_in_ols[i][k] = 0; + for (m = k + 1; m <= i; m++) { + uint8_t max_sublayer_needed = + FFMIN(num_sub_layers_in_layer_in_ols[i][m], + current->vps_max_tid_il_ref_pics_plus1[m][k]); + if (current->vps_direct_ref_layer_flag[m][k] && + num_sub_layers_in_layer_in_ols[i][k] < max_sublayer_needed) + num_sub_layers_in_layer_in_ols[i][k] = max_sublayer_needed; + } + } + } + } else if (current->vps_ols_mode_idc == 1) { + num_output_layers_in_ols[i] = i + 1; + for (j = 0; j < num_output_layers_in_ols[i]; j++) { + num_sub_layers_in_layer_in_ols[i][j] = + current->vps_ptl_max_tid[current->vps_ols_ptl_idx[i]] + 1; + } + } else if (current->vps_ols_mode_idc == 2) { + uint8_t highest_included_layer = 0; + for (j = 0; j <= current->vps_max_layers_minus1; j++) { + layer_included_in_ols_flag[i][j] = 0; + num_sub_layers_in_layer_in_ols[i][j] = 0; + } + for (k = 0, j = 0; k <= current->vps_max_layers_minus1; k++) { + if (current->vps_ols_output_layer_flag[i][k]) { + layer_included_in_ols_flag[i][k] = 1; + highest_included_layer = k; + output_layer_idx[i][j] = k; + num_sub_layers_in_layer_in_ols[i][k] = + current->vps_ptl_max_tid[current-> + vps_ols_ptl_idx[i]] + 1; + j++; + } + } + num_output_layers_in_ols[i] = j; + for (j = 0; j < num_output_layers_in_ols[i]; j++) { + int idx = output_layer_idx[i][j]; + for (k = 0; k < num_ref_layers[idx]; k++) { + if (!layer_included_in_ols_flag[i][reference_layer_idx[idx][k]]) + layer_included_in_ols_flag[i][reference_layer_idx[idx][k]] = 1; + } + } + for (k = highest_included_layer - 1; k >= 0; k--) { + if (layer_included_in_ols_flag[i][k] && + !current->vps_ols_output_layer_flag[i][k]) { + for (m = k + 1; m <= highest_included_layer; m++) { + uint8_t max_sublayer_needed = + FFMIN(num_sub_layers_in_layer_in_ols[i][m], + current->vps_max_tid_il_ref_pics_plus1[m][k]); + if (current->vps_direct_ref_layer_flag[m][k] && + layer_included_in_ols_flag[i][m] && + num_sub_layers_in_layer_in_ols[i][k] < + max_sublayer_needed) + num_sub_layers_in_layer_in_ols[i][k] = + max_sublayer_needed; + } + } + } + } + } + for (i = 1; i < total_num_olss; i++) { + int num_layers_in_ols = 0; + if (current->vps_each_layer_is_an_ols_flag) { + num_layers_in_ols = 1; + } else if (current->vps_ols_mode_idc == 0 || + current->vps_ols_mode_idc == 1) { + num_layers_in_ols = i + 1; + } else if (current->vps_ols_mode_idc == 2) { + for (k = 0, j = 0; k <= current->vps_max_layers_minus1; k++) { + if (layer_included_in_ols_flag[i][k]) + j++; + num_layers_in_ols = j; + } + } + if (num_layers_in_ols > 1) { + num_multi_layer_olss++; + } + } + } + + for (i = 0; i <= current->vps_num_ptls_minus1; i++) { + if (i > 0) + flags(vps_pt_present_flag[i], 1, i); + else + infer(vps_pt_present_flag[i], 1); + + if (!current->vps_default_ptl_dpb_hrd_max_tid_flag) + us(3, vps_ptl_max_tid[i], 0, current->vps_max_sublayers_minus1, 1, i); + else + infer(vps_ptl_max_tid[i], current->vps_max_sublayers_minus1); + } + while (byte_alignment(rw) != 0) + fixed(1, vps_ptl_alignment_zero_bit, 0); + for (i = 0; i <= current->vps_num_ptls_minus1; i++) { + CHECK(FUNC(profile_tier_level) (ctx, rw, + current->vps_profile_tier_level + i, + current->vps_pt_present_flag[i], + current->vps_ptl_max_tid[i])); + } + for (i = 0; i < total_num_olss; i++) { + if (current->vps_num_ptls_minus1 > 0 && + current->vps_num_ptls_minus1 + 1 != total_num_olss) { + us(8, vps_ols_ptl_idx[i], 0, current->vps_num_ptls_minus1, 1, i); + } else if (current->vps_num_ptls_minus1 == 0) { + infer(vps_ols_ptl_idx[i], 0); + } else { + infer(vps_ols_ptl_idx[i], i); + } + } + + if (!current->vps_each_layer_is_an_ols_flag) { + uint16_t vps_num_dpb_params; + ue(vps_num_dpb_params_minus1, 0, num_multi_layer_olss - 1); + if (current->vps_each_layer_is_an_ols_flag) + vps_num_dpb_params = 0; + else + vps_num_dpb_params = current->vps_num_dpb_params_minus1 + 1; + + if (current->vps_max_sublayers_minus1 > 0) + flag(vps_sublayer_dpb_params_present_flag); + else + infer(vps_sublayer_dpb_params_present_flag, 0); + + for (i = 0; i < vps_num_dpb_params; i++) { + if (!current->vps_default_ptl_dpb_hrd_max_tid_flag) + us(3, vps_dpb_max_tid[i], 0, current->vps_max_sublayers_minus1, + 1, i); + else + infer(vps_dpb_max_tid[i], current->vps_max_sublayers_minus1); + CHECK(FUNC(dpb_parameters) (ctx, rw, current->vps_dpb_params + i, + current->vps_dpb_max_tid[i], + current-> + vps_sublayer_dpb_params_present_flag)); + } + for (i = 0; i < num_multi_layer_olss; i++) { + ues(vps_ols_dpb_pic_width[i], 0, UINT16_MAX, 1, i); + ues(vps_ols_dpb_pic_height[i], 0, UINT16_MAX, 1, i); + ubs(2, vps_ols_dpb_chroma_format[i], 1, i); + ues(vps_ols_dpb_bitdepth_minus8[i], 0, 8, 1, i); + if (vps_num_dpb_params > 1 + && vps_num_dpb_params != num_multi_layer_olss) + ues(vps_ols_dpb_params_idx[i], 0, vps_num_dpb_params - 1, 1, i); + else if (vps_num_dpb_params == 1) + infer(vps_ols_dpb_params_idx[i], 0); + else + infer(vps_ols_dpb_params_idx[i], i); + } + flag(vps_timing_hrd_params_present_flag); + if (current->vps_timing_hrd_params_present_flag) { + CHECK(FUNC(general_timing_hrd_parameters) (ctx, rw, + ¤t-> + vps_general_timing_hrd_parameters)); + if (current->vps_max_sublayers_minus1 > 0) + flag(vps_sublayer_cpb_params_present_flag); + else + infer(vps_sublayer_cpb_params_present_flag, 0); + ue(vps_num_ols_timing_hrd_params_minus1, 0, + num_multi_layer_olss - 1); + for (i = 0; i <= current->vps_num_ols_timing_hrd_params_minus1; i++) { + uint8_t first_sublayer; + if (!current->vps_default_ptl_dpb_hrd_max_tid_flag) + us(3, vps_hrd_max_tid[i], 0, + current->vps_max_sublayers_minus1, 1, i); + else + infer(vps_hrd_max_tid[i], + current->vps_max_sublayers_minus1); + first_sublayer = current->vps_sublayer_cpb_params_present_flag ? + 0 : current->vps_hrd_max_tid[i]; + CHECK(FUNC(ols_timing_hrd_parameters) + (ctx, rw, ¤t->vps_ols_timing_hrd_parameters, + first_sublayer, current->vps_max_sublayers_minus1, + ¤t->vps_general_timing_hrd_parameters)); + + } + if (current->vps_num_ols_timing_hrd_params_minus1 > 0 && + current->vps_num_ols_timing_hrd_params_minus1 + 1 != + num_multi_layer_olss) { + for (i = 0; i < num_multi_layer_olss; i++) { + ues(vps_ols_timing_hrd_idx[i], 0, + current->vps_num_ols_timing_hrd_params_minus1, 1, i); + } + } else if (current->vps_num_ols_timing_hrd_params_minus1 == 0) { + for (i = 0; i < num_multi_layer_olss; i++) + infer(vps_ols_timing_hrd_idx[i], 0); + } else { + for (i = 0; i < num_multi_layer_olss; i++) + infer(vps_ols_timing_hrd_idx[i], i); + } + } + } + + flag(vps_extension_flag); + if (current->vps_extension_flag) + CHECK(FUNC(extension_data) (ctx, rw, ¤t->extension_data)); + CHECK(FUNC(rbsp_trailing_bits) (ctx, rw)); + + return 0; +} + +static int FUNC(sps_range_extension)(CodedBitstreamContext *ctx, RWContext *rw, + H266RawSPS *current) +{ + int err; + + flag(sps_extended_precision_flag); + if (current->sps_transform_skip_enabled_flag) + flag(sps_ts_residual_coding_rice_present_in_sh_flag); + else + infer(sps_ts_residual_coding_rice_present_in_sh_flag, 0); + flag(sps_rrc_rice_extension_flag); + flag(sps_persistent_rice_adaptation_enabled_flag); + flag(sps_reverse_last_sig_coeff_enabled_flag); + + return 0; +} + +static int FUNC(sps)(CodedBitstreamContext *ctx, RWContext *rw, + H266RawSPS *current) +{ + CodedBitstreamH266Context *h266 = ctx->priv_data; + int err, i, j; + unsigned int ctb_log2_size_y, min_cb_log2_size_y, + min_qt_log2_size_intra_y, min_qt_log2_size_inter_y, + ctb_size_y, max_num_merge_cand, tmp_width_val, tmp_height_val; + uint8_t qp_bd_offset; + + static const uint8_t h266_sub_width_c[] = { + 1, 2, 2, 1 + }; + static const uint8_t h266_sub_height_c[] = { + 1, 2, 1, 1 + }; + + HEADER("Sequence Parameter Set"); + + CHECK(FUNC(nal_unit_header) (ctx, rw, + ¤t->nal_unit_header, VVC_SPS_NUT)); + + ub(4, sps_seq_parameter_set_id); + ub(4, sps_video_parameter_set_id); + if (current->sps_video_parameter_set_id == 0 && !h266->vps_ref[0]) { + H266RawVPS *vps; + AVBufferRef *ref = av_buffer_allocz(sizeof(H266RawVPS)); + if (!ref) { + return AVERROR(ENOMEM); + } + vps = (H266RawVPS *) ref->data; + vps->vps_max_layers_minus1 = 0; + vps->vps_independent_layer_flag[0] = 1; + vps->vps_layer_id[0] = current->nal_unit_header.nuh_layer_id; + h266->vps_ref[0] = ref; + h266->vps[0] = vps; + } + + u(3, sps_max_sublayers_minus1, 0, VVC_MAX_SUBLAYERS - 1); + u(2, sps_chroma_format_idc, 0, 3); + u(2, sps_log2_ctu_size_minus5, 0, 3); + ctb_log2_size_y = current->sps_log2_ctu_size_minus5 + 5; + ctb_size_y = 1 << ctb_log2_size_y; + + flag(sps_ptl_dpb_hrd_params_present_flag); + if (current->sps_ptl_dpb_hrd_params_present_flag) { + CHECK(FUNC(profile_tier_level) (ctx, rw, ¤t->profile_tier_level, + 1, current->sps_max_sublayers_minus1)); + } + flag(sps_gdr_enabled_flag); + flag(sps_ref_pic_resampling_enabled_flag); + if (current->sps_ref_pic_resampling_enabled_flag) + flag(sps_res_change_in_clvs_allowed_flag); + else + infer(sps_res_change_in_clvs_allowed_flag, 0); + + ue(sps_pic_width_max_in_luma_samples, 1, VVC_MAX_WIDTH); + ue(sps_pic_height_max_in_luma_samples, 1, VVC_MAX_HEIGHT); + + flag(sps_conformance_window_flag); + if (current->sps_conformance_window_flag) { + uint8_t sub_width_c = h266_sub_width_c[current->sps_chroma_format_idc]; + uint8_t sub_height_c = h266_sub_height_c[current->sps_chroma_format_idc]; + uint16_t width = current->sps_pic_width_max_in_luma_samples / sub_width_c; + uint16_t height = current->sps_pic_height_max_in_luma_samples / sub_height_c; + ue(sps_conf_win_left_offset, 0, width); + ue(sps_conf_win_right_offset, 0, width - current->sps_conf_win_left_offset); + ue(sps_conf_win_top_offset, 0, height); + ue(sps_conf_win_bottom_offset, 0, height - current->sps_conf_win_top_offset); + } else { + infer(sps_conf_win_left_offset, 0); + infer(sps_conf_win_right_offset, 0); + infer(sps_conf_win_top_offset, 0); + infer(sps_conf_win_bottom_offset, 0); + } + + tmp_width_val = AV_CEIL_RSHIFT(current->sps_pic_width_max_in_luma_samples, + ctb_log2_size_y); + tmp_height_val = AV_CEIL_RSHIFT(current->sps_pic_height_max_in_luma_samples, + ctb_log2_size_y); + + flag(sps_subpic_info_present_flag); + if (current->sps_subpic_info_present_flag) { + ue(sps_num_subpics_minus1, 1, VVC_MAX_SLICES - 1); + if (current->sps_num_subpics_minus1 > 0) { + flag(sps_independent_subpics_flag); + flag(sps_subpic_same_size_flag); + } + + if (current->sps_num_subpics_minus1 > 0) { + int wlen = av_ceil_log2(tmp_width_val); + int hlen = av_ceil_log2(tmp_height_val); + infer(sps_subpic_ctu_top_left_x[0], 0); + infer(sps_subpic_ctu_top_left_y[0], 0); + if (current->sps_pic_width_max_in_luma_samples > ctb_size_y) + ubs(wlen, sps_subpic_width_minus1[0], 1, 0); + else + infer(sps_subpic_width_minus1[0], tmp_width_val - 1); + if (current->sps_pic_height_max_in_luma_samples > ctb_size_y) + ubs(hlen, sps_subpic_height_minus1[0], 1, 0); + else + infer(sps_subpic_height_minus1[0], tmp_height_val - 1); + if (!current->sps_independent_subpics_flag) { + flags(sps_subpic_treated_as_pic_flag[0], 1, 0); + flags(sps_loop_filter_across_subpic_enabled_flag[0], 1, 0); + } else { + infer(sps_subpic_treated_as_pic_flag[0], 1); + infer(sps_loop_filter_across_subpic_enabled_flag[0], 1); + } + for (i = 1; i <= current->sps_num_subpics_minus1; i++) { + if (!current->sps_subpic_same_size_flag) { + if (current->sps_pic_width_max_in_luma_samples > ctb_size_y) + ubs(wlen, sps_subpic_ctu_top_left_x[i], 1, i); + else + infer(sps_subpic_ctu_top_left_x[i], 0); + if (current->sps_pic_height_max_in_luma_samples > + ctb_size_y) + ubs(hlen, sps_subpic_ctu_top_left_y[i], 1, i); + else + infer(sps_subpic_ctu_top_left_y[i], 0); + if (i < current->sps_num_subpics_minus1 && + current->sps_pic_width_max_in_luma_samples > + ctb_size_y) { + ubs(wlen, sps_subpic_width_minus1[i], 1, i); + } else { + infer(sps_subpic_width_minus1[i], + tmp_width_val - + current->sps_subpic_ctu_top_left_x[i] - 1); + } + if (i < current->sps_num_subpics_minus1 && + current->sps_pic_height_max_in_luma_samples > + ctb_size_y) { + ubs(hlen, sps_subpic_height_minus1[i], 1, i); + } else { + infer(sps_subpic_height_minus1[i], + tmp_height_val - + current->sps_subpic_ctu_top_left_y[i] - 1); + } + } else { + int num_subpic_cols = tmp_width_val / + (current->sps_subpic_width_minus1[0] + 1); + if (tmp_width_val % (current->sps_subpic_width_minus1[0] + 1) || + tmp_height_val % (current->sps_subpic_width_minus1[0] + 1) || + current->sps_num_subpics_minus1 != + (num_subpic_cols * tmp_height_val / + (current->sps_subpic_height_minus1[0] + 1) - 1)) + return AVERROR_INVALIDDATA; + infer(sps_subpic_ctu_top_left_x[i], + (i % num_subpic_cols) * + (current->sps_subpic_width_minus1[0] + 1)); + infer(sps_subpic_ctu_top_left_y[i], + (i / num_subpic_cols) * + (current->sps_subpic_height_minus1[0] + 1)); + infer(sps_subpic_width_minus1[i], + current->sps_subpic_width_minus1[0]); + infer(sps_subpic_height_minus1[i], + current->sps_subpic_height_minus1[0]); + } + if (!current->sps_independent_subpics_flag) { + flags(sps_subpic_treated_as_pic_flag[i], 1, i); + flags(sps_loop_filter_across_subpic_enabled_flag[i], 1, i); + } else { + infer(sps_subpic_treated_as_pic_flag[i], 1); + infer(sps_loop_filter_across_subpic_enabled_flag[i], 0); + } + } + ue(sps_subpic_id_len_minus1, 0, 15); + if ((1 << (current->sps_subpic_id_len_minus1 + 1)) < + current->sps_num_subpics_minus1 + 1) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "sps_subpic_id_len_minus1(%d) is too small\n", + current->sps_subpic_id_len_minus1); + return AVERROR_INVALIDDATA; + } + flag(sps_subpic_id_mapping_explicitly_signalled_flag); + if (current->sps_subpic_id_mapping_explicitly_signalled_flag) { + flag(sps_subpic_id_mapping_present_flag); + if (current->sps_subpic_id_mapping_present_flag) { + for (i = 0; i <= current->sps_num_subpics_minus1; i++) { + ubs(current->sps_subpic_id_len_minus1 + 1, + sps_subpic_id[i], 1, i); + } + } + } + } else { + infer(sps_subpic_ctu_top_left_x[0], 0); + infer(sps_subpic_ctu_top_left_y[0], 0); + infer(sps_subpic_width_minus1[0], tmp_width_val - 1); + infer(sps_subpic_height_minus1[0], tmp_height_val - 1); + } + } else { + infer(sps_num_subpics_minus1, 0); + infer(sps_independent_subpics_flag, 1); + infer(sps_subpic_same_size_flag, 0); + infer(sps_subpic_id_mapping_explicitly_signalled_flag, 0); + infer(sps_subpic_ctu_top_left_x[0], 0); + infer(sps_subpic_ctu_top_left_y[0], 0); + infer(sps_subpic_width_minus1[0], tmp_width_val - 1); + infer(sps_subpic_height_minus1[0], tmp_height_val - 1); + } + + + ue(sps_bitdepth_minus8, 0, 8); + qp_bd_offset = 6 * current->sps_bitdepth_minus8; + + flag(sps_entropy_coding_sync_enabled_flag); + flag(sps_entry_point_offsets_present_flag); + + u(4, sps_log2_max_pic_order_cnt_lsb_minus4, 0, 12); + flag(sps_poc_msb_cycle_flag); + if (current->sps_poc_msb_cycle_flag) + ue(sps_poc_msb_cycle_len_minus1, + 0, 32 - current->sps_log2_max_pic_order_cnt_lsb_minus4 - 5); + + u(2, sps_num_extra_ph_bytes, 0, 2); + for (i = 0; i < (current->sps_num_extra_ph_bytes * 8); i++) { + flags(sps_extra_ph_bit_present_flag[i], 1, i); + } + + u(2, sps_num_extra_sh_bytes, 0, 2); + for (i = 0; i < (current->sps_num_extra_sh_bytes * 8); i++) { + flags(sps_extra_sh_bit_present_flag[i], 1, i); + } + + if (current->sps_ptl_dpb_hrd_params_present_flag) { + if (current->sps_max_sublayers_minus1 > 0) + flag(sps_sublayer_dpb_params_flag); + else + infer(sps_sublayer_dpb_params_flag, 0); + CHECK(FUNC(dpb_parameters) (ctx, rw, ¤t->sps_dpb_params, + current->sps_max_sublayers_minus1, + current->sps_sublayer_dpb_params_flag)); + } + + ue(sps_log2_min_luma_coding_block_size_minus2, + 0, FFMIN(4, current->sps_log2_ctu_size_minus5 + 3)); + min_cb_log2_size_y = + current->sps_log2_min_luma_coding_block_size_minus2 + 2; + + flag(sps_partition_constraints_override_enabled_flag); + + ue(sps_log2_diff_min_qt_min_cb_intra_slice_luma, + 0, FFMIN(6, ctb_log2_size_y) - min_cb_log2_size_y); + min_qt_log2_size_intra_y = + current->sps_log2_diff_min_qt_min_cb_intra_slice_luma + + min_cb_log2_size_y; + + ue(sps_max_mtt_hierarchy_depth_intra_slice_luma, + 0, 2 * (ctb_log2_size_y - min_cb_log2_size_y)); + + if (current->sps_max_mtt_hierarchy_depth_intra_slice_luma != 0) { + ue(sps_log2_diff_max_bt_min_qt_intra_slice_luma, + 0, ctb_log2_size_y - min_qt_log2_size_intra_y); + ue(sps_log2_diff_max_tt_min_qt_intra_slice_luma, + 0, FFMIN(6, ctb_log2_size_y) - min_qt_log2_size_intra_y); + } else { + infer(sps_log2_diff_max_bt_min_qt_intra_slice_luma, 0); + infer(sps_log2_diff_max_tt_min_qt_intra_slice_luma, 0); + } + + if (current->sps_chroma_format_idc != 0) { + flag(sps_qtbtt_dual_tree_intra_flag); + } else { + infer(sps_qtbtt_dual_tree_intra_flag, 0); + } + + if (current->sps_qtbtt_dual_tree_intra_flag) { + ue(sps_log2_diff_min_qt_min_cb_intra_slice_chroma, + 0, FFMIN(6, ctb_log2_size_y) - min_cb_log2_size_y); + ue(sps_max_mtt_hierarchy_depth_intra_slice_chroma, + 0, 2 * (ctb_log2_size_y - min_cb_log2_size_y)); + if (current->sps_max_mtt_hierarchy_depth_intra_slice_chroma != 0) { + unsigned int min_qt_log2_size_intra_c = + current->sps_log2_diff_min_qt_min_cb_intra_slice_chroma + + min_cb_log2_size_y; + ue(sps_log2_diff_max_bt_min_qt_intra_slice_chroma, + 0, FFMIN(6, ctb_log2_size_y) - min_qt_log2_size_intra_c); + ue(sps_log2_diff_max_tt_min_qt_intra_slice_chroma, + 0, FFMIN(6, ctb_log2_size_y) - min_qt_log2_size_intra_c); + } + } else { + infer(sps_log2_diff_min_qt_min_cb_intra_slice_chroma, 0); + infer(sps_max_mtt_hierarchy_depth_intra_slice_chroma, 0); + } + if (current->sps_max_mtt_hierarchy_depth_intra_slice_chroma == 0) { + infer(sps_log2_diff_max_bt_min_qt_intra_slice_chroma, 0); + infer(sps_log2_diff_max_tt_min_qt_intra_slice_chroma, 0); + } + + ue(sps_log2_diff_min_qt_min_cb_inter_slice, + 0, FFMIN(6, ctb_log2_size_y) - min_cb_log2_size_y); + min_qt_log2_size_inter_y = + current->sps_log2_diff_min_qt_min_cb_inter_slice + min_cb_log2_size_y; + + ue(sps_max_mtt_hierarchy_depth_inter_slice, + 0, 2 * (ctb_log2_size_y - min_cb_log2_size_y)); + if (current->sps_max_mtt_hierarchy_depth_inter_slice != 0) { + ue(sps_log2_diff_max_bt_min_qt_inter_slice, + 0, ctb_log2_size_y - min_qt_log2_size_inter_y); + ue(sps_log2_diff_max_tt_min_qt_inter_slice, + 0, FFMIN(6, ctb_log2_size_y) - min_qt_log2_size_inter_y); + } else { + infer(sps_log2_diff_max_bt_min_qt_inter_slice, 0); + infer(sps_log2_diff_max_tt_min_qt_inter_slice, 0); + } + + if (ctb_size_y > 32) + flag(sps_max_luma_transform_size_64_flag); + else + infer(sps_max_luma_transform_size_64_flag, 0); + + flag(sps_transform_skip_enabled_flag); + if (current->sps_transform_skip_enabled_flag) { + ue(sps_log2_transform_skip_max_size_minus2, 0, 3); + flag(sps_bdpcm_enabled_flag); + } + + flag(sps_mts_enabled_flag); + if (current->sps_mts_enabled_flag) { + flag(sps_explicit_mts_intra_enabled_flag); + flag(sps_explicit_mts_inter_enabled_flag); + } else { + infer(sps_explicit_mts_intra_enabled_flag, 0); + infer(sps_explicit_mts_inter_enabled_flag, 0); + } + + flag(sps_lfnst_enabled_flag); + + if (current->sps_chroma_format_idc != 0) { + uint8_t num_qp_tables; + flag(sps_joint_cbcr_enabled_flag); + flag(sps_same_qp_table_for_chroma_flag); + num_qp_tables = current->sps_same_qp_table_for_chroma_flag ? + 1 : (current->sps_joint_cbcr_enabled_flag ? 3 : 2); + for (i = 0; i < num_qp_tables; i++) { + ses(sps_qp_table_start_minus26[i], -26 - qp_bd_offset, 36, 1, i); + ues(sps_num_points_in_qp_table_minus1[i], + 0, 36 - current->sps_qp_table_start_minus26[i], 1, i); + for (j = 0; j <= current->sps_num_points_in_qp_table_minus1[i]; j++) { + uint8_t max = MAX_UINT_BITS(8); + ues(sps_delta_qp_in_val_minus1[i][j], 0, max, 2, i, j); + ues(sps_delta_qp_diff_val[i][j], 0, max, 2, i, j); + } + } + } else { + infer(sps_joint_cbcr_enabled_flag, 0); + infer(sps_same_qp_table_for_chroma_flag, 0); + } + + flag(sps_sao_enabled_flag); + flag(sps_alf_enabled_flag); + if (current->sps_alf_enabled_flag && current->sps_chroma_format_idc) + flag(sps_ccalf_enabled_flag); + else + infer(sps_ccalf_enabled_flag, 0); + flag(sps_lmcs_enabled_flag); + flag(sps_weighted_pred_flag); + flag(sps_weighted_bipred_flag); + flag(sps_long_term_ref_pics_flag); + if (current->sps_video_parameter_set_id > 0) + flag(sps_inter_layer_prediction_enabled_flag); + else + infer(sps_inter_layer_prediction_enabled_flag, 0); + flag(sps_idr_rpl_present_flag); + flag(sps_rpl1_same_as_rpl0_flag); + + for (i = 0; i < (current->sps_rpl1_same_as_rpl0_flag ? 1 : 2); i++) { + ues(sps_num_ref_pic_lists[i], 0, VVC_MAX_REF_PIC_LISTS, 1, i); + for (j = 0; j < current->sps_num_ref_pic_lists[i]; j++) + CHECK(FUNC(ref_pic_list_struct) (ctx, rw, + ¤t-> + sps_ref_pic_list_struct[i][j], i, + j, current)); + } + + if (current->sps_rpl1_same_as_rpl0_flag) { + current->sps_num_ref_pic_lists[1] = current->sps_num_ref_pic_lists[0]; + for (j = 0; j < current->sps_num_ref_pic_lists[0]; j++) + memcpy(¤t->sps_ref_pic_list_struct[1][j], + ¤t->sps_ref_pic_list_struct[0][j], + sizeof(current->sps_ref_pic_list_struct[0][j])); + } + + flag(sps_ref_wraparound_enabled_flag); + + flag(sps_temporal_mvp_enabled_flag); + if (current->sps_temporal_mvp_enabled_flag) + flag(sps_sbtmvp_enabled_flag); + else + infer(sps_sbtmvp_enabled_flag, 0); + + flag(sps_amvr_enabled_flag); + flag(sps_bdof_enabled_flag); + if (current->sps_bdof_enabled_flag) + flag(sps_bdof_control_present_in_ph_flag); + else + infer(sps_bdof_control_present_in_ph_flag, 0); + + flag(sps_smvd_enabled_flag); + flag(sps_dmvr_enabled_flag); + if (current->sps_dmvr_enabled_flag) + flag(sps_dmvr_control_present_in_ph_flag); + else + infer(sps_dmvr_control_present_in_ph_flag, 0); + + flag(sps_mmvd_enabled_flag); + if (current->sps_mmvd_enabled_flag) + flag(sps_mmvd_fullpel_only_enabled_flag); + else + infer(sps_mmvd_fullpel_only_enabled_flag, 0); + + ue(sps_six_minus_max_num_merge_cand, 0, 5); + max_num_merge_cand = 6 - current->sps_six_minus_max_num_merge_cand; + + flag(sps_sbt_enabled_flag); + + flag(sps_affine_enabled_flag); + if (current->sps_affine_enabled_flag) { + ue(sps_five_minus_max_num_subblock_merge_cand, + 0, 5 - current->sps_sbtmvp_enabled_flag); + flag(sps_6param_affine_enabled_flag); + if (current->sps_amvr_enabled_flag) + flag(sps_affine_amvr_enabled_flag); + else + infer(sps_affine_amvr_enabled_flag, 0); + flag(sps_affine_prof_enabled_flag); + if (current->sps_affine_prof_enabled_flag) + flag(sps_prof_control_present_in_ph_flag); + else + infer(sps_prof_control_present_in_ph_flag, 0); + } else { + infer(sps_6param_affine_enabled_flag, 0); + infer(sps_affine_amvr_enabled_flag, 0); + infer(sps_affine_prof_enabled_flag, 0); + infer(sps_prof_control_present_in_ph_flag, 0); + } + + flag(sps_bcw_enabled_flag); + flag(sps_ciip_enabled_flag); + + if (max_num_merge_cand >= 2) { + flag(sps_gpm_enabled_flag); + if (current->sps_gpm_enabled_flag && max_num_merge_cand >= 3) + ue(sps_max_num_merge_cand_minus_max_num_gpm_cand, + 0, max_num_merge_cand - 2); + } else { + infer(sps_gpm_enabled_flag, 0); + } + + ue(sps_log2_parallel_merge_level_minus2, 0, ctb_log2_size_y - 2); + + flag(sps_isp_enabled_flag); + flag(sps_mrl_enabled_flag); + flag(sps_mip_enabled_flag); + + if (current->sps_chroma_format_idc != 0) + flag(sps_cclm_enabled_flag); + else + infer(sps_cclm_enabled_flag, 0); + if (current->sps_chroma_format_idc == 1) { + flag(sps_chroma_horizontal_collocated_flag); + flag(sps_chroma_vertical_collocated_flag); + } else { + infer(sps_chroma_horizontal_collocated_flag, 1); + infer(sps_chroma_vertical_collocated_flag, 1); + } + + flag(sps_palette_enabled_flag); + if (current->sps_chroma_format_idc == 3 && + !current->sps_max_luma_transform_size_64_flag) + flag(sps_act_enabled_flag); + else + infer(sps_act_enabled_flag, 0); + if (current->sps_transform_skip_enabled_flag || + current->sps_palette_enabled_flag) + ue(sps_min_qp_prime_ts, 0, 8); + + flag(sps_ibc_enabled_flag); + if (current->sps_ibc_enabled_flag) + ue(sps_six_minus_max_num_ibc_merge_cand, 0, 5); + + flag(sps_ladf_enabled_flag); + if (current->sps_ladf_enabled_flag) { + ub(2, sps_num_ladf_intervals_minus2); + se(sps_ladf_lowest_interval_qp_offset, -63, 63); + for (i = 0; i < current->sps_num_ladf_intervals_minus2 + 1; i++) { + ses(sps_ladf_qp_offset[i], -63, 63, 1, i); + ues(sps_ladf_delta_threshold_minus1[i], + 0, (2 << (8 + current->sps_bitdepth_minus8)) - 3, 1, i); + } + } + + flag(sps_explicit_scaling_list_enabled_flag); + if (current->sps_lfnst_enabled_flag && + current->sps_explicit_scaling_list_enabled_flag) + flag(sps_scaling_matrix_for_lfnst_disabled_flag); + + if (current->sps_act_enabled_flag && + current->sps_explicit_scaling_list_enabled_flag) + flag(sps_scaling_matrix_for_alternative_colour_space_disabled_flag); + else + infer(sps_scaling_matrix_for_alternative_colour_space_disabled_flag, 0); + if (current->sps_scaling_matrix_for_alternative_colour_space_disabled_flag) + flag(sps_scaling_matrix_designated_colour_space_flag); + + flag(sps_dep_quant_enabled_flag); + flag(sps_sign_data_hiding_enabled_flag); + + flag(sps_virtual_boundaries_enabled_flag); + if (current->sps_virtual_boundaries_enabled_flag) { + flag(sps_virtual_boundaries_present_flag); + if (current->sps_virtual_boundaries_present_flag) { + ue(sps_num_ver_virtual_boundaries, + 0, current->sps_pic_width_max_in_luma_samples <= 8 ? 0 : 3); + for (i = 0; i < current->sps_num_ver_virtual_boundaries; i++) + ues(sps_virtual_boundary_pos_x_minus1[i], + 0, (current->sps_pic_width_max_in_luma_samples + 7) / 8 - 2, + 1, i); + ue(sps_num_hor_virtual_boundaries, + 0, current->sps_pic_height_max_in_luma_samples <= 8 ? 0 : 3); + for (i = 0; i < current->sps_num_hor_virtual_boundaries; i++) + ues(sps_virtual_boundary_pos_y_minus1[i], + 0, (current->sps_pic_height_max_in_luma_samples + 7) / + 8 - 2, 1, i); + } + } else { + infer(sps_virtual_boundaries_present_flag, 0); + infer(sps_num_ver_virtual_boundaries, 0); + infer(sps_num_hor_virtual_boundaries, 0); + } + + if (current->sps_ptl_dpb_hrd_params_present_flag) { + flag(sps_timing_hrd_params_present_flag); + if (current->sps_timing_hrd_params_present_flag) { + uint8_t first_sublayer; + CHECK(FUNC(general_timing_hrd_parameters) (ctx, rw, + ¤t->sps_general_timing_hrd_parameters)); + if (current->sps_max_sublayers_minus1 > 0) + flag(sps_sublayer_cpb_params_present_flag); + else + infer(sps_sublayer_cpb_params_present_flag, 0); + first_sublayer = current->sps_sublayer_cpb_params_present_flag ? + 0 : current->sps_max_sublayers_minus1; + CHECK(FUNC(ols_timing_hrd_parameters) (ctx, rw, + ¤t->sps_ols_timing_hrd_parameters, first_sublayer, + current->sps_max_sublayers_minus1, + ¤t->sps_general_timing_hrd_parameters)); + } + } + + flag(sps_field_seq_flag); + flag(sps_vui_parameters_present_flag); + if (current->sps_vui_parameters_present_flag) { + ue(sps_vui_payload_size_minus1, 0, 1023); + while (byte_alignment(rw) != 0) + fixed(1, sps_vui_alignment_zero_bit, 0); + CHECK(FUNC(vui_payload) (ctx, rw, ¤t->vui, + current->sps_vui_payload_size_minus1 + 1, + current->sps_chroma_format_idc)); + } else { + CHECK(FUNC(vui_parameters_default) (ctx, rw, ¤t->vui)); + } + + flag(sps_extension_flag); + if (current->sps_extension_flag) { + flag(sps_range_extension_flag); + ub(7, sps_extension_7bits); + + if (current->sps_range_extension_flag) { + CHECK(FUNC(sps_range_extension)(ctx, rw, current)); + } else { + infer(sps_extended_precision_flag, 0); + infer(sps_ts_residual_coding_rice_present_in_sh_flag, 0); + infer(sps_rrc_rice_extension_flag, 0); + infer(sps_persistent_rice_adaptation_enabled_flag, 0); + infer(sps_reverse_last_sig_coeff_enabled_flag, 0); + } + } else { + infer(sps_range_extension_flag, 0); + infer(sps_extension_7bits, 0); + infer(sps_extended_precision_flag, 0); + infer(sps_ts_residual_coding_rice_present_in_sh_flag, 0); + infer(sps_rrc_rice_extension_flag, 0); + infer(sps_persistent_rice_adaptation_enabled_flag, 0); + infer(sps_reverse_last_sig_coeff_enabled_flag, 0); + } + + if (current->sps_extension_7bits) + CHECK(FUNC(extension_data)(ctx, rw, ¤t->extension_data)); + + CHECK(FUNC(rbsp_trailing_bits) (ctx, rw)); + + return 0; +} + +static int FUNC(pps) (CodedBitstreamContext *ctx, RWContext *rw, + H266RawPPS *current) +{ + CodedBitstreamH266Context *h266 = ctx->priv_data; + const H266RawSPS *sps; + int err, i; + unsigned int min_cb_size_y, divisor, ctb_size_y, + pic_width_in_ctbs_y, pic_height_in_ctbs_y; + uint8_t sub_width_c, sub_height_c, qp_bd_offset; + + static const uint8_t h266_sub_width_c[] = { + 1, 2, 2, 1 + }; + static const uint8_t h266_sub_height_c[] = { + 1, 2, 1, 1 + }; + + HEADER("Picture Parameter Set"); + + CHECK(FUNC(nal_unit_header) (ctx, rw, + ¤t->nal_unit_header, VVC_PPS_NUT)); + + ub(6, pps_pic_parameter_set_id); + ub(4, pps_seq_parameter_set_id); + sps = h266->sps[current->pps_seq_parameter_set_id]; + if (!sps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "SPS id %d not available.\n", + current->pps_seq_parameter_set_id); + return AVERROR_INVALIDDATA; + } + + flag(pps_mixed_nalu_types_in_pic_flag); + ue(pps_pic_width_in_luma_samples, + 1, sps->sps_pic_width_max_in_luma_samples); + ue(pps_pic_height_in_luma_samples, + 1, sps->sps_pic_height_max_in_luma_samples); + + min_cb_size_y = 1 << (sps->sps_log2_min_luma_coding_block_size_minus2 + 2); + divisor = FFMAX(min_cb_size_y, 8); + if (current->pps_pic_width_in_luma_samples % divisor || + current->pps_pic_height_in_luma_samples % divisor) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Invalid dimensions: %ux%u not divisible " + "by %u, MinCbSizeY = %u.\n", + current->pps_pic_width_in_luma_samples, + current->pps_pic_height_in_luma_samples, divisor, min_cb_size_y); + return AVERROR_INVALIDDATA; + } + if (!sps->sps_res_change_in_clvs_allowed_flag && + (current->pps_pic_width_in_luma_samples != + sps->sps_pic_width_max_in_luma_samples || + current->pps_pic_height_in_luma_samples != + sps->sps_pic_height_max_in_luma_samples)) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Resoltuion change is not allowed, " + "in max resolution (%ux%u) mismatched with pps(%ux%u).\n", + sps->sps_pic_width_max_in_luma_samples, + sps->sps_pic_height_max_in_luma_samples, + current->pps_pic_width_in_luma_samples, + current->pps_pic_height_in_luma_samples); + return AVERROR_INVALIDDATA; + } + + ctb_size_y = 1 << (sps->sps_log2_ctu_size_minus5 + 5); + if (sps->sps_ref_wraparound_enabled_flag) { + if ((ctb_size_y / min_cb_size_y + 1) > + (current->pps_pic_width_in_luma_samples / min_cb_size_y - 1)) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Invalid width(%u), ctb_size_y = %u, min_cb_size_y = %u.\n", + current->pps_pic_width_in_luma_samples, + ctb_size_y, min_cb_size_y); + return AVERROR_INVALIDDATA; + } + } + + flag(pps_conformance_window_flag); + if (current->pps_pic_width_in_luma_samples == + sps->sps_pic_width_max_in_luma_samples && + current->pps_pic_height_in_luma_samples == + sps->sps_pic_height_max_in_luma_samples && + current->pps_conformance_window_flag) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Conformance window flag should not true.\n"); + return AVERROR_INVALIDDATA; + } + + sub_width_c = h266_sub_width_c[sps->sps_chroma_format_idc]; + sub_height_c = h266_sub_height_c[sps->sps_chroma_format_idc]; + if (current->pps_conformance_window_flag) { + ue(pps_conf_win_left_offset, 0, current->pps_pic_width_in_luma_samples); + ue(pps_conf_win_right_offset, + 0, current->pps_pic_width_in_luma_samples); + ue(pps_conf_win_top_offset, 0, current->pps_pic_height_in_luma_samples); + ue(pps_conf_win_bottom_offset, + 0, current->pps_pic_height_in_luma_samples); + if (sub_width_c * + (current->pps_conf_win_left_offset + + current->pps_conf_win_right_offset) >= + current->pps_pic_width_in_luma_samples || + sub_height_c * + (current->pps_conf_win_top_offset + + current->pps_conf_win_bottom_offset) >= + current->pps_pic_height_in_luma_samples) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Invalid pps conformance window: (%u, %u, %u, %u), " + "resolution is %ux%u, sub wxh is %ux%u.\n", + current->pps_conf_win_left_offset, + current->pps_conf_win_right_offset, + current->pps_conf_win_top_offset, + current->pps_conf_win_bottom_offset, + current->pps_pic_width_in_luma_samples, + current->pps_pic_height_in_luma_samples, + sub_width_c, sub_height_c); + return AVERROR_INVALIDDATA; + } + } else { + if (current->pps_pic_width_in_luma_samples == + sps->sps_pic_width_max_in_luma_samples && + current->pps_pic_height_in_luma_samples == + sps->sps_pic_height_max_in_luma_samples) { + infer(pps_conf_win_left_offset, sps->sps_conf_win_left_offset); + infer(pps_conf_win_right_offset, sps->sps_conf_win_right_offset); + infer(pps_conf_win_top_offset, sps->sps_conf_win_top_offset); + infer(pps_conf_win_bottom_offset, sps->sps_conf_win_bottom_offset); + } else { + infer(pps_conf_win_left_offset, 0); + infer(pps_conf_win_right_offset, 0); + infer(pps_conf_win_top_offset, 0); + infer(pps_conf_win_bottom_offset, 0); + } + + } + + flag(pps_scaling_window_explicit_signalling_flag); + if (!sps->sps_ref_pic_resampling_enabled_flag && + current->pps_scaling_window_explicit_signalling_flag) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Invalid data: sps_ref_pic_resampling_enabled_flag is false, " + "but pps_scaling_window_explicit_signalling_flag is true.\n"); + return AVERROR_INVALIDDATA; + } + if (current->pps_scaling_window_explicit_signalling_flag) { + se(pps_scaling_win_left_offset, + -current->pps_pic_width_in_luma_samples * 15 / sub_width_c, + current->pps_pic_width_in_luma_samples / sub_width_c); + se(pps_scaling_win_right_offset, + -current->pps_pic_width_in_luma_samples * 15 / sub_width_c, + current->pps_pic_width_in_luma_samples / sub_width_c); + se(pps_scaling_win_top_offset, + -current->pps_pic_height_in_luma_samples * 15 / sub_height_c, + current->pps_pic_height_in_luma_samples / sub_height_c); + se(pps_scaling_win_bottom_offset, + -current->pps_pic_height_in_luma_samples * 15 / sub_height_c, + current->pps_pic_height_in_luma_samples / sub_height_c); + } else { + infer(pps_scaling_win_left_offset, current->pps_conf_win_left_offset); + infer(pps_scaling_win_right_offset, current->pps_conf_win_right_offset); + infer(pps_scaling_win_top_offset, current->pps_conf_win_top_offset); + infer(pps_scaling_win_bottom_offset, current->pps_conf_win_bottom_offset); + } + + flag(pps_output_flag_present_flag); + flag(pps_no_pic_partition_flag); + flag(pps_subpic_id_mapping_present_flag); + + if (current->pps_subpic_id_mapping_present_flag) { + if (!current->pps_no_pic_partition_flag) { + ue(pps_num_subpics_minus1, + sps->sps_num_subpics_minus1, sps->sps_num_subpics_minus1); + } else { + infer(pps_num_subpics_minus1, 0); + } + ue(pps_subpic_id_len_minus1, sps->sps_subpic_id_len_minus1, + sps->sps_subpic_id_len_minus1); + for (i = 0; i <= current->pps_num_subpics_minus1; i++) { + ubs(sps->sps_subpic_id_len_minus1 + 1, pps_subpic_id[i], 1, i); + } + } + + for (i = 0; i <= sps->sps_num_subpics_minus1; i++) { + if (sps->sps_subpic_id_mapping_explicitly_signalled_flag) + current->sub_pic_id_val[i] = current->pps_subpic_id_mapping_present_flag + ? current->pps_subpic_id[i] + : sps->sps_subpic_id[i]; + else + current->sub_pic_id_val[i] = i; + } + + pic_width_in_ctbs_y = AV_CEIL_RSHIFT + (current->pps_pic_width_in_luma_samples, (sps->sps_log2_ctu_size_minus5 + 5)); + pic_height_in_ctbs_y = AV_CEIL_RSHIFT( + current->pps_pic_height_in_luma_samples,(sps->sps_log2_ctu_size_minus5 + 5)); + if (!current->pps_no_pic_partition_flag) { + unsigned int exp_tile_width = 0, exp_tile_height = 0; + unsigned int unified_size, remaining_size; + + u(2, pps_log2_ctu_size_minus5, + sps->sps_log2_ctu_size_minus5, sps->sps_log2_ctu_size_minus5); + ue(pps_num_exp_tile_columns_minus1, + 0, FFMIN(pic_width_in_ctbs_y - 1, VVC_MAX_TILE_COLUMNS - 1)); + ue(pps_num_exp_tile_rows_minus1, + 0, FFMIN(pic_height_in_ctbs_y - 1, VVC_MAX_TILE_ROWS - 1)); + + for (i = 0; i <= current->pps_num_exp_tile_columns_minus1; i++) { + ues(pps_tile_column_width_minus1[i], + 0, pic_width_in_ctbs_y - exp_tile_width - 1, 1, i); + exp_tile_width += current->pps_tile_column_width_minus1[i] + 1; + } + for (i = 0; i <= current->pps_num_exp_tile_rows_minus1; i++) { + ues(pps_tile_row_height_minus1[i], + 0, pic_height_in_ctbs_y - exp_tile_height - 1, 1, i); + exp_tile_height += current->pps_tile_row_height_minus1[i] + 1; + } + + remaining_size = pic_width_in_ctbs_y; + for (i = 0; i <= current->pps_num_exp_tile_columns_minus1; i++) { + if (current->pps_tile_column_width_minus1[i] >= remaining_size) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Tile column width(%d) exceeds picture width\n",i); + return AVERROR_INVALIDDATA; + } + current->col_width_val[i] = current->pps_tile_column_width_minus1[i] + 1; + remaining_size -= (current->pps_tile_column_width_minus1[i] + 1); + } + unified_size = current->pps_tile_column_width_minus1[i - 1] + 1; + while (remaining_size > 0) { + if (current->num_tile_columns > VVC_MAX_TILE_COLUMNS) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "NumTileColumns(%d) > than VVC_MAX_TILE_COLUMNS(%d)\n", + current->num_tile_columns, VVC_MAX_TILE_COLUMNS); + return AVERROR_INVALIDDATA; + } + unified_size = FFMIN(remaining_size, unified_size); + current->col_width_val[i] = unified_size; + remaining_size -= unified_size; + i++; + } + current->num_tile_columns = i; + if (current->num_tile_columns > VVC_MAX_TILE_COLUMNS) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "NumTileColumns(%d) > than VVC_MAX_TILE_COLUMNS(%d)\n", + current->num_tile_columns, VVC_MAX_TILE_COLUMNS); + return AVERROR_INVALIDDATA; + } + + remaining_size = pic_height_in_ctbs_y; + for (i = 0; i <= current->pps_num_exp_tile_rows_minus1; i++) { + if (current->pps_tile_row_height_minus1[i] >= remaining_size) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Tile row height(%d) exceeds picture height\n",i); + return AVERROR_INVALIDDATA; + } + current->row_height_val[i] = current->pps_tile_row_height_minus1[i] + 1; + remaining_size -= (current->pps_tile_row_height_minus1[i] + 1); + } + unified_size = current->pps_tile_row_height_minus1[i - 1] + 1; + + while (remaining_size > 0) { + unified_size = FFMIN(remaining_size, unified_size); + current->row_height_val[i] = unified_size; + remaining_size -= unified_size; + i++; + } + current->num_tile_rows=i; + if (current->num_tile_rows > VVC_MAX_TILE_ROWS) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "NumTileRows(%d) > than VVC_MAX_TILE_ROWS(%d)\n", + current->num_tile_rows, VVC_MAX_TILE_ROWS); + return AVERROR_INVALIDDATA; + } + + current->num_tiles_in_pic = current->num_tile_columns * + current->num_tile_rows; + if (current->num_tiles_in_pic > VVC_MAX_TILES_PER_AU) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "NumTilesInPic(%d) > than VVC_MAX_TILES_PER_AU(%d)\n", + current->num_tiles_in_pic, VVC_MAX_TILES_PER_AU); + return AVERROR_INVALIDDATA; + } + + if (current->num_tiles_in_pic > 1) { + flag(pps_loop_filter_across_tiles_enabled_flag); + flag(pps_rect_slice_flag); + } else { + infer(pps_loop_filter_across_tiles_enabled_flag, 0); + infer(pps_rect_slice_flag, 1); + } + if (current->pps_rect_slice_flag) + flag(pps_single_slice_per_subpic_flag); + else + infer(pps_single_slice_per_subpic_flag, 1); + if (current->pps_rect_slice_flag && + !current->pps_single_slice_per_subpic_flag) { + int j; + uint16_t tile_idx = 0, tile_x, tile_y, ctu_x, ctu_y; + uint16_t slice_top_left_ctu_x[VVC_MAX_SLICES]; + uint16_t slice_top_left_ctu_y[VVC_MAX_SLICES]; + ue(pps_num_slices_in_pic_minus1, 0, VVC_MAX_SLICES - 1); + if (current->pps_num_slices_in_pic_minus1 > 1) + flag(pps_tile_idx_delta_present_flag); + else + infer(pps_tile_idx_delta_present_flag, 0); + for (i = 0; i < current->pps_num_slices_in_pic_minus1; i++) { + tile_x = tile_idx % current->num_tile_columns; + tile_y = tile_idx / current->num_tile_columns; + if (tile_x != current->num_tile_columns - 1) { + ues(pps_slice_width_in_tiles_minus1[i], + 0, current->num_tile_columns - 1, 1, i); + } else { + infer(pps_slice_width_in_tiles_minus1[i], 0); + } + if (tile_y != current->num_tile_rows - 1 && + (current->pps_tile_idx_delta_present_flag || tile_x == 0)) { + ues(pps_slice_height_in_tiles_minus1[i], + 0, current->num_tile_rows - 1, 1, i); + } else { + if (tile_y == current->num_tile_rows - 1) + infer(pps_slice_height_in_tiles_minus1[i], 0); + else + infer(pps_slice_height_in_tiles_minus1[i], + current->pps_slice_height_in_tiles_minus1[i - 1]); + } + + ctu_x = ctu_y = 0; + for (j = 0; j < tile_x; j++) { + ctu_x += current->col_width_val[j]; + } + for (j = 0; j < tile_y; j++) { + ctu_y += current->row_height_val[j]; + } + if (current->pps_slice_width_in_tiles_minus1[i] == 0 && + current->pps_slice_height_in_tiles_minus1[i] == 0 && + current->row_height_val[tile_y] > 1) { + int num_slices_in_tile, + uniform_slice_height, remaining_height_in_ctbs_y; + remaining_height_in_ctbs_y = + current->row_height_val[tile_y]; + ues(pps_num_exp_slices_in_tile[i], + 0, current->row_height_val[tile_y] - 1, 1, i); + if (current->pps_num_exp_slices_in_tile[i] == 0) { + num_slices_in_tile = 1; + current->slice_height_in_ctus[i] = current->row_height_val[tile_y]; + slice_top_left_ctu_x[i] = ctu_x; + slice_top_left_ctu_y[i] = ctu_y; + } else { + uint16_t slice_height_in_ctus; + for (j = 0; j < current->pps_num_exp_slices_in_tile[i]; + j++) { + ues(pps_exp_slice_height_in_ctus_minus1[i][j], 0, + current->row_height_val[tile_y] - 1, 2, + i, j); + slice_height_in_ctus = + current-> + pps_exp_slice_height_in_ctus_minus1[i][j] + 1; + + current->slice_height_in_ctus[i + j] = + slice_height_in_ctus; + slice_top_left_ctu_x[i + j] = ctu_x; + slice_top_left_ctu_y[i + j] = ctu_y; + ctu_y += slice_height_in_ctus; + + remaining_height_in_ctbs_y -= slice_height_in_ctus; + } + uniform_slice_height = 1 + + (j == 0 ? current->row_height_val[tile_y] - 1: + current->pps_exp_slice_height_in_ctus_minus1[i][j-1]); + while (remaining_height_in_ctbs_y > uniform_slice_height) { + current->slice_height_in_ctus[i + j] = + uniform_slice_height; + slice_top_left_ctu_x[i + j] = ctu_x; + slice_top_left_ctu_y[i + j] = ctu_y; + ctu_y += uniform_slice_height; + + remaining_height_in_ctbs_y -= uniform_slice_height; + j++; + } + if (remaining_height_in_ctbs_y > 0) { + current->slice_height_in_ctus[i + j] = + remaining_height_in_ctbs_y; + slice_top_left_ctu_x[i + j] = ctu_x; + slice_top_left_ctu_y[i + j] = ctu_y; + j++; + } + num_slices_in_tile = j; + } + i += num_slices_in_tile - 1; + } else { + uint16_t height = 0; + infer(pps_num_exp_slices_in_tile[i], 0); + for (j = 0; + j <= current->pps_slice_height_in_tiles_minus1[i]; + j++) { + height += + current->row_height_val[tile_y + j]; + } + current->slice_height_in_ctus[i] = height; + + slice_top_left_ctu_x[i] = ctu_x; + slice_top_left_ctu_y[i] = ctu_y; + } + if (i < current->pps_num_slices_in_pic_minus1) { + if (current->pps_tile_idx_delta_present_flag) { + ses(pps_tile_idx_delta_val[i], + -current->num_tiles_in_pic + 1, + current->num_tiles_in_pic - 1, 1, i); + if (current->pps_tile_idx_delta_val[i] == 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "pps_tile_idx_delta_val[i] shall not be equal to 0.\n"); + } + tile_idx += current->pps_tile_idx_delta_val[i]; + } else { + infer(pps_tile_idx_delta_val[i], 0); + tile_idx += + current->pps_slice_width_in_tiles_minus1[i] + 1; + if (tile_idx % current->num_tile_columns == 0) { + tile_idx += + current->pps_slice_height_in_tiles_minus1[i] * + current->num_tile_columns; + } + } + } + } + if (i == current->pps_num_slices_in_pic_minus1) { + uint16_t height = 0; + + tile_x = tile_idx % current->num_tile_columns; + tile_y = tile_idx / current->num_tile_columns; + + ctu_x = 0, ctu_y = 0; + for (j = 0; j < tile_x; j++) { + ctu_x += current->col_width_val[j]; + } + for (j = 0; j < tile_y; j++) { + ctu_y += current->row_height_val[j]; + } + slice_top_left_ctu_x[i] = ctu_x; + slice_top_left_ctu_y[i] = ctu_y; + + current->pps_slice_width_in_tiles_minus1[i] = + current->num_tile_columns - tile_x - 1; + current->pps_slice_height_in_tiles_minus1[i] = + current->num_tile_rows - tile_y - 1; + + for (j = 0; j <= current->pps_slice_height_in_tiles_minus1[i]; + j++) { + height += + current->row_height_val[tile_y + j]; + } + current->slice_height_in_ctus[i] = height; + + infer(pps_num_exp_slices_in_tile[i], 0); + } + //now, we got all slice information, let's resolve NumSlicesInSubpic + for (i = 0; i <= sps->sps_num_subpics_minus1; i++) { + current->num_slices_in_subpic[i] = 0; + for (j = 0; j <= current->pps_num_slices_in_pic_minus1; j++) { + uint16_t pos_x = 0, pos_y = 0; + pos_x = slice_top_left_ctu_x[j]; + pos_y = slice_top_left_ctu_y[j]; + if ((pos_x >= sps->sps_subpic_ctu_top_left_x[i]) && + (pos_x < + sps->sps_subpic_ctu_top_left_x[i] + + sps->sps_subpic_width_minus1[i] + 1) && + (pos_y >= sps->sps_subpic_ctu_top_left_y[i]) && + (pos_y < sps->sps_subpic_ctu_top_left_y[i] + + sps->sps_subpic_height_minus1[i] + 1)) { + current->num_slices_in_subpic[i]++; + } + } + } + } else { + if (current->pps_no_pic_partition_flag) + infer(pps_num_slices_in_pic_minus1, 0); + else if (current->pps_single_slice_per_subpic_flag) + infer(pps_num_slices_in_pic_minus1, + sps->sps_num_subpics_minus1); + // else? + } + if (!current->pps_rect_slice_flag || + current->pps_single_slice_per_subpic_flag || + current->pps_num_slices_in_pic_minus1 > 0) + flag(pps_loop_filter_across_slices_enabled_flag); + else + infer(pps_loop_filter_across_slices_enabled_flag, 0); + } else { + infer(pps_num_exp_tile_columns_minus1, 0); + infer(pps_tile_column_width_minus1[0], pic_width_in_ctbs_y - 1); + infer(pps_num_exp_tile_rows_minus1, 0); + infer(pps_tile_row_height_minus1[0], pic_height_in_ctbs_y - 1); + current->col_width_val[0] = pic_width_in_ctbs_y; + current->row_height_val[0] = pic_height_in_ctbs_y; + current->num_tile_columns = 1; + current->num_tile_rows = 1; + current->num_tiles_in_pic = 1; + } + + flag(pps_cabac_init_present_flag); + for (i = 0; i < 2; i++) + ues(pps_num_ref_idx_default_active_minus1[i], 0, 14, 1, i); + flag(pps_rpl1_idx_present_flag); + flag(pps_weighted_pred_flag); + flag(pps_weighted_bipred_flag); + flag(pps_ref_wraparound_enabled_flag); + if (current->pps_ref_wraparound_enabled_flag) { + ue(pps_pic_width_minus_wraparound_offset, + 0, (current->pps_pic_width_in_luma_samples / min_cb_size_y) + - (ctb_size_y / min_cb_size_y) - 2); + } + + qp_bd_offset = 6 * sps->sps_bitdepth_minus8; + se(pps_init_qp_minus26, -(26 + qp_bd_offset), 37); + flag(pps_cu_qp_delta_enabled_flag); + flag(pps_chroma_tool_offsets_present_flag); + if (current->pps_chroma_tool_offsets_present_flag) { + se(pps_cb_qp_offset, -12, 12); + se(pps_cr_qp_offset, -12, 12); + flag(pps_joint_cbcr_qp_offset_present_flag); + if (current->pps_joint_cbcr_qp_offset_present_flag) + se(pps_joint_cbcr_qp_offset_value, -12, 12); + else + infer(pps_joint_cbcr_qp_offset_value, 0); + flag(pps_slice_chroma_qp_offsets_present_flag); + flag(pps_cu_chroma_qp_offset_list_enabled_flag); + if (current->pps_cu_chroma_qp_offset_list_enabled_flag) { + ue(pps_chroma_qp_offset_list_len_minus1, 0, 5); + for (i = 0; i <= current->pps_chroma_qp_offset_list_len_minus1; i++) { + ses(pps_cb_qp_offset_list[i], -12, 12, 1, i); + ses(pps_cr_qp_offset_list[i], -12, 12, 1, i); + if (current->pps_joint_cbcr_qp_offset_present_flag) + ses(pps_joint_cbcr_qp_offset_list[i], -12, 12, 1, i); + else + infer(pps_joint_cbcr_qp_offset_list[i], 0); + } + } + } else { + infer(pps_cb_qp_offset, 0); + infer(pps_cr_qp_offset, 0); + infer(pps_joint_cbcr_qp_offset_present_flag, 0); + infer(pps_joint_cbcr_qp_offset_value, 0); + infer(pps_slice_chroma_qp_offsets_present_flag, 0); + infer(pps_cu_chroma_qp_offset_list_enabled_flag, 0); + } + flag(pps_deblocking_filter_control_present_flag); + if (current->pps_deblocking_filter_control_present_flag) { + flag(pps_deblocking_filter_override_enabled_flag); + flag(pps_deblocking_filter_disabled_flag); + if (!current->pps_no_pic_partition_flag && + current->pps_deblocking_filter_override_enabled_flag) + flag(pps_dbf_info_in_ph_flag); + else + infer(pps_dbf_info_in_ph_flag, 0); + if (!current->pps_deblocking_filter_disabled_flag) { + se(pps_luma_beta_offset_div2, -12, 12); + se(pps_luma_tc_offset_div2, -12, 12); + if (current->pps_chroma_tool_offsets_present_flag) { + se(pps_cb_beta_offset_div2, -12, 12); + se(pps_cb_tc_offset_div2, -12, 12); + se(pps_cr_beta_offset_div2, -12, 12); + se(pps_cr_tc_offset_div2, -12, 12); + } else { + infer(pps_cb_beta_offset_div2, + current->pps_luma_beta_offset_div2); + infer(pps_cb_tc_offset_div2, current->pps_luma_tc_offset_div2); + infer(pps_cr_beta_offset_div2, + current->pps_luma_beta_offset_div2); + infer(pps_cr_tc_offset_div2, current->pps_luma_tc_offset_div2); + } + } else { + infer(pps_luma_beta_offset_div2, 0); + infer(pps_luma_tc_offset_div2, 0); + infer(pps_cb_beta_offset_div2, 0); + infer(pps_cb_tc_offset_div2, 0); + infer(pps_cr_beta_offset_div2, 0); + infer(pps_cr_tc_offset_div2, 0); + } + } else { + infer(pps_deblocking_filter_override_enabled_flag, 0); + infer(pps_deblocking_filter_disabled_flag, 0); + infer(pps_dbf_info_in_ph_flag, 0); + infer(pps_luma_beta_offset_div2, 0); + infer(pps_luma_tc_offset_div2, 0); + infer(pps_cb_beta_offset_div2, 0); + infer(pps_cb_tc_offset_div2, 0); + infer(pps_cr_beta_offset_div2, 0); + infer(pps_cr_tc_offset_div2, 0); + } + + if (!current->pps_no_pic_partition_flag) { + flag(pps_rpl_info_in_ph_flag); + flag(pps_sao_info_in_ph_flag); + flag(pps_alf_info_in_ph_flag); + if ((current->pps_weighted_pred_flag || + current->pps_weighted_bipred_flag) && + current->pps_rpl_info_in_ph_flag) + flag(pps_wp_info_in_ph_flag); + flag(pps_qp_delta_info_in_ph_flag); + } + flag(pps_picture_header_extension_present_flag); + flag(pps_slice_header_extension_present_flag); + + flag(pps_extension_flag); + if (current->pps_extension_flag) + CHECK(FUNC(extension_data) (ctx, rw, ¤t->extension_data)); + + CHECK(FUNC(rbsp_trailing_bits) (ctx, rw)); + return 0; +} + +static int FUNC(alf_data)(CodedBitstreamContext *ctx, RWContext *rw, + H266RawAPS *current) +{ + int err, j, k; + + flag(alf_luma_filter_signal_flag); + + if (current->aps_chroma_present_flag) { + flag(alf_chroma_filter_signal_flag); + flag(alf_cc_cb_filter_signal_flag); + flag(alf_cc_cr_filter_signal_flag); + } else { + infer(alf_chroma_filter_signal_flag, 0); + infer(alf_cc_cb_filter_signal_flag, 0); + infer(alf_cc_cr_filter_signal_flag, 0); + } + + if (current->alf_luma_filter_signal_flag) { + flag(alf_luma_clip_flag); + ue(alf_luma_num_filters_signalled_minus1, 0, VVC_NUM_ALF_FILTERS - 1); + if (current->alf_luma_num_filters_signalled_minus1 > 0) { + unsigned int bits = av_ceil_log2(current->alf_luma_num_filters_signalled_minus1 + 1); + for (int filt_idx = 0; filt_idx < VVC_NUM_ALF_FILTERS; filt_idx++) + us(bits, alf_luma_coeff_delta_idx[filt_idx], + 0, current->alf_luma_num_filters_signalled_minus1, + 1, filt_idx); + } + for (int sf_idx = 0; sf_idx <= current->alf_luma_num_filters_signalled_minus1; sf_idx++) + for (j = 0; j < 12; j++) { + ues(alf_luma_coeff_abs[sf_idx][j], 0, 128, 2, sf_idx, j); + if (current->alf_luma_coeff_abs[sf_idx][j]) + ubs(1, alf_luma_coeff_sign[sf_idx][j], 2, sf_idx, j); + else + infer(alf_luma_coeff_sign[sf_idx][j], 0); + } + } else { + infer(alf_luma_clip_flag, 0); + infer(alf_luma_num_filters_signalled_minus1, 0); + } + for (int sf_idx = 0; sf_idx <= current->alf_luma_num_filters_signalled_minus1; sf_idx++) { + for (j = 0; j < 12; j++) { + if (current->alf_luma_clip_flag) + ubs(2, alf_luma_clip_idx[sf_idx][j], 2, sf_idx, j); + else + infer(alf_luma_clip_idx[sf_idx][j], 0); + } + } + + if (current->alf_chroma_filter_signal_flag) { + flag(alf_chroma_clip_flag); + ue(alf_chroma_num_alt_filters_minus1, 0, 7); + } else { + infer(alf_chroma_clip_flag, 0); + infer(alf_chroma_num_alt_filters_minus1, 0); + } + for (int alt_idx = 0; alt_idx <= current->alf_chroma_num_alt_filters_minus1; alt_idx++) { + for (j = 0; j < 6; j++) { + if (current->alf_chroma_filter_signal_flag) + ues(alf_chroma_coeff_abs[alt_idx][j], 0, 128, 2, alt_idx, j); + else + infer(alf_chroma_coeff_abs[alt_idx][j], 0); + if (current->alf_chroma_coeff_abs[alt_idx][j] > 0) + ubs(1, alf_chroma_coeff_sign[alt_idx][j], 2, alt_idx, j); + else + infer(alf_chroma_coeff_sign[alt_idx][j], 0); + } + for (j = 0; j < 6; j++) { + if (current->alf_chroma_clip_flag) + ubs(2, alf_chroma_clip_idx[alt_idx][j], 2, alt_idx, j); + else + infer(alf_chroma_clip_idx[alt_idx][j], 0); + } + } + + if (current->alf_cc_cb_filter_signal_flag) + ue(alf_cc_cb_filters_signalled_minus1, 0, 3); + else + infer(alf_cc_cb_filters_signalled_minus1, 0); + for (k = 0; k <= current->alf_cc_cb_filters_signalled_minus1; k++) { + for (j = 0; j < 7; j++) { + if (current->alf_cc_cb_filter_signal_flag) + ubs(3, alf_cc_cb_mapped_coeff_abs[k][j], 2, k, j); + else + infer(alf_cc_cb_mapped_coeff_abs[k][j], 0); + if (current->alf_cc_cb_mapped_coeff_abs[k][j]) + ubs(1, alf_cc_cb_coeff_sign[k][j], 2, k, j); + else + infer(alf_cc_cb_coeff_sign[k][j], 0); + } + } + + if (current->alf_cc_cr_filter_signal_flag) + ue(alf_cc_cr_filters_signalled_minus1, 0, 3); + else + infer(alf_cc_cr_filters_signalled_minus1, 0); + for (k = 0; k < current->alf_cc_cr_filters_signalled_minus1 + 1; k++) { + for (j = 0; j < 7; j++) { + if (current->alf_cc_cr_filter_signal_flag) + ubs(3, alf_cc_cr_mapped_coeff_abs[k][j], 2, k, j); + else + infer(alf_cc_cr_mapped_coeff_abs[k][j], 0); + if (current->alf_cc_cr_mapped_coeff_abs[k][j]) + ubs(1, alf_cc_cr_coeff_sign[k][j], 2, k, j); + else + infer(alf_cc_cr_coeff_sign[k][j], 0); + } + } + + return 0; +} + +static int FUNC(lmcs_data)(CodedBitstreamContext *ctx, RWContext *rw, + H266RawAPS *current) +{ + int err, i, lmcs_max_bin_idx; + + ue(lmcs_min_bin_idx, 0, 15); + ue(lmcs_delta_max_bin_idx, 0, 15); + ue(lmcs_delta_cw_prec_minus1, 0, 14); + + lmcs_max_bin_idx = 15 - current->lmcs_delta_max_bin_idx; + + if (lmcs_max_bin_idx < current->lmcs_min_bin_idx) + return AVERROR_INVALIDDATA; + + for (i = current->lmcs_min_bin_idx; i <= lmcs_max_bin_idx; i++) { + ubs(current->lmcs_delta_cw_prec_minus1 + 1, lmcs_delta_abs_cw[i], 1, i); + if (current->lmcs_delta_abs_cw[i] > 0) + flags(lmcs_delta_sign_cw_flag[i], 1, i); + else + infer(lmcs_delta_sign_cw_flag[i], 0); + } + + if (current->aps_chroma_present_flag) { + ub(3, lmcs_delta_abs_crs); + if (current->lmcs_delta_abs_crs > 0) + flag(lmcs_delta_sign_crs_flag); + else + infer(lmcs_delta_sign_crs_flag, 0); + } else { + infer(lmcs_delta_abs_crs, 0); + infer(lmcs_delta_sign_crs_flag, 0); + } + + return 0; +} + +static int FUNC(scaling_list_data)(CodedBitstreamContext *ctx, RWContext *rw, + H266RawAPS *current) +{ + // 7.4.3.4, deriving DiagScanOrder + static const uint8_t diag_scan_order[64][2] = { + { 0, 0, }, { 0, 1, }, { 1, 0, }, { 0, 2, }, { 1, 1, }, { 2, 0, }, { 0, 3, }, { 1, 2, }, + { 2, 1, }, { 3, 0, }, { 0, 4, }, { 1, 3, }, { 2, 2, }, { 3, 1, }, { 4, 0, }, { 0, 5, }, + { 1, 4, }, { 2, 3, }, { 3, 2, }, { 4, 1, }, { 5, 0, }, { 0, 6, }, { 1, 5, }, { 2, 4, }, + { 3, 3, }, { 4, 2, }, { 5, 1, }, { 6, 0, }, { 0, 7, }, { 1, 6, }, { 2, 5, }, { 3, 4, }, + { 4, 3, }, { 5, 2, }, { 6, 1, }, { 7, 0, }, { 1, 7, }, { 2, 6, }, { 3, 5, }, { 4, 4, }, + { 5, 3, }, { 6, 2, }, { 7, 1, }, { 2, 7, }, { 3, 6, }, { 4, 5, }, { 5, 4, }, { 6, 3, }, + { 7, 2, }, { 3, 7, }, { 4, 6, }, { 5, 5, }, { 6, 4, }, { 7, 3, }, { 4, 7, }, { 5, 6, }, + { 6, 5, }, { 7, 4, }, { 5, 7, }, { 6, 6, }, { 7, 5, }, { 6, 7, }, { 7, 6, }, { 7, 7, }, }; + int err; + + for (int id = 0; id < 28; id ++) { + if (current->aps_chroma_present_flag || id % 3 == 2 || id == 27) { + flags(scaling_list_copy_mode_flag[id], 1, id); + if (!current->scaling_list_copy_mode_flag[id]) + flags(scaling_list_pred_mode_flag[id], 1, id); + else + infer(scaling_list_pred_mode_flag[id], 0); + if ((current->scaling_list_copy_mode_flag[id] || + current->scaling_list_pred_mode_flag[id]) && + id != 0 && id != 2 && id != 8) { + int max_id_delta = (id < 2) ? id : ((id < 8) ? (id - 2) : (id - 8)); + ues(scaling_list_pred_id_delta[id], 0, max_id_delta, 1, id); + } + if (!current->scaling_list_copy_mode_flag[id]) { + int matrix_size = id < 2 ? 2 : (id < 8 ? 4 : 8); + if (id > 13) { + int idx = id - 14; + ses(scaling_list_dc_coef[idx], -128, 127, 1, idx); + } + for (int i = 0; i < matrix_size * matrix_size; i++) { + int x = diag_scan_order[i][0]; + int y = diag_scan_order[i][1]; + if (!(id > 25 && x >= 4 && y >= 4)) + ses(scaling_list_delta_coef[id][i], -128, 127, 2, id, i); + } + } else if (id > 13) { + int idx = id - 14; + infer(scaling_list_dc_coef[idx], 0); + } + } else { + infer(scaling_list_copy_mode_flag[id], 1); + infer(scaling_list_pred_mode_flag[id], 0); + } + } + + return 0; +} + +static int FUNC(aps)(CodedBitstreamContext *ctx, RWContext *rw, + H266RawAPS *current, int prefix) +{ + int err; + + if (prefix) + HEADER("Prefix Adaptation parameter set"); + else + HEADER("Suffix Adaptation parameter set"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, + prefix ? VVC_PREFIX_APS_NUT + : VVC_SUFFIX_APS_NUT)); + + ub(3, aps_params_type); + ub(5, aps_adaptation_parameter_set_id); + flag(aps_chroma_present_flag); + if (current->aps_params_type == VVC_ASP_TYPE_ALF) + CHECK(FUNC(alf_data)(ctx, rw, current)); + else if(current->aps_params_type == VVC_ASP_TYPE_LMCS) + CHECK(FUNC(lmcs_data)(ctx, rw, current)); + else if (current->aps_params_type == VVC_ASP_TYPE_SCALING) + CHECK(FUNC(scaling_list_data)(ctx, rw, current)); + flag(aps_extension_flag); + if (current->aps_extension_flag) + CHECK(FUNC(extension_data) (ctx, rw, ¤t->extension_data)); + CHECK(FUNC(rbsp_trailing_bits) (ctx, rw)); + + return 0; +} + +static int FUNC(aud) (CodedBitstreamContext *ctx, RWContext *rw, + H266RawAUD *current) +{ + int err; + + HEADER("Access Unit Delimiter"); + + CHECK(FUNC(nal_unit_header) (ctx, rw, + ¤t->nal_unit_header, VVC_AUD_NUT)); + + flag(aud_irap_or_gdr_flag); + u(3, aud_pic_type, 0, 2); + + CHECK(FUNC(rbsp_trailing_bits) (ctx, rw)); + return 0; +} + +static int FUNC(pred_weight_table) (CodedBitstreamContext *ctx, RWContext *rw, + const H266RawSPS *sps, + const H266RawPPS *pps, + const H266RefPicLists *ref_lists, + uint8_t num_ref_idx_active[2], + H266RawPredWeightTable *current) +{ + int err, i, j; + ue(luma_log2_weight_denom, 0, 7); + if (sps->sps_chroma_format_idc != 0) { + se(delta_chroma_log2_weight_denom, + -current->luma_log2_weight_denom, + 7 - current->luma_log2_weight_denom); + } else { + infer(delta_chroma_log2_weight_denom, 0); + } + if (pps->pps_wp_info_in_ph_flag) { + ue(num_l0_weights, 0, + FFMIN(15, ref_lists->rpl_ref_list[0].num_ref_entries)); + infer(num_weights_l0, current->num_l0_weights); + } else { + infer(num_weights_l0, num_ref_idx_active[0]); + } + for (i = 0; i < current->num_weights_l0; i++) { + flags(luma_weight_l0_flag[i], 1, i); + } + if (sps->sps_chroma_format_idc != 0) { + for (i = 0; i < current->num_weights_l0; i++) + flags(chroma_weight_l0_flag[i], 1, i); + } + for (i = 0; i < current->num_weights_l0; i++) { + if (current->luma_weight_l0_flag[i]) { + ses(delta_luma_weight_l0[i], -128, 127, 1, i); + ses(luma_offset_l0[i], -128, 127, 1, i); + } else { + infer(delta_luma_weight_l0[i], 0); + infer(luma_offset_l0[i], 0); + } + if (current->chroma_weight_l0_flag[i]) { + for (j = 0; j < 2; j++) { + ses(delta_chroma_weight_l0[i][j], -128, 127, 2, i, j); + ses(delta_chroma_offset_l0[i][j], -4 * 128, 4 * 127, 2, i, j); + } + } + } + + if (pps->pps_weighted_bipred_flag && + ref_lists->rpl_ref_list[1].num_ref_entries > 0) { + if (pps->pps_wp_info_in_ph_flag) { + ue(num_l1_weights, 0, + FFMIN(15, ref_lists->rpl_ref_list[1].num_ref_entries)); + infer(num_weights_l1, current->num_l1_weights); + } else { + infer(num_weights_l1, num_ref_idx_active[1]); + } + } else { + infer(num_weights_l1, 0); + } + + for (i = 0; i < current->num_weights_l1; i++) + flags(luma_weight_l1_flag[i], 1, i); + if (sps->sps_chroma_format_idc != 0) { + for (i = 0; i < current->num_weights_l1; i++) + flags(chroma_weight_l1_flag[i], 1, i); + } + for (i = 0; i < current->num_weights_l1; i++) { + if (current->luma_weight_l1_flag[i]) { + ses(delta_luma_weight_l1[i], -128, 127, 1, i); + ses(luma_offset_l1[i], -128, 127, 1, i); + } else { + infer(delta_luma_weight_l1[i], 0); + infer(luma_offset_l1[i], 0); + } + if (current->chroma_weight_l1_flag[i]) { + for (j = 0; j < 2; j++) { + ses(delta_chroma_weight_l1[i][j], -128, 127, 2, i, j); + ses(delta_chroma_offset_l1[i][j], -4 * 128, 4 * 127, 2, i, j); + } + } + } + return 0; +} + +static int FUNC(picture_header) (CodedBitstreamContext *ctx, RWContext *rw, + H266RawPictureHeader *current) { + CodedBitstreamH266Context *h266 = ctx->priv_data; + const H266RawVPS *vps; + const H266RawSPS *sps; + const H266RawPPS *pps; + int err, i; + unsigned int ctb_log2_size_y, min_cb_log2_size_y, + min_qt_log2_size_intra_y, min_qt_log2_size_inter_y; + uint8_t qp_bd_offset; + + flag(ph_gdr_or_irap_pic_flag); + flag(ph_non_ref_pic_flag); + if (current->ph_gdr_or_irap_pic_flag) + flag(ph_gdr_pic_flag); + else + infer(ph_gdr_pic_flag, 0); + flag(ph_inter_slice_allowed_flag); + if (current->ph_inter_slice_allowed_flag) + flag(ph_intra_slice_allowed_flag); + else + infer(ph_intra_slice_allowed_flag, 1); + ue(ph_pic_parameter_set_id, 0, VVC_MAX_PPS_COUNT - 1); + pps = h266->pps[current->ph_pic_parameter_set_id]; + if (!pps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "PPS id %d not available.\n", + current->ph_pic_parameter_set_id); + return AVERROR_INVALIDDATA; + } + sps = h266->sps[pps->pps_seq_parameter_set_id]; + if (!sps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "SPS id %d not available.\n", + pps->pps_seq_parameter_set_id); + return AVERROR_INVALIDDATA; + } + vps = h266->vps[sps->sps_video_parameter_set_id]; + if (!vps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "VPS id %d not available.\n", + sps->sps_video_parameter_set_id); + return AVERROR_INVALIDDATA; + } + + ub(sps->sps_log2_max_pic_order_cnt_lsb_minus4 + 4, ph_pic_order_cnt_lsb); + if (current->ph_gdr_pic_flag) + ue(ph_recovery_poc_cnt, 0, + 1 << (sps->sps_log2_max_pic_order_cnt_lsb_minus4 + 4)); + + for (i = 0; i < sps->sps_num_extra_ph_bytes * 8; i++) { + if (sps->sps_extra_ph_bit_present_flag[i]) + flags(ph_extra_bit[i], 1, i); + } + if (sps->sps_poc_msb_cycle_flag) { + flag(ph_poc_msb_cycle_present_flag); + if (current->ph_poc_msb_cycle_present_flag) + ub(sps->sps_poc_msb_cycle_len_minus1 + 1, ph_poc_msb_cycle_val); + } + if (sps->sps_alf_enabled_flag && pps->pps_alf_info_in_ph_flag) { + flag(ph_alf_enabled_flag); + if (current->ph_alf_enabled_flag) { + + ub(3, ph_num_alf_aps_ids_luma); + for (i = 0; i < current->ph_num_alf_aps_ids_luma; i++) + ubs(3, ph_alf_aps_id_luma[i], 1, i); + + if (sps->sps_chroma_format_idc != 0) { + flag(ph_alf_cb_enabled_flag); + flag(ph_alf_cr_enabled_flag); + } else { + infer(ph_alf_cb_enabled_flag, 0); + infer(ph_alf_cr_enabled_flag, 0); + } + + if (current->ph_alf_cb_enabled_flag + || current->ph_alf_cr_enabled_flag) { + ub(3, ph_alf_aps_id_chroma); + } + + if (sps->sps_ccalf_enabled_flag) { + flag(ph_alf_cc_cb_enabled_flag); + if (current->ph_alf_cc_cb_enabled_flag) + ub(3, ph_alf_cc_cb_aps_id); + flag(ph_alf_cc_cr_enabled_flag); + if (current->ph_alf_cc_cr_enabled_flag) + ub(3, ph_alf_cc_cr_aps_id); + } + } + } else { + infer(ph_alf_enabled_flag, 0); + } + if (sps->sps_lmcs_enabled_flag) { + flag(ph_lmcs_enabled_flag); + if (current->ph_lmcs_enabled_flag) { + ub(2, ph_lmcs_aps_id); + if (sps->sps_chroma_format_idc != 0) + flag(ph_chroma_residual_scale_flag); + else + infer(ph_chroma_residual_scale_flag, 0); + } + } else { + infer(ph_lmcs_enabled_flag, 0); + infer(ph_chroma_residual_scale_flag, 0); + } + + if (sps->sps_explicit_scaling_list_enabled_flag) { + flag(ph_explicit_scaling_list_enabled_flag); + if (current->ph_explicit_scaling_list_enabled_flag) { + //todo: check the ph_scaling_list_aps_id range, when aps ready + ub(3, ph_scaling_list_aps_id); + } + } else { + infer(ph_explicit_scaling_list_enabled_flag, 0); + } + if (sps->sps_virtual_boundaries_enabled_flag && + !sps->sps_virtual_boundaries_present_flag) { + flag(ph_virtual_boundaries_present_flag); + if (current->ph_virtual_boundaries_present_flag) { + ue(ph_num_ver_virtual_boundaries, + 0, pps->pps_pic_width_in_luma_samples <= 8 ? 0 : 3); + for (i = 0; i < current->ph_num_ver_virtual_boundaries; i++) { + ues(ph_virtual_boundary_pos_x_minus1[i], + 0, (pps->pps_pic_width_in_luma_samples + 7) / 8 - 2, 1, i); + } + ue(ph_num_hor_virtual_boundaries, + 0, pps->pps_pic_height_in_luma_samples <= 8 ? 0 : 3); + for (i = 0; i < current->ph_num_hor_virtual_boundaries; i++) { + ues(ph_virtual_boundary_pos_y_minus1[i], + 0, (pps->pps_pic_height_in_luma_samples + 7) / 8 - 2, 1, i); + } + } else { + infer(ph_num_ver_virtual_boundaries, 0); + infer(ph_num_hor_virtual_boundaries, 0); + } + } + if (pps->pps_output_flag_present_flag && !current->ph_non_ref_pic_flag) + flag(ph_pic_output_flag); + else + infer(ph_pic_output_flag, 1); + if (pps->pps_rpl_info_in_ph_flag) { + CHECK(FUNC(ref_pic_lists) + (ctx, rw, sps, pps, ¤t->ph_ref_pic_lists)); + } + if (sps->sps_partition_constraints_override_enabled_flag) + flag(ph_partition_constraints_override_flag); + else + infer(ph_partition_constraints_override_flag, 0); + + ctb_log2_size_y = sps->sps_log2_ctu_size_minus5 + 5; + min_cb_log2_size_y = sps->sps_log2_min_luma_coding_block_size_minus2 + 2; + if (current->ph_intra_slice_allowed_flag) { + if (current->ph_partition_constraints_override_flag) { + ue(ph_log2_diff_min_qt_min_cb_intra_slice_luma, + 0, FFMIN(6, ctb_log2_size_y) - min_cb_log2_size_y); + ue(ph_max_mtt_hierarchy_depth_intra_slice_luma, + 0, 2 * (ctb_log2_size_y - min_cb_log2_size_y)); + if (current->ph_max_mtt_hierarchy_depth_intra_slice_luma != 0) { + min_qt_log2_size_intra_y = + current->ph_log2_diff_min_qt_min_cb_intra_slice_luma + + min_cb_log2_size_y; + ue(ph_log2_diff_max_bt_min_qt_intra_slice_luma, + 0, (sps->sps_qtbtt_dual_tree_intra_flag ? + FFMIN(6, ctb_log2_size_y) : + ctb_log2_size_y) - min_qt_log2_size_intra_y); + ue(ph_log2_diff_max_tt_min_qt_intra_slice_luma, + 0, FFMIN(6, ctb_log2_size_y) - min_qt_log2_size_intra_y); + } else { + infer(ph_log2_diff_max_bt_min_qt_intra_slice_luma, + sps->sps_log2_diff_max_bt_min_qt_intra_slice_luma); + infer(ph_log2_diff_max_tt_min_qt_intra_slice_luma, + sps->sps_log2_diff_max_tt_min_qt_intra_slice_luma); + } + if (sps->sps_qtbtt_dual_tree_intra_flag) { + ue(ph_log2_diff_min_qt_min_cb_intra_slice_chroma, + 0, FFMIN(6, ctb_log2_size_y) - min_cb_log2_size_y); + ue(ph_max_mtt_hierarchy_depth_intra_slice_chroma, + 0, 2 * (ctb_log2_size_y - min_cb_log2_size_y)); + if (sps->sps_max_mtt_hierarchy_depth_intra_slice_chroma != 0) { + unsigned int min_qt_log2_size_intra_c = + sps->sps_log2_diff_min_qt_min_cb_intra_slice_chroma + + min_cb_log2_size_y; + ue(ph_log2_diff_max_bt_min_qt_intra_slice_chroma, + 0, FFMIN(6, ctb_log2_size_y) - min_qt_log2_size_intra_c); + ue(ph_log2_diff_max_tt_min_qt_intra_slice_chroma, + 0, FFMIN(6, ctb_log2_size_y) - min_qt_log2_size_intra_c); + } else { + infer(ph_log2_diff_max_bt_min_qt_intra_slice_chroma, + sps->sps_log2_diff_max_bt_min_qt_intra_slice_chroma); + infer(ph_log2_diff_max_tt_min_qt_intra_slice_chroma, + sps->sps_log2_diff_max_tt_min_qt_intra_slice_chroma); + } + } + } else { + infer(ph_log2_diff_min_qt_min_cb_intra_slice_luma, + sps->sps_log2_diff_min_qt_min_cb_intra_slice_luma); + infer(ph_max_mtt_hierarchy_depth_intra_slice_luma, + sps->sps_max_mtt_hierarchy_depth_intra_slice_luma); + infer(ph_log2_diff_max_bt_min_qt_intra_slice_luma, + sps->sps_log2_diff_max_bt_min_qt_intra_slice_luma); + infer(ph_log2_diff_max_tt_min_qt_intra_slice_luma, + sps->sps_log2_diff_max_tt_min_qt_intra_slice_luma); + infer(ph_log2_diff_min_qt_min_cb_intra_slice_chroma, + sps->sps_log2_diff_min_qt_min_cb_intra_slice_chroma); + infer(ph_max_mtt_hierarchy_depth_intra_slice_chroma, + sps->sps_max_mtt_hierarchy_depth_intra_slice_chroma); + infer(ph_log2_diff_max_bt_min_qt_intra_slice_chroma, + sps->sps_log2_diff_max_bt_min_qt_intra_slice_chroma); + infer(ph_log2_diff_max_tt_min_qt_intra_slice_chroma, + sps->sps_log2_diff_max_tt_min_qt_intra_slice_chroma); + } + + min_qt_log2_size_intra_y = + current->ph_log2_diff_min_qt_min_cb_intra_slice_luma + + min_cb_log2_size_y; + if (pps->pps_cu_qp_delta_enabled_flag) + ue(ph_cu_qp_delta_subdiv_intra_slice, 0, + 2 * (ctb_log2_size_y - min_qt_log2_size_intra_y + + current->ph_max_mtt_hierarchy_depth_intra_slice_luma)); + else + infer(ph_cu_qp_delta_subdiv_intra_slice, 0); + + if (pps->pps_cu_chroma_qp_offset_list_enabled_flag) + ue(ph_cu_chroma_qp_offset_subdiv_intra_slice, 0, + 2 * (ctb_log2_size_y - min_qt_log2_size_intra_y + + current->ph_max_mtt_hierarchy_depth_intra_slice_luma)); + else + infer(ph_cu_chroma_qp_offset_subdiv_intra_slice, 0); + } + if (current->ph_inter_slice_allowed_flag) { + if (current->ph_partition_constraints_override_flag) { + ue(ph_log2_diff_min_qt_min_cb_inter_slice, + 0, FFMIN(6, ctb_log2_size_y) - min_cb_log2_size_y); + min_qt_log2_size_inter_y = + current->ph_log2_diff_min_qt_min_cb_inter_slice + + min_cb_log2_size_y; + ue(ph_max_mtt_hierarchy_depth_inter_slice, 0, + 2 * (ctb_log2_size_y - min_cb_log2_size_y)); + if (current->ph_max_mtt_hierarchy_depth_inter_slice != 0) { + ue(ph_log2_diff_max_bt_min_qt_inter_slice, + 0, ctb_log2_size_y - min_qt_log2_size_inter_y); + ue(ph_log2_diff_max_tt_min_qt_inter_slice, + 0, FFMIN(6, ctb_log2_size_y) - min_qt_log2_size_inter_y); + } + } else { + infer(ph_log2_diff_min_qt_min_cb_inter_slice, + sps->sps_log2_diff_min_qt_min_cb_inter_slice); + min_qt_log2_size_inter_y = + current->ph_log2_diff_min_qt_min_cb_inter_slice + + min_cb_log2_size_y; + infer(ph_max_mtt_hierarchy_depth_inter_slice, + sps->sps_max_mtt_hierarchy_depth_inter_slice); + infer(ph_log2_diff_max_bt_min_qt_inter_slice, + sps->sps_log2_diff_max_bt_min_qt_inter_slice); + infer(ph_log2_diff_max_tt_min_qt_inter_slice, + sps->sps_log2_diff_max_tt_min_qt_inter_slice); + } + + if (pps->pps_cu_qp_delta_enabled_flag) + ue(ph_cu_qp_delta_subdiv_inter_slice, 0, + 2 * (ctb_log2_size_y - min_qt_log2_size_inter_y + + current->ph_max_mtt_hierarchy_depth_inter_slice)); + else + infer(ph_cu_qp_delta_subdiv_inter_slice, 0); + + if (pps->pps_cu_chroma_qp_offset_list_enabled_flag) + ue(ph_cu_chroma_qp_offset_subdiv_inter_slice, 0, + 2 * (ctb_log2_size_y - min_qt_log2_size_inter_y + + current->ph_max_mtt_hierarchy_depth_inter_slice)); + else + infer(ph_cu_chroma_qp_offset_subdiv_inter_slice, 0); + if (sps->sps_temporal_mvp_enabled_flag) { + flag(ph_temporal_mvp_enabled_flag); + if (current->ph_temporal_mvp_enabled_flag && + pps->pps_rpl_info_in_ph_flag) { + if (current->ph_ref_pic_lists.rpl_ref_list[1].num_ref_entries > 0) + flag(ph_collocated_from_l0_flag); + else + infer(ph_collocated_from_l0_flag, 1); + if ((current->ph_collocated_from_l0_flag && + current->ph_ref_pic_lists.rpl_ref_list[0].num_ref_entries > 1) + || (!current->ph_collocated_from_l0_flag && + current->ph_ref_pic_lists.rpl_ref_list[1].num_ref_entries > 1)) { + unsigned int idx = + current->ph_collocated_from_l0_flag ? 0 : 1; + ue(ph_collocated_ref_idx, 0, + current->ph_ref_pic_lists.rpl_ref_list[idx]. + num_ref_entries - 1); + } else { + infer(ph_collocated_ref_idx, 0); + } + } + } + if (sps->sps_mmvd_fullpel_only_enabled_flag) + flag(ph_mmvd_fullpel_only_flag); + else + infer(ph_mmvd_fullpel_only_flag, 0); + if (!pps->pps_rpl_info_in_ph_flag || + current->ph_ref_pic_lists.rpl_ref_list[1].num_ref_entries > 0) { + flag(ph_mvd_l1_zero_flag); + if (sps->sps_bdof_control_present_in_ph_flag) { + flag(ph_bdof_disabled_flag); + } else { + if (!sps->sps_bdof_control_present_in_ph_flag) + infer(ph_bdof_disabled_flag, + 1 - sps->sps_bdof_enabled_flag); + else + infer(ph_bdof_disabled_flag, 1); + } + if (sps->sps_dmvr_control_present_in_ph_flag) { + flag(ph_dmvr_disabled_flag); + } else { + if (!sps->sps_dmvr_control_present_in_ph_flag) + infer(ph_dmvr_disabled_flag, + 1 - sps->sps_dmvr_enabled_flag); + else + infer(ph_dmvr_disabled_flag, 1); + } + } else { + infer(ph_mvd_l1_zero_flag, 1); + } + if (sps->sps_prof_control_present_in_ph_flag) + flag(ph_prof_disabled_flag); + else + infer(ph_prof_disabled_flag, !sps->sps_affine_prof_enabled_flag); + if ((pps->pps_weighted_pred_flag || + pps->pps_weighted_bipred_flag) && pps->pps_wp_info_in_ph_flag) { + + // if pps->pps_wp_info_in_ph_fla == 1 + // pred_weight_table will not use num_ref_idx_active + uint8_t num_ref_idx_active[2] = { 0, 0 }; + CHECK(FUNC(pred_weight_table) + (ctx, rw, sps, pps, ¤t->ph_ref_pic_lists, + num_ref_idx_active, ¤t->ph_pred_weight_table)); + } + } + + qp_bd_offset = 6 * sps->sps_bitdepth_minus8; + if (pps->pps_qp_delta_info_in_ph_flag) + se(ph_qp_delta, -qp_bd_offset - (26 + pps->pps_init_qp_minus26), + 63 - (26 + pps->pps_init_qp_minus26)); + + if (sps->sps_joint_cbcr_enabled_flag) + flag(ph_joint_cbcr_sign_flag); + else + infer(ph_joint_cbcr_sign_flag, 0); + if (sps->sps_sao_enabled_flag && pps->pps_sao_info_in_ph_flag) { + flag(ph_sao_luma_enabled_flag); + if (sps->sps_chroma_format_idc != 0) + flag(ph_sao_chroma_enabled_flag); + else + infer(ph_sao_chroma_enabled_flag, 0); + } else { + infer(ph_sao_luma_enabled_flag, 0); + infer(ph_sao_chroma_enabled_flag, 0); + } + + if (pps->pps_dbf_info_in_ph_flag) + flag(ph_deblocking_params_present_flag); + else + infer(ph_deblocking_params_present_flag, 0); + + if (current->ph_deblocking_params_present_flag) { + if (!pps->pps_deblocking_filter_disabled_flag) { + flag(ph_deblocking_filter_disabled_flag); + if (!current->ph_deblocking_filter_disabled_flag) { + se(ph_luma_beta_offset_div2, -12, 12); + se(ph_luma_tc_offset_div2, -12, 12); + if (pps->pps_chroma_tool_offsets_present_flag) { + se(ph_cb_beta_offset_div2, -12, 12); + se(ph_cb_tc_offset_div2, -12, 12); + se(ph_cr_beta_offset_div2, -12, 12); + se(ph_cr_tc_offset_div2, -12, 12); + } else { + infer(ph_cb_beta_offset_div2, + current->ph_luma_beta_offset_div2); + infer(ph_cb_tc_offset_div2, + current->ph_luma_tc_offset_div2); + infer(ph_cr_beta_offset_div2, + current->ph_luma_beta_offset_div2); + infer(ph_cr_tc_offset_div2, + current->ph_luma_tc_offset_div2); + } + } + } else { + infer(ph_deblocking_filter_disabled_flag, 0); + } + } else { + infer(ph_deblocking_filter_disabled_flag, pps->pps_deblocking_filter_disabled_flag); + if (!current->ph_deblocking_filter_disabled_flag) { + infer(ph_luma_beta_offset_div2, pps->pps_luma_beta_offset_div2); + infer(ph_luma_tc_offset_div2, pps->pps_luma_tc_offset_div2); + infer(ph_cb_beta_offset_div2, pps->pps_cb_beta_offset_div2); + infer(ph_cb_tc_offset_div2, pps->pps_cb_tc_offset_div2); + infer(ph_cr_beta_offset_div2, pps->pps_cr_beta_offset_div2); + infer(ph_cr_tc_offset_div2, pps->pps_cr_tc_offset_div2); + } + } + + if (pps->pps_picture_header_extension_present_flag) { + ue(ph_extension_length, 0, 256); + for (i = 0; i < current->ph_extension_length; i++) + us(8, ph_extension_data_byte[i], 0x00, 0xff, 1, i); + } + + return 0; +} + +static int FUNC(ph) (CodedBitstreamContext *ctx, RWContext *rw, + H266RawPH *current) +{ + int err; + + HEADER("Picture Header"); + + CHECK(FUNC(nal_unit_header) (ctx, rw, ¤t->nal_unit_header, VVC_PH_NUT)); + CHECK(FUNC(picture_header) (ctx, rw, ¤t->ph_picture_header)); + CHECK(FUNC(rbsp_trailing_bits) (ctx, rw)); + return 0; +} + +static int FUNC(slice_header) (CodedBitstreamContext *ctx, RWContext *rw, + H266RawSliceHeader *current) +{ + CodedBitstreamH266Context *h266 = ctx->priv_data; + const H266RawSPS *sps; + const H266RawPPS *pps; + const H266RawPictureHeader *ph; + const H266RefPicLists *ref_pic_lists; + int err, i; + uint8_t nal_unit_type, qp_bd_offset; + uint16_t curr_subpic_idx; + uint16_t num_slices_in_subpic; + + HEADER("Slice Header"); + + CHECK(FUNC(nal_unit_header) (ctx, rw, ¤t->nal_unit_header, -1)); + + flag(sh_picture_header_in_slice_header_flag); + if (current->sh_picture_header_in_slice_header_flag) { + // 7.4.8 if sh_picture_header_in_slice_header_flag is true, we do not have a PH NAL unit + CHECK(FUNC(picture_header) (ctx, rw, ¤t->sh_picture_header)); + ph = ¤t->sh_picture_header; + } else { + ph = h266->ph; + if (!ph) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Picture header not available.\n"); + return AVERROR_INVALIDDATA; + } + } + + pps = h266->pps[ph->ph_pic_parameter_set_id]; + if (!pps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "PPS id %d not available.\n", + ph->ph_pic_parameter_set_id); + return AVERROR_INVALIDDATA; + } + sps = h266->sps[pps->pps_seq_parameter_set_id]; + if (!sps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "SPS id %d not available.\n", + pps->pps_seq_parameter_set_id); + return AVERROR_INVALIDDATA; + } + + if (sps->sps_subpic_info_present_flag) { + ub(sps->sps_subpic_id_len_minus1 + 1, sh_subpic_id); + for (i = 0; i <= sps->sps_num_subpics_minus1; i++) { + if (pps->sub_pic_id_val[i] == current->sh_subpic_id) { + curr_subpic_idx = i; + break; + } + } + if (i > sps->sps_num_subpics_minus1) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "invalid CurrSubpicIdx %d\n", i); + return AVERROR_INVALIDDATA; + } + } else { + curr_subpic_idx = 0; + } + + num_slices_in_subpic = pps->num_slices_in_subpic[curr_subpic_idx]; + + if ((pps->pps_rect_slice_flag && num_slices_in_subpic > 1) || + (!pps->pps_rect_slice_flag && pps->num_tiles_in_pic > 1)) { + unsigned int bits, max; + if (!pps->pps_rect_slice_flag) { + bits = av_ceil_log2(pps->num_tiles_in_pic); + max = pps->num_tiles_in_pic - 1; + } else { + bits = av_ceil_log2(num_slices_in_subpic); + max = num_slices_in_subpic - 1; + } + u(bits, sh_slice_address, 0, max); + } else { + infer(sh_slice_address, 0); + } + + for (i = 0; i < sps->sps_num_extra_sh_bytes * 8; i++) { + if (sps->sps_extra_sh_bit_present_flag[i]) + flags(sh_extra_bit[i], 1, i); + } + + if (!pps->pps_rect_slice_flag && + pps->num_tiles_in_pic - current->sh_slice_address > 1) + ue(sh_num_tiles_in_slice_minus1, 0, pps->num_tiles_in_pic - 1); + else + infer(sh_num_tiles_in_slice_minus1, 0); + + if (ph->ph_inter_slice_allowed_flag) + ue(sh_slice_type, 0, 2); + else + infer(sh_slice_type, 2); + + nal_unit_type = current->nal_unit_header.nal_unit_type; + if (nal_unit_type == VVC_IDR_W_RADL || nal_unit_type == VVC_IDR_N_LP || + nal_unit_type == VVC_CRA_NUT || nal_unit_type == VVC_GDR_NUT) + flag(sh_no_output_of_prior_pics_flag); + + if (sps->sps_alf_enabled_flag) { + if (!pps->pps_alf_info_in_ph_flag) { + flag(sh_alf_enabled_flag); + if (current->sh_alf_enabled_flag) { + ub(3, sh_num_alf_aps_ids_luma); + for (i = 0; i < current->sh_num_alf_aps_ids_luma; i++) + ubs(3, sh_alf_aps_id_luma[i], 1, i); + + if (sps->sps_chroma_format_idc != 0) { + flag(sh_alf_cb_enabled_flag); + flag(sh_alf_cr_enabled_flag); + } + if (current->sh_alf_cb_enabled_flag || + current->sh_alf_cr_enabled_flag) { + ub(3, sh_alf_aps_id_chroma); + } + + if (sps->sps_ccalf_enabled_flag) { + flag(sh_alf_cc_cb_enabled_flag); + if (current->sh_alf_cc_cb_enabled_flag) + ub(3, sh_alf_cc_cb_aps_id); + + flag(sh_alf_cc_cr_enabled_flag); + if (current->sh_alf_cc_cr_enabled_flag) + ub(3, sh_alf_cc_cr_aps_id); + } + } + } else { + infer(sh_alf_enabled_flag, ph->ph_alf_enabled_flag); + if (current->sh_alf_enabled_flag) { + infer(sh_num_alf_aps_ids_luma, ph->ph_num_alf_aps_ids_luma); + for (i = 0; i < current->sh_num_alf_aps_ids_luma; i++) + infer(sh_alf_aps_id_luma[i], ph->ph_alf_aps_id_luma[i]); + + infer(sh_alf_cb_enabled_flag, ph->ph_alf_cb_enabled_flag); + infer(sh_alf_cr_enabled_flag, ph->ph_alf_cr_enabled_flag); + if (current->sh_alf_cb_enabled_flag ||current->sh_alf_cr_enabled_flag) + infer(sh_alf_aps_id_chroma, ph->ph_alf_aps_id_chroma); + + if (sps->sps_ccalf_enabled_flag) { + infer(sh_alf_cc_cb_enabled_flag, ph->ph_alf_cc_cb_enabled_flag); + if (current->sh_alf_cc_cb_enabled_flag) + infer(sh_alf_cc_cb_aps_id, ph->ph_alf_cc_cb_aps_id); + + infer(sh_alf_cc_cr_enabled_flag, ph->ph_alf_cc_cr_enabled_flag); + if (current->sh_alf_cc_cr_enabled_flag) + infer(sh_alf_cc_cr_aps_id, ph->ph_alf_cc_cr_aps_id); + } + } + } + } + + if (current->sh_picture_header_in_slice_header_flag) { + infer(sh_lmcs_used_flag, ph->ph_lmcs_enabled_flag); + infer(sh_explicit_scaling_list_used_flag, + ph->ph_explicit_scaling_list_enabled_flag); + } else { + if (ph->ph_lmcs_enabled_flag) + flag(sh_lmcs_used_flag); + else + infer(sh_lmcs_used_flag, 0); + + if (ph->ph_explicit_scaling_list_enabled_flag) + flag(sh_explicit_scaling_list_used_flag); + else + infer(sh_explicit_scaling_list_used_flag, 0); + } + + if (!pps->pps_rpl_info_in_ph_flag && + ((nal_unit_type != VVC_IDR_W_RADL && + nal_unit_type != VVC_IDR_N_LP) || sps->sps_idr_rpl_present_flag)) { + CHECK(FUNC(ref_pic_lists) + (ctx, rw, sps, pps, ¤t->sh_ref_pic_lists)); + ref_pic_lists = ¤t->sh_ref_pic_lists; + } else { + ref_pic_lists = &ph->ph_ref_pic_lists; + } + if ((current->sh_slice_type != VVC_SLICE_TYPE_I && + ref_pic_lists->rpl_ref_list[0].num_ref_entries > 1) || + (current->sh_slice_type == VVC_SLICE_TYPE_B && + ref_pic_lists->rpl_ref_list[1].num_ref_entries > 1)) { + flag(sh_num_ref_idx_active_override_flag); + if (current->sh_num_ref_idx_active_override_flag) { + for (i = 0; + i < (current->sh_slice_type == VVC_SLICE_TYPE_B ? 2 : 1); i++) + if (ref_pic_lists->rpl_ref_list[i].num_ref_entries > 1) + ues(sh_num_ref_idx_active_minus1[i], 0, 14, 1, i); + else + infer(sh_num_ref_idx_active_minus1[i], 0); + } + } else { + infer(sh_num_ref_idx_active_override_flag, 1); + } + + for (i = 0; i < 2; i++) { + if (current->sh_slice_type == VVC_SLICE_TYPE_B || + (current->sh_slice_type == VVC_SLICE_TYPE_P && i == 0)) { + if (current->sh_num_ref_idx_active_override_flag) { + current->num_ref_idx_active[i] = current->sh_num_ref_idx_active_minus1[i] + 1; + } else { + current->num_ref_idx_active[i] = + FFMIN(ref_pic_lists->rpl_ref_list[i].num_ref_entries, + pps->pps_num_ref_idx_default_active_minus1[i] + 1); + } + } else { + current->num_ref_idx_active[i] = 0; + } + } + + if (current->sh_slice_type != VVC_SLICE_TYPE_I) { + if (pps->pps_cabac_init_present_flag) + flag(sh_cabac_init_flag); + else + infer(sh_cabac_init_flag, 0); + if (ph->ph_temporal_mvp_enabled_flag && !pps->pps_rpl_info_in_ph_flag) { + if (current->sh_slice_type == VVC_SLICE_TYPE_B) + flag(sh_collocated_from_l0_flag); + else + infer(sh_collocated_from_l0_flag, 1); + if ((current->sh_collocated_from_l0_flag && + current->num_ref_idx_active[0] > 1) || + (!current->sh_collocated_from_l0_flag && + current->num_ref_idx_active[1] > 1)) { + unsigned int idx = current->sh_collocated_from_l0_flag ? 0 : 1; + ue(sh_collocated_ref_idx, 0, current->num_ref_idx_active[idx] - 1); + } else { + infer(sh_collocated_ref_idx, 0); + } + } + if (!pps->pps_wp_info_in_ph_flag && + ((pps->pps_weighted_pred_flag && + current->sh_slice_type == VVC_SLICE_TYPE_P) || + (pps->pps_weighted_bipred_flag && + current->sh_slice_type == VVC_SLICE_TYPE_B))) { + CHECK(FUNC(pred_weight_table) (ctx, rw, sps, pps, ref_pic_lists, + current->num_ref_idx_active, + ¤t->sh_pred_weight_table)); + } + } + qp_bd_offset = 6 * sps->sps_bitdepth_minus8; + if (!pps->pps_qp_delta_info_in_ph_flag) + se(sh_qp_delta, -qp_bd_offset - (26 + pps->pps_init_qp_minus26), + 63 - (26 + pps->pps_init_qp_minus26)); + if (pps->pps_slice_chroma_qp_offsets_present_flag) { + int8_t off; + + se(sh_cb_qp_offset, -12, 12); + off = pps->pps_cb_qp_offset + current->sh_cb_qp_offset; + if (off < -12 || off > 12) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "pps_cb_qp_offset + sh_cb_qp_offset (%d) not in range [-12, 12].\n", + off); + return AVERROR_INVALIDDATA; + } + + se(sh_cr_qp_offset, -12, 12); + off = pps->pps_cr_qp_offset + current->sh_cr_qp_offset; + if (off < -12 || off > 12) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "pps_cr_qp_offset + sh_cr_qp_offset (%d) not in range [-12, 12].\n", + off); + return AVERROR_INVALIDDATA; + } + + if (sps->sps_joint_cbcr_enabled_flag) { + se(sh_joint_cbcr_qp_offset, -12, 12); + off = + pps->pps_joint_cbcr_qp_offset_value + + current->sh_joint_cbcr_qp_offset; + if (off < -12 || off > 12) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "pps_joint_cbcr_qp_offset_value + sh_joint_cbcr_qp_offset (%d)" + "not in range [-12, 12]. \n", off); + return AVERROR_INVALIDDATA; + } + } else { + infer(sh_joint_cbcr_qp_offset, 0); + } + } else { + infer(sh_cb_qp_offset, 0); + infer(sh_cr_qp_offset, 0); + infer(sh_joint_cbcr_qp_offset, 0); + } + if (pps->pps_cu_chroma_qp_offset_list_enabled_flag) + flag(sh_cu_chroma_qp_offset_enabled_flag); + else + infer(sh_cu_chroma_qp_offset_enabled_flag, 0); + if (sps->sps_sao_enabled_flag && !pps->pps_sao_info_in_ph_flag) { + flag(sh_sao_luma_used_flag); + if (sps->sps_chroma_format_idc != 0) + flag(sh_sao_chroma_used_flag); + else + infer(sh_sao_chroma_used_flag, ph->ph_sao_chroma_enabled_flag); + } else { + infer(sh_sao_luma_used_flag, ph->ph_sao_luma_enabled_flag); + infer(sh_sao_chroma_used_flag, ph->ph_sao_chroma_enabled_flag); + } + + if (pps->pps_deblocking_filter_override_enabled_flag && + !pps->pps_dbf_info_in_ph_flag) + flag(sh_deblocking_params_present_flag); + else + infer(sh_deblocking_params_present_flag, 0); + if (current->sh_deblocking_params_present_flag) { + if (!pps->pps_deblocking_filter_disabled_flag) + flag(sh_deblocking_filter_disabled_flag); + else + infer(sh_deblocking_filter_disabled_flag, 0); + if (!current->sh_deblocking_filter_disabled_flag) { + se(sh_luma_beta_offset_div2, -12, 12); + se(sh_luma_tc_offset_div2, -12, 12); + if (pps->pps_chroma_tool_offsets_present_flag) { + se(sh_cb_beta_offset_div2, -12, 12); + se(sh_cb_tc_offset_div2, -12, 12); + se(sh_cr_beta_offset_div2, -12, 12); + se(sh_cr_tc_offset_div2, -12, 12); + } else { + infer(sh_cb_beta_offset_div2, + current->sh_luma_beta_offset_div2); + infer(sh_cb_tc_offset_div2, current->sh_luma_tc_offset_div2); + infer(sh_cr_beta_offset_div2, + current->sh_luma_beta_offset_div2); + infer(sh_cr_tc_offset_div2, current->sh_luma_tc_offset_div2); + } + } + } else { + infer(sh_deblocking_filter_disabled_flag, ph->ph_deblocking_filter_disabled_flag); + if (!current->sh_deblocking_filter_disabled_flag) { + infer(sh_luma_beta_offset_div2, ph->ph_luma_beta_offset_div2); + infer(sh_luma_tc_offset_div2, ph->ph_luma_tc_offset_div2); + infer(sh_cb_beta_offset_div2, ph->ph_cb_beta_offset_div2); + infer(sh_cb_tc_offset_div2, ph->ph_cb_tc_offset_div2); + infer(sh_cr_beta_offset_div2, ph->ph_cr_beta_offset_div2); + infer(sh_cr_tc_offset_div2, ph->ph_cr_tc_offset_div2); + } + } + + if (sps->sps_dep_quant_enabled_flag) + flag(sh_dep_quant_used_flag); + else + infer(sh_dep_quant_used_flag, 0); + + if (sps->sps_sign_data_hiding_enabled_flag && + !current->sh_dep_quant_used_flag) + flag(sh_sign_data_hiding_used_flag); + else + infer(sh_sign_data_hiding_used_flag, 0); + + if (sps->sps_transform_skip_enabled_flag && + !current->sh_dep_quant_used_flag && + !current->sh_sign_data_hiding_used_flag) + flag(sh_ts_residual_coding_disabled_flag); + else + infer(sh_ts_residual_coding_disabled_flag, 0); + + if (!current->sh_ts_residual_coding_disabled_flag && + sps->sps_ts_residual_coding_rice_present_in_sh_flag) + ub(3, sh_ts_residual_coding_rice_idx_minus1); + else + infer(sh_ts_residual_coding_rice_idx_minus1, 0); + + if (sps->sps_reverse_last_sig_coeff_enabled_flag) + flag(sh_reverse_last_sig_coeff_flag); + else + infer(sh_reverse_last_sig_coeff_flag, 0); + + if (pps->pps_slice_header_extension_present_flag) { + ue(sh_slice_header_extension_length, 0, 256); + for (i = 0; i < current->sh_slice_header_extension_length; i++) + us(8, sh_slice_header_extension_data_byte[i], 0x00, 0xff, 1, i); + } + + current->num_entry_points = 0; + if (sps->sps_entry_point_offsets_present_flag) { + uint8_t entropy_sync = sps->sps_entropy_coding_sync_enabled_flag; + int height; + if (pps->pps_rect_slice_flag) { + int width_in_tiles; + int slice_idx = current->sh_slice_address; + for (i = 0; i < curr_subpic_idx; i++) { + slice_idx += pps->num_slices_in_subpic[i]; + } + width_in_tiles = + pps->pps_slice_width_in_tiles_minus1[slice_idx] + 1; + + if (entropy_sync) + height = pps->slice_height_in_ctus[slice_idx]; + else + height = pps->pps_slice_height_in_tiles_minus1[slice_idx] + 1; + + current->num_entry_points = width_in_tiles * height; + } else { + int tile_idx; + int tile_y; + for (tile_idx = current->sh_slice_address; + tile_idx <= + current->sh_slice_address + + current->sh_num_tiles_in_slice_minus1; tile_idx++) { + tile_y = tile_idx / pps->num_tile_rows; + height = pps->row_height_val[tile_y]; + current->num_entry_points += (entropy_sync ? height : 1); + } + } + current->num_entry_points--; + if (current->num_entry_points > VVC_MAX_ENTRY_POINTS) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Too many entry points: " + "%" PRIu16 ".\n", current->num_entry_points); + return AVERROR_PATCHWELCOME; + } + if (current->num_entry_points > 0) { + ue(sh_entry_offset_len_minus1, 0, 31); + for (i = 0; i < current->num_entry_points; i++) { + ubs(current->sh_entry_offset_len_minus1 + 1, + sh_entry_point_offset_minus1[i], 1, i); + } + } + } + CHECK(FUNC(byte_alignment) (ctx, rw)); + + return 0; +} + +static int FUNC(sei_decoded_picture_hash) (CodedBitstreamContext *ctx, + RWContext *rw, + H266RawSEIDecodedPictureHash * + current) +{ + int err, c_idx, i; + + HEADER("Decoded Picture Hash"); + + u(8, dph_sei_hash_type, 0, 2); + flag(dph_sei_single_component_flag); + ub(7, dph_sei_reserved_zero_7bits); + + for (c_idx = 0; c_idx < (current->dph_sei_single_component_flag ? 1 : 3); + c_idx++) { + if (current->dph_sei_hash_type == 0) { + for (i = 0; i < 16; i++) + us(8, dph_sei_picture_md5[c_idx][i], 0x00, 0xff, 2, c_idx, i); + } else if (current->dph_sei_hash_type == 1) { + us(16, dph_sei_picture_crc[c_idx], 0x0000, 0xffff, 1, c_idx); + } else if (current->dph_sei_hash_type == 2) { + us(32, dph_sei_picture_checksum[c_idx], 0x00000000, 0xffffffff, 1, + c_idx); + } + } + return 0; +} + +static int FUNC(sei) (CodedBitstreamContext *ctx, RWContext *rw, + H266RawSEI *current, int prefix) +{ + int err; + + if (prefix) + HEADER("Prefix Supplemental Enhancement Information"); + else + HEADER("Suffix Supplemental Enhancement Information"); + + CHECK(FUNC(nal_unit_header) (ctx, rw, ¤t->nal_unit_header, + prefix ? VVC_PREFIX_SEI_NUT + : VVC_SUFFIX_SEI_NUT)); + + CHECK(FUNC_SEI(message_list) (ctx, rw, ¤t->message_list, prefix)); + + CHECK(FUNC(rbsp_trailing_bits) (ctx, rw)); + + return 0; +} diff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h index e585c779341..da84697a29f 100644 --- a/libavcodec/cbs_internal.h +++ b/libavcodec/cbs_internal.h @@ -133,6 +133,12 @@ typedef struct CodedBitstreamType { CodedBitstreamUnit *unit, PutBitContext *pbc); + // Return 1 when the unit should be dropped according to 'skip', + // 0 otherwise. + int (*discarded_unit)(CodedBitstreamContext *ctx, + const CodedBitstreamUnit *unit, + enum AVDiscard skip); + // Read the data from all of frag->units and assemble it into // a bitstream for the whole fragment. int (*assemble_fragment)(CodedBitstreamContext *ctx, @@ -157,18 +163,27 @@ void ff_cbs_trace_syntax_element(CodedBitstreamContext *ctx, int position, // Helper functions for read/write of common bitstream elements, including -// generation of trace output. +// generation of trace output. The simple functions are equivalent to +// their non-simple counterparts except that their range is unrestricted +// (i.e. only limited by the amount of bits used) and they lack +// the ability to use subscripts. int ff_cbs_read_unsigned(CodedBitstreamContext *ctx, GetBitContext *gbc, int width, const char *name, const int *subscripts, uint32_t *write_to, uint32_t range_min, uint32_t range_max); +int ff_cbs_read_simple_unsigned(CodedBitstreamContext *ctx, GetBitContext *gbc, + int width, const char *name, uint32_t *write_to); + int ff_cbs_write_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc, int width, const char *name, const int *subscripts, uint32_t value, uint32_t range_min, uint32_t range_max); +int ff_cbs_write_simple_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc, + int width, const char *name, uint32_t value); + int ff_cbs_read_signed(CodedBitstreamContext *ctx, GetBitContext *gbc, int width, const char *name, const int *subscripts, int32_t *write_to, @@ -245,6 +260,7 @@ int ff_cbs_write_signed(CodedBitstreamContext *ctx, PutBitContext *pbc, extern const CodedBitstreamType ff_cbs_type_av1; extern const CodedBitstreamType ff_cbs_type_h264; extern const CodedBitstreamType ff_cbs_type_h265; +extern const CodedBitstreamType ff_cbs_type_h266; extern const CodedBitstreamType ff_cbs_type_jpeg; extern const CodedBitstreamType ff_cbs_type_mpeg2; extern const CodedBitstreamType ff_cbs_type_vp9; diff --git a/libavcodec/cbs_mpeg2.c b/libavcodec/cbs_mpeg2.c index 04b0c7f87dd..37fc28a4e62 100644 --- a/libavcodec/cbs_mpeg2.c +++ b/libavcodec/cbs_mpeg2.c @@ -40,8 +40,6 @@ #define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]){ subs, __VA_ARGS__ }) : NULL) -#define ui(width, name) \ - xui(width, name, current->name, 0, MAX_UINT_BITS(width), 0, ) #define uir(width, name) \ xui(width, name, current->name, 1, MAX_UINT_BITS(width), 0, ) #define uis(width, name, subs, ...) \ @@ -65,6 +63,12 @@ #define READWRITE read #define RWContext GetBitContext +#define ui(width, name) do { \ + uint32_t value; \ + CHECK(ff_cbs_read_simple_unsigned(ctx, rw, width, #name, \ + &value)); \ + current->name = value; \ + } while (0) #define xuia(width, string, var, range_min, range_max, subs, ...) do { \ uint32_t value; \ CHECK(ff_cbs_read_unsigned(ctx, rw, width, string, \ @@ -95,6 +99,7 @@ #undef READ #undef READWRITE #undef RWContext +#undef ui #undef xuia #undef xsi #undef nextbits @@ -105,6 +110,11 @@ #define READWRITE write #define RWContext PutBitContext +#define ui(width, name) do { \ + CHECK(ff_cbs_write_simple_unsigned(ctx, rw, width, #name, \ + current->name)); \ + } while (0) + #define xuia(width, string, var, range_min, range_max, subs, ...) do { \ CHECK(ff_cbs_write_unsigned(ctx, rw, width, string, \ SUBSCRIPTS(subs, __VA_ARGS__), \ @@ -134,6 +144,7 @@ #undef WRITE #undef READWRITE #undef RWContext +#undef ui #undef xuia #undef xsi #undef nextbits diff --git a/libavcodec/cbs_sei.c b/libavcodec/cbs_sei.c index 50a513f592a..bd7f6f49382 100644 --- a/libavcodec/cbs_sei.c +++ b/libavcodec/cbs_sei.c @@ -20,6 +20,7 @@ #include "cbs_internal.h" #include "cbs_h264.h" #include "cbs_h265.h" +#include "cbs_h266.h" #include "cbs_sei.h" static void cbs_free_user_data_registered(void *opaque, uint8_t *data) @@ -132,6 +133,13 @@ static int cbs_sei_get_unit(CodedBitstreamContext *ctx, else sei_type = HEVC_NAL_SEI_SUFFIX; break; + case AV_CODEC_ID_H266: + highest_vcl_type = VVC_RSV_IRAP_11; + if (prefix) + sei_type = VVC_PREFIX_SEI_NUT; + else + sei_type = VVC_SUFFIX_SEI_NUT; + break; default: return AVERROR(EINVAL); } @@ -207,6 +215,18 @@ static int cbs_sei_get_unit(CodedBitstreamContext *ctx, memcpy(unit->content, &sei, sizeof(sei)); } break; + case AV_CODEC_ID_H266: + { + H266RawSEI sei = { + .nal_unit_header = { + .nal_unit_type = sei_type, + .nuh_layer_id = 0, + .nuh_temporal_id_plus1 = 1, + }, + }; + memcpy(unit->content, &sei, sizeof(sei)); + } + break; default: av_assert0(0); } @@ -237,6 +257,15 @@ static int cbs_sei_get_message_list(CodedBitstreamContext *ctx, *list = &sei->message_list; } break; + case AV_CODEC_ID_H266: + { + H266RawSEI *sei = unit->content; + if (unit->type != VVC_PREFIX_SEI_NUT && + unit->type != VVC_SUFFIX_SEI_NUT) + return AVERROR(EINVAL); + *list = &sei->message_list; + } + break; default: return AVERROR(EINVAL); } diff --git a/libavcodec/cbs_vp9.c b/libavcodec/cbs_vp9.c index 184fdcade6f..b0d5bd8763f 100644 --- a/libavcodec/cbs_vp9.c +++ b/libavcodec/cbs_vp9.c @@ -251,8 +251,6 @@ static int cbs_vp9_write_le(CodedBitstreamContext *ctx, PutBitContext *pbc, #define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]){ subs, __VA_ARGS__ }) : NULL) -#define f(width, name) \ - xf(width, name, current->name, 0, ) #define s(width, name) \ xs(width, name, current->name, 0, ) #define fs(width, name, subs, ...) \ @@ -264,6 +262,12 @@ static int cbs_vp9_write_le(CodedBitstreamContext *ctx, PutBitContext *pbc, #define READWRITE read #define RWContext GetBitContext +#define f(width, name) do { \ + uint32_t value; \ + CHECK(ff_cbs_read_simple_unsigned(ctx, rw, width, #name, \ + &value)); \ + current->name = value; \ + } while (0) #define xf(width, name, var, subs, ...) do { \ uint32_t value; \ CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \ @@ -329,6 +333,7 @@ static int cbs_vp9_write_le(CodedBitstreamContext *ctx, PutBitContext *pbc, #undef READ #undef READWRITE #undef RWContext +#undef f #undef xf #undef xs #undef increment @@ -344,6 +349,10 @@ static int cbs_vp9_write_le(CodedBitstreamContext *ctx, PutBitContext *pbc, #define READWRITE write #define RWContext PutBitContext +#define f(width, name) do { \ + CHECK(ff_cbs_write_simple_unsigned(ctx, rw, width, #name, \ + current->name)); \ + } while (0) #define xf(width, name, var, subs, ...) do { \ CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \ SUBSCRIPTS(subs, __VA_ARGS__), \ @@ -396,6 +405,7 @@ static int cbs_vp9_write_le(CodedBitstreamContext *ctx, PutBitContext *pbc, #undef WRITE #undef READWRITE #undef RWContext +#undef f #undef xf #undef xs #undef increment diff --git a/libavcodec/ccaption_dec.c b/libavcodec/ccaption_dec.c index 61eda9ff56f..f91c92b3733 100644 --- a/libavcodec/ccaption_dec.c +++ b/libavcodec/ccaption_dec.c @@ -262,7 +262,6 @@ typedef struct CCaptionSubContext { static av_cold int init_decoder(AVCodecContext *avctx) { - int ret; CCaptionSubContext *ctx = avctx->priv_data; av_bprint_init(&ctx->buffer[0], 0, AV_BPRINT_SIZE_UNLIMITED); @@ -272,7 +271,7 @@ static av_cold int init_decoder(AVCodecContext *avctx) ctx->bg_color = CCCOL_BLACK; ctx->rollup = 2; ctx->cursor_row = 10; - ret = ff_ass_subtitle_header(avctx, "Monospace", + return ff_ass_subtitle_header(avctx, "Monospace", ASS_DEFAULT_FONT_SIZE, ASS_DEFAULT_COLOR, ASS_DEFAULT_BACK_COLOR, @@ -281,11 +280,6 @@ static av_cold int init_decoder(AVCodecContext *avctx) ASS_DEFAULT_UNDERLINE, 3, ASS_DEFAULT_ALIGNMENT); - if (ret < 0) { - return ret; - } - - return ret; } static av_cold int close_decoder(AVCodecContext *avctx) @@ -900,6 +894,7 @@ static int decode(AVCodecContext *avctx, AVSubtitle *sub, ret = ff_ass_add_rect2(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL, &nb_rect_allocated); if (ret < 0) return ret; + av_bprint_clear(&ctx->buffer[bidx]); sub->pts = ctx->buffer_time[1]; sub->end_display_time = av_rescale_q(ctx->buffer_time[1] - ctx->buffer_time[0], AV_TIME_BASE_Q, ms_tb); @@ -922,7 +917,7 @@ static int decode(AVCodecContext *avctx, AVSubtitle *sub, } *got_sub = sub->num_rects > 0; - return ret; + return avpkt->size; } #define OFFSET(x) offsetof(CCaptionSubContext, x) diff --git a/libavcodec/cdgraphics.c b/libavcodec/cdgraphics.c index 51363b6be21..0c5022a5d68 100644 --- a/libavcodec/cdgraphics.c +++ b/libavcodec/cdgraphics.c @@ -125,7 +125,11 @@ static void cdg_load_palette(CDGraphicsContext *cc, uint8_t *data, int low) b = ((color ) & 0x000F) * 17; palette[i + array_offset] = (uint32_t)cc->alpha[i + array_offset] << 24 | r << 16 | g << 8 | b; } +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS cc->frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } static int cdg_tile_block(CDGraphicsContext *cc, uint8_t *data, int b) @@ -374,7 +378,7 @@ static void cdg_decode_flush(AVCodecContext *avctx) return; memset(cc->frame->data[0], 0, cc->frame->linesize[0] * avctx->height); - if (!avctx->frame_number) + if (!avctx->frame_num) memset(cc->frame->data[1], 0, AVPALETTE_SIZE); } diff --git a/libavcodec/cdtoons.c b/libavcodec/cdtoons.c index 3ebed2267cd..94c49f0c814 100644 --- a/libavcodec/cdtoons.c +++ b/libavcodec/cdtoons.c @@ -384,7 +384,11 @@ static int cdtoons_decode_frame(AVCodecContext *avctx, AVFrame *rframe, } /* first palette entry indicates transparency */ c->pal[0] = 0; +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS c->frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } } diff --git a/libavcodec/cdxl.c b/libavcodec/cdxl.c index 6b3b3e85e0f..885047af84f 100644 --- a/libavcodec/cdxl.c +++ b/libavcodec/cdxl.c @@ -305,7 +305,7 @@ static int cdxl_decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; if (encoding) { av_fast_padded_malloc(&c->new_video, &c->new_video_size, diff --git a/libavcodec/cfhd.c b/libavcodec/cfhd.c index c23eb069c65..42d7dcc3f67 100644 --- a/libavcodec/cfhd.c +++ b/libavcodec/cfhd.c @@ -1087,8 +1087,8 @@ static int cfhd_decode(AVCodecContext *avctx, AVFrame *pic, dst += dst_linesize; } } else { - av_log(avctx, AV_LOG_DEBUG, "interlaced frame ? %d", pic->interlaced_frame); - pic->interlaced_frame = 1; + av_log(avctx, AV_LOG_DEBUG, "interlaced frame ? %d", !!(pic->flags & AV_FRAME_FLAG_INTERLACED)); + pic->flags |= AV_FRAME_FLAG_INTERLACED; low = s->plane[plane].subband[0]; high = s->plane[plane].subband[7]; output = s->plane[plane].l_h[6]; @@ -1284,7 +1284,7 @@ static int cfhd_decode(AVCodecContext *avctx, AVFrame *pic, dst += dst_linesize; } } else { - pic->interlaced_frame = 1; + pic->flags |= AV_FRAME_FLAG_INTERLACED; low = s->plane[plane].l_h[7]; high = s->plane[plane].subband[14]; output = s->plane[plane].l_h[6]; diff --git a/libavcodec/cfhdenc.c b/libavcodec/cfhdenc.c index 29fb56f25ad..f447438491d 100644 --- a/libavcodec/cfhdenc.c +++ b/libavcodec/cfhdenc.c @@ -859,7 +859,8 @@ const FFCodec ff_cfhd_encoder = { CODEC_LONG_NAME("GoPro CineForm HD"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_CFHD, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(CFHDEncContext), .p.priv_class = &cfhd_class, .init = cfhd_encode_init, diff --git a/libavcodec/cinepak.c b/libavcodec/cinepak.c index 282614fd1db..2ec0ce88829 100644 --- a/libavcodec/cinepak.c +++ b/libavcodec/cinepak.c @@ -379,7 +379,7 @@ static int cinepak_decode (CinepakContext *s) num_strips = FFMIN(num_strips, MAX_STRIPS); - s->frame->key_frame = 0; + s->frame->flags &= ~AV_FRAME_FLAG_KEY; for (i=0; i < num_strips; i++) { if ((s->data + 12) > eod) @@ -395,7 +395,7 @@ static int cinepak_decode (CinepakContext *s) s->strips[i].x2 = AV_RB16 (&s->data[10]); if (s->strips[i].id == 0x10) - s->frame->key_frame = 1; + s->frame->flags |= AV_FRAME_FLAG_KEY; strip_size = AV_RB24 (&s->data[1]) - 12; if (strip_size < 0) @@ -476,7 +476,14 @@ static int cinepak_decode_frame(AVCodecContext *avctx, AVFrame *rframe, return ret; if (s->palette_video) { - s->frame->palette_has_changed = ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS + s->frame->palette_has_changed = +#endif + ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_ENABLE_DEPRECATION_WARNINGS +#endif } if ((ret = cinepak_decode(s)) < 0) { diff --git a/libavcodec/cinepakenc.c b/libavcodec/cinepakenc.c index c05449e89c8..f15325ad0d0 100644 --- a/libavcodec/cinepakenc.c +++ b/libavcodec/cinepakenc.c @@ -1219,7 +1219,7 @@ const FFCodec ff_cinepak_encoder = { CODEC_LONG_NAME("Cinepak"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_CINEPAK, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(CinepakEncContext), .init = cinepak_encode_init, FF_CODEC_ENCODE_CB(cinepak_encode_frame), diff --git a/libavcodec/clearvideo.c b/libavcodec/clearvideo.c index e77661d187b..f84e3e6ea4f 100644 --- a/libavcodec/clearvideo.c +++ b/libavcodec/clearvideo.c @@ -511,7 +511,7 @@ static int clv_decode_frame(AVCodecContext *avctx, AVFrame *rframe, if ((ret = ff_reget_buffer(avctx, c->pic, 0)) < 0) return ret; - c->pic->key_frame = 1; + c->pic->flags |= AV_FRAME_FLAG_KEY; c->pic->pict_type = AV_PICTURE_TYPE_I; bytestream2_get_be32(&gb); // frame size; @@ -605,7 +605,7 @@ static int clv_decode_frame(AVCodecContext *avctx, AVFrame *rframe, } extend_edges(c->pic, c->tile_size); - c->pic->key_frame = 0; + c->pic->flags &= ~AV_FRAME_FLAG_KEY; c->pic->pict_type = AV_PICTURE_TYPE_P; } diff --git a/libavcodec/cljrdec.c b/libavcodec/cljrdec.c index 914f853c8fd..a4baa015f68 100644 --- a/libavcodec/cljrdec.c +++ b/libavcodec/cljrdec.c @@ -51,7 +51,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; init_get_bits(&gb, buf, buf_size * 8); diff --git a/libavcodec/cljrenc.c b/libavcodec/cljrenc.c index c1f8810a5a1..31ad5ce0cfe 100644 --- a/libavcodec/cljrenc.c +++ b/libavcodec/cljrenc.c @@ -42,7 +42,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, CLJRContext *a = avctx->priv_data; PutBitContext pb; int x, y, ret; - uint32_t dither= avctx->frame_number; + uint32_t dither= avctx->frame_num; static const uint32_t ordered_dither[2][2] = { { 0x10400000, 0x104F0000 }, @@ -113,7 +113,7 @@ const FFCodec ff_cljr_encoder = { CODEC_LONG_NAME("Cirrus Logic AccuPak"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_CLJR, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(CLJRContext), FF_CODEC_ENCODE_CB(encode_frame), .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV411P, diff --git a/libavcodec/cllc.c b/libavcodec/cllc.c index 911717b68db..52cb86e50b4 100644 --- a/libavcodec/cllc.c +++ b/libavcodec/cllc.c @@ -460,7 +460,7 @@ static int cllc_decode_frame(AVCodecContext *avctx, AVFrame *pic, return AVERROR_INVALIDDATA; } - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; pic->pict_type = AV_PICTURE_TYPE_I; *got_picture_ptr = 1; diff --git a/libavcodec/cngenc.c b/libavcodec/cngenc.c index ff40017f0dc..596d6f8c2ae 100644 --- a/libavcodec/cngenc.c +++ b/libavcodec/cngenc.c @@ -101,7 +101,7 @@ const FFCodec ff_comfortnoise_encoder = { CODEC_LONG_NAME("RFC 3389 comfort noise generator"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_COMFORT_NOISE, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(CNGContext), .init = cng_encode_init, FF_CODEC_ENCODE_CB(cng_encode_frame), diff --git a/libavcodec/codec.h b/libavcodec/codec.h index 77a1a3f5a29..7a932d75dde 100644 --- a/libavcodec/codec.h +++ b/libavcodec/codec.h @@ -50,12 +50,6 @@ * avcodec_default_get_buffer2 or avcodec_default_get_encode_buffer. */ #define AV_CODEC_CAP_DR1 (1 << 1) -#if FF_API_FLAG_TRUNCATED -/** - * @deprecated Use parsers to always send proper frames. - */ -#define AV_CODEC_CAP_TRUNCATED (1 << 3) -#endif /** * Encoder or decoder requires flushing with NULL input at the end in order to * give the complete and correct output. @@ -86,6 +80,7 @@ */ #define AV_CODEC_CAP_SMALL_LAST_FRAME (1 << 6) +#if FF_API_SUBFRAMES /** * Codec can output multiple frames per AVPacket * Normally demuxers return one frame at a time, demuxers which do not do @@ -98,6 +93,8 @@ * as a last resort. */ #define AV_CODEC_CAP_SUBFRAMES (1 << 8) +#endif + /** * Codec is experimental and is thus avoided in favor of non experimental * encoders @@ -125,9 +122,6 @@ * multithreading-capable external libraries. */ #define AV_CODEC_CAP_OTHER_THREADS (1 << 15) -#if FF_API_AUTO_THREADS -#define AV_CODEC_CAP_AUTO_THREADS AV_CODEC_CAP_OTHER_THREADS -#endif /** * Audio encoder supports receiving a different number of samples in each call. */ @@ -143,17 +137,6 @@ */ #define AV_CODEC_CAP_AVOID_PROBING (1 << 17) -#if FF_API_UNUSED_CODEC_CAPS -/** - * Deprecated and unused. Use AVCodecDescriptor.props instead - */ -#define AV_CODEC_CAP_INTRA_ONLY 0x40000000 -/** - * Deprecated and unused. Use AVCodecDescriptor.props instead - */ -#define AV_CODEC_CAP_LOSSLESS 0x80000000 -#endif - /** * Codec is backed by a hardware implementation. Typically used to * identify a non-hwaccel hardware decoder. For information about hwaccels, use @@ -169,9 +152,9 @@ #define AV_CODEC_CAP_HYBRID (1 << 19) /** - * This codec takes the reordered_opaque field from input AVFrames - * and returns it in the corresponding field in AVCodecContext after - * encoding. + * This encoder can reorder user opaque values from input AVFrames and return + * them with corresponding output packets. + * @see AV_CODEC_FLAG_COPY_OPAQUE */ #define AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE (1 << 20) diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index 24a0433dbac..4406dd8318f 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -38,14 +38,20 @@ static const AVCodecDescriptor codec_descriptors[] = { .type = AVMEDIA_TYPE_VIDEO, .name = "mpeg1video", .long_name = NULL_IF_CONFIG_SMALL("MPEG-1 video"), - .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_REORDER, + .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_REORDER | + // FIXME this is strigly speaking not true, as MPEG-1 does + // not allow field coding, but our mpeg12 code (decoder and + // parser) can sometimes change codec id at runtime, so + // this is safer + AV_CODEC_PROP_FIELDS, }, { .id = AV_CODEC_ID_MPEG2VIDEO, .type = AVMEDIA_TYPE_VIDEO, .name = "mpeg2video", .long_name = NULL_IF_CONFIG_SMALL("MPEG-2 video"), - .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_REORDER, + .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_REORDER | + AV_CODEC_PROP_FIELDS, .profiles = NULL_IF_CONFIG_SMALL(ff_mpeg2_video_profiles), }, { @@ -225,7 +231,8 @@ static const AVCodecDescriptor codec_descriptors[] = { .type = AVMEDIA_TYPE_VIDEO, .name = "h264", .long_name = NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"), - .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_LOSSLESS | AV_CODEC_PROP_REORDER, + .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_LOSSLESS | + AV_CODEC_PROP_REORDER | AV_CODEC_PROP_FIELDS, .profiles = NULL_IF_CONFIG_SMALL(ff_h264_profiles), }, { @@ -529,7 +536,8 @@ static const AVCodecDescriptor codec_descriptors[] = { .type = AVMEDIA_TYPE_VIDEO, .name = "vc1", .long_name = NULL_IF_CONFIG_SMALL("SMPTE VC-1"), - .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_REORDER, + .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_REORDER | + AV_CODEC_PROP_FIELDS, .profiles = NULL_IF_CONFIG_SMALL(ff_vc1_profiles), }, { @@ -1923,6 +1931,35 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("ViewQuest VQC"), .props = AV_CODEC_PROP_LOSSY, }, + { + .id = AV_CODEC_ID_PDV, + .type = AVMEDIA_TYPE_VIDEO, + .name = "pdv", + .long_name = NULL_IF_CONFIG_SMALL("PDV (PlayDate Video)"), + .props = AV_CODEC_PROP_LOSSY, + }, + { + .id = AV_CODEC_ID_EVC, + .type = AVMEDIA_TYPE_VIDEO, + .name = "evc", + .long_name = NULL_IF_CONFIG_SMALL("MPEG-5 EVC (Essential Video Coding)"), + .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_REORDER, + .profiles = NULL_IF_CONFIG_SMALL(ff_evc_profiles), + }, + { + .id = AV_CODEC_ID_RTV1, + .type = AVMEDIA_TYPE_VIDEO, + .name = "rtv1", + .long_name = NULL_IF_CONFIG_SMALL("RTV1 (RivaTuner Video)"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, + { + .id = AV_CODEC_ID_VMIX, + .type = AVMEDIA_TYPE_VIDEO, + .name = "vmix", + .long_name = NULL_IF_CONFIG_SMALL("vMix Video"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, /* various PCM "codecs" */ { @@ -2536,6 +2573,13 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA Acorn Replay"), .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, + { + .id = AV_CODEC_ID_ADPCM_XMD, + .type = AVMEDIA_TYPE_AUDIO, + .name = "adpcm_xmd", + .long_name = NULL_IF_CONFIG_SMALL("ADPCM Konami XMD"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, /* AMR */ { @@ -2619,6 +2663,20 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("DPCM Xilam DERF"), .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, + { + .id = AV_CODEC_ID_WADY_DPCM, + .type = AVMEDIA_TYPE_AUDIO, + .name = "wady_dpcm", + .long_name = NULL_IF_CONFIG_SMALL("DPCM Marble WADY"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, + { + .id = AV_CODEC_ID_CBD2_DPCM, + .type = AVMEDIA_TYPE_AUDIO, + .name = "cbd2_dpcm", + .long_name = NULL_IF_CONFIG_SMALL("DPCM Cuberoot-Delta-Exact"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, /* audio codecs */ { @@ -2910,6 +2968,7 @@ static const AVCodecDescriptor codec_descriptors[] = { .name = "eac3", .long_name = NULL_IF_CONFIG_SMALL("ATSC A/52B (AC-3, E-AC-3)"), .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + .profiles = NULL_IF_CONFIG_SMALL(ff_eac3_profiles), }, { .id = AV_CODEC_ID_SIPR, @@ -2938,6 +2997,7 @@ static const AVCodecDescriptor codec_descriptors[] = { .name = "truehd", .long_name = NULL_IF_CONFIG_SMALL("TrueHD"), .props = AV_CODEC_PROP_LOSSLESS, + .profiles = NULL_IF_CONFIG_SMALL(ff_truehd_profiles), }, { .id = AV_CODEC_ID_MP4ALS, @@ -3332,6 +3392,27 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("FTR Voice"), .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, + { + .id = AV_CODEC_ID_WAVARC, + .type = AVMEDIA_TYPE_AUDIO, + .name = "wavarc", + .long_name = NULL_IF_CONFIG_SMALL("Waveform Archiver"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, + }, + { + .id = AV_CODEC_ID_RKA, + .type = AVMEDIA_TYPE_AUDIO, + .name = "rka", + .long_name = NULL_IF_CONFIG_SMALL("RKA (RK Audio)"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_LOSSLESS, + }, + { + .id = AV_CODEC_ID_AC4, + .type = AVMEDIA_TYPE_AUDIO, + .name = "ac4", + .long_name = NULL_IF_CONFIG_SMALL("AC-4"), + .props = AV_CODEC_PROP_LOSSY, + }, /* subtitle codecs */ { @@ -3513,7 +3594,6 @@ static const AVCodecDescriptor codec_descriptors[] = { .type = AVMEDIA_TYPE_SUBTITLE, .name = "arib_caption", .long_name = NULL_IF_CONFIG_SMALL("ARIB STD-B24 caption"), - .props = AV_CODEC_PROP_TEXT_SUB, .profiles = NULL_IF_CONFIG_SMALL(ff_arib_caption_profiles), }, @@ -3590,6 +3670,12 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("binary data"), .mime_types= MT("application/octet-stream"), }, + { + .id = AV_CODEC_ID_SMPTE_2038, + .type = AVMEDIA_TYPE_DATA, + .name = "smpte_2038", + .long_name = NULL_IF_CONFIG_SMALL("SMPTE ST 2038 VANC in MPEG-2 TS"), + }, { .id = AV_CODEC_ID_MPEG2TS, .type = AVMEDIA_TYPE_DATA, @@ -3604,6 +3690,18 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("AVFrame to AVPacket passthrough"), .props = AV_CODEC_PROP_LOSSLESS, }, + { + .id = AV_CODEC_ID_VNULL, + .type = AVMEDIA_TYPE_VIDEO, + .name = "vnull", + .long_name = NULL_IF_CONFIG_SMALL("Null video codec"), + }, + { + .id = AV_CODEC_ID_ANULL, + .type = AVMEDIA_TYPE_AUDIO, + .name = "anull", + .long_name = NULL_IF_CONFIG_SMALL("Null audio codec"), + }, }; static int descriptor_compare(const void *key, const void *member) diff --git a/libavcodec/codec_desc.h b/libavcodec/codec_desc.h index 126b52df476..dd4491112b2 100644 --- a/libavcodec/codec_desc.h +++ b/libavcodec/codec_desc.h @@ -90,6 +90,12 @@ typedef struct AVCodecDescriptor { * equal. */ #define AV_CODEC_PROP_REORDER (1 << 3) + +/** + * Video codec supports separate coding of fields in interlaced frames. + */ +#define AV_CODEC_PROP_FIELDS (1 << 4) + /** * Subtitle codec is bitmap based * Decoded AVSubtitle data can be read from the AVSubtitleRect->pict field. diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h index f436a2b624f..a5a0cb85255 100644 --- a/libavcodec/codec_id.h +++ b/libavcodec/codec_id.h @@ -320,6 +320,10 @@ enum AVCodecID { AV_CODEC_ID_WBMP, AV_CODEC_ID_MEDIA100, AV_CODEC_ID_VQC, + AV_CODEC_ID_PDV, + AV_CODEC_ID_EVC, + AV_CODEC_ID_RTV1, + AV_CODEC_ID_VMIX, /* various PCM "codecs" */ AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs @@ -413,6 +417,7 @@ enum AVCodecID { AV_CODEC_ID_ADPCM_IMA_CUNNING, AV_CODEC_ID_ADPCM_IMA_MOFLEX, AV_CODEC_ID_ADPCM_IMA_ACORN, + AV_CODEC_ID_ADPCM_XMD, /* AMR */ AV_CODEC_ID_AMR_NB = 0x12000, @@ -430,6 +435,8 @@ enum AVCodecID { AV_CODEC_ID_SDX2_DPCM, AV_CODEC_ID_GREMLIN_DPCM, AV_CODEC_ID_DERF_DPCM, + AV_CODEC_ID_WADY_DPCM, + AV_CODEC_ID_CBD2_DPCM, /* audio codecs */ AV_CODEC_ID_MP2 = 0x15000, @@ -533,6 +540,9 @@ enum AVCodecID { AV_CODEC_ID_MISC4, AV_CODEC_ID_APAC, AV_CODEC_ID_FTR, + AV_CODEC_ID_WAVARC, + AV_CODEC_ID_RKA, + AV_CODEC_ID_AC4, /* subtitle codecs */ AV_CODEC_ID_FIRST_SUBTITLE = 0x17000, ///< A dummy ID pointing at the start of subtitle codecs. @@ -577,6 +587,7 @@ enum AVCodecID { AV_CODEC_ID_DVD_NAV, AV_CODEC_ID_TIMED_ID3, AV_CODEC_ID_BIN_DATA, + AV_CODEC_ID_SMPTE_2038, AV_CODEC_ID_PROBE = 0x19000, ///< codec_id is not known (like AV_CODEC_ID_NONE) but lavf should attempt to identify it @@ -587,6 +598,16 @@ enum AVCodecID { * stream (only used by libavformat) */ AV_CODEC_ID_FFMETADATA = 0x21000, ///< Dummy codec for streams containing only metadata information. AV_CODEC_ID_WRAPPED_AVFRAME = 0x21001, ///< Passthrough codec, AVFrames wrapped in AVPacket + /** + * Dummy null video codec, useful mainly for development and debugging. + * Null encoder/decoder discard all input and never return any output. + */ + AV_CODEC_ID_VNULL, + /** + * Dummy null audio codec, useful mainly for development and debugging. + * Null encoder/decoder discard all input and never return any output. + */ + AV_CODEC_ID_ANULL, }; /** diff --git a/libavcodec/codec_internal.h b/libavcodec/codec_internal.h index e3b77e6deaf..130a7dc3cd7 100644 --- a/libavcodec/codec_internal.h +++ b/libavcodec/codec_internal.h @@ -80,6 +80,14 @@ * Codec supports embedded ICC profiles (AV_FRAME_DATA_ICC_PROFILE). */ #define FF_CODEC_CAP_ICC_PROFILES (1 << 9) +/** + * The encoder has AV_CODEC_CAP_DELAY set, but does not actually have delay - it + * only wants to be flushed at the end to update some context variables (e.g. + * 2pass stats) or produce a trailing packet. Besides that it immediately + * produces exactly one output packet per each input frame, just as no-delay + * encoders do. + */ +#define FF_CODEC_CAP_EOF_FLUSH (1 << 10) /** * FFCodec.codec_tags termination value diff --git a/libavcodec/codec_par.c b/libavcodec/codec_par.c index abda649aa88..a38a475dc73 100644 --- a/libavcodec/codec_par.c +++ b/libavcodec/codec_par.c @@ -46,6 +46,7 @@ static void codec_parameters_reset(AVCodecParameters *par) par->color_space = AVCOL_SPC_UNSPECIFIED; par->chroma_location = AVCHROMA_LOC_UNSPECIFIED; par->sample_aspect_ratio = (AVRational){ 0, 1 }; + par->framerate = (AVRational){ 0, 1 }; par->profile = FF_PROFILE_UNKNOWN; par->level = FF_LEVEL_UNKNOWN; } @@ -126,6 +127,7 @@ int avcodec_parameters_from_context(AVCodecParameters *par, par->chroma_location = codec->chroma_sample_location; par->sample_aspect_ratio = codec->sample_aspect_ratio; par->video_delay = codec->has_b_frames; + par->framerate = codec->framerate; break; case AVMEDIA_TYPE_AUDIO: par->format = codec->sample_fmt; @@ -207,6 +209,7 @@ int avcodec_parameters_to_context(AVCodecContext *codec, codec->chroma_sample_location = par->chroma_location; codec->sample_aspect_ratio = par->sample_aspect_ratio; codec->has_b_frames = par->video_delay; + codec->framerate = par->framerate; break; case AVMEDIA_TYPE_AUDIO: codec->sample_fmt = par->format; @@ -250,8 +253,8 @@ FF_ENABLE_DEPRECATION_WARNINGS break; } + av_freep(&codec->extradata); if (par->extradata) { - av_freep(&codec->extradata); codec->extradata = av_mallocz(par->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); if (!codec->extradata) return AVERROR(ENOMEM); diff --git a/libavcodec/codec_par.h b/libavcodec/codec_par.h index f51d27c5908..add90fdb1e6 100644 --- a/libavcodec/codec_par.h +++ b/libavcodec/codec_par.h @@ -211,6 +211,18 @@ typedef struct AVCodecParameters { * Audio only. The channel layout and number of channels. */ AVChannelLayout ch_layout; + + /** + * Video only. Number of frames per second, for streams with constant frame + * durations. Should be set to { 0, 1 } when some frames have differing + * durations or if the value is not known. + * + * @note This field correponds to values that are stored in codec-level + * headers and is typically overridden by container/transport-layer + * timestamps, when available. It should thus be used only as a last resort, + * when no higher-level timing information is available. + */ + AVRational framerate; } AVCodecParameters; /** diff --git a/libavcodec/cpia.c b/libavcodec/cpia.c index 99362e73f07..f62100c4193 100644 --- a/libavcodec/cpia.c +++ b/libavcodec/cpia.c @@ -94,10 +94,10 @@ static int cpia_decode_frame(AVCodecContext *avctx, AVFrame *rframe, if (header[28] == NOT_COMPRESSED) { frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; } else { frame->pict_type = AV_PICTURE_TYPE_P; - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; } // Get buffer filled with previous frame @@ -198,14 +198,6 @@ static av_cold int cpia_decode_init(AVCodecContext *avctx) // output pixel format avctx->pix_fmt = AV_PIX_FMT_YUV420P; - /* The default timebase set by the v4l2 demuxer leads to probing which is buggy. - * Set some reasonable time_base to skip this. - */ - if (avctx->time_base.num == 1 && avctx->time_base.den == 1000000) { - avctx->time_base.num = 1; - avctx->time_base.den = 60; - } - s->frame = av_frame_alloc(); if (!s->frame) return AVERROR(ENOMEM); diff --git a/libavcodec/cri.c b/libavcodec/cri.c index 5761152c2d5..0380a0c665b 100644 --- a/libavcodec/cri.c +++ b/libavcodec/cri.c @@ -408,7 +408,7 @@ static int cri_decode_frame(AVCodecContext *avctx, AVFrame *p, } p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; diff --git a/libavcodec/crystalhd.c b/libavcodec/crystalhd.c index 8673a491d42..86f6cfa6c15 100644 --- a/libavcodec/crystalhd.c +++ b/libavcodec/crystalhd.c @@ -329,6 +329,10 @@ static av_cold int init(AVCodecContext *avctx) av_log(avctx, AV_LOG_VERBOSE, "CrystalHD Init for %s\n", avctx->codec->name); + av_log(avctx, AV_LOG_WARNING, "CrystalHD support is deprecated and will " + "be removed. Please contact the developers if you are interested in " + "maintaining it.\n"); + avctx->pix_fmt = AV_PIX_FMT_YUYV422; /* Initialize the library */ @@ -539,15 +543,19 @@ static inline CopyRet copy_frame(AVCodecContext *avctx, av_image_copy_plane(dst, dStride, src, sStride, bwidth, height); } - frame->interlaced_frame = interlaced; + frame->flags |= AV_FRAME_FLAG_INTERLACED * !!interlaced; if (interlaced) - frame->top_field_first = !bottom_first; + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST * !bottom_first; frame->pts = pkt_pts; - frame->pkt_pos = -1; frame->duration = 0; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS + frame->pkt_pos = -1; frame->pkt_size = -1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif if (!priv->need_second_field) { *got_frame = 1; diff --git a/libavcodec/cscd.c b/libavcodec/cscd.c index b4ed3332a96..23dd2df99e8 100644 --- a/libavcodec/cscd.c +++ b/libavcodec/cscd.c @@ -110,12 +110,12 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, // flip upside down, add difference frame if (buf[0] & 1) { // keyframe c->pic->pict_type = AV_PICTURE_TYPE_I; - c->pic->key_frame = 1; + c->pic->flags |= AV_FRAME_FLAG_KEY; copy_frame_default(c->pic, c->decomp_buf, c->linelen, c->height); } else { c->pic->pict_type = AV_PICTURE_TYPE_P; - c->pic->key_frame = 0; + c->pic->flags &= ~AV_FRAME_FLAG_KEY; add_frame_default(c->pic, c->decomp_buf, c->linelen, c->height); } diff --git a/libavcodec/cuviddec.c b/libavcodec/cuviddec.c index 4ba7918b644..814bc53f70f 100644 --- a/libavcodec/cuviddec.c +++ b/libavcodec/cuviddec.c @@ -115,6 +115,12 @@ typedef struct CuvidParsedFrame #define CHECK_CU(x) FF_CUDA_CHECK_DL(avctx, ctx->cudl, x) +// NV recommends [2;4] range +#define CUVID_MAX_DISPLAY_DELAY (4) + +// Actual pool size will be determined by parser. +#define CUVID_DEFAULT_NUM_SURFACES (CUVID_MAX_DISPLAY_DELAY + 1) + static int CUDAAPI cuvid_handle_video_sequence(void *opaque, CUVIDEOFORMAT* format) { AVCodecContext *avctx = opaque; @@ -124,6 +130,7 @@ static int CUDAAPI cuvid_handle_video_sequence(void *opaque, CUVIDEOFORMAT* form CUVIDDECODECREATEINFO cuinfo; int surface_fmt; int chroma_444; + int fifo_size_inc; int old_width = avctx->width; int old_height = avctx->height; @@ -309,6 +316,25 @@ static int CUDAAPI cuvid_handle_video_sequence(void *opaque, CUVIDEOFORMAT* form return 0; } + fifo_size_inc = ctx->nb_surfaces; + ctx->nb_surfaces = FFMAX(ctx->nb_surfaces, format->min_num_decode_surfaces + 3); + + if (avctx->extra_hw_frames > 0) + ctx->nb_surfaces += avctx->extra_hw_frames; + + fifo_size_inc = ctx->nb_surfaces - fifo_size_inc; + if (fifo_size_inc > 0 && av_fifo_grow2(ctx->frame_queue, fifo_size_inc) < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to grow frame queue on video sequence callback\n"); + ctx->internal_error = AVERROR(ENOMEM); + return 0; + } + + if (fifo_size_inc > 0 && av_reallocp_array(&ctx->key_frame, ctx->nb_surfaces, sizeof(int)) < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to grow key frame array on video sequence callback\n"); + ctx->internal_error = AVERROR(ENOMEM); + return 0; + } + cuinfo.ulNumDecodeSurfaces = ctx->nb_surfaces; cuinfo.ulNumOutputSurfaces = 1; cuinfo.ulCreationFlags = cudaVideoCreate_PreferCUVID; @@ -599,7 +625,10 @@ static int cuvid_output_frame(AVCodecContext *avctx, AVFrame *frame) goto error; } - frame->key_frame = ctx->key_frame[parsed_frame.dispinfo.picture_index]; + if (ctx->key_frame[parsed_frame.dispinfo.picture_index]) + frame->flags |= AV_FRAME_FLAG_KEY; + else + frame->flags &= ~AV_FRAME_FLAG_KEY; ctx->key_frame[parsed_frame.dispinfo.picture_index] = 0; frame->width = avctx->width; @@ -623,14 +652,19 @@ static int cuvid_output_frame(AVCodecContext *avctx, AVFrame *frame) /* CUVIDs opaque reordering breaks the internal pkt logic. * So set pkt_pts and clear all the other pkt_ fields. */ - frame->pkt_pos = -1; frame->duration = 0; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS + frame->pkt_pos = -1; frame->pkt_size = -1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif - frame->interlaced_frame = !parsed_frame.is_deinterlacing && !parsed_frame.dispinfo.progressive_frame; + if (!parsed_frame.is_deinterlacing && !parsed_frame.dispinfo.progressive_frame) + frame->flags |= AV_FRAME_FLAG_INTERLACED; - if (frame->interlaced_frame) - frame->top_field_first = parsed_frame.dispinfo.top_field_first; + if ((frame->flags & AV_FRAME_FLAG_INTERLACED) && parsed_frame.dispinfo.top_field_first) + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; } else if (ctx->decoder_flushing) { ret = AVERROR_EOF; } else { @@ -838,6 +872,10 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx) goto error; } + // respect the deprecated "surfaces" option if non-default value is given by user; + if(ctx->nb_surfaces < 0) + ctx->nb_surfaces = CUVID_DEFAULT_NUM_SURFACES; + ctx->frame_queue = av_fifo_alloc2(ctx->nb_surfaces, sizeof(CuvidParsedFrame), 0); if (!ctx->frame_queue) { ret = AVERROR(ENOMEM); @@ -985,7 +1023,7 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx) } ctx->cuparseinfo.ulMaxNumDecodeSurfaces = ctx->nb_surfaces; - ctx->cuparseinfo.ulMaxDisplayDelay = (avctx->flags & AV_CODEC_FLAG_LOW_DELAY) ? 0 : 4; + ctx->cuparseinfo.ulMaxDisplayDelay = (avctx->flags & AV_CODEC_FLAG_LOW_DELAY) ? 0 : CUVID_MAX_DISPLAY_DELAY; ctx->cuparseinfo.pUserData = avctx; ctx->cuparseinfo.pfnSequenceCallback = cuvid_handle_video_sequence; ctx->cuparseinfo.pfnDecodePicture = cuvid_handle_picture_decode; @@ -1089,7 +1127,7 @@ static const AVOption options[] = { { "bob", "Bob deinterlacing", 0, AV_OPT_TYPE_CONST, { .i64 = cudaVideoDeinterlaceMode_Bob }, 0, 0, VD, "deint" }, { "adaptive", "Adaptive deinterlacing", 0, AV_OPT_TYPE_CONST, { .i64 = cudaVideoDeinterlaceMode_Adaptive }, 0, 0, VD, "deint" }, { "gpu", "GPU to be used for decoding", OFFSET(cu_gpu), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VD }, - { "surfaces", "Maximum surfaces to be used for decoding", OFFSET(nb_surfaces), AV_OPT_TYPE_INT, { .i64 = 25 }, 0, INT_MAX, VD }, + { "surfaces", "Maximum surfaces to be used for decoding", OFFSET(nb_surfaces), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VD | AV_OPT_FLAG_DEPRECATED }, { "drop_second_field", "Drop second field when deinterlacing", OFFSET(drop_second_field), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VD }, { "crop", "Crop (top)x(bottom)x(left)x(right)", OFFSET(crop_expr), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VD }, { "resize", "Resize (width)x(height)", OFFSET(resize_expr), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VD }, diff --git a/libavcodec/dca_syncwords.h b/libavcodec/dca_syncwords.h index 4d2cd5f56d5..649bbd90dcf 100644 --- a/libavcodec/dca_syncwords.h +++ b/libavcodec/dca_syncwords.h @@ -33,4 +33,7 @@ #define DCA_SYNCWORD_SUBSTREAM_CORE 0x02B09261U #define DCA_SYNCWORD_REV1AUX 0x9A1105A0U +#define DCA_SYNCWORD_XLL_X 0x02000850U +#define DCA_SYNCWORD_XLL_X_IMAX 0xF14000D0U + #endif /* AVCODEC_DCA_SYNCWORDS_H */ diff --git a/libavcodec/dca_xll.c b/libavcodec/dca_xll.c index fe2c766d981..b8cf37a35f1 100644 --- a/libavcodec/dca_xll.c +++ b/libavcodec/dca_xll.c @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "avcodec.h" #include "libavutil/channel_layout.h" #include "dcadec.h" #include "dcadata.h" @@ -1054,6 +1055,22 @@ static int parse_frame(DCAXllDecoder *s, const uint8_t *data, int size, DCAExssA return ret; if ((ret = parse_band_data(s)) < 0) return ret; + + if (s->frame_size * 8 > FFALIGN(get_bits_count(&s->gb), 32)) { + unsigned int extradata_syncword; + + // Align to dword + skip_bits_long(&s->gb, -get_bits_count(&s->gb) & 31); + + extradata_syncword = show_bits_long(&s->gb, 32); + + if (extradata_syncword == DCA_SYNCWORD_XLL_X) { + s->x_syncword_present = 1; + } else if ((extradata_syncword >> 1) == (DCA_SYNCWORD_XLL_X_IMAX >> 1)) { + s->x_imax_syncword_present = 1; + } + } + if (ff_dca_seek_bits(&s->gb, s->frame_size * 8)) { av_log(s->avctx, AV_LOG_ERROR, "Read past end of XLL frame\n"); return AVERROR_INVALIDDATA; @@ -1428,8 +1445,15 @@ int ff_dca_xll_filter_frame(DCAXllDecoder *s, AVFrame *frame) return AVERROR(EINVAL); } + if (s->x_imax_syncword_present) { + avctx->profile = FF_PROFILE_DTS_HD_MA_X_IMAX; + } else if (s->x_syncword_present) { + avctx->profile = FF_PROFILE_DTS_HD_MA_X; + } else { + avctx->profile = FF_PROFILE_DTS_HD_MA; + } + avctx->bits_per_raw_sample = p->storage_bit_res; - avctx->profile = FF_PROFILE_DTS_HD_MA; avctx->bit_rate = 0; frame->nb_samples = nsamples = s->nframesamples << (s->nfreqbands - 1); diff --git a/libavcodec/dca_xll.h b/libavcodec/dca_xll.h index d7c1a13ec86..a22bbb8d770 100644 --- a/libavcodec/dca_xll.h +++ b/libavcodec/dca_xll.h @@ -135,6 +135,9 @@ typedef struct DCAXllDecoder { DCADSPContext *dcadsp; + int x_syncword_present; ///< Syncword for extension data at end of frame (DTS:X) is present + int x_imax_syncword_present; ///< Syncword for extension data at end of frame (DTS:X IMAX) is present + int output_mask; int32_t *output_samples[DCA_SPEAKER_COUNT]; } DCAXllDecoder; diff --git a/libavcodec/dcaenc.c b/libavcodec/dcaenc.c index 4cab54ef1be..c731d79381c 100644 --- a/libavcodec/dcaenc.c +++ b/libavcodec/dcaenc.c @@ -1315,7 +1315,8 @@ const FFCodec ff_dca_encoder = { CODEC_LONG_NAME("DCA (DTS Coherent Acoustics)"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_DTS, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_EXPERIMENTAL, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_EXPERIMENTAL | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(DCAEncContext), .init = encode_init, .close = encode_close, diff --git a/libavcodec/dct.h b/libavcodec/dct.h index 0a03e256d13..75f40b9f84c 100644 --- a/libavcodec/dct.h +++ b/libavcodec/dct.h @@ -52,13 +52,6 @@ void ff_dct_end (DCTContext *s); void ff_dct_init_x86(DCTContext *s); -void ff_fdct_ifast(int16_t *data); -void ff_fdct_ifast248(int16_t *data); -void ff_jpeg_fdct_islow_8(int16_t *data); -void ff_jpeg_fdct_islow_10(int16_t *data); -void ff_fdct248_islow_8(int16_t *data); -void ff_fdct248_islow_10(int16_t *data); - void ff_j_rev_dct(int16_t *data); void ff_j_rev_dct4(int16_t *data); void ff_j_rev_dct2(int16_t *data); diff --git a/libavcodec/dds.c b/libavcodec/dds.c index 4bb425dbb3c..31a327a579d 100644 --- a/libavcodec/dds.c +++ b/libavcodec/dds.c @@ -651,7 +651,11 @@ static int dds_decode(AVCodecContext *avctx, AVFrame *frame, ((unsigned)frame->data[1][3+i*4]<<24) ); } +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif if (bytestream2_get_bytes_left(gbc) < frame->height * frame->width / 2) { av_log(avctx, AV_LOG_ERROR, "Buffer is too small (%d < %d).\n", @@ -682,7 +686,11 @@ static int dds_decode(AVCodecContext *avctx, AVFrame *frame, ((unsigned)frame->data[1][3+i*4]<<24) ); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } if (bytestream2_get_bytes_left(gbc) < frame->height * linesize) { @@ -702,7 +710,7 @@ static int dds_decode(AVCodecContext *avctx, AVFrame *frame, /* Frame is ready to be output. */ frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; diff --git a/libavcodec/decode.c b/libavcodec/decode.c index 0abc88737b2..7eada8e9aba 100644 --- a/libavcodec/decode.c +++ b/libavcodec/decode.c @@ -41,14 +41,34 @@ #include "libavutil/opt.h" #include "avcodec.h" +#include "avcodec_internal.h" #include "bytestream.h" #include "bsf.h" #include "codec_internal.h" #include "decode.h" +#include "hwaccel_internal.h" #include "hwconfig.h" #include "internal.h" +#include "packet_internal.h" #include "thread.h" +typedef struct DecodeContext { + AVCodecInternal avci; + + /* to prevent infinite loop on errors when draining */ + int nb_draining_errors; + + /** + * The caller has submitted a NULL packet on input. + */ + int draining_started; +} DecodeContext; + +static DecodeContext *decode_ctx(AVCodecInternal *avci) +{ + return (DecodeContext *)avci; +} + static int apply_param_change(AVCodecContext *avctx, const AVPacket *avpkt) { int ret; @@ -85,15 +105,26 @@ FF_DISABLE_DEPRECATION_WARNINGS ret = AVERROR_INVALIDDATA; goto fail2; } - avctx->channels = val; + av_channel_layout_uninit(&avctx->ch_layout); + avctx->ch_layout.nb_channels = val; + avctx->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC; size -= 4; } if (flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT) { if (size < 8) goto fail; - avctx->channel_layout = bytestream_get_le64(&data); + av_channel_layout_uninit(&avctx->ch_layout); + ret = av_channel_layout_from_mask(&avctx->ch_layout, bytestream_get_le64(&data)); + if (ret < 0) + goto fail2; size -= 8; } + if (flags & (AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT | + AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT)) { + avctx->channels = avctx->ch_layout.nb_channels; + avctx->channel_layout = (avctx->ch_layout.order == AV_CHANNEL_ORDER_NATIVE) ? + avctx->ch_layout.u.mask : 0; + } FF_ENABLE_DEPRECATION_WARNINGS #endif if (flags & AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE) { @@ -139,8 +170,10 @@ static int extract_packet_props(AVCodecInternal *avci, const AVPacket *pkt) av_packet_unref(avci->last_pkt_props); if (pkt) { ret = av_packet_copy_props(avci->last_pkt_props, pkt); +#if FF_API_FRAME_PKT if (!ret) - avci->last_pkt_props->opaque = (void *)(intptr_t)pkt->size; // Needed for ff_decode_frame_props(). + avci->last_pkt_props->stream_index = pkt->size; // Needed for ff_decode_frame_props(). +#endif } return ret; } @@ -180,14 +213,11 @@ static int decode_bsfs_init(AVCodecContext *avctx) return ret; } -int ff_decode_get_packet(AVCodecContext *avctx, AVPacket *pkt) +static int decode_get_packet(AVCodecContext *avctx, AVPacket *pkt) { AVCodecInternal *avci = avctx->internal; int ret; - if (avci->draining) - return AVERROR_EOF; - ret = av_bsf_receive_packet(avci->bsf, pkt); if (ret == AVERROR_EOF) avci->draining = 1; @@ -210,6 +240,31 @@ int ff_decode_get_packet(AVCodecContext *avctx, AVPacket *pkt) return ret; } +int ff_decode_get_packet(AVCodecContext *avctx, AVPacket *pkt) +{ + AVCodecInternal *avci = avctx->internal; + DecodeContext *dc = decode_ctx(avci); + + if (avci->draining) + return AVERROR_EOF; + + while (1) { + int ret = decode_get_packet(avctx, pkt); + if (ret == AVERROR(EAGAIN) && + (!AVPACKET_IS_EMPTY(avci->buffer_pkt) || dc->draining_started)) { + ret = av_bsf_send_packet(avci->bsf, avci->buffer_pkt); + if (ret < 0) { + av_packet_unref(avci->buffer_pkt); + return ret; + } + + continue; + } + + return ret; + } +} + /** * Attempt to guess proper monotonic timestamps for decoded video frames * which might have incorrect times. Input timestamps may wrap around, in @@ -246,6 +301,98 @@ static int64_t guess_correct_pts(AVCodecContext *ctx, return pts; } +static int discard_samples(AVCodecContext *avctx, AVFrame *frame, int64_t *discarded_samples) +{ + AVCodecInternal *avci = avctx->internal; + AVFrameSideData *side; + uint32_t discard_padding = 0; + uint8_t skip_reason = 0; + uint8_t discard_reason = 0; + + side = av_frame_get_side_data(frame, AV_FRAME_DATA_SKIP_SAMPLES); + if (side && side->size >= 10) { + avci->skip_samples = AV_RL32(side->data); + avci->skip_samples = FFMAX(0, avci->skip_samples); + discard_padding = AV_RL32(side->data + 4); + av_log(avctx, AV_LOG_DEBUG, "skip %d / discard %d samples due to side data\n", + avci->skip_samples, (int)discard_padding); + skip_reason = AV_RL8(side->data + 8); + discard_reason = AV_RL8(side->data + 9); + } + + if ((avctx->flags2 & AV_CODEC_FLAG2_SKIP_MANUAL)) { + if (!side && (avci->skip_samples || discard_padding)) + side = av_frame_new_side_data(frame, AV_FRAME_DATA_SKIP_SAMPLES, 10); + if (side && (avci->skip_samples || discard_padding)) { + AV_WL32(side->data, avci->skip_samples); + AV_WL32(side->data + 4, discard_padding); + AV_WL8(side->data + 8, skip_reason); + AV_WL8(side->data + 9, discard_reason); + avci->skip_samples = 0; + } + return 0; + } + av_frame_remove_side_data(frame, AV_FRAME_DATA_SKIP_SAMPLES); + + if ((frame->flags & AV_FRAME_FLAG_DISCARD)) { + avci->skip_samples = FFMAX(0, avci->skip_samples - frame->nb_samples); + *discarded_samples += frame->nb_samples; + return AVERROR(EAGAIN); + } + + if (avci->skip_samples > 0) { + if (frame->nb_samples <= avci->skip_samples){ + *discarded_samples += frame->nb_samples; + avci->skip_samples -= frame->nb_samples; + av_log(avctx, AV_LOG_DEBUG, "skip whole frame, skip left: %d\n", + avci->skip_samples); + return AVERROR(EAGAIN); + } else { + av_samples_copy(frame->extended_data, frame->extended_data, 0, avci->skip_samples, + frame->nb_samples - avci->skip_samples, avctx->ch_layout.nb_channels, frame->format); + if (avctx->pkt_timebase.num && avctx->sample_rate) { + int64_t diff_ts = av_rescale_q(avci->skip_samples, + (AVRational){1, avctx->sample_rate}, + avctx->pkt_timebase); + if (frame->pts != AV_NOPTS_VALUE) + frame->pts += diff_ts; + if (frame->pkt_dts != AV_NOPTS_VALUE) + frame->pkt_dts += diff_ts; + if (frame->duration >= diff_ts) + frame->duration -= diff_ts; + } else + av_log(avctx, AV_LOG_WARNING, "Could not update timestamps for skipped samples.\n"); + + av_log(avctx, AV_LOG_DEBUG, "skip %d/%d samples\n", + avci->skip_samples, frame->nb_samples); + *discarded_samples += avci->skip_samples; + frame->nb_samples -= avci->skip_samples; + avci->skip_samples = 0; + } + } + + if (discard_padding > 0 && discard_padding <= frame->nb_samples) { + if (discard_padding == frame->nb_samples) { + *discarded_samples += frame->nb_samples; + return AVERROR(EAGAIN); + } else { + if (avctx->pkt_timebase.num && avctx->sample_rate) { + int64_t diff_ts = av_rescale_q(frame->nb_samples - discard_padding, + (AVRational){1, avctx->sample_rate}, + avctx->pkt_timebase); + frame->duration = diff_ts; + } else + av_log(avctx, AV_LOG_WARNING, "Could not update timestamps for discarded samples.\n"); + + av_log(avctx, AV_LOG_DEBUG, "discard %d/%d samples\n", + (int)discard_padding, frame->nb_samples); + frame->nb_samples -= discard_padding; + } + } + + return 0; +} + /* * The core of the receive_frame_wrapper for the decoders implementing * the simple API. Certain decoders might consume partial packets without @@ -257,7 +404,7 @@ static inline int decode_simple_internal(AVCodecContext *avctx, AVFrame *frame, AVCodecInternal *avci = avctx->internal; AVPacket *const pkt = avci->in_pkt; const FFCodec *const codec = ffcodec(avctx->codec); - int got_frame, actual_got_frame; + int got_frame, consumed; int ret; if (!pkt->data && !avci->draining) { @@ -280,175 +427,54 @@ static inline int decode_simple_internal(AVCodecContext *avctx, AVFrame *frame, got_frame = 0; if (HAVE_THREADS && avctx->active_thread_type & FF_THREAD_FRAME) { - ret = ff_thread_decode_frame(avctx, frame, &got_frame, pkt); + consumed = ff_thread_decode_frame(avctx, frame, &got_frame, pkt); } else { - ret = codec->cb.decode(avctx, frame, &got_frame, pkt); + consumed = codec->cb.decode(avctx, frame, &got_frame, pkt); if (!(codec->caps_internal & FF_CODEC_CAP_SETS_PKT_DTS)) frame->pkt_dts = pkt->dts; if (avctx->codec->type == AVMEDIA_TYPE_VIDEO) { +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS if(!avctx->has_b_frames) frame->pkt_pos = pkt->pos; - //FIXME these should be under if(!avctx->has_b_frames) - /* get_buffer is supposed to set frame parameters */ - if (!(avctx->codec->capabilities & AV_CODEC_CAP_DR1)) { - if (!frame->sample_aspect_ratio.num) frame->sample_aspect_ratio = avctx->sample_aspect_ratio; - if (!frame->width) frame->width = avctx->width; - if (!frame->height) frame->height = avctx->height; - if (frame->format == AV_PIX_FMT_NONE) frame->format = avctx->pix_fmt; - } +FF_ENABLE_DEPRECATION_WARNINGS +#endif } } emms_c(); - actual_got_frame = got_frame; if (avctx->codec->type == AVMEDIA_TYPE_VIDEO) { - if (frame->flags & AV_FRAME_FLAG_DISCARD) - got_frame = 0; + ret = (!got_frame || frame->flags & AV_FRAME_FLAG_DISCARD) + ? AVERROR(EAGAIN) + : 0; } else if (avctx->codec->type == AVMEDIA_TYPE_AUDIO) { - uint8_t *side; - size_t side_size; - uint32_t discard_padding = 0; - uint8_t skip_reason = 0; - uint8_t discard_reason = 0; - - if (ret >= 0 && got_frame) { - if (frame->format == AV_SAMPLE_FMT_NONE) - frame->format = avctx->sample_fmt; - if (!frame->ch_layout.nb_channels) { - int ret2 = av_channel_layout_copy(&frame->ch_layout, &avctx->ch_layout); - if (ret2 < 0) { - ret = ret2; - got_frame = 0; - } - } -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - if (!frame->channel_layout) - frame->channel_layout = avctx->ch_layout.order == AV_CHANNEL_ORDER_NATIVE ? - avctx->ch_layout.u.mask : 0; - if (!frame->channels) - frame->channels = avctx->ch_layout.nb_channels; -FF_ENABLE_DEPRECATION_WARNINGS -#endif - if (!frame->sample_rate) - frame->sample_rate = avctx->sample_rate; - } - - side= av_packet_get_side_data(avci->last_pkt_props, AV_PKT_DATA_SKIP_SAMPLES, &side_size); - if(side && side_size>=10) { - avci->skip_samples = AV_RL32(side); - avci->skip_samples = FFMAX(0, avci->skip_samples); - discard_padding = AV_RL32(side + 4); - av_log(avctx, AV_LOG_DEBUG, "skip %d / discard %d samples due to side data\n", - avci->skip_samples, (int)discard_padding); - skip_reason = AV_RL8(side + 8); - discard_reason = AV_RL8(side + 9); - } - - if ((frame->flags & AV_FRAME_FLAG_DISCARD) && got_frame && - !(avctx->flags2 & AV_CODEC_FLAG2_SKIP_MANUAL)) { - avci->skip_samples = FFMAX(0, avci->skip_samples - frame->nb_samples); - got_frame = 0; - *discarded_samples += frame->nb_samples; - } - - if (avci->skip_samples > 0 && got_frame && - !(avctx->flags2 & AV_CODEC_FLAG2_SKIP_MANUAL)) { - if(frame->nb_samples <= avci->skip_samples){ - got_frame = 0; - *discarded_samples += frame->nb_samples; - avci->skip_samples -= frame->nb_samples; - av_log(avctx, AV_LOG_DEBUG, "skip whole frame, skip left: %d\n", - avci->skip_samples); - } else { - av_samples_copy(frame->extended_data, frame->extended_data, 0, avci->skip_samples, - frame->nb_samples - avci->skip_samples, avctx->ch_layout.nb_channels, frame->format); - if(avctx->pkt_timebase.num && avctx->sample_rate) { - int64_t diff_ts = av_rescale_q(avci->skip_samples, - (AVRational){1, avctx->sample_rate}, - avctx->pkt_timebase); - if(frame->pts!=AV_NOPTS_VALUE) - frame->pts += diff_ts; - if(frame->pkt_dts!=AV_NOPTS_VALUE) - frame->pkt_dts += diff_ts; - if (frame->duration >= diff_ts) - frame->duration -= diff_ts; - } else { - av_log(avctx, AV_LOG_WARNING, "Could not update timestamps for skipped samples.\n"); - } - av_log(avctx, AV_LOG_DEBUG, "skip %d/%d samples\n", - avci->skip_samples, frame->nb_samples); - *discarded_samples += avci->skip_samples; - frame->nb_samples -= avci->skip_samples; - avci->skip_samples = 0; - } - } - - if (discard_padding > 0 && discard_padding <= frame->nb_samples && got_frame && - !(avctx->flags2 & AV_CODEC_FLAG2_SKIP_MANUAL)) { - if (discard_padding == frame->nb_samples) { - *discarded_samples += frame->nb_samples; - got_frame = 0; - } else { - if(avctx->pkt_timebase.num && avctx->sample_rate) { - int64_t diff_ts = av_rescale_q(frame->nb_samples - discard_padding, - (AVRational){1, avctx->sample_rate}, - avctx->pkt_timebase); - frame->duration = diff_ts; - } else { - av_log(avctx, AV_LOG_WARNING, "Could not update timestamps for discarded samples.\n"); - } - av_log(avctx, AV_LOG_DEBUG, "discard %d/%d samples\n", - (int)discard_padding, frame->nb_samples); - frame->nb_samples -= discard_padding; - } - } - - if ((avctx->flags2 & AV_CODEC_FLAG2_SKIP_MANUAL) && got_frame) { - AVFrameSideData *fside = av_frame_new_side_data(frame, AV_FRAME_DATA_SKIP_SAMPLES, 10); - if (fside) { - AV_WL32(fside->data, avci->skip_samples); - AV_WL32(fside->data + 4, discard_padding); - AV_WL8(fside->data + 8, skip_reason); - AV_WL8(fside->data + 9, discard_reason); - avci->skip_samples = 0; - } - } - } - - if (avctx->codec->type == AVMEDIA_TYPE_AUDIO && - !avci->showed_multi_packet_warning && - ret >= 0 && ret != pkt->size && !(avctx->codec->capabilities & AV_CODEC_CAP_SUBFRAMES)) { - av_log(avctx, AV_LOG_WARNING, "Multiple frames in a packet.\n"); - avci->showed_multi_packet_warning = 1; + ret = !got_frame ? AVERROR(EAGAIN) + : discard_samples(avctx, frame, discarded_samples); } - if (!got_frame) + if (ret == AVERROR(EAGAIN)) av_frame_unref(frame); -#if FF_API_FLAG_TRUNCATED - if (ret >= 0 && avctx->codec->type == AVMEDIA_TYPE_VIDEO && !(avctx->flags & AV_CODEC_FLAG_TRUNCATED)) -#else - if (ret >= 0 && avctx->codec->type == AVMEDIA_TYPE_VIDEO) -#endif - ret = pkt->size; + if (consumed < 0) + ret = consumed; + if (consumed >= 0 && avctx->codec->type == AVMEDIA_TYPE_VIDEO) + consumed = pkt->size; -#if FF_API_AVCTX_TIMEBASE - if (avctx->framerate.num > 0 && avctx->framerate.den > 0) - avctx->time_base = av_inv_q(av_mul_q(avctx->framerate, (AVRational){avctx->ticks_per_frame, 1})); -#endif + if (!ret) + av_assert0(frame->buf[0]); + if (ret == AVERROR(EAGAIN)) + ret = 0; - /* do not stop draining when actual_got_frame != 0 or ret < 0 */ - /* got_frame == 0 but actual_got_frame != 0 when frame is discarded */ - if (avci->draining && !actual_got_frame) { + /* do not stop draining when got_frame != 0 or ret < 0 */ + if (avci->draining && !got_frame) { if (ret < 0) { /* prevent infinite loop if a decoder wrongly always return error on draining */ /* reasonable nb_errors_max = maximum b frames + thread count */ int nb_errors_max = 20 + (HAVE_THREADS && avctx->active_thread_type & FF_THREAD_FRAME ? avctx->thread_count : 1); - if (avci->nb_draining_errors++ >= nb_errors_max) { + if (decode_ctx(avci)->nb_draining_errors++ >= nb_errors_max) { av_log(avctx, AV_LOG_ERROR, "Too many errors when draining, this is a bug. " "Stop draining and force EOF.\n"); avci->draining_done = 1; @@ -459,27 +485,24 @@ FF_ENABLE_DEPRECATION_WARNINGS } } - if (ret >= pkt->size || ret < 0) { + if (consumed >= pkt->size || ret < 0) { av_packet_unref(pkt); } else { - int consumed = ret; - pkt->data += consumed; pkt->size -= consumed; pkt->pts = AV_NOPTS_VALUE; pkt->dts = AV_NOPTS_VALUE; if (!(codec->caps_internal & FF_CODEC_CAP_SETS_FRAME_PROPS)) { +#if FF_API_FRAME_PKT // See extract_packet_props() comment. - avci->last_pkt_props->opaque = (void *)((intptr_t)avci->last_pkt_props->opaque - consumed); + avci->last_pkt_props->stream_index = avci->last_pkt_props->stream_index - consumed; +#endif avci->last_pkt_props->pts = AV_NOPTS_VALUE; avci->last_pkt_props->dts = AV_NOPTS_VALUE; } } - if (got_frame) - av_assert0(frame->buf[0]); - - return ret < 0 ? ret : 0; + return ret; } #if CONFIG_LCMS2 @@ -530,6 +553,48 @@ static int detect_colorspace(av_unused AVCodecContext *c, av_unused AVFrame *f) } #endif +static int fill_frame_props(const AVCodecContext *avctx, AVFrame *frame) +{ + int ret; + + if (frame->color_primaries == AVCOL_PRI_UNSPECIFIED) + frame->color_primaries = avctx->color_primaries; + if (frame->color_trc == AVCOL_TRC_UNSPECIFIED) + frame->color_trc = avctx->color_trc; + if (frame->colorspace == AVCOL_SPC_UNSPECIFIED) + frame->colorspace = avctx->colorspace; + if (frame->color_range == AVCOL_RANGE_UNSPECIFIED) + frame->color_range = avctx->color_range; + if (frame->chroma_location == AVCHROMA_LOC_UNSPECIFIED) + frame->chroma_location = avctx->chroma_sample_location; + + if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) { + if (!frame->sample_aspect_ratio.num) frame->sample_aspect_ratio = avctx->sample_aspect_ratio; + if (frame->format == AV_PIX_FMT_NONE) frame->format = avctx->pix_fmt; + } else if (avctx->codec->type == AVMEDIA_TYPE_AUDIO) { + if (frame->format == AV_SAMPLE_FMT_NONE) + frame->format = avctx->sample_fmt; + if (!frame->ch_layout.nb_channels) { + ret = av_channel_layout_copy(&frame->ch_layout, &avctx->ch_layout); + if (ret < 0) + return ret; + } +#if FF_API_OLD_CHANNEL_LAYOUT +FF_DISABLE_DEPRECATION_WARNINGS + if (!frame->channel_layout) + frame->channel_layout = avctx->ch_layout.order == AV_CHANNEL_ORDER_NATIVE ? + avctx->ch_layout.u.mask : 0; + if (!frame->channels) + frame->channels = avctx->ch_layout.nb_channels; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + if (!frame->sample_rate) + frame->sample_rate = avctx->sample_rate; + } + + return 0; +} + static int decode_simple_receive_frame(AVCodecContext *avctx, AVFrame *frame) { int ret; @@ -556,6 +621,15 @@ static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame) if (codec->cb_type == FF_CODEC_CB_TYPE_RECEIVE_FRAME) { ret = codec->cb.receive_frame(avctx, frame); + emms_c(); + if (!ret) { + if (avctx->codec->type == AVMEDIA_TYPE_VIDEO) + ret = (frame->flags & AV_FRAME_FLAG_DISCARD) ? AVERROR(EAGAIN) : 0; + else if (avctx->codec->type == AVMEDIA_TYPE_AUDIO) { + int64_t discarded_samples = 0; + ret = discard_samples(avctx, frame, &discarded_samples); + } + } } else ret = decode_simple_receive_frame(avctx, frame); @@ -570,6 +644,31 @@ static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame) } if (!ret) { + if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) { + if (!frame->width) + frame->width = avctx->width; + if (!frame->height) + frame->height = avctx->height; + } else + frame->flags |= AV_FRAME_FLAG_KEY; + + ret = fill_frame_props(avctx, frame); + if (ret < 0) { + av_frame_unref(frame); + return ret; + } + +#if FF_API_FRAME_KEY +FF_DISABLE_DEPRECATION_WARNINGS + frame->key_frame = !!(frame->flags & AV_FRAME_FLAG_KEY); +FF_ENABLE_DEPRECATION_WARNINGS +#endif +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS + frame->interlaced_frame = !!(frame->flags & AV_FRAME_FLAG_INTERLACED); + frame->top_field_first = !!(frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST); +FF_ENABLE_DEPRECATION_WARNINGS +#endif frame->best_effort_timestamp = guess_correct_pts(avctx, frame->pts, frame->pkt_dts); @@ -607,31 +706,28 @@ FF_ENABLE_DEPRECATION_WARNINGS int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt) { AVCodecInternal *avci = avctx->internal; + DecodeContext *dc = decode_ctx(avci); int ret; if (!avcodec_is_open(avctx) || !av_codec_is_decoder(avctx->codec)) return AVERROR(EINVAL); - if (avctx->internal->draining) + if (dc->draining_started) return AVERROR_EOF; if (avpkt && !avpkt->size && avpkt->data) return AVERROR(EINVAL); - av_packet_unref(avci->buffer_pkt); if (avpkt && (avpkt->data || avpkt->side_data_elems)) { + if (!AVPACKET_IS_EMPTY(avci->buffer_pkt)) + return AVERROR(EAGAIN); ret = av_packet_ref(avci->buffer_pkt, avpkt); if (ret < 0) return ret; - } - - ret = av_bsf_send_packet(avci->bsf, avci->buffer_pkt); - if (ret < 0) { - av_packet_unref(avci->buffer_pkt); - return ret; - } + } else + dc->draining_started = 1; - if (!avci->buffer_frame->buf[0]) { + if (!avci->buffer_frame->buf[0] && !dc->draining_started) { ret = decode_receive_frame_internal(avctx, avci->buffer_frame); if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) return ret; @@ -697,7 +793,7 @@ static int frame_validate(AVCodecContext *avctx, AVFrame *frame) int ff_decode_receive_frame(AVCodecContext *avctx, AVFrame *frame) { AVCodecInternal *avci = avctx->internal; - int ret, changed; + int ret; if (!avcodec_is_open(avctx) || !av_codec_is_decoder(avctx->codec)) return AVERROR(EINVAL); @@ -720,11 +816,17 @@ int ff_decode_receive_frame(AVCodecContext *avctx, AVFrame *frame) goto fail; } - avctx->frame_number++; + avctx->frame_num++; +#if FF_API_AVCTX_FRAME_NUMBER +FF_DISABLE_DEPRECATION_WARNINGS + avctx->frame_number = avctx->frame_num; +FF_ENABLE_DEPRECATION_WARNINGS +#endif +#if FF_API_DROPCHANGED if (avctx->flags & AV_CODEC_FLAG_DROPCHANGED) { - if (avctx->frame_number == 1) { + if (avctx->frame_num == 1) { avci->initial_format = frame->format; switch(avctx->codec_type) { case AVMEDIA_TYPE_VIDEO: @@ -741,8 +843,8 @@ int ff_decode_receive_frame(AVCodecContext *avctx, AVFrame *frame) } } - if (avctx->frame_number > 1) { - changed = avci->initial_format != frame->format; + if (avctx->frame_num > 1) { + int changed = avci->initial_format != frame->format; switch(avctx->codec_type) { case AVMEDIA_TYPE_VIDEO: @@ -758,15 +860,16 @@ int ff_decode_receive_frame(AVCodecContext *avctx, AVFrame *frame) if (changed) { avci->changed_frames_dropped++; - av_log(avctx, AV_LOG_INFO, "dropped changed frame #%d pts %"PRId64 + av_log(avctx, AV_LOG_INFO, "dropped changed frame #%"PRId64" pts %"PRId64 " drop count: %d \n", - avctx->frame_number, frame->pts, + avctx->frame_num, frame->pts, avci->changed_frames_dropped); ret = AVERROR_INPUT_CHANGED; goto fail; } } } +#endif return 0; fail: av_frame_unref(frame); @@ -780,8 +883,8 @@ static void get_subtitle_defaults(AVSubtitle *sub) } #define UTF8_MAX_BYTES 4 /* 5 and 6 bytes sequences should not be used */ -static int recode_subtitle(AVCodecContext *avctx, AVPacket **outpkt, - AVPacket *inpkt, AVPacket *buf_pkt) +static int recode_subtitle(AVCodecContext *avctx, const AVPacket **outpkt, + const AVPacket *inpkt, AVPacket *buf_pkt) { #if CONFIG_ICONV iconv_t cd = (iconv_t)-1; @@ -861,8 +964,7 @@ static int utf8_check(const uint8_t *str) } int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub, - int *got_sub_ptr, - AVPacket *avpkt) + int *got_sub_ptr, const AVPacket *avpkt) { int ret = 0; @@ -882,7 +984,7 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub, if ((avctx->codec->capabilities & AV_CODEC_CAP_DELAY) || avpkt->size) { AVCodecInternal *avci = avctx->internal; - AVPacket *pkt; + const AVPacket *pkt; ret = recode_subtitle(avctx, &pkt, avpkt, avci->buffer_pkt); if (ret < 0) @@ -926,7 +1028,12 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub, } if (*got_sub_ptr) - avctx->frame_number++; + avctx->frame_num++; +#if FF_API_AVCTX_FRAME_NUMBER +FF_DISABLE_DEPRECATION_WARNINGS + avctx->frame_number = avctx->frame_num; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } return ret; @@ -1053,7 +1160,7 @@ int avcodec_get_hw_frames_parameters(AVCodecContext *avctx, { AVBufferRef *frames_ref = NULL; const AVCodecHWConfigInternal *hw_config; - const AVHWAccel *hwa; + const FFHWAccel *hwa; int i, ret; for (i = 0;; i++) { @@ -1072,6 +1179,15 @@ int avcodec_get_hw_frames_parameters(AVCodecContext *avctx, if (!frames_ref) return AVERROR(ENOMEM); + if (!avctx->internal->hwaccel_priv_data) { + avctx->internal->hwaccel_priv_data = + av_mallocz(hwa->priv_data_size); + if (!avctx->internal->hwaccel_priv_data) { + av_buffer_unref(&frames_ref); + return AVERROR(ENOMEM); + } + } + ret = hwa->frame_params(avctx, frames_ref); if (ret >= 0) { AVHWFramesContext *frames_ctx = (AVHWFramesContext*)frames_ref->data; @@ -1096,33 +1212,31 @@ int avcodec_get_hw_frames_parameters(AVCodecContext *avctx, } static int hwaccel_init(AVCodecContext *avctx, - const AVCodecHWConfigInternal *hw_config) + const FFHWAccel *hwaccel) { - const AVHWAccel *hwaccel; int err; - hwaccel = hw_config->hwaccel; - if (hwaccel->capabilities & AV_HWACCEL_CODEC_CAP_EXPERIMENTAL && + if (hwaccel->p.capabilities & AV_HWACCEL_CODEC_CAP_EXPERIMENTAL && avctx->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) { av_log(avctx, AV_LOG_WARNING, "Ignoring experimental hwaccel: %s\n", - hwaccel->name); + hwaccel->p.name); return AVERROR_PATCHWELCOME; } - if (hwaccel->priv_data_size) { + if (!avctx->internal->hwaccel_priv_data && hwaccel->priv_data_size) { avctx->internal->hwaccel_priv_data = av_mallocz(hwaccel->priv_data_size); if (!avctx->internal->hwaccel_priv_data) return AVERROR(ENOMEM); } - avctx->hwaccel = hwaccel; + avctx->hwaccel = &hwaccel->p; if (hwaccel->init) { err = hwaccel->init(avctx); if (err < 0) { av_log(avctx, AV_LOG_ERROR, "Failed setup for format %s: " "hwaccel initialisation returned error.\n", - av_get_pix_fmt_name(hw_config->public.pix_fmt)); + av_get_pix_fmt_name(hwaccel->p.pix_fmt)); av_freep(&avctx->internal->hwaccel_priv_data); avctx->hwaccel = NULL; return err; @@ -1132,10 +1246,10 @@ static int hwaccel_init(AVCodecContext *avctx, return 0; } -static void hwaccel_uninit(AVCodecContext *avctx) +void ff_hwaccel_uninit(AVCodecContext *avctx) { - if (avctx->hwaccel && avctx->hwaccel->uninit) - avctx->hwaccel->uninit(avctx); + if (FF_HW_HAS_CB(avctx, uninit)) + FF_HW_SIMPLE_CALL(avctx, uninit); av_freep(&avctx->internal->hwaccel_priv_data); @@ -1171,7 +1285,7 @@ int ff_get_format(AVCodecContext *avctx, const enum AVPixelFormat *fmt) for (;;) { // Remove the previous hwaccel, if there was one. - hwaccel_uninit(avctx); + ff_hwaccel_uninit(avctx); user_choice = avctx->get_format(avctx, choices); if (user_choice == AV_PIX_FMT_NONE) { @@ -1256,7 +1370,7 @@ int ff_get_format(AVCodecContext *avctx, const enum AVPixelFormat *fmt) if (hw_config->hwaccel) { av_log(avctx, AV_LOG_DEBUG, "Format %s requires hwaccel " "initialisation.\n", desc->name); - err = hwaccel_init(avctx, hw_config); + err = hwaccel_init(avctx, hw_config->hwaccel); if (err < 0) goto try_again; } @@ -1275,6 +1389,9 @@ int ff_get_format(AVCodecContext *avctx, const enum AVPixelFormat *fmt) --n; } + if (ret < 0) + ff_hwaccel_uninit(avctx); + av_freep(&choices); return ret; } @@ -1291,7 +1408,8 @@ static int add_metadata_from_side_data(const AVPacket *avpkt, AVFrame *frame) return av_packet_unpack_dictionary(side_metadata, size, frame_md); } -int ff_decode_frame_props_from_pkt(AVFrame *frame, const AVPacket *pkt) +int ff_decode_frame_props_from_pkt(const AVCodecContext *avctx, + AVFrame *frame, const AVPacket *pkt) { static const struct { enum AVPacketSideDataType packet; @@ -1305,15 +1423,21 @@ int ff_decode_frame_props_from_pkt(AVFrame *frame, const AVPacket *pkt) { AV_PKT_DATA_MASTERING_DISPLAY_METADATA, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA }, { AV_PKT_DATA_CONTENT_LIGHT_LEVEL, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL }, { AV_PKT_DATA_A53_CC, AV_FRAME_DATA_A53_CC }, + { AV_PKT_DATA_AFD, AV_FRAME_DATA_AFD }, { AV_PKT_DATA_ICC_PROFILE, AV_FRAME_DATA_ICC_PROFILE }, { AV_PKT_DATA_S12M_TIMECODE, AV_FRAME_DATA_S12M_TIMECODE }, { AV_PKT_DATA_DYNAMIC_HDR10_PLUS, AV_FRAME_DATA_DYNAMIC_HDR_PLUS }, + { AV_PKT_DATA_SKIP_SAMPLES, AV_FRAME_DATA_SKIP_SAMPLES }, }; frame->pts = pkt->pts; - frame->pkt_pos = pkt->pos; frame->duration = pkt->duration; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS + frame->pkt_pos = pkt->pos; frame->pkt_size = pkt->size; +FF_ENABLE_DEPRECATION_WARNINGS +#endif for (int i = 0; i < FF_ARRAY_ELEMS(sd); i++) { size_t size; @@ -1336,38 +1460,43 @@ int ff_decode_frame_props_from_pkt(AVFrame *frame, const AVPacket *pkt) frame->flags = (frame->flags & ~AV_FRAME_FLAG_DISCARD); } + if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { + int ret = av_buffer_replace(&frame->opaque_ref, pkt->opaque_ref); + if (ret < 0) + return ret; + frame->opaque = pkt->opaque; + } + return 0; } int ff_decode_frame_props(AVCodecContext *avctx, AVFrame *frame) { const AVPacket *pkt = avctx->internal->last_pkt_props; + int ret; if (!(ffcodec(avctx->codec)->caps_internal & FF_CODEC_CAP_SETS_FRAME_PROPS)) { - int ret = ff_decode_frame_props_from_pkt(frame, pkt); + ret = ff_decode_frame_props_from_pkt(avctx, frame, pkt); if (ret < 0) return ret; - frame->pkt_size = (int)(intptr_t)pkt->opaque; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS + frame->pkt_size = pkt->stream_index; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } +#if FF_API_REORDERED_OPAQUE +FF_DISABLE_DEPRECATION_WARNINGS frame->reordered_opaque = avctx->reordered_opaque; +FF_ENABLE_DEPRECATION_WARNINGS +#endif - if (frame->color_primaries == AVCOL_PRI_UNSPECIFIED) - frame->color_primaries = avctx->color_primaries; - if (frame->color_trc == AVCOL_TRC_UNSPECIFIED) - frame->color_trc = avctx->color_trc; - if (frame->colorspace == AVCOL_SPC_UNSPECIFIED) - frame->colorspace = avctx->colorspace; - if (frame->color_range == AVCOL_RANGE_UNSPECIFIED) - frame->color_range = avctx->color_range; - if (frame->chroma_location == AVCHROMA_LOC_UNSPECIFIED) - frame->chroma_location = avctx->chroma_sample_location; + ret = fill_frame_props(avctx, frame); + if (ret < 0) + return ret; switch (avctx->codec->type) { case AVMEDIA_TYPE_VIDEO: - frame->format = avctx->pix_fmt; - if (!frame->sample_aspect_ratio.num) - frame->sample_aspect_ratio = avctx->sample_aspect_ratio; - if (frame->width && frame->height && av_image_check_sar(frame->width, frame->height, frame->sample_aspect_ratio) < 0) { @@ -1376,25 +1505,6 @@ int ff_decode_frame_props(AVCodecContext *avctx, AVFrame *frame) frame->sample_aspect_ratio.den); frame->sample_aspect_ratio = (AVRational){ 0, 1 }; } - - break; - case AVMEDIA_TYPE_AUDIO: - if (!frame->sample_rate) - frame->sample_rate = avctx->sample_rate; - if (frame->format < 0) - frame->format = avctx->sample_fmt; - if (!frame->ch_layout.nb_channels) { - int ret = av_channel_layout_copy(&frame->ch_layout, &avctx->ch_layout); - if (ret < 0) - return ret; - } -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - frame->channels = frame->ch_layout.nb_channels; - frame->channel_layout = frame->ch_layout.order == AV_CHANNEL_ORDER_NATIVE ? - frame->ch_layout.u.mask : 0; -FF_ENABLE_DEPRECATION_WARNINGS -#endif break; } return 0; @@ -1460,7 +1570,7 @@ int ff_attach_decode_data(AVFrame *frame) int ff_get_buffer(AVCodecContext *avctx, AVFrame *frame, int flags) { - const AVHWAccel *hwaccel = avctx->hwaccel; + const FFHWAccel *hwaccel = ffhwaccel(avctx->hwaccel); int override_dimensions = 1; int ret; @@ -1593,19 +1703,6 @@ int ff_decode_preinit(AVCodecContext *avctx) * free the already allocated subtitle_header before overwriting it */ av_freep(&avctx->subtitle_header); -#if FF_API_THREAD_SAFE_CALLBACKS -FF_DISABLE_DEPRECATION_WARNINGS - if ((avctx->thread_type & FF_THREAD_FRAME) && - avctx->get_buffer2 != avcodec_default_get_buffer2 && - !avctx->thread_safe_callbacks) { - av_log(avctx, AV_LOG_WARNING, "Requested frame threading with a " - "custom get_buffer2() implementation which is not marked as " - "thread safe. This is not supported anymore, make your " - "callback thread-safe.\n"); - } -FF_ENABLE_DEPRECATION_WARNINGS -#endif - if (avctx->codec->max_lowres < avctx->lowres || avctx->lowres < 0) { av_log(avctx, AV_LOG_WARNING, "The maximum value for lowres supported by the decoder is %d\n", avctx->codec->max_lowres); @@ -1669,6 +1766,11 @@ FF_ENABLE_DEPRECATION_WARNINGS if (ret < 0) return ret; +#if FF_API_DROPCHANGED + if (avctx->flags & AV_CODEC_FLAG_DROPCHANGED) + av_log(avctx, AV_LOG_WARNING, "The dropchanged flag is deprecated.\n"); +#endif + return 0; } @@ -1686,3 +1788,57 @@ int ff_copy_palette(void *dst, const AVPacket *src, void *logctx) } return 0; } + +int ff_hwaccel_frame_priv_alloc(AVCodecContext *avctx, void **hwaccel_picture_private, + AVBufferRef **hwaccel_priv_buf) +{ + const FFHWAccel *hwaccel = ffhwaccel(avctx->hwaccel); + AVBufferRef *ref; + AVHWFramesContext *frames_ctx; + uint8_t *data; + + if (!hwaccel || !hwaccel->frame_priv_data_size) + return 0; + + av_assert0(!*hwaccel_picture_private); + data = av_mallocz(hwaccel->frame_priv_data_size); + if (!data) + return AVERROR(ENOMEM); + + frames_ctx = (AVHWFramesContext *)avctx->hw_frames_ctx->data; + + ref = av_buffer_create(data, hwaccel->frame_priv_data_size, + hwaccel->free_frame_priv, + frames_ctx->device_ctx, 0); + if (!ref) { + av_free(data); + return AVERROR(ENOMEM); + } + + *hwaccel_priv_buf = ref; + *hwaccel_picture_private = ref->data; + + return 0; +} + +void ff_decode_flush_buffers(AVCodecContext *avctx) +{ + AVCodecInternal *avci = avctx->internal; + DecodeContext *dc = decode_ctx(avci); + + av_packet_unref(avci->last_pkt_props); + av_packet_unref(avci->in_pkt); + + avctx->pts_correction_last_pts = + avctx->pts_correction_last_dts = INT64_MIN; + + av_bsf_flush(avci->bsf); + + dc->nb_draining_errors = 0; + dc->draining_started = 0; +} + +AVCodecInternal *ff_decode_internal_alloc(void) +{ + return av_mallocz(sizeof(DecodeContext)); +} diff --git a/libavcodec/decode.h b/libavcodec/decode.h index 906122b4a73..a52152e4a71 100644 --- a/libavcodec/decode.h +++ b/libavcodec/decode.h @@ -53,11 +53,6 @@ typedef struct FrameDecodeData { void (*hwaccel_priv_free)(void *priv); } FrameDecodeData; -/** - * avcodec_receive_frame() implementation for decoders. - */ -int ff_decode_receive_frame(AVCodecContext *avctx, AVFrame *frame); - /** * Called by decoders to get the next packet for decoding. * @@ -72,7 +67,8 @@ int ff_decode_get_packet(AVCodecContext *avctx, AVPacket *pkt); /** * Set various frame properties from the provided packet. */ -int ff_decode_frame_props_from_pkt(AVFrame *frame, const AVPacket *pkt); +int ff_decode_frame_props_from_pkt(const AVCodecContext *avctx, + AVFrame *frame, const AVPacket *pkt); /** * Set various frame properties from the codec context / packet data. @@ -98,12 +94,6 @@ int ff_attach_decode_data(AVFrame *frame); */ int ff_copy_palette(void *dst, const AVPacket *src, void *logctx); -/** - * Perform decoder initialization and validation. - * Called when opening the decoder, before the FFCodec.init() call. - */ -int ff_decode_preinit(AVCodecContext *avctx); - /** * Check that the provided frame dimensions are valid and set them on the codec * context. @@ -149,4 +139,18 @@ int ff_reget_buffer(AVCodecContext *avctx, AVFrame *frame, int flags); int ff_side_data_update_matrix_encoding(AVFrame *frame, enum AVMatrixEncoding matrix_encoding); +/** + * Allocate a hwaccel frame private data if the provided avctx + * uses a hwaccel method that needs it. The private data will + * be refcounted via the AVBuffer API (if allocated). + * + * @param avctx The codec context + * @param hwaccel_picture_private Pointer to return hwaccel_picture_private + * @param hwaccel_priv_buf Pointer to return the AVBufferRef owning + * hwaccel_picture_private + * @return 0 on success, < 0 on error + */ +int ff_hwaccel_frame_priv_alloc(AVCodecContext *avctx, void **hwaccel_picture_private, + AVBufferRef **hwaccel_priv_buf); + #endif /* AVCODEC_DECODE_H */ diff --git a/libavcodec/dfa.c b/libavcodec/dfa.c index 114c803f32e..9114feb0b3a 100644 --- a/libavcodec/dfa.c +++ b/libavcodec/dfa.c @@ -367,7 +367,11 @@ static int dfa_decode_frame(AVCodecContext *avctx, AVFrame *frame, s->pal[i] = bytestream2_get_be24(&gb) << 2; s->pal[i] |= 0xFFU << 24 | (s->pal[i] >> 6) & 0x30303; } +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } else if (chunk_type <= 9) { if (decoder[chunk_type - 2](&gb, s->frame_buf, avctx->width, avctx->height)) { av_log(avctx, AV_LOG_ERROR, "Error decoding %s chunk\n", diff --git a/libavcodec/dfpwmenc.c b/libavcodec/dfpwmenc.c index 7f465a446e4..5318b04a390 100644 --- a/libavcodec/dfpwmenc.c +++ b/libavcodec/dfpwmenc.c @@ -116,5 +116,6 @@ const FFCodec ff_dfpwm_encoder = { .init = dfpwm_enc_init, FF_CODEC_ENCODE_CB(dfpwm_enc_frame), .p.sample_fmts = (const enum AVSampleFormat[]){AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_NONE}, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_VARIABLE_FRAME_SIZE, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_VARIABLE_FRAME_SIZE | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, }; diff --git a/libavcodec/dirac.c b/libavcodec/dirac.c index bc51a2fbd72..4736304977c 100644 --- a/libavcodec/dirac.c +++ b/libavcodec/dirac.c @@ -26,9 +26,12 @@ * @author Marco Gerards , David Conrad, Jordi Ortiz */ +#include "config.h" + #include "libavutil/pixdesc.h" #include "dirac.h" +#include "get_bits.h" #include "golomb.h" #include "mpeg12data.h" diff --git a/libavcodec/dirac.h b/libavcodec/dirac.h index e6d9d346d9c..8c348cdc027 100644 --- a/libavcodec/dirac.h +++ b/libavcodec/dirac.h @@ -31,7 +31,11 @@ * @author Jordi Ortiz */ -#include "avcodec.h" +#include +#include + +#include "libavutil/pixfmt.h" +#include "libavutil/rational.h" /** * The spec limits the number of wavelet decompositions to 4 for both diff --git a/libavcodec/diracdec.c b/libavcodec/diracdec.c index a5cad295974..277c730cc78 100644 --- a/libavcodec/diracdec.c +++ b/libavcodec/diracdec.c @@ -77,6 +77,7 @@ typedef struct { uint8_t *hpel[3][4]; uint8_t *hpel_base[3][4]; int reference; + unsigned picture_number; } DiracFrame; typedef struct { @@ -252,13 +253,13 @@ static inline int divide3(int x) return (int)((x+1U)*21845 + 10922) >> 16; } -static DiracFrame *remove_frame(DiracFrame *framelist[], int picnum) +static DiracFrame *remove_frame(DiracFrame *framelist[], unsigned picnum) { DiracFrame *remove_pic = NULL; int i, remove_idx = -1; for (i = 0; framelist[i]; i++) - if (framelist[i]->avframe->display_picture_number == picnum) { + if (framelist[i]->picture_number == picnum) { remove_pic = framelist[i]; remove_idx = i; } @@ -2002,7 +2003,7 @@ static int dirac_decode_picture_header(DiracContext *s) GetBitContext *gb = &s->gb; /* [DIRAC_STD] 11.1.1 Picture Header. picture_header() PICTURE_NUM */ - picnum = s->current_picture->avframe->display_picture_number = get_bits_long(gb, 32); + picnum = s->current_picture->picture_number = get_bits_long(gb, 32); av_log(s->avctx,AV_LOG_DEBUG,"PICTURE_NUM: %d\n",picnum); @@ -2021,9 +2022,9 @@ static int dirac_decode_picture_header(DiracContext *s) /* Jordi: this is needed if the referenced picture hasn't yet arrived */ for (j = 0; j < MAX_REFERENCE_FRAMES && refdist; j++) if (s->ref_frames[j] - && FFABS(s->ref_frames[j]->avframe->display_picture_number - refnum) < refdist) { + && FFABS(s->ref_frames[j]->picture_number - refnum) < refdist) { s->ref_pics[i] = s->ref_frames[j]; - refdist = FFABS(s->ref_frames[j]->avframe->display_picture_number - refnum); + refdist = FFABS(s->ref_frames[j]->picture_number - refnum); } if (!s->ref_pics[i] || refdist) @@ -2062,7 +2063,7 @@ static int dirac_decode_picture_header(DiracContext *s) /* if reference array is full, remove the oldest as per the spec */ while (add_frame(s->ref_frames, MAX_REFERENCE_FRAMES, s->current_picture)) { av_log(s->avctx, AV_LOG_ERROR, "Reference frame overflow\n"); - remove_frame(s->ref_frames, s->ref_frames[0]->avframe->display_picture_number)->reference &= DELAYED_PIC_REF; + remove_frame(s->ref_frames, s->ref_frames[0]->picture_number)->reference &= DELAYED_PIC_REF; } } @@ -2090,7 +2091,7 @@ static int get_delayed_pic(DiracContext *s, AVFrame *picture, int *got_frame) /* find frame with lowest picture number */ for (i = 1; s->delay_frames[i]; i++) - if (s->delay_frames[i]->avframe->display_picture_number < out->avframe->display_picture_number) { + if (s->delay_frames[i]->picture_number < out->picture_number) { out = s->delay_frames[i]; out_idx = i; } @@ -2102,6 +2103,11 @@ static int get_delayed_pic(DiracContext *s, AVFrame *picture, int *got_frame) out->reference ^= DELAYED_PIC_REF; if((ret = av_frame_ref(picture, out->avframe)) < 0) return ret; +#if FF_API_FRAME_PICTURE_NUMBER +FF_DISABLE_DEPRECATION_WARNINGS + picture->display_picture_number = out->picture_number; +FF_ENABLE_DEPRECATION_WARNINGS +#endif *got_frame = 1; } @@ -2224,7 +2230,10 @@ static int dirac_decode_data_unit(AVCodecContext *avctx, const uint8_t *buf, int s->hq_picture = (parse_code & 0xF8) == 0xE8; /* [DIRAC_STD] is_hq_picture() */ s->dc_prediction = (parse_code & 0x28) == 0x08; /* [DIRAC_STD] using_dc_prediction() */ pic->reference = (parse_code & 0x0C) == 0x0C; /* [DIRAC_STD] is_reference() */ - pic->avframe->key_frame = s->num_refs == 0; /* [DIRAC_STD] is_intra() */ + if (s->num_refs == 0) /* [DIRAC_STD] is_intra() */ + pic->avframe->flags |= AV_FRAME_FLAG_KEY; + else + pic->avframe->flags &= ~AV_FRAME_FLAG_KEY; pic->avframe->pict_type = s->num_refs + 1; /* Definition of AVPictureType in avutil.h */ /* VC-2 Low Delay has a different parse code than the Dirac Low Delay */ @@ -2318,19 +2327,19 @@ static int dirac_decode_frame(AVCodecContext *avctx, AVFrame *picture, if (!s->current_picture) return buf_size; - if (s->current_picture->avframe->display_picture_number > s->frame_number) { + if (s->current_picture->picture_number > s->frame_number) { DiracFrame *delayed_frame = remove_frame(s->delay_frames, s->frame_number); s->current_picture->reference |= DELAYED_PIC_REF; if (add_frame(s->delay_frames, MAX_DELAY, s->current_picture)) { - int min_num = s->delay_frames[0]->avframe->display_picture_number; + unsigned min_num = s->delay_frames[0]->picture_number; /* Too many delayed frames, so we display the frame with the lowest pts */ av_log(avctx, AV_LOG_ERROR, "Delay frame overflow\n"); for (i = 1; s->delay_frames[i]; i++) - if (s->delay_frames[i]->avframe->display_picture_number < min_num) - min_num = s->delay_frames[i]->avframe->display_picture_number; + if (s->delay_frames[i]->picture_number < min_num) + min_num = s->delay_frames[i]->picture_number; delayed_frame = remove_frame(s->delay_frames, min_num); add_frame(s->delay_frames, MAX_DELAY, s->current_picture); @@ -2340,18 +2349,27 @@ static int dirac_decode_frame(AVCodecContext *avctx, AVFrame *picture, delayed_frame->reference ^= DELAYED_PIC_REF; if((ret = av_frame_ref(picture, delayed_frame->avframe)) < 0) return ret; + s->frame_number = delayed_frame->picture_number + 1LL; +#if FF_API_FRAME_PICTURE_NUMBER +FF_DISABLE_DEPRECATION_WARNINGS + picture->display_picture_number = delayed_frame->picture_number; +FF_ENABLE_DEPRECATION_WARNINGS +#endif *got_frame = 1; } - } else if (s->current_picture->avframe->display_picture_number == s->frame_number) { + } else if (s->current_picture->picture_number == s->frame_number) { /* The right frame at the right time :-) */ if((ret = av_frame_ref(picture, s->current_picture->avframe)) < 0) return ret; + s->frame_number = s->current_picture->picture_number + 1LL; +#if FF_API_FRAME_PICTURE_NUMBER +FF_DISABLE_DEPRECATION_WARNINGS + picture->display_picture_number = s->current_picture->picture_number; +FF_ENABLE_DEPRECATION_WARNINGS +#endif *got_frame = 1; } - if (*got_frame) - s->frame_number = picture->display_picture_number + 1LL; - return buf_idx; } diff --git a/libavcodec/dnxhddec.c b/libavcodec/dnxhddec.c index 7cc4f94c7f8..834390b93b3 100644 --- a/libavcodec/dnxhddec.c +++ b/libavcodec/dnxhddec.c @@ -195,8 +195,9 @@ static int dnxhd_decode_header(DNXHDContext *ctx, AVFrame *frame, } if (buf[5] & 2) { /* interlaced */ ctx->cur_field = first_field ? buf[5] & 1 : !ctx->cur_field; - frame->interlaced_frame = 1; - frame->top_field_first = first_field ^ ctx->cur_field; + frame->flags |= AV_FRAME_FLAG_INTERLACED; + if (first_field ^ ctx->cur_field) + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; av_log(ctx->avctx, AV_LOG_DEBUG, "interlaced %d, cur field %d\n", buf[5] & 3, ctx->cur_field); } else { @@ -298,7 +299,7 @@ static int dnxhd_decode_header(DNXHDContext *ctx, AVFrame *frame, ctx->mb_width = (ctx->width + 15)>> 4; ctx->mb_height = AV_RB16(buf + 0x16c); - if ((ctx->height + 15) >> 4 == ctx->mb_height && frame->interlaced_frame) + if ((ctx->height + 15) >> 4 == ctx->mb_height && (frame->flags & AV_FRAME_FLAG_INTERLACED)) ctx->height <<= 1; av_log(ctx->avctx, AV_LOG_VERBOSE, "%dx%d, 4:%s %d bits, MBAFF=%d ACT=%d\n", @@ -316,7 +317,7 @@ static int dnxhd_decode_header(DNXHDContext *ctx, AVFrame *frame, } ctx->data_offset = 0x280; } - if ((ctx->mb_height << frame->interlaced_frame) > (ctx->height + 15) >> 4) { + if ((ctx->mb_height << !!(frame->flags & AV_FRAME_FLAG_INTERLACED)) > (ctx->height + 15) >> 4) { av_log(ctx->avctx, AV_LOG_ERROR, "mb height too big: %d\n", ctx->mb_height); return AVERROR_INVALIDDATA; @@ -530,7 +531,7 @@ static int dnxhd_decode_macroblock(const DNXHDContext *ctx, RowContext *row, return AVERROR_INVALIDDATA; } - if (frame->interlaced_frame) { + if (frame->flags & AV_FRAME_FLAG_INTERLACED) { dct_linesize_luma <<= 1; dct_linesize_chroma <<= 1; } @@ -539,7 +540,7 @@ static int dnxhd_decode_macroblock(const DNXHDContext *ctx, RowContext *row, dest_u = frame->data[1] + ((y * dct_linesize_chroma) << 4) + (x << (3 + shift1 + ctx->is_444)); dest_v = frame->data[2] + ((y * dct_linesize_chroma) << 4) + (x << (3 + shift1 + ctx->is_444)); - if (frame->interlaced_frame && ctx->cur_field) { + if ((frame->flags & AV_FRAME_FLAG_INTERLACED) && ctx->cur_field) { dest_y += frame->linesize[0]; dest_u += frame->linesize[1]; dest_v += frame->linesize[2]; @@ -652,14 +653,14 @@ static int dnxhd_decode_frame(AVCodecContext *avctx, AVFrame *picture, if ((ret = ff_thread_get_buffer(avctx, picture, 0)) < 0) return ret; picture->pict_type = AV_PICTURE_TYPE_I; - picture->key_frame = 1; + picture->flags |= AV_FRAME_FLAG_KEY; } ctx->buf_size = buf_size - ctx->data_offset; ctx->buf = buf + ctx->data_offset; avctx->execute2(avctx, dnxhd_decode_row, picture, NULL, ctx->mb_height); - if (first_field && picture->interlaced_frame) { + if (first_field && (picture->flags & AV_FRAME_FLAG_INTERLACED)) { buf += ctx->cid_table->coding_unit_size; buf_size -= ctx->cid_table->coding_unit_size; first_field = 0; diff --git a/libavcodec/dnxhdenc.c b/libavcodec/dnxhdenc.c index b7dc54f86ae..a1852fa6b74 100644 --- a/libavcodec/dnxhdenc.c +++ b/libavcodec/dnxhdenc.c @@ -1251,7 +1251,8 @@ static void dnxhd_load_picture(DNXHDEncContext *ctx, const AVFrame *frame) ctx->thread[i]->dct_uv_offset = ctx->m.uvlinesize*8; } - ctx->cur_field = frame->interlaced_frame && !frame->top_field_first; + ctx->cur_field = (frame->flags & AV_FRAME_FLAG_INTERLACED) && + !(frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST); } static int dnxhd_encode_picture(AVCodecContext *avctx, AVPacket *pkt, @@ -1359,7 +1360,7 @@ const FFCodec ff_dnxhd_encoder = { .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_DNXHD, .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | - AV_CODEC_CAP_SLICE_THREADS, + AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(DNXHDEncContext), .init = dnxhd_encode_init, FF_CODEC_ENCODE_CB(dnxhd_encode_picture), diff --git a/libavcodec/dolby_e.c b/libavcodec/dolby_e.c index 921c33f3ba5..b8dac0fa3f8 100644 --- a/libavcodec/dolby_e.c +++ b/libavcodec/dolby_e.c @@ -1200,7 +1200,7 @@ static av_cold void init_tables(void) gain_tab[i] = exp2f((i - 960) / 64.0f); // short 1 - ff_kbd_window_init(window, 3.0f, 128); + avpriv_kbd_window_init(window, 3.0f, 128); for (i = 0; i < 128; i++) window[128 + i] = window[127 - i]; @@ -1227,7 +1227,7 @@ static av_cold void init_tables(void) window[1088 + i] = 1.0f; // long - ff_kbd_window_init(window + 1408, 3.0f, 256); + avpriv_kbd_window_init(window + 1408, 3.0f, 256); for (i = 0; i < 640; i++) window[1664 + i] = 1.0f; for (i = 0; i < 256; i++) diff --git a/libavcodec/dpcm.c b/libavcodec/dpcm.c index 2425f84eb9c..eff6587404d 100644 --- a/libavcodec/dpcm.c +++ b/libavcodec/dpcm.c @@ -45,7 +45,8 @@ typedef struct DPCMContext { int16_t array[256]; - int sample[2]; ///< previous sample (for SOL_DPCM) + int sample[2]; ///< previous sample (for SOL_DPCM and WADY_DPCM) + int scale; ///< scale for WADY_DPCM const int8_t *sol_table; ///< delta table for SOL_DPCM } DPCMContext; @@ -126,6 +127,24 @@ static const int16_t sol_table_16[128] = { 0xF00, 0x1000, 0x1400, 0x1800, 0x1C00, 0x2000, 0x3000, 0x4000 }; +static const int16_t wady_table[128] = { + 0, 2, 4, 6, 8, 10, 12, 15, + 18, 21, 24, 28, 32, 36, 40, 44, + 49, 54, 59, 64, 70, 76, 82, 88, + 95, 102, 109, 116, 124, 132, 140, 148, + 160, 170, 180, 190, 200, 210, 220, 230, + 240, 255, 270, 285, 300, 320, 340, 360, + 380, 400, 425, 450, 475, 500, 525, 550, + 580, 610, 650, 700, 750, 800, 900, 1000, + -0, -2, -4, -6, -8, -10, -12, -15, + -18, -21, -24, -28, -32, -36, -40, -44, + -49, -54, -59, -64, -70, -76, -82, -88, + -95, -102,-109,-116,-124,-132,-140,-148, + -160,-170,-180,-190,-200,-210,-220,-230, + -240,-255,-270,-285,-300,-320,-340,-360, + -380,-400,-425,-450,-475,-500,-525,-550, + -580,-610,-650,-700,-750,-800,-900,-1000, +}; static av_cold int dpcm_decode_init(AVCodecContext *avctx) { @@ -139,7 +158,7 @@ static av_cold int dpcm_decode_init(AVCodecContext *avctx) s->sample[0] = s->sample[1] = 0; - switch(avctx->codec->id) { + switch (avctx->codec->id) { case AV_CODEC_ID_ROQ_DPCM: /* initialize square table */ @@ -175,6 +194,13 @@ static av_cold int dpcm_decode_init(AVCodecContext *avctx) } break; + case AV_CODEC_ID_CBD2_DPCM: + for (i = -128; i < 128; i++) { + int16_t cube = (i * i * i) / 64; + s->array[i+128] = cube; + } + break; + case AV_CODEC_ID_GREMLIN_DPCM: { int delta = 0; int code = 64; @@ -193,6 +219,10 @@ static av_cold int dpcm_decode_init(AVCodecContext *avctx) } break; + case AV_CODEC_ID_WADY_DPCM: + s->scale = (avctx->extradata && avctx->extradata_size > 0) ? avctx->extradata[0] : 1; + break; + default: break; } @@ -239,8 +269,10 @@ static int dpcm_decode_frame(AVCodecContext *avctx, AVFrame *frame, else out = buf_size; break; + case AV_CODEC_ID_WADY_DPCM: case AV_CODEC_ID_DERF_DPCM: case AV_CODEC_ID_GREMLIN_DPCM: + case AV_CODEC_ID_CBD2_DPCM: case AV_CODEC_ID_SDX2_DPCM: out = buf_size; break; @@ -362,6 +394,7 @@ static int dpcm_decode_frame(AVCodecContext *avctx, AVFrame *frame, } break; + case AV_CODEC_ID_CBD2_DPCM: case AV_CODEC_ID_SDX2_DPCM: while (output_samples < samples_end) { int8_t n = bytestream2_get_byteu(&gb); @@ -401,6 +434,22 @@ static int dpcm_decode_frame(AVCodecContext *avctx, AVFrame *frame, } } break; + + case AV_CODEC_ID_WADY_DPCM: { + int idx = 0; + + while (output_samples < samples_end) { + const uint8_t n = bytestream2_get_byteu(&gb); + + if (n & 0x80) + s->sample[idx] = sign_extend((n & 0x7f) << 9, 16); + else + s->sample[idx] += s->scale * (unsigned)wady_table[n & 0x7f]; + *output_samples++ = av_clip_int16(s->sample[idx]); + idx ^= stereo; + } + } + break; } *got_frame_ptr = 1; @@ -408,6 +457,13 @@ static int dpcm_decode_frame(AVCodecContext *avctx, AVFrame *frame, return avpkt->size; } +static void dpcm_flush(AVCodecContext *avctx) +{ + DPCMContext *s = avctx->priv_data; + + s->sample[0] = s->sample[1] = 0; +} + #define DPCM_DECODER(id_, name_, long_name_) \ const FFCodec ff_ ## name_ ## _decoder = { \ .p.name = #name_, \ @@ -417,9 +473,11 @@ const FFCodec ff_ ## name_ ## _decoder = { \ .p.capabilities = AV_CODEC_CAP_DR1, \ .priv_data_size = sizeof(DPCMContext), \ .init = dpcm_decode_init, \ + .flush = dpcm_flush, \ FF_CODEC_DECODE_CB(dpcm_decode_frame), \ } +DPCM_DECODER(AV_CODEC_ID_CBD2_DPCM, cbd2_dpcm, "DPCM Cuberoot-Delta-Exact"); DPCM_DECODER(AV_CODEC_ID_DERF_DPCM, derf_dpcm, "DPCM Xilam DERF"); DPCM_DECODER(AV_CODEC_ID_GREMLIN_DPCM, gremlin_dpcm, "DPCM Gremlin"); DPCM_DECODER(AV_CODEC_ID_INTERPLAY_DPCM, interplay_dpcm, "DPCM Interplay"); @@ -427,3 +485,4 @@ DPCM_DECODER(AV_CODEC_ID_ROQ_DPCM, roq_dpcm, "DPCM id RoQ"); DPCM_DECODER(AV_CODEC_ID_SDX2_DPCM, sdx2_dpcm, "DPCM Squareroot-Delta-Exact"); DPCM_DECODER(AV_CODEC_ID_SOL_DPCM, sol_dpcm, "DPCM Sol"); DPCM_DECODER(AV_CODEC_ID_XAN_DPCM, xan_dpcm, "DPCM Xan"); +DPCM_DECODER(AV_CODEC_ID_WADY_DPCM, wady_dpcm, "DPCM Marble WADY"); diff --git a/libavcodec/dpx.c b/libavcodec/dpx.c index 4f50608461e..31e4a3f82c4 100644 --- a/libavcodec/dpx.c +++ b/libavcodec/dpx.c @@ -476,14 +476,31 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, avctx->colorspace = AVCOL_SPC_RGB; } + av_strlcpy(creator, avpkt->data + 160, 100); + creator[100] = '\0'; + av_dict_set(&p->metadata, "Creator", creator, 0); + + av_strlcpy(input_device, avpkt->data + 1556, 32); + input_device[32] = '\0'; + av_dict_set(&p->metadata, "Input Device", input_device, 0); + + // Some devices do not pad 10bit samples to whole 32bit words per row + if (!memcmp(input_device, "Scanity", 7) || + !memcmp(creator, "Lasergraphics Inc.", 18)) { + if (bits_per_color == 10) + unpadded_10bit = 1; + } + // Table 3c: Runs will always break at scan line boundaries. Packing // will always break to the next 32-bit word at scan-line boundaries. // Unfortunately, the encoder produced invalid files, so attempt // to detect it + // Also handle special case with unpadded content need_align = FFALIGN(stride, 4); - if (need_align*avctx->height + (int64_t)offset > avpkt->size) { + if (need_align*avctx->height + (int64_t)offset > avpkt->size && + (!unpadded_10bit || (avctx->width * avctx->height * elements + 2) / 3 * 4 + (int64_t)offset > avpkt->size)) { // Alignment seems unappliable, try without - if (stride*avctx->height + (int64_t)offset > avpkt->size) { + if (stride*avctx->height + (int64_t)offset > avpkt->size || unpadded_10bit) { av_log(avctx, AV_LOG_ERROR, "Overread buffer. Invalid header?\n"); return AVERROR_INVALIDDATA; } else { @@ -609,20 +626,6 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; - av_strlcpy(creator, avpkt->data + 160, 100); - creator[100] = '\0'; - av_dict_set(&p->metadata, "Creator", creator, 0); - - av_strlcpy(input_device, avpkt->data + 1556, 32); - input_device[32] = '\0'; - av_dict_set(&p->metadata, "Input Device", input_device, 0); - - // Some devices do not pad 10bit samples to whole 32bit words per row - if (!memcmp(input_device, "Scanity", 7) || - !memcmp(creator, "Lasergraphics Inc.", 18)) { - unpadded_10bit = 1; - } - // Move pointer to offset from start of file buf = avpkt->data + offset; diff --git a/libavcodec/dsicinvideo.c b/libavcodec/dsicinvideo.c index 222044d125a..000d79e169e 100644 --- a/libavcodec/dsicinvideo.c +++ b/libavcodec/dsicinvideo.c @@ -293,7 +293,11 @@ static int cinvideo_decode_frame(AVCodecContext *avctx, AVFrame *rframe, return res; memcpy(cin->frame->data[1], cin->palette, sizeof(cin->palette)); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS cin->frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif for (y = 0; y < cin->avctx->height; ++y) memcpy(cin->frame->data[0] + (cin->avctx->height - 1 - y) * cin->frame->linesize[0], cin->bitmap_table[CIN_CUR_BMP] + y * cin->avctx->width, diff --git a/libavcodec/dv.h b/libavcodec/dv.h index 29f97b6089a..abff9f1ea9a 100644 --- a/libavcodec/dv.h +++ b/libavcodec/dv.h @@ -60,6 +60,12 @@ enum DVPackType { */ #define DV_MAX_FRAME_SIZE 576000 +// LCM of video framerate numerators +#define DV_TIMESCALE_VIDEO 60000 + +// LCM of audio sample rates +#define DV_TIMESCALE_AUDIO 14112000 + /** * maximum number of blocks per macroblock in any DV format */ diff --git a/libavcodec/dvdec.c b/libavcodec/dvdec.c index afc4bb0bcdd..c57578a2089 100644 --- a/libavcodec/dvdec.c +++ b/libavcodec/dvdec.c @@ -646,10 +646,13 @@ static int dvvideo_decode_frame(AVCodecContext *avctx, AVFrame *frame, } s->frame = frame; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; avctx->pix_fmt = s->sys->pix_fmt; avctx->framerate = av_inv_q(s->sys->time_base); + avctx->bit_rate = av_rescale_q(s->sys->frame_size, + (AVRational) { 8, 1 }, + s->sys->time_base); ret = ff_set_dimensions(avctx, s->sys->width, s->sys->height); if (ret < 0) @@ -670,14 +673,14 @@ static int dvvideo_decode_frame(AVCodecContext *avctx, AVFrame *frame, /* Determine the codec's field order from the packet */ if ( *vsc_pack == DV_VIDEO_CONTROL ) { if (avctx->height == 720) { - frame->interlaced_frame = 0; - frame->top_field_first = 0; + frame->flags &= ~AV_FRAME_FLAG_INTERLACED; + frame->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; } else if (avctx->height == 1080) { - frame->interlaced_frame = 1; - frame->top_field_first = (vsc_pack[3] & 0x40) == 0x40; + frame->flags |= AV_FRAME_FLAG_INTERLACED; + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST * ((vsc_pack[3] & 0x40) == 0x40); } else { - frame->interlaced_frame = (vsc_pack[3] & 0x10) == 0x10; - frame->top_field_first = !(vsc_pack[3] & 0x40); + frame->flags |= AV_FRAME_FLAG_INTERLACED * ((vsc_pack[3] & 0x10) == 0x10); + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST * !(vsc_pack[3] & 0x40); } } diff --git a/libavcodec/dvdsubenc.c b/libavcodec/dvdsubenc.c index 0874aaa02dd..d272b576757 100644 --- a/libavcodec/dvdsubenc.c +++ b/libavcodec/dvdsubenc.c @@ -379,7 +379,7 @@ static int encode_dvd_subtitles(AVCodecContext *avctx, if (x2 > avctx->width || y2 > avctx->height) { av_log(avctx, AV_LOG_ERROR, "canvas_size(%d:%d) is too small(%d:%d) for render\n", avctx->width, avctx->height, x2, y2); - ret = AVERROR(EINVAL);; + ret = AVERROR(EINVAL); goto fail; } *q++ = 0x05; diff --git a/libavcodec/dvenc.c b/libavcodec/dvenc.c index 4c747ef71ff..7bd50174b7f 100644 --- a/libavcodec/dvenc.c +++ b/libavcodec/dvenc.c @@ -104,7 +104,9 @@ static av_cold int dvvideo_encode_init(AVCodecContext *avctx) ff_fdctdsp_init(&fdsp, avctx); ff_me_cmp_init(&mecc, avctx); ff_pixblockdsp_init(&pdsp, avctx); - ff_set_cmp(&mecc, mecc.ildct_cmp, avctx->ildct_cmp); + ret = ff_set_cmp(&mecc, mecc.ildct_cmp, avctx->ildct_cmp); + if (ret < 0) + return AVERROR(EINVAL); s->get_pixels = pdsp.get_pixels; s->ildct_cmp = mecc.ildct_cmp[5]; @@ -1046,9 +1048,9 @@ static inline int dv_write_pack(enum DVPackType pack_id, DVEncContext *c, int fs; if (c->avctx->height >= 720) - fs = c->avctx->height == 720 || c->frame->top_field_first ? 0x40 : 0x00; + fs = c->avctx->height == 720 || (c->frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? 0x40 : 0x00; else - fs = c->frame->top_field_first ? 0x00 : 0x40; + fs = (c->frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? 0x00 : 0x40; if (DV_PROFILE_IS_HD(c->sys) || (int)(av_q2d(c->avctx->sample_aspect_ratio) * @@ -1144,7 +1146,7 @@ static void dv_format_frame(DVEncContext *c, uint8_t *buf) { int chan, i, j, k; /* We work with 720p frames split in half. The odd half-frame is chan 2,3 */ - int chan_offset = 2*(c->sys->height == 720 && c->avctx->frame_number & 1); + int chan_offset = 2*(c->sys->height == 720 && c->avctx->frame_num & 1); for (chan = 0; chan < c->sys->n_difchan; chan++) { for (i = 0; i < c->sys->difseg_size; i++) { @@ -1239,7 +1241,8 @@ const FFCodec ff_dvvideo_encoder = { .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_DVVIDEO, .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | - AV_CODEC_CAP_SLICE_THREADS, + AV_CODEC_CAP_SLICE_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(DVEncContext), .init = dvvideo_encode_init, FF_CODEC_ENCODE_CB(dvvideo_encode_frame), diff --git a/libavcodec/dxa.c b/libavcodec/dxa.c index 8d2d2d771ba..d903b7ecd46 100644 --- a/libavcodec/dxa.c +++ b/libavcodec/dxa.c @@ -230,7 +230,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if ((ret = ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF)) < 0) return ret; memcpy(frame->data[1], c->pal, AVPALETTE_SIZE); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = pc; +FF_ENABLE_DEPRECATION_WARNINGS +#endif outptr = frame->data[0]; srcptr = c->decomp_buf; @@ -258,19 +262,19 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, switch(compr){ case -1: - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_P; if (c->prev->data[0]) memcpy(frame->data[0], c->prev->data[0], frame->linesize[0] * avctx->height); else{ // Should happen only when first frame is 'NULL' memset(frame->data[0], 0, frame->linesize[0] * avctx->height); - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; } break; case 2: case 4: - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; for (j = 0; j < avctx->height; j++) { memcpy(outptr, srcptr, avctx->width); @@ -285,7 +289,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if (!(avctx->flags2 & AV_CODEC_FLAG2_SHOW_ALL)) return AVERROR_INVALIDDATA; } - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_P; for (j = 0; j < avctx->height; j++) { if(tmpptr){ @@ -300,7 +304,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, break; case 12: // ScummVM coding case 13: - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_P; if (!c->prev->data[0]) { av_log(avctx, AV_LOG_ERROR, "Missing reference frame\n"); diff --git a/libavcodec/dxtory.c b/libavcodec/dxtory.c index e13d274862e..f36420cdd9c 100644 --- a/libavcodec/dxtory.c +++ b/libavcodec/dxtory.c @@ -864,7 +864,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *pic, return ret; pic->pict_type = AV_PICTURE_TYPE_I; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; diff --git a/libavcodec/dxv.c b/libavcodec/dxv.c index 7c84874229b..5923811b297 100644 --- a/libavcodec/dxv.c +++ b/libavcodec/dxv.c @@ -1220,7 +1220,7 @@ static int dxv_decode(AVCodecContext *avctx, AVFrame *frame, /* Frame is ready to be output. */ frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; diff --git a/libavcodec/dxva2_av1.c b/libavcodec/dxva2_av1.c index 228f72ba18e..ab118a43566 100644 --- a/libavcodec/dxva2_av1.c +++ b/libavcodec/dxva2_av1.c @@ -27,6 +27,7 @@ #include "dxva2_internal.h" #include "av1dec.h" +#include "hwaccel_internal.h" #define MAX_TILES 256 @@ -457,11 +458,11 @@ static int dxva2_av1_uninit(AVCodecContext *avctx) } #if CONFIG_AV1_DXVA2_HWACCEL -const AVHWAccel ff_av1_dxva2_hwaccel = { - .name = "av1_dxva2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_AV1, - .pix_fmt = AV_PIX_FMT_DXVA2_VLD, +const FFHWAccel ff_av1_dxva2_hwaccel = { + .p.name = "av1_dxva2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_AV1, + .p.pix_fmt = AV_PIX_FMT_DXVA2_VLD, .init = ff_dxva2_decode_init, .uninit = dxva2_av1_uninit, .start_frame = dxva2_av1_start_frame, @@ -474,11 +475,11 @@ const AVHWAccel ff_av1_dxva2_hwaccel = { #endif #if CONFIG_AV1_D3D11VA_HWACCEL -const AVHWAccel ff_av1_d3d11va_hwaccel = { - .name = "av1_d3d11va", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_AV1, - .pix_fmt = AV_PIX_FMT_D3D11VA_VLD, +const FFHWAccel ff_av1_d3d11va_hwaccel = { + .p.name = "av1_d3d11va", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_AV1, + .p.pix_fmt = AV_PIX_FMT_D3D11VA_VLD, .init = ff_dxva2_decode_init, .uninit = dxva2_av1_uninit, .start_frame = dxva2_av1_start_frame, @@ -491,11 +492,11 @@ const AVHWAccel ff_av1_d3d11va_hwaccel = { #endif #if CONFIG_AV1_D3D11VA2_HWACCEL -const AVHWAccel ff_av1_d3d11va2_hwaccel = { - .name = "av1_d3d11va2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_AV1, - .pix_fmt = AV_PIX_FMT_D3D11, +const FFHWAccel ff_av1_d3d11va2_hwaccel = { + .p.name = "av1_d3d11va2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_AV1, + .p.pix_fmt = AV_PIX_FMT_D3D11, .init = ff_dxva2_decode_init, .uninit = dxva2_av1_uninit, .start_frame = dxva2_av1_start_frame, diff --git a/libavcodec/dxva2_h264.c b/libavcodec/dxva2_h264.c index 6300b1418df..20e64f848d8 100644 --- a/libavcodec/dxva2_h264.c +++ b/libavcodec/dxva2_h264.c @@ -28,6 +28,7 @@ #include "h264dec.h" #include "h264data.h" #include "h264_ps.h" +#include "hwaccel_internal.h" #include "mpegutils.h" struct dxva2_picture_context { @@ -516,11 +517,11 @@ static int dxva2_h264_end_frame(AVCodecContext *avctx) } #if CONFIG_H264_DXVA2_HWACCEL -const AVHWAccel ff_h264_dxva2_hwaccel = { - .name = "h264_dxva2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_H264, - .pix_fmt = AV_PIX_FMT_DXVA2_VLD, +const FFHWAccel ff_h264_dxva2_hwaccel = { + .p.name = "h264_dxva2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_H264, + .p.pix_fmt = AV_PIX_FMT_DXVA2_VLD, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_h264_start_frame, @@ -533,11 +534,11 @@ const AVHWAccel ff_h264_dxva2_hwaccel = { #endif #if CONFIG_H264_D3D11VA_HWACCEL -const AVHWAccel ff_h264_d3d11va_hwaccel = { - .name = "h264_d3d11va", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_H264, - .pix_fmt = AV_PIX_FMT_D3D11VA_VLD, +const FFHWAccel ff_h264_d3d11va_hwaccel = { + .p.name = "h264_d3d11va", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_H264, + .p.pix_fmt = AV_PIX_FMT_D3D11VA_VLD, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_h264_start_frame, @@ -550,11 +551,11 @@ const AVHWAccel ff_h264_d3d11va_hwaccel = { #endif #if CONFIG_H264_D3D11VA2_HWACCEL -const AVHWAccel ff_h264_d3d11va2_hwaccel = { - .name = "h264_d3d11va2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_H264, - .pix_fmt = AV_PIX_FMT_D3D11, +const FFHWAccel ff_h264_d3d11va2_hwaccel = { + .p.name = "h264_d3d11va2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_H264, + .p.pix_fmt = AV_PIX_FMT_D3D11, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_h264_start_frame, diff --git a/libavcodec/dxva2_hevc.c b/libavcodec/dxva2_hevc.c index 6b239d9917c..b6c08943f0f 100644 --- a/libavcodec/dxva2_hevc.c +++ b/libavcodec/dxva2_hevc.c @@ -27,6 +27,7 @@ #include "dxva2_internal.h" #include "hevc_data.h" #include "hevcdec.h" +#include "hwaccel_internal.h" #define MAX_SLICES 256 @@ -420,11 +421,11 @@ static int dxva2_hevc_end_frame(AVCodecContext *avctx) } #if CONFIG_HEVC_DXVA2_HWACCEL -const AVHWAccel ff_hevc_dxva2_hwaccel = { - .name = "hevc_dxva2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_HEVC, - .pix_fmt = AV_PIX_FMT_DXVA2_VLD, +const FFHWAccel ff_hevc_dxva2_hwaccel = { + .p.name = "hevc_dxva2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_HEVC, + .p.pix_fmt = AV_PIX_FMT_DXVA2_VLD, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_hevc_start_frame, @@ -437,11 +438,11 @@ const AVHWAccel ff_hevc_dxva2_hwaccel = { #endif #if CONFIG_HEVC_D3D11VA_HWACCEL -const AVHWAccel ff_hevc_d3d11va_hwaccel = { - .name = "hevc_d3d11va", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_HEVC, - .pix_fmt = AV_PIX_FMT_D3D11VA_VLD, +const FFHWAccel ff_hevc_d3d11va_hwaccel = { + .p.name = "hevc_d3d11va", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_HEVC, + .p.pix_fmt = AV_PIX_FMT_D3D11VA_VLD, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_hevc_start_frame, @@ -454,11 +455,11 @@ const AVHWAccel ff_hevc_d3d11va_hwaccel = { #endif #if CONFIG_HEVC_D3D11VA2_HWACCEL -const AVHWAccel ff_hevc_d3d11va2_hwaccel = { - .name = "hevc_d3d11va2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_HEVC, - .pix_fmt = AV_PIX_FMT_D3D11, +const FFHWAccel ff_hevc_d3d11va2_hwaccel = { + .p.name = "hevc_d3d11va2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_HEVC, + .p.pix_fmt = AV_PIX_FMT_D3D11, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_hevc_start_frame, diff --git a/libavcodec/dxva2_mpeg2.c b/libavcodec/dxva2_mpeg2.c index 1989c588dc4..75c416654fd 100644 --- a/libavcodec/dxva2_mpeg2.c +++ b/libavcodec/dxva2_mpeg2.c @@ -25,6 +25,7 @@ #include "libavutil/log.h" #include "dxva2_internal.h" +#include "hwaccel_internal.h" #include "mpegutils.h" #include "mpegvideodec.h" @@ -316,11 +317,11 @@ static int dxva2_mpeg2_end_frame(AVCodecContext *avctx) } #if CONFIG_MPEG2_DXVA2_HWACCEL -const AVHWAccel ff_mpeg2_dxva2_hwaccel = { - .name = "mpeg2_dxva2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG2VIDEO, - .pix_fmt = AV_PIX_FMT_DXVA2_VLD, +const FFHWAccel ff_mpeg2_dxva2_hwaccel = { + .p.name = "mpeg2_dxva2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG2VIDEO, + .p.pix_fmt = AV_PIX_FMT_DXVA2_VLD, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_mpeg2_start_frame, @@ -333,11 +334,11 @@ const AVHWAccel ff_mpeg2_dxva2_hwaccel = { #endif #if CONFIG_MPEG2_D3D11VA_HWACCEL -const AVHWAccel ff_mpeg2_d3d11va_hwaccel = { - .name = "mpeg2_d3d11va", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG2VIDEO, - .pix_fmt = AV_PIX_FMT_D3D11VA_VLD, +const FFHWAccel ff_mpeg2_d3d11va_hwaccel = { + .p.name = "mpeg2_d3d11va", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG2VIDEO, + .p.pix_fmt = AV_PIX_FMT_D3D11VA_VLD, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_mpeg2_start_frame, @@ -350,11 +351,11 @@ const AVHWAccel ff_mpeg2_d3d11va_hwaccel = { #endif #if CONFIG_MPEG2_D3D11VA2_HWACCEL -const AVHWAccel ff_mpeg2_d3d11va2_hwaccel = { - .name = "mpeg2_d3d11va2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG2VIDEO, - .pix_fmt = AV_PIX_FMT_D3D11, +const FFHWAccel ff_mpeg2_d3d11va2_hwaccel = { + .p.name = "mpeg2_d3d11va2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG2VIDEO, + .p.pix_fmt = AV_PIX_FMT_D3D11, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_mpeg2_start_frame, diff --git a/libavcodec/dxva2_vc1.c b/libavcodec/dxva2_vc1.c index 12e3de59ec2..b35fb115f70 100644 --- a/libavcodec/dxva2_vc1.c +++ b/libavcodec/dxva2_vc1.c @@ -23,6 +23,7 @@ #include "config_components.h" #include "dxva2_internal.h" +#include "hwaccel_internal.h" #include "mpegutils.h" #include "mpegvideodec.h" #include "vc1.h" @@ -376,11 +377,11 @@ static int dxva2_vc1_end_frame(AVCodecContext *avctx) } #if CONFIG_WMV3_DXVA2_HWACCEL -const AVHWAccel ff_wmv3_dxva2_hwaccel = { - .name = "wmv3_dxva2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_WMV3, - .pix_fmt = AV_PIX_FMT_DXVA2_VLD, +const FFHWAccel ff_wmv3_dxva2_hwaccel = { + .p.name = "wmv3_dxva2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_WMV3, + .p.pix_fmt = AV_PIX_FMT_DXVA2_VLD, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_vc1_start_frame, @@ -393,11 +394,11 @@ const AVHWAccel ff_wmv3_dxva2_hwaccel = { #endif #if CONFIG_VC1_DXVA2_HWACCEL -const AVHWAccel ff_vc1_dxva2_hwaccel = { - .name = "vc1_dxva2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VC1, - .pix_fmt = AV_PIX_FMT_DXVA2_VLD, +const FFHWAccel ff_vc1_dxva2_hwaccel = { + .p.name = "vc1_dxva2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VC1, + .p.pix_fmt = AV_PIX_FMT_DXVA2_VLD, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_vc1_start_frame, @@ -410,11 +411,11 @@ const AVHWAccel ff_vc1_dxva2_hwaccel = { #endif #if CONFIG_WMV3_D3D11VA_HWACCEL -const AVHWAccel ff_wmv3_d3d11va_hwaccel = { - .name = "wmv3_d3d11va", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_WMV3, - .pix_fmt = AV_PIX_FMT_D3D11VA_VLD, +const FFHWAccel ff_wmv3_d3d11va_hwaccel = { + .p.name = "wmv3_d3d11va", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_WMV3, + .p.pix_fmt = AV_PIX_FMT_D3D11VA_VLD, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_vc1_start_frame, @@ -427,11 +428,11 @@ const AVHWAccel ff_wmv3_d3d11va_hwaccel = { #endif #if CONFIG_WMV3_D3D11VA2_HWACCEL -const AVHWAccel ff_wmv3_d3d11va2_hwaccel = { - .name = "wmv3_d3d11va2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_WMV3, - .pix_fmt = AV_PIX_FMT_D3D11, +const FFHWAccel ff_wmv3_d3d11va2_hwaccel = { + .p.name = "wmv3_d3d11va2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_WMV3, + .p.pix_fmt = AV_PIX_FMT_D3D11, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_vc1_start_frame, @@ -444,11 +445,11 @@ const AVHWAccel ff_wmv3_d3d11va2_hwaccel = { #endif #if CONFIG_VC1_D3D11VA_HWACCEL -const AVHWAccel ff_vc1_d3d11va_hwaccel = { - .name = "vc1_d3d11va", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VC1, - .pix_fmt = AV_PIX_FMT_D3D11VA_VLD, +const FFHWAccel ff_vc1_d3d11va_hwaccel = { + .p.name = "vc1_d3d11va", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VC1, + .p.pix_fmt = AV_PIX_FMT_D3D11VA_VLD, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_vc1_start_frame, @@ -461,11 +462,11 @@ const AVHWAccel ff_vc1_d3d11va_hwaccel = { #endif #if CONFIG_VC1_D3D11VA2_HWACCEL -const AVHWAccel ff_vc1_d3d11va2_hwaccel = { - .name = "vc1_d3d11va2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VC1, - .pix_fmt = AV_PIX_FMT_D3D11, +const FFHWAccel ff_vc1_d3d11va2_hwaccel = { + .p.name = "vc1_d3d11va2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VC1, + .p.pix_fmt = AV_PIX_FMT_D3D11, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_vc1_start_frame, diff --git a/libavcodec/dxva2_vp9.c b/libavcodec/dxva2_vp9.c index dbe6c08ad19..eba4df9031b 100644 --- a/libavcodec/dxva2_vp9.c +++ b/libavcodec/dxva2_vp9.c @@ -26,6 +26,7 @@ #include "libavutil/pixdesc.h" #include "dxva2_internal.h" +#include "hwaccel_internal.h" #include "vp9shared.h" struct vp9_dxva2_picture_context { @@ -307,11 +308,11 @@ static int dxva2_vp9_end_frame(AVCodecContext *avctx) } #if CONFIG_VP9_DXVA2_HWACCEL -const AVHWAccel ff_vp9_dxva2_hwaccel = { - .name = "vp9_dxva2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VP9, - .pix_fmt = AV_PIX_FMT_DXVA2_VLD, +const FFHWAccel ff_vp9_dxva2_hwaccel = { + .p.name = "vp9_dxva2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VP9, + .p.pix_fmt = AV_PIX_FMT_DXVA2_VLD, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_vp9_start_frame, @@ -324,11 +325,11 @@ const AVHWAccel ff_vp9_dxva2_hwaccel = { #endif #if CONFIG_VP9_D3D11VA_HWACCEL -const AVHWAccel ff_vp9_d3d11va_hwaccel = { - .name = "vp9_d3d11va", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VP9, - .pix_fmt = AV_PIX_FMT_D3D11VA_VLD, +const FFHWAccel ff_vp9_d3d11va_hwaccel = { + .p.name = "vp9_d3d11va", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VP9, + .p.pix_fmt = AV_PIX_FMT_D3D11VA_VLD, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_vp9_start_frame, @@ -341,11 +342,11 @@ const AVHWAccel ff_vp9_d3d11va_hwaccel = { #endif #if CONFIG_VP9_D3D11VA2_HWACCEL -const AVHWAccel ff_vp9_d3d11va2_hwaccel = { - .name = "vp9_d3d11va2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VP9, - .pix_fmt = AV_PIX_FMT_D3D11, +const FFHWAccel ff_vp9_d3d11va2_hwaccel = { + .p.name = "vp9_d3d11va2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VP9, + .p.pix_fmt = AV_PIX_FMT_D3D11, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_vp9_start_frame, diff --git a/libavcodec/dynamic_hdr10_plus.c b/libavcodec/dynamic_hdr10_plus.c deleted file mode 100644 index 34a44aac655..00000000000 --- a/libavcodec/dynamic_hdr10_plus.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * 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 "dynamic_hdr10_plus.h" -#include "get_bits.h" - -static const int64_t luminance_den = 1; -static const int32_t peak_luminance_den = 15; -static const int64_t rgb_den = 100000; -static const int32_t fraction_pixel_den = 1000; -static const int32_t knee_point_den = 4095; -static const int32_t bezier_anchor_den = 1023; -static const int32_t saturation_weight_den = 8; - -int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t *data, - int size) -{ - GetBitContext gbc, *gb = &gbc; - int ret; - - if (!s) - return AVERROR(ENOMEM); - - ret = init_get_bits8(gb, data, size); - if (ret < 0) - return ret; - - if (get_bits_left(gb) < 10) - return AVERROR_INVALIDDATA; - - s->application_version = get_bits(gb, 8); - s->num_windows = get_bits(gb, 2); - - if (s->num_windows < 1 || s->num_windows > 3) { - return AVERROR_INVALIDDATA; - } - - if (get_bits_left(gb) < ((19 * 8 + 1) * (s->num_windows - 1))) - return AVERROR_INVALIDDATA; - - for (int w = 1; w < s->num_windows; w++) { - // The corners are set to absolute coordinates here. They should be - // converted to the relative coordinates (in [0, 1]) in the decoder. - AVHDRPlusColorTransformParams *params = &s->params[w]; - params->window_upper_left_corner_x = - (AVRational){get_bits(gb, 16), 1}; - params->window_upper_left_corner_y = - (AVRational){get_bits(gb, 16), 1}; - params->window_lower_right_corner_x = - (AVRational){get_bits(gb, 16), 1}; - params->window_lower_right_corner_y = - (AVRational){get_bits(gb, 16), 1}; - - params->center_of_ellipse_x = get_bits(gb, 16); - params->center_of_ellipse_y = get_bits(gb, 16); - params->rotation_angle = get_bits(gb, 8); - params->semimajor_axis_internal_ellipse = get_bits(gb, 16); - params->semimajor_axis_external_ellipse = get_bits(gb, 16); - params->semiminor_axis_external_ellipse = get_bits(gb, 16); - params->overlap_process_option = get_bits1(gb); - } - - if (get_bits_left(gb) < 28) - return AVERROR_INVALIDDATA; - - s->targeted_system_display_maximum_luminance = - (AVRational){get_bits_long(gb, 27), luminance_den}; - s->targeted_system_display_actual_peak_luminance_flag = get_bits1(gb); - - if (s->targeted_system_display_actual_peak_luminance_flag) { - int rows, cols; - if (get_bits_left(gb) < 10) - return AVERROR_INVALIDDATA; - rows = get_bits(gb, 5); - cols = get_bits(gb, 5); - if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols > 25))) { - return AVERROR_INVALIDDATA; - } - s->num_rows_targeted_system_display_actual_peak_luminance = rows; - s->num_cols_targeted_system_display_actual_peak_luminance = cols; - - if (get_bits_left(gb) < (rows * cols * 4)) - return AVERROR_INVALIDDATA; - - for (int i = 0; i < rows; i++) { - for (int j = 0; j < cols; j++) { - s->targeted_system_display_actual_peak_luminance[i][j] = - (AVRational){get_bits(gb, 4), peak_luminance_den}; - } - } - } - for (int w = 0; w < s->num_windows; w++) { - AVHDRPlusColorTransformParams *params = &s->params[w]; - if (get_bits_left(gb) < (3 * 17 + 17 + 4)) - return AVERROR_INVALIDDATA; - - for (int i = 0; i < 3; i++) { - params->maxscl[i] = - (AVRational){get_bits(gb, 17), rgb_den}; - } - params->average_maxrgb = - (AVRational){get_bits(gb, 17), rgb_den}; - params->num_distribution_maxrgb_percentiles = get_bits(gb, 4); - - if (get_bits_left(gb) < - (params->num_distribution_maxrgb_percentiles * 24)) - return AVERROR_INVALIDDATA; - - for (int i = 0; i < params->num_distribution_maxrgb_percentiles; i++) { - params->distribution_maxrgb[i].percentage = get_bits(gb, 7); - params->distribution_maxrgb[i].percentile = - (AVRational){get_bits(gb, 17), rgb_den}; - } - - if (get_bits_left(gb) < 10) - return AVERROR_INVALIDDATA; - - params->fraction_bright_pixels = (AVRational){get_bits(gb, 10), fraction_pixel_den}; - } - if (get_bits_left(gb) < 1) - return AVERROR_INVALIDDATA; - s->mastering_display_actual_peak_luminance_flag = get_bits1(gb); - if (s->mastering_display_actual_peak_luminance_flag) { - int rows, cols; - if (get_bits_left(gb) < 10) - return AVERROR_INVALIDDATA; - rows = get_bits(gb, 5); - cols = get_bits(gb, 5); - if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols > 25))) { - return AVERROR_INVALIDDATA; - } - s->num_rows_mastering_display_actual_peak_luminance = rows; - s->num_cols_mastering_display_actual_peak_luminance = cols; - - if (get_bits_left(gb) < (rows * cols * 4)) - return AVERROR_INVALIDDATA; - - for (int i = 0; i < rows; i++) { - for (int j = 0; j < cols; j++) { - s->mastering_display_actual_peak_luminance[i][j] = - (AVRational){get_bits(gb, 4), peak_luminance_den}; - } - } - } - - for (int w = 0; w < s->num_windows; w++) { - AVHDRPlusColorTransformParams *params = &s->params[w]; - if (get_bits_left(gb) < 1) - return AVERROR_INVALIDDATA; - - params->tone_mapping_flag = get_bits1(gb); - if (params->tone_mapping_flag) { - if (get_bits_left(gb) < 28) - return AVERROR_INVALIDDATA; - - params->knee_point_x = - (AVRational){get_bits(gb, 12), knee_point_den}; - params->knee_point_y = - (AVRational){get_bits(gb, 12), knee_point_den}; - params->num_bezier_curve_anchors = get_bits(gb, 4); - - if (get_bits_left(gb) < (params->num_bezier_curve_anchors * 10)) - return AVERROR_INVALIDDATA; - - for (int i = 0; i < params->num_bezier_curve_anchors; i++) { - params->bezier_curve_anchors[i] = - (AVRational){get_bits(gb, 10), bezier_anchor_den}; - } - } - - if (get_bits_left(gb) < 1) - return AVERROR_INVALIDDATA; - params->color_saturation_mapping_flag = get_bits1(gb); - if (params->color_saturation_mapping_flag) { - if (get_bits_left(gb) < 6) - return AVERROR_INVALIDDATA; - params->color_saturation_weight = - (AVRational){get_bits(gb, 6), saturation_weight_den}; - } - } - - return 0; -} diff --git a/libavcodec/dynamic_hdr10_plus.h b/libavcodec/dynamic_hdr10_plus.h deleted file mode 100644 index cd7acf04329..00000000000 --- a/libavcodec/dynamic_hdr10_plus.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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_DYNAMIC_HDR10_PLUS_H -#define AVCODEC_DYNAMIC_HDR10_PLUS_H - -#include "libavutil/hdr_dynamic_metadata.h" - -/** - * Parse the user data registered ITU-T T.35 to AVbuffer (AVDynamicHDRPlus). - * @param s A pointer containing the decoded AVDynamicHDRPlus structure. - * @param data The byte array containing the raw ITU-T T.35 data. - * @param size Size of the data array in bytes. - * - * @return 0 if succeed. Otherwise, returns the appropriate AVERROR. - */ -int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t *data, - int size); - -#endif /* AVCODEC_DYNAMIC_HDR10_PLUS_H */ diff --git a/libavcodec/dynamic_hdr_vivid.c b/libavcodec/dynamic_hdr_vivid.c index d689669dec0..a9b69107982 100644 --- a/libavcodec/dynamic_hdr_vivid.c +++ b/libavcodec/dynamic_hdr_vivid.c @@ -46,7 +46,8 @@ int ff_parse_itu_t_t35_to_dynamic_hdr_vivid(AVDynamicHDRVivid *s, const uint8_t return AVERROR_INVALIDDATA; s->system_start_code = get_bits(gb, 8); - if (s->system_start_code == 0x01) { + // T/UWA 005.1-2022, table 11 + if (s->system_start_code >= 0x01 && s->system_start_code <= 0x07) { s->num_windows = 1; if (get_bits_left(gb) < 12 * 4 * s->num_windows) @@ -89,35 +90,43 @@ int ff_parse_itu_t_t35_to_dynamic_hdr_vivid(AVDynamicHDRVivid *s, const uint8_t tm_params->base_param_k2 = get_bits(gb, 2); tm_params->base_param_k3 = get_bits(gb, 4); tm_params->base_param_Delta_enable_mode = get_bits(gb, 3); - if (tm_params->base_param_Delta_enable_mode == 2 || tm_params->base_param_Delta_enable_mode == 6) - tm_params->base_param_Delta = (AVRational){get_bits(gb, 7) * -1, base_param_Delta_den}; - else - tm_params->base_param_Delta = (AVRational){get_bits(gb, 7), base_param_Delta_den}; + tm_params->base_param_Delta = (AVRational){get_bits(gb, 7), base_param_Delta_den}; + } + if (get_bits_left(gb) < 1) + return AVERROR_INVALIDDATA; + tm_params->three_Spline_enable_flag = get_bits(gb, 1); + if (tm_params->three_Spline_enable_flag) { + AVHDRVivid3SplineParams *three_spline; - if (get_bits_left(gb) < 1) + if (get_bits_left(gb) < 1 + tm_params->three_Spline_num * (2 + 12 + 28 + 1)) return AVERROR_INVALIDDATA; - tm_params->three_Spline_enable_flag = get_bits(gb, 1); - if (tm_params->three_Spline_enable_flag) { - if (get_bits_left(gb) < 1 + tm_params->three_Spline_num * (2 + 12 + 28 + 1)) - return AVERROR_INVALIDDATA; - tm_params->three_Spline_num = get_bits(gb, 1) + 1; - for (int j = 0; j < tm_params->three_Spline_num; j++) { - tm_params->three_Spline_TH_mode = get_bits(gb, 2); - if (tm_params->three_Spline_TH_mode == 0 || tm_params->three_Spline_TH_mode == 2) { - if (get_bits_left(gb) < 8) - return AVERROR_INVALIDDATA; - tm_params->three_Spline_TH_enable_MB = (AVRational){get_bits(gb, 8), 255}; - } - tm_params->three_Spline_TH_enable = (AVRational){get_bits(gb, 12), 4095}; - tm_params->three_Spline_TH_Delta1 = (AVRational){get_bits(gb, 10), 1023}; - tm_params->three_Spline_TH_Delta2 = (AVRational){get_bits(gb, 10), 1023}; - tm_params->three_Spline_enable_Strength = (AVRational){get_bits(gb, 8), 255}; + tm_params->three_Spline_num = get_bits(gb, 1) + 1; + if (tm_params->three_Spline_num > FF_ARRAY_ELEMS(tm_params->three_spline)) + return AVERROR_INVALIDDATA; + for (int j = 0; j < tm_params->three_Spline_num; j++) { + three_spline = &tm_params->three_spline[j]; + three_spline->th_mode = get_bits(gb, 2); + if (three_spline->th_mode == 0 || three_spline->th_mode == 2) { + if (get_bits_left(gb) < 8) + return AVERROR_INVALIDDATA; + three_spline->th_enable_mb = (AVRational){get_bits(gb, 8), 255}; } - } else { - tm_params->three_Spline_num = 1; - tm_params->three_Spline_TH_mode = 0; + three_spline->th_enable = (AVRational){get_bits(gb, 12), 4095}; + three_spline->th_delta1 = (AVRational){get_bits(gb, 10), 1023}; + three_spline->th_delta2 = (AVRational){get_bits(gb, 10), 1023}; + three_spline->enable_strength = (AVRational){get_bits(gb, 8), 255}; } - +#if FF_API_HDR_VIVID_THREE_SPLINE + three_spline = &tm_params->three_spline[0]; +FF_DISABLE_DEPRECATION_WARNINGS + tm_params->three_Spline_TH_mode = three_spline->th_mode; + tm_params->three_Spline_TH_enable_MB = three_spline->th_enable_mb; + tm_params->three_Spline_TH_enable = three_spline->th_enable; + tm_params->three_Spline_TH_Delta1 = three_spline->th_delta1; + tm_params->three_Spline_TH_Delta2 = three_spline->th_delta2; + tm_params->three_Spline_enable_Strength = three_spline->enable_strength; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } } } diff --git a/libavcodec/eac3dec.c b/libavcodec/eac3dec.c index deca51dd3da..5c71751a0c8 100644 --- a/libavcodec/eac3dec.c +++ b/libavcodec/eac3dec.c @@ -464,7 +464,16 @@ static int ff_eac3_parse_header(AC3DecodeContext *s) if (get_bits1(gbc)) { int addbsil = get_bits(gbc, 6); for (i = 0; i < addbsil + 1; i++) { - skip_bits(gbc, 8); // skip additional bit stream info + if (i == 0) { + /* In this 8 bit chunk, the LSB is equal to flag_ec3_extension_type_a + which can be used to detect Atmos presence */ + skip_bits(gbc, 7); + if (get_bits1(gbc)) { + s->eac3_extension_type_a = 1; + } + } else { + skip_bits(gbc, 8); // skip additional bit stream info + } } } diff --git a/libavcodec/eac3enc.c b/libavcodec/eac3enc.c index 78d4f1399a1..4b3236d4e5e 100644 --- a/libavcodec/eac3enc.c +++ b/libavcodec/eac3enc.c @@ -189,7 +189,7 @@ void ff_eac3_output_frame_header(AC3EncodeContext *s) put_bits(&s->pb, 1, 0); } if (s->num_blocks != 6) - put_bits(&s->pb, 1, !(s->avctx->frame_number % 6)); /* converter sync flag */ + put_bits(&s->pb, 1, !(s->avctx->frame_num % 6)); /* converter sync flag */ put_bits(&s->pb, 1, 0); /* no additional bit stream info */ /* frame header */ @@ -254,7 +254,7 @@ const FFCodec ff_eac3_encoder = { CODEC_LONG_NAME("ATSC A/52 E-AC-3"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_EAC3, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(AC3EncodeContext), .init = ff_ac3_float_encode_init, FF_CODEC_ENCODE_CB(ff_ac3_float_encode_frame), diff --git a/libavcodec/eacmv.c b/libavcodec/eacmv.c index 18f27dfdf07..e73e310c4a9 100644 --- a/libavcodec/eacmv.c +++ b/libavcodec/eacmv.c @@ -202,10 +202,10 @@ static int cmv_decode_frame(AVCodecContext *avctx, AVFrame *frame, buf += EA_PREAMBLE_SIZE; if ((buf[0]&1)) { // subtype cmv_decode_inter(s, frame, buf+2, buf_end); - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_P; }else{ - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; cmv_decode_intra(s, frame, buf+2, buf_end); } diff --git a/libavcodec/eatgq.c b/libavcodec/eatgq.c index 89e9f20880a..0f0ed3585fa 100644 --- a/libavcodec/eatgq.c +++ b/libavcodec/eatgq.c @@ -56,7 +56,7 @@ static av_cold int tgq_decode_init(AVCodecContext *avctx) return 0; } -static void tgq_decode_block(TgqContext *s, int16_t block[64], GetBitContext *gb) +static int tgq_decode_block(TgqContext *s, int16_t block[64], GetBitContext *gb) { const uint8_t *scantable = ff_zigzag_direct; int i, j, value; @@ -64,6 +64,8 @@ static void tgq_decode_block(TgqContext *s, int16_t block[64], GetBitContext *gb for (i = 1; i < 64;) { switch (show_bits(gb, 3)) { case 4: + if (i >= 63) + return AVERROR_INVALIDDATA; block[scantable[i++]] = 0; case 0: block[scantable[i++]] = 0; @@ -73,6 +75,8 @@ static void tgq_decode_block(TgqContext *s, int16_t block[64], GetBitContext *gb case 1: skip_bits(gb, 2); value = get_bits(gb, 6); + if (value > 64 - i) + return AVERROR_INVALIDDATA; for (j = 0; j < value; j++) block[scantable[i++]] = 0; break; @@ -100,6 +104,7 @@ static void tgq_decode_block(TgqContext *s, int16_t block[64], GetBitContext *gb } } block[0] += 128 << 4; + return 0; } static void tgq_idct_put_mb(TgqContext *s, int16_t (*block)[64], AVFrame *frame, @@ -160,8 +165,11 @@ static int tgq_decode_mb(TgqContext *s, GetByteContext *gbyte, if (ret < 0) return ret; - for (i = 0; i < 6; i++) - tgq_decode_block(s, s->block[i], &gb); + for (i = 0; i < 6; i++) { + int ret = tgq_decode_block(s, s->block[i], &gb); + if (ret < 0) + return ret; + } tgq_idct_put_mb(s, s->block, frame, mb_x, mb_y); bytestream2_skip(gbyte, mode); } else { @@ -229,7 +237,7 @@ static int tgq_decode_frame(AVCodecContext *avctx, AVFrame *frame, if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) return ret; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; for (y = 0; y < FFALIGN(avctx->height, 16) >> 4; y++) diff --git a/libavcodec/eatgv.c b/libavcodec/eatgv.c index 29f7ee12f5b..a2aead46ebd 100644 --- a/libavcodec/eatgv.c +++ b/libavcodec/eatgv.c @@ -310,7 +310,7 @@ static int tgv_decode_frame(AVCodecContext *avctx, AVFrame *frame, if (chunk_type == kVGT_TAG) { int y; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; if (!s->frame_buffer && @@ -330,7 +330,7 @@ static int tgv_decode_frame(AVCodecContext *avctx, AVFrame *frame, av_log(avctx, AV_LOG_WARNING, "inter frame without corresponding intra frame\n"); return buf_size; } - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_P; if (tgv_decode_inter(s, frame, buf, buf_end) < 0) { av_log(avctx, AV_LOG_WARNING, "truncated inter frame\n"); diff --git a/libavcodec/elbg.c b/libavcodec/elbg.c index d97a7bc3f99..7a6a84fb6ba 100644 --- a/libavcodec/elbg.c +++ b/libavcodec/elbg.c @@ -44,13 +44,13 @@ typedef struct cell_s { * ELBG internal data */ typedef struct ELBGContext { - int64_t error; + int error; int dim; int num_cb; int *codebook; cell **cells; - int64_t *utility; - int64_t *utility_inc; + int *utility; + int *utility_inc; int *nearest_cb; int *points; int *temp_points; @@ -75,9 +75,12 @@ static inline int distance_limited(int *a, int *b, int dim, int limit) { int i, dist=0; for (i=0; i limit) - return INT_MAX; + int64_t distance = a[i] - b[i]; + + distance *= distance; + if (dist >= limit - distance) + return limit; + dist += distance; } return dist; @@ -97,8 +100,12 @@ static inline void vect_division(int *res, int *vect, int div, int dim) static int eval_error_cell(ELBGContext *elbg, int *centroid, cell *cells) { int error=0; - for (; cells; cells=cells->next) - error += distance_limited(centroid, elbg->points + cells->index*elbg->dim, elbg->dim, INT_MAX); + for (; cells; cells=cells->next) { + int distance = distance_limited(centroid, elbg->points + cells->index*elbg->dim, elbg->dim, INT_MAX); + if (error >= INT_MAX - distance) + return INT_MAX; + error += distance; + } return error; } @@ -178,10 +185,13 @@ static int simple_lbg(ELBGContext *elbg, int dist[2] = {distance_limited(centroid[0], points + tempcell->index*dim, dim, INT_MAX), distance_limited(centroid[1], points + tempcell->index*dim, dim, INT_MAX)}; int idx = dist[0] > dist[1]; - newutility[idx] += dist[idx]; + if (newutility[idx] >= INT_MAX - dist[idx]) + newutility[idx] = INT_MAX; + else + newutility[idx] += dist[idx]; } - return newutility[0] + newutility[1]; + return (newutility[0] >= INT_MAX - newutility[1]) ? INT_MAX : newutility[0] + newutility[1]; } static void get_new_centroids(ELBGContext *elbg, int huc, int *newcentroid_i, @@ -253,9 +263,9 @@ static void evaluate_utility_inc(ELBGContext *elbg) int64_t inc=0; for (int i = 0; i < elbg->num_cb; i++) { - if (elbg->num_cb * elbg->utility[i] > elbg->error) + if (elbg->num_cb * (int64_t)elbg->utility[i] > elbg->error) inc += elbg->utility[i]; - elbg->utility_inc[i] = inc; + elbg->utility_inc[i] = FFMIN(inc, INT_MAX); } } @@ -278,7 +288,7 @@ static void update_utility_and_n_cb(ELBGContext *elbg, int idx, int newutility) */ static void try_shift_candidate(ELBGContext *elbg, int idx[3]) { - int j, k, cont=0; + int j, k, cont=0, tmp; int64_t olderror=0, newerror; int newutility[3]; int *newcentroid[3] = { @@ -305,12 +315,17 @@ static void try_shift_candidate(ELBGContext *elbg, int idx[3]) get_new_centroids(elbg, idx[1], newcentroid[0], newcentroid[1]); newutility[2] = eval_error_cell(elbg, newcentroid[2], elbg->cells[idx[0]]); - newutility[2] += eval_error_cell(elbg, newcentroid[2], elbg->cells[idx[2]]); + tmp = eval_error_cell(elbg, newcentroid[2], elbg->cells[idx[2]]); + newutility[2] = (tmp >= INT_MAX - newutility[2]) ? INT_MAX : newutility[2] + tmp; newerror = newutility[2]; - newerror += simple_lbg(elbg, elbg->dim, newcentroid, newutility, elbg->points, + tmp = simple_lbg(elbg, elbg->dim, newcentroid, newutility, elbg->points, elbg->cells[idx[1]]); + if (tmp >= INT_MAX - newerror) + newerror = INT_MAX; + else + newerror += tmp; if (olderror > newerror) { shift_codebook(elbg, idx, newcentroid); @@ -334,7 +349,7 @@ static void do_shiftings(ELBGContext *elbg) evaluate_utility_inc(elbg); for (idx[0]=0; idx[0] < elbg->num_cb; idx[0]++) - if (elbg->num_cb * elbg->utility[idx[0]] < elbg->error) { + if (elbg->num_cb * (int64_t)elbg->utility[idx[0]] < elbg->error) { if (elbg->utility_inc[elbg->num_cb - 1] == 0) return; @@ -352,9 +367,9 @@ static void do_elbg(ELBGContext *av_restrict elbg, int *points, int numpoints, int *const size_part = elbg->size_part; int i, j, steps = 0; int best_idx = 0; - int64_t last_error; + int last_error; - elbg->error = INT64_MAX; + elbg->error = INT_MAX; elbg->points = points; do { @@ -382,8 +397,9 @@ static void do_elbg(ELBGContext *av_restrict elbg, int *points, int numpoints, } } elbg->nearest_cb[i] = best_idx; - elbg->error += best_dist; - elbg->utility[elbg->nearest_cb[i]] += best_dist; + elbg->error = (elbg->error >= INT_MAX - best_dist) ? INT_MAX : elbg->error + best_dist; + elbg->utility[elbg->nearest_cb[i]] = (elbg->utility[elbg->nearest_cb[i]] >= INT_MAX - best_dist) ? + INT_MAX : elbg->utility[elbg->nearest_cb[i]] + best_dist; free_cells->index = i; free_cells->next = elbg->cells[elbg->nearest_cb[i]]; elbg->cells[elbg->nearest_cb[i]] = free_cells; diff --git a/libavcodec/encode.c b/libavcodec/encode.c index fbe2c97cd62..32cc903b1f8 100644 --- a/libavcodec/encode.c +++ b/libavcodec/encode.c @@ -24,14 +24,38 @@ #include "libavutil/frame.h" #include "libavutil/imgutils.h" #include "libavutil/internal.h" +#include "libavutil/pixdesc.h" #include "libavutil/samplefmt.h" #include "avcodec.h" +#include "avcodec_internal.h" #include "codec_internal.h" #include "encode.h" #include "frame_thread_encoder.h" #include "internal.h" +typedef struct EncodeContext { + AVCodecInternal avci; + + /** + * This is set to AV_PKT_FLAG_KEY for encoders that encode intra-only + * formats (i.e. whose codec descriptor has AV_CODEC_PROP_INTRA_ONLY set). + * This is used to set said flag generically for said encoders. + */ + int intra_only_flag; + + /** + * An audio frame with less than required samples has been submitted (and + * potentially padded with silence). Reject all subsequent frames. + */ + int last_audio_frame; +} EncodeContext; + +static EncodeContext *encode_ctx(AVCodecInternal *avci) +{ + return (EncodeContext*)avci; +} + int ff_alloc_packet(AVCodecContext *avctx, AVPacket *avpkt, int64_t size) { if (size < 0 || size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE) { @@ -157,7 +181,7 @@ static int pad_last_frame(AVCodecContext *s, AVFrame *frame, const AVFrame *src, fail: av_frame_unref(frame); - s->internal->last_audio_frame = 0; + encode_ctx(s->internal)->last_audio_frame = 0; return ret; } @@ -171,7 +195,12 @@ int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size, } ret = ffcodec(avctx->codec)->cb.encode_sub(avctx, buf, buf_size, sub); - avctx->frame_number++; + avctx->frame_num++; +#if FF_API_AVCTX_FRAME_NUMBER +FF_DISABLE_DEPRECATION_WARNINGS + avctx->frame_number = avctx->frame_num; +FF_ENABLE_DEPRECATION_WARNINGS +#endif return ret; } @@ -187,6 +216,40 @@ int ff_encode_get_frame(AVCodecContext *avctx, AVFrame *frame) av_frame_move_ref(frame, avci->buffer_frame); +#if FF_API_FRAME_KEY +FF_DISABLE_DEPRECATION_WARNINGS + if (frame->key_frame) + frame->flags |= AV_FRAME_FLAG_KEY; +FF_ENABLE_DEPRECATION_WARNINGS +#endif +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS + if (frame->interlaced_frame) + frame->flags |= AV_FRAME_FLAG_INTERLACED; + if (frame->top_field_first) + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + return 0; +} + +int ff_encode_reordered_opaque(AVCodecContext *avctx, + AVPacket *pkt, const AVFrame *frame) +{ +#if FF_API_REORDERED_OPAQUE +FF_DISABLE_DEPRECATION_WARNINGS + avctx->reordered_opaque = frame->reordered_opaque; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { + int ret = av_buffer_replace(&pkt->opaque_ref, frame->opaque_ref); + if (ret < 0) + return ret; + pkt->opaque = frame->opaque; + } + return 0; } @@ -211,30 +274,38 @@ int ff_encode_encode_cb(AVCodecContext *avctx, AVPacket *avpkt, // set the timestamps for the simple no-delay case // encoders with delay have to set the timestamps themselves - if (!(avctx->codec->capabilities & AV_CODEC_CAP_DELAY)) { + if (!(avctx->codec->capabilities & AV_CODEC_CAP_DELAY) || + (frame && (codec->caps_internal & FF_CODEC_CAP_EOF_FLUSH))) { if (avpkt->pts == AV_NOPTS_VALUE) avpkt->pts = frame->pts; - if (avctx->codec->type == AVMEDIA_TYPE_AUDIO) { - if (!avpkt->duration) + if (!avpkt->duration) { + if (frame->duration) + avpkt->duration = frame->duration; + else if (avctx->codec->type == AVMEDIA_TYPE_AUDIO) { avpkt->duration = ff_samples_to_time_base(avctx, frame->nb_samples); + } } + + ret = ff_encode_reordered_opaque(avctx, avpkt, frame); + if (ret < 0) + goto unref; } // dts equals pts unless there is reordering // there can be no reordering if there is no encoder delay if (!(avctx->codec_descriptor->props & AV_CODEC_PROP_REORDER) || - !(avctx->codec->capabilities & AV_CODEC_CAP_DELAY)) + !(avctx->codec->capabilities & AV_CODEC_CAP_DELAY) || + (codec->caps_internal & FF_CODEC_CAP_EOF_FLUSH)) avpkt->dts = avpkt->pts; } else { unref: av_packet_unref(avpkt); } -#if !FF_API_THREAD_SAFE_CALLBACKS + if (frame) av_frame_unref(frame); -#endif return ret; } @@ -275,10 +346,6 @@ static int encode_simple_internal(AVCodecContext *avctx, AVPacket *avpkt) ret = ff_thread_video_encode_frame(avctx, avpkt, frame, &got_packet); else { ret = ff_encode_encode_cb(avctx, avpkt, frame, &got_packet); -#if FF_API_THREAD_SAFE_CALLBACKS - if (frame) - av_frame_unref(frame); -#endif } if (avci->draining && !got_packet) @@ -328,7 +395,7 @@ static int encode_receive_packet_internal(AVCodecContext *avctx, AVPacket *avpkt } else ret = encode_simple_receive_packet(avctx, avpkt); if (ret >= 0) - avpkt->flags |= avci->intra_only_flag; + avpkt->flags |= encode_ctx(avci)->intra_only_flag; if (ret == AVERROR_EOF) avci->draining_done = 1; @@ -386,6 +453,7 @@ static int encode_generate_icc_profile(av_unused AVCodecContext *c, av_unused AV static int encode_send_frame_internal(AVCodecContext *avctx, const AVFrame *src) { AVCodecInternal *avci = avctx->internal; + EncodeContext *ec = encode_ctx(avci); AVFrame *dst = avci->buffer_frame; int ret; @@ -398,7 +466,7 @@ static int encode_send_frame_internal(AVCodecContext *avctx, const AVFrame *src) /* check for valid frame size */ if (!(avctx->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)) { /* if we already got an undersized frame, that must have been the last */ - if (avctx->internal->last_audio_frame) { + if (ec->last_audio_frame) { av_log(avctx, AV_LOG_ERROR, "frame_size (%d) was not respected for a non-last frame\n", avctx->frame_size); return AVERROR(EINVAL); } @@ -407,7 +475,7 @@ static int encode_send_frame_internal(AVCodecContext *avctx, const AVFrame *src) return AVERROR(EINVAL); } if (src->nb_samples < avctx->frame_size) { - avctx->internal->last_audio_frame = 1; + ec->last_audio_frame = 1; if (!(avctx->codec->capabilities & AV_CODEC_CAP_SMALL_LAST_FRAME)) { int pad_samples = avci->pad_samples ? avci->pad_samples : avctx->frame_size; int out_samples = (src->nb_samples + pad_samples - 1) / pad_samples * pad_samples; @@ -429,19 +497,19 @@ static int encode_send_frame_internal(AVCodecContext *avctx, const AVFrame *src) finish: -#if FF_API_PKT_DURATION -FF_DISABLE_DEPRECATION_WARNINGS - if (dst->pkt_duration && dst->pkt_duration != dst->duration) - dst->duration = dst->pkt_duration; -FF_ENABLE_DEPRECATION_WARNINGS -#endif - if (avctx->codec->type == AVMEDIA_TYPE_VIDEO) { ret = encode_generate_icc_profile(avctx, dst); if (ret < 0) return ret; } + // unset frame duration unless AV_CODEC_FLAG_FRAME_DURATION is set, + // since otherwise we cannot be sure that whatever value it has is in the + // right timebase, so we would produce an incorrect value, which is worse + // than none at all + if (!(avctx->flags & AV_CODEC_FLAG_FRAME_DURATION)) + dst->duration = 0; + return 0; } @@ -473,7 +541,12 @@ int attribute_align_arg avcodec_send_frame(AVCodecContext *avctx, const AVFrame return ret; } - avctx->frame_number++; + avctx->frame_num++; +#if FF_API_AVCTX_FRAME_NUMBER +FF_DISABLE_DEPRECATION_WARNINGS + avctx->frame_number = avctx->frame_num; +FF_ENABLE_DEPRECATION_WARNINGS +#endif return 0; } @@ -501,25 +574,38 @@ int attribute_align_arg avcodec_receive_packet(AVCodecContext *avctx, AVPacket * static int encode_preinit_video(AVCodecContext *avctx) { + const AVCodec *c = avctx->codec; const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(avctx->pix_fmt); int i; - if (avctx->codec->pix_fmts) { - for (i = 0; avctx->codec->pix_fmts[i] != AV_PIX_FMT_NONE; i++) - if (avctx->pix_fmt == avctx->codec->pix_fmts[i]) + if (!av_get_pix_fmt_name(avctx->pix_fmt)) { + av_log(avctx, AV_LOG_ERROR, "Invalid video pixel format: %d\n", + avctx->pix_fmt); + return AVERROR(EINVAL); + } + + if (c->pix_fmts) { + for (i = 0; c->pix_fmts[i] != AV_PIX_FMT_NONE; i++) + if (avctx->pix_fmt == c->pix_fmts[i]) break; - if (avctx->codec->pix_fmts[i] == AV_PIX_FMT_NONE) { - char buf[128]; - snprintf(buf, sizeof(buf), "%d", avctx->pix_fmt); - av_log(avctx, AV_LOG_ERROR, "Specified pixel format %s is invalid or not supported\n", - (char *)av_x_if_null(av_get_pix_fmt_name(avctx->pix_fmt), buf)); + if (c->pix_fmts[i] == AV_PIX_FMT_NONE) { + av_log(avctx, AV_LOG_ERROR, + "Specified pixel format %s is not supported by the %s encoder.\n", + av_get_pix_fmt_name(avctx->pix_fmt), c->name); + + av_log(avctx, AV_LOG_ERROR, "Supported pixel formats:\n"); + for (int p = 0; c->pix_fmts[p] != AV_PIX_FMT_NONE; p++) { + av_log(avctx, AV_LOG_ERROR, " %s\n", + av_get_pix_fmt_name(c->pix_fmts[p])); + } + return AVERROR(EINVAL); } - if (avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ420P || - avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ411P || - avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ422P || - avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ440P || - avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ444P) + if (c->pix_fmts[i] == AV_PIX_FMT_YUVJ420P || + c->pix_fmts[i] == AV_PIX_FMT_YUVJ411P || + c->pix_fmts[i] == AV_PIX_FMT_YUVJ422P || + c->pix_fmts[i] == AV_PIX_FMT_YUVJ440P || + c->pix_fmts[i] == AV_PIX_FMT_YUVJ444P) avctx->color_range = AVCOL_RANGE_JPEG; } @@ -534,6 +620,8 @@ static int encode_preinit_video(AVCodecContext *avctx) return AVERROR(EINVAL); } +#if FF_API_TICKS_PER_FRAME +FF_DISABLE_DEPRECATION_WARNINGS if (avctx->ticks_per_frame && avctx->time_base.num && avctx->ticks_per_frame > INT_MAX / avctx->time_base.num) { av_log(avctx, AV_LOG_ERROR, @@ -543,6 +631,8 @@ static int encode_preinit_video(AVCodecContext *avctx) avctx->time_base.den); return AVERROR(EINVAL); } +FF_ENABLE_DEPRECATION_WARNINGS +#endif if (avctx->hw_frames_ctx) { AVHWFramesContext *frames_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data; @@ -568,52 +658,78 @@ static int encode_preinit_video(AVCodecContext *avctx) static int encode_preinit_audio(AVCodecContext *avctx) { + const AVCodec *c = avctx->codec; int i; - if (avctx->codec->sample_fmts) { - for (i = 0; avctx->codec->sample_fmts[i] != AV_SAMPLE_FMT_NONE; i++) { - if (avctx->sample_fmt == avctx->codec->sample_fmts[i]) + if (!av_get_sample_fmt_name(avctx->sample_fmt)) { + av_log(avctx, AV_LOG_ERROR, "Invalid audio sample format: %d\n", + avctx->sample_fmt); + return AVERROR(EINVAL); + } + if (avctx->sample_rate <= 0) { + av_log(avctx, AV_LOG_ERROR, "Invalid audio sample rate: %d\n", + avctx->sample_rate); + return AVERROR(EINVAL); + } + + if (c->sample_fmts) { + for (i = 0; c->sample_fmts[i] != AV_SAMPLE_FMT_NONE; i++) { + if (avctx->sample_fmt == c->sample_fmts[i]) break; if (avctx->ch_layout.nb_channels == 1 && av_get_planar_sample_fmt(avctx->sample_fmt) == - av_get_planar_sample_fmt(avctx->codec->sample_fmts[i])) { - avctx->sample_fmt = avctx->codec->sample_fmts[i]; + av_get_planar_sample_fmt(c->sample_fmts[i])) { + avctx->sample_fmt = c->sample_fmts[i]; break; } } - if (avctx->codec->sample_fmts[i] == AV_SAMPLE_FMT_NONE) { - char buf[128]; - snprintf(buf, sizeof(buf), "%d", avctx->sample_fmt); - av_log(avctx, AV_LOG_ERROR, "Specified sample format %s is invalid or not supported\n", - (char *)av_x_if_null(av_get_sample_fmt_name(avctx->sample_fmt), buf)); + if (c->sample_fmts[i] == AV_SAMPLE_FMT_NONE) { + av_log(avctx, AV_LOG_ERROR, + "Specified sample format %s is not supported by the %s encoder\n", + av_get_sample_fmt_name(avctx->sample_fmt), c->name); + + av_log(avctx, AV_LOG_ERROR, "Supported sample formats:\n"); + for (int p = 0; c->sample_fmts[p] != AV_SAMPLE_FMT_NONE; p++) { + av_log(avctx, AV_LOG_ERROR, " %s\n", + av_get_sample_fmt_name(c->sample_fmts[p])); + } + return AVERROR(EINVAL); } } - if (avctx->codec->supported_samplerates) { - for (i = 0; avctx->codec->supported_samplerates[i] != 0; i++) - if (avctx->sample_rate == avctx->codec->supported_samplerates[i]) + if (c->supported_samplerates) { + for (i = 0; c->supported_samplerates[i] != 0; i++) + if (avctx->sample_rate == c->supported_samplerates[i]) break; - if (avctx->codec->supported_samplerates[i] == 0) { - av_log(avctx, AV_LOG_ERROR, "Specified sample rate %d is not supported\n", - avctx->sample_rate); + if (c->supported_samplerates[i] == 0) { + av_log(avctx, AV_LOG_ERROR, + "Specified sample rate %d is not supported by the %s encoder\n", + avctx->sample_rate, c->name); + + av_log(avctx, AV_LOG_ERROR, "Supported sample rates:\n"); + for (int p = 0; c->supported_samplerates[p]; p++) + av_log(avctx, AV_LOG_ERROR, " %d\n", c->supported_samplerates[p]); + return AVERROR(EINVAL); } } - if (avctx->sample_rate < 0) { - av_log(avctx, AV_LOG_ERROR, "Specified sample rate %d is not supported\n", - avctx->sample_rate); - return AVERROR(EINVAL); - } - if (avctx->codec->ch_layouts) { - for (i = 0; avctx->codec->ch_layouts[i].nb_channels; i++) { - if (!av_channel_layout_compare(&avctx->ch_layout, &avctx->codec->ch_layouts[i])) + if (c->ch_layouts) { + for (i = 0; c->ch_layouts[i].nb_channels; i++) { + if (!av_channel_layout_compare(&avctx->ch_layout, &c->ch_layouts[i])) break; } - if (!avctx->codec->ch_layouts[i].nb_channels) { + if (!c->ch_layouts[i].nb_channels) { char buf[512]; int ret = av_channel_layout_describe(&avctx->ch_layout, buf, sizeof(buf)); - if (ret > 0) - av_log(avctx, AV_LOG_ERROR, "Specified channel layout '%s' is not supported\n", buf); + av_log(avctx, AV_LOG_ERROR, + "Specified channel layout '%s' is not supported by the %s encoder\n", + ret > 0 ? buf : "?", c->name); + + av_log(avctx, AV_LOG_ERROR, "Supported channel layouts:\n"); + for (int p = 0; c->ch_layouts[p].nb_channels; p++) { + ret = av_channel_layout_describe(&c->ch_layouts[p], buf, sizeof(buf)); + av_log(avctx, AV_LOG_ERROR, " %s\n", ret > 0 ? buf : "?"); + } return AVERROR(EINVAL); } } @@ -627,6 +743,7 @@ static int encode_preinit_audio(AVCodecContext *avctx) int ff_encode_preinit(AVCodecContext *avctx) { AVCodecInternal *avci = avctx->internal; + EncodeContext *ec = encode_ctx(avci); int ret = 0; if (avctx->time_base.num <= 0 || avctx->time_base.den <= 0) { @@ -634,6 +751,13 @@ int ff_encode_preinit(AVCodecContext *avctx) return AVERROR(EINVAL); } + if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE && + !(avctx->codec->capabilities & AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE)) { + av_log(avctx, AV_LOG_ERROR, "The copy_opaque flag is set, but the " + "encoder does not support it.\n"); + return AVERROR(EINVAL); + } + switch (avctx->codec_type) { case AVMEDIA_TYPE_VIDEO: ret = encode_preinit_video(avctx); break; case AVMEDIA_TYPE_AUDIO: ret = encode_preinit_audio(avctx); break; @@ -650,7 +774,7 @@ int ff_encode_preinit(AVCodecContext *avctx) avctx->rc_initial_buffer_occupancy = avctx->rc_buffer_size * 3LL / 4; if (avctx->codec_descriptor->props & AV_CODEC_PROP_INTRA_ONLY) - avctx->internal->intra_only_flag = AV_PKT_FLAG_KEY; + ec->intra_only_flag = AV_PKT_FLAG_KEY; if (ffcodec(avctx->codec)->cb_type == FF_CODEC_CB_TYPE_ENCODE) { avci->in_frame = av_frame_alloc(); @@ -725,3 +849,18 @@ int ff_encode_receive_frame(AVCodecContext *avctx, AVFrame *frame) av_frame_move_ref(frame, avci->recon_frame); return 0; } + +void ff_encode_flush_buffers(AVCodecContext *avctx) +{ + AVCodecInternal *avci = avctx->internal; + + if (avci->in_frame) + av_frame_unref(avci->in_frame); + if (avci->recon_frame) + av_frame_unref(avci->recon_frame); +} + +AVCodecInternal *ff_encode_internal_alloc(void) +{ + return av_mallocz(sizeof(EncodeContext)); +} diff --git a/libavcodec/encode.h b/libavcodec/encode.h index 81d18d6eadf..dfaab7c9760 100644 --- a/libavcodec/encode.h +++ b/libavcodec/encode.h @@ -26,11 +26,6 @@ #include "avcodec.h" #include "packet.h" -/** - * avcodec_receive_frame() implementation for encoders. - */ -int ff_encode_receive_frame(AVCodecContext *avctx, AVFrame *frame); - /** * Called by encoders to get the next frame for encoding. * @@ -69,11 +64,11 @@ int ff_encode_alloc_frame(AVCodecContext *avctx, AVFrame *frame); */ int ff_alloc_packet(AVCodecContext *avctx, AVPacket *avpkt, int64_t size); -/* - * Perform encoder initialization and validation. - * Called when opening the encoder, before the FFCodec.init() call. +/** + * Propagate user opaque values from the frame to avctx/pkt as needed. */ -int ff_encode_preinit(AVCodecContext *avctx); +int ff_encode_reordered_opaque(AVCodecContext *avctx, + AVPacket *pkt, const AVFrame *frame); int ff_encode_encode_cb(AVCodecContext *avctx, AVPacket *avpkt, AVFrame *frame, int *got_packet); diff --git a/libavcodec/error_resilience.c b/libavcodec/error_resilience.c index 2aa6f1d8640..68e20925e0a 100644 --- a/libavcodec/error_resilience.c +++ b/libavcodec/error_resilience.c @@ -804,7 +804,7 @@ void ff_er_frame_start(ERContext *s) static int er_supported(ERContext *s) { - if(s->avctx->hwaccel && s->avctx->hwaccel->decode_slice || + if (s->avctx->hwaccel || !s->cur_pic.f || s->cur_pic.field_picture ) @@ -828,7 +828,7 @@ void ff_er_add_slice(ERContext *s, int startx, int starty, const int end_xy = s->mb_index2xy[end_i]; int mask = -1; - if (s->avctx->hwaccel && s->avctx->hwaccel->decode_slice) + if (s->avctx->hwaccel) return; if (start_i > end_i || start_xy > end_xy) { diff --git a/libavcodec/escape124.c b/libavcodec/escape124.c index 024eec59ce8..592de09a9fd 100644 --- a/libavcodec/escape124.c +++ b/libavcodec/escape124.c @@ -89,11 +89,6 @@ static CodeBook unpack_codebook(GetBitContext* gb, unsigned depth, unsigned i, j; CodeBook cb = { 0 }; - if (size >= INT_MAX / 34 || get_bits_left(gb) < size * 34) - return cb; - - if (size >= INT_MAX / sizeof(MacroBlock)) - return cb; cb.blocks = av_malloc(size ? size * sizeof(MacroBlock) : 1); if (!cb.blocks) return cb; @@ -102,15 +97,12 @@ static CodeBook unpack_codebook(GetBitContext* gb, unsigned depth, cb.size = size; for (i = 0; i < size; i++) { unsigned mask_bits = get_bits(gb, 4); - unsigned color0 = get_bits(gb, 15); - unsigned color1 = get_bits(gb, 15); - - for (j = 0; j < 4; j++) { - if (mask_bits & (1 << j)) - cb.blocks[i].pixels[j] = color1; - else - cb.blocks[i].pixels[j] = color0; - } + unsigned color[2]; + color[0] = get_bits(gb, 15); + color[1] = get_bits(gb, 15); + + for (j = 0; j < 4; j++) + cb.blocks[i].pixels[j] = color[(mask_bits>>j) & 1]; } return cb; } @@ -163,7 +155,7 @@ static MacroBlock decode_macroblock(Escape124Context* s, GetBitContext* gb, // This condition can occur with invalid bitstreams and // *codebook_index == 2 - if (block_index >= s->codebooks[*codebook_index].size) + if (block_index >= s->codebooks[*codebook_index].size || !s->codebooks[*codebook_index].blocks) return (MacroBlock) { { 0 } }; return s->codebooks[*codebook_index].blocks[block_index]; @@ -225,7 +217,7 @@ static int escape124_decode_frame(AVCodecContext *avctx, AVFrame *frame, // represent a lower bound of the space needed for skipped superblocks. Non // skipped SBs need more space. if (get_bits_left(&gb) < 64 + s->num_superblocks * 23LL / 4320) - return -1; + return AVERROR_INVALIDDATA; frame_flags = get_bits_long(&gb, 32); frame_size = get_bits_long(&gb, 32); @@ -276,9 +268,14 @@ static int escape124_decode_frame(AVCodecContext *avctx, AVFrame *frame, } av_freep(&s->codebooks[i].blocks); + if (cb_size >= INT_MAX / 34 || get_bits_left(&gb) < (int)cb_size * 34) + return AVERROR_INVALIDDATA; + + if (cb_size >= INT_MAX / sizeof(MacroBlock)) + return AVERROR_INVALIDDATA; s->codebooks[i] = unpack_codebook(&gb, cb_depth, cb_size); if (!s->codebooks[i].blocks) - return -1; + return AVERROR(ENOMEM); } } diff --git a/libavcodec/evc.h b/libavcodec/evc.h new file mode 100644 index 00000000000..9711c760fef --- /dev/null +++ b/libavcodec/evc.h @@ -0,0 +1,155 @@ +/* + * EVC definitions and enums + * Copyright (c) 2022 Dawid Kozinski + * + * 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_EVC_H +#define AVCODEC_EVC_H + +// The length field that indicates the length in bytes of the following NAL unit is configured to be of 4 bytes +#define EVC_NALU_LENGTH_PREFIX_SIZE (4) /* byte */ +#define EVC_NALU_HEADER_SIZE (2) /* byte */ + +/** + * @see ISO_IEC_23094-1_2020, 7.4.2.2 NAL unit header semantic + * Table 4 - NAL unit type codes and NAL unit type classes + */ +enum EVCNALUnitType { + EVC_NOIDR_NUT = 0, /* Coded slice of a non-IDR picture */ + EVC_IDR_NUT = 1, /* Coded slice of an IDR picture */ + EVC_RSV_VCL_NUT02 = 2, + EVC_RSV_VCL_NUT03 = 3, + EVC_RSV_VCL_NUT04 = 4, + EVC_RSV_VCL_NUT05 = 5, + EVC_RSV_VCL_NUT06 = 6, + EVC_RSV_VCL_NUT07 = 7, + EVC_RSV_VCL_NUT08 = 8, + EVC_RSV_VCL_NUT09 = 9, + EVC_RSV_VCL_NUT10 = 10, + EVC_RSV_VCL_NUT11 = 11, + EVC_RSV_VCL_NUT12 = 12, + EVC_RSV_VCL_NUT13 = 13, + EVC_RSV_VCL_NUT14 = 14, + EVC_RSV_VCL_NUT15 = 15, + EVC_RSV_VCL_NUT16 = 16, + EVC_RSV_VCL_NUT17 = 17, + EVC_RSV_VCL_NUT18 = 18, + EVC_RSV_VCL_NUT19 = 19, + EVC_RSV_VCL_NUT20 = 20, + EVC_RSV_VCL_NUT21 = 21, + EVC_RSV_VCL_NUT22 = 22, + EVC_RSV_VCL_NUT23 = 23, + EVC_SPS_NUT = 24, /* Sequence parameter set */ + EVC_PPS_NUT = 25, /* Picture paremeter set */ + EVC_APS_NUT = 26, /* Adaptation parameter set */ + EVC_FD_NUT = 27, /* Filler data */ + EVC_SEI_NUT = 28, /* Supplemental enhancement information */ + EVC_RSV_NONVCL29 = 29, + EVC_RSV_NONVCL30 = 30, + EVC_RSV_NONVCL31 = 31, + EVC_RSV_NONVCL32 = 32, + EVC_RSV_NONVCL33 = 33, + EVC_RSV_NONVCL34 = 34, + EVC_RSV_NONVCL35 = 35, + EVC_RSV_NONVCL36 = 36, + EVC_RSV_NONVCL37 = 37, + EVC_RSV_NONVCL38 = 38, + EVC_RSV_NONVCL39 = 39, + EVC_RSV_NONVCL40 = 40, + EVC_RSV_NONVCL41 = 41, + EVC_RSV_NONVCL42 = 42, + EVC_RSV_NONVCL43 = 43, + EVC_RSV_NONVCL44 = 44, + EVC_RSV_NONVCL45 = 45, + EVC_RSV_NONVCL46 = 46, + EVC_RSV_NONVCL47 = 47, + EVC_RSV_NONVCL48 = 48, + EVC_RSV_NONVCL49 = 49, + EVC_RSV_NONVCL50 = 50, + EVC_RSV_NONVCL51 = 51, + EVC_RSV_NONVCL52 = 52, + EVC_RSV_NONVCL53 = 53, + EVC_RSV_NONVCL54 = 54, + EVC_RSV_NONVCL55 = 55, + EVC_UNSPEC_NUT56 = 56, + EVC_UNSPEC_NUT57 = 57, + EVC_UNSPEC_NUT58 = 58, + EVC_UNSPEC_NUT59 = 59, + EVC_UNSPEC_NUT60 = 60, + EVC_UNSPEC_NUT61 = 61, + EVC_UNSPEC_NUT62 = 62 +}; + +// slice type +// @see ISO_IEC_23094-1_2020 7.4.5 Slice header semantics +// +enum EVCSliceType { + EVC_SLICE_TYPE_B = 0, + EVC_SLICE_TYPE_P = 1, + EVC_SLICE_TYPE_I = 2 +}; + +enum { + // 7.4.3.2: aps_video_parameter_set_id is u(4). + EVC_MAX_APS_COUNT = 32, + + // 7.4.3.1: sps_seq_parameter_set_id is in [0, 15]. + EVC_MAX_SPS_COUNT = 16, + + // 7.4.3.2: pps_pic_parameter_set_id is in [0, 63]. + EVC_MAX_PPS_COUNT = 64, + + // 7.4.5: slice header slice_pic_parameter_set_id in [0, 63] + EVC_MAX_SH_COUNT = 64, + + // E.3.2: cpb_cnt_minus1[i] is in [0, 31]. + EVC_MAX_CPB_CNT = 32, + + // A.4.1: in table A.1 the highest level allows a MaxLumaPs of 35 651 584. + EVC_MAX_LUMA_PS = 35651584, + + EVC_MAX_NUM_REF_PICS = 21, + + EVC_MAX_NUM_RPLS = 64, + + // A.4.1: pic_width_in_luma_samples and pic_height_in_luma_samples are + // constrained to be not greater than sqrt(MaxLumaPs * 8). Hence height/ + // width are bounded above by sqrt(8 * 35651584) = 16888.2 samples. + EVC_MAX_WIDTH = 16888, + EVC_MAX_HEIGHT = 16888, + + // A.4.1: table A.1 allows at most 22 tile rows for any level. + EVC_MAX_TILE_ROWS = 22, + // A.4.1: table A.1 allows at most 20 tile columns for any level. + EVC_MAX_TILE_COLUMNS = 20, + + // A.4.1: table A.1 allows at most 600 slice segments for any level. + EVC_MAX_SLICE_SEGMENTS = 600, + + // 7.4.7.1: in the worst case (tiles_enabled_flag and + // entropy_coding_sync_enabled_flag are both set), entry points can be + // placed at the beginning of every Ctb row in every tile, giving an + // upper bound of (num_tile_columns_minus1 + 1) * PicHeightInCtbsY - 1. + // Only a stream with very high resolution and perverse parameters could + // get near that, though, so set a lower limit here with the maximum + // possible value for 4K video (at most 135 16x16 Ctb rows). + HEVC_MAX_ENTRY_POINT_OFFSETS = EVC_MAX_TILE_COLUMNS * 135, +}; + +#endif // AVCODEC_EVC_H diff --git a/libavcodec/evc_frame_merge_bsf.c b/libavcodec/evc_frame_merge_bsf.c new file mode 100644 index 00000000000..7b8e6b1c9ee --- /dev/null +++ b/libavcodec/evc_frame_merge_bsf.c @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2019 James Almer + * + * 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 "get_bits.h" +#include "bsf.h" +#include "bsf_internal.h" + +#include "evc.h" +#include "evc_parse.h" +#include "evc_ps.h" + +// Access unit data +typedef struct AccessUnitBuffer { + uint8_t *data; // the data buffer + size_t data_size; // size of data in bytes + unsigned capacity; // buffer capacity +} AccessUnitBuffer; + +typedef struct EVCFMergeContext { + AVPacket *in, *buffer_pkt; + EVCParamSets ps; + EVCParserPoc poc; + AccessUnitBuffer au_buffer; +} EVCFMergeContext; + +static int end_of_access_unit_found(const EVCParamSets *ps, const EVCParserSliceHeader *sh, + const EVCParserPoc *poc, enum EVCNALUnitType nalu_type) +{ + EVCParserPPS *pps = ps->pps[sh->slice_pic_parameter_set_id]; + EVCParserSPS *sps = ps->sps[pps->pps_seq_parameter_set_id]; + + av_assert0(sps && pps); + + if (sps->profile_idc == 0) { // BASELINE profile + if (nalu_type == EVC_NOIDR_NUT || nalu_type == EVC_IDR_NUT) + return 1; + } else { // MAIN profile + if (nalu_type == EVC_NOIDR_NUT) { + if (poc->PicOrderCntVal != poc->prevPicOrderCntVal) + return 1; + } else if (nalu_type == EVC_IDR_NUT) + return 1; + } + return 0; +} + +static void evc_frame_merge_flush(AVBSFContext *bsf) +{ + EVCFMergeContext *ctx = bsf->priv_data; + + ff_evc_ps_free(&ctx->ps); + av_packet_unref(ctx->in); + av_packet_unref(ctx->buffer_pkt); + ctx->au_buffer.data_size = 0; +} + +static int parse_nal_unit(AVBSFContext *bsf, const uint8_t *buf, int buf_size) +{ + EVCFMergeContext *ctx = bsf->priv_data; + GetBitContext gb; + enum EVCNALUnitType nalu_type; + int tid, err; + + err = init_get_bits8(&gb, buf, buf_size); + if (err < 0) + return err; + + // @see ISO_IEC_23094-1_2020, 7.4.2.2 NAL unit header semantic (Table 4 - NAL unit type codes and NAL unit type classes) + // @see enum EVCNALUnitType in evc.h + if (get_bits1(&gb)) {// forbidden_zero_bit + av_log(bsf, AV_LOG_ERROR, "Invalid NAL unit header\n"); + return AVERROR_INVALIDDATA; + } + + nalu_type = get_bits(&gb, 6) - 1; + if (nalu_type < EVC_NOIDR_NUT || nalu_type > EVC_UNSPEC_NUT62) { + av_log(bsf, AV_LOG_ERROR, "Invalid NAL unit type: (%d)\n", nalu_type); + return AVERROR_INVALIDDATA; + } + + tid = get_bits(&gb, 3); + skip_bits(&gb, 5); // nuh_reserved_zero_5bits + skip_bits1(&gb); // nuh_extension_flag + + switch (nalu_type) { + case EVC_SPS_NUT: + err = ff_evc_parse_sps(&gb, &ctx->ps); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "SPS parsing error\n"); + return err; + } + break; + case EVC_PPS_NUT: + err = ff_evc_parse_pps(&gb, &ctx->ps); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "PPS parsing error\n"); + return err; + } + break; + case EVC_IDR_NUT: // Coded slice of a IDR or non-IDR picture + case EVC_NOIDR_NUT: { + EVCParserSliceHeader sh; + + err = ff_evc_parse_slice_header(&gb, &sh, &ctx->ps, nalu_type); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Slice header parsing error\n"); + return err; + } + + // POC (picture order count of the current picture) derivation + // @see ISO/IEC 23094-1:2020(E) 8.3.1 Decoding process for picture order count + err = ff_evc_derive_poc(&ctx->ps, &sh, &ctx->poc, nalu_type, tid); + if (err < 0) + return err; + + return end_of_access_unit_found(&ctx->ps, &sh, &ctx->poc, nalu_type); + + break; + } + case EVC_SEI_NUT: // Supplemental Enhancement Information + case EVC_APS_NUT: // Adaptation parameter set + case EVC_FD_NUT: // Filler data + default: + break; + } + + return 0; +} + +static int evc_frame_merge_filter(AVBSFContext *bsf, AVPacket *out) +{ + EVCFMergeContext *ctx = bsf->priv_data; + AVPacket *in = ctx->in, *buffer_pkt = ctx->buffer_pkt; + size_t data_size; + int au_end_found = 0, err; + + while (!au_end_found) { + uint8_t *buffer; + uint32_t nalu_size; + + if (!in->size) { + av_packet_unref(in); + err = ff_bsf_get_packet_ref(bsf, in); + if (err < 0) { + if (err == AVERROR_EOF && ctx->au_buffer.data_size > 0) + break; + return err; + } + /* Buffer packets with timestamps (there should be at most one per AU) + * or any packet if buffer_pkt is empty. The latter is needed to + * passthrough positions in case there are no timestamps like with + * the raw EVC demuxer. */ + if (!buffer_pkt->data || + in->pts != AV_NOPTS_VALUE && buffer_pkt->pts == AV_NOPTS_VALUE) { + err = av_packet_ref(buffer_pkt, in); + if (err < 0) + goto end; + } + } + + // Buffer size is not enough for buffer to store NAL unit 4-bytes prefix (length) + if (in->size < EVC_NALU_LENGTH_PREFIX_SIZE) + return AVERROR_INVALIDDATA; + + nalu_size = evc_read_nal_unit_length(in->data, EVC_NALU_LENGTH_PREFIX_SIZE, bsf); + if (!nalu_size || nalu_size > INT_MAX) { + av_log(bsf, AV_LOG_ERROR, "Invalid NAL unit size: (%u)\n", nalu_size); + err = AVERROR_INVALIDDATA; + goto end; + } + + if (in->size < nalu_size + EVC_NALU_LENGTH_PREFIX_SIZE) { + err = AVERROR_INVALIDDATA; + goto end; + } + + err = parse_nal_unit(bsf, in->data + EVC_NALU_LENGTH_PREFIX_SIZE, nalu_size); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Parsing of NAL unit failed\n"); + goto end; + } + au_end_found = err; + + nalu_size += EVC_NALU_LENGTH_PREFIX_SIZE; + + data_size = ctx->au_buffer.data_size + nalu_size; + if (data_size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE) { + av_log(bsf, AV_LOG_ERROR, "Assembled packet is too big\n"); + err = AVERROR(ERANGE); + goto end; + } + + buffer = av_fast_realloc(ctx->au_buffer.data, &ctx->au_buffer.capacity, + data_size); + if (!buffer) { + av_freep(&ctx->au_buffer.data); + err = AVERROR_INVALIDDATA; + goto end; + } + + ctx->au_buffer.data = buffer; + memcpy(ctx->au_buffer.data + ctx->au_buffer.data_size, in->data, nalu_size); + + ctx->au_buffer.data_size = data_size; + + in->data += nalu_size; + in->size -= nalu_size; + } + + av_packet_unref(in); + data_size = ctx->au_buffer.data_size; + + ctx->au_buffer.data_size = 0; + // drop the data in buffer_pkt, if any, but keep the props + av_buffer_unref(&buffer_pkt->buf); + err = av_buffer_realloc(&buffer_pkt->buf, data_size + AV_INPUT_BUFFER_PADDING_SIZE); + if (err < 0) + goto end; + + buffer_pkt->data = buffer_pkt->buf->data; + buffer_pkt->size = data_size; + av_packet_move_ref(out, buffer_pkt); + memcpy(out->data, ctx->au_buffer.data, data_size); + memset(out->data + data_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); + + err = 0; +end: + if (err < 0) { + av_packet_unref(in); + av_packet_unref(buffer_pkt); + ctx->au_buffer.data_size = 0; + } + return err; +} + +static int evc_frame_merge_init(AVBSFContext *bsf) +{ + EVCFMergeContext *ctx = bsf->priv_data; + + ctx->in = av_packet_alloc(); + ctx->buffer_pkt = av_packet_alloc(); + if (!ctx->in || !ctx->buffer_pkt) + return AVERROR(ENOMEM); + + return 0; +} + +static void evc_frame_merge_close(AVBSFContext *bsf) +{ + EVCFMergeContext *ctx = bsf->priv_data; + + av_packet_free(&ctx->in); + av_packet_free(&ctx->buffer_pkt); + ff_evc_ps_free(&ctx->ps); + + ctx->au_buffer.capacity = 0; + av_freep(&ctx->au_buffer.data); + ctx->au_buffer.data_size = 0; +} + +static const enum AVCodecID evc_frame_merge_codec_ids[] = { + AV_CODEC_ID_EVC, AV_CODEC_ID_NONE, +}; + +const FFBitStreamFilter ff_evc_frame_merge_bsf = { + .p.name = "evc_frame_merge", + .p.codec_ids = evc_frame_merge_codec_ids, + .priv_data_size = sizeof(EVCFMergeContext), + .init = evc_frame_merge_init, + .flush = evc_frame_merge_flush, + .close = evc_frame_merge_close, + .filter = evc_frame_merge_filter, +}; diff --git a/libavcodec/evc_parse.c b/libavcodec/evc_parse.c new file mode 100644 index 00000000000..bd3a4416f2d --- /dev/null +++ b/libavcodec/evc_parse.c @@ -0,0 +1,205 @@ +/* + * 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 "golomb.h" +#include "evc.h" +#include "evc_parse.h" + +// @see ISO_IEC_23094-1 (7.3.2.6 Slice layer RBSP syntax) +int ff_evc_parse_slice_header(GetBitContext *gb, EVCParserSliceHeader *sh, + const EVCParamSets *ps, enum EVCNALUnitType nalu_type) +{ + const EVCParserPPS *pps; + const EVCParserSPS *sps; + int num_tiles_in_slice = 0; + unsigned slice_pic_parameter_set_id; + + slice_pic_parameter_set_id = get_ue_golomb_31(gb); + + if (slice_pic_parameter_set_id >= EVC_MAX_PPS_COUNT) + return AVERROR_INVALIDDATA; + + pps = ps->pps[slice_pic_parameter_set_id]; + if(!pps) + return AVERROR_INVALIDDATA; + + sps = ps->sps[pps->pps_seq_parameter_set_id]; + if(!sps) + return AVERROR_INVALIDDATA; + + memset(sh, 0, sizeof(*sh)); + sh->slice_pic_parameter_set_id = slice_pic_parameter_set_id; + + if (!pps->single_tile_in_pic_flag) { + sh->single_tile_in_slice_flag = get_bits1(gb); + sh->first_tile_id = get_bits(gb, pps->tile_id_len_minus1 + 1); + } else + sh->single_tile_in_slice_flag = 1; + + if (!sh->single_tile_in_slice_flag) { + if (pps->arbitrary_slice_present_flag) + sh->arbitrary_slice_flag = get_bits1(gb); + + if (!sh->arbitrary_slice_flag) + sh->last_tile_id = get_bits(gb, pps->tile_id_len_minus1 + 1); + else { + sh->num_remaining_tiles_in_slice_minus1 = get_ue_golomb_long(gb); + num_tiles_in_slice = sh->num_remaining_tiles_in_slice_minus1 + 2; + for (int i = 0; i < num_tiles_in_slice - 1; ++i) + sh->delta_tile_id_minus1[i] = get_ue_golomb_long(gb); + } + } + + sh->slice_type = get_ue_golomb_31(gb); + + if (nalu_type == EVC_IDR_NUT) + sh->no_output_of_prior_pics_flag = get_bits1(gb); + + if (sps->sps_mmvd_flag && ((sh->slice_type == EVC_SLICE_TYPE_B) || (sh->slice_type == EVC_SLICE_TYPE_P))) + sh->mmvd_group_enable_flag = get_bits1(gb); + else + sh->mmvd_group_enable_flag = 0; + + if (sps->sps_alf_flag) { + int ChromaArrayType = sps->chroma_format_idc; + + sh->slice_alf_enabled_flag = get_bits1(gb); + + if (sh->slice_alf_enabled_flag) { + sh->slice_alf_luma_aps_id = get_bits(gb, 5); + sh->slice_alf_map_flag = get_bits1(gb); + sh->slice_alf_chroma_idc = get_bits(gb, 2); + + if ((ChromaArrayType == 1 || ChromaArrayType == 2) && sh->slice_alf_chroma_idc > 0) + sh->slice_alf_chroma_aps_id = get_bits(gb, 5); + } + if (ChromaArrayType == 3) { + int sliceChromaAlfEnabledFlag = 0; + int sliceChroma2AlfEnabledFlag = 0; + + if (sh->slice_alf_chroma_idc == 1) { // @see ISO_IEC_23094-1 (7.4.5) + sliceChromaAlfEnabledFlag = 1; + sliceChroma2AlfEnabledFlag = 0; + } else if (sh->slice_alf_chroma_idc == 2) { + sliceChromaAlfEnabledFlag = 0; + sliceChroma2AlfEnabledFlag = 1; + } else if (sh->slice_alf_chroma_idc == 3) { + sliceChromaAlfEnabledFlag = 1; + sliceChroma2AlfEnabledFlag = 1; + } else { + sliceChromaAlfEnabledFlag = 0; + sliceChroma2AlfEnabledFlag = 0; + } + + if (!sh->slice_alf_enabled_flag) + sh->slice_alf_chroma_idc = get_bits(gb, 2); + + if (sliceChromaAlfEnabledFlag) { + sh->slice_alf_chroma_aps_id = get_bits(gb, 5); + sh->slice_alf_chroma_map_flag = get_bits1(gb); + } + + if (sliceChroma2AlfEnabledFlag) { + sh->slice_alf_chroma2_aps_id = get_bits(gb, 5); + sh->slice_alf_chroma2_map_flag = get_bits1(gb); + } + } + } + + if (nalu_type != EVC_IDR_NUT) { + if (sps->sps_pocs_flag) + sh->slice_pic_order_cnt_lsb = get_bits(gb, sps->log2_max_pic_order_cnt_lsb_minus4 + 4); + } + + // @note + // If necessary, add the missing fields to the EVCParserSliceHeader structure + // and then extend parser implementation + + return 0; +} + +int ff_evc_derive_poc(const EVCParamSets *ps, const EVCParserSliceHeader *sh, + EVCParserPoc *poc, enum EVCNALUnitType nalu_type, int tid) +{ + const EVCParserPPS *pps = ps->pps[sh->slice_pic_parameter_set_id]; + const EVCParserSPS *sps; + + if (!pps) + return AVERROR_INVALIDDATA; + + sps = ps->sps[pps->pps_seq_parameter_set_id]; + if (!sps) + return AVERROR_INVALIDDATA; + + if (sps->sps_pocs_flag) { + int PicOrderCntMsb = 0; + poc->prevPicOrderCntVal = poc->PicOrderCntVal; + + if (nalu_type == EVC_IDR_NUT) + PicOrderCntMsb = 0; + else { + int MaxPicOrderCntLsb = 1 << (sps->log2_max_pic_order_cnt_lsb_minus4 + 4); + int prevPicOrderCntLsb = poc->PicOrderCntVal & (MaxPicOrderCntLsb - 1); + int prevPicOrderCntMsb = poc->PicOrderCntVal - prevPicOrderCntLsb; + + if ((sh->slice_pic_order_cnt_lsb < prevPicOrderCntLsb) && + ((prevPicOrderCntLsb - sh->slice_pic_order_cnt_lsb) >= (MaxPicOrderCntLsb / 2))) + PicOrderCntMsb = prevPicOrderCntMsb + MaxPicOrderCntLsb; + else if ((sh->slice_pic_order_cnt_lsb > prevPicOrderCntLsb) && + ((sh->slice_pic_order_cnt_lsb - prevPicOrderCntLsb) > (MaxPicOrderCntLsb / 2))) + PicOrderCntMsb = prevPicOrderCntMsb - MaxPicOrderCntLsb; + else + PicOrderCntMsb = prevPicOrderCntMsb; + } + poc->PicOrderCntVal = PicOrderCntMsb + sh->slice_pic_order_cnt_lsb; + } else { + if (nalu_type == EVC_IDR_NUT) { + poc->PicOrderCntVal = 0; + poc->DocOffset = -1; + } else { + int SubGopLength = (int)pow(2.0, sps->log2_sub_gop_length); + if (tid == 0) { + poc->PicOrderCntVal = poc->prevPicOrderCntVal + SubGopLength; + poc->DocOffset = 0; + poc->prevPicOrderCntVal = poc->PicOrderCntVal; + } else { + int ExpectedTemporalId; + int PocOffset; + int prevDocOffset = poc->DocOffset; + + poc->DocOffset = (prevDocOffset + 1) % SubGopLength; + if (poc->DocOffset == 0) { + poc->prevPicOrderCntVal += SubGopLength; + ExpectedTemporalId = 0; + } else + ExpectedTemporalId = 1 + (int)log2(poc->DocOffset); + while (tid != ExpectedTemporalId) { + poc->DocOffset = (poc->DocOffset + 1) % SubGopLength; + if (poc->DocOffset == 0) + ExpectedTemporalId = 0; + else + ExpectedTemporalId = 1 + (int)log2(poc->DocOffset); + } + PocOffset = (int)(SubGopLength * ((2.0 * poc->DocOffset + 1) / (int)pow(2.0, tid) - 2)); + poc->PicOrderCntVal = poc->prevPicOrderCntVal + PocOffset; + } + } + } + + return 0; +} diff --git a/libavcodec/evc_parse.h b/libavcodec/evc_parse.h new file mode 100644 index 00000000000..4712310826b --- /dev/null +++ b/libavcodec/evc_parse.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 + */ + +/** + * @file + * EVC decoder/parser shared code + */ + +#ifndef AVCODEC_EVC_PARSE_H +#define AVCODEC_EVC_PARSE_H + +#include + +#include "libavutil/intreadwrite.h" +#include "libavutil/log.h" +#include "evc.h" +#include "evc_ps.h" + +// The sturcture reflects Slice Header RBSP(raw byte sequence payload) layout +// @see ISO_IEC_23094-1 section 7.3.2.6 +// +// The following descriptors specify the parsing process of each element +// u(n) - unsigned integer using n bits +// ue(v) - unsigned integer 0-th order Exp_Golomb-coded syntax element with the left bit first +// u(n) - unsigned integer using n bits. +// When n is "v" in the syntax table, the number of bits varies in a manner dependent on the value of other syntax elements. +typedef struct EVCParserSliceHeader { + uint8_t slice_pic_parameter_set_id; // ue(v) + uint8_t single_tile_in_slice_flag; // u(1) + uint8_t first_tile_id; // u(v) + uint8_t arbitrary_slice_flag; // u(1) + uint8_t last_tile_id; // u(v) + uint32_t num_remaining_tiles_in_slice_minus1; // ue(v) + uint16_t delta_tile_id_minus1[EVC_MAX_TILE_ROWS * EVC_MAX_TILE_COLUMNS]; // ue(v) + + uint8_t slice_type; // ue(v) + uint8_t no_output_of_prior_pics_flag; // u(1) + uint8_t mmvd_group_enable_flag; // u(1) + uint8_t slice_alf_enabled_flag; // u(1) + + uint8_t slice_alf_luma_aps_id; // u(5) + uint8_t slice_alf_map_flag; // u(1) + uint8_t slice_alf_chroma_idc; // u(2) + uint8_t slice_alf_chroma_aps_id; // u(5) + uint8_t slice_alf_chroma_map_flag; // u(1) + uint8_t slice_alf_chroma2_aps_id; // u(5) + uint8_t slice_alf_chroma2_map_flag; // u(1) + uint16_t slice_pic_order_cnt_lsb; // u(v) + + // @note + // Currently the structure does not reflect the entire Slice Header RBSP layout. + // It contains only the fields that are necessary to read from the NAL unit all the values + // necessary for the correct initialization of the AVCodecContext structure. + + // @note + // If necessary, add the missing fields to the structure to reflect + // the contents of the entire NAL unit of the SPS type + +} EVCParserSliceHeader; + +// picture order count of the current picture +typedef struct EVCParserPoc { + int PicOrderCntVal; // current picture order count value + int prevPicOrderCntVal; // the picture order count of the previous Tid0 picture + int DocOffset; // the decoding order count of the previous picture +} EVCParserPoc; + +static inline uint32_t evc_read_nal_unit_length(const uint8_t *bits, int bits_size, void *logctx) +{ + uint32_t nalu_len = 0; + + if (bits_size < EVC_NALU_LENGTH_PREFIX_SIZE) { + av_log(logctx, AV_LOG_ERROR, "Can't read NAL unit length\n"); + return 0; + } + + nalu_len = AV_RB32(bits); + + return nalu_len; +} + +int ff_evc_parse_slice_header(GetBitContext *gb, EVCParserSliceHeader *sh, + const EVCParamSets *ps, enum EVCNALUnitType nalu_type); + +// POC (picture order count of the current picture) derivation +// @see ISO/IEC 23094-1:2020(E) 8.3.1 Decoding process for picture order count +int ff_evc_derive_poc(const EVCParamSets *ps, const EVCParserSliceHeader *sh, + EVCParserPoc *poc, enum EVCNALUnitType nalu_type, int tid); + +#endif /* AVCODEC_EVC_PARSE_H */ diff --git a/libavcodec/evc_parser.c b/libavcodec/evc_parser.c new file mode 100644 index 00000000000..8590ebcdafc --- /dev/null +++ b/libavcodec/evc_parser.c @@ -0,0 +1,377 @@ +/* + * EVC format parser + * + * Copyright (C) 2021 Dawid Kozinski + * + * 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 "avcodec.h" +#include "bytestream.h" +#include "evc.h" +#include "evc_parse.h" + +typedef struct EVCParserContext { + EVCParamSets ps; + EVCParserPoc poc; + + int parsed_extradata; +} EVCParserContext; + +#define NUM_CHROMA_FORMATS 4 // @see ISO_IEC_23094-1 section 6.2 table 2 + +static const enum AVPixelFormat pix_fmts_8bit[NUM_CHROMA_FORMATS] = { + AV_PIX_FMT_GRAY8, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P +}; + +static const enum AVPixelFormat pix_fmts_9bit[NUM_CHROMA_FORMATS] = { + AV_PIX_FMT_GRAY9, AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9 +}; + +static const enum AVPixelFormat pix_fmts_10bit[NUM_CHROMA_FORMATS] = { + AV_PIX_FMT_GRAY10, AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10 +}; + +static const enum AVPixelFormat pix_fmts_12bit[NUM_CHROMA_FORMATS] = { + AV_PIX_FMT_GRAY12, AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12 +}; + +static const enum AVPixelFormat pix_fmts_14bit[NUM_CHROMA_FORMATS] = { + AV_PIX_FMT_GRAY14, AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14 +}; + +static const enum AVPixelFormat pix_fmts_16bit[NUM_CHROMA_FORMATS] = { + AV_PIX_FMT_GRAY16, AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16 +}; + +static int parse_nal_unit(AVCodecParserContext *s, AVCodecContext *avctx, + const uint8_t *buf, int buf_size) +{ + EVCParserContext *ctx = s->priv_data; + GetBitContext gb; + int nalu_type, tid; + int ret; + + if (buf_size <= 0) { + av_log(avctx, AV_LOG_ERROR, "Invalid NAL unit size: (%d)\n", buf_size); + return AVERROR_INVALIDDATA; + } + + ret = init_get_bits8(&gb, buf, buf_size); + if (ret < 0) + return ret; + + // @see ISO_IEC_23094-1_2020, 7.4.2.2 NAL unit header semantic (Table 4 - NAL unit type codes and NAL unit type classes) + // @see enum EVCNALUnitType in evc.h + if (get_bits1(&gb)) {// forbidden_zero_bit + av_log(avctx, AV_LOG_ERROR, "Invalid NAL unit header\n"); + return AVERROR_INVALIDDATA; + } + + nalu_type = get_bits(&gb, 6) - 1; + if (nalu_type < EVC_NOIDR_NUT || nalu_type > EVC_UNSPEC_NUT62) { + av_log(avctx, AV_LOG_ERROR, "Invalid NAL unit type: (%d)\n", nalu_type); + return AVERROR_INVALIDDATA; + } + + tid = get_bits(&gb, 3); + skip_bits(&gb, 5); // nuh_reserved_zero_5bits + skip_bits1(&gb); // nuh_extension_flag + + switch (nalu_type) { + case EVC_SPS_NUT: + ret = ff_evc_parse_sps(&gb, &ctx->ps); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "SPS parsing error\n"); + return ret; + } + break; + case EVC_PPS_NUT: + ret = ff_evc_parse_pps(&gb, &ctx->ps); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "PPS parsing error\n"); + return ret; + } + break; + case EVC_IDR_NUT: // Coded slice of a IDR or non-IDR picture + case EVC_NOIDR_NUT: { + const EVCParserPPS *pps; + const EVCParserSPS *sps; + EVCParserSliceHeader sh; + int bit_depth; + + ret = ff_evc_parse_slice_header(&gb, &sh, &ctx->ps, nalu_type); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Slice header parsing error\n"); + return ret; + } + + pps = ctx->ps.pps[sh.slice_pic_parameter_set_id]; + sps = ctx->ps.sps[pps->pps_seq_parameter_set_id]; + av_assert0(sps && pps); + + s->coded_width = sps->pic_width_in_luma_samples; + s->coded_height = sps->pic_height_in_luma_samples; + + if (sps->picture_cropping_flag) { + s->width = sps->pic_width_in_luma_samples - sps->picture_crop_left_offset - sps->picture_crop_right_offset; + s->height = sps->pic_height_in_luma_samples - sps->picture_crop_top_offset - sps->picture_crop_bottom_offset; + } else { + s->width = sps->pic_width_in_luma_samples; + s->height = sps->pic_height_in_luma_samples; + } + + switch (sh.slice_type) { + case EVC_SLICE_TYPE_B: { + s->pict_type = AV_PICTURE_TYPE_B; + break; + } + case EVC_SLICE_TYPE_P: { + s->pict_type = AV_PICTURE_TYPE_P; + break; + } + case EVC_SLICE_TYPE_I: { + s->pict_type = AV_PICTURE_TYPE_I; + break; + } + default: { + s->pict_type = AV_PICTURE_TYPE_NONE; + } + } + + avctx->profile = sps->profile_idc; + + if (sps->vui_parameters_present_flag && sps->vui_parameters.timing_info_present_flag) { + int64_t num = sps->vui_parameters.num_units_in_tick; + int64_t den = sps->vui_parameters.time_scale; + if (num != 0 && den != 0) + av_reduce(&avctx->framerate.den, &avctx->framerate.num, num, den, 1 << 30); + } else + avctx->framerate = (AVRational) { 0, 1 }; + + bit_depth = sps->bit_depth_chroma_minus8 + 8; + s->format = AV_PIX_FMT_NONE; + + switch (bit_depth) { + case 8: + s->format = pix_fmts_8bit[sps->chroma_format_idc]; + break; + case 9: + s->format = pix_fmts_9bit[sps->chroma_format_idc]; + break; + case 10: + s->format = pix_fmts_10bit[sps->chroma_format_idc]; + break; + case 12: + s->format = pix_fmts_12bit[sps->chroma_format_idc]; + break; + case 14: + s->format = pix_fmts_14bit[sps->chroma_format_idc]; + break; + case 16: + s->format = pix_fmts_16bit[sps->chroma_format_idc]; + break; + } + + s->key_frame = (nalu_type == EVC_IDR_NUT) ? 1 : 0; + + // POC (picture order count of the current picture) derivation + // @see ISO/IEC 23094-1:2020(E) 8.3.1 Decoding process for picture order count + ret = ff_evc_derive_poc(&ctx->ps, &sh, &ctx->poc, nalu_type, tid); + if (ret < 0) + return ret; + + s->output_picture_number = ctx->poc.PicOrderCntVal; + + break; + } + case EVC_SEI_NUT: // Supplemental Enhancement Information + case EVC_APS_NUT: // Adaptation parameter set + case EVC_FD_NUT: // Filler data + default: + break; + } + + return 0; +} + +/** + * Parse NAL units of found picture and decode some basic information. + * + * @param s codec parser context + * @param avctx codec context + * @param buf buffer with field/frame data + * @param buf_size size of the buffer + */ +static int parse_nal_units(AVCodecParserContext *s, AVCodecContext *avctx, const uint8_t *buf, int buf_size) +{ + const uint8_t *data = buf; + int data_size = buf_size; + + while (data_size > 0) { + int nalu_size = 0; + int ret; + + // Buffer size is not enough for buffer to store NAL unit 4-bytes prefix (length) + if (data_size < EVC_NALU_LENGTH_PREFIX_SIZE) + return AVERROR_INVALIDDATA; + + nalu_size = evc_read_nal_unit_length(data, data_size, avctx); + + + data += EVC_NALU_LENGTH_PREFIX_SIZE; + data_size -= EVC_NALU_LENGTH_PREFIX_SIZE; + + if (data_size < nalu_size) + return AVERROR_INVALIDDATA; + + ret = parse_nal_unit(s, avctx, data, nalu_size); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Parsing of NAL unit failed\n"); + return AVERROR_INVALIDDATA; + } + + data += nalu_size; + data_size -= nalu_size; + } + return 0; +} + +// Decoding nal units from evcC (EVCDecoderConfigurationRecord) +// @see @see ISO/IEC 14496-15:2021 Coding of audio-visual objects - Part 15: section 12.3.3.2 +static int decode_extradata(AVCodecParserContext *s, AVCodecContext *avctx) +{ + const uint8_t *data = avctx->extradata; + int size = avctx->extradata_size; + int ret = 0; + GetByteContext gb; + + bytestream2_init(&gb, data, size); + + if (!data || size <= 0) + return -1; + + // extradata is encoded as evcC format. + if (data[0] == 1) { + int num_of_arrays; // indicates the number of arrays of NAL units of the indicated type(s) + + int nalu_length_field_size; // indicates the length in bytes of the NALUnitLenght field in EVC video stream sample in the stream + // The value of this field shall be one of 0, 1, or 3 corresponding to a length encoded with 1, 2, or 4 bytes, respectively. + + if (bytestream2_get_bytes_left(&gb) < 18) { + av_log(avctx, AV_LOG_ERROR, "evcC %d too short\n", size); + return AVERROR_INVALIDDATA; + } + + bytestream2_skip(&gb, 16); + + // @see ISO/IEC 14496-15:2021 Coding of audio-visual objects - Part 15: section 12.3.3.3 + // LengthSizeMinusOne plus 1 indicates the length in bytes of the NALUnitLength field in a EVC video stream sample in the stream to which this configuration record applies. For example, a size of one byte is indicated with a value of 0. + // The value of this field shall be one of 0, 1, or 3 corresponding to a length encoded with 1, 2, or 4 bytes, respectively. + nalu_length_field_size = (bytestream2_get_byte(&gb) & 3) + 1; + if( nalu_length_field_size != 1 && + nalu_length_field_size != 2 && + nalu_length_field_size != 4 ) { + av_log(avctx, AV_LOG_ERROR, "The length in bytes of the NALUnitLenght field in a EVC video stream has unsupported value of %d\n", nalu_length_field_size); + return AVERROR_INVALIDDATA; + } + + num_of_arrays = bytestream2_get_byte(&gb); + + /* Decode nal units from evcC. */ + for (int i = 0; i < num_of_arrays; i++) { + + // @see ISO/IEC 14496-15:2021 Coding of audio-visual objects - Part 15: section 12.3.3.3 + // NAL_unit_type indicates the type of the NAL units in the following array (which shall be all of that type); + // - it takes a value as defined in ISO/IEC 23094-1; + // - it is restricted to take one of the values indicating a SPS, PPS, APS, or SEI NAL unit. + int nal_unit_type = bytestream2_get_byte(&gb) & 0x3f; + int num_nalus = bytestream2_get_be16(&gb); + + for (int j = 0; j < num_nalus; j++) { + + int nal_unit_length = bytestream2_get_be16(&gb); + + if (bytestream2_get_bytes_left(&gb) < nal_unit_length) { + av_log(avctx, AV_LOG_ERROR, "Invalid NAL unit size in extradata.\n"); + return AVERROR_INVALIDDATA; + } + + if( nal_unit_type == EVC_SPS_NUT || + nal_unit_type == EVC_PPS_NUT || + nal_unit_type == EVC_APS_NUT || + nal_unit_type == EVC_SEI_NUT ) { + if (parse_nal_unit(s, avctx, gb.buffer, nal_unit_length) != 0) { + av_log(avctx, AV_LOG_ERROR, "Parsing of NAL unit failed\n"); + return AVERROR_INVALIDDATA; + } + } + + bytestream2_skip(&gb, nal_unit_length); + } + } + } else + return -1; + + return ret; +} + +static int evc_parse(AVCodecParserContext *s, AVCodecContext *avctx, + const uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size) +{ + int next; + int ret; + EVCParserContext *ctx = s->priv_data; + + s->picture_structure = AV_PICTURE_STRUCTURE_FRAME; + s->key_frame = 0; + + if (avctx->extradata && !ctx->parsed_extradata) { + decode_extradata(s, avctx); + ctx->parsed_extradata = 1; + } + + next = buf_size; + + ret = parse_nal_units(s, avctx, buf, buf_size); + if(ret < 0) { + *poutbuf = NULL; + *poutbuf_size = 0; + return buf_size; + } + + // poutbuf contains just one Access Unit + *poutbuf = buf; + *poutbuf_size = buf_size; + + return next; +} + +static void evc_parser_close(AVCodecParserContext *s) +{ + EVCParserContext *ctx = s->priv_data; + + ff_evc_ps_free(&ctx->ps); +} + +const AVCodecParser ff_evc_parser = { + .codec_ids = { AV_CODEC_ID_EVC }, + .priv_data_size = sizeof(EVCParserContext), + .parser_parse = evc_parse, + .parser_close = evc_parser_close, +}; diff --git a/libavcodec/evc_ps.c b/libavcodec/evc_ps.c new file mode 100644 index 00000000000..64384a392c2 --- /dev/null +++ b/libavcodec/evc_ps.c @@ -0,0 +1,409 @@ +/* + * 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 "get_bits.h" +#include "golomb.h" +#include "evc.h" +#include "evc_ps.h" + +#define EXTENDED_SAR 255 + +// @see ISO_IEC_23094-1 (7.3.7 Reference picture list structure syntax) +static int ref_pic_list_struct(GetBitContext *gb, RefPicListStruct *rpl) +{ + uint32_t delta_poc_st, strp_entry_sign_flag = 0; + rpl->ref_pic_num = get_ue_golomb_long(gb); + if (rpl->ref_pic_num > 0) { + delta_poc_st = get_ue_golomb_long(gb); + + rpl->ref_pics[0] = delta_poc_st; + if (rpl->ref_pics[0] != 0) { + strp_entry_sign_flag = get_bits(gb, 1); + + rpl->ref_pics[0] *= 1 - (strp_entry_sign_flag << 1); + } + } + + for (int i = 1; i < rpl->ref_pic_num; ++i) { + delta_poc_st = get_ue_golomb_long(gb); + if (delta_poc_st != 0) + strp_entry_sign_flag = get_bits(gb, 1); + rpl->ref_pics[i] = rpl->ref_pics[i - 1] + delta_poc_st * (1 - (strp_entry_sign_flag << 1)); + } + + return 0; +} + +// @see ISO_IEC_23094-1 (E.2.2 HRD parameters syntax) +static int hrd_parameters(GetBitContext *gb, HRDParameters *hrd) +{ + hrd->cpb_cnt_minus1 = get_ue_golomb_31(gb); + hrd->bit_rate_scale = get_bits(gb, 4); + hrd->cpb_size_scale = get_bits(gb, 4); + for (int SchedSelIdx = 0; SchedSelIdx <= hrd->cpb_cnt_minus1; SchedSelIdx++) { + hrd->bit_rate_value_minus1[SchedSelIdx] = get_ue_golomb_long(gb); + hrd->cpb_size_value_minus1[SchedSelIdx] = get_ue_golomb_long(gb); + hrd->cbr_flag[SchedSelIdx] = get_bits(gb, 1); + } + hrd->initial_cpb_removal_delay_length_minus1 = get_bits(gb, 5); + hrd->cpb_removal_delay_length_minus1 = get_bits(gb, 5); + hrd->cpb_removal_delay_length_minus1 = get_bits(gb, 5); + hrd->time_offset_length = get_bits(gb, 5); + + return 0; +} + +// @see ISO_IEC_23094-1 (E.2.1 VUI parameters syntax) +static int vui_parameters(GetBitContext *gb, VUIParameters *vui) +{ + vui->aspect_ratio_info_present_flag = get_bits(gb, 1); + if (vui->aspect_ratio_info_present_flag) { + vui->aspect_ratio_idc = get_bits(gb, 8); + if (vui->aspect_ratio_idc == EXTENDED_SAR) { + vui->sar_width = get_bits(gb, 16); + vui->sar_height = get_bits(gb, 16); + } + } + vui->overscan_info_present_flag = get_bits(gb, 1); + if (vui->overscan_info_present_flag) + vui->overscan_appropriate_flag = get_bits(gb, 1); + vui->video_signal_type_present_flag = get_bits(gb, 1); + if (vui->video_signal_type_present_flag) { + vui->video_format = get_bits(gb, 3); + vui->video_full_range_flag = get_bits(gb, 1); + vui->colour_description_present_flag = get_bits(gb, 1); + if (vui->colour_description_present_flag) { + vui->colour_primaries = get_bits(gb, 8); + vui->transfer_characteristics = get_bits(gb, 8); + vui->matrix_coefficients = get_bits(gb, 8); + } + } + vui->chroma_loc_info_present_flag = get_bits(gb, 1); + if (vui->chroma_loc_info_present_flag) { + vui->chroma_sample_loc_type_top_field = get_ue_golomb_31(gb); + vui->chroma_sample_loc_type_bottom_field = get_ue_golomb_31(gb); + } + vui->neutral_chroma_indication_flag = get_bits(gb, 1); + + vui->field_seq_flag = get_bits(gb, 1); + + vui->timing_info_present_flag = get_bits(gb, 1); + if (vui->timing_info_present_flag) { + vui->num_units_in_tick = get_bits_long(gb, 32); + vui->time_scale = get_bits_long(gb, 32); + vui->fixed_pic_rate_flag = get_bits(gb, 1); + } + vui->nal_hrd_parameters_present_flag = get_bits(gb, 1); + if (vui->nal_hrd_parameters_present_flag) + hrd_parameters(gb, &vui->hrd_parameters); + vui->vcl_hrd_parameters_present_flag = get_bits(gb, 1); + if (vui->vcl_hrd_parameters_present_flag) + hrd_parameters(gb, &vui->hrd_parameters); + if (vui->nal_hrd_parameters_present_flag || vui->vcl_hrd_parameters_present_flag) + vui->low_delay_hrd_flag = get_bits(gb, 1); + vui->pic_struct_present_flag = get_bits(gb, 1); + vui->bitstream_restriction_flag = get_bits(gb, 1); + if (vui->bitstream_restriction_flag) { + vui->motion_vectors_over_pic_boundaries_flag = get_bits(gb, 1); + vui->max_bytes_per_pic_denom = get_ue_golomb_31(gb); + vui->max_bits_per_mb_denom = get_ue_golomb_31(gb); + vui->log2_max_mv_length_horizontal = get_ue_golomb_31(gb); + vui->log2_max_mv_length_vertical = get_ue_golomb_31(gb); + vui->num_reorder_pics = get_ue_golomb_long(gb); + vui->max_dec_pic_buffering = get_ue_golomb_long(gb); + } + + return 0; +} + +// @see ISO_IEC_23094-1 (7.3.2.1 SPS RBSP syntax) +int ff_evc_parse_sps(GetBitContext *gb, EVCParamSets *ps) +{ + EVCParserSPS *sps; + unsigned sps_seq_parameter_set_id; + int ret; + + sps_seq_parameter_set_id = get_ue_golomb(gb); + + if (sps_seq_parameter_set_id >= EVC_MAX_SPS_COUNT) + return AVERROR_INVALIDDATA; + + sps = av_mallocz(sizeof(*sps)); + if (!sps) + return AVERROR(ENOMEM); + + sps->sps_seq_parameter_set_id = sps_seq_parameter_set_id; + + // the Baseline profile is indicated by profile_idc eqal to 0 + // the Main profile is indicated by profile_idc eqal to 1 + sps->profile_idc = get_bits(gb, 8); + + sps->level_idc = get_bits(gb, 8); + + skip_bits_long(gb, 32); /* skip toolset_idc_h */ + skip_bits_long(gb, 32); /* skip toolset_idc_l */ + + // 0 - monochrome + // 1 - 4:2:0 + // 2 - 4:2:2 + // 3 - 4:4:4 + sps->chroma_format_idc = get_ue_golomb_31(gb); + + sps->pic_width_in_luma_samples = get_ue_golomb_long(gb); + sps->pic_height_in_luma_samples = get_ue_golomb_long(gb); + + sps->bit_depth_luma_minus8 = get_ue_golomb_31(gb); + sps->bit_depth_chroma_minus8 = get_ue_golomb_31(gb); + + sps->sps_btt_flag = get_bits1(gb); + if (sps->sps_btt_flag) { + sps->log2_ctu_size_minus2 = get_ue_golomb_long(gb); + sps->log2_min_cb_size_minus2 = get_ue_golomb_long(gb); + sps->log2_diff_ctu_max_14_cb_size = get_ue_golomb_long(gb); + sps->log2_diff_ctu_max_tt_cb_size = get_ue_golomb_long(gb); + sps->log2_diff_min_cb_min_tt_cb_size_minus2 = get_ue_golomb_long(gb); + } + + sps->sps_suco_flag = get_bits1(gb); + if (sps->sps_suco_flag) { + sps->log2_diff_ctu_size_max_suco_cb_size = get_ue_golomb_long(gb); + sps->log2_diff_max_suco_min_suco_cb_size = get_ue_golomb_long(gb); + } + + sps->sps_admvp_flag = get_bits1(gb); + if (sps->sps_admvp_flag) { + sps->sps_affine_flag = get_bits1(gb); + sps->sps_amvr_flag = get_bits1(gb); + sps->sps_dmvr_flag = get_bits1(gb); + sps->sps_mmvd_flag = get_bits1(gb); + sps->sps_hmvp_flag = get_bits1(gb); + } + + sps->sps_eipd_flag = get_bits1(gb); + if (sps->sps_eipd_flag) { + sps->sps_ibc_flag = get_bits1(gb); + if (sps->sps_ibc_flag) + sps->log2_max_ibc_cand_size_minus2 = get_ue_golomb(gb); + } + + sps->sps_cm_init_flag = get_bits1(gb); + if (sps->sps_cm_init_flag) + sps->sps_adcc_flag = get_bits1(gb); + + sps->sps_iqt_flag = get_bits1(gb); + if (sps->sps_iqt_flag) + sps->sps_ats_flag = get_bits1(gb); + + sps->sps_addb_flag = get_bits1(gb); + sps->sps_alf_flag = get_bits1(gb); + sps->sps_htdf_flag = get_bits1(gb); + sps->sps_rpl_flag = get_bits1(gb); + sps->sps_pocs_flag = get_bits1(gb); + sps->sps_dquant_flag = get_bits1(gb); + sps->sps_dra_flag = get_bits1(gb); + + if (sps->sps_pocs_flag) { + sps->log2_max_pic_order_cnt_lsb_minus4 = get_ue_golomb(gb); + if (sps->log2_max_pic_order_cnt_lsb_minus4 > 12U) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + } + + if (!sps->sps_pocs_flag || !sps->sps_rpl_flag) { + sps->log2_sub_gop_length = get_ue_golomb(gb); + if (sps->log2_sub_gop_length > 5U) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + if (sps->log2_sub_gop_length == 0) + sps->log2_ref_pic_gap_length = get_ue_golomb(gb); + } + + if (!sps->sps_rpl_flag) + sps->max_num_tid0_ref_pics = get_ue_golomb_31(gb); + else { + sps->sps_max_dec_pic_buffering_minus1 = get_ue_golomb_long(gb); + sps->long_term_ref_pic_flag = get_bits1(gb); + sps->rpl1_same_as_rpl0_flag = get_bits1(gb); + sps->num_ref_pic_list_in_sps[0] = get_ue_golomb(gb); + + if ((unsigned)sps->num_ref_pic_list_in_sps[0] >= EVC_MAX_NUM_RPLS) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + for (int i = 0; i < sps->num_ref_pic_list_in_sps[0]; ++i) + ref_pic_list_struct(gb, &sps->rpls[0][i]); + + if (!sps->rpl1_same_as_rpl0_flag) { + sps->num_ref_pic_list_in_sps[1] = get_ue_golomb(gb); + if ((unsigned)sps->num_ref_pic_list_in_sps[1] >= EVC_MAX_NUM_RPLS) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + for (int i = 0; i < sps->num_ref_pic_list_in_sps[1]; ++i) + ref_pic_list_struct(gb, &sps->rpls[1][i]); + } + } + + sps->picture_cropping_flag = get_bits1(gb); + + if (sps->picture_cropping_flag) { + sps->picture_crop_left_offset = get_ue_golomb_long(gb); + sps->picture_crop_right_offset = get_ue_golomb_long(gb); + sps->picture_crop_top_offset = get_ue_golomb_long(gb); + sps->picture_crop_bottom_offset = get_ue_golomb_long(gb); + } + + if (sps->chroma_format_idc != 0) { + sps->chroma_qp_table_struct.chroma_qp_table_present_flag = get_bits1(gb); + + if (sps->chroma_qp_table_struct.chroma_qp_table_present_flag) { + sps->chroma_qp_table_struct.same_qp_table_for_chroma = get_bits1(gb); + sps->chroma_qp_table_struct.global_offset_flag = get_bits1(gb); + for (int i = 0; i < (sps->chroma_qp_table_struct.same_qp_table_for_chroma ? 1 : 2); i++) { + sps->chroma_qp_table_struct.num_points_in_qp_table_minus1[i] = get_ue_golomb(gb); + if (sps->chroma_qp_table_struct.num_points_in_qp_table_minus1[i] >= EVC_MAX_QP_TABLE_SIZE) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + for (int j = 0; j <= sps->chroma_qp_table_struct.num_points_in_qp_table_minus1[i]; j++) { + sps->chroma_qp_table_struct.delta_qp_in_val_minus1[i][j] = get_bits(gb, 6); + sps->chroma_qp_table_struct.delta_qp_out_val[i][j] = get_se_golomb_long(gb); + } + } + } + } + + sps->vui_parameters_present_flag = get_bits1(gb); + if (sps->vui_parameters_present_flag) + vui_parameters(gb, &(sps->vui_parameters)); + + // @note + // If necessary, add the missing fields to the EVCParserSPS structure + // and then extend parser implementation + + av_freep(&ps->sps[sps_seq_parameter_set_id]); + ps->sps[sps_seq_parameter_set_id] = sps; + + return 0; +fail: + av_free(sps); + return ret; +} + +// @see ISO_IEC_23094-1 (7.3.2.2 SPS RBSP syntax) +// +// @note +// The current implementation of parse_sps function doesn't handle VUI parameters parsing. +// If it will be needed, parse_sps function could be extended to handle VUI parameters parsing +// to initialize fields of the AVCodecContex i.e. color_primaries, color_trc,color_range +// +int ff_evc_parse_pps(GetBitContext *gb, EVCParamSets *ps) +{ + EVCParserPPS *pps; + unsigned pps_pic_parameter_set_id; + int ret; + + pps_pic_parameter_set_id = get_ue_golomb(gb); + if (pps_pic_parameter_set_id >= EVC_MAX_PPS_COUNT) + return AVERROR_INVALIDDATA; + + pps = av_mallocz(sizeof(*pps)); + if (!pps) + return AVERROR(ENOMEM); + + pps->pps_pic_parameter_set_id = pps_pic_parameter_set_id; + + pps->pps_seq_parameter_set_id = get_ue_golomb(gb); + if (pps->pps_seq_parameter_set_id >= EVC_MAX_SPS_COUNT) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + pps->num_ref_idx_default_active_minus1[0] = get_ue_golomb(gb); + pps->num_ref_idx_default_active_minus1[1] = get_ue_golomb(gb); + pps->additional_lt_poc_lsb_len = get_ue_golomb(gb); + pps->rpl1_idx_present_flag = get_bits1(gb); + pps->single_tile_in_pic_flag = get_bits1(gb); + + if (!pps->single_tile_in_pic_flag) { + pps->num_tile_columns_minus1 = get_ue_golomb(gb); + pps->num_tile_rows_minus1 = get_ue_golomb(gb); + if (pps->num_tile_columns_minus1 >= EVC_MAX_TILE_COLUMNS || + pps->num_tile_rows_minus1 >= EVC_MAX_TILE_ROWS) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + pps->uniform_tile_spacing_flag = get_bits1(gb); + + if (!pps->uniform_tile_spacing_flag) { + for (int i = 0; i < pps->num_tile_columns_minus1; i++) + pps->tile_column_width_minus1[i] = get_ue_golomb(gb); + + for (int i = 0; i < pps->num_tile_rows_minus1; i++) + pps->tile_row_height_minus1[i] = get_ue_golomb(gb); + } + pps->loop_filter_across_tiles_enabled_flag = get_bits1(gb); + pps->tile_offset_len_minus1 = get_ue_golomb(gb); + } + + pps->tile_id_len_minus1 = get_ue_golomb(gb); + if (pps->tile_id_len_minus1 > 15U) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + pps->explicit_tile_id_flag = get_bits1(gb); + + if (pps->explicit_tile_id_flag) { + for (int i = 0; i <= pps->num_tile_rows_minus1; i++) { + for (int j = 0; j <= pps->num_tile_columns_minus1; j++) + pps->tile_id_val[i][j] = get_bits(gb, pps->tile_id_len_minus1 + 1); + } + } + + pps->pic_dra_enabled_flag = 0; + pps->pic_dra_enabled_flag = get_bits1(gb); + + if (pps->pic_dra_enabled_flag) + pps->pic_dra_aps_id = get_bits(gb, 5); + + pps->arbitrary_slice_present_flag = get_bits1(gb); + pps->constrained_intra_pred_flag = get_bits1(gb); + pps->cu_qp_delta_enabled_flag = get_bits1(gb); + + if (pps->cu_qp_delta_enabled_flag) + pps->log2_cu_qp_delta_area_minus6 = get_ue_golomb(gb); + + av_freep(&ps->pps[pps_pic_parameter_set_id]); + ps->pps[pps_pic_parameter_set_id] = pps; + + return 0; +fail: + av_free(pps); + return ret; +} + +void ff_evc_ps_free(EVCParamSets *ps) { + for (int i = 0; i < EVC_MAX_SPS_COUNT; i++) + av_freep(&ps->sps[i]); + + for (int i = 0; i < EVC_MAX_PPS_COUNT; i++) + av_freep(&ps->pps[i]); +} diff --git a/libavcodec/evc_ps.h b/libavcodec/evc_ps.h new file mode 100644 index 00000000000..336953b1769 --- /dev/null +++ b/libavcodec/evc_ps.h @@ -0,0 +1,224 @@ +/* + * 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 + * EVC decoder/parser shared code + */ + +#ifndef AVCODEC_EVC_PS_H +#define AVCODEC_EVC_PS_H + +#include + +#include "evc.h" +#include "get_bits.h" + +#define EVC_MAX_QP_TABLE_SIZE 58 +#define NUM_CPB 32 + +// rpl structure +typedef struct RefPicListStruct { + uint32_t ref_pic_num; + uint32_t ref_pics[EVC_MAX_NUM_REF_PICS]; +} RefPicListStruct; + +// chromaQP table structure to be signalled in SPS +typedef struct ChromaQpTable { + uint8_t chroma_qp_table_present_flag; // u(1) + uint8_t same_qp_table_for_chroma; // u(1) + uint8_t global_offset_flag; // u(1) + uint8_t num_points_in_qp_table_minus1[2]; // ue(v) + uint8_t delta_qp_in_val_minus1[2][EVC_MAX_QP_TABLE_SIZE]; // u(6) + int delta_qp_out_val[2][EVC_MAX_QP_TABLE_SIZE]; // se(v) +} ChromaQpTable; + +// Hypothetical Reference Decoder (HRD) parameters, part of VUI +typedef struct HRDParameters { + uint8_t cpb_cnt_minus1; // ue(v) + uint8_t bit_rate_scale; // u(4) + uint8_t cpb_size_scale; // u(4) + uint32_t bit_rate_value_minus1[NUM_CPB]; // ue(v) + uint32_t cpb_size_value_minus1[NUM_CPB]; // ue(v) + uint8_t cbr_flag[NUM_CPB]; // u(1) + uint8_t initial_cpb_removal_delay_length_minus1; // u(5) + uint8_t cpb_removal_delay_length_minus1; // u(5) + uint8_t dpb_output_delay_length_minus1; // u(5) + uint8_t time_offset_length; // u(5) +} HRDParameters; + +// video usability information (VUI) part of SPS +typedef struct VUIParameters { + uint8_t aspect_ratio_info_present_flag; // u(1) + uint8_t aspect_ratio_idc; // u(8) + uint16_t sar_width; // u(16) + uint16_t sar_height; // u(16) + uint8_t overscan_info_present_flag; // u(1) + uint8_t overscan_appropriate_flag; // u(1) + uint8_t video_signal_type_present_flag; // u(1) + uint8_t video_format; // u(3) + uint8_t video_full_range_flag; // u(1) + uint8_t colour_description_present_flag; // u(1) + uint8_t colour_primaries; // u(8) + uint8_t transfer_characteristics; // u(8) + uint8_t matrix_coefficients; // u(8) + uint8_t chroma_loc_info_present_flag; // u(1) + uint8_t chroma_sample_loc_type_top_field; // ue(v) + uint8_t chroma_sample_loc_type_bottom_field; // ue(v) + uint8_t neutral_chroma_indication_flag; // u(1) + uint8_t field_seq_flag; // u(1) + uint8_t timing_info_present_flag; // u(1) + uint32_t num_units_in_tick; // u(32) + uint32_t time_scale; // u(32) + uint8_t fixed_pic_rate_flag; // u(1) + uint8_t nal_hrd_parameters_present_flag; // u(1) + uint8_t vcl_hrd_parameters_present_flag; // u(1) + uint8_t low_delay_hrd_flag; // u(1) + uint8_t pic_struct_present_flag; // u(1) + uint8_t bitstream_restriction_flag; // u(1) + uint8_t motion_vectors_over_pic_boundaries_flag; // u(1) + uint8_t max_bytes_per_pic_denom; // ue(v) + uint8_t max_bits_per_mb_denom; // ue(v) + uint8_t log2_max_mv_length_horizontal; // ue(v) + uint8_t log2_max_mv_length_vertical; // ue(v) + uint32_t num_reorder_pics; // ue(v) + uint32_t max_dec_pic_buffering; // ue(v) + + HRDParameters hrd_parameters; +} VUIParameters; + +// The sturcture reflects SPS RBSP(raw byte sequence payload) layout +// @see ISO_IEC_23094-1 section 7.3.2.1 +// +// The following descriptors specify the parsing process of each element +// u(n) - unsigned integer using n bits +// ue(v) - unsigned integer 0-th order Exp_Golomb-coded syntax element with the left bit first +typedef struct EVCParserSPS { + uint8_t sps_seq_parameter_set_id; // ue(v) + uint8_t profile_idc; // u(8) + uint8_t level_idc; // u(8) + uint32_t toolset_idc_h; // u(32) + uint32_t toolset_idc_l; // u(32) + uint8_t chroma_format_idc; // ue(v) + uint32_t pic_width_in_luma_samples; // ue(v) + uint32_t pic_height_in_luma_samples; // ue(v) + uint8_t bit_depth_luma_minus8; // ue(v) + uint8_t bit_depth_chroma_minus8; // ue(v) + + uint8_t sps_btt_flag; // u(1) + uint32_t log2_ctu_size_minus2; // ue(v) + uint32_t log2_min_cb_size_minus2; // ue(v) + uint32_t log2_diff_ctu_max_14_cb_size; // ue(v) + uint32_t log2_diff_ctu_max_tt_cb_size; // ue(v) + uint32_t log2_diff_min_cb_min_tt_cb_size_minus2; // ue(v) + + uint8_t sps_suco_flag; // u(1) + uint32_t log2_diff_ctu_size_max_suco_cb_size; // ue(v) + uint32_t log2_diff_max_suco_min_suco_cb_size; // ue(v) + + uint8_t sps_admvp_flag; // u(1) + uint8_t sps_affine_flag; // u(1) + uint8_t sps_amvr_flag; // u(1) + uint8_t sps_dmvr_flag; // u(1) + uint8_t sps_mmvd_flag; // u(1) + uint8_t sps_hmvp_flag; // u(1) + + uint8_t sps_eipd_flag; // u(1) + uint8_t sps_ibc_flag; // u(1) + uint32_t log2_max_ibc_cand_size_minus2; // ue(v) + + uint8_t sps_cm_init_flag; // u(1) + uint8_t sps_adcc_flag; // u(1) + + uint8_t sps_iqt_flag; // u(1) + uint8_t sps_ats_flag; // u(1) + + uint8_t sps_addb_flag; // u(1) + uint8_t sps_alf_flag; // u(1) + uint8_t sps_htdf_flag; // u(1) + uint8_t sps_rpl_flag; // u(1) + uint8_t sps_pocs_flag; // u(1) + uint8_t sps_dquant_flag; // u(1) + uint8_t sps_dra_flag; // u(1) + + uint32_t log2_max_pic_order_cnt_lsb_minus4; // ue(v) + uint32_t log2_sub_gop_length; // ue(v) + uint32_t log2_ref_pic_gap_length; // ue(v) + + uint8_t max_num_tid0_ref_pics; // ue(v) + + uint32_t sps_max_dec_pic_buffering_minus1; // ue(v) + uint8_t long_term_ref_pic_flag; // u(1) + uint8_t rpl1_same_as_rpl0_flag; // u(1) + uint8_t num_ref_pic_list_in_sps[2]; // ue(v) + struct RefPicListStruct rpls[2][EVC_MAX_NUM_RPLS]; + + uint8_t picture_cropping_flag; // u(1) + uint32_t picture_crop_left_offset; // ue(v) + uint32_t picture_crop_right_offset; // ue(v) + uint32_t picture_crop_top_offset; // ue(v) + uint32_t picture_crop_bottom_offset; // ue(v) + + struct ChromaQpTable chroma_qp_table_struct; + + uint8_t vui_parameters_present_flag; // u(1) + + struct VUIParameters vui_parameters; + +} EVCParserSPS; + +typedef struct EVCParserPPS { + uint8_t pps_pic_parameter_set_id; // ue(v) + uint8_t pps_seq_parameter_set_id; // ue(v) + uint8_t num_ref_idx_default_active_minus1[2]; // ue(v) + uint8_t additional_lt_poc_lsb_len; // ue(v) + uint8_t rpl1_idx_present_flag; // u(1) + uint8_t single_tile_in_pic_flag; // u(1) + uint32_t num_tile_columns_minus1; // ue(v) + uint32_t num_tile_rows_minus1; // ue(v) + uint8_t uniform_tile_spacing_flag; // u(1) + uint32_t tile_column_width_minus1[EVC_MAX_TILE_COLUMNS]; // ue(v) + uint32_t tile_row_height_minus1[EVC_MAX_TILE_ROWS]; // ue(v) + uint8_t loop_filter_across_tiles_enabled_flag; // u(1) + uint32_t tile_offset_len_minus1; // ue(v) + uint8_t tile_id_len_minus1; // ue(v) + uint8_t explicit_tile_id_flag; // u(1) + uint32_t tile_id_val[EVC_MAX_TILE_ROWS][EVC_MAX_TILE_COLUMNS]; // u(v) + uint8_t pic_dra_enabled_flag; // u(1) + uint8_t pic_dra_aps_id; // u(5) + uint8_t arbitrary_slice_present_flag; // u(1) + uint8_t constrained_intra_pred_flag; // u(1) + uint8_t cu_qp_delta_enabled_flag; // u(1) + uint32_t log2_cu_qp_delta_area_minus6; // ue(v) + +} EVCParserPPS; + +typedef struct EVCParamSets { + EVCParserSPS *sps[EVC_MAX_SPS_COUNT]; + EVCParserPPS *pps[EVC_MAX_PPS_COUNT]; +} EVCParamSets; + +// @see ISO_IEC_23094-1 (7.3.2.1 SPS RBSP syntax) +int ff_evc_parse_sps(GetBitContext *gb, EVCParamSets *ps); + +// @see ISO_IEC_23094-1 (7.3.2.2 SPS RBSP syntax) +int ff_evc_parse_pps(GetBitContext *gb, EVCParamSets *ps); + +void ff_evc_ps_free(EVCParamSets *ps); + +#endif /* AVCODEC_EVC_PS_H */ diff --git a/libavcodec/evrcdec.c b/libavcodec/evrcdec.c index c4b0ad2957b..af7640d7e15 100644 --- a/libavcodec/evrcdec.c +++ b/libavcodec/evrcdec.c @@ -221,8 +221,8 @@ static evrc_packet_rate determine_bitrate(AVCodecContext *avctx, static void warn_insufficient_frame_quality(AVCodecContext *avctx, const char *message) { - av_log(avctx, AV_LOG_WARNING, "Frame #%d, %s\n", - avctx->frame_number, message); + av_log(avctx, AV_LOG_WARNING, "Frame #%"PRId64", %s\n", + avctx->frame_num, message); } /** diff --git a/libavcodec/exr.c b/libavcodec/exr.c index 6a0af96ce4f..fae1d08ab03 100644 --- a/libavcodec/exr.c +++ b/libavcodec/exr.c @@ -36,11 +36,11 @@ #include "libavutil/avassert.h" #include "libavutil/common.h" +#include "libavutil/csp.h" #include "libavutil/imgutils.h" #include "libavutil/intfloat.h" #include "libavutil/avstring.h" #include "libavutil/opt.h" -#include "libavutil/color_utils.h" #include "libavutil/half2float.h" #include "avcodec.h" @@ -1119,7 +1119,6 @@ static int dwa_uncompress(const EXRContext *s, const uint8_t *src, int compresse } { - const float scale = s->pixel_type == EXR_FLOAT ? 2.f : 1.f; const int o = s->nb_channels == 4; float *bo = ((float *)td->uncompressed_data) + y * td->xsize * s->nb_channels + td->xsize * (o + 0) + x; @@ -1137,9 +1136,9 @@ static int dwa_uncompress(const EXRContext *s, const uint8_t *src, int compresse convert(yb[idx], ub[idx], vb[idx], &bo[xx], &go[xx], &ro[xx]); - bo[xx] = to_linear(bo[xx], scale); - go[xx] = to_linear(go[xx], scale); - ro[xx] = to_linear(ro[xx], scale); + bo[xx] = to_linear(bo[xx], 1.f); + go[xx] = to_linear(go[xx], 1.f); + ro[xx] = to_linear(ro[xx], 1.f); } bo += td->xsize * s->nb_channels; @@ -1189,7 +1188,7 @@ static int decode_block(AVCodecContext *avctx, void *tdata, int i, x, buf_size = s->buf_size; int c, rgb_channel_count; float one_gamma = 1.0f / s->gamma; - avpriv_trc_function trc_func = avpriv_get_trc_function_from_trc(s->apply_trc_type); + av_csp_trc_function trc_func = av_csp_trc_func_from_id(s->apply_trc_type); int ret; line_offset = AV_RL64(s->gb.buffer + jobnr * 8); @@ -1930,8 +1929,10 @@ static int decode_header(EXRContext *s, AVFrame *frame) bytestream2_get_buffer(gb, key, FFMIN(sizeof(key) - 1, var_size)); if (strncmp("scanlineimage", key, var_size) && - strncmp("tiledimage", key, var_size)) - return AVERROR_PATCHWELCOME; + strncmp("tiledimage", key, var_size)) { + ret = AVERROR_PATCHWELCOME; + goto fail; + } continue; } else if ((var_size = check_header_variable(s, "preview", @@ -1939,12 +1940,16 @@ static int decode_header(EXRContext *s, AVFrame *frame) uint32_t pw = bytestream2_get_le32(gb); uint32_t ph = bytestream2_get_le32(gb); uint64_t psize = pw * ph; - if (psize > INT64_MAX / 4) - return AVERROR_INVALIDDATA; + if (psize > INT64_MAX / 4) { + ret = AVERROR_INVALIDDATA; + goto fail; + } psize *= 4; - if ((int64_t)psize >= bytestream2_get_bytes_left(gb)) - return AVERROR_INVALIDDATA; + if ((int64_t)psize >= bytestream2_get_bytes_left(gb)) { + ret = AVERROR_INVALIDDATA; + goto fail; + } bytestream2_skip(gb, psize); @@ -2215,7 +2220,7 @@ static av_cold int decode_init(AVCodecContext *avctx) uint32_t i; union av_intfloat32 t; float one_gamma = 1.0f / s->gamma; - avpriv_trc_function trc_func = NULL; + av_csp_trc_function trc_func = NULL; ff_init_half2float_tables(&s->h2f_tables); @@ -2227,7 +2232,7 @@ static av_cold int decode_init(AVCodecContext *avctx) ff_bswapdsp_init(&s->bbdsp); #endif - trc_func = avpriv_get_trc_function_from_trc(s->apply_trc_type); + trc_func = av_csp_trc_func_from_id(s->apply_trc_type); if (trc_func) { for (i = 0; i < 65536; ++i) { t.i = half2float(i, &s->h2f_tables); diff --git a/libavcodec/exrenc.c b/libavcodec/exrenc.c index 10ed8768880..36327f498cb 100644 --- a/libavcodec/exrenc.c +++ b/libavcodec/exrenc.c @@ -547,7 +547,8 @@ const FFCodec ff_exr_encoder = { .p.priv_class = &exr_class, .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_EXR, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .init = encode_init, FF_CODEC_ENCODE_CB(encode_frame), .close = encode_close, diff --git a/libavcodec/extract_extradata_bsf.c b/libavcodec/extract_extradata_bsf.c index 329b1a61744..d5c81a27689 100644 --- a/libavcodec/extract_extradata_bsf.c +++ b/libavcodec/extract_extradata_bsf.c @@ -31,6 +31,7 @@ #include "hevc.h" #include "startcode.h" #include "vc1_common.h" +#include "vvc.h" typedef struct ExtractExtradataContext { const AVClass *class; @@ -134,6 +135,9 @@ static int extract_extradata_av1(AVBSFContext *ctx, AVPacket *pkt, static int extract_extradata_h2645(AVBSFContext *ctx, AVPacket *pkt, uint8_t **data, int *size) { + static const int extradata_nal_types_vvc[] = { + VVC_VPS_NUT, VVC_SPS_NUT, VVC_PPS_NUT, + }; static const int extradata_nal_types_hevc[] = { HEVC_NAL_VPS, HEVC_NAL_SPS, HEVC_NAL_PPS, }; @@ -148,7 +152,10 @@ static int extract_extradata_h2645(AVBSFContext *ctx, AVPacket *pkt, int nb_extradata_nal_types; int i, has_sps = 0, has_vps = 0, ret = 0; - if (ctx->par_in->codec_id == AV_CODEC_ID_HEVC) { + if (ctx->par_in->codec_id == AV_CODEC_ID_VVC) { + extradata_nal_types = extradata_nal_types_vvc; + nb_extradata_nal_types = FF_ARRAY_ELEMS(extradata_nal_types_vvc); + } else if (ctx->par_in->codec_id == AV_CODEC_ID_HEVC) { extradata_nal_types = extradata_nal_types_hevc; nb_extradata_nal_types = FF_ARRAY_ELEMS(extradata_nal_types_hevc); } else { @@ -165,7 +172,10 @@ static int extract_extradata_h2645(AVBSFContext *ctx, AVPacket *pkt, H2645NAL *nal = &s->h2645_pkt.nals[i]; if (val_in_array(extradata_nal_types, nb_extradata_nal_types, nal->type)) { extradata_size += nal->raw_size + 3; - if (ctx->par_in->codec_id == AV_CODEC_ID_HEVC) { + if (ctx->par_in->codec_id == AV_CODEC_ID_VVC) { + if (nal->type == VVC_SPS_NUT) has_sps = 1; + if (nal->type == VVC_VPS_NUT) has_vps = 1; + } else if (ctx->par_in->codec_id == AV_CODEC_ID_HEVC) { if (nal->type == HEVC_NAL_SPS) has_sps = 1; if (nal->type == HEVC_NAL_VPS) has_vps = 1; } else { @@ -177,7 +187,8 @@ static int extract_extradata_h2645(AVBSFContext *ctx, AVPacket *pkt, } if (extradata_size && - ((ctx->par_in->codec_id == AV_CODEC_ID_HEVC && has_sps && has_vps) || + ((ctx->par_in->codec_id == AV_CODEC_ID_VVC && has_sps) || + (ctx->par_in->codec_id == AV_CODEC_ID_HEVC && has_sps && has_vps) || (ctx->par_in->codec_id == AV_CODEC_ID_H264 && has_sps))) { AVBufferRef *filtered_buf = NULL; PutByteContext pb_filtered_data, pb_extradata; @@ -335,6 +346,7 @@ static const struct { { AV_CODEC_ID_MPEG2VIDEO, extract_extradata_mpeg12 }, { AV_CODEC_ID_MPEG4, extract_extradata_mpeg4 }, { AV_CODEC_ID_VC1, extract_extradata_vc1 }, + { AV_CODEC_ID_VVC, extract_extradata_h2645 }, }; static int extract_extradata_init(AVBSFContext *ctx) @@ -404,6 +416,7 @@ static const enum AVCodecID codec_ids[] = { AV_CODEC_ID_MPEG2VIDEO, AV_CODEC_ID_MPEG4, AV_CODEC_ID_VC1, + AV_CODEC_ID_VVC, AV_CODEC_ID_NONE, }; diff --git a/libavcodec/fdctdsp.c b/libavcodec/fdctdsp.c index 5306c9d047e..f8ba17426c1 100644 --- a/libavcodec/fdctdsp.c +++ b/libavcodec/fdctdsp.c @@ -18,7 +18,6 @@ #include "libavutil/attributes.h" #include "avcodec.h" -#include "dct.h" #include "faandct.h" #include "fdctdsp.h" #include "config.h" diff --git a/libavcodec/fdctdsp.h b/libavcodec/fdctdsp.h index 3e1f683b9ea..7378eab870c 100644 --- a/libavcodec/fdctdsp.h +++ b/libavcodec/fdctdsp.h @@ -21,17 +21,28 @@ #include -#include "avcodec.h" +#include "libavutil/attributes_internal.h" + +struct AVCodecContext; typedef struct FDCTDSPContext { void (*fdct)(int16_t *block /* align 16 */); void (*fdct248)(int16_t *block /* align 16 */); } FDCTDSPContext; -void ff_fdctdsp_init(FDCTDSPContext *c, AVCodecContext *avctx); -void ff_fdctdsp_init_ppc(FDCTDSPContext *c, AVCodecContext *avctx, +FF_VISIBILITY_PUSH_HIDDEN +void ff_fdctdsp_init(FDCTDSPContext *c, struct AVCodecContext *avctx); +void ff_fdctdsp_init_ppc(FDCTDSPContext *c, struct AVCodecContext *avctx, unsigned high_bit_depth); -void ff_fdctdsp_init_x86(FDCTDSPContext *c, AVCodecContext *avctx, +void ff_fdctdsp_init_x86(FDCTDSPContext *c, struct AVCodecContext *avctx, unsigned high_bit_depth); +void ff_fdct_ifast(int16_t *data); +void ff_fdct_ifast248(int16_t *data); +void ff_jpeg_fdct_islow_8(int16_t *data); +void ff_jpeg_fdct_islow_10(int16_t *data); +void ff_fdct248_islow_8(int16_t *data); +void ff_fdct248_islow_10(int16_t *data); +FF_VISIBILITY_POP_HIDDEN + #endif /* AVCODEC_FDCTDSP_H */ diff --git a/libavcodec/fflcms2.c b/libavcodec/fflcms2.c index fd370fb310f..5443f178bc9 100644 --- a/libavcodec/fflcms2.c +++ b/libavcodec/fflcms2.c @@ -17,7 +17,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/color_utils.h" #include "libavutil/csp.h" #include "fflcms2.h" diff --git a/libavcodec/ffv1.h b/libavcodec/ffv1.h index 005f3087840..04869da5c9a 100644 --- a/libavcodec/ffv1.h +++ b/libavcodec/ffv1.h @@ -85,7 +85,7 @@ typedef struct FFV1Context { int chroma_h_shift, chroma_v_shift; int transparency; int flags; - int picture_number; + int64_t picture_number; int key_frame; ThreadFrame picture, last_picture; struct FFV1Context *fsrc; diff --git a/libavcodec/ffv1dec.c b/libavcodec/ffv1dec.c index d74786cec3a..54cf075b8f0 100644 --- a/libavcodec/ffv1dec.c +++ b/libavcodec/ffv1dec.c @@ -217,13 +217,13 @@ static int decode_slice_header(const FFV1Context *f, FFV1Context *fs) ps = get_symbol(c, state, 0); if (ps == 1) { - f->cur->interlaced_frame = 1; - f->cur->top_field_first = 1; + f->cur->flags |= AV_FRAME_FLAG_INTERLACED; + f->cur->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; } else if (ps == 2) { - f->cur->interlaced_frame = 1; - f->cur->top_field_first = 0; + f->cur->flags |= AV_FRAME_FLAG_INTERLACED; + f->cur->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; } else if (ps == 3) { - f->cur->interlaced_frame = 0; + f->cur->flags &= ~AV_FRAME_FLAG_INTERLACED; } f->cur->sample_aspect_ratio.num = get_symbol(c, state, 0); f->cur->sample_aspect_ratio.den = get_symbol(c, state, 0); @@ -264,16 +264,16 @@ static int decode_slice(AVCodecContext *c, void *arg) for( si=0; fs != f->slice_context[si]; si ++) ; - if(f->fsrc && !p->key_frame) + if(f->fsrc && !(p->flags & AV_FRAME_FLAG_KEY)) ff_thread_await_progress(&f->last_picture, si, 0); - if(f->fsrc && !p->key_frame) { + if(f->fsrc && !(p->flags & AV_FRAME_FLAG_KEY)) { FFV1Context *fssrc = f->fsrc->slice_context[si]; FFV1Context *fsdst = f->slice_context[si]; av_assert1(fsdst->plane_count == fssrc->plane_count); av_assert1(fsdst == fs); - if (!p->key_frame) + if (!(p->flags & AV_FRAME_FLAG_KEY)) fsdst->slice_damaged |= fssrc->slice_damaged; for (i = 0; i < f->plane_count; i++) { @@ -310,7 +310,7 @@ static int decode_slice(AVCodecContext *c, void *arg) } if ((ret = ff_ffv1_init_slice_state(f, fs)) < 0) return ret; - if (f->cur->key_frame || fs->slice_reset_contexts) { + if ((f->cur->flags & AV_FRAME_FLAG_KEY) || fs->slice_reset_contexts) { ff_ffv1_clear_slice_state(f, fs); } else if (fs->slice_damaged) { return AVERROR_INVALIDDATA; @@ -439,6 +439,11 @@ static int read_extra_header(FFV1Context *f) av_log(f->avctx, AV_LOG_ERROR, "Invalid version in global header\n"); return AVERROR_INVALIDDATA; } + if (f->version > 4) { + av_log(f->avctx, AV_LOG_ERROR, "unsupported version %d\n", + f->version); + return AVERROR_PATCHWELCOME; + } if (f->version > 2) { c->bytestream_end -= 4; f->micro_version = get_symbol(c, state, 0); @@ -475,6 +480,11 @@ static int read_extra_header(FFV1Context *f) return AVERROR_INVALIDDATA; } + if (f->num_h_slices > MAX_SLICES / f->num_v_slices) { + av_log(f->avctx, AV_LOG_ERROR, "slice count unsupported\n"); + return AVERROR_PATCHWELCOME; + } + f->quant_table_count = get_symbol(c, state, 0); if (f->quant_table_count > (unsigned)MAX_QUANT_TABLES || !f->quant_table_count) { av_log(f->avctx, AV_LOG_ERROR, "quant table count %d is invalid\n", f->quant_table_count); @@ -871,9 +881,9 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, if (f->version < 3 && avctx->field_order > AV_FIELD_PROGRESSIVE) { /* we have interlaced material flagged in container */ - p->interlaced_frame = 1; + p->flags |= AV_FRAME_FLAG_INTERLACED; if (avctx->field_order == AV_FIELD_TT || avctx->field_order == AV_FIELD_TB) - p->top_field_first = 1; + p->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; } f->avctx = avctx; @@ -882,7 +892,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, p->pict_type = AV_PICTURE_TYPE_I; //FIXME I vs. P if (get_rac(c, &keystate)) { - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; f->key_frame_ok = 0; if ((ret = read_header(f)) < 0) return ret; @@ -893,7 +903,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, "Cannot decode non-keyframe without valid keyframe\n"); return AVERROR_INVALIDDATA; } - p->key_frame = 0; + p->flags &= ~AV_FRAME_FLAG_KEY; } if (f->ac != AC_GOLOMB_RICE) { @@ -917,7 +927,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, if (avctx->debug & FF_DEBUG_PICT_INFO) av_log(avctx, AV_LOG_DEBUG, "ver:%d keyframe:%d coder:%d ec:%d slices:%d bps:%d\n", - f->version, p->key_frame, f->ac, f->ec, f->slice_count, f->avctx->bits_per_raw_sample); + f->version, !!(p->flags & AV_FRAME_FLAG_KEY), f->ac, f->ec, f->slice_count, f->avctx->bits_per_raw_sample); ff_thread_finish_setup(avctx); diff --git a/libavcodec/ffv1enc.c b/libavcodec/ffv1enc.c index 0237ac48ebc..746f7175683 100644 --- a/libavcodec/ffv1enc.c +++ b/libavcodec/ffv1enc.c @@ -916,10 +916,10 @@ static void encode_slice_header(FFV1Context *f, FFV1Context *fs) put_symbol(c, state, f->plane[j].quant_table_index, 0); av_assert0(f->plane[j].quant_table_index == f->context_model); } - if (!f->cur_enc_frame->interlaced_frame) + if (!(f->cur_enc_frame->flags & AV_FRAME_FLAG_INTERLACED)) put_symbol(c, state, 3, 0); else - put_symbol(c, state, 1 + !f->cur_enc_frame->top_field_first, 0); + put_symbol(c, state, 1 + !(f->cur_enc_frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST), 0); put_symbol(c, state, f->cur_enc_frame->sample_aspect_ratio.num, 0); put_symbol(c, state, f->cur_enc_frame->sample_aspect_ratio.den, 0); if (f->version > 3) { @@ -1231,8 +1231,6 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, f->picture_number++; pkt->size = buf_p - pkt->data; - pkt->pts = - pkt->dts = pict->pts; pkt->flags |= AV_PKT_FLAG_KEY * f->key_frame; *got_packet = 1; @@ -1272,7 +1270,8 @@ const FFCodec ff_ffv1_encoder = { .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_FFV1, .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | - AV_CODEC_CAP_SLICE_THREADS, + AV_CODEC_CAP_SLICE_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(FFV1Context), .init = encode_init, FF_CODEC_ENCODE_CB(encode_frame), @@ -1301,5 +1300,5 @@ const FFCodec ff_ffv1_encoder = { }, .p.priv_class = &ffv1_class, - .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_EOF_FLUSH, }; diff --git a/libavcodec/fic.c b/libavcodec/fic.c index 94cf42887f3..fb998021371 100644 --- a/libavcodec/fic.c +++ b/libavcodec/fic.c @@ -406,11 +406,11 @@ static int fic_decode_frame(AVCodecContext *avctx, AVFrame *rframe, NULL, nslices, sizeof(ctx->slice_data[0]))) < 0) return ret; - ctx->frame->key_frame = 1; + ctx->frame->flags |= AV_FRAME_FLAG_KEY; ctx->frame->pict_type = AV_PICTURE_TYPE_I; for (slice = 0; slice < nslices; slice++) { if (ctx->slice_data[slice].p_frame) { - ctx->frame->key_frame = 0; + ctx->frame->flags &= ~AV_FRAME_FLAG_KEY; ctx->frame->pict_type = AV_PICTURE_TYPE_P; break; } diff --git a/libavcodec/filter_units_bsf.c b/libavcodec/filter_units_bsf.c index 38756baf420..93367531486 100644 --- a/libavcodec/filter_units_bsf.c +++ b/libavcodec/filter_units_bsf.c @@ -34,6 +34,8 @@ typedef struct FilterUnitsContext { const char *pass_types; const char *remove_types; + enum AVDiscard discard; + int discard_flags; enum { NOOP, @@ -109,7 +111,7 @@ static int filter_units_filter(AVBSFContext *bsf, AVPacket *pkt) if (err < 0) return err; - if (ctx->mode == NOOP) + if (ctx->mode == NOOP && ctx->discard <= AVDISCARD_DEFAULT) return 0; err = ff_cbs_read_packet(ctx->cbc, frag, pkt); @@ -118,14 +120,17 @@ static int filter_units_filter(AVBSFContext *bsf, AVPacket *pkt) goto fail; } - for (i = frag->nb_units - 1; i >= 0; i--) { - for (j = 0; j < ctx->nb_types; j++) { - if (frag->units[i].type == ctx->type_list[j]) - break; + ff_cbs_discard_units(ctx->cbc, frag, ctx->discard, ctx->discard_flags); + if (ctx->mode != NOOP) { + for (i = frag->nb_units - 1; i >= 0; i--) { + for (j = 0; j < ctx->nb_types; j++) { + if (frag->units[i].type == ctx->type_list[j]) + break; + } + if (ctx->mode == REMOVE ? j < ctx->nb_types + : j >= ctx->nb_types) + ff_cbs_delete_unit(frag, i); } - if (ctx->mode == REMOVE ? j < ctx->nb_types - : j >= ctx->nb_types) - ff_cbs_delete_unit(frag, i); } if (frag->nb_units == 0) { @@ -175,7 +180,7 @@ static int filter_units_init(AVBSFContext *bsf) av_log(bsf, AV_LOG_ERROR, "Failed to parse remove_types.\n"); return err; } - } else { + } else if (ctx->discard == AVDISCARD_NONE) { return 0; } @@ -183,9 +188,11 @@ static int filter_units_init(AVBSFContext *bsf) if (err < 0) return err; - // Don't actually decompose anything, we only want the unit data. - ctx->cbc->decompose_unit_types = ctx->type_list; - ctx->cbc->nb_decompose_unit_types = 0; + if (ctx->discard == AVDISCARD_NONE) { + // Don't actually decompose anything, we only want the unit data. + ctx->cbc->decompose_unit_types = ctx->type_list; + ctx->cbc->nb_decompose_unit_types = 0; + } if (bsf->par_in->extradata) { CodedBitstreamFragment *frag = &ctx->fragment; @@ -225,6 +232,37 @@ static const AVOption filter_units_options[] = { OFFSET(remove_types), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS }, + { "discard", "Remove the selected frames", + OFFSET(discard), AV_OPT_TYPE_INT, + { .i64 = AVDISCARD_NONE }, INT_MIN, INT_MAX, FLAGS, "discard"}, + { "none" , "discard none", + 0, AV_OPT_TYPE_CONST, + { .i64 = AVDISCARD_NONE }, INT_MIN, INT_MAX, FLAGS, "discard"}, + { "default" , "discard none, but can be changed after dynamically", + 0, AV_OPT_TYPE_CONST, + { .i64 = AVDISCARD_DEFAULT }, INT_MIN, INT_MAX, FLAGS, "discard"}, + { "nonref", "discard all non-reference frames", + 0, AV_OPT_TYPE_CONST, + { .i64 = AVDISCARD_NONREF }, INT_MIN, INT_MAX, FLAGS, "discard"}, + { "bidir", "discard all bidirectional frames", + 0, AV_OPT_TYPE_CONST, + { .i64 = AVDISCARD_BIDIR }, INT_MIN, INT_MAX, FLAGS, "discard"}, + { "nonintra", "discard all frames except I frames", + 0, AV_OPT_TYPE_CONST, + { .i64 = AVDISCARD_NONINTRA }, INT_MIN, INT_MAX, FLAGS, "discard"}, + { "nonkey", "discard all frames except keyframes", + 0, AV_OPT_TYPE_CONST, + { .i64 = AVDISCARD_NONKEY }, INT_MIN, INT_MAX, FLAGS, "discard"}, + { "all", "discard all frames", + 0, AV_OPT_TYPE_CONST, + { .i64 = AVDISCARD_ALL }, INT_MIN, INT_MAX, FLAGS, "discard"}, + + { "discard_flags", "flags to control the discard frame behavior", + OFFSET(discard_flags), AV_OPT_TYPE_FLAGS, + { .i64 = DISCARD_FLAG_NONE }, INT_MIN, INT_MAX, FLAGS, "discard_flags"}, + { "keep_non_vcl", "non-vcl units even if the picture has been dropped", + 0, AV_OPT_TYPE_CONST, + { .i64 = DISCARD_FLAG_KEEP_NON_VCL }, INT_MIN, INT_MAX, FLAGS, "discard_flags"}, { NULL } }; diff --git a/libavcodec/fitsdec.c b/libavcodec/fitsdec.c index b9c51e70c31..284e945ba5b 100644 --- a/libavcodec/fitsdec.c +++ b/libavcodec/fitsdec.c @@ -301,7 +301,7 @@ static int fits_decode_frame(AVCodecContext *avctx, AVFrame *p, } } - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; p->pict_type = AV_PICTURE_TYPE_I; *got_frame = 1; @@ -319,6 +319,7 @@ static const AVClass fits_decoder_class = { .item_name = av_default_item_name, .option = fits_options, .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_DECODER, }; const FFCodec ff_fits_decoder = { diff --git a/libavcodec/fitsenc.c b/libavcodec/fitsenc.c index ac910499e58..86ea11f0c08 100644 --- a/libavcodec/fitsenc.c +++ b/libavcodec/fitsenc.c @@ -104,6 +104,7 @@ static int fits_encode_frame(AVCodecContext *avctx, AVPacket *pkt, bytes_left = padded_data_size - data_size; memset(bytestream, 0, bytes_left); + pkt->flags |= AV_PKT_FLAG_KEY; *got_packet = 1; return 0; @@ -114,7 +115,7 @@ const FFCodec ff_fits_encoder = { CODEC_LONG_NAME("Flexible Image Transport System"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_FITS, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, FF_CODEC_ENCODE_CB(fits_encode_frame), .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_GBRAP16BE, AV_PIX_FMT_GBRP16BE, diff --git a/libavcodec/flacdec.c b/libavcodec/flacdec.c index cc778a8dff1..524a0469495 100644 --- a/libavcodec/flacdec.c +++ b/libavcodec/flacdec.c @@ -513,7 +513,7 @@ static int decode_subframe_lpc_33bps(FLACContext *s, int64_t *decoded, for (i = pred_order; i < s->blocksize; i++, decoded++) { int64_t sum = 0; for (j = 0; j < pred_order; j++) - sum += (int64_t)coeffs[j] * decoded[j]; + sum += (int64_t)coeffs[j] * (uint64_t)decoded[j]; decoded[j] = residual[i] + (sum >> qlevel); } diff --git a/libavcodec/flacenc.c b/libavcodec/flacenc.c index 8aacc93e283..a449b732353 100644 --- a/libavcodec/flacenc.c +++ b/libavcodec/flacenc.c @@ -1690,10 +1690,7 @@ static int flac_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, if (out_bytes < s->min_framesize) s->min_framesize = out_bytes; - avpkt->pts = frame->pts; - avpkt->duration = ff_samples_to_time_base(avctx, frame->nb_samples); - - s->next_pts = avpkt->pts + avpkt->duration; + s->next_pts = frame->pts + ff_samples_to_time_base(avctx, frame->nb_samples); av_shrink_packet(avpkt, out_bytes); @@ -1757,7 +1754,8 @@ const FFCodec ff_flac_encoder = { .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_FLAC, .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | - AV_CODEC_CAP_SMALL_LAST_FRAME, + AV_CODEC_CAP_SMALL_LAST_FRAME | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(FlacEncodeContext), .init = flac_encode_init, FF_CODEC_ENCODE_CB(flac_encode_frame), @@ -1766,5 +1764,5 @@ const FFCodec ff_flac_encoder = { AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_NONE }, .p.priv_class = &flac_encoder_class, - .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_EOF_FLUSH, }; diff --git a/libavcodec/flashsv2enc.c b/libavcodec/flashsv2enc.c index 668ca6a85ff..75b48eb1fd3 100644 --- a/libavcodec/flashsv2enc.c +++ b/libavcodec/flashsv2enc.c @@ -105,7 +105,7 @@ typedef struct FlashSV2Context { int rows, cols; - int last_key_frame; + int64_t last_key_frame; int image_width, image_height; int block_width, block_height; @@ -787,7 +787,7 @@ static int optimum_use15_7(FlashSV2Context * s) { #ifndef FLASHSV2_DUMB double ideal = ((double)(s->avctx->bit_rate * s->avctx->time_base.den * s->avctx->ticks_per_frame)) / - ((double) s->avctx->time_base.num) * s->avctx->frame_number; + ((double) s->avctx->time_base.num) * s->avctx->frame_num; if (ideal + use15_7_threshold < s->total_bits) { return 1; } else { @@ -861,20 +861,20 @@ static int flashsv2_encode_frame(AVCodecContext *avctx, AVPacket *pkt, return res; /* First frame needs to be a keyframe */ - if (avctx->frame_number == 0) + if (avctx->frame_num == 0) keyframe = 1; /* Check the placement of keyframes */ if (avctx->gop_size > 0) { - if (avctx->frame_number >= s->last_key_frame + avctx->gop_size) + if (avctx->frame_num >= s->last_key_frame + avctx->gop_size) keyframe = 1; } if (!keyframe - && avctx->frame_number > s->last_key_frame + avctx->keyint_min) { + && avctx->frame_num > s->last_key_frame + avctx->keyint_min) { recommend_keyframe(s, &keyframe); if (keyframe) - av_log(avctx, AV_LOG_DEBUG, "Recommending key frame at frame %d\n", avctx->frame_number); + av_log(avctx, AV_LOG_DEBUG, "Recommending key frame at frame %"PRId64"\n", avctx->frame_num); } if (keyframe) { @@ -890,9 +890,9 @@ static int flashsv2_encode_frame(AVCodecContext *avctx, AVPacket *pkt, if (keyframe) { new_key_frame(s); - s->last_key_frame = avctx->frame_number; + s->last_key_frame = avctx->frame_num; pkt->flags |= AV_PKT_FLAG_KEY; - av_log(avctx, AV_LOG_DEBUG, "Inserting key frame at frame %d\n", avctx->frame_number); + av_log(avctx, AV_LOG_DEBUG, "Inserting key frame at frame %"PRId64"\n", avctx->frame_num); } pkt->size = res; @@ -915,7 +915,7 @@ const FFCodec ff_flashsv2_encoder = { CODEC_LONG_NAME("Flash Screen Video Version 2"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_FLASHSV2, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(FlashSV2Context), .init = flashsv2_encode_init, FF_CODEC_ENCODE_CB(flashsv2_encode_frame), diff --git a/libavcodec/flashsvenc.c b/libavcodec/flashsvenc.c index 3a35876d9c5..5cf0602f5da 100644 --- a/libavcodec/flashsvenc.c +++ b/libavcodec/flashsvenc.c @@ -65,7 +65,7 @@ typedef struct FlashSVContext { AVBufferRef *prev_frame_buf; int image_width, image_height; unsigned packet_size; - int last_key_frame; + int64_t last_key_frame; uint8_t tmpblock[3 * 256 * 256]; } FlashSVContext; @@ -215,7 +215,7 @@ static int flashsv_encode_frame(AVCodecContext *avctx, AVPacket *pkt, /* Check the placement of keyframes */ if (avctx->gop_size > 0 && - avctx->frame_number >= s->last_key_frame + avctx->gop_size) { + avctx->frame_num >= s->last_key_frame + avctx->gop_size) { I_frame = 1; } @@ -229,8 +229,8 @@ static int flashsv_encode_frame(AVCodecContext *avctx, AVPacket *pkt, //mark the frame type so the muxer can mux it correctly if (I_frame) { - s->last_key_frame = avctx->frame_number; - ff_dlog(avctx, "Inserting keyframe at frame %d\n", avctx->frame_number); + s->last_key_frame = avctx->frame_num; + ff_dlog(avctx, "Inserting keyframe at frame %"PRId64"\n", avctx->frame_num); } if (I_frame) @@ -251,7 +251,7 @@ const FFCodec ff_flashsv_encoder = { CODEC_LONG_NAME("Flash Screen Video"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_FLASHSV, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(FlashSVContext), .init = flashsv_encode_init, FF_CODEC_ENCODE_CB(flashsv_encode_frame), diff --git a/libavcodec/flicvideo.c b/libavcodec/flicvideo.c index 228f6527752..e4b334e10fd 100644 --- a/libavcodec/flicvideo.c +++ b/libavcodec/flicvideo.c @@ -473,7 +473,11 @@ static int flic_decode_frame_8BPP(AVCodecContext *avctx, /* make the palette available on the way out */ memcpy(s->frame->data[1], s->palette, AVPALETTE_SIZE); if (s->new_palette) { +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS s->frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif s->new_palette = 0; } diff --git a/libavcodec/flvenc.c b/libavcodec/flvenc.c index b49ca2e0d5b..6a96cb0f2f9 100644 --- a/libavcodec/flvenc.c +++ b/libavcodec/flvenc.c @@ -25,7 +25,7 @@ #include "mpegvideodata.h" #include "mpegvideoenc.h" -void ff_flv_encode_picture_header(MpegEncContext *s, int picture_number) +void ff_flv_encode_picture_header(MpegEncContext *s) { int format; @@ -105,4 +105,5 @@ const FFCodec ff_flv_encoder = { .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE}, + .p.capabilities = AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, }; diff --git a/libavcodec/flvenc.h b/libavcodec/flvenc.h index aaa0fcffaba..1ecbb46b17a 100644 --- a/libavcodec/flvenc.h +++ b/libavcodec/flvenc.h @@ -24,7 +24,7 @@ #include "mpegvideo.h" #include "put_bits.h" -void ff_flv_encode_picture_header(MpegEncContext *s, int picture_number); +void ff_flv_encode_picture_header(MpegEncContext *s); void ff_flv2_encode_ac_esc(PutBitContext *pb, int slevel, int level, int run, int last); diff --git a/libavcodec/fmvc.c b/libavcodec/fmvc.c index 3ee915cc4cb..5e26a541ca7 100644 --- a/libavcodec/fmvc.c +++ b/libavcodec/fmvc.c @@ -433,7 +433,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) return ret; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; src = s->buffer; @@ -519,7 +519,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) return ret; - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_P; ssrc = s->buffer; diff --git a/libavcodec/frame_thread_encoder.c b/libavcodec/frame_thread_encoder.c index 35775ae823e..62d9580ad4c 100644 --- a/libavcodec/frame_thread_encoder.c +++ b/libavcodec/frame_thread_encoder.c @@ -48,9 +48,6 @@ typedef struct{ typedef struct{ AVCodecContext *parent_avctx; -#if FF_API_THREAD_SAFE_CALLBACKS - pthread_mutex_t buffer_mutex; -#endif pthread_mutex_t task_fifo_mutex; /* Used to guard (next_)task_index */ pthread_cond_t task_fifo_cond; @@ -70,15 +67,9 @@ typedef struct{ } ThreadContext; #define OFF(member) offsetof(ThreadContext, member) -#if FF_API_THREAD_SAFE_CALLBACKS -DEFINE_OFFSET_ARRAY(ThreadContext, thread_ctx, pthread_init_cnt, - (OFF(buffer_mutex), OFF(task_fifo_mutex), OFF(finished_task_mutex)), - (OFF(task_fifo_cond), OFF(finished_task_cond))); -#else DEFINE_OFFSET_ARRAY(ThreadContext, thread_ctx, pthread_init_cnt, (OFF(task_fifo_mutex), OFF(finished_task_mutex)), (OFF(task_fifo_cond), OFF(finished_task_cond))); -#endif #undef OFF static void * attribute_align_arg worker(void *v){ @@ -112,11 +103,6 @@ static void * attribute_align_arg worker(void *v){ pkt = task->outdata; ret = ff_encode_encode_cb(avctx, pkt, frame, &task->got_packet); -#if FF_API_THREAD_SAFE_CALLBACKS - pthread_mutex_lock(&c->buffer_mutex); - av_frame_unref(frame); - pthread_mutex_unlock(&c->buffer_mutex); -#endif pthread_mutex_lock(&c->finished_task_mutex); task->return_code = ret; task->finished = 1; @@ -124,13 +110,7 @@ static void * attribute_align_arg worker(void *v){ pthread_mutex_unlock(&c->finished_task_mutex); } end: -#if FF_API_THREAD_SAFE_CALLBACKS - pthread_mutex_lock(&c->buffer_mutex); -#endif avcodec_close(avctx); -#if FF_API_THREAD_SAFE_CALLBACKS - pthread_mutex_unlock(&c->buffer_mutex); -#endif av_freep(&avctx); return NULL; } diff --git a/libavcodec/fraps.c b/libavcodec/fraps.c index 4c4c46b6027..e7a23f20fe3 100644 --- a/libavcodec/fraps.c +++ b/libavcodec/fraps.c @@ -216,7 +216,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *f, } f->pict_type = AV_PICTURE_TYPE_I; - f->key_frame = 1; + f->flags |= AV_FRAME_FLAG_KEY; avctx->pix_fmt = version & 1 ? is_pal ? AV_PIX_FMT_PAL8 : AV_PIX_FMT_BGR24 : AV_PIX_FMT_YUVJ420P; avctx->color_range = version & 1 ? AVCOL_RANGE_UNSPECIFIED diff --git a/libavcodec/frwu.c b/libavcodec/frwu.c index cf183f84107..70bc136765b 100644 --- a/libavcodec/frwu.c +++ b/libavcodec/frwu.c @@ -63,7 +63,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *pic, return ret; pic->pict_type = AV_PICTURE_TYPE_I; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; for (field = 0; field < 2; field++) { int i; diff --git a/libavcodec/ftr.c b/libavcodec/ftr.c index 277b9be5b88..7cf1b1586fb 100644 --- a/libavcodec/ftr.c +++ b/libavcodec/ftr.c @@ -37,7 +37,7 @@ static av_cold int ftr_init(AVCodecContext *avctx) if (avctx->ch_layout.nb_channels > 64 || avctx->ch_layout.nb_channels <= 0) - return AVERROR(ENOTSUP); + return AVERROR(EINVAL); s->packet = av_packet_alloc(); if (!s->packet) @@ -203,6 +203,10 @@ const FFCodec ff_ftr_decoder = { .close = ftr_close, .flush = ftr_flush, .priv_data_size = sizeof(FTRContext), - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, }; diff --git a/libavcodec/g2meet.c b/libavcodec/g2meet.c index 761fd22fc37..2dfa73df74e 100644 --- a/libavcodec/g2meet.c +++ b/libavcodec/g2meet.c @@ -145,7 +145,8 @@ typedef struct G2MContext { int got_header; uint8_t *framebuf; - int framebuf_stride, old_width, old_height; + int framebuf_stride; + unsigned int framebuf_allocated; uint8_t *synth_tile, *jpeg_tile, *epic_buf, *epic_buf_base; int tile_stride, epic_buf_stride, old_tile_w, old_tile_h; @@ -931,8 +932,8 @@ static int epic_jb_decode_tile(G2MContext *c, int tile_x, int tile_y, if (ret) { av_log(avctx, AV_LOG_ERROR, - "ePIC: tile decoding failed, frame=%d, tile_x=%d, tile_y=%d\n", - avctx->frame_number, tile_x, tile_y); + "ePIC: tile decoding failed, frame=%"PRId64", tile_x=%d, tile_y=%d\n", + avctx->frame_num, tile_x, tile_y); return AVERROR_INVALIDDATA; } @@ -1160,14 +1161,13 @@ static int g2m_init_buffers(G2MContext *c) { int aligned_height; - if (!c->framebuf || c->old_width < c->width || c->old_height < c->height) { - c->framebuf_stride = FFALIGN(c->width + 15, 16) * 3; - aligned_height = c->height + 15; - av_free(c->framebuf); - c->framebuf = av_calloc(c->framebuf_stride, aligned_height); - if (!c->framebuf) - return AVERROR(ENOMEM); - } + c->framebuf_stride = FFALIGN(c->width + 15, 16) * 3; + aligned_height = c->height + 15; + + av_fast_mallocz(&c->framebuf, &c->framebuf_allocated, c->framebuf_stride * aligned_height); + if (!c->framebuf) + return AVERROR(ENOMEM); + if (!c->synth_tile || !c->jpeg_tile || (c->compression == 2 && !c->epic_buf_base) || c->old_tile_w < c->tile_width || @@ -1560,7 +1560,10 @@ static int g2m_decode_frame(AVCodecContext *avctx, AVFrame *pic, if ((ret = ff_get_buffer(avctx, pic, 0)) < 0) return ret; - pic->key_frame = got_header; + if (got_header) + pic->flags |= AV_FRAME_FLAG_KEY; + else + pic->flags &= ~AV_FRAME_FLAG_KEY; pic->pict_type = got_header ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; for (i = 0; i < avctx->height; i++) @@ -1617,6 +1620,7 @@ static av_cold int g2m_decode_end(AVCodecContext *avctx) av_freep(&c->jpeg_tile); av_freep(&c->cursor); av_freep(&c->framebuf); + c->framebuf_allocated = 0; return 0; } diff --git a/libavcodec/g722enc.c b/libavcodec/g722enc.c index bc08211b1df..47811cee4d6 100644 --- a/libavcodec/g722enc.c +++ b/libavcodec/g722enc.c @@ -375,7 +375,8 @@ const FFCodec ff_adpcm_g722_encoder = { CODEC_LONG_NAME("G.722 ADPCM"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_ADPCM_G722, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SMALL_LAST_FRAME, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SMALL_LAST_FRAME | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(G722Context), .init = g722_encode_init, .close = g722_encode_close, diff --git a/libavcodec/g723_1dec.c b/libavcodec/g723_1dec.c index 55e20de5b5f..5fe4a21d9b2 100644 --- a/libavcodec/g723_1dec.c +++ b/libavcodec/g723_1dec.c @@ -1118,6 +1118,10 @@ const FFCodec ff_g723_1_decoder = { .priv_data_size = sizeof(G723_1_Context), .init = g723_1_decode_init, FF_CODEC_DECODE_CB(g723_1_decode_frame), - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1, .p.priv_class = &g723_1dec_class, }; diff --git a/libavcodec/g723_1enc.c b/libavcodec/g723_1enc.c index 84660671858..be80153130e 100644 --- a/libavcodec/g723_1enc.c +++ b/libavcodec/g723_1enc.c @@ -1241,7 +1241,7 @@ const FFCodec ff_g723_1_encoder = { CODEC_LONG_NAME("G.723.1"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_G723_1, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(G723_1_Context), .init = g723_1_encode_init, FF_CODEC_ENCODE_CB(g723_1_encode_frame), diff --git a/libavcodec/g726.c b/libavcodec/g726.c index 7bbb7f900c8..6c5638760d4 100644 --- a/libavcodec/g726.c +++ b/libavcodec/g726.c @@ -405,7 +405,8 @@ const FFCodec ff_adpcm_g726_encoder = { CODEC_LONG_NAME("G.726 ADPCM"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_ADPCM_G726, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SMALL_LAST_FRAME, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SMALL_LAST_FRAME | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(G726Context), .init = g726_encode_init, FF_CODEC_ENCODE_CB(g726_encode_frame), @@ -422,7 +423,8 @@ const FFCodec ff_adpcm_g726le_encoder = { CODEC_LONG_NAME("G.726 little endian ADPCM (\"right-justified\")"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_ADPCM_G726LE, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SMALL_LAST_FRAME, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SMALL_LAST_FRAME | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(G726Context), .init = g726_encode_init, FF_CODEC_ENCODE_CB(g726_encode_frame), diff --git a/libavcodec/g729dec.c b/libavcodec/g729dec.c index f783812cc7c..33e1fb9c29d 100644 --- a/libavcodec/g729dec.c +++ b/libavcodec/g729dec.c @@ -760,7 +760,11 @@ const FFCodec ff_g729_decoder = { .init = decoder_init, FF_CODEC_DECODE_CB(decode_frame), .close = decode_close, - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1, }; const FFCodec ff_acelp_kelvin_decoder = { @@ -772,5 +776,9 @@ const FFCodec ff_acelp_kelvin_decoder = { .init = decoder_init, FF_CODEC_DECODE_CB(decode_frame), .close = decode_close, - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1, }; diff --git a/libavcodec/g729postfilter.c b/libavcodec/g729postfilter.c index f3cacbac05b..26e937f0baf 100644 --- a/libavcodec/g729postfilter.c +++ b/libavcodec/g729postfilter.c @@ -353,7 +353,7 @@ static int16_t long_term_filter(AudioDSPContext *adsp, int pitch_delay_int, if (tmp > 0) L_temp0 >>= tmp; else - L_temp1 >>= -tmp; + L_temp1 >>= FFMIN(-tmp, 31); /* Check if longer filter increases the values of R'(k). */ if (L_temp1 > L_temp0) { diff --git a/libavcodec/gemdec.c b/libavcodec/gemdec.c index c8fd8dcdcd2..9e2a50c7661 100644 --- a/libavcodec/gemdec.c +++ b/libavcodec/gemdec.c @@ -180,8 +180,12 @@ static int gem_decode_frame(AVCodecContext *avctx, AVFrame *p, return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS p->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif palette = (uint32_t *)p->data[1]; if (tag == AV_RB32("STTT")) { diff --git a/libavcodec/get_bits.h b/libavcodec/get_bits.h index 1aa327a4acb..65dc080ddb3 100644 --- a/libavcodec/get_bits.h +++ b/libavcodec/get_bits.h @@ -85,7 +85,7 @@ typedef BitstreamContext GetBitContext; #define get_bits1 bits_read_bit #define get_bits64 bits_read_64 #define get_xbits bits_read_xbits -#define get_sbits bits_read_signed +#define get_sbits bits_read_signed_nz #define get_sbits_long bits_read_signed #define show_bits bits_peek #define show_bits_long bits_peek diff --git a/libavcodec/gif.c b/libavcodec/gif.c index 7e717d8220d..131af6198ad 100644 --- a/libavcodec/gif.c +++ b/libavcodec/gif.c @@ -318,7 +318,7 @@ static int gif_image_write_image(AVCodecContext *avctx, disposal = GCE_DISPOSAL_INPLACE; } - if (s->image || !avctx->frame_number) { /* GIF header */ + if (s->image || !avctx->frame_num) { /* GIF header */ const uint32_t *global_palette = palette ? palette : s->palette; const AVRational sar = avctx->sample_aspect_ratio; int64_t aspect = 0; @@ -510,7 +510,7 @@ static int gif_encode_frame(AVCodecContext *avctx, AVPacket *pkt, } pkt->size = outbuf_ptr - pkt->data; - if (s->image || !avctx->frame_number) + if (s->image || !avctx->frame_num) pkt->flags |= AV_PKT_FLAG_KEY; *got_packet = 1; @@ -553,7 +553,7 @@ const FFCodec ff_gif_encoder = { CODEC_LONG_NAME("GIF (Graphics Interchange Format)"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_GIF, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(GIFContext), .init = gif_encode_init, FF_CODEC_ENCODE_CB(gif_encode_frame), diff --git a/libavcodec/gif_parser.c b/libavcodec/gif_parser.c index 4c18c00d7d8..aa07585f4cd 100644 --- a/libavcodec/gif_parser.c +++ b/libavcodec/gif_parser.c @@ -24,9 +24,6 @@ * GIF parser */ -#include "libavutil/bswap.h" -#include "libavutil/common.h" - #include "gif.h" #include "parser.h" @@ -50,11 +47,13 @@ typedef struct GIFParseContext { int block_size; int etype; int delay; + int keyframe; } GIFParseContext; static int gif_find_frame_end(GIFParseContext *g, const uint8_t *buf, int buf_size, void *logctx) { + ParseContext *pc = &g->pc; int index, next = END_NOT_FOUND; for (index = 0; index < buf_size; index++) { @@ -63,9 +62,10 @@ static int gif_find_frame_end(GIFParseContext *g, const uint8_t *buf, !memcmp(buf + index, gif89a_sig, 6)) { g->state = GIF_HEADER; g->found_sig++; + g->keyframe = 1; } else if (buf[index] == GIF_EXTENSION_INTRODUCER) { g->state = GIF_EXTENSION; - g->found_start = 1; + g->found_start = pc->frame_start_found = 1; } else if (buf[index] == GIF_IMAGE_SEPARATOR) { g->state = GIF_IMAGE; } else if (buf[index] == GIF_TRAILER) { @@ -93,7 +93,7 @@ static int gif_find_frame_end(GIFParseContext *g, const uint8_t *buf, } else if (g->state == GIF_EXTENSION) { if (g->found_start && g->found_end && g->found_sig) { next = index; - g->found_start = 0; + g->found_start = pc->frame_start_found = 0; g->found_end = 0; g->index = 0; g->gct_flag = 0; @@ -140,7 +140,7 @@ static int gif_find_frame_end(GIFParseContext *g, const uint8_t *buf, } g->index++; } else if (g->state == GIF_IMAGE) { - if (g->index == 8) { + if (g->index == 9) { g->gct_flag = !!(buf[index] & 0x80); g->gct_size = 3 * (1 << ((buf[index] & 0x07) + 1)); } @@ -165,14 +165,24 @@ static int gif_parse(AVCodecParserContext *s, AVCodecContext *avctx, GIFParseContext *g = s->priv_data; int next; - next = gif_find_frame_end(g, buf, buf_size, avctx); - if (ff_combine_frame(&g->pc, next, &buf, &buf_size) < 0) { - *poutbuf = NULL; - *poutbuf_size = 0; - return buf_size; + *poutbuf_size = 0; + *poutbuf = NULL; + + if (s->flags & PARSER_FLAG_COMPLETE_FRAMES) { + next = buf_size; + } else { + next = gif_find_frame_end(g, buf, buf_size, avctx); + if (ff_combine_frame(&g->pc, next, &buf, &buf_size) < 0) { + *poutbuf = NULL; + *poutbuf_size = 0; + return buf_size; + } } - s->duration = g->delay; + s->duration = g->delay ? g->delay : 10; + s->key_frame = g->keyframe; + s->pict_type = g->keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; + g->keyframe = 0; *poutbuf = buf; *poutbuf_size = buf_size; diff --git a/libavcodec/gifdec.c b/libavcodec/gifdec.c index f47390c3bd9..86aaa8881ce 100644 --- a/libavcodec/gifdec.c +++ b/libavcodec/gifdec.c @@ -472,10 +472,6 @@ static int gif_decode_frame(AVCodecContext *avctx, AVFrame *rframe, bytestream2_init(&s->gb, avpkt->data, avpkt->size); - s->frame->pts = avpkt->pts; - s->frame->pkt_dts = avpkt->dts; - s->frame->duration = avpkt->duration; - if (avpkt->size >= 6) { s->keyframe = memcmp(avpkt->data, gif87a_sig, 6) == 0 || memcmp(avpkt->data, gif89a_sig, 6) == 0; @@ -493,35 +489,29 @@ static int gif_decode_frame(AVCodecContext *avctx, AVFrame *rframe, return ret; av_frame_unref(s->frame); - if ((ret = ff_get_buffer(avctx, s->frame, 0)) < 0) - return ret; - av_fast_malloc(&s->idx_line, &s->idx_line_size, s->screen_width); if (!s->idx_line) return AVERROR(ENOMEM); - - s->frame->pict_type = AV_PICTURE_TYPE_I; - s->frame->key_frame = 1; - s->keyframe_ok = 1; - } else { - if (!s->keyframe_ok) { - av_log(avctx, AV_LOG_ERROR, "cannot decode frame without keyframe\n"); - return AVERROR_INVALIDDATA; - } - - if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) - return ret; - - s->frame->pict_type = AV_PICTURE_TYPE_P; - s->frame->key_frame = 0; + } else if (!s->keyframe_ok) { + av_log(avctx, AV_LOG_ERROR, "cannot decode frame without keyframe\n"); + return AVERROR_INVALIDDATA; } + ret = ff_reget_buffer(avctx, s->frame, 0); + if (ret < 0) + return ret; + ret = gif_parse_next_image(s, s->frame); if (ret < 0) return ret; if ((ret = av_frame_ref(rframe, s->frame)) < 0) return ret; + + rframe->pict_type = s->keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; + rframe->flags = AV_FRAME_FLAG_KEY * s->keyframe; + s->keyframe_ok |= !!s->keyframe; + *got_frame = 1; return bytestream2_tell(&s->gb); diff --git a/libavcodec/h261dec.c b/libavcodec/h261dec.c index 57f7e8bf357..620b7eef839 100644 --- a/libavcodec/h261dec.c +++ b/libavcodec/h261dec.c @@ -610,7 +610,7 @@ static int h261_decode_frame(AVCodecContext *avctx, AVFrame *pict, MpegEncContext *s = &h->s; int ret; - ff_dlog(avctx, "*****frame %d size=%d\n", avctx->frame_number, buf_size); + ff_dlog(avctx, "*****frame %"PRId64" size=%d\n", avctx->frame_num, buf_size); ff_dlog(avctx, "bytes=%x %x %x %x\n", buf[0], buf[1], buf[2], buf[3]); h->gob_start_code_skipped = 0; @@ -643,7 +643,10 @@ static int h261_decode_frame(AVCodecContext *avctx, AVFrame *pict, // for skipping the frame s->current_picture.f->pict_type = s->pict_type; - s->current_picture.f->key_frame = s->pict_type == AV_PICTURE_TYPE_I; + if (s->pict_type == AV_PICTURE_TYPE_I) + s->current_picture.f->flags |= AV_FRAME_FLAG_KEY; + else + s->current_picture.f->flags &= ~AV_FRAME_FLAG_KEY; if ((avctx->skip_frame >= AVDISCARD_NONREF && s->pict_type == AV_PICTURE_TYPE_B) || (avctx->skip_frame >= AVDISCARD_NONKEY && s->pict_type != AV_PICTURE_TYPE_I) || diff --git a/libavcodec/h261enc.c b/libavcodec/h261enc.c index e8ea357cbbf..438ebb63d91 100644 --- a/libavcodec/h261enc.c +++ b/libavcodec/h261enc.c @@ -52,7 +52,7 @@ typedef struct H261EncContext { } format; } H261EncContext; -void ff_h261_encode_picture_header(MpegEncContext *s, int picture_number) +void ff_h261_encode_picture_header(MpegEncContext *s) { H261EncContext *const h = (H261EncContext *)s; int temp_ref; @@ -413,4 +413,5 @@ const FFCodec ff_h261_encoder = { .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }, + .p.capabilities = AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, }; diff --git a/libavcodec/h261enc.h b/libavcodec/h261enc.h index 102e994494d..d8fdcad7aad 100644 --- a/libavcodec/h261enc.h +++ b/libavcodec/h261enc.h @@ -33,7 +33,7 @@ void ff_h261_reorder_mb_index(MpegEncContext *s); void ff_h261_encode_mb(MpegEncContext *s, int16_t block[6][64], int motion_x, int motion_y); -void ff_h261_encode_picture_header(MpegEncContext *s, int picture_number); +void ff_h261_encode_picture_header(MpegEncContext *s); int ff_h261_encode_init(MpegEncContext *s); #endif diff --git a/libavcodec/h263_parser.c b/libavcodec/h263_parser.c index 7a742caa80b..f70a7911777 100644 --- a/libavcodec/h263_parser.c +++ b/libavcodec/h263_parser.c @@ -25,16 +25,9 @@ */ #include "parser.h" -#if FF_API_FLAG_TRUNCATED -/* Nuke this header when removing FF_API_FLAG_TRUNCATED */ -#include "h263_parser.h" - -int ff_h263_find_frame_end(ParseContext *pc, const uint8_t *buf, int buf_size){ -#else static int h263_find_frame_end(ParseContext *pc, const uint8_t *buf, int buf_size) { -#endif int vop_found, i; uint32_t state; @@ -80,11 +73,7 @@ static int h263_parse(AVCodecParserContext *s, if (s->flags & PARSER_FLAG_COMPLETE_FRAMES) { next = buf_size; } else { -#if FF_API_FLAG_TRUNCATED - next= ff_h263_find_frame_end(pc, buf, buf_size); -#else next = h263_find_frame_end(pc, buf, buf_size); -#endif if (ff_combine_frame(pc, next, &buf, &buf_size) < 0) { *poutbuf = NULL; diff --git a/libavcodec/h263dec.c b/libavcodec/h263dec.c index 0a2d7487a83..52e51dd4893 100644 --- a/libavcodec/h263dec.c +++ b/libavcodec/h263dec.c @@ -36,17 +36,12 @@ #include "flvdec.h" #include "h263.h" #include "h263dec.h" -#if FF_API_FLAG_TRUNCATED -#include "h263_parser.h" -#endif +#include "hwaccel_internal.h" #include "hwconfig.h" #include "mpeg_er.h" #include "mpeg4video.h" #include "mpeg4videodec.h" #include "mpeg4videodefs.h" -#if FF_API_FLAG_TRUNCATED -#include "mpeg4video_parser.h" -#endif #include "mpegutils.h" #include "mpegvideo.h" #include "mpegvideodec.h" @@ -163,14 +158,6 @@ static int get_consumed_bytes(MpegEncContext *s, int buf_size) /* We would have to scan through the whole buf to handle the weird * reordering ... */ return buf_size; -#if FF_API_FLAG_TRUNCATED - } else if (s->avctx->flags & AV_CODEC_FLAG_TRUNCATED) { - pos -= s->parse_context.last_index; - // padding is not really read so this might be -1 - if (pos < 0) - pos = 0; - return pos; -#endif } else { // avoid infinite loops (maybe not needed...) if (pos == 0) @@ -204,7 +191,7 @@ static int decode_slice(MpegEncContext *s) if (s->avctx->hwaccel) { const uint8_t *start = s->gb.buffer + get_bits_count(&s->gb) / 8; - ret = s->avctx->hwaccel->decode_slice(s->avctx, start, s->gb.buffer_end - start); + ret = FF_HW_CALL(s->avctx, decode_slice, start, s->gb.buffer_end - start); // ensure we exit decode loop s->mb_y = s->mb_height; return ret; @@ -295,7 +282,7 @@ static int decode_slice(MpegEncContext *s) ff_er_add_slice(&s->er, s->resync_mb_x, s->resync_mb_y, s->mb_x, s->mb_y, ER_MB_ERROR & part_mask); - if (s->avctx->err_recognition & AV_EF_IGNORE_ERR) + if ((s->avctx->err_recognition & AV_EF_IGNORE_ERR) && get_bits_left(&s->gb) > 0) continue; return AVERROR_INVALIDDATA; } @@ -448,28 +435,6 @@ int ff_h263_decode_frame(AVCodecContext *avctx, AVFrame *pict, return 0; } -#if FF_API_FLAG_TRUNCATED - if (s->avctx->flags & AV_CODEC_FLAG_TRUNCATED) { - int next; - - if (CONFIG_MPEG4_DECODER && s->codec_id == AV_CODEC_ID_MPEG4) { - next = ff_mpeg4_find_frame_end(&s->parse_context, buf, buf_size); - } else if (CONFIG_H263_DECODER && s->codec_id == AV_CODEC_ID_H263) { - next = ff_h263_find_frame_end(&s->parse_context, buf, buf_size); - } else if (CONFIG_H263P_DECODER && s->codec_id == AV_CODEC_ID_H263P) { - next = ff_h263_find_frame_end(&s->parse_context, buf, buf_size); - } else { - av_log(s->avctx, AV_LOG_ERROR, - "this codec does not support truncated bitstreams\n"); - return AVERROR(ENOSYS); - } - - if (ff_combine_frame(&s->parse_context, next, (const uint8_t **)&buf, - &buf_size) < 0) - return buf_size; - } -#endif - retry: if (s->divx_packed && s->bitstream_buffer_size) { int i; @@ -604,8 +569,8 @@ int ff_h263_decode_frame(AVCodecContext *avctx, AVFrame *pict, ff_thread_finish_setup(avctx); if (avctx->hwaccel) { - ret = avctx->hwaccel->start_frame(avctx, s->gb.buffer, - s->gb.buffer_end - s->gb.buffer); + ret = FF_HW_CALL(avctx, start_frame, + s->gb.buffer, s->gb.buffer_end - s->gb.buffer); if (ret < 0 ) return ret; } @@ -660,7 +625,7 @@ int ff_h263_decode_frame(AVCodecContext *avctx, AVFrame *pict, ff_er_frame_end(&s->er); if (avctx->hwaccel) { - ret = avctx->hwaccel->end_frame(avctx); + ret = FF_HW_SIMPLE_CALL(avctx, end_frame); if (ret < 0) return ret; } @@ -749,9 +714,6 @@ const FFCodec ff_h263_decoder = { .close = ff_h263_decode_end, FF_CODEC_DECODE_CB(ff_h263_decode_frame), .p.capabilities = AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_DR1 | -#if FF_API_FLAG_TRUNCATED - AV_CODEC_CAP_TRUNCATED | -#endif AV_CODEC_CAP_DELAY, .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM, .flush = ff_mpeg_flush, @@ -770,9 +732,6 @@ const FFCodec ff_h263p_decoder = { .close = ff_h263_decode_end, FF_CODEC_DECODE_CB(ff_h263_decode_frame), .p.capabilities = AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_DR1 | -#if FF_API_FLAG_TRUNCATED - AV_CODEC_CAP_TRUNCATED | -#endif AV_CODEC_CAP_DELAY, .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM, .flush = ff_mpeg_flush, diff --git a/libavcodec/h263enc.h b/libavcodec/h263enc.h index fff85a18f23..e45475686ec 100644 --- a/libavcodec/h263enc.h +++ b/libavcodec/h263enc.h @@ -25,7 +25,7 @@ #include "mpegvideoenc.h" void ff_h263_encode_init(MpegEncContext *s); -void ff_h263_encode_picture_header(MpegEncContext *s, int picture_number); +void ff_h263_encode_picture_header(MpegEncContext *s); void ff_h263_encode_gob_header(MpegEncContext * s, int mb_line); void ff_h263_encode_mb(MpegEncContext *s, int16_t block[6][64], diff --git a/libavcodec/h2645_parse.c b/libavcodec/h2645_parse.c index 90944177c72..28db465059e 100644 --- a/libavcodec/h2645_parse.c +++ b/libavcodec/h2645_parse.c @@ -30,6 +30,7 @@ #include "hevc.h" #include "h264.h" #include "h2645_parse.h" +#include "vvc.h" int ff_h2645_extract_rbsp(const uint8_t *src, int length, H2645RBSP *rbsp, H2645NAL *nal, int small_padding) @@ -145,6 +146,47 @@ int ff_h2645_extract_rbsp(const uint8_t *src, int length, return si; } +static const char *const vvc_nal_type_name[32] = { + "TRAIL_NUT", // VVC_TRAIL_NUT + "STSA_NUT", // VVC_STSA_NUT + "RADL_NUT", // VVC_RADL_NUT + "RASL_NUT", // VVC_RASL_NUT + "RSV_VCL4", // VVC_RSV_VCL_4 + "RSV_VCL5", // VVC_RSV_VCL_5 + "RSV_VCL6", // VVC_RSV_VCL_6 + "IDR_W_RADL", // VVC_IDR_W_RADL + "IDR_N_LP", // VVC_IDR_N_LP + "CRA_NUT", // VVC_CRA_NUT + "GDR_NUT", // VVC_GDR_NUT + "RSV_IRAP_11", // VVC_RSV_IRAP_11 + "OPI_NUT", // VVC_OPI_NUT + "DCI_NUT", // VVC_DCI_NUT + "VPS_NUT", // VVC_VPS_NUT + "SPS_NUT", // VVC_SPS_NUT + "PPS_NUT", // VVC_PPS_NUT + "APS_PREFIX", // VVC_PREFIX_APS_NUT + "APS_SUFFIX", // VVC_SUFFIX_APS_NUT + "PH_NUT", // VVC_PH_NUT + "AUD_NUT", // VVC_AUD_NUT + "EOS_NUT", // VVC_EOS_NUT + "EOB_NUT", // VVC_EOB_NUT + "SEI_PREFIX", // VVC_PREFIX_SEI_NUT + "SEI_SUFFIX", // VVC_SUFFIX_SEI_NUT + "FD_NUT", // VVC_FD_NUT + "RSV_NVCL26", // VVC_RSV_NVCL_26 + "RSV_NVCL27", // VVC_RSV_NVCL_27 + "UNSPEC28", // VVC_UNSPEC_28 + "UNSPEC29", // VVC_UNSPEC_29 + "UNSPEC30", // VVC_UNSPEC_30 + "UNSPEC31", // VVC_UNSPEC_31 +}; + +static const char *vvc_nal_unit_name(int nal_type) +{ + av_assert0(nal_type >= 0 && nal_type < 32); + return vvc_nal_type_name[nal_type]; +} + static const char *const hevc_nal_type_name[64] = { "TRAIL_N", // HEVC_NAL_TRAIL_N "TRAIL_R", // HEVC_NAL_TRAIL_R @@ -293,6 +335,31 @@ static int get_bit_length(H2645NAL *nal, int min_size, int skip_trailing_zeros) * @return AVERROR_INVALIDDATA if the packet is not a valid NAL unit, * 0 otherwise */ +static int vvc_parse_nal_header(H2645NAL *nal, void *logctx) +{ + GetBitContext *gb = &nal->gb; + + if (get_bits1(gb) != 0) //forbidden_zero_bit + return AVERROR_INVALIDDATA; + + skip_bits1(gb); //nuh_reserved_zero_bit + + nal->nuh_layer_id = get_bits(gb, 6); + nal->type = get_bits(gb, 5); + nal->temporal_id = get_bits(gb, 3) - 1; + if (nal->temporal_id < 0) + return AVERROR_INVALIDDATA; + + if ((nal->type >= VVC_IDR_W_RADL && nal->type <= VVC_RSV_IRAP_11) && nal->temporal_id) + return AVERROR_INVALIDDATA; + + av_log(logctx, AV_LOG_DEBUG, + "nal_unit_type: %d(%s), nuh_layer_id: %d, temporal_id: %d\n", + nal->type, vvc_nal_unit_name(nal->type), nal->nuh_layer_id, nal->temporal_id); + + return 0; +} + static int hevc_parse_nal_header(H2645NAL *nal, void *logctx) { GetBitContext *gb = &nal->gb; @@ -509,7 +576,9 @@ int ff_h2645_packet_split(H2645Packet *pkt, const uint8_t *buf, int length, /* Reset type in case it contains a stale value from a previously parsed NAL */ nal->type = 0; - if (codec_id == AV_CODEC_ID_HEVC) + if (codec_id == AV_CODEC_ID_VVC) + ret = vvc_parse_nal_header(nal, logctx); + else if (codec_id == AV_CODEC_ID_HEVC) ret = hevc_parse_nal_header(nal, logctx); else ret = h264_parse_nal_header(nal, logctx); diff --git a/libavcodec/h2645_sei.c b/libavcodec/h2645_sei.c index 6e4a9a1af25..cb6be0594b4 100644 --- a/libavcodec/h2645_sei.c +++ b/libavcodec/h2645_sei.c @@ -27,13 +27,14 @@ #include "libavutil/ambient_viewing_environment.h" #include "libavutil/display.h" +#include "libavutil/hdr_dynamic_metadata.h" #include "libavutil/film_grain_params.h" +#include "libavutil/mastering_display_metadata.h" #include "libavutil/pixdesc.h" #include "libavutil/stereo3d.h" #include "atsc_a53.h" #include "avcodec.h" -#include "dynamic_hdr10_plus.h" #include "dynamic_hdr_vivid.h" #include "get_bits.h" #include "golomb.h" @@ -52,8 +53,8 @@ static int decode_registered_user_data_dynamic_hdr_plus(HEVCSEIDynamicHDRPlus *s if (!metadata) return AVERROR(ENOMEM); - err = ff_parse_itu_t_t35_to_dynamic_hdr10_plus(metadata, gb->buffer, - bytestream2_get_bytes_left(gb)); + err = av_dynamic_hdr_plus_from_t35(metadata, gb->buffer, + bytestream2_get_bytes_left(gb)); if (err < 0) { av_free(metadata); return err; @@ -392,6 +393,52 @@ static int decode_film_grain_characteristics(H2645SEIFilmGrainCharacteristics *h return 0; } +static int decode_nal_sei_mastering_display_info(H2645SEIMasteringDisplay *s, + GetByteContext *gb) +{ + int i; + + if (bytestream2_get_bytes_left(gb) < 24) + return AVERROR_INVALIDDATA; + + // Mastering primaries + for (i = 0; i < 3; i++) { + s->display_primaries[i][0] = bytestream2_get_be16u(gb); + s->display_primaries[i][1] = bytestream2_get_be16u(gb); + } + // White point (x, y) + s->white_point[0] = bytestream2_get_be16u(gb); + s->white_point[1] = bytestream2_get_be16u(gb); + + // Max and min luminance of mastering display + s->max_luminance = bytestream2_get_be32u(gb); + s->min_luminance = bytestream2_get_be32u(gb); + + // As this SEI message comes before the first frame that references it, + // initialize the flag to 2 and decrement on IRAP access unit so it + // persists for the coded video sequence (e.g., between two IRAPs) + s->present = 2; + + return 0; +} + +static int decode_nal_sei_content_light_info(H2645SEIContentLight *s, + GetByteContext *gb) +{ + if (bytestream2_get_bytes_left(gb) < 4) + return AVERROR_INVALIDDATA; + + // Max and average light levels + s->max_content_light_level = bytestream2_get_be16u(gb); + s->max_pic_average_light_level = bytestream2_get_be16u(gb); + // As this SEI message comes before the first frame that references it, + // initialize the flag to 2 and decrement on IRAP access unit so it + // persists for the coded video sequence (e.g., between two IRAPs) + s->present = 2; + + return 0; +} + int ff_h2645_sei_message_decode(H2645SEI *h, enum SEIType type, enum AVCodecID codec_id, GetBitContext *gb, GetByteContext *gbyte, void *logctx) @@ -412,6 +459,11 @@ int ff_h2645_sei_message_decode(H2645SEI *h, enum SEIType type, case SEI_TYPE_AMBIENT_VIEWING_ENVIRONMENT: return decode_ambient_viewing_environment(&h->ambient_viewing_environment, gbyte); + case SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME: + return decode_nal_sei_mastering_display_info(&h->mastering_display, + gbyte); + case SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO: + return decode_nal_sei_content_light_info(&h->content_light, gbyte); default: return FF_H2645_SEI_MESSAGE_UNHANDLED; } @@ -652,6 +704,64 @@ int ff_h2645_sei_to_frame(AVFrame *frame, H2645SEI *sei, dst_env->ambient_light_y = av_make_q(env->ambient_light_y, 50000); } + if (sei->mastering_display.present) { + // HEVC uses a g,b,r ordering, which we convert to a more natural r,g,b + const int mapping[3] = {2, 0, 1}; + const int chroma_den = 50000; + const int luma_den = 10000; + int i; + AVMasteringDisplayMetadata *metadata = + av_mastering_display_metadata_create_side_data(frame); + if (!metadata) + return AVERROR(ENOMEM); + + for (i = 0; i < 3; i++) { + const int j = mapping[i]; + metadata->display_primaries[i][0].num = sei->mastering_display.display_primaries[j][0]; + metadata->display_primaries[i][0].den = chroma_den; + metadata->display_primaries[i][1].num = sei->mastering_display.display_primaries[j][1]; + metadata->display_primaries[i][1].den = chroma_den; + } + metadata->white_point[0].num = sei->mastering_display.white_point[0]; + metadata->white_point[0].den = chroma_den; + metadata->white_point[1].num = sei->mastering_display.white_point[1]; + metadata->white_point[1].den = chroma_den; + + metadata->max_luminance.num = sei->mastering_display.max_luminance; + metadata->max_luminance.den = luma_den; + metadata->min_luminance.num = sei->mastering_display.min_luminance; + metadata->min_luminance.den = luma_den; + metadata->has_luminance = 1; + metadata->has_primaries = 1; + + av_log(avctx, AV_LOG_DEBUG, "Mastering Display Metadata:\n"); + av_log(avctx, AV_LOG_DEBUG, + "r(%5.4f,%5.4f) g(%5.4f,%5.4f) b(%5.4f %5.4f) wp(%5.4f, %5.4f)\n", + av_q2d(metadata->display_primaries[0][0]), + av_q2d(metadata->display_primaries[0][1]), + av_q2d(metadata->display_primaries[1][0]), + av_q2d(metadata->display_primaries[1][1]), + av_q2d(metadata->display_primaries[2][0]), + av_q2d(metadata->display_primaries[2][1]), + av_q2d(metadata->white_point[0]), av_q2d(metadata->white_point[1])); + av_log(avctx, AV_LOG_DEBUG, + "min_luminance=%f, max_luminance=%f\n", + av_q2d(metadata->min_luminance), av_q2d(metadata->max_luminance)); + } + + if (sei->content_light.present) { + AVContentLightMetadata *metadata = + av_content_light_metadata_create_side_data(frame); + if (!metadata) + return AVERROR(ENOMEM); + metadata->MaxCLL = sei->content_light.max_content_light_level; + metadata->MaxFALL = sei->content_light.max_pic_average_light_level; + + av_log(avctx, AV_LOG_DEBUG, "Content Light Level Metadata:\n"); + av_log(avctx, AV_LOG_DEBUG, "MaxCLL=%d, MaxFALL=%d\n", + metadata->MaxCLL, metadata->MaxFALL); + } + return 0; } @@ -667,4 +777,6 @@ void ff_h2645_sei_reset(H2645SEI *s) av_buffer_unref(&s->dynamic_hdr_vivid.info); s->ambient_viewing_environment.present = 0; + s->mastering_display.present = 0; + s->content_light.present = 0; } diff --git a/libavcodec/h2645_sei.h b/libavcodec/h2645_sei.h index e07ae103761..0ebf48011af 100644 --- a/libavcodec/h2645_sei.h +++ b/libavcodec/h2645_sei.h @@ -105,6 +105,20 @@ typedef struct H2645SEIFilmGrainCharacteristics { int persistence_flag; //< HEVC only } H2645SEIFilmGrainCharacteristics; +typedef struct H2645SEIMasteringDisplay { + int present; + uint16_t display_primaries[3][2]; + uint16_t white_point[2]; + uint32_t max_luminance; + uint32_t min_luminance; +} H2645SEIMasteringDisplay; + +typedef struct H2645SEIContentLight { + int present; + uint16_t max_content_light_level; + uint16_t max_pic_average_light_level; +} H2645SEIContentLight; + typedef struct H2645SEI { H2645SEIA53Caption a53_caption; H2645SEIAFD afd; @@ -116,6 +130,8 @@ typedef struct H2645SEI { H2645SEIAlternativeTransfer alternative_transfer; H2645SEIFilmGrainCharacteristics film_grain_characteristics; H2645SEIAmbientViewingEnvironment ambient_viewing_environment; + H2645SEIMasteringDisplay mastering_display; + H2645SEIContentLight content_light; } H2645SEI; enum { diff --git a/libavcodec/h2645_vui.c b/libavcodec/h2645_vui.c index 0633fcbddd2..e5c7bf46f9b 100644 --- a/libavcodec/h2645_vui.c +++ b/libavcodec/h2645_vui.c @@ -36,21 +36,19 @@ void ff_h2645_decode_common_vui_params(GetBitContext *gb, H2645VUI *vui, void *logctx) { - int aspect_ratio_info_present_flag; - av_log(logctx, AV_LOG_DEBUG, "Decoding VUI\n"); - aspect_ratio_info_present_flag = get_bits1(gb); - if (aspect_ratio_info_present_flag) { - uint8_t aspect_ratio_idc = get_bits(gb, 8); - if (aspect_ratio_idc < FF_ARRAY_ELEMS(ff_h2645_pixel_aspect)) - vui->sar = ff_h2645_pixel_aspect[aspect_ratio_idc]; - else if (aspect_ratio_idc == EXTENDED_SAR) { + vui->aspect_ratio_info_present_flag = get_bits1(gb); + if (vui->aspect_ratio_info_present_flag) { + vui->aspect_ratio_idc = get_bits(gb, 8); + if (vui->aspect_ratio_idc < FF_ARRAY_ELEMS(ff_h2645_pixel_aspect)) + vui->sar = ff_h2645_pixel_aspect[vui->aspect_ratio_idc]; + else if (vui->aspect_ratio_idc == EXTENDED_SAR) { vui->sar.num = get_bits(gb, 16); vui->sar.den = get_bits(gb, 16); } else av_log(logctx, AV_LOG_WARNING, - "Unknown SAR index: %u.\n", aspect_ratio_idc); + "Unknown SAR index: %u.\n", vui->aspect_ratio_idc); } else vui->sar = (AVRational){ 0, 1 }; diff --git a/libavcodec/h2645_vui.h b/libavcodec/h2645_vui.h index 638da7c3667..2c839f4b015 100644 --- a/libavcodec/h2645_vui.h +++ b/libavcodec/h2645_vui.h @@ -26,6 +26,8 @@ typedef struct H2645VUI { AVRational sar; + int aspect_ratio_idc; + int aspect_ratio_info_present_flag; int overscan_info_present_flag; int overscan_appropriate_flag; diff --git a/libavcodec/h264_mb.c b/libavcodec/h264_mb.c index 0b317745566..32d29cfb4d6 100644 --- a/libavcodec/h264_mb.c +++ b/libavcodec/h264_mb.c @@ -34,6 +34,7 @@ #include "h264dec.h" #include "h264_ps.h" #include "qpeldsp.h" +#include "rectangle.h" #include "threadframe.h" static inline int get_lowest_part_list_y(H264SliceContext *sl, diff --git a/libavcodec/h264_metadata_bsf.c b/libavcodec/h264_metadata_bsf.c index d318bf0ceec..39b9e8eee4e 100644 --- a/libavcodec/h264_metadata_bsf.c +++ b/libavcodec/h264_metadata_bsf.c @@ -26,10 +26,11 @@ #include "cbs.h" #include "cbs_bsf.h" #include "cbs_h264.h" +#include "cbs_sei.h" #include "h264.h" #include "h264_levels.h" -#include "h264_sei.h" #include "h2645data.h" +#include "sei.h" enum { FLIP_HORIZONTAL = 1, @@ -469,12 +470,13 @@ static int h264_metadata_update_fragment(AVBSFContext *bsf, AVPacket *pkt, H264MetadataContext *ctx = bsf->priv_data; int err, i, has_sps, seek_point; - // If an AUD is present, it must be the first NAL unit. - if (au->nb_units && au->units[0].type == H264_NAL_AUD) { - if (ctx->aud == BSF_ELEMENT_REMOVE) - ff_cbs_delete_unit(au, 0); - } else { - if (pkt && ctx->aud == BSF_ELEMENT_INSERT) { + if (ctx->aud == BSF_ELEMENT_REMOVE) { + for (i = au->nb_units - 1; i >= 0; i--) { + if (au->units[i].type == H264_NAL_AUD) + ff_cbs_delete_unit(au, i); + } + } else if (ctx->aud == BSF_ELEMENT_INSERT) { + if (pkt) { err = h264_metadata_insert_aud(bsf, au); if (err < 0) return err; diff --git a/libavcodec/h264_mvpred.h b/libavcodec/h264_mvpred.h index 46ae2738f92..bc9fef50e4e 100644 --- a/libavcodec/h264_mvpred.h +++ b/libavcodec/h264_mvpred.h @@ -30,10 +30,142 @@ #include "h264dec.h" #include "mpegutils.h" +#include "rectangle.h" + #include "libavutil/avassert.h" #include "libavutil/mem_internal.h" +/** + * Get the predicted intra4x4 prediction mode. + */ +static av_always_inline int pred_intra_mode(const H264Context *h, + H264SliceContext *sl, int n) +{ + const int index8 = scan8[n]; + const int left = sl->intra4x4_pred_mode_cache[index8 - 1]; + const int top = sl->intra4x4_pred_mode_cache[index8 - 8]; + const int min = FFMIN(left, top); + + ff_tlog(h->avctx, "mode:%d %d min:%d\n", left, top, min); + + if (min < 0) + return DC_PRED; + else + return min; +} + +static av_always_inline void write_back_intra_pred_mode(const H264Context *h, + H264SliceContext *sl) +{ + int8_t *i4x4 = sl->intra4x4_pred_mode + h->mb2br_xy[sl->mb_xy]; + int8_t *i4x4_cache = sl->intra4x4_pred_mode_cache; + + AV_COPY32(i4x4, i4x4_cache + 4 + 8 * 4); + i4x4[4] = i4x4_cache[7 + 8 * 3]; + i4x4[5] = i4x4_cache[7 + 8 * 2]; + i4x4[6] = i4x4_cache[7 + 8 * 1]; +} + +static av_always_inline void write_back_non_zero_count(const H264Context *h, + H264SliceContext *sl) +{ + const int mb_xy = sl->mb_xy; + uint8_t *nnz = h->non_zero_count[mb_xy]; + uint8_t *nnz_cache = sl->non_zero_count_cache; + + AV_COPY32(&nnz[ 0], &nnz_cache[4 + 8 * 1]); + AV_COPY32(&nnz[ 4], &nnz_cache[4 + 8 * 2]); + AV_COPY32(&nnz[ 8], &nnz_cache[4 + 8 * 3]); + AV_COPY32(&nnz[12], &nnz_cache[4 + 8 * 4]); + AV_COPY32(&nnz[16], &nnz_cache[4 + 8 * 6]); + AV_COPY32(&nnz[20], &nnz_cache[4 + 8 * 7]); + AV_COPY32(&nnz[32], &nnz_cache[4 + 8 * 11]); + AV_COPY32(&nnz[36], &nnz_cache[4 + 8 * 12]); + + if (!h->chroma_y_shift) { + AV_COPY32(&nnz[24], &nnz_cache[4 + 8 * 8]); + AV_COPY32(&nnz[28], &nnz_cache[4 + 8 * 9]); + AV_COPY32(&nnz[40], &nnz_cache[4 + 8 * 13]); + AV_COPY32(&nnz[44], &nnz_cache[4 + 8 * 14]); + } +} + +static av_always_inline void write_back_motion_list(const H264Context *h, + H264SliceContext *sl, + int b_stride, + int b_xy, int b8_xy, + int mb_type, int list) +{ + int16_t(*mv_dst)[2] = &h->cur_pic.motion_val[list][b_xy]; + int16_t(*mv_src)[2] = &sl->mv_cache[list][scan8[0]]; + AV_COPY128(mv_dst + 0 * b_stride, mv_src + 8 * 0); + AV_COPY128(mv_dst + 1 * b_stride, mv_src + 8 * 1); + AV_COPY128(mv_dst + 2 * b_stride, mv_src + 8 * 2); + AV_COPY128(mv_dst + 3 * b_stride, mv_src + 8 * 3); + if (CABAC(h)) { + uint8_t (*mvd_dst)[2] = &sl->mvd_table[list][FMO ? 8 * sl->mb_xy + : h->mb2br_xy[sl->mb_xy]]; + uint8_t(*mvd_src)[2] = &sl->mvd_cache[list][scan8[0]]; + if (IS_SKIP(mb_type)) { + AV_ZERO128(mvd_dst); + } else { + AV_COPY64(mvd_dst, mvd_src + 8 * 3); + AV_COPY16(mvd_dst + 3 + 3, mvd_src + 3 + 8 * 0); + AV_COPY16(mvd_dst + 3 + 2, mvd_src + 3 + 8 * 1); + AV_COPY16(mvd_dst + 3 + 1, mvd_src + 3 + 8 * 2); + } + } + + { + int8_t *ref_index = &h->cur_pic.ref_index[list][b8_xy]; + int8_t *ref_cache = sl->ref_cache[list]; + ref_index[0 + 0 * 2] = ref_cache[scan8[0]]; + ref_index[1 + 0 * 2] = ref_cache[scan8[4]]; + ref_index[0 + 1 * 2] = ref_cache[scan8[8]]; + ref_index[1 + 1 * 2] = ref_cache[scan8[12]]; + } +} + +static av_always_inline void write_back_motion(const H264Context *h, + H264SliceContext *sl, + int mb_type) +{ + const int b_stride = h->b_stride; + const int b_xy = 4 * sl->mb_x + 4 * sl->mb_y * h->b_stride; // try mb2b(8)_xy + const int b8_xy = 4 * sl->mb_xy; + + if (USES_LIST(mb_type, 0)) { + write_back_motion_list(h, sl, b_stride, b_xy, b8_xy, mb_type, 0); + } else { + fill_rectangle(&h->cur_pic.ref_index[0][b8_xy], + 2, 2, 2, (uint8_t)LIST_NOT_USED, 1); + } + if (USES_LIST(mb_type, 1)) + write_back_motion_list(h, sl, b_stride, b_xy, b8_xy, mb_type, 1); + + if (sl->slice_type_nos == AV_PICTURE_TYPE_B && CABAC(h)) { + if (IS_8X8(mb_type)) { + uint8_t *direct_table = &h->direct_table[4 * sl->mb_xy]; + direct_table[1] = sl->sub_mb_type[1] >> 1; + direct_table[2] = sl->sub_mb_type[2] >> 1; + direct_table[3] = sl->sub_mb_type[3] >> 1; + } + } +} + +static av_always_inline int get_dct8x8_allowed(const H264Context *h, H264SliceContext *sl) +{ + if (h->ps.sps->direct_8x8_inference_flag) + return !(AV_RN64A(sl->sub_mb_type) & + ((MB_TYPE_16x8 | MB_TYPE_8x16 | MB_TYPE_8x8) * + 0x0001000100010001ULL)); + else + return !(AV_RN64A(sl->sub_mb_type) & + ((MB_TYPE_16x8 | MB_TYPE_8x16 | MB_TYPE_8x8 | MB_TYPE_DIRECT2) * + 0x0001000100010001ULL)); +} + static av_always_inline int fetch_diagonal_mv(const H264Context *h, H264SliceContext *sl, const int16_t **C, int i, int list, int part_width) diff --git a/libavcodec/h264_parser.c b/libavcodec/h264_parser.c index 3ed23fb9cae..43abc45f9cd 100644 --- a/libavcodec/h264_parser.c +++ b/libavcodec/h264_parser.c @@ -568,7 +568,7 @@ static inline int parse_nal_units(AVCodecParserContext *s, if (p->sei.common.unregistered.x264_build < 44U) den *= 2; av_reduce(&avctx->framerate.den, &avctx->framerate.num, - sps->num_units_in_tick * avctx->ticks_per_frame, den, 1 << 30); + sps->num_units_in_tick * 2, den, 1 << 30); } av_freep(&rbsp.rbsp_buffer); @@ -593,6 +593,7 @@ static int h264_parse(AVCodecParserContext *s, { H264ParseContext *p = s->priv_data; ParseContext *pc = &p->pc; + AVRational time_base = { 0, 1 }; int next; if (!p->got_first) { @@ -624,7 +625,7 @@ static int h264_parse(AVCodecParserContext *s, parse_nal_units(s, avctx, buf, buf_size); if (avctx->framerate.num) - avctx->time_base = av_inv_q(av_mul_q(avctx->framerate, (AVRational){avctx->ticks_per_frame, 1})); + time_base = av_inv_q(av_mul_q(avctx->framerate, (AVRational){2, 1})); if (p->sei.picture_timing.cpb_removal_delay >= 0) { s->dts_sync_point = p->sei.buffering_period.present; s->dts_ref_dts_delta = p->sei.picture_timing.cpb_removal_delay; @@ -640,9 +641,9 @@ static int h264_parse(AVCodecParserContext *s, } if (s->dts_sync_point >= 0) { - int64_t den = avctx->time_base.den * (int64_t)avctx->pkt_timebase.num; + int64_t den = time_base.den * (int64_t)avctx->pkt_timebase.num; if (den > 0) { - int64_t num = avctx->time_base.num * (int64_t)avctx->pkt_timebase.den; + int64_t num = time_base.num * (int64_t)avctx->pkt_timebase.den; if (s->dts != AV_NOPTS_VALUE) { // got DTS from the stream, update reference timestamp p->reference_dts = s->dts - av_rescale(s->dts_ref_dts_delta, num, den); diff --git a/libavcodec/h264_picture.c b/libavcodec/h264_picture.c index 2661ff4698e..3192c70a912 100644 --- a/libavcodec/h264_picture.c +++ b/libavcodec/h264_picture.c @@ -29,6 +29,7 @@ #include "error_resilience.h" #include "avcodec.h" #include "h264dec.h" +#include "hwaccel_internal.h" #include "mpegutils.h" #include "thread.h" #include "threadframe.h" @@ -154,8 +155,7 @@ int ff_h264_replace_picture(H264Context *h, H264Picture *dst, const H264Picture av_assert0(src->tf.f == src->f); dst->tf.f = dst->f; - ff_thread_release_ext_buffer(h->avctx, &dst->tf); - ret = ff_thread_ref_frame(&dst->tf, &src->tf); + ret = ff_thread_replace_frame(h->avctx, &dst->tf, &src->tf); if (ret < 0) goto fail; @@ -234,7 +234,7 @@ int ff_h264_field_end(H264Context *h, H264SliceContext *sl, int in_setup) } if (avctx->hwaccel) { - err = avctx->hwaccel->end_frame(avctx); + err = FF_HW_SIMPLE_CALL(avctx, end_frame); if (err < 0) av_log(avctx, AV_LOG_ERROR, "hardware accelerator failed to decode picture\n"); diff --git a/libavcodec/h264_ps.c b/libavcodec/h264_ps.c index d0d1e659039..53446e9aabe 100644 --- a/libavcodec/h264_ps.c +++ b/libavcodec/h264_ps.c @@ -113,12 +113,13 @@ static inline int decode_hrd_parameters(GetBitContext *gb, void *logctx, return AVERROR_INVALIDDATA; } - get_bits(gb, 4); /* bit_rate_scale */ + sps->cpr_flag = 0x0; + sps->bit_rate_scale = get_bits(gb, 4); get_bits(gb, 4); /* cpb_size_scale */ for (i = 0; i < cpb_count; i++) { - get_ue_golomb_long(gb); /* bit_rate_value_minus1 */ - get_ue_golomb_long(gb); /* cpb_size_value_minus1 */ - get_bits1(gb); /* cbr_flag */ + sps->bit_rate_value[i] = get_ue_golomb_long(gb) + 1; /* bit_rate_value_minus1 + 1 */ + sps->cpb_size_value[i] = get_ue_golomb_long(gb) + 1; /* cpb_size_value_minus1 + 1 */ + sps->cpr_flag |= get_bits1(gb) << i; } sps->initial_cpb_removal_delay_length = get_bits(gb, 5) + 1; sps->cpb_removal_delay_length = get_bits(gb, 5) + 1; @@ -176,7 +177,7 @@ static inline int decode_vui_parameters(GetBitContext *gb, void *logctx, get_ue_golomb_31(gb); /* log2_max_mv_length_horizontal */ get_ue_golomb_31(gb); /* log2_max_mv_length_vertical */ sps->num_reorder_frames = get_ue_golomb_31(gb); - get_ue_golomb_31(gb); /*max_dec_frame_buffering*/ + sps->max_dec_frame_buffering = get_ue_golomb_31(gb); if (get_bits_left(gb) < 0) { sps->num_reorder_frames = 0; @@ -197,12 +198,14 @@ static inline int decode_vui_parameters(GetBitContext *gb, void *logctx, } static int decode_scaling_list(GetBitContext *gb, uint8_t *factors, int size, - const uint8_t *jvt_list, - const uint8_t *fallback_list) + const uint8_t *jvt_list, const uint8_t *fallback_list, + uint16_t *mask, int pos) { int i, last = 8, next = 8; const uint8_t *scan = size == 16 ? ff_zigzag_scan : ff_zigzag_direct; - if (!get_bits1(gb)) /* matrix not written, we use the predicted one */ + uint16_t seq_scaling_list_present_flag = get_bits1(gb); + *mask |= (seq_scaling_list_present_flag << pos); + if (!seq_scaling_list_present_flag) /* matrix not written, we use the predicted one */ memcpy(factors, fallback_list, size * sizeof(uint8_t)); else for (i = 0; i < size; i++) { @@ -226,6 +229,7 @@ static int decode_scaling_list(GetBitContext *gb, uint8_t *factors, int size, /* returns non zero if the provided SPS scaling matrix has been filled */ static int decode_scaling_matrices(GetBitContext *gb, const SPS *sps, const PPS *pps, int is_sps, + int present_flag, uint16_t *mask, uint8_t(*scaling_matrix4)[16], uint8_t(*scaling_matrix8)[64]) { @@ -237,21 +241,22 @@ static int decode_scaling_matrices(GetBitContext *gb, const SPS *sps, fallback_sps ? sps->scaling_matrix8[3] : default_scaling8[1] }; int ret = 0; - if (get_bits1(gb)) { - ret |= decode_scaling_list(gb, scaling_matrix4[0], 16, default_scaling4[0], fallback[0]); // Intra, Y - ret |= decode_scaling_list(gb, scaling_matrix4[1], 16, default_scaling4[0], scaling_matrix4[0]); // Intra, Cr - ret |= decode_scaling_list(gb, scaling_matrix4[2], 16, default_scaling4[0], scaling_matrix4[1]); // Intra, Cb - ret |= decode_scaling_list(gb, scaling_matrix4[3], 16, default_scaling4[1], fallback[1]); // Inter, Y - ret |= decode_scaling_list(gb, scaling_matrix4[4], 16, default_scaling4[1], scaling_matrix4[3]); // Inter, Cr - ret |= decode_scaling_list(gb, scaling_matrix4[5], 16, default_scaling4[1], scaling_matrix4[4]); // Inter, Cb + *mask = 0x0; + if (present_flag) { + ret |= decode_scaling_list(gb, scaling_matrix4[0], 16, default_scaling4[0], fallback[0], mask, 0); // Intra, Y + ret |= decode_scaling_list(gb, scaling_matrix4[1], 16, default_scaling4[0], scaling_matrix4[0], mask, 1); // Intra, Cr + ret |= decode_scaling_list(gb, scaling_matrix4[2], 16, default_scaling4[0], scaling_matrix4[1], mask, 2); // Intra, Cb + ret |= decode_scaling_list(gb, scaling_matrix4[3], 16, default_scaling4[1], fallback[1], mask, 3); // Inter, Y + ret |= decode_scaling_list(gb, scaling_matrix4[4], 16, default_scaling4[1], scaling_matrix4[3], mask, 4); // Inter, Cr + ret |= decode_scaling_list(gb, scaling_matrix4[5], 16, default_scaling4[1], scaling_matrix4[4], mask, 5); // Inter, Cb if (is_sps || pps->transform_8x8_mode) { - ret |= decode_scaling_list(gb, scaling_matrix8[0], 64, default_scaling8[0], fallback[2]); // Intra, Y - ret |= decode_scaling_list(gb, scaling_matrix8[3], 64, default_scaling8[1], fallback[3]); // Inter, Y + ret |= decode_scaling_list(gb, scaling_matrix8[0], 64, default_scaling8[0], fallback[2], mask, 6); // Intra, Y + ret |= decode_scaling_list(gb, scaling_matrix8[3], 64, default_scaling8[1], fallback[3], mask, 7); // Inter, Y if (sps->chroma_format_idc == 3) { - ret |= decode_scaling_list(gb, scaling_matrix8[1], 64, default_scaling8[0], scaling_matrix8[0]); // Intra, Cr - ret |= decode_scaling_list(gb, scaling_matrix8[4], 64, default_scaling8[1], scaling_matrix8[3]); // Inter, Cr - ret |= decode_scaling_list(gb, scaling_matrix8[2], 64, default_scaling8[0], scaling_matrix8[1]); // Intra, Cb - ret |= decode_scaling_list(gb, scaling_matrix8[5], 64, default_scaling8[1], scaling_matrix8[4]); // Inter, Cb + ret |= decode_scaling_list(gb, scaling_matrix8[1], 64, default_scaling8[0], scaling_matrix8[0], mask, 8); // Intra, Cr + ret |= decode_scaling_list(gb, scaling_matrix8[4], 64, default_scaling8[1], scaling_matrix8[3], mask, 9); // Inter, Cr + ret |= decode_scaling_list(gb, scaling_matrix8[2], 64, default_scaling8[0], scaling_matrix8[1], mask, 10); // Intra, Cb + ret |= decode_scaling_list(gb, scaling_matrix8[5], 64, default_scaling8[1], scaling_matrix8[4], mask, 11); // Inter, Cb } } if (!ret) @@ -368,7 +373,8 @@ int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx, goto fail; } sps->transform_bypass = get_bits1(gb); - ret = decode_scaling_matrices(gb, sps, NULL, 1, + ret = decode_scaling_matrices(gb, sps, NULL, 1, get_bits1(gb), + &sps->scaling_matrix_present_mask, sps->scaling_matrix4, sps->scaling_matrix8); if (ret < 0) goto fail; @@ -731,6 +737,7 @@ int ff_h264_decode_picture_parameter_set(GetBitContext *gb, AVCodecContext *avct if (!(bit_length & 7) && pps->data_size < sizeof(pps->data)) pps->data[pps->data_size++] = 0x80; + pps->pps_id = pps_id; pps->sps_id = get_ue_golomb_31(gb); if ((unsigned)pps->sps_id >= MAX_SPS_COUNT || !ps->sps_list[pps->sps_id]) { @@ -802,7 +809,10 @@ int ff_h264_decode_picture_parameter_set(GetBitContext *gb, AVCodecContext *avct bits_left = bit_length - get_bits_count(gb); if (bits_left > 0 && more_rbsp_data_in_pps(sps, avctx)) { pps->transform_8x8_mode = get_bits1(gb); + pps->pic_scaling_matrix_present_flag = get_bits1(gb); ret = decode_scaling_matrices(gb, sps, pps, 0, + pps->pic_scaling_matrix_present_flag, + &pps->pic_scaling_matrix_present_mask, pps->scaling_matrix4, pps->scaling_matrix8); if (ret < 0) goto fail; diff --git a/libavcodec/h264_ps.h b/libavcodec/h264_ps.h index 5c35761fbc8..e6756196352 100644 --- a/libavcodec/h264_ps.h +++ b/libavcodec/h264_ps.h @@ -80,7 +80,9 @@ typedef struct SPS { int32_t offset_for_ref_frame[256]; int bitstream_restriction_flag; int num_reorder_frames; + int max_dec_frame_buffering; int scaling_matrix_present; + uint16_t scaling_matrix_present_mask; uint8_t scaling_matrix4[6][16]; uint8_t scaling_matrix8[6][64]; int nal_hrd_parameters_present_flag; @@ -88,6 +90,10 @@ typedef struct SPS { int pic_struct_present_flag; int time_offset_length; int cpb_cnt; ///< See H.264 E.1.2 + int bit_rate_scale; + uint32_t bit_rate_value[32]; ///< bit_rate_value_minus1 + 1 + uint32_t cpb_size_value[32]; ///< cpb_size_value_minus1 + 1 + uint32_t cpr_flag; int initial_cpb_removal_delay_length; ///< initial_cpb_removal_delay_length_minus1 + 1 int cpb_removal_delay_length; ///< cpb_removal_delay_length_minus1 + 1 int dpb_output_delay_length; ///< dpb_output_delay_length_minus1 + 1 @@ -103,9 +109,10 @@ typedef struct SPS { * Picture parameter set */ typedef struct PPS { + unsigned int pps_id; unsigned int sps_id; int cabac; ///< entropy_coding_mode_flag - int pic_order_present; ///< pic_order_present_flag + int pic_order_present; ///< bottom_field_pic_order_in_frame_present_flag int slice_group_count; ///< num_slice_groups_minus1 + 1 int mb_slice_group_map_type; unsigned int ref_count[2]; ///< num_ref_idx_l0/1_active_minus1 + 1 @@ -118,6 +125,8 @@ typedef struct PPS { int constrained_intra_pred; ///< constrained_intra_pred_flag int redundant_pic_cnt_present; ///< redundant_pic_cnt_present_flag int transform_8x8_mode; ///< transform_8x8_mode_flag + int pic_scaling_matrix_present_flag; + uint16_t pic_scaling_matrix_present_mask; uint8_t scaling_matrix4[6][16]; uint8_t scaling_matrix8[6][64]; uint8_t chroma_qp_table[2][QP_MAX_NUM+1]; ///< pre-scaled (with chroma_qp_index_offset) version of qp_table diff --git a/libavcodec/h264_slice.c b/libavcodec/h264_slice.c index 6188c746324..6cd7bb8fe72 100644 --- a/libavcodec/h264_slice.c +++ b/libavcodec/h264_slice.c @@ -28,14 +28,11 @@ #include "config_components.h" #include "libavutil/avassert.h" -#include "libavutil/display.h" -#include "libavutil/film_grain_params.h" #include "libavutil/pixdesc.h" #include "libavutil/timecode.h" -#include "internal.h" +#include "decode.h" #include "cabac.h" #include "cabac_functions.h" -#include "decode.h" #include "error_resilience.h" #include "avcodec.h" #include "h264.h" @@ -208,16 +205,11 @@ static int alloc_picture(H264Context *h, H264Picture *pic) goto fail; } - if (h->avctx->hwaccel) { - const AVHWAccel *hwaccel = h->avctx->hwaccel; - av_assert0(!pic->hwaccel_picture_private); - if (hwaccel->frame_priv_data_size) { - pic->hwaccel_priv_buf = av_buffer_allocz(hwaccel->frame_priv_data_size); - if (!pic->hwaccel_priv_buf) - return AVERROR(ENOMEM); - pic->hwaccel_picture_private = pic->hwaccel_priv_buf->data; - } - } + ret = ff_hwaccel_frame_priv_alloc(h->avctx, &pic->hwaccel_picture_private, + &pic->hwaccel_priv_buf); + if (ret < 0) + goto fail; + 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, @@ -303,6 +295,34 @@ static void copy_picture_range(H264Picture **to, H264Picture **from, int count, } } +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) { + ((uint16_t*)dst)[0] = c[p]; + av_memcpy_backptr(dst + 2, 2, bytes - 2); + 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, @@ -438,6 +458,8 @@ int ff_h264_update_thread_context(AVCodecContext *dst, return ret; h->sei.common.unregistered.x264_build = h1->sei.common.unregistered.x264_build; + h->sei.common.mastering_display = h1->sei.common.mastering_display; + h->sei.common.content_light = h1->sei.common.content_light; if (!h->cur_pic_ptr) return 0; @@ -489,7 +511,11 @@ static int h264_frame_start(H264Context *h) pic = &h->DPB[i]; pic->reference = h->droppable ? 0 : h->picture_structure; +#if FF_API_FRAME_PICTURE_NUMBER +FF_DISABLE_DEPRECATION_WARNINGS pic->f->coded_picture_number = h->coded_picture_number++; +FF_ENABLE_DEPRECATION_WARNINGS +#endif pic->field_picture = h->picture_structure != PICT_FRAME; pic->frame_num = h->poc.frame_num; /* @@ -497,7 +523,7 @@ static int h264_frame_start(H264Context *h) * in later. * See decode_nal_units(). */ - pic->f->key_frame = 0; + pic->f->flags &= ~AV_FRAME_FLAG_KEY; pic->mmco_reset = 0; pic->recovered = 0; pic->invalid_gap = 0; @@ -776,10 +802,9 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback) CONFIG_H264_NVDEC_HWACCEL + \ CONFIG_H264_VAAPI_HWACCEL + \ CONFIG_H264_VIDEOTOOLBOX_HWACCEL + \ - CONFIG_H264_VDPAU_HWACCEL) + CONFIG_H264_VDPAU_HWACCEL + \ + CONFIG_H264_VULKAN_HWACCEL) enum AVPixelFormat pix_fmts[HWACCEL_MAX + 2], *fmt = pix_fmts; - const enum AVPixelFormat *choices = pix_fmts; - int i; switch (h->ps.sps->bit_depth_luma) { case 9: @@ -797,6 +822,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_VULKAN_HWACCEL + *fmt++ = AV_PIX_FMT_VULKAN; #endif if (CHROMA444(h)) { if (h->avctx->colorspace == AVCOL_SPC_RGB) { @@ -805,10 +833,20 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback) *fmt++ = AV_PIX_FMT_YUV444P10; } else if (CHROMA422(h)) *fmt++ = AV_PIX_FMT_YUV422P10; - else + 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; @@ -834,6 +872,9 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback) #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 @@ -864,9 +905,7 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback) #if CONFIG_H264_VAAPI_HWACCEL *fmt++ = AV_PIX_FMT_VAAPI; #endif - if (h->avctx->codec->pix_fmts) - choices = h->avctx->codec->pix_fmts; - else if (h->avctx->color_range == AVCOL_RANGE_JPEG) + if (h->avctx->color_range == AVCOL_RANGE_JPEG) *fmt++ = AV_PIX_FMT_YUVJ420P; else *fmt++ = AV_PIX_FMT_YUV420P; @@ -880,10 +919,10 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback) *fmt = AV_PIX_FMT_NONE; - for (i=0; choices[i] != AV_PIX_FMT_NONE; i++) - if (choices[i] == h->avctx->pix_fmt && !force_callback) - return choices[i]; - return ff_thread_get_format(h->avctx, choices); + 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_thread_get_format(h->avctx, pix_fmts); } /* export coded and cropped frame dimensions to AVCodecContext */ @@ -946,7 +985,7 @@ static int h264_slice_header_init(H264Context *h) if (h->x264_build < 44U) den *= 2; av_reduce(&h->avctx->framerate.den, &h->avctx->framerate.num, - sps->num_units_in_tick * h->avctx->ticks_per_frame, den, 1 << 30); + sps->num_units_in_tick * 2, den, 1 << 30); } ff_h264_free_tables(h); @@ -1144,9 +1183,10 @@ 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->interlaced_frame = 0; + out->flags &= ~AV_FRAME_FLAG_INTERLACED; out->repeat_pict = 0; /* Signal interlacing information externally. */ @@ -1170,15 +1210,15 @@ static int h264_export_frame_props(H264Context *h) break; case H264_SEI_PIC_STRUCT_TOP_FIELD: case H264_SEI_PIC_STRUCT_BOTTOM_FIELD: - out->interlaced_frame = 1; + interlaced_frame = 1; break; case H264_SEI_PIC_STRUCT_TOP_BOTTOM: case H264_SEI_PIC_STRUCT_BOTTOM_TOP: if (FIELD_OR_MBAFF_PICTURE(h)) - out->interlaced_frame = 1; + interlaced_frame = 1; else // try to flag soft telecine progressive - out->interlaced_frame = h->prev_interlaced_frame; + interlaced_frame = !!h->prev_interlaced_frame; break; case H264_SEI_PIC_STRUCT_TOP_BOTTOM_TOP: case H264_SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM: @@ -1197,35 +1237,34 @@ static int h264_export_frame_props(H264Context *h) if ((pt->ct_type & 3) && pt->pic_struct <= H264_SEI_PIC_STRUCT_BOTTOM_TOP) - out->interlaced_frame = (pt->ct_type & (1 << 1)) != 0; + interlaced_frame = ((pt->ct_type & (1 << 1)) != 0); } else { /* Derive interlacing flag from used decoding process. */ - out->interlaced_frame = FIELD_OR_MBAFF_PICTURE(h); + interlaced_frame = !!FIELD_OR_MBAFF_PICTURE(h); } - h->prev_interlaced_frame = out->interlaced_frame; + h->prev_interlaced_frame = interlaced_frame; if (cur->field_poc[0] != cur->field_poc[1]) { /* Derive top_field_first from field pocs. */ - out->top_field_first = cur->field_poc[0] < cur->field_poc[1]; + 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) - out->top_field_first = 1; - else - out->top_field_first = 0; - } else if (out->interlaced_frame) { + 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 */ - out->top_field_first = 1; - } else { + top_field_first = 1; + } // else /* Most likely progressive */ - out->top_field_first = 0; - } } + 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)); @@ -1297,7 +1336,7 @@ static int h264_select_output_frame(H264Context *h) 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_number > 1 ? AV_LOG_WARNING : AV_LOG_VERBOSE; + 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; } @@ -1315,7 +1354,7 @@ static int h264_select_output_frame(H264Context *h) out = h->delayed_pic[0]; out_idx = 0; for (i = 1; h->delayed_pic[i] && - !h->delayed_pic[i]->f->key_frame && + !(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) { @@ -1323,7 +1362,7 @@ static int h264_select_output_frame(H264Context *h) out_idx = i; } if (h->avctx->has_b_frames == 0 && - (h->delayed_pic[0]->f->key_frame || h->delayed_pic[0]->mmco_reset)) + ((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; @@ -1334,7 +1373,7 @@ static int h264_select_output_frame(H264Context *h) } 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->key_frame || h->delayed_pic[0]->mmco_reset)) { + 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; @@ -1532,7 +1571,7 @@ static int h264_field_start(H264Context *h, const H264SliceContext *sl, if (h->short_ref[0]->field_picture) ff_thread_report_progress(&h->short_ref[0]->tf, INT_MAX, 1); } else if (!h->frame_recovered && !h->avctx->hwaccel) - ff_color_frame(h->short_ref[0]->f, c); + color_frame(h->short_ref[0]->f, c); h->short_ref[0]->frame_num = h->poc.prev_frame_num; } } @@ -1624,7 +1663,7 @@ static int h264_field_start(H264Context *h, const H264SliceContext *sl, } } - h->cur_pic_ptr->f->key_frame |= (nal->type == H264_NAL_IDR_SLICE); + h->cur_pic_ptr->f->flags |= AV_FRAME_FLAG_KEY * !!(nal->type == H264_NAL_IDR_SLICE); if (nal->type == H264_NAL_IDR_SLICE || (h->recovery_frame == h->poc.frame_num && nal->ref_idc)) { diff --git a/libavcodec/h264chroma.c b/libavcodec/h264chroma.c index 60b86b6fba0..1eeab7bc40c 100644 --- a/libavcodec/h264chroma.c +++ b/libavcodec/h264chroma.c @@ -58,5 +58,7 @@ av_cold void ff_h264chroma_init(H264ChromaContext *c, int bit_depth) ff_h264chroma_init_mips(c, bit_depth); #elif ARCH_LOONGARCH64 ff_h264chroma_init_loongarch(c, bit_depth); +#elif ARCH_RISCV + ff_h264chroma_init_riscv(c, bit_depth); #endif } diff --git a/libavcodec/h264chroma.h b/libavcodec/h264chroma.h index b8f9c8f4fcc..9c81c18a76f 100644 --- a/libavcodec/h264chroma.h +++ b/libavcodec/h264chroma.h @@ -37,5 +37,6 @@ void ff_h264chroma_init_ppc(H264ChromaContext *c, int bit_depth); void ff_h264chroma_init_x86(H264ChromaContext *c, int bit_depth); void ff_h264chroma_init_mips(H264ChromaContext *c, int bit_depth); void ff_h264chroma_init_loongarch(H264ChromaContext *c, int bit_depth); +void ff_h264chroma_init_riscv(H264ChromaContext *c, int bit_depth); #endif /* AVCODEC_H264CHROMA_H */ diff --git a/libavcodec/h264dec.c b/libavcodec/h264dec.c index 9f7b3782e84..1c79d9842f6 100644 --- a/libavcodec/h264dec.c +++ b/libavcodec/h264dec.c @@ -45,6 +45,7 @@ #include "h264data.h" #include "h264_ps.h" #include "golomb.h" +#include "hwaccel_internal.h" #include "hwconfig.h" #include "mpegutils.h" #include "profiles.h" @@ -382,13 +383,11 @@ static av_cold int h264_decode_init(AVCodecContext *avctx) return AVERROR_UNKNOWN; } - if (avctx->ticks_per_frame == 1) { - if(h->avctx->time_base.den < INT_MAX/2) { - h->avctx->time_base.den *= 2; - } else - h->avctx->time_base.num /= 2; - } +#if FF_API_TICKS_PER_FRAME +FF_DISABLE_DEPRECATION_WARNINGS avctx->ticks_per_frame = 2; +FF_ENABLE_DEPRECATION_WARNINGS +#endif if (!avctx->internal->is_copy) { if (avctx->extradata_size > 0 && avctx->extradata) { @@ -486,6 +485,9 @@ static void h264_decode_flush(AVCodecContext *avctx) 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) @@ -651,14 +653,14 @@ static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size) } if (h->avctx->hwaccel && - (ret = h->avctx->hwaccel->start_frame(h->avctx, buf, buf_size)) < 0) + (ret = FF_HW_CALL(h->avctx, start_frame, 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 = avctx->hwaccel->decode_slice(avctx, nal->raw_data, nal->raw_size); + 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); @@ -685,11 +687,9 @@ static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size) break; case H264_NAL_SPS: { GetBitContext tmp_gb = nal->gb; - if (avctx->hwaccel && avctx->hwaccel->decode_params) { - ret = avctx->hwaccel->decode_params(avctx, - nal->type, - nal->raw_data, - nal->raw_size); + 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; } @@ -704,11 +704,9 @@ static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size) break; } case H264_NAL_PPS: - if (avctx->hwaccel && avctx->hwaccel->decode_params) { - ret = avctx->hwaccel->decode_params(avctx, - nal->type, - nal->raw_data, - nal->raw_size); + 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; } @@ -855,7 +853,7 @@ static int output_frame(H264Context *h, AVFrame *dst, H264Picture *srcp) 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->key_frame = 1; + 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); @@ -957,7 +955,7 @@ static int send_next_delayed_frame(H264Context *h, AVFrame *dst_frame, out_idx = 0; for (i = 1; h->delayed_pic[i] && - !h->delayed_pic[i]->f->key_frame && + !(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) { @@ -1099,6 +1097,9 @@ const FFCodec ff_h264_decoder = { #endif #if CONFIG_H264_VIDEOTOOLBOX_HWACCEL HWACCEL_VIDEOTOOLBOX(h264), +#endif +#if CONFIG_H264_VULKAN_HWACCEL + HWACCEL_VULKAN(h264), #endif NULL }, diff --git a/libavcodec/h264dec.h b/libavcodec/h264dec.h index 9a1ec1bacec..beaab3902c1 100644 --- a/libavcodec/h264dec.h +++ b/libavcodec/h264dec.h @@ -29,7 +29,6 @@ #define AVCODEC_H264DEC_H #include "libavutil/buffer.h" -#include "libavutil/intreadwrite.h" #include "libavutil/mem_internal.h" #include "cabac.h" @@ -44,7 +43,6 @@ #include "h264qpel.h" #include "h274.h" #include "mpegutils.h" -#include "rectangle.h" #include "videodsp.h" #define H264_MAX_PICTURE_COUNT 36 @@ -649,136 +647,6 @@ static av_always_inline int get_chroma_qp(const PPS *pps, int t, int qscale) return pps->chroma_qp_table[t][qscale]; } -/** - * Get the predicted intra4x4 prediction mode. - */ -static av_always_inline int pred_intra_mode(const H264Context *h, - H264SliceContext *sl, int n) -{ - const int index8 = scan8[n]; - const int left = sl->intra4x4_pred_mode_cache[index8 - 1]; - const int top = sl->intra4x4_pred_mode_cache[index8 - 8]; - const int min = FFMIN(left, top); - - ff_tlog(h->avctx, "mode:%d %d min:%d\n", left, top, min); - - if (min < 0) - return DC_PRED; - else - return min; -} - -static av_always_inline void write_back_intra_pred_mode(const H264Context *h, - H264SliceContext *sl) -{ - int8_t *i4x4 = sl->intra4x4_pred_mode + h->mb2br_xy[sl->mb_xy]; - int8_t *i4x4_cache = sl->intra4x4_pred_mode_cache; - - AV_COPY32(i4x4, i4x4_cache + 4 + 8 * 4); - i4x4[4] = i4x4_cache[7 + 8 * 3]; - i4x4[5] = i4x4_cache[7 + 8 * 2]; - i4x4[6] = i4x4_cache[7 + 8 * 1]; -} - -static av_always_inline void write_back_non_zero_count(const H264Context *h, - H264SliceContext *sl) -{ - const int mb_xy = sl->mb_xy; - uint8_t *nnz = h->non_zero_count[mb_xy]; - uint8_t *nnz_cache = sl->non_zero_count_cache; - - AV_COPY32(&nnz[ 0], &nnz_cache[4 + 8 * 1]); - AV_COPY32(&nnz[ 4], &nnz_cache[4 + 8 * 2]); - AV_COPY32(&nnz[ 8], &nnz_cache[4 + 8 * 3]); - AV_COPY32(&nnz[12], &nnz_cache[4 + 8 * 4]); - AV_COPY32(&nnz[16], &nnz_cache[4 + 8 * 6]); - AV_COPY32(&nnz[20], &nnz_cache[4 + 8 * 7]); - AV_COPY32(&nnz[32], &nnz_cache[4 + 8 * 11]); - AV_COPY32(&nnz[36], &nnz_cache[4 + 8 * 12]); - - if (!h->chroma_y_shift) { - AV_COPY32(&nnz[24], &nnz_cache[4 + 8 * 8]); - AV_COPY32(&nnz[28], &nnz_cache[4 + 8 * 9]); - AV_COPY32(&nnz[40], &nnz_cache[4 + 8 * 13]); - AV_COPY32(&nnz[44], &nnz_cache[4 + 8 * 14]); - } -} - -static av_always_inline void write_back_motion_list(const H264Context *h, - H264SliceContext *sl, - int b_stride, - int b_xy, int b8_xy, - int mb_type, int list) -{ - int16_t(*mv_dst)[2] = &h->cur_pic.motion_val[list][b_xy]; - int16_t(*mv_src)[2] = &sl->mv_cache[list][scan8[0]]; - AV_COPY128(mv_dst + 0 * b_stride, mv_src + 8 * 0); - AV_COPY128(mv_dst + 1 * b_stride, mv_src + 8 * 1); - AV_COPY128(mv_dst + 2 * b_stride, mv_src + 8 * 2); - AV_COPY128(mv_dst + 3 * b_stride, mv_src + 8 * 3); - if (CABAC(h)) { - uint8_t (*mvd_dst)[2] = &sl->mvd_table[list][FMO ? 8 * sl->mb_xy - : h->mb2br_xy[sl->mb_xy]]; - uint8_t(*mvd_src)[2] = &sl->mvd_cache[list][scan8[0]]; - if (IS_SKIP(mb_type)) { - AV_ZERO128(mvd_dst); - } else { - AV_COPY64(mvd_dst, mvd_src + 8 * 3); - AV_COPY16(mvd_dst + 3 + 3, mvd_src + 3 + 8 * 0); - AV_COPY16(mvd_dst + 3 + 2, mvd_src + 3 + 8 * 1); - AV_COPY16(mvd_dst + 3 + 1, mvd_src + 3 + 8 * 2); - } - } - - { - int8_t *ref_index = &h->cur_pic.ref_index[list][b8_xy]; - int8_t *ref_cache = sl->ref_cache[list]; - ref_index[0 + 0 * 2] = ref_cache[scan8[0]]; - ref_index[1 + 0 * 2] = ref_cache[scan8[4]]; - ref_index[0 + 1 * 2] = ref_cache[scan8[8]]; - ref_index[1 + 1 * 2] = ref_cache[scan8[12]]; - } -} - -static av_always_inline void write_back_motion(const H264Context *h, - H264SliceContext *sl, - int mb_type) -{ - const int b_stride = h->b_stride; - const int b_xy = 4 * sl->mb_x + 4 * sl->mb_y * h->b_stride; // try mb2b(8)_xy - const int b8_xy = 4 * sl->mb_xy; - - if (USES_LIST(mb_type, 0)) { - write_back_motion_list(h, sl, b_stride, b_xy, b8_xy, mb_type, 0); - } else { - fill_rectangle(&h->cur_pic.ref_index[0][b8_xy], - 2, 2, 2, (uint8_t)LIST_NOT_USED, 1); - } - if (USES_LIST(mb_type, 1)) - write_back_motion_list(h, sl, b_stride, b_xy, b8_xy, mb_type, 1); - - if (sl->slice_type_nos == AV_PICTURE_TYPE_B && CABAC(h)) { - if (IS_8X8(mb_type)) { - uint8_t *direct_table = &h->direct_table[4 * sl->mb_xy]; - direct_table[1] = sl->sub_mb_type[1] >> 1; - direct_table[2] = sl->sub_mb_type[2] >> 1; - direct_table[3] = sl->sub_mb_type[3] >> 1; - } - } -} - -static av_always_inline int get_dct8x8_allowed(const H264Context *h, H264SliceContext *sl) -{ - if (h->ps.sps->direct_8x8_inference_flag) - return !(AV_RN64A(sl->sub_mb_type) & - ((MB_TYPE_16x8 | MB_TYPE_8x16 | MB_TYPE_8x8) * - 0x0001000100010001ULL)); - else - return !(AV_RN64A(sl->sub_mb_type) & - ((MB_TYPE_16x8 | MB_TYPE_8x16 | MB_TYPE_8x8 | MB_TYPE_DIRECT2) * - 0x0001000100010001ULL)); -} - int ff_h264_field_end(H264Context *h, H264SliceContext *sl, int in_setup); int ff_h264_ref_picture(H264Context *h, H264Picture *dst, H264Picture *src); diff --git a/libavcodec/h266_metadata_bsf.c b/libavcodec/h266_metadata_bsf.c new file mode 100644 index 00000000000..1f0f875cfee --- /dev/null +++ b/libavcodec/h266_metadata_bsf.c @@ -0,0 +1,150 @@ +/* + * 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 "libavutil/common.h" +#include "libavutil/opt.h" + +#include "bsf.h" +#include "bsf_internal.h" +#include "cbs.h" +#include "cbs_bsf.h" +#include "cbs_h266.h" +#include "vvc.h" + +#define IS_H266_SLICE(nut) (nut <= VVC_RASL_NUT || (nut >= VVC_IDR_W_RADL && nut <= VVC_GDR_NUT)) + +typedef struct H266MetadataContext { + CBSBSFContext common; + + H266RawAUD aud_nal; + + int aud; +} H266MetadataContext; + +static int h266_metadata_update_fragment(AVBSFContext *bsf, AVPacket *pkt, + CodedBitstreamFragment *pu) +{ + H266MetadataContext *ctx = bsf->priv_data; + int err, i; + + // If an AUD is present, it must be the first NAL unit. + if (pu->nb_units && pu->units[0].type == VVC_AUD_NUT) { + if (ctx->aud == BSF_ELEMENT_REMOVE) + ff_cbs_delete_unit(pu, 0); + } else if ( pkt && ctx->aud == BSF_ELEMENT_INSERT) { + const H266RawSlice *first_slice = NULL; + const H266RawPictureHeader *ph = NULL; + H266RawAUD *aud = &ctx->aud_nal; + int pic_type = 0, temporal_id = 8, layer_id = 0; + for (i = 0; i < pu->nb_units; i++) { + const H266RawNALUnitHeader *nal = pu->units[i].content; + if (!nal) + continue; + if (nal->nuh_temporal_id_plus1 < temporal_id + 1) + temporal_id = nal->nuh_temporal_id_plus1 - 1; + if ( nal->nal_unit_type == VVC_PH_NUT ) { + const H266RawPH *header = pu->units[i].content; + ph = &header->ph_picture_header; + } else if (IS_H266_SLICE(nal->nal_unit_type)) { + const H266RawSlice *slice = pu->units[i].content; + layer_id = nal->nuh_layer_id; + if (slice->header.sh_slice_type == VVC_SLICE_TYPE_B && + pic_type < 2) + pic_type = 2; + if (slice->header.sh_slice_type == VVC_SLICE_TYPE_P && + pic_type < 1) + pic_type = 1; + if (!first_slice) { + first_slice = slice; + if (first_slice->header. + sh_picture_header_in_slice_header_flag) + ph = &first_slice->header.sh_picture_header; + else if (!ph) + break; + } + } + } + if (!ph) { + av_log(bsf, AV_LOG_ERROR, "no avaliable picture header"); + return AVERROR_INVALIDDATA; + } + + aud->nal_unit_header = (H266RawNALUnitHeader) { + .nal_unit_type = VVC_AUD_NUT, + .nuh_layer_id = layer_id, + .nuh_temporal_id_plus1 = temporal_id + 1, + }; + aud->aud_pic_type = pic_type; + aud->aud_irap_or_gdr_flag = ph->ph_gdr_or_irap_pic_flag; + + err = ff_cbs_insert_unit_content(pu, 0, VVC_AUD_NUT, aud, NULL); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to insert AUD.\n"); + return err; + } + } + + /* TODO: implement more metadata parsing, like VUI, Levels etc. */ + //for (i = 0; i < pu->nb_units; i++) { + // if (pu->units[i].type == VVC_SPS_NUT) { + // } + //} + return 0; +} + +static const CBSBSFType h266_metadata_type = { + .codec_id = AV_CODEC_ID_VVC, + .fragment_name = "access unit", + .unit_name = "NAL unit", + .update_fragment = &h266_metadata_update_fragment, +}; + +static int h266_metadata_init(AVBSFContext *bsf) +{ + return ff_cbs_bsf_generic_init(bsf, &h266_metadata_type); +} + +#define OFFSET(x) offsetof(H266MetadataContext, x) +#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_BSF_PARAM) +static const AVOption h266_metadata_options[] = { + BSF_ELEMENT_OPTIONS_PIR("aud", "Access Unit Delimiter NAL units", + aud, FLAGS), + + { NULL } +}; + +static const AVClass h266_metadata_class = { + .class_name = "h266_metadata_bsf", + .item_name = av_default_item_name, + .option = h266_metadata_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static const enum AVCodecID h266_metadata_codec_ids[] = { + AV_CODEC_ID_VVC, AV_CODEC_ID_NONE, +}; + +const FFBitStreamFilter ff_vvc_metadata_bsf = { + .p.name = "vvc_metadata", + .p.codec_ids = h266_metadata_codec_ids, + .p.priv_class = &h266_metadata_class, + .priv_data_size = sizeof(H266MetadataContext), + .init = &h266_metadata_init, + .close = &ff_cbs_bsf_generic_close, + .filter = &ff_cbs_bsf_generic_filter, +}; diff --git a/libavcodec/hapdec.c b/libavcodec/hapdec.c index 3df69e63357..fee3c04d84b 100644 --- a/libavcodec/hapdec.c +++ b/libavcodec/hapdec.c @@ -328,7 +328,7 @@ static int hap_decode(AVCodecContext *avctx, AVFrame *frame, /* Frame is ready to be output */ frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; diff --git a/libavcodec/hapenc.c b/libavcodec/hapenc.c index a890dc64926..7de7358e3d9 100644 --- a/libavcodec/hapenc.c +++ b/libavcodec/hapenc.c @@ -351,7 +351,8 @@ const FFCodec ff_hap_encoder = { CODEC_LONG_NAME("Vidvox Hap"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_HAP, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SLICE_THREADS, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SLICE_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(HapContext), .p.priv_class = &hapenc_class, .init = hap_init, diff --git a/libavcodec/hdrdec.c b/libavcodec/hdrdec.c index 998227744b3..9b6395bb6d3 100644 --- a/libavcodec/hdrdec.c +++ b/libavcodec/hdrdec.c @@ -212,7 +212,7 @@ static int hdr_decode_frame(AVCodecContext *avctx, AVFrame *p, } } - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; p->pict_type = AV_PICTURE_TYPE_I; *got_frame = 1; diff --git a/libavcodec/hdrenc.c b/libavcodec/hdrenc.c index d8c3111c93f..40d283ee610 100644 --- a/libavcodec/hdrenc.c +++ b/libavcodec/hdrenc.c @@ -177,7 +177,8 @@ const FFCodec ff_hdr_encoder = { .priv_data_size = sizeof(HDREncContext), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_RADIANCE_HDR, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .init = hdr_encode_init, FF_CODEC_ENCODE_CB(hdr_encode_frame), .close = hdr_encode_close, diff --git a/libavcodec/hevc.h b/libavcodec/hevc.h index 1804755327e..6b454a75c18 100644 --- a/libavcodec/hevc.h +++ b/libavcodec/hevc.h @@ -154,6 +154,9 @@ enum { // get near that, though, so set a lower limit here with the maximum // possible value for 4K video (at most 135 16x16 Ctb rows). HEVC_MAX_ENTRY_POINT_OFFSETS = HEVC_MAX_TILE_COLUMNS * 135, + + // A.3.7: Screen content coding extensions + HEVC_MAX_PALETTE_PREDICTOR_SIZE = 128, }; diff --git a/libavcodec/hevc_parse.c b/libavcodec/hevc_parse.c index 29dfd479f38..7bc28fd081c 100644 --- a/libavcodec/hevc_parse.c +++ b/libavcodec/hevc_parse.c @@ -86,11 +86,13 @@ int ff_hevc_decode_extradata(const uint8_t *data, int size, HEVCParamSets *ps, bytestream2_init(&gb, data, size); - if (size > 3 && (data[0] || data[1] || data[2] > 1)) { - /* It seems the extradata is encoded as hvcC format. - * Temporarily, we support configurationVersion==0 until 14496-15 3rd - * is finalized. When finalized, configurationVersion will be 1 and we - * can recognize hvcC by checking if avctx->extradata[0]==1 or not. */ + /* data[0] == 1 is configurationVersion from 14496-15. + * data[0] == 0 is for backward compatibility predates the standard. + * + * Minimum number of bytes of hvcC with 0 numOfArrays is 23. + */ + if (size >= 23 && ((data[0] == 1) || (data[0] == 0 && (data[1] || data[2] > 1)))) { + /* It seems the extradata is encoded as hvcC format. */ int i, j, num_arrays, nal_len_size; *is_nalff = 1; diff --git a/libavcodec/hevc_ps.c b/libavcodec/hevc_ps.c index 5fe62ec35b7..1db2d3a2429 100644 --- a/libavcodec/hevc_ps.c +++ b/libavcodec/hevc_ps.c @@ -100,51 +100,50 @@ static void remove_vps(HEVCParamSets *s, int id) int ff_hevc_decode_short_term_rps(GetBitContext *gb, AVCodecContext *avctx, ShortTermRPS *rps, const HEVCSPS *sps, int is_slice_header) { - uint8_t rps_predict = 0; int delta_poc; int k0 = 0; int k = 0; int i; + rps->rps_predict = 0; + if (rps != sps->st_rps && sps->nb_st_rps) - rps_predict = get_bits1(gb); + rps->rps_predict = get_bits1(gb); - if (rps_predict) { + if (rps->rps_predict) { const ShortTermRPS *rps_ridx; int delta_rps; - unsigned abs_delta_rps; - uint8_t use_delta_flag = 0; - uint8_t delta_rps_sign; if (is_slice_header) { - unsigned int delta_idx = get_ue_golomb_long(gb) + 1; - if (delta_idx > sps->nb_st_rps) { + rps->delta_idx = get_ue_golomb_long(gb) + 1; + if (rps->delta_idx > sps->nb_st_rps) { av_log(avctx, AV_LOG_ERROR, "Invalid value of delta_idx in slice header RPS: %d > %d.\n", - delta_idx, sps->nb_st_rps); + rps->delta_idx, sps->nb_st_rps); return AVERROR_INVALIDDATA; } - rps_ridx = &sps->st_rps[sps->nb_st_rps - delta_idx]; + rps_ridx = &sps->st_rps[sps->nb_st_rps - rps->delta_idx]; rps->rps_idx_num_delta_pocs = rps_ridx->num_delta_pocs; } else rps_ridx = &sps->st_rps[rps - sps->st_rps - 1]; - delta_rps_sign = get_bits1(gb); - abs_delta_rps = get_ue_golomb_long(gb) + 1; - if (abs_delta_rps < 1 || abs_delta_rps > 32768) { + rps->delta_rps_sign = get_bits1(gb); + rps->abs_delta_rps = get_ue_golomb_long(gb) + 1; + if (rps->abs_delta_rps > 32768) { av_log(avctx, AV_LOG_ERROR, "Invalid value of abs_delta_rps: %d\n", - abs_delta_rps); + rps->abs_delta_rps); return AVERROR_INVALIDDATA; } - delta_rps = (1 - (delta_rps_sign << 1)) * abs_delta_rps; + delta_rps = (1 - (rps->delta_rps_sign << 1)) * rps->abs_delta_rps; for (i = 0; i <= rps_ridx->num_delta_pocs; i++) { int used = rps->used[k] = get_bits1(gb); + rps->use_delta_flag = 0; if (!used) - use_delta_flag = get_bits1(gb); + rps->use_delta_flag = get_bits1(gb); - if (used || use_delta_flag) { + if (used || rps->use_delta_flag) { if (i < rps_ridx->num_delta_pocs) delta_poc = delta_rps + rps_ridx->delta_poc[i]; else @@ -210,7 +209,7 @@ int ff_hevc_decode_short_term_rps(GetBitContext *gb, AVCodecContext *avctx, if (rps->num_delta_pocs) { prev = 0; for (i = 0; i < rps->num_negative_pics; i++) { - delta_poc = get_ue_golomb_long(gb) + 1; + delta_poc = rps->delta_poc_s0[i] = get_ue_golomb_long(gb) + 1; if (delta_poc < 1 || delta_poc > 32768) { av_log(avctx, AV_LOG_ERROR, "Invalid value of delta_poc: %d\n", @@ -223,7 +222,7 @@ int ff_hevc_decode_short_term_rps(GetBitContext *gb, AVCodecContext *avctx, } prev = 0; for (i = 0; i < nb_positive_pics; i++) { - delta_poc = get_ue_golomb_long(gb) + 1; + delta_poc = rps->delta_poc_s1[i] = get_ue_golomb_long(gb) + 1; if (delta_poc < 1 || delta_poc > 32768) { av_log(avctx, AV_LOG_ERROR, "Invalid value of delta_poc: %d\n", @@ -259,6 +258,8 @@ static int decode_profile_tier_level(GetBitContext *gb, AVCodecContext *avctx, av_log(avctx, AV_LOG_DEBUG, "Main Still Picture profile bitstream\n"); else if (ptl->profile_idc == FF_PROFILE_HEVC_REXT) av_log(avctx, AV_LOG_DEBUG, "Range Extension profile bitstream\n"); + else if (ptl->profile_idc == FF_PROFILE_HEVC_SCC) + av_log(avctx, AV_LOG_DEBUG, "Screen Content Coding Extension profile bitstream\n"); else av_log(avctx, AV_LOG_WARNING, "Unknown HEVC profile: %d\n", ptl->profile_idc); @@ -355,81 +356,84 @@ static int parse_ptl(GetBitContext *gb, AVCodecContext *avctx, } static void decode_sublayer_hrd(GetBitContext *gb, unsigned int nb_cpb, - int subpic_params_present) + HEVCSublayerHdrParams *par, int subpic_params_present) { int i; for (i = 0; i < nb_cpb; i++) { - get_ue_golomb_long(gb); // bit_rate_value_minus1 - get_ue_golomb_long(gb); // cpb_size_value_minus1 + par->bit_rate_value_minus1[i] = get_ue_golomb_long(gb); + par->cpb_size_value_minus1[i] = get_ue_golomb_long(gb); if (subpic_params_present) { - get_ue_golomb_long(gb); // cpb_size_du_value_minus1 - get_ue_golomb_long(gb); // bit_rate_du_value_minus1 + par->cpb_size_du_value_minus1[i] = get_ue_golomb_long(gb); + par->bit_rate_du_value_minus1[i] = get_ue_golomb_long(gb); } - skip_bits1(gb); // cbr_flag + + par->cbr_flag = get_bits1(gb); } } static int decode_hrd(GetBitContext *gb, int common_inf_present, - int max_sublayers) + HEVCHdrParams *hdr, int max_sublayers) { - int nal_params_present = 0, vcl_params_present = 0; - int subpic_params_present = 0; - int i; - if (common_inf_present) { - nal_params_present = get_bits1(gb); - vcl_params_present = get_bits1(gb); - - if (nal_params_present || vcl_params_present) { - subpic_params_present = get_bits1(gb); - - if (subpic_params_present) { - skip_bits(gb, 8); // tick_divisor_minus2 - skip_bits(gb, 5); // du_cpb_removal_delay_increment_length_minus1 - skip_bits(gb, 1); // sub_pic_cpb_params_in_pic_timing_sei_flag - skip_bits(gb, 5); // dpb_output_delay_du_length_minus1 + hdr->flags.nal_hrd_parameters_present_flag = get_bits1(gb); + hdr->flags.vcl_hrd_parameters_present_flag = get_bits1(gb); + + if (hdr->flags.nal_hrd_parameters_present_flag || + hdr->flags.vcl_hrd_parameters_present_flag) { + hdr->flags.sub_pic_hrd_params_present_flag = get_bits1(gb); + + if (hdr->flags.sub_pic_hrd_params_present_flag) { + hdr->tick_divisor_minus2 = get_bits(gb, 8); + hdr->du_cpb_removal_delay_increment_length_minus1 = get_bits(gb, 5); + hdr->flags.sub_pic_cpb_params_in_pic_timing_sei_flag = get_bits1(gb); + hdr->dpb_output_delay_du_length_minus1 = get_bits(gb, 5); } - skip_bits(gb, 4); // bit_rate_scale - skip_bits(gb, 4); // cpb_size_scale + hdr->bit_rate_scale = get_bits(gb, 4); + hdr->cpb_size_scale = get_bits(gb, 4); - if (subpic_params_present) - skip_bits(gb, 4); // cpb_size_du_scale + if (hdr->flags.sub_pic_hrd_params_present_flag) + hdr->cpb_size_du_scale = get_bits(gb, 4); - skip_bits(gb, 5); // initial_cpb_removal_delay_length_minus1 - skip_bits(gb, 5); // au_cpb_removal_delay_length_minus1 - skip_bits(gb, 5); // dpb_output_delay_length_minus1 + hdr->initial_cpb_removal_delay_length_minus1 = get_bits(gb, 5); + hdr->au_cpb_removal_delay_length_minus1 = get_bits(gb, 5); + hdr->dpb_output_delay_length_minus1 = get_bits(gb, 5); } } - for (i = 0; i < max_sublayers; i++) { - int low_delay = 0; - unsigned int nb_cpb = 1; - int fixed_rate = get_bits1(gb); + for (int i = 0; i < max_sublayers; i++) { + hdr->flags.fixed_pic_rate_general_flag = get_bits1(gb); - if (!fixed_rate) - fixed_rate = get_bits1(gb); + hdr->cpb_cnt_minus1[i] = 1; - if (fixed_rate) - get_ue_golomb_long(gb); // elemental_duration_in_tc_minus1 + if (!hdr->flags.fixed_pic_rate_general_flag) + hdr->flags.fixed_pic_rate_within_cvs_flag = get_bits1(gb); + + if (hdr->flags.fixed_pic_rate_within_cvs_flag) + hdr->elemental_duration_in_tc_minus1[i] = get_ue_golomb_long(gb); else - low_delay = get_bits1(gb); + hdr->flags.low_delay_hrd_flag = get_bits1(gb); - if (!low_delay) { - nb_cpb = get_ue_golomb_long(gb) + 1; - if (nb_cpb < 1 || nb_cpb > 32) { - av_log(NULL, AV_LOG_ERROR, "nb_cpb %d invalid\n", nb_cpb); + if (!hdr->flags.low_delay_hrd_flag) { + hdr->cpb_cnt_minus1[i] = get_ue_golomb_long(gb); + if (hdr->cpb_cnt_minus1[i] > 31) { + av_log(NULL, AV_LOG_ERROR, "nb_cpb %d invalid\n", + hdr->cpb_cnt_minus1[i]); return AVERROR_INVALIDDATA; } } - if (nal_params_present) - decode_sublayer_hrd(gb, nb_cpb, subpic_params_present); - if (vcl_params_present) - decode_sublayer_hrd(gb, nb_cpb, subpic_params_present); + if (hdr->flags.nal_hrd_parameters_present_flag) + decode_sublayer_hrd(gb, hdr->cpb_cnt_minus1[i], &hdr->nal_params[i], + hdr->flags.sub_pic_hrd_params_present_flag); + + if (hdr->flags.vcl_hrd_parameters_present_flag) + decode_sublayer_hrd(gb, hdr->cpb_cnt_minus1[i], &hdr->vcl_params[i], + hdr->flags.sub_pic_hrd_params_present_flag); } + return 0; } @@ -459,7 +463,7 @@ int ff_hevc_decode_nal_vps(GetBitContext *gb, AVCodecContext *avctx, } memcpy(vps->data, gb->buffer, vps->data_size); - vps_id = get_bits(gb, 4); + vps_id = vps->vps_id = get_bits(gb, 4); if (get_bits(gb, 2) != 3) { // vps_reserved_three_2bits av_log(avctx, AV_LOG_ERROR, "vps_reserved_three_2bits is not three\n"); @@ -536,7 +540,8 @@ int ff_hevc_decode_nal_vps(GetBitContext *gb, AVCodecContext *avctx, get_ue_golomb_long(gb); // hrd_layer_set_idx if (i) common_inf_present = get_bits1(gb); - decode_hrd(gb, common_inf_present, vps->vps_max_sub_layers); + decode_hrd(gb, common_inf_present, &vps->hdr[i], + vps->vps_max_sub_layers); } } get_bits1(gb); /* vps_extension_flag */ @@ -655,7 +660,7 @@ static void decode_vui(GetBitContext *gb, AVCodecContext *avctx, vui->vui_num_ticks_poc_diff_one_minus1 = get_ue_golomb_long(gb); vui->vui_hrd_parameters_present_flag = get_bits1(gb); if (vui->vui_hrd_parameters_present_flag) - decode_hrd(gb, 1, sps->max_sub_layers); + decode_hrd(gb, 1, &sps->hdr, sps->max_sub_layers); } vui->bitstream_restriction_flag = get_bits1(gb); @@ -720,7 +725,8 @@ static void set_default_scaling_list_data(ScalingList *sl) memcpy(sl->sl[3][5], default_scaling_list_inter, 64); } -static int scaling_list_data(GetBitContext *gb, AVCodecContext *avctx, ScalingList *sl, HEVCSPS *sps) +static int scaling_list_data(GetBitContext *gb, AVCodecContext *avctx, + ScalingList *sl, const HEVCSPS *sps) { uint8_t scaling_list_pred_mode_flag; uint8_t scaling_list_dc_coef[2][6]; @@ -850,8 +856,7 @@ int ff_hevc_parse_sps(HEVCSPS *sps, GetBitContext *gb, unsigned int *sps_id, { HEVCWindow *ow; int ret = 0; - int log2_diff_max_min_transform_block_size; - int bit_depth_chroma, start, vui_present, sublayer_ordering_info; + int bit_depth_chroma, start, num_comps; int i; // Coded parameters @@ -900,7 +905,8 @@ int ff_hevc_parse_sps(HEVCSPS *sps, GetBitContext *gb, unsigned int *sps_id, sps->height, 0, avctx)) < 0) return ret; - if (get_bits1(gb)) { // pic_conformance_flag + sps->conformance_window_flag = get_bits1(gb); + if (sps->conformance_window_flag) { int vert_mult = hevc_sub_height_c[sps->chroma_format_idc]; int horiz_mult = hevc_sub_width_c[sps->chroma_format_idc]; sps->pic_conf_win.left_offset = get_ue_golomb_long(gb) * horiz_mult; @@ -925,8 +931,18 @@ int ff_hevc_parse_sps(HEVCSPS *sps, GetBitContext *gb, unsigned int *sps_id, sps->output_window = sps->pic_conf_win; } - sps->bit_depth = get_ue_golomb_long(gb) + 8; - bit_depth_chroma = get_ue_golomb_long(gb) + 8; + sps->bit_depth = get_ue_golomb_31(gb) + 8; + if (sps->bit_depth > 16) { + av_log(avctx, AV_LOG_ERROR, "Luma bit depth (%d) is out of range\n", + sps->bit_depth); + return AVERROR_INVALIDDATA; + } + bit_depth_chroma = get_ue_golomb_31(gb) + 8; + if (bit_depth_chroma > 16) { + av_log(avctx, AV_LOG_ERROR, "Chroma bit depth (%d) is out of range\n", + bit_depth_chroma); + return AVERROR_INVALIDDATA; + } if (sps->chroma_format_idc && bit_depth_chroma != sps->bit_depth) { av_log(avctx, AV_LOG_ERROR, "Luma bit depth (%d) is different from chroma bit depth (%d), " @@ -947,8 +963,8 @@ int ff_hevc_parse_sps(HEVCSPS *sps, GetBitContext *gb, unsigned int *sps_id, return AVERROR_INVALIDDATA; } - sublayer_ordering_info = get_bits1(gb); - start = sublayer_ordering_info ? 0 : sps->max_sub_layers - 1; + sps->sublayer_ordering_info_flag = get_bits1(gb); + start = sps->sublayer_ordering_info_flag ? 0 : sps->max_sub_layers - 1; for (i = start; i < sps->max_sub_layers; i++) { sps->temporal_layer[i].max_dec_pic_buffering = get_ue_golomb_long(gb) + 1; sps->temporal_layer[i].num_reorder_pics = get_ue_golomb_long(gb); @@ -969,7 +985,7 @@ int ff_hevc_parse_sps(HEVCSPS *sps, GetBitContext *gb, unsigned int *sps_id, } } - if (!sublayer_ordering_info) { + if (!sps->sublayer_ordering_info_flag) { for (i = 0; i < start; i++) { sps->temporal_layer[i].max_dec_pic_buffering = sps->temporal_layer[start].max_dec_pic_buffering; sps->temporal_layer[i].num_reorder_pics = sps->temporal_layer[start].num_reorder_pics; @@ -977,12 +993,12 @@ int ff_hevc_parse_sps(HEVCSPS *sps, GetBitContext *gb, unsigned int *sps_id, } } - sps->log2_min_cb_size = get_ue_golomb_long(gb) + 3; - sps->log2_diff_max_min_coding_block_size = get_ue_golomb_long(gb); - sps->log2_min_tb_size = get_ue_golomb_long(gb) + 2; - log2_diff_max_min_transform_block_size = get_ue_golomb_long(gb); - sps->log2_max_trafo_size = log2_diff_max_min_transform_block_size + - sps->log2_min_tb_size; + sps->log2_min_cb_size = get_ue_golomb_long(gb) + 3; + sps->log2_diff_max_min_coding_block_size = get_ue_golomb_long(gb); + sps->log2_min_tb_size = get_ue_golomb_long(gb) + 2; + sps->log2_diff_max_min_transform_block_size = get_ue_golomb_long(gb); + sps->log2_max_trafo_size = sps->log2_diff_max_min_transform_block_size + + sps->log2_min_tb_size; if (sps->log2_min_cb_size < 3 || sps->log2_min_cb_size > 30) { av_log(avctx, AV_LOG_ERROR, "Invalid value %d for log2_min_cb_size", sps->log2_min_cb_size); @@ -999,8 +1015,9 @@ int ff_hevc_parse_sps(HEVCSPS *sps, GetBitContext *gb, unsigned int *sps_id, return AVERROR_INVALIDDATA; } - if (log2_diff_max_min_transform_block_size < 0 || log2_diff_max_min_transform_block_size > 30) { - av_log(avctx, AV_LOG_ERROR, "Invalid value %d for log2_diff_max_min_transform_block_size", log2_diff_max_min_transform_block_size); + if (sps->log2_diff_max_min_transform_block_size > 30) { + av_log(avctx, AV_LOG_ERROR, "Invalid value %d for log2_diff_max_min_transform_block_size", + sps->log2_diff_max_min_transform_block_size); return AVERROR_INVALIDDATA; } @@ -1067,13 +1084,18 @@ int ff_hevc_parse_sps(HEVCSPS *sps, GetBitContext *gb, unsigned int *sps_id, sps->sps_temporal_mvp_enabled_flag = get_bits1(gb); sps->sps_strong_intra_smoothing_enable_flag = get_bits1(gb); sps->vui.common.sar = (AVRational){0, 1}; - vui_present = get_bits1(gb); - if (vui_present) + sps->vui_present = get_bits1(gb); + if (sps->vui_present) decode_vui(gb, avctx, apply_defdispwin, sps); - if (get_bits1(gb)) { // sps_extension_flag - sps->sps_range_extension_flag = get_bits1(gb); - skip_bits(gb, 7); //sps_extension_7bits = get_bits(gb, 7); + sps->sps_extension_present_flag = get_bits1(gb); + if (sps->sps_extension_present_flag) { + sps->sps_range_extension_flag = get_bits1(gb); + sps->sps_multilayer_extension_flag = get_bits1(gb); + sps->sps_3d_extension_flag = get_bits1(gb); + sps->sps_scc_extension_flag = get_bits1(gb); + skip_bits(gb, 4); // sps_extension_4bits + if (sps->sps_range_extension_flag) { sps->transform_skip_rotation_enabled_flag = get_bits1(gb); sps->transform_skip_context_enabled_flag = get_bits1(gb); @@ -1099,6 +1121,64 @@ int ff_hevc_parse_sps(HEVCSPS *sps, GetBitContext *gb, unsigned int *sps_id, av_log(avctx, AV_LOG_WARNING, "cabac_bypass_alignment_enabled_flag not yet implemented\n"); } + + if (sps->sps_multilayer_extension_flag) { + skip_bits1(gb); // inter_view_mv_vert_constraint_flag + av_log(avctx, AV_LOG_WARNING, + "sps_multilayer_extension_flag not yet implemented\n"); + } + + if (sps->sps_3d_extension_flag) { + for (i = 0; i <= 1; i++) { + skip_bits1(gb); // iv_di_mc_enabled_flag + skip_bits1(gb); // iv_mv_scal_enabled_flag + if (i == 0) { + get_ue_golomb_long(gb); // log2_ivmc_sub_pb_size_minus3 + skip_bits1(gb); // iv_res_pred_enabled_flag + skip_bits1(gb); // depth_ref_enabled_flag + skip_bits1(gb); // vsp_mc_enabled_flag + skip_bits1(gb); // dbbp_enabled_flag + } else { + skip_bits1(gb); // tex_mc_enabled_flag + get_ue_golomb_long(gb); // log2_ivmc_sub_pb_size_minus3 + skip_bits1(gb); // intra_contour_enabled_flag + skip_bits1(gb); // intra_dc_only_wedge_enabled_flag + skip_bits1(gb); // cqt_cu_part_pred_enabled_flag + skip_bits1(gb); // inter_dc_only_enabled_flag + skip_bits1(gb); // skip_intra_enabled_flag + } + } + av_log(avctx, AV_LOG_WARNING, + "sps_3d_extension_flag not yet implemented\n"); + } + + if (sps->sps_scc_extension_flag) { + sps->sps_curr_pic_ref_enabled_flag = get_bits1(gb); + sps->palette_mode_enabled_flag = get_bits1(gb); + if (sps->palette_mode_enabled_flag) { + sps->palette_max_size = get_ue_golomb(gb); + sps->delta_palette_max_predictor_size = get_ue_golomb(gb); + sps->sps_palette_predictor_initializers_present_flag = get_bits1(gb); + + if (sps->sps_palette_predictor_initializers_present_flag) { + sps->sps_num_palette_predictor_initializers = get_ue_golomb(gb) + 1; + if (sps->sps_num_palette_predictor_initializers > HEVC_MAX_PALETTE_PREDICTOR_SIZE) { + av_log(avctx, AV_LOG_ERROR, + "sps_num_palette_predictor_initializers out of range: %u\n", + sps->sps_num_palette_predictor_initializers); + return AVERROR_INVALIDDATA; + } + num_comps = !sps->chroma_format_idc ? 1 : 3; + for (int comp = 0; comp < num_comps; comp++) { + int bit_depth = !comp ? sps->bit_depth : sps->bit_depth_chroma; + for (i = 0; i < sps->sps_num_palette_predictor_initializers; i++) + sps->sps_palette_predictor_initializer[comp][i] = get_bits(gb, bit_depth); + } + } + } + sps->motion_vector_resolution_control_idc = get_bits(gb, 2); + sps->intra_boundary_filtering_disabled_flag = get_bits1(gb); + } } if (apply_defdispwin) { sps->output_window.left_offset += sps->vui.def_disp_win.left_offset; @@ -1263,38 +1343,201 @@ static void hevc_pps_free(void *opaque, uint8_t *data) av_freep(&pps); } -static int pps_range_extensions(GetBitContext *gb, AVCodecContext *avctx, - HEVCPPS *pps, HEVCSPS *sps) { - int i; +static void colour_mapping_octants(GetBitContext *gb, HEVCPPS *pps, int inp_depth, + int idx_y, int idx_cb, int idx_cr, int inp_length) +{ + unsigned int split_octant_flag, part_num_y, coded_res_flag, res_coeff_q, res_coeff_r; + int cm_res_bits; + + part_num_y = 1 << pps->cm_y_part_num_log2; + + split_octant_flag = inp_depth < pps->cm_octant_depth ? get_bits1(gb) : 0; + + if (split_octant_flag) + for (int k = 0; k < 2; k++) + for (int m = 0; m < 2; m++) + for (int n = 0; n < 2; n++) + colour_mapping_octants(gb, pps, inp_depth + 1, + idx_y + part_num_y * k * inp_length / 2, + idx_cb + m * inp_length / 2, + idx_cr + n * inp_length / 2, + inp_length / 2); + else + for (int i = 0; i < part_num_y; i++) { + for (int j = 0; j < 4; j++) { + coded_res_flag = get_bits1(gb); + if (coded_res_flag) + for (int c = 0; c < 3; c++) { + res_coeff_q = get_ue_golomb_long(gb); + cm_res_bits = FFMAX(0, 10 + pps->luma_bit_depth_cm_input - + pps->luma_bit_depth_cm_output - + pps->cm_res_quant_bits - pps->cm_delta_flc_bits); + res_coeff_r = cm_res_bits ? get_bits(gb, cm_res_bits) : 0; + if (res_coeff_q || res_coeff_r) + skip_bits1(gb); + } + } + } +} + +static int colour_mapping_table(GetBitContext *gb, AVCodecContext *avctx, HEVCPPS *pps) +{ + pps->num_cm_ref_layers = get_ue_golomb(gb) + 1; + if (pps->num_cm_ref_layers > 62) { + av_log(avctx, AV_LOG_ERROR, + "num_cm_ref_layers_minus1 shall be in the range [0, 61].\n"); + return AVERROR_INVALIDDATA; + } + for (int i = 0; i < pps->num_cm_ref_layers; i++) + pps->cm_ref_layer_id[i] = get_bits(gb, 6); + + pps->cm_octant_depth = get_bits(gb, 2); + pps->cm_y_part_num_log2 = get_bits(gb, 2); + + pps->luma_bit_depth_cm_input = get_ue_golomb(gb) + 8; + pps->chroma_bit_depth_cm_input = get_ue_golomb(gb) + 8; + pps->luma_bit_depth_cm_output = get_ue_golomb(gb) + 8; + pps->chroma_bit_depth_cm_output = get_ue_golomb(gb) + 8; + + pps->cm_res_quant_bits = get_bits(gb, 2); + pps->cm_delta_flc_bits = get_bits(gb, 2) + 1; + + if (pps->cm_octant_depth == 1) { + pps->cm_adapt_threshold_u_delta = get_se_golomb_long(gb); + pps->cm_adapt_threshold_v_delta = get_se_golomb_long(gb); + } + + colour_mapping_octants(gb, pps, 0, 0, 0, 0, 1 << pps->cm_octant_depth); + + return 0; +} + +static int pps_multilayer_extension(GetBitContext *gb, AVCodecContext *avctx, + HEVCPPS *pps, const HEVCSPS *sps, const HEVCVPS *vps) +{ + pps->poc_reset_info_present_flag = get_bits1(gb); + pps->pps_infer_scaling_list_flag = get_bits1(gb); + if (pps->pps_infer_scaling_list_flag) + pps->pps_scaling_list_ref_layer_id = get_bits(gb, 6); + + pps->num_ref_loc_offsets = get_ue_golomb(gb); + if (pps->num_ref_loc_offsets > vps->vps_max_layers - 1) + return AVERROR_INVALIDDATA; + + for (int i = 0; i < pps->num_ref_loc_offsets; i++) { + pps->ref_loc_offset_layer_id[i] = get_bits(gb, 6); + pps->scaled_ref_layer_offset_present_flag[i] = get_bits1(gb); + if (pps->scaled_ref_layer_offset_present_flag[i]) { + pps->scaled_ref_layer_left_offset[pps->ref_loc_offset_layer_id[i]] = get_se_golomb_long(gb); + pps->scaled_ref_layer_top_offset[pps->ref_loc_offset_layer_id[i]] = get_se_golomb_long(gb); + pps->scaled_ref_layer_right_offset[pps->ref_loc_offset_layer_id[i]] = get_se_golomb_long(gb); + pps->scaled_ref_layer_bottom_offset[pps->ref_loc_offset_layer_id[i]] = get_se_golomb_long(gb); + } + + pps->ref_region_offset_present_flag[i] = get_bits1(gb); + if (pps->ref_region_offset_present_flag[i]) { + pps->ref_region_left_offset[pps->ref_loc_offset_layer_id[i]] = get_se_golomb_long(gb); + pps->ref_region_top_offset[pps->ref_loc_offset_layer_id[i]] = get_se_golomb_long(gb); + pps->ref_region_right_offset[pps->ref_loc_offset_layer_id[i]] = get_se_golomb_long(gb); + pps->ref_region_bottom_offset[pps->ref_loc_offset_layer_id[i]] = get_se_golomb_long(gb); + } + + pps->resample_phase_set_present_flag[i] = get_bits1(gb); + if (pps->resample_phase_set_present_flag[i]) { + pps->phase_hor_luma[pps->ref_loc_offset_layer_id[i]] = get_ue_golomb_31(gb); + pps->phase_ver_luma[pps->ref_loc_offset_layer_id[i]] = get_ue_golomb_31(gb); + pps->phase_hor_chroma[pps->ref_loc_offset_layer_id[i]] = get_ue_golomb(gb) - 8; + pps->phase_ver_chroma[pps->ref_loc_offset_layer_id[i]] = get_ue_golomb(gb) - 8; + } + } + + pps->colour_mapping_enabled_flag = get_bits1(gb); + if (pps->colour_mapping_enabled_flag) { + int ret = colour_mapping_table(gb, avctx, pps); + if (ret < 0) + return ret; + } + + return 0; +} + +static void delta_dlt(GetBitContext *gb, HEVCPPS *pps) +{ + unsigned int num_val_delta_dlt, max_diff = 0; + int min_diff_minus1 = -1; + unsigned int len; + + num_val_delta_dlt = get_bits(gb, pps->pps_bit_depth_for_depth_layers_minus8 + 8); + if (num_val_delta_dlt) { + if (num_val_delta_dlt > 1) + max_diff = get_bits(gb, pps->pps_bit_depth_for_depth_layers_minus8 + 8); + if (num_val_delta_dlt > 2 && max_diff) { + len = av_log2(max_diff) + 1; + min_diff_minus1 = get_bits(gb, len); + } + if (max_diff > (min_diff_minus1 + 1)) + for (int k = 1; k < num_val_delta_dlt; k++) { + len = av_log2(max_diff - (min_diff_minus1 + 1)) + 1; + skip_bits(gb, len); // delta_val_diff_minus_min + } + } +} +static int pps_3d_extension(GetBitContext *gb, AVCodecContext *avctx, + HEVCPPS *pps, const HEVCSPS *sps) +{ + unsigned int pps_depth_layers_minus1; + + if (get_bits1(gb)) { // dlts_present_flag + pps_depth_layers_minus1 = get_bits(gb, 6); + pps->pps_bit_depth_for_depth_layers_minus8 = get_bits(gb, 4); + for (int i = 0; i <= pps_depth_layers_minus1; i++) { + if (get_bits1(gb)) { // dlt_flag[i] + if (!get_bits1(gb)) { // dlt_pred_flag[i] + if (get_bits1(gb)) { // dlt_val_flags_present_flag[i] + for (int j = 0; j <= ((1 << (pps->pps_bit_depth_for_depth_layers_minus8 + 8)) - 1); j++) + skip_bits1(gb); // dlt_value_flag[i][j] + } else + delta_dlt(gb, pps); + } + } + } + } + + return 0; +} + +static int pps_range_extensions(GetBitContext *gb, AVCodecContext *avctx, + HEVCPPS *pps, const HEVCSPS *sps) +{ if (pps->transform_skip_enabled_flag) { - pps->log2_max_transform_skip_block_size = get_ue_golomb_long(gb) + 2; + pps->log2_max_transform_skip_block_size = get_ue_golomb_31(gb) + 2; } pps->cross_component_prediction_enabled_flag = get_bits1(gb); pps->chroma_qp_offset_list_enabled_flag = get_bits1(gb); if (pps->chroma_qp_offset_list_enabled_flag) { - pps->diff_cu_chroma_qp_offset_depth = get_ue_golomb_long(gb); - pps->chroma_qp_offset_list_len_minus1 = get_ue_golomb_long(gb); + pps->diff_cu_chroma_qp_offset_depth = get_ue_golomb_31(gb); + pps->chroma_qp_offset_list_len_minus1 = get_ue_golomb_31(gb); if (pps->chroma_qp_offset_list_len_minus1 > 5) { av_log(avctx, AV_LOG_ERROR, "chroma_qp_offset_list_len_minus1 shall be in the range [0, 5].\n"); return AVERROR_INVALIDDATA; } - for (i = 0; i <= pps->chroma_qp_offset_list_len_minus1; i++) { - pps->cb_qp_offset_list[i] = get_se_golomb_long(gb); + for (int i = 0; i <= pps->chroma_qp_offset_list_len_minus1; i++) { + pps->cb_qp_offset_list[i] = get_se_golomb(gb); if (pps->cb_qp_offset_list[i]) { av_log(avctx, AV_LOG_WARNING, "cb_qp_offset_list not tested yet.\n"); } - pps->cr_qp_offset_list[i] = get_se_golomb_long(gb); + pps->cr_qp_offset_list[i] = get_se_golomb(gb); if (pps->cr_qp_offset_list[i]) { av_log(avctx, AV_LOG_WARNING, "cb_qp_offset_list not tested yet.\n"); } } } - pps->log2_sao_offset_scale_luma = get_ue_golomb_long(gb); - pps->log2_sao_offset_scale_chroma = get_ue_golomb_long(gb); + pps->log2_sao_offset_scale_luma = get_ue_golomb_31(gb); + pps->log2_sao_offset_scale_chroma = get_ue_golomb_31(gb); if ( pps->log2_sao_offset_scale_luma > FFMAX(sps->bit_depth - 10, 0) || pps->log2_sao_offset_scale_chroma > FFMAX(sps->bit_depth_chroma - 10, 0) @@ -1304,8 +1547,62 @@ static int pps_range_extensions(GetBitContext *gb, AVCodecContext *avctx, return(0); } +static int pps_scc_extension(GetBitContext *gb, AVCodecContext *avctx, + HEVCPPS *pps, const HEVCSPS *sps) +{ + int num_comps, ret; + + pps->pps_curr_pic_ref_enabled_flag = get_bits1(gb); + if (pps->residual_adaptive_colour_transform_enabled_flag = get_bits1(gb)) { + pps->pps_slice_act_qp_offsets_present_flag = get_bits1(gb); + pps->pps_act_y_qp_offset = get_se_golomb(gb) - 5; + pps->pps_act_cb_qp_offset = get_se_golomb(gb) - 5; + pps->pps_act_cr_qp_offset = get_se_golomb(gb) - 3; + +#define CHECK_QP_OFFSET(name) (pps->pps_act_ ## name ## _qp_offset <= -12 || \ + pps->pps_act_ ## name ## _qp_offset >= 12) + ret = CHECK_QP_OFFSET(y) || CHECK_QP_OFFSET(cb) || CHECK_QP_OFFSET(cr); +#undef CHECK_QP_OFFSET + if (ret) { + av_log(avctx, AV_LOG_ERROR, + "PpsActQpOffsetY/Cb/Cr shall be in the range of [-12, 12].\n"); + return AVERROR_INVALIDDATA; + } + } + + if (pps->pps_palette_predictor_initializers_present_flag = get_bits1(gb)) { + pps->pps_num_palette_predictor_initializers = get_ue_golomb(gb); + if (pps->pps_num_palette_predictor_initializers > 0) { + if (pps->pps_num_palette_predictor_initializers > HEVC_MAX_PALETTE_PREDICTOR_SIZE) { + av_log(avctx, AV_LOG_ERROR, + "pps_num_palette_predictor_initializers out of range: %u\n", + pps->pps_num_palette_predictor_initializers); + return AVERROR_INVALIDDATA; + } + pps->monochrome_palette_flag = get_bits1(gb); + pps->luma_bit_depth_entry = get_ue_golomb_31(gb) + 8; + if (pps->luma_bit_depth_entry != sps->bit_depth) + return AVERROR_INVALIDDATA; + if (!pps->monochrome_palette_flag) { + pps->chroma_bit_depth_entry = get_ue_golomb_31(gb) + 8; + if (pps->chroma_bit_depth_entry != sps->bit_depth_chroma) + return AVERROR_INVALIDDATA; + } + + num_comps = pps->monochrome_palette_flag ? 1 : 3; + for (int comp = 0; comp < num_comps; comp++) { + int bit_depth = !comp ? pps->luma_bit_depth_entry : pps->chroma_bit_depth_entry; + for (int i = 0; i < pps->pps_num_palette_predictor_initializers; i++) + pps->pps_palette_predictor_initializer[comp][i] = get_bits(gb, bit_depth); + } + } + } + + return 0; +} + static inline int setup_pps(AVCodecContext *avctx, GetBitContext *gb, - HEVCPPS *pps, HEVCSPS *sps) + HEVCPPS *pps, const HEVCSPS *sps) { int log2_diff; int pic_area_in_ctbs; @@ -1439,7 +1736,8 @@ static inline int setup_pps(AVCodecContext *avctx, GetBitContext *gb, int ff_hevc_decode_nal_pps(GetBitContext *gb, AVCodecContext *avctx, HEVCParamSets *ps) { - HEVCSPS *sps = NULL; + const HEVCSPS *sps = NULL; + const HEVCVPS *vps = NULL; int i, ret = 0; unsigned int pps_id = 0; ptrdiff_t nal_size; @@ -1482,7 +1780,7 @@ int ff_hevc_decode_nal_pps(GetBitContext *gb, AVCodecContext *avctx, pps->log2_max_transform_skip_block_size = 2; // Coded parameters - pps_id = get_ue_golomb_long(gb); + pps_id = pps->pps_id = get_ue_golomb_long(gb); if (pps_id >= HEVC_MAX_PPS_COUNT) { av_log(avctx, AV_LOG_ERROR, "PPS id out of range: %d\n", pps_id); ret = AVERROR_INVALIDDATA; @@ -1500,6 +1798,7 @@ int ff_hevc_decode_nal_pps(GetBitContext *gb, AVCodecContext *avctx, goto err; } sps = (HEVCSPS *)ps->sps_list[pps->sps_id]->data; + vps = (HEVCVPS *)ps->vps_list[sps->vps_id]->data; pps->dependent_slice_segments_enabled_flag = get_bits1(gb); pps->output_flag_present_flag = get_bits1(gb); @@ -1509,8 +1808,14 @@ int ff_hevc_decode_nal_pps(GetBitContext *gb, AVCodecContext *avctx, pps->cabac_init_present_flag = get_bits1(gb); - pps->num_ref_idx_l0_default_active = get_ue_golomb_long(gb) + 1; - pps->num_ref_idx_l1_default_active = get_ue_golomb_long(gb) + 1; + pps->num_ref_idx_l0_default_active = get_ue_golomb_31(gb) + 1; + pps->num_ref_idx_l1_default_active = get_ue_golomb_31(gb) + 1; + if (pps->num_ref_idx_l0_default_active >= HEVC_MAX_REFS || + pps->num_ref_idx_l1_default_active >= HEVC_MAX_REFS) { + av_log(avctx, AV_LOG_ERROR, "Too many default refs in PPS: %d/%d.\n", + pps->num_ref_idx_l0_default_active, pps->num_ref_idx_l1_default_active); + goto err; + } pps->pic_init_qp_minus26 = get_se_golomb(gb); @@ -1655,13 +1960,33 @@ int ff_hevc_decode_nal_pps(GetBitContext *gb, AVCodecContext *avctx, pps->slice_header_extension_present_flag = get_bits1(gb); - if (get_bits1(gb)) { // pps_extension_present_flag - pps->pps_range_extensions_flag = get_bits1(gb); - skip_bits(gb, 7); // pps_extension_7bits - if (sps->ptl.general_ptl.profile_idc == FF_PROFILE_HEVC_REXT && pps->pps_range_extensions_flag) { + pps->pps_extension_present_flag = get_bits1(gb); + if (pps->pps_extension_present_flag) { + pps->pps_range_extensions_flag = get_bits1(gb); + pps->pps_multilayer_extension_flag = get_bits1(gb); + pps->pps_3d_extension_flag = get_bits1(gb); + pps->pps_scc_extension_flag = get_bits1(gb); + skip_bits(gb, 4); // pps_extension_4bits + + if (sps->ptl.general_ptl.profile_idc >= FF_PROFILE_HEVC_REXT && pps->pps_range_extensions_flag) { if ((ret = pps_range_extensions(gb, avctx, pps, sps)) < 0) goto err; } + + if (pps->pps_multilayer_extension_flag) { + if ((ret = pps_multilayer_extension(gb, avctx, pps, sps, vps)) < 0) + goto err; + } + + if (pps->pps_3d_extension_flag) { + if ((ret = pps_3d_extension(gb, avctx, pps, sps)) < 0) + goto err; + } + + if (pps->pps_scc_extension_flag) { + if ((ret = pps_scc_extension(gb, avctx, pps, sps)) < 0) + goto err; + } } ret = setup_pps(avctx, gb, pps, sps); @@ -1669,9 +1994,8 @@ int ff_hevc_decode_nal_pps(GetBitContext *gb, AVCodecContext *avctx, goto err; if (get_bits_left(gb) < 0) { - av_log(avctx, AV_LOG_ERROR, + av_log(avctx, AV_LOG_WARNING, "Overread PPS by %d bits\n", -get_bits_left(gb)); - goto err; } remove_pps(ps, pps_id); diff --git a/libavcodec/hevc_ps.h b/libavcodec/hevc_ps.h index 18894cfed18..ef11e51ee72 100644 --- a/libavcodec/hevc_ps.h +++ b/libavcodec/hevc_ps.h @@ -32,10 +32,54 @@ #include "h2645_vui.h" #include "hevc.h" +typedef struct HEVCSublayerHdrParams { + uint32_t bit_rate_value_minus1[HEVC_MAX_CPB_CNT]; + uint32_t cpb_size_value_minus1[HEVC_MAX_CPB_CNT]; + uint32_t cpb_size_du_value_minus1[HEVC_MAX_CPB_CNT]; + uint32_t bit_rate_du_value_minus1[HEVC_MAX_CPB_CNT]; + uint32_t cbr_flag; +} HEVCSublayerHdrParams; + +typedef struct HEVCHdrFlagParams { + uint32_t nal_hrd_parameters_present_flag; + uint32_t vcl_hrd_parameters_present_flag; + uint32_t sub_pic_hrd_params_present_flag; + uint32_t sub_pic_cpb_params_in_pic_timing_sei_flag; + uint32_t fixed_pic_rate_general_flag; + uint32_t fixed_pic_rate_within_cvs_flag; + uint32_t low_delay_hrd_flag; +} HEVCHdrFlagParams; + +typedef struct HEVCHdrParams { + HEVCHdrFlagParams flags; + + uint8_t tick_divisor_minus2; + uint8_t du_cpb_removal_delay_increment_length_minus1; + uint8_t dpb_output_delay_du_length_minus1; + uint8_t bit_rate_scale; + uint8_t cpb_size_scale; + uint8_t cpb_size_du_scale; + uint8_t initial_cpb_removal_delay_length_minus1; + uint8_t au_cpb_removal_delay_length_minus1; + uint8_t dpb_output_delay_length_minus1; + uint8_t cpb_cnt_minus1[HEVC_MAX_SUB_LAYERS]; + uint16_t elemental_duration_in_tc_minus1[HEVC_MAX_SUB_LAYERS]; + + HEVCSublayerHdrParams nal_params[HEVC_MAX_SUB_LAYERS]; + HEVCSublayerHdrParams vcl_params[HEVC_MAX_SUB_LAYERS]; +} HEVCHdrParams; + typedef struct ShortTermRPS { + uint8_t rps_predict; + unsigned int delta_idx; + uint8_t use_delta_flag; + uint8_t delta_rps_sign; + unsigned int abs_delta_rps; unsigned int num_negative_pics; int num_delta_pocs; int rps_idx_num_delta_pocs; + int32_t delta_poc_s0[32]; + int32_t delta_poc_s1[32]; int32_t delta_poc[32]; uint8_t used[32]; } ShortTermRPS; @@ -108,6 +152,9 @@ typedef struct PTL { } PTL; typedef struct HEVCVPS { + unsigned int vps_id; + HEVCHdrParams hdr[HEVC_MAX_LAYER_SETS]; + uint8_t vps_temporal_id_nesting_flag; int vps_max_layers; int vps_max_sub_layers; ///< vps_max_temporal_layers_minus1 + 1 @@ -144,8 +191,11 @@ typedef struct HEVCSPS { HEVCWindow output_window; + uint8_t conformance_window_flag; HEVCWindow pic_conf_win; + HEVCHdrParams hdr; + int bit_depth; int bit_depth_chroma; int pixel_shift; @@ -154,6 +204,7 @@ typedef struct HEVCSPS { unsigned int log2_max_poc_lsb; int pcm_enabled_flag; + uint8_t sublayer_ordering_info_flag; int max_sub_layers; struct { int max_dec_pic_buffering; @@ -162,9 +213,11 @@ typedef struct HEVCSPS { } temporal_layer[HEVC_MAX_SUB_LAYERS]; uint8_t temporal_id_nesting_flag; + int vui_present; VUI vui; PTL ptl; + uint8_t sps_extension_present_flag; uint8_t scaling_list_enable_flag; ScalingList scaling_list; @@ -195,6 +248,7 @@ typedef struct HEVCSPS { unsigned int log2_max_trafo_size; unsigned int log2_ctb_size; unsigned int log2_min_pu_size; + unsigned int log2_diff_max_min_transform_block_size; int max_transform_hierarchy_depth_inter; int max_transform_hierarchy_depth_intra; @@ -210,6 +264,20 @@ typedef struct HEVCSPS { int persistent_rice_adaptation_enabled_flag; int cabac_bypass_alignment_enabled_flag; + int sps_multilayer_extension_flag; + int sps_3d_extension_flag; + + int sps_scc_extension_flag; + int sps_curr_pic_ref_enabled_flag; + int palette_mode_enabled_flag; + int palette_max_size; + int delta_palette_max_predictor_size; + int sps_palette_predictor_initializers_present_flag; + int sps_num_palette_predictor_initializers; + int sps_palette_predictor_initializer[3][HEVC_MAX_PALETTE_PREDICTOR_SIZE]; + int motion_vector_resolution_control_idc; + int intra_boundary_filtering_disabled_flag; + ///< coded frame dimension in various units int width; int height; @@ -234,6 +302,7 @@ typedef struct HEVCSPS { } HEVCSPS; typedef struct HEVCPPS { + unsigned int pps_id; unsigned int sps_id; ///< seq_parameter_set_id uint8_t sign_data_hiding_flag; @@ -283,7 +352,11 @@ typedef struct HEVCPPS { int num_extra_slice_header_bits; uint8_t slice_header_extension_present_flag; uint8_t log2_max_transform_skip_block_size; + uint8_t pps_extension_present_flag; uint8_t pps_range_extensions_flag; + uint8_t pps_multilayer_extension_flag; + uint8_t pps_3d_extension_flag; + uint8_t pps_scc_extension_flag; uint8_t cross_component_prediction_enabled_flag; uint8_t chroma_qp_offset_list_enabled_flag; uint8_t diff_cu_chroma_qp_offset_depth; @@ -293,6 +366,58 @@ typedef struct HEVCPPS { uint8_t log2_sao_offset_scale_luma; uint8_t log2_sao_offset_scale_chroma; + // Multilayer extension parameters + uint8_t poc_reset_info_present_flag; + uint8_t pps_infer_scaling_list_flag; + uint8_t pps_scaling_list_ref_layer_id; + uint8_t num_ref_loc_offsets; + uint8_t ref_loc_offset_layer_id[64]; + uint8_t scaled_ref_layer_offset_present_flag[64]; + int16_t scaled_ref_layer_left_offset[64]; + int16_t scaled_ref_layer_top_offset[64]; + int16_t scaled_ref_layer_right_offset[64]; + int16_t scaled_ref_layer_bottom_offset[64]; + uint8_t ref_region_offset_present_flag[64]; + int16_t ref_region_left_offset[64]; + int16_t ref_region_top_offset[64]; + int16_t ref_region_right_offset[64]; + int16_t ref_region_bottom_offset[64]; + uint8_t resample_phase_set_present_flag[64]; + uint8_t phase_hor_luma[64]; + uint8_t phase_ver_luma[64]; + int8_t phase_hor_chroma[64]; + int8_t phase_ver_chroma[64]; + uint8_t colour_mapping_enabled_flag; + uint8_t num_cm_ref_layers; + uint8_t cm_ref_layer_id[62]; + uint8_t cm_octant_depth; + uint8_t cm_y_part_num_log2; + uint8_t luma_bit_depth_cm_input; + uint8_t chroma_bit_depth_cm_input; + uint8_t luma_bit_depth_cm_output; + uint8_t chroma_bit_depth_cm_output; + uint8_t cm_res_quant_bits; + uint8_t cm_delta_flc_bits; + int8_t cm_adapt_threshold_u_delta; + int8_t cm_adapt_threshold_v_delta; + + // 3D extension parameters + uint8_t pps_bit_depth_for_depth_layers_minus8; + + // SCC extension parameters + uint8_t pps_curr_pic_ref_enabled_flag; + uint8_t residual_adaptive_colour_transform_enabled_flag; + uint8_t pps_slice_act_qp_offsets_present_flag; + int8_t pps_act_y_qp_offset; // _plus5 + int8_t pps_act_cb_qp_offset; // _plus5 + int8_t pps_act_cr_qp_offset; // _plus3 + uint8_t pps_palette_predictor_initializers_present_flag; + uint8_t pps_num_palette_predictor_initializers; + uint8_t monochrome_palette_flag; + uint8_t luma_bit_depth_entry; + uint8_t chroma_bit_depth_entry; + uint16_t pps_palette_predictor_initializer[3][HEVC_MAX_PALETTE_PREDICTOR_SIZE]; + // Inferred parameters unsigned int *column_width; ///< ColumnWidth unsigned int *row_height; ///< RowHeight diff --git a/libavcodec/hevc_refs.c b/libavcodec/hevc_refs.c index 811e8feff8a..c5c1203ef8c 100644 --- a/libavcodec/hevc_refs.c +++ b/libavcodec/hevc_refs.c @@ -23,6 +23,7 @@ #include "libavutil/avassert.h" +#include "decode.h" #include "thread.h" #include "hevc.h" #include "hevcdec.h" @@ -111,19 +112,16 @@ static HEVCFrame *alloc_frame(HEVCContext *s) for (j = 0; j < frame->ctb_count; j++) frame->rpl_tab[j] = (RefPicListTab *)frame->rpl_buf->data; - frame->frame->top_field_first = s->sei.picture_timing.picture_struct == AV_PICTURE_STRUCTURE_TOP_FIELD; - frame->frame->interlaced_frame = (s->sei.picture_timing.picture_struct == AV_PICTURE_STRUCTURE_TOP_FIELD) || (s->sei.picture_timing.picture_struct == AV_PICTURE_STRUCTURE_BOTTOM_FIELD); - - if (s->avctx->hwaccel) { - const AVHWAccel *hwaccel = s->avctx->hwaccel; - av_assert0(!frame->hwaccel_picture_private); - if (hwaccel->frame_priv_data_size) { - frame->hwaccel_priv_buf = av_buffer_allocz(hwaccel->frame_priv_data_size); - if (!frame->hwaccel_priv_buf) - goto fail; - frame->hwaccel_picture_private = frame->hwaccel_priv_buf->data; - } - } + if (s->sei.picture_timing.picture_struct == AV_PICTURE_STRUCTURE_TOP_FIELD) + frame->frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; + if ((s->sei.picture_timing.picture_struct == AV_PICTURE_STRUCTURE_TOP_FIELD) || + (s->sei.picture_timing.picture_struct == AV_PICTURE_STRUCTURE_BOTTOM_FIELD)) + frame->frame->flags |= AV_FRAME_FLAG_INTERLACED; + + ret = ff_hwaccel_frame_priv_alloc(s->avctx, &frame->hwaccel_picture_private, + &frame->hwaccel_priv_buf); + if (ret < 0) + goto fail; return frame; fail: @@ -322,7 +320,7 @@ int ff_hevc_slice_rpl(HEVCContext *s) return ret; if (!(s->rps[ST_CURR_BEF].nb_refs + s->rps[ST_CURR_AFT].nb_refs + - s->rps[LT_CURR].nb_refs)) { + s->rps[LT_CURR].nb_refs) && !s->ps.pps->pps_curr_pic_ref_enabled_flag) { av_log(s->avctx, AV_LOG_ERROR, "Zero refs in the frame RPS.\n"); return AVERROR_INVALIDDATA; } @@ -349,6 +347,13 @@ int ff_hevc_slice_rpl(HEVCContext *s) rpl_tmp.nb_refs++; } } + // Construct RefPicList0, RefPicList1 (8-8, 8-10) + if (s->ps.pps->pps_curr_pic_ref_enabled_flag && rpl_tmp.nb_refs < HEVC_MAX_REFS) { + rpl_tmp.list[rpl_tmp.nb_refs] = s->ref->poc; + rpl_tmp.ref[rpl_tmp.nb_refs] = s->ref; + rpl_tmp.isLongTerm[rpl_tmp.nb_refs] = 1; + rpl_tmp.nb_refs++; + } } /* reorder the references if necessary */ @@ -371,6 +376,14 @@ int ff_hevc_slice_rpl(HEVCContext *s) rpl->nb_refs = FFMIN(rpl->nb_refs, sh->nb_refs[list_idx]); } + // 8-9 + if (s->ps.pps->pps_curr_pic_ref_enabled_flag && + !sh->rpl_modification_flag[list_idx] && + rpl_tmp.nb_refs > sh->nb_refs[L0]) { + rpl->list[sh->nb_refs[L0] - 1] = s->ref->poc; + rpl->ref[sh->nb_refs[L0] - 1] = s->ref; + } + if (sh->collocated_list == list_idx && sh->collocated_ref_idx < rpl->nb_refs) s->ref->collocated_ref = rpl->ref[sh->collocated_ref_idx]; @@ -541,5 +554,9 @@ int ff_hevc_frame_nb_refs(const HEVCContext *s) for (i = 0; i < long_rps->nb_refs; i++) ret += !!long_rps->used[i]; } + + if (s->ps.pps->pps_curr_pic_ref_enabled_flag) + ret++; + return ret; } diff --git a/libavcodec/hevc_sei.c b/libavcodec/hevc_sei.c index 3c6bde1b62f..351e699726f 100644 --- a/libavcodec/hevc_sei.c +++ b/libavcodec/hevc_sei.c @@ -49,52 +49,6 @@ static int decode_nal_sei_decoded_picture_hash(HEVCSEIPictureHash *s, return 0; } -static int decode_nal_sei_mastering_display_info(HEVCSEIMasteringDisplay *s, - GetByteContext *gb) -{ - int i; - - if (bytestream2_get_bytes_left(gb) < 24) - return AVERROR_INVALIDDATA; - - // Mastering primaries - for (i = 0; i < 3; i++) { - s->display_primaries[i][0] = bytestream2_get_be16u(gb); - s->display_primaries[i][1] = bytestream2_get_be16u(gb); - } - // White point (x, y) - s->white_point[0] = bytestream2_get_be16u(gb); - s->white_point[1] = bytestream2_get_be16u(gb); - - // Max and min luminance of mastering display - s->max_luminance = bytestream2_get_be32u(gb); - s->min_luminance = bytestream2_get_be32u(gb); - - // As this SEI message comes before the first frame that references it, - // initialize the flag to 2 and decrement on IRAP access unit so it - // persists for the coded video sequence (e.g., between two IRAPs) - s->present = 2; - - return 0; -} - -static int decode_nal_sei_content_light_info(HEVCSEIContentLight *s, - GetByteContext *gb) -{ - if (bytestream2_get_bytes_left(gb) < 4) - return AVERROR_INVALIDDATA; - - // Max and average light levels - s->max_content_light_level = bytestream2_get_be16u(gb); - s->max_pic_average_light_level = bytestream2_get_be16u(gb); - // As this SEI message comes before the first frame that references it, - // initialize the flag to 2 and decrement on IRAP access unit so it - // persists for the coded video sequence (e.g., between two IRAPs) - s->present = 2; - - return 0; -} - static int decode_nal_sei_pic_timing(HEVCSEI *s, GetBitContext *gb, const HEVCParamSets *ps, void *logctx) { @@ -206,10 +160,6 @@ static int decode_nal_sei_prefix(GetBitContext *gb, GetByteContext *gbyte, return decode_nal_sei_decoded_picture_hash(&s->picture_hash, gbyte); case SEI_TYPE_PIC_TIMING: return decode_nal_sei_pic_timing(s, gb, ps, logctx); - case SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME: - return decode_nal_sei_mastering_display_info(&s->mastering_display, gbyte); - case SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO: - return decode_nal_sei_content_light_info(&s->content_light, gbyte); case SEI_TYPE_ACTIVE_PARAMETER_SETS: return decode_nal_sei_active_parameter_sets(s, gb, logctx); case SEI_TYPE_TIME_CODE: diff --git a/libavcodec/hevc_sei.h b/libavcodec/hevc_sei.h index 4189f5e6f74..a23a64ec4f8 100644 --- a/libavcodec/hevc_sei.h +++ b/libavcodec/hevc_sei.h @@ -53,20 +53,6 @@ typedef struct HEVCSEIPictureTiming { int picture_struct; } HEVCSEIPictureTiming; -typedef struct HEVCSEIMasteringDisplay { - int present; - uint16_t display_primaries[3][2]; - uint16_t white_point[2]; - uint32_t max_luminance; - uint32_t min_luminance; -} HEVCSEIMasteringDisplay; - -typedef struct HEVCSEIContentLight { - int present; - uint16_t max_content_light_level; - uint16_t max_pic_average_light_level; -} HEVCSEIContentLight; - typedef struct HEVCSEIAlternativeTransfer { int present; int preferred_transfer_characteristics; @@ -96,8 +82,6 @@ typedef struct HEVCSEI { H2645SEI common; HEVCSEIPictureHash picture_hash; HEVCSEIPictureTiming picture_timing; - HEVCSEIMasteringDisplay mastering_display; - HEVCSEIContentLight content_light; int active_seq_parameter_set_id; HEVCSEITimeCode timecode; } HEVCSEI; diff --git a/libavcodec/hevcdec.c b/libavcodec/hevcdec.c index 567e8d81d4a..df40c91ba61 100644 --- a/libavcodec/hevcdec.c +++ b/libavcodec/hevcdec.c @@ -31,7 +31,6 @@ #include "libavutil/display.h" #include "libavutil/film_grain_params.h" #include "libavutil/internal.h" -#include "libavutil/mastering_display_metadata.h" #include "libavutil/md5.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" @@ -47,6 +46,7 @@ #include "hevc_data.h" #include "hevc_parse.h" #include "hevcdec.h" +#include "hwaccel_internal.h" #include "hwconfig.h" #include "internal.h" #include "profiles.h" @@ -405,7 +405,8 @@ static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps) CONFIG_HEVC_NVDEC_HWACCEL + \ CONFIG_HEVC_VAAPI_HWACCEL + \ CONFIG_HEVC_VIDEOTOOLBOX_HWACCEL + \ - CONFIG_HEVC_VDPAU_HWACCEL) + CONFIG_HEVC_VDPAU_HWACCEL + \ + CONFIG_HEVC_VULKAN_HWACCEL) enum AVPixelFormat pix_fmts[HWACCEL_MAX + 2], *fmt = pix_fmts; switch (sps->pix_fmt) { @@ -429,6 +430,9 @@ static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps) #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: @@ -445,6 +449,9 @@ static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps) #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 @@ -464,6 +471,9 @@ static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps) #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: @@ -473,12 +483,16 @@ static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps) #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_YUV444P10: #if CONFIG_HEVC_VIDEOTOOLBOX_HWACCEL *fmt++ = AV_PIX_FMT_VIDEOTOOLBOX; #endif + /* NOTE: fallthrough */ case AV_PIX_FMT_YUV420P12: case AV_PIX_FMT_YUV444P12: #if CONFIG_HEVC_VAAPI_HWACCEL @@ -487,6 +501,9 @@ static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps) #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 @@ -494,6 +511,9 @@ static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps) 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 break; } @@ -668,7 +688,8 @@ static int hls_slice_header(HEVCContext *s) sh->slice_type); return AVERROR_INVALIDDATA; } - if (IS_IRAP(s) && sh->slice_type != HEVC_SLICE_I) { + if (IS_IRAP(s) && sh->slice_type != HEVC_SLICE_I && + !s->ps.pps->pps_curr_pic_ref_enabled_flag) { av_log(s->avctx, AV_LOG_ERROR, "Inter slices in an IRAP frame.\n"); return AVERROR_INVALIDDATA; } @@ -731,8 +752,13 @@ static int hls_slice_header(HEVCContext *s) else sh->slice_temporal_mvp_enabled_flag = 0; } else { - s->sh.short_term_rps = NULL; - s->poc = 0; + s->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->slice_temporal_mvp_enabled_flag = 0; } /* 8.3.1 */ @@ -767,11 +793,11 @@ static int hls_slice_header(HEVCContext *s) sh->nb_refs[L1] = s->ps.pps->num_ref_idx_l1_default_active; if (get_bits1(gb)) { // num_ref_idx_active_override_flag - sh->nb_refs[L0] = get_ue_golomb_long(gb) + 1; + sh->nb_refs[L0] = get_ue_golomb_31(gb) + 1; if (sh->slice_type == HEVC_SLICE_B) - sh->nb_refs[L1] = get_ue_golomb_long(gb) + 1; + 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) { + 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; @@ -839,6 +865,14 @@ static int hls_slice_header(HEVCContext *s) sh->max_num_merge_cand); return AVERROR_INVALIDDATA; } + + // Syntax in 7.3.6.1 + if (s->ps.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 = s->ps.sps->motion_vector_resolution_control_idc; + } sh->slice_qp_delta = get_se_golomb(gb); @@ -856,6 +890,12 @@ static int hls_slice_header(HEVCContext *s) sh->slice_cr_qp_offset = 0; } + if (s->ps.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 (s->ps.pps->chroma_qp_offset_list_enabled_flag) sh->cu_chroma_qp_offset_enabled_flag = get_bits1(gb); else @@ -1920,13 +1960,13 @@ static void hls_prediction_unit(HEVCLocalContext *lc, int x0, int y0, if (current_mv.pred_flag & PF_L0) { ref0 = refPicList[0].ref[current_mv.ref_idx[0]]; - if (!ref0) + if (!ref0 || !ref0->frame->data[0]) 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) + if (!ref1 || !ref1->frame->data[0]) return; hevc_await_progress(s, ref1, ¤t_mv.mv[1], y0, nPbH); } @@ -2477,7 +2517,7 @@ static void hls_decode_neighbour(HEVCLocalContext *lc, int x_ctb, int y_ctb, lc->ctb_up_left_flag = ((x_ctb > 0) && (y_ctb > 0) && (ctb_addr_in_slice-1 >= s->ps.sps->ctb_width) && (s->ps.pps->tile_id[ctb_addr_ts] == s->ps.pps->tile_id[s->ps.pps->ctb_addr_rs_to_ts[ctb_addr_rs-1 - s->ps.sps->ctb_width]])); } -static int hls_decode_entry(AVCodecContext *avctxt, void *isFilterThread) +static int hls_decode_entry(AVCodecContext *avctxt, void *arg) { HEVCContext *s = avctxt->priv_data; HEVCLocalContext *const lc = s->HEVClc; @@ -2541,14 +2581,10 @@ static int hls_decode_entry(AVCodecContext *avctxt, void *isFilterThread) static int hls_slice_data(HEVCContext *s) { - int arg[2]; - int ret[2]; - - arg[0] = 0; - arg[1] = 1; + int ret = 0; - s->avctx->execute(s->avctx, hls_decode_entry, arg, ret , 1, sizeof(int)); - return ret[0]; + s->avctx->execute(s->avctx, hls_decode_entry, NULL, &ret , 1, 0); + return ret; } static int hls_decode_entry_wpp(AVCodecContext *avctxt, void *hevc_lclist, int job, int self_id) @@ -2725,73 +2761,15 @@ static int set_side_data(HEVCContext *s) AVFrame *out = s->ref->frame; int ret; - // Decrement the mastering display flag when IRAP frame has no_rasl_output_flag=1 - // so the side data persists for the entire coded video sequence. - if (s->sei.mastering_display.present > 0 && - IS_IRAP(s) && s->no_rasl_output_flag) { - s->sei.mastering_display.present--; - } - if (s->sei.mastering_display.present) { - // HEVC uses a g,b,r ordering, which we convert to a more natural r,g,b - const int mapping[3] = {2, 0, 1}; - const int chroma_den = 50000; - const int luma_den = 10000; - int i; - AVMasteringDisplayMetadata *metadata = - av_mastering_display_metadata_create_side_data(out); - if (!metadata) - return AVERROR(ENOMEM); - - for (i = 0; i < 3; i++) { - const int j = mapping[i]; - metadata->display_primaries[i][0].num = s->sei.mastering_display.display_primaries[j][0]; - metadata->display_primaries[i][0].den = chroma_den; - metadata->display_primaries[i][1].num = s->sei.mastering_display.display_primaries[j][1]; - metadata->display_primaries[i][1].den = chroma_den; - } - metadata->white_point[0].num = s->sei.mastering_display.white_point[0]; - metadata->white_point[0].den = chroma_den; - metadata->white_point[1].num = s->sei.mastering_display.white_point[1]; - metadata->white_point[1].den = chroma_den; - - metadata->max_luminance.num = s->sei.mastering_display.max_luminance; - metadata->max_luminance.den = luma_den; - metadata->min_luminance.num = s->sei.mastering_display.min_luminance; - metadata->min_luminance.den = luma_den; - metadata->has_luminance = 1; - metadata->has_primaries = 1; - - av_log(s->avctx, AV_LOG_DEBUG, "Mastering Display Metadata:\n"); - av_log(s->avctx, AV_LOG_DEBUG, - "r(%5.4f,%5.4f) g(%5.4f,%5.4f) b(%5.4f %5.4f) wp(%5.4f, %5.4f)\n", - av_q2d(metadata->display_primaries[0][0]), - av_q2d(metadata->display_primaries[0][1]), - av_q2d(metadata->display_primaries[1][0]), - av_q2d(metadata->display_primaries[1][1]), - av_q2d(metadata->display_primaries[2][0]), - av_q2d(metadata->display_primaries[2][1]), - av_q2d(metadata->white_point[0]), av_q2d(metadata->white_point[1])); - av_log(s->avctx, AV_LOG_DEBUG, - "min_luminance=%f, max_luminance=%f\n", - av_q2d(metadata->min_luminance), av_q2d(metadata->max_luminance)); - } - // Decrement the mastering display flag when IRAP frame has no_rasl_output_flag=1 - // so the side data persists for the entire coded video sequence. - if (s->sei.content_light.present > 0 && - IS_IRAP(s) && s->no_rasl_output_flag) { - s->sei.content_light.present--; - } - if (s->sei.content_light.present) { - AVContentLightMetadata *metadata = - av_content_light_metadata_create_side_data(out); - if (!metadata) - return AVERROR(ENOMEM); - metadata->MaxCLL = s->sei.content_light.max_content_light_level; - metadata->MaxFALL = s->sei.content_light.max_pic_average_light_level; + // 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--; - av_log(s->avctx, AV_LOG_DEBUG, "Content Light Level Metadata:\n"); - av_log(s->avctx, AV_LOG_DEBUG, "MaxCLL=%d, MaxFALL=%d\n", - metadata->MaxCLL, metadata->MaxFALL); + 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, NULL, @@ -2894,7 +2872,10 @@ static int hevc_frame_start(HEVCContext *s) goto fail; } - s->ref->frame->key_frame = IS_IRAP(s); + if (IS_IRAP(s)) + s->ref->frame->flags |= AV_FRAME_FLAG_KEY; + else + s->ref->frame->flags &= ~AV_FRAME_FLAG_KEY; s->ref->needs_fg = s->sei.common.film_grain_characteristics.present && !(s->avctx->export_side_data & AV_CODEC_EXPORT_DATA_FILM_GRAIN) && @@ -2968,11 +2949,9 @@ static int decode_nal_unit(HEVCContext *s, const H2645NAL *nal) switch (s->nal_unit_type) { case HEVC_NAL_VPS: - if (s->avctx->hwaccel && s->avctx->hwaccel->decode_params) { - ret = s->avctx->hwaccel->decode_params(s->avctx, - nal->type, - nal->raw_data, - nal->raw_size); + if (FF_HW_HAS_CB(s->avctx, decode_params)) { + ret = FF_HW_CALL(s->avctx, decode_params, + nal->type, nal->raw_data, nal->raw_size); if (ret < 0) goto fail; } @@ -2981,11 +2960,9 @@ static int decode_nal_unit(HEVCContext *s, const H2645NAL *nal) goto fail; break; case HEVC_NAL_SPS: - if (s->avctx->hwaccel && s->avctx->hwaccel->decode_params) { - ret = s->avctx->hwaccel->decode_params(s->avctx, - nal->type, - nal->raw_data, - nal->raw_size); + if (FF_HW_HAS_CB(s->avctx, decode_params)) { + ret = FF_HW_CALL(s->avctx, decode_params, + nal->type, nal->raw_data, nal->raw_size); if (ret < 0) goto fail; } @@ -2995,11 +2972,9 @@ static int decode_nal_unit(HEVCContext *s, const H2645NAL *nal) goto fail; break; case HEVC_NAL_PPS: - if (s->avctx->hwaccel && s->avctx->hwaccel->decode_params) { - ret = s->avctx->hwaccel->decode_params(s->avctx, - nal->type, - nal->raw_data, - nal->raw_size); + if (FF_HW_HAS_CB(s->avctx, decode_params)) { + ret = FF_HW_CALL(s->avctx, decode_params, + nal->type, nal->raw_data, nal->raw_size); if (ret < 0) goto fail; } @@ -3009,11 +2984,9 @@ static int decode_nal_unit(HEVCContext *s, const H2645NAL *nal) break; case HEVC_NAL_SEI_PREFIX: case HEVC_NAL_SEI_SUFFIX: - if (s->avctx->hwaccel && s->avctx->hwaccel->decode_params) { - ret = s->avctx->hwaccel->decode_params(s->avctx, - nal->type, - nal->raw_data, - nal->raw_size); + if (FF_HW_HAS_CB(s->avctx, decode_params)) { + ret = FF_HW_CALL(s->avctx, decode_params, + nal->type, nal->raw_data, nal->raw_size); if (ret < 0) goto fail; } @@ -3099,16 +3072,23 @@ static int decode_nal_unit(HEVCContext *s, const H2645NAL *nal) } if (s->sh.first_slice_in_pic_flag && s->avctx->hwaccel) { - ret = s->avctx->hwaccel->start_frame(s->avctx, NULL, 0); + ret = FF_HW_CALL(s->avctx, start_frame, NULL, 0); if (ret < 0) goto fail; } if (s->avctx->hwaccel) { - ret = s->avctx->hwaccel->decode_slice(s->avctx, nal->raw_data, nal->raw_size); + ret = FF_HW_CALL(s->avctx, decode_slice, nal->raw_data, nal->raw_size); if (ret < 0) goto fail; } else { + if (s->avctx->profile == FF_PROFILE_HEVC_SCC) { + av_log(s->avctx, AV_LOG_ERROR, + "SCC profile is not yet implemented in hevc native decoder.\n"); + ret = AVERROR_PATCHWELCOME; + goto fail; + } + if (s->threads_number > 1 && s->sh.num_entry_point_offsets > 0) ctb_addr_ts = hls_slice_data_wpp(s, nal); else @@ -3366,7 +3346,7 @@ static int hevc_decode_frame(AVCodecContext *avctx, AVFrame *rframe, return ret; if (avctx->hwaccel) { - if (s->ref && (ret = avctx->hwaccel->end_frame(avctx)) < 0) { + if (s->ref && (ret = FF_HW_SIMPLE_CALL(avctx, end_frame)) < 0) { av_log(avctx, AV_LOG_ERROR, "hardware accelerator failed to decode picture\n"); ff_hevc_unref_frame(s, s->ref, ~0); @@ -3621,8 +3601,8 @@ static int hevc_update_thread_context(AVCodecContext *dst, 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.mastering_display = s0->sei.mastering_display; - s->sei.content_light = s0->sei.content_light; + s->sei.common.mastering_display = s0->sei.common.mastering_display; + s->sei.common.content_light = s0->sei.common.content_light; ret = export_stream_params_from_sei(s); if (ret < 0) @@ -3681,6 +3661,9 @@ static void hevc_decode_flush(AVCodecContext *avctx) av_buffer_unref(&s->rpu_buf); s->max_ra = INT_MAX; s->eos = 1; + + if (FF_HW_HAS_CB(avctx, flush)) + FF_HW_SIMPLE_CALL(avctx, flush); } #define OFFSET(x) offsetof(HEVCContext, x) @@ -3739,6 +3722,9 @@ const FFCodec ff_hevc_decoder = { #endif #if CONFIG_HEVC_VIDEOTOOLBOX_HWACCEL HWACCEL_VIDEOTOOLBOX(hevc), +#endif +#if CONFIG_HEVC_VULKAN_HWACCEL + HWACCEL_VULKAN(hevc), #endif NULL }, diff --git a/libavcodec/hevcdec.h b/libavcodec/hevcdec.h index 9d3f4adbb3c..94609e46993 100644 --- a/libavcodec/hevcdec.h +++ b/libavcodec/hevcdec.h @@ -75,7 +75,7 @@ #define IS_IDR(s) ((s)->nal_unit_type == HEVC_NAL_IDR_W_RADL || (s)->nal_unit_type == HEVC_NAL_IDR_N_LP) #define IS_BLA(s) ((s)->nal_unit_type == HEVC_NAL_BLA_W_RADL || (s)->nal_unit_type == HEVC_NAL_BLA_W_LP || \ (s)->nal_unit_type == HEVC_NAL_BLA_N_LP) -#define IS_IRAP(s) ((s)->nal_unit_type >= 16 && (s)->nal_unit_type <= 23) +#define IS_IRAP(s) ((s)->nal_unit_type >= HEVC_NAL_BLA_W_LP && (s)->nal_unit_type <= HEVC_NAL_RSV_IRAP_VCL23) enum RPSType { ST_CURR_BEF = 0, @@ -295,12 +295,17 @@ typedef struct SliceHeader { int slice_cb_qp_offset; int slice_cr_qp_offset; + int slice_act_y_qp_offset; + int slice_act_cb_qp_offset; + int slice_act_cr_qp_offset; + uint8_t cu_chroma_qp_offset_enabled_flag; int beta_offset; ///< beta_offset_div2 * 2 int tc_offset; ///< tc_offset_div2 * 2 - unsigned int max_num_merge_cand; ///< 5 - 5_minus_max_num_merge_cand + uint8_t max_num_merge_cand; ///< 5 - 5_minus_max_num_merge_cand + uint8_t use_integer_mv_flag; unsigned *entry_point_offset; int * offset; diff --git a/libavcodec/hnm4video.c b/libavcodec/hnm4video.c index f223bb82fc7..51e75789d2a 100644 --- a/libavcodec/hnm4video.c +++ b/libavcodec/hnm4video.c @@ -420,7 +420,7 @@ static int hnm_decode_frame(AVCodecContext *avctx, AVFrame *frame, postprocess_current_frame(avctx); copy_processed_frame(avctx, frame); frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; memcpy(frame->data[1], hnm->palette, 256 * 4); *got_frame = 1; } else if (chunk_id == HNM4_CHUNK_ID_IU) { @@ -438,7 +438,7 @@ static int hnm_decode_frame(AVCodecContext *avctx, AVFrame *frame, } copy_processed_frame(avctx, frame); frame->pict_type = AV_PICTURE_TYPE_P; - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; memcpy(frame->data[1], hnm->palette, 256 * 4); *got_frame = 1; FFSWAP(uint8_t *, hnm->current, hnm->previous); diff --git a/libavcodec/hq_hqa.c b/libavcodec/hq_hqa.c index 6ce73b7ae49..db0ac7d9e4e 100644 --- a/libavcodec/hq_hqa.c +++ b/libavcodec/hq_hqa.c @@ -354,7 +354,7 @@ static int hq_hqa_decode_frame(AVCodecContext *avctx, AVFrame *pic, return ret; } - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; pic->pict_type = AV_PICTURE_TYPE_I; *got_frame = 1; diff --git a/libavcodec/hqx.c b/libavcodec/hqx.c index 60839465502..6554b15ec65 100644 --- a/libavcodec/hqx.c +++ b/libavcodec/hqx.c @@ -504,7 +504,7 @@ static int hqx_decode_frame(AVCodecContext *avctx, AVFrame *frame, avctx->execute2(avctx, decode_slice_thread, NULL, NULL, 16); - ctx->pic->key_frame = 1; + ctx->pic->flags |= AV_FRAME_FLAG_KEY; ctx->pic->pict_type = AV_PICTURE_TYPE_I; *got_picture_ptr = 1; diff --git a/libavcodec/huffyuvdec.c b/libavcodec/huffyuvdec.c index 7d3515cc889..8ba67bbdeb5 100644 --- a/libavcodec/huffyuvdec.c +++ b/libavcodec/huffyuvdec.c @@ -695,9 +695,9 @@ static void decode_422_bitstream(HYuvDecContext *s, int count) /* TODO instead of restarting the read when the code isn't in the first level * of the joint table, jump into the 2nd level of the individual table. */ #define READ_2PIX_PLANE16(dst0, dst1, plane){\ - dst0 = get_vlc2(&s->gb, s->vlc[plane].table, VLC_BITS, 3)<<2;\ + dst0 = get_vlc2(&s->gb, s->vlc[plane].table, VLC_BITS, 3)*4;\ dst0 += get_bits(&s->gb, 2);\ - dst1 = get_vlc2(&s->gb, s->vlc[plane].table, VLC_BITS, 3)<<2;\ + dst1 = get_vlc2(&s->gb, s->vlc[plane].table, VLC_BITS, 3)*4;\ dst1 += get_bits(&s->gb, 2);\ } static void decode_plane_bitstream(HYuvDecContext *s, int width, int plane) diff --git a/libavcodec/huffyuvenc.c b/libavcodec/huffyuvenc.c index db274e37ad5..72d6246ebe0 100644 --- a/libavcodec/huffyuvenc.c +++ b/libavcodec/huffyuvenc.c @@ -1082,7 +1082,8 @@ const FFCodec ff_huffyuv_encoder = { CODEC_LONG_NAME("Huffyuv / HuffYUV"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_HUFFYUV, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(HYuvEncContext), .init = encode_init, FF_CODEC_ENCODE_CB(encode_frame), @@ -1101,7 +1102,8 @@ const FFCodec ff_ffvhuff_encoder = { CODEC_LONG_NAME("Huffyuv FFmpeg variant"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_FFVHUFF, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(HYuvEncContext), .init = encode_init, FF_CODEC_ENCODE_CB(encode_frame), diff --git a/libavcodec/hwaccel_internal.h b/libavcodec/hwaccel_internal.h new file mode 100644 index 00000000000..edfe283150a --- /dev/null +++ b/libavcodec/hwaccel_internal.h @@ -0,0 +1,179 @@ +/* + * 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 + */ + +/** + * Header providing the internals of AVHWAccel. + */ + +#ifndef AVCODEC_HWACCEL_INTERNAL_H +#define AVCODEC_HWACCEL_INTERNAL_H + +#include + +#include "avcodec.h" + +#define HWACCEL_CAP_ASYNC_SAFE (1 << 0) +#define HWACCEL_CAP_THREAD_SAFE (1 << 1) + +typedef struct FFHWAccel { + /** + * The public AVHWAccel. See avcodec.h for it. + */ + AVHWAccel p; + + /** + * Allocate a custom buffer + */ + int (*alloc_frame)(AVCodecContext *avctx, AVFrame *frame); + + /** + * Called at the beginning of each frame or field picture. + * + * Meaningful frame information (codec specific) is guaranteed to + * be parsed at this point. This function is mandatory. + * + * Note that buf can be NULL along with buf_size set to 0. + * Otherwise, this means the whole frame is available at this point. + * + * @param avctx the codec context + * @param buf the frame data buffer base + * @param buf_size the size of the frame in bytes + * @return zero if successful, a negative value otherwise + */ + int (*start_frame)(AVCodecContext *avctx, const uint8_t *buf, uint32_t buf_size); + + /** + * Callback for parameter data (SPS/PPS/VPS etc). + * + * Useful for hardware decoders which keep persistent state about the + * video parameters, and need to receive any changes to update that state. + * + * @param avctx the codec context + * @param type the nal unit type + * @param buf the nal unit data buffer + * @param buf_size the size of the nal unit in bytes + * @return zero if successful, a negative value otherwise + */ + int (*decode_params)(AVCodecContext *avctx, int type, const uint8_t *buf, uint32_t buf_size); + + /** + * Callback for each slice. + * + * Meaningful slice information (codec specific) is guaranteed to + * be parsed at this point. This function is mandatory. + * + * @param avctx the codec context + * @param buf the slice data buffer base + * @param buf_size the size of the slice in bytes + * @return zero if successful, a negative value otherwise + */ + int (*decode_slice)(AVCodecContext *avctx, const uint8_t *buf, uint32_t buf_size); + + /** + * Called at the end of each frame or field picture. + * + * The whole picture is parsed at this point and can now be sent + * to the hardware accelerator. This function is mandatory. + * + * @param avctx the codec context + * @return zero if successful, a negative value otherwise + */ + int (*end_frame)(AVCodecContext *avctx); + + /** + * Size of per-frame hardware accelerator private data. + * + * Private data is allocated with av_mallocz() before + * AVCodecContext.get_buffer() and deallocated after + * AVCodecContext.release_buffer(). + */ + int frame_priv_data_size; + + /** + * Size of the private data to allocate in + * AVCodecInternal.hwaccel_priv_data. + */ + int priv_data_size; + + /** + * Internal hwaccel capabilities. + */ + int caps_internal; + + /** + * Initialize the hwaccel private data. + * + * This will be called from ff_get_format(), after hwaccel and + * hwaccel_context are set and the hwaccel private data in AVCodecInternal + * is allocated. + */ + int (*init)(AVCodecContext *avctx); + + /** + * Uninitialize the hwaccel private data. + * + * This will be called from get_format() or avcodec_close(), after hwaccel + * and hwaccel_context are already uninitialized. + */ + int (*uninit)(AVCodecContext *avctx); + + /** + * Fill the given hw_frames context with current codec parameters. Called + * from get_format. Refer to avcodec_get_hw_frames_parameters() for + * details. + * + * This CAN be called before AVHWAccel.init is called, and you must assume + * that avctx->hwaccel_priv_data is invalid. + */ + int (*frame_params)(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx); + + /** + * Copy necessary context variables from a previous thread context to the current one. + * For thread-safe hwaccels only. + */ + int (*update_thread_context)(AVCodecContext *dst, const AVCodecContext *src); + + /** + * Callback to free the hwaccel-specific frame data. + * + * @param hwctx a pointer to an AVHWDeviceContext. + * @param data the per-frame hardware accelerator private data to be freed. + */ + void (*free_frame_priv)(void *hwctx, uint8_t *data); + + /** + * Callback to flush the hwaccel state. + */ + void (*flush)(AVCodecContext *avctx); +} FFHWAccel; + +static inline const FFHWAccel *ffhwaccel(const AVHWAccel *codec) +{ + return (const FFHWAccel*)codec; +} + +#define FF_HW_CALL(avctx, function, ...) \ + (ffhwaccel((avctx)->hwaccel)->function((avctx), __VA_ARGS__)) + +#define FF_HW_SIMPLE_CALL(avctx, function) \ + (ffhwaccel((avctx)->hwaccel)->function(avctx)) + +#define FF_HW_HAS_CB(avctx, function) \ + ((avctx)->hwaccel && ffhwaccel((avctx)->hwaccel)->function) + +#endif /* AVCODEC_HWACCEL_INTERNAL */ diff --git a/libavcodec/hwaccels.h b/libavcodec/hwaccels.h index aca55831f32..c4630718cf7 100644 --- a/libavcodec/hwaccels.h +++ b/libavcodec/hwaccels.h @@ -19,67 +19,68 @@ #ifndef AVCODEC_HWACCELS_H #define AVCODEC_HWACCELS_H -#include "avcodec.h" - -extern const AVHWAccel ff_av1_d3d11va_hwaccel; -extern const AVHWAccel ff_av1_d3d11va2_hwaccel; -extern const AVHWAccel ff_av1_dxva2_hwaccel; -extern const AVHWAccel ff_av1_nvdec_hwaccel; -extern const AVHWAccel ff_av1_vaapi_hwaccel; -extern const AVHWAccel ff_av1_vdpau_hwaccel; -extern const AVHWAccel ff_h263_vaapi_hwaccel; -extern const AVHWAccel ff_h263_videotoolbox_hwaccel; -extern const AVHWAccel ff_h264_d3d11va_hwaccel; -extern const AVHWAccel ff_h264_d3d11va2_hwaccel; -extern const AVHWAccel ff_h264_dxva2_hwaccel; -extern const AVHWAccel ff_h264_nvdec_hwaccel; -extern const AVHWAccel ff_h264_vaapi_hwaccel; -extern const AVHWAccel ff_h264_vdpau_hwaccel; -extern const AVHWAccel ff_h264_videotoolbox_hwaccel; -extern const AVHWAccel ff_hevc_d3d11va_hwaccel; -extern const AVHWAccel ff_hevc_d3d11va2_hwaccel; -extern const AVHWAccel ff_hevc_dxva2_hwaccel; -extern const AVHWAccel ff_hevc_nvdec_hwaccel; -extern const AVHWAccel ff_hevc_vaapi_hwaccel; -extern const AVHWAccel ff_hevc_vdpau_hwaccel; -extern const AVHWAccel ff_hevc_videotoolbox_hwaccel; -extern const AVHWAccel ff_mjpeg_nvdec_hwaccel; -extern const AVHWAccel ff_mjpeg_vaapi_hwaccel; -extern const AVHWAccel ff_mpeg1_nvdec_hwaccel; -extern const AVHWAccel ff_mpeg1_vdpau_hwaccel; -extern const AVHWAccel ff_mpeg1_videotoolbox_hwaccel; -extern const AVHWAccel ff_mpeg2_d3d11va_hwaccel; -extern const AVHWAccel ff_mpeg2_d3d11va2_hwaccel; -extern const AVHWAccel ff_mpeg2_nvdec_hwaccel; -extern const AVHWAccel ff_mpeg2_dxva2_hwaccel; -extern const AVHWAccel ff_mpeg2_vaapi_hwaccel; -extern const AVHWAccel ff_mpeg2_vdpau_hwaccel; -extern const AVHWAccel ff_mpeg2_videotoolbox_hwaccel; -extern const AVHWAccel ff_mpeg4_nvdec_hwaccel; -extern const AVHWAccel ff_mpeg4_vaapi_hwaccel; -extern const AVHWAccel ff_mpeg4_vdpau_hwaccel; -extern const AVHWAccel ff_mpeg4_videotoolbox_hwaccel; -extern const AVHWAccel ff_prores_videotoolbox_hwaccel; -extern const AVHWAccel ff_vc1_d3d11va_hwaccel; -extern const AVHWAccel ff_vc1_d3d11va2_hwaccel; -extern const AVHWAccel ff_vc1_dxva2_hwaccel; -extern const AVHWAccel ff_vc1_nvdec_hwaccel; -extern const AVHWAccel ff_vc1_vaapi_hwaccel; -extern const AVHWAccel ff_vc1_vdpau_hwaccel; -extern const AVHWAccel ff_vp8_nvdec_hwaccel; -extern const AVHWAccel ff_vp8_vaapi_hwaccel; -extern const AVHWAccel ff_vp9_d3d11va_hwaccel; -extern const AVHWAccel ff_vp9_d3d11va2_hwaccel; -extern const AVHWAccel ff_vp9_dxva2_hwaccel; -extern const AVHWAccel ff_vp9_nvdec_hwaccel; -extern const AVHWAccel ff_vp9_vaapi_hwaccel; -extern const AVHWAccel ff_vp9_vdpau_hwaccel; -extern const AVHWAccel ff_vp9_videotoolbox_hwaccel; -extern const AVHWAccel ff_wmv3_d3d11va_hwaccel; -extern const AVHWAccel ff_wmv3_d3d11va2_hwaccel; -extern const AVHWAccel ff_wmv3_dxva2_hwaccel; -extern const AVHWAccel ff_wmv3_nvdec_hwaccel; -extern const AVHWAccel ff_wmv3_vaapi_hwaccel; -extern const AVHWAccel ff_wmv3_vdpau_hwaccel; +extern const struct FFHWAccel ff_av1_d3d11va_hwaccel; +extern const struct FFHWAccel ff_av1_d3d11va2_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_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_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_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_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_vc1_d3d11va_hwaccel; +extern const struct FFHWAccel ff_vc1_d3d11va2_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_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_wmv3_d3d11va_hwaccel; +extern const struct FFHWAccel ff_wmv3_d3d11va2_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/libavcodec/hwconfig.h b/libavcodec/hwconfig.h index 721424912c4..e164722a94f 100644 --- a/libavcodec/hwconfig.h +++ b/libavcodec/hwconfig.h @@ -22,10 +22,6 @@ #include "avcodec.h" #include "hwaccels.h" - -#define HWACCEL_CAP_ASYNC_SAFE (1 << 0) - - typedef struct AVCodecHWConfigInternal { /** * This is the structure which will be returned to the user by @@ -36,9 +32,10 @@ typedef struct AVCodecHWConfigInternal { * If this configuration uses a hwaccel, a pointer to it. * If not, NULL. */ - const AVHWAccel *hwaccel; + const struct FFHWAccel *hwaccel; } AVCodecHWConfigInternal; +void ff_hwaccel_uninit(AVCodecContext *avctx); // These macros are used to simplify AVCodecHWConfigInternal definitions. @@ -76,6 +73,8 @@ typedef struct AVCodecHWConfigInternal { HW_CONFIG_HWACCEL(1, 1, 1, VDPAU, VDPAU, ff_ ## codec ## _vdpau_hwaccel) #define HWACCEL_VIDEOTOOLBOX(codec) \ HW_CONFIG_HWACCEL(1, 1, 1, VIDEOTOOLBOX, VIDEOTOOLBOX, ff_ ## codec ## _videotoolbox_hwaccel) +#define HWACCEL_VULKAN(codec) \ + HW_CONFIG_HWACCEL(1, 1, 1, VULKAN, VULKAN, ff_ ## codec ## _vulkan_hwaccel) #define HWACCEL_D3D11VA(codec) \ HW_CONFIG_HWACCEL(0, 0, 1, D3D11VA_VLD, NONE, ff_ ## codec ## _d3d11va_hwaccel) diff --git a/libavcodec/idcinvideo.c b/libavcodec/idcinvideo.c index f6b8b3cd697..d9e46335480 100644 --- a/libavcodec/idcinvideo.c +++ b/libavcodec/idcinvideo.c @@ -224,7 +224,14 @@ static int idcin_decode_frame(AVCodecContext *avctx, AVFrame *frame, if (idcin_decode_vlcs(s, frame)) return AVERROR_INVALIDDATA; - frame->palette_has_changed = ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS + frame->palette_has_changed = +#endif + ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_ENABLE_DEPRECATION_WARNINGS +#endif /* make the palette available on the way out */ memcpy(frame->data[1], s->pal, AVPALETTE_SIZE); diff --git a/libavcodec/iff.c b/libavcodec/iff.c index e02d2e77e53..faf4e21c428 100644 --- a/libavcodec/iff.c +++ b/libavcodec/iff.c @@ -1887,10 +1887,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, } if (avpkt->flags & AV_PKT_FLAG_KEY) { - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; } else { - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_P; } diff --git a/libavcodec/imm4.c b/libavcodec/imm4.c index ccec5dff43a..b95ad86921d 100644 --- a/libavcodec/imm4.c +++ b/libavcodec/imm4.c @@ -420,11 +420,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, switch (type) { case 0x19781977: - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; break; case 0x12250926: - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_P; break; default: @@ -434,7 +434,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if (avctx->width != width || avctx->height != height) { - if (!frame->key_frame) { + if (!(frame->flags & AV_FRAME_FLAG_KEY)) { av_log(avctx, AV_LOG_ERROR, "Frame size change is unsupported.\n"); return AVERROR_INVALIDDATA; } @@ -445,10 +445,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if (ret < 0) return ret; - if ((ret = ff_get_buffer(avctx, frame, frame->key_frame ? AV_GET_BUFFER_FLAG_REF : 0)) < 0) + if ((ret = ff_get_buffer(avctx, frame, (frame->flags & AV_FRAME_FLAG_KEY) ? AV_GET_BUFFER_FLAG_REF : 0)) < 0) return ret; - if (frame->key_frame) { + if (frame->flags & AV_FRAME_FLAG_KEY) { ret = decode_intra(avctx, gb, frame); if (ret < 0) return ret; diff --git a/libavcodec/imx.c b/libavcodec/imx.c index 44bab23c270..0d9d9b5bb95 100644 --- a/libavcodec/imx.c +++ b/libavcodec/imx.c @@ -58,11 +58,19 @@ static int imx_decode_frame(AVCodecContext *avctx, AVFrame *rframe, return ret; if (ff_copy_palette(imx->pal, avpkt, avctx)) { +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; - frame->key_frame = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + frame->flags |= AV_FRAME_FLAG_KEY; } else { - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } bytestream2_init(&gb, avpkt->data, avpkt->size); @@ -92,7 +100,7 @@ static int imx_decode_frame(AVCodecContext *avctx, AVFrame *rframe, break; } - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; break; case 1: if (len == 0) { @@ -114,7 +122,7 @@ static int imx_decode_frame(AVCodecContext *avctx, AVFrame *rframe, break; } - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; } else { while (len > 0) { fill = bytestream2_get_byte(&gb); @@ -150,7 +158,7 @@ static int imx_decode_frame(AVCodecContext *avctx, AVFrame *rframe, } } - frame->pict_type = frame->key_frame ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; + frame->pict_type = (frame->flags & AV_FRAME_FLAG_KEY) ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; if ((ret = av_frame_ref(rframe, frame)) < 0) return ret; diff --git a/libavcodec/internal.h b/libavcodec/internal.h index a283c52e012..a67cf713ca4 100644 --- a/libavcodec/internal.h +++ b/libavcodec/internal.h @@ -56,12 +56,6 @@ typedef struct AVCodecInternal { */ int is_copy; - /** - * An audio frame with less than required samples has been submitted (and - * potentially padded with silence). Reject all subsequent frames. - */ - int last_audio_frame; - /** * Audio encoders can set this flag during init to indicate that they * want the small last frame to be padded to a multiple of pad_samples. @@ -95,13 +89,6 @@ typedef struct AVCodecInternal { uint8_t *byte_buffer; unsigned int byte_buffer_size; - /** - * This is set to AV_PKT_FLAG_KEY for encoders that encode intra-only - * formats (i.e. whose codec descriptor has AV_CODEC_PROP_INTRA_ONLY set). - * This is used to set said flag generically for said encoders. - */ - int intra_only_flag; - void *frame_thread_encoder; /** @@ -148,17 +135,14 @@ typedef struct AVCodecInternal { AVFrame *buffer_frame; int draining_done; - int showed_multi_packet_warning; - - /* to prevent infinite loop on errors when draining */ - int nb_draining_errors; - +#if FF_API_DROPCHANGED /* used when avctx flag AV_CODEC_FLAG_DROPCHANGED is set */ int changed_frames_dropped; int initial_format; int initial_width, initial_height; int initial_sample_rate; AVChannelLayout initial_ch_layout; +#endif #if CONFIG_LCMS2 FFIccContext icc; /* used to read and write embedded ICC profiles */ @@ -173,15 +157,6 @@ int ff_match_2uint16(const uint16_t (*tab)[2], int size, int a, int b); unsigned int ff_toupper4(unsigned int x); -void ff_color_frame(AVFrame *frame, const int color[4]); - -/** - * Maximum size in bytes of extradata. - * This value was chosen such that every bit of the buffer is - * addressable by a 32-bit signed integer as used by get_bits. - */ -#define FF_MAX_EXTRADATA_SIZE ((1 << 28) - AV_INPUT_BUFFER_PADDING_SIZE) - /** * 2^(x) for integer x * @return correctly rounded float @@ -232,16 +207,4 @@ int ff_alloc_timecode_sei(const AVFrame *frame, AVRational rate, size_t prefix_l */ int64_t ff_guess_coded_bitrate(AVCodecContext *avctx); -/** - * Check if a value is in the list. If not, return the default value - * - * @param ctx Context for the log msg - * @param val_name Name of the checked value, for log msg - * @param array_valid_values Array of valid int, ended with INT_MAX - * @param default_value Value return if checked value is not in the array - * @return Value or default_value. - */ -int ff_int_from_list_or_default(void *ctx, const char * val_name, int val, - const int * array_valid_values, int default_value); - #endif /* AVCODEC_INTERNAL_H */ diff --git a/libavcodec/interplayacm.c b/libavcodec/interplayacm.c index a0b9655e7a1..057ab16e85a 100644 --- a/libavcodec/interplayacm.c +++ b/libavcodec/interplayacm.c @@ -37,6 +37,7 @@ static int mul_3x5 [5 * 5 * 5]; static int mul_2x11[11 * 11]; typedef struct InterplayACMContext { + AVCodecContext *avctx; GetBitContext gb; uint8_t *bitstream; int max_framesize; @@ -77,6 +78,7 @@ static av_cold int decode_init(AVCodecContext *avctx) static AVOnce init_static_once = AV_ONCE_INIT; InterplayACMContext *s = avctx->priv_data; + s->avctx = avctx; if (avctx->extradata_size < 14) return AVERROR_INVALIDDATA; @@ -343,7 +345,7 @@ static int t15(InterplayACMContext *s, unsigned ind, unsigned col) /* b = (x1) + (x2 * 3) + (x3 * 9) */ b = get_bits(gb, 5); if (b > 26) { - av_log(NULL, AV_LOG_ERROR, "Too large b = %d > 26\n", b); + av_log(s->avctx, AV_LOG_ERROR, "Too large b = %d > 26\n", b); return AVERROR_INVALIDDATA; } @@ -372,7 +374,7 @@ static int t27(InterplayACMContext *s, unsigned ind, unsigned col) /* b = (x1) + (x2 * 5) + (x3 * 25) */ b = get_bits(gb, 7); if (b > 124) { - av_log(NULL, AV_LOG_ERROR, "Too large b = %d > 124\n", b); + av_log(s->avctx, AV_LOG_ERROR, "Too large b = %d > 124\n", b); return AVERROR_INVALIDDATA; } @@ -400,7 +402,7 @@ static int t37(InterplayACMContext *s, unsigned ind, unsigned col) /* b = (x1) + (x2 * 11) */ b = get_bits(gb, 7); if (b > 120) { - av_log(NULL, AV_LOG_ERROR, "Too large b = %d > 120\n", b); + av_log(s->avctx, AV_LOG_ERROR, "Too large b = %d > 120\n", b); return AVERROR_INVALIDDATA; } diff --git a/libavcodec/interplayvideo.c b/libavcodec/interplayvideo.c index 655326a7f1f..1f0414bbaff 100644 --- a/libavcodec/interplayvideo.c +++ b/libavcodec/interplayvideo.c @@ -926,7 +926,7 @@ static void ipvideo_format_06_firstpass(IpvideoContext *s, AVFrame *frame, int16 } } else { /* Don't try to copy second_last_frame data on the first frames */ - if (s->avctx->frame_number > 2) + if (s->avctx->frame_num > 2) copy_from(s, s->second_last_frame, frame, 0, 0); } } @@ -1085,7 +1085,7 @@ static void ipvideo_decode_format_10_opcodes(IpvideoContext *s, AVFrame *frame) copy_from(s, s->cur_decode_frame, frame, 0, 0); } else { /* Don't try to copy last_frame data on the first frame */ - if (s->avctx->frame_number) + if (s->avctx->frame_num) copy_from(s, s->last_frame, frame, 0, 0); } skip *= 2; @@ -1144,8 +1144,8 @@ static void ipvideo_decode_format_11_opcodes(IpvideoContext *s, AVFrame *frame) ret = ipvideo_decode_block16[opcode](s, frame); } if (ret != 0) { - av_log(s->avctx, AV_LOG_ERROR, "decode problem on frame %d, @ block (%d, %d)\n", - s->avctx->frame_number, x, y); + av_log(s->avctx, AV_LOG_ERROR, "decode problem on frame %"PRId64", @ block (%d, %d)\n", + s->avctx->frame_num, x, y); return; } } @@ -1315,7 +1315,14 @@ static int ipvideo_decode_frame(AVCodecContext *avctx, AVFrame *frame, return ret; if (!s->is_16bpp) { - frame->palette_has_changed = ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS + frame->palette_has_changed = +#endif + ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_ENABLE_DEPRECATION_WARNINGS +#endif } switch (frame_format) { diff --git a/libavcodec/intrax8.c b/libavcodec/intrax8.c index e4c8b96c9c0..c5c67272825 100644 --- a/libavcodec/intrax8.c +++ b/libavcodec/intrax8.c @@ -109,7 +109,7 @@ static inline void x8_select_ac_table(IntraX8Context *const w, int mode) table_index = get_bits(w->gb, 3); // 2 modes use same tables w->j_ac_vlc_table[mode] = j_ac_vlc[w->quant < 13][mode >> 1][table_index].table; - av_assert2(w->j_ac_vlc[mode]); + av_assert2(j_ac_vlc[mode]); } static inline int x8_get_orient_vlc(IntraX8Context *w) diff --git a/libavcodec/ituh263dec.c b/libavcodec/ituh263dec.c index 2164cd7346e..d4a07071ec1 100644 --- a/libavcodec/ituh263dec.c +++ b/libavcodec/ituh263dec.c @@ -1093,7 +1093,7 @@ int ff_h263_decode_picture_header(MpegEncContext *s) align_get_bits(&s->gb); - if (show_bits(&s->gb, 2) == 2 && s->avctx->frame_number == 0) { + if (show_bits(&s->gb, 2) == 2 && s->avctx->frame_num == 0) { av_log(s->avctx, AV_LOG_WARNING, "Header looks like RTP instead of H.263\n"); } diff --git a/libavcodec/ituh263enc.c b/libavcodec/ituh263enc.c index 22e5a8368df..97abfb3f456 100644 --- a/libavcodec/ituh263enc.c +++ b/libavcodec/ituh263enc.c @@ -105,7 +105,7 @@ av_const int ff_h263_aspect_to_info(AVRational aspect){ return FF_ASPECT_EXTENDED; } -void ff_h263_encode_picture_header(MpegEncContext * s, int picture_number) +void ff_h263_encode_picture_header(MpegEncContext * s) { int format, coded_frame_rate, coded_frame_rate_base, i, temp_ref; int best_clock_code=1; @@ -865,6 +865,11 @@ av_cold void ff_h263_encode_init(MpegEncContext *s) s->c_dc_scale_table= ff_mpeg1_dc_scale_table; } + if (s->lmin > s->lmax) { + av_log(s->avctx, AV_LOG_WARNING, "Clipping lmin value to %d\n", s->lmax); + s->lmin = s->lmax; + } + ff_thread_once(&init_static_once, h263_encode_init_static); } @@ -903,6 +908,7 @@ const FFCodec ff_h263_encoder = { .p.id = AV_CODEC_ID_H263, .p.pix_fmts = (const enum AVPixelFormat[]){AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE}, .p.priv_class = &h263_class, + .p.capabilities = AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .priv_data_size = sizeof(MpegEncContext), .init = ff_mpv_encode_init, @@ -933,7 +939,7 @@ const FFCodec ff_h263p_encoder = { .p.id = AV_CODEC_ID_H263P, .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }, .p.priv_class = &h263p_class, - .p.capabilities = AV_CODEC_CAP_SLICE_THREADS, + .p.capabilities = AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .priv_data_size = sizeof(MpegEncContext), .init = ff_mpv_encode_init, diff --git a/libavcodec/j2kenc.c b/libavcodec/j2kenc.c index e883d5deb79..5f95b772d19 100644 --- a/libavcodec/j2kenc.c +++ b/libavcodec/j2kenc.c @@ -115,6 +115,7 @@ typedef struct { int width, height; ///< image width and height uint8_t cbps[4]; ///< bits per sample in particular components + uint8_t comp_remap[4]; int chroma_shift[2]; uint8_t planar; int ncomponents; @@ -126,7 +127,7 @@ typedef struct { uint8_t *buf_end; int bit_index; - int64_t lambda; + uint64_t lambda; Jpeg2000CodingStyle codsty; Jpeg2000QuantStyle qntsty; @@ -319,8 +320,8 @@ static int put_siz(Jpeg2000EncoderContext *s) for (i = 0; i < s->ncomponents; i++){ // Ssiz_i XRsiz_i, YRsiz_i bytestream_put_byte(&s->buf, s->cbps[i] - 1); - bytestream_put_byte(&s->buf, i?1<chroma_shift[0]:1); - bytestream_put_byte(&s->buf, i?1<chroma_shift[1]:1); + bytestream_put_byte(&s->buf, (i+1&2)?1<chroma_shift[0]:1); + bytestream_put_byte(&s->buf, (i+1&2)?1<chroma_shift[1]:1); } return 0; } @@ -431,7 +432,7 @@ static void compute_rates(Jpeg2000EncoderContext* s) for (compno = 0; compno < s->ncomponents; compno++) { int tilew = tile->comp[compno].coord[0][1] - tile->comp[compno].coord[0][0]; int tileh = tile->comp[compno].coord[1][1] - tile->comp[compno].coord[1][0]; - int scale = (compno?1 << s->chroma_shift[0]:1) * (compno?1 << s->chroma_shift[1]:1); + int scale = ((compno+1&2)?1 << s->chroma_shift[0]:1) * ((compno+1&2)?1 << s->chroma_shift[1]:1); for (layno = 0; layno < s->nlayers; layno++) { if (s->layer_rates[layno] > 0) { tile->layer_rates[layno] += (double)(tilew * tileh) * s->ncomponents * s->cbps[compno] / @@ -483,7 +484,7 @@ static int init_tiles(Jpeg2000EncoderContext *s) comp->coord[0][1] = comp->coord_o[0][1] = FFMIN((tilex+1)*s->tile_width, s->width); comp->coord[1][0] = comp->coord_o[1][0] = tiley * s->tile_height; comp->coord[1][1] = comp->coord_o[1][1] = FFMIN((tiley+1)*s->tile_height, s->height); - if (compno > 0) + if (compno + 1 & 2) for (i = 0; i < 2; i++) for (j = 0; j < 2; j++) comp->coord[i][j] = comp->coord_o[i][j] = ff_jpeg2000_ceildivpow2(comp->coord[i][j], s->chroma_shift[i]); @@ -492,8 +493,8 @@ static int init_tiles(Jpeg2000EncoderContext *s) codsty, qntsty, s->cbps[compno], - compno?1<chroma_shift[0]:1, - compno?1<chroma_shift[1]:1, + (compno+1&2)?1<chroma_shift[0]:1, + (compno+1&2)?1<chroma_shift[1]:1, s->avctx )) < 0) return ret; @@ -512,17 +513,18 @@ static int init_tiles(Jpeg2000EncoderContext *s) Jpeg2000Tile *tile = s->tile + tileno; \ if (s->planar){ \ for (compno = 0; compno < s->ncomponents; compno++){ \ + int icompno = s->comp_remap[compno]; \ Jpeg2000Component *comp = tile->comp + compno; \ int *dst = comp->i_data; \ int cbps = s->cbps[compno]; \ - line = (const PIXEL*)s->picture->data[compno] \ - + comp->coord[1][0] * (s->picture->linesize[compno] / sizeof(PIXEL)) \ + line = (const PIXEL*)s->picture->data[icompno] \ + + comp->coord[1][0] * (s->picture->linesize[icompno] / sizeof(PIXEL)) \ + comp->coord[0][0]; \ for (y = comp->coord[1][0]; y < comp->coord[1][1]; y++){ \ const PIXEL *ptr = line; \ for (x = comp->coord[0][0]; x < comp->coord[0][1]; x++) \ *dst++ = *ptr++ - (1 << (cbps - 1)); \ - line += s->picture->linesize[compno] / sizeof(PIXEL); \ + line += s->picture->linesize[icompno] / sizeof(PIXEL); \ } \ } \ } else{ \ @@ -721,11 +723,10 @@ static void encode_cblk(Jpeg2000EncoderContext *s, Jpeg2000T1Context *t1, Jpeg20 if (max == 0){ cblk->nonzerobits = 0; - bpno = 0; } else{ cblk->nonzerobits = av_log2(max) + 1 - NMSEDEC_FRACBITS; - bpno = cblk->nonzerobits - 1; } + bpno = cblk->nonzerobits - 1; cblk->data[0] = 0; ff_mqc_initenc(&t1->mqc, cblk->data + 1); @@ -1008,7 +1009,7 @@ static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int til Jpeg2000Component *comp = tile->comp + compno; uint8_t reducedresno = codsty->nreslevels - 1 -reslevelno; // ==> N_L - r Jpeg2000ResLevel *reslevel = comp->reslevel + reslevelno; - int log_subsampling[2] = { compno?s->chroma_shift[0]:0, compno?s->chroma_shift[1]:0}; + int log_subsampling[2] = { (compno+1&2)?s->chroma_shift[0]:0, (compno+1&2)?s->chroma_shift[1]:0}; unsigned prcx, prcy; int trx0, try0; @@ -1069,7 +1070,7 @@ static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int til for (x = tile_coord[0][0]; x < tile_coord[0][1]; x = (x/step_x + 1)*step_x) { for (compno = 0; compno < s->ncomponents; compno++) { Jpeg2000Component *comp = tile->comp + compno; - int log_subsampling[2] = { compno?s->chroma_shift[0]:0, compno?s->chroma_shift[1]:0}; + int log_subsampling[2] = { (compno+1&2)?s->chroma_shift[0]:0, (compno+1&2)?s->chroma_shift[1]:0}; for (reslevelno = 0; reslevelno < codsty->nreslevels; reslevelno++) { unsigned prcx, prcy; @@ -1115,7 +1116,7 @@ static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int til case JPEG2000_PGOD_CPRL: for (compno = 0; compno < s->ncomponents; compno++) { Jpeg2000Component *comp = tile->comp + compno; - int log_subsampling[2] = { compno?s->chroma_shift[0]:0, compno?s->chroma_shift[1]:0}; + int log_subsampling[2] = { (compno+1&2)?s->chroma_shift[0]:0, (compno+1&2)?s->chroma_shift[1]:0}; step_x = 32; step_y = 32; @@ -1347,7 +1348,7 @@ static void makelayers(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile) } } -static int getcut(Jpeg2000Cblk *cblk, int64_t lambda, int dwt_norm) +static int getcut(Jpeg2000Cblk *cblk, uint64_t lambda, int dwt_norm) { int passno, res = 0; for (passno = 0; passno < cblk->npasses; passno++){ @@ -1531,6 +1532,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, int tileno, ret; Jpeg2000EncoderContext *s = avctx->priv_data; uint8_t *chunkstart, *jp2cstart, *jp2hstart; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(avctx->pix_fmt); if ((ret = ff_alloc_packet(avctx, pkt, avctx->width*avctx->height*9 + AV_INPUT_BUFFER_MIN_SIZE)) < 0) return ret; @@ -1543,7 +1545,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, s->lambda = s->picture->quality * LAMBDA_SCALE; - if (avctx->pix_fmt == AV_PIX_FMT_BGR48 || avctx->pix_fmt == AV_PIX_FMT_GRAY16) + if (s->cbps[0] > 8) copy_frame_16(s); else copy_frame_8(s); @@ -1587,7 +1589,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, bytestream_put_byte(&s->buf, 1); bytestream_put_byte(&s->buf, 0); bytestream_put_byte(&s->buf, 0); - if (avctx->pix_fmt == AV_PIX_FMT_RGB24 || avctx->pix_fmt == AV_PIX_FMT_PAL8) { + if ((desc->flags & AV_PIX_FMT_FLAG_RGB) || avctx->pix_fmt == AV_PIX_FMT_PAL8) { bytestream_put_be32(&s->buf, 16); } else if (s->ncomponents == 1) { bytestream_put_be32(&s->buf, 17); @@ -1717,6 +1719,7 @@ static av_cold int j2kenc_init(AVCodecContext *avctx) Jpeg2000EncoderContext *s = avctx->priv_data; Jpeg2000CodingStyle *codsty = &s->codsty; Jpeg2000QuantStyle *qntsty = &s->qntsty; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(avctx->pix_fmt); s->avctx = avctx; av_log(s->avctx, AV_LOG_DEBUG, "init\n"); @@ -1729,7 +1732,7 @@ static av_cold int j2kenc_init(AVCodecContext *avctx) if (avctx->pix_fmt == AV_PIX_FMT_PAL8 && (s->pred != FF_DWT97_INT || s->format != CODEC_JP2)) { av_log(s->avctx, AV_LOG_WARNING, "Forcing lossless jp2 for pal8\n"); - s->pred = FF_DWT97_INT; + s->pred = 1; s->format = CODEC_JP2; } @@ -1759,24 +1762,23 @@ static av_cold int j2kenc_init(AVCodecContext *avctx) s->width = avctx->width; s->height = avctx->height; - for (i = 0; i < 3; i++) { - if (avctx->pix_fmt == AV_PIX_FMT_GRAY16 || avctx->pix_fmt == AV_PIX_FMT_RGB48) - s->cbps[i] = 16; - else - s->cbps[i] = 8; + s->ncomponents = desc->nb_components; + for (i = 0; i < 4; i++) { + s->cbps[i] = desc->comp[i].depth; + s->comp_remap[i] = i; //default } - if (avctx->pix_fmt == AV_PIX_FMT_RGB24 || avctx->pix_fmt == AV_PIX_FMT_RGB48){ - s->ncomponents = 3; - } else if (avctx->pix_fmt == AV_PIX_FMT_GRAY8 || avctx->pix_fmt == AV_PIX_FMT_PAL8 || avctx->pix_fmt == AV_PIX_FMT_GRAY16){ - s->ncomponents = 1; - } else{ // planar YUV + if ((desc->flags & AV_PIX_FMT_FLAG_PLANAR) && s->ncomponents > 1) { s->planar = 1; - s->ncomponents = 3; ret = av_pix_fmt_get_chroma_sub_sample(avctx->pix_fmt, s->chroma_shift, s->chroma_shift + 1); if (ret) return ret; + if (desc->flags & AV_PIX_FMT_FLAG_RGB) { + s->comp_remap[0] = 2; + s->comp_remap[1] = 0; + s->comp_remap[2] = 1; + } } ff_thread_once(&init_static_once, init_luts); @@ -1810,7 +1812,7 @@ static const AVOption options[] = { { "tile_height", "Tile Height", OFFSET(tile_height), AV_OPT_TYPE_INT, { .i64 = 256 }, 1, 1<<30, VE, }, { "pred", "DWT Type", OFFSET(pred), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE, "pred" }, { "dwt97int", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, "pred" }, - { "dwt53", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, "pred" }, + { "dwt53", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, "pred" }, { "sop", "SOP marker", OFFSET(sop), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE, }, { "eph", "EPH marker", OFFSET(eph), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE, }, { "prog", "Progression Order", OFFSET(prog), AV_OPT_TYPE_INT, { .i64 = 0 }, JPEG2000_PGOD_LRCP, JPEG2000_PGOD_CPRL, VE, "prog" }, @@ -1835,17 +1837,30 @@ const FFCodec ff_jpeg2000_encoder = { CODEC_LONG_NAME("JPEG 2000"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_JPEG2000, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE | + AV_CODEC_CAP_FRAME_THREADS, .priv_data_size = sizeof(Jpeg2000EncoderContext), .init = j2kenc_init, FF_CODEC_ENCODE_CB(encode_frame), .close = j2kenc_destroy, .p.pix_fmts = (const enum AVPixelFormat[]) { - AV_PIX_FMT_RGB24, AV_PIX_FMT_YUV444P, AV_PIX_FMT_GRAY8, - AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, - AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P, + AV_PIX_FMT_RGB24, AV_PIX_FMT_RGB48, + AV_PIX_FMT_GBR24P,AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16, + AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16, + AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV420P16, + AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV422P16, + AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV444P16, + AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV440P10, AV_PIX_FMT_YUV440P12, + AV_PIX_FMT_YUV411P, + AV_PIX_FMT_YUV410P, + AV_PIX_FMT_YA8, AV_PIX_FMT_YA16, + AV_PIX_FMT_RGBA, AV_PIX_FMT_RGBA64, + AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16, + AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA420P16, + AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA422P16, + AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P16, + AV_PIX_FMT_PAL8, - AV_PIX_FMT_RGB48, AV_PIX_FMT_GRAY16, AV_PIX_FMT_NONE }, .p.priv_class = &j2k_class, diff --git a/libavcodec/jfdctfst.c b/libavcodec/jfdctfst.c index 805e05808c9..946b12f379e 100644 --- a/libavcodec/jfdctfst.c +++ b/libavcodec/jfdctfst.c @@ -68,7 +68,7 @@ #include #include "libavutil/attributes.h" -#include "dct.h" +#include "fdctdsp.h" #define DCTSIZE 8 #define GLOBAL(x) x diff --git a/libavcodec/jfdctint_template.c b/libavcodec/jfdctint_template.c index 67fb77b5e12..ca17300c324 100644 --- a/libavcodec/jfdctint_template.c +++ b/libavcodec/jfdctint_template.c @@ -60,7 +60,7 @@ */ #include "libavutil/common.h" -#include "dct.h" +#include "fdctdsp.h" #include "bit_depth_template.c" diff --git a/libavcodec/jpeg2000.h b/libavcodec/jpeg2000.h index e5ecb4cbf91..d004c08f104 100644 --- a/libavcodec/jpeg2000.h +++ b/libavcodec/jpeg2000.h @@ -111,7 +111,7 @@ enum Jpeg2000Quantsty { // quantization style #define JPEG2000_CSTY_SOP 0x02 // SOP marker present #define JPEG2000_CSTY_EPH 0x04 // EPH marker present #define JPEG2000_CTSY_HTJ2K_F 0x40 // Only HT code-blocks (Rec. ITU-T T.814 | ISO/IEC 15444-15) are present -#define JPEG2000_CTSY_HTJ2K_M 0xC0 // HT code blocks (Rec. ITU-T T.814 | ISO/IEC 15444-15) can be present +#define JPEG2000_CTSY_HTJ2K_M 0xC0 // HT code-blocks (Rec. ITU-T T.814 | ISO/IEC 15444-15) can be present // Progression orders #define JPEG2000_PGOD_LRCP 0x00 // Layer-resolution level-component-position progression @@ -189,6 +189,9 @@ typedef struct Jpeg2000Cblk { Jpeg2000Pass *passes; Jpeg2000Layer *layers; int coord[2][2]; // border coordinates {{x0, x1}, {y0, y1}} + /* specific to HT code-blocks */ + int zbp; + int pass_lengths[2]; } Jpeg2000Cblk; // code block typedef struct Jpeg2000Prec { diff --git a/libavcodec/jpeg2000dec.c b/libavcodec/jpeg2000dec.c index c2b81ec103d..d6f2a5938e8 100644 --- a/libavcodec/jpeg2000dec.c +++ b/libavcodec/jpeg2000dec.c @@ -42,6 +42,8 @@ #include "jpeg2000.h" #include "jpeg2000dsp.h" #include "profiles.h" +#include "jpeg2000dec.h" +#include "jpeg2000htdec.h" #define JP2_SIG_TYPE 0x6A502020 #define JP2_SIG_VALUE 0x0D0A870A @@ -51,93 +53,6 @@ #define HAD_COC 0x01 #define HAD_QCC 0x02 -#define MAX_POCS 32 - -typedef struct Jpeg2000POCEntry { - uint16_t LYEpoc; - uint16_t CSpoc; - uint16_t CEpoc; - uint8_t RSpoc; - uint8_t REpoc; - uint8_t Ppoc; -} Jpeg2000POCEntry; - -typedef struct Jpeg2000POC { - Jpeg2000POCEntry poc[MAX_POCS]; - int nb_poc; - int is_default; -} Jpeg2000POC; - -typedef struct Jpeg2000TilePart { - uint8_t tile_index; // Tile index who refers the tile-part - const uint8_t *tp_end; - GetByteContext header_tpg; // bit stream of header if PPM header is used - GetByteContext tpg; // bit stream in tile-part -} Jpeg2000TilePart; - -/* RMK: For JPEG2000 DCINEMA 3 tile-parts in a tile - * one per component, so tile_part elements have a size of 3 */ -typedef struct Jpeg2000Tile { - Jpeg2000Component *comp; - uint8_t properties[4]; - Jpeg2000CodingStyle codsty[4]; - Jpeg2000QuantStyle qntsty[4]; - Jpeg2000POC poc; - Jpeg2000TilePart tile_part[32]; - uint8_t has_ppt; // whether this tile has a ppt marker - uint8_t *packed_headers; // contains packed headers. Used only along with PPT marker - int packed_headers_size; // size in bytes of the packed headers - GetByteContext packed_headers_stream; // byte context corresponding to packed headers - uint16_t tp_idx; // Tile-part index - int coord[2][2]; // border coordinates {{x0, x1}, {y0, y1}} -} Jpeg2000Tile; - -typedef struct Jpeg2000DecoderContext { - AVClass *class; - AVCodecContext *avctx; - GetByteContext g; - - int width, height; - int image_offset_x, image_offset_y; - int tile_offset_x, tile_offset_y; - uint8_t cbps[4]; // bits per sample in particular components - uint8_t sgnd[4]; // if a component is signed - uint8_t properties[4]; - - uint8_t has_ppm; - uint8_t *packed_headers; // contains packed headers. Used only along with PPM marker - int packed_headers_size; - GetByteContext packed_headers_stream; - uint8_t in_tile_headers; - - int cdx[4], cdy[4]; - int precision; - int ncomponents; - int colour_space; - uint32_t palette[256]; - int8_t pal8; - int cdef[4]; - int tile_width, tile_height; - unsigned numXtiles, numYtiles; - int maxtilelen; - AVRational sar; - - Jpeg2000CodingStyle codsty[4]; - Jpeg2000QuantStyle qntsty[4]; - Jpeg2000POC poc; - uint8_t roi_shift[4]; - - int bit_index; - - int curtileno; - - Jpeg2000Tile *tile; - Jpeg2000DSPContext dsp; - - /*options parameters*/ - int reduction_factor; -} Jpeg2000DecoderContext; - /* get_bits functions for JPEG2000 packet bitstream * It is a get_bit function with a bit-stuffing routine. If the value of the * byte is 0xFF, the next byte includes an extra zero bit stuffed into the MSB. @@ -323,6 +238,11 @@ static int get_siz(Jpeg2000DecoderContext *s) return AVERROR_INVALIDDATA; } + if (s->reduction_factor && (s->image_offset_x || s->image_offset_y) ){ + av_log(s->avctx, AV_LOG_ERROR, "reduction factor with image offsets is not fully implemented"); + return AVERROR_PATCHWELCOME; + } + s->ncomponents = ncomponents; if (s->tile_width <= 0 || s->tile_height <= 0) { @@ -449,6 +369,10 @@ static int get_siz(Jpeg2000DecoderContext *s) s->cdx[0] == s->cdx[1] && s->cdy[0] == s->cdy[1]) { s->avctx->pix_fmt = AV_PIX_FMT_YA8; i = 0; + } else if (ncomponents == 2 && s->precision == 16 && + s->cdx[0] == s->cdx[1] && s->cdy[0] == s->cdy[1]) { + s->avctx->pix_fmt = AV_PIX_FMT_YA16; + i = 0; } else if (ncomponents == 1 && s->precision == 8) { s->avctx->pix_fmt = AV_PIX_FMT_GRAY8; i = 0; @@ -522,12 +446,12 @@ static int get_cox(Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *c) c->cblk_style = bytestream2_get_byteu(&s->g); if (c->cblk_style != 0) { // cblk style if (c->cblk_style & JPEG2000_CTSY_HTJ2K_M || c->cblk_style & JPEG2000_CTSY_HTJ2K_F) { - av_log(s->avctx, AV_LOG_ERROR, "Support for High throughput JPEG 2000 is not yet available\n"); - return AVERROR_PATCHWELCOME; + av_log(s->avctx,AV_LOG_TRACE,"High Throughput jpeg 2000 codestream.\n"); + } else { + av_log(s->avctx, AV_LOG_WARNING, "extra cblk styles %X\n", c->cblk_style); + if (c->cblk_style & JPEG2000_CBLK_BYPASS) + av_log(s->avctx, AV_LOG_WARNING, "Selective arithmetic coding bypass\n"); } - av_log(s->avctx, AV_LOG_WARNING, "extra cblk styles %X\n", c->cblk_style); - if (c->cblk_style & JPEG2000_CBLK_BYPASS) - av_log(s->avctx, AV_LOG_WARNING, "Selective arithmetic coding bypass\n"); } c->transform = bytestream2_get_byteu(&s->g); // DWT transformation type /* set integer 9/7 DWT in case of BITEXACT flag */ @@ -1152,13 +1076,15 @@ static int jpeg2000_decode_packet(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile, return incl; if (!cblk->npasses) { - int v = expn[bandno] + numgbits - 1 - - tag_tree_decode(s, prec->zerobits + cblkno, 100); + int zbp = tag_tree_decode(s, prec->zerobits + cblkno, 100); + int v = expn[bandno] + numgbits - 1 - zbp; + if (v < 0 || v > 30) { av_log(s->avctx, AV_LOG_ERROR, "nonzerobits %d invalid or unsupported\n", v); return AVERROR_INVALIDDATA; } + cblk->zbp = zbp; cblk->nonzerobits = v; } if ((newpasses = getnpasses(s)) < 0) @@ -1199,8 +1125,23 @@ static int jpeg2000_decode_packet(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile, } } - if ((ret = get_bits(s, av_log2(newpasses1) + cblk->lblock)) < 0) - return ret; + if (newpasses > 1 && (codsty->cblk_style & JPEG2000_CTSY_HTJ2K_F)) { + // Retrieve pass lengths for each pass + int href_passes = (cblk->npasses + newpasses - 1) % 3; + int eb = av_log2(newpasses - href_passes); + int extra_bit = newpasses > 2 ? 1 : 0; + if ((ret = get_bits(s, llen + eb + 3)) < 0) + return ret; + cblk->pass_lengths[0] = ret; + if ((ret = get_bits(s, llen + 3 + extra_bit)) < 0) + return ret; + cblk->pass_lengths[1] = ret; + ret = cblk->pass_lengths[0] + cblk->pass_lengths[1]; + } else { + if ((ret = get_bits(s, av_log2(newpasses1) + cblk->lblock)) < 0) + return ret; + cblk->pass_lengths[0] = ret; + } if (ret > cblk->data_allocated) { size_t new_size = FFMAX(2*cblk->data_allocated, ret); void *new = av_realloc(cblk->data, new_size); @@ -1949,9 +1890,12 @@ static inline void tile_codeblocks(const Jpeg2000DecoderContext *s, Jpeg2000Tile /* Loop on tile components */ for (compno = 0; compno < s->ncomponents; compno++) { - Jpeg2000Component *comp = tile->comp + compno; - Jpeg2000CodingStyle *codsty = tile->codsty + compno; + Jpeg2000Component *comp = tile->comp + compno; + Jpeg2000CodingStyle *codsty = tile->codsty + compno; + Jpeg2000QuantStyle *quantsty = tile->qntsty + compno; + int coded = 0; + int subbandno = 0; t1.stride = (1<log2_cblk_width) + 2; @@ -1959,7 +1903,7 @@ static inline void tile_codeblocks(const Jpeg2000DecoderContext *s, Jpeg2000Tile for (reslevelno = 0; reslevelno < codsty->nreslevels2decode; reslevelno++) { Jpeg2000ResLevel *rlevel = comp->reslevel + reslevelno; /* Loop on bands */ - for (bandno = 0; bandno < rlevel->nbands; bandno++) { + for (bandno = 0; bandno < rlevel->nbands; bandno++, subbandno++) { int nb_precincts, precno; Jpeg2000Band *band = rlevel->band + bandno; int cblkno = 0, bandpos; @@ -1979,12 +1923,23 @@ static inline void tile_codeblocks(const Jpeg2000DecoderContext *s, Jpeg2000Tile for (cblkno = 0; cblkno < prec->nb_codeblocks_width * prec->nb_codeblocks_height; cblkno++) { - int x, y; + int x, y, ret; + /* See Rec. ITU-T T.800, Equation E-2 */ + int magp = quantsty->expn[subbandno] + quantsty->nguardbits - 1; + Jpeg2000Cblk *cblk = prec->cblk + cblkno; - int ret = decode_cblk(s, codsty, &t1, cblk, - cblk->coord[0][1] - cblk->coord[0][0], - cblk->coord[1][1] - cblk->coord[1][0], - bandpos, comp->roi_shift); + + if (codsty->cblk_style & JPEG2000_CTSY_HTJ2K_F) + ret = ff_jpeg2000_decode_htj2k(s, codsty, &t1, cblk, + cblk->coord[0][1] - cblk->coord[0][0], + cblk->coord[1][1] - cblk->coord[1][0], + magp, comp->roi_shift); + else + ret = decode_cblk(s, codsty, &t1, cblk, + cblk->coord[0][1] - cblk->coord[0][0], + cblk->coord[1][1] - cblk->coord[1][0], + bandpos, comp->roi_shift); + if (ret) coded = 1; else @@ -2532,7 +2487,7 @@ static int jpeg2000_decode_frame(AVCodecContext *avctx, AVFrame *picture, if ((ret = ff_thread_get_buffer(avctx, picture, 0)) < 0) goto end; picture->pict_type = AV_PICTURE_TYPE_I; - picture->key_frame = 1; + picture->flags |= AV_FRAME_FLAG_KEY; if (ret = jpeg2000_read_bitstream_packets(s)) goto end; diff --git a/libavcodec/jpeg2000dec.h b/libavcodec/jpeg2000dec.h new file mode 100644 index 00000000000..d0ca6e7a794 --- /dev/null +++ b/libavcodec/jpeg2000dec.h @@ -0,0 +1,119 @@ +/* + * JPEG 2000 image decoder + * Copyright (c) 2007 Kamil Nowosad + * Copyright (c) 2013 Nicolas Bertrand + * Copyright (c) 2022 Caleb Etemesi + * + * 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_JPEG2000DEC_H +#define AVCODEC_JPEG2000DEC_H + +#include "bytestream.h" +#include "jpeg2000.h" +#include "jpeg2000dsp.h" + + +#define MAX_POCS 32 + +typedef struct Jpeg2000POCEntry { + uint16_t LYEpoc; + uint16_t CSpoc; + uint16_t CEpoc; + uint8_t RSpoc; + uint8_t REpoc; + uint8_t Ppoc; +} Jpeg2000POCEntry; + +typedef struct Jpeg2000POC { + Jpeg2000POCEntry poc[MAX_POCS]; + int nb_poc; + int is_default; +} Jpeg2000POC; + +typedef struct Jpeg2000TilePart { + uint8_t tile_index; // Tile index who refers the tile-part + const uint8_t *tp_end; + GetByteContext header_tpg; // bit stream of header if PPM header is used + GetByteContext tpg; // bit stream in tile-part +} Jpeg2000TilePart; + +/* RMK: For JPEG2000 DCINEMA 3 tile-parts in a tile + * one per component, so tile_part elements have a size of 3 */ +typedef struct Jpeg2000Tile { + Jpeg2000Component *comp; + uint8_t properties[4]; + Jpeg2000CodingStyle codsty[4]; + Jpeg2000QuantStyle qntsty[4]; + Jpeg2000POC poc; + Jpeg2000TilePart tile_part[32]; + uint8_t has_ppt; // whether this tile has a ppt marker + uint8_t *packed_headers; // contains packed headers. Used only along with PPT marker + int packed_headers_size; // size in bytes of the packed headers + GetByteContext packed_headers_stream; // byte context corresponding to packed headers + uint16_t tp_idx; // Tile-part index + int coord[2][2]; // border coordinates {{x0, x1}, {y0, y1}} +} Jpeg2000Tile; + +typedef struct Jpeg2000DecoderContext { + AVClass *class; + AVCodecContext *avctx; + GetByteContext g; + + int width, height; + int image_offset_x, image_offset_y; + int tile_offset_x, tile_offset_y; + uint8_t cbps[4]; // bits per sample in particular components + uint8_t sgnd[4]; // if a component is signed + uint8_t properties[4]; + + uint8_t has_ppm; + uint8_t *packed_headers; // contains packed headers. Used only along with PPM marker + int packed_headers_size; + GetByteContext packed_headers_stream; + uint8_t in_tile_headers; + + int cdx[4], cdy[4]; + int precision; + int ncomponents; + int colour_space; + uint32_t palette[256]; + int8_t pal8; + int cdef[4]; + int tile_width, tile_height; + unsigned numXtiles, numYtiles; + int maxtilelen; + AVRational sar; + + Jpeg2000CodingStyle codsty[4]; + Jpeg2000QuantStyle qntsty[4]; + Jpeg2000POC poc; + uint8_t roi_shift[4]; + + int bit_index; + + int curtileno; + + Jpeg2000Tile *tile; + Jpeg2000DSPContext dsp; + + /*options parameters*/ + int reduction_factor; +} Jpeg2000DecoderContext; + +#endif //AVCODEC_JPEG2000DEC_H diff --git a/libavcodec/jpeg2000htdec.c b/libavcodec/jpeg2000htdec.c new file mode 100644 index 00000000000..2c4cea5dd92 --- /dev/null +++ b/libavcodec/jpeg2000htdec.c @@ -0,0 +1,1443 @@ +/* + * Copyright (c) 2022 Caleb Etemesi + * + * 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 + */ + +/* + * Copyright 2019 - 2021, Osamu Watanabe + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "libavutil/attributes.h" +#include "libavutil/common.h" +#include "libavutil/avassert.h" +#include "jpeg2000htdec.h" +#include "jpeg2000.h" +#include "jpeg2000dec.h" + +#define J2K_Q1 0 +#define J2K_Q2 1 + +#define HT_SHIFT_SIGMA 0 +#define HT_SHIFT_SCAN 4 +#define HT_SHIFT_REF 3 +#define HT_SHIFT_REF_IND 2 + +/* See Rec. ITU-T T.800, Table 2 */ +const static uint8_t mel_e[13] = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 4, 5 }; + +static const uint16_t dec_cxt_vlc_table1[1024]; +static const uint16_t dec_cxt_vlc_table0[1024]; + +typedef struct StateVars { + int32_t pos; + uint32_t bits; + uint32_t tmp; + uint32_t last; + uint8_t bits_left; + uint64_t bit_buf; +} StateVars; + +typedef struct MelDecoderState { + uint8_t k; + uint8_t run; + uint8_t one; +} MelDecoderState; + +/** + * Given a precomputed c, checks whether n % d == 0. c is precomputed from d + * using precompute_c(). + */ +av_always_inline +static uint32_t is_divisible(uint32_t n, uint64_t c) +{ + return n * c <= c - 1; +} + +/** + * Precompute the number c used by is_divisible(). + */ +av_always_inline +static uint64_t precompute_c(uint32_t d) +{ + return 1 + (0xffffffffffffffffull / d); +} + +static void jpeg2000_init_zero(StateVars *s) +{ + s->bits_left = 0; + s->bit_buf = 0; + s->tmp = 0; + s->bits = 0; + s->pos = 0; + s->last = 0; +} + +static void jpeg2000_init_mel(StateVars *s, uint32_t Pcup) +{ + jpeg2000_init_zero(s); + s->pos = Pcup; +} + +static void jpeg2000_init_mag_ref(StateVars *s, uint32_t Lref) +{ + s->pos = Lref - 2; + s->bits = 0; + s->last = 0xFF; + s->tmp = 0; + s->bits_left = 0; + s->bit_buf = 0; +} + +static void jpeg2000_init_mel_decoder(MelDecoderState *mel_state) +{ + mel_state->k = 0; + mel_state->run = 0; + mel_state->one = 0; +} + +/** + * Refill the buffer backwards in little endian while skipping over stuffing + * bits. Stuffing bits are those that appear in the position of any byte whose + * LSBs are all 1's if the last consumed byte was larger than 0x8F. + */ +static int jpeg2000_bitbuf_refill_backwards(StateVars *buffer, const uint8_t *array) +{ + uint64_t tmp = 0; + int32_t position = buffer->pos - 4; + uint32_t new_bits = 32; + + if (buffer->bits_left >= 32) + return 0; // enough data, no need to pull in more bits + + /** + * Unstuff bits. Load a temporary byte, which precedes the position we + * currently at, to ensure that we can also un-stuff if the stuffed bit is + * the bottom most bits. + */ + + for(int i = FFMAX(0, position + 1); i <= buffer->pos + 1; i++) + tmp = 256*tmp + array[i]; + + if ((tmp & 0x7FFF000000) > 0x7F8F000000) { + tmp &= 0x7FFFFFFFFF; + new_bits--; + } + if ((tmp & 0x007FFF0000) > 0x007F8F0000) { + tmp = (tmp & 0x007FFFFFFF) + ((tmp & 0xFF00000000) >> 1); + new_bits--; + } + if ((tmp & 0x00007FFF00) > 0x00007F8F00) { + tmp = (tmp & 0x00007FFFFF) + ((tmp & 0xFFFF000000) >> 1); + new_bits--; + } + if ((tmp & 0x0000007FFF) > 0x0000007F8F) { + tmp = (tmp & 0x0000007FFF) + ((tmp & 0xFFFFFF0000) >> 1); + new_bits--; + } + + tmp >>= 8; // Remove temporary byte loaded + + /* Add bits to the MSB of the bit buffer */ + buffer->bit_buf |= tmp << buffer->bits_left; + buffer->bits_left += new_bits; + buffer->pos = FFMAX(0, position); + return 0; +} + +/** + * Refill the bit-buffer reading new bits going forward + * in the stream while skipping over stuffed bits. + */ +static void jpeg2000_bitbuf_refill_forward(StateVars *buffer, const uint8_t *array, + uint32_t length) +{ + while (buffer->bits_left < 32) { + buffer->tmp = 0xFF; + buffer->bits = (buffer->last == 0xFF) ? 7 : 8; + if (buffer->pos <= length) { + buffer->tmp = array[buffer->pos]; + buffer->pos += 1; + buffer->last = buffer->tmp; + } + buffer->bit_buf |= ((uint64_t) buffer->tmp) << buffer->bits_left; + buffer->bits_left += buffer->bits; + } +} + +/** + * Drops bits from lower bits in the bit buffer. buf contains the bit buffers. + * nbits is the number of bits to remove. + */ +av_always_inline +static void jpeg2000_bitbuf_drop_bits_lsb(StateVars *buf, uint8_t nbits) +{ + av_assert2(buf->bits_left >= nbits); // cannot read more bits than available + buf->bit_buf >>= nbits; + buf->bits_left -= nbits; +} + +/** + * Get bits from the bit buffer reading them from the least significant bits + * moving to the most significant bits. In case there are fewer bits, refill + * from buf moving backwards. + */ +av_always_inline +static uint64_t jpeg2000_bitbuf_get_bits_lsb(StateVars *bit_stream, uint8_t nbits, + const uint8_t *buf) +{ + uint64_t bits; + uint64_t mask = (1ull << nbits) - 1; + if (bit_stream->bits_left < nbits) + jpeg2000_bitbuf_refill_backwards(bit_stream, buf); + bits = bit_stream->bit_buf & mask; + jpeg2000_bitbuf_drop_bits_lsb(bit_stream, nbits); + return bits; +} + +/** + * Get bits from the bit buffer reading them from the least significant bits + * moving to the most significant bits. In case there are fewer bits, refill from + * buf moving forward. + */ +av_always_inline +static uint64_t jpeg2000_bitbuf_get_bits_lsb_forward(StateVars *bit_stream, + uint8_t nbits, const uint8_t *buf, + uint32_t length) +{ + uint64_t bits; + uint64_t mask = (1ull << nbits) - 1; + + if (bit_stream->bits_left <= nbits) + jpeg2000_bitbuf_refill_forward(bit_stream, buf, length); + bits = bit_stream->bit_buf & mask; + jpeg2000_bitbuf_drop_bits_lsb(bit_stream, nbits); + return bits; +} + +/** + * Look ahead bit buffer without discarding bits. + */ +av_always_inline +static uint64_t jpeg2000_bitbuf_peek_bits_lsb(StateVars *stream, uint8_t nbits) +{ + uint64_t mask = (1ull << nbits) - 1; + return stream->bit_buf & mask; +} + +static void jpeg2000_init_vlc(StateVars *s, uint32_t Lcup, uint32_t Pcup, + const uint8_t *Dcup) +{ + s->bits_left = 0; + s->bit_buf = 0; + s->pos = Lcup - 2 - Pcup; + s->last = Dcup[Lcup - 2]; + s->tmp = (s->last) >> 4; + s->bits = ((s->tmp & 7) < 7) ? 4 : 3; + + jpeg2000_bitbuf_refill_backwards(s, Dcup + Pcup); + jpeg2000_bitbuf_drop_bits_lsb(s, 4); +} + +/** + * Decode prefix codes for VLC segment. See Rec. ITU-T T.814, 7.3.5. + */ +av_always_inline +static int jpeg2000_decode_ctx_vlc(const Jpeg2000DecoderContext *s, + StateVars *vlc_stream, const uint16_t *table, + const uint8_t *Dcup, uint8_t *sig_pat, + uint8_t *res_off, uint8_t *emb_pat_k, + uint8_t *emb_pat_1, uint8_t pos, + uint32_t Pcup, uint16_t context) +{ + uint32_t value; + uint8_t len; + uint64_t index; + uint64_t code_word; + + jpeg2000_bitbuf_refill_backwards(vlc_stream, Dcup + Pcup); + + code_word = vlc_stream->bit_buf & 0x7f; + index = code_word + (context << 7); + + av_assert0(index < 1024); // The CxtVLC table has 1024 entries. + + value = table[index]; + + len = (value & 0x000F) >> 1; + + res_off[pos] = (uint8_t) (value & 1); + sig_pat[pos] = (uint8_t) ((value & 0x00F0) >> 4); + emb_pat_k[pos] = (uint8_t) ((value & 0x0F00) >> 8); + emb_pat_1[pos] = (uint8_t) ((value & 0xF000) >> 12); + + jpeg2000_bitbuf_drop_bits_lsb(vlc_stream, len); + return 0; +} + +/** + * Decode variable length u-vlc prefix. See decodeUPrefix procedure at Rec. + * ITU-T T.814, 7.3.6. + */ +av_always_inline +static uint8_t vlc_decode_u_prefix(StateVars *vlc_stream, const uint8_t *refill_array) +{ + static const uint8_t return_value[8] = { 5, 1, 2, 1, 3, 1, 2, 1 }; + static const uint8_t drop_bits[8] = { 3, 1, 2, 1, 3, 1, 2, 1 }; + + uint8_t bits; + + if (vlc_stream->bits_left < 3) + jpeg2000_bitbuf_refill_backwards(vlc_stream, refill_array); + + bits = jpeg2000_bitbuf_peek_bits_lsb(vlc_stream, 3); + + jpeg2000_bitbuf_drop_bits_lsb(vlc_stream, drop_bits[bits]); + return return_value[bits]; +} + +/** + * Decode variable length u-vlc suffix. See decodeUSuffix procedure at Rec. + * ITU-T T.814, 7.3.6. + */ +av_always_inline +static uint8_t vlc_decode_u_suffix(StateVars *vlc_stream, uint8_t suffix, + const uint8_t *refill_array) +{ + static const int mask[] = { 1, 31 }; + static const int drop_bits[] = { 1, 5 }; + + uint8_t bits; + int cond = suffix != 3; + if (suffix < 3) + return 0; + + if (vlc_stream->bits_left < 5) + jpeg2000_bitbuf_refill_backwards(vlc_stream, refill_array); + + bits = jpeg2000_bitbuf_peek_bits_lsb(vlc_stream, 5); + + jpeg2000_bitbuf_drop_bits_lsb(vlc_stream, drop_bits[cond]); + return bits & mask[cond]; +} + +/** + * Decode u-vlc extension values. See decodeUExtension procedure at Rec. ITU-T + * T.814, 7.3.6. + */ +av_always_inline +static uint8_t vlc_decode_u_extension(StateVars *vlc_stream, uint8_t suffix, + const uint8_t *refill_array) +{ + return jpeg2000_bitbuf_get_bits_lsb(vlc_stream, 4 * (suffix >= 28), refill_array); +} + +/** + * Magnitude and Sign decode procedures. See decodeMagSgnValue procedure at Rec. + * ITU-T T.814, 7.3.8. + */ +av_always_inline +static int32_t jpeg2000_decode_mag_sgn(StateVars *mag_sgn_stream, int32_t m_n, + int32_t i_n, const uint8_t *buf, uint32_t length) +{ + int32_t val = 0; + if (m_n > 0) { + val = jpeg2000_bitbuf_get_bits_lsb_forward(mag_sgn_stream,m_n,buf,length); + val += (i_n << m_n); + } + return val; +} + +av_always_inline +static void recover_mag_sgn(StateVars *mag_sgn, uint8_t pos, uint16_t q, int32_t m_n[2], + int32_t known_1[2], const uint8_t emb_pat_1[2], + int32_t v[2][4], int32_t m[2][4], uint8_t *E, + uint32_t *mu_n, const uint8_t *Dcup, uint32_t Pcup, + uint32_t pLSB) +{ + for (int i = 0; i < 4; i++) { + int32_t n = 4 * q + i; + m_n[pos] = m[pos][i]; + known_1[pos] = (emb_pat_1[pos] >> i) & 1; + v[pos][i] = jpeg2000_decode_mag_sgn(mag_sgn, m_n[pos], known_1[pos], Dcup, Pcup); + + if (m_n[pos] != 0) { + E[n] = 32 - ff_clz(v[pos][i] | 1); + mu_n[n] = (v[pos][i] >> 1) + 1; + mu_n[n] <<= pLSB; + mu_n[n] |= ((uint32_t) (v[pos][i] & 1)) << 31; // sign bit. + } + } +} + +static int jpeg2000_import_bit(StateVars *stream, const uint8_t *array, uint32_t length) +{ + int cond = stream->pos <= length; + int pos = FFMIN(stream->pos, length); + if (stream->bits == 0) { + stream->bits = (stream->tmp == 0xFF) ? 7 : 8; + stream->pos += cond; + stream->tmp = cond ? array[pos] : 0xFF; + } + stream->bits -= 1; + return (stream->tmp >> stream->bits) & 1; +} + +static int jpeg2000_peek_bit(StateVars *stream, const uint8_t *array, uint32_t length) +{ + if (stream->bits == 0) { + int cond = stream->pos <= length; + int pos = FFMIN(stream->pos, length); + stream->bits = (stream->tmp == 0xFF) ? 7 : 8; + stream->pos += cond; + stream->tmp = cond ? array[pos] : 0xFF; + } + return (stream->tmp >> stream->bits) & 1; +} + +static int jpeg2000_decode_mel_sym(MelDecoderState *mel_state, + StateVars *mel_stream, + const uint8_t *Dcup, + uint32_t Lcup) +{ + + if (mel_state->run == 0 && mel_state->one == 0) { + uint8_t eval; + uint8_t bit; + + eval = mel_e[mel_state->k]; + bit = jpeg2000_import_bit(mel_stream, Dcup, Lcup); + if (bit == 1) { + mel_state->run = 1 << eval; + mel_state->k = FFMIN(12, mel_state->k + 1); + } else { + mel_state->run = 0; + while (eval > 0) { + bit = jpeg2000_import_bit(mel_stream, Dcup, Lcup); + mel_state->run = (2 * (mel_state->run)) + bit; + eval -= 1; + } + mel_state->k = FFMAX(0, mel_state->k - 1); + mel_state->one = 1; + } + } + if (mel_state->run > 0) { + mel_state->run -= 1; + return 0; + } else { + mel_state->one = 0; + return 1; + } +} + +/** + * Magref decoding procedures. + */ +av_always_inline +static int jpeg2000_import_magref_bit(StateVars *stream, const uint8_t *array, + uint32_t length) +{ + return jpeg2000_bitbuf_get_bits_lsb(stream, 1, array); +} + +/** + * Signal EMB decode. + */ +static int jpeg2000_decode_sig_emb(const Jpeg2000DecoderContext *s, MelDecoderState *mel_state, + StateVars *mel_stream, StateVars *vlc_stream, + const uint16_t *vlc_table, const uint8_t *Dcup, + uint8_t *sig_pat, uint8_t *res_off, uint8_t *emb_pat_k, + uint8_t *emb_pat_1, uint8_t pos, uint16_t context, + uint32_t Lcup, uint32_t Pcup) +{ + if (context == 0) { + uint8_t sym; + sym = jpeg2000_decode_mel_sym(mel_state, mel_stream, Dcup, Lcup); + if (sym == 0) { + sig_pat[pos] = 0; + res_off[pos] = 0; + emb_pat_k[pos] = 0; + emb_pat_1[pos] = 0; + return 0; + } + } + return jpeg2000_decode_ctx_vlc(s, vlc_stream, vlc_table, Dcup, sig_pat, + res_off, emb_pat_k, emb_pat_1, pos, Pcup, + context); +} + +av_always_inline +static int jpeg2000_get_state(int x1, int x2, int width, int shift_by, + const uint8_t *block_states) +{ + return (block_states[(x1 + 1) * (width + 2) + (x2 + 1)] >> shift_by) & 1; +} + +av_always_inline +static void jpeg2000_modify_state(int x1, int x2, int width, + int value, uint8_t *block_states) +{ + block_states[(x1 + 1) * (width + 2) + (x2 + 1)] |= value; +} + +av_always_inline +static int jpeg2000_decode_ht_cleanup_segment(const Jpeg2000DecoderContext *s, + Jpeg2000Cblk *cblk, Jpeg2000T1Context *t1, + MelDecoderState *mel_state, + StateVars *mel_stream, StateVars *vlc_stream, + StateVars *mag_sgn_stream, const uint8_t *Dcup, + uint32_t Lcup, uint32_t Pcup, uint8_t pLSB, + int width, int height, int32_t *sample_buf, + uint8_t *block_states) +{ + uint16_t q = 0; // Represents current quad position + uint16_t q1, q2; + uint16_t context1, context2; + uint16_t context = 0; + + uint8_t sig_pat[2] = { 0 }; // significance pattern + uint8_t res_off[2] = { 0 }; // residual offset + uint8_t emb_pat_k[2] = { 0 }; // exponent Max Bound pattern K + uint8_t emb_pat_1[2] = { 0 }; // exponent Max Bound pattern 1 + uint8_t gamma[2] = { 0 }; + + uint8_t E_n[2] = { 0 }; + uint8_t E_ne[2] = { 0 }; + uint8_t E_nw[2] = { 0 }; + uint8_t E_nf[2] = { 0 }; + + uint8_t max_e[2] = { 0 }; + uint8_t u_pfx[2] = { 0 }; + uint8_t u_sfx[2] = { 0 }; + uint8_t u_ext[2] = { 0 }; + + int32_t u[2] = { 0 }; + int32_t U[2] = { 0 }; // exponent bound + int32_t m_n[2] = { 0 }; + int32_t known_1[2] = { 0 }; + + int32_t m[2][4] = { 0 }; + int32_t v[2][4] = { 0 }; + + uint8_t kappa[2] = { 1, 1 }; + + int ret = 0; + + int sp; + + uint64_t c; + + uint8_t *sigma; + uint32_t *mu; + + const uint8_t *vlc_buf = Dcup + Pcup; + + /* convert to raster-scan */ + const uint16_t is_border_x = width % 2; + const uint16_t is_border_y = height % 2; + + const uint16_t quad_width = ff_jpeg2000_ceildivpow2(width, 1); + const uint16_t quad_height = ff_jpeg2000_ceildivpow2(height, 1); + + size_t buf_size = 4 * quad_width * quad_height; + + uint8_t *sigma_n = av_calloc(buf_size, sizeof(uint8_t)); + uint8_t *E = av_calloc(buf_size, sizeof(uint8_t)); + uint32_t *mu_n = av_calloc(buf_size, sizeof(uint32_t)); + + if (!sigma_n || !E || !mu_n) { + ret = AVERROR(ENOMEM); + goto free; + } + + sigma = sigma_n; + mu = mu_n; + + while (q < quad_width - 1) { + q1 = q; + q2 = q1 + 1; + + if ((ret = jpeg2000_decode_sig_emb(s, mel_state, mel_stream, vlc_stream, + dec_cxt_vlc_table0, Dcup, sig_pat, res_off, + emb_pat_k, emb_pat_1, J2K_Q1, context, Lcup, + Pcup)) < 0) + goto free; + + for (int i = 0; i < 4; i++) + sigma_n[4 * q1 + i] = (sig_pat[J2K_Q1] >> i) & 1; + + /* calculate context */ + context = sigma_n[4 * q1]; // f + context |= sigma_n[4 * q1 + 1]; // sf + context += sigma_n[4 * q1 + 2] << 1; // w << 1 + context += sigma_n[4 * q1 + 3] << 2; + + if ((ret = jpeg2000_decode_sig_emb(s, mel_state, mel_stream, vlc_stream, + dec_cxt_vlc_table0, Dcup, sig_pat, res_off, + emb_pat_k, emb_pat_1, J2K_Q2, context, Lcup, + Pcup)) < 0) + goto free; + + for (int i = 0; i < 4; i++) + sigma_n[4 * q2 + i] = (sig_pat[J2K_Q2] >> i) & 1; + + /* calculate context for the next quad */ + context = sigma_n[4 * q2]; // f + context |= sigma_n[4 * q2 + 1]; // sf + context += sigma_n[4 * q2 + 2] << 1; // w << 1 + context += sigma_n[4 * q2 + 3] << 2; // sw << 2 + + u[0] = 0; + u[1] = 0; + + jpeg2000_bitbuf_refill_backwards(vlc_stream, vlc_buf); + + if (res_off[J2K_Q1] == 1 && res_off[J2K_Q2] == 1) { + + if (jpeg2000_decode_mel_sym(mel_state, mel_stream, Dcup, Lcup) == 1) { + + u_pfx[J2K_Q1] = vlc_decode_u_prefix(vlc_stream, vlc_buf); + u_pfx[J2K_Q2] = vlc_decode_u_prefix(vlc_stream, vlc_buf); + + u_sfx[J2K_Q1] = vlc_decode_u_suffix(vlc_stream, u_pfx[J2K_Q1], vlc_buf); + u_sfx[J2K_Q2] = vlc_decode_u_suffix(vlc_stream, u_pfx[J2K_Q2], vlc_buf); + + u_ext[J2K_Q1] = vlc_decode_u_extension(vlc_stream, u_sfx[J2K_Q1], vlc_buf); + u_ext[J2K_Q2] = vlc_decode_u_extension(vlc_stream, u_sfx[J2K_Q2], vlc_buf); + + u[J2K_Q1] = 2 + u_pfx[J2K_Q1] + u_sfx[J2K_Q1] + (u_ext[J2K_Q1] * 4); + u[J2K_Q2] = 2 + u_pfx[J2K_Q2] + u_sfx[J2K_Q2] + (u_ext[J2K_Q2] * 4); + + } else { + u_pfx[J2K_Q1] = vlc_decode_u_prefix(vlc_stream, vlc_buf); + + if (u_pfx[J2K_Q1] > 2) { + u[J2K_Q2] = jpeg2000_bitbuf_get_bits_lsb(vlc_stream, 1, vlc_buf) + 1; + u_sfx[J2K_Q1] = vlc_decode_u_suffix(vlc_stream, u_pfx[J2K_Q1], vlc_buf); + u_ext[J2K_Q1] = vlc_decode_u_extension(vlc_stream, u_sfx[J2K_Q1], vlc_buf); + } else { + u_pfx[J2K_Q2] = vlc_decode_u_prefix(vlc_stream, vlc_buf); + u_sfx[J2K_Q1] = vlc_decode_u_suffix(vlc_stream, u_pfx[J2K_Q1], vlc_buf); + u_sfx[J2K_Q2] = vlc_decode_u_suffix(vlc_stream, u_pfx[J2K_Q2], vlc_buf); + u_ext[J2K_Q1] = vlc_decode_u_extension(vlc_stream, u_sfx[J2K_Q1], vlc_buf); + u_ext[J2K_Q2] = vlc_decode_u_extension(vlc_stream, u_sfx[J2K_Q2], vlc_buf); + u[J2K_Q2] = u_pfx[J2K_Q2] + u_sfx[J2K_Q2] + (u_ext[J2K_Q2] * 4); + } + /* See Rec. ITU-T T.814, 7.3.6(3) */ + u[J2K_Q1] = u_pfx[J2K_Q1] + u_sfx[J2K_Q1] + (u_ext[J2K_Q1] * 4); + } + + } else if (res_off[J2K_Q1] == 1 || res_off[J2K_Q2] == 1) { + uint8_t pos = res_off[J2K_Q1] == 1 ? 0 : 1; + u_pfx[pos] = vlc_decode_u_prefix(vlc_stream, vlc_buf); + u_sfx[pos] = vlc_decode_u_suffix(vlc_stream, u_pfx[pos], vlc_buf); + u_ext[pos] = vlc_decode_u_extension(vlc_stream, u_sfx[pos], vlc_buf); + u[pos] = u_pfx[pos] + u_sfx[pos] + (u_ext[pos] * 4); + } + U[J2K_Q1] = kappa[J2K_Q1] + u[J2K_Q1]; + U[J2K_Q2] = kappa[J2K_Q2] + u[J2K_Q2]; + + for (int i = 0; i < 4; i++) { + m[J2K_Q1][i] = sigma_n[4 * q1 + i] * U[J2K_Q1] - ((emb_pat_k[J2K_Q1] >> i) & 1); + m[J2K_Q2][i] = sigma_n[4 * q2 + i] * U[J2K_Q2] - ((emb_pat_k[J2K_Q2] >> i) & 1); + } + + recover_mag_sgn(mag_sgn_stream, J2K_Q1, q1, m_n, known_1, emb_pat_1, v, m, + E, mu_n, Dcup, Pcup, pLSB); + + recover_mag_sgn(mag_sgn_stream, J2K_Q2, q2, m_n, known_1, emb_pat_1, v, m, + E, mu_n, Dcup, Pcup, pLSB); + + q += 2; // Move to the next quad pair + } + + if (quad_width % 2 == 1) { + q1 = q; + + if ((ret = jpeg2000_decode_sig_emb(s, mel_state, mel_stream, vlc_stream, + dec_cxt_vlc_table0, Dcup, sig_pat, res_off, + emb_pat_k, emb_pat_1, J2K_Q1, context, Lcup, + Pcup)) < 0) + goto free; + + for (int i = 0; i < 4; i++) + sigma_n[4 * q1 + i] = (sig_pat[J2K_Q1] >> i) & 1; + + u[J2K_Q1] = 0; + + if (res_off[J2K_Q1] == 1) { + u_pfx[J2K_Q1] = vlc_decode_u_prefix(vlc_stream, vlc_buf); + u_sfx[J2K_Q1] = vlc_decode_u_suffix(vlc_stream, u_pfx[J2K_Q1], vlc_buf); + u_ext[J2K_Q1] = vlc_decode_u_extension(vlc_stream, u_sfx[J2K_Q1], vlc_buf); + u[J2K_Q1] = u_pfx[J2K_Q1] + u_sfx[J2K_Q1] + (u_ext[J2K_Q1] * 4); + } + + U[J2K_Q1] = kappa[J2K_Q1] + u[J2K_Q1]; + + for (int i = 0; i < 4; i++) + m[J2K_Q1][i] = sigma_n[4 * q1 + i] * U[J2K_Q1] - ((emb_pat_k[J2K_Q1] >> i) & 1); + + recover_mag_sgn(mag_sgn_stream, J2K_Q1, q1, m_n, known_1, emb_pat_1, v, m, + E, mu_n, Dcup, Pcup, pLSB); + + q++; // move to next quad pair + } + + /** + * Initial line pair end. As an optimization, we can replace modulo + * operations with checking if a number is divisible , since that's the only + * thing we need. This is paired with is_divisible. Credits to Daniel Lemire + * blog post [1]. + * + * [1] + * https://lemire.me/blog/2019/02/08/faster-remainders-when-the-divisor-is-a-constant-beating-compilers-and-libdivide/ + * + * It's UB on zero, but the spec doesn't allow a quad being zero, so we + * error out early in case that's the case. + */ + c = precompute_c(quad_width); + + for (int row = 1; row < quad_height; row++) { + while ((q - (row * quad_width)) < quad_width - 1 && q < (quad_height * quad_width)) { + q1 = q; + q2 = q + 1; + context1 = sigma_n[4 * (q1 - quad_width) + 1]; + context1 += sigma_n[4 * (q1 - quad_width) + 3] << 2; // ne + + if (!is_divisible(q1, c)) { + context1 |= sigma_n[4 * (q1 - quad_width) - 1]; // nw + context1 += (sigma_n[4 * q1 - 1] | sigma_n[4 * q1 - 2]) << 1; // sw | q + } + if (!is_divisible(q1 + 1, c)) + context1 |= sigma_n[4 * (q1 - quad_width) + 5] << 2; + + if ((ret = jpeg2000_decode_sig_emb(s, mel_state, mel_stream, vlc_stream, + dec_cxt_vlc_table1, Dcup, sig_pat, res_off, + emb_pat_k, emb_pat_1, J2K_Q1, context1, Lcup, + Pcup)) + < 0) + goto free; + + for (int i = 0; i < 4; i++) + sigma_n[4 * q1 + i] = (sig_pat[J2K_Q1] >> i) & 1; + + context2 = sigma_n[4 * (q2 - quad_width) + 1]; + context2 += sigma_n[4 * (q2 - quad_width) + 3] << 2; + + if (!is_divisible(q2, c)) { + context2 |= sigma_n[4 * (q2 - quad_width) - 1]; + context2 += (sigma_n[4 * q2 - 1] | sigma_n[4 * q2 - 2]) << 1; + } + if (!is_divisible(q2 + 1, c)) + context2 |= sigma_n[4 * (q2 - quad_width) + 5] << 2; + + if ((ret = jpeg2000_decode_sig_emb(s, mel_state, mel_stream, vlc_stream, + dec_cxt_vlc_table1, Dcup, sig_pat, res_off, + emb_pat_k, emb_pat_1, J2K_Q2, context2, Lcup, + Pcup)) + < 0) + goto free; + + for (int i = 0; i < 4; i++) + sigma_n[4 * q2 + i] = (sig_pat[J2K_Q2] >> i) & 1; + + u[J2K_Q1] = 0; + u[J2K_Q2] = 0; + + jpeg2000_bitbuf_refill_backwards(vlc_stream, vlc_buf); + + if (res_off[J2K_Q1] == 1 && res_off[J2K_Q2] == 1) { + u_pfx[J2K_Q1] = vlc_decode_u_prefix(vlc_stream, vlc_buf); + u_pfx[J2K_Q2] = vlc_decode_u_prefix(vlc_stream, vlc_buf); + + u_sfx[J2K_Q1] = vlc_decode_u_suffix(vlc_stream, u_pfx[J2K_Q1], vlc_buf); + u_sfx[J2K_Q2] = vlc_decode_u_suffix(vlc_stream, u_pfx[J2K_Q2], vlc_buf); + + u_ext[J2K_Q1] = vlc_decode_u_extension(vlc_stream, u_sfx[J2K_Q1], vlc_buf); + u_ext[J2K_Q2] = vlc_decode_u_extension(vlc_stream, u_sfx[J2K_Q2], vlc_buf); + + u[J2K_Q1] = u_pfx[J2K_Q1] + u_sfx[J2K_Q1] + (u_ext[J2K_Q1] << 2); + u[J2K_Q2] = u_pfx[J2K_Q2] + u_sfx[J2K_Q2] + (u_ext[J2K_Q2] << 2); + + } else if (res_off[J2K_Q1] == 1 || res_off[J2K_Q2] == 1) { + uint8_t pos = res_off[J2K_Q1] == 1 ? 0 : 1; + + u_pfx[pos] = vlc_decode_u_prefix(vlc_stream, vlc_buf); + u_sfx[pos] = vlc_decode_u_suffix(vlc_stream, u_pfx[pos], vlc_buf); + u_ext[pos] = vlc_decode_u_extension(vlc_stream, u_sfx[pos], vlc_buf); + + u[pos] = u_pfx[pos] + u_sfx[pos] + (u_ext[pos] << 2); + } + sp = sig_pat[J2K_Q1]; + + gamma[J2K_Q1] = 1; + + if (sp == 0 || sp == 1 || sp == 2 || sp == 4 || sp == 8) + gamma[J2K_Q1] = 0; + + sp = sig_pat[J2K_Q2]; + + gamma[J2K_Q2] = 1; + + if (sp == 0 || sp == 1 || sp == 2 || sp == 4 || sp == 8) + gamma[J2K_Q2] = 0; + + E_n[J2K_Q1] = E[4 * (q1 - quad_width) + 1]; + E_n[J2K_Q2] = E[4 * (q2 - quad_width) + 1]; + + E_ne[J2K_Q1] = E[4 * (q1 - quad_width) + 3]; + E_ne[J2K_Q2] = E[4 * (q2 - quad_width) + 3]; + + E_nw[J2K_Q1] = (!is_divisible(q1, c)) * E[FFMAX((4 * (q1 - quad_width) - 1), 0)]; + E_nw[J2K_Q2] = (!is_divisible(q2, c)) * E[FFMAX((4 * (q2 - quad_width) - 1), 0)]; + + E_nf[J2K_Q1] = (!is_divisible(q1 + 1, c)) * E[4 * (q1 - quad_width) + 5]; + E_nf[J2K_Q2] = (!is_divisible(q2 + 1, c)) * E[4 * (q2 - quad_width) + 5]; + + max_e[J2K_Q1] = FFMAX(E_nw[J2K_Q1], FFMAX3(E_n[J2K_Q1], E_ne[J2K_Q1], E_nf[J2K_Q1])); + max_e[J2K_Q2] = FFMAX(E_nw[J2K_Q2], FFMAX3(E_n[J2K_Q2], E_ne[J2K_Q2], E_nf[J2K_Q2])); + + kappa[J2K_Q1] = FFMAX(1, gamma[J2K_Q1] * (max_e[J2K_Q1] - 1)); + kappa[J2K_Q2] = FFMAX(1, gamma[J2K_Q2] * (max_e[J2K_Q2] - 1)); + + U[J2K_Q1] = kappa[J2K_Q1] + u[J2K_Q1]; + U[J2K_Q2] = kappa[J2K_Q2] + u[J2K_Q2]; + + for (int i = 0; i < 4; i++) { + m[J2K_Q1][i] = sigma_n[4 * q1 + i] * U[J2K_Q1] - ((emb_pat_k[J2K_Q1] >> i) & 1); + m[J2K_Q2][i] = sigma_n[4 * q2 + i] * U[J2K_Q2] - ((emb_pat_k[J2K_Q2] >> i) & 1); + } + recover_mag_sgn(mag_sgn_stream, J2K_Q1, q1, m_n, known_1, emb_pat_1, v, m, + E, mu_n, Dcup, Pcup, pLSB); + + recover_mag_sgn(mag_sgn_stream, J2K_Q2, q2, m_n, known_1, emb_pat_1, v, m, + E, mu_n, Dcup, Pcup, pLSB); + + q += 2; // Move to the next quad pair + } + + if (quad_width % 2 == 1) { + q1 = q; + + /* calculate context for current quad */ + context1 = sigma_n[4 * (q1 - quad_width) + 1]; + context1 += (sigma_n[4 * (q1 - quad_width) + 3] << 2); + + if (!is_divisible(q1, c)) { + context1 |= sigma_n[4 * (q1 - quad_width) - 1]; + context1 += (sigma_n[4 * q1 - 1] | sigma_n[4 * q1 - 2]) << 1; + } + if (!is_divisible(q1 + 1, c)) + context1 |= sigma_n[4 * (q1 - quad_width) + 5] << 2; + + if ((ret = jpeg2000_decode_sig_emb(s, mel_state, mel_stream, vlc_stream, + dec_cxt_vlc_table1, Dcup, sig_pat, res_off, + emb_pat_k, emb_pat_1, J2K_Q1, context1, Lcup, + Pcup)) < 0) + goto free; + + for (int i = 0; i < 4; i++) + sigma_n[4 * q1 + i] = (sig_pat[J2K_Q1] >> i) & 1; + + u[J2K_Q1] = 0; + + /* Recover mag_sgn value */ + if (res_off[J2K_Q1] == 1) { + u_pfx[J2K_Q1] = vlc_decode_u_prefix(vlc_stream, vlc_buf); + u_sfx[J2K_Q1] = vlc_decode_u_suffix(vlc_stream, u_pfx[J2K_Q1], vlc_buf); + u_ext[J2K_Q1] = vlc_decode_u_extension(vlc_stream, u_sfx[J2K_Q1], vlc_buf); + + u[J2K_Q1] = u_pfx[J2K_Q1] + u_sfx[J2K_Q1] + (u_ext[J2K_Q1] << 2); + } + + sp = sig_pat[J2K_Q1]; + + gamma[J2K_Q1] = 1; + + if (sp == 0 || sp == 1 || sp == 2 || sp == 4 || sp == 8) + gamma[J2K_Q1] = 0; + + E_n[J2K_Q1] = E[4 * (q1 - quad_width) + 1]; + + E_ne[J2K_Q1] = E[4 * (q1 - quad_width) + 3]; + + E_nw[J2K_Q1] = (!is_divisible(q1, c)) * E[FFMAX((4 * (q1 - quad_width) - 1), 0)]; + + E_nf[J2K_Q1] = (!is_divisible(q1 + 1, c)) * E[4 * (q1 - quad_width) + 5]; + + max_e[J2K_Q1] = FFMAX(E_nw[J2K_Q1], FFMAX3(E_n[J2K_Q1], E_ne[J2K_Q1], E_nf[J2K_Q1])); + + kappa[J2K_Q1] = FFMAX(1, gamma[J2K_Q1] * (max_e[J2K_Q1] - 1)); + + U[J2K_Q1] = kappa[J2K_Q1] + u[J2K_Q1]; + + for (int i = 0; i < 4; i++) + m[J2K_Q1][i] = sigma_n[4 * q1 + i] * U[J2K_Q1] - ((emb_pat_k[J2K_Q1] >> i) & 1); + + recover_mag_sgn(mag_sgn_stream, J2K_Q1, q1, m_n, known_1, emb_pat_1, v, m, + E, mu_n, Dcup, Pcup, pLSB); + q += 1; + } + } + + // convert to raster-scan + for (int y = 0; y < quad_height; y++) { + for (int x = 0; x < quad_width; x++) { + int j1, j2; + int x1, x2 , x3; + + j1 = 2 * y; + j2 = 2 * x; + + sample_buf[j2 + (j1 * width)] = (int32_t)*mu; + jpeg2000_modify_state(j1, j2, width, *sigma, block_states); + sigma += 1; + mu += 1; + + x1 = y != quad_height - 1 || is_border_y == 0; + sample_buf[j2 + ((j1 + 1) * width)] = ((int32_t)*mu) * x1; + jpeg2000_modify_state(j1 + 1, j2, width, (*sigma) * x1, block_states); + sigma += 1; + mu += 1; + + x2 = x != quad_width - 1 || is_border_x == 0; + sample_buf[(j2 + 1) + (j1 * width)] = ((int32_t)*mu) * x2; + jpeg2000_modify_state(j1, j2 + 1, width, (*sigma) * x2, block_states); + sigma += 1; + mu += 1; + + x3 = x1 | x2; + sample_buf[(j2 + 1) + (j1 + 1) * width] = ((int32_t)*mu) * x3; + jpeg2000_modify_state(j1 + 1, j2 + 1, width, (*sigma) * x3, block_states); + sigma += 1; + mu += 1; + } + } + ret = 1; +free: + av_freep(&sigma_n); + av_freep(&E); + av_freep(&mu_n); + return ret; +} + +static void jpeg2000_calc_mbr(uint8_t *mbr, const uint16_t i, const uint16_t j, + const uint32_t mbr_info, uint8_t causal_cond, + uint8_t *block_states, int width) +{ + int local_mbr = 0; + + local_mbr |= jpeg2000_get_state(i - 1, j - 1, width, HT_SHIFT_SIGMA, block_states); + local_mbr |= jpeg2000_get_state(i - 1, j + 0, width, HT_SHIFT_SIGMA, block_states); + local_mbr |= jpeg2000_get_state(i - 1, j + 1, width, HT_SHIFT_SIGMA, block_states); + + local_mbr |= jpeg2000_get_state(i + 0, j - 1, width, HT_SHIFT_SIGMA, block_states); + local_mbr |= jpeg2000_get_state(i + 0, j + 1, width, HT_SHIFT_SIGMA, block_states); + + local_mbr |= jpeg2000_get_state(i + 1, j - 1, width, HT_SHIFT_SIGMA, block_states) * causal_cond; + local_mbr |= jpeg2000_get_state(i + 1, j + 0, width, HT_SHIFT_SIGMA, block_states) * causal_cond; + local_mbr |= jpeg2000_get_state(i + 1, j + 1, width, HT_SHIFT_SIGMA, block_states) * causal_cond; + + local_mbr |= jpeg2000_get_state(i - 1, j - 1, width, HT_SHIFT_REF, block_states) * + jpeg2000_get_state(i - 1, j - 1, width, HT_SHIFT_SCAN, block_states); + local_mbr |= jpeg2000_get_state(i - 1, j + 0, width, HT_SHIFT_REF, block_states) * + jpeg2000_get_state(i - 1, j - 1, width, HT_SHIFT_SCAN, block_states); + local_mbr |= jpeg2000_get_state(i - 1, j + 1, width, HT_SHIFT_REF, block_states) * + jpeg2000_get_state(i - 1, j + 1, width, HT_SHIFT_SCAN, block_states); + + local_mbr |= jpeg2000_get_state(i + 0, j - 1, width, HT_SHIFT_REF, block_states) * + jpeg2000_get_state(i + 0, j - 1, width, HT_SHIFT_SCAN, block_states); + local_mbr |= jpeg2000_get_state(i + 0, j + 1, width, HT_SHIFT_REF, block_states) * + jpeg2000_get_state(i + 0, j + 1, width, HT_SHIFT_SCAN, block_states); + + local_mbr |= jpeg2000_get_state(i + 1, j - 1, width, HT_SHIFT_REF, block_states) * + jpeg2000_get_state(i + 1, j - 1, width, HT_SHIFT_SCAN, block_states) * causal_cond; + local_mbr |= jpeg2000_get_state(i + 1, j + 0, width, HT_SHIFT_REF, block_states) * + jpeg2000_get_state(i + 1, j + 0, width, HT_SHIFT_SCAN, block_states) * causal_cond; + local_mbr |= jpeg2000_get_state(i + 1, j + 1, width, HT_SHIFT_REF, block_states) * + jpeg2000_get_state(i + 1, j + 1, width, HT_SHIFT_SCAN, block_states) * causal_cond; + + *mbr |= local_mbr; +} + +static void jpeg2000_process_stripes_block(StateVars *sig_prop, int i_s, int j_s, + int width, int height, int stride, int pLSB, + int32_t *sample_buf, uint8_t *block_states, + uint8_t *magref_segment, uint32_t magref_length) +{ + for (int j = j_s; j < j_s + width; j++) { + uint32_t mbr_info = 0; + for (int i = i_s; i < i_s + height; i++) { + int modify_state, cond; + uint8_t bit; + uint8_t causal_cond = i != (i_s + height - 1); + int32_t *sp = &sample_buf[j + (i * (stride - 2))]; + uint8_t mbr = 0; + + if (jpeg2000_get_state(i, j, stride - 2, HT_SHIFT_SIGMA, block_states) == 0) + jpeg2000_calc_mbr(&mbr, i, j, mbr_info & 0x1EF, causal_cond, block_states, stride - 2); + mbr_info >>= 3; + cond = mbr != 0; + bit = jpeg2000_peek_bit(sig_prop, magref_segment, magref_length); + *sp |= (bit * cond) << pLSB; + sig_prop->bits -= cond; + modify_state = (((1 << HT_SHIFT_REF_IND) | (1 << HT_SHIFT_REF)) * cond) | 1 << HT_SHIFT_SCAN; + jpeg2000_modify_state(i, j, stride - 2, modify_state, block_states); + } + } +} + +/** + * See procedure decodeSigPropMag at Rec. ITU-T T.814, 7.4. +*/ +av_noinline +static void jpeg2000_decode_sigprop_segment(Jpeg2000Cblk *cblk, uint16_t width, + uint16_t height, uint8_t *magref_segment, + uint32_t magref_length, uint8_t pLSB, + int32_t *sample_buf, uint8_t *block_states) +{ + StateVars sp_dec; + + const uint16_t num_v_stripe = height / 4; + const uint16_t num_h_stripe = width / 4; + int b_width = 4; + int b_height = 4; + int stride = width + 2; + + int last_width; + uint16_t i = 0, j = 0; + + jpeg2000_init_zero(&sp_dec); + + for (int n1 = 0; n1 < num_v_stripe; n1++) { + j = 0; + for (int n2 = 0; n2 < num_h_stripe; n2++) { + jpeg2000_process_stripes_block(&sp_dec, i, j, b_width, b_height, stride, + pLSB, sample_buf, block_states, magref_segment, + magref_length); + j += 4; + } + last_width = width % 4; + if (last_width) + jpeg2000_process_stripes_block(&sp_dec, i, j, last_width, b_height, stride, + pLSB, sample_buf, block_states, magref_segment, + magref_length); + i += 4; + } + + /* Decode remaining height stripes */ + b_height = height % 4; + j = 0; + for (int n2 = 0; n2 < num_h_stripe; n2++) { + jpeg2000_process_stripes_block(&sp_dec, i, j, b_width, b_height, stride, + pLSB, sample_buf, block_states, magref_segment, + magref_length); + j += 4; + } + last_width = width % 4; + if (last_width) + jpeg2000_process_stripes_block(&sp_dec, i, j, last_width, b_height, stride, + pLSB, sample_buf, block_states, magref_segment, + magref_length); +} + +/** + * See procedure decodeSigPropMag at Rec. ITU-T T.814, 7.5. +*/ +static int +jpeg2000_decode_magref_segment( uint16_t width, uint16_t block_height, + uint8_t *magref_segment,uint32_t magref_length, + uint8_t pLSB, int32_t *sample_buf, uint8_t *block_states) +{ + + StateVars mag_ref = { 0 }; + const uint16_t num_v_stripe = block_height / 4; + uint16_t height = 4; + uint16_t i_start = 0; + int32_t *sp; + + jpeg2000_init_mag_ref(&mag_ref, magref_length); + + for (int n1 = 0; n1 < num_v_stripe; n1++) { + for (int j = 0; j < width; j++) { + for (int i = i_start; i < i_start + height; i++) { + /** + * We move column wise, going from one quad to another. See + * Rec. ITU-T T.814, Figure 7. + */ + sp = &sample_buf[j + i * width]; + if (jpeg2000_get_state(i, j, width, HT_SHIFT_SIGMA, block_states) != 0) { + jpeg2000_modify_state(i, j, width, 1 << HT_SHIFT_REF_IND, block_states); + *sp |= jpeg2000_import_magref_bit(&mag_ref, magref_segment, magref_length) << pLSB; + } + } + } + i_start += 4; + } + height = block_height % 4; + for (int j = 0; j < width; j++) { + for (int i = i_start; i < i_start + height; i++) { + sp = &sample_buf[j + i * width]; + if (jpeg2000_get_state(i, j, width, HT_SHIFT_SIGMA, block_states) != 0) { + jpeg2000_modify_state(i, j, width, 1 << HT_SHIFT_REF_IND, block_states); + *sp |= jpeg2000_import_magref_bit(&mag_ref, magref_segment, magref_length) << pLSB; + } + } + } + return 1; +} + + +int +ff_jpeg2000_decode_htj2k(const Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *codsty, Jpeg2000T1Context *t1, Jpeg2000Cblk *cblk, + int width, int height, int magp, uint8_t roi_shift) +{ + uint8_t p0 = 0; // Number of placeholder passes + uint32_t Lcup; // Length of HT cleanup segment + uint32_t Lref; // Length of Refinement segment + uint32_t Scup; // HT cleanup segment suffix length + uint32_t Pcup; // HT cleanup segment prefix length + + uint8_t S_blk; // Number of skipped magnitude bitplanes + uint8_t pLSB; + + uint8_t *Dcup; // Byte of an HT cleanup segment + uint8_t *Dref; // Byte of an HT refinement segment + + int z_blk; // Number of ht coding pass + + uint8_t empty_passes; + + StateVars mag_sgn; // Magnitude and Sign + StateVars mel; // Adaptive run-length coding + StateVars vlc; // Variable Length coding + StateVars sig_prop; // Significance propagation + + MelDecoderState mel_state; + + int ret; + + /* Temporary buffers */ + int32_t *sample_buf = NULL; + uint8_t *block_states = NULL; + + int32_t n, val; // Post-processing + + int32_t M_b = magp; + + /* codeblock size as constrained by Rec. ITU-T T.800, Table A.18 */ + av_assert0(width <= 1024U && height <= 1024U); + av_assert0(width * height <= 4096); + av_assert0(width * height > 0); + + memset(t1->data, 0, t1->stride * height * sizeof(*t1->data)); + memset(t1->flags, 0, t1->stride * (height + 2) * sizeof(*t1->flags)); + + if (cblk->npasses == 0) + return 0; + + if (cblk->npasses > 3) + p0 = 0; + else if (cblk->length == 0) + p0 = 1; + + empty_passes = p0 * 3; + z_blk = cblk->npasses - empty_passes; + + if (z_blk <= 0) + return 0; // No passes within this set, continue + + Lcup = cblk->pass_lengths[0]; + Lref = cblk->pass_lengths[1]; + + if (Lcup < 2) { + av_log(s->avctx, AV_LOG_ERROR, + "Cleanup pass length must be at least 2 bytes in length\n"); + return AVERROR_INVALIDDATA; + } + Dcup = cblk->data; + Dref = cblk->data + Lcup; // Dref comes after the refinement segment + S_blk = p0 + cblk->zbp; + pLSB = 30 - S_blk; + + Scup = (Dcup[Lcup - 1] << 4) + (Dcup[Lcup - 2] & 0x0F); + + if (Scup < 2 || Scup > Lcup || Scup > 4079) { + av_log(s->avctx, AV_LOG_ERROR, "Cleanup pass suffix length is invalid %d\n", + Scup); + ret = AVERROR_INVALIDDATA; + goto free; + } + Pcup = Lcup - Scup; + + /* modDcup shall be done before the creation of vlc instance. */ + Dcup[Lcup - 1] = 0xFF; + Dcup[Lcup - 2] |= 0x0F; + + /* Magnitude and refinement */ + jpeg2000_init_zero(&mag_sgn); + jpeg2000_bitbuf_refill_forward(&mag_sgn, Dcup, Pcup); + + /* Significance propagation */ + jpeg2000_init_zero(&sig_prop); + + /* Adaptive run length */ + jpeg2000_init_mel(&mel, Pcup); + + /* Variable Length coding */ + jpeg2000_init_vlc(&vlc, Lcup, Pcup, Dcup); + + jpeg2000_init_mel_decoder(&mel_state); + + sample_buf = av_calloc((width + 4) * (height + 4), sizeof(int32_t)); + block_states = av_calloc((width + 4) * (height + 4), sizeof(uint8_t)); + + if (!sample_buf || !block_states) { + ret = AVERROR(ENOMEM); + goto free; + } + if ((ret = jpeg2000_decode_ht_cleanup_segment(s, cblk, t1, &mel_state, &mel, &vlc, + &mag_sgn, Dcup, Lcup, Pcup, pLSB, width, + height, sample_buf, block_states)) < 0) + goto free; + + if (cblk->npasses > 1) + jpeg2000_decode_sigprop_segment(cblk, width, height, Dref, Lref, + pLSB - 1, sample_buf, block_states); + + if (cblk->npasses > 2) { + + if (Lref < 2){ + av_log(s->avctx,AV_LOG_ERROR,"Invalid magnitude refinement length\n"); + ret = AVERROR_INVALIDDATA; + goto free; + } + if ((ret = jpeg2000_decode_magref_segment(width, height, Dref, Lref, + pLSB - 1, sample_buf, block_states)) < 0) + goto free; + } + + pLSB = 31 - M_b; + + /* Reconstruct the sample values */ + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + n = x + (y * t1->stride); + val = sample_buf[x + (y * width)]; + /* Convert sign-magnitude to two's complement. */ + val = val >> 31 ? 0x80000000 - val : val; + val >>= (pLSB - 1); + t1->data[n] = val; + } + } +free: + av_freep(&sample_buf); + av_freep(&block_states); + return ret; +} + +/** + * CtxVLC tables (see Rec. ITU-T T.800, Annex C) as found at + * https://github.com/osamu620/OpenHTJ2K (author: Osamu Watanabe) + */ +static const uint16_t dec_cxt_vlc_table1[1024] = { + 0x0016, 0x006A, 0x0046, 0x00DD, 0x0086, 0x888B, 0x0026, 0x444D, 0x0016, 0x00AA, 0x0046, 0x88AD, 0x0086, + 0x003A, 0x0026, 0x00DE, 0x0016, 0x00CA, 0x0046, 0x009D, 0x0086, 0x005A, 0x0026, 0x222D, 0x0016, 0x009A, + 0x0046, 0x007D, 0x0086, 0x01FD, 0x0026, 0x007E, 0x0016, 0x006A, 0x0046, 0x88CD, 0x0086, 0x888B, 0x0026, + 0x111D, 0x0016, 0x00AA, 0x0046, 0x005D, 0x0086, 0x003A, 0x0026, 0x00EE, 0x0016, 0x00CA, 0x0046, 0x00BD, + 0x0086, 0x005A, 0x0026, 0x11FF, 0x0016, 0x009A, 0x0046, 0x003D, 0x0086, 0x04ED, 0x0026, 0x2AAF, 0x0016, + 0x006A, 0x0046, 0x00DD, 0x0086, 0x888B, 0x0026, 0x444D, 0x0016, 0x00AA, 0x0046, 0x88AD, 0x0086, 0x003A, + 0x0026, 0x44EF, 0x0016, 0x00CA, 0x0046, 0x009D, 0x0086, 0x005A, 0x0026, 0x222D, 0x0016, 0x009A, 0x0046, + 0x007D, 0x0086, 0x01FD, 0x0026, 0x00BE, 0x0016, 0x006A, 0x0046, 0x88CD, 0x0086, 0x888B, 0x0026, 0x111D, + 0x0016, 0x00AA, 0x0046, 0x005D, 0x0086, 0x003A, 0x0026, 0x4CCF, 0x0016, 0x00CA, 0x0046, 0x00BD, 0x0086, + 0x005A, 0x0026, 0x00FE, 0x0016, 0x009A, 0x0046, 0x003D, 0x0086, 0x04ED, 0x0026, 0x006F, 0x0002, 0x0088, + 0x0002, 0x005C, 0x0002, 0x0018, 0x0002, 0x00DE, 0x0002, 0x0028, 0x0002, 0x009C, 0x0002, 0x004A, 0x0002, + 0x007E, 0x0002, 0x0088, 0x0002, 0x00CC, 0x0002, 0x0018, 0x0002, 0x888F, 0x0002, 0x0028, 0x0002, 0x00FE, + 0x0002, 0x003A, 0x0002, 0x222F, 0x0002, 0x0088, 0x0002, 0x04FD, 0x0002, 0x0018, 0x0002, 0x00BE, 0x0002, + 0x0028, 0x0002, 0x00BF, 0x0002, 0x004A, 0x0002, 0x006E, 0x0002, 0x0088, 0x0002, 0x00AC, 0x0002, 0x0018, + 0x0002, 0x444F, 0x0002, 0x0028, 0x0002, 0x00EE, 0x0002, 0x003A, 0x0002, 0x113F, 0x0002, 0x0088, 0x0002, + 0x005C, 0x0002, 0x0018, 0x0002, 0x00CF, 0x0002, 0x0028, 0x0002, 0x009C, 0x0002, 0x004A, 0x0002, 0x006F, + 0x0002, 0x0088, 0x0002, 0x00CC, 0x0002, 0x0018, 0x0002, 0x009F, 0x0002, 0x0028, 0x0002, 0x00EF, 0x0002, + 0x003A, 0x0002, 0x233F, 0x0002, 0x0088, 0x0002, 0x04FD, 0x0002, 0x0018, 0x0002, 0x00AF, 0x0002, 0x0028, + 0x0002, 0x44FF, 0x0002, 0x004A, 0x0002, 0x005F, 0x0002, 0x0088, 0x0002, 0x00AC, 0x0002, 0x0018, 0x0002, + 0x007F, 0x0002, 0x0028, 0x0002, 0x00DF, 0x0002, 0x003A, 0x0002, 0x111F, 0x0002, 0x0028, 0x0002, 0x005C, + 0x0002, 0x008A, 0x0002, 0x00BF, 0x0002, 0x0018, 0x0002, 0x00FE, 0x0002, 0x00CC, 0x0002, 0x007E, 0x0002, + 0x0028, 0x0002, 0x8FFF, 0x0002, 0x004A, 0x0002, 0x007F, 0x0002, 0x0018, 0x0002, 0x00DF, 0x0002, 0x00AC, + 0x0002, 0x133F, 0x0002, 0x0028, 0x0002, 0x222D, 0x0002, 0x008A, 0x0002, 0x00BE, 0x0002, 0x0018, 0x0002, + 0x44EF, 0x0002, 0x2AAD, 0x0002, 0x006E, 0x0002, 0x0028, 0x0002, 0x15FF, 0x0002, 0x004A, 0x0002, 0x009E, + 0x0002, 0x0018, 0x0002, 0x00CF, 0x0002, 0x003C, 0x0002, 0x223F, 0x0002, 0x0028, 0x0002, 0x005C, 0x0002, + 0x008A, 0x0002, 0x2BBF, 0x0002, 0x0018, 0x0002, 0x04EF, 0x0002, 0x00CC, 0x0002, 0x006F, 0x0002, 0x0028, + 0x0002, 0x27FF, 0x0002, 0x004A, 0x0002, 0x009F, 0x0002, 0x0018, 0x0002, 0x00DE, 0x0002, 0x00AC, 0x0002, + 0x444F, 0x0002, 0x0028, 0x0002, 0x222D, 0x0002, 0x008A, 0x0002, 0x8AAF, 0x0002, 0x0018, 0x0002, 0x00EE, + 0x0002, 0x2AAD, 0x0002, 0x005F, 0x0002, 0x0028, 0x0002, 0x44FF, 0x0002, 0x004A, 0x0002, 0x888F, 0x0002, + 0x0018, 0x0002, 0xAAAF, 0x0002, 0x003C, 0x0002, 0x111F, 0x0004, 0x8FFD, 0x0028, 0x005C, 0x0004, 0x00BC, + 0x008A, 0x66FF, 0x0004, 0x00CD, 0x0018, 0x111D, 0x0004, 0x009C, 0x003A, 0x8AAF, 0x0004, 0x00FC, 0x0028, + 0x133D, 0x0004, 0x00AC, 0x004A, 0x3BBF, 0x0004, 0x2BBD, 0x0018, 0x5FFF, 0x0004, 0x006C, 0x157D, 0x455F, + 0x0004, 0x2FFD, 0x0028, 0x222D, 0x0004, 0x22AD, 0x008A, 0x44EF, 0x0004, 0x00CC, 0x0018, 0x4FFF, 0x0004, + 0x007C, 0x003A, 0x447F, 0x0004, 0x04DD, 0x0028, 0x233D, 0x0004, 0x009D, 0x004A, 0x00DE, 0x0004, 0x88BD, + 0x0018, 0xAFFF, 0x0004, 0x115D, 0x1FFD, 0x444F, 0x0004, 0x8FFD, 0x0028, 0x005C, 0x0004, 0x00BC, 0x008A, + 0x8CEF, 0x0004, 0x00CD, 0x0018, 0x111D, 0x0004, 0x009C, 0x003A, 0x888F, 0x0004, 0x00FC, 0x0028, 0x133D, + 0x0004, 0x00AC, 0x004A, 0x44DF, 0x0004, 0x2BBD, 0x0018, 0x8AFF, 0x0004, 0x006C, 0x157D, 0x006F, 0x0004, + 0x2FFD, 0x0028, 0x222D, 0x0004, 0x22AD, 0x008A, 0x00EE, 0x0004, 0x00CC, 0x0018, 0x2EEF, 0x0004, 0x007C, + 0x003A, 0x277F, 0x0004, 0x04DD, 0x0028, 0x233D, 0x0004, 0x009D, 0x004A, 0x1BBF, 0x0004, 0x88BD, 0x0018, + 0x37FF, 0x0004, 0x115D, 0x1FFD, 0x333F, 0x0002, 0x0088, 0x0002, 0x02ED, 0x0002, 0x00CA, 0x0002, 0x4CCF, + 0x0002, 0x0048, 0x0002, 0x23FF, 0x0002, 0x001A, 0x0002, 0x888F, 0x0002, 0x0088, 0x0002, 0x006C, 0x0002, + 0x002A, 0x0002, 0x00AF, 0x0002, 0x0048, 0x0002, 0x22EF, 0x0002, 0x00AC, 0x0002, 0x005F, 0x0002, 0x0088, + 0x0002, 0x444D, 0x0002, 0x00CA, 0x0002, 0xCCCF, 0x0002, 0x0048, 0x0002, 0x00FE, 0x0002, 0x001A, 0x0002, + 0x006F, 0x0002, 0x0088, 0x0002, 0x005C, 0x0002, 0x002A, 0x0002, 0x009F, 0x0002, 0x0048, 0x0002, 0x00DF, + 0x0002, 0x03FD, 0x0002, 0x222F, 0x0002, 0x0088, 0x0002, 0x02ED, 0x0002, 0x00CA, 0x0002, 0x8CCF, 0x0002, + 0x0048, 0x0002, 0x11FF, 0x0002, 0x001A, 0x0002, 0x007E, 0x0002, 0x0088, 0x0002, 0x006C, 0x0002, 0x002A, + 0x0002, 0x007F, 0x0002, 0x0048, 0x0002, 0x00EE, 0x0002, 0x00AC, 0x0002, 0x003E, 0x0002, 0x0088, 0x0002, + 0x444D, 0x0002, 0x00CA, 0x0002, 0x00BE, 0x0002, 0x0048, 0x0002, 0x00BF, 0x0002, 0x001A, 0x0002, 0x003F, + 0x0002, 0x0088, 0x0002, 0x005C, 0x0002, 0x002A, 0x0002, 0x009E, 0x0002, 0x0048, 0x0002, 0x00DE, 0x0002, + 0x03FD, 0x0002, 0x111F, 0x0004, 0x8AED, 0x0048, 0x888D, 0x0004, 0x00DC, 0x00CA, 0x3FFF, 0x0004, 0xCFFD, + 0x002A, 0x003D, 0x0004, 0x00BC, 0x005A, 0x8DDF, 0x0004, 0x8FFD, 0x0048, 0x006C, 0x0004, 0x027D, 0x008A, + 0x99FF, 0x0004, 0x00EC, 0x00FA, 0x003C, 0x0004, 0x00AC, 0x001A, 0x009F, 0x0004, 0x2FFD, 0x0048, 0x007C, + 0x0004, 0x44CD, 0x00CA, 0x67FF, 0x0004, 0x1FFD, 0x002A, 0x444D, 0x0004, 0x00AD, 0x005A, 0x8CCF, 0x0004, + 0x4FFD, 0x0048, 0x445D, 0x0004, 0x01BD, 0x008A, 0x4EEF, 0x0004, 0x45DD, 0x00FA, 0x111D, 0x0004, 0x009C, + 0x001A, 0x222F, 0x0004, 0x8AED, 0x0048, 0x888D, 0x0004, 0x00DC, 0x00CA, 0xAFFF, 0x0004, 0xCFFD, 0x002A, + 0x003D, 0x0004, 0x00BC, 0x005A, 0x11BF, 0x0004, 0x8FFD, 0x0048, 0x006C, 0x0004, 0x027D, 0x008A, 0x22EF, + 0x0004, 0x00EC, 0x00FA, 0x003C, 0x0004, 0x00AC, 0x001A, 0x227F, 0x0004, 0x2FFD, 0x0048, 0x007C, 0x0004, + 0x44CD, 0x00CA, 0x5DFF, 0x0004, 0x1FFD, 0x002A, 0x444D, 0x0004, 0x00AD, 0x005A, 0x006F, 0x0004, 0x4FFD, + 0x0048, 0x445D, 0x0004, 0x01BD, 0x008A, 0x11DF, 0x0004, 0x45DD, 0x00FA, 0x111D, 0x0004, 0x009C, 0x001A, + 0x155F, 0x0006, 0x00FC, 0x0018, 0x111D, 0x0048, 0x888D, 0x00AA, 0x4DDF, 0x0006, 0x2AAD, 0x005A, 0x67FF, + 0x0028, 0x223D, 0x00BC, 0xAAAF, 0x0006, 0x00EC, 0x0018, 0x5FFF, 0x0048, 0x006C, 0x008A, 0xCCCF, 0x0006, + 0x009D, 0x00CA, 0x44EF, 0x0028, 0x003C, 0x8FFD, 0x137F, 0x0006, 0x8EED, 0x0018, 0x1FFF, 0x0048, 0x007C, + 0x00AA, 0x4CCF, 0x0006, 0x227D, 0x005A, 0x1DDF, 0x0028, 0x444D, 0x4FFD, 0x155F, 0x0006, 0x00DC, 0x0018, + 0x2EEF, 0x0048, 0x445D, 0x008A, 0x22BF, 0x0006, 0x009C, 0x00CA, 0x8CDF, 0x0028, 0x222D, 0x2FFD, 0x226F, + 0x0006, 0x00FC, 0x0018, 0x111D, 0x0048, 0x888D, 0x00AA, 0x1BBF, 0x0006, 0x2AAD, 0x005A, 0x33FF, 0x0028, + 0x223D, 0x00BC, 0x8AAF, 0x0006, 0x00EC, 0x0018, 0x9BFF, 0x0048, 0x006C, 0x008A, 0x8ABF, 0x0006, 0x009D, + 0x00CA, 0x4EEF, 0x0028, 0x003C, 0x8FFD, 0x466F, 0x0006, 0x8EED, 0x0018, 0xCFFF, 0x0048, 0x007C, 0x00AA, + 0x8CCF, 0x0006, 0x227D, 0x005A, 0xAEEF, 0x0028, 0x444D, 0x4FFD, 0x477F, 0x0006, 0x00DC, 0x0018, 0xAFFF, + 0x0048, 0x445D, 0x008A, 0x2BBF, 0x0006, 0x009C, 0x00CA, 0x44DF, 0x0028, 0x222D, 0x2FFD, 0x133F, 0x00F6, + 0xAFFD, 0x1FFB, 0x003C, 0x0008, 0x23BD, 0x007A, 0x11DF, 0x00F6, 0x45DD, 0x2FFB, 0x4EEF, 0x00DA, 0x177D, + 0xCFFD, 0x377F, 0x00F6, 0x3FFD, 0x8FFB, 0x111D, 0x0008, 0x009C, 0x005A, 0x1BBF, 0x00F6, 0x00CD, 0x00BA, + 0x8DDF, 0x4FFB, 0x006C, 0x9BFD, 0x455F, 0x00F6, 0x67FD, 0x1FFB, 0x002C, 0x0008, 0x00AC, 0x007A, 0x009F, + 0x00F6, 0x00AD, 0x2FFB, 0x7FFF, 0x00DA, 0x004C, 0x5FFD, 0x477F, 0x00F6, 0x00EC, 0x8FFB, 0x001C, 0x0008, + 0x008C, 0x005A, 0x888F, 0x00F6, 0x00CC, 0x00BA, 0x2EEF, 0x4FFB, 0x115D, 0x8AED, 0x113F, 0x00F6, 0xAFFD, + 0x1FFB, 0x003C, 0x0008, 0x23BD, 0x007A, 0x1DDF, 0x00F6, 0x45DD, 0x2FFB, 0xBFFF, 0x00DA, 0x177D, 0xCFFD, + 0x447F, 0x00F6, 0x3FFD, 0x8FFB, 0x111D, 0x0008, 0x009C, 0x005A, 0x277F, 0x00F6, 0x00CD, 0x00BA, 0x22EF, + 0x4FFB, 0x006C, 0x9BFD, 0x444F, 0x00F6, 0x67FD, 0x1FFB, 0x002C, 0x0008, 0x00AC, 0x007A, 0x11BF, 0x00F6, + 0x00AD, 0x2FFB, 0xFFFF, 0x00DA, 0x004C, 0x5FFD, 0x233F, 0x00F6, 0x00EC, 0x8FFB, 0x001C, 0x0008, 0x008C, + 0x005A, 0x006F, 0x00F6, 0x00CC, 0x00BA, 0x8BBF, 0x4FFB, 0x115D, 0x8AED, 0x222F}; + +static const uint16_t dec_cxt_vlc_table0[1024] = { + 0x0026, 0x00AA, 0x0046, 0x006C, 0x0086, 0x8AED, 0x0018, 0x8DDF, 0x0026, 0x01BD, 0x0046, 0x5FFF, 0x0086, + 0x027D, 0x005A, 0x155F, 0x0026, 0x003A, 0x0046, 0x444D, 0x0086, 0x4CCD, 0x0018, 0xCCCF, 0x0026, 0x2EFD, + 0x0046, 0x99FF, 0x0086, 0x009C, 0x00CA, 0x133F, 0x0026, 0x00AA, 0x0046, 0x445D, 0x0086, 0x8CCD, 0x0018, + 0x11DF, 0x0026, 0x4FFD, 0x0046, 0xCFFF, 0x0086, 0x009D, 0x005A, 0x007E, 0x0026, 0x003A, 0x0046, 0x1FFF, + 0x0086, 0x88AD, 0x0018, 0x00BE, 0x0026, 0x8FFD, 0x0046, 0x4EEF, 0x0086, 0x888D, 0x00CA, 0x111F, 0x0026, + 0x00AA, 0x0046, 0x006C, 0x0086, 0x8AED, 0x0018, 0x45DF, 0x0026, 0x01BD, 0x0046, 0x22EF, 0x0086, 0x027D, + 0x005A, 0x227F, 0x0026, 0x003A, 0x0046, 0x444D, 0x0086, 0x4CCD, 0x0018, 0x11BF, 0x0026, 0x2EFD, 0x0046, + 0x00FE, 0x0086, 0x009C, 0x00CA, 0x223F, 0x0026, 0x00AA, 0x0046, 0x445D, 0x0086, 0x8CCD, 0x0018, 0x00DE, + 0x0026, 0x4FFD, 0x0046, 0xABFF, 0x0086, 0x009D, 0x005A, 0x006F, 0x0026, 0x003A, 0x0046, 0x6EFF, 0x0086, + 0x88AD, 0x0018, 0x2AAF, 0x0026, 0x8FFD, 0x0046, 0x00EE, 0x0086, 0x888D, 0x00CA, 0x222F, 0x0004, 0x00CA, + 0x0088, 0x027D, 0x0004, 0x4CCD, 0x0028, 0x00FE, 0x0004, 0x2AFD, 0x0048, 0x005C, 0x0004, 0x009D, 0x0018, + 0x00DE, 0x0004, 0x01BD, 0x0088, 0x006C, 0x0004, 0x88AD, 0x0028, 0x11DF, 0x0004, 0x8AED, 0x0048, 0x003C, + 0x0004, 0x888D, 0x0018, 0x111F, 0x0004, 0x00CA, 0x0088, 0x006D, 0x0004, 0x88CD, 0x0028, 0x88FF, 0x0004, + 0x8BFD, 0x0048, 0x444D, 0x0004, 0x009C, 0x0018, 0x00BE, 0x0004, 0x4EFD, 0x0088, 0x445D, 0x0004, 0x00AC, + 0x0028, 0x00EE, 0x0004, 0x45DD, 0x0048, 0x222D, 0x0004, 0x003D, 0x0018, 0x007E, 0x0004, 0x00CA, 0x0088, + 0x027D, 0x0004, 0x4CCD, 0x0028, 0x1FFF, 0x0004, 0x2AFD, 0x0048, 0x005C, 0x0004, 0x009D, 0x0018, 0x11BF, + 0x0004, 0x01BD, 0x0088, 0x006C, 0x0004, 0x88AD, 0x0028, 0x22EF, 0x0004, 0x8AED, 0x0048, 0x003C, 0x0004, + 0x888D, 0x0018, 0x227F, 0x0004, 0x00CA, 0x0088, 0x006D, 0x0004, 0x88CD, 0x0028, 0x4EEF, 0x0004, 0x8BFD, + 0x0048, 0x444D, 0x0004, 0x009C, 0x0018, 0x2AAF, 0x0004, 0x4EFD, 0x0088, 0x445D, 0x0004, 0x00AC, 0x0028, + 0x8DDF, 0x0004, 0x45DD, 0x0048, 0x222D, 0x0004, 0x003D, 0x0018, 0x155F, 0x0004, 0x005A, 0x0088, 0x006C, + 0x0004, 0x88DD, 0x0028, 0x23FF, 0x0004, 0x11FD, 0x0048, 0x444D, 0x0004, 0x00AD, 0x0018, 0x00BE, 0x0004, + 0x137D, 0x0088, 0x155D, 0x0004, 0x00CC, 0x0028, 0x00DE, 0x0004, 0x02ED, 0x0048, 0x111D, 0x0004, 0x009D, + 0x0018, 0x007E, 0x0004, 0x005A, 0x0088, 0x455D, 0x0004, 0x44CD, 0x0028, 0x00EE, 0x0004, 0x1FFD, 0x0048, + 0x003C, 0x0004, 0x00AC, 0x0018, 0x555F, 0x0004, 0x47FD, 0x0088, 0x113D, 0x0004, 0x02BD, 0x0028, 0x477F, + 0x0004, 0x4CDD, 0x0048, 0x8FFF, 0x0004, 0x009C, 0x0018, 0x222F, 0x0004, 0x005A, 0x0088, 0x006C, 0x0004, + 0x88DD, 0x0028, 0x00FE, 0x0004, 0x11FD, 0x0048, 0x444D, 0x0004, 0x00AD, 0x0018, 0x888F, 0x0004, 0x137D, + 0x0088, 0x155D, 0x0004, 0x00CC, 0x0028, 0x8CCF, 0x0004, 0x02ED, 0x0048, 0x111D, 0x0004, 0x009D, 0x0018, + 0x006F, 0x0004, 0x005A, 0x0088, 0x455D, 0x0004, 0x44CD, 0x0028, 0x1DDF, 0x0004, 0x1FFD, 0x0048, 0x003C, + 0x0004, 0x00AC, 0x0018, 0x227F, 0x0004, 0x47FD, 0x0088, 0x113D, 0x0004, 0x02BD, 0x0028, 0x22BF, 0x0004, + 0x4CDD, 0x0048, 0x22EF, 0x0004, 0x009C, 0x0018, 0x233F, 0x0006, 0x4DDD, 0x4FFB, 0xCFFF, 0x0018, 0x113D, + 0x005A, 0x888F, 0x0006, 0x23BD, 0x008A, 0x00EE, 0x002A, 0x155D, 0xAAFD, 0x277F, 0x0006, 0x44CD, 0x8FFB, + 0x44EF, 0x0018, 0x467D, 0x004A, 0x2AAF, 0x0006, 0x00AC, 0x555B, 0x99DF, 0x1FFB, 0x003C, 0x5FFD, 0x266F, + 0x0006, 0x1DDD, 0x4FFB, 0x6EFF, 0x0018, 0x177D, 0x005A, 0x1BBF, 0x0006, 0x88AD, 0x008A, 0x5DDF, 0x002A, + 0x444D, 0x2FFD, 0x667F, 0x0006, 0x00CC, 0x8FFB, 0x2EEF, 0x0018, 0x455D, 0x004A, 0x119F, 0x0006, 0x009C, + 0x555B, 0x8CCF, 0x1FFB, 0x111D, 0x8CED, 0x006E, 0x0006, 0x4DDD, 0x4FFB, 0x3FFF, 0x0018, 0x113D, 0x005A, + 0x11BF, 0x0006, 0x23BD, 0x008A, 0x8DDF, 0x002A, 0x155D, 0xAAFD, 0x222F, 0x0006, 0x44CD, 0x8FFB, 0x00FE, + 0x0018, 0x467D, 0x004A, 0x899F, 0x0006, 0x00AC, 0x555B, 0x00DE, 0x1FFB, 0x003C, 0x5FFD, 0x446F, 0x0006, + 0x1DDD, 0x4FFB, 0x9BFF, 0x0018, 0x177D, 0x005A, 0x00BE, 0x0006, 0x88AD, 0x008A, 0xCDDF, 0x002A, 0x444D, + 0x2FFD, 0x007E, 0x0006, 0x00CC, 0x8FFB, 0x4EEF, 0x0018, 0x455D, 0x004A, 0x377F, 0x0006, 0x009C, 0x555B, + 0x8BBF, 0x1FFB, 0x111D, 0x8CED, 0x233F, 0x0004, 0x00AA, 0x0088, 0x047D, 0x0004, 0x01DD, 0x0028, 0x11DF, + 0x0004, 0x27FD, 0x0048, 0x005C, 0x0004, 0x8AAD, 0x0018, 0x2BBF, 0x0004, 0x009C, 0x0088, 0x006C, 0x0004, + 0x00CC, 0x0028, 0x00EE, 0x0004, 0x8CED, 0x0048, 0x222D, 0x0004, 0x888D, 0x0018, 0x007E, 0x0004, 0x00AA, + 0x0088, 0x006D, 0x0004, 0x88CD, 0x0028, 0x00FE, 0x0004, 0x19FD, 0x0048, 0x003C, 0x0004, 0x2AAD, 0x0018, + 0xAAAF, 0x0004, 0x8BFD, 0x0088, 0x005D, 0x0004, 0x00BD, 0x0028, 0x4CCF, 0x0004, 0x44ED, 0x0048, 0x4FFF, + 0x0004, 0x223D, 0x0018, 0x111F, 0x0004, 0x00AA, 0x0088, 0x047D, 0x0004, 0x01DD, 0x0028, 0x99FF, 0x0004, + 0x27FD, 0x0048, 0x005C, 0x0004, 0x8AAD, 0x0018, 0x00BE, 0x0004, 0x009C, 0x0088, 0x006C, 0x0004, 0x00CC, + 0x0028, 0x00DE, 0x0004, 0x8CED, 0x0048, 0x222D, 0x0004, 0x888D, 0x0018, 0x444F, 0x0004, 0x00AA, 0x0088, + 0x006D, 0x0004, 0x88CD, 0x0028, 0x2EEF, 0x0004, 0x19FD, 0x0048, 0x003C, 0x0004, 0x2AAD, 0x0018, 0x447F, + 0x0004, 0x8BFD, 0x0088, 0x005D, 0x0004, 0x00BD, 0x0028, 0x009F, 0x0004, 0x44ED, 0x0048, 0x67FF, 0x0004, + 0x223D, 0x0018, 0x133F, 0x0006, 0x00CC, 0x008A, 0x9DFF, 0x2FFB, 0x467D, 0x1FFD, 0x99BF, 0x0006, 0x2AAD, + 0x002A, 0x66EF, 0x4FFB, 0x005C, 0x2EED, 0x377F, 0x0006, 0x89BD, 0x004A, 0x00FE, 0x8FFB, 0x006C, 0x67FD, + 0x889F, 0x0006, 0x888D, 0x001A, 0x5DDF, 0x00AA, 0x222D, 0x89DD, 0x444F, 0x0006, 0x2BBD, 0x008A, 0xCFFF, + 0x2FFB, 0x226D, 0x009C, 0x00BE, 0x0006, 0xAAAD, 0x002A, 0x1DDF, 0x4FFB, 0x003C, 0x4DDD, 0x466F, 0x0006, + 0x8AAD, 0x004A, 0xAEEF, 0x8FFB, 0x445D, 0x8EED, 0x177F, 0x0006, 0x233D, 0x001A, 0x4CCF, 0x00AA, 0xAFFF, + 0x88CD, 0x133F, 0x0006, 0x00CC, 0x008A, 0x77FF, 0x2FFB, 0x467D, 0x1FFD, 0x3BBF, 0x0006, 0x2AAD, 0x002A, + 0x00EE, 0x4FFB, 0x005C, 0x2EED, 0x007E, 0x0006, 0x89BD, 0x004A, 0x4EEF, 0x8FFB, 0x006C, 0x67FD, 0x667F, + 0x0006, 0x888D, 0x001A, 0x00DE, 0x00AA, 0x222D, 0x89DD, 0x333F, 0x0006, 0x2BBD, 0x008A, 0x57FF, 0x2FFB, + 0x226D, 0x009C, 0x199F, 0x0006, 0xAAAD, 0x002A, 0x99DF, 0x4FFB, 0x003C, 0x4DDD, 0x155F, 0x0006, 0x8AAD, + 0x004A, 0xCEEF, 0x8FFB, 0x445D, 0x8EED, 0x277F, 0x0006, 0x233D, 0x001A, 0x1BBF, 0x00AA, 0x3FFF, 0x88CD, + 0x111F, 0x0006, 0x45DD, 0x2FFB, 0x111D, 0x0018, 0x467D, 0x8FFD, 0xCCCF, 0x0006, 0x19BD, 0x004A, 0x22EF, + 0x002A, 0x222D, 0x3FFD, 0x888F, 0x0006, 0x00CC, 0x008A, 0x00FE, 0x0018, 0x115D, 0xCFFD, 0x8AAF, 0x0006, + 0x00AC, 0x003A, 0x8CDF, 0x1FFB, 0x133D, 0x66FD, 0x466F, 0x0006, 0x8CCD, 0x2FFB, 0x5FFF, 0x0018, 0x006C, + 0x4FFD, 0xABBF, 0x0006, 0x22AD, 0x004A, 0x00EE, 0x002A, 0x233D, 0xAEFD, 0x377F, 0x0006, 0x2BBD, 0x008A, + 0x55DF, 0x0018, 0x005C, 0x177D, 0x119F, 0x0006, 0x009C, 0x003A, 0x4CCF, 0x1FFB, 0x333D, 0x8EED, 0x444F, + 0x0006, 0x45DD, 0x2FFB, 0x111D, 0x0018, 0x467D, 0x8FFD, 0x99BF, 0x0006, 0x19BD, 0x004A, 0x2EEF, 0x002A, + 0x222D, 0x3FFD, 0x667F, 0x0006, 0x00CC, 0x008A, 0x4EEF, 0x0018, 0x115D, 0xCFFD, 0x899F, 0x0006, 0x00AC, + 0x003A, 0x00DE, 0x1FFB, 0x133D, 0x66FD, 0x226F, 0x0006, 0x8CCD, 0x2FFB, 0x9BFF, 0x0018, 0x006C, 0x4FFD, + 0x00BE, 0x0006, 0x22AD, 0x004A, 0x1DDF, 0x002A, 0x233D, 0xAEFD, 0x007E, 0x0006, 0x2BBD, 0x008A, 0xCEEF, + 0x0018, 0x005C, 0x177D, 0x277F, 0x0006, 0x009C, 0x003A, 0x8BBF, 0x1FFB, 0x333D, 0x8EED, 0x455F, 0x1FF9, + 0x1DDD, 0xAFFB, 0x00DE, 0x8FF9, 0x001C, 0xFFFB, 0x477F, 0x4FF9, 0x177D, 0x3FFB, 0x3BBF, 0x2FF9, 0xAEEF, + 0x8EED, 0x444F, 0x1FF9, 0x22AD, 0x000A, 0x8BBF, 0x8FF9, 0x00FE, 0xCFFD, 0x007E, 0x4FF9, 0x115D, 0x5FFB, + 0x577F, 0x2FF9, 0x8DDF, 0x2EED, 0x333F, 0x1FF9, 0x2BBD, 0xAFFB, 0x88CF, 0x8FF9, 0xBFFF, 0xFFFB, 0x377F, + 0x4FF9, 0x006D, 0x3FFB, 0x00BE, 0x2FF9, 0x66EF, 0x9FFD, 0x133F, 0x1FF9, 0x009D, 0x000A, 0xABBF, 0x8FF9, + 0xDFFF, 0x6FFD, 0x006E, 0x4FF9, 0x002C, 0x5FFB, 0x888F, 0x2FF9, 0xCDDF, 0x4DDD, 0x222F, 0x1FF9, 0x1DDD, + 0xAFFB, 0x4CCF, 0x8FF9, 0x001C, 0xFFFB, 0x277F, 0x4FF9, 0x177D, 0x3FFB, 0x99BF, 0x2FF9, 0xCEEF, 0x8EED, + 0x004E, 0x1FF9, 0x22AD, 0x000A, 0x00AE, 0x8FF9, 0x7FFF, 0xCFFD, 0x005E, 0x4FF9, 0x115D, 0x5FFB, 0x009E, + 0x2FF9, 0x5DDF, 0x2EED, 0x003E, 0x1FF9, 0x2BBD, 0xAFFB, 0x00CE, 0x8FF9, 0xEFFF, 0xFFFB, 0x667F, 0x4FF9, + 0x006D, 0x3FFB, 0x8AAF, 0x2FF9, 0x00EE, 0x9FFD, 0x233F, 0x1FF9, 0x009D, 0x000A, 0x1BBF, 0x8FF9, 0x4EEF, + 0x6FFD, 0x455F, 0x4FF9, 0x002C, 0x5FFB, 0x008E, 0x2FF9, 0x99DF, 0x4DDD, 0x111F}; diff --git a/libavcodec/jpeg2000htdec.h b/libavcodec/jpeg2000htdec.h new file mode 100644 index 00000000000..572d095c927 --- /dev/null +++ b/libavcodec/jpeg2000htdec.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2022 Caleb Etemesi + * + * 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_JPEG2000HTDEC_H +#define AVCODEC_JPEG2000HTDEC_H + +#include "jpeg2000dec.h" + +/** + * HT Block decoder as specified in Rec. ITU-T T.814 | ISO/IEC 15444-15 + */ + +int ff_jpeg2000_decode_htj2k(const Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *codsty, + Jpeg2000T1Context *t1, Jpeg2000Cblk *cblk, int width, + int height, int magp, uint8_t roi_shift); + +#endif /* AVCODEC_JPEG2000HTDEC_H */ diff --git a/libavcodec/jpeglsenc.c b/libavcodec/jpeglsenc.c index 5ee39ac2d63..53394102df3 100644 --- a/libavcodec/jpeglsenc.c +++ b/libavcodec/jpeglsenc.c @@ -476,7 +476,8 @@ const FFCodec ff_jpegls_encoder = { CODEC_LONG_NAME("JPEG-LS"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_JPEGLS, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(JPEGLSContext), .p.priv_class = &jpegls_class, .init = encode_jpegls_init, diff --git a/libavcodec/jvdec.c b/libavcodec/jvdec.c index e0287a9cb9d..13ede9068a8 100644 --- a/libavcodec/jvdec.c +++ b/libavcodec/jvdec.c @@ -37,7 +37,9 @@ typedef struct JvContext { BlockDSPContext bdsp; AVFrame *frame; uint32_t palette[AVPALETTE_COUNT]; +#if FF_API_PALETTE_HAS_CHANGED int palette_has_changed; +#endif } JvContext; static av_cold int decode_init(AVCodecContext *avctx) @@ -207,14 +209,20 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, s->palette[i] = 0xFFU << 24 | pal << 2 | ((pal >> 4) & 0x30303); buf += 3; } +#if FF_API_PALETTE_HAS_CHANGED s->palette_has_changed = 1; +#endif } if (video_size) { - s->frame->key_frame = 1; + s->frame->flags |= AV_FRAME_FLAG_KEY; s->frame->pict_type = AV_PICTURE_TYPE_I; +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS s->frame->palette_has_changed = s->palette_has_changed; s->palette_has_changed = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif memcpy(s->frame->data[1], s->palette, AVPALETTE_SIZE); if ((ret = av_frame_ref(rframe, s->frame)) < 0) diff --git a/libavcodec/kbdwin.c b/libavcodec/kbdwin.c index bf32aeb3176..eacdb467742 100644 --- a/libavcodec/kbdwin.c +++ b/libavcodec/kbdwin.c @@ -19,39 +19,49 @@ #include "libavutil/avassert.h" #include "libavutil/mathematics.h" #include "libavutil/attributes.h" +#include "libavutil/mem.h" #include "kbdwin.h" -#define BESSEL_I0_ITER 50 // default: 50 iterations of Bessel I0 approximation - -av_cold void ff_kbd_window_init(float *window, float alpha, int n) +av_cold static int kbd_window_init(float *float_window, int *int_window, float alpha, int n) { - int i, j; - double sum = 0.0, bessel, tmp; - double local_window[FF_KBD_WINDOW_MAX]; - double alpha2 = (alpha * M_PI / n) * (alpha * M_PI / n); + int i; + double sum = 0.0, tmp; + double scale = 0.0; + double temp_small[FF_KBD_WINDOW_MAX / 2 + 1]; + double *temp= n<=FF_KBD_WINDOW_MAX ? temp_small : av_malloc((n/2+1) * sizeof(*temp)); + double alpha2 = 4 * (alpha * M_PI / n) * (alpha * M_PI / n); - av_assert0(n <= FF_KBD_WINDOW_MAX); + if (!temp) + return AVERROR(ENOMEM); - for (i = 0; i < n; i++) { - tmp = i * (n - i) * alpha2; - bessel = 1.0; - for (j = BESSEL_I0_ITER; j > 0; j--) - bessel = bessel * tmp / (j * j) + 1; - sum += bessel; - local_window[i] = sum; + for (i = 0; i <= n / 2; i++) { + tmp = alpha2 * i * (n - i); + temp[i] = av_bessel_i0(sqrt(tmp)); + scale += temp[i] * (1 + (i && i /** - * Maximum window size for ff_kbd_window_init. + * Maximum window size for avpriv_kbd_window_init. */ #define FF_KBD_WINDOW_MAX 1024 @@ -30,9 +30,11 @@ * Generate a Kaiser-Bessel Derived Window. * @param window pointer to half window * @param alpha determines window shape - * @param n size of half window, max FF_KBD_WINDOW_MAX + * @param n size of half window + * + * @return if n is larger than FF_KBD_WINDOW_MAX then AVERROR(ENOMEM) is possible */ -void ff_kbd_window_init(float *window, float alpha, int n); -void ff_kbd_window_init_fixed(int32_t *window, float alpha, int n); +int avpriv_kbd_window_init(float *window, float alpha, int n); +int avpriv_kbd_window_init_fixed(int32_t *window, float alpha, int n); #endif /* AVCODEC_KBDWIN_H */ diff --git a/libavcodec/kmvc.c b/libavcodec/kmvc.c index 153cea03b9c..83aba4b2528 100644 --- a/libavcodec/kmvc.c +++ b/libavcodec/kmvc.c @@ -273,7 +273,14 @@ static int decode_frame(AVCodecContext * avctx, AVFrame *frame, if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) return ret; - frame->palette_has_changed = ff_copy_palette(ctx->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS + frame->palette_has_changed = +#endif + ff_copy_palette(ctx->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_ENABLE_DEPRECATION_WARNINGS +#endif header = bytestream2_get_byte(&ctx->g); @@ -288,15 +295,19 @@ static int decode_frame(AVCodecContext * avctx, AVFrame *frame, } if (header & KMVC_KEYFRAME) { - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; } else { - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_P; } if (header & KMVC_PALETTE) { +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif // palette starts from index 1 and has 127 entries for (i = 1; i <= ctx->palsize; i++) { ctx->pal[i] = 0xFFU << 24 | bytestream2_get_be24(&ctx->g); @@ -305,7 +316,11 @@ static int decode_frame(AVCodecContext * avctx, AVFrame *frame, if (ctx->setpal) { ctx->setpal = 0; +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } /* make the palette available on the way out */ diff --git a/libavcodec/lagarith.c b/libavcodec/lagarith.c index 78ccbc15b9c..ebc1f7613a4 100644 --- a/libavcodec/lagarith.c +++ b/libavcodec/lagarith.c @@ -550,7 +550,7 @@ static int lag_decode_frame(AVCodecContext *avctx, AVFrame *p, int i, j, planes = 3; int ret = 0; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; p->pict_type = AV_PICTURE_TYPE_I; frametype = buf[0]; diff --git a/libavcodec/lcldec.c b/libavcodec/lcldec.c index 5cc0a29bcd2..ed78d9d5703 100644 --- a/libavcodec/lcldec.c +++ b/libavcodec/lcldec.c @@ -152,6 +152,8 @@ static int zlib_decomp(AVCodecContext *avctx, const uint8_t *src, int src_len, i if (expected != (unsigned int)zstream->total_out) { av_log(avctx, AV_LOG_ERROR, "Decoded size differs (%d != %lu)\n", expected, zstream->total_out); + if (expected > (unsigned int)zstream->total_out) + return (unsigned int)zstream->total_out; return AVERROR_UNKNOWN; } return zstream->total_out; @@ -169,8 +171,8 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, int row, col; unsigned char *encoded = avpkt->data, *outptr; uint8_t *y_out, *u_out, *v_out; - unsigned int width = avctx->width; // Real image width - unsigned int height = avctx->height; // Real image height + int width = avctx->width; // Real image width + int height = avctx->height; // Real image height unsigned int mszh_dlen; unsigned char yq, y1q, uq, vq; int uqvq, ret; @@ -219,7 +221,9 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if (c->decomp_size != mszh_dlen) { av_log(avctx, AV_LOG_ERROR, "Decoded size differs (%d != %d)\n", c->decomp_size, mszh_dlen); - return AVERROR_INVALIDDATA; + if (c->decomp_size != mszh_dlen && + c->decomp_size != mszh_dlen + 2) // YUV420 306x306 is missing 2 bytes + return AVERROR_INVALIDDATA; } encoded = c->decomp_buf; len = mszh_dlen; @@ -276,12 +280,13 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, ret = zlib_decomp(avctx, buf + 8 + mthread_inlen, len - 8 - mthread_inlen, mthread_outlen, mthread_outlen); if (ret < 0) return ret; + len = c->decomp_size; } else { int ret = zlib_decomp(avctx, buf, len, 0, c->decomp_size); if (ret < 0) return ret; + len = ret; } encoded = c->decomp_buf; - len = c->decomp_size; break; #endif default: @@ -403,6 +408,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, v_out[ col >> 1 ] = *encoded++ + 128; v_out[(col >> 1) + 1] = *encoded++ + 128; } + if (col && col < width) { + u_out[ col >> 1 ] = u_out[(col>>1) - 1]; + v_out[ col >> 1 ] = v_out[(col>>1) - 1]; + } + y_out -= frame->linesize[0]; u_out -= frame->linesize[1]; v_out -= frame->linesize[2]; @@ -424,6 +434,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, u_out[col >> 2] = *encoded++ + 128; v_out[col >> 2] = *encoded++ + 128; } + if (col && col < width) { + u_out[col >> 2] = u_out[(col>>2) - 1]; + v_out[col >> 2] = v_out[(col>>2) - 1]; + } y_out -= frame->linesize[0]; u_out -= frame->linesize[1]; v_out -= frame->linesize[2]; @@ -464,7 +478,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, return AVERROR_INVALIDDATA; } - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; *got_frame = 1; @@ -481,6 +495,7 @@ static av_cold int decode_init(AVCodecContext *avctx) FFALIGN(avctx->height, 4); unsigned int max_decomp_size; int subsample_h, subsample_v; + int partial_h_supported = 0; if (avctx->extradata_size < 8) { av_log(avctx, AV_LOG_ERROR, "Extradata size too small.\n"); @@ -502,26 +517,24 @@ static av_cold int decode_init(AVCodecContext *avctx) av_log(avctx, AV_LOG_DEBUG, "Image type is YUV 1:1:1.\n"); break; case IMGTYPE_YUV422: - c->decomp_size = basesize * 2; + c->decomp_size = (avctx->width & ~3) * avctx->height * 2; max_decomp_size = max_basesize * 2; avctx->pix_fmt = AV_PIX_FMT_YUV422P; av_log(avctx, AV_LOG_DEBUG, "Image type is YUV 4:2:2.\n"); - if (avctx->width % 4) { - avpriv_request_sample(avctx, "Unsupported dimensions"); - return AVERROR_INVALIDDATA; - } + partial_h_supported = 1; break; case IMGTYPE_RGB24: - c->decomp_size = basesize * 3; + c->decomp_size = FFALIGN(avctx->width*3, 4) * avctx->height; max_decomp_size = max_basesize * 3; avctx->pix_fmt = AV_PIX_FMT_BGR24; av_log(avctx, AV_LOG_DEBUG, "Image type is RGB 24.\n"); break; case IMGTYPE_YUV411: - c->decomp_size = basesize / 2 * 3; + c->decomp_size = (avctx->width & ~3) * avctx->height / 2 * 3; max_decomp_size = max_basesize / 2 * 3; avctx->pix_fmt = AV_PIX_FMT_YUV411P; av_log(avctx, AV_LOG_DEBUG, "Image type is YUV 4:1:1.\n"); + partial_h_supported = 1; break; case IMGTYPE_YUV211: c->decomp_size = basesize * 2; @@ -541,7 +554,7 @@ static av_cold int decode_init(AVCodecContext *avctx) } av_pix_fmt_get_chroma_sub_sample(avctx->pix_fmt, &subsample_h, &subsample_v); - if (avctx->width % (1<height % (1<width % (1<height % (1<decoder, AOMD_GET_FRAME_FLAGS, &flags); if (ret == AOM_CODEC_OK) { - picture->key_frame = !!(flags & AOM_FRAME_IS_KEY); + if (flags & AOM_FRAME_IS_KEY) + picture->flags |= AV_FRAME_FLAG_KEY; + else + picture->flags &= ~AV_FRAME_FLAG_KEY; if (flags & (AOM_FRAME_IS_KEY | AOM_FRAME_IS_INTRAONLY)) picture->pict_type = AV_PICTURE_TYPE_I; else if (flags & AOM_FRAME_IS_SWITCH) diff --git a/libavcodec/libaomenc.c b/libavcodec/libaomenc.c index bd576fdd3aa..f29cb0784a8 100644 --- a/libavcodec/libaomenc.c +++ b/libavcodec/libaomenc.c @@ -23,6 +23,8 @@ * AV1 encoder support via libaom */ +#include + #define AOM_DISABLE_CTRL_TYPECHECKS 1 #include #include @@ -1094,6 +1096,7 @@ static int storeframe(AVCodecContext *avctx, struct FrameListData *cx_frame, } memcpy(pkt->data, cx_frame->buf, pkt->size); pkt->pts = pkt->dts = cx_frame->pts; + pkt->duration = cx_frame->duration; if (!!(cx_frame->flags & AOM_FRAME_IS_KEY)) { pkt->flags |= AV_PKT_FLAG_KEY; @@ -1275,6 +1278,7 @@ static int aom_encode(AVCodecContext *avctx, AVPacket *pkt, AOMContext *ctx = avctx->priv_data; struct aom_image *rawimg = NULL; int64_t timestamp = 0; + unsigned long duration = 0; int res, coded_size; aom_enc_frame_flags_t flags = 0; @@ -1287,6 +1291,24 @@ static int aom_encode(AVCodecContext *avctx, AVPacket *pkt, rawimg->stride[AOM_PLANE_U] = frame->linesize[1]; rawimg->stride[AOM_PLANE_V] = frame->linesize[2]; timestamp = frame->pts; + + if (frame->duration > ULONG_MAX) { + av_log(avctx, AV_LOG_WARNING, + "Frame duration too large: %"PRId64"\n", frame->duration); + } else if (frame->duration) + duration = frame->duration; + else if (avctx->framerate.num > 0 && avctx->framerate.den > 0) + duration = av_rescale_q(1, av_inv_q(avctx->framerate), avctx->time_base); + else { +FF_DISABLE_DEPRECATION_WARNINGS + duration = +#if FF_API_TICKS_PER_FRAME + avctx->ticks_per_frame ? avctx->ticks_per_frame : +#endif + 1; +FF_ENABLE_DEPRECATION_WARNINGS + } + switch (frame->color_range) { case AVCOL_RANGE_MPEG: rawimg->range = AOM_CR_STUDIO_RANGE; @@ -1300,8 +1322,7 @@ static int aom_encode(AVCodecContext *avctx, AVPacket *pkt, flags |= AOM_EFLAG_FORCE_KF; } - res = aom_codec_encode(&ctx->encoder, rawimg, timestamp, - avctx->ticks_per_frame, flags); + res = aom_codec_encode(&ctx->encoder, rawimg, timestamp, duration, flags); if (res != AOM_CODEC_OK) { log_encoder_error(avctx, "Error encoding frame"); return AVERROR_INVALIDDATA; diff --git a/libavcodec/libaribb24.c b/libavcodec/libaribb24.c index 8ccf3c4b5d8..e3e244be996 100644 --- a/libavcodec/libaribb24.c +++ b/libavcodec/libaribb24.c @@ -291,6 +291,7 @@ static int libaribb24_handle_regions(AVCodecContext *avctx, AVSubtitle *sub) av_log(avctx, AV_LOG_DEBUG, "Styled ASS line: %s\n", buf.str); + sub->format = 1; /* text */ ret = ff_ass_add_rect(sub, buf.str, b24->read_order++, 0, NULL, NULL); } diff --git a/libavcodec/libaribcaption.c b/libavcodec/libaribcaption.c new file mode 100644 index 00000000000..747ca8a2e47 --- /dev/null +++ b/libavcodec/libaribcaption.c @@ -0,0 +1,1171 @@ +/* + * ARIB STD-B24 caption decoder using the libaribcaption library + * Copyright (c) 2022 TADANO Tokumei + * + * 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 "avcodec.h" +#include "codec_internal.h" +#include "internal.h" +#include "libavcodec/ass.h" +#include "libavutil/avstring.h" +#include "libavutil/avutil.h" +#include "libavutil/thread.h" +#include "libavutil/log.h" +#include "libavutil/opt.h" + +#include + +#if !defined(DEFAULT_FONT_ASS) +# define DEFAULT_FONT_ASS "sans-serif" +#endif + +#define ARIBC_BPRINT_SIZE_INIT 64 +#define ARIBC_BPRINT_SIZE_MAX (8 * 1024) +#define ARIBC_ALPHA_MAX_NUM 4 +#define ARIBC_ALPHA_DEFAULT_FRONT 0xFF +#define ARIBC_ALPHA_DEFAULT_BACK 0x80 + +#define ARIBCC_COLOR_RGB(c) ((c) & 0xFFFFFF) +#define ARIBCC_COLOR_DIFF_RGB(c1,c2) (((c1) ^ (c2)) & 0x00FFFFFF) +#define ARIBCC_COLOR_DIFF_A(c1,c2) (((c1) ^ (c2)) & 0xFF000000) + +#define CLUT_RGBA(r,g,b,a) (((unsigned)(a) << 24) | ((r) << 16) | ((g) << 8) | (b)) +#define CLUT_A(c) (((c) >> 24) & 0xFF) +#define CLUT_R(c) (((c) >> 16) & 0xFF) +#define CLUT_G(c) (((c) >> 8) & 0xFF) +#define CLUT_B(c) ( (c) & 0xFF) + +#define ARIBCC_COLOR_TO_CLUT_RGBA(c,a) (((ARIBCC_COLOR_A(c) ? ARIBCC_COLOR_A(c) : (a)) << 24) | \ + (ARIBCC_COLOR_R(c) << 16) | \ + (ARIBCC_COLOR_G(c) << 8) | \ + (ARIBCC_COLOR_B(c))) + +typedef struct ARIBCaptionContext { + AVClass *class; + AVCodecContext *avctx; + const AVPacket *avpkt; + AVSubtitle *sub; + + aribcc_context_t *context; + aribcc_decoder_t *decoder; + aribcc_renderer_t *renderer; + + int subtitle_type; + int encoding_scheme; + bool ass_single_rect; + char *font; + bool replace_fullwidth_ascii; + bool force_stroke_text; + bool ignore_background; + bool ignore_ruby; + float stroke_width; + bool replace_drcs; + + int64_t pts; + AVRational time_base; + int canvas_width; + int canvas_height; + int plane_width; + int plane_height; + int frame_width; + int frame_height; + int bitmap_plane_width; + int bitmap_plane_height; + int font_size; + int charstyle; + int border_style; + int readorder; + + aribcc_caption_t caption; + aribcc_render_result_t render_result; + uint32_t *clut; + int clut_idx; + int clut_overflow; + uint8_t clut_alpha[ARIBC_ALPHA_MAX_NUM]; +} ARIBCaptionContext; + +static void hex_dump_debug(void *ctx, const char *buf, int buf_size) +{ + int i; + + for (i = 0; i < buf_size; i++) { + ff_dlog(ctx, "%02hhx ", buf[i]); + if (i % 16 == 15) + ff_dlog(ctx, "\n"); + } + if (i % 16) + ff_dlog(ctx, "\n"); +} + +static void logcat_callback(aribcc_loglevel_t level, const char* message, void* userdata) +{ + ARIBCaptionContext *ctx = userdata; + int lvl; + + if (ctx->decoder != NULL) { + switch (level) { + case ARIBCC_LOGLEVEL_ERROR: + lvl = AV_LOG_ERROR; + break; + case ARIBCC_LOGLEVEL_WARNING: + lvl = AV_LOG_WARNING; + break; + default: + lvl = AV_LOG_INFO; + } + + av_log(ctx, lvl, "%s\n", message); + } +} + +static void estimate_video_frame_size(ARIBCaptionContext *ctx) +{ + if (ctx->avctx->width > 0 && ctx->avctx->height > 0) { + /* input video size specified by -canvas_size option */ + ctx->bitmap_plane_width = ctx->avctx->width; + ctx->bitmap_plane_height = ctx->avctx->height; + } else if (ctx->plane_width == 960) { + /* ARIB TR-B14 Fascicle 2 Volume 3 [Section 2] 4.3.1 */ + /* ARIB TR-B14 Fascicle 2 Volume 3 [Section 2] Appendix-4 */ + ctx->bitmap_plane_width = 1440; + ctx->bitmap_plane_height = 1080; + } else { + ctx->bitmap_plane_width = ctx->plane_width; + ctx->bitmap_plane_height = ctx->plane_height; + } + /* Expand either width or height */ + if (ctx->bitmap_plane_height * ctx->plane_width > ctx->bitmap_plane_width * ctx->plane_height) { + ctx->frame_height = ctx->bitmap_plane_height; + ctx->frame_width = ctx->frame_height * ctx->plane_width / ctx->plane_height; + } else { + ctx->frame_width = ctx->bitmap_plane_width; + ctx->frame_height = ctx->frame_width * ctx->plane_height / ctx->plane_width; + } +} + +static void clut_set_alpha(ARIBCaptionContext *ctx, uint8_t a) +{ + int i; + + for (i = 0; i < ARIBC_ALPHA_MAX_NUM; i++) { + if (ctx->clut_alpha[i] == 0) { + ctx->clut_alpha[i] = a; + return; + } + if (ctx->clut_alpha[i] == a) + return; + } + return; +} + +static uint8_t clut_find_nearlest_alpha(ARIBCaptionContext *ctx, uint8_t a) +{ + int i, j, d; + + if (a == 0) + return a; + d = 256; + j = 0; + for (i = 0; i < ARIBC_ALPHA_MAX_NUM; i++) { + if (ctx->clut_alpha[i] == a) + return a; + if (ctx->clut_alpha[i] == 0) + break; + if (abs((int)a - (int)ctx->clut_alpha[i]) < d) { + d = abs((int)a - (int)ctx->clut_alpha[i]); + j = i; + } + } + return ctx->clut_alpha[j]; +} + +static int clut_find(ARIBCaptionContext *ctx, uint32_t rgba) +{ + int i; + + for (i = 0; i < ctx->clut_idx; i++) { + if (ctx->clut[i] == rgba) + return i; + } + return -1; +} + +static inline int clut_color_distance(uint32_t rgba1, uint32_t rgba2) +{ + return abs((int)CLUT_R(rgba1) - (int)CLUT_R(rgba2)) + + abs((int)CLUT_G(rgba1) - (int)CLUT_G(rgba2)) + + abs((int)CLUT_B(rgba1) - (int)CLUT_B(rgba2)); +} + +static uint8_t clut_pick_or_set(ARIBCaptionContext *ctx, int r, int g, int b, int a) +{ + int c, i, d, d_min; + uint32_t rgba; + + a = clut_find_nearlest_alpha(ctx, a); + if (a == 0) + return 0; /* transparent */ + rgba = CLUT_RGBA(r,g,b,a); + + d_min = 256 * 3; + c = 0; + for (i = 0; i < ctx->clut_idx; i++) { + if (ctx->clut[i] == rgba) + return i; + if (CLUT_A(ctx->clut[i]) != a) + continue; + d = clut_color_distance(ctx->clut[i], rgba); + if (d < d_min) { + d_min = d; + c = i; + } + } + if (d_min > 3) { + if (ctx->clut_idx >= AVPALETTE_COUNT) + ctx->clut_overflow++; + else { + c = ctx->clut_idx; + ctx->clut[ctx->clut_idx++] = rgba; + } + } + return c; +} + +/* initialiaze CLUT with each character colors */ +static void clut_init(ARIBCaptionContext *ctx, aribcc_caption_region_t *region) +{ + aribcc_color_t text_color, back_color, stroke_color; + uint32_t rgba; + + ctx->clut[0] = CLUT_RGBA(0,0,0,0); /* transparent */ + ctx->clut_alpha[0] = 0xFF; + ctx->clut_idx = 1; + ctx->clut_overflow = 0; + text_color = region->chars[0].text_color; + back_color = region->chars[0].back_color; + stroke_color = region->chars[0].stroke_color; + rgba = ARIBCC_COLOR_TO_CLUT_RGBA(text_color, ARIBC_ALPHA_DEFAULT_FRONT); + ctx->clut[ctx->clut_idx++] = rgba; + clut_set_alpha(ctx, CLUT_A(rgba)); + rgba = ARIBCC_COLOR_TO_CLUT_RGBA(back_color, ARIBC_ALPHA_DEFAULT_BACK); + ctx->clut[ctx->clut_idx++] = rgba; + clut_set_alpha(ctx, CLUT_A(rgba)); + rgba = ARIBCC_COLOR_TO_CLUT_RGBA(stroke_color, ARIBC_ALPHA_DEFAULT_FRONT); + if (clut_find(ctx, rgba) < 0) { + ctx->clut[ctx->clut_idx++] = rgba; + clut_set_alpha(ctx, CLUT_A(rgba)); + } + + for (int i = 1; i < region->char_count; i++) { + if (region->chars[i].text_color != text_color) { + rgba = ARIBCC_COLOR_TO_CLUT_RGBA(region->chars[i].text_color, + ARIBC_ALPHA_DEFAULT_FRONT); + if (clut_find(ctx, rgba) < 0) { + ctx->clut[ctx->clut_idx++] = rgba; + clut_set_alpha(ctx, CLUT_A(rgba)); + } + } + if (region->chars[i].back_color != back_color) { + rgba = ARIBCC_COLOR_TO_CLUT_RGBA(region->chars[i].back_color, + ARIBC_ALPHA_DEFAULT_BACK); + if (clut_find(ctx, rgba) < 0) { + ctx->clut[ctx->clut_idx++] = rgba; + clut_set_alpha(ctx, CLUT_A(rgba)); + } + } + if (region->chars[i].stroke_color != stroke_color) { + rgba = ARIBCC_COLOR_TO_CLUT_RGBA(region->chars[i].stroke_color, + ARIBC_ALPHA_DEFAULT_FRONT); + if (clut_find(ctx, rgba) < 0) { + if (ctx->clut_idx < AVPALETTE_COUNT) + ctx->clut[ctx->clut_idx++] = rgba; + clut_set_alpha(ctx, CLUT_A(rgba)); + } + } + } +} + +/** + * aribcaption_trans_{bitmap|ass|text}_subtitle() + * + * Transfer decoded subtitle to AVSubtitle with corresponding subtitle type. + * + * @param ctx pointer to the ARIBCaptionContext + * @return > 0 number of rectangles to be displayed + * = 0 no subtitle + * < 0 error code + */ +static int aribcaption_trans_bitmap_subtitle(ARIBCaptionContext *ctx) +{ + int ret = 0; + AVSubtitle *sub = ctx->sub; + int status, rect_idx; + int old_width = ctx->frame_width; + int old_height = ctx->frame_height; + + if (ctx->caption.plane_width > 0 && ctx->caption.plane_height > 0) { + ctx->plane_width = ctx->caption.plane_width; + ctx->plane_height = ctx->caption.plane_height; + } + estimate_video_frame_size(ctx); + if (ctx->frame_width != old_width || ctx->frame_height != old_height) { + ff_dlog(ctx, "canvas: %dx%d plane: %dx%d bitmap: %dx%d frame: %dx%d\n", + ctx->avctx->width, ctx->avctx->height, + ctx->plane_width, ctx->plane_height, + ctx->bitmap_plane_width, ctx->bitmap_plane_height, + ctx->frame_width, ctx->frame_height); + if (!aribcc_renderer_set_frame_size(ctx->renderer, + ctx->frame_width, ctx->frame_height)) { + av_log(ctx, AV_LOG_ERROR, + "aribcc_renderer_set_frame_size() returned with error.\n"); + return AVERROR_EXTERNAL; + } + } + + status = aribcc_renderer_append_caption(ctx->renderer, &ctx->caption); + if (!status) { + av_log(ctx, AV_LOG_ERROR, + "aribcc_renderer_append_caption() returned with error.\n"); + return AVERROR_EXTERNAL; + } + + status = aribcc_renderer_render(ctx->renderer, ctx->pts, &ctx->render_result); + switch (status) { + case ARIBCC_RENDER_STATUS_GOT_IMAGE: + break; + + case ARIBCC_RENDER_STATUS_GOT_IMAGE_UNCHANGED: + aribcc_render_result_cleanup(&ctx->render_result); + ff_dlog(ctx, "got image unchanged\n"); + return 0; + + case ARIBCC_RENDER_STATUS_NO_IMAGE: + ff_dlog(ctx, "no image\n"); + return 0; + + case ARIBCC_RENDER_STATUS_ERROR: + av_log(ctx, AV_LOG_ERROR, + "aribcc_renderer_render() returned with error.\n"); + return AVERROR_EXTERNAL; + + default: + aribcc_render_result_cleanup(&ctx->render_result); + av_log(ctx, AV_LOG_ERROR, + "aribcc_renderer_render() returned unknown status: %d\n", status); + return AVERROR_EXTERNAL; + } + + if (!ctx->render_result.image_count || ctx->render_result.images == NULL) { + aribcc_render_result_cleanup(&ctx->render_result); + ff_dlog(ctx, "no image (%d)\n", ctx->render_result.image_count); + return 0; + } + + sub->format = 0; /* graphic */ + sub->rects = av_calloc(ctx->render_result.image_count, sizeof(*sub->rects)); + if (!sub->rects) { + ret = AVERROR(ENOMEM); + goto fail; + } + for (int i = 0; i < ctx->render_result.image_count; i++) { + sub->rects[i] = av_mallocz(sizeof(*sub->rects[i])); + if (!sub->rects[i]) { + ret = AVERROR(ENOMEM); + goto fail; + } + } + + for (rect_idx = 0; rect_idx < ctx->caption.region_count; rect_idx++) { + AVSubtitleRect *rect = sub->rects[rect_idx]; + aribcc_image_t *image = &ctx->render_result.images[rect_idx]; + int w, h, shrink_height, dst_idx; + + clut_init(ctx, &ctx->caption.regions[rect_idx]); + + rect->w = image->width * ctx->bitmap_plane_width / ctx->frame_width; + rect->h = image->height * ctx->bitmap_plane_height / ctx->frame_height; + rect->data[0] = av_mallocz(rect->w * rect->h); + if (!rect->data[0]) { + ret = AVERROR(ENOMEM); + goto fail; + } + if ((image->height != rect->h && image->width != rect->w) || + image->stride < image->width * 4 || + image->stride * image->height > image->bitmap_size) { + av_log(ctx, AV_LOG_ERROR, "Bug: unexpected rendered image: %d(%d)x%d -> %dx%d\n", + image->width, image->stride / 4, image->height, rect->w, rect->h); + ret = AVERROR_EXTERNAL; + goto fail; + } + + shrink_height = image->height != rect->h; + dst_idx = 0; + for (h = 0; h < rect->h; h++) { + for (w = 0; w < rect->w; w++) { + /* Bi-linear interpolation */ + int n, m, idx0, idx1, r, g, b, a; + if (shrink_height) { + int div_a, y0, y1; + div_a = h * ctx->frame_height; + n = ctx->bitmap_plane_height; + y0 = div_a / n; + y1 = FFMIN(y0 + 1, image->height - 1); + m = div_a - n * y0; + idx0 = image->stride * y0 + w * 4; + idx1 = image->stride * y1 + w * 4; + } else { + int div_a, x0, x1; + div_a = w * ctx->frame_width; + n = ctx->bitmap_plane_width; + x0 = div_a / n; + x1 = FFMIN(x0 + 1, image->width - 1); + m = div_a - n * x0; + idx0 = image->stride * h + x0 * 4; + idx1 = image->stride * h + x1 * 4; + } + r = (image->bitmap[idx0++] * (n - m) + image->bitmap[idx1++] * m) / n; + g = (image->bitmap[idx0++] * (n - m) + image->bitmap[idx1++] * m) / n; + b = (image->bitmap[idx0++] * (n - m) + image->bitmap[idx1++] * m) / n; + a = (image->bitmap[idx0++] * (n - m) + image->bitmap[idx1++] * m) / n; + rect->data[0][dst_idx++] = clut_pick_or_set(ctx, r, g, b, a); + } + } + rect->data[1] = av_memdup(ctx->clut, AVPALETTE_SIZE); + if (!rect->data[1]) { + ret = AVERROR(ENOMEM); + goto fail; + } + + if (ctx->avctx->profile == FF_PROFILE_ARIB_PROFILE_C) { + /* ARIB TR-B14 version 3.8 Fascicle 1-(2/2) Volume 3 [Section 4] */ + /* No position information is provided for profile C */ + rect->x = (ctx->frame_width - rect->w) / 2; + rect->y = ctx->frame_height - rect->h * (ctx->caption.region_count - rect_idx); + } else { + rect->x = image->dst_x * ctx->bitmap_plane_width / ctx->frame_width; + rect->y = image->dst_y * ctx->bitmap_plane_height / ctx->frame_height; + } + rect->type = SUBTITLE_BITMAP; + rect->linesize[0] = rect->w; + rect->nb_colors = 256; + + ff_dlog(ctx, "BITMAP subtitle%s (%d,%d) %dx%d -> (%d,%d) %dx%d [%d]: %d colors\n", + (ctx->caption.regions[rect_idx].is_ruby) ? " (ruby)" : "", + image->dst_x, image->dst_y, image->width, image->height, + rect->x, rect->y, rect->w, rect->h, + rect_idx, ctx->clut_idx); + if (ctx->clut_overflow) + av_log(ctx, AV_LOG_WARNING, "CLUT overflow (%d).\n", ctx->clut_overflow); + } + sub->num_rects = rect_idx; + + return rect_idx; + +fail: + if (sub->rects) { + for (int i = 0; i < ctx->caption.region_count; i++) { + if (sub->rects[i]) { + av_freep(&sub->rects[i]->data[0]); + av_freep(&sub->rects[i]->data[1]); + av_freep(&sub->rects[i]); + } + } + av_freep(&sub->rects); + } + sub->num_rects = 0; + + return ret; +} + +static int set_ass_header(ARIBCaptionContext *ctx) +{ + AVCodecContext *avctx = ctx->avctx; + int outline, shadow; + const char *font_name; + const char *fonts = ctx->font; + + if (ctx->border_style == 4) { + outline = 0; + shadow = 4; + } else { + outline = 1; + shadow = 0; + } + if (ctx->force_stroke_text) + outline = (int)(ctx->stroke_width * 4.0 / 3.0); + + if (fonts && *fonts) + font_name = av_get_token(&fonts, ","); + else + font_name = av_strdup(DEFAULT_FONT_ASS); + if (!font_name) + return AVERROR(ENOMEM); + + av_freep(&avctx->subtitle_header); + avctx->subtitle_header = av_asprintf( + "[Script Info]\r\n" + "ScriptType: v4.00+\r\n" + "PlayResX: %d\r\n" + "PlayResY: %d\r\n" + "WrapStyle: 2\r\n" /* 2: no word wrapping */ + "\r\n" + + "[V4+ Styles]\r\n" + "Format: Name, " + "Fontname, Fontsize, " + "PrimaryColour, SecondaryColour, OutlineColour, BackColour, " + "Bold, Italic, Underline, StrikeOut, " + "ScaleX, ScaleY, " + "Spacing, Angle, " + "BorderStyle, Outline, Shadow, " + "Alignment, MarginL, MarginR, MarginV, " + "Encoding\r\n" + + "Style: " + "Default," /* Name */ + "%s,%d," /* Font{name,size} */ + "&H%x,&H%x,&H%x,&H%x," /* {Primary,Secondary,Outline,Back}Colour */ + "%d,%d,%d,0," /* Bold, Italic, Underline, StrikeOut */ + "100,100," /* Scale{X,Y} */ + "0,0," /* Spacing, Angle */ + "%d,%d,%d," /* BorderStyle, Outline, Shadow */ + "%d,10,10,10," /* Alignment, Margin[LRV] */ + "0\r\n" /* Encoding */ + "\r\n" + + "[Events]\r\n" + "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n", + ctx->plane_width, ctx->plane_height, + font_name, ctx->font_size, + ASS_DEFAULT_COLOR, ASS_DEFAULT_COLOR, + ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR, + -ASS_DEFAULT_BOLD, -ASS_DEFAULT_ITALIC, -ASS_DEFAULT_UNDERLINE, + ctx->border_style, outline, shadow, ASS_DEFAULT_ALIGNMENT); + + av_freep(&font_name); + if (!avctx->subtitle_header) + return AVERROR(ENOMEM); + avctx->subtitle_header_size = strlen(avctx->subtitle_header); + return 0; +} + +static void set_ass_color(AVBPrint *buf, int color_num, + aribcc_color_t new_color, aribcc_color_t old_color) +{ + if (ARIBCC_COLOR_DIFF_RGB(new_color, old_color)) + av_bprintf(buf, "{\\%dc&H%06x&}", color_num, + ARIBCC_COLOR_RGB(new_color)); + if (ARIBCC_COLOR_DIFF_A(new_color, old_color)) + av_bprintf(buf, "{\\%da&H%02x&}", color_num, + 0xFF - ARIBCC_COLOR_A(new_color)); +} + +static int aribcaption_trans_ass_subtitle(ARIBCaptionContext *ctx) +{ + AVSubtitle *sub = ctx->sub; + AVBPrint buf; + bool single_rect = ctx->ass_single_rect; + int ret = 0, rect_idx; + + if (ctx->caption.plane_width > 0 && ctx->caption.plane_height > 0 && + (ctx->caption.plane_width != ctx->plane_width || + ctx->caption.plane_height != ctx->plane_height)) { + ctx->plane_width = ctx->caption.plane_width; + ctx->plane_height = ctx->caption.plane_height; + if ((ret = set_ass_header(ctx)) < 0) + return ret; + } + + /* ARIB TR-B14 version 3.8 Fascicle 1-(2/2) Volume 3 [Section 4] */ + /* No position information is provided for profile C */ + if (ctx->avctx->profile == FF_PROFILE_ARIB_PROFILE_C) + single_rect = true; + + sub->format = 1; /* text */ + if (ctx->caption.region_count == 0) { + /* clear previous caption for indefinite duration */ + ff_ass_add_rect(sub, "", ctx->readorder++, 0, NULL, NULL); + return 1; + } + + av_bprint_init(&buf, ARIBC_BPRINT_SIZE_INIT, ARIBC_BPRINT_SIZE_MAX); + + if (single_rect && ctx->avctx->profile != FF_PROFILE_ARIB_PROFILE_C) { + int x, y, rx, ry; + x = ctx->plane_width; + y = ctx->plane_height; + for (int i = 0; i < ctx->caption.region_count; i++) { + rx = ctx->caption.regions[i].x; + ry = ctx->caption.regions[i].y; + if (rx < x) + x = rx; + if (ry < y) + y = ry; + } + av_bprintf(&buf, "{\\an7}"); + if (y < 0) + y += ctx->plane_height; + if (x > 0 || y > 0) + av_bprintf(&buf, "{\\pos(%d,%d)}", x, y); + } + + rect_idx = 0; + for (int i = 0; i < ctx->caption.region_count; i++) { + aribcc_caption_region_t *region = &ctx->caption.regions[i]; + aribcc_color_t text_color = ARIBCC_MAKE_RGBA(0xFF, 0xFF, 0xFF, + ARIBC_ALPHA_DEFAULT_FRONT); + aribcc_color_t stroke_color = ARIBCC_MAKE_RGBA(0, 0, 0, + ARIBC_ALPHA_DEFAULT_FRONT); + aribcc_color_t back_color = ARIBCC_MAKE_RGBA(0, 0, 0, + ARIBC_ALPHA_DEFAULT_BACK); + aribcc_charstyle_t charstyle = ctx->charstyle; + int char_width = ctx->font_size; + int char_height = ctx->font_size; + int char_horizontal_spacing = 0; + + if (region->is_ruby && ctx->ignore_ruby) + continue; + + if (!single_rect) { + int x = region->x; + int y = region->y; + if (x < 0) + x += ctx->plane_width; + if (y < 0) + y += ctx->plane_height; + av_bprint_clear(&buf); + av_bprintf(&buf, "{\\an7}"); + if (x > 0 || y > 0) + av_bprintf(&buf, "{\\pos(%d,%d)}", x, y); + } + if (region->is_ruby) + av_bprintf(&buf, "{\\fs%d}", char_height / 2); + + for (int j = 0; j < region->char_count; j++) { + aribcc_caption_char_t *ch = ®ion->chars[j]; + + if (ctx->avctx->profile != FF_PROFILE_ARIB_PROFILE_C) { + if (ch->char_horizontal_spacing != char_horizontal_spacing) { + av_bprintf(&buf, "{\\fsp%d}", (region->is_ruby) ? + ch->char_horizontal_spacing / 2 : + ch->char_horizontal_spacing); + char_horizontal_spacing = ch->char_horizontal_spacing; + } + if (ch->char_width != char_width) { + av_bprintf(&buf, "{\\fscx%"PRId64"}", + av_rescale(ch->char_width, 100, ctx->font_size)); + char_width = ch->char_width; + } + if (ch->char_height != char_height) { + av_bprintf(&buf, "{\\fscy%"PRId64"}", + av_rescale(ch->char_height, 100, ctx->font_size)); + char_height = ch->char_height; + } + } + if (ch->style != charstyle) { + aribcc_charstyle_t diff = ch->style ^ charstyle; + if (diff & ARIBCC_CHARSTYLE_STROKE) { + if (charstyle & ARIBCC_CHARSTYLE_STROKE) { + if (ctx->force_stroke_text) + av_bprintf(&buf, "{\\bord%d}", + (int)(ctx->stroke_width * 4.0 / 3.0)); + else + av_bprintf(&buf, "{\\bord0}"); + } else + av_bprintf(&buf, "{\\bord3}"); + } + if (diff & ARIBCC_CHARSTYLE_BOLD) { + if (charstyle & ARIBCC_CHARSTYLE_BOLD) + av_bprintf(&buf, "{\\b0}"); + else + av_bprintf(&buf, "{\\b1}"); + } + if (diff & ARIBCC_CHARSTYLE_ITALIC) { + if (charstyle & ARIBCC_CHARSTYLE_ITALIC) + av_bprintf(&buf, "{\\i0}"); + else + av_bprintf(&buf, "{\\i1}"); + } + if (diff & ARIBCC_CHARSTYLE_UNDERLINE) { + if (charstyle & ARIBCC_CHARSTYLE_UNDERLINE) + av_bprintf(&buf, "{\\u0}"); + else + av_bprintf(&buf, "{\\u1}"); + } + charstyle = ch->style; + } + if (ch->text_color != text_color) { + set_ass_color(&buf, 1, ch->text_color, text_color); + text_color = ch->text_color; + } + if (ch->stroke_color != stroke_color) { + set_ass_color(&buf, 3, ch->stroke_color, stroke_color); + stroke_color = ch->stroke_color; + } + if (ch->back_color != back_color) { + if (ctx->border_style == 4) + set_ass_color(&buf, 4, ch->back_color, back_color); + else + set_ass_color(&buf, 3, ch->back_color, back_color); + back_color = ch->back_color; + } + if (region->chars[j].type == ARIBCC_CHARTYPE_DRCS) + av_bprintf(&buf, "\xe3\x80\x93"); /* Geta Mark */ + else + ff_ass_bprint_text_event(&buf, ch->u8str, strlen(ch->u8str), "", 0); + } + + if (single_rect) { + if (i + 1 < ctx->caption.region_count) + av_bprintf(&buf, "{\\r}\\N"); + ff_dlog(ctx, "ASS subtitle%s (%d,%d) %dx%d [%d]\n", + (region->is_ruby) ? " (ruby)" : "", + region->x, region->y, region->width, region->height, + rect_idx); + } else { + if (!av_bprint_is_complete(&buf)) { + ret = AVERROR(ENOMEM); + goto fail; + } + ff_dlog(ctx, "ASS subtitle%s (%d,%d) %dx%d [%d]: %s\n", + (region->is_ruby) ? " (ruby)" : "", + region->x, region->y, region->width, region->height, + rect_idx, buf.str); + + ret = ff_ass_add_rect(sub, buf.str, ctx->readorder++, 0 , NULL, NULL); + if (ret != 0) + goto fail; + rect_idx++; + } + } + if (single_rect) { + if (!av_bprint_is_complete(&buf)) { + ret = AVERROR(ENOMEM); + goto fail; + } + ff_dlog(ctx, "ASS subtitle: %s\n", buf.str); + + ret = ff_ass_add_rect(sub, buf.str, ctx->readorder++, 0 , NULL, NULL); + if (ret != 0) + goto fail; + rect_idx++; + } + + av_bprint_finalize(&buf, NULL); + return rect_idx; + +fail: + if (sub->rects) { + for (int i = 0; i < ctx->caption.region_count; i++) { + if (sub->rects[i]) { + av_freep(&sub->rects[i]->ass); + av_freep(&sub->rects[i]); + } + } + av_freep(&sub->rects); + } + sub->num_rects = 0; + av_bprint_finalize(&buf, NULL); + + return ret; +} + +static int aribcaption_trans_text_subtitle(ARIBCaptionContext *ctx) +{ + AVSubtitle *sub = ctx->sub; + AVSubtitleRect *rect; + int ret = 0; + const char *text; + + sub->rects = av_calloc(ctx->caption.region_count, sizeof(*sub->rects)); + if (!sub->rects) { + ret = AVERROR(ENOMEM); + goto fail; + } + sub->num_rects = 1; + + sub->rects[0] = av_mallocz(sizeof(*sub->rects[0])); + if (!sub->rects[0]) { + ret = AVERROR(ENOMEM); + goto fail; + } + rect = sub->rects[0]; + + if (ctx->caption.region_count == 0) + text = ""; /* clear previous caption */ + else { + text = ctx->caption.text; + ff_dlog(ctx, "TEXT subtitle: %s\n", text); + } + rect->text = av_strdup(text); + if (!rect->text) { + ret = AVERROR(ENOMEM); + goto fail; + } + + sub->format = 1; /* text */ + rect->type = SUBTITLE_TEXT; + + return 1; + +fail: + if (sub->rects) { + rect = sub->rects[0]; + if (rect) { + av_freep(&rect->text); + av_freep(&rect); + } + av_freep(&sub->rects); + } + sub->num_rects = 0; + + return ret; +} + +static int aribcaption_decode(AVCodecContext *avctx, AVSubtitle *sub, + int *got_sub_ptr, const AVPacket *avpkt) +{ + ARIBCaptionContext *ctx = avctx->priv_data; + int status; + + ff_dlog(ctx, "ARIB caption packet pts=%"PRIx64":\n", avpkt->pts); + if (sub->num_rects) { + avpriv_request_sample(ctx, "Different Version of Segment asked Twice"); + return AVERROR_PATCHWELCOME; + } + hex_dump_debug(ctx, avpkt->data, avpkt->size); + + ctx->sub = sub; + ctx->avpkt = avpkt; + ctx->time_base = avctx->pkt_timebase; + if (ctx->time_base.num <= 0 || ctx->time_base.den <= 0) { + av_log(ctx, AV_LOG_VERBOSE, "No timebase set. assuming 90kHz.\n"); + ctx->time_base = av_make_q(1, 90000); + } + if (avpkt->pts == AV_NOPTS_VALUE) + ctx->pts = ARIBCC_PTS_NOPTS; + else + ctx->pts = av_rescale_q(avpkt->pts, ctx->time_base, (AVRational){1, 1000}); + + status = aribcc_decoder_decode(ctx->decoder, avpkt->data, avpkt->size, + ctx->pts, &ctx->caption); + if (status == ARIBCC_DECODE_STATUS_ERROR) { + av_log(ctx, AV_LOG_ERROR, + "aribcc_decoder_decode() returned with error.\n"); + return AVERROR(EAGAIN); + } + if (status == ARIBCC_DECODE_STATUS_NO_CAPTION) { + ff_dlog(ctx, "No caption.\n"); + return avpkt->size; + } else { + ff_dlog(ctx, "type=%02x, flags=%x, lang=%03x\n", + ctx->caption.type, ctx->caption.flags, ctx->caption.iso6392_language_code); + ff_dlog(ctx, "region count = %d, start=%d.%d, duration=%d.%d\n", + ctx->caption.region_count, + (int)(ctx->caption.pts / 1000), (int)(ctx->caption.pts % 1000), + (int)((ctx->caption.wait_duration == ARIBCC_DURATION_INDEFINITE) ? + -1 : ctx->caption.wait_duration / 1000), + (int)((ctx->caption.wait_duration == ARIBCC_DURATION_INDEFINITE) ? + 0 : ctx->caption.wait_duration % 1000)); + } + + switch ((enum AVSubtitleType) ctx->subtitle_type) { + case SUBTITLE_TEXT: + status = aribcaption_trans_text_subtitle(ctx); + break; + + case SUBTITLE_ASS: + status = aribcaption_trans_ass_subtitle(ctx); + break; + + case SUBTITLE_BITMAP: + status = aribcaption_trans_bitmap_subtitle(ctx); + break; + + case SUBTITLE_NONE: + default: + status = 0; + } + + if (status < 0) { + av_log(ctx, AV_LOG_ERROR, "Failed to set Subtitle: %s\n", + av_err2str(status)); + aribcc_caption_cleanup(&ctx->caption); + return status; + } + if (status > 0) { + *got_sub_ptr = 1; + if (ctx->avpkt->pts != AV_NOPTS_VALUE) + sub->pts = av_rescale_q(ctx->avpkt->pts, + ctx->time_base, AV_TIME_BASE_Q); + if (ctx->caption.wait_duration == ARIBCC_DURATION_INDEFINITE) + sub->end_display_time = UINT32_MAX; + else + sub->end_display_time = (uint32_t)ctx->caption.wait_duration; + } + + aribcc_caption_cleanup(&ctx->caption); + return avpkt->size; +} + +static void aribcaption_flush(AVCodecContext *avctx) +{ + ARIBCaptionContext *ctx = avctx->priv_data; + + if (ctx->decoder) + aribcc_decoder_flush(ctx->decoder); + if (ctx->renderer) + aribcc_renderer_flush(ctx->renderer); + if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP)) + ctx->readorder = 0; +} + +static int aribcaption_close(AVCodecContext *avctx) +{ + ARIBCaptionContext *ctx = avctx->priv_data; + + av_freep(&ctx->clut); + if (ctx->renderer) + aribcc_renderer_free(ctx->renderer); + if (ctx->decoder) + aribcc_decoder_free(ctx->decoder); + if (ctx->context) + aribcc_context_free(ctx->context); + + return 0; +} + +static int aribcaption_init(AVCodecContext *avctx) +{ + ARIBCaptionContext *ctx = avctx->priv_data; + aribcc_profile_t profile; + int ret = 0; + + ctx->avctx = avctx; + + switch (avctx->profile) { + case FF_PROFILE_ARIB_PROFILE_A: + profile = ARIBCC_PROFILE_A; + /* assume 960x540 at initial state */ + ctx->plane_width = 960; + ctx->plane_height = 540; + ctx->font_size = 36; + break; + case FF_PROFILE_ARIB_PROFILE_C: + profile = ARIBCC_PROFILE_C; + ctx->plane_width = 320; + ctx->plane_height = 180; + ctx->font_size = 16; + break; + default: + av_log(avctx, AV_LOG_ERROR, "Unknown or unsupported profile set.\n"); + return AVERROR(EINVAL); + } + /* determine BorderStyle of ASS header */ + if (ctx->ignore_background) + ctx->border_style = 1; + else + ctx->border_style = 4; + ctx->charstyle = ARIBCC_CHARSTYLE_DEFAULT; + if (ctx->force_stroke_text || ctx->ignore_background) + ctx->charstyle |= ARIBCC_CHARSTYLE_STROKE; + + if (!(ctx->context = aribcc_context_alloc())) { + av_log(avctx, AV_LOG_ERROR, "Failed to alloc libaribcaption context.\n"); + return AVERROR_EXTERNAL; + } + aribcc_context_set_logcat_callback(ctx->context, logcat_callback, avctx); + if (!(ctx->decoder = aribcc_decoder_alloc(ctx->context))) { + av_log(avctx, AV_LOG_ERROR, "Failed to alloc libaribcaption decoder.\n"); + return AVERROR_EXTERNAL; + } + if (!aribcc_decoder_initialize(ctx->decoder, + (enum aribcc_encoding_scheme_t) ctx->encoding_scheme, + ARIBCC_CAPTIONTYPE_CAPTION, + profile, + ARIBCC_LANGUAGEID_FIRST)) { + av_log(avctx, AV_LOG_ERROR, "Failed to initialize libaribcaption decoder.\n"); + return AVERROR_EXTERNAL; + } + aribcc_decoder_set_replace_msz_fullwidth_ascii(ctx->decoder, + ctx->replace_fullwidth_ascii); + + /* Similar behavior as ffmpeg tool to set canvas size */ + if (ctx->canvas_width > 0 && ctx->canvas_height > 0 && + (ctx->avctx->width == 0 || ctx->avctx->height == 0)) { + ctx->avctx->width = ctx->canvas_width; + ctx->avctx->height = ctx->canvas_height; + } + + switch ((enum AVSubtitleType) ctx->subtitle_type) { + case SUBTITLE_ASS: + ret = set_ass_header(ctx); + if (ret != 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to set ASS header: %s\n", + av_err2str(ret)); + return ret; + } + break; + + case SUBTITLE_BITMAP: + if(!(ctx->renderer = aribcc_renderer_alloc(ctx->context))) { + av_log(avctx, AV_LOG_ERROR, "Failed to alloc libaribcaption renderer.\n"); + return AVERROR_EXTERNAL; + } + if(!aribcc_renderer_initialize(ctx->renderer, + ARIBCC_CAPTIONTYPE_CAPTION, + ARIBCC_FONTPROVIDER_TYPE_AUTO, + ARIBCC_TEXTRENDERER_TYPE_AUTO)) { + av_log(avctx, AV_LOG_ERROR, "Failed to initialize libaribcaption renderer.\n"); + return AVERROR_EXTERNAL; + } + estimate_video_frame_size(ctx); + ff_dlog(ctx, "canvas: %dx%d plane: %dx%d bitmap: %dx%d frame: %dx%d\n", + ctx->avctx->width, ctx->avctx->height, + ctx->plane_width, ctx->plane_height, + ctx->bitmap_plane_width, ctx->bitmap_plane_height, + ctx->frame_width, ctx->frame_height); + if (!aribcc_renderer_set_frame_size(ctx->renderer, + ctx->frame_width, ctx->frame_height)) { + av_log(ctx, AV_LOG_ERROR, + "aribcc_renderer_set_frame_size() returned with error.\n"); + return AVERROR_EXTERNAL; + } + + if (!(ctx->clut = av_mallocz(AVPALETTE_SIZE))) + return AVERROR(ENOMEM); + + aribcc_renderer_set_storage_policy(ctx->renderer, ARIBCC_CAPTION_STORAGE_POLICY_MINIMUM, 0); + aribcc_renderer_set_replace_drcs(ctx->renderer, ctx->replace_drcs); + aribcc_renderer_set_force_stroke_text(ctx->renderer, ctx->force_stroke_text); + aribcc_renderer_set_force_no_background(ctx->renderer, ctx->ignore_background); + aribcc_renderer_set_force_no_ruby(ctx->renderer, ctx->ignore_ruby); + aribcc_renderer_set_stroke_width(ctx->renderer, ctx->stroke_width); + if (ctx->font) { + int is_nomem = 0; + size_t count = 0; + const char **font_families = NULL; + const char *fonts = ctx->font; + + while (*fonts) { + const char **ff = av_realloc_array(font_families, count + 1, sizeof(*font_families)); + if (!ff) { + is_nomem = 1; + break; + } else { + font_families = ff; + ff[count++] = av_get_token(&fonts, ","); + if (!ff[count - 1]) { + is_nomem = 1; + break; + } else if (*fonts) + fonts++; + } + } + if (!is_nomem && count) + aribcc_renderer_set_default_font_family(ctx->renderer, font_families, count, true); + while (count) + av_freep(&font_families[--count]); + av_freep(&font_families); + if (is_nomem) + return AVERROR(ENOMEM); + } + break; + + case SUBTITLE_TEXT: + case SUBTITLE_NONE: + default: + /* do nothing */ ; + } + + ctx->readorder = 0; + + return 0; +} + +#if !defined(ASS_SINGLE_RECT) +# define ASS_SINGLE_RECT 0 +#endif + +#define OFFSET(x) offsetof(ARIBCaptionContext, x) +#define SD AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM +static const AVOption options[] = { + { "sub_type", "subtitle rendering type", + OFFSET(subtitle_type), AV_OPT_TYPE_INT, + { .i64 = SUBTITLE_ASS }, SUBTITLE_NONE, SUBTITLE_ASS, SD, "type" }, + { "none", "do nothing", 0, AV_OPT_TYPE_CONST, + { .i64 = SUBTITLE_NONE }, .flags = SD, .unit = "type" }, + { "bitmap", "bitmap rendering", 0, AV_OPT_TYPE_CONST, + { .i64 = SUBTITLE_BITMAP }, .flags = SD, .unit = "type" }, + { "text", "plain text", 0, AV_OPT_TYPE_CONST, + { .i64 = SUBTITLE_TEXT }, .flags = SD, .unit = "type" }, + { "ass", "formatted text", 0, AV_OPT_TYPE_CONST, + { .i64 = SUBTITLE_ASS }, .flags = SD, .unit = "type" }, + { "caption_encoding", "encoding scheme of subtitle text", + OFFSET(encoding_scheme), AV_OPT_TYPE_INT, { .i64 = ARIBCC_ENCODING_SCHEME_AUTO }, + ARIBCC_ENCODING_SCHEME_AUTO, ARIBCC_ENCODING_SCHEME_ABNT_NBR_15606_1_LATIN, SD, "encoding" }, + { "auto", "automatically detect encoding scheme", 0, AV_OPT_TYPE_CONST, + { .i64 = ARIBCC_ENCODING_SCHEME_AUTO }, .flags = SD, .unit = "encoding" }, + { "jis", "8bit-char JIS encoding (Japanese ISDB captions)", 0, AV_OPT_TYPE_CONST, + { .i64 = ARIBCC_ENCODING_SCHEME_ARIB_STD_B24_JIS }, .flags = SD, .unit = "encoding" }, + { "utf8", "UTF-8 encoding (Philippines ISDB-T captions)", 0, AV_OPT_TYPE_CONST, + { .i64 = ARIBCC_ENCODING_SCHEME_ARIB_STD_B24_UTF8 }, .flags = SD, .unit = "encoding" }, + { "latin", "latin characters (SBTVD / ISDB-Tb captions used in South America)", 0, AV_OPT_TYPE_CONST, + { .i64 = ARIBCC_ENCODING_SCHEME_ABNT_NBR_15606_1_LATIN }, .flags = SD, .unit = "encoding" }, + { "ass_single_rect", "workaround of ASS subtitle for players which can't handle multi-rectangle [ass]", + OFFSET(ass_single_rect), AV_OPT_TYPE_BOOL, { .i64 = ASS_SINGLE_RECT }, 0, 1, SD }, + { "font", "comma-separated font family [ass, bitmap]", + OFFSET(font), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, SD }, + { "replace_fullwidth_ascii", "replace MSZ fullwidth alphanumerics with halfwidth alphanumerics [ass, bitmap]", + OFFSET(replace_fullwidth_ascii), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, SD }, + { "force_outline_text", "always render characters with outline [(ass), bitmap]", + OFFSET(force_stroke_text), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, SD }, + { "ignore_background", "ignore rendering caption background [(ass), bitmap]", + OFFSET(ignore_background), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, SD }, + { "ignore_ruby", "ignore ruby-like characters [ass, bitmap]", + OFFSET(ignore_ruby), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, SD }, + { "outline_width", "outline width of text [(ass), bitmap]", + OFFSET(stroke_width), AV_OPT_TYPE_FLOAT, { .dbl = 1.5 }, 0.0, 3.0, SD }, + { "replace_drcs", "replace known DRCS [bitmap]", + OFFSET(replace_drcs), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, SD }, + {"canvas_size", "set input video size (WxH or abbreviation) [bitmap]", + OFFSET(canvas_width), AV_OPT_TYPE_IMAGE_SIZE, { .str = NULL }, 0, INT_MAX, SD }, + { NULL } +}; + +static const AVClass aribcaption_class = { + .class_name = "aribcaption decoder", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const FFCodec ff_libaribcaption_decoder = { + .p.name = "libaribcaption", + .p.long_name = NULL_IF_CONFIG_SMALL("ARIB STD-B24 caption decoder"), + .p.type = AVMEDIA_TYPE_SUBTITLE, + .p.id = AV_CODEC_ID_ARIB_CAPTION, + .priv_data_size = sizeof(ARIBCaptionContext), + .init = aribcaption_init, + .close = aribcaption_close, + FF_CODEC_DECODE_SUB_CB(aribcaption_decode), + .flush = aribcaption_flush, + .p.priv_class = &aribcaption_class, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, +}; diff --git a/libavcodec/libcodec2.c b/libavcodec/libcodec2.c index 5728d915c23..83f68e85c79 100644 --- a/libavcodec/libcodec2.c +++ b/libavcodec/libcodec2.c @@ -197,7 +197,8 @@ const FFCodec ff_libcodec2_encoder = { CODEC_LONG_NAME("codec2 encoder using libcodec2"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_CODEC2, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .p.supported_samplerates = (const int[]){ 8000, 0 }, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE }, .p.ch_layouts = (const AVChannelLayout[]) { AV_CHANNEL_LAYOUT_MONO, { 0 } }, diff --git a/libavcodec/libdav1d.c b/libavcodec/libdav1d.c index b43af03732a..11cdbca2748 100644 --- a/libavcodec/libdav1d.c +++ b/libavcodec/libdav1d.c @@ -24,11 +24,13 @@ #include "libavutil/avassert.h" #include "libavutil/cpu.h" #include "libavutil/film_grain_params.h" +#include "libavutil/hdr_dynamic_metadata.h" #include "libavutil/mastering_display_metadata.h" #include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "atsc_a53.h" +#include "av1_parse.h" #include "avcodec.h" #include "bytestream.h" #include "codec_internal.h" @@ -153,12 +155,9 @@ static void libdav1d_init_params(AVCodecContext *c, const Dav1dSequenceHeader *s else c->pix_fmt = pix_fmt[seq->layout][seq->hbd]; - if (seq->num_units_in_tick && seq->time_scale) { - av_reduce(&c->framerate.den, &c->framerate.num, - seq->num_units_in_tick, seq->time_scale, INT_MAX); - if (seq->equal_picture_interval) - c->ticks_per_frame = seq->num_ticks_per_picture; - } + c->framerate = ff_av1_framerate(seq->num_ticks_per_picture, + (unsigned)seq->num_units_in_tick, + (unsigned)seq->time_scale); if (seq->film_grain_present) c->properties |= FF_CODEC_PROPERTY_FILM_GRAIN; @@ -277,6 +276,15 @@ static av_cold int libdav1d_init(AVCodecContext *c) if (res < 0) return AVERROR(ENOMEM); +#if FF_DAV1D_VERSION_AT_LEAST(6,7) + res = dav1d_get_frame_delay(&s); + if (res < 0) // Should not happen + return AVERROR_EXTERNAL; + + // When dav1d_get_frame_delay() returns 1, there's no delay whatsoever + c->delay = res > 1 ? res : 0; +#endif + return 0; } @@ -288,6 +296,13 @@ static void libdav1d_flush(AVCodecContext *c) dav1d_flush(dav1d->c); } +typedef struct OpaqueData { + void *pkt_orig_opaque; +#if FF_API_REORDERED_OPAQUE + int64_t reordered_opaque; +#endif +} OpaqueData; + static void libdav1d_data_free(const uint8_t *data, void *opaque) { AVBufferRef *buf = opaque; @@ -301,19 +316,14 @@ static void libdav1d_user_data_free(const uint8_t *data, void *opaque) { av_packet_free(&pkt); } -static int libdav1d_receive_frame(AVCodecContext *c, AVFrame *frame) +static int libdav1d_receive_frame_internal(AVCodecContext *c, Dav1dPicture *p) { Libdav1dContext *dav1d = c->priv_data; Dav1dData *data = &dav1d->data; - Dav1dPicture pic = { 0 }, *p = &pic; - AVPacket *pkt; -#if FF_DAV1D_VERSION_AT_LEAST(5,1) - enum Dav1dEventFlags event_flags = 0; -#endif int res; if (!data->sz) { - pkt = av_packet_alloc(); + AVPacket *pkt = av_packet_alloc(); if (!pkt) return AVERROR(ENOMEM); @@ -325,6 +335,8 @@ static int libdav1d_receive_frame(AVCodecContext *c, AVFrame *frame) } if (pkt->size) { + OpaqueData *od = NULL; + res = dav1d_data_wrap(data, pkt->data, pkt->size, libdav1d_data_free, pkt->buf); if (res < 0) { @@ -333,17 +345,26 @@ static int libdav1d_receive_frame(AVCodecContext *c, AVFrame *frame) } pkt->buf = NULL; - pkt->opaque = NULL; - if (c->reordered_opaque != AV_NOPTS_VALUE) { - pkt->opaque = av_memdup(&c->reordered_opaque, - sizeof(c->reordered_opaque)); - if (!pkt->opaque) { +FF_DISABLE_DEPRECATION_WARNINGS + if ( +#if FF_API_REORDERED_OPAQUE + c->reordered_opaque != AV_NOPTS_VALUE || +#endif + (pkt->opaque && (c->flags & AV_CODEC_FLAG_COPY_OPAQUE))) { + od = av_mallocz(sizeof(*od)); + if (!od) { av_packet_free(&pkt); dav1d_data_unref(data); return AVERROR(ENOMEM); } + od->pkt_orig_opaque = pkt->opaque; +#if FF_API_REORDERED_OPAQUE + od->reordered_opaque = c->reordered_opaque; +#endif +FF_ENABLE_DEPRECATION_WARNINGS } + pkt->opaque = od; res = dav1d_data_wrap_user_data(data, (const uint8_t *)pkt, libdav1d_user_data_free, pkt); @@ -375,11 +396,30 @@ static int libdav1d_receive_frame(AVCodecContext *c, AVFrame *frame) if (res < 0) { if (res == AVERROR(EINVAL)) res = AVERROR_INVALIDDATA; - else if (res == AVERROR(EAGAIN) && c->internal->draining) - res = AVERROR_EOF; + else if (res == AVERROR(EAGAIN)) + res = c->internal->draining ? AVERROR_EOF : 1; + } + return res; +} + +static int libdav1d_receive_frame(AVCodecContext *c, AVFrame *frame) +{ + Libdav1dContext *dav1d = c->priv_data; + Dav1dPicture pic = { 0 }, *p = &pic; + AVPacket *pkt; + OpaqueData *od = NULL; +#if FF_DAV1D_VERSION_AT_LEAST(5,1) + enum Dav1dEventFlags event_flags = 0; +#endif + int res; + + do { + res = libdav1d_receive_frame_internal(c, p); + } while (res > 0); + + if (res < 0) return res; - } av_assert0(p->data[0] && p->allocator_data); @@ -423,18 +463,32 @@ static int libdav1d_receive_frame(AVCodecContext *c, AVFrame *frame) ff_set_sar(c, frame->sample_aspect_ratio); pkt = (AVPacket *)p->m.user_data.data; - if (pkt->opaque) - memcpy(&frame->reordered_opaque, pkt->opaque, sizeof(frame->reordered_opaque)); + od = pkt->opaque; +#if FF_API_REORDERED_OPAQUE +FF_DISABLE_DEPRECATION_WARNINGS + if (od && od->reordered_opaque != AV_NOPTS_VALUE) + frame->reordered_opaque = od->reordered_opaque; else frame->reordered_opaque = AV_NOPTS_VALUE; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + // restore the original user opaque value for + // ff_decode_frame_props_from_pkt() + pkt->opaque = od ? od->pkt_orig_opaque : NULL; + av_freep(&od); // match timestamps and packet size - res = ff_decode_frame_props_from_pkt(frame, pkt); + res = ff_decode_frame_props_from_pkt(c, frame, pkt); + pkt->opaque = NULL; if (res < 0) goto fail; frame->pkt_dts = pkt->pts; - frame->key_frame = p->frame_hdr->frame_type == DAV1D_FRAME_TYPE_KEY; + if (p->frame_hdr->frame_type == DAV1D_FRAME_TYPE_KEY) + frame->flags |= AV_FRAME_FLAG_KEY; + else + frame->flags &= ~AV_FRAME_FLAG_KEY; switch (p->frame_hdr->frame_type) { case DAV1D_FRAME_TYPE_KEY: @@ -482,32 +536,69 @@ static int libdav1d_receive_frame(AVCodecContext *c, AVFrame *frame) light->MaxFALL = p->content_light->max_frame_average_light_level; } if (p->itut_t35) { +#if FF_DAV1D_VERSION_AT_LEAST(6,9) + for (size_t i = 0; i < p->n_itut_t35; i++) { + const Dav1dITUTT35 *itut_t35 = &p->itut_t35[i]; +#else + const Dav1dITUTT35 *itut_t35 = p->itut_t35; +#endif GetByteContext gb; - unsigned int user_identifier; + int provider_code; - bytestream2_init(&gb, p->itut_t35->payload, p->itut_t35->payload_size); - bytestream2_skip(&gb, 1); // terminal provider code - bytestream2_skip(&gb, 1); // terminal provider oriented code - user_identifier = bytestream2_get_be32(&gb); - switch (user_identifier) { - case MKBETAG('G', 'A', '9', '4'): { // closed captions - AVBufferRef *buf = NULL; + bytestream2_init(&gb, itut_t35->payload, itut_t35->payload_size); - res = ff_parse_a53_cc(&buf, gb.buffer, bytestream2_get_bytes_left(&gb)); - if (res < 0) - goto fail; - if (!res) + provider_code = bytestream2_get_be16(&gb); + switch (provider_code) { + case 0x31: { // atsc_provider_code + uint32_t user_identifier = bytestream2_get_be32(&gb); + switch (user_identifier) { + case MKBETAG('G', 'A', '9', '4'): { // closed captions + AVBufferRef *buf = NULL; + + res = ff_parse_a53_cc(&buf, gb.buffer, bytestream2_get_bytes_left(&gb)); + if (res < 0) + goto fail; + if (!res) + break; + + if (!av_frame_new_side_data_from_buf(frame, AV_FRAME_DATA_A53_CC, buf)) + av_buffer_unref(&buf); + + c->properties |= FF_CODEC_PROPERTY_CLOSED_CAPTIONS; break; + } + default: // ignore unsupported identifiers + break; + } + break; + } + case 0x3C: { // smpte_provider_code + AVDynamicHDRPlus *hdrplus; + int provider_oriented_code = bytestream2_get_be16(&gb); + int application_identifier = bytestream2_get_byte(&gb); - if (!av_frame_new_side_data_from_buf(frame, AV_FRAME_DATA_A53_CC, buf)) - av_buffer_unref(&buf); + if (itut_t35->country_code != 0xB5 || + provider_oriented_code != 1 || application_identifier != 4) + break; - c->properties |= FF_CODEC_PROPERTY_CLOSED_CAPTIONS; + hdrplus = av_dynamic_hdr_plus_create_side_data(frame); + if (!hdrplus) { + res = AVERROR(ENOMEM); + goto fail; + } + + res = av_dynamic_hdr_plus_from_t35(hdrplus, gb.buffer, + bytestream2_get_bytes_left(&gb)); + if (res < 0) + goto fail; break; } - default: // ignore unsupported identifiers + default: // ignore unsupported provider codes break; } +#if FF_DAV1D_VERSION_AT_LEAST(6,9) + } +#endif } if (p->frame_hdr->film_grain.present && (!dav1d->apply_grain || (c->export_side_data & AV_CODEC_EXPORT_DATA_FILM_GRAIN))) { @@ -607,7 +698,7 @@ const FFCodec ff_libdav1d_decoder = { .flush = libdav1d_flush, FF_CODEC_RECEIVE_FRAME_CB(libdav1d_receive_frame), .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_OTHER_THREADS, - .caps_internal = FF_CODEC_CAP_SETS_PKT_DTS | FF_CODEC_CAP_SETS_FRAME_PROPS | + .caps_internal = FF_CODEC_CAP_SETS_FRAME_PROPS | FF_CODEC_CAP_AUTO_THREADS, .p.priv_class = &libdav1d_class, .p.wrapper_name = "libdav1d", diff --git a/libavcodec/libfdk-aacenc.c b/libavcodec/libfdk-aacenc.c index 54549de4735..e08c6a0c6c1 100644 --- a/libavcodec/libfdk-aacenc.c +++ b/libavcodec/libfdk-aacenc.c @@ -21,6 +21,7 @@ #include "libavutil/channel_layout.h" #include "libavutil/common.h" +#include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "avcodec.h" #include "audio_frame_queue.h" @@ -46,6 +47,15 @@ typedef struct AACContext { int latm; int header_period; int vbr; + int drc_profile; + int drc_target_ref; + int comp_profile; + int comp_target_ref; + int prog_ref; + int metadata_mode; + AACENC_MetaData metaDataSetup; + int delay_sent; + int frame_length; AudioFrameQueue afq; } AACContext; @@ -64,6 +74,12 @@ static const AVOption aac_enc_options[] = { { "latm", "Output LATM/LOAS encapsulated data", offsetof(AACContext, latm), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, { "header_period", "StreamMuxConfig and PCE repetition period (in frames)", offsetof(AACContext, header_period), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 0xffff, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, { "vbr", "VBR mode (1-5)", offsetof(AACContext, vbr), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 5, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, + { "drc_profile", "The desired compression profile for AAC DRC", offsetof(AACContext, drc_profile), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 256, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, + { "drc_target_ref", "Expected target reference level at decoder side in dB (for clipping prevention/limiter)", offsetof(AACContext, drc_target_ref), AV_OPT_TYPE_INT, { .i64 = 0.0 }, -31.75, 0, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, + { "comp_profile", "The desired compression profile for AAC DRC", offsetof(AACContext, comp_profile), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 256, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, + { "comp_target_ref", "Expected target reference level at decoder side in dB (for clipping prevention/limiter)", offsetof(AACContext, comp_target_ref), AV_OPT_TYPE_INT, { .i64 = 0.0 }, -31.75, 0, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, + { "prog_ref", "The program reference level or dialog level in dB", offsetof(AACContext, prog_ref), AV_OPT_TYPE_INT, { .i64 = 0.0 }, -31.75, 0, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, + { "frame_length", "The desired frame length", offsetof(AACContext, frame_length), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1024, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, FF_AAC_PROFILE_OPTS { NULL } }; @@ -118,6 +134,44 @@ static int aac_encode_close(AVCodecContext *avctx) return 0; } +static void aac_encode_flush(AVCodecContext *avctx) +{ + AACContext *s = avctx->priv_data; + AACENC_BufDesc in_buf = { 0 }, out_buf = { 0 }; + AACENC_InArgs in_args = { 0 }; + AACENC_OutArgs out_args; + int64_t pts, duration; + uint8_t dummy_in[1], dummy_out[1]; + int in_buffer_identifiers[] = { IN_AUDIO_DATA, IN_METADATA_SETUP }; + int in_buffer_element_sizes[] = { 2, sizeof(AACENC_MetaData) }; + int in_buffer_sizes[] = { 0, sizeof(s->metaDataSetup) }; + int out_buffer_identifier = OUT_BITSTREAM_DATA; + int out_buffer_size = sizeof(dummy_out), out_buffer_element_size = 1; + void* inBuffer[] = { dummy_in, &s->metaDataSetup }; + void *out_ptr = dummy_out; + AACENC_ERROR err; + + ff_af_queue_remove(&s->afq, s->afq.frame_count, &pts, &duration); + + in_buf.bufs = (void **)inBuffer; + in_buf.numBufs = s->metadata_mode == 0 ? 1 : 2; + in_buf.bufferIdentifiers = in_buffer_identifiers; + in_buf.bufSizes = in_buffer_sizes; + in_buf.bufElSizes = in_buffer_element_sizes; + + out_buf.numBufs = 1; + out_buf.bufs = &out_ptr; + out_buf.bufferIdentifiers = &out_buffer_identifier; + out_buf.bufSizes = &out_buffer_size; + out_buf.bufElSizes = &out_buffer_element_size; + + err = aacEncEncode(s->handle, &in_buf, &out_buf, &in_args, &out_args); + if (err != AACENC_OK) { + av_log(avctx, AV_LOG_ERROR, "Unexpected error while flushing: %s\n", + aac_get_error(err)); + } +} + static av_cold int aac_encode_init(AVCodecContext *avctx) { AACContext *s = avctx->priv_data; @@ -152,6 +206,15 @@ static av_cold int aac_encode_init(AVCodecContext *avctx) } } + if (s->frame_length >= 0) { + if ((err = aacEncoder_SetParam(s->handle, AACENC_GRANULE_LENGTH, + s->frame_length)) != AACENC_OK) { + av_log(avctx, AV_LOG_ERROR, "Unable to set granule length: %s\n", + aac_get_error(err)); + goto error; + } + } + if ((err = aacEncoder_SetParam(s->handle, AACENC_SAMPLERATE, avctx->sample_rate)) != AACENC_OK) { av_log(avctx, AV_LOG_ERROR, "Unable to set the sample rate %d: %s\n", @@ -319,6 +382,30 @@ static av_cold int aac_encode_init(AVCodecContext *avctx) } } + s->metadata_mode = 0; + if (s->prog_ref) { + s->metadata_mode = 1; + s->metaDataSetup.prog_ref_level_present = 1; + s->metaDataSetup.prog_ref_level = s->prog_ref << 16; + } + if (s->drc_profile) { + s->metadata_mode = 1; + s->metaDataSetup.drc_profile = s->drc_profile; + s->metaDataSetup.drc_TargetRefLevel = s->drc_target_ref << 16; + if (s->comp_profile) { + /* Including the comp_profile means that we need to set the mode to ETSI */ + s->metadata_mode = 2; + s->metaDataSetup.comp_profile = s->comp_profile; + s->metaDataSetup.comp_TargetRefLevel = s->comp_target_ref << 16; + } + } + + if ((err = aacEncoder_SetParam(s->handle, AACENC_METADATA_MODE, s->metadata_mode)) != AACENC_OK) { + av_log(avctx, AV_LOG_ERROR, "Unable to set metadata mode to %d: %s\n", + s->metadata_mode, aac_get_error(err)); + goto error; + } + if ((err = aacEncEncode(s->handle, NULL, NULL, NULL, NULL)) != AACENC_OK) { av_log(avctx, AV_LOG_ERROR, "Unable to initialize the encoder: %s\n", aac_get_error(err)); @@ -363,12 +450,14 @@ static int aac_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, AACENC_BufDesc in_buf = { 0 }, out_buf = { 0 }; AACENC_InArgs in_args = { 0 }; AACENC_OutArgs out_args = { 0 }; - int in_buffer_identifier = IN_AUDIO_DATA; - int in_buffer_size, in_buffer_element_size; + void* inBuffer[] = { 0, &s->metaDataSetup }; + int in_buffer_identifiers[] = { IN_AUDIO_DATA, IN_METADATA_SETUP }; + int in_buffer_element_sizes[] = { 2, sizeof(AACENC_MetaData) }; + int in_buffer_sizes[] = { 0, sizeof(s->metaDataSetup) }; int out_buffer_identifier = OUT_BITSTREAM_DATA; int out_buffer_size, out_buffer_element_size; - void *in_ptr, *out_ptr; - int ret; + void *out_ptr; + int ret, discard_padding; uint8_t dummy_buf[1]; AACENC_ERROR err; @@ -376,13 +465,12 @@ static int aac_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, if (!frame) { /* Must be a non-null pointer, even if it's a dummy. We could use * the address of anything else on the stack as well. */ - in_ptr = dummy_buf; - in_buffer_size = 0; + inBuffer[0] = dummy_buf; in_args.numInSamples = -1; } else { - in_ptr = frame->data[0]; - in_buffer_size = 2 * avctx->ch_layout.nb_channels * frame->nb_samples; + inBuffer[0] = frame->data[0]; + in_buffer_sizes[0] = 2 * avctx->ch_layout.nb_channels * frame->nb_samples; in_args.numInSamples = avctx->ch_layout.nb_channels * frame->nb_samples; @@ -391,12 +479,16 @@ static int aac_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, return ret; } - in_buffer_element_size = 2; - in_buf.numBufs = 1; - in_buf.bufs = &in_ptr; - in_buf.bufferIdentifiers = &in_buffer_identifier; - in_buf.bufSizes = &in_buffer_size; - in_buf.bufElSizes = &in_buffer_element_size; + if (s->metadata_mode == 0) { + in_buf.numBufs = 1; + } else { + in_buf.numBufs = 2; + } + + in_buf.bufs = (void**)inBuffer; + in_buf.bufferIdentifiers = in_buffer_identifiers; + in_buf.bufSizes = in_buffer_sizes; + in_buf.bufElSizes = in_buffer_element_sizes; /* The maximum packet size is 6144 bits aka 768 bytes per channel. */ ret = ff_alloc_packet(avctx, avpkt, FFMAX(8192, 768 * avctx->ch_layout.nb_channels)); @@ -428,6 +520,24 @@ static int aac_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, ff_af_queue_remove(&s->afq, avctx->frame_size, &avpkt->pts, &avpkt->duration); + discard_padding = avctx->frame_size - avpkt->duration; + // Check if subtraction resulted in an overflow + if ((discard_padding < avctx->frame_size) != (avpkt->duration > 0)) { + av_log(avctx, AV_LOG_ERROR, "discard padding overflow\n"); + return AVERROR(EINVAL); + } + if ((!s->delay_sent && avctx->initial_padding > 0) || discard_padding > 0) { + uint8_t *side_data = + av_packet_new_side_data(avpkt, AV_PKT_DATA_SKIP_SAMPLES, 10); + if (!side_data) + return AVERROR(ENOMEM); + if (!s->delay_sent) { + AV_WL32(side_data, avctx->initial_padding); + s->delay_sent = 1; + } + AV_WL32(side_data + 4, discard_padding); + } + avpkt->size = out_args.numOutBytes; *got_packet_ptr = 1; return 0; @@ -500,11 +610,13 @@ const FFCodec ff_libfdk_aac_encoder = { .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_AAC, .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | + AV_CODEC_CAP_ENCODER_FLUSH | AV_CODEC_CAP_SMALL_LAST_FRAME, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE, .priv_data_size = sizeof(AACContext), .init = aac_encode_init, FF_CODEC_ENCODE_CB(aac_encode_frame), + .flush = aac_encode_flush, .close = aac_encode_close, .p.sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE }, diff --git a/libavcodec/libgsmenc.c b/libavcodec/libgsmenc.c index bd3b1420aab..640954491a7 100644 --- a/libavcodec/libgsmenc.c +++ b/libavcodec/libgsmenc.c @@ -122,7 +122,7 @@ const FFCodec ff_libgsm_encoder = { CODEC_LONG_NAME("libgsm GSM"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_GSM, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .init = libgsm_encode_init, FF_CODEC_ENCODE_CB(libgsm_encode_frame), .close = libgsm_encode_close, @@ -141,7 +141,7 @@ const FFCodec ff_libgsm_ms_encoder = { CODEC_LONG_NAME("libgsm GSM Microsoft variant"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_GSM_MS, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .init = libgsm_encode_init, FF_CODEC_ENCODE_CB(libgsm_encode_frame), .close = libgsm_encode_close, diff --git a/libavcodec/libilbc.c b/libavcodec/libilbc.c index 9ef6e16bc54..9ca90bf0c66 100644 --- a/libavcodec/libilbc.c +++ b/libavcodec/libilbc.c @@ -205,7 +205,7 @@ const FFCodec ff_libilbc_encoder = { CODEC_LONG_NAME("iLBC (Internet Low Bitrate Codec)"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_ILBC, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE, .priv_data_size = sizeof(ILBCEncContext), .init = ilbc_encode_init, diff --git a/libavcodec/libjxl.h b/libavcodec/libjxl.h index 5387c438fd0..e305b6e7588 100644 --- a/libavcodec/libjxl.h +++ b/libavcodec/libjxl.h @@ -27,8 +27,20 @@ #ifndef AVCODEC_LIBJXL_H #define AVCODEC_LIBJXL_H +#include #include +/* + * libjxl version 0.7.0 and earlier doesn't contain these macros at all + * so to detect version 0.7.0 versus 0.8.0 we need to define them ourselves + */ +#ifndef JPEGXL_COMPUTE_NUMERIC_VERSION + #define JPEGXL_COMPUTE_NUMERIC_VERSION(major,minor,patch) ((major<<24) | (minor<<16) | (patch<<8) | 0) +#endif +#ifndef JPEGXL_NUMERIC_VERSION + #define JPEGXL_NUMERIC_VERSION JPEGXL_COMPUTE_NUMERIC_VERSION(0, 7, 0) +#endif + /** * Transform threadcount in ffmpeg to one used by libjxl. * diff --git a/libavcodec/libjxldec.c b/libavcodec/libjxldec.c index de48bea4b2c..e45ac02c078 100644 --- a/libavcodec/libjxldec.c +++ b/libavcodec/libjxldec.c @@ -37,6 +37,7 @@ #include "avcodec.h" #include "codec_internal.h" #include "decode.h" +#include "internal.h" #include #include @@ -47,15 +48,24 @@ typedef struct LibJxlDecodeContext { JxlDecoder *decoder; JxlBasicInfo basic_info; JxlPixelFormat jxl_pixfmt; +#if JPEGXL_NUMERIC_VERSION >= JPEGXL_COMPUTE_NUMERIC_VERSION(0, 8, 0) + JxlBitDepth jxl_bit_depth; +#endif JxlDecoderStatus events; AVBufferRef *iccp; + AVPacket *avpkt; + int64_t pts; + int64_t frame_duration; + int prev_is_last; + AVRational timebase; } LibJxlDecodeContext; static int libjxl_init_jxl_decoder(AVCodecContext *avctx) { LibJxlDecodeContext *ctx = avctx->priv_data; - ctx->events = JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE | JXL_DEC_COLOR_ENCODING; + ctx->events = JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE + | JXL_DEC_COLOR_ENCODING | JXL_DEC_FRAME; if (JxlDecoderSubscribeEvents(ctx->decoder, ctx->events) != JXL_DEC_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Error subscribing to JXL events\n"); return AVERROR_EXTERNAL; @@ -68,6 +78,8 @@ static int libjxl_init_jxl_decoder(AVCodecContext *avctx) memset(&ctx->basic_info, 0, sizeof(JxlBasicInfo)); memset(&ctx->jxl_pixfmt, 0, sizeof(JxlPixelFormat)); + ctx->prev_is_last = 1; + ctx->frame_duration = 1; return 0; } @@ -90,13 +102,23 @@ static av_cold int libjxl_decode_init(AVCodecContext *avctx) return AVERROR_EXTERNAL; } + ctx->avpkt = avctx->internal->in_pkt; + ctx->pts = 0; + return libjxl_init_jxl_decoder(avctx); } -static enum AVPixelFormat libjxl_get_pix_fmt(void *avctx, const JxlBasicInfo *basic_info, JxlPixelFormat *format) +static enum AVPixelFormat libjxl_get_pix_fmt(AVCodecContext *avctx, LibJxlDecodeContext *ctx) { + const JxlBasicInfo *basic_info = &ctx->basic_info; + JxlPixelFormat *format = &ctx->jxl_pixfmt; format->endianness = JXL_NATIVE_ENDIAN; format->num_channels = basic_info->num_color_channels + (basic_info->alpha_bits > 0); +#if JPEGXL_NUMERIC_VERSION >= JPEGXL_COMPUTE_NUMERIC_VERSION(0, 8, 0) + ctx->jxl_bit_depth.bits_per_sample = avctx->bits_per_raw_sample = basic_info->bits_per_sample; + ctx->jxl_bit_depth.type = JXL_BIT_DEPTH_FROM_PIXEL_FORMAT; + ctx->jxl_bit_depth.exponent_bits_per_sample = basic_info->exponent_bits_per_sample; +#endif /* Gray */ if (basic_info->num_color_channels == 1) { if (basic_info->bits_per_sample <= 8) { @@ -119,10 +141,10 @@ static enum AVPixelFormat libjxl_get_pix_fmt(void *avctx, const JxlBasicInfo *ba format->data_type = JXL_TYPE_UINT8; return basic_info->alpha_bits ? AV_PIX_FMT_RGBA : AV_PIX_FMT_RGB24; } - if (basic_info->bits_per_sample > 16) - av_log(avctx, AV_LOG_WARNING, "Downsampling larger integer to 16-bit via libjxl\n"); if (basic_info->exponent_bits_per_sample) av_log(avctx, AV_LOG_WARNING, "Downsampling float to 16-bit integer via libjxl\n"); + else if (basic_info->bits_per_sample > 16) + av_log(avctx, AV_LOG_WARNING, "Downsampling larger integer to 16-bit via libjxl\n"); format->data_type = JXL_TYPE_UINT16; return basic_info->alpha_bits ? AV_PIX_FMT_RGBA64 : AV_PIX_FMT_RGB48; } @@ -167,9 +189,9 @@ static enum AVColorTransferCharacteristic libjxl_get_trc(void *avctx, const JxlC case JXL_TRANSFER_FUNCTION_DCI: return AVCOL_TRC_SMPTE428; case JXL_TRANSFER_FUNCTION_HLG: return AVCOL_TRC_ARIB_STD_B67; case JXL_TRANSFER_FUNCTION_GAMMA: - if (jxl_color->gamma > 2.199 && jxl_color->gamma < 2.201) + if (jxl_color->gamma > 0.45355 && jxl_color->gamma < 0.45555) return AVCOL_TRC_GAMMA22; - else if (jxl_color->gamma > 2.799 && jxl_color->gamma < 2.801) + else if (jxl_color->gamma > 0.35614 && jxl_color->gamma < 0.35814) return AVCOL_TRC_GAMMA28; else av_log(avctx, AV_LOG_WARNING, "Unsupported gamma transfer: %f\n", jxl_color->gamma); @@ -188,14 +210,22 @@ static int libjxl_get_icc(AVCodecContext *avctx) JxlDecoderStatus jret; /* an ICC profile is present, and we can meaningfully get it, * because the pixel data is not XYB-encoded */ +#if JPEGXL_NUMERIC_VERSION < JPEGXL_COMPUTE_NUMERIC_VERSION(0, 9, 0) jret = JxlDecoderGetICCProfileSize(ctx->decoder, &ctx->jxl_pixfmt, JXL_COLOR_PROFILE_TARGET_DATA, &icc_len); +#else + jret = JxlDecoderGetICCProfileSize(ctx->decoder, JXL_COLOR_PROFILE_TARGET_DATA, &icc_len); +#endif if (jret == JXL_DEC_SUCCESS && icc_len > 0) { av_buffer_unref(&ctx->iccp); ctx->iccp = av_buffer_alloc(icc_len); if (!ctx->iccp) return AVERROR(ENOMEM); +#if JPEGXL_NUMERIC_VERSION < JPEGXL_COMPUTE_NUMERIC_VERSION(0, 9, 0) jret = JxlDecoderGetColorAsICCProfile(ctx->decoder, &ctx->jxl_pixfmt, JXL_COLOR_PROFILE_TARGET_DATA, - ctx->iccp->data, icc_len); + ctx->iccp->data, icc_len); +#else + jret = JxlDecoderGetColorAsICCProfile(ctx->decoder, JXL_COLOR_PROFILE_TARGET_DATA, ctx->iccp->data, icc_len); +#endif if (jret != JXL_DEC_SUCCESS) { av_log(avctx, AV_LOG_WARNING, "Unable to obtain ICC Profile\n"); av_buffer_unref(&ctx->iccp); @@ -231,12 +261,21 @@ static int libjxl_color_encoding_event(AVCodecContext *avctx, AVFrame *frame) /* set this flag if we need to fall back on wide gamut */ int fallback = 0; +#if JPEGXL_NUMERIC_VERSION < JPEGXL_COMPUTE_NUMERIC_VERSION(0, 9, 0) jret = JxlDecoderGetColorAsEncodedProfile(ctx->decoder, NULL, JXL_COLOR_PROFILE_TARGET_ORIGINAL, &jxl_color); +#else + jret = JxlDecoderGetColorAsEncodedProfile(ctx->decoder, JXL_COLOR_PROFILE_TARGET_ORIGINAL, &jxl_color); +#endif if (jret == JXL_DEC_SUCCESS) { /* enum values describe the colors of this image */ jret = JxlDecoderSetPreferredColorProfile(ctx->decoder, &jxl_color); if (jret == JXL_DEC_SUCCESS) - jret = JxlDecoderGetColorAsEncodedProfile(ctx->decoder, &ctx->jxl_pixfmt, JXL_COLOR_PROFILE_TARGET_DATA, &jxl_color); +#if JPEGXL_NUMERIC_VERSION < JPEGXL_COMPUTE_NUMERIC_VERSION(0, 9, 0) + jret = JxlDecoderGetColorAsEncodedProfile(ctx->decoder, &ctx->jxl_pixfmt, + JXL_COLOR_PROFILE_TARGET_DATA, &jxl_color); +#else + jret = JxlDecoderGetColorAsEncodedProfile(ctx->decoder, JXL_COLOR_PROFILE_TARGET_DATA, &jxl_color); +#endif /* if we couldn't successfully request the pixel data space, we fall back on wide gamut */ /* this code path is very unlikely to happen in practice */ if (jret != JXL_DEC_SUCCESS) @@ -318,19 +357,33 @@ static int libjxl_color_encoding_event(AVCodecContext *avctx, AVFrame *frame) return 0; } -static int libjxl_decode_frame(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *avpkt) +static int libjxl_receive_frame(AVCodecContext *avctx, AVFrame *frame) { LibJxlDecodeContext *ctx = avctx->priv_data; - const uint8_t *buf = avpkt->data; - size_t remaining = avpkt->size; - JxlDecoderStatus jret; + JxlDecoderStatus jret = JXL_DEC_SUCCESS; int ret; - *got_frame = 0; + AVPacket *pkt = ctx->avpkt; while (1) { + size_t remaining; - jret = JxlDecoderSetInput(ctx->decoder, buf, remaining); + if (!pkt->size) { + av_packet_unref(pkt); + ret = ff_decode_get_packet(avctx, pkt); + if (ret < 0 && ret != AVERROR_EOF) + return ret; + if (!pkt->size) { + /* jret set by the last iteration of the loop */ + if (jret == JXL_DEC_NEED_MORE_INPUT) { + av_log(avctx, AV_LOG_ERROR, "Unexpected end of JXL codestream\n"); + return AVERROR_INVALIDDATA; + } else { + return AVERROR_EOF; + } + } + } + jret = JxlDecoderSetInput(ctx->decoder, pkt->data, pkt->size); if (jret == JXL_DEC_ERROR) { /* this should never happen here unless there's a bug in libjxl */ av_log(avctx, AV_LOG_ERROR, "Unknown libjxl decode error\n"); @@ -344,18 +397,19 @@ static int libjxl_decode_frame(AVCodecContext *avctx, AVFrame *frame, int *got_f * the number of bytes that it did read */ remaining = JxlDecoderReleaseInput(ctx->decoder); - buf = avpkt->data + avpkt->size - remaining; + pkt->data += pkt->size - remaining; + pkt->size = remaining; switch(jret) { case JXL_DEC_ERROR: av_log(avctx, AV_LOG_ERROR, "Unknown libjxl decode error\n"); return AVERROR_INVALIDDATA; case JXL_DEC_NEED_MORE_INPUT: - if (remaining == 0) { - av_log(avctx, AV_LOG_ERROR, "Unexpected end of JXL codestream\n"); - return AVERROR_INVALIDDATA; - } av_log(avctx, AV_LOG_DEBUG, "NEED_MORE_INPUT event emitted\n"); + if (!pkt->size) { + av_packet_unref(pkt); + return AVERROR(EAGAIN); + } continue; case JXL_DEC_BASIC_INFO: av_log(avctx, AV_LOG_DEBUG, "BASIC_INFO event emitted\n"); @@ -367,13 +421,20 @@ static int libjxl_decode_frame(AVCodecContext *avctx, AVFrame *frame, int *got_f av_log(avctx, AV_LOG_ERROR, "Bad libjxl basic info event\n"); return AVERROR_EXTERNAL; } - avctx->pix_fmt = libjxl_get_pix_fmt(avctx, &ctx->basic_info, &ctx->jxl_pixfmt); + avctx->pix_fmt = libjxl_get_pix_fmt(avctx, ctx); if (avctx->pix_fmt == AV_PIX_FMT_NONE) { av_log(avctx, AV_LOG_ERROR, "Bad libjxl pixel format\n"); return AVERROR_EXTERNAL; } if ((ret = ff_set_dimensions(avctx, ctx->basic_info.xsize, ctx->basic_info.ysize)) < 0) return ret; + if (ctx->basic_info.have_animation) + ctx->timebase = av_make_q(ctx->basic_info.animation.tps_denominator, + ctx->basic_info.animation.tps_numerator); + else if (avctx->pkt_timebase.num) + ctx->timebase = avctx->pkt_timebase; + else + ctx->timebase = AV_TIME_BASE_Q; continue; case JXL_DEC_COLOR_ENCODING: av_log(avctx, AV_LOG_DEBUG, "COLOR_ENCODING event emitted\n"); @@ -390,12 +451,35 @@ static int libjxl_decode_frame(AVCodecContext *avctx, AVFrame *frame, int *got_f av_log(avctx, AV_LOG_ERROR, "Bad libjxl dec need image out buffer event\n"); return AVERROR_EXTERNAL; } +#if JPEGXL_NUMERIC_VERSION >= JPEGXL_COMPUTE_NUMERIC_VERSION(0, 8, 0) + if (JxlDecoderSetImageOutBitDepth(ctx->decoder, &ctx->jxl_bit_depth) != JXL_DEC_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Error setting output bit depth\n"); + return AVERROR_EXTERNAL; + } +#endif + continue; + case JXL_DEC_FRAME: + av_log(avctx, AV_LOG_DEBUG, "FRAME event emitted\n"); + if (!ctx->basic_info.have_animation || ctx->prev_is_last) { + frame->pict_type = AV_PICTURE_TYPE_I; + frame->flags |= AV_FRAME_FLAG_KEY; + } + if (ctx->basic_info.have_animation) { + JxlFrameHeader header; + if (JxlDecoderGetFrameHeader(ctx->decoder, &header) != JXL_DEC_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Bad libjxl dec frame event\n"); + return AVERROR_EXTERNAL; + } + ctx->prev_is_last = header.is_last; + ctx->frame_duration = header.duration; + } else { + ctx->prev_is_last = 1; + ctx->frame_duration = 1; + } continue; case JXL_DEC_FULL_IMAGE: /* full image is one frame, even if animated */ av_log(avctx, AV_LOG_DEBUG, "FULL_IMAGE event emitted\n"); - frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; if (ctx->iccp) { AVFrameSideData *sd = av_frame_new_side_data_from_buf(frame, AV_FRAME_DATA_ICC_PROFILE, ctx->iccp); if (!sd) @@ -403,25 +487,25 @@ static int libjxl_decode_frame(AVCodecContext *avctx, AVFrame *frame, int *got_f /* ownership is transfered, and it is not ref-ed */ ctx->iccp = NULL; } - *got_frame = 1; - return avpkt->size - remaining; + if (avctx->pkt_timebase.num) { + frame->pts = av_rescale_q(ctx->pts, ctx->timebase, avctx->pkt_timebase); + frame->duration = av_rescale_q(ctx->frame_duration, ctx->timebase, avctx->pkt_timebase); + } else { + frame->pts = ctx->pts; + frame->duration = ctx->frame_duration; + } + ctx->pts += ctx->frame_duration; + return 0; case JXL_DEC_SUCCESS: av_log(avctx, AV_LOG_DEBUG, "SUCCESS event emitted\n"); /* - * The SUCCESS event isn't fired until after JXL_DEC_FULL_IMAGE. If this - * stream only contains one JXL image then JXL_DEC_SUCCESS will never fire. - * If the image2 sequence being decoded contains several JXL files, then - * libjxl will fire this event after the next AVPacket has been passed, - * which means the current packet is actually the next image in the sequence. - * This is why we reset the decoder and populate the packet data now, since - * this is the next packet and it has not been decoded yet. The decoder does - * have to be reset to allow us to use it for the next image, or libjxl - * will become very confused if the header information is not identical. + * this event will be fired when the zero-length EOF + * packet is sent to the decoder by the client, + * but it will also be fired when the next image of + * an image2pipe sequence is loaded up */ JxlDecoderReset(ctx->decoder); libjxl_init_jxl_decoder(avctx); - buf = avpkt->data; - remaining = avpkt->size; continue; default: av_log(avctx, AV_LOG_ERROR, "Bad libjxl event: %d\n", jret); @@ -452,7 +536,7 @@ const FFCodec ff_libjxl_decoder = { .p.id = AV_CODEC_ID_JPEGXL, .priv_data_size = sizeof(LibJxlDecodeContext), .init = libjxl_decode_init, - FF_CODEC_DECODE_CB(libjxl_decode_frame), + FF_CODEC_RECEIVE_FRAME_CB(libjxl_receive_frame), .close = libjxl_decode_close, .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_OTHER_THREADS, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | diff --git a/libavcodec/libjxlenc.c b/libavcodec/libjxlenc.c index 0793ed251b0..d707f3a61b5 100644 --- a/libavcodec/libjxlenc.c +++ b/libavcodec/libjxlenc.c @@ -250,6 +250,10 @@ static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFra JxlBasicInfo info; JxlColorEncoding jxl_color; JxlPixelFormat jxl_fmt; + int bits_per_sample; +#if JPEGXL_NUMERIC_VERSION >= JPEGXL_COMPUTE_NUMERIC_VERSION(0, 8, 0) + JxlBitDepth jxl_bit_depth; +#endif JxlEncoderStatus jret; int ret; size_t available = ctx->buffer_size; @@ -269,7 +273,9 @@ static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFra info.ysize = frame->height; info.num_extra_channels = (jxl_fmt.num_channels + 1) % 2; info.num_color_channels = jxl_fmt.num_channels - info.num_extra_channels; - info.bits_per_sample = av_get_bits_per_pixel(pix_desc) / jxl_fmt.num_channels; + bits_per_sample = av_get_bits_per_pixel(pix_desc) / jxl_fmt.num_channels; + info.bits_per_sample = avctx->bits_per_raw_sample > 0 && !(pix_desc->flags & AV_PIX_FMT_FLAG_FLOAT) + ? avctx->bits_per_raw_sample : bits_per_sample; info.alpha_bits = (info.num_extra_channels > 0) * info.bits_per_sample; if (pix_desc->flags & AV_PIX_FMT_FLAG_FLOAT) { info.exponent_bits_per_sample = info.bits_per_sample > 16 ? 8 : 5; @@ -281,6 +287,13 @@ static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFra jxl_fmt.data_type = info.bits_per_sample <= 8 ? JXL_TYPE_UINT8 : JXL_TYPE_UINT16; } +#if JPEGXL_NUMERIC_VERSION >= JPEGXL_COMPUTE_NUMERIC_VERSION(0, 8, 0) + jxl_bit_depth.bits_per_sample = bits_per_sample; + jxl_bit_depth.type = JXL_BIT_DEPTH_FROM_PIXEL_FORMAT; + jxl_bit_depth.exponent_bits_per_sample = pix_desc->flags & AV_PIX_FMT_FLAG_FLOAT ? + info.exponent_bits_per_sample : 0; +#endif + /* JPEG XL format itself does not support limited range */ if (avctx->color_range == AVCOL_RANGE_MPEG || avctx->color_range == AVCOL_RANGE_UNSPECIFIED && frame->color_range == AVCOL_RANGE_MPEG) @@ -322,11 +335,11 @@ static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFra break; case AVCOL_TRC_GAMMA22: jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_GAMMA; - jxl_color.gamma = 2.2; + jxl_color.gamma = 1/2.2f; break; case AVCOL_TRC_GAMMA28: jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_GAMMA; - jxl_color.gamma = 2.8; + jxl_color.gamma = 1/2.8f; break; default: if (pix_desc->flags & AV_PIX_FMT_FLAG_FLOAT) { @@ -357,6 +370,11 @@ static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFra if (JxlEncoderSetColorEncoding(ctx->encoder, &jxl_color) != JXL_ENC_SUCCESS) av_log(avctx, AV_LOG_WARNING, "Failed to set JxlColorEncoding\n"); +#if JPEGXL_NUMERIC_VERSION >= JPEGXL_COMPUTE_NUMERIC_VERSION(0, 8, 0) + if (JxlEncoderSetFrameBitDepth(ctx->options, &jxl_bit_depth) != JXL_ENC_SUCCESS) + av_log(avctx, AV_LOG_WARNING, "Failed to set JxlBitDepth\n"); +#endif + /* depending on basic info, level 10 might * be required instead of level 5 */ if (JxlEncoderGetRequiredCodestreamLevel(ctx->encoder) > 5) { @@ -467,7 +485,8 @@ const FFCodec ff_libjxl_encoder = { .init = libjxl_encode_init, FF_CODEC_ENCODE_CB(libjxl_encode_frame), .close = libjxl_encode_close, - .p.capabilities = AV_CODEC_CAP_OTHER_THREADS, + .p.capabilities = AV_CODEC_CAP_OTHER_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | FF_CODEC_CAP_AUTO_THREADS | FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_ICC_PROFILES, diff --git a/libavcodec/libkvazaar.c b/libavcodec/libkvazaar.c index 168486f4ecb..2ef34dd82e2 100644 --- a/libavcodec/libkvazaar.c +++ b/libavcodec/libkvazaar.c @@ -85,13 +85,14 @@ static av_cold int libkvazaar_init(AVCodecContext *avctx) cfg->framerate_num = avctx->framerate.num; cfg->framerate_denom = avctx->framerate.den; } else { - if (avctx->ticks_per_frame > INT_MAX / avctx->time_base.num) { - av_log(avctx, AV_LOG_ERROR, - "Could not set framerate for kvazaar: integer overflow\n"); - return AVERROR(EINVAL); - } cfg->framerate_num = avctx->time_base.den; - cfg->framerate_denom = avctx->time_base.num * avctx->ticks_per_frame; +FF_DISABLE_DEPRECATION_WARNINGS + cfg->framerate_denom = avctx->time_base.num +#if FF_API_TICKS_PER_FRAME + * avctx->ticks_per_frame +#endif + ; +FF_ENABLE_DEPRECATION_WARNINGS } cfg->target_bitrate = avctx->bit_rate; cfg->vui.sar_width = avctx->sample_aspect_ratio.num; diff --git a/libavcodec/libmp3lame.c b/libavcodec/libmp3lame.c index 26e58baa3d0..312bc4230fa 100644 --- a/libavcodec/libmp3lame.c +++ b/libavcodec/libmp3lame.c @@ -55,6 +55,8 @@ typedef struct LAMEContext { float *samples_flt[2]; AudioFrameQueue afq; AVFloatDSPContext *fdsp; + int copyright; + int original; } LAMEContext; @@ -137,6 +139,12 @@ static av_cold int mp3lame_encode_init(AVCodecContext *avctx) /* bit reservoir usage */ lame_set_disable_reservoir(s->gfp, !s->reservoir); + /* copyright flag */ + lame_set_copyright(s->gfp, s->copyright); + + /* original flag */ + lame_set_original(s->gfp, s->original); + /* set specified parameters */ if (lame_init_params(s->gfp) < 0) { ret = AVERROR_EXTERNAL; @@ -280,17 +288,14 @@ static int mp3lame_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, // Check if subtraction resulted in an overflow if ((discard_padding < avctx->frame_size) != (avpkt->duration > 0)) { av_log(avctx, AV_LOG_ERROR, "discard padding overflow\n"); - av_packet_unref(avpkt); return AVERROR(EINVAL); } if ((!s->delay_sent && avctx->initial_padding > 0) || discard_padding > 0) { uint8_t* side_data = av_packet_new_side_data(avpkt, AV_PKT_DATA_SKIP_SAMPLES, 10); - if(!side_data) { - av_packet_unref(avpkt); + if (!side_data) return AVERROR(ENOMEM); - } if (!s->delay_sent) { AV_WL32(side_data, avctx->initial_padding); s->delay_sent = 1; @@ -306,9 +311,11 @@ static int mp3lame_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, #define OFFSET(x) offsetof(LAMEContext, x) #define AE AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - { "reservoir", "use bit reservoir", OFFSET(reservoir), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, AE }, - { "joint_stereo", "use joint stereo", OFFSET(joint_stereo), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, AE }, - { "abr", "use ABR", OFFSET(abr), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AE }, + { "reservoir", "use bit reservoir", OFFSET(reservoir), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, AE }, + { "joint_stereo", "use joint stereo", OFFSET(joint_stereo), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, AE }, + { "abr", "use ABR", OFFSET(abr), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AE }, + { "copyright", "set copyright flag", OFFSET(copyright), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AE}, + { "original", "set original flag", OFFSET(original), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, AE}, { NULL }, }; diff --git a/libavcodec/libopencore-amr.c b/libavcodec/libopencore-amr.c index fd9e6e63430..641a1561298 100644 --- a/libavcodec/libopencore-amr.c +++ b/libavcodec/libopencore-amr.c @@ -106,8 +106,8 @@ static int amr_nb_decode_frame(AVCodecContext *avctx, AVFrame *frame, enum Mode dec_mode; int packet_size, ret; - ff_dlog(avctx, "amr_decode_frame buf=%p buf_size=%d frame_count=%d!!\n", - buf, buf_size, avctx->frame_number); + ff_dlog(avctx, "amr_decode_frame buf=%p buf_size=%d frame_count=%"PRId64"!!\n", + buf, buf_size, avctx->frame_num); /* get output buffer */ frame->nb_samples = 160; diff --git a/libavcodec/libopenh264enc.c b/libavcodec/libopenh264enc.c index bbd69695682..5b59af6f947 100644 --- a/libavcodec/libopenh264enc.c +++ b/libavcodec/libopenh264enc.c @@ -50,9 +50,6 @@ typedef struct SVCContext { int max_nal_size; int skip_frames; int skipped; -#if FF_API_OPENH264_CABAC - int cabac; // deprecated -#endif int coder; // rate control mode @@ -63,22 +60,6 @@ typedef struct SVCContext { #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM #define DEPRECATED AV_OPT_FLAG_DEPRECATED static const AVOption options[] = { -#if FF_API_OPENH264_SLICE_MODE -#if OPENH264_VER_AT_LEAST(1, 6) - { "slice_mode", "set slice mode, use slices/max_nal_size", OFFSET(slice_mode), AV_OPT_TYPE_INT, { .i64 = SM_FIXEDSLCNUM_SLICE }, SM_SINGLE_SLICE, SM_RESERVED, VE|DEPRECATED, "slice_mode" }, -#else - { "slice_mode", "set slice mode, use slices/max_nal_size", OFFSET(slice_mode), AV_OPT_TYPE_INT, { .i64 = SM_AUTO_SLICE }, SM_SINGLE_SLICE, SM_RESERVED, VE|DEPRECATED, "slice_mode" }, -#endif - { "fixed", "a fixed number of slices", 0, AV_OPT_TYPE_CONST, { .i64 = SM_FIXEDSLCNUM_SLICE }, 0, 0, VE, "slice_mode" }, -#if OPENH264_VER_AT_LEAST(1, 6) - { "dyn", "Size limited (compatibility name)", 0, AV_OPT_TYPE_CONST, { .i64 = SM_SIZELIMITED_SLICE }, 0, 0, VE, "slice_mode" }, - { "sizelimited", "Size limited", 0, AV_OPT_TYPE_CONST, { .i64 = SM_SIZELIMITED_SLICE }, 0, 0, VE, "slice_mode" }, -#else - { "rowmb", "one slice per row of macroblocks", 0, AV_OPT_TYPE_CONST, { .i64 = SM_ROWMB_SLICE }, 0, 0, VE, "slice_mode" }, - { "auto", "automatic number of slices according to number of threads", 0, AV_OPT_TYPE_CONST, { .i64 = SM_AUTO_SLICE }, 0, 0, VE, "slice_mode" }, - { "dyn", "Dynamic slicing", 0, AV_OPT_TYPE_CONST, { .i64 = SM_DYN_SLICE }, 0, 0, VE, "slice_mode" }, -#endif -#endif { "loopfilter", "enable loop filter", OFFSET(loopfilter), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, VE }, { "profile", "set profile restrictions", OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = FF_PROFILE_UNKNOWN }, FF_PROFILE_UNKNOWN, 0xffff, VE, "profile" }, #define PROFILE(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, { .i64 = value }, 0, 0, VE, "profile" @@ -88,9 +69,6 @@ static const AVOption options[] = { #undef PROFILE { "max_nal_size", "set maximum NAL size in bytes", OFFSET(max_nal_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, { "allow_skip_frames", "allow skipping frames to hit the target bitrate", OFFSET(skip_frames), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, -#if FF_API_OPENH264_CABAC - { "cabac", "Enable cabac(deprecated, use coder)", OFFSET(cabac), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE|DEPRECATED }, -#endif { "coder", "Coder type", OFFSET(coder), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, VE, "coder" }, { "default", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, INT_MIN, INT_MAX, VE, "coder" }, { "cavlc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, "coder" }, @@ -161,12 +139,13 @@ static av_cold int svc_encode_init(AVCodecContext *avctx) if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { param.fMaxFrameRate = av_q2d(avctx->framerate); } else { - if (avctx->ticks_per_frame > INT_MAX / avctx->time_base.num) { - av_log(avctx, AV_LOG_ERROR, - "Could not set framerate for libopenh264enc: integer overflow\n"); - return AVERROR(EINVAL); - } - param.fMaxFrameRate = 1.0 / av_q2d(avctx->time_base) / FFMAX(avctx->ticks_per_frame, 1); +FF_DISABLE_DEPRECATION_WARNINGS + param.fMaxFrameRate = 1.0 / av_q2d(avctx->time_base) +#if FF_API_TICKS_PER_FRAME + / FFMAX(avctx->ticks_per_frame, 1) +#endif + ; +FF_ENABLE_DEPRECATION_WARNINGS } param.iPicWidth = avctx->width; param.iPicHeight = avctx->height; @@ -333,15 +312,15 @@ static av_cold int svc_encode_init(AVCodecContext *avctx) #if OPENH264_VER_AT_LEAST(1, 6) param.sSpatialLayers[0].uiVideoFormat = VF_UNDEF; + if (avctx->color_range != AVCOL_RANGE_UNSPECIFIED) { - param.sSpatialLayers[0].bVideoSignalTypePresent = true; param.sSpatialLayers[0].bFullRange = (avctx->color_range == AVCOL_RANGE_JPEG); - } + } else if (avctx->pix_fmt == AV_PIX_FMT_YUVJ420P) + param.sSpatialLayers[0].bFullRange = 1; if (avctx->colorspace != AVCOL_SPC_UNSPECIFIED || avctx->color_primaries != AVCOL_PRI_UNSPECIFIED || avctx->color_trc != AVCOL_TRC_UNSPECIFIED) { - param.sSpatialLayers[0].bVideoSignalTypePresent = true; param.sSpatialLayers[0].bColorDescriptionPresent = true; } @@ -351,6 +330,9 @@ static av_cold int svc_encode_init(AVCodecContext *avctx) param.sSpatialLayers[0].uiColorPrimaries = avctx->color_primaries; if (avctx->color_trc != AVCOL_TRC_UNSPECIFIED) param.sSpatialLayers[0].uiTransferCharacteristics = avctx->color_trc; + + param.sSpatialLayers[0].bVideoSignalTypePresent = + (param.sSpatialLayers[0].bFullRange || param.sSpatialLayers[0].bColorDescriptionPresent); #endif if ((*s->encoder)->InitializeExt(s->encoder, ¶m) != cmResultSuccess) { @@ -456,7 +438,8 @@ const FFCodec ff_libopenh264_encoder = { CODEC_LONG_NAME("OpenH264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_H264, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_OTHER_THREADS, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_OTHER_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(SVCContext), .init = svc_encode_init, FF_CODEC_ENCODE_CB(svc_encode_frame), @@ -464,6 +447,7 @@ const FFCodec ff_libopenh264_encoder = { .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_AUTO_THREADS, .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_NONE }, .defaults = svc_enc_defaults, .p.priv_class = &class, diff --git a/libavcodec/libopenjpegdec.c b/libavcodec/libopenjpegdec.c deleted file mode 100644 index 206db07ec70..00000000000 --- a/libavcodec/libopenjpegdec.c +++ /dev/null @@ -1,516 +0,0 @@ -/* - * JPEG 2000 decoding support via OpenJPEG - * Copyright (c) 2009 Jaikrishnan Menon - * - * 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 - * JPEG 2000 decoder using libopenjpeg - */ - -#include "libavutil/common.h" -#include "libavutil/imgutils.h" -#include "libavutil/intreadwrite.h" -#include "libavutil/opt.h" -#include "libavutil/pixfmt.h" - -#include "avcodec.h" -#include "codec_internal.h" -#include "decode.h" -#include "thread.h" - -#include - -#define JP2_SIG_TYPE 0x6A502020 -#define JP2_SIG_VALUE 0x0D0A870A - -// pix_fmts with lower bpp have to be listed before -// similar pix_fmts with higher bpp. -#define RGB_PIXEL_FORMATS AV_PIX_FMT_RGB24, AV_PIX_FMT_RGBA, \ - AV_PIX_FMT_RGB48, AV_PIX_FMT_RGBA64 - -#define GRAY_PIXEL_FORMATS AV_PIX_FMT_GRAY8, AV_PIX_FMT_YA8, \ - AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14, \ - AV_PIX_FMT_GRAY16, AV_PIX_FMT_YA16 - -#define YUV_PIXEL_FORMATS AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUVA420P, \ - AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA422P, \ - AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVA444P, \ - AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9, \ - AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9, \ - AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10, \ - AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10, \ - AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, \ - AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14, \ - AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, \ - AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16 - -#define XYZ_PIXEL_FORMATS AV_PIX_FMT_XYZ12 - -static const enum AVPixelFormat libopenjpeg_rgb_pix_fmts[] = { - RGB_PIXEL_FORMATS -}; -static const enum AVPixelFormat libopenjpeg_gray_pix_fmts[] = { - GRAY_PIXEL_FORMATS -}; -static const enum AVPixelFormat libopenjpeg_yuv_pix_fmts[] = { - YUV_PIXEL_FORMATS -}; -static const enum AVPixelFormat libopenjpeg_all_pix_fmts[] = { - RGB_PIXEL_FORMATS, GRAY_PIXEL_FORMATS, YUV_PIXEL_FORMATS, XYZ_PIXEL_FORMATS -}; - -typedef struct LibOpenJPEGContext { - AVClass *class; - opj_dparameters_t dec_params; - int lowqual; -} LibOpenJPEGContext; - -static void error_callback(const char *msg, void *data) -{ - av_log(data, AV_LOG_ERROR, "%s", msg); -} - -static void warning_callback(const char *msg, void *data) -{ - av_log(data, AV_LOG_WARNING, "%s", msg); -} - -static void info_callback(const char *msg, void *data) -{ - av_log(data, AV_LOG_DEBUG, "%s", msg); -} - -typedef struct BufferReader { - int pos; - int size; - const uint8_t *buffer; -} BufferReader; - -static OPJ_SIZE_T stream_read(void *out_buffer, OPJ_SIZE_T nb_bytes, void *user_data) -{ - BufferReader *reader = user_data; - int remaining; - - if (reader->pos == reader->size) { - return (OPJ_SIZE_T)-1; - } - remaining = reader->size - reader->pos; - if (nb_bytes > remaining) { - nb_bytes = remaining; - } - memcpy(out_buffer, reader->buffer + reader->pos, nb_bytes); - reader->pos += (int)nb_bytes; - return nb_bytes; -} - -static OPJ_OFF_T stream_skip(OPJ_OFF_T nb_bytes, void *user_data) -{ - BufferReader *reader = user_data; - if (nb_bytes < 0) { - if (reader->pos == 0) { - return (OPJ_SIZE_T)-1; - } - if (nb_bytes + reader->pos < 0) { - nb_bytes = -reader->pos; - } - } else { - int remaining; - - if (reader->pos == reader->size) { - return (OPJ_SIZE_T)-1; - } - remaining = reader->size - reader->pos; - if (nb_bytes > remaining) { - nb_bytes = remaining; - } - } - reader->pos += (int)nb_bytes; - return nb_bytes; -} - -static OPJ_BOOL stream_seek(OPJ_OFF_T nb_bytes, void *user_data) -{ - BufferReader *reader = user_data; - if (nb_bytes < 0 || nb_bytes > reader->size) { - return OPJ_FALSE; - } - reader->pos = (int)nb_bytes; - return OPJ_TRUE; -} - -static inline int libopenjpeg_matches_pix_fmt(const opj_image_t *image, enum AVPixelFormat pix_fmt) -{ - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt); - int match = 1; - - if (desc->nb_components != image->numcomps) { - return 0; - } - - switch (desc->nb_components) { - case 4: - match = match && - desc->comp[3].depth >= image->comps[3].prec && - 1 == image->comps[3].dx && - 1 == image->comps[3].dy; - case 3: - match = match && - desc->comp[2].depth >= image->comps[2].prec && - 1 << desc->log2_chroma_w == image->comps[2].dx && - 1 << desc->log2_chroma_h == image->comps[2].dy; - case 2: - match = match && - desc->comp[1].depth >= image->comps[1].prec && - 1 << desc->log2_chroma_w == image->comps[1].dx && - 1 << desc->log2_chroma_h == image->comps[1].dy; - case 1: - match = match && - desc->comp[0].depth >= image->comps[0].prec && - 1 == image->comps[0].dx && - 1 == image->comps[0].dy; - default: - break; - } - - return match; -} - -static inline enum AVPixelFormat libopenjpeg_guess_pix_fmt(const opj_image_t *image) { - int index; - const enum AVPixelFormat *possible_fmts = NULL; - int possible_fmts_nb = 0; - - switch (image->color_space) { - case OPJ_CLRSPC_SRGB: - possible_fmts = libopenjpeg_rgb_pix_fmts; - possible_fmts_nb = FF_ARRAY_ELEMS(libopenjpeg_rgb_pix_fmts); - break; - case OPJ_CLRSPC_GRAY: - possible_fmts = libopenjpeg_gray_pix_fmts; - possible_fmts_nb = FF_ARRAY_ELEMS(libopenjpeg_gray_pix_fmts); - break; - case OPJ_CLRSPC_SYCC: - possible_fmts = libopenjpeg_yuv_pix_fmts; - possible_fmts_nb = FF_ARRAY_ELEMS(libopenjpeg_yuv_pix_fmts); - break; - default: - possible_fmts = libopenjpeg_all_pix_fmts; - possible_fmts_nb = FF_ARRAY_ELEMS(libopenjpeg_all_pix_fmts); - break; - } - - for (index = 0; index < possible_fmts_nb; ++index) - if (libopenjpeg_matches_pix_fmt(image, possible_fmts[index])) { - return possible_fmts[index]; - } - - return AV_PIX_FMT_NONE; -} - -static inline int libopenjpeg_ispacked(enum AVPixelFormat pix_fmt) -{ - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt); - int i, component_plane; - - if (pix_fmt == AV_PIX_FMT_GRAY16) - return 0; - - component_plane = desc->comp[0].plane; - for (i = 1; i < desc->nb_components; i++) - if (component_plane != desc->comp[i].plane) - return 0; - return 1; -} - -static inline void libopenjpeg_copy_to_packed8(AVFrame *picture, opj_image_t *image) { - uint8_t *img_ptr; - int index, x, y, c; - for (y = 0; y < picture->height; y++) { - index = y * picture->width; - img_ptr = picture->data[0] + y * picture->linesize[0]; - for (x = 0; x < picture->width; x++, index++) - for (c = 0; c < image->numcomps; c++) - *img_ptr++ = 0x80 * image->comps[c].sgnd + image->comps[c].data[index]; - } -} - -static inline void libopenjpeg_copy_to_packed16(AVFrame *picture, opj_image_t *image) { - uint16_t *img_ptr; - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(picture->format); - int index, x, y, c; - int adjust[4]; - for (x = 0; x < image->numcomps; x++) - adjust[x] = FFMAX(FFMIN(desc->comp[x].depth - image->comps[x].prec, 8), 0) + desc->comp[x].shift; - - for (y = 0; y < picture->height; y++) { - index = y * picture->width; - img_ptr = (uint16_t *) (picture->data[0] + y * picture->linesize[0]); - for (x = 0; x < picture->width; x++, index++) - for (c = 0; c < image->numcomps; c++) - *img_ptr++ = (1 << image->comps[c].prec - 1) * image->comps[c].sgnd + - (unsigned)image->comps[c].data[index] << adjust[c]; - } -} - -static inline void libopenjpeg_copyto8(AVFrame *picture, opj_image_t *image) { - int *comp_data; - uint8_t *img_ptr; - int index, x, y; - - for (index = 0; index < image->numcomps; index++) { - comp_data = image->comps[index].data; - for (y = 0; y < image->comps[index].h; y++) { - img_ptr = picture->data[index] + y * picture->linesize[index]; - for (x = 0; x < image->comps[index].w; x++) { - *img_ptr = 0x80 * image->comps[index].sgnd + *comp_data; - img_ptr++; - comp_data++; - } - } - } -} - -static inline void libopenjpeg_copyto16(AVFrame *picture, opj_image_t *image) { - int *comp_data; - uint16_t *img_ptr; - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(picture->format); - int index, x, y; - int adjust[4]; - for (x = 0; x < image->numcomps; x++) - adjust[x] = FFMAX(FFMIN(desc->comp[x].depth - image->comps[x].prec, 8), 0) + desc->comp[x].shift; - - for (index = 0; index < image->numcomps; index++) { - comp_data = image->comps[index].data; - for (y = 0; y < image->comps[index].h; y++) { - img_ptr = (uint16_t *)(picture->data[index] + y * picture->linesize[index]); - for (x = 0; x < image->comps[index].w; x++) { - *img_ptr = (1 << image->comps[index].prec - 1) * image->comps[index].sgnd + - (unsigned)*comp_data << adjust[index]; - img_ptr++; - comp_data++; - } - } - } -} - -static av_cold int libopenjpeg_decode_init(AVCodecContext *avctx) -{ - LibOpenJPEGContext *ctx = avctx->priv_data; - - opj_set_default_decoder_parameters(&ctx->dec_params); - return 0; -} - -static int libopenjpeg_decode_frame(AVCodecContext *avctx, AVFrame *picture, - int *got_frame, AVPacket *avpkt) -{ - const uint8_t *buf = avpkt->data; - int buf_size = avpkt->size; - LibOpenJPEGContext *ctx = avctx->priv_data; - const AVPixFmtDescriptor *desc; - int width, height, ret; - int pixel_size = 0; - int ispacked = 0; - int i; - opj_image_t *image = NULL; - BufferReader reader = {0, avpkt->size, avpkt->data}; - opj_codec_t *dec = NULL; - opj_stream_t *stream = NULL; - - *got_frame = 0; - - // Check if input is a raw jpeg2k codestream or in jp2 wrapping - if ((AV_RB32(buf) == 12) && - (AV_RB32(buf + 4) == JP2_SIG_TYPE) && - (AV_RB32(buf + 8) == JP2_SIG_VALUE)) { - dec = opj_create_decompress(OPJ_CODEC_JP2); - } else { - /* If the AVPacket contains a jp2c box, then skip to - * the starting byte of the codestream. */ - if (AV_RB32(buf + 4) == AV_RB32("jp2c")) - buf += 8; - dec = opj_create_decompress(OPJ_CODEC_J2K); - } - - if (!dec) { - av_log(avctx, AV_LOG_ERROR, "Error initializing decoder.\n"); - ret = AVERROR_EXTERNAL; - goto done; - } - - if (!opj_set_error_handler(dec, error_callback, avctx) || - !opj_set_warning_handler(dec, warning_callback, avctx) || - !opj_set_info_handler(dec, info_callback, avctx)) { - av_log(avctx, AV_LOG_ERROR, "Error setting decoder handlers.\n"); - ret = AVERROR_EXTERNAL; - goto done; - } - - ctx->dec_params.cp_layer = ctx->lowqual; - ctx->dec_params.cp_reduce = avctx->lowres; - - // Tie decoder with decoding parameters - opj_setup_decoder(dec, &ctx->dec_params); - - stream = opj_stream_default_create(OPJ_STREAM_READ); - - if (!stream) { - av_log(avctx, AV_LOG_ERROR, - "Codestream could not be opened for reading.\n"); - ret = AVERROR_EXTERNAL; - goto done; - } - - opj_stream_set_read_function(stream, stream_read); - opj_stream_set_skip_function(stream, stream_skip); - opj_stream_set_seek_function(stream, stream_seek); - opj_stream_set_user_data(stream, &reader, NULL); - opj_stream_set_user_data_length(stream, avpkt->size); - // Decode the header only. - ret = !opj_read_header(stream, dec, &image); - - if (ret) { - av_log(avctx, AV_LOG_ERROR, "Error decoding codestream header.\n"); - ret = AVERROR_EXTERNAL; - goto done; - } - - width = image->x1 - image->x0; - height = image->y1 - image->y0; - - ret = ff_set_dimensions(avctx, width, height); - if (ret < 0) - goto done; - - if (avctx->pix_fmt != AV_PIX_FMT_NONE) - if (!libopenjpeg_matches_pix_fmt(image, avctx->pix_fmt)) - avctx->pix_fmt = AV_PIX_FMT_NONE; - - if (avctx->pix_fmt == AV_PIX_FMT_NONE) - avctx->pix_fmt = libopenjpeg_guess_pix_fmt(image); - - if (avctx->pix_fmt == AV_PIX_FMT_NONE) { - av_log(avctx, AV_LOG_ERROR, "Unable to determine pixel format.\n"); - ret = AVERROR_UNKNOWN; - goto done; - } - for (i = 0; i < image->numcomps; i++) - if (image->comps[i].prec > avctx->bits_per_raw_sample) - avctx->bits_per_raw_sample = image->comps[i].prec; - - if ((ret = ff_thread_get_buffer(avctx, picture, 0)) < 0) - goto done; - - ret = !opj_decode(dec, stream, image); - - if (ret) { - av_log(avctx, AV_LOG_ERROR, "Error decoding codestream.\n"); - ret = AVERROR_EXTERNAL; - goto done; - } - - for (i = 0; i < image->numcomps; i++) { - if (!image->comps[i].data) { - av_log(avctx, AV_LOG_ERROR, - "Image component %d contains no data.\n", i); - ret = AVERROR_INVALIDDATA; - goto done; - } - } - - desc = av_pix_fmt_desc_get(avctx->pix_fmt); - pixel_size = desc->comp[0].step; - ispacked = libopenjpeg_ispacked(avctx->pix_fmt); - - switch (pixel_size) { - case 1: - if (ispacked) { - libopenjpeg_copy_to_packed8(picture, image); - } else { - libopenjpeg_copyto8(picture, image); - } - break; - case 2: - if (ispacked) { - libopenjpeg_copy_to_packed8(picture, image); - } else { - libopenjpeg_copyto16(picture, image); - } - break; - case 3: - case 4: - if (ispacked) { - libopenjpeg_copy_to_packed8(picture, image); - } - break; - case 6: - case 8: - if (ispacked) { - libopenjpeg_copy_to_packed16(picture, image); - } - break; - default: - avpriv_report_missing_feature(avctx, "Pixel size %d", pixel_size); - ret = AVERROR_PATCHWELCOME; - goto done; - } - - *got_frame = 1; - picture->pict_type = AV_PICTURE_TYPE_I; - picture->key_frame = 1; - ret = buf_size; - -done: - opj_image_destroy(image); - opj_stream_destroy(stream); - opj_destroy_codec(dec); - return ret; -} - -#define OFFSET(x) offsetof(LibOpenJPEGContext, x) -#define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM - -static const AVOption options[] = { - { "lowqual", "Limit the number of layers used for decoding", - OFFSET(lowqual), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VD }, - { NULL }, -}; - -static const AVClass openjpeg_class = { - .class_name = "libopenjpeg", - .item_name = av_default_item_name, - .option = options, - .version = LIBAVUTIL_VERSION_INT, -}; - -const FFCodec ff_libopenjpeg_decoder = { - .p.name = "libopenjpeg", - CODEC_LONG_NAME("OpenJPEG JPEG 2000"), - .p.type = AVMEDIA_TYPE_VIDEO, - .p.id = AV_CODEC_ID_JPEG2000, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, - .p.max_lowres = 31, - .p.priv_class = &openjpeg_class, - .p.wrapper_name = "libopenjpeg", - .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE, - .priv_data_size = sizeof(LibOpenJPEGContext), - .init = libopenjpeg_decode_init, - FF_CODEC_DECODE_CB(libopenjpeg_decode_frame), -}; diff --git a/libavcodec/libopenjpegenc.c b/libavcodec/libopenjpegenc.c index 6f77780391f..009c7a43774 100644 --- a/libavcodec/libopenjpegenc.c +++ b/libavcodec/libopenjpegenc.c @@ -763,7 +763,8 @@ const FFCodec ff_libopenjpeg_encoder = { .priv_data_size = sizeof(LibOpenJPEGContext), .init = libopenjpeg_encode_init, FF_CODEC_ENCODE_CB(libopenjpeg_encode_frame), - .p.capabilities = AV_CODEC_CAP_FRAME_THREADS, + .p.capabilities = AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_RGB24, AV_PIX_FMT_RGBA, AV_PIX_FMT_RGB48, AV_PIX_FMT_RGBA64, AV_PIX_FMT_GBR24P, diff --git a/libavcodec/libopusenc.c b/libavcodec/libopusenc.c index 75bc491c9ef..5a0786f32fd 100644 --- a/libavcodec/libopusenc.c +++ b/libavcodec/libopusenc.c @@ -512,18 +512,14 @@ static int libopus_encode(AVCodecContext *avctx, AVPacket *avpkt, discard_padding = opus->opts.packet_size - avpkt->duration; // Check if subtraction resulted in an overflow - if ((discard_padding < opus->opts.packet_size) != (avpkt->duration > 0)) { - av_packet_unref(avpkt); + if ((discard_padding < opus->opts.packet_size) != (avpkt->duration > 0)) return AVERROR(EINVAL); - } if (discard_padding > 0) { uint8_t* side_data = av_packet_new_side_data(avpkt, AV_PKT_DATA_SKIP_SAMPLES, 10); - if(!side_data) { - av_packet_unref(avpkt); + if (!side_data) return AVERROR(ENOMEM); - } AV_WL32(side_data + 4, discard_padding); } diff --git a/libavcodec/librav1e.c b/libavcodec/librav1e.c index 3481b7637d6..56539435a7e 100644 --- a/libavcodec/librav1e.c +++ b/libavcodec/librav1e.c @@ -22,6 +22,7 @@ #include +#include "libavutil/buffer.h" #include "libavutil/internal.h" #include "libavutil/avassert.h" #include "libavutil/base64.h" @@ -53,6 +54,17 @@ typedef struct librav1eContext { int tile_cols; } librav1eContext; +typedef struct FrameData { + int64_t pts; + int64_t duration; +#if FF_API_REORDERED_OPAQUE + int64_t reordered_opaque; +#endif + + void *frame_opaque; + AVBufferRef *frame_opaque_ref; +} FrameData; + static inline RaPixelRange range_map(enum AVPixelFormat pix_fmt, enum AVColorRange range) { switch (pix_fmt) { @@ -207,10 +219,15 @@ static av_cold int librav1e_encode_init(AVCodecContext *avctx) avctx->framerate.den, avctx->framerate.num }); } else { +FF_DISABLE_DEPRECATION_WARNINGS rav1e_config_set_time_base(cfg, (RaRational) { - avctx->time_base.num * avctx->ticks_per_frame, - avctx->time_base.den + avctx->time_base.num +#if FF_API_TICKS_PER_FRAME + * avctx->ticks_per_frame +#endif + , avctx->time_base.den }); +FF_ENABLE_DEPRECATION_WARNINGS } if ((avctx->flags & AV_CODEC_FLAG_PASS1 || avctx->flags & AV_CODEC_FLAG_PASS2) && !avctx->bit_rate) { @@ -419,11 +436,23 @@ static av_cold int librav1e_encode_init(AVCodecContext *avctx) return ret; } +static void frame_data_free(void *data) +{ + FrameData *fd = data; + + if (!fd) + return; + + av_buffer_unref(&fd->frame_opaque_ref); + av_free(data); +} + static int librav1e_receive_packet(AVCodecContext *avctx, AVPacket *pkt) { librav1eContext *ctx = avctx->priv_data; RaFrame *rframe = ctx->rframe; RaPacket *rpkt = NULL; + FrameData *fd; int ret; if (!rframe) { @@ -436,18 +465,34 @@ static int librav1e_receive_packet(AVCodecContext *avctx, AVPacket *pkt) if (frame->buf[0]) { const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format); - int64_t *pts = av_malloc(sizeof(int64_t)); - if (!pts) { + fd = av_mallocz(sizeof(*fd)); + if (!fd) { av_log(avctx, AV_LOG_ERROR, "Could not allocate PTS buffer.\n"); return AVERROR(ENOMEM); } - *pts = frame->pts; + fd->pts = frame->pts; + fd->duration = frame->duration; +#if FF_API_REORDERED_OPAQUE +FF_DISABLE_DEPRECATION_WARNINGS + fd->reordered_opaque = frame->reordered_opaque; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { + fd->frame_opaque = frame->opaque; + ret = av_buffer_replace(&fd->frame_opaque_ref, frame->opaque_ref); + if (ret < 0) { + frame_data_free(fd); + av_frame_unref(frame); + return ret; + } + } rframe = rav1e_frame_new(ctx->ctx); if (!rframe) { av_log(avctx, AV_LOG_ERROR, "Could not allocate new rav1e frame.\n"); av_frame_unref(frame); - av_freep(&pts); + frame_data_free(fd); return AVERROR(ENOMEM); } @@ -459,7 +504,7 @@ static int librav1e_receive_packet(AVCodecContext *avctx, AVPacket *pkt) frame->linesize[i], bytes); } av_frame_unref(frame); - rav1e_frame_set_opaque(rframe, pts, av_free); + rav1e_frame_set_opaque(rframe, fd, frame_data_free); } } @@ -535,8 +580,22 @@ static int librav1e_receive_packet(AVCodecContext *avctx, AVPacket *pkt) if (rpkt->frame_type == RA_FRAME_TYPE_KEY) pkt->flags |= AV_PKT_FLAG_KEY; - pkt->pts = pkt->dts = *((int64_t *) rpkt->opaque); - av_free(rpkt->opaque); + fd = rpkt->opaque; + pkt->pts = pkt->dts = fd->pts; + pkt->duration = fd->duration; +#if FF_API_REORDERED_OPAQUE +FF_DISABLE_DEPRECATION_WARNINGS + avctx->reordered_opaque = fd->reordered_opaque; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { + pkt->opaque = fd->frame_opaque; + pkt->opaque_ref = fd->frame_opaque_ref; + fd->frame_opaque_ref = NULL; + } + + frame_data_free(fd); if (avctx->flags & AV_CODEC_FLAG_RECON_FRAME) { AVCodecInternal *avci = avctx->internal; @@ -626,7 +685,8 @@ const FFCodec ff_librav1e_encoder = { .defaults = librav1e_defaults, .p.pix_fmts = librav1e_pix_fmts, .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_OTHER_THREADS | - AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_RECON_FRAME, + AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_RECON_FRAME | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_AUTO_THREADS, .p.wrapper_name = "librav1e", diff --git a/libavcodec/librsvgdec.c b/libavcodec/librsvgdec.c index 9c8aa2dedc8..2f160edcdf2 100644 --- a/libavcodec/librsvgdec.c +++ b/libavcodec/librsvgdec.c @@ -73,7 +73,7 @@ static int librsvg_decode_frame(AVCodecContext *avctx, AVFrame *frame, if ((ret = ff_get_buffer(avctx, frame, 0))) return ret; frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; image = cairo_image_surface_create_for_data(frame->data[0], CAIRO_FORMAT_ARGB32, frame->width, frame->height, diff --git a/libavcodec/libspeexdec.c b/libavcodec/libspeexdec.c index 47fc5d6a4b2..84b308490a5 100644 --- a/libavcodec/libspeexdec.c +++ b/libavcodec/libspeexdec.c @@ -195,7 +195,11 @@ const FFCodec ff_libspeex_decoder = { CODEC_LONG_NAME("libspeex Speex"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_SPEEX, - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_CHANNEL_CONF, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DELAY | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_CHANNEL_CONF, .p.wrapper_name = "libspeex", .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE, .priv_data_size = sizeof(LibSpeexContext), diff --git a/libavcodec/libsvtav1.c b/libavcodec/libsvtav1.c index 56e1e22b512..f2b73361d8f 100644 --- a/libavcodec/libsvtav1.c +++ b/libavcodec/libsvtav1.c @@ -170,7 +170,7 @@ static int config_enc_params(EbSvtAv1EncConfiguration *param, param->look_ahead_distance = svt_enc->la_depth; #endif - if (svt_enc->enc_mode >= 0) + if (svt_enc->enc_mode >= -1) param->enc_mode = svt_enc->enc_mode; if (avctx->bit_rate) { @@ -184,8 +184,10 @@ static int config_enc_params(EbSvtAv1EncConfiguration *param, param->min_qp_allowed = avctx->qmin; } param->max_bit_rate = avctx->rc_max_rate; - if (avctx->bit_rate && avctx->rc_buffer_size) - param->maximum_buffer_size_ms = avctx->rc_buffer_size * 1000LL / avctx->bit_rate; + if ((avctx->bit_rate > 0 || avctx->rc_max_rate > 0) && avctx->rc_buffer_size) + param->maximum_buffer_size_ms = + avctx->rc_buffer_size * 1000LL / + FFMAX(avctx->bit_rate, avctx->rc_max_rate); if (svt_enc->crf > 0) { param->qp = svt_enc->crf; @@ -240,15 +242,32 @@ static int config_enc_params(EbSvtAv1EncConfiguration *param, if (avctx->level != FF_LEVEL_UNKNOWN) param->level = avctx->level; - if (avctx->gop_size > 0) + // gop_size == 1 case is handled when encoding each frame by setting + // pic_type to EB_AV1_KEY_PICTURE. For gop_size > 1, set the + // intra_period_length. Even though setting intra_period_length to 0 should + // work in this case, it does not. + // See: https://gitlab.com/AOMediaCodec/SVT-AV1/-/issues/2076 + if (avctx->gop_size > 1) param->intra_period_length = avctx->gop_size - 1; + // In order for SVT-AV1 to force keyframes by setting pic_type to + // EB_AV1_KEY_PICTURE on any frame, force_key_frames has to be set. Note + // that this does not force all frames to be keyframes (it only forces a + // keyframe with pic_type is set to EB_AV1_KEY_PICTURE). + param->force_key_frames = 1; + if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { param->frame_rate_numerator = avctx->framerate.num; param->frame_rate_denominator = avctx->framerate.den; } else { param->frame_rate_numerator = avctx->time_base.den; - param->frame_rate_denominator = avctx->time_base.num * avctx->ticks_per_frame; +FF_DISABLE_DEPRECATION_WARNINGS + param->frame_rate_denominator = avctx->time_base.num +#if FF_API_TICKS_PER_FRAME + * avctx->ticks_per_frame +#endif + ; +FF_ENABLE_DEPRECATION_WARNINGS } /* 2 = IDR, closed GOP, 1 = CRA, open GOP */ @@ -302,7 +321,8 @@ static int config_enc_params(EbSvtAv1EncConfiguration *param, avctx->bit_rate = param->rate_control_mode > 0 ? param->target_bit_rate : 0; avctx->rc_max_rate = param->max_bit_rate; - avctx->rc_buffer_size = param->maximum_buffer_size_ms * avctx->bit_rate / 1000LL; + avctx->rc_buffer_size = param->maximum_buffer_size_ms * + FFMAX(avctx->bit_rate, avctx->rc_max_rate) / 1000LL; if (avctx->bit_rate || avctx->rc_max_rate || avctx->rc_buffer_size) { AVCPBProperties *cpb_props = ff_add_cpb_side_data(avctx); @@ -453,6 +473,9 @@ static int eb_send_frame(AVCodecContext *avctx, const AVFrame *frame) break; } + if (avctx->gop_size == 1) + headerPtr->pic_type = EB_AV1_KEY_PICTURE; + svt_av1_enc_send_picture(svt_enc->svt_handle, headerPtr); return 0; @@ -590,7 +613,7 @@ static const AVOption options[] = { { "high", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, VE, "tier" }, #endif { "preset", "Encoding preset", - OFFSET(enc_mode), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, MAX_ENC_PRESET, VE }, + OFFSET(enc_mode), AV_OPT_TYPE_INT, { .i64 = -2 }, -2, MAX_ENC_PRESET, VE }, FF_AV1_PROFILE_OPTS diff --git a/libavcodec/libtheoraenc.c b/libavcodec/libtheoraenc.c index da16c6372e2..06eeaae0063 100644 --- a/libavcodec/libtheoraenc.c +++ b/libavcodec/libtheoraenc.c @@ -346,7 +346,13 @@ static int encode_frame(AVCodecContext* avc_context, AVPacket *pkt, // HACK: assumes no encoder delay, this is true until libtheora becomes // multithreaded (which will be disabled unless explicitly requested) - pkt->pts = pkt->dts = frame->pts; + pkt->pts = frame->pts; + pkt->duration = frame->duration; + + ret = ff_encode_reordered_opaque(avc_context, pkt, frame); + if (ret < 0) + return ret; + if (!(o_packet.granulepos & h->keyframe_mask)) pkt->flags |= AV_PKT_FLAG_KEY; *got_packet = 1; @@ -373,7 +379,9 @@ const FFCodec ff_libtheora_encoder = { .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_THEORA, .p.capabilities = AV_CODEC_CAP_DR1 | - AV_CODEC_CAP_DELAY /* for statsfile summary */, + /* for statsfile summary */ + AV_CODEC_CAP_DELAY | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE, .priv_data_size = sizeof(TheoraContext), .init = encode_init, diff --git a/libavcodec/libuavs3d.c b/libavcodec/libuavs3d.c index f5a6e59496f..66e8d31001b 100644 --- a/libavcodec/libuavs3d.c +++ b/libavcodec/libuavs3d.c @@ -79,16 +79,27 @@ static void uavs3d_output_callback(uavs3d_io_frm_t *dec_frame) { frm->pts = dec_frame->pts; frm->pkt_dts = dec_frame->dts; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS frm->pkt_pos = dec_frame->pkt_pos; frm->pkt_size = dec_frame->pkt_size; +FF_ENABLE_DEPRECATION_WARNINGS +#endif +#if FF_API_FRAME_PICTURE_NUMBER +FF_DISABLE_DEPRECATION_WARNINGS frm->coded_picture_number = dec_frame->dtr; frm->display_picture_number = dec_frame->ptr; +FF_ENABLE_DEPRECATION_WARNINGS +#endif if (dec_frame->type < 0 || dec_frame->type >= FF_ARRAY_ELEMS(ff_avs3_image_type)) { av_log(NULL, AV_LOG_WARNING, "Error frame type in uavs3d: %d.\n", dec_frame->type); } else { frm->pict_type = ff_avs3_image_type[dec_frame->type]; - frm->key_frame = (frm->pict_type == AV_PICTURE_TYPE_I); + if (frm->pict_type == AV_PICTURE_TYPE_I) + frm->flags |= AV_FRAME_FLAG_KEY; + else + frm->flags &= ~AV_FRAME_FLAG_KEY; } for (i = 0; i < 3; i++) { @@ -171,8 +182,12 @@ static int libuavs3d_decode_frame(AVCodecContext *avctx, AVFrame *frm, uavs3d_io_frm_t *frm_dec = &h->dec_frame; buf_end = buf + buf_size; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS frm_dec->pkt_pos = avpkt->pos; frm_dec->pkt_size = avpkt->size; +FF_ENABLE_DEPRECATION_WARNINGS +#endif while (!finish) { int bs_len; diff --git a/libavcodec/libvo-amrwbenc.c b/libavcodec/libvo-amrwbenc.c index 76a6b69994c..02b8941a6d2 100644 --- a/libavcodec/libvo-amrwbenc.c +++ b/libavcodec/libvo-amrwbenc.c @@ -144,7 +144,8 @@ const FFCodec ff_libvo_amrwbenc_encoder = { CODEC_LONG_NAME("Android VisualOn AMR-WB (Adaptive Multi-Rate Wide-Band)"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_AMR_WB, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .p.priv_class = &amrwb_class, .p.wrapper_name = "libvo_amrwbenc", .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE, diff --git a/libavcodec/libvpx.c b/libavcodec/libvpx.c deleted file mode 100644 index 8601f82bd24..00000000000 --- a/libavcodec/libvpx.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2013 Guillaume Martres - * - * 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 -#include "libvpx.h" -#include "config.h" -#include "config_components.h" - -#if CONFIG_LIBVPX_VP9_ENCODER -#include -#include -#endif - -static const enum AVPixelFormat vp9_pix_fmts_def[] = { - AV_PIX_FMT_YUV420P, - AV_PIX_FMT_YUVA420P, - AV_PIX_FMT_NONE -}; - -#if CONFIG_LIBVPX_VP9_ENCODER -static const enum AVPixelFormat vp9_pix_fmts_highcol[] = { - AV_PIX_FMT_YUV420P, - AV_PIX_FMT_YUVA420P, - AV_PIX_FMT_YUV422P, - AV_PIX_FMT_YUV440P, - AV_PIX_FMT_YUV444P, - AV_PIX_FMT_GBRP, - AV_PIX_FMT_NONE -}; - -static const enum AVPixelFormat vp9_pix_fmts_highbd[] = { - AV_PIX_FMT_YUV420P, - AV_PIX_FMT_YUVA420P, - AV_PIX_FMT_YUV422P, - AV_PIX_FMT_YUV440P, - AV_PIX_FMT_YUV444P, - AV_PIX_FMT_YUV420P10, - AV_PIX_FMT_YUV422P10, - AV_PIX_FMT_YUV440P10, - AV_PIX_FMT_YUV444P10, - AV_PIX_FMT_YUV420P12, - AV_PIX_FMT_YUV422P12, - AV_PIX_FMT_YUV440P12, - AV_PIX_FMT_YUV444P12, - AV_PIX_FMT_GBRP, - AV_PIX_FMT_GBRP10, - AV_PIX_FMT_GBRP12, - AV_PIX_FMT_NONE -}; -#endif - -av_cold void ff_vp9_init_static(FFCodec *codec) -{ - codec->p.pix_fmts = vp9_pix_fmts_def; -#if CONFIG_LIBVPX_VP9_ENCODER - { - vpx_codec_caps_t codec_caps = vpx_codec_get_caps(vpx_codec_vp9_cx()); - if (codec_caps & VPX_CODEC_CAP_HIGHBITDEPTH) - codec->p.pix_fmts = vp9_pix_fmts_highbd; - else - codec->p.pix_fmts = vp9_pix_fmts_highcol; - } -#endif -} diff --git a/libavcodec/libvpx.h b/libavcodec/libvpx.h index 0caed8cdcb3..4671e0edef3 100644 --- a/libavcodec/libvpx.h +++ b/libavcodec/libvpx.h @@ -21,14 +21,6 @@ #ifndef AVCODEC_LIBVPX_H #define AVCODEC_LIBVPX_H -#include - -#include "codec_internal.h" - -void ff_vp9_init_static(FFCodec *codec); -#if 0 -enum AVPixelFormat ff_vpx_imgfmt_to_pixfmt(vpx_img_fmt_t img); -vpx_img_fmt_t ff_vpx_pixfmt_to_imgfmt(enum AVPixelFormat pix); -#endif +#define MAX_VPX_THREADS 64 #endif /* AVCODEC_LIBVPX_H */ diff --git a/libavcodec/libvpxdec.c b/libavcodec/libvpxdec.c index 9cd2c56cafd..f480545ae04 100644 --- a/libavcodec/libvpxdec.c +++ b/libavcodec/libvpxdec.c @@ -88,7 +88,7 @@ static av_cold int vpx_init(AVCodecContext *avctx, const struct vpx_codec_iface *iface) { struct vpx_codec_dec_cfg deccfg = { - .threads = FFMIN(avctx->thread_count ? avctx->thread_count : av_cpu_count(), 16) + .threads = FFMIN(avctx->thread_count ? avctx->thread_count : av_cpu_count(), MAX_VPX_THREADS) }; av_log(avctx, AV_LOG_INFO, "%s\n", vpx_codec_version_str()); @@ -377,7 +377,7 @@ static av_cold int vp9_init(AVCodecContext *avctx) return vpx_init(avctx, &ctx->decoder, vpx_codec_vp9_dx()); } -FFCodec ff_libvpx_vp9_decoder = { +const FFCodec ff_libvpx_vp9_decoder = { .p.name = "libvpx-vp9", CODEC_LONG_NAME("libvpx VP9"), .p.type = AVMEDIA_TYPE_VIDEO, @@ -391,6 +391,5 @@ FFCodec ff_libvpx_vp9_decoder = { FF_CODEC_DECODE_CB(vpx_decode), .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | FF_CODEC_CAP_AUTO_THREADS, - .init_static_data = ff_vp9_init_static, }; #endif /* CONFIG_LIBVPX_VP9_DECODER */ diff --git a/libavcodec/libvpxenc.c b/libavcodec/libvpxenc.c index 9aa5510c280..7a545527a99 100644 --- a/libavcodec/libvpxenc.c +++ b/libavcodec/libvpxenc.c @@ -58,19 +58,24 @@ struct FrameListData { size_t sz; /**< length of compressed data */ int64_t pts; /**< time stamp to show frame (in timebase units) */ - unsigned long duration; /**< duration to show frame - (in timebase units) */ uint32_t flags; /**< flags for this frame */ uint64_t sse[4]; int have_sse; /**< true if we have pending sse[] */ - uint64_t frame_number; struct FrameListData *next; }; -typedef struct FrameHDR10Plus { +typedef struct FrameData { int64_t pts; + int64_t duration; + +#if FF_API_REORDERED_OPAQUE + int64_t reordered_opaque; +#endif + void *frame_opaque; + AVBufferRef *frame_opaque_ref; + AVBufferRef *hdr10_plus; -} FrameHDR10Plus; +} FrameData; typedef struct VPxEncoderContext { AVClass *class; @@ -84,7 +89,6 @@ typedef struct VPxEncoderContext { int deadline; //i.e., RT/GOOD/BEST uint64_t sse[4]; int have_sse; /**< true if we have pending sse[] */ - uint64_t frame_number; struct FrameListData *coded_frame_list; struct FrameListData *alpha_coded_frame_list; @@ -132,7 +136,9 @@ typedef struct VPxEncoderContext { int corpus_complexity; int tpl_model; int min_gf_interval; - AVFifo *hdr10_plus_fifo; + + // This FIFO is used to propagate various properties from frames to packets. + AVFifo *fifo; /** * If the driver does not support ROI then warn the first time we * encounter a frame with ROI side data. @@ -329,33 +335,109 @@ static av_cold void free_frame_list(struct FrameListData *list) } } -static av_cold void free_hdr10_plus_fifo(AVFifo **fifo) +static void frame_data_uninit(FrameData *fd) { - FrameHDR10Plus frame_hdr10_plus; - while (av_fifo_read(*fifo, &frame_hdr10_plus, 1) >= 0) - av_buffer_unref(&frame_hdr10_plus.hdr10_plus); + av_buffer_unref(&fd->frame_opaque_ref); + av_buffer_unref(&fd->hdr10_plus); +} + +static av_cold void fifo_free(AVFifo **fifo) +{ + FrameData fd; + while (av_fifo_read(*fifo, &fd, 1) >= 0) + frame_data_uninit(&fd); av_fifo_freep2(fifo); } -static int copy_hdr10_plus_to_pkt(AVFifo *fifo, AVPacket *pkt) +static int frame_data_submit(AVCodecContext *avctx, AVFifo *fifo, + const AVFrame *frame) { - FrameHDR10Plus frame_hdr10_plus; + VPxContext *ctx = avctx->priv_data; + const struct vpx_codec_enc_cfg *enccfg = ctx->encoder.config.enc; + + FrameData fd = { .pts = frame->pts }; + + AVFrameSideData *av_uninit(sd); + int ret; + +#if CONFIG_LIBVPX_VP9_ENCODER + // Keep HDR10+ if it has bit depth higher than 8 and + // it has PQ trc (SMPTE2084). + sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DYNAMIC_HDR_PLUS); + if (avctx->codec_id == AV_CODEC_ID_VP9 && sd && + enccfg->g_bit_depth > 8 && avctx->color_trc == AVCOL_TRC_SMPTE2084) { + fd.hdr10_plus = av_buffer_ref(sd->buf); + if (!fd.hdr10_plus) + return AVERROR(ENOMEM); + } +#endif + + fd.duration = frame->duration; + fd.frame_opaque = frame->opaque; + if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE && frame->opaque_ref) { + ret = av_buffer_replace(&fd.frame_opaque_ref, frame->opaque_ref); + if (ret < 0) + goto fail; + } +#if FF_API_REORDERED_OPAQUE +FF_DISABLE_DEPRECATION_WARNINGS + fd.reordered_opaque = frame->reordered_opaque; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + ret = av_fifo_write(fifo, &fd, 1); + if (ret < 0) + goto fail; + + return 0; +fail: + frame_data_uninit(&fd); + return ret; +} + +static int frame_data_apply(AVCodecContext *avctx, AVFifo *fifo, AVPacket *pkt) +{ + FrameData fd; uint8_t *data; - if (!pkt || av_fifo_peek(fifo, &frame_hdr10_plus, 1, 0) < 0) - return 0; - if (!frame_hdr10_plus.hdr10_plus || frame_hdr10_plus.pts != pkt->pts) + int ret = 0; + + if (av_fifo_peek(fifo, &fd, 1, 0) < 0) return 0; - av_fifo_drain2(fifo, 1); + if (fd.pts != pkt->pts) { + av_log(avctx, AV_LOG_WARNING, + "Mismatching timestamps: libvpx %"PRId64" queued %"PRId64"; " + "this is a bug, please report it\n", pkt->pts, fd.pts); + goto skip; + } - data = av_packet_new_side_data(pkt, AV_PKT_DATA_DYNAMIC_HDR10_PLUS, frame_hdr10_plus.hdr10_plus->size); - if (!data) { - av_buffer_unref(&frame_hdr10_plus.hdr10_plus); - return AVERROR(ENOMEM); +#if FF_API_REORDERED_OPAQUE +FF_DISABLE_DEPRECATION_WARNINGS + avctx->reordered_opaque = fd.reordered_opaque; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + pkt->duration = fd.duration; + if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { + pkt->opaque = fd.frame_opaque; + pkt->opaque_ref = fd.frame_opaque_ref; + fd.frame_opaque_ref = NULL; } - memcpy(data, frame_hdr10_plus.hdr10_plus->data, frame_hdr10_plus.hdr10_plus->size); - av_buffer_unref(&frame_hdr10_plus.hdr10_plus); - return 0; + if (fd.hdr10_plus) { + data = av_packet_new_side_data(pkt, AV_PKT_DATA_DYNAMIC_HDR10_PLUS, fd.hdr10_plus->size); + if (!data) { + ret = AVERROR(ENOMEM); + goto skip; + } + + memcpy(data, fd.hdr10_plus->data, fd.hdr10_plus->size); + } + +skip: + av_fifo_drain2(fifo, 1); + frame_data_uninit(&fd); + + return ret; } static av_cold int codecctl_int(AVCodecContext *avctx, @@ -449,8 +531,8 @@ static av_cold int vpx_free(AVCodecContext *avctx) av_freep(&avctx->stats_out); free_frame_list(ctx->coded_frame_list); free_frame_list(ctx->alpha_coded_frame_list); - if (ctx->hdr10_plus_fifo) - free_hdr10_plus_fifo(&ctx->hdr10_plus_fifo); + if (ctx->fifo) + fifo_free(&ctx->fifo); return 0; } @@ -914,18 +996,14 @@ static av_cold int vpx_init(AVCodecContext *avctx, return AVERROR(EINVAL); } + ctx->fifo = av_fifo_alloc2(1, sizeof(FrameData), AV_FIFO_FLAG_AUTO_GROW); + if (!ctx->fifo) + return AVERROR(ENOMEM); + #if CONFIG_LIBVPX_VP9_ENCODER if (avctx->codec_id == AV_CODEC_ID_VP9) { if (set_pix_fmt(avctx, codec_caps, &enccfg, &flags, &img_fmt)) return AVERROR(EINVAL); - // Keep HDR10+ if it has bit depth higher than 8 and - // it has PQ trc (SMPTE2084). - if (enccfg.g_bit_depth > 8 && avctx->color_trc == AVCOL_TRC_SMPTE2084) { - ctx->hdr10_plus_fifo = av_fifo_alloc2(1, sizeof(FrameHDR10Plus), - AV_FIFO_FLAG_AUTO_GROW); - if (!ctx->hdr10_plus_fifo) - return AVERROR(ENOMEM); - } } #endif @@ -942,7 +1020,7 @@ static av_cold int vpx_init(AVCodecContext *avctx, enccfg.g_timebase.num = avctx->time_base.num; enccfg.g_timebase.den = avctx->time_base.den; enccfg.g_threads = - FFMIN(avctx->thread_count ? avctx->thread_count : av_cpu_count(), 16); + FFMIN(avctx->thread_count ? avctx->thread_count : av_cpu_count(), MAX_VPX_THREADS); enccfg.g_lag_in_frames= ctx->lag_in_frames; if (avctx->flags & AV_CODEC_FLAG_PASS1) @@ -1215,14 +1293,12 @@ static inline void cx_pktcpy(struct FrameListData *dst, VPxContext *ctx) { dst->pts = src->data.frame.pts; - dst->duration = src->data.frame.duration; dst->flags = src->data.frame.flags; dst->sz = src->data.frame.sz; dst->buf = src->data.frame.buf; dst->have_sse = 0; - /* For alt-ref frame, don't store PSNR or increment frame_number */ + /* For alt-ref frame, don't store PSNR */ if (!(dst->flags & VPX_FRAME_IS_INVISIBLE)) { - dst->frame_number = ++ctx->frame_number; dst->have_sse = ctx->have_sse; if (ctx->have_sse) { /* associate last-seen SSE to the frame. */ @@ -1232,8 +1308,6 @@ static inline void cx_pktcpy(struct FrameListData *dst, memcpy(dst->sse, ctx->sse, sizeof(dst->sse)); ctx->have_sse = 0; } - } else { - dst->frame_number = -1; /* sanity marker */ } } @@ -1289,13 +1363,9 @@ static int storeframe(AVCodecContext *avctx, struct FrameListData *cx_frame, AV_WB64(side_data, 1); memcpy(side_data + 8, alpha_cx_frame->buf, alpha_cx_frame->sz); } - if (cx_frame->frame_number != -1) { - if (ctx->hdr10_plus_fifo) { - int err = copy_hdr10_plus_to_pkt(ctx->hdr10_plus_fifo, pkt); - if (err < 0) - return err; - } - } + ret = frame_data_apply(avctx, ctx->fifo, pkt); + if (ret < 0) + return ret; return pkt->size; } @@ -1622,6 +1692,7 @@ static int vpx_encode(AVCodecContext *avctx, AVPacket *pkt, const struct vpx_codec_enc_cfg *enccfg = ctx->encoder.config.enc; vpx_svc_layer_id_t layer_id; int layer_id_valid = 0; + unsigned long duration = 0; if (avctx->qmax >= 0 && enccfg->rc_max_quantizer != avctx->qmax) { struct vpx_codec_enc_cfg cfg = *enccfg; @@ -1709,23 +1780,10 @@ static int vpx_encode(AVCodecContext *avctx, AVPacket *pkt, } } - if (ctx->hdr10_plus_fifo) { - AVFrameSideData *hdr10_plus_metadata; - // Add HDR10+ metadata to queue. - hdr10_plus_metadata = av_frame_get_side_data(frame, AV_FRAME_DATA_DYNAMIC_HDR_PLUS); - if (hdr10_plus_metadata) { - int err; - struct FrameHDR10Plus data; - data.pts = frame->pts; - data.hdr10_plus = av_buffer_ref(hdr10_plus_metadata->buf); - if (!data.hdr10_plus) - return AVERROR(ENOMEM); - err = av_fifo_write(ctx->hdr10_plus_fifo, &data, 1); - if (err < 0) { - av_buffer_unref(&data.hdr10_plus); - return err; - } - } + if (!(avctx->flags & AV_CODEC_FLAG_PASS1)) { + res = frame_data_submit(avctx, ctx->fifo, frame); + if (res < 0) + return res; } } @@ -1765,8 +1823,25 @@ static int vpx_encode(AVCodecContext *avctx, AVPacket *pkt, #endif } + if (frame && frame->duration > ULONG_MAX) { + av_log(avctx, AV_LOG_WARNING, + "Frame duration too large: %"PRId64"\n", frame->duration); + } else if (frame && frame->duration) + duration = frame->duration; + else if (avctx->framerate.num > 0 && avctx->framerate.den > 0) + duration = av_rescale_q(1, av_inv_q(avctx->framerate), avctx->time_base); + else { +FF_DISABLE_DEPRECATION_WARNINGS + duration = +#if FF_API_TICKS_PER_FRAME + avctx->ticks_per_frame ? avctx->ticks_per_frame : +#endif + 1; +FF_ENABLE_DEPRECATION_WARNINGS + } + res = vpx_codec_encode(&ctx->encoder, rawimg, timestamp, - avctx->ticks_per_frame, flags, ctx->deadline); + duration, flags, ctx->deadline); if (res != VPX_CODEC_OK) { log_encoder_error(avctx, "Error encoding frame"); return AVERROR_INVALIDDATA; @@ -1774,7 +1849,7 @@ static int vpx_encode(AVCodecContext *avctx, AVPacket *pkt, if (ctx->is_alpha) { res = vpx_codec_encode(&ctx->encoder_alpha, rawimg_alpha, timestamp, - avctx->ticks_per_frame, flags, ctx->deadline); + duration, flags, ctx->deadline); if (res != VPX_CODEC_OK) { log_encoder_error(avctx, "Error encoding alpha frame"); return AVERROR_INVALIDDATA; @@ -1959,7 +2034,8 @@ const FFCodec ff_libvpx_vp8_encoder = { .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_VP8, .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | - AV_CODEC_CAP_OTHER_THREADS, + AV_CODEC_CAP_OTHER_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(VPxContext), .init = vp8_init, FF_CODEC_ENCODE_CB(vpx_encode), @@ -1979,6 +2055,45 @@ static av_cold int vp9_init(AVCodecContext *avctx) return vpx_init(avctx, vpx_codec_vp9_cx()); } +static const enum AVPixelFormat vp9_pix_fmts_highcol[] = { + AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUVA420P, + AV_PIX_FMT_YUV422P, + AV_PIX_FMT_YUV440P, + AV_PIX_FMT_YUV444P, + AV_PIX_FMT_GBRP, + AV_PIX_FMT_NONE +}; + +static const enum AVPixelFormat vp9_pix_fmts_highbd[] = { + AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUVA420P, + AV_PIX_FMT_YUV422P, + AV_PIX_FMT_YUV440P, + AV_PIX_FMT_YUV444P, + AV_PIX_FMT_YUV420P10, + AV_PIX_FMT_YUV422P10, + AV_PIX_FMT_YUV440P10, + AV_PIX_FMT_YUV444P10, + AV_PIX_FMT_YUV420P12, + AV_PIX_FMT_YUV422P12, + AV_PIX_FMT_YUV440P12, + AV_PIX_FMT_YUV444P12, + AV_PIX_FMT_GBRP, + AV_PIX_FMT_GBRP10, + AV_PIX_FMT_GBRP12, + AV_PIX_FMT_NONE +}; + +static av_cold void vp9_init_static(FFCodec *codec) +{ + vpx_codec_caps_t codec_caps = vpx_codec_get_caps(vpx_codec_vp9_cx()); + if (codec_caps & VPX_CODEC_CAP_HIGHBITDEPTH) + codec->p.pix_fmts = vp9_pix_fmts_highbd; + else + codec->p.pix_fmts = vp9_pix_fmts_highcol; +} + static const AVClass class_vp9 = { .class_name = "libvpx-vp9 encoder", .item_name = av_default_item_name, @@ -1992,7 +2107,8 @@ FFCodec ff_libvpx_vp9_encoder = { .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_VP9, .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | - AV_CODEC_CAP_OTHER_THREADS, + AV_CODEC_CAP_OTHER_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .p.profiles = NULL_IF_CONFIG_SMALL(ff_vp9_profiles), .p.priv_class = &class_vp9, .p.wrapper_name = "libvpx", @@ -2003,6 +2119,6 @@ FFCodec ff_libvpx_vp9_encoder = { .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | FF_CODEC_CAP_AUTO_THREADS, .defaults = defaults, - .init_static_data = ff_vp9_init_static, + .init_static_data = vp9_init_static, }; #endif /* CONFIG_LIBVPX_VP9_ENCODER */ diff --git a/libavcodec/libwebpenc.c b/libavcodec/libwebpenc.c index ee5795f0418..d6edd866037 100644 --- a/libavcodec/libwebpenc.c +++ b/libavcodec/libwebpenc.c @@ -92,7 +92,7 @@ const FFCodec ff_libwebp_encoder = { CODEC_LONG_NAME("libwebp WebP image"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_WEBP, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .p.pix_fmts = ff_libwebpenc_pix_fmts, .p.priv_class = &ff_libwebpenc_class, .p.wrapper_name = "libwebp", diff --git a/libavcodec/libwebpenc_animencoder.c b/libavcodec/libwebpenc_animencoder.c index 0f2c190c8cb..8756231f23c 100644 --- a/libavcodec/libwebpenc_animencoder.c +++ b/libavcodec/libwebpenc_animencoder.c @@ -24,6 +24,8 @@ * WebP encoder using libwebp (WebPAnimEncoder API) */ +#include "libavutil/buffer.h" + #include "config.h" #include "codec_internal.h" #include "encode.h" @@ -35,6 +37,14 @@ typedef struct LibWebPAnimContext { LibWebPContextCommon cc; WebPAnimEncoder *enc; // the main AnimEncoder object int64_t first_frame_pts; // pts of the first encoded frame. + int64_t end_pts; // pts + duration of the last frame + +#if FF_API_REORDERED_OPAQUE + int64_t reordered_opaque; +#endif + void *first_frame_opaque; + AVBufferRef *first_frame_opaque_ref; + int done; // If true, we have assembled the bitstream already } LibWebPAnimContext; @@ -77,7 +87,22 @@ static int libwebp_anim_encode_frame(AVCodecContext *avctx, AVPacket *pkt, memcpy(pkt->data, assembled_data.bytes, assembled_data.size); WebPDataClear(&assembled_data); s->done = 1; - pkt->pts = pkt->dts = s->first_frame_pts; + pkt->pts = s->first_frame_pts; + + if (pkt->pts != AV_NOPTS_VALUE && s->end_pts > pkt->pts) + pkt->duration = s->end_pts - pkt->pts; + +#if FF_API_REORDERED_OPAQUE +FF_DISABLE_DEPRECATION_WARNINGS + avctx->reordered_opaque = s->reordered_opaque; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { + pkt->opaque = s->first_frame_opaque; + pkt->opaque_ref = s->first_frame_opaque_ref; + s->first_frame_opaque_ref = NULL; + } + *got_packet = 1; return 0; } else { @@ -107,8 +132,25 @@ static int libwebp_anim_encode_frame(AVCodecContext *avctx, AVPacket *pkt, goto end; } - if (!avctx->frame_number) + if (!avctx->frame_num) { s->first_frame_pts = frame->pts; +#if FF_API_REORDERED_OPAQUE +FF_DISABLE_DEPRECATION_WARNINGS + s->reordered_opaque = frame->reordered_opaque; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { + s->first_frame_opaque = frame->opaque; + ret = av_buffer_replace(&s->first_frame_opaque_ref, frame->opaque_ref); + if (ret < 0) + goto end; + } + } + + if (frame->pts != AV_NOPTS_VALUE) + s->end_pts = frame->pts + frame->duration; + ret = 0; *got_packet = 0; @@ -126,6 +168,8 @@ static int libwebp_anim_encode_close(AVCodecContext *avctx) av_frame_free(&s->cc.ref); WebPAnimEncoderDelete(s->enc); + av_buffer_unref(&s->first_frame_opaque_ref); + return 0; } @@ -134,7 +178,8 @@ const FFCodec ff_libwebp_anim_encoder = { CODEC_LONG_NAME("libwebp WebP image"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_WEBP, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .p.pix_fmts = ff_libwebpenc_pix_fmts, .p.priv_class = &ff_libwebpenc_class, .p.wrapper_name = "libwebp", diff --git a/libavcodec/libx264.c b/libavcodec/libx264.c index 2bbd9044b68..1a7dc7bdd5b 100644 --- a/libavcodec/libx264.c +++ b/libavcodec/libx264.c @@ -21,6 +21,7 @@ #include "config_components.h" +#include "libavutil/buffer.h" #include "libavutil/eval.h" #include "libavutil/internal.h" #include "libavutil/opt.h" @@ -29,6 +30,7 @@ #include "libavutil/stereo3d.h" #include "libavutil/time.h" #include "libavutil/intreadwrite.h" +#include "libavutil/video_hint.h" #include "avcodec.h" #include "codec_internal.h" #include "encode.h" @@ -47,10 +49,19 @@ // from x264.h, for quant_offsets, Macroblocks are 16x16 // blocks of pixels (with respect to the luma plane) #define MB_SIZE 16 +#define MB_LSIZE 4 +#define MB_FLOOR(x) ((x) >> (MB_LSIZE)) +#define MB_CEIL(x) MB_FLOOR((x) + (MB_SIZE - 1)) typedef struct X264Opaque { +#if FF_API_REORDERED_OPAQUE int64_t reordered_opaque; +#endif int64_t wallclock; + int64_t duration; + + void *frame_opaque; + AVBufferRef *frame_opaque_ref; } X264Opaque; typedef struct X264Context { @@ -116,6 +127,8 @@ typedef struct X264Context { * encounter a frame with ROI side data. */ int roi_warned; + + int mb_info; } X264Context; static void X264_log(void *p, int level, const char *fmt, va_list args) @@ -133,13 +146,18 @@ static void X264_log(void *p, int level, const char *fmt, va_list args) av_vlog(p, level_map[level], fmt, args); } +static void opaque_uninit(X264Opaque *o) +{ + av_buffer_unref(&o->frame_opaque_ref); + memset(o, 0, sizeof(*o)); +} static int encode_nals(AVCodecContext *ctx, AVPacket *pkt, const x264_nal_t *nals, int nnal) { X264Context *x4 = ctx->priv_data; uint8_t *p; - uint64_t size = x4->sei_size; + uint64_t size = FFMAX(x4->sei_size, 0); int ret; if (!nnal) @@ -166,8 +184,8 @@ static int encode_nals(AVCodecContext *ctx, AVPacket *pkt, memcpy(p, x4->sei, x4->sei_size); p += x4->sei_size; size -= x4->sei_size; - x4->sei_size = 0; - av_freep(&x4->sei); + /* Keep the value around in case of flush */ + x4->sei_size = -x4->sei_size; } /* x264 guarantees the payloads of the NALs @@ -177,28 +195,6 @@ static int encode_nals(AVCodecContext *ctx, AVPacket *pkt, return 1; } -static int avfmt2_num_planes(int avfmt) -{ - switch (avfmt) { - case AV_PIX_FMT_YUV420P: - case AV_PIX_FMT_YUVJ420P: - case AV_PIX_FMT_YUV420P9: - case AV_PIX_FMT_YUV420P10: - case AV_PIX_FMT_YUV444P: - return 3; - - case AV_PIX_FMT_BGR0: - case AV_PIX_FMT_BGR24: - case AV_PIX_FMT_RGB24: - case AV_PIX_FMT_GRAY8: - case AV_PIX_FMT_GRAY10: - return 1; - - default: - return 3; - } -} - static void reconfig_encoder(AVCodecContext *ctx, const AVFrame *frame) { X264Context *x4 = ctx->priv_data; @@ -206,9 +202,9 @@ static void reconfig_encoder(AVCodecContext *ctx, const AVFrame *frame) if (x4->avcintra_class < 0) { - if (x4->params.b_interlaced && x4->params.b_tff != frame->top_field_first) { + if (x4->params.b_interlaced && x4->params.b_tff != !!(frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)) { - x4->params.b_tff = frame->top_field_first; + x4->params.b_tff = !!(frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST); x264_encoder_reconfig(x4->enc, &x4->params); } if (x4->params.vui.i_sar_height*ctx->sample_aspect_ratio.num != ctx->sample_aspect_ratio.den * x4->params.vui.i_sar_width) { @@ -299,15 +295,13 @@ static void reconfig_encoder(AVCodecContext *ctx, const AVFrame *frame) } } -static void free_picture(AVCodecContext *ctx) +static void free_picture(x264_picture_t *pic) { - X264Context *x4 = ctx->priv_data; - x264_picture_t *pic = &x4->pic; - for (int i = 0; i < pic->extra_sei.num_payloads; i++) av_free(pic->extra_sei.payloads[i].payload); av_freep(&pic->extra_sei.payloads); av_freep(&pic->prop.quant_offsets); + av_freep(&pic->prop.mb_info); pic->extra_sei.num_payloads = 0; } @@ -333,6 +327,74 @@ static enum AVPixelFormat csp_to_pixfmt(int csp) return AV_PIX_FMT_NONE; } +static void av_always_inline mbinfo_compute_changed_coords(const AVVideoRect *rect, + int *min_x, + int *max_x, + int *min_y, + int *max_y) +{ + *min_y = MB_FLOOR(rect->y); + *max_y = MB_CEIL(rect->y + rect->height); + *min_x = MB_FLOOR(rect->x); + *max_x = MB_CEIL(rect->x + rect->width); +} + +static void av_always_inline mbinfo_compute_constant_coords(const AVVideoRect *rect, + int *min_x, + int *max_x, + int *min_y, + int *max_y) +{ + *min_y = MB_CEIL(rect->y); + *max_y = MB_FLOOR(rect->y + rect->height); + *min_x = MB_CEIL(rect->x); + *max_x = MB_FLOOR(rect->x + rect->width); +} + +static int setup_mb_info(AVCodecContext *ctx, x264_picture_t *pic, + const AVFrame *frame, + const AVVideoHint *info) +{ + int mb_width = (frame->width + MB_SIZE - 1) / MB_SIZE; + int mb_height = (frame->height + MB_SIZE - 1) / MB_SIZE; + + const AVVideoRect *mbinfo_rects; + int nb_rects; + uint8_t *mbinfo; + + mbinfo_rects = (const AVVideoRect *)av_video_hint_rects(info); + nb_rects = info->nb_rects; + + mbinfo = av_calloc(mb_width * mb_height, sizeof(*mbinfo)); + if (!mbinfo) + return AVERROR(ENOMEM); + +#define COMPUTE_MBINFO(mbinfo_filler_, mbinfo_marker_, compute_coords_fn_) \ + memset(mbinfo, mbinfo_filler_, sizeof(*mbinfo) * mb_width * mb_height); \ + \ + for (int i = 0; i < nb_rects; i++) { \ + int min_x, max_x, min_y, max_y; \ + \ + compute_coords_fn_(mbinfo_rects, &min_x, &max_x, &min_y, &max_y); \ + for (int mb_y = min_y; mb_y < max_y; ++mb_y) { \ + memset(mbinfo + mb_y * mb_width + min_x, mbinfo_marker_, max_x - min_x); \ + } \ + \ + mbinfo_rects++; \ + } \ + + if (info->type == AV_VIDEO_HINT_TYPE_CHANGED) { + COMPUTE_MBINFO(X264_MBINFO_CONSTANT, 0, mbinfo_compute_changed_coords); + } else /* if (info->type == AV_VIDEO_HINT_TYPE_CHANGED) */ { + COMPUTE_MBINFO(0, X264_MBINFO_CONSTANT, mbinfo_compute_constant_coords); + } + + pic->prop.mb_info = mbinfo; + pic->prop.mb_info_free = av_free; + + return 0; +} + static int setup_roi(AVCodecContext *ctx, x264_picture_t *pic, int bit_depth, const AVFrame *frame, const uint8_t *data, size_t size) { @@ -352,7 +414,7 @@ static int setup_roi(AVCodecContext *ctx, x264_picture_t *pic, int bit_depth, av_log(ctx, AV_LOG_WARNING, "Adaptive quantization must be enabled to use ROI encoding, skipping ROI.\n"); } return 0; - } else if (frame->interlaced_frame) { + } else if (frame->flags & AV_FRAME_FLAG_INTERLACED) { if (!x4->roi_warned) { x4->roi_warned = 1; av_log(ctx, AV_LOG_WARNING, "interlaced_frame not supported for ROI encoding yet, skipping ROI.\n"); @@ -417,6 +479,7 @@ static int setup_frame(AVCodecContext *ctx, const AVFrame *frame, int64_t wallclock = 0; int bit_depth, ret; AVFrameSideData *sd; + AVFrameSideData *mbinfo_sd; *ppic = NULL; if (!frame) @@ -431,7 +494,7 @@ static int setup_frame(AVCodecContext *ctx, const AVFrame *frame, #endif if (bit_depth > 8) pic->img.i_csp |= X264_CSP_HIGH_DEPTH; - pic->img.i_plane = avfmt2_num_planes(ctx->pix_fmt); + pic->img.i_plane = av_pix_fmt_count_planes(ctx->pix_fmt); for (int i = 0; i < pic->img.i_plane; i++) { pic->img.plane[i] = frame->data[i]; @@ -440,7 +503,21 @@ static int setup_frame(AVCodecContext *ctx, const AVFrame *frame, pic->i_pts = frame->pts; + opaque_uninit(opaque); + + if (ctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { + opaque->frame_opaque = frame->opaque; + ret = av_buffer_replace(&opaque->frame_opaque_ref, frame->opaque_ref); + if (ret < 0) + goto fail; + } + +#if FF_API_REORDERED_OPAQUE +FF_DISABLE_DEPRECATION_WARNINGS opaque->reordered_opaque = frame->reordered_opaque; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + opaque->duration = frame->duration; opaque->wallclock = wallclock; if (ctx->export_side_data & AV_CODEC_EXPORT_DATA_PRFT) opaque->wallclock = av_gettime(); @@ -475,18 +552,19 @@ static int setup_frame(AVCodecContext *ctx, const AVFrame *frame, goto fail; if (sei_data) { - pic->extra_sei.payloads = av_mallocz(sizeof(pic->extra_sei.payloads[0])); - if (pic->extra_sei.payloads == NULL) { + sei->payloads = av_mallocz(sizeof(sei->payloads[0])); + if (!sei->payloads) { + av_free(sei_data); ret = AVERROR(ENOMEM); goto fail; } - pic->extra_sei.sei_free = av_free; + sei->sei_free = av_free; - pic->extra_sei.payloads[0].payload_size = sei_size; - pic->extra_sei.payloads[0].payload = sei_data; - pic->extra_sei.num_payloads = 1; - pic->extra_sei.payloads[0].payload_type = 4; + sei->payloads[0].payload_size = sei_size; + sei->payloads[0].payload = sei_data; + sei->payloads[0].payload_type = 4; + sei->num_payloads = 1; } } @@ -497,6 +575,17 @@ static int setup_frame(AVCodecContext *ctx, const AVFrame *frame, goto fail; } + mbinfo_sd = av_frame_get_side_data(frame, AV_FRAME_DATA_VIDEO_HINT); + if (mbinfo_sd) { + int ret = setup_mb_info(ctx, pic, frame, (const AVVideoHint *)mbinfo_sd->data); + if (ret < 0) { + /* No need to fail here, this is not fatal. We just proceed with no + * mb_info and log a message */ + + av_log(ctx, AV_LOG_WARNING, "setup_mb_info failed with error: %s\n", av_err2str(ret)); + } + } + if (x4->udu_sei) { for (int j = 0; j < frame->nb_side_data; j++) { AVFrameSideData *side_data = frame->side_data[j]; @@ -527,7 +616,7 @@ static int setup_frame(AVCodecContext *ctx, const AVFrame *frame, return 0; fail: - free_picture(ctx); + free_picture(pic); *ppic = NULL; return ret; } @@ -592,13 +681,30 @@ static int X264_frame(AVCodecContext *ctx, AVPacket *pkt, const AVFrame *frame, out_opaque = pic_out.opaque; if (out_opaque >= x4->reordered_opaque && out_opaque < &x4->reordered_opaque[x4->nb_reordered_opaque]) { +#if FF_API_REORDERED_OPAQUE +FF_DISABLE_DEPRECATION_WARNINGS ctx->reordered_opaque = out_opaque->reordered_opaque; +FF_ENABLE_DEPRECATION_WARNINGS +#endif wallclock = out_opaque->wallclock; + pkt->duration = out_opaque->duration; + + if (ctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { + pkt->opaque = out_opaque->frame_opaque; + pkt->opaque_ref = out_opaque->frame_opaque_ref; + out_opaque->frame_opaque_ref = NULL; + } + + opaque_uninit(out_opaque); } else { // Unexpected opaque pointer on picture output av_log(ctx, AV_LOG_ERROR, "Unexpected opaque pointer; " "this is a bug, please report it.\n"); +#if FF_API_REORDERED_OPAQUE +FF_DISABLE_DEPRECATION_WARNINGS ctx->reordered_opaque = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } switch (pic_out.i_type) { @@ -629,11 +735,32 @@ static int X264_frame(AVCodecContext *ctx, AVPacket *pkt, const AVFrame *frame, return 0; } +static void X264_flush(AVCodecContext *avctx) +{ + X264Context *x4 = avctx->priv_data; + x264_nal_t *nal; + int nnal, ret; + x264_picture_t pic_out = {0}; + + do { + ret = x264_encoder_encode(x4->enc, &nal, &nnal, NULL, &pic_out); + } while (ret > 0 && x264_encoder_delayed_frames(x4->enc)); + + for (int i = 0; i < x4->nb_reordered_opaque; i++) + opaque_uninit(&x4->reordered_opaque[i]); + + if (x4->sei_size < 0) + x4->sei_size = -x4->sei_size; +} + static av_cold int X264_close(AVCodecContext *avctx) { X264Context *x4 = avctx->priv_data; av_freep(&x4->sei); + + for (int i = 0; i < x4->nb_reordered_opaque; i++) + opaque_uninit(&x4->reordered_opaque[i]); av_freep(&x4->reordered_opaque); #if X264_BUILD >= 161 @@ -978,7 +1105,13 @@ static av_cold int X264_init(AVCodecContext *avctx) x4->params.i_fps_den = avctx->framerate.den; } else { x4->params.i_fps_num = avctx->time_base.den; - x4->params.i_fps_den = avctx->time_base.num * avctx->ticks_per_frame; +FF_DISABLE_DEPRECATION_WARNINGS + x4->params.i_fps_den = avctx->time_base.num +#if FF_API_TICKS_PER_FRAME + * avctx->ticks_per_frame +#endif + ; +FF_ENABLE_DEPRECATION_WARNINGS } x4->params.analyse.b_psnr = avctx->flags & AV_CODEC_FLAG_PSNR; @@ -1056,6 +1189,9 @@ static av_cold int X264_init(AVCodecContext *avctx) } } + x4->params.analyse.b_mb_info = x4->mb_info; + x4->params.analyse.b_fast_pskip = 1; + // update AVCodecContext with x264 parameters avctx->has_b_frames = x4->params.i_bframe ? x4->params.i_bframe_pyramid ? 2 : 1 : 0; @@ -1265,6 +1401,7 @@ static const AVOption options[] = { { "noise_reduction", "Noise reduction", OFFSET(noise_reduction), AV_OPT_TYPE_INT, { .i64 = -1 }, INT_MIN, INT_MAX, VE }, { "udu_sei", "Use user data unregistered SEI if available", OFFSET(udu_sei), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, { "x264-params", "Override the x264 configuration using a :-separated list of key=value parameters", OFFSET(x264_params), AV_OPT_TYPE_DICT, { 0 }, 0, 0, VE }, + { "mb_info", "Set mb_info data through AVSideData, only useful when used from the API", OFFSET(mb_info), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, { NULL }, }; @@ -1313,12 +1450,14 @@ FFCodec ff_libx264_encoder = { .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_OTHER_THREADS | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE | + AV_CODEC_CAP_ENCODER_FLUSH | AV_CODEC_CAP_ENCODER_RECON_FRAME, .p.priv_class = &x264_class, .p.wrapper_name = "libx264", .priv_data_size = sizeof(X264Context), .init = X264_init, FF_CODEC_ENCODE_CB(X264_frame), + .flush = X264_flush, .close = X264_close, .defaults = x264_defaults, #if X264_BUILD < 153 diff --git a/libavcodec/libx265.c b/libavcodec/libx265.c index 25de3c669be..873b3021eea 100644 --- a/libavcodec/libx265.c +++ b/libavcodec/libx265.c @@ -28,6 +28,7 @@ #include #include "libavutil/avassert.h" +#include "libavutil/buffer.h" #include "libavutil/internal.h" #include "libavutil/common.h" #include "libavutil/opt.h" @@ -41,7 +42,13 @@ #include "sei.h" typedef struct ReorderedData { +#if FF_API_REORDERED_OPAQUE int64_t reordered_opaque; +#endif + int64_t duration; + + void *frame_opaque; + AVBufferRef *frame_opaque_ref; int in_use; } ReorderedData; @@ -121,7 +128,7 @@ static int rd_get(libx265Context *ctx) static void rd_release(libx265Context *ctx, int idx) { av_assert0(idx >= 0 && idx < ctx->nb_rd); - + av_buffer_unref(&ctx->rd[idx].frame_opaque_ref); memset(&ctx->rd[idx], 0, sizeof(ctx->rd[idx])); } @@ -132,6 +139,8 @@ static av_cold int libx265_encode_close(AVCodecContext *avctx) ctx->api->param_free(ctx->params); av_freep(&ctx->sei_data); + for (int i = 0; i < ctx->nb_rd; i++) + rd_release(ctx, i); av_freep(&ctx->rd); if (ctx->encoder) @@ -211,7 +220,13 @@ static av_cold int libx265_encode_init(AVCodecContext *avctx) ctx->params->fpsDenom = avctx->framerate.den; } else { ctx->params->fpsNum = avctx->time_base.den; - ctx->params->fpsDenom = avctx->time_base.num * avctx->ticks_per_frame; +FF_DISABLE_DEPRECATION_WARNINGS + ctx->params->fpsDenom = avctx->time_base.num +#if FF_API_TICKS_PER_FRAME + * avctx->ticks_per_frame +#endif + ; +FF_ENABLE_DEPRECATION_WARNINGS } ctx->params->sourceWidth = avctx->width; ctx->params->sourceHeight = avctx->height; @@ -582,6 +597,9 @@ static int libx265_encode_frame(AVCodecContext *avctx, AVPacket *pkt, sei->numPayloads = 0; if (pic) { + ReorderedData *rd; + int rd_idx; + for (i = 0; i < 3; i++) { x265pic.planes[i] = pic->data[i]; x265pic.stride[i] = pic->linesize[i]; @@ -600,21 +618,31 @@ static int libx265_encode_frame(AVCodecContext *avctx, AVPacket *pkt, if (ret < 0) return ret; - if (pic->reordered_opaque) { - ReorderedData *rd; - int rd_idx = rd_get(ctx); + rd_idx = rd_get(ctx); + if (rd_idx < 0) { + free_picture(ctx, &x265pic); + return rd_idx; + } + rd = &ctx->rd[rd_idx]; - if (rd_idx < 0) { + rd->duration = pic->duration; +#if FF_API_REORDERED_OPAQUE +FF_DISABLE_DEPRECATION_WARNINGS + rd->reordered_opaque = pic->reordered_opaque; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { + rd->frame_opaque = pic->opaque; + ret = av_buffer_replace(&rd->frame_opaque_ref, pic->opaque_ref); + if (ret < 0) { + rd_release(ctx, rd_idx); free_picture(ctx, &x265pic); - return rd_idx; + return ret; } - - x265pic.userData = (void*)(intptr_t)(rd_idx + 1); - - rd = &ctx->rd[rd_idx]; - rd->reordered_opaque = pic->reordered_opaque; } + x265pic.userData = (void*)(intptr_t)(rd_idx + 1); + if (ctx->a53_cc) { void *sei_data; size_t sei_size; @@ -740,11 +768,28 @@ static int libx265_encode_frame(AVCodecContext *avctx, AVPacket *pkt, int idx = (int)(intptr_t)x265pic_out.userData - 1; ReorderedData *rd = &ctx->rd[idx]; +#if FF_API_REORDERED_OPAQUE +FF_DISABLE_DEPRECATION_WARNINGS avctx->reordered_opaque = rd->reordered_opaque; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + pkt->duration = rd->duration; + + if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { + pkt->opaque = rd->frame_opaque; + pkt->opaque_ref = rd->frame_opaque_ref; + rd->frame_opaque_ref = NULL; + } rd_release(ctx, idx); - } else + } +#if FF_API_REORDERED_OPAQUE + else { +FF_DISABLE_DEPRECATION_WARNINGS avctx->reordered_opaque = 0; +FF_ENABLE_DEPRECATION_WARNINGS + } +#endif *got_packet = 1; return 0; diff --git a/libavcodec/libxavs.c b/libavcodec/libxavs.c index 9ed73d1042a..6c29539f243 100644 --- a/libavcodec/libxavs.c +++ b/libavcodec/libxavs.c @@ -141,7 +141,7 @@ static int XAVS_frame(AVCodecContext *avctx, AVPacket *pkt, x4->pic.i_pts = frame->pts; x4->pic.i_type = XAVS_TYPE_AUTO; - x4->pts_buffer[avctx->frame_number % (avctx->max_b_frames+1)] = frame->pts; + x4->pts_buffer[avctx->frame_num % (avctx->max_b_frames+1)] = frame->pts; } if (xavs_encoder_encode(x4->enc, &nal, &nnal, diff --git a/libavcodec/libxvid.c b/libavcodec/libxvid.c index 4e04b3c0989..aba875b6b85 100644 --- a/libavcodec/libxvid.c +++ b/libavcodec/libxvid.c @@ -902,7 +902,7 @@ const FFCodec ff_libxvid_encoder = { CODEC_LONG_NAME("libxvidcore MPEG-4 part 2"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_MPEG4, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(struct xvid_context), .init = xvid_encode_init, FF_CODEC_ENCODE_CB(xvid_encode_frame), diff --git a/libavcodec/ljpegenc.c b/libavcodec/ljpegenc.c index 81c52a7c784..aa62beac71e 100644 --- a/libavcodec/ljpegenc.c +++ b/libavcodec/ljpegenc.c @@ -316,7 +316,8 @@ const FFCodec ff_ljpeg_encoder = { CODEC_LONG_NAME("Lossless JPEG"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_LJPEG, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(LJpegEncContext), .p.priv_class = &ljpeg_class, .init = ljpeg_encode_init, diff --git a/libavcodec/loco.c b/libavcodec/loco.c index d57a67317a0..3d11823284a 100644 --- a/libavcodec/loco.c +++ b/libavcodec/loco.c @@ -206,7 +206,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; #define ADVANCE_BY_DECODED do { \ if (decoded < 0 || decoded >= buf_size) goto buf_too_small; \ diff --git a/libavcodec/loongarch/Makefile b/libavcodec/loongarch/Makefile index c1b5de5c443..06cfab5c205 100644 --- a/libavcodec/loongarch/Makefile +++ b/libavcodec/loongarch/Makefile @@ -9,12 +9,9 @@ OBJS-$(CONFIG_HPELDSP) += loongarch/hpeldsp_init_loongarch.o OBJS-$(CONFIG_IDCTDSP) += loongarch/idctdsp_init_loongarch.o OBJS-$(CONFIG_VIDEODSP) += loongarch/videodsp_init.o OBJS-$(CONFIG_HEVC_DECODER) += loongarch/hevcdsp_init_loongarch.o -LASX-OBJS-$(CONFIG_H264CHROMA) += loongarch/h264chroma_lasx.o LASX-OBJS-$(CONFIG_H264QPEL) += loongarch/h264qpel_lasx.o LASX-OBJS-$(CONFIG_H264DSP) += loongarch/h264dsp_lasx.o \ - loongarch/h264idct_lasx.o \ loongarch/h264_deblock_lasx.o -LASX-OBJS-$(CONFIG_H264PRED) += loongarch/h264_intrapred_lasx.o LASX-OBJS-$(CONFIG_VC1_DECODER) += loongarch/vc1dsp_lasx.o LASX-OBJS-$(CONFIG_HPELDSP) += loongarch/hpeldsp_lasx.o LASX-OBJS-$(CONFIG_IDCTDSP) += loongarch/simple_idct_lasx.o \ @@ -31,3 +28,10 @@ LSX-OBJS-$(CONFIG_HEVC_DECODER) += loongarch/hevcdsp_lsx.o \ loongarch/hevc_mc_bi_lsx.o \ loongarch/hevc_mc_uni_lsx.o \ loongarch/hevc_mc_uniw_lsx.o +LSX-OBJS-$(CONFIG_H264DSP) += loongarch/h264idct.o \ + loongarch/h264idct_loongarch.o \ + loongarch/h264dsp.o +LSX-OBJS-$(CONFIG_H264QPEL) += loongarch/h264qpel.o \ + loongarch/h264qpel_lsx.o +LSX-OBJS-$(CONFIG_H264CHROMA) += loongarch/h264chroma.o +LSX-OBJS-$(CONFIG_H264PRED) += loongarch/h264intrapred.o diff --git a/libavcodec/loongarch/h264_deblock_lasx.c b/libavcodec/loongarch/h264_deblock_lasx.c index c89bea9a846..eead931dcf5 100644 --- a/libavcodec/loongarch/h264_deblock_lasx.c +++ b/libavcodec/loongarch/h264_deblock_lasx.c @@ -20,7 +20,7 @@ */ #include "libavcodec/bit_depth_template.c" -#include "h264dsp_lasx.h" +#include "h264dsp_loongarch.h" #include "libavutil/loongarch/loongson_intrinsics.h" #define H264_LOOP_FILTER_STRENGTH_ITERATION_LASX(edges, step, mask_mv, dir, \ diff --git a/libavcodec/loongarch/h264_intrapred_init_loongarch.c b/libavcodec/loongarch/h264_intrapred_init_loongarch.c index 12620bd842b..c415fa30dab 100644 --- a/libavcodec/loongarch/h264_intrapred_init_loongarch.c +++ b/libavcodec/loongarch/h264_intrapred_init_loongarch.c @@ -21,7 +21,7 @@ #include "libavutil/loongarch/cpu.h" #include "libavcodec/h264pred.h" -#include "h264_intrapred_lasx.h" +#include "h264_intrapred_loongarch.h" av_cold void ff_h264_pred_init_loongarch(H264PredContext *h, int codec_id, const int bit_depth, @@ -30,6 +30,22 @@ av_cold void ff_h264_pred_init_loongarch(H264PredContext *h, int codec_id, int cpu_flags = av_get_cpu_flags(); if (bit_depth == 8) { + if (have_lsx(cpu_flags)) { + if (chroma_format_idc <= 1) { + } + if (codec_id == AV_CODEC_ID_VP7 || codec_id == AV_CODEC_ID_VP8) { + } else { + if (chroma_format_idc <= 1) { + } + if (codec_id == AV_CODEC_ID_SVQ3) { + h->pred16x16[PLANE_PRED8x8] = ff_h264_pred16x16_plane_svq3_8_lsx; + } else if (codec_id == AV_CODEC_ID_RV40) { + h->pred16x16[PLANE_PRED8x8] = ff_h264_pred16x16_plane_rv40_8_lsx; + } else { + h->pred16x16[PLANE_PRED8x8] = ff_h264_pred16x16_plane_h264_8_lsx; + } + } + } if (have_lasx(cpu_flags)) { if (chroma_format_idc <= 1) { } diff --git a/libavcodec/loongarch/h264_intrapred_lasx.c b/libavcodec/loongarch/h264_intrapred_lasx.c deleted file mode 100644 index c38cd611b84..00000000000 --- a/libavcodec/loongarch/h264_intrapred_lasx.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2021 Loongson Technology Corporation Limited - * Contributed by Hao Chen - * - * 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 "libavutil/loongarch/loongson_intrinsics.h" -#include "h264_intrapred_lasx.h" - -#define PRED16X16_PLANE \ - ptrdiff_t stride_1, stride_2, stride_3, stride_4, stride_5, stride_6; \ - ptrdiff_t stride_8, stride_15; \ - int32_t res0, res1, res2, res3, cnt; \ - uint8_t *src0, *src1; \ - __m256i reg0, reg1, reg2, reg3, reg4; \ - __m256i tmp0, tmp1, tmp2, tmp3; \ - __m256i shuff = {0x0B040A0509060807, 0x0F000E010D020C03, 0, 0}; \ - __m256i mult = {0x0004000300020001, 0x0008000700060005, 0, 0}; \ - __m256i int_mult1 = {0x0000000100000000, 0x0000000300000002, \ - 0x0000000500000004, 0x0000000700000006}; \ - \ - stride_1 = -stride; \ - stride_2 = stride << 1; \ - stride_3 = stride_2 + stride; \ - stride_4 = stride_2 << 1; \ - stride_5 = stride_4 + stride; \ - stride_6 = stride_3 << 1; \ - stride_8 = stride_4 << 1; \ - stride_15 = (stride_8 << 1) - stride; \ - src0 = src - 1; \ - src1 = src0 + stride_8; \ - \ - reg0 = __lasx_xvldx(src0, -stride); \ - reg1 = __lasx_xvldx(src, (8 - stride)); \ - reg0 = __lasx_xvilvl_d(reg1, reg0); \ - reg0 = __lasx_xvshuf_b(reg0, reg0, shuff); \ - reg0 = __lasx_xvhsubw_hu_bu(reg0, reg0); \ - reg0 = __lasx_xvmul_h(reg0, mult); \ - res1 = (src1[0] - src0[stride_6]) + \ - 2 * (src1[stride] - src0[stride_5]) + \ - 3 * (src1[stride_2] - src0[stride_4]) + \ - 4 * (src1[stride_3] - src0[stride_3]) + \ - 5 * (src1[stride_4] - src0[stride_2]) + \ - 6 * (src1[stride_5] - src0[stride]) + \ - 7 * (src1[stride_6] - src0[0]) + \ - 8 * (src0[stride_15] - src0[stride_1]); \ - reg0 = __lasx_xvhaddw_w_h(reg0, reg0); \ - reg0 = __lasx_xvhaddw_d_w(reg0, reg0); \ - reg0 = __lasx_xvhaddw_q_d(reg0, reg0); \ - res0 = __lasx_xvpickve2gr_w(reg0, 0); \ - -#define PRED16X16_PLANE_END \ - res2 = (src0[stride_15] + src[15 - stride] + 1) << 4; \ - res3 = 7 * (res0 + res1); \ - res2 -= res3; \ - reg0 = __lasx_xvreplgr2vr_w(res0); \ - reg1 = __lasx_xvreplgr2vr_w(res1); \ - reg2 = __lasx_xvreplgr2vr_w(res2); \ - reg3 = __lasx_xvmul_w(reg0, int_mult1); \ - reg4 = __lasx_xvslli_w(reg0, 3); \ - reg4 = __lasx_xvadd_w(reg4, reg3); \ - for (cnt = 8; cnt--;) { \ - tmp0 = __lasx_xvadd_w(reg2, reg3); \ - tmp1 = __lasx_xvadd_w(reg2, reg4); \ - tmp0 = __lasx_xvssrani_hu_w(tmp1, tmp0, 5); \ - tmp0 = __lasx_xvpermi_d(tmp0, 0xD8); \ - reg2 = __lasx_xvadd_w(reg2, reg1); \ - tmp2 = __lasx_xvadd_w(reg2, reg3); \ - tmp3 = __lasx_xvadd_w(reg2, reg4); \ - tmp1 = __lasx_xvssrani_hu_w(tmp3, tmp2, 5); \ - tmp1 = __lasx_xvpermi_d(tmp1, 0xD8); \ - tmp0 = __lasx_xvssrani_bu_h(tmp1, tmp0, 0); \ - reg2 = __lasx_xvadd_w(reg2, reg1); \ - __lasx_xvstelm_d(tmp0, src, 0, 0); \ - __lasx_xvstelm_d(tmp0, src, 8, 2); \ - src += stride; \ - __lasx_xvstelm_d(tmp0, src, 0, 1); \ - __lasx_xvstelm_d(tmp0, src, 8, 3); \ - src += stride; \ - } - - -void ff_h264_pred16x16_plane_h264_8_lasx(uint8_t *src, ptrdiff_t stride) -{ - PRED16X16_PLANE - res0 = (5 * res0 + 32) >> 6; - res1 = (5 * res1 + 32) >> 6; - PRED16X16_PLANE_END -} - -void ff_h264_pred16x16_plane_rv40_8_lasx(uint8_t *src, ptrdiff_t stride) -{ - PRED16X16_PLANE - res0 = (res0 + (res0 >> 2)) >> 4; - res1 = (res1 + (res1 >> 2)) >> 4; - PRED16X16_PLANE_END -} - -void ff_h264_pred16x16_plane_svq3_8_lasx(uint8_t *src, ptrdiff_t stride) -{ - PRED16X16_PLANE - cnt = (5 * (res0/4)) / 16; - res0 = (5 * (res1/4)) / 16; - res1 = cnt; - PRED16X16_PLANE_END -} diff --git a/libavcodec/loongarch/h264_intrapred_lasx.h b/libavcodec/loongarch/h264_intrapred_loongarch.h similarity index 70% rename from libavcodec/loongarch/h264_intrapred_lasx.h rename to libavcodec/loongarch/h264_intrapred_loongarch.h index 0c2653300c6..39be87ee9fb 100644 --- a/libavcodec/loongarch/h264_intrapred_lasx.h +++ b/libavcodec/loongarch/h264_intrapred_loongarch.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Loongson Technology Corporation Limited + * Copyright (c) 2023 Loongson Technology Corporation Limited * Contributed by Hao Chen * * This file is part of FFmpeg. @@ -19,13 +19,17 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef AVCODEC_LOONGARCH_H264_INTRAPRED_LASX_H -#define AVCODEC_LOONGARCH_H264_INTRAPRED_LASX_H +#ifndef AVCODEC_LOONGARCH_H264_INTRAPRED_LOONGARCH_H +#define AVCODEC_LOONGARCH_H264_INTRAPRED_LOONGARCH_H #include "libavcodec/avcodec.h" +void ff_h264_pred16x16_plane_h264_8_lsx(uint8_t *src, ptrdiff_t stride); +void ff_h264_pred16x16_plane_rv40_8_lsx(uint8_t *src, ptrdiff_t stride); +void ff_h264_pred16x16_plane_svq3_8_lsx(uint8_t *src, ptrdiff_t stride); + void ff_h264_pred16x16_plane_h264_8_lasx(uint8_t *src, ptrdiff_t stride); void ff_h264_pred16x16_plane_rv40_8_lasx(uint8_t *src, ptrdiff_t stride); void ff_h264_pred16x16_plane_svq3_8_lasx(uint8_t *src, ptrdiff_t stride); -#endif // #ifndef AVCODEC_LOONGARCH_H264_INTRAPRED_LASX_H +#endif // #ifndef AVCODEC_LOONGARCH_H264_INTRAPRED_LOONGARCH_H diff --git a/libavcodec/loongarch/h264chroma.S b/libavcodec/loongarch/h264chroma.S new file mode 100644 index 00000000000..353b8d004bf --- /dev/null +++ b/libavcodec/loongarch/h264chroma.S @@ -0,0 +1,966 @@ +/* + * Loongson LSX/LASX optimized h264chroma + * + * Copyright (c) 2023 Loongson Technology Corporation Limited + * Contributed by Lu Wang + * + * 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 "loongson_asm.S" + +/* void ff_put_h264_chroma_mc8_lsx(uint8_t *dst, uint8_t *src, ptrdiff_t stride, + int h, int x, int y) */ +function ff_put_h264_chroma_mc8_lsx + li.d t8, 8 + sub.d t1, t8, a4 // 8-x + sub.d t2, t8, a5 // 8-y + mul.d t3, t1, t2 // A + mul.d t4, a4, t2 // B + mul.d t5, t1, a5 // C + mul.d t6, a4, a5 // D + add.d t0, t4, t5 // E + vreplgr2vr.b vr0, t3 + vreplgr2vr.b vr1, t4 + vreplgr2vr.b vr2, t5 + vreplgr2vr.b vr3, t6 + vreplgr2vr.b vr4, t0 + slli.d t2, a2, 1 + add.d t3, t2, a2 + slli.d t4, a2, 2 + + bge zero, t6, .ENDLOOP_D + move t1, a3 + vilvl.b vr9, vr1, vr0 + vilvl.b vr10, vr3, vr2 +.LOOP_D: + vld vr5, a1, 0 + vld vr6, a1, 1 + add.d a1, a1, a2 + vld vr7, a1, 0 + vld vr8, a1, 1 + vilvl.b vr11, vr6, vr5 + vilvl.b vr12, vr8, vr7 + vmulwev.h.bu vr13, vr9, vr11 + vmaddwod.h.bu vr13, vr9, vr11 + vmulwev.h.bu vr14, vr10, vr12 + vmaddwod.h.bu vr14, vr10, vr12 + vadd.h vr13, vr13, vr14 + vsrarni.b.h vr13, vr13, 6 + vstelm.d vr13, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + vld vr5, a1, 0 + vld vr6, a1, 1 + vilvl.b vr11, vr8, vr7 + vilvl.b vr12, vr6, vr5 + vmulwev.h.bu vr13, vr9, vr11 + vmaddwod.h.bu vr13, vr9, vr11 + vmulwev.h.bu vr14, vr10, vr12 + vmaddwod.h.bu vr14, vr10, vr12 + vadd.h vr13, vr13, vr14 + vsrarni.b.h vr13, vr13, 6 + vstelm.d vr13, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + vld vr7, a1, 0 + vld vr8, a1, 1 + vilvl.b vr11, vr6, vr5 + vilvl.b vr12, vr8, vr7 + vmulwev.h.bu vr13, vr9, vr11 + vmaddwod.h.bu vr13, vr9, vr11 + vmulwev.h.bu vr14, vr10, vr12 + vmaddwod.h.bu vr14, vr10, vr12 + vadd.h vr13, vr13, vr14 + vsrarni.b.h vr13, vr13, 6 + vstelm.d vr13, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + vld vr5, a1, 0 + vld vr6, a1, 1 + vilvl.b vr11, vr8, vr7 + vilvl.b vr12, vr6, vr5 + vmulwev.h.bu vr13, vr9, vr11 + vmaddwod.h.bu vr13, vr9, vr11 + vmulwev.h.bu vr14, vr10, vr12 + vmaddwod.h.bu vr14, vr10, vr12 + vadd.h vr13, vr13, vr14 + vsrarni.b.h vr13, vr13, 6 + vstelm.d vr13, a0, 0, 0 + add.d a0, a0, a2 + + addi.d t1, t1, -4 + blt zero, t1, .LOOP_D + b .ENDLOOP +.ENDLOOP_D: + + bge zero, t0, .ENDLOOP_E + move t1, a3 + li.d t7, 1 + slt t8, zero, t5 + maskeqz t5, a2, t8 + masknez t7, t7, t8 + or t7, t7, t5 + vilvl.b vr7, vr4, vr0 +.LOOP_E: + vld vr5, a1, 0 + vldx vr6, a1, t7 + vilvl.b vr5, vr6, vr5 + vmulwev.h.bu vr6, vr7, vr5 + vmaddwod.h.bu vr6, vr7, vr5 + vsrarni.b.h vr6, vr6, 6 + vstelm.d vr6, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + vld vr5, a1, 0 + vldx vr6, a1, t7 + vilvl.b vr5, vr6, vr5 + vmulwev.h.bu vr6, vr7, vr5 + vmaddwod.h.bu vr6, vr7, vr5 + vsrarni.b.h vr6, vr6, 6 + vstelm.d vr6, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + vld vr5, a1, 0 + vldx vr6, a1, t7 + vilvl.b vr5, vr6, vr5 + vmulwev.h.bu vr6, vr7, vr5 + vmaddwod.h.bu vr6, vr7, vr5 + vsrarni.b.h vr6, vr6, 6 + vstelm.d vr6, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + vld vr5, a1, 0 + vldx vr6, a1, t7 + vilvl.b vr5, vr6, vr5 + vmulwev.h.bu vr6, vr7, vr5 + vmaddwod.h.bu vr6, vr7, vr5 + vsrarni.b.h vr6, vr6, 6 + vstelm.d vr6, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + + addi.d t1, t1, -4 + blt zero, t1, .LOOP_E + b .ENDLOOP +.ENDLOOP_E: + + move t1, a3 +.LOOP: + vld vr5, a1, 0 + vmulwev.h.bu vr6, vr0, vr5 + vmulwod.h.bu vr7, vr0, vr5 + vsrarni.b.h vr6, vr6, 6 + vsrarni.b.h vr7, vr7, 6 + vilvl.b vr6, vr7, vr6 + vstelm.d vr6, a0, 0, 0 + add.d a0, a0, a2 + vldx vr5, a1, a2 + vmulwev.h.bu vr6, vr0, vr5 + vmulwod.h.bu vr7, vr0, vr5 + vsrarni.b.h vr6, vr6, 6 + vsrarni.b.h vr7, vr7, 6 + vilvl.b vr6, vr7, vr6 + vstelm.d vr6, a0, 0, 0 + add.d a0, a0, a2 + vldx vr5, a1, t2 + vmulwev.h.bu vr6, vr0, vr5 + vmulwod.h.bu vr7, vr0, vr5 + vsrarni.b.h vr6, vr6, 6 + vsrarni.b.h vr7, vr7, 6 + vilvl.b vr6, vr7, vr6 + vstelm.d vr6, a0, 0, 0 + add.d a0, a0, a2 + vldx vr5, a1, t3 + vmulwev.h.bu vr6, vr0, vr5 + vmulwod.h.bu vr7, vr0, vr5 + vsrarni.b.h vr6, vr6, 6 + vsrarni.b.h vr7, vr7, 6 + vilvl.b vr6, vr7, vr6 + vstelm.d vr6, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, t4 + + addi.d t1, t1, -4 + blt zero, t1, .LOOP +.ENDLOOP: +endfunc + +/* void ff_avg_h264_chroma_mc8_lsx(uint8_t *dst, uint8_t *src, ptrdiff_t stride, + int h, int x, int y) */ +function ff_avg_h264_chroma_mc8_lsx + li.d t8, 8 + sub.d t1, t8, a4 // 8-x + sub.d t2, t8, a5 // 8-y + mul.d t3, t1, t2 // A + mul.d t4, a4, t2 // B + mul.d t5, t1, a5 // C + mul.d t6, a4, a5 // D + add.d t0, t4, t5 // E + vreplgr2vr.b vr0, t3 + vreplgr2vr.b vr1, t4 + vreplgr2vr.b vr2, t5 + vreplgr2vr.b vr3, t6 + vreplgr2vr.b vr4, t0 + slli.d t2, a2, 1 + add.d t3, t2, a2 + slli.d t4, a2, 2 + + bge zero, t6, .ENDLOOPD + move t1, a3 + vilvl.b vr9, vr1, vr0 + vilvl.b vr10, vr3, vr2 +.LOOPD: + vld vr5, a1, 0 + vld vr6, a1, 1 + add.d a1, a1, a2 + vld vr7, a1, 0 + vld vr8, a1, 1 + vld vr11, a0, 0 + vilvl.b vr12, vr6, vr5 + vilvl.b vr13, vr8, vr7 + vmulwev.h.bu vr14, vr9, vr12 + vmaddwod.h.bu vr14, vr9, vr12 + vmulwev.h.bu vr15, vr10, vr13 + vmaddwod.h.bu vr15, vr10, vr13 + vadd.h vr14, vr14, vr15 + vsrari.h vr14, vr14, 6 + vsllwil.hu.bu vr11, vr11, 0 + vadd.h vr11, vr14, vr11 + vsrarni.b.h vr11, vr11, 1 + vstelm.d vr11, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + vld vr5, a1, 0 + vld vr6, a1, 1 + vld vr11, a0, 0 + vilvl.b vr12, vr8, vr7 + vilvl.b vr13, vr6, vr5 + vmulwev.h.bu vr14, vr9, vr12 + vmaddwod.h.bu vr14, vr9, vr12 + vmulwev.h.bu vr15, vr10, vr13 + vmaddwod.h.bu vr15, vr10, vr13 + vadd.h vr14, vr14, vr15 + vsrari.h vr14, vr14, 6 + vsllwil.hu.bu vr11, vr11, 0 + vadd.h vr11, vr14, vr11 + vsrarni.b.h vr11, vr11, 1 + vstelm.d vr11, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + vld vr7, a1, 0 + vld vr8, a1, 1 + vld vr11, a0, 0 + vilvl.b vr12, vr6, vr5 + vilvl.b vr13, vr8, vr7 + vmulwev.h.bu vr14, vr9, vr12 + vmaddwod.h.bu vr14, vr9, vr12 + vmulwev.h.bu vr15, vr10, vr13 + vmaddwod.h.bu vr15, vr10, vr13 + vadd.h vr14, vr14, vr15 + vsrari.h vr14, vr14, 6 + vsllwil.hu.bu vr11, vr11, 0 + vadd.h vr11, vr14, vr11 + vsrarni.b.h vr11, vr11, 1 + vstelm.d vr11, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + vld vr5, a1, 0 + vld vr6, a1, 1 + vld vr11, a0, 0 + vilvl.b vr12, vr8, vr7 + vilvl.b vr13, vr6, vr5 + vmulwev.h.bu vr14, vr9, vr12 + vmaddwod.h.bu vr14, vr9, vr12 + vmulwev.h.bu vr15, vr10, vr13 + vmaddwod.h.bu vr15, vr10, vr13 + vadd.h vr14, vr14, vr15 + vsrari.h vr14, vr14, 6 + vsllwil.hu.bu vr11, vr11, 0 + vadd.h vr11, vr14, vr11 + vsrarni.b.h vr11, vr11, 1 + vstelm.d vr11, a0, 0, 0 + add.d a0, a0, a2 + + addi.d t1, t1, -4 + blt zero, t1, .LOOPD + b .ENDLOOPELSE +.ENDLOOPD: + + bge zero, t0, .ENDLOOPE + move t1, a3 + li.d t7, 1 + slt t8, zero, t5 + maskeqz t5, a2, t8 + masknez t7, t7, t8 + or t7, t7, t5 + vilvl.b vr7, vr4, vr0 +.LOOPE: + vld vr5, a1, 0 + vldx vr6, a1, t7 + vld vr8, a0, 0 + vilvl.b vr5, vr6, vr5 + vmulwev.h.bu vr6, vr7, vr5 + vmaddwod.h.bu vr6, vr7, vr5 + vsrari.h vr6, vr6, 6 + vsllwil.hu.bu vr8, vr8, 0 + vadd.h vr8, vr6, vr8 + vsrarni.b.h vr8, vr8, 1 + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + vld vr5, a1, 0 + vldx vr6, a1, t7 + vld vr8, a0, 0 + vilvl.b vr5, vr6, vr5 + vmulwev.h.bu vr6, vr7, vr5 + vmaddwod.h.bu vr6, vr7, vr5 + vsrari.h vr6, vr6, 6 + vsllwil.hu.bu vr8, vr8, 0 + vadd.h vr8, vr6, vr8 + vsrarni.b.h vr8, vr8, 1 + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + vld vr5, a1, 0 + vldx vr6, a1, t7 + vld vr8, a0, 0 + vilvl.b vr5, vr6, vr5 + vmulwev.h.bu vr6, vr7, vr5 + vmaddwod.h.bu vr6, vr7, vr5 + vsrari.h vr6, vr6, 6 + vsllwil.hu.bu vr8, vr8, 0 + vadd.h vr8, vr6, vr8 + vsrarni.b.h vr8, vr8, 1 + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + vld vr5, a1, 0 + vldx vr6, a1, t7 + vld vr8, a0, 0 + vilvl.b vr5, vr6, vr5 + vmulwev.h.bu vr6, vr7, vr5 + vmaddwod.h.bu vr6, vr7, vr5 + vsrari.h vr6, vr6, 6 + vsllwil.hu.bu vr8, vr8, 0 + vadd.h vr8, vr6, vr8 + vsrarni.b.h vr8, vr8, 1 + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + + addi.d t1, t1, -4 + blt zero, t1, .LOOPE + b .ENDLOOPELSE +.ENDLOOPE: + + move t1, a3 +.LOOPELSE: + vld vr5, a1, 0 + vld vr8, a0, 0 + vmulwev.h.bu vr6, vr0, vr5 + vmulwod.h.bu vr7, vr0, vr5 + vilvl.h vr6, vr7, vr6 + vsrari.h vr6, vr6, 6 + vsllwil.hu.bu vr8, vr8, 0 + vadd.h vr8, vr6, vr8 + vsrarni.b.h vr8, vr8, 1 + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + vldx vr5, a1, a2 + vld vr8, a0, 0 + vmulwev.h.bu vr6, vr0, vr5 + vmulwod.h.bu vr7, vr0, vr5 + vilvl.h vr6, vr7, vr6 + vsrari.h vr6, vr6, 6 + vsllwil.hu.bu vr8, vr8, 0 + vadd.h vr8, vr6, vr8 + vsrarni.b.h vr8, vr8, 1 + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + vldx vr5, a1, t2 + vld vr8, a0, 0 + vmulwev.h.bu vr6, vr0, vr5 + vmulwod.h.bu vr7, vr0, vr5 + vilvl.h vr6, vr7, vr6 + vsrari.h vr6, vr6, 6 + vsllwil.hu.bu vr8, vr8, 0 + vadd.h vr8, vr6, vr8 + vsrarni.b.h vr8, vr8, 1 + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + vldx vr5, a1, t3 + vld vr8, a0, 0 + vmulwev.h.bu vr6, vr0, vr5 + vmulwod.h.bu vr7, vr0, vr5 + vilvl.h vr6, vr7, vr6 + vsrari.h vr6, vr6, 6 + vsllwil.hu.bu vr8, vr8, 0 + vadd.h vr8, vr6, vr8 + vsrarni.b.h vr8, vr8, 1 + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, t4 + + addi.d t1, t1, -4 + blt zero, t1, .LOOPELSE +.ENDLOOPELSE: +endfunc + +/* void ff_put_h264_chroma_mc4_lsx(uint8_t *dst, uint8_t *src, ptrdiff_t stride, + int h, int x, int y) */ +function ff_put_h264_chroma_mc4_lsx + li.d t8, 8 + sub.d t1, t8, a4 // 8-x + sub.d t2, t8, a5 // 8-y + mul.d t3, t1, t2 // A + mul.d t4, a4, t2 // B + mul.d t5, t1, a5 // C + mul.d t6, a4, a5 // D + add.d t0, t4, t5 // E + slli.d t8, a2, 1 + vreplgr2vr.b vr0, t3 + vreplgr2vr.b vr1, t4 + vreplgr2vr.b vr2, t5 + vreplgr2vr.b vr3, t6 + vreplgr2vr.b vr4, t0 + + bge zero, t6, .ENDPUT_D + move t1, a3 + vilvl.b vr9, vr1, vr0 + vilvl.b vr10, vr3, vr2 +.PUT_D: + vld vr5, a1, 0 + vld vr6, a1, 1 + add.d a1, a1, a2 + vld vr7, a1, 0 + vld vr8, a1, 1 + add.d a1, a1, a2 + vld vr11, a1, 0 + vld vr12, a1, 1 + vilvl.b vr5, vr6, vr5 + vilvl.b vr7, vr8, vr7 + vilvl.b vr13, vr12, vr11 + vilvl.d vr5, vr7, vr5 + vilvl.d vr13, vr13, vr7 + vmulwev.h.bu vr14, vr9, vr5 + vmaddwod.h.bu vr14, vr9, vr5 + vmulwev.h.bu vr15, vr10, vr13 + vmaddwod.h.bu vr15, vr10, vr13 + vadd.h vr14, vr14, vr15 + vsrarni.b.h vr14, vr14, 6 + vstelm.w vr14, a0, 0, 0 + add.d a0, a0, a2 + vstelm.w vr14, a0, 0, 1 + add.d a0, a0, a2 + addi.d t1, t1, -2 + blt zero, t1, .PUT_D + b .ENDPUT +.ENDPUT_D: + + bge zero, t0, .ENDPUT_E + move t1, a3 + li.d t7, 1 + slt t8, zero, t5 + maskeqz t5, a2, t8 + masknez t7, t7, t8 + or t7, t7, t5 + vilvl.b vr7, vr4, vr0 +.PUT_E: + vld vr5, a1, 0 + vldx vr6, a1, t7 + vilvl.b vr5, vr6, vr5 + add.d a1, a1, a2 + vld vr8, a1, 0 + vldx vr9, a1, t7 + vilvl.b vr8, vr9, vr8 + vilvl.d vr5, vr8, vr5 + vmulwev.h.bu vr6, vr7, vr5 + vmaddwod.h.bu vr6, vr7, vr5 + vsrarni.b.h vr6, vr6, 6 + vstelm.w vr6, a0, 0, 0 + add.d a0, a0, a2 + vstelm.w vr6, a0, 0, 1 + add.d a0, a0, a2 + add.d a1, a1, a2 + addi.d t1, t1, -2 + blt zero, t1, .PUT_E + b .ENDPUT +.ENDPUT_E: + + move t1, a3 +.PUT: + vld vr5, a1, 0 + vldx vr8, a1, a2 + vilvl.w vr5, vr8, vr5 + vmulwev.h.bu vr6, vr0, vr5 + vmulwod.h.bu vr7, vr0, vr5 + vsrarni.b.h vr6, vr6, 6 + vsrarni.b.h vr7, vr7, 6 + vilvl.b vr6, vr7, vr6 + vstelm.w vr6, a0, 0, 0 + add.d a0, a0, a2 + vstelm.w vr6, a0, 0, 1 + add.d a0, a0, a2 + add.d a1, a1, t8 + addi.d t1, t1, -2 + blt zero, t1, .PUT +.ENDPUT: +endfunc + +/* void ff_put_h264_chroma_mc8_lasx(uint8_t *dst, uint8_t *src, ptrdiff_t stride, + int h, int x, int y) */ +function ff_put_h264_chroma_mc8_lasx + li.d t8, 8 + sub.d t1, t8, a4 // 8-x + sub.d t2, t8, a5 // 8-y + mul.d t3, t1, t2 // A + mul.d t4, a4, t2 // B + mul.d t5, t1, a5 // C + mul.d t6, a4, a5 // D + add.d t0, t4, t5 // E + xvreplgr2vr.b xr0, t3 + xvreplgr2vr.b xr1, t4 + xvreplgr2vr.b xr2, t5 + xvreplgr2vr.b xr3, t6 + xvreplgr2vr.b xr4, t0 + slli.d t2, a2, 1 + add.d t3, t2, a2 + slli.d t4, a2, 2 + + bge zero, t6, .ENDLOOP_DA + move t1, a3 + xvilvl.b xr9, xr1, xr0 + xvilvl.b xr10, xr3, xr2 +.LOOP_DA: + fld.d f5, a1, 0 + fld.d f6, a1, 1 + add.d a1, a1, a2 + fld.d f7, a1, 0 + fld.d f8, a1, 1 + add.d a1, a1, a2 + fld.d f13, a1, 0 + fld.d f14, a1, 1 + add.d a1, a1, a2 + fld.d f15, a1, 0 + fld.d f16, a1, 1 + add.d a1, a1, a2 + fld.d f17, a1, 0 + fld.d f18, a1, 1 + vilvl.b vr11, vr6, vr5 + vilvl.b vr12, vr8, vr7 + vilvl.b vr14, vr14, vr13 + vilvl.b vr15, vr16, vr15 + vilvl.b vr16, vr18, vr17 + xvpermi.q xr11, xr12, 0x02 + xvpermi.q xr12, xr14, 0x02 + xvpermi.q xr14, xr15, 0x02 + xvpermi.q xr15, xr16, 0x02 + + xvmulwev.h.bu xr19, xr9, xr11 + xvmaddwod.h.bu xr19, xr9, xr11 + xvmulwev.h.bu xr20, xr10, xr12 + xvmaddwod.h.bu xr20, xr10, xr12 + xvadd.h xr21, xr19, xr20 + xvsrarni.b.h xr21, xr21, 6 + vstelm.d vr21, a0, 0, 0 + add.d a0, a0, a2 + xvstelm.d xr21, a0, 0, 2 + add.d a0, a0, a2 + xvmulwev.h.bu xr13, xr9, xr14 + xvmaddwod.h.bu xr13, xr9, xr14 + xvmulwev.h.bu xr14, xr10, xr15 + xvmaddwod.h.bu xr14, xr10, xr15 + xvadd.h xr13, xr13, xr14 + xvsrarni.b.h xr13, xr13, 6 + vstelm.d vr13, a0, 0, 0 + add.d a0, a0, a2 + xvstelm.d xr13, a0, 0, 2 + add.d a0, a0, a2 + + addi.d t1, t1, -4 + blt zero, t1, .LOOP_DA + b .ENDLOOPA +.ENDLOOP_DA: + + bge zero, t0, .ENDLOOP_EA + move t1, a3 + li.d t7, 1 + slt t8, zero, t5 + maskeqz t5, a2, t8 + masknez t7, t7, t8 + or t7, t7, t5 + xvilvl.b xr7, xr4, xr0 +.LOOP_EA: + fld.d f5, a1, 0 + fldx.d f6, a1, t7 + add.d a1, a1, a2 + fld.d f9, a1, 0 + fldx.d f10, a1, t7 + add.d a1, a1, a2 + fld.d f11, a1, 0 + fldx.d f12, a1, t7 + add.d a1, a1, a2 + fld.d f13, a1, 0 + fldx.d f14, a1, t7 + vilvl.b vr5, vr6, vr5 + vilvl.b vr9, vr10, vr9 + vilvl.b vr11, vr12, vr11 + vilvl.b vr13, vr14, vr13 + xvpermi.q xr5, xr9, 0x02 + xvpermi.q xr11, xr13, 0x02 + + xvmulwev.h.bu xr8, xr7, xr5 + xvmaddwod.h.bu xr8, xr7, xr5 + xvmulwev.h.bu xr6, xr7, xr11 + xvmaddwod.h.bu xr6, xr7, xr11 + xvsrarni.b.h xr8, xr8, 6 + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + xvstelm.d xr8, a0, 0, 2 + add.d a0, a0, a2 + xvsrarni.b.h xr6, xr6, 6 + vstelm.d vr6, a0, 0, 0 + add.d a0, a0, a2 + xvstelm.d xr6, a0, 0, 2 + add.d a0, a0, a2 + add.d a1, a1, a2 + + addi.d t1, t1, -4 + blt zero, t1, .LOOP_EA + b .ENDLOOPA +.ENDLOOP_EA: + + move t1, a3 +.LOOPA: + fld.d f5, a1, 0 + fldx.d f6, a1, a2 + fldx.d f7, a1, t2 + fldx.d f8, a1, t3 + vilvl.d vr5, vr6, vr5 + vilvl.d vr7, vr8, vr7 + xvpermi.q xr5, xr7, 0x02 + xvmulwev.h.bu xr6, xr0, xr5 + xvmulwod.h.bu xr7, xr0, xr5 + xvilvl.h xr8, xr7, xr6 + xvilvh.h xr9, xr7, xr6 + xvsrarni.b.h xr9, xr8, 6 + vstelm.d vr9, a0, 0, 0 + add.d a0, a0, a2 + vstelm.d vr9, a0, 0, 1 + add.d a0, a0, a2 + xvstelm.d xr9, a0, 0, 2 + add.d a0, a0, a2 + xvstelm.d xr9, a0, 0, 3 + add.d a0, a0, a2 + add.d a1, a1, t4 + + addi.d t1, t1, -4 + blt zero, t1, .LOOPA +.ENDLOOPA: +endfunc + +/* void ff_avg_h264_chroma_mc8_lasx(uint8_t *dst, uint8_t *src, ptrdiff_t stride, + int h, int x, int y) */ +function ff_avg_h264_chroma_mc8_lasx + li.d t8, 8 + sub.d t1, t8, a4 // 8-x + sub.d t2, t8, a5 // 8-y + mul.d t3, t1, t2 // A + mul.d t4, a4, t2 // B + mul.d t5, t1, a5 // C + mul.d t6, a4, a5 // D + add.d t0, t4, t5 // E + xvreplgr2vr.b xr0, t3 + xvreplgr2vr.b xr1, t4 + xvreplgr2vr.b xr2, t5 + xvreplgr2vr.b xr3, t6 + xvreplgr2vr.b xr4, t0 + slli.d t2, a2, 1 + add.d t3, t2, a2 + slli.d t4, a2, 2 + + bge zero, t6, .ENDLOOPDA + move t1, a3 + xvilvl.b xr9, xr1, xr0 + xvilvl.b xr10, xr3, xr2 +.LOOPDA: + fld.d f5, a1, 0 + fld.d f6, a1, 1 + add.d a1, a1, a2 + fld.d f7, a1, 0 + fld.d f8, a1, 1 + add.d a1, a1, a2 + fld.d f11, a1, 0 + fld.d f12, a1, 1 + add.d a1, a1, a2 + fld.d f13, a1, 0 + fld.d f14, a1, 1 + add.d a1, a1, a2 + fld.d f15, a1, 0 + fld.d f16, a1, 1 + fld.d f17, a0, 0 + fldx.d f18, a0, a2 + fldx.d f19, a0, t2 + fldx.d f20, a0, t3 + vilvl.b vr5, vr6, vr5 + vilvl.b vr7, vr8, vr7 + vilvl.b vr11, vr12, vr11 + vilvl.b vr13, vr14, vr13 + vilvl.b vr16, vr16, vr15 + xvpermi.q xr5, xr7, 0x02 + xvpermi.q xr7, xr11, 0x02 + xvpermi.q xr11, xr13, 0x02 + xvpermi.q xr13, xr16, 0x02 + xvpermi.q xr17, xr18, 0x02 + xvpermi.q xr19, xr20, 0x02 + + xvmulwev.h.bu xr14, xr9, xr5 + xvmaddwod.h.bu xr14, xr9, xr5 + xvmulwev.h.bu xr15, xr10, xr7 + xvmaddwod.h.bu xr15, xr10, xr7 + xvadd.h xr14, xr14, xr15 + xvsrari.h xr14, xr14, 6 + xvsllwil.hu.bu xr17, xr17, 0 + xvadd.h xr20, xr14, xr17 + xvsrarni.b.h xr20, xr20, 1 + xvstelm.d xr20, a0, 0, 0 + add.d a0, a0, a2 + xvstelm.d xr20, a0, 0, 2 + add.d a0, a0, a2 + xvmulwev.h.bu xr14, xr9, xr11 + xvmaddwod.h.bu xr14, xr9, xr11 + xvmulwev.h.bu xr15, xr10, xr13 + xvmaddwod.h.bu xr15, xr10, xr13 + xvadd.h xr14, xr14, xr15 + xvsrari.h xr14, xr14, 6 + xvsllwil.hu.bu xr19, xr19, 0 + xvadd.h xr21, xr14, xr19 + xvsrarni.b.h xr21, xr21, 1 + xvstelm.d xr21, a0, 0, 0 + add.d a0, a0, a2 + xvstelm.d xr21, a0, 0, 2 + add.d a0, a0, a2 + + addi.d t1, t1, -4 + blt zero, t1, .LOOPDA + b .ENDLOOPELSEA +.ENDLOOPDA: + + bge zero, t0, .ENDLOOPEA + move t1, a3 + li.d t7, 1 + slt t8, zero, t5 + maskeqz t5, a2, t8 + masknez t7, t7, t8 + or t7, t7, t5 + xvilvl.b xr7, xr4, xr0 +.LOOPEA: + fld.d f5, a1, 0 + fldx.d f6, a1, t7 + add.d a1, a1, a2 + fld.d f8, a1, 0 + fldx.d f9, a1, t7 + add.d a1, a1, a2 + fld.d f10, a1, 0 + fldx.d f11, a1, t7 + add.d a1, a1, a2 + fld.d f12, a1, 0 + fldx.d f13, a1, t7 + add.d a1, a1, a2 + fld.d f14, a0, 0 + fldx.d f15, a0, a2 + fldx.d f16, a0, t2 + fldx.d f17, a0, t3 + vilvl.b vr5, vr6, vr5 + vilvl.b vr8, vr9, vr8 + vilvl.b vr10, vr11, vr10 + vilvl.b vr12, vr13, vr12 + xvpermi.q xr5, xr8, 0x02 + xvpermi.q xr10, xr12, 0x02 + xvpermi.q xr14, xr15, 0x02 + xvpermi.q xr16, xr17, 0x02 + + xvmulwev.h.bu xr6, xr7, xr5 + xvmaddwod.h.bu xr6, xr7, xr5 + xvsrari.h xr6, xr6, 6 + xvsllwil.hu.bu xr14, xr14, 0 + xvadd.h xr8, xr6, xr14 + xvsrarni.b.h xr8, xr8, 1 + xvstelm.d xr8, a0, 0, 0 + add.d a0, a0, a2 + xvstelm.d xr8, a0, 0, 2 + add.d a0, a0, a2 + xvmulwev.h.bu xr6, xr7, xr10 + xvmaddwod.h.bu xr6, xr7, xr10 + xvsrari.h xr6, xr6, 6 + xvsllwil.hu.bu xr16, xr16, 0 + xvadd.h xr8, xr6, xr16 + xvsrarni.b.h xr8, xr8, 1 + xvstelm.d xr8, a0, 0, 0 + add.d a0, a0, a2 + xvstelm.d xr8, a0, 0, 2 + add.d a0, a0, a2 + + addi.d t1, t1, -4 + blt zero, t1, .LOOPEA + b .ENDLOOPELSEA +.ENDLOOPEA: + + move t1, a3 +.LOOPELSEA: + fld.d f5, a1, 0 + fldx.d f6, a1, a2 + fldx.d f7, a1, t2 + fldx.d f8, a1, t3 + fld.d f9, a0, 0 + fldx.d f10, a0, a2 + fldx.d f11, a0, t2 + fldx.d f12, a0, t3 + xvpermi.q xr5, xr6, 0x02 + xvpermi.q xr7, xr8, 0x02 + xvpermi.q xr9, xr10, 0x02 + xvpermi.q xr11, xr12, 0x02 + + xvmulwev.h.bu xr12, xr0, xr5 + xvmulwod.h.bu xr13, xr0, xr5 + xvilvl.h xr12, xr13, xr12 + xvsrari.h xr12, xr12, 6 + xvsllwil.hu.bu xr9, xr9, 0 + xvadd.h xr9, xr12, xr9 + xvsrarni.b.h xr9, xr9, 1 + xvstelm.d xr9, a0, 0, 0 + add.d a0, a0, a2 + xvstelm.d xr9, a0, 0, 2 + add.d a0, a0, a2 + xvmulwev.h.bu xr12, xr0, xr7 + xvmulwod.h.bu xr13, xr0, xr7 + xvilvl.h xr12, xr13, xr12 + xvsrari.h xr12, xr12, 6 + xvsllwil.hu.bu xr11, xr11, 0 + xvadd.h xr13, xr12, xr11 + xvsrarni.b.h xr13, xr13, 1 + xvstelm.d xr13, a0, 0, 0 + add.d a0, a0, a2 + xvstelm.d xr13, a0, 0, 2 + add.d a0, a0, a2 + add.d a1, a1, t4 + + addi.d t1, t1, -4 + blt zero, t1, .LOOPELSEA +.ENDLOOPELSEA: +endfunc + +/* void ff_put_h264_chroma_mc4_lasx(uint8_t *dst, uint8_t *src, ptrdiff_t stride, + int h, int x, int y) */ +function ff_put_h264_chroma_mc4_lasx + li.d t8, 8 + sub.d t1, t8, a4 // 8-x + sub.d t2, t8, a5 // 8-y + mul.d t3, t1, t2 // A + mul.d t4, a4, t2 // B + mul.d t5, t1, a5 // C + mul.d t6, a4, a5 // D + add.d t0, t4, t5 // E + slli.d t8, a2, 1 + vreplgr2vr.b vr0, t3 + vreplgr2vr.b vr1, t4 + vreplgr2vr.b vr2, t5 + vreplgr2vr.b vr3, t6 + vreplgr2vr.b vr4, t0 + + bge zero, t6, .ENDPUT_DA + move t1, a3 + vilvl.b vr9, vr1, vr0 + vilvl.b vr10, vr3, vr2 +.PUT_DA: + fld.d f5, a1, 0 + fld.d f6, a1, 1 + add.d a1, a1, a2 + fld.d f7, a1, 0 + fld.d f8, a1, 1 + add.d a1, a1, a2 + fld.d f11, a1, 0 + fld.d f12, a1, 1 + vilvl.b vr5, vr6, vr5 + vilvl.b vr7, vr8, vr7 + vilvl.b vr13, vr12, vr11 + vilvl.d vr5, vr7, vr5 + vilvl.d vr13, vr13, vr7 + vmulwev.h.bu vr14, vr9, vr5 + vmaddwod.h.bu vr14, vr9, vr5 + vmulwev.h.bu vr15, vr10, vr13 + vmaddwod.h.bu vr15, vr10, vr13 + xvadd.h xr14, xr14, xr15 + vsrarni.b.h vr16, vr14, 6 + vstelm.w vr16, a0, 0, 0 + add.d a0, a0, a2 + vstelm.w vr16, a0, 0, 1 + add.d a0, a0, a2 + addi.d t1, t1, -2 + blt zero, t1, .PUT_DA + b .ENDPUTA +.ENDPUT_DA: + + bge zero, t0, .ENDPUT_EA + move t1, a3 + li.d t7, 1 + slt t8, zero, t5 + maskeqz t5, a2, t8 + masknez t7, t7, t8 + or t7, t7, t5 + vilvl.b vr7, vr4, vr0 +.PUT_EA: + fld.d f5, a1, 0 + fldx.d f6, a1, t7 + vilvl.b vr5, vr6, vr5 + add.d a1, a1, a2 + fld.d f8, a1, 0 + fldx.d f9, a1, t7 + vilvl.b vr8, vr9, vr8 + vilvl.d vr5, vr8, vr5 + vmulwev.h.bu vr6, vr7, vr5 + vmaddwod.h.bu vr6, vr7, vr5 + vsrarni.b.h vr6, vr6, 6 + vstelm.w vr6, a0, 0, 0 + add.d a0, a0, a2 + vstelm.w vr6, a0, 0, 1 + add.d a0, a0, a2 + add.d a1, a1, a2 + addi.d t1, t1, -2 + blt zero, t1, .PUT_EA + b .ENDPUTA +.ENDPUT_EA: + + move t1, a3 +.PUTA: + fld.d f5, a1, 0 + fldx.d f8, a1, a2 + vilvl.w vr5, vr8, vr5 + vmulwev.h.bu vr6, vr0, vr5 + vmulwod.h.bu vr7, vr0, vr5 + vilvl.h vr6, vr7, vr6 + vsrarni.b.h vr6, vr6, 6 + vstelm.w vr6, a0, 0, 0 + add.d a0, a0, a2 + vstelm.w vr6, a0, 0, 1 + add.d a0, a0, a2 + add.d a1, a1, t8 + addi.d t1, t1, -2 + blt zero, t1, .PUTA +.ENDPUTA: +endfunc diff --git a/libavcodec/loongarch/h264chroma_init_loongarch.c b/libavcodec/loongarch/h264chroma_init_loongarch.c index 0ca24ecc477..40a957aad3e 100644 --- a/libavcodec/loongarch/h264chroma_init_loongarch.c +++ b/libavcodec/loongarch/h264chroma_init_loongarch.c @@ -19,7 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "h264chroma_lasx.h" +#include "h264chroma_loongarch.h" #include "libavutil/attributes.h" #include "libavutil/loongarch/cpu.h" #include "libavcodec/h264chroma.h" @@ -27,6 +27,14 @@ av_cold void ff_h264chroma_init_loongarch(H264ChromaContext *c, int bit_depth) { int cpu_flags = av_get_cpu_flags(); + if (have_lsx(cpu_flags)) { + if (bit_depth <= 8) { + c->put_h264_chroma_pixels_tab[0] = ff_put_h264_chroma_mc8_lsx; + c->avg_h264_chroma_pixels_tab[0] = ff_avg_h264_chroma_mc8_lsx; + c->put_h264_chroma_pixels_tab[1] = ff_put_h264_chroma_mc4_lsx; + } + } + if (have_lasx(cpu_flags)) { if (bit_depth <= 8) { c->put_h264_chroma_pixels_tab[0] = ff_put_h264_chroma_mc8_lasx; diff --git a/libavcodec/loongarch/h264chroma_lasx.c b/libavcodec/loongarch/h264chroma_lasx.c deleted file mode 100644 index 1c0e002bdf6..00000000000 --- a/libavcodec/loongarch/h264chroma_lasx.c +++ /dev/null @@ -1,1280 +0,0 @@ -/* - * Loongson LASX optimized h264chroma - * - * Copyright (c) 2020 Loongson Technology Corporation Limited - * Contributed by Shiyou Yin - * - * 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 "h264chroma_lasx.h" -#include "libavutil/attributes.h" -#include "libavutil/avassert.h" -#include "libavutil/loongarch/loongson_intrinsics.h" - -static const uint8_t chroma_mask_arr[64] = { - 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, - 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, - 0, 1, 1, 2, 2, 3, 3, 4, 16, 17, 17, 18, 18, 19, 19, 20, - 0, 1, 1, 2, 2, 3, 3, 4, 16, 17, 17, 18, 18, 19, 19, 20 -}; - -static av_always_inline void avc_chroma_hv_8x4_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride, uint32_t coef_hor0, - uint32_t coef_hor1, uint32_t coef_ver0, - uint32_t coef_ver1) -{ - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - ptrdiff_t stride_4x = stride_2x << 1; - __m256i src0, src1, src2, src3, src4, out; - __m256i res_hz0, res_hz1, res_hz2, res_vt0, res_vt1; - __m256i mask; - __m256i coeff_hz_vec0 = __lasx_xvreplgr2vr_b(coef_hor0); - __m256i coeff_hz_vec1 = __lasx_xvreplgr2vr_b(coef_hor1); - __m256i coeff_hz_vec = __lasx_xvilvl_b(coeff_hz_vec0, coeff_hz_vec1); - __m256i coeff_vt_vec0 = __lasx_xvreplgr2vr_h(coef_ver0); - __m256i coeff_vt_vec1 = __lasx_xvreplgr2vr_h(coef_ver1); - - DUP2_ARG2(__lasx_xvld, chroma_mask_arr, 0, src, 0, mask, src0); - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2x, src, stride_3x, src, stride_4x, - src1, src2, src3, src4); - DUP2_ARG3(__lasx_xvpermi_q, src2, src1, 0x20, src4, src3, 0x20, src1, src3); - src0 = __lasx_xvshuf_b(src0, src0, mask); - DUP2_ARG3(__lasx_xvshuf_b, src1, src1, mask, src3, src3, mask, src1, src3); - DUP2_ARG2(__lasx_xvdp2_h_bu, src0, coeff_hz_vec, src1, coeff_hz_vec, res_hz0, res_hz1); - res_hz2 = __lasx_xvdp2_h_bu(src3, coeff_hz_vec); - res_vt0 = __lasx_xvmul_h(res_hz1, coeff_vt_vec0); - res_vt1 = __lasx_xvmul_h(res_hz2, coeff_vt_vec0); - res_hz0 = __lasx_xvpermi_q(res_hz1, res_hz0, 0x20); - res_hz1 = __lasx_xvpermi_q(res_hz1, res_hz2, 0x3); - res_vt0 = __lasx_xvmadd_h(res_vt0, res_hz0, coeff_vt_vec1); - res_vt1 = __lasx_xvmadd_h(res_vt1, res_hz1, coeff_vt_vec1); - out = __lasx_xvssrarni_bu_h(res_vt1, res_vt0, 6); - __lasx_xvstelm_d(out, dst, 0, 0); - __lasx_xvstelm_d(out, dst + stride, 0, 2); - __lasx_xvstelm_d(out, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out, dst + stride_3x, 0, 3); -} - -static av_always_inline void avc_chroma_hv_8x8_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride, uint32_t coef_hor0, - uint32_t coef_hor1, uint32_t coef_ver0, - uint32_t coef_ver1) -{ - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - ptrdiff_t stride_4x = stride << 2; - __m256i src0, src1, src2, src3, src4, src5, src6, src7, src8; - __m256i out0, out1; - __m256i res_hz0, res_hz1, res_hz2, res_hz3, res_hz4; - __m256i res_vt0, res_vt1, res_vt2, res_vt3; - __m256i mask; - __m256i coeff_hz_vec0 = __lasx_xvreplgr2vr_b(coef_hor0); - __m256i coeff_hz_vec1 = __lasx_xvreplgr2vr_b(coef_hor1); - __m256i coeff_hz_vec = __lasx_xvilvl_b(coeff_hz_vec0, coeff_hz_vec1); - __m256i coeff_vt_vec0 = __lasx_xvreplgr2vr_h(coef_ver0); - __m256i coeff_vt_vec1 = __lasx_xvreplgr2vr_h(coef_ver1); - - DUP2_ARG2(__lasx_xvld, chroma_mask_arr, 0, src, 0, mask, src0); - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2x, src, stride_3x, src, stride_4x, - src1, src2, src3, src4); - src += stride_4x; - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2x, src, stride_3x, src, stride_4x, - src5, src6, src7, src8); - DUP4_ARG3(__lasx_xvpermi_q, src2, src1, 0x20, src4, src3, 0x20, src6, src5, 0x20, - src8, src7, 0x20, src1, src3, src5, src7); - src0 = __lasx_xvshuf_b(src0, src0, mask); - DUP4_ARG3(__lasx_xvshuf_b, src1, src1, mask, src3, src3, mask, src5, src5, mask, src7, - src7, mask, src1, src3, src5, src7); - DUP4_ARG2(__lasx_xvdp2_h_bu, src0, coeff_hz_vec, src1, coeff_hz_vec, src3, - coeff_hz_vec, src5, coeff_hz_vec, res_hz0, res_hz1, res_hz2, res_hz3); - res_hz4 = __lasx_xvdp2_h_bu(src7, coeff_hz_vec); - res_vt0 = __lasx_xvmul_h(res_hz1, coeff_vt_vec0); - res_vt1 = __lasx_xvmul_h(res_hz2, coeff_vt_vec0); - res_vt2 = __lasx_xvmul_h(res_hz3, coeff_vt_vec0); - res_vt3 = __lasx_xvmul_h(res_hz4, coeff_vt_vec0); - res_hz0 = __lasx_xvpermi_q(res_hz1, res_hz0, 0x20); - res_hz1 = __lasx_xvpermi_q(res_hz1, res_hz2, 0x3); - res_hz2 = __lasx_xvpermi_q(res_hz2, res_hz3, 0x3); - res_hz3 = __lasx_xvpermi_q(res_hz3, res_hz4, 0x3); - DUP4_ARG3(__lasx_xvmadd_h, res_vt0, res_hz0, coeff_vt_vec1, res_vt1, res_hz1, coeff_vt_vec1, - res_vt2, res_hz2, coeff_vt_vec1, res_vt3, res_hz3, coeff_vt_vec1, - res_vt0, res_vt1, res_vt2, res_vt3); - DUP2_ARG3(__lasx_xvssrarni_bu_h, res_vt1, res_vt0, 6, res_vt3, res_vt2, 6, out0, out1); - __lasx_xvstelm_d(out0, dst, 0, 0); - __lasx_xvstelm_d(out0, dst + stride, 0, 2); - __lasx_xvstelm_d(out0, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out0, dst + stride_3x, 0, 3); - dst += stride_4x; - __lasx_xvstelm_d(out1, dst, 0, 0); - __lasx_xvstelm_d(out1, dst + stride, 0, 2); - __lasx_xvstelm_d(out1, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out1, dst + stride_3x, 0, 3); -} - -static av_always_inline void avc_chroma_hz_8x4_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride, uint32_t coeff0, uint32_t coeff1) -{ - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - __m256i src0, src1, src2, src3, out; - __m256i res0, res1; - __m256i mask; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - coeff_vec = __lasx_xvslli_b(coeff_vec, 3); - DUP2_ARG2(__lasx_xvld, chroma_mask_arr, 0, src, 0, mask, src0); - DUP2_ARG2(__lasx_xvldx, src, stride, src, stride_2x, src1, src2); - src3 = __lasx_xvldx(src, stride_3x); - DUP2_ARG3(__lasx_xvpermi_q, src1, src0, 0x20, src3, src2, 0x20, src0, src2); - DUP2_ARG3(__lasx_xvshuf_b, src0, src0, mask, src2, src2, mask, src0, src2); - DUP2_ARG2(__lasx_xvdp2_h_bu, src0, coeff_vec, src2, coeff_vec, res0, res1); - out = __lasx_xvssrarni_bu_h(res1, res0, 6); - __lasx_xvstelm_d(out, dst, 0, 0); - __lasx_xvstelm_d(out, dst + stride, 0, 2); - __lasx_xvstelm_d(out, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out, dst + stride_3x, 0, 3); - -} - -static av_always_inline void avc_chroma_hz_8x8_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride, uint32_t coeff0, uint32_t coeff1) -{ - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - ptrdiff_t stride_4x = stride << 2; - __m256i src0, src1, src2, src3, src4, src5, src6, src7; - __m256i out0, out1; - __m256i res0, res1, res2, res3; - __m256i mask; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - coeff_vec = __lasx_xvslli_b(coeff_vec, 3); - DUP2_ARG2(__lasx_xvld, chroma_mask_arr, 0, src, 0, mask, src0); - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2x, src, stride_3x, src, stride_4x, - src1, src2, src3, src4); - src += stride_4x; - DUP2_ARG2(__lasx_xvldx, src, stride, src, stride_2x, src5, src6); - src7 = __lasx_xvldx(src, stride_3x); - DUP4_ARG3(__lasx_xvpermi_q, src1, src0, 0x20, src3, src2, 0x20, src5, src4, 0x20, - src7, src6, 0x20, src0, src2, src4, src6); - DUP4_ARG3(__lasx_xvshuf_b, src0, src0, mask, src2, src2, mask, src4, src4, mask, - src6, src6, mask, src0, src2, src4, src6); - DUP4_ARG2(__lasx_xvdp2_h_bu, src0, coeff_vec, src2, coeff_vec, src4, coeff_vec, src6, - coeff_vec, res0, res1, res2, res3); - DUP2_ARG3(__lasx_xvssrarni_bu_h, res1, res0, 6, res3, res2, 6, out0, out1); - __lasx_xvstelm_d(out0, dst, 0, 0); - __lasx_xvstelm_d(out0, dst + stride, 0, 2); - __lasx_xvstelm_d(out0, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out0, dst + stride_3x, 0, 3); - dst += stride_4x; - __lasx_xvstelm_d(out1, dst, 0, 0); - __lasx_xvstelm_d(out1, dst + stride, 0, 2); - __lasx_xvstelm_d(out1, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out1, dst + stride_3x, 0, 3); -} - -static av_always_inline void avc_chroma_hz_nonmult_lasx(const uint8_t *src, - uint8_t *dst, ptrdiff_t stride, uint32_t coeff0, - uint32_t coeff1, int32_t height) -{ - uint32_t row; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - ptrdiff_t stride_4x = stride << 2; - __m256i src0, src1, src2, src3, out; - __m256i res0, res1; - __m256i mask; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - mask = __lasx_xvld(chroma_mask_arr, 0); - coeff_vec = __lasx_xvslli_b(coeff_vec, 3); - - for (row = height >> 2; row--;) { - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, src, stride_3x, - src0, src1, src2, src3); - src += stride_4x; - DUP2_ARG3(__lasx_xvpermi_q, src1, src0, 0x20, src3, src2, 0x20, src0, src2); - DUP2_ARG3(__lasx_xvshuf_b, src0, src0, mask, src2, src2, mask, src0, src2); - DUP2_ARG2(__lasx_xvdp2_h_bu, src0, coeff_vec, src2, coeff_vec, res0, res1); - out = __lasx_xvssrarni_bu_h(res1, res0, 6); - __lasx_xvstelm_d(out, dst, 0, 0); - __lasx_xvstelm_d(out, dst + stride, 0, 2); - __lasx_xvstelm_d(out, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out, dst + stride_3x, 0, 3); - dst += stride_4x; - } - - if ((height & 3)) { - src0 = __lasx_xvld(src, 0); - src1 = __lasx_xvldx(src, stride); - src1 = __lasx_xvpermi_q(src1, src0, 0x20); - src0 = __lasx_xvshuf_b(src1, src1, mask); - res0 = __lasx_xvdp2_h_bu(src0, coeff_vec); - out = __lasx_xvssrarni_bu_h(res0, res0, 6); - __lasx_xvstelm_d(out, dst, 0, 0); - dst += stride; - __lasx_xvstelm_d(out, dst, 0, 2); - } -} - -static av_always_inline void avc_chroma_vt_8x4_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride, uint32_t coeff0, uint32_t coeff1) -{ - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - __m256i src0, src1, src2, src3, src4, out; - __m256i res0, res1; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - coeff_vec = __lasx_xvslli_b(coeff_vec, 3); - src0 = __lasx_xvld(src, 0); - src += stride; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, src, stride_3x, - src1, src2, src3, src4); - DUP4_ARG3(__lasx_xvpermi_q, src1, src0, 0x20, src2, src1, 0x20, src3, src2, 0x20, - src4, src3, 0x20, src0, src1, src2, src3); - DUP2_ARG2(__lasx_xvilvl_b, src1, src0, src3, src2, src0, src2); - DUP2_ARG2(__lasx_xvdp2_h_bu, src0, coeff_vec, src2, coeff_vec, res0, res1); - out = __lasx_xvssrarni_bu_h(res1, res0, 6); - __lasx_xvstelm_d(out, dst, 0, 0); - __lasx_xvstelm_d(out, dst + stride, 0, 2); - __lasx_xvstelm_d(out, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out, dst + stride_3x, 0, 3); -} - -static av_always_inline void avc_chroma_vt_8x8_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride, uint32_t coeff0, uint32_t coeff1) -{ - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - ptrdiff_t stride_4x = stride << 2; - __m256i src0, src1, src2, src3, src4, src5, src6, src7, src8; - __m256i out0, out1; - __m256i res0, res1, res2, res3; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - coeff_vec = __lasx_xvslli_b(coeff_vec, 3); - src0 = __lasx_xvld(src, 0); - src += stride; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, src, stride_3x, - src1, src2, src3, src4); - src += stride_4x; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, src, stride_3x, - src5, src6, src7, src8); - DUP4_ARG3(__lasx_xvpermi_q, src1, src0, 0x20, src2, src1, 0x20, src3, src2, 0x20, - src4, src3, 0x20, src0, src1, src2, src3); - DUP4_ARG3(__lasx_xvpermi_q, src5, src4, 0x20, src6, src5, 0x20, src7, src6, 0x20, - src8, src7, 0x20, src4, src5, src6, src7); - DUP4_ARG2(__lasx_xvilvl_b, src1, src0, src3, src2, src5, src4, src7, src6, - src0, src2, src4, src6); - DUP4_ARG2(__lasx_xvdp2_h_bu, src0, coeff_vec, src2, coeff_vec, src4, coeff_vec, - src6, coeff_vec, res0, res1, res2, res3); - DUP2_ARG3(__lasx_xvssrarni_bu_h, res1, res0, 6, res3, res2, 6, out0, out1); - __lasx_xvstelm_d(out0, dst, 0, 0); - __lasx_xvstelm_d(out0, dst + stride, 0, 2); - __lasx_xvstelm_d(out0, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out0, dst + stride_3x, 0, 3); - dst += stride_4x; - __lasx_xvstelm_d(out1, dst, 0, 0); - __lasx_xvstelm_d(out1, dst + stride, 0, 2); - __lasx_xvstelm_d(out1, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out1, dst + stride_3x, 0, 3); -} - -static av_always_inline void copy_width8x8_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride) -{ - uint64_t tmp[8]; - ptrdiff_t stride_2, stride_3, stride_4; - __asm__ volatile ( - "slli.d %[stride_2], %[stride], 1 \n\t" - "add.d %[stride_3], %[stride_2], %[stride] \n\t" - "slli.d %[stride_4], %[stride_2], 1 \n\t" - "ld.d %[tmp0], %[src], 0x0 \n\t" - "ldx.d %[tmp1], %[src], %[stride] \n\t" - "ldx.d %[tmp2], %[src], %[stride_2] \n\t" - "ldx.d %[tmp3], %[src], %[stride_3] \n\t" - "add.d %[src], %[src], %[stride_4] \n\t" - "ld.d %[tmp4], %[src], 0x0 \n\t" - "ldx.d %[tmp5], %[src], %[stride] \n\t" - "ldx.d %[tmp6], %[src], %[stride_2] \n\t" - "ldx.d %[tmp7], %[src], %[stride_3] \n\t" - - "st.d %[tmp0], %[dst], 0x0 \n\t" - "stx.d %[tmp1], %[dst], %[stride] \n\t" - "stx.d %[tmp2], %[dst], %[stride_2] \n\t" - "stx.d %[tmp3], %[dst], %[stride_3] \n\t" - "add.d %[dst], %[dst], %[stride_4] \n\t" - "st.d %[tmp4], %[dst], 0x0 \n\t" - "stx.d %[tmp5], %[dst], %[stride] \n\t" - "stx.d %[tmp6], %[dst], %[stride_2] \n\t" - "stx.d %[tmp7], %[dst], %[stride_3] \n\t" - : [tmp0]"=&r"(tmp[0]), [tmp1]"=&r"(tmp[1]), - [tmp2]"=&r"(tmp[2]), [tmp3]"=&r"(tmp[3]), - [tmp4]"=&r"(tmp[4]), [tmp5]"=&r"(tmp[5]), - [tmp6]"=&r"(tmp[6]), [tmp7]"=&r"(tmp[7]), - [dst]"+&r"(dst), [src]"+&r"(src), - [stride_2]"=&r"(stride_2), [stride_3]"=&r"(stride_3), - [stride_4]"=&r"(stride_4) - : [stride]"r"(stride) - : "memory" - ); -} - -static av_always_inline void copy_width8x4_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride) -{ - uint64_t tmp[4]; - ptrdiff_t stride_2, stride_3; - __asm__ volatile ( - "slli.d %[stride_2], %[stride], 1 \n\t" - "add.d %[stride_3], %[stride_2], %[stride] \n\t" - "ld.d %[tmp0], %[src], 0x0 \n\t" - "ldx.d %[tmp1], %[src], %[stride] \n\t" - "ldx.d %[tmp2], %[src], %[stride_2] \n\t" - "ldx.d %[tmp3], %[src], %[stride_3] \n\t" - - "st.d %[tmp0], %[dst], 0x0 \n\t" - "stx.d %[tmp1], %[dst], %[stride] \n\t" - "stx.d %[tmp2], %[dst], %[stride_2] \n\t" - "stx.d %[tmp3], %[dst], %[stride_3] \n\t" - : [tmp0]"=&r"(tmp[0]), [tmp1]"=&r"(tmp[1]), - [tmp2]"=&r"(tmp[2]), [tmp3]"=&r"(tmp[3]), - [stride_2]"=&r"(stride_2), [stride_3]"=&r"(stride_3) - : [stride]"r"(stride), [dst]"r"(dst), [src]"r"(src) - : "memory" - ); -} - -static void avc_chroma_hv_8w_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coef_hor0, uint32_t coef_hor1, - uint32_t coef_ver0, uint32_t coef_ver1, - int32_t height) -{ - if (4 == height) { - avc_chroma_hv_8x4_lasx(src, dst, stride, coef_hor0, coef_hor1, coef_ver0, - coef_ver1); - } else if (8 == height) { - avc_chroma_hv_8x8_lasx(src, dst, stride, coef_hor0, coef_hor1, coef_ver0, - coef_ver1); - } -} - -static void avc_chroma_hv_4x2_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coef_hor0, uint32_t coef_hor1, - uint32_t coef_ver0, uint32_t coef_ver1) -{ - ptrdiff_t stride_2 = stride << 1; - __m256i src0, src1, src2; - __m256i res_hz, res_vt; - __m256i mask; - __m256i coeff_hz_vec0 = __lasx_xvreplgr2vr_b(coef_hor0); - __m256i coeff_hz_vec1 = __lasx_xvreplgr2vr_b(coef_hor1); - __m256i coeff_hz_vec = __lasx_xvilvl_b(coeff_hz_vec0, coeff_hz_vec1); - __m256i coeff_vt_vec0 = __lasx_xvreplgr2vr_h(coef_ver0); - __m256i coeff_vt_vec1 = __lasx_xvreplgr2vr_h(coef_ver1); - __m256i coeff_vt_vec = __lasx_xvpermi_q(coeff_vt_vec1, coeff_vt_vec0, 0x02); - - DUP2_ARG2(__lasx_xvld, chroma_mask_arr, 32, src, 0, mask, src0); - DUP2_ARG2(__lasx_xvldx, src, stride, src, stride_2, src1, src2); - DUP2_ARG3(__lasx_xvshuf_b, src1, src0, mask, src2, src1, mask, src0, src1); - src0 = __lasx_xvpermi_q(src0, src1, 0x02); - res_hz = __lasx_xvdp2_h_bu(src0, coeff_hz_vec); - res_vt = __lasx_xvmul_h(res_hz, coeff_vt_vec); - res_hz = __lasx_xvpermi_q(res_hz, res_vt, 0x01); - res_vt = __lasx_xvadd_h(res_hz, res_vt); - res_vt = __lasx_xvssrarni_bu_h(res_vt, res_vt, 6); - __lasx_xvstelm_w(res_vt, dst, 0, 0); - __lasx_xvstelm_w(res_vt, dst + stride, 0, 1); -} - -static void avc_chroma_hv_4x4_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coef_hor0, uint32_t coef_hor1, - uint32_t coef_ver0, uint32_t coef_ver1) -{ - ptrdiff_t stride_2 = stride << 1; - ptrdiff_t stride_3 = stride_2 + stride; - ptrdiff_t stride_4 = stride_2 << 1; - __m256i src0, src1, src2, src3, src4; - __m256i res_hz0, res_hz1, res_vt0, res_vt1; - __m256i mask; - __m256i coeff_hz_vec0 = __lasx_xvreplgr2vr_b(coef_hor0); - __m256i coeff_hz_vec1 = __lasx_xvreplgr2vr_b(coef_hor1); - __m256i coeff_hz_vec = __lasx_xvilvl_b(coeff_hz_vec0, coeff_hz_vec1); - __m256i coeff_vt_vec0 = __lasx_xvreplgr2vr_h(coef_ver0); - __m256i coeff_vt_vec1 = __lasx_xvreplgr2vr_h(coef_ver1); - - DUP2_ARG2(__lasx_xvld, chroma_mask_arr, 32, src, 0, mask, src0); - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2, src, stride_3, - src, stride_4, src1, src2, src3, src4); - DUP4_ARG3(__lasx_xvshuf_b, src1, src0, mask, src2, src1, mask, src3, src2, mask, - src4, src3, mask, src0, src1, src2, src3); - DUP2_ARG3(__lasx_xvpermi_q, src0, src2, 0x02, src1, src3, 0x02, src0, src1); - DUP2_ARG2(__lasx_xvdp2_h_bu, src0, coeff_hz_vec, src1, coeff_hz_vec, res_hz0, res_hz1); - DUP2_ARG2(__lasx_xvmul_h, res_hz0, coeff_vt_vec1, res_hz1, coeff_vt_vec0, res_vt0, res_vt1); - res_hz0 = __lasx_xvadd_h(res_vt0, res_vt1); - res_hz0 = __lasx_xvssrarni_bu_h(res_hz0, res_hz0, 6); - __lasx_xvstelm_w(res_hz0, dst, 0, 0); - __lasx_xvstelm_w(res_hz0, dst + stride, 0, 1); - __lasx_xvstelm_w(res_hz0, dst + stride_2, 0, 4); - __lasx_xvstelm_w(res_hz0, dst + stride_3, 0, 5); -} - -static void avc_chroma_hv_4x8_lasx(const uint8_t *src, uint8_t * dst, ptrdiff_t stride, - uint32_t coef_hor0, uint32_t coef_hor1, - uint32_t coef_ver0, uint32_t coef_ver1) -{ - ptrdiff_t stride_2 = stride << 1; - ptrdiff_t stride_3 = stride_2 + stride; - ptrdiff_t stride_4 = stride_2 << 1; - __m256i src0, src1, src2, src3, src4, src5, src6, src7, src8; - __m256i res_hz0, res_hz1, res_hz2, res_hz3; - __m256i res_vt0, res_vt1, res_vt2, res_vt3; - __m256i mask; - __m256i coeff_hz_vec0 = __lasx_xvreplgr2vr_b(coef_hor0); - __m256i coeff_hz_vec1 = __lasx_xvreplgr2vr_b(coef_hor1); - __m256i coeff_hz_vec = __lasx_xvilvl_b(coeff_hz_vec0, coeff_hz_vec1); - __m256i coeff_vt_vec0 = __lasx_xvreplgr2vr_h(coef_ver0); - __m256i coeff_vt_vec1 = __lasx_xvreplgr2vr_h(coef_ver1); - - DUP2_ARG2(__lasx_xvld, chroma_mask_arr, 32, src, 0, mask, src0); - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2, src, stride_3, - src, stride_4, src1, src2, src3, src4); - src += stride_4; - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2, src, stride_3, - src, stride_4, src5, src6, src7, src8); - DUP4_ARG3(__lasx_xvshuf_b, src1, src0, mask, src2, src1, mask, src3, src2, mask, - src4, src3, mask, src0, src1, src2, src3); - DUP4_ARG3(__lasx_xvshuf_b, src5, src4, mask, src6, src5, mask, src7, src6, mask, - src8, src7, mask, src4, src5, src6, src7); - DUP4_ARG3(__lasx_xvpermi_q, src0, src2, 0x02, src1, src3, 0x02, src4, src6, 0x02, - src5, src7, 0x02, src0, src1, src4, src5); - DUP4_ARG2(__lasx_xvdp2_h_bu, src0, coeff_hz_vec, src1, coeff_hz_vec, src4, coeff_hz_vec, - src5, coeff_hz_vec, res_hz0, res_hz1, res_hz2, res_hz3); - DUP4_ARG2(__lasx_xvmul_h, res_hz0, coeff_vt_vec1, res_hz1, coeff_vt_vec0, res_hz2, - coeff_vt_vec1, res_hz3, coeff_vt_vec0, res_vt0, res_vt1, res_vt2, res_vt3); - DUP2_ARG2(__lasx_xvadd_h, res_vt0, res_vt1, res_vt2, res_vt3, res_vt0, res_vt2); - res_hz0 = __lasx_xvssrarni_bu_h(res_vt2, res_vt0, 6); - __lasx_xvstelm_w(res_hz0, dst, 0, 0); - __lasx_xvstelm_w(res_hz0, dst + stride, 0, 1); - __lasx_xvstelm_w(res_hz0, dst + stride_2, 0, 4); - __lasx_xvstelm_w(res_hz0, dst + stride_3, 0, 5); - dst += stride_4; - __lasx_xvstelm_w(res_hz0, dst, 0, 2); - __lasx_xvstelm_w(res_hz0, dst + stride, 0, 3); - __lasx_xvstelm_w(res_hz0, dst + stride_2, 0, 6); - __lasx_xvstelm_w(res_hz0, dst + stride_3, 0, 7); -} - -static void avc_chroma_hv_4w_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coef_hor0, uint32_t coef_hor1, - uint32_t coef_ver0, uint32_t coef_ver1, - int32_t height) -{ - if (8 == height) { - avc_chroma_hv_4x8_lasx(src, dst, stride, coef_hor0, coef_hor1, coef_ver0, - coef_ver1); - } else if (4 == height) { - avc_chroma_hv_4x4_lasx(src, dst, stride, coef_hor0, coef_hor1, coef_ver0, - coef_ver1); - } else if (2 == height) { - avc_chroma_hv_4x2_lasx(src, dst, stride, coef_hor0, coef_hor1, coef_ver0, - coef_ver1); - } -} - -static void avc_chroma_hz_4x2_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coeff0, uint32_t coeff1) -{ - __m256i src0, src1; - __m256i res, mask; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - DUP2_ARG2(__lasx_xvld, chroma_mask_arr, 32, src, 0, mask, src0); - src1 = __lasx_xvldx(src, stride); - src0 = __lasx_xvshuf_b(src1, src0, mask); - res = __lasx_xvdp2_h_bu(src0, coeff_vec); - res = __lasx_xvslli_h(res, 3); - res = __lasx_xvssrarni_bu_h(res, res, 6); - __lasx_xvstelm_w(res, dst, 0, 0); - __lasx_xvstelm_w(res, dst + stride, 0, 1); -} - -static void avc_chroma_hz_4x4_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coeff0, uint32_t coeff1) -{ - ptrdiff_t stride_2 = stride << 1; - ptrdiff_t stride_3 = stride_2 + stride; - __m256i src0, src1, src2, src3; - __m256i res, mask; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - DUP2_ARG2(__lasx_xvld, chroma_mask_arr, 32, src, 0, mask, src0); - DUP2_ARG2(__lasx_xvldx, src, stride, src, stride_2, src1, src2); - src3 = __lasx_xvldx(src, stride_3); - DUP2_ARG3(__lasx_xvshuf_b, src1, src0, mask, src3, src2, mask, src0, src2); - src0 = __lasx_xvpermi_q(src0, src2, 0x02); - res = __lasx_xvdp2_h_bu(src0, coeff_vec); - res = __lasx_xvslli_h(res, 3); - res = __lasx_xvssrarni_bu_h(res, res, 6); - __lasx_xvstelm_w(res, dst, 0, 0); - __lasx_xvstelm_w(res, dst + stride, 0, 1); - __lasx_xvstelm_w(res, dst + stride_2, 0, 4); - __lasx_xvstelm_w(res, dst + stride_3, 0, 5); -} - -static void avc_chroma_hz_4x8_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coeff0, uint32_t coeff1) -{ - ptrdiff_t stride_2 = stride << 1; - ptrdiff_t stride_3 = stride_2 + stride; - ptrdiff_t stride_4 = stride_2 << 1; - __m256i src0, src1, src2, src3, src4, src5, src6, src7; - __m256i res0, res1, mask; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - coeff_vec = __lasx_xvslli_b(coeff_vec, 3); - DUP2_ARG2(__lasx_xvld, chroma_mask_arr, 32, src, 0, mask, src0); - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2, src, stride_3, - src, stride_4, src1, src2, src3, src4); - src += stride_4; - DUP2_ARG2(__lasx_xvldx, src, stride, src, stride_2, src5, src6); - src7 = __lasx_xvldx(src, stride_3); - DUP4_ARG3(__lasx_xvshuf_b, src1, src0, mask, src3, src2, mask, src5, src4, mask, - src7, src6, mask, src0, src2, src4, src6); - DUP2_ARG3(__lasx_xvpermi_q, src0, src2, 0x02, src4, src6, 0x02, src0, src4); - DUP2_ARG2(__lasx_xvdp2_h_bu, src0, coeff_vec, src4, coeff_vec, res0, res1); - res0 = __lasx_xvssrarni_bu_h(res1, res0, 6); - __lasx_xvstelm_w(res0, dst, 0, 0); - __lasx_xvstelm_w(res0, dst + stride, 0, 1); - __lasx_xvstelm_w(res0, dst + stride_2, 0, 4); - __lasx_xvstelm_w(res0, dst + stride_3, 0, 5); - dst += stride_4; - __lasx_xvstelm_w(res0, dst, 0, 2); - __lasx_xvstelm_w(res0, dst + stride, 0, 3); - __lasx_xvstelm_w(res0, dst + stride_2, 0, 6); - __lasx_xvstelm_w(res0, dst + stride_3, 0, 7); -} - -static void avc_chroma_hz_4w_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coeff0, uint32_t coeff1, - int32_t height) -{ - if (8 == height) { - avc_chroma_hz_4x8_lasx(src, dst, stride, coeff0, coeff1); - } else if (4 == height) { - avc_chroma_hz_4x4_lasx(src, dst, stride, coeff0, coeff1); - } else if (2 == height) { - avc_chroma_hz_4x2_lasx(src, dst, stride, coeff0, coeff1); - } -} - -static void avc_chroma_hz_8w_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coeff0, uint32_t coeff1, - int32_t height) -{ - if (4 == height) { - avc_chroma_hz_8x4_lasx(src, dst, stride, coeff0, coeff1); - } else if (8 == height) { - avc_chroma_hz_8x8_lasx(src, dst, stride, coeff0, coeff1); - } else { - avc_chroma_hz_nonmult_lasx(src, dst, stride, coeff0, coeff1, height); - } -} - -static void avc_chroma_vt_4x2_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coeff0, uint32_t coeff1) -{ - __m256i src0, src1, src2; - __m256i tmp0, tmp1; - __m256i res; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - src0 = __lasx_xvld(src, 0); - DUP2_ARG2(__lasx_xvldx, src, stride, src, stride << 1, src1, src2); - DUP2_ARG2(__lasx_xvilvl_b, src1, src0, src2, src1, tmp0, tmp1); - tmp0 = __lasx_xvilvl_d(tmp1, tmp0); - res = __lasx_xvdp2_h_bu(tmp0, coeff_vec); - res = __lasx_xvslli_h(res, 3); - res = __lasx_xvssrarni_bu_h(res, res, 6); - __lasx_xvstelm_w(res, dst, 0, 0); - __lasx_xvstelm_w(res, dst + stride, 0, 1); -} - -static void avc_chroma_vt_4x4_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coeff0, uint32_t coeff1) -{ - ptrdiff_t stride_2 = stride << 1; - ptrdiff_t stride_3 = stride_2 + stride; - ptrdiff_t stride_4 = stride_2 << 1; - __m256i src0, src1, src2, src3, src4; - __m256i tmp0, tmp1, tmp2, tmp3; - __m256i res; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - src0 = __lasx_xvld(src, 0); - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2, src, stride_3, - src, stride_4, src1, src2, src3, src4); - DUP4_ARG2(__lasx_xvilvl_b, src1, src0, src2, src1, src3, src2, src4, src3, - tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp2); - tmp0 = __lasx_xvpermi_q(tmp0, tmp2, 0x02); - res = __lasx_xvdp2_h_bu(tmp0, coeff_vec); - res = __lasx_xvslli_h(res, 3); - res = __lasx_xvssrarni_bu_h(res, res, 6); - __lasx_xvstelm_w(res, dst, 0, 0); - __lasx_xvstelm_w(res, dst + stride, 0, 1); - __lasx_xvstelm_w(res, dst + stride_2, 0, 4); - __lasx_xvstelm_w(res, dst + stride_3, 0, 5); -} - -static void avc_chroma_vt_4x8_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coeff0, uint32_t coeff1) -{ - ptrdiff_t stride_2 = stride << 1; - ptrdiff_t stride_3 = stride_2 + stride; - ptrdiff_t stride_4 = stride_2 << 1; - __m256i src0, src1, src2, src3, src4, src5, src6, src7, src8; - __m256i tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; - __m256i res0, res1; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - coeff_vec = __lasx_xvslli_b(coeff_vec, 3); - src0 = __lasx_xvld(src, 0); - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2, src, stride_3, - src, stride_4, src1, src2, src3, src4); - src += stride_4; - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2, src, stride_3, - src, stride_4, src5, src6, src7, src8); - DUP4_ARG2(__lasx_xvilvl_b, src1, src0, src2, src1, src3, src2, src4, src3, - tmp0, tmp1, tmp2, tmp3); - DUP4_ARG2(__lasx_xvilvl_b, src5, src4, src6, src5, src7, src6, src8, src7, - tmp4, tmp5, tmp6, tmp7); - DUP4_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp5, tmp4, tmp7, tmp6, - tmp0, tmp2, tmp4, tmp6); - tmp0 = __lasx_xvpermi_q(tmp0, tmp2, 0x02); - tmp4 = __lasx_xvpermi_q(tmp4, tmp6, 0x02); - DUP2_ARG2(__lasx_xvdp2_h_bu, tmp0, coeff_vec, tmp4, coeff_vec, res0, res1); - res0 = __lasx_xvssrarni_bu_h(res1, res0, 6); - __lasx_xvstelm_w(res0, dst, 0, 0); - __lasx_xvstelm_w(res0, dst + stride, 0, 1); - __lasx_xvstelm_w(res0, dst + stride_2, 0, 4); - __lasx_xvstelm_w(res0, dst + stride_3, 0, 5); - dst += stride_4; - __lasx_xvstelm_w(res0, dst, 0, 2); - __lasx_xvstelm_w(res0, dst + stride, 0, 3); - __lasx_xvstelm_w(res0, dst + stride_2, 0, 6); - __lasx_xvstelm_w(res0, dst + stride_3, 0, 7); -} - -static void avc_chroma_vt_4w_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coeff0, uint32_t coeff1, - int32_t height) -{ - if (8 == height) { - avc_chroma_vt_4x8_lasx(src, dst, stride, coeff0, coeff1); - } else if (4 == height) { - avc_chroma_vt_4x4_lasx(src, dst, stride, coeff0, coeff1); - } else if (2 == height) { - avc_chroma_vt_4x2_lasx(src, dst, stride, coeff0, coeff1); - } -} - -static void avc_chroma_vt_8w_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coeff0, uint32_t coeff1, - int32_t height) -{ - if (4 == height) { - avc_chroma_vt_8x4_lasx(src, dst, stride, coeff0, coeff1); - } else if (8 == height) { - avc_chroma_vt_8x8_lasx(src, dst, stride, coeff0, coeff1); - } -} - -static void copy_width4_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - int32_t height) -{ - uint32_t tp0, tp1, tp2, tp3, tp4, tp5, tp6, tp7; - - if (8 == height) { - ptrdiff_t stride_2, stride_3, stride_4; - - __asm__ volatile ( - "slli.d %[stride_2], %[stride], 1 \n\t" - "add.d %[stride_3], %[stride_2], %[stride] \n\t" - "slli.d %[stride_4], %[stride_2], 1 \n\t" - "ld.wu %[tp0], %[src], 0 \n\t" - "ldx.wu %[tp1], %[src], %[stride] \n\t" - "ldx.wu %[tp2], %[src], %[stride_2] \n\t" - "ldx.wu %[tp3], %[src], %[stride_3] \n\t" - "add.d %[src], %[src], %[stride_4] \n\t" - "ld.wu %[tp4], %[src], 0 \n\t" - "ldx.wu %[tp5], %[src], %[stride] \n\t" - "ldx.wu %[tp6], %[src], %[stride_2] \n\t" - "ldx.wu %[tp7], %[src], %[stride_3] \n\t" - "st.w %[tp0], %[dst], 0 \n\t" - "stx.w %[tp1], %[dst], %[stride] \n\t" - "stx.w %[tp2], %[dst], %[stride_2] \n\t" - "stx.w %[tp3], %[dst], %[stride_3] \n\t" - "add.d %[dst], %[dst], %[stride_4] \n\t" - "st.w %[tp4], %[dst], 0 \n\t" - "stx.w %[tp5], %[dst], %[stride] \n\t" - "stx.w %[tp6], %[dst], %[stride_2] \n\t" - "stx.w %[tp7], %[dst], %[stride_3] \n\t" - : [stride_2]"+&r"(stride_2), [stride_3]"+&r"(stride_3), [stride_4]"+&r"(stride_4), - [src]"+&r"(src), [dst]"+&r"(dst), [tp0]"+&r"(tp0), [tp1]"+&r"(tp1), - [tp2]"+&r"(tp2), [tp3]"+&r"(tp3), [tp4]"+&r"(tp4), [tp5]"+&r"(tp5), - [tp6]"+&r"(tp6), [tp7]"+&r"(tp7) - : [stride]"r"(stride) - : "memory" - ); - } else if (4 == height) { - ptrdiff_t stride_2, stride_3; - - __asm__ volatile ( - "slli.d %[stride_2], %[stride], 1 \n\t" - "add.d %[stride_3], %[stride_2], %[stride] \n\t" - "ld.wu %[tp0], %[src], 0 \n\t" - "ldx.wu %[tp1], %[src], %[stride] \n\t" - "ldx.wu %[tp2], %[src], %[stride_2] \n\t" - "ldx.wu %[tp3], %[src], %[stride_3] \n\t" - "st.w %[tp0], %[dst], 0 \n\t" - "stx.w %[tp1], %[dst], %[stride] \n\t" - "stx.w %[tp2], %[dst], %[stride_2] \n\t" - "stx.w %[tp3], %[dst], %[stride_3] \n\t" - : [stride_2]"+&r"(stride_2), [stride_3]"+&r"(stride_3), - [src]"+&r"(src), [dst]"+&r"(dst), [tp0]"+&r"(tp0), [tp1]"+&r"(tp1), - [tp2]"+&r"(tp2), [tp3]"+&r"(tp3) - : [stride]"r"(stride) - : "memory" - ); - } else if (2 == height) { - __asm__ volatile ( - "ld.wu %[tp0], %[src], 0 \n\t" - "ldx.wu %[tp1], %[src], %[stride] \n\t" - "st.w %[tp0], %[dst], 0 \n\t" - "stx.w %[tp1], %[dst], %[stride] \n\t" - : [tp0]"+&r"(tp0), [tp1]"+&r"(tp1) - : [src]"r"(src), [dst]"r"(dst), [stride]"r"(stride) - : "memory" - ); - } -} - -static void copy_width8_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - int32_t height) -{ - if (8 == height) { - copy_width8x8_lasx(src, dst, stride); - } else if (4 == height) { - copy_width8x4_lasx(src, dst, stride); - } -} - -void ff_put_h264_chroma_mc4_lasx(uint8_t *dst, const uint8_t *src, ptrdiff_t stride, - int height, int x, int y) -{ - av_assert2(x < 8 && y < 8 && x >= 0 && y >= 0); - - if(x && y) { - avc_chroma_hv_4w_lasx(src, dst, stride, x, (8 - x), y, (8 - y), height); - } else if (x) { - avc_chroma_hz_4w_lasx(src, dst, stride, x, (8 - x), height); - } else if (y) { - avc_chroma_vt_4w_lasx(src, dst, stride, y, (8 - y), height); - } else { - copy_width4_lasx(src, dst, stride, height); - } -} - -void ff_put_h264_chroma_mc8_lasx(uint8_t *dst, const uint8_t *src, ptrdiff_t stride, - int height, int x, int y) -{ - av_assert2(x < 8 && y < 8 && x >= 0 && y >= 0); - - if (!(x || y)) { - copy_width8_lasx(src, dst, stride, height); - } else if (x && y) { - avc_chroma_hv_8w_lasx(src, dst, stride, x, (8 - x), y, (8 - y), height); - } else if (x) { - avc_chroma_hz_8w_lasx(src, dst, stride, x, (8 - x), height); - } else { - avc_chroma_vt_8w_lasx(src, dst, stride, y, (8 - y), height); - } -} - -static av_always_inline void avc_chroma_hv_and_aver_dst_8x4_lasx(const uint8_t *src, - uint8_t *dst, ptrdiff_t stride, uint32_t coef_hor0, - uint32_t coef_hor1, uint32_t coef_ver0, - uint32_t coef_ver1) -{ - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - ptrdiff_t stride_4x = stride << 2; - __m256i tp0, tp1, tp2, tp3; - __m256i src0, src1, src2, src3, src4, out; - __m256i res_hz0, res_hz1, res_hz2, res_vt0, res_vt1; - __m256i mask; - __m256i coeff_hz_vec0 = __lasx_xvreplgr2vr_b(coef_hor0); - __m256i coeff_hz_vec1 = __lasx_xvreplgr2vr_b(coef_hor1); - __m256i coeff_hz_vec = __lasx_xvilvl_b(coeff_hz_vec0, coeff_hz_vec1); - __m256i coeff_vt_vec0 = __lasx_xvreplgr2vr_h(coef_ver0); - __m256i coeff_vt_vec1 = __lasx_xvreplgr2vr_h(coef_ver1); - - DUP2_ARG2(__lasx_xvld, chroma_mask_arr, 0, src, 0, mask, src0); - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2x, src, stride_3x, src, stride_4x, - src1, src2, src3, src4); - DUP2_ARG3(__lasx_xvpermi_q, src2, src1, 0x20, src4, src3, 0x20, src1, src3); - src0 = __lasx_xvshuf_b(src0, src0, mask); - DUP2_ARG3(__lasx_xvshuf_b, src1, src1, mask, src3, src3, mask, src1, src3); - DUP2_ARG2(__lasx_xvdp2_h_bu, src0, coeff_hz_vec, src1, coeff_hz_vec, res_hz0, res_hz1); - res_hz2 = __lasx_xvdp2_h_bu(src3, coeff_hz_vec); - res_vt0 = __lasx_xvmul_h(res_hz1, coeff_vt_vec0); - res_vt1 = __lasx_xvmul_h(res_hz2, coeff_vt_vec0); - res_hz0 = __lasx_xvpermi_q(res_hz1, res_hz0, 0x20); - res_hz1 = __lasx_xvpermi_q(res_hz1, res_hz2, 0x3); - res_vt0 = __lasx_xvmadd_h(res_vt0, res_hz0, coeff_vt_vec1); - res_vt1 = __lasx_xvmadd_h(res_vt1, res_hz1, coeff_vt_vec1); - out = __lasx_xvssrarni_bu_h(res_vt1, res_vt0, 6); - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, dst, stride_3x, - tp0, tp1, tp2, tp3); - DUP2_ARG2(__lasx_xvilvl_d, tp2, tp0, tp3, tp1, tp0, tp2); - tp0 = __lasx_xvpermi_q(tp2, tp0, 0x20); - out = __lasx_xvavgr_bu(out, tp0); - __lasx_xvstelm_d(out, dst, 0, 0); - __lasx_xvstelm_d(out, dst + stride, 0, 2); - __lasx_xvstelm_d(out, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out, dst + stride_3x, 0, 3); -} - -static av_always_inline void avc_chroma_hv_and_aver_dst_8x8_lasx(const uint8_t *src, - uint8_t *dst, ptrdiff_t stride, uint32_t coef_hor0, - uint32_t coef_hor1, uint32_t coef_ver0, - uint32_t coef_ver1) -{ - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - ptrdiff_t stride_4x = stride << 2; - __m256i tp0, tp1, tp2, tp3, dst0, dst1; - __m256i src0, src1, src2, src3, src4, src5, src6, src7, src8; - __m256i out0, out1; - __m256i res_hz0, res_hz1, res_hz2, res_hz3, res_hz4; - __m256i res_vt0, res_vt1, res_vt2, res_vt3; - __m256i mask; - __m256i coeff_hz_vec0 = __lasx_xvreplgr2vr_b(coef_hor0); - __m256i coeff_hz_vec1 = __lasx_xvreplgr2vr_b(coef_hor1); - __m256i coeff_vt_vec0 = __lasx_xvreplgr2vr_h(coef_ver0); - __m256i coeff_vt_vec1 = __lasx_xvreplgr2vr_h(coef_ver1); - __m256i coeff_hz_vec = __lasx_xvilvl_b(coeff_hz_vec0, coeff_hz_vec1); - - DUP2_ARG2(__lasx_xvld, chroma_mask_arr, 0, src, 0, mask, src0); - src += stride; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, src, stride_3x, - src1, src2, src3, src4); - src += stride_4x; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, src, stride_3x, - src5, src6, src7, src8); - DUP4_ARG3(__lasx_xvpermi_q, src2, src1, 0x20, src4, src3, 0x20, src6, src5, 0x20, - src8, src7, 0x20, src1, src3, src5, src7); - src0 = __lasx_xvshuf_b(src0, src0, mask); - DUP4_ARG3(__lasx_xvshuf_b, src1, src1, mask, src3, src3, mask, src5, src5, mask, src7, - src7, mask, src1, src3, src5, src7); - DUP4_ARG2(__lasx_xvdp2_h_bu, src0, coeff_hz_vec, src1, coeff_hz_vec, src3, - coeff_hz_vec, src5, coeff_hz_vec, res_hz0, res_hz1, res_hz2, res_hz3); - res_hz4 = __lasx_xvdp2_h_bu(src7, coeff_hz_vec); - res_vt0 = __lasx_xvmul_h(res_hz1, coeff_vt_vec0); - res_vt1 = __lasx_xvmul_h(res_hz2, coeff_vt_vec0); - res_vt2 = __lasx_xvmul_h(res_hz3, coeff_vt_vec0); - res_vt3 = __lasx_xvmul_h(res_hz4, coeff_vt_vec0); - res_hz0 = __lasx_xvpermi_q(res_hz1, res_hz0, 0x20); - res_hz1 = __lasx_xvpermi_q(res_hz1, res_hz2, 0x3); - res_hz2 = __lasx_xvpermi_q(res_hz2, res_hz3, 0x3); - res_hz3 = __lasx_xvpermi_q(res_hz3, res_hz4, 0x3); - res_vt0 = __lasx_xvmadd_h(res_vt0, res_hz0, coeff_vt_vec1); - res_vt1 = __lasx_xvmadd_h(res_vt1, res_hz1, coeff_vt_vec1); - res_vt2 = __lasx_xvmadd_h(res_vt2, res_hz2, coeff_vt_vec1); - res_vt3 = __lasx_xvmadd_h(res_vt3, res_hz3, coeff_vt_vec1); - DUP2_ARG3(__lasx_xvssrarni_bu_h, res_vt1, res_vt0, 6, res_vt3, res_vt2, 6, - out0, out1); - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, dst, stride_3x, - tp0, tp1, tp2, tp3); - DUP2_ARG2(__lasx_xvilvl_d, tp2, tp0, tp3, tp1, tp0, tp2); - dst0 = __lasx_xvpermi_q(tp2, tp0, 0x20); - dst += stride_4x; - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, dst, stride_3x, - tp0, tp1, tp2, tp3); - dst -= stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tp2, tp0, tp3, tp1, tp0, tp2); - dst1 = __lasx_xvpermi_q(tp2, tp0, 0x20); - out0 = __lasx_xvavgr_bu(out0, dst0); - out1 = __lasx_xvavgr_bu(out1, dst1); - __lasx_xvstelm_d(out0, dst, 0, 0); - __lasx_xvstelm_d(out0, dst + stride, 0, 2); - __lasx_xvstelm_d(out0, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out0, dst + stride_3x, 0, 3); - dst += stride_4x; - __lasx_xvstelm_d(out1, dst, 0, 0); - __lasx_xvstelm_d(out1, dst + stride, 0, 2); - __lasx_xvstelm_d(out1, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out1, dst + stride_3x, 0, 3); -} - -static av_always_inline void avc_chroma_hz_and_aver_dst_8x4_lasx(const uint8_t *src, - uint8_t *dst, ptrdiff_t stride, uint32_t coeff0, - uint32_t coeff1) -{ - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - __m256i tp0, tp1, tp2, tp3; - __m256i src0, src1, src2, src3, out; - __m256i res0, res1; - __m256i mask; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - coeff_vec = __lasx_xvslli_b(coeff_vec, 3); - mask = __lasx_xvld(chroma_mask_arr, 0); - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, src, stride_3x, - src0, src1, src2, src3); - DUP2_ARG3(__lasx_xvpermi_q, src1, src0, 0x20, src3, src2, 0x20, src0, src2); - DUP2_ARG3(__lasx_xvshuf_b, src0, src0, mask, src2, src2, mask, src0, src2); - DUP2_ARG2(__lasx_xvdp2_h_bu, src0, coeff_vec, src2, coeff_vec, res0, res1); - out = __lasx_xvssrarni_bu_h(res1, res0, 6); - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, dst, stride_3x, - tp0, tp1, tp2, tp3); - DUP2_ARG2(__lasx_xvilvl_d, tp2, tp0, tp3, tp1, tp0, tp2); - tp0 = __lasx_xvpermi_q(tp2, tp0, 0x20); - out = __lasx_xvavgr_bu(out, tp0); - __lasx_xvstelm_d(out, dst, 0, 0); - __lasx_xvstelm_d(out, dst + stride, 0, 2); - __lasx_xvstelm_d(out, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out, dst + stride_3x, 0, 3); -} - -static av_always_inline void avc_chroma_hz_and_aver_dst_8x8_lasx(const uint8_t *src, - uint8_t *dst, ptrdiff_t stride, uint32_t coeff0, - uint32_t coeff1) -{ - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - ptrdiff_t stride_4x = stride << 2; - __m256i tp0, tp1, tp2, tp3, dst0, dst1; - __m256i src0, src1, src2, src3, src4, src5, src6, src7; - __m256i out0, out1; - __m256i res0, res1, res2, res3; - __m256i mask; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - coeff_vec = __lasx_xvslli_b(coeff_vec, 3); - mask = __lasx_xvld(chroma_mask_arr, 0); - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, src, stride_3x, - src0, src1, src2, src3); - src += stride_4x; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, src, stride_3x, - src4, src5, src6, src7); - DUP4_ARG3(__lasx_xvpermi_q, src1, src0, 0x20, src3, src2, 0x20, src5, src4, 0x20, - src7, src6, 0x20, src0, src2, src4, src6); - DUP4_ARG3(__lasx_xvshuf_b, src0, src0, mask, src2, src2, mask, src4, src4, - mask, src6, src6, mask, src0, src2, src4, src6); - DUP4_ARG2(__lasx_xvdp2_h_bu, src0, coeff_vec, src2, coeff_vec, src4, coeff_vec, src6, - coeff_vec, res0, res1, res2, res3); - DUP2_ARG3(__lasx_xvssrarni_bu_h, res1, res0, 6, res3, res2, 6, out0, out1); - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, dst, stride_3x, - tp0, tp1, tp2, tp3); - DUP2_ARG2(__lasx_xvilvl_d, tp2, tp0, tp3, tp1, tp0, tp2); - dst0 = __lasx_xvpermi_q(tp2, tp0, 0x20); - dst += stride_4x; - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, dst, stride_3x, - tp0, tp1, tp2, tp3); - dst -= stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tp2, tp0, tp3, tp1, tp0, tp2); - dst1 = __lasx_xvpermi_q(tp2, tp0, 0x20); - out0 = __lasx_xvavgr_bu(out0, dst0); - out1 = __lasx_xvavgr_bu(out1, dst1); - __lasx_xvstelm_d(out0, dst, 0, 0); - __lasx_xvstelm_d(out0, dst + stride, 0, 2); - __lasx_xvstelm_d(out0, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out0, dst + stride_3x, 0, 3); - dst += stride_4x; - __lasx_xvstelm_d(out1, dst, 0, 0); - __lasx_xvstelm_d(out1, dst + stride, 0, 2); - __lasx_xvstelm_d(out1, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out1, dst + stride_3x, 0, 3); -} - -static av_always_inline void avc_chroma_vt_and_aver_dst_8x4_lasx(const uint8_t *src, - uint8_t *dst, ptrdiff_t stride, uint32_t coeff0, - uint32_t coeff1) -{ - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - ptrdiff_t stride_4x = stride << 2; - __m256i tp0, tp1, tp2, tp3; - __m256i src0, src1, src2, src3, src4, out; - __m256i res0, res1; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - coeff_vec = __lasx_xvslli_b(coeff_vec, 3); - src0 = __lasx_xvld(src, 0); - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2x, src, stride_3x, src, stride_4x, - src1, src2, src3, src4); - DUP4_ARG3(__lasx_xvpermi_q, src1, src0, 0x20, src2, src1, 0x20, src3, src2, 0x20, - src4, src3, 0x20, src0, src1, src2, src3); - DUP2_ARG2(__lasx_xvilvl_b, src1, src0, src3, src2, src0, src2); - DUP2_ARG2(__lasx_xvdp2_h_bu, src0, coeff_vec, src2, coeff_vec, res0, res1); - out = __lasx_xvssrarni_bu_h(res1, res0, 6); - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, dst, stride_3x, - tp0, tp1, tp2, tp3); - DUP2_ARG2(__lasx_xvilvl_d, tp2, tp0, tp3, tp1, tp0, tp2); - tp0 = __lasx_xvpermi_q(tp2, tp0, 0x20); - out = __lasx_xvavgr_bu(out, tp0); - __lasx_xvstelm_d(out, dst, 0, 0); - __lasx_xvstelm_d(out, dst + stride, 0, 2); - __lasx_xvstelm_d(out, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out, dst + stride_3x, 0, 3); -} - -static av_always_inline void avc_chroma_vt_and_aver_dst_8x8_lasx(const uint8_t *src, - uint8_t *dst, ptrdiff_t stride, uint32_t coeff0, - uint32_t coeff1) -{ - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - ptrdiff_t stride_4x = stride << 2; - __m256i tp0, tp1, tp2, tp3, dst0, dst1; - __m256i src0, src1, src2, src3, src4, src5, src6, src7, src8; - __m256i out0, out1; - __m256i res0, res1, res2, res3; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - coeff_vec = __lasx_xvslli_b(coeff_vec, 3); - src0 = __lasx_xvld(src, 0); - src += stride; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, src, stride_3x, - src1, src2, src3, src4); - src += stride_4x; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, src, stride_3x, - src5, src6, src7, src8); - DUP4_ARG3(__lasx_xvpermi_q, src1, src0, 0x20, src2, src1, 0x20, src3, src2, 0x20, - src4, src3, 0x20, src0, src1, src2, src3); - DUP4_ARG3(__lasx_xvpermi_q, src5, src4, 0x20, src6, src5, 0x20, src7, src6, 0x20, - src8, src7, 0x20, src4, src5, src6, src7); - DUP4_ARG2(__lasx_xvilvl_b, src1, src0, src3, src2, src5, src4, src7, src6, - src0, src2, src4, src6); - DUP4_ARG2(__lasx_xvdp2_h_bu, src0, coeff_vec, src2, coeff_vec, src4, coeff_vec, src6, - coeff_vec, res0, res1, res2, res3); - DUP2_ARG3(__lasx_xvssrarni_bu_h, res1, res0, 6, res3, res2, 6, out0, out1); - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, dst, stride_3x, - tp0, tp1, tp2, tp3); - DUP2_ARG2(__lasx_xvilvl_d, tp2, tp0, tp3, tp1, tp0, tp2); - dst0 = __lasx_xvpermi_q(tp2, tp0, 0x20); - dst += stride_4x; - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, dst, stride_3x, - tp0, tp1, tp2, tp3); - dst -= stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tp2, tp0, tp3, tp1, tp0, tp2); - dst1 = __lasx_xvpermi_q(tp2, tp0, 0x20); - out0 = __lasx_xvavgr_bu(out0, dst0); - out1 = __lasx_xvavgr_bu(out1, dst1); - __lasx_xvstelm_d(out0, dst, 0, 0); - __lasx_xvstelm_d(out0, dst + stride, 0, 2); - __lasx_xvstelm_d(out0, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out0, dst + stride_3x, 0, 3); - dst += stride_4x; - __lasx_xvstelm_d(out1, dst, 0, 0); - __lasx_xvstelm_d(out1, dst + stride, 0, 2); - __lasx_xvstelm_d(out1, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out1, dst + stride_3x, 0, 3); -} - -static av_always_inline void avg_width8x8_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride) -{ - __m256i src0, src1, src2, src3; - __m256i dst0, dst1, dst2, dst3; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - ptrdiff_t stride_4x = stride << 2; - - src0 = __lasx_xvldrepl_d(src, 0); - src1 = __lasx_xvldrepl_d(src + stride, 0); - src2 = __lasx_xvldrepl_d(src + stride_2x, 0); - src3 = __lasx_xvldrepl_d(src + stride_3x, 0); - dst0 = __lasx_xvldrepl_d(dst, 0); - dst1 = __lasx_xvldrepl_d(dst + stride, 0); - dst2 = __lasx_xvldrepl_d(dst + stride_2x, 0); - dst3 = __lasx_xvldrepl_d(dst + stride_3x, 0); - src0 = __lasx_xvpackev_d(src1,src0); - src2 = __lasx_xvpackev_d(src3,src2); - src0 = __lasx_xvpermi_q(src0, src2, 0x02); - dst0 = __lasx_xvpackev_d(dst1,dst0); - dst2 = __lasx_xvpackev_d(dst3,dst2); - dst0 = __lasx_xvpermi_q(dst0, dst2, 0x02); - dst0 = __lasx_xvavgr_bu(src0, dst0); - __lasx_xvstelm_d(dst0, dst, 0, 0); - __lasx_xvstelm_d(dst0, dst + stride, 0, 1); - __lasx_xvstelm_d(dst0, dst + stride_2x, 0, 2); - __lasx_xvstelm_d(dst0, dst + stride_3x, 0, 3); - - src += stride_4x; - dst += stride_4x; - src0 = __lasx_xvldrepl_d(src, 0); - src1 = __lasx_xvldrepl_d(src + stride, 0); - src2 = __lasx_xvldrepl_d(src + stride_2x, 0); - src3 = __lasx_xvldrepl_d(src + stride_3x, 0); - dst0 = __lasx_xvldrepl_d(dst, 0); - dst1 = __lasx_xvldrepl_d(dst + stride, 0); - dst2 = __lasx_xvldrepl_d(dst + stride_2x, 0); - dst3 = __lasx_xvldrepl_d(dst + stride_3x, 0); - src0 = __lasx_xvpackev_d(src1,src0); - src2 = __lasx_xvpackev_d(src3,src2); - src0 = __lasx_xvpermi_q(src0, src2, 0x02); - dst0 = __lasx_xvpackev_d(dst1,dst0); - dst2 = __lasx_xvpackev_d(dst3,dst2); - dst0 = __lasx_xvpermi_q(dst0, dst2, 0x02); - dst0 = __lasx_xvavgr_bu(src0, dst0); - __lasx_xvstelm_d(dst0, dst, 0, 0); - __lasx_xvstelm_d(dst0, dst + stride, 0, 1); - __lasx_xvstelm_d(dst0, dst + stride_2x, 0, 2); - __lasx_xvstelm_d(dst0, dst + stride_3x, 0, 3); -} - -static av_always_inline void avg_width8x4_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride) -{ - __m256i src0, src1, src2, src3; - __m256i dst0, dst1, dst2, dst3; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - - src0 = __lasx_xvldrepl_d(src, 0); - src1 = __lasx_xvldrepl_d(src + stride, 0); - src2 = __lasx_xvldrepl_d(src + stride_2x, 0); - src3 = __lasx_xvldrepl_d(src + stride_3x, 0); - dst0 = __lasx_xvldrepl_d(dst, 0); - dst1 = __lasx_xvldrepl_d(dst + stride, 0); - dst2 = __lasx_xvldrepl_d(dst + stride_2x, 0); - dst3 = __lasx_xvldrepl_d(dst + stride_3x, 0); - src0 = __lasx_xvpackev_d(src1,src0); - src2 = __lasx_xvpackev_d(src3,src2); - src0 = __lasx_xvpermi_q(src0, src2, 0x02); - dst0 = __lasx_xvpackev_d(dst1,dst0); - dst2 = __lasx_xvpackev_d(dst3,dst2); - dst0 = __lasx_xvpermi_q(dst0, dst2, 0x02); - dst0 = __lasx_xvavgr_bu(src0, dst0); - __lasx_xvstelm_d(dst0, dst, 0, 0); - __lasx_xvstelm_d(dst0, dst + stride, 0, 1); - __lasx_xvstelm_d(dst0, dst + stride_2x, 0, 2); - __lasx_xvstelm_d(dst0, dst + stride_3x, 0, 3); -} - -static void avc_chroma_hv_and_aver_dst_8w_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride, - uint32_t coef_hor0, - uint32_t coef_hor1, - uint32_t coef_ver0, - uint32_t coef_ver1, - int32_t height) -{ - if (4 == height) { - avc_chroma_hv_and_aver_dst_8x4_lasx(src, dst, stride, coef_hor0, - coef_hor1, coef_ver0, coef_ver1); - } else if (8 == height) { - avc_chroma_hv_and_aver_dst_8x8_lasx(src, dst, stride, coef_hor0, - coef_hor1, coef_ver0, coef_ver1); - } -} - -static void avc_chroma_hz_and_aver_dst_8w_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride, uint32_t coeff0, - uint32_t coeff1, int32_t height) -{ - if (4 == height) { - avc_chroma_hz_and_aver_dst_8x4_lasx(src, dst, stride, coeff0, coeff1); - } else if (8 == height) { - avc_chroma_hz_and_aver_dst_8x8_lasx(src, dst, stride, coeff0, coeff1); - } -} - -static void avc_chroma_vt_and_aver_dst_8w_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride, uint32_t coeff0, - uint32_t coeff1, int32_t height) -{ - if (4 == height) { - avc_chroma_vt_and_aver_dst_8x4_lasx(src, dst, stride, coeff0, coeff1); - } else if (8 == height) { - avc_chroma_vt_and_aver_dst_8x8_lasx(src, dst, stride, coeff0, coeff1); - } -} - -static void avg_width8_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - int32_t height) -{ - if (8 == height) { - avg_width8x8_lasx(src, dst, stride); - } else if (4 == height) { - avg_width8x4_lasx(src, dst, stride); - } -} - -void ff_avg_h264_chroma_mc8_lasx(uint8_t *dst, const uint8_t *src, ptrdiff_t stride, - int height, int x, int y) -{ - av_assert2(x < 8 && y < 8 && x >= 0 && y >= 0); - - if (!(x || y)) { - avg_width8_lasx(src, dst, stride, height); - } else if (x && y) { - avc_chroma_hv_and_aver_dst_8w_lasx(src, dst, stride, x, (8 - x), y, - (8 - y), height); - } else if (x) { - avc_chroma_hz_and_aver_dst_8w_lasx(src, dst, stride, x, (8 - x), height); - } else { - avc_chroma_vt_and_aver_dst_8w_lasx(src, dst, stride, y, (8 - y), height); - } -} diff --git a/libavcodec/loongarch/h264chroma_lasx.h b/libavcodec/loongarch/h264chroma_lasx.h deleted file mode 100644 index 633752035e0..00000000000 --- a/libavcodec/loongarch/h264chroma_lasx.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2020 Loongson Technology Corporation Limited - * Contributed by Shiyou Yin - * - * 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_LOONGARCH_H264CHROMA_LASX_H -#define AVCODEC_LOONGARCH_H264CHROMA_LASX_H - -#include -#include -#include "libavcodec/h264.h" - -void ff_put_h264_chroma_mc4_lasx(uint8_t *dst, const uint8_t *src, ptrdiff_t stride, - int h, int x, int y); -void ff_put_h264_chroma_mc8_lasx(uint8_t *dst, const uint8_t *src, ptrdiff_t stride, - int h, int x, int y); -void ff_avg_h264_chroma_mc8_lasx(uint8_t *dst, const uint8_t *src, ptrdiff_t stride, - int h, int x, int y); - -#endif /* AVCODEC_LOONGARCH_H264CHROMA_LASX_H */ diff --git a/libavcodec/loongarch/h264chroma_loongarch.h b/libavcodec/loongarch/h264chroma_loongarch.h new file mode 100644 index 00000000000..e65fcfe9f38 --- /dev/null +++ b/libavcodec/loongarch/h264chroma_loongarch.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 Loongson Technology Corporation Limited + * Contributed by Shiyou Yin + * + * 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_LOONGARCH_H264CHROMA_LOONGARCH_H +#define AVCODEC_LOONGARCH_H264CHROMA_LOONGARCH_H + +#include "libavcodec/h264.h" + +void ff_put_h264_chroma_mc8_lsx(unsigned char *dst, const unsigned char *src, + long int stride, int h, int x, int y); +void ff_avg_h264_chroma_mc8_lsx(unsigned char *dst, const unsigned char *src, + long int stride, int h, int x, int y); +void ff_put_h264_chroma_mc4_lsx(unsigned char *dst, const unsigned char *src, + long int stride, int h, int x, int y); + +void ff_put_h264_chroma_mc4_lasx(unsigned char *dst, const unsigned char *src, + long int stride, int h, int x, int y); +void ff_put_h264_chroma_mc8_lasx(unsigned char *dst, const unsigned char *src, + long int stride, int h, int x, int y); +void ff_avg_h264_chroma_mc8_lasx(unsigned char *dst, const unsigned char *src, + long int stride, int h, int x, int y); + +#endif /* AVCODEC_LOONGARCH_H264CHROMA_LOONGARCH_H */ diff --git a/libavcodec/loongarch/h264dsp.S b/libavcodec/loongarch/h264dsp.S new file mode 100644 index 00000000000..750fe49143c --- /dev/null +++ b/libavcodec/loongarch/h264dsp.S @@ -0,0 +1,1977 @@ +/* + * Loongson LSX/LASX optimized h264dsp + * + * Copyright (c) 2023 Loongson Technology Corporation Limited + * Contributed by Hao Chen + * + * 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 "loongson_asm.S" + +const vec_shuf +.rept 2 +.byte 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3 +.endr +endconst + +.macro AVC_LPF_P1_OR_Q1 _in0, _in1, _in2, _in3, _in4, _in5, _out, _tmp0, _tmp1 + vavgr.hu \_tmp0, \_in0, \_in1 + vslli.h \_tmp1, \_in2, 1 + vsub.h \_tmp0, \_tmp0, \_tmp1 + vavg.h \_tmp0, \_in3, \_tmp0 + vclip.h \_tmp0, \_tmp0, \_in4, \_in5 + vadd.h \_out, \_in2, \_tmp0 +.endm + +.macro AVC_LPF_P0Q0 _in0, _in1, _in2, _in3, _in4, _in5, _out0, \ + _out1, _tmp0, _tmp1 + vsub.h \_tmp0, \_in0, \_in1 + vsub.h \_tmp1, \_in2, \_in3 + vslli.h \_tmp0, \_tmp0, 2 + vaddi.hu \_tmp1, \_tmp1, 4 + vadd.h \_tmp0, \_tmp0, \_tmp1 + vsrai.h \_tmp0, \_tmp0, 3 + vclip.h \_tmp0, \_tmp0, \_in4, \_in5 + vadd.h \_out0, \_in1, \_tmp0 + vsub.h \_out1, \_in0, \_tmp0 + vclip255.h \_out0, \_out0 + vclip255.h \_out1, \_out1 +.endm + +.macro SAVE_REG + addi.d sp, sp, -64 + fst.d f24, sp, 0 + fst.d f25, sp, 8 + fst.d f26, sp, 16 + fst.d f27, sp, 24 + fst.d f28, sp, 32 + fst.d f29, sp, 40 + fst.d f30, sp, 48 + fst.d f31, sp, 56 +.endm + +.macro RESTORE_REG + fld.d f24, sp, 0 + fld.d f25, sp, 8 + fld.d f26, sp, 16 + fld.d f27, sp, 24 + fld.d f28, sp, 32 + fld.d f29, sp, 40 + fld.d f30, sp, 48 + fld.d f31, sp, 56 + addi.d sp, sp, 64 +.endm + +.macro load_double _in0, _in1, _in2, _in3, _src, _str0, _str1, _str2 + fld.d \_in0, \_src, 0 + fldx.d \_in1, \_src, \_str0 + fldx.d \_in2, \_src, \_str1 + fldx.d \_in3, \_src, \_str2 +.endm + +.macro store_double _in0, _in1, _in2, _in3, _dst, _str0, _str1, _str2 + fst.d \_in0, \_dst, 0 + fstx.d \_in1, \_dst, \_str0 + fstx.d \_in2, \_dst, \_str1 + fstx.d \_in3, \_dst, \_str2 +.endm + +function ff_h264_h_lpf_luma_8_lsx + slli.d t0, a1, 1 //img_width_2x + slli.d t1, a1, 2 //img_width_4x + slli.d t2, a1, 3 //img_width_8x + SAVE_REG + la.local t4, vec_shuf + add.d t3, t0, a1 //img_width_3x + vldrepl.w vr0, a4, 0 //tmp_vec0 + vld vr1, t4, 0 //tc_vec + vshuf.b vr1, vr0, vr0, vr1 //tc_vec + vslti.b vr2, vr1, 0 + vxori.b vr2, vr2, 255 + vandi.b vr2, vr2, 1 //bs_vec + vsetnez.v $fcc0, vr2 + bceqz $fcc0, .END_LUMA_8 + vldi vr0, 0 //zero + addi.d t4, a0, -4 //src + vslt.bu vr3, vr0, vr2 //is_bs_greater_than0 + add.d t5, t4, t2 //src_tmp + vld vr4, t4, 0 //row0 + vldx vr5, t4, a1 //row1 + vldx vr6, t4, t0 //row2 + vldx vr7, t4, t3 //row3 + add.d t6, t4, t1 // src += img_width_4x + vld vr8, t6, 0 //row4 + vldx vr9, t6, a1 //row5 + vldx vr10, t6, t0 //row6 + vldx vr11, t6, t3 //row7 + vld vr12, t5, 0 //row8 + vldx vr13, t5, a1 //row9 + vldx vr14, t5, t0 //row10 + vldx vr15, t5, t3 //row11 + add.d t6, t5, t1 // src_tmp += img_width_4x + vld vr16, t6, 0 //row12 + vldx vr17, t6, a1 //row13 + vldx vr18, t6, t0 //row14 + vldx vr19, t6, t3 //row15 + LSX_TRANSPOSE16X8_B vr4, vr5, vr6, vr7, vr8, vr9, vr10, vr11, \ + vr12, vr13, vr14, vr15, vr16, vr17, vr18, vr19, \ + vr10, vr11, vr12, vr13, vr14, vr15, vr16, vr17, \ + vr20, vr21, vr22, vr23, vr24, vr25, vr26, vr27 + //vr10: p3_org, vr11: p2_org, vr12: p1_org, vr13: p0_org + //vr14: q0_org, vr15: q1_org, vr16: q2_org, vr17: q3_org + vabsd.bu vr20, vr13, vr14 //p0_asub_q0 + vabsd.bu vr21, vr12, vr13 //p1_asub_p0 + vabsd.bu vr22, vr15, vr14 //q1_asub_q0 + + vreplgr2vr.b vr4, a2 //alpha + vreplgr2vr.b vr5, a3 //beta + + vslt.bu vr6, vr20, vr4 //is_less_than_alpha + vslt.bu vr7, vr21, vr5 //is_less_than_beta + vand.v vr8, vr6, vr7 //is_less_than + vslt.bu vr7, vr22, vr5 //is_less_than_beta + vand.v vr8, vr7, vr8 //is_less_than + vand.v vr8, vr8, vr3 //is_less_than + vsetnez.v $fcc0, vr8 + bceqz $fcc0, .END_LUMA_8 + vneg.b vr9, vr1 //neg_tc_h + vsllwil.hu.bu vr18, vr1, 0 //tc_h.0 + vexth.hu.bu vr19, vr1 //tc_h.1 + vexth.h.b vr2, vr9 //neg_tc_h.1 + vsllwil.h.b vr9, vr9, 0 //neg_tc_h.0 + + vsllwil.hu.bu vr23, vr12, 0 //p1_org_h.0 + vexth.hu.bu vr3, vr12 //p1_org_h.1 + vsllwil.hu.bu vr24, vr13, 0 //p0_org_h.0 + vexth.hu.bu vr4, vr13 //p0_org_h.1 + vsllwil.hu.bu vr25, vr14, 0 //q0_org_h.0 + vexth.hu.bu vr6, vr14 //q0_org_h.1 + + vabsd.bu vr0, vr11, vr13 //p2_asub_p0 + vslt.bu vr7, vr0, vr5 + vand.v vr7, vr8, vr7 //is_less_than_beta + vsetnez.v $fcc0, vr7 + bceqz $fcc0, .END_LUMA_BETA + vsllwil.hu.bu vr26, vr11, 0 //p2_org_h.0 + vexth.hu.bu vr0, vr11 //p2_org_h.1 + AVC_LPF_P1_OR_Q1 vr24, vr25, vr23, vr26, vr9, vr18, vr27, vr28, vr29 + AVC_LPF_P1_OR_Q1 vr4, vr6, vr3, vr0, vr2, vr19, vr28, vr29, vr30 + vpickev.b vr27, vr28, vr27 + vbitsel.v vr12, vr12, vr27, vr7 + vandi.b vr7, vr7, 1 + vadd.b vr1, vr1, vr7 +.END_LUMA_BETA: + vabsd.bu vr26, vr16, vr14 //q2_asub_q0 + vslt.bu vr7, vr26, vr5 + vand.v vr7, vr7, vr8 + vsllwil.hu.bu vr27, vr15, 0 //q1_org_h.0 + vexth.hu.bu vr26, vr15 //q1_org_h.1 + vsetnez.v $fcc0, vr7 + bceqz $fcc0, .END_LUMA_BETA_SEC + vsllwil.hu.bu vr28, vr16, 0 //q2_org_h.0 + vexth.hu.bu vr0, vr16 //q2_org_h.1 + AVC_LPF_P1_OR_Q1 vr24, vr25, vr27, vr28, vr9, vr18, vr29, vr30, vr31 + AVC_LPF_P1_OR_Q1 vr4, vr6, vr26, vr0, vr2, vr19, vr22, vr30, vr31 + vpickev.b vr29, vr22, vr29 + vbitsel.v vr15, vr15, vr29, vr7 + vandi.b vr7, vr7, 1 + vadd.b vr1, vr1, vr7 +.END_LUMA_BETA_SEC: + vneg.b vr22, vr1 //neg_thresh_h + vsllwil.h.b vr28, vr22, 0 //neg_thresh_h.0 + vexth.h.b vr29, vr22 //neg_thresh_h.1 + vsllwil.hu.bu vr18, vr1, 0 //tc_h.0 + vexth.hu.bu vr1, vr1 //tc_h.1 + AVC_LPF_P0Q0 vr25, vr24, vr23, vr27, vr28, vr18, vr30, vr31, vr0, vr2 + AVC_LPF_P0Q0 vr6, vr4, vr3, vr26, vr29, vr1, vr20, vr21, vr0, vr2 + vpickev.b vr30, vr20, vr30 //p0_h + vpickev.b vr31, vr21, vr31 //q0_h + vbitsel.v vr13, vr13, vr30, vr8 //p0_org + vbitsel.v vr14, vr14, vr31, vr8 //q0_org + + vilvl.b vr4, vr12, vr10 // row0.0 + vilvl.b vr5, vr16, vr14 // row0.1 + vilvl.b vr6, vr13, vr11 // row2.0 + vilvl.b vr7, vr17, vr15 // row2.1 + + vilvh.b vr8, vr12, vr10 // row1.0 + vilvh.b vr9, vr16, vr14 // row1.1 + vilvh.b vr10, vr13, vr11 // row3.0 + vilvh.b vr11, vr17, vr15 // row3.1 + + vilvl.b vr12, vr6, vr4 // row4.0 + vilvl.b vr13, vr7, vr5 // row4.1 + vilvl.b vr14, vr10, vr8 // row6.0 + vilvl.b vr15, vr11, vr9 // row6.1 + + vilvh.b vr16, vr6, vr4 // row5.0 + vilvh.b vr17, vr7, vr5 // row5.1 + vilvh.b vr18, vr10, vr8 // row7.0 + vilvh.b vr19, vr11, vr9 // row7.1 + + vilvl.w vr4, vr13, vr12 // row4: 0, 4, 1, 5 + vilvh.w vr5, vr13, vr12 // row4: 2, 6, 3, 7 + vilvl.w vr6, vr17, vr16 // row5: 0, 4, 1, 5 + vilvh.w vr7, vr17, vr16 // row5: 2, 6, 3, 7 + + vilvl.w vr8, vr15, vr14 // row6: 0, 4, 1, 5 + vilvh.w vr9, vr15, vr14 // row6: 2, 6, 3, 7 + vilvl.w vr10, vr19, vr18 // row7: 0, 4, 1, 5 + vilvh.w vr11, vr19, vr18 // row7: 2, 6, 3, 7 + + vbsrl.v vr20, vr4, 8 + vbsrl.v vr21, vr5, 8 + vbsrl.v vr22, vr6, 8 + vbsrl.v vr23, vr7, 8 + + vbsrl.v vr24, vr8, 8 + vbsrl.v vr25, vr9, 8 + vbsrl.v vr26, vr10, 8 + vbsrl.v vr27, vr11, 8 + + store_double f4, f20, f5, f21, t4, a1, t0, t3 + add.d t4, t4, t1 + store_double f6, f22, f7, f23, t4, a1, t0, t3 + add.d t4, t4, t1 + store_double f8, f24, f9, f25, t4, a1, t0, t3 + add.d t4, t4, t1 + store_double f10, f26, f11, f27, t4, a1, t0, t3 +.END_LUMA_8: + RESTORE_REG +endfunc + +function ff_h264_v_lpf_luma_8_lsx + slli.d t0, a1, 1 //img_width_2x + la.local t4, vec_shuf + vldrepl.w vr0, a4, 0 //tmp_vec0 + vld vr1, t4, 0 //tc_vec + add.d t1, t0, a1 //img_width_3x + vshuf.b vr1, vr0, vr0, vr1 //tc_vec + addi.d sp, sp, -24 + fst.d f24, sp, 0 + fst.d f25, sp, 8 + fst.d f26, sp, 16 + vslti.b vr2, vr1, 0 + vxori.b vr2, vr2, 255 + vandi.b vr2, vr2, 1 //bs_vec + vsetnez.v $fcc0, vr2 + bceqz $fcc0, .END_V_LUMA_8 + sub.d t2, a0, t1 //data - img_width_3x + vreplgr2vr.b vr4, a2 //alpha + vreplgr2vr.b vr5, a3 //beta + vldi vr0, 0 //zero + vld vr10, t2, 0 //p2_org + vldx vr11, t2, a1 //p1_org + vldx vr12, t2, t0 //p0_org + vld vr13, a0, 0 //q0_org + vldx vr14, a0, a1 //q1_org + + vslt.bu vr0, vr0, vr2 //is_bs_greater_than0 + vabsd.bu vr16, vr11, vr12 //p1_asub_p0 + vabsd.bu vr15, vr12, vr13 //p0_asub_q0 + vabsd.bu vr17, vr14, vr13 //q1_asub_q0 + + vslt.bu vr6, vr15, vr4 //is_less_than_alpha + vslt.bu vr7, vr16, vr5 //is_less_than_beta + vand.v vr8, vr6, vr7 //is_less_than + vslt.bu vr7, vr17, vr5 //is_less_than_beta + vand.v vr8, vr7, vr8 + vand.v vr8, vr8, vr0 //is_less_than + + vsetnez.v $fcc0, vr8 + bceqz $fcc0, .END_V_LUMA_8 + vldx vr15, a0, t0 //q2_org + vneg.b vr0, vr1 //neg_tc_h + vsllwil.h.b vr18, vr1, 0 //tc_h.0 + vexth.h.b vr19, vr1 //tc_h.1 + vsllwil.h.b vr9, vr0, 0 //neg_tc_h.0 + vexth.h.b vr2, vr0 //neg_tc_h.1 + + vsllwil.hu.bu vr16, vr11, 0 //p1_org_h.0 + vexth.hu.bu vr17, vr11 //p1_org_h.1 + vsllwil.hu.bu vr20, vr12, 0 //p0_org_h.0 + vexth.hu.bu vr21, vr12 //p0_org_h.1 + vsllwil.hu.bu vr22, vr13, 0 //q0_org_h.0 + vexth.hu.bu vr23, vr13 //q0_org_h.1 + + vabsd.bu vr0, vr10, vr12 //p2_asub_p0 + vslt.bu vr7, vr0, vr5 //is_less_than_beta + vand.v vr7, vr7, vr8 //is_less_than_beta + + vsetnez.v $fcc0, vr8 + bceqz $fcc0, .END_V_LESS_BETA + vsllwil.hu.bu vr3, vr10, 0 //p2_org_h.0 + vexth.hu.bu vr4, vr10 //p2_org_h.1 + AVC_LPF_P1_OR_Q1 vr20, vr22, vr16, vr3, vr9, vr18, vr24, vr0, vr26 + AVC_LPF_P1_OR_Q1 vr21, vr23, vr17, vr4, vr2, vr19, vr25, vr0, vr26 + vpickev.b vr24, vr25, vr24 + vbitsel.v vr24, vr11, vr24, vr7 + addi.d t3, t2, 16 + vstx vr24, t2, a1 + vandi.b vr7, vr7, 1 + vadd.b vr1, vr7, vr1 +.END_V_LESS_BETA: + vabsd.bu vr0, vr15, vr13 //q2_asub_q0 + vslt.bu vr7, vr0, vr5 //is_less_than_beta + vand.v vr7, vr7, vr8 //is_less_than_beta + vsllwil.hu.bu vr3, vr14, 0 //q1_org_h.0 + vexth.hu.bu vr4, vr14 //q1_org_h.1 + + vsetnez.v $fcc0, vr7 + bceqz $fcc0, .END_V_LESS_BETA_SEC + vsllwil.hu.bu vr11, vr15, 0 //q2_org_h.0 + vexth.hu.bu vr15, vr15 //q2_org_h.1 + AVC_LPF_P1_OR_Q1 vr20, vr22, vr3, vr11, vr9, vr18, vr24, vr0, vr26 + AVC_LPF_P1_OR_Q1 vr21, vr23, vr4, vr15, vr2, vr19, vr25, vr0, vr26 + vpickev.b vr24, vr25, vr24 + vbitsel.v vr24, vr14, vr24, vr7 + vstx vr24, a0, a1 + vandi.b vr7, vr7, 1 + vadd.b vr1, vr1, vr7 +.END_V_LESS_BETA_SEC: + vneg.b vr0, vr1 + vsllwil.h.b vr9, vr0, 0 //neg_thresh_h.0 + vexth.h.b vr2, vr0 //neg_thresh_h.1 + vsllwil.hu.bu vr18, vr1, 0 //tc_h.0 + vexth.hu.bu vr19, vr1 //tc_h.1 + AVC_LPF_P0Q0 vr22, vr20, vr16, vr3, vr9, vr18, vr11, vr15, vr0, vr26 + AVC_LPF_P0Q0 vr23, vr21, vr17, vr4, vr2, vr19, vr10, vr14, vr0, vr26 + vpickev.b vr11, vr10, vr11 //p0_h + vpickev.b vr15, vr14, vr15 //q0_h + vbitsel.v vr11, vr12, vr11, vr8 //p0_h + vbitsel.v vr15, vr13, vr15, vr8 //q0_h + vstx vr11, t2, t0 + vst vr15, a0, 0 +.END_V_LUMA_8: + fld.d f24, sp, 0 + fld.d f25, sp, 8 + fld.d f26, sp, 16 + addi.d sp, sp, 24 +endfunc + +const chroma_shuf +.byte 0, 0, 1, 1, 2, 2, 3, 3, 0, 0, 1, 1, 2, 2, 3, 3 +endconst + +function ff_h264_h_lpf_chroma_8_lsx + slli.d t0, a1, 1 //img_width_2x + slli.d t1, a1, 2 //img_width_4x + la.local t4, chroma_shuf + add.d t2, t0, a1 //img_width_3x + vldrepl.w vr0, a4, 0 //tmp_vec0 + vld vr1, t4, 0 //tc_vec + vshuf.b vr1, vr0, vr0, vr1 //tc_vec + vslti.b vr2, vr1, 0 + vxori.b vr2, vr2, 255 + vandi.b vr2, vr2, 1 //bs_vec + vsetnez.v $fcc0, vr2 + bceqz $fcc0, .END_CHROMA_8 + vldi vr0, 0 + addi.d t4, a0, -2 + vslt.bu vr3, vr0, vr2 //is_bs_greater_than0 + add.d t5, t4, t1 + vld vr4, t4, 0 //row0 + vldx vr5, t4, a1 //row1 + vldx vr6, t4, t0 //row2 + vldx vr7, t4, t2 //row3 + vld vr8, t5, 0 //row4 + vldx vr9, t5, a1 //row5 + vldx vr10, t5, t0 //row6 + vldx vr11, t5, t2 //row7 + vilvl.b vr12, vr6, vr4 //p1_org + vilvl.b vr13, vr7, vr5 //p0_org + vilvl.b vr14, vr10, vr8 //q0_org + vilvl.b vr15, vr11, vr9 //q1_org + vilvl.b vr4, vr13, vr12 //row0 + vilvl.b vr5, vr15, vr14 //row1 + vilvl.w vr6, vr5, vr4 //row2 + vilvh.w vr7, vr5, vr4 //row3 + vilvl.d vr12, vr6, vr6 //p1_org + vilvh.d vr13, vr6, vr6 //p0_org + vilvl.d vr14, vr7, vr7 //q0_org + vilvh.d vr15, vr7, vr7 //q1_org + + vabsd.bu vr20, vr13, vr14 //p0_asub_q0 + vabsd.bu vr21, vr12, vr13 //p1_asub_p0 + vabsd.bu vr22, vr15, vr14 //q1_asub_q0 + + vreplgr2vr.b vr4, a2 //alpha + vreplgr2vr.b vr5, a3 //beta + + vslt.bu vr6, vr20, vr4 //is_less_than_alpha + vslt.bu vr7, vr21, vr5 //is_less_than_beta + vand.v vr8, vr6, vr7 //is_less_than + vslt.bu vr7, vr22, vr5 //is_less_than_beta + vand.v vr8, vr7, vr8 //is_less_than + vand.v vr8, vr8, vr3 //is_less_than + vsetnez.v $fcc0, vr8 + bceqz $fcc0, .END_CHROMA_8 + + vneg.b vr9, vr1 //neg_tc_h + vexth.hu.bu vr3, vr12 //p1_org_h + vexth.hu.bu vr4, vr13 //p0_org_h.1 + vexth.hu.bu vr5, vr14 //q0_org_h.1 + vexth.hu.bu vr6, vr15 //q1_org_h.1 + + vexth.hu.bu vr18, vr1 //tc_h.1 + vexth.h.b vr2, vr9 //neg_tc_h.1 + + AVC_LPF_P0Q0 vr5, vr4, vr3, vr6, vr2, vr18, vr10, vr11, vr16, vr17 + vpickev.b vr10, vr10, vr10 //p0_h + vpickev.b vr11, vr11, vr11 //q0_h + vbitsel.v vr13, vr13, vr10, vr8 + vbitsel.v vr14, vr14, vr11, vr8 + vilvl.b vr15, vr14, vr13 + addi.d t4, t4, 1 + add.d t5, t4, a1 + add.d t6, t4, t0 + add.d t7, t4, t2 + vstelm.h vr15, t4, 0, 0 + vstelm.h vr15, t5, 0, 1 + vstelm.h vr15, t6, 0, 2 + vstelm.h vr15, t7, 0, 3 + add.d t4, t4, t1 + add.d t5, t4, a1 + add.d t6, t4, t0 + add.d t7, t4, t2 + vstelm.h vr15, t4, 0, 4 + vstelm.h vr15, t5, 0, 5 + vstelm.h vr15, t6, 0, 6 + vstelm.h vr15, t7, 0, 7 +.END_CHROMA_8: +endfunc + +function ff_h264_v_lpf_chroma_8_lsx + slli.d t0, a1, 1 //img_width_2x + la.local t4, chroma_shuf + vldrepl.w vr0, a4, 0 //tmp_vec0 + vld vr1, t4, 0 //tc_vec + vshuf.b vr1, vr0, vr0, vr1 //tc_vec + vslti.b vr2, vr1, 0 + vxori.b vr2, vr2, 255 + vandi.b vr2, vr2, 1 //bs_vec + vsetnez.v $fcc0, vr2 + bceqz $fcc0, .END_CHROMA_V_8 + vldi vr0, 0 + sub.d t4, a0, t0 + vslt.bu vr3, vr0, vr2 //is_bs_greater_than0 + vld vr12, t4, 0 //p1_org + vldx vr13, t4, a1 //p0_org + vld vr14, a0, 0 //q0_org + vldx vr15, a0, a1 //q1_org + + vabsd.bu vr20, vr13, vr14 //p0_asub_q0 + vabsd.bu vr21, vr12, vr13 //p1_asub_p0 + vabsd.bu vr22, vr15, vr14 //q1_asub_q0 + + vreplgr2vr.b vr4, a2 //alpha + vreplgr2vr.b vr5, a3 //beta + + vslt.bu vr6, vr20, vr4 //is_less_than_alpha + vslt.bu vr7, vr21, vr5 //is_less_than_beta + vand.v vr8, vr6, vr7 //is_less_than + vslt.bu vr7, vr22, vr5 //is_less_than_beta + vand.v vr8, vr7, vr8 //is_less_than + vand.v vr8, vr8, vr3 //is_less_than + vsetnez.v $fcc0, vr8 + bceqz $fcc0, .END_CHROMA_V_8 + + vneg.b vr9, vr1 //neg_tc_h + vsllwil.hu.bu vr3, vr12, 0 //p1_org_h + vsllwil.hu.bu vr4, vr13, 0 //p0_org_h.1 + vsllwil.hu.bu vr5, vr14, 0 //q0_org_h.1 + vsllwil.hu.bu vr6, vr15, 0 //q1_org_h.1 + + vexth.hu.bu vr18, vr1 //tc_h.1 + vexth.h.b vr2, vr9 //neg_tc_h.1 + + AVC_LPF_P0Q0 vr5, vr4, vr3, vr6, vr2, vr18, vr10, vr11, vr16, vr17 + vpickev.b vr10, vr10, vr10 //p0_h + vpickev.b vr11, vr11, vr11 //q0_h + vbitsel.v vr10, vr13, vr10, vr8 + vbitsel.v vr11, vr14, vr11, vr8 + fstx.d f10, t4, a1 + fst.d f11, a0, 0 +.END_CHROMA_V_8: +endfunc + +.macro AVC_LPF_P0P1P2_OR_Q0Q1Q2 _in0, _in1, _in2, _in3, _in4, _in5 \ + _out0, _out1, _out2, _tmp0, _const3 + vadd.h \_tmp0, \_in1, \_in2 + vadd.h \_tmp0, \_tmp0, \_in3 + vslli.h \_out2, \_in0, 1 + vslli.h \_out0, \_tmp0, 1 + vadd.h \_out0, \_out0, \_in4 + vadd.h \_out1, \_in4, \_tmp0 + vadd.h \_out0, \_out0, \_in5 + vmadd.h \_out2, \_in4, \_const3 + vsrar.h \_out0, \_out0, \_const3 + vadd.h \_out2, \_out2, \_tmp0 + vsrari.h \_out1, \_out1, 2 + vsrar.h \_out2, \_out2, \_const3 +.endm + +.macro AVC_LPF_P0_OR_Q0 _in0, _in1, _in2, _out0, _tmp0 + vslli.h \_tmp0, \_in2, 1 + vadd.h \_out0, \_in0, \_in1 + vadd.h \_out0, \_out0, \_tmp0 + vsrari.h \_out0, \_out0, 2 +.endm + +////LSX optimization is sufficient for this function. +function ff_h264_h_lpf_luma_intra_8_lsx + slli.d t0, a1, 1 //img_width_2x + slli.d t1, a1, 2 //img_width_4x + addi.d t4, a0, -4 //src + SAVE_REG + add.d t2, t0, a1 //img_width_3x + add.d t5, t4, t1 + vld vr0, t4, 0 //row0 + vldx vr1, t4, a1 //row1 + vldx vr2, t4, t0 //row2 + vldx vr3, t4, t2 //row3 + add.d t6, t5, t1 + vld vr4, t5, 0 //row4 + vldx vr5, t5, a1 //row5 + vldx vr6, t5, t0 //row6 + vldx vr7, t5, t2 //row7 + add.d t7, t6, t1 + vld vr8, t6, 0 //row8 + vldx vr9, t6, a1 //row9 + vldx vr10, t6, t0 //row10 + vldx vr11, t6, t2 //row11 + vld vr12, t7, 0 //row12 + vldx vr13, t7, a1 //row13 + vldx vr14, t7, t0 //row14 + vldx vr15, t7, t2 //row15 + LSX_TRANSPOSE16X8_B vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr7, \ + vr8, vr9, vr10, vr11, vr12, vr13, vr14, vr15, \ + vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr7, \ + vr16, vr17, vr18, vr19, vr20, vr21, vr22, vr23 + // vr0: p3_org, vr1: p2_org, vr2: p1_org, vr3: p0_org + // vr4: q0_org, vr5: q1_org, vr6: q2_org, vr7: q3_org + + vreplgr2vr.b vr16, a2 //alpha_in + vreplgr2vr.b vr17, a3 //beta_in + vabsd.bu vr10, vr3, vr4 //p0_asub_q0 + vabsd.bu vr11, vr2, vr3 //p1_asub_p0 + vabsd.bu vr12, vr5, vr4 //q1_asub_q0 + + vslt.bu vr8, vr10, vr16 //is_less_than_alpha + vslt.bu vr9, vr11, vr17 //is_less_than_beta + vand.v vr18, vr8, vr9 //is_less_than + vslt.bu vr9, vr12, vr17 //is_less_than_beta + vand.v vr18, vr18, vr9 //is_less_than + + vsetnez.v $fcc0, vr18 + bceqz $fcc0, .END_H_INTRA_8 + vsrli.b vr16, vr16, 2 //less_alpha_shift2_add2 + vaddi.bu vr16, vr16, 2 + vslt.bu vr16, vr10, vr16 + vsllwil.hu.bu vr10, vr2, 0 //p1_org_h.0 + vexth.hu.bu vr11, vr2 //p1_org_h.1 + vsllwil.hu.bu vr12, vr3, 0 //p0_org_h.0 + vexth.hu.bu vr13, vr3 //p0_org_h.1 + + vsllwil.hu.bu vr14, vr4, 0 //q0_org_h.0 + vexth.hu.bu vr15, vr4 //q0_org_h.1 + vsllwil.hu.bu vr19, vr5, 0 //q1_org_h.0 + vexth.hu.bu vr20, vr5 //q1_org_h.1 + + vabsd.bu vr21, vr1, vr3 //p2_asub_p0 + vslt.bu vr9, vr21, vr17 //is_less_than_beta + vand.v vr9, vr9, vr16 + vxori.b vr22, vr9, 0xff //negate_is_less_than_beta + vand.v vr9, vr9, vr18 + vand.v vr22, vr22, vr18 + + vsetnez.v $fcc0, vr9 + bceqz $fcc0, .END_H_INTRA_LESS_BETA + vsllwil.hu.bu vr23, vr1, 0 //p2_org_h.0 + vexth.hu.bu vr24, vr1 //p2_org_h.1 + vsllwil.hu.bu vr25, vr0, 0 //p3_org_h.0 + vexth.hu.bu vr26, vr0 //p3_org_h.1 + vldi vr27, 0x403 + + AVC_LPF_P0P1P2_OR_Q0Q1Q2 vr25, vr12, vr14, vr10, vr23, vr19, vr28, vr29, vr30, vr31, vr27 + AVC_LPF_P0P1P2_OR_Q0Q1Q2 vr26, vr13, vr15, vr11, vr24, vr20, vr23, vr25, vr21, vr31, vr27 + vpickev.b vr28, vr23, vr28 //p0_h + vpickev.b vr29, vr25, vr29 //p1_h + vpickev.b vr30, vr21, vr30 //p2_h + vbitsel.v vr3, vr3, vr28, vr9 + vbitsel.v vr2, vr2, vr29, vr9 + vbitsel.v vr1, vr1, vr30, vr9 +.END_H_INTRA_LESS_BETA: + AVC_LPF_P0_OR_Q0 vr12, vr19, vr10, vr23, vr25 + AVC_LPF_P0_OR_Q0 vr13, vr20, vr11, vr24, vr25 + //vr23: p0_h.0 vr24: p0_h.1 + vpickev.b vr23, vr24, vr23 + vbitsel.v vr3, vr3, vr23, vr22 + + vabsd.bu vr21, vr6, vr4 //q2_asub_q0 + vslt.bu vr9, vr21, vr17 //is_less_than_beta + vand.v vr9, vr9, vr16 + vxori.b vr22, vr9, 0xff //negate_is_less_than_beta + vand.v vr9, vr9, vr18 + vand.v vr22, vr22, vr18 + + vsetnez.v $fcc0, vr9 + bceqz $fcc0, .END_H_INTRA_LESS_BETA_SEC + vsllwil.hu.bu vr23, vr6, 0 //q2_org_h.0 + vexth.hu.bu vr24, vr6 //q2_org_h.1 + vsllwil.hu.bu vr25, vr7, 0 //q3_org_h.0 + vexth.hu.bu vr26, vr7 //q3_org_h.1 + vldi vr27, 0x403 + + AVC_LPF_P0P1P2_OR_Q0Q1Q2 vr25, vr14, vr12, vr19, vr23, vr10, vr28, vr29, vr30, vr31, vr27 + AVC_LPF_P0P1P2_OR_Q0Q1Q2 vr26, vr15, vr13, vr20, vr24, vr11, vr23, vr25, vr21, vr31, vr27 + vpickev.b vr28, vr23, vr28 //q0_h + vpickev.b vr29, vr25, vr29 //q1_h + vpickev.b vr30, vr21, vr30 //q2_h + vbitsel.v vr4, vr4, vr28, vr9 + vbitsel.v vr5, vr5, vr29, vr9 + vbitsel.v vr6, vr6, vr30, vr9 +.END_H_INTRA_LESS_BETA_SEC: + AVC_LPF_P0_OR_Q0 vr14, vr10, vr19, vr23, vr25 + AVC_LPF_P0_OR_Q0 vr15, vr11, vr20, vr24, vr25 + vpickev.b vr23, vr24, vr23 + vbitsel.v vr4, vr4, vr23, vr22 + + vilvl.b vr14, vr2, vr0 // row0.0 + vilvl.b vr15, vr6, vr4 // row0.1 + vilvl.b vr16, vr3, vr1 // row2.0 + vilvl.b vr17, vr7, vr5 // row2.1 + + vilvh.b vr18, vr2, vr0 // row1.0 + vilvh.b vr19, vr6, vr4 // row1.1 + vilvh.b vr20, vr3, vr1 // row3.0 + vilvh.b vr21, vr7, vr5 // row3.1 + + vilvl.b vr2, vr16, vr14 // row4.0 + vilvl.b vr3, vr17, vr15 // row4.1 + vilvl.b vr4, vr20, vr18 // row6.0 + vilvl.b vr5, vr21, vr19 // row6.1 + + vilvh.b vr6, vr16, vr14 // row5.0 + vilvh.b vr7, vr17, vr15 // row5.1 + vilvh.b vr8, vr20, vr18 // row7.0 + vilvh.b vr9, vr21, vr19 // row7.1 + + vilvl.w vr14, vr3, vr2 // row4: 0, 4, 1, 5 + vilvh.w vr15, vr3, vr2 // row4: 2, 6, 3, 7 + vilvl.w vr16, vr7, vr6 // row5: 0, 4, 1, 5 + vilvh.w vr17, vr7, vr6 // row5: 2, 6, 3, 7 + + vilvl.w vr18, vr5, vr4 // row6: 0, 4, 1, 5 + vilvh.w vr19, vr5, vr4 // row6: 2, 6, 3, 7 + vilvl.w vr20, vr9, vr8 // row7: 0, 4, 1, 5 + vilvh.w vr21, vr9, vr8 // row7: 2, 6, 3, 7 + + vbsrl.v vr0, vr14, 8 + vbsrl.v vr1, vr15, 8 + vbsrl.v vr2, vr16, 8 + vbsrl.v vr3, vr17, 8 + + vbsrl.v vr4, vr18, 8 + vbsrl.v vr5, vr19, 8 + vbsrl.v vr6, vr20, 8 + vbsrl.v vr7, vr21, 8 + + store_double f14, f0, f15, f1, t4, a1, t0, t2 + store_double f16, f2, f17, f3, t5, a1, t0, t2 + store_double f18, f4, f19, f5, t6, a1, t0, t2 + store_double f20, f6, f21, f7, t7, a1, t0, t2 +.END_H_INTRA_8: + RESTORE_REG +endfunc + +//LSX optimization is sufficient for this function. +function ff_h264_v_lpf_luma_intra_8_lsx + slli.d t0, a1, 1 //img_width_2x + add.d t1, t0, a1 //img_width_3x + SAVE_REG + sub.d t4, a0, t1 //src - img_width_3x + + vld vr0, a0, 0 //q0_org + vldx vr1, a0, a1 //q1_org + vldx vr2, t4, a1 //p1_org + vldx vr3, t4, t0 //p0_org + + vreplgr2vr.b vr4, a2 //alpha + vreplgr2vr.b vr5, a3 //beta + + vabsd.bu vr6, vr3, vr0 //p0_asub_q0 + vabsd.bu vr7, vr2, vr3 //p1_asub_p0 + vabsd.bu vr8, vr1, vr0 //q1_asub_q0 + + vslt.bu vr9, vr6, vr4 //is_less_than_alpha + vslt.bu vr10, vr7, vr5 //is_less_than_beta + vand.v vr11, vr9, vr10 //is_less_than + vslt.bu vr10, vr8, vr5 + vand.v vr11, vr10, vr11 + + vsetnez.v $fcc0, vr11 + bceqz $fcc0, .END_V_INTRA_8 + + vld vr12, t4, 0 //p2_org + vldx vr13, a0, t0 //q2_org + vsrli.b vr14, vr4, 2 //is_alpha_shift2_add2 + vsllwil.hu.bu vr15, vr2, 0 //p1_org_h.0 + vexth.hu.bu vr16, vr2 //p1_org_h.1 + vaddi.bu vr14, vr14, 2 + vsllwil.hu.bu vr17, vr3, 0 //p0_org_h.0 + vexth.hu.bu vr18, vr3 //p0_org_h.1 + vslt.bu vr14, vr6, vr14 + vsllwil.hu.bu vr19, vr0, 0 //q0_org_h.0 + vexth.hu.bu vr20, vr0 //q0_org_h.1 + vsllwil.hu.bu vr21, vr1, 0 //q1_org_h.0 + vexth.hu.bu vr22, vr1 //q1_org_h.1 + + vabsd.bu vr23, vr12, vr3 //p2_asub_p0 + vslt.bu vr10, vr23, vr5 //is_less_than_beta + vand.v vr10, vr10, vr14 + vxori.b vr23, vr10, 0xff //negate_is_less_than_beta + vand.v vr10, vr10, vr11 + vand.v vr23, vr23, vr11 + + vsetnez.v $fcc0, vr10 + bceqz $fcc0, .END_V_INTRA_LESS_BETA + sub.d t5, t4, a1 + vld vr24, t5, 0 //p3_org + vsllwil.hu.bu vr26, vr12, 0 //p2_org_h.0 + vexth.hu.bu vr27, vr12 //p2_org_h.1 + vsllwil.hu.bu vr28, vr24, 0 //p3_org_h.0 + vexth.hu.bu vr29, vr24 //p3_org_h.1 + vldi vr4, 0x403 + + AVC_LPF_P0P1P2_OR_Q0Q1Q2 vr28, vr17, vr19, vr15, vr26, vr21, vr25, vr30, vr31, vr24, vr4 + AVC_LPF_P0P1P2_OR_Q0Q1Q2 vr29, vr18, vr20, vr16, vr27, vr22, vr6, vr7, vr8, vr24, vr4 + + vpickev.b vr25, vr6, vr25 //p0_h + vpickev.b vr30, vr7, vr30 //p1_h + vpickev.b vr31, vr8, vr31 //p2_h + + vbitsel.v vr3, vr3, vr25, vr10 + vbitsel.v vr2, vr2, vr30, vr10 + vbitsel.v vr12, vr12, vr31, vr10 + + vstx vr2, t4, a1 + vst vr12, t4, 0 +.END_V_INTRA_LESS_BETA: + AVC_LPF_P0_OR_Q0 vr17, vr21, vr15, vr24, vr30 + AVC_LPF_P0_OR_Q0 vr18, vr22, vr16, vr25, vr30 + vpickev.b vr24, vr25, vr24 + vbitsel.v vr3, vr3, vr24, vr23 + vstx vr3, t4, t0 + + vabsd.bu vr23, vr13, vr0 //q2_asub_q0 + vslt.bu vr10, vr23, vr5 //is_less_than_beta + vand.v vr10, vr10, vr14 + vxori.b vr23, vr10, 0xff //negate_is_less_than_beta + vand.v vr10, vr10, vr11 + vand.v vr23, vr23, vr11 + + vsetnez.v $fcc0, vr10 + bceqz $fcc0, .END_V_INTRA_LESS_BETA_SEC + vldx vr24, a0, t1 //q3_org + + vsllwil.hu.bu vr26, vr13, 0 //q2_org_h.0 + vexth.hu.bu vr27, vr13 //q2_org_h.1 + vsllwil.hu.bu vr28, vr24, 0 //q3_org_h.0 + vexth.hu.bu vr29, vr24 //q3_org_h.1 + vldi vr4, 0x403 + + AVC_LPF_P0P1P2_OR_Q0Q1Q2 vr28, vr19, vr17, vr21, vr26, vr15, vr25, vr30, vr31, vr24, vr4 + AVC_LPF_P0P1P2_OR_Q0Q1Q2 vr29, vr20, vr18, vr22, vr27, vr16, vr6, vr7, vr8, vr24, vr4 + + vpickev.b vr25, vr6, vr25 + vpickev.b vr30, vr7, vr30 + vpickev.b vr31, vr8, vr31 + + vbitsel.v vr0, vr0, vr25, vr10 + vbitsel.v vr1, vr1, vr30, vr10 + vbitsel.v vr13, vr13, vr31, vr10 + vstx vr1, a0, a1 + vstx vr13, a0, t0 +.END_V_INTRA_LESS_BETA_SEC: + AVC_LPF_P0_OR_Q0 vr19, vr15, vr21, vr24, vr30 + AVC_LPF_P0_OR_Q0 vr20, vr16, vr22, vr25, vr30 + vpickev.b vr24, vr25, vr24 + vbitsel.v vr0, vr0, vr24, vr23 + vst vr0, a0, 0 +.END_V_INTRA_8: + RESTORE_REG +endfunc + +function ff_h264_h_lpf_chroma_intra_8_lsx + addi.d t4, a0, -2 + slli.d t0, a1, 1 //img_2x + slli.d t2, a1, 2 //img_4x + add.d t1, t0, a1 //img_3x + + add.d t5, t4, t2 + fld.s f0, t4, 0 //row0 + fldx.s f1, t4, a1 //row1 + fldx.s f2, t4, t0 //row2 + fldx.s f3, t4, t1 //row3 + fld.s f4, t5, 0 //row4 + fldx.s f5, t5, a1 //row5 + fldx.s f6, t5, t0 //row6 + fldx.s f7, t5, t1 //row7 + + vilvl.b vr8, vr2, vr0 //p1_org + vilvl.b vr9, vr3, vr1 //p0_org + vilvl.b vr10, vr6, vr4 //q0_org + vilvl.b vr11, vr7, vr5 //q1_org + + vilvl.b vr0, vr9, vr8 + vilvl.b vr1, vr11, vr10 + vilvl.w vr2, vr1, vr0 + vilvh.w vr3, vr1, vr0 + + vilvl.d vr8, vr2, vr2 //p1_org + vilvh.d vr9, vr2, vr2 //p0_org + vilvl.d vr10, vr3, vr3 //q0_org + vilvh.d vr11, vr3, vr3 //q1_org + + vreplgr2vr.b vr0, a2 //alpha + vreplgr2vr.b vr1, a3 //beta + + vabsd.bu vr2, vr9, vr10 //p0_asub_q0 + vabsd.bu vr3, vr8, vr9 //p1_asub_p0 + vabsd.bu vr4, vr11, vr10 //q1_asub_q0 + + vslt.bu vr5, vr2, vr0 //is_less_than_alpha + vslt.bu vr6, vr3, vr1 //is_less_than_beta + vand.v vr7, vr5, vr6 //is_less_than + vslt.bu vr6, vr4, vr1 + vand.v vr7, vr7, vr6 + + vsetnez.v $fcc0, vr7 + bceqz $fcc0, .END_H_CHROMA_INTRA_8 + + vexth.hu.bu vr12, vr8 //p1_org_h + vexth.hu.bu vr13, vr9 //p0_org_h + vexth.hu.bu vr14, vr10 //q0_org_h + vexth.hu.bu vr15, vr11 //q1_org_h + + AVC_LPF_P0_OR_Q0 vr13, vr15, vr12, vr16, vr18 + AVC_LPF_P0_OR_Q0 vr14, vr12, vr15, vr17, vr18 + + vpickev.b vr18, vr16, vr16 + vpickev.b vr19, vr17, vr17 + vbitsel.v vr9, vr9, vr18, vr7 + vbitsel.v vr10, vr10, vr19, vr7 +.END_H_CHROMA_INTRA_8: + vilvl.b vr11, vr10, vr9 + addi.d t4, t4, 1 + vstelm.h vr11, t4, 0, 0 + add.d t4, t4, a1 + vstelm.h vr11, t4, 0, 1 + add.d t4, t4, a1 + vstelm.h vr11, t4, 0, 2 + add.d t4, t4, a1 + vstelm.h vr11, t4, 0, 3 + add.d t4, t4, a1 + vstelm.h vr11, t4, 0, 4 + add.d t4, t4, a1 + vstelm.h vr11, t4, 0, 5 + add.d t4, t4, a1 + vstelm.h vr11, t4, 0, 6 + add.d t4, t4, a1 + vstelm.h vr11, t4, 0, 7 +endfunc + +function ff_h264_v_lpf_chroma_intra_8_lsx + slli.d t0, a1, 1 //img_width_2x + sub.d t2, a0, a1 + sub.d t1, a0, t0 //data - img_width_2x + + vreplgr2vr.b vr0, a2 + vreplgr2vr.b vr1, a3 + + vld vr2, t1, 0 //p1_org + vldx vr3, t1, a1 //p0_org + vld vr4, a0, 0 //q0_org + vldx vr5, a0, a1 //q1_org + + vabsd.bu vr6, vr3, vr4 //p0_asub_q0 + vabsd.bu vr7, vr2, vr3 //p1_asub_p0 + vabsd.bu vr8, vr5, vr4 //q1_asub_q0 + + vslt.bu vr9, vr6, vr0 //is_less_than_alpha + vslt.bu vr10, vr7, vr1 //is_less_than_beta + vand.v vr11, vr9, vr10 //is_less_than + vslt.bu vr10, vr8, vr1 + vand.v vr11, vr10, vr11 + + vsetnez.v $fcc0, vr11 + bceqz $fcc0, .END_V_CHROMA_INTRA_8 + + vsllwil.hu.bu vr6, vr2, 0 //p1_org_h.0 + vsllwil.hu.bu vr8, vr3, 0 //p0_org_h.0 + vsllwil.hu.bu vr13, vr4, 0 //q0_org_h.0 + vsllwil.hu.bu vr15, vr5, 0 //q1_org_h.0 + + AVC_LPF_P0_OR_Q0 vr8, vr15, vr6, vr17, vr23 + AVC_LPF_P0_OR_Q0 vr13, vr6, vr15, vr18, vr23 + + vpickev.b vr19, vr17, vr17 + vpickev.b vr20, vr18, vr18 + vbitsel.v vr3, vr3, vr19, vr11 + vbitsel.v vr4, vr4, vr20, vr11 + + vstelm.d vr3, t2, 0, 0 + vstelm.d vr4, a0, 0, 0 +.END_V_CHROMA_INTRA_8: +endfunc + +.macro biweight_calc _in0, _in1, _in2, _in3, _reg0, _reg1, _reg2,\ + _out0, _out1, _out2, _out3 + vmov \_out0, \_reg0 + vmov \_out1, \_reg0 + vmov \_out2, \_reg0 + vmov \_out3, \_reg0 + vmaddwev.h.bu.b \_out0, \_in0, \_reg1 + vmaddwev.h.bu.b \_out1, \_in1, \_reg1 + vmaddwev.h.bu.b \_out2, \_in2, \_reg1 + vmaddwev.h.bu.b \_out3, \_in3, \_reg1 + vmaddwod.h.bu.b \_out0, \_in0, \_reg1 + vmaddwod.h.bu.b \_out1, \_in1, \_reg1 + vmaddwod.h.bu.b \_out2, \_in2, \_reg1 + vmaddwod.h.bu.b \_out3, \_in3, \_reg1 + + vssran.bu.h \_out0, \_out0, \_reg2 + vssran.bu.h \_out1, \_out1, \_reg2 + vssran.bu.h \_out2, \_out2, \_reg2 + vssran.bu.h \_out3, \_out3, \_reg2 +.endm + +.macro biweight_load_8 + load_double f0, f1, f2, f3, a1, a2, t0, t1 + load_double f10, f11, f12, f13, a0, a2, t0, t1 + + vilvl.d vr0, vr1, vr0 //src0 + vilvl.d vr2, vr3, vr2 //src2 + vilvl.d vr10, vr11, vr10 //dst0 + vilvl.d vr12, vr13, vr12 //dst2 + + vilvl.b vr1, vr10, vr0 //vec0.0 + vilvh.b vr3, vr10, vr0 //vec0.1 + vilvl.b vr5, vr12, vr2 //vec1.0 + vilvh.b vr7, vr12, vr2 //vec1.1 +.endm + +.macro biweight_8 + biweight_calc vr1, vr3, vr5, vr7, vr8, vr20, vr9, vr0, vr2, vr4, vr6 + vilvl.d vr0, vr2, vr0 + vilvl.d vr2, vr6, vr4 + + vbsrl.v vr1, vr0, 8 + vbsrl.v vr3, vr2, 8 + + store_double f0, f1, f2, f3, a0, a2, t0, t1 +.endm + +.macro biweight_load2_8 + biweight_load_8 + load_double f0, f2, f4, f6, t4, a2, t0, t1 + load_double f14, f15, f16, f17, t5, a2, t0, t1 + + vilvl.d vr0, vr2, vr0 //src4 + vilvl.d vr4, vr6, vr4 //src6 + vilvl.d vr14, vr15, vr14 //dst4 + vilvl.d vr16, vr17, vr16 //dst6 + + vilvl.b vr11, vr14, vr0 //vec4.0 + vilvh.b vr13, vr14, vr0 //vec4.1 + vilvl.b vr15, vr16, vr4 //vec6.0 + vilvh.b vr17, vr16, vr4 //vec6.1 +.endm + +.macro biweight2_8 + biweight_8 + biweight_calc vr11, vr13, vr15, vr17, vr8, vr20, vr9, \ + vr10, vr12, vr14, vr16 + vilvl.d vr10, vr12, vr10 + vilvl.d vr12, vr16, vr14 + + vbsrl.v vr11, vr10, 8 + vbsrl.v vr13, vr12, 8 + + store_double f10, f11, f12, f13, t5, a2, t0, t1 +.endm + +.macro biweight_load_16 + add.d t4, a1, t2 + vld vr0, a1, 0 + vldx vr1, a1, a2 + vldx vr2, a1, t0 + vldx vr3, a1, t1 + vld vr4, t4, 0 + vldx vr5, t4, a2 + vldx vr6, t4, t0 + vldx vr7, t4, t1 + + add.d t5, a0, t2 + vld vr10, a0, 0 + vldx vr11, a0, a2 + vldx vr12, a0, t0 + vldx vr13, a0, t1 + vld vr14, t5, 0 + vldx vr15, t5, a2 + vldx vr16, t5, t0 + vldx vr17, t5, t1 + + vilvl.b vr18, vr10, vr0 + vilvl.b vr19, vr11, vr1 + vilvl.b vr21, vr12, vr2 + vilvl.b vr22, vr13, vr3 + vilvh.b vr0, vr10, vr0 + vilvh.b vr1, vr11, vr1 + vilvh.b vr2, vr12, vr2 + vilvh.b vr3, vr13, vr3 + + vilvl.b vr10, vr14, vr4 + vilvl.b vr11, vr15, vr5 + vilvl.b vr12, vr16, vr6 + vilvl.b vr13, vr17, vr7 + vilvh.b vr14, vr14, vr4 + vilvh.b vr15, vr15, vr5 + vilvh.b vr16, vr16, vr6 + vilvh.b vr17, vr17, vr7 +.endm + +.macro biweight_16 + biweight_calc vr18, vr19, vr21, vr22, vr8, vr20, vr9, vr4, vr5, vr6, vr7 + biweight_calc vr0, vr1, vr2, vr3, vr8, vr20, vr9, vr18, vr19, vr21, vr22 + biweight_calc vr10, vr11, vr12, vr13, vr8, vr20, vr9, vr0, vr1, vr2, vr3 + biweight_calc vr14, vr15, vr16, vr17, vr8, vr20, vr9, vr10, vr11, vr12, vr13 + + vilvl.d vr4, vr18, vr4 + vilvl.d vr5, vr19, vr5 + vilvl.d vr6, vr21, vr6 + vilvl.d vr7, vr22, vr7 + vilvl.d vr0, vr10, vr0 + vilvl.d vr1, vr11, vr1 + vilvl.d vr2, vr12, vr2 + vilvl.d vr3, vr13, vr3 + + vst vr4, a0, 0 + vstx vr5, a0, a2 + vstx vr6, a0, t0 + vstx vr7, a0, t1 + vst vr0, t5, 0 + vstx vr1, t5, a2 + vstx vr2, t5, t0 + vstx vr3, t5, t1 +.endm + +.macro biweight_func w +function ff_biweight_h264_pixels\w\()_8_lsx + slli.d t0, a2, 1 + slli.d t2, a2, 2 + add.d t1, t0, a2 + addi.d a7, a7, 1 + ori a7, a7, 1 + sll.d a7, a7, a4 + addi.d a4, a4, 1 + + vreplgr2vr.b vr0, a6 //tmp0 + vreplgr2vr.b vr1, a5 //tmp1 + vreplgr2vr.h vr8, a7 //offset + vreplgr2vr.h vr9, a4 //denom + vilvh.b vr20, vr1, vr0 //wgt +.endm + +biweight_func 8 + addi.d t3, zero, 8 + biweight_load_8 + biweight_8 + blt a3, t3, .END_BIWEIGHT_H264_PIXELS8 + addi.d t3, zero, 16 + add.d a1, a1, t2 + add.d a0, a0, t2 + biweight_load_8 + biweight_8 + blt a3, t3, .END_BIWEIGHT_H264_PIXELS8 + add.d a1, a1, t2 + add.d a0, a0, t2 + add.d t4, a1, t2 + add.d t5, a0, t2 + biweight_load2_8 + biweight2_8 +.END_BIWEIGHT_H264_PIXELS8: +endfunc + +biweight_func 16 + addi.d t6, zero, 16 + biweight_load_16 + biweight_16 + + bne a3, t6, .END_BIWEIGHT_PIXELS16 + add.d a1, t4, t2 + add.d a0, t5, t2 + biweight_load_16 + biweight_16 +.END_BIWEIGHT_PIXELS16: +endfunc + +.macro biweight_calc_4 _in0, _out0 + vmov \_out0, vr8 + vmaddwev.h.bu.b \_out0, \_in0, vr20 + vmaddwod.h.bu.b \_out0, \_in0, vr20 + vssran.bu.h \_out0, \_out0, vr9 +.endm + +//LSX optimization is sufficient for this function. +biweight_func 4 + addi.d t3, zero, 4 + fld.s f0, a1, 0 + fldx.s f1, a1, a2 + fld.s f10, a0, 0 + fldx.s f11, a0, a2 + vilvl.w vr2, vr1, vr0 + vilvl.w vr12, vr11, vr10 + vilvl.b vr0, vr12, vr2 + + biweight_calc_4 vr0, vr1 + vbsrl.v vr2, vr1, 4 + fst.s f1, a0, 0 + fstx.s f2, a0, a2 + + blt a3, t3, .END_BIWEIGHT_H264_PIXELS4 + addi.d t3, zero, 8 + fldx.s f0, a1, t0 + fldx.s f1, a1, t1 + fldx.s f10, a0, t0 + fldx.s f11, a0, t1 + vilvl.w vr2, vr1, vr0 + vilvl.w vr12, vr11, vr10 + vilvl.b vr0, vr12, vr2 + + biweight_calc_4 vr0, vr1 + vbsrl.v vr2, vr1, 4 + fstx.s f1, a0, t0 + fstx.s f2, a0, t1 + blt a3, t3, .END_BIWEIGHT_H264_PIXELS4 + add.d a1, a1, t2 + add.d a0, a0, t2 + fld.s f0, a1, 0 + fldx.s f1, a1, a2 + fldx.s f2, a1, t0 + fldx.s f3, a1, t1 + fld.s f10, a0, 0 + fldx.s f11, a0, a2 + fldx.s f12, a0, t0 + fldx.s f13, a0, t1 + vilvl.w vr4, vr1, vr0 + vilvl.w vr5, vr3, vr2 + vilvl.w vr14, vr11, vr10 + vilvl.w vr15, vr13, vr12 + + vilvl.b vr0, vr14, vr4 + vilvl.b vr10, vr15, vr5 + + vmov vr1, vr8 + vmov vr11, vr8 + vmaddwev.h.bu.b vr1, vr0, vr20 + vmaddwev.h.bu.b vr11, vr10, vr20 + vmaddwod.h.bu.b vr1, vr0, vr20 + vmaddwod.h.bu.b vr11, vr10, vr20 + + vssran.bu.h vr0, vr1, vr9 //vec0 + vssran.bu.h vr10, vr11, vr9 //vec0 + vbsrl.v vr2, vr0, 4 + vbsrl.v vr12, vr10, 4 + + fst.s f0, a0, 0 + fstx.s f2, a0, a2 + fstx.s f10, a0, t0 + fstx.s f12, a0, t1 +.END_BIWEIGHT_H264_PIXELS4: +endfunc + +.macro biweight_func_lasx w +function ff_biweight_h264_pixels\w\()_8_lasx + slli.d t0, a2, 1 + slli.d t2, a2, 2 + add.d t1, t0, a2 + addi.d a7, a7, 1 + ori a7, a7, 1 + sll.d a7, a7, a4 + addi.d a4, a4, 1 + + xvreplgr2vr.b xr0, a6 //tmp0 + xvreplgr2vr.b xr1, a5 //tmp1 + xvreplgr2vr.h xr8, a7 //offset + xvreplgr2vr.h xr9, a4 //denom + xvilvh.b xr20, xr1, xr0 //wgt +.endm + +.macro biweight_calc_lasx _in0, _in1, _reg0, _reg1, _reg2, _out0, _out1 + xmov \_out0, \_reg0 + xmov \_out1, \_reg0 + xvmaddwev.h.bu.b \_out0, \_in0, \_reg1 + xvmaddwev.h.bu.b \_out1, \_in1, \_reg1 + xvmaddwod.h.bu.b \_out0, \_in0, \_reg1 + xvmaddwod.h.bu.b \_out1, \_in1, \_reg1 + + xvssran.bu.h \_out0, \_out0, \_reg2 + xvssran.bu.h \_out1, \_out1, \_reg2 +.endm + +.macro biweight_load_lasx_8 + load_double f0, f1, f2, f3, a1, a2, t0, t1 + load_double f10, f11, f12, f13, a0, a2, t0, t1 + + vilvl.d vr0, vr1, vr0 //src0 + vilvl.d vr2, vr3, vr2 //src2 + vilvl.d vr10, vr11, vr10 //dst0 + vilvl.d vr12, vr13, vr12 //dst2 + + xvpermi.q xr2, xr0, 0x20 + xvpermi.q xr12, xr10, 0x20 + + xvilvl.b xr0, xr12, xr2 + xvilvh.b xr1, xr12, xr2 +.endm + +.macro biweight_lasx_8 + biweight_calc_lasx xr0, xr1, xr8, xr20, xr9, xr2, xr3 + xvilvl.d xr0, xr3, xr2 + xvpermi.d xr2, xr0, 0x4E + vbsrl.v vr1, vr0, 8 + vbsrl.v vr3, vr2, 8 + + store_double f0, f1, f2, f3, a0, a2, t0, t1 +.endm + +biweight_func_lasx 8 + addi.d t3, zero, 8 + biweight_load_lasx_8 + biweight_lasx_8 + blt a3, t3, .END_BIWEIGHT_H264_PIXELS8_LASX + addi.d t3, zero, 16 + add.d a1, a1, t2 + add.d a0, a0, t2 + biweight_load_lasx_8 + biweight_lasx_8 + blt a3, t3, .END_BIWEIGHT_H264_PIXELS8_LASX + add.d a1, a1, t2 + add.d a0, a0, t2 + add.d t4, a1, t2 + add.d t5, a0, t2 + biweight_load_lasx_8 + load_double f4, f5, f6, f7, t4, a2, t0, t1 + load_double f14, f15, f16, f17, t5, a2, t0, t1 + vilvl.d vr4, vr5, vr4 //src4 + vilvl.d vr6, vr7, vr6 //src6 + vilvl.d vr14, vr15, vr14 //dst4 + vilvl.d vr16, vr17, vr16 //dst6 + xvpermi.q xr6, xr4, 0x20 + xvpermi.q xr16, xr14, 0x20 + xvilvl.b xr10, xr16, xr6 + xvilvh.b xr11, xr16, xr6 + biweight_lasx_8 + biweight_calc_lasx xr10, xr11, xr8, xr20, xr9, xr12, xr13 + xvilvl.d xr10, xr13, xr12 + xvpermi.d xr12, xr10, 0x4E + vbsrl.v vr11, vr10, 8 + vbsrl.v vr13, vr12, 8 + store_double f10, f11, f12, f13, t5, a2, t0, t1 +.END_BIWEIGHT_H264_PIXELS8_LASX: +endfunc + +.macro biweight_load_lasx_16 + add.d t4, a1, t2 + vld vr0, a1, 0 + vldx vr1, a1, a2 + vldx vr2, a1, t0 + vldx vr3, a1, t1 + vld vr4, t4, 0 + vldx vr5, t4, a2 + vldx vr6, t4, t0 + vldx vr7, t4, t1 + + add.d t5, a0, t2 + vld vr10, a0, 0 + vldx vr11, a0, a2 + vldx vr12, a0, t0 + vldx vr13, a0, t1 + vld vr14, t5, 0 + vldx vr15, t5, a2 + vldx vr16, t5, t0 + vldx vr17, t5, t1 + + xvpermi.q xr1, xr0, 0x20 + xvpermi.q xr3, xr2, 0x20 + xvpermi.q xr5, xr4, 0x20 + xvpermi.q xr7, xr6, 0x20 + + xvpermi.q xr11, xr10, 0x20 + xvpermi.q xr13, xr12, 0x20 + xvpermi.q xr15, xr14, 0x20 + xvpermi.q xr17, xr16, 0x20 + + xvilvl.b xr0, xr11, xr1 //vec0 + xvilvl.b xr2, xr13, xr3 //vec2 + xvilvl.b xr4, xr15, xr5 //vec4 + xvilvl.b xr6, xr17, xr7 //vec6 + + xvilvh.b xr10, xr11, xr1 //vec1 + xvilvh.b xr12, xr13, xr3 //vec2 + xvilvh.b xr14, xr15, xr5 //vec5 + xvilvh.b xr16, xr17, xr7 //vec7 +.endm + +.macro biweight_lasx_16 + biweight_calc_lasx xr0, xr2, xr8, xr20, xr9, xr1, xr3 + biweight_calc_lasx xr4, xr6, xr8, xr20, xr9, xr5, xr7 + biweight_calc_lasx xr10, xr12, xr8, xr20, xr9, xr11, xr13 + biweight_calc_lasx xr14, xr16, xr8, xr20, xr9, xr15, xr17 + xvilvl.d xr0, xr11, xr1 + xvilvl.d xr2, xr13, xr3 + xvilvl.d xr4, xr15, xr5 + xvilvl.d xr6, xr17, xr7 + + xvpermi.d xr1, xr0, 0x4E + xvpermi.d xr3, xr2, 0x4E + xvpermi.d xr5, xr4, 0x4E + xvpermi.d xr7, xr6, 0x4E + vst vr0, a0, 0 + vstx vr1, a0, a2 + vstx vr2, a0, t0 + vstx vr3, a0, t1 + vst vr4, t5, 0 + vstx vr5, t5, a2 + vstx vr6, t5, t0 + vstx vr7, t5, t1 +.endm + +biweight_func_lasx 16 + addi.d t6, zero, 16 + biweight_load_lasx_16 + biweight_lasx_16 + bne a3, t6, .END_BIWEIGHT_PIXELS16_LASX + add.d a1, t4, t2 + add.d a0, t5, t2 + biweight_load_lasx_16 + biweight_lasx_16 +.END_BIWEIGHT_PIXELS16_LASX: +endfunc + +.macro weight_func w +function ff_weight_h264_pixels\w\()_8_lsx + slli.d t0, a1, 1 + slli.d t2, a1, 2 + add.d t1, t0, a1 + + sll.d a5, a5, a3 + vreplgr2vr.h vr20, a4 //weight + vreplgr2vr.h vr8, a5 //offset + vreplgr2vr.h vr9, a3 //log2_denom +.endm + +.macro weight_load_16 + add.d t4, a0, t2 + vld vr0, a0, 0 + vldx vr1, a0, a1 + vldx vr2, a0, t0 + vldx vr3, a0, t1 + vld vr4, t4, 0 + vldx vr5, t4, a1 + vldx vr6, t4, t0 + vldx vr7, t4, t1 + + vilvl.b vr10, vr23, vr0 + vilvl.b vr11, vr23, vr1 + vilvl.b vr12, vr23, vr2 + vilvl.b vr13, vr23, vr3 + vilvl.b vr14, vr23, vr4 + vilvl.b vr15, vr23, vr5 + vilvl.b vr16, vr23, vr6 + vilvl.b vr17, vr23, vr7 +.endm + +.macro weight_extend_16 + vilvl.b vr10, vr23, vr0 + vilvl.b vr11, vr23, vr1 + vilvl.b vr12, vr23, vr2 + vilvl.b vr13, vr23, vr3 + vilvl.b vr14, vr23, vr4 + vilvl.b vr15, vr23, vr5 + vilvl.b vr16, vr23, vr6 + vilvl.b vr17, vr23, vr7 + + vilvh.b vr18, vr23, vr0 + vilvh.b vr19, vr23, vr1 + vilvh.b vr21, vr23, vr2 + vilvh.b vr22, vr23, vr3 + vilvh.b vr0, vr23, vr4 + vilvh.b vr1, vr23, vr5 + vilvh.b vr2, vr23, vr6 + vilvh.b vr3, vr23, vr7 +.endm + +.macro weight_calc _in0, _in1, _in2, _in3, _reg0, _reg1, _reg2, \ + _out0, _out1, _out2, _out3 + vmul.h \_in0, \_in0, \_reg1 + vmul.h \_in1, \_in1, \_reg1 + vmul.h \_in2, \_in2, \_reg1 + vmul.h \_in3, \_in3, \_reg1 + vsadd.h \_out0, \_reg0, \_in0 + vsadd.h \_out1, \_reg0, \_in1 + vsadd.h \_out2, \_reg0, \_in2 + vsadd.h \_out3, \_reg0, \_in3 + vssrarn.bu.h \_out0, \_out0, \_reg2 + vssrarn.bu.h \_out1, \_out1, \_reg2 + vssrarn.bu.h \_out2, \_out2, \_reg2 + vssrarn.bu.h \_out3, \_out3, \_reg2 +.endm + +.macro weight_16 + weight_calc vr10, vr11, vr12, vr13, vr8, vr20, vr9, vr10, vr11, vr12, vr13 + weight_calc vr14, vr15, vr16, vr17, vr8, vr20, vr9, vr14, vr15, vr16, vr17 + weight_calc vr18, vr19, vr21, vr22, vr8, vr20, vr9, vr4, vr5, vr6, vr7 + weight_calc vr0, vr1, vr2, vr3, vr8, vr20, vr9, vr0, vr1, vr2, vr3 + + vilvl.d vr10, vr4, vr10 + vilvl.d vr11, vr5, vr11 + vilvl.d vr12, vr6, vr12 + vilvl.d vr13, vr7, vr13 + vilvl.d vr14, vr0, vr14 + vilvl.d vr15, vr1, vr15 + vilvl.d vr16, vr2, vr16 + vilvl.d vr17, vr3, vr17 + + vst vr10, a0, 0 + vstx vr11, a0, a1 + vstx vr12, a0, t0 + vstx vr13, a0, t1 + vst vr14, t4, 0 + vstx vr15, t4, a1 + vstx vr16, t4, t0 + vstx vr17, t4, t1 +.endm + +weight_func 16 + vldi vr23, 0 + addi.d t3, zero, 16 + weight_load_16 + weight_extend_16 + weight_16 + bne a2, t3, .END_WEIGHT_H264_PIXELS16_8 + add.d a0, t4, t2 + weight_load_16 + weight_extend_16 + weight_16 +.END_WEIGHT_H264_PIXELS16_8: +endfunc + +.macro weight_load_8 + load_double f0, f1, f2, f3, a0, a1, t0, t1 +.endm + +.macro weight_extend_8 + vilvl.b vr10, vr21, vr0 + vilvl.b vr11, vr21, vr1 + vilvl.b vr12, vr21, vr2 + vilvl.b vr13, vr21, vr3 +.endm + +.macro weight_8 + weight_calc vr10, vr11, vr12, vr13, vr8, vr20, vr9, vr0, vr1, vr2, vr3 + store_double f0, f1, f2, f3, a0, a1, t0, t1 +.endm + +weight_func 8 + vldi vr21, 0 + addi.d t3, zero, 8 + weight_load_8 + weight_extend_8 + weight_8 + blt a2, t3, .END_WEIGHT_H264_PIXELS8 + add.d a0, a0, t2 + addi.d t3, zero, 16 + weight_load_8 + weight_extend_8 + weight_8 + blt a2, t3, .END_WEIGHT_H264_PIXELS8 + add.d a0, a0, t2 + add.d t4, a0, t2 + weight_load_8 + load_double f4, f5, f6, f7, t4, a1, t0, t1 + weight_extend_8 + vilvl.b vr14, vr21, vr4 + vilvl.b vr15, vr21, vr5 + vilvl.b vr16, vr21, vr6 + vilvl.b vr17, vr21, vr7 + weight_8 + weight_calc vr14, vr15, vr16, vr17, vr8, vr20, vr9, vr4, vr5, vr6, vr7 + store_double f4, f5, f6, f7, t4, a1, t0, t1 +.END_WEIGHT_H264_PIXELS8: +endfunc + +.macro weight_func_lasx w +function ff_weight_h264_pixels\w\()_8_lasx + slli.d t0, a1, 1 + slli.d t2, a1, 2 + add.d t1, t0, a1 + + sll.d a5, a5, a3 + xvreplgr2vr.h xr20, a4 //weight + xvreplgr2vr.h xr8, a5 //offset + xvreplgr2vr.h xr9, a3 //log2_denom +.endm + +.macro weight_calc_lasx _in0, _in1, _reg0, _reg1, _reg2, _out0, _out1 + xvmul.h \_out0, \_in0, \_reg1 + xvmul.h \_out1, \_in1, \_reg1 + xvsadd.h \_out0, \_reg0, \_out0 + xvsadd.h \_out1, \_reg0, \_out1 + xvssrarn.bu.h \_out0, \_out0, \_reg2 + xvssrarn.bu.h \_out1, \_out1, \_reg2 +.endm + +.macro weight_load_lasx_8 + load_double f0, f1, f2, f3, a0, a1, t0, t1 + vilvl.d vr4, vr1, vr0 + vilvl.d vr5, vr3, vr2 + vext2xv.hu.bu xr6, xr4 + vext2xv.hu.bu xr7, xr5 +.endm + +.macro weight_lasx_8 + weight_calc_lasx xr6, xr7, xr8, xr20, xr9, xr1, xr3 + xvpermi.d xr2, xr1, 0x2 + xvpermi.d xr4, xr3, 0x2 + store_double f1, f2, f3, f4, a0, a1, t0, t1 +.endm + +weight_func_lasx 8 + addi.d t3, zero, 8 + weight_load_lasx_8 + weight_lasx_8 + blt a2, t3, .END_WEIGHT_H264_PIXELS8_LASX + add.d a0, a0, t2 + addi.d t3, zero, 16 + weight_load_lasx_8 + weight_lasx_8 + blt a2, t3, .END_WEIGHT_H264_PIXELS8_LASX + add.d a0, a0, t2 + add.d t4, a0, t2 + weight_load_lasx_8 + load_double f14, f15, f16, f17, t4, a1, t0, t1 + vilvl.d vr4, vr15, vr14 + vilvl.d vr5, vr17, vr16 + vext2xv.hu.bu xr10, xr4 + vext2xv.hu.bu xr11, xr5 + weight_lasx_8 + weight_calc_lasx xr10, xr11, xr8, xr20, xr9, xr4, xr6 + xvpermi.d xr5, xr4, 0x2 + xvpermi.d xr7, xr6, 0x2 + store_double f4, f5, f6, f7, t4, a1, t0, t1 +.END_WEIGHT_H264_PIXELS8_LASX: +endfunc + +.macro weight_load_lasx_16 + add.d t4, a0, t2 + vld vr0, a0, 0 + vldx vr1, a0, a1 + vldx vr2, a0, t0 + vldx vr3, a0, t1 + vld vr4, t4, 0 + vldx vr5, t4, a1 + vldx vr6, t4, t0 + vldx vr7, t4, t1 + + vext2xv.hu.bu xr0, xr0 + vext2xv.hu.bu xr1, xr1 + vext2xv.hu.bu xr2, xr2 + vext2xv.hu.bu xr3, xr3 + vext2xv.hu.bu xr4, xr4 + vext2xv.hu.bu xr5, xr5 + vext2xv.hu.bu xr6, xr6 + vext2xv.hu.bu xr7, xr7 +.endm + +.macro weight_lasx_16 + weight_calc_lasx xr0, xr1, xr8, xr20, xr9, xr10, xr11 + weight_calc_lasx xr2, xr3, xr8, xr20, xr9, xr12, xr13 + weight_calc_lasx xr4, xr5, xr8, xr20, xr9, xr14, xr15 + weight_calc_lasx xr6, xr7, xr8, xr20, xr9, xr16, xr17 + xvpermi.d xr10, xr10, 0xD8 + xvpermi.d xr11, xr11, 0xD8 + xvpermi.d xr12, xr12, 0xD8 + xvpermi.d xr13, xr13, 0xD8 + xvpermi.d xr14, xr14, 0xD8 + xvpermi.d xr15, xr15, 0xD8 + xvpermi.d xr16, xr16, 0xD8 + xvpermi.d xr17, xr17, 0xD8 + + vst vr10, a0, 0 + vstx vr11, a0, a1 + vstx vr12, a0, t0 + vstx vr13, a0, t1 + vst vr14, t4, 0 + vstx vr15, t4, a1 + vstx vr16, t4, t0 + vstx vr17, t4, t1 +.endm + +weight_func_lasx 16 + addi.d t3, zero, 16 + weight_load_lasx_16 + weight_lasx_16 + bne a2, t3, .END_WEIGHT_H264_PIXELS16_8_LASX + add.d a0, t4, t2 + weight_load_lasx_16 + weight_lasx_16 +.END_WEIGHT_H264_PIXELS16_8_LASX: +endfunc + +//LSX optimization is sufficient for this function. +function ff_weight_h264_pixels4_8_lsx + add.d t0, a0, a1 + addi.d t3, zero, 4 + + sll.d a5, a5, a3 + vreplgr2vr.h vr20, a4 //weight + vreplgr2vr.h vr8, a5 //offset + vreplgr2vr.h vr9, a3 //log2_denom + vldi vr21, 0 + + fld.s f0, a0, 0 + fldx.s f1, a0, a1 + vilvl.w vr4, vr1, vr0 + vilvl.b vr5, vr21, vr4 + vmul.h vr10, vr5, vr20 + vsadd.h vr0, vr8, vr10 + vssrarn.bu.h vr0, vr0, vr9 + + fst.s f0, a0, 0 + vstelm.w vr0, t0, 0, 1 + blt a2, t3, .END_WEIGHT_H264_PIXELS4 + add.d a0, t0, a1 + addi.d t3, zero, 8 + fld.s f0, a0, 0 + fldx.s f1, a0, a1 + add.d t0, a0, a1 + vilvl.w vr4, vr1, vr0 + vilvl.b vr5, vr21, vr4 + + vmul.h vr10, vr5, vr20 + vsadd.h vr0, vr8, vr10 + vssrarn.bu.h vr0, vr0, vr9 + + fst.s f0, a0, 0 + vstelm.w vr0, t0, 0, 1 + blt a2, t3, .END_WEIGHT_H264_PIXELS4 + add.d a0, t0, a1 + add.d t0, a0, a1 + add.d t1, t0, a1 + add.d t2, t1, a1 + + fld.s f0, a0, 0 + fld.s f1, t0, 0 + fld.s f2, t1, 0 + fld.s f3, t2, 0 + + vilvl.w vr4, vr1, vr0 + vilvl.w vr5, vr3, vr2 + vilvl.b vr6, vr21, vr4 + vilvl.b vr7, vr21, vr5 + + vmul.h vr10, vr6, vr20 + vmul.h vr11, vr7, vr20 + vsadd.h vr0, vr8, vr10 + vsadd.h vr1, vr8, vr11 + vssrarn.bu.h vr10, vr0, vr9 + vssrarn.bu.h vr11, vr1, vr9 + + fst.s f10, a0, 0 + vstelm.w vr10, t0, 0, 1 + fst.s f11, t1, 0 + vstelm.w vr11, t2, 0, 1 +.END_WEIGHT_H264_PIXELS4: +endfunc + +function ff_h264_add_pixels4_8_lsx + slli.d t0, a2, 1 + add.d t1, t0, a2 + vld vr0, a1, 0 + vld vr1, a1, 16 + vldi vr2, 0 + fld.s f3, a0, 0 + fldx.s f4, a0, a2 + fldx.s f5, a0, t0 + fldx.s f6, a0, t1 + vilvl.w vr7, vr4, vr3 + vilvl.w vr8, vr6, vr5 + vilvl.b vr9, vr2, vr7 + vilvl.b vr10, vr2, vr8 + vadd.h vr11, vr0, vr9 + vadd.h vr12, vr1, vr10 + vpickev.b vr0, vr12, vr11 + vbsrl.v vr3, vr0, 4 + vbsrl.v vr4, vr0, 8 + vbsrl.v vr5, vr0, 12 + fst.s f0, a0, 0 + fstx.s f3, a0, a2 + fstx.s f4, a0, t0 + fstx.s f5, a0, t1 + vst vr2, a1, 0 + vst vr2, a1, 16 +endfunc + +function ff_h264_add_pixels8_8_lsx + slli.d t0, a2, 1 + slli.d t2, a2, 2 + add.d t1, t0, a2 + add.d t3, a0, t2 + vldi vr0, 0 + vld vr1, a1, 0 + vld vr2, a1, 16 + vld vr3, a1, 32 + vld vr4, a1, 48 + vld vr5, a1, 64 + vld vr6, a1, 80 + vld vr7, a1, 96 + vld vr8, a1, 112 + load_double f10, f11, f12, f13, a0, a2, t0, t1 + load_double f14, f15, f16, f17, t3, a2, t0, t1 + vilvl.b vr10, vr0, vr10 + vilvl.b vr11, vr0, vr11 + vilvl.b vr12, vr0, vr12 + vilvl.b vr13, vr0, vr13 + vilvl.b vr14, vr0, vr14 + vilvl.b vr15, vr0, vr15 + vilvl.b vr16, vr0, vr16 + vilvl.b vr17, vr0, vr17 + vadd.h vr1, vr1, vr10 + vadd.h vr2, vr2, vr11 + vadd.h vr3, vr3, vr12 + vadd.h vr4, vr4, vr13 + vadd.h vr5, vr5, vr14 + vadd.h vr6, vr6, vr15 + vadd.h vr7, vr7, vr16 + vadd.h vr8, vr8, vr17 + vpickev.b vr10, vr2, vr1 + vpickev.b vr12, vr4, vr3 + vpickev.b vr14, vr6, vr5 + vpickev.b vr16, vr8, vr7 + vbsrl.v vr11, vr10, 8 + vbsrl.v vr13, vr12, 8 + vbsrl.v vr15, vr14, 8 + vbsrl.v vr17, vr16, 8 + vst vr0, a1, 0 + vst vr0, a1, 16 + vst vr0, a1, 32 + vst vr0, a1, 48 + vst vr0, a1, 64 + vst vr0, a1, 80 + vst vr0, a1, 96 + vst vr0, a1, 112 + store_double f10, f11, f12, f13, a0, a2, t0, t1 + store_double f14, f15, f16, f17, t3, a2, t0, t1 +endfunc + +const cnst_value +.byte 6, 2, 6, 2, 6, 2, 6, 2, 6, 2, 6, 2, 6, 2, 6, 2 +.byte 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1 +endconst + +function ff_h264_loop_filter_strength_lsx + vldi vr0, 0 + ldptr.w t0, sp, 0 //mask_mv1 + ldptr.w t1, sp, 8 //field + beqz t1, .FIELD + la.local t2, cnst_value + vld vr1, t2, 0 + vld vr2, t2, 16 + b .END_FIELD +.FIELD: + vldi vr1, 0x06 + vldi vr2, 0x03 +.END_FIELD: + vldi vr3, 0x01 + slli.d a6, a6, 3 //step <<= 3 + slli.d a5, a5, 3 //edges <<= 3 + move t3, zero + slli.d t4, a6, 2 + move t5, a2 + move t6, a3 + move t7, a1 + move t8, a0 + slli.d t0, t0, 3 +.ITERATION_FIR: + bge t3, a5, .END_ITERATION_FIR + vand.v vr20, vr20, vr0 + and t2, t0, t3 + bnez t2, .MASK_MV_FIR + beqz a4, .BIDIR_FIR + vld vr4, t5, 4 + vld vr5, t5, 44 + vld vr6, t5, 12 + vld vr7, t5, 52 + vilvl.w vr4, vr5, vr4 + vilvl.w vr6, vr6, vr6 + vilvl.w vr7, vr7, vr7 + vshuf4i.h vr5, vr4, 0x4e + vsub.b vr6, vr6, vr4 + vsub.b vr7, vr7, vr5 + vor.v vr6, vr6, vr7 + vld vr10, t6, 16 + vld vr11, t6, 48 + vld vr12, t6, 208 + vld vr8, t6, 176 + vsub.h vr13, vr10, vr11 + vsub.h vr14, vr10, vr12 + vsub.h vr15, vr8, vr11 + vsub.h vr16, vr8, vr12 + vssrarni.b.h vr14, vr13, 0 + vssrarni.b.h vr16, vr15, 0 + vadd.b vr14, vr2, vr14 + vadd.b vr16, vr2, vr16 + vssub.bu vr14, vr14, vr1 + vssub.bu vr16, vr16, vr1 + vssrarni.b.h vr14, vr14, 0 + vssrarni.b.h vr16, vr16, 0 + vor.v vr20, vr6, vr14 + vshuf4i.h vr16, vr16, 0x4e + vor.v vr20, vr20, vr16 + vshuf4i.h vr21, vr20, 0x4e + vmin.bu vr20, vr20, vr21 + b .MASK_MV_FIR +.BIDIR_FIR: + vld vr4, t5, 4 + vld vr5, t5, 12 + vld vr10, t6, 16 + vld vr11, t6, 48 + vsub.h vr12, vr11, vr10 + vssrarni.b.h vr12, vr12, 0 + vadd.b vr13, vr12, vr2 + vssub.bu vr14, vr13, vr1 + vsat.h vr15, vr14, 7 + vpickev.b vr20, vr15, vr15 + vsub.b vr6, vr5, vr4 + vor.v vr20, vr20, vr6 +.MASK_MV_FIR: + vld vr4, t7, 12 + vld vr5, t7, 4 + vor.v vr6, vr4, vr5 + vmin.bu vr6, vr6, vr3 + vmin.bu vr20, vr20, vr3 + vslli.h vr6, vr6, 1 + vmax.bu vr6, vr20, vr6 + vilvl.b vr7, vr0, vr6 + add.d t3, t3, a6 + fst.d f7, t8, 32 + add.d t5, t5, a6 + add.d t6, t6, t4 + add.d t7, t7, a6 + add.d t8, t8, a6 + b .ITERATION_FIR +.END_ITERATION_FIR: + move t3, zero + addi.d a5, zero, 32 + vldi vr21, 0xff + move t5, a2 + move t6, a3 + move t7, a1 + move t8, a0 + slli.d a7, a7, 3 +.ITERATION_SEC: + bge t3, a5, .END_ITERATION_SEC + vand.v vr20, vr20, vr21 + and t2, a7, t3 + bnez t2, .MASK_MV_SEC + beqz a4, .BIDIR_SEC + vld vr4, t5, 11 + vld vr5, t5, 51 + vld vr6, t5, 12 + vld vr7, t5, 52 + vilvl.w vr4, vr5, vr4 + vilvl.w vr6, vr6, vr6 + vilvl.w vr7, vr7, vr7 + vshuf4i.h vr5, vr4, 0x4e + vsub.b vr6, vr6, vr4 + vsub.b vr7, vr7, vr5 + vor.v vr6, vr6, vr7 + vld vr10, t6, 44 + vld vr11, t6, 48 + vld vr12, t6, 208 + vld vr8, t6, 204 + vsub.h vr13, vr10, vr11 + vsub.h vr14, vr10, vr12 + vsub.h vr15, vr8, vr11 + vsub.h vr16, vr8, vr12 + vssrarni.b.h vr14, vr13, 0 + vssrarni.b.h vr16, vr15, 0 + vadd.b vr14, vr2, vr14 + vadd.b vr16, vr2, vr16 + vssub.bu vr14, vr14, vr1 + vssub.bu vr16, vr16, vr1 + vssrarni.b.h vr14, vr14, 0 + vssrarni.b.h vr16, vr16, 0 + vor.v vr20, vr6, vr14 + vshuf4i.h vr16, vr16, 0x4e + vor.v vr20, vr20, vr16 + vshuf4i.h vr22, vr20, 0x4e + vmin.bu vr20, vr20, vr22 + b .MASK_MV_SEC +.BIDIR_SEC: + vld vr4, t5, 11 + vld vr5, t5, 12 + vld vr10, t6, 44 + vld vr11, t6, 48 + vsub.h vr12, vr11, vr10 + vssrarni.b.h vr12, vr12, 0 + vadd.b vr13, vr12, vr2 + vssub.bu vr14, vr13, vr1 + vssrarni.b.h vr14, vr14, 0 + vsub.b vr6, vr5, vr4 + vor.v vr20, vr14, vr6 +.MASK_MV_SEC: + vld vr4, t7, 12 + vld vr5, t7, 11 + vor.v vr6, vr4, vr5 + vmin.bu vr6, vr6, vr3 + vmin.bu vr20, vr20, vr3 + vslli.h vr6, vr6, 1 + vmax.bu vr6, vr20, vr6 + vilvl.b vr7, vr0, vr6 + addi.d t3, t3, 8 + fst.d f7, t8, 0 + addi.d t5, t5, 8 + addi.d t6, t6, 32 + addi.d t7, t7, 8 + addi.d t8, t8, 8 + b .ITERATION_SEC +.END_ITERATION_SEC: + vld vr4, a0, 0 + vld vr5, a0, 16 + vilvh.d vr6, vr4, vr4 + vilvh.d vr7, vr5, vr5 + LSX_TRANSPOSE4x4_H vr4, vr6, vr5, vr7, vr6, vr7, vr8, vr9, vr10, vr11 + vilvl.d vr4, vr7, vr6 + vilvl.d vr5, vr9, vr8 + vst vr4, a0, 0 + vst vr5, a0, 16 +endfunc diff --git a/libavcodec/loongarch/h264dsp_init_loongarch.c b/libavcodec/loongarch/h264dsp_init_loongarch.c index 37633c3e51c..b70fe696d29 100644 --- a/libavcodec/loongarch/h264dsp_init_loongarch.c +++ b/libavcodec/loongarch/h264dsp_init_loongarch.c @@ -21,13 +21,55 @@ */ #include "libavutil/loongarch/cpu.h" -#include "h264dsp_lasx.h" +#include "h264dsp_loongarch.h" av_cold void ff_h264dsp_init_loongarch(H264DSPContext *c, const int bit_depth, const int chroma_format_idc) { int cpu_flags = av_get_cpu_flags(); + if (have_lsx(cpu_flags)) { + if (chroma_format_idc <= 1) + c->h264_loop_filter_strength = ff_h264_loop_filter_strength_lsx; + if (bit_depth == 8) { + c->h264_idct_add = ff_h264_idct_add_8_lsx; + c->h264_idct8_add = ff_h264_idct8_add_8_lsx; + c->h264_idct_dc_add = ff_h264_idct_dc_add_8_lsx; + c->h264_idct8_dc_add = ff_h264_idct8_dc_add_8_lsx; + + if (chroma_format_idc <= 1) { + c->h264_idct_add8 = ff_h264_idct_add8_8_lsx; + c->h264_h_loop_filter_chroma = ff_h264_h_lpf_chroma_8_lsx; + c->h264_h_loop_filter_chroma_intra = ff_h264_h_lpf_chroma_intra_8_lsx; + } else + c->h264_idct_add8 = ff_h264_idct_add8_422_8_lsx; + + c->h264_idct_add16 = ff_h264_idct_add16_8_lsx; + c->h264_idct8_add4 = ff_h264_idct8_add4_8_lsx; + c->h264_luma_dc_dequant_idct = ff_h264_luma_dc_dequant_idct_8_lsx; + c->h264_idct_add16intra = ff_h264_idct_add16_intra_8_lsx; + + c->h264_add_pixels4_clear = ff_h264_add_pixels4_8_lsx; + c->h264_add_pixels8_clear = ff_h264_add_pixels8_8_lsx; + c->h264_v_loop_filter_luma = ff_h264_v_lpf_luma_8_lsx; + c->h264_h_loop_filter_luma = ff_h264_h_lpf_luma_8_lsx; + c->h264_v_loop_filter_luma_intra = ff_h264_v_lpf_luma_intra_8_lsx; + c->h264_h_loop_filter_luma_intra = ff_h264_h_lpf_luma_intra_8_lsx; + c->h264_v_loop_filter_chroma = ff_h264_v_lpf_chroma_8_lsx; + + c->h264_v_loop_filter_chroma_intra = ff_h264_v_lpf_chroma_intra_8_lsx; + + c->biweight_h264_pixels_tab[0] = ff_biweight_h264_pixels16_8_lsx; + c->biweight_h264_pixels_tab[1] = ff_biweight_h264_pixels8_8_lsx; + c->biweight_h264_pixels_tab[2] = ff_biweight_h264_pixels4_8_lsx; + c->weight_h264_pixels_tab[0] = ff_weight_h264_pixels16_8_lsx; + c->weight_h264_pixels_tab[1] = ff_weight_h264_pixels8_8_lsx; + c->weight_h264_pixels_tab[2] = ff_weight_h264_pixels4_8_lsx; + c->h264_idct8_add = ff_h264_idct8_add_8_lsx; + c->h264_idct8_dc_add = ff_h264_idct8_dc_add_8_lsx; + } + } +#if HAVE_LASX if (have_lasx(cpu_flags)) { if (chroma_format_idc <= 1) c->h264_loop_filter_strength = ff_h264_loop_filter_strength_lasx; @@ -38,38 +80,18 @@ av_cold void ff_h264dsp_init_loongarch(H264DSPContext *c, const int bit_depth, c->h264_h_loop_filter_luma = ff_h264_h_lpf_luma_8_lasx; c->h264_v_loop_filter_luma_intra = ff_h264_v_lpf_luma_intra_8_lasx; c->h264_h_loop_filter_luma_intra = ff_h264_h_lpf_luma_intra_8_lasx; - c->h264_v_loop_filter_chroma = ff_h264_v_lpf_chroma_8_lasx; - - if (chroma_format_idc <= 1) - c->h264_h_loop_filter_chroma = ff_h264_h_lpf_chroma_8_lasx; - c->h264_v_loop_filter_chroma_intra = ff_h264_v_lpf_chroma_intra_8_lasx; - - if (chroma_format_idc <= 1) - c->h264_h_loop_filter_chroma_intra = ff_h264_h_lpf_chroma_intra_8_lasx; /* Weighted MC */ c->weight_h264_pixels_tab[0] = ff_weight_h264_pixels16_8_lasx; c->weight_h264_pixels_tab[1] = ff_weight_h264_pixels8_8_lasx; - c->weight_h264_pixels_tab[2] = ff_weight_h264_pixels4_8_lasx; c->biweight_h264_pixels_tab[0] = ff_biweight_h264_pixels16_8_lasx; c->biweight_h264_pixels_tab[1] = ff_biweight_h264_pixels8_8_lasx; - c->biweight_h264_pixels_tab[2] = ff_biweight_h264_pixels4_8_lasx; - - c->h264_idct_add = ff_h264_idct_add_lasx; - c->h264_idct8_add = ff_h264_idct8_addblk_lasx; - c->h264_idct_dc_add = ff_h264_idct4x4_addblk_dc_lasx; - c->h264_idct8_dc_add = ff_h264_idct8_dc_addblk_lasx; - c->h264_idct_add16 = ff_h264_idct_add16_lasx; - c->h264_idct8_add4 = ff_h264_idct8_add4_lasx; - - if (chroma_format_idc <= 1) - c->h264_idct_add8 = ff_h264_idct_add8_lasx; - else - c->h264_idct_add8 = ff_h264_idct_add8_422_lasx; - c->h264_idct_add16intra = ff_h264_idct_add16_intra_lasx; - c->h264_luma_dc_dequant_idct = ff_h264_deq_idct_luma_dc_lasx; + c->h264_idct8_add = ff_h264_idct8_add_8_lasx; + c->h264_idct8_dc_add = ff_h264_idct8_dc_add_8_lasx; + c->h264_idct8_add4 = ff_h264_idct8_add4_8_lasx; } } +#endif // #if HAVE_LASX } diff --git a/libavcodec/loongarch/h264dsp_lasx.c b/libavcodec/loongarch/h264dsp_lasx.c index 7fd4cedf7ef..5205cc849f1 100644 --- a/libavcodec/loongarch/h264dsp_lasx.c +++ b/libavcodec/loongarch/h264dsp_lasx.c @@ -23,7 +23,7 @@ */ #include "libavutil/loongarch/loongson_intrinsics.h" -#include "h264dsp_lasx.h" +#include "h264dsp_loongarch.h" #define AVC_LPF_P1_OR_Q1(p0_or_q0_org_in, q0_or_p0_org_in, \ p1_or_q1_org_in, p2_or_q2_org_in, \ @@ -67,10 +67,10 @@ void ff_h264_h_lpf_luma_8_lasx(uint8_t *data, ptrdiff_t img_width, int alpha_in, int beta_in, int8_t *tc) { - ptrdiff_t img_width_2x = img_width << 1; - ptrdiff_t img_width_4x = img_width << 2; - ptrdiff_t img_width_8x = img_width << 3; - ptrdiff_t img_width_3x = img_width_2x + img_width; + int img_width_2x = img_width << 1; + int img_width_4x = img_width << 2; + int img_width_8x = img_width << 3; + int img_width_3x = img_width_2x + img_width; __m256i tmp_vec0, bs_vec; __m256i tc_vec = {0x0101010100000000, 0x0303030302020202, 0x0101010100000000, 0x0303030302020202}; @@ -244,8 +244,8 @@ void ff_h264_h_lpf_luma_8_lasx(uint8_t *data, ptrdiff_t img_width, void ff_h264_v_lpf_luma_8_lasx(uint8_t *data, ptrdiff_t img_width, int alpha_in, int beta_in, int8_t *tc) { - ptrdiff_t img_width_2x = img_width << 1; - ptrdiff_t img_width_3x = img_width + img_width_2x; + int img_width_2x = img_width << 1; + int img_width_3x = img_width + img_width_2x; __m256i tmp_vec0, bs_vec; __m256i tc_vec = {0x0101010100000000, 0x0303030302020202, 0x0101010100000000, 0x0303030302020202}; @@ -363,184 +363,6 @@ void ff_h264_v_lpf_luma_8_lasx(uint8_t *data, ptrdiff_t img_width, } } -void ff_h264_h_lpf_chroma_8_lasx(uint8_t *data, ptrdiff_t img_width, - int alpha_in, int beta_in, int8_t *tc) -{ - __m256i tmp_vec0, bs_vec; - __m256i tc_vec = {0x0303020201010000, 0x0303020201010000, 0x0, 0x0}; - __m256i zero = __lasx_xvldi(0); - ptrdiff_t img_width_2x = img_width << 1; - ptrdiff_t img_width_4x = img_width << 2; - ptrdiff_t img_width_3x = img_width_2x + img_width; - - tmp_vec0 = __lasx_xvldrepl_w((uint32_t*)tc, 0); - tc_vec = __lasx_xvshuf_b(tmp_vec0, tmp_vec0, tc_vec); - bs_vec = __lasx_xvslti_b(tc_vec, 0); - bs_vec = __lasx_xvxori_b(bs_vec, 255); - bs_vec = __lasx_xvandi_b(bs_vec, 1); - bs_vec = __lasx_xvpermi_q(zero, bs_vec, 0x30); - - if (__lasx_xbnz_v(bs_vec)) { - uint8_t *src = data - 2; - __m256i p1_org, p0_org, q0_org, q1_org; - __m256i p0_asub_q0, p1_asub_p0, q1_asub_q0, alpha, beta; - __m256i is_less_than, is_less_than_beta, is_less_than_alpha; - __m256i is_bs_greater_than0; - - is_bs_greater_than0 = __lasx_xvslt_bu(zero, bs_vec); - - { - __m256i row0, row1, row2, row3, row4, row5, row6, row7; - - DUP4_ARG2(__lasx_xvldx, src, 0, src, img_width, src, img_width_2x, - src, img_width_3x, row0, row1, row2, row3); - src += img_width_4x; - DUP4_ARG2(__lasx_xvldx, src, 0, src, img_width, src, img_width_2x, - src, img_width_3x, row4, row5, row6, row7); - src -= img_width_4x; - /* LASX_TRANSPOSE8x4_B */ - DUP4_ARG2(__lasx_xvilvl_b, row2, row0, row3, row1, row6, row4, - row7, row5, p1_org, p0_org, q0_org, q1_org); - row0 = __lasx_xvilvl_b(p0_org, p1_org); - row1 = __lasx_xvilvl_b(q1_org, q0_org); - row3 = __lasx_xvilvh_w(row1, row0); - row2 = __lasx_xvilvl_w(row1, row0); - p1_org = __lasx_xvpermi_d(row2, 0x00); - p0_org = __lasx_xvpermi_d(row2, 0x55); - q0_org = __lasx_xvpermi_d(row3, 0x00); - q1_org = __lasx_xvpermi_d(row3, 0x55); - } - - p0_asub_q0 = __lasx_xvabsd_bu(p0_org, q0_org); - p1_asub_p0 = __lasx_xvabsd_bu(p1_org, p0_org); - q1_asub_q0 = __lasx_xvabsd_bu(q1_org, q0_org); - - alpha = __lasx_xvreplgr2vr_b(alpha_in); - beta = __lasx_xvreplgr2vr_b(beta_in); - - is_less_than_alpha = __lasx_xvslt_bu(p0_asub_q0, alpha); - is_less_than_beta = __lasx_xvslt_bu(p1_asub_p0, beta); - is_less_than = is_less_than_alpha & is_less_than_beta; - is_less_than_beta = __lasx_xvslt_bu(q1_asub_q0, beta); - is_less_than = is_less_than_beta & is_less_than; - is_less_than = is_less_than & is_bs_greater_than0; - - if (__lasx_xbnz_v(is_less_than)) { - __m256i p1_org_h, p0_org_h, q0_org_h, q1_org_h; - - p1_org_h = __lasx_vext2xv_hu_bu(p1_org); - p0_org_h = __lasx_vext2xv_hu_bu(p0_org); - q0_org_h = __lasx_vext2xv_hu_bu(q0_org); - q1_org_h = __lasx_vext2xv_hu_bu(q1_org); - - { - __m256i tc_h, neg_thresh_h, p0_h, q0_h; - - neg_thresh_h = __lasx_xvneg_b(tc_vec); - neg_thresh_h = __lasx_vext2xv_h_b(neg_thresh_h); - tc_h = __lasx_vext2xv_hu_bu(tc_vec); - - AVC_LPF_P0Q0(q0_org_h, p0_org_h, p1_org_h, q1_org_h, - neg_thresh_h, tc_h, p0_h, q0_h); - DUP2_ARG2(__lasx_xvpickev_b, p0_h, p0_h, q0_h, q0_h, - p0_h, q0_h); - DUP2_ARG2(__lasx_xvpermi_d, p0_h, 0xd8, q0_h, 0xd8, - p0_h, q0_h); - p0_org = __lasx_xvbitsel_v(p0_org, p0_h, is_less_than); - q0_org = __lasx_xvbitsel_v(q0_org, q0_h, is_less_than); - } - - p0_org = __lasx_xvilvl_b(q0_org, p0_org); - src = data - 1; - __lasx_xvstelm_h(p0_org, src, 0, 0); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 1); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 2); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 3); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 4); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 5); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 6); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 7); - } - } -} - -void ff_h264_v_lpf_chroma_8_lasx(uint8_t *data, ptrdiff_t img_width, - int alpha_in, int beta_in, int8_t *tc) -{ - int img_width_2x = img_width << 1; - __m256i tmp_vec0, bs_vec; - __m256i tc_vec = {0x0303020201010000, 0x0303020201010000, 0x0, 0x0}; - __m256i zero = __lasx_xvldi(0); - - tmp_vec0 = __lasx_xvldrepl_w((uint32_t*)tc, 0); - tc_vec = __lasx_xvshuf_b(tmp_vec0, tmp_vec0, tc_vec); - bs_vec = __lasx_xvslti_b(tc_vec, 0); - bs_vec = __lasx_xvxori_b(bs_vec, 255); - bs_vec = __lasx_xvandi_b(bs_vec, 1); - bs_vec = __lasx_xvpermi_q(zero, bs_vec, 0x30); - - if (__lasx_xbnz_v(bs_vec)) { - __m256i p1_org, p0_org, q0_org, q1_org; - __m256i p0_asub_q0, p1_asub_p0, q1_asub_q0, alpha, beta; - __m256i is_less_than, is_less_than_beta, is_less_than_alpha; - __m256i is_bs_greater_than0; - - alpha = __lasx_xvreplgr2vr_b(alpha_in); - beta = __lasx_xvreplgr2vr_b(beta_in); - - DUP2_ARG2(__lasx_xvldx, data, -img_width_2x, data, -img_width, - p1_org, p0_org); - DUP2_ARG2(__lasx_xvldx, data, 0, data, img_width, q0_org, q1_org); - - is_bs_greater_than0 = __lasx_xvslt_bu(zero, bs_vec); - p0_asub_q0 = __lasx_xvabsd_bu(p0_org, q0_org); - p1_asub_p0 = __lasx_xvabsd_bu(p1_org, p0_org); - q1_asub_q0 = __lasx_xvabsd_bu(q1_org, q0_org); - - is_less_than_alpha = __lasx_xvslt_bu(p0_asub_q0, alpha); - is_less_than_beta = __lasx_xvslt_bu(p1_asub_p0, beta); - is_less_than = is_less_than_alpha & is_less_than_beta; - is_less_than_beta = __lasx_xvslt_bu(q1_asub_q0, beta); - is_less_than = is_less_than_beta & is_less_than; - is_less_than = is_less_than & is_bs_greater_than0; - - if (__lasx_xbnz_v(is_less_than)) { - __m256i p1_org_h, p0_org_h, q0_org_h, q1_org_h; - - p1_org_h = __lasx_vext2xv_hu_bu(p1_org); - p0_org_h = __lasx_vext2xv_hu_bu(p0_org); - q0_org_h = __lasx_vext2xv_hu_bu(q0_org); - q1_org_h = __lasx_vext2xv_hu_bu(q1_org); - - { - __m256i neg_thresh_h, tc_h, p0_h, q0_h; - - neg_thresh_h = __lasx_xvneg_b(tc_vec); - neg_thresh_h = __lasx_vext2xv_h_b(neg_thresh_h); - tc_h = __lasx_vext2xv_hu_bu(tc_vec); - - AVC_LPF_P0Q0(q0_org_h, p0_org_h, p1_org_h, q1_org_h, - neg_thresh_h, tc_h, p0_h, q0_h); - DUP2_ARG2(__lasx_xvpickev_b, p0_h, p0_h, q0_h, q0_h, - p0_h, q0_h); - DUP2_ARG2(__lasx_xvpermi_d, p0_h, 0xd8, q0_h, 0xd8, - p0_h, q0_h); - p0_h = __lasx_xvbitsel_v(p0_org, p0_h, is_less_than); - q0_h = __lasx_xvbitsel_v(q0_org, q0_h, is_less_than); - __lasx_xvstelm_d(p0_h, data - img_width, 0, 0); - __lasx_xvstelm_d(q0_h, data, 0, 0); - } - } - } -} - #define AVC_LPF_P0P1P2_OR_Q0Q1Q2(p3_or_q3_org_in, p0_or_q0_org_in, \ q3_or_p3_org_in, p1_or_q1_org_in, \ p2_or_q2_org_in, q1_or_p1_org_in, \ @@ -584,9 +406,9 @@ void ff_h264_v_lpf_chroma_8_lasx(uint8_t *data, ptrdiff_t img_width, void ff_h264_h_lpf_luma_intra_8_lasx(uint8_t *data, ptrdiff_t img_width, int alpha_in, int beta_in) { - ptrdiff_t img_width_2x = img_width << 1; - ptrdiff_t img_width_4x = img_width << 2; - ptrdiff_t img_width_3x = img_width_2x + img_width; + int img_width_2x = img_width << 1; + int img_width_4x = img_width << 2; + int img_width_3x = img_width_2x + img_width; uint8_t *src = data - 4; __m256i p0_asub_q0, p1_asub_p0, q1_asub_q0, alpha, beta; __m256i is_less_than, is_less_than_beta, is_less_than_alpha; @@ -760,8 +582,8 @@ void ff_h264_h_lpf_luma_intra_8_lasx(uint8_t *data, ptrdiff_t img_width, void ff_h264_v_lpf_luma_intra_8_lasx(uint8_t *data, ptrdiff_t img_width, int alpha_in, int beta_in) { - ptrdiff_t img_width_2x = img_width << 1; - ptrdiff_t img_width_3x = img_width_2x + img_width; + int img_width_2x = img_width << 1; + int img_width_3x = img_width_2x + img_width; uint8_t *src = data - img_width_2x; __m256i p0_asub_q0, p1_asub_p0, q1_asub_q0, alpha, beta; __m256i is_less_than, is_less_than_beta, is_less_than_alpha; @@ -877,1160 +699,6 @@ void ff_h264_v_lpf_luma_intra_8_lasx(uint8_t *data, ptrdiff_t img_width, } } -void ff_h264_h_lpf_chroma_intra_8_lasx(uint8_t *data, ptrdiff_t img_width, - int alpha_in, int beta_in) -{ - uint8_t *src = data - 2; - ptrdiff_t img_width_2x = img_width << 1; - ptrdiff_t img_width_4x = img_width << 2; - ptrdiff_t img_width_3x = img_width_2x + img_width; - __m256i p1_org, p0_org, q0_org, q1_org; - __m256i p0_asub_q0, p1_asub_p0, q1_asub_q0, alpha, beta; - __m256i is_less_than, is_less_than_beta, is_less_than_alpha; - - { - __m256i row0, row1, row2, row3, row4, row5, row6, row7; - - DUP4_ARG2(__lasx_xvldx, src, 0, src, img_width, src, img_width_2x, src, - img_width_3x, row0, row1, row2, row3); - src += img_width_4x; - DUP4_ARG2(__lasx_xvldx, src, 0, src, img_width, src, img_width_2x, src, - img_width_3x, row4, row5, row6, row7); - - /* LASX_TRANSPOSE8x4_B */ - DUP4_ARG2(__lasx_xvilvl_b, row2, row0, row3, row1, row6, row4, row7, row5, - p1_org, p0_org, q0_org, q1_org); - row0 = __lasx_xvilvl_b(p0_org, p1_org); - row1 = __lasx_xvilvl_b(q1_org, q0_org); - row3 = __lasx_xvilvh_w(row1, row0); - row2 = __lasx_xvilvl_w(row1, row0); - p1_org = __lasx_xvpermi_d(row2, 0x00); - p0_org = __lasx_xvpermi_d(row2, 0x55); - q0_org = __lasx_xvpermi_d(row3, 0x00); - q1_org = __lasx_xvpermi_d(row3, 0x55); - } - - alpha = __lasx_xvreplgr2vr_b(alpha_in); - beta = __lasx_xvreplgr2vr_b(beta_in); - - p0_asub_q0 = __lasx_xvabsd_bu(p0_org, q0_org); - p1_asub_p0 = __lasx_xvabsd_bu(p1_org, p0_org); - q1_asub_q0 = __lasx_xvabsd_bu(q1_org, q0_org); - - is_less_than_alpha = __lasx_xvslt_bu(p0_asub_q0, alpha); - is_less_than_beta = __lasx_xvslt_bu(p1_asub_p0, beta); - is_less_than = is_less_than_alpha & is_less_than_beta; - is_less_than_beta = __lasx_xvslt_bu(q1_asub_q0, beta); - is_less_than = is_less_than_beta & is_less_than; - - if (__lasx_xbnz_v(is_less_than)) { - __m256i p0_h, q0_h, p1_org_h, p0_org_h, q0_org_h, q1_org_h; - - p1_org_h = __lasx_vext2xv_hu_bu(p1_org); - p0_org_h = __lasx_vext2xv_hu_bu(p0_org); - q0_org_h = __lasx_vext2xv_hu_bu(q0_org); - q1_org_h = __lasx_vext2xv_hu_bu(q1_org); - - AVC_LPF_P0_OR_Q0(p0_org_h, q1_org_h, p1_org_h, p0_h); - AVC_LPF_P0_OR_Q0(q0_org_h, p1_org_h, q1_org_h, q0_h); - DUP2_ARG2(__lasx_xvpickev_b, p0_h, p0_h, q0_h, q0_h, p0_h, q0_h); - DUP2_ARG2(__lasx_xvpermi_d, p0_h, 0xd8, q0_h, 0xd8, p0_h, q0_h); - p0_org = __lasx_xvbitsel_v(p0_org, p0_h, is_less_than); - q0_org = __lasx_xvbitsel_v(q0_org, q0_h, is_less_than); - } - p0_org = __lasx_xvilvl_b(q0_org, p0_org); - src = data - 1; - __lasx_xvstelm_h(p0_org, src, 0, 0); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 1); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 2); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 3); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 4); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 5); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 6); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 7); -} - -void ff_h264_v_lpf_chroma_intra_8_lasx(uint8_t *data, ptrdiff_t img_width, - int alpha_in, int beta_in) -{ - ptrdiff_t img_width_2x = img_width << 1; - __m256i p1_org, p0_org, q0_org, q1_org; - __m256i p0_asub_q0, p1_asub_p0, q1_asub_q0, alpha, beta; - __m256i is_less_than, is_less_than_beta, is_less_than_alpha; - - alpha = __lasx_xvreplgr2vr_b(alpha_in); - beta = __lasx_xvreplgr2vr_b(beta_in); - - p1_org = __lasx_xvldx(data, -img_width_2x); - p0_org = __lasx_xvldx(data, -img_width); - DUP2_ARG2(__lasx_xvldx, data, 0, data, img_width, q0_org, q1_org); - - p0_asub_q0 = __lasx_xvabsd_bu(p0_org, q0_org); - p1_asub_p0 = __lasx_xvabsd_bu(p1_org, p0_org); - q1_asub_q0 = __lasx_xvabsd_bu(q1_org, q0_org); - - is_less_than_alpha = __lasx_xvslt_bu(p0_asub_q0, alpha); - is_less_than_beta = __lasx_xvslt_bu(p1_asub_p0, beta); - is_less_than = is_less_than_alpha & is_less_than_beta; - is_less_than_beta = __lasx_xvslt_bu(q1_asub_q0, beta); - is_less_than = is_less_than_beta & is_less_than; - - if (__lasx_xbnz_v(is_less_than)) { - __m256i p0_h, q0_h, p1_org_h, p0_org_h, q0_org_h, q1_org_h; - - p1_org_h = __lasx_vext2xv_hu_bu(p1_org); - p0_org_h = __lasx_vext2xv_hu_bu(p0_org); - q0_org_h = __lasx_vext2xv_hu_bu(q0_org); - q1_org_h = __lasx_vext2xv_hu_bu(q1_org); - - AVC_LPF_P0_OR_Q0(p0_org_h, q1_org_h, p1_org_h, p0_h); - AVC_LPF_P0_OR_Q0(q0_org_h, p1_org_h, q1_org_h, q0_h); - DUP2_ARG2(__lasx_xvpickev_b, p0_h, p0_h, q0_h, q0_h, p0_h, q0_h); - DUP2_ARG2(__lasx_xvpermi_d, p0_h, 0xd8, q0_h, 0xd8, p0_h, q0_h); - p0_h = __lasx_xvbitsel_v(p0_org, p0_h, is_less_than); - q0_h = __lasx_xvbitsel_v(q0_org, q0_h, is_less_than); - __lasx_xvstelm_d(p0_h, data - img_width, 0, 0); - __lasx_xvstelm_d(q0_h, data, 0, 0); - } -} - -void ff_biweight_h264_pixels16_8_lasx(uint8_t *dst, uint8_t *src, - ptrdiff_t stride, int height, - int log2_denom, int weight_dst, - int weight_src, int offset_in) -{ - __m256i wgt; - __m256i src0, src1, src2, src3; - __m256i dst0, dst1, dst2, dst3; - __m256i vec0, vec1, vec2, vec3, vec4, vec5, vec6, vec7; - __m256i tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; - __m256i denom, offset; - int stride_2x = stride << 1; - int stride_4x = stride << 2; - int stride_3x = stride_2x + stride; - - offset_in = (unsigned) ((offset_in + 1) | 1) << log2_denom; - offset_in += ((weight_src + weight_dst) << 7); - log2_denom += 1; - - tmp0 = __lasx_xvreplgr2vr_b(weight_src); - tmp1 = __lasx_xvreplgr2vr_b(weight_dst); - wgt = __lasx_xvilvh_b(tmp1, tmp0); - offset = __lasx_xvreplgr2vr_h(offset_in); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - src += stride_4x; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp4, tmp5, tmp6, tmp7); - src += stride_4x; - DUP4_ARG3(__lasx_xvpermi_q, tmp1, tmp0, 0x20, tmp3, tmp2, 0x20, tmp5, tmp4, - 0x20, tmp7, tmp6, 0x20, src0, src1, src2, src3); - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, - dst, stride_3x, tmp0, tmp1, tmp2, tmp3); - dst += stride_4x; - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, - dst, stride_3x, tmp4, tmp5, tmp6, tmp7); - dst -= stride_4x; - DUP4_ARG3(__lasx_xvpermi_q, tmp1, tmp0, 0x20, tmp3, tmp2, 0x20, tmp5, tmp4, - 0x20, tmp7, tmp6, 0x20, dst0, dst1, dst2, dst3); - - DUP4_ARG2(__lasx_xvxori_b, src0, 128, src1, 128, src2, 128, src3, 128, - src0, src1, src2, src3); - DUP4_ARG2(__lasx_xvxori_b, dst0, 128, dst1, 128, dst2, 128, dst3, 128, - dst0, dst1, dst2, dst3); - DUP4_ARG2(__lasx_xvilvl_b, dst0, src0, dst1, src1, dst2, src2, - dst3, src3, vec0, vec2, vec4, vec6); - DUP4_ARG2(__lasx_xvilvh_b, dst0, src0, dst1, src1, dst2, src2, - dst3, src3, vec1, vec3, vec5, vec7); - - DUP4_ARG3(__lasx_xvdp2add_h_b, offset, wgt, vec0, offset, wgt, vec1, - offset, wgt, vec2, offset, wgt, vec3, tmp0, tmp1, tmp2, tmp3); - DUP4_ARG3(__lasx_xvdp2add_h_b, offset, wgt, vec4, offset, wgt, vec5, - offset, wgt, vec6, offset, wgt, vec7, tmp4, tmp5, tmp6, tmp7); - - tmp0 = __lasx_xvsra_h(tmp0, denom); - tmp1 = __lasx_xvsra_h(tmp1, denom); - tmp2 = __lasx_xvsra_h(tmp2, denom); - tmp3 = __lasx_xvsra_h(tmp3, denom); - tmp4 = __lasx_xvsra_h(tmp4, denom); - tmp5 = __lasx_xvsra_h(tmp5, denom); - tmp6 = __lasx_xvsra_h(tmp6, denom); - tmp7 = __lasx_xvsra_h(tmp7, denom); - - DUP4_ARG1(__lasx_xvclip255_h, tmp0, tmp1, tmp2, tmp3, - tmp0, tmp1, tmp2, tmp3); - DUP4_ARG1(__lasx_xvclip255_h, tmp4, tmp5, tmp6, tmp7, - tmp4, tmp5, tmp6, tmp7); - DUP4_ARG2(__lasx_xvpickev_b, tmp1, tmp0, tmp3, tmp2, tmp5, tmp4, tmp7, tmp6, - dst0, dst1, dst2, dst3); - __lasx_xvstelm_d(dst0, dst, 0, 0); - __lasx_xvstelm_d(dst0, dst, 8, 1); - dst += stride; - __lasx_xvstelm_d(dst0, dst, 0, 2); - __lasx_xvstelm_d(dst0, dst, 8, 3); - dst += stride; - __lasx_xvstelm_d(dst1, dst, 0, 0); - __lasx_xvstelm_d(dst1, dst, 8, 1); - dst += stride; - __lasx_xvstelm_d(dst1, dst, 0, 2); - __lasx_xvstelm_d(dst1, dst, 8, 3); - dst += stride; - __lasx_xvstelm_d(dst2, dst, 0, 0); - __lasx_xvstelm_d(dst2, dst, 8, 1); - dst += stride; - __lasx_xvstelm_d(dst2, dst, 0, 2); - __lasx_xvstelm_d(dst2, dst, 8, 3); - dst += stride; - __lasx_xvstelm_d(dst3, dst, 0, 0); - __lasx_xvstelm_d(dst3, dst, 8, 1); - dst += stride; - __lasx_xvstelm_d(dst3, dst, 0, 2); - __lasx_xvstelm_d(dst3, dst, 8, 3); - dst += stride; - - if (16 == height) { - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - src += stride_4x; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp4, tmp5, tmp6, tmp7); - src += stride_4x; - DUP4_ARG3(__lasx_xvpermi_q, tmp1, tmp0, 0x20, tmp3, tmp2, 0x20, tmp5, - tmp4, 0x20, tmp7, tmp6, 0x20, src0, src1, src2, src3); - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, - dst, stride_3x, tmp0, tmp1, tmp2, tmp3); - dst += stride_4x; - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, - dst, stride_3x, tmp4, tmp5, tmp6, tmp7); - dst -= stride_4x; - DUP4_ARG3(__lasx_xvpermi_q, tmp1, tmp0, 0x20, tmp3, tmp2, 0x20, tmp5, - tmp4, 0x20, tmp7, tmp6, 0x20, dst0, dst1, dst2, dst3); - - DUP4_ARG2(__lasx_xvxori_b, src0, 128, src1, 128, src2, 128, src3, 128, - src0, src1, src2, src3); - DUP4_ARG2(__lasx_xvxori_b, dst0, 128, dst1, 128, dst2, 128, dst3, 128, - dst0, dst1, dst2, dst3); - DUP4_ARG2(__lasx_xvilvl_b, dst0, src0, dst1, src1, dst2, src2, - dst3, src3, vec0, vec2, vec4, vec6); - DUP4_ARG2(__lasx_xvilvh_b, dst0, src0, dst1, src1, dst2, src2, - dst3, src3, vec1, vec3, vec5, vec7); - - DUP4_ARG3(__lasx_xvdp2add_h_b, offset, wgt, vec0, offset, wgt, vec1, - offset, wgt, vec2, offset, wgt, vec3, tmp0, tmp1, tmp2, tmp3); - DUP4_ARG3(__lasx_xvdp2add_h_b, offset, wgt, vec4, offset, wgt, vec5, - offset, wgt, vec6, offset, wgt, vec7, tmp4, tmp5, tmp6, tmp7); - - tmp0 = __lasx_xvsra_h(tmp0, denom); - tmp1 = __lasx_xvsra_h(tmp1, denom); - tmp2 = __lasx_xvsra_h(tmp2, denom); - tmp3 = __lasx_xvsra_h(tmp3, denom); - tmp4 = __lasx_xvsra_h(tmp4, denom); - tmp5 = __lasx_xvsra_h(tmp5, denom); - tmp6 = __lasx_xvsra_h(tmp6, denom); - tmp7 = __lasx_xvsra_h(tmp7, denom); - - DUP4_ARG1(__lasx_xvclip255_h, tmp0, tmp1, tmp2, tmp3, - tmp0, tmp1, tmp2, tmp3); - DUP4_ARG1(__lasx_xvclip255_h, tmp4, tmp5, tmp6, tmp7, - tmp4, tmp5, tmp6, tmp7); - DUP4_ARG2(__lasx_xvpickev_b, tmp1, tmp0, tmp3, tmp2, tmp5, tmp4, tmp7, - tmp6, dst0, dst1, dst2, dst3); - __lasx_xvstelm_d(dst0, dst, 0, 0); - __lasx_xvstelm_d(dst0, dst, 8, 1); - dst += stride; - __lasx_xvstelm_d(dst0, dst, 0, 2); - __lasx_xvstelm_d(dst0, dst, 8, 3); - dst += stride; - __lasx_xvstelm_d(dst1, dst, 0, 0); - __lasx_xvstelm_d(dst1, dst, 8, 1); - dst += stride; - __lasx_xvstelm_d(dst1, dst, 0, 2); - __lasx_xvstelm_d(dst1, dst, 8, 3); - dst += stride; - __lasx_xvstelm_d(dst2, dst, 0, 0); - __lasx_xvstelm_d(dst2, dst, 8, 1); - dst += stride; - __lasx_xvstelm_d(dst2, dst, 0, 2); - __lasx_xvstelm_d(dst2, dst, 8, 3); - dst += stride; - __lasx_xvstelm_d(dst3, dst, 0, 0); - __lasx_xvstelm_d(dst3, dst, 8, 1); - dst += stride; - __lasx_xvstelm_d(dst3, dst, 0, 2); - __lasx_xvstelm_d(dst3, dst, 8, 3); - } -} - -static void avc_biwgt_8x4_lasx(uint8_t *src, uint8_t *dst, ptrdiff_t stride, - int32_t log2_denom, int32_t weight_src, - int32_t weight_dst, int32_t offset_in) -{ - __m256i wgt, vec0, vec1; - __m256i src0, dst0; - __m256i tmp0, tmp1, tmp2, tmp3, denom, offset; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - - offset_in = (unsigned) ((offset_in + 1) | 1) << log2_denom; - offset_in += ((weight_src + weight_dst) << 7); - log2_denom += 1; - - tmp0 = __lasx_xvreplgr2vr_b(weight_src); - tmp1 = __lasx_xvreplgr2vr_b(weight_dst); - wgt = __lasx_xvilvh_b(tmp1, tmp0); - offset = __lasx_xvreplgr2vr_h(offset_in); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src0 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, - dst, stride_3x, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - dst0 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP2_ARG2(__lasx_xvxori_b, src0, 128, dst0, 128, src0, dst0); - vec0 = __lasx_xvilvl_b(dst0, src0); - vec1 = __lasx_xvilvh_b(dst0, src0); - DUP2_ARG3(__lasx_xvdp2add_h_b, offset, wgt, vec0, offset, wgt, vec1, - tmp0, tmp1); - tmp0 = __lasx_xvsra_h(tmp0, denom); - tmp1 = __lasx_xvsra_h(tmp1, denom); - DUP2_ARG1(__lasx_xvclip255_h, tmp0, tmp1, tmp0, tmp1); - dst0 = __lasx_xvpickev_b(tmp1, tmp0); - __lasx_xvstelm_d(dst0, dst, 0, 0); - __lasx_xvstelm_d(dst0, dst + stride, 0, 1); - __lasx_xvstelm_d(dst0, dst + stride_2x, 0, 2); - __lasx_xvstelm_d(dst0, dst + stride_3x, 0, 3); -} - -static void avc_biwgt_8x8_lasx(uint8_t *src, uint8_t *dst, ptrdiff_t stride, - int32_t log2_denom, int32_t weight_src, - int32_t weight_dst, int32_t offset_in) -{ - __m256i wgt, vec0, vec1, vec2, vec3; - __m256i src0, src1, dst0, dst1; - __m256i tmp0, tmp1, tmp2, tmp3, denom, offset; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_4x = stride << 2; - ptrdiff_t stride_3x = stride_2x + stride; - uint8_t* dst_tmp = dst; - - offset_in = (unsigned) ((offset_in + 1) | 1) << log2_denom; - offset_in += ((weight_src + weight_dst) << 7); - log2_denom += 1; - - tmp0 = __lasx_xvreplgr2vr_b(weight_src); - tmp1 = __lasx_xvreplgr2vr_b(weight_dst); - wgt = __lasx_xvilvh_b(tmp1, tmp0); - offset = __lasx_xvreplgr2vr_h(offset_in); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - src += stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src0 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src1 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - tmp0 = __lasx_xvld(dst_tmp, 0); - DUP2_ARG2(__lasx_xvldx, dst_tmp, stride, dst_tmp, stride_2x, tmp1, tmp2); - tmp3 = __lasx_xvldx(dst_tmp, stride_3x); - dst_tmp += stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - dst0 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, dst_tmp, 0, dst_tmp, stride, dst_tmp, stride_2x, - dst_tmp, stride_3x, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - dst1 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - - DUP4_ARG2(__lasx_xvxori_b, src0, 128, src1, 128, dst0, 128, dst1, 128, - src0, src1, dst0, dst1); - DUP2_ARG2(__lasx_xvilvl_b, dst0, src0, dst1, src1, vec0, vec2); - DUP2_ARG2(__lasx_xvilvh_b, dst0, src0, dst1, src1, vec1, vec3); - DUP4_ARG3(__lasx_xvdp2add_h_b, offset, wgt, vec0, offset, wgt, vec1, - offset, wgt, vec2, offset, wgt, vec3, tmp0, tmp1, tmp2, tmp3); - tmp0 = __lasx_xvsra_h(tmp0, denom); - tmp1 = __lasx_xvsra_h(tmp1, denom); - tmp2 = __lasx_xvsra_h(tmp2, denom); - tmp3 = __lasx_xvsra_h(tmp3, denom); - DUP4_ARG1(__lasx_xvclip255_h, tmp0, tmp1, tmp2, tmp3, - tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvpickev_b, tmp1, tmp0, tmp3, tmp2, dst0, dst1); - __lasx_xvstelm_d(dst0, dst, 0, 0); - __lasx_xvstelm_d(dst0, dst + stride, 0, 1); - __lasx_xvstelm_d(dst0, dst + stride_2x, 0, 2); - __lasx_xvstelm_d(dst0, dst + stride_3x, 0, 3); - dst += stride_4x; - __lasx_xvstelm_d(dst1, dst, 0, 0); - __lasx_xvstelm_d(dst1, dst + stride, 0, 1); - __lasx_xvstelm_d(dst1, dst + stride_2x, 0, 2); - __lasx_xvstelm_d(dst1, dst + stride_3x, 0, 3); -} - -static void avc_biwgt_8x16_lasx(uint8_t *src, uint8_t *dst, ptrdiff_t stride, - int32_t log2_denom, int32_t weight_src, - int32_t weight_dst, int32_t offset_in) -{ - __m256i wgt, vec0, vec1, vec2, vec3, vec4, vec5, vec6, vec7; - __m256i src0, src1, src2, src3, dst0, dst1, dst2, dst3; - __m256i tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, denom, offset; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_4x = stride << 2; - ptrdiff_t stride_3x = stride_2x + stride; - uint8_t* dst_tmp = dst; - - offset_in = (unsigned) ((offset_in + 1) | 1) << log2_denom; - offset_in += ((weight_src + weight_dst) << 7); - log2_denom += 1; - - tmp0 = __lasx_xvreplgr2vr_b(weight_src); - tmp1 = __lasx_xvreplgr2vr_b(weight_dst); - wgt = __lasx_xvilvh_b(tmp1, tmp0); - offset = __lasx_xvreplgr2vr_h(offset_in); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - src += stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src0 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - src += stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src1 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - src += stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src2 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src3 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - - DUP4_ARG2(__lasx_xvldx, dst_tmp, 0, dst_tmp, stride, dst_tmp, stride_2x, - dst_tmp, stride_3x, tmp0, tmp1, tmp2, tmp3); - dst_tmp += stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - dst0 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, dst_tmp, 0, dst_tmp, stride, dst_tmp, stride_2x, - dst_tmp, stride_3x, tmp0, tmp1, tmp2, tmp3); - dst_tmp += stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - dst1 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, dst_tmp, 0, dst_tmp, stride, dst_tmp, stride_2x, - dst_tmp, stride_3x, tmp0, tmp1, tmp2, tmp3); - dst_tmp += stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - dst2 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, dst_tmp, 0, dst_tmp, stride, dst_tmp, stride_2x, - dst_tmp, stride_3x, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - dst3 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - - DUP4_ARG2(__lasx_xvxori_b, src0, 128, src1, 128, src2, 128, src3, 128, - src0, src1, src2, src3); - DUP4_ARG2(__lasx_xvxori_b, dst0, 128, dst1, 128, dst2, 128, dst3, 128, - dst0, dst1, dst2, dst3); - DUP4_ARG2(__lasx_xvilvl_b, dst0, src0, dst1, src1, dst2, src2, - dst3, src3, vec0, vec2, vec4, vec6); - DUP4_ARG2(__lasx_xvilvh_b, dst0, src0, dst1, src1, dst2, src2, - dst3, src3, vec1, vec3, vec5, vec7); - DUP4_ARG3(__lasx_xvdp2add_h_b, offset, wgt, vec0, offset, wgt, vec1, - offset, wgt, vec2, offset, wgt, vec3, tmp0, tmp1, tmp2, tmp3); - DUP4_ARG3(__lasx_xvdp2add_h_b,offset, wgt, vec4, offset, wgt, vec5, - offset, wgt, vec6, offset, wgt, vec7, tmp4, tmp5, tmp6, tmp7); - tmp0 = __lasx_xvsra_h(tmp0, denom); - tmp1 = __lasx_xvsra_h(tmp1, denom); - tmp2 = __lasx_xvsra_h(tmp2, denom); - tmp3 = __lasx_xvsra_h(tmp3, denom); - tmp4 = __lasx_xvsra_h(tmp4, denom); - tmp5 = __lasx_xvsra_h(tmp5, denom); - tmp6 = __lasx_xvsra_h(tmp6, denom); - tmp7 = __lasx_xvsra_h(tmp7, denom); - DUP4_ARG1(__lasx_xvclip255_h, tmp0, tmp1, tmp2, tmp3, - tmp0, tmp1, tmp2, tmp3); - DUP4_ARG1(__lasx_xvclip255_h, tmp4, tmp5, tmp6, tmp7, - tmp4, tmp5, tmp6, tmp7); - DUP4_ARG2(__lasx_xvpickev_b, tmp1, tmp0, tmp3, tmp2, tmp5, tmp4, tmp7, tmp6, - dst0, dst1, dst2, dst3) - __lasx_xvstelm_d(dst0, dst, 0, 0); - __lasx_xvstelm_d(dst0, dst + stride, 0, 1); - __lasx_xvstelm_d(dst0, dst + stride_2x, 0, 2); - __lasx_xvstelm_d(dst0, dst + stride_3x, 0, 3); - dst += stride_4x; - __lasx_xvstelm_d(dst1, dst, 0, 0); - __lasx_xvstelm_d(dst1, dst + stride, 0, 1); - __lasx_xvstelm_d(dst1, dst + stride_2x, 0, 2); - __lasx_xvstelm_d(dst1, dst + stride_3x, 0, 3); - dst += stride_4x; - __lasx_xvstelm_d(dst2, dst, 0, 0); - __lasx_xvstelm_d(dst2, dst + stride, 0, 1); - __lasx_xvstelm_d(dst2, dst + stride_2x, 0, 2); - __lasx_xvstelm_d(dst2, dst + stride_3x, 0, 3); - dst += stride_4x; - __lasx_xvstelm_d(dst3, dst, 0, 0); - __lasx_xvstelm_d(dst3, dst + stride, 0, 1); - __lasx_xvstelm_d(dst3, dst + stride_2x, 0, 2); - __lasx_xvstelm_d(dst3, dst + stride_3x, 0, 3); -} - -void ff_biweight_h264_pixels8_8_lasx(uint8_t *dst, uint8_t *src, - ptrdiff_t stride, int height, - int log2_denom, int weight_dst, - int weight_src, int offset) -{ - if (4 == height) { - avc_biwgt_8x4_lasx(src, dst, stride, log2_denom, weight_src, weight_dst, - offset); - } else if (8 == height) { - avc_biwgt_8x8_lasx(src, dst, stride, log2_denom, weight_src, weight_dst, - offset); - } else { - avc_biwgt_8x16_lasx(src, dst, stride, log2_denom, weight_src, weight_dst, - offset); - } -} - -static void avc_biwgt_4x2_lasx(uint8_t *src, uint8_t *dst, ptrdiff_t stride, - int32_t log2_denom, int32_t weight_src, - int32_t weight_dst, int32_t offset_in) -{ - __m256i wgt, vec0; - __m256i src0, dst0; - __m256i tmp0, tmp1, denom, offset; - - offset_in = (unsigned) ((offset_in + 1) | 1) << log2_denom; - offset_in += ((weight_src + weight_dst) << 7); - log2_denom += 1; - - tmp0 = __lasx_xvreplgr2vr_b(weight_src); - tmp1 = __lasx_xvreplgr2vr_b(weight_dst); - wgt = __lasx_xvilvh_b(tmp1, tmp0); - offset = __lasx_xvreplgr2vr_h(offset_in); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP2_ARG2(__lasx_xvldx, src, 0, src, stride, tmp0, tmp1); - src0 = __lasx_xvilvl_w(tmp1, tmp0); - DUP2_ARG2(__lasx_xvldx, dst, 0, dst, stride, tmp0, tmp1); - dst0 = __lasx_xvilvl_w(tmp1, tmp0); - DUP2_ARG2(__lasx_xvxori_b, src0, 128, dst0, 128, src0, dst0); - vec0 = __lasx_xvilvl_b(dst0, src0); - tmp0 = __lasx_xvdp2add_h_b(offset, wgt, vec0); - tmp0 = __lasx_xvsra_h(tmp0, denom); - tmp0 = __lasx_xvclip255_h(tmp0); - tmp0 = __lasx_xvpickev_b(tmp0, tmp0); - __lasx_xvstelm_w(tmp0, dst, 0, 0); - __lasx_xvstelm_w(tmp0, dst + stride, 0, 1); -} - -static void avc_biwgt_4x4_lasx(uint8_t *src, uint8_t *dst, ptrdiff_t stride, - int32_t log2_denom, int32_t weight_src, - int32_t weight_dst, int32_t offset_in) -{ - __m256i wgt, vec0; - __m256i src0, dst0; - __m256i tmp0, tmp1, tmp2, tmp3, denom, offset; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - - offset_in = (unsigned) ((offset_in + 1) | 1) << log2_denom; - offset_in += ((weight_src + weight_dst) << 7); - log2_denom += 1; - - tmp0 = __lasx_xvreplgr2vr_b(weight_src); - tmp1 = __lasx_xvreplgr2vr_b(weight_dst); - wgt = __lasx_xvilvh_b(tmp1, tmp0); - offset = __lasx_xvreplgr2vr_h(offset_in); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_w, tmp2, tmp0, tmp3, tmp1, tmp0, tmp1); - src0 = __lasx_xvilvl_w(tmp1, tmp0); - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, - dst, stride_3x, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_w, tmp2, tmp0, tmp3, tmp1, tmp0, tmp1); - dst0 = __lasx_xvilvl_w(tmp1, tmp0); - DUP2_ARG2(__lasx_xvxori_b, src0, 128, dst0, 128, src0, dst0); - vec0 = __lasx_xvilvl_b(dst0, src0); - dst0 = __lasx_xvilvh_b(dst0, src0); - vec0 = __lasx_xvpermi_q(vec0, dst0, 0x02); - tmp0 = __lasx_xvdp2add_h_b(offset, wgt, vec0); - tmp0 = __lasx_xvsra_h(tmp0, denom); - tmp0 = __lasx_xvclip255_h(tmp0); - tmp0 = __lasx_xvpickev_b(tmp0, tmp0); - __lasx_xvstelm_w(tmp0, dst, 0, 0); - __lasx_xvstelm_w(tmp0, dst + stride, 0, 1); - __lasx_xvstelm_w(tmp0, dst + stride_2x, 0, 4); - __lasx_xvstelm_w(tmp0, dst + stride_3x, 0, 5); -} - -static void avc_biwgt_4x8_lasx(uint8_t *src, uint8_t *dst, ptrdiff_t stride, - int32_t log2_denom, int32_t weight_src, - int32_t weight_dst, int32_t offset_in) -{ - __m256i wgt, vec0, vec1; - __m256i src0, dst0; - __m256i tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, denom, offset; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_4x = stride << 2; - ptrdiff_t stride_3x = stride_2x + stride; - - offset_in = (unsigned) ((offset_in + 1) | 1) << log2_denom; - offset_in += ((weight_src + weight_dst) << 7); - log2_denom += 1; - - tmp0 = __lasx_xvreplgr2vr_b(weight_src); - tmp1 = __lasx_xvreplgr2vr_b(weight_dst); - wgt = __lasx_xvilvh_b(tmp1, tmp0); - offset = __lasx_xvreplgr2vr_h(offset_in); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - src += stride_4x; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp4, tmp5, tmp6, tmp7); - DUP4_ARG2(__lasx_xvilvl_w, tmp2, tmp0, tmp3, tmp1, tmp6, tmp4, tmp7, tmp5, - tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_w, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src0 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, - dst, stride_3x, tmp0, tmp1, tmp2, tmp3); - dst += stride_4x; - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, - dst, stride_3x, tmp4, tmp5, tmp6, tmp7); - dst -= stride_4x; - DUP4_ARG2(__lasx_xvilvl_w, tmp2, tmp0, tmp3, tmp1, tmp6, tmp4, tmp7, tmp5, - tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_w, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - dst0 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP2_ARG2(__lasx_xvxori_b, src0, 128, dst0, 128, src0, dst0); - vec0 = __lasx_xvilvl_b(dst0, src0); - vec1 = __lasx_xvilvh_b(dst0, src0); - DUP2_ARG3(__lasx_xvdp2add_h_b, offset, wgt, vec0, offset, wgt, vec1, - tmp0, tmp1); - tmp0 = __lasx_xvsra_h(tmp0, denom); - tmp1 = __lasx_xvsra_h(tmp1, denom); - DUP2_ARG1(__lasx_xvclip255_h, tmp0, tmp1, tmp0, tmp1); - tmp0 = __lasx_xvpickev_b(tmp1, tmp0); - __lasx_xvstelm_w(tmp0, dst, 0, 0); - __lasx_xvstelm_w(tmp0, dst + stride, 0, 1); - __lasx_xvstelm_w(tmp0, dst + stride_2x, 0, 2); - __lasx_xvstelm_w(tmp0, dst + stride_3x, 0, 3); - dst += stride_4x; - __lasx_xvstelm_w(tmp0, dst, 0, 4); - __lasx_xvstelm_w(tmp0, dst + stride, 0, 5); - __lasx_xvstelm_w(tmp0, dst + stride_2x, 0, 6); - __lasx_xvstelm_w(tmp0, dst + stride_3x, 0, 7); -} - -void ff_biweight_h264_pixels4_8_lasx(uint8_t *dst, uint8_t *src, - ptrdiff_t stride, int height, - int log2_denom, int weight_dst, - int weight_src, int offset) -{ - if (2 == height) { - avc_biwgt_4x2_lasx(src, dst, stride, log2_denom, weight_src, - weight_dst, offset); - } else if (4 == height) { - avc_biwgt_4x4_lasx(src, dst, stride, log2_denom, weight_src, - weight_dst, offset); - } else { - avc_biwgt_4x8_lasx(src, dst, stride, log2_denom, weight_src, - weight_dst, offset); - } -} - -void ff_weight_h264_pixels16_8_lasx(uint8_t *src, ptrdiff_t stride, - int height, int log2_denom, - int weight_src, int offset_in) -{ - uint32_t offset_val; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_4x = stride << 2; - ptrdiff_t stride_3x = stride_2x + stride; - __m256i zero = __lasx_xvldi(0); - __m256i src0, src1, src2, src3; - __m256i src0_l, src1_l, src2_l, src3_l, src0_h, src1_h, src2_h, src3_h; - __m256i tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; - __m256i wgt, denom, offset; - - offset_val = (unsigned) offset_in << log2_denom; - - wgt = __lasx_xvreplgr2vr_h(weight_src); - offset = __lasx_xvreplgr2vr_h(offset_val); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - src += stride_4x; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp4, tmp5, tmp6, tmp7); - src -= stride_4x; - DUP4_ARG3(__lasx_xvpermi_q, tmp1, tmp0, 0x20, tmp3, tmp2, 0x20, tmp5, tmp4, - 0x20, tmp7, tmp6, 0x20, src0, src1, src2, src3); - DUP4_ARG2(__lasx_xvilvl_b, zero, src0, zero, src1, zero, src2, - zero, src3, src0_l, src1_l, src2_l, src3_l); - DUP4_ARG2(__lasx_xvilvh_b, zero, src0, zero, src1, zero, src2, - zero, src3, src0_h, src1_h, src2_h, src3_h); - src0_l = __lasx_xvmul_h(wgt, src0_l); - src0_h = __lasx_xvmul_h(wgt, src0_h); - src1_l = __lasx_xvmul_h(wgt, src1_l); - src1_h = __lasx_xvmul_h(wgt, src1_h); - src2_l = __lasx_xvmul_h(wgt, src2_l); - src2_h = __lasx_xvmul_h(wgt, src2_h); - src3_l = __lasx_xvmul_h(wgt, src3_l); - src3_h = __lasx_xvmul_h(wgt, src3_h); - DUP4_ARG2(__lasx_xvsadd_h, src0_l, offset, src0_h, offset, src1_l, offset, - src1_h, offset, src0_l, src0_h, src1_l, src1_h); - DUP4_ARG2(__lasx_xvsadd_h, src2_l, offset, src2_h, offset, src3_l, offset, - src3_h, offset, src2_l, src2_h, src3_l, src3_h); - src0_l = __lasx_xvmaxi_h(src0_l, 0); - src0_h = __lasx_xvmaxi_h(src0_h, 0); - src1_l = __lasx_xvmaxi_h(src1_l, 0); - src1_h = __lasx_xvmaxi_h(src1_h, 0); - src2_l = __lasx_xvmaxi_h(src2_l, 0); - src2_h = __lasx_xvmaxi_h(src2_h, 0); - src3_l = __lasx_xvmaxi_h(src3_l, 0); - src3_h = __lasx_xvmaxi_h(src3_h, 0); - src0_l = __lasx_xvssrlrn_bu_h(src0_l, denom); - src0_h = __lasx_xvssrlrn_bu_h(src0_h, denom); - src1_l = __lasx_xvssrlrn_bu_h(src1_l, denom); - src1_h = __lasx_xvssrlrn_bu_h(src1_h, denom); - src2_l = __lasx_xvssrlrn_bu_h(src2_l, denom); - src2_h = __lasx_xvssrlrn_bu_h(src2_h, denom); - src3_l = __lasx_xvssrlrn_bu_h(src3_l, denom); - src3_h = __lasx_xvssrlrn_bu_h(src3_h, denom); - __lasx_xvstelm_d(src0_l, src, 0, 0); - __lasx_xvstelm_d(src0_h, src, 8, 0); - src += stride; - __lasx_xvstelm_d(src0_l, src, 0, 2); - __lasx_xvstelm_d(src0_h, src, 8, 2); - src += stride; - __lasx_xvstelm_d(src1_l, src, 0, 0); - __lasx_xvstelm_d(src1_h, src, 8, 0); - src += stride; - __lasx_xvstelm_d(src1_l, src, 0, 2); - __lasx_xvstelm_d(src1_h, src, 8, 2); - src += stride; - __lasx_xvstelm_d(src2_l, src, 0, 0); - __lasx_xvstelm_d(src2_h, src, 8, 0); - src += stride; - __lasx_xvstelm_d(src2_l, src, 0, 2); - __lasx_xvstelm_d(src2_h, src, 8, 2); - src += stride; - __lasx_xvstelm_d(src3_l, src, 0, 0); - __lasx_xvstelm_d(src3_h, src, 8, 0); - src += stride; - __lasx_xvstelm_d(src3_l, src, 0, 2); - __lasx_xvstelm_d(src3_h, src, 8, 2); - src += stride; - - if (16 == height) { - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - src += stride_4x; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp4, tmp5, tmp6, tmp7); - src -= stride_4x; - DUP4_ARG3(__lasx_xvpermi_q, tmp1, tmp0, 0x20, tmp3, tmp2, 0x20, tmp5, - tmp4, 0x20, tmp7, tmp6, 0x20, src0, src1, src2, src3); - DUP4_ARG2(__lasx_xvilvl_b, zero, src0, zero, src1, zero, src2, - zero, src3, src0_l, src1_l, src2_l, src3_l); - DUP4_ARG2(__lasx_xvilvh_b, zero, src0, zero, src1, zero, src2, - zero, src3, src0_h, src1_h, src2_h, src3_h); - src0_l = __lasx_xvmul_h(wgt, src0_l); - src0_h = __lasx_xvmul_h(wgt, src0_h); - src1_l = __lasx_xvmul_h(wgt, src1_l); - src1_h = __lasx_xvmul_h(wgt, src1_h); - src2_l = __lasx_xvmul_h(wgt, src2_l); - src2_h = __lasx_xvmul_h(wgt, src2_h); - src3_l = __lasx_xvmul_h(wgt, src3_l); - src3_h = __lasx_xvmul_h(wgt, src3_h); - DUP4_ARG2(__lasx_xvsadd_h, src0_l, offset, src0_h, offset, src1_l, - offset, src1_h, offset, src0_l, src0_h, src1_l, src1_h); - DUP4_ARG2(__lasx_xvsadd_h, src2_l, offset, src2_h, offset, src3_l, - offset, src3_h, offset, src2_l, src2_h, src3_l, src3_h); - src0_l = __lasx_xvmaxi_h(src0_l, 0); - src0_h = __lasx_xvmaxi_h(src0_h, 0); - src1_l = __lasx_xvmaxi_h(src1_l, 0); - src1_h = __lasx_xvmaxi_h(src1_h, 0); - src2_l = __lasx_xvmaxi_h(src2_l, 0); - src2_h = __lasx_xvmaxi_h(src2_h, 0); - src3_l = __lasx_xvmaxi_h(src3_l, 0); - src3_h = __lasx_xvmaxi_h(src3_h, 0); - src0_l = __lasx_xvssrlrn_bu_h(src0_l, denom); - src0_h = __lasx_xvssrlrn_bu_h(src0_h, denom); - src1_l = __lasx_xvssrlrn_bu_h(src1_l, denom); - src1_h = __lasx_xvssrlrn_bu_h(src1_h, denom); - src2_l = __lasx_xvssrlrn_bu_h(src2_l, denom); - src2_h = __lasx_xvssrlrn_bu_h(src2_h, denom); - src3_l = __lasx_xvssrlrn_bu_h(src3_l, denom); - src3_h = __lasx_xvssrlrn_bu_h(src3_h, denom); - __lasx_xvstelm_d(src0_l, src, 0, 0); - __lasx_xvstelm_d(src0_h, src, 8, 0); - src += stride; - __lasx_xvstelm_d(src0_l, src, 0, 2); - __lasx_xvstelm_d(src0_h, src, 8, 2); - src += stride; - __lasx_xvstelm_d(src1_l, src, 0, 0); - __lasx_xvstelm_d(src1_h, src, 8, 0); - src += stride; - __lasx_xvstelm_d(src1_l, src, 0, 2); - __lasx_xvstelm_d(src1_h, src, 8, 2); - src += stride; - __lasx_xvstelm_d(src2_l, src, 0, 0); - __lasx_xvstelm_d(src2_h, src, 8, 0); - src += stride; - __lasx_xvstelm_d(src2_l, src, 0, 2); - __lasx_xvstelm_d(src2_h, src, 8, 2); - src += stride; - __lasx_xvstelm_d(src3_l, src, 0, 0); - __lasx_xvstelm_d(src3_h, src, 8, 0); - src += stride; - __lasx_xvstelm_d(src3_l, src, 0, 2); - __lasx_xvstelm_d(src3_h, src, 8, 2); - } -} - -static void avc_wgt_8x4_lasx(uint8_t *src, ptrdiff_t stride, - int32_t log2_denom, int32_t weight_src, - int32_t offset_in) -{ - uint32_t offset_val; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - __m256i wgt, zero = __lasx_xvldi(0); - __m256i src0, src0_h, src0_l; - __m256i tmp0, tmp1, tmp2, tmp3, denom, offset; - - offset_val = (unsigned) offset_in << log2_denom; - - wgt = __lasx_xvreplgr2vr_h(weight_src); - offset = __lasx_xvreplgr2vr_h(offset_val); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src0 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - src0_l = __lasx_xvilvl_b(zero, src0); - src0_h = __lasx_xvilvh_b(zero, src0); - src0_l = __lasx_xvmul_h(wgt, src0_l); - src0_h = __lasx_xvmul_h(wgt, src0_h); - src0_l = __lasx_xvsadd_h(src0_l, offset); - src0_h = __lasx_xvsadd_h(src0_h, offset); - src0_l = __lasx_xvmaxi_h(src0_l, 0); - src0_h = __lasx_xvmaxi_h(src0_h, 0); - src0_l = __lasx_xvssrlrn_bu_h(src0_l, denom); - src0_h = __lasx_xvssrlrn_bu_h(src0_h, denom); - - src0 = __lasx_xvpickev_d(src0_h, src0_l); - __lasx_xvstelm_d(src0, src, 0, 0); - __lasx_xvstelm_d(src0, src + stride, 0, 1); - __lasx_xvstelm_d(src0, src + stride_2x, 0, 2); - __lasx_xvstelm_d(src0, src + stride_3x, 0, 3); -} - -static void avc_wgt_8x8_lasx(uint8_t *src, ptrdiff_t stride, int32_t log2_denom, - int32_t src_weight, int32_t offset_in) -{ - __m256i src0, src1, src0_h, src0_l, src1_h, src1_l, zero = __lasx_xvldi(0); - __m256i tmp0, tmp1, tmp2, tmp3, denom, offset, wgt; - uint32_t offset_val; - uint8_t* src_tmp = src; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_4x = stride << 2; - ptrdiff_t stride_3x = stride_2x + stride; - - offset_val = (unsigned) offset_in << log2_denom; - - wgt = __lasx_xvreplgr2vr_h(src_weight); - offset = __lasx_xvreplgr2vr_h(offset_val); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP4_ARG2(__lasx_xvldx, src_tmp, 0, src_tmp, stride, src_tmp, stride_2x, - src_tmp, stride_3x, tmp0, tmp1, tmp2, tmp3); - src_tmp += stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src0 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, src_tmp, 0, src_tmp, stride, src_tmp, stride_2x, - src_tmp, stride_3x, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src1 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP2_ARG2(__lasx_xvilvl_b, zero, src0, zero, src1, src0_l, src1_l); - DUP2_ARG2(__lasx_xvilvh_b, zero, src0, zero, src1, src0_h, src1_h); - src0_l = __lasx_xvmul_h(wgt, src0_l); - src0_h = __lasx_xvmul_h(wgt, src0_h); - src1_l = __lasx_xvmul_h(wgt, src1_l); - src1_h = __lasx_xvmul_h(wgt, src1_h); - DUP4_ARG2(__lasx_xvsadd_h, src0_l, offset, src0_h, offset, src1_l, offset, - src1_h, offset, src0_l, src0_h, src1_l, src1_h); - src0_l = __lasx_xvmaxi_h(src0_l, 0); - src0_h = __lasx_xvmaxi_h(src0_h, 0); - src1_l = __lasx_xvmaxi_h(src1_l, 0); - src1_h = __lasx_xvmaxi_h(src1_h, 0); - src0_l = __lasx_xvssrlrn_bu_h(src0_l, denom); - src0_h = __lasx_xvssrlrn_bu_h(src0_h, denom); - src1_l = __lasx_xvssrlrn_bu_h(src1_l, denom); - src1_h = __lasx_xvssrlrn_bu_h(src1_h, denom); - - DUP2_ARG2(__lasx_xvpickev_d, src0_h, src0_l, src1_h, src1_l, src0, src1); - __lasx_xvstelm_d(src0, src, 0, 0); - __lasx_xvstelm_d(src0, src + stride, 0, 1); - __lasx_xvstelm_d(src0, src + stride_2x, 0, 2); - __lasx_xvstelm_d(src0, src + stride_3x, 0, 3); - src += stride_4x; - __lasx_xvstelm_d(src1, src, 0, 0); - __lasx_xvstelm_d(src1, src + stride, 0, 1); - __lasx_xvstelm_d(src1, src + stride_2x, 0, 2); - __lasx_xvstelm_d(src1, src + stride_3x, 0, 3); -} - -static void avc_wgt_8x16_lasx(uint8_t *src, ptrdiff_t stride, - int32_t log2_denom, int32_t src_weight, - int32_t offset_in) -{ - __m256i src0, src1, src2, src3; - __m256i src0_h, src0_l, src1_h, src1_l, src2_h, src2_l, src3_h, src3_l; - __m256i tmp0, tmp1, tmp2, tmp3, denom, offset, wgt; - __m256i zero = __lasx_xvldi(0); - uint32_t offset_val; - uint8_t* src_tmp = src; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_4x = stride << 2; - ptrdiff_t stride_3x = stride_2x + stride; - - offset_val = (unsigned) offset_in << log2_denom; - - wgt = __lasx_xvreplgr2vr_h(src_weight); - offset = __lasx_xvreplgr2vr_h(offset_val); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP4_ARG2(__lasx_xvldx, src_tmp, 0, src_tmp, stride, src_tmp, stride_2x, - src_tmp, stride_3x, tmp0, tmp1, tmp2, tmp3); - src_tmp += stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src0 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, src_tmp, 0, src_tmp, stride, src_tmp, stride_2x, - src_tmp, stride_3x, tmp0, tmp1, tmp2, tmp3); - src_tmp += stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src1 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, src_tmp, 0, src_tmp, stride, src_tmp, stride_2x, - src_tmp, stride_3x, tmp0, tmp1, tmp2, tmp3); - src_tmp += stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src2 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, src_tmp, 0, src_tmp, stride, src_tmp, stride_2x, - src_tmp, stride_3x, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src3 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - - DUP4_ARG2(__lasx_xvilvl_b, zero, src0, zero, src1, zero, src2, zero, src3, - src0_l, src1_l, src2_l, src3_l); - DUP4_ARG2(__lasx_xvilvh_b, zero, src0, zero, src1, zero, src2, zero, src3, - src0_h, src1_h, src2_h, src3_h); - src0_l = __lasx_xvmul_h(wgt, src0_l); - src0_h = __lasx_xvmul_h(wgt, src0_h); - src1_l = __lasx_xvmul_h(wgt, src1_l); - src1_h = __lasx_xvmul_h(wgt, src1_h); - src2_l = __lasx_xvmul_h(wgt, src2_l); - src2_h = __lasx_xvmul_h(wgt, src2_h); - src3_l = __lasx_xvmul_h(wgt, src3_l); - src3_h = __lasx_xvmul_h(wgt, src3_h); - - DUP4_ARG2(__lasx_xvsadd_h, src0_l, offset, src0_h, offset, src1_l, offset, - src1_h, offset, src0_l, src0_h, src1_l, src1_h); - DUP4_ARG2(__lasx_xvsadd_h, src2_l, offset, src2_h, offset, src3_l, offset, - src3_h, offset, src2_l, src2_h, src3_l, src3_h); - - src0_l = __lasx_xvmaxi_h(src0_l, 0); - src0_h = __lasx_xvmaxi_h(src0_h, 0); - src1_l = __lasx_xvmaxi_h(src1_l, 0); - src1_h = __lasx_xvmaxi_h(src1_h, 0); - src2_l = __lasx_xvmaxi_h(src2_l, 0); - src2_h = __lasx_xvmaxi_h(src2_h, 0); - src3_l = __lasx_xvmaxi_h(src3_l, 0); - src3_h = __lasx_xvmaxi_h(src3_h, 0); - src0_l = __lasx_xvssrlrn_bu_h(src0_l, denom); - src0_h = __lasx_xvssrlrn_bu_h(src0_h, denom); - src1_l = __lasx_xvssrlrn_bu_h(src1_l, denom); - src1_h = __lasx_xvssrlrn_bu_h(src1_h, denom); - src2_l = __lasx_xvssrlrn_bu_h(src2_l, denom); - src2_h = __lasx_xvssrlrn_bu_h(src2_h, denom); - src3_l = __lasx_xvssrlrn_bu_h(src3_l, denom); - src3_h = __lasx_xvssrlrn_bu_h(src3_h, denom); - DUP4_ARG2(__lasx_xvpickev_d, src0_h, src0_l, src1_h, src1_l, src2_h, src2_l, - src3_h, src3_l, src0, src1, src2, src3); - - __lasx_xvstelm_d(src0, src, 0, 0); - __lasx_xvstelm_d(src0, src + stride, 0, 1); - __lasx_xvstelm_d(src0, src + stride_2x, 0, 2); - __lasx_xvstelm_d(src0, src + stride_3x, 0, 3); - src += stride_4x; - __lasx_xvstelm_d(src1, src, 0, 0); - __lasx_xvstelm_d(src1, src + stride, 0, 1); - __lasx_xvstelm_d(src1, src + stride_2x, 0, 2); - __lasx_xvstelm_d(src1, src + stride_3x, 0, 3); - src += stride_4x; - __lasx_xvstelm_d(src2, src, 0, 0); - __lasx_xvstelm_d(src2, src + stride, 0, 1); - __lasx_xvstelm_d(src2, src + stride_2x, 0, 2); - __lasx_xvstelm_d(src2, src + stride_3x, 0, 3); - src += stride_4x; - __lasx_xvstelm_d(src3, src, 0, 0); - __lasx_xvstelm_d(src3, src + stride, 0, 1); - __lasx_xvstelm_d(src3, src + stride_2x, 0, 2); - __lasx_xvstelm_d(src3, src + stride_3x, 0, 3); -} - -void ff_weight_h264_pixels8_8_lasx(uint8_t *src, ptrdiff_t stride, - int height, int log2_denom, - int weight_src, int offset) -{ - if (4 == height) { - avc_wgt_8x4_lasx(src, stride, log2_denom, weight_src, offset); - } else if (8 == height) { - avc_wgt_8x8_lasx(src, stride, log2_denom, weight_src, offset); - } else { - avc_wgt_8x16_lasx(src, stride, log2_denom, weight_src, offset); - } -} - -static void avc_wgt_4x2_lasx(uint8_t *src, ptrdiff_t stride, - int32_t log2_denom, int32_t weight_src, - int32_t offset_in) -{ - uint32_t offset_val; - __m256i wgt, zero = __lasx_xvldi(0); - __m256i src0, tmp0, tmp1, denom, offset; - - offset_val = (unsigned) offset_in << log2_denom; - - wgt = __lasx_xvreplgr2vr_h(weight_src); - offset = __lasx_xvreplgr2vr_h(offset_val); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP2_ARG2(__lasx_xvldx, src, 0, src, stride, tmp0, tmp1); - src0 = __lasx_xvilvl_w(tmp1, tmp0); - src0 = __lasx_xvilvl_b(zero, src0); - src0 = __lasx_xvmul_h(wgt, src0); - src0 = __lasx_xvsadd_h(src0, offset); - src0 = __lasx_xvmaxi_h(src0, 0); - src0 = __lasx_xvssrlrn_bu_h(src0, denom); - __lasx_xvstelm_w(src0, src, 0, 0); - __lasx_xvstelm_w(src0, src + stride, 0, 1); -} - -static void avc_wgt_4x4_lasx(uint8_t *src, ptrdiff_t stride, - int32_t log2_denom, int32_t weight_src, - int32_t offset_in) -{ - __m256i wgt; - __m256i src0, tmp0, tmp1, tmp2, tmp3, denom, offset; - uint32_t offset_val; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - - offset_val = (unsigned) offset_in << log2_denom; - - wgt = __lasx_xvreplgr2vr_h(weight_src); - offset = __lasx_xvreplgr2vr_h(offset_val); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_w, tmp2, tmp0, tmp3, tmp1, tmp0, tmp1); - src0 = __lasx_xvilvl_w(tmp1, tmp0); - src0 = __lasx_vext2xv_hu_bu(src0); - src0 = __lasx_xvmul_h(wgt, src0); - src0 = __lasx_xvsadd_h(src0, offset); - src0 = __lasx_xvmaxi_h(src0, 0); - src0 = __lasx_xvssrlrn_bu_h(src0, denom); - __lasx_xvstelm_w(src0, src, 0, 0); - __lasx_xvstelm_w(src0, src + stride, 0, 1); - __lasx_xvstelm_w(src0, src + stride_2x, 0, 4); - __lasx_xvstelm_w(src0, src + stride_3x, 0, 5); -} - -static void avc_wgt_4x8_lasx(uint8_t *src, ptrdiff_t stride, - int32_t log2_denom, int32_t weight_src, - int32_t offset_in) -{ - __m256i src0, src0_h, src0_l; - __m256i tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, denom, offset; - __m256i wgt, zero = __lasx_xvldi(0); - uint32_t offset_val; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_4x = stride << 2; - ptrdiff_t stride_3x = stride_2x + stride; - - offset_val = (unsigned) offset_in << log2_denom; - - wgt = __lasx_xvreplgr2vr_h(weight_src); - offset = __lasx_xvreplgr2vr_h(offset_val); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - src += stride_4x; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp4, tmp5, tmp6, tmp7); - src -= stride_4x; - DUP4_ARG2(__lasx_xvilvl_w, tmp2, tmp0, tmp3, tmp1, tmp6, tmp4, tmp7, - tmp5, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_w, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src0 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - src0_l = __lasx_xvilvl_b(zero, src0); - src0_h = __lasx_xvilvh_b(zero, src0); - src0_l = __lasx_xvmul_h(wgt, src0_l); - src0_h = __lasx_xvmul_h(wgt, src0_h); - src0_l = __lasx_xvsadd_h(src0_l, offset); - src0_h = __lasx_xvsadd_h(src0_h, offset); - src0_l = __lasx_xvmaxi_h(src0_l, 0); - src0_h = __lasx_xvmaxi_h(src0_h, 0); - src0_l = __lasx_xvssrlrn_bu_h(src0_l, denom); - src0_h = __lasx_xvssrlrn_bu_h(src0_h, denom); - __lasx_xvstelm_w(src0_l, src, 0, 0); - __lasx_xvstelm_w(src0_l, src + stride, 0, 1); - __lasx_xvstelm_w(src0_h, src + stride_2x, 0, 0); - __lasx_xvstelm_w(src0_h, src + stride_3x, 0, 1); - src += stride_4x; - __lasx_xvstelm_w(src0_l, src, 0, 4); - __lasx_xvstelm_w(src0_l, src + stride, 0, 5); - __lasx_xvstelm_w(src0_h, src + stride_2x, 0, 4); - __lasx_xvstelm_w(src0_h, src + stride_3x, 0, 5); -} - -void ff_weight_h264_pixels4_8_lasx(uint8_t *src, ptrdiff_t stride, - int height, int log2_denom, - int weight_src, int offset) -{ - if (2 == height) { - avc_wgt_4x2_lasx(src, stride, log2_denom, weight_src, offset); - } else if (4 == height) { - avc_wgt_4x4_lasx(src, stride, log2_denom, weight_src, offset); - } else { - avc_wgt_4x8_lasx(src, stride, log2_denom, weight_src, offset); - } -} - void ff_h264_add_pixels4_8_lasx(uint8_t *_dst, int16_t *_src, int stride) { __m256i src0, dst0, dst1, dst2, dst3, zero; diff --git a/libavcodec/loongarch/h264dsp_lasx.h b/libavcodec/loongarch/h264dsp_lasx.h deleted file mode 100644 index 4cf813750bb..00000000000 --- a/libavcodec/loongarch/h264dsp_lasx.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2021 Loongson Technology Corporation Limited - * Contributed by Shiyou Yin - * Xiwei Gu - * - * 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_LOONGARCH_H264DSP_LASX_H -#define AVCODEC_LOONGARCH_H264DSP_LASX_H - -#include "libavcodec/h264dec.h" - -void ff_h264_h_lpf_luma_8_lasx(uint8_t *src, ptrdiff_t stride, - int alpha, int beta, int8_t *tc0); -void ff_h264_v_lpf_luma_8_lasx(uint8_t *src, ptrdiff_t stride, - int alpha, int beta, int8_t *tc0); -void ff_h264_h_lpf_luma_intra_8_lasx(uint8_t *src, ptrdiff_t stride, - int alpha, int beta); -void ff_h264_v_lpf_luma_intra_8_lasx(uint8_t *src, ptrdiff_t stride, - int alpha, int beta); -void ff_h264_h_lpf_chroma_8_lasx(uint8_t *src, ptrdiff_t stride, - int alpha, int beta, int8_t *tc0); -void ff_h264_v_lpf_chroma_8_lasx(uint8_t *src, ptrdiff_t stride, - int alpha, int beta, int8_t *tc0); -void ff_h264_h_lpf_chroma_intra_8_lasx(uint8_t *src, ptrdiff_t stride, - int alpha, int beta); -void ff_h264_v_lpf_chroma_intra_8_lasx(uint8_t *src, ptrdiff_t stride, - int alpha, int beta); -void ff_biweight_h264_pixels16_8_lasx(uint8_t *dst, uint8_t *src, - ptrdiff_t stride, int height, - int log2_denom, int weight_dst, - int weight_src, int offset_in); -void ff_biweight_h264_pixels8_8_lasx(uint8_t *dst, uint8_t *src, - ptrdiff_t stride, int height, - int log2_denom, int weight_dst, - int weight_src, int offset); -void ff_biweight_h264_pixels4_8_lasx(uint8_t *dst, uint8_t *src, - ptrdiff_t stride, int height, - int log2_denom, int weight_dst, - int weight_src, int offset); -void ff_weight_h264_pixels16_8_lasx(uint8_t *src, ptrdiff_t stride, - int height, int log2_denom, - int weight_src, int offset_in); -void ff_weight_h264_pixels8_8_lasx(uint8_t *src, ptrdiff_t stride, - int height, int log2_denom, - int weight_src, int offset); -void ff_weight_h264_pixels4_8_lasx(uint8_t *src, ptrdiff_t stride, - int height, int log2_denom, - int weight_src, int offset); -void ff_h264_add_pixels4_8_lasx(uint8_t *_dst, int16_t *_src, int stride); - -void ff_h264_add_pixels8_8_lasx(uint8_t *_dst, int16_t *_src, int stride); -void ff_h264_idct_add_lasx(uint8_t *dst, int16_t *src, int32_t dst_stride); -void ff_h264_idct8_addblk_lasx(uint8_t *dst, int16_t *src, int32_t dst_stride); -void ff_h264_idct4x4_addblk_dc_lasx(uint8_t *dst, int16_t *src, - int32_t dst_stride); -void ff_h264_idct8_dc_addblk_lasx(uint8_t *dst, int16_t *src, - int32_t dst_stride); -void ff_h264_idct_add16_lasx(uint8_t *dst, const int32_t *blk_offset, - int16_t *block, int32_t dst_stride, - const uint8_t nzc[15 * 8]); -void ff_h264_idct8_add4_lasx(uint8_t *dst, const int32_t *blk_offset, - int16_t *block, int32_t dst_stride, - const uint8_t nzc[15 * 8]); -void ff_h264_idct_add8_lasx(uint8_t **dst, const int32_t *blk_offset, - int16_t *block, int32_t dst_stride, - const uint8_t nzc[15 * 8]); -void ff_h264_idct_add8_422_lasx(uint8_t **dst, const int32_t *blk_offset, - int16_t *block, int32_t dst_stride, - const uint8_t nzc[15 * 8]); -void ff_h264_idct_add16_intra_lasx(uint8_t *dst, const int32_t *blk_offset, - int16_t *block, int32_t dst_stride, - const uint8_t nzc[15 * 8]); -void ff_h264_deq_idct_luma_dc_lasx(int16_t *dst, int16_t *src, - int32_t de_qval); - -void ff_h264_loop_filter_strength_lasx(int16_t bS[2][4][4], uint8_t nnz[40], - int8_t ref[2][40], int16_t mv[2][40][2], - int bidir, int edges, int step, - int mask_mv0, int mask_mv1, int field); - -#endif // #ifndef AVCODEC_LOONGARCH_H264DSP_LASX_H diff --git a/libavcodec/loongarch/h264dsp_loongarch.h b/libavcodec/loongarch/h264dsp_loongarch.h new file mode 100644 index 00000000000..e17522dfe04 --- /dev/null +++ b/libavcodec/loongarch/h264dsp_loongarch.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2023 Loongson Technology Corporation Limited + * Contributed by Shiyou Yin + * Xiwei Gu + * + * 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_LOONGARCH_H264DSP_LOONGARCH_H +#define AVCODEC_LOONGARCH_H264DSP_LOONGARCH_H + +#include "libavcodec/h264dec.h" +#include "config.h" + +void ff_h264_idct_add_8_lsx(uint8_t *dst, int16_t *src, int dst_stride); +void ff_h264_idct8_add_8_lsx(uint8_t *dst, int16_t *src, int dst_stride); +void ff_h264_idct_dc_add_8_lsx(uint8_t *dst, int16_t *src, int dst_stride); +void ff_h264_idct8_dc_add_8_lsx(uint8_t *dst, int16_t *src, int dst_stride); +void ff_h264_luma_dc_dequant_idct_8_lsx(int16_t *_output, int16_t *_input, int qmul); +void ff_h264_idct_add16_8_lsx(uint8_t *dst, const int32_t *blk_offset, + int16_t *block, int32_t dst_stride, + const uint8_t nzc[15 * 8]); +void ff_h264_idct8_add4_8_lsx(uint8_t *dst, const int32_t *blk_offset, + int16_t *block, int32_t dst_stride, + const uint8_t nzc[15 * 8]); +void ff_h264_idct_add8_8_lsx(uint8_t **dst, const int32_t *blk_offset, + int16_t *block, int32_t dst_stride, + const uint8_t nzc[15 * 8]); +void ff_h264_idct_add8_422_8_lsx(uint8_t **dst, const int32_t *blk_offset, + int16_t *block, int32_t dst_stride, + const uint8_t nzc[15 * 8]); +void ff_h264_idct_add16_intra_8_lsx(uint8_t *dst, const int32_t *blk_offset, + int16_t *block, int32_t dst_stride, + const uint8_t nzc[15 * 8]); + +void ff_h264_h_lpf_luma_8_lsx(uint8_t *src, ptrdiff_t stride, + int alpha, int beta, int8_t *tc0); +void ff_h264_v_lpf_luma_8_lsx(uint8_t *src, ptrdiff_t stride, + int alpha, int beta, int8_t *tc0); +void ff_h264_h_lpf_luma_intra_8_lsx(uint8_t *src, ptrdiff_t stride, + int alpha, int beta); +void ff_h264_v_lpf_luma_intra_8_lsx(uint8_t *src, ptrdiff_t stride, + int alpha, int beta); +void ff_h264_h_lpf_chroma_8_lsx(uint8_t *src, ptrdiff_t stride, + int alpha, int beta, int8_t *tc0); +void ff_h264_v_lpf_chroma_8_lsx(uint8_t *src, ptrdiff_t stride, + int alpha, int beta, int8_t *tc0); +void ff_h264_h_lpf_chroma_intra_8_lsx(uint8_t *src, ptrdiff_t stride, + int alpha, int beta); +void ff_h264_v_lpf_chroma_intra_8_lsx(uint8_t *src, ptrdiff_t stride, + int alpha, int beta); +void ff_biweight_h264_pixels16_8_lsx(uint8_t *dst, uint8_t *src, + ptrdiff_t stride, int height, + int log2_denom, int weight_dst, + int weight_src, int offset_in); +void ff_biweight_h264_pixels8_8_lsx(uint8_t *dst, uint8_t *src, + ptrdiff_t stride, int height, + int log2_denom, int weight_dst, + int weight_src, int offset); +void ff_biweight_h264_pixels4_8_lsx(uint8_t *dst, uint8_t *src, + ptrdiff_t stride, int height, + int log2_denom, int weight_dst, + int weight_src, int offset); +void ff_weight_h264_pixels16_8_lsx(uint8_t *src, ptrdiff_t stride, + int height, int log2_denom, + int weight_src, int offset_in); +void ff_weight_h264_pixels8_8_lsx(uint8_t *src, ptrdiff_t stride, + int height, int log2_denom, + int weight_src, int offset); +void ff_weight_h264_pixels4_8_lsx(uint8_t *src, ptrdiff_t stride, + int height, int log2_denom, + int weight_src, int offset); +void ff_h264_add_pixels4_8_lsx(uint8_t *_dst, int16_t *_src, int stride); +void ff_h264_add_pixels8_8_lsx(uint8_t *_dst, int16_t *_src, int stride); +void ff_h264_loop_filter_strength_lsx(int16_t bS[2][4][4], uint8_t nnz[40], + int8_t ref[2][40], int16_t mv[2][40][2], + int bidir, int edges, int step, + int mask_mv0, int mask_mv1, int field); + +#if HAVE_LASX +void ff_h264_h_lpf_luma_8_lasx(uint8_t *src, ptrdiff_t stride, + int alpha, int beta, int8_t *tc0); +void ff_h264_v_lpf_luma_8_lasx(uint8_t *src, ptrdiff_t stride, + int alpha, int beta, int8_t *tc0); +void ff_h264_h_lpf_luma_intra_8_lasx(uint8_t *src, ptrdiff_t stride, + int alpha, int beta); +void ff_h264_v_lpf_luma_intra_8_lasx(uint8_t *src, ptrdiff_t stride, + int alpha, int beta); +void ff_biweight_h264_pixels16_8_lasx(unsigned char *dst, unsigned char *src, + long int stride, int height, + int log2_denom, int weight_dst, + int weight_src, int offset_in); +void ff_biweight_h264_pixels8_8_lasx(unsigned char *dst, unsigned char *src, + long int stride, int height, + int log2_denom, int weight_dst, + int weight_src, int offset); +void ff_weight_h264_pixels16_8_lasx(uint8_t *src, ptrdiff_t stride, + int height, int log2_denom, + int weight_src, int offset_in); +void ff_weight_h264_pixels8_8_lasx(uint8_t *src, ptrdiff_t stride, + int height, int log2_denom, + int weight_src, int offset); +void ff_h264_add_pixels4_8_lasx(uint8_t *_dst, int16_t *_src, int stride); + +void ff_h264_add_pixels8_8_lasx(uint8_t *_dst, int16_t *_src, int stride); +void ff_h264_idct8_add_8_lasx(uint8_t *dst, int16_t *src, int32_t dst_stride); +void ff_h264_idct8_dc_add_8_lasx(uint8_t *dst, int16_t *src, + int32_t dst_stride); +void ff_h264_idct8_add4_8_lasx(uint8_t *dst, const int32_t *blk_offset, + int16_t *block, int32_t dst_stride, + const uint8_t nzc[15 * 8]); +void ff_h264_loop_filter_strength_lasx(int16_t bS[2][4][4], uint8_t nnz[40], + int8_t ref[2][40], int16_t mv[2][40][2], + int bidir, int edges, int step, + int mask_mv0, int mask_mv1, int field); +#endif // #if HAVE_LASX + +#endif // #ifndef AVCODEC_LOONGARCH_H264DSP_LOONGARCH_H diff --git a/libavcodec/loongarch/h264idct.S b/libavcodec/loongarch/h264idct.S new file mode 100644 index 00000000000..f504cfb7148 --- /dev/null +++ b/libavcodec/loongarch/h264idct.S @@ -0,0 +1,658 @@ +/* + * Loongson LASX optimized h264idct + * + * Copyright (c) 2023 Loongson Technology Corporation Limited + * Contributed by Shiyou Yin + * + * 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 "loongson_asm.S" + +/* + * #define FUNC2(a, b, c) FUNC3(a, b, c) + * #define FUNCC(a) FUNC2(a, BIT_DEPTH, _c) + * void FUNCC(ff_h264_idct_add)(uint8_t *_dst, int16_t *_block, int stride) + * LSX optimization is enough for this function. + */ +function ff_h264_idct_add_8_lsx + fld.d f0, a1, 0 + fld.d f1, a1, 8 + fld.d f2, a1, 16 + fld.d f3, a1, 24 + vxor.v vr7, vr7, vr7 + add.d t2, a2, a2 + add.d t3, t2, a2 + vst vr7, a1, 0 + vst vr7, a1, 16 + + vadd.h vr4, vr0, vr2 + vsub.h vr5, vr0, vr2 + vsrai.h vr6, vr1, 1 + vsrai.h vr7, vr3, 1 + vsub.h vr6, vr6, vr3 + vadd.h vr7, vr1, vr7 + LSX_BUTTERFLY_4_H vr4, vr5, vr6, vr7, vr0, vr1, vr2, vr3 + LSX_TRANSPOSE4x4_H vr0, vr1, vr2, vr3, vr0, vr1, vr2, vr3, vr4, vr5 + vadd.h vr4, vr0, vr2 + vsub.h vr5, vr0, vr2 + vsrai.h vr6, vr1, 1 + vsrai.h vr7, vr3, 1 + vsub.h vr6, vr6, vr3 + vadd.h vr7, vr1, vr7 + LSX_BUTTERFLY_4_H vr4, vr5, vr6, vr7, vr0, vr1, vr2, vr3 + + fld.s f4, a0, 0 + fldx.s f5, a0, a2 + fldx.s f6, a0, t2 + fldx.s f7, a0, t3 + + vsrari.h vr0, vr0, 6 + vsrari.h vr1, vr1, 6 + vsrari.h vr2, vr2, 6 + vsrari.h vr3, vr3, 6 + + vsllwil.hu.bu vr4, vr4, 0 + vsllwil.hu.bu vr5, vr5, 0 + vsllwil.hu.bu vr6, vr6, 0 + vsllwil.hu.bu vr7, vr7, 0 + vadd.h vr0, vr0, vr4 + vadd.h vr1, vr1, vr5 + vadd.h vr2, vr2, vr6 + vadd.h vr3, vr3, vr7 + vssrarni.bu.h vr1, vr0, 0 + vssrarni.bu.h vr3, vr2, 0 + + vbsrl.v vr0, vr1, 8 + vbsrl.v vr2, vr3, 8 + fst.s f1, a0, 0 + fstx.s f0, a0, a2 + fstx.s f3, a0, t2 + fstx.s f2, a0, t3 +endfunc + +/* + * #define FUNC2(a, b, c) FUNC3(a, b, c) + * #define FUNCC(a) FUNC2(a, BIT_DEPTH, _c) + * void FUNCC(ff_h264_idct8_add)(uint8_t *_dst, int16_t *_block, int stride) + */ +function ff_h264_idct8_add_8_lsx + ld.h t0, a1, 0 + add.d t2, a2, a2 + add.d t3, t2, a2 + add.d t4, t3, a2 + add.d t5, t4, a2 + add.d t6, t5, a2 + add.d t7, t6, a2 + addi.w t0, t0, 32 + st.h t0, a1, 0 + + vld vr0, a1, 0 + vld vr1, a1, 16 + vld vr2, a1, 32 + vld vr3, a1, 48 + vld vr4, a1, 64 + vld vr5, a1, 80 + vld vr6, a1, 96 + vld vr7, a1, 112 + vxor.v vr8, vr8, vr8 + vst vr8, a1, 0 + vst vr8, a1, 16 + vst vr8, a1, 32 + vst vr8, a1, 48 + vst vr8, a1, 64 + vst vr8, a1, 80 + vst vr8, a1, 96 + vst vr8, a1, 112 + + vadd.h vr18, vr0, vr4 + vsub.h vr19, vr0, vr4 + vsrai.h vr20, vr2, 1 + vsrai.h vr21, vr6, 1 + vsub.h vr20, vr20, vr6 + vadd.h vr21, vr21, vr2 + LSX_BUTTERFLY_4_H vr18, vr19, vr20, vr21, vr10, vr12, vr14, vr16 + vsrai.h vr11, vr7, 1 + vsrai.h vr13, vr3, 1 + vsrai.h vr15, vr5, 1 + vsrai.h vr17, vr1, 1 + vsub.h vr11, vr5, vr11 + vsub.h vr13, vr7, vr13 + vadd.h vr15, vr7, vr15 + vadd.h vr17, vr5, vr17 + vsub.h vr11, vr11, vr7 + vsub.h vr13, vr13, vr3 + vadd.h vr15, vr15, vr5 + vadd.h vr17, vr17, vr1 + vsub.h vr11, vr11, vr3 + vadd.h vr13, vr13, vr1 + vsub.h vr15, vr15, vr1 + vadd.h vr17, vr17, vr3 + vsrai.h vr18, vr11, 2 + vsrai.h vr19, vr13, 2 + vsrai.h vr20, vr15, 2 + vsrai.h vr21, vr17, 2 + vadd.h vr11, vr11, vr21 + vadd.h vr13, vr13, vr20 + vsub.h vr15, vr19, vr15 + vsub.h vr17, vr17, vr18 + LSX_BUTTERFLY_8_H vr10, vr16, vr12, vr14, vr13, vr15, vr11, vr17, \ + vr0, vr3, vr1, vr2, vr5, vr6, vr4, vr7 + + LSX_TRANSPOSE8x8_H vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr7, \ + vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr7, \ + vr10, vr11, vr12, vr13, vr14, vr15, vr16, vr17 + vexth.w.h vr20, vr0 + vexth.w.h vr21, vr1 + vexth.w.h vr22, vr2 + vexth.w.h vr23, vr3 + vexth.w.h vr8, vr4 + vexth.w.h vr9, vr5 + vexth.w.h vr18, vr6 + vexth.w.h vr19, vr7 + vsllwil.w.h vr0, vr0, 0 + vsllwil.w.h vr1, vr1, 0 + vsllwil.w.h vr2, vr2, 0 + vsllwil.w.h vr3, vr3, 0 + vsllwil.w.h vr4, vr4, 0 + vsllwil.w.h vr5, vr5, 0 + vsllwil.w.h vr6, vr6, 0 + vsllwil.w.h vr7, vr7, 0 + + vadd.w vr11, vr0, vr4 + vsub.w vr13, vr0, vr4 + vsrai.w vr15, vr2, 1 + vsrai.w vr17, vr6, 1 + vsub.w vr15, vr15, vr6 + vadd.w vr17, vr17, vr2 + LSX_BUTTERFLY_4_W vr11, vr13, vr15, vr17, vr10, vr12, vr14, vr16 + vsrai.w vr11, vr7, 1 + vsrai.w vr13, vr3, 1 + vsrai.w vr15, vr5, 1 + vsrai.w vr17, vr1, 1 + vsub.w vr11, vr5, vr11 + vsub.w vr13, vr7, vr13 + vadd.w vr15, vr7, vr15 + vadd.w vr17, vr5, vr17 + vsub.w vr11, vr11, vr7 + vsub.w vr13, vr13, vr3 + vadd.w vr15, vr15, vr5 + vadd.w vr17, vr17, vr1 + vsub.w vr11, vr11, vr3 + vadd.w vr13, vr13, vr1 + vsub.w vr15, vr15, vr1 + vadd.w vr17, vr17, vr3 + vsrai.w vr0, vr11, 2 + vsrai.w vr1, vr13, 2 + vsrai.w vr2, vr15, 2 + vsrai.w vr3, vr17, 2 + vadd.w vr11, vr11, vr3 + vadd.w vr13, vr13, vr2 + vsub.w vr15, vr1, vr15 + vsub.w vr17, vr17, vr0 + LSX_BUTTERFLY_8_W vr10, vr12, vr14, vr16, vr11, vr13, vr15, vr17, \ + vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr7 + + vadd.w vr11, vr20, vr8 + vsub.w vr13, vr20, vr8 + vsrai.w vr15, vr22, 1 + vsrai.w vr17, vr18, 1 + vsub.w vr15, vr15, vr18 + vadd.w vr17, vr17, vr22 + LSX_BUTTERFLY_4_W vr11, vr13, vr15, vr17, vr10, vr12, vr14, vr16 + vsrai.w vr11, vr19, 1 + vsrai.w vr13, vr23, 1 + vsrai.w vr15, vr9, 1 + vsrai.w vr17, vr21, 1 + vsub.w vr11, vr9, vr11 + vsub.w vr13, vr19, vr13 + vadd.w vr15, vr19, vr15 + vadd.w vr17, vr9, vr17 + vsub.w vr11, vr11, vr19 + vsub.w vr13, vr13, vr23 + vadd.w vr15, vr15, vr9 + vadd.w vr17, vr17, vr21 + vsub.w vr11, vr11, vr23 + vadd.w vr13, vr13, vr21 + vsub.w vr15, vr15, vr21 + vadd.w vr17, vr17, vr23 + vsrai.w vr20, vr11, 2 + vsrai.w vr21, vr13, 2 + vsrai.w vr22, vr15, 2 + vsrai.w vr23, vr17, 2 + vadd.w vr11, vr11, vr23 + vadd.w vr13, vr13, vr22 + vsub.w vr15, vr21, vr15 + vsub.w vr17, vr17, vr20 + LSX_BUTTERFLY_8_W vr10, vr12, vr14, vr16, vr11, vr13, vr15, vr17, \ + vr20, vr21, vr22, vr23, vr8, vr9, vr18, vr19 + + vld vr10, a0, 0 + vldx vr11, a0, a2 + vldx vr12, a0, t2 + vldx vr13, a0, t3 + vldx vr14, a0, t4 + vldx vr15, a0, t5 + vldx vr16, a0, t6 + vldx vr17, a0, t7 + vsrani.h.w vr20, vr0, 6 + vsrani.h.w vr21, vr1, 6 + vsrani.h.w vr22, vr2, 6 + vsrani.h.w vr23, vr3, 6 + vsrani.h.w vr8, vr4, 6 + vsrani.h.w vr9, vr5, 6 + vsrani.h.w vr18, vr6, 6 + vsrani.h.w vr19, vr7, 6 + vsllwil.hu.bu vr10, vr10, 0 + vsllwil.hu.bu vr11, vr11, 0 + vsllwil.hu.bu vr12, vr12, 0 + vsllwil.hu.bu vr13, vr13, 0 + vsllwil.hu.bu vr14, vr14, 0 + vsllwil.hu.bu vr15, vr15, 0 + vsllwil.hu.bu vr16, vr16, 0 + vsllwil.hu.bu vr17, vr17, 0 + + vadd.h vr0, vr20, vr10 + vadd.h vr1, vr21, vr11 + vadd.h vr2, vr22, vr12 + vadd.h vr3, vr23, vr13 + vadd.h vr4, vr8, vr14 + vadd.h vr5, vr9, vr15 + vadd.h vr6, vr18, vr16 + vadd.h vr7, vr19, vr17 + vssrarni.bu.h vr1, vr0, 0 + vssrarni.bu.h vr3, vr2, 0 + vssrarni.bu.h vr5, vr4, 0 + vssrarni.bu.h vr7, vr6, 0 + vbsrl.v vr0, vr1, 8 + vbsrl.v vr2, vr3, 8 + vbsrl.v vr4, vr5, 8 + vbsrl.v vr6, vr7, 8 + fst.d f1, a0, 0 + fstx.d f0, a0, a2 + fstx.d f3, a0, t2 + fstx.d f2, a0, t3 + fstx.d f5, a0, t4 + fstx.d f4, a0, t5 + fstx.d f7, a0, t6 + fstx.d f6, a0, t7 +endfunc + +/* + * #define FUNC2(a, b, c) FUNC3(a, b, c) + * #define FUNCC(a) FUNC2(a, BIT_DEPTH, _c) + * void FUNCC(ff_h264_idct8_add)(uint8_t *_dst, int16_t *_block, int stride) + */ +function ff_h264_idct8_add_8_lasx + ld.h t0, a1, 0 + add.d t2, a2, a2 + add.d t3, t2, a2 + add.d t4, t3, a2 + add.d t5, t4, a2 + add.d t6, t5, a2 + add.d t7, t6, a2 + addi.w t0, t0, 32 + st.h t0, a1, 0 + + vld vr0, a1, 0 + vld vr1, a1, 16 + vld vr2, a1, 32 + vld vr3, a1, 48 + vld vr4, a1, 64 + vld vr5, a1, 80 + vld vr6, a1, 96 + vld vr7, a1, 112 + xvxor.v xr8, xr8, xr8 + xvst xr8, a1, 0 + xvst xr8, a1, 32 + xvst xr8, a1, 64 + xvst xr8, a1, 96 + + vadd.h vr18, vr0, vr4 + vsub.h vr19, vr0, vr4 + vsrai.h vr20, vr2, 1 + vsrai.h vr21, vr6, 1 + vsub.h vr20, vr20, vr6 + vadd.h vr21, vr21, vr2 + LSX_BUTTERFLY_4_H vr18, vr19, vr20, vr21, vr10, vr12, vr14, vr16 + vsrai.h vr11, vr7, 1 + vsrai.h vr13, vr3, 1 + vsrai.h vr15, vr5, 1 + vsrai.h vr17, vr1, 1 + vsub.h vr11, vr5, vr11 + vsub.h vr13, vr7, vr13 + vadd.h vr15, vr7, vr15 + vadd.h vr17, vr5, vr17 + vsub.h vr11, vr11, vr7 + vsub.h vr13, vr13, vr3 + vadd.h vr15, vr15, vr5 + vadd.h vr17, vr17, vr1 + vsub.h vr11, vr11, vr3 + vadd.h vr13, vr13, vr1 + vsub.h vr15, vr15, vr1 + vadd.h vr17, vr17, vr3 + vsrai.h vr18, vr11, 2 + vsrai.h vr19, vr13, 2 + vsrai.h vr20, vr15, 2 + vsrai.h vr21, vr17, 2 + vadd.h vr11, vr11, vr21 + vadd.h vr13, vr13, vr20 + vsub.h vr15, vr19, vr15 + vsub.h vr17, vr17, vr18 + LSX_BUTTERFLY_8_H vr10, vr16, vr12, vr14, vr13, vr15, vr11, vr17, \ + vr0, vr3, vr1, vr2, vr5, vr6, vr4, vr7 + + LSX_TRANSPOSE8x8_H vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr7, \ + vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr7, \ + vr10, vr11, vr12, vr13, vr14, vr15, vr16, vr17 + vext2xv.w.h xr0, xr0 + vext2xv.w.h xr1, xr1 + vext2xv.w.h xr2, xr2 + vext2xv.w.h xr3, xr3 + vext2xv.w.h xr4, xr4 + vext2xv.w.h xr5, xr5 + vext2xv.w.h xr6, xr6 + vext2xv.w.h xr7, xr7 + + xvadd.w xr11, xr0, xr4 + xvsub.w xr13, xr0, xr4 + xvsrai.w xr15, xr2, 1 + xvsrai.w xr17, xr6, 1 + xvsub.w xr15, xr15, xr6 + xvadd.w xr17, xr17, xr2 + LASX_BUTTERFLY_4_W xr11, xr13, xr15, xr17, xr10, xr12, xr14, xr16 + xvsrai.w xr11, xr7, 1 + xvsrai.w xr13, xr3, 1 + xvsrai.w xr15, xr5, 1 + xvsrai.w xr17, xr1, 1 + xvsub.w xr11, xr5, xr11 + xvsub.w xr13, xr7, xr13 + xvadd.w xr15, xr7, xr15 + xvadd.w xr17, xr5, xr17 + xvsub.w xr11, xr11, xr7 + xvsub.w xr13, xr13, xr3 + xvadd.w xr15, xr15, xr5 + xvadd.w xr17, xr17, xr1 + xvsub.w xr11, xr11, xr3 + xvadd.w xr13, xr13, xr1 + xvsub.w xr15, xr15, xr1 + xvadd.w xr17, xr17, xr3 + xvsrai.w xr0, xr11, 2 + xvsrai.w xr1, xr13, 2 + xvsrai.w xr2, xr15, 2 + xvsrai.w xr3, xr17, 2 + xvadd.w xr11, xr11, xr3 + xvadd.w xr13, xr13, xr2 + xvsub.w xr15, xr1, xr15 + xvsub.w xr17, xr17, xr0 + LASX_BUTTERFLY_8_W xr10, xr12, xr14, xr16, xr11, xr13, xr15, xr17, \ + xr0, xr1, xr2, xr3, xr4, xr5, xr6, xr7 + + vld vr10, a0, 0 + vldx vr11, a0, a2 + vldx vr12, a0, t2 + vldx vr13, a0, t3 + vldx vr14, a0, t4 + vldx vr15, a0, t5 + vldx vr16, a0, t6 + vldx vr17, a0, t7 + xvldi xr8, 0x806 //"xvldi.w xr8 6" + xvsran.h.w xr0, xr0, xr8 + xvsran.h.w xr1, xr1, xr8 + xvsran.h.w xr2, xr2, xr8 + xvsran.h.w xr3, xr3, xr8 + xvsran.h.w xr4, xr4, xr8 + xvsran.h.w xr5, xr5, xr8 + xvsran.h.w xr6, xr6, xr8 + xvsran.h.w xr7, xr7, xr8 + xvpermi.d xr0, xr0, 0x08 + xvpermi.d xr1, xr1, 0x08 + xvpermi.d xr2, xr2, 0x08 + xvpermi.d xr3, xr3, 0x08 + xvpermi.d xr4, xr4, 0x08 + xvpermi.d xr5, xr5, 0x08 + xvpermi.d xr6, xr6, 0x08 + xvpermi.d xr7, xr7, 0x08 + + vsllwil.hu.bu vr10, vr10, 0 + vsllwil.hu.bu vr11, vr11, 0 + vsllwil.hu.bu vr12, vr12, 0 + vsllwil.hu.bu vr13, vr13, 0 + vsllwil.hu.bu vr14, vr14, 0 + vsllwil.hu.bu vr15, vr15, 0 + vsllwil.hu.bu vr16, vr16, 0 + vsllwil.hu.bu vr17, vr17, 0 + + vadd.h vr0, vr0, vr10 + vadd.h vr1, vr1, vr11 + vadd.h vr2, vr2, vr12 + vadd.h vr3, vr3, vr13 + vadd.h vr4, vr4, vr14 + vadd.h vr5, vr5, vr15 + vadd.h vr6, vr6, vr16 + vadd.h vr7, vr7, vr17 + vssrarni.bu.h vr1, vr0, 0 + vssrarni.bu.h vr3, vr2, 0 + vssrarni.bu.h vr5, vr4, 0 + vssrarni.bu.h vr7, vr6, 0 + vbsrl.v vr0, vr1, 8 + vbsrl.v vr2, vr3, 8 + vbsrl.v vr4, vr5, 8 + vbsrl.v vr6, vr7, 8 + fst.d f1, a0, 0 + fstx.d f0, a0, a2 + fstx.d f3, a0, t2 + fstx.d f2, a0, t3 + fstx.d f5, a0, t4 + fstx.d f4, a0, t5 + fstx.d f7, a0, t6 + fstx.d f6, a0, t7 +endfunc + +/* + * #define FUNC2(a, b, c) FUNC3(a, b, c) + * #define FUNCC(a) FUNC2(a, BIT_DEPTH, _c) + * void FUNCC(ff_h264_idct_dc_add)(uint8_t *_dst, int16_t *_block, int stride) + * LSX optimization is enough for this function. + */ +function ff_h264_idct_dc_add_8_lsx + vldrepl.h vr4, a1, 0 + add.d t2, a2, a2 + add.d t3, t2, a2 + fld.s f0, a0, 0 + fldx.s f1, a0, a2 + fldx.s f2, a0, t2 + fldx.s f3, a0, t3 + st.h zero, a1, 0 + + vsrari.h vr4, vr4, 6 + vilvl.w vr0, vr1, vr0 + vilvl.w vr1, vr3, vr2 + vsllwil.hu.bu vr0, vr0, 0 + vsllwil.hu.bu vr1, vr1, 0 + vadd.h vr0, vr0, vr4 + vadd.h vr1, vr1, vr4 + vssrarni.bu.h vr1, vr0, 0 + + vbsrl.v vr2, vr1, 4 + vbsrl.v vr3, vr1, 8 + vbsrl.v vr4, vr1, 12 + fst.s f1, a0, 0 + fstx.s f2, a0, a2 + fstx.s f3, a0, t2 + fstx.s f4, a0, t3 +endfunc + +/* + * #define FUNC2(a, b, c) FUNC3(a, b, c) + * #define FUNCC(a) FUNC2(a, BIT_DEPTH, _c) + * void FUNCC(ff_h264_idct8_dc_add)(uint8_t *_dst, int16_t *_block, int stride) + */ +function ff_h264_idct8_dc_add_8_lsx + vldrepl.h vr8, a1, 0 + add.d t2, a2, a2 + add.d t3, t2, a2 + add.d t4, t3, a2 + add.d t5, t4, a2 + add.d t6, t5, a2 + add.d t7, t6, a2 + + fld.d f0, a0, 0 + fldx.d f1, a0, a2 + fldx.d f2, a0, t2 + fldx.d f3, a0, t3 + fldx.d f4, a0, t4 + fldx.d f5, a0, t5 + fldx.d f6, a0, t6 + fldx.d f7, a0, t7 + st.h zero, a1, 0 + + vsrari.h vr8, vr8, 6 + vsllwil.hu.bu vr0, vr0, 0 + vsllwil.hu.bu vr1, vr1, 0 + vsllwil.hu.bu vr2, vr2, 0 + vsllwil.hu.bu vr3, vr3, 0 + vsllwil.hu.bu vr4, vr4, 0 + vsllwil.hu.bu vr5, vr5, 0 + vsllwil.hu.bu vr6, vr6, 0 + vsllwil.hu.bu vr7, vr7, 0 + vadd.h vr0, vr0, vr8 + vadd.h vr1, vr1, vr8 + vadd.h vr2, vr2, vr8 + vadd.h vr3, vr3, vr8 + vadd.h vr4, vr4, vr8 + vadd.h vr5, vr5, vr8 + vadd.h vr6, vr6, vr8 + vadd.h vr7, vr7, vr8 + vssrarni.bu.h vr1, vr0, 0 + vssrarni.bu.h vr3, vr2, 0 + vssrarni.bu.h vr5, vr4, 0 + vssrarni.bu.h vr7, vr6, 0 + + vbsrl.v vr0, vr1, 8 + vbsrl.v vr2, vr3, 8 + vbsrl.v vr4, vr5, 8 + vbsrl.v vr6, vr7, 8 + fst.d f1, a0, 0 + fstx.d f0, a0, a2 + fstx.d f3, a0, t2 + fstx.d f2, a0, t3 + fstx.d f5, a0, t4 + fstx.d f4, a0, t5 + fstx.d f7, a0, t6 + fstx.d f6, a0, t7 +endfunc +function ff_h264_idct8_dc_add_8_lasx + xvldrepl.h xr8, a1, 0 + add.d t2, a2, a2 + add.d t3, t2, a2 + add.d t4, t3, a2 + add.d t5, t4, a2 + add.d t6, t5, a2 + add.d t7, t6, a2 + + fld.d f0, a0, 0 + fldx.d f1, a0, a2 + fldx.d f2, a0, t2 + fldx.d f3, a0, t3 + fldx.d f4, a0, t4 + fldx.d f5, a0, t5 + fldx.d f6, a0, t6 + fldx.d f7, a0, t7 + st.h zero, a1, 0 + + xvsrari.h xr8, xr8, 6 + xvpermi.q xr1, xr0, 0x20 + xvpermi.q xr3, xr2, 0x20 + xvpermi.q xr5, xr4, 0x20 + xvpermi.q xr7, xr6, 0x20 + xvsllwil.hu.bu xr1, xr1, 0 + xvsllwil.hu.bu xr3, xr3, 0 + xvsllwil.hu.bu xr5, xr5, 0 + xvsllwil.hu.bu xr7, xr7, 0 + xvadd.h xr1, xr1, xr8 + xvadd.h xr3, xr3, xr8 + xvadd.h xr5, xr5, xr8 + xvadd.h xr7, xr7, xr8 + + xvssrarni.bu.h xr3, xr1, 0 + xvssrarni.bu.h xr7, xr5, 0 + + xvpermi.q xr1, xr3, 0x11 + xvpermi.q xr5, xr7, 0x11 + xvbsrl.v xr0, xr1, 8 + xvbsrl.v xr2, xr3, 8 + xvbsrl.v xr4, xr5, 8 + xvbsrl.v xr6, xr7, 8 + + fst.d f3, a0, 0 + fstx.d f1, a0, a2 + fstx.d f2, a0, t2 + fstx.d f0, a0, t3 + fstx.d f7, a0, t4 + fstx.d f5, a0, t5 + fstx.d f6, a0, t6 + fstx.d f4, a0, t7 +endfunc + +/** + * IDCT transforms the 16 dc values and dequantizes them. + * @param qmul quantization parameter + * void FUNCC(ff_h264_luma_dc_dequant_idct)(int16_t *_output, int16_t *_input, int qmul){ + * LSX optimization is enough for this function. + */ +function ff_h264_luma_dc_dequant_idct_8_lsx + vld vr0, a1, 0 + vld vr1, a1, 8 + vld vr2, a1, 16 + vld vr3, a1, 24 + vreplgr2vr.w vr8, a2 + LSX_TRANSPOSE4x4_H vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr7, vr9, vr10 + LSX_BUTTERFLY_4_H vr4, vr6, vr7, vr5, vr0, vr3, vr2, vr1 + LSX_BUTTERFLY_4_H vr0, vr1, vr2, vr3, vr4, vr7, vr6, vr5 + LSX_TRANSPOSE4x4_H vr4, vr5, vr6, vr7, vr0, vr1, vr2, vr3, vr9, vr10 + LSX_BUTTERFLY_4_H vr0, vr1, vr3, vr2, vr4, vr7, vr6, vr5 + LSX_BUTTERFLY_4_H vr4, vr5, vr6, vr7, vr0, vr1, vr2, vr3 + vsllwil.w.h vr0, vr0, 0 + vsllwil.w.h vr1, vr1, 0 + vsllwil.w.h vr2, vr2, 0 + vsllwil.w.h vr3, vr3, 0 + vmul.w vr0, vr0, vr8 + vmul.w vr1, vr1, vr8 + vmul.w vr2, vr2, vr8 + vmul.w vr3, vr3, vr8 + vsrarni.h.w vr1, vr0, 8 + vsrarni.h.w vr3, vr2, 8 + + vstelm.h vr1, a0, 0, 0 + vstelm.h vr1, a0, 32, 4 + vstelm.h vr1, a0, 64, 1 + vstelm.h vr1, a0, 96, 5 + vstelm.h vr3, a0, 128, 0 + vstelm.h vr3, a0, 160, 4 + vstelm.h vr3, a0, 192, 1 + vstelm.h vr3, a0, 224, 5 + addi.d a0, a0, 256 + vstelm.h vr1, a0, 0, 2 + vstelm.h vr1, a0, 32, 6 + vstelm.h vr1, a0, 64, 3 + vstelm.h vr1, a0, 96, 7 + vstelm.h vr3, a0, 128, 2 + vstelm.h vr3, a0, 160, 6 + vstelm.h vr3, a0, 192, 3 + vstelm.h vr3, a0, 224, 7 +endfunc diff --git a/libavcodec/loongarch/h264idct_lasx.c b/libavcodec/loongarch/h264idct_lasx.c deleted file mode 100644 index 46bd3b74d5f..00000000000 --- a/libavcodec/loongarch/h264idct_lasx.c +++ /dev/null @@ -1,498 +0,0 @@ -/* - * Loongson LASX optimized h264dsp - * - * Copyright (c) 2021 Loongson Technology Corporation Limited - * Contributed by Shiyou Yin - * Xiwei Gu - * - * 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 "libavutil/loongarch/loongson_intrinsics.h" -#include "h264dsp_lasx.h" -#include "libavcodec/bit_depth_template.c" - -#define AVC_ITRANS_H(in0, in1, in2, in3, out0, out1, out2, out3) \ -{ \ - __m256i tmp0_m, tmp1_m, tmp2_m, tmp3_m; \ - \ - tmp0_m = __lasx_xvadd_h(in0, in2); \ - tmp1_m = __lasx_xvsub_h(in0, in2); \ - tmp2_m = __lasx_xvsrai_h(in1, 1); \ - tmp2_m = __lasx_xvsub_h(tmp2_m, in3); \ - tmp3_m = __lasx_xvsrai_h(in3, 1); \ - tmp3_m = __lasx_xvadd_h(in1, tmp3_m); \ - \ - LASX_BUTTERFLY_4_H(tmp0_m, tmp1_m, tmp2_m, tmp3_m, \ - out0, out1, out2, out3); \ -} - -void ff_h264_idct_add_lasx(uint8_t *dst, int16_t *src, int32_t dst_stride) -{ - __m256i src0_m, src1_m, src2_m, src3_m; - __m256i dst0_m, dst1_m; - __m256i hres0, hres1, hres2, hres3, vres0, vres1, vres2, vres3; - __m256i inp0_m, inp1_m, res0_m, src1, src3; - __m256i src0 = __lasx_xvld(src, 0); - __m256i src2 = __lasx_xvld(src, 16); - __m256i zero = __lasx_xvldi(0); - int32_t dst_stride_2x = dst_stride << 1; - int32_t dst_stride_3x = dst_stride_2x + dst_stride; - - __lasx_xvst(zero, src, 0); - DUP2_ARG2(__lasx_xvilvh_d, src0, src0, src2, src2, src1, src3); - AVC_ITRANS_H(src0, src1, src2, src3, hres0, hres1, hres2, hres3); - LASX_TRANSPOSE4x4_H(hres0, hres1, hres2, hres3, hres0, hres1, hres2, hres3); - AVC_ITRANS_H(hres0, hres1, hres2, hres3, vres0, vres1, vres2, vres3); - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, dst_stride, dst, dst_stride_2x, - dst, dst_stride_3x, src0_m, src1_m, src2_m, src3_m); - DUP4_ARG2(__lasx_xvld, dst, 0, dst + dst_stride, 0, dst + dst_stride_2x, - 0, dst + dst_stride_3x, 0, src0_m, src1_m, src2_m, src3_m); - DUP2_ARG2(__lasx_xvilvl_d, vres1, vres0, vres3, vres2, inp0_m, inp1_m); - inp0_m = __lasx_xvpermi_q(inp1_m, inp0_m, 0x20); - inp0_m = __lasx_xvsrari_h(inp0_m, 6); - DUP2_ARG2(__lasx_xvilvl_w, src1_m, src0_m, src3_m, src2_m, dst0_m, dst1_m); - dst0_m = __lasx_xvilvl_d(dst1_m, dst0_m); - res0_m = __lasx_vext2xv_hu_bu(dst0_m); - res0_m = __lasx_xvadd_h(res0_m, inp0_m); - res0_m = __lasx_xvclip255_h(res0_m); - dst0_m = __lasx_xvpickev_b(res0_m, res0_m); - __lasx_xvstelm_w(dst0_m, dst, 0, 0); - __lasx_xvstelm_w(dst0_m, dst + dst_stride, 0, 1); - __lasx_xvstelm_w(dst0_m, dst + dst_stride_2x, 0, 4); - __lasx_xvstelm_w(dst0_m, dst + dst_stride_3x, 0, 5); -} - -void ff_h264_idct8_addblk_lasx(uint8_t *dst, int16_t *src, - int32_t dst_stride) -{ - __m256i src0, src1, src2, src3, src4, src5, src6, src7; - __m256i vec0, vec1, vec2, vec3; - __m256i tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; - __m256i res0, res1, res2, res3, res4, res5, res6, res7; - __m256i dst0, dst1, dst2, dst3, dst4, dst5, dst6, dst7; - __m256i zero = __lasx_xvldi(0); - int32_t dst_stride_2x = dst_stride << 1; - int32_t dst_stride_4x = dst_stride << 2; - int32_t dst_stride_3x = dst_stride_2x + dst_stride; - - src[0] += 32; - DUP4_ARG2(__lasx_xvld, src, 0, src, 16, src, 32, src, 48, - src0, src1, src2, src3); - DUP4_ARG2(__lasx_xvld, src, 64, src, 80, src, 96, src, 112, - src4, src5, src6, src7); - __lasx_xvst(zero, src, 0); - __lasx_xvst(zero, src, 32); - __lasx_xvst(zero, src, 64); - __lasx_xvst(zero, src, 96); - - vec0 = __lasx_xvadd_h(src0, src4); - vec1 = __lasx_xvsub_h(src0, src4); - vec2 = __lasx_xvsrai_h(src2, 1); - vec2 = __lasx_xvsub_h(vec2, src6); - vec3 = __lasx_xvsrai_h(src6, 1); - vec3 = __lasx_xvadd_h(src2, vec3); - - LASX_BUTTERFLY_4_H(vec0, vec1, vec2, vec3, tmp0, tmp1, tmp2, tmp3); - - vec0 = __lasx_xvsrai_h(src7, 1); - vec0 = __lasx_xvsub_h(src5, vec0); - vec0 = __lasx_xvsub_h(vec0, src3); - vec0 = __lasx_xvsub_h(vec0, src7); - - vec1 = __lasx_xvsrai_h(src3, 1); - vec1 = __lasx_xvsub_h(src1, vec1); - vec1 = __lasx_xvadd_h(vec1, src7); - vec1 = __lasx_xvsub_h(vec1, src3); - - vec2 = __lasx_xvsrai_h(src5, 1); - vec2 = __lasx_xvsub_h(vec2, src1); - vec2 = __lasx_xvadd_h(vec2, src7); - vec2 = __lasx_xvadd_h(vec2, src5); - - vec3 = __lasx_xvsrai_h(src1, 1); - vec3 = __lasx_xvadd_h(src3, vec3); - vec3 = __lasx_xvadd_h(vec3, src5); - vec3 = __lasx_xvadd_h(vec3, src1); - - tmp4 = __lasx_xvsrai_h(vec3, 2); - tmp4 = __lasx_xvadd_h(tmp4, vec0); - tmp5 = __lasx_xvsrai_h(vec2, 2); - tmp5 = __lasx_xvadd_h(tmp5, vec1); - tmp6 = __lasx_xvsrai_h(vec1, 2); - tmp6 = __lasx_xvsub_h(tmp6, vec2); - tmp7 = __lasx_xvsrai_h(vec0, 2); - tmp7 = __lasx_xvsub_h(vec3, tmp7); - - LASX_BUTTERFLY_8_H(tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, - res0, res1, res2, res3, res4, res5, res6, res7); - LASX_TRANSPOSE8x8_H(res0, res1, res2, res3, res4, res5, res6, res7, - res0, res1, res2, res3, res4, res5, res6, res7); - - DUP4_ARG1(__lasx_vext2xv_w_h, res0, res1, res2, res3, - tmp0, tmp1, tmp2, tmp3); - DUP4_ARG1(__lasx_vext2xv_w_h, res4, res5, res6, res7, - tmp4, tmp5, tmp6, tmp7); - vec0 = __lasx_xvadd_w(tmp0, tmp4); - vec1 = __lasx_xvsub_w(tmp0, tmp4); - - vec2 = __lasx_xvsrai_w(tmp2, 1); - vec2 = __lasx_xvsub_w(vec2, tmp6); - vec3 = __lasx_xvsrai_w(tmp6, 1); - vec3 = __lasx_xvadd_w(vec3, tmp2); - - tmp0 = __lasx_xvadd_w(vec0, vec3); - tmp2 = __lasx_xvadd_w(vec1, vec2); - tmp4 = __lasx_xvsub_w(vec1, vec2); - tmp6 = __lasx_xvsub_w(vec0, vec3); - - vec0 = __lasx_xvsrai_w(tmp7, 1); - vec0 = __lasx_xvsub_w(tmp5, vec0); - vec0 = __lasx_xvsub_w(vec0, tmp3); - vec0 = __lasx_xvsub_w(vec0, tmp7); - - vec1 = __lasx_xvsrai_w(tmp3, 1); - vec1 = __lasx_xvsub_w(tmp1, vec1); - vec1 = __lasx_xvadd_w(vec1, tmp7); - vec1 = __lasx_xvsub_w(vec1, tmp3); - - vec2 = __lasx_xvsrai_w(tmp5, 1); - vec2 = __lasx_xvsub_w(vec2, tmp1); - vec2 = __lasx_xvadd_w(vec2, tmp7); - vec2 = __lasx_xvadd_w(vec2, tmp5); - - vec3 = __lasx_xvsrai_w(tmp1, 1); - vec3 = __lasx_xvadd_w(tmp3, vec3); - vec3 = __lasx_xvadd_w(vec3, tmp5); - vec3 = __lasx_xvadd_w(vec3, tmp1); - - tmp1 = __lasx_xvsrai_w(vec3, 2); - tmp1 = __lasx_xvadd_w(tmp1, vec0); - tmp3 = __lasx_xvsrai_w(vec2, 2); - tmp3 = __lasx_xvadd_w(tmp3, vec1); - tmp5 = __lasx_xvsrai_w(vec1, 2); - tmp5 = __lasx_xvsub_w(tmp5, vec2); - tmp7 = __lasx_xvsrai_w(vec0, 2); - tmp7 = __lasx_xvsub_w(vec3, tmp7); - - LASX_BUTTERFLY_4_W(tmp0, tmp2, tmp5, tmp7, res0, res1, res6, res7); - LASX_BUTTERFLY_4_W(tmp4, tmp6, tmp1, tmp3, res2, res3, res4, res5); - - DUP4_ARG2(__lasx_xvsrai_w, res0, 6, res1, 6, res2, 6, res3, 6, - res0, res1, res2, res3); - DUP4_ARG2(__lasx_xvsrai_w, res4, 6, res5, 6, res6, 6, res7, 6, - res4, res5, res6, res7); - DUP4_ARG2(__lasx_xvpickev_h, res1, res0, res3, res2, res5, res4, res7, - res6, res0, res1, res2, res3); - DUP4_ARG2(__lasx_xvpermi_d, res0, 0xd8, res1, 0xd8, res2, 0xd8, res3, 0xd8, - res0, res1, res2, res3); - - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, dst_stride, dst, dst_stride_2x, - dst, dst_stride_3x, dst0, dst1, dst2, dst3); - dst += dst_stride_4x; - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, dst_stride, dst, dst_stride_2x, - dst, dst_stride_3x, dst4, dst5, dst6, dst7); - dst -= dst_stride_4x; - DUP4_ARG2(__lasx_xvilvl_b, zero, dst0, zero, dst1, zero, dst2, zero, dst3, - dst0, dst1, dst2, dst3); - DUP4_ARG2(__lasx_xvilvl_b, zero, dst4, zero, dst5, zero, dst6, zero, dst7, - dst4, dst5, dst6, dst7); - DUP4_ARG3(__lasx_xvpermi_q, dst1, dst0, 0x20, dst3, dst2, 0x20, dst5, - dst4, 0x20, dst7, dst6, 0x20, dst0, dst1, dst2, dst3); - res0 = __lasx_xvadd_h(res0, dst0); - res1 = __lasx_xvadd_h(res1, dst1); - res2 = __lasx_xvadd_h(res2, dst2); - res3 = __lasx_xvadd_h(res3, dst3); - DUP4_ARG1(__lasx_xvclip255_h, res0, res1, res2, res3, res0, res1, - res2, res3); - DUP2_ARG2(__lasx_xvpickev_b, res1, res0, res3, res2, res0, res1); - __lasx_xvstelm_d(res0, dst, 0, 0); - __lasx_xvstelm_d(res0, dst + dst_stride, 0, 2); - __lasx_xvstelm_d(res0, dst + dst_stride_2x, 0, 1); - __lasx_xvstelm_d(res0, dst + dst_stride_3x, 0, 3); - dst += dst_stride_4x; - __lasx_xvstelm_d(res1, dst, 0, 0); - __lasx_xvstelm_d(res1, dst + dst_stride, 0, 2); - __lasx_xvstelm_d(res1, dst + dst_stride_2x, 0, 1); - __lasx_xvstelm_d(res1, dst + dst_stride_3x, 0, 3); -} - -void ff_h264_idct4x4_addblk_dc_lasx(uint8_t *dst, int16_t *src, - int32_t dst_stride) -{ - const int16_t dc = (src[0] + 32) >> 6; - int32_t dst_stride_2x = dst_stride << 1; - int32_t dst_stride_3x = dst_stride_2x + dst_stride; - __m256i pred, out; - __m256i src0, src1, src2, src3; - __m256i input_dc = __lasx_xvreplgr2vr_h(dc); - - src[0] = 0; - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, dst_stride, dst, dst_stride_2x, - dst, dst_stride_3x, src0, src1, src2, src3); - DUP2_ARG2(__lasx_xvilvl_w, src1, src0, src3, src2, src0, src1); - - pred = __lasx_xvpermi_q(src0, src1, 0x02); - pred = __lasx_xvaddw_h_h_bu(input_dc, pred); - pred = __lasx_xvclip255_h(pred); - out = __lasx_xvpickev_b(pred, pred); - __lasx_xvstelm_w(out, dst, 0, 0); - __lasx_xvstelm_w(out, dst + dst_stride, 0, 1); - __lasx_xvstelm_w(out, dst + dst_stride_2x, 0, 4); - __lasx_xvstelm_w(out, dst + dst_stride_3x, 0, 5); -} - -void ff_h264_idct8_dc_addblk_lasx(uint8_t *dst, int16_t *src, - int32_t dst_stride) -{ - int32_t dc_val; - int32_t dst_stride_2x = dst_stride << 1; - int32_t dst_stride_4x = dst_stride << 2; - int32_t dst_stride_3x = dst_stride_2x + dst_stride; - __m256i dst0, dst1, dst2, dst3, dst4, dst5, dst6, dst7; - __m256i dc; - - dc_val = (src[0] + 32) >> 6; - dc = __lasx_xvreplgr2vr_h(dc_val); - - src[0] = 0; - - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, dst_stride, dst, dst_stride_2x, - dst, dst_stride_3x, dst0, dst1, dst2, dst3); - dst += dst_stride_4x; - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, dst_stride, dst, dst_stride_2x, - dst, dst_stride_3x, dst4, dst5, dst6, dst7); - dst -= dst_stride_4x; - DUP4_ARG1(__lasx_vext2xv_hu_bu, dst0, dst1, dst2, dst3, - dst0, dst1, dst2, dst3); - DUP4_ARG1(__lasx_vext2xv_hu_bu, dst4, dst5, dst6, dst7, - dst4, dst5, dst6, dst7); - DUP4_ARG3(__lasx_xvpermi_q, dst1, dst0, 0x20, dst3, dst2, 0x20, dst5, - dst4, 0x20, dst7, dst6, 0x20, dst0, dst1, dst2, dst3); - dst0 = __lasx_xvadd_h(dst0, dc); - dst1 = __lasx_xvadd_h(dst1, dc); - dst2 = __lasx_xvadd_h(dst2, dc); - dst3 = __lasx_xvadd_h(dst3, dc); - DUP4_ARG1(__lasx_xvclip255_h, dst0, dst1, dst2, dst3, - dst0, dst1, dst2, dst3); - DUP2_ARG2(__lasx_xvpickev_b, dst1, dst0, dst3, dst2, dst0, dst1); - __lasx_xvstelm_d(dst0, dst, 0, 0); - __lasx_xvstelm_d(dst0, dst + dst_stride, 0, 2); - __lasx_xvstelm_d(dst0, dst + dst_stride_2x, 0, 1); - __lasx_xvstelm_d(dst0, dst + dst_stride_3x, 0, 3); - dst += dst_stride_4x; - __lasx_xvstelm_d(dst1, dst, 0, 0); - __lasx_xvstelm_d(dst1, dst + dst_stride, 0, 2); - __lasx_xvstelm_d(dst1, dst + dst_stride_2x, 0, 1); - __lasx_xvstelm_d(dst1, dst + dst_stride_3x, 0, 3); -} - -void ff_h264_idct_add16_lasx(uint8_t *dst, - const int32_t *blk_offset, - int16_t *block, int32_t dst_stride, - const uint8_t nzc[15 * 8]) -{ - int32_t i; - - for (i = 0; i < 16; i++) { - int32_t nnz = nzc[scan8[i]]; - - if (nnz) { - if (nnz == 1 && ((dctcoef *) block)[i * 16]) - ff_h264_idct4x4_addblk_dc_lasx(dst + blk_offset[i], - block + i * 16 * sizeof(pixel), - dst_stride); - else - ff_h264_idct_add_lasx(dst + blk_offset[i], - block + i * 16 * sizeof(pixel), - dst_stride); - } - } -} - -void ff_h264_idct8_add4_lasx(uint8_t *dst, const int32_t *blk_offset, - int16_t *block, int32_t dst_stride, - const uint8_t nzc[15 * 8]) -{ - int32_t cnt; - - for (cnt = 0; cnt < 16; cnt += 4) { - int32_t nnz = nzc[scan8[cnt]]; - - if (nnz) { - if (nnz == 1 && ((dctcoef *) block)[cnt * 16]) - ff_h264_idct8_dc_addblk_lasx(dst + blk_offset[cnt], - block + cnt * 16 * sizeof(pixel), - dst_stride); - else - ff_h264_idct8_addblk_lasx(dst + blk_offset[cnt], - block + cnt * 16 * sizeof(pixel), - dst_stride); - } - } -} - - -void ff_h264_idct_add8_lasx(uint8_t **dst, - const int32_t *blk_offset, - int16_t *block, int32_t dst_stride, - const uint8_t nzc[15 * 8]) -{ - int32_t i; - - for (i = 16; i < 20; i++) { - if (nzc[scan8[i]]) - ff_h264_idct_add_lasx(dst[0] + blk_offset[i], - block + i * 16 * sizeof(pixel), - dst_stride); - else if (((dctcoef *) block)[i * 16]) - ff_h264_idct4x4_addblk_dc_lasx(dst[0] + blk_offset[i], - block + i * 16 * sizeof(pixel), - dst_stride); - } - for (i = 32; i < 36; i++) { - if (nzc[scan8[i]]) - ff_h264_idct_add_lasx(dst[1] + blk_offset[i], - block + i * 16 * sizeof(pixel), - dst_stride); - else if (((dctcoef *) block)[i * 16]) - ff_h264_idct4x4_addblk_dc_lasx(dst[1] + blk_offset[i], - block + i * 16 * sizeof(pixel), - dst_stride); - } -} - -void ff_h264_idct_add8_422_lasx(uint8_t **dst, - const int32_t *blk_offset, - int16_t *block, int32_t dst_stride, - const uint8_t nzc[15 * 8]) -{ - int32_t i; - - for (i = 16; i < 20; i++) { - if (nzc[scan8[i]]) - ff_h264_idct_add_lasx(dst[0] + blk_offset[i], - block + i * 16 * sizeof(pixel), - dst_stride); - else if (((dctcoef *) block)[i * 16]) - ff_h264_idct4x4_addblk_dc_lasx(dst[0] + blk_offset[i], - block + i * 16 * sizeof(pixel), - dst_stride); - } - for (i = 32; i < 36; i++) { - if (nzc[scan8[i]]) - ff_h264_idct_add_lasx(dst[1] + blk_offset[i], - block + i * 16 * sizeof(pixel), - dst_stride); - else if (((dctcoef *) block)[i * 16]) - ff_h264_idct4x4_addblk_dc_lasx(dst[1] + blk_offset[i], - block + i * 16 * sizeof(pixel), - dst_stride); - } - for (i = 20; i < 24; i++) { - if (nzc[scan8[i + 4]]) - ff_h264_idct_add_lasx(dst[0] + blk_offset[i + 4], - block + i * 16 * sizeof(pixel), - dst_stride); - else if (((dctcoef *) block)[i * 16]) - ff_h264_idct4x4_addblk_dc_lasx(dst[0] + blk_offset[i + 4], - block + i * 16 * sizeof(pixel), - dst_stride); - } - for (i = 36; i < 40; i++) { - if (nzc[scan8[i + 4]]) - ff_h264_idct_add_lasx(dst[1] + blk_offset[i + 4], - block + i * 16 * sizeof(pixel), - dst_stride); - else if (((dctcoef *) block)[i * 16]) - ff_h264_idct4x4_addblk_dc_lasx(dst[1] + blk_offset[i + 4], - block + i * 16 * sizeof(pixel), - dst_stride); - } -} - -void ff_h264_idct_add16_intra_lasx(uint8_t *dst, - const int32_t *blk_offset, - int16_t *block, - int32_t dst_stride, - const uint8_t nzc[15 * 8]) -{ - int32_t i; - - for (i = 0; i < 16; i++) { - if (nzc[scan8[i]]) - ff_h264_idct_add_lasx(dst + blk_offset[i], - block + i * 16 * sizeof(pixel), dst_stride); - else if (((dctcoef *) block)[i * 16]) - ff_h264_idct4x4_addblk_dc_lasx(dst + blk_offset[i], - block + i * 16 * sizeof(pixel), - dst_stride); - } -} - -void ff_h264_deq_idct_luma_dc_lasx(int16_t *dst, int16_t *src, - int32_t de_qval) -{ -#define DC_DEST_STRIDE 16 - - __m256i src0, src1, src2, src3; - __m256i vec0, vec1, vec2, vec3; - __m256i tmp0, tmp1, tmp2, tmp3; - __m256i hres0, hres1, hres2, hres3; - __m256i vres0, vres1, vres2, vres3; - __m256i de_q_vec = __lasx_xvreplgr2vr_w(de_qval); - - DUP4_ARG2(__lasx_xvld, src, 0, src, 8, src, 16, src, 24, - src0, src1, src2, src3); - LASX_TRANSPOSE4x4_H(src0, src1, src2, src3, tmp0, tmp1, tmp2, tmp3); - LASX_BUTTERFLY_4_H(tmp0, tmp2, tmp3, tmp1, vec0, vec3, vec2, vec1); - LASX_BUTTERFLY_4_H(vec0, vec1, vec2, vec3, hres0, hres3, hres2, hres1); - LASX_TRANSPOSE4x4_H(hres0, hres1, hres2, hres3, - hres0, hres1, hres2, hres3); - LASX_BUTTERFLY_4_H(hres0, hres1, hres3, hres2, vec0, vec3, vec2, vec1); - LASX_BUTTERFLY_4_H(vec0, vec1, vec2, vec3, vres0, vres1, vres2, vres3); - DUP4_ARG1(__lasx_vext2xv_w_h, vres0, vres1, vres2, vres3, - vres0, vres1, vres2, vres3); - DUP2_ARG3(__lasx_xvpermi_q, vres1, vres0, 0x20, vres3, vres2, 0x20, - vres0, vres1); - - vres0 = __lasx_xvmul_w(vres0, de_q_vec); - vres1 = __lasx_xvmul_w(vres1, de_q_vec); - - vres0 = __lasx_xvsrari_w(vres0, 8); - vres1 = __lasx_xvsrari_w(vres1, 8); - vec0 = __lasx_xvpickev_h(vres1, vres0); - vec0 = __lasx_xvpermi_d(vec0, 0xd8); - __lasx_xvstelm_h(vec0, dst + 0 * DC_DEST_STRIDE, 0, 0); - __lasx_xvstelm_h(vec0, dst + 2 * DC_DEST_STRIDE, 0, 1); - __lasx_xvstelm_h(vec0, dst + 8 * DC_DEST_STRIDE, 0, 2); - __lasx_xvstelm_h(vec0, dst + 10 * DC_DEST_STRIDE, 0, 3); - __lasx_xvstelm_h(vec0, dst + 1 * DC_DEST_STRIDE, 0, 4); - __lasx_xvstelm_h(vec0, dst + 3 * DC_DEST_STRIDE, 0, 5); - __lasx_xvstelm_h(vec0, dst + 9 * DC_DEST_STRIDE, 0, 6); - __lasx_xvstelm_h(vec0, dst + 11 * DC_DEST_STRIDE, 0, 7); - __lasx_xvstelm_h(vec0, dst + 4 * DC_DEST_STRIDE, 0, 8); - __lasx_xvstelm_h(vec0, dst + 6 * DC_DEST_STRIDE, 0, 9); - __lasx_xvstelm_h(vec0, dst + 12 * DC_DEST_STRIDE, 0, 10); - __lasx_xvstelm_h(vec0, dst + 14 * DC_DEST_STRIDE, 0, 11); - __lasx_xvstelm_h(vec0, dst + 5 * DC_DEST_STRIDE, 0, 12); - __lasx_xvstelm_h(vec0, dst + 7 * DC_DEST_STRIDE, 0, 13); - __lasx_xvstelm_h(vec0, dst + 13 * DC_DEST_STRIDE, 0, 14); - __lasx_xvstelm_h(vec0, dst + 15 * DC_DEST_STRIDE, 0, 15); - -#undef DC_DEST_STRIDE -} diff --git a/libavcodec/loongarch/h264idct_loongarch.c b/libavcodec/loongarch/h264idct_loongarch.c new file mode 100644 index 00000000000..26af45503f1 --- /dev/null +++ b/libavcodec/loongarch/h264idct_loongarch.c @@ -0,0 +1,184 @@ +/* + * Loongson LSX/LASX optimized h264idct + * + * Copyright (c) 2023 Loongson Technology Corporation Limited + * Contributed by Shiyou Yin + * Xiwei Gu + * + * 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 "h264dsp_loongarch.h" +#include "libavcodec/bit_depth_template.c" + +void ff_h264_idct_add16_8_lsx(uint8_t *dst, const int32_t *blk_offset, + int16_t *block, int32_t dst_stride, + const uint8_t nzc[15 * 8]) +{ + int32_t i; + + for (i = 0; i < 16; i++) { + int32_t nnz = nzc[scan8[i]]; + + if (nnz == 1 && ((dctcoef *) block)[i * 16]) { + ff_h264_idct_dc_add_8_lsx(dst + blk_offset[i], + block + i * 16 * sizeof(pixel), + dst_stride); + } else if (nnz) { + ff_h264_idct_add_8_lsx(dst + blk_offset[i], + block + i * 16 * sizeof(pixel), + dst_stride); + } + } +} + +void ff_h264_idct8_add4_8_lsx(uint8_t *dst, const int32_t *blk_offset, + int16_t *block, int32_t dst_stride, + const uint8_t nzc[15 * 8]) +{ + int32_t cnt; + + for (cnt = 0; cnt < 16; cnt += 4) { + int32_t nnz = nzc[scan8[cnt]]; + + if (nnz == 1 && ((dctcoef *) block)[cnt * 16]) { + ff_h264_idct8_dc_add_8_lsx(dst + blk_offset[cnt], + block + cnt * 16 * sizeof(pixel), + dst_stride); + } else if (nnz) { + ff_h264_idct8_add_8_lsx(dst + blk_offset[cnt], + block + cnt * 16 * sizeof(pixel), + dst_stride); + } + } +} + +#if HAVE_LASX +void ff_h264_idct8_add4_8_lasx(uint8_t *dst, const int32_t *blk_offset, + int16_t *block, int32_t dst_stride, + const uint8_t nzc[15 * 8]) +{ + int32_t cnt; + + for (cnt = 0; cnt < 16; cnt += 4) { + int32_t nnz = nzc[scan8[cnt]]; + + if (nnz == 1 && ((dctcoef *) block)[cnt * 16]) { + ff_h264_idct8_dc_add_8_lasx(dst + blk_offset[cnt], + block + cnt * 16 * sizeof(pixel), + dst_stride); + } else if (nnz) { + ff_h264_idct8_add_8_lasx(dst + blk_offset[cnt], + block + cnt * 16 * sizeof(pixel), + dst_stride); + } + } +} +#endif // #if HAVE_LASX + +void ff_h264_idct_add8_8_lsx(uint8_t **dst, const int32_t *blk_offset, + int16_t *block, int32_t dst_stride, + const uint8_t nzc[15 * 8]) +{ + int32_t i; + + for (i = 16; i < 20; i++) { + if (nzc[scan8[i]]) + ff_h264_idct_add_8_lsx(dst[0] + blk_offset[i], + block + i * 16 * sizeof(pixel), + dst_stride); + else if (((dctcoef *) block)[i * 16]) + ff_h264_idct_dc_add_8_lsx(dst[0] + blk_offset[i], + block + i * 16 * sizeof(pixel), + dst_stride); + } + for (i = 32; i < 36; i++) { + if (nzc[scan8[i]]) + ff_h264_idct_add_8_lsx(dst[1] + blk_offset[i], + block + i * 16 * sizeof(pixel), + dst_stride); + else if (((dctcoef *) block)[i * 16]) + ff_h264_idct_dc_add_8_lsx(dst[1] + blk_offset[i], + block + i * 16 * sizeof(pixel), + dst_stride); + } +} + +void ff_h264_idct_add8_422_8_lsx(uint8_t **dst, const int32_t *blk_offset, + int16_t *block, int32_t dst_stride, + const uint8_t nzc[15 * 8]) +{ + int32_t i; + + for (i = 16; i < 20; i++) { + if (nzc[scan8[i]]) + ff_h264_idct_add_8_lsx(dst[0] + blk_offset[i], + block + i * 16 * sizeof(pixel), + dst_stride); + else if (((dctcoef *) block)[i * 16]) + ff_h264_idct_dc_add_8_lsx(dst[0] + blk_offset[i], + block + i * 16 * sizeof(pixel), + dst_stride); + } + for (i = 20; i < 24; i++) { + if (nzc[scan8[i + 4]]) + ff_h264_idct_add_8_lsx(dst[0] + blk_offset[i + 4], + block + i * 16 * sizeof(pixel), + dst_stride); + else if (((dctcoef *) block)[i * 16]) + ff_h264_idct_dc_add_8_lsx(dst[0] + blk_offset[i + 4], + block + i * 16 * sizeof(pixel), + dst_stride); + } + for (i = 32; i < 36; i++) { + if (nzc[scan8[i]]) + ff_h264_idct_add_8_lsx(dst[1] + blk_offset[i], + block + i * 16 * sizeof(pixel), + dst_stride); + else if (((dctcoef *) block)[i * 16]) + ff_h264_idct_dc_add_8_lsx(dst[1] + blk_offset[i], + block + i * 16 * sizeof(pixel), + dst_stride); + } + for (i = 36; i < 40; i++) { + if (nzc[scan8[i + 4]]) + ff_h264_idct_add_8_lsx(dst[1] + blk_offset[i + 4], + block + i * 16 * sizeof(pixel), + dst_stride); + else if (((dctcoef *) block)[i * 16]) + ff_h264_idct_dc_add_8_lsx(dst[1] + blk_offset[i + 4], + block + i * 16 * sizeof(pixel), + dst_stride); + } +} + +void ff_h264_idct_add16_intra_8_lsx(uint8_t *dst, const int32_t *blk_offset, + int16_t *block, int32_t dst_stride, + const uint8_t nzc[15 * 8]) +{ + int32_t i; + + for (i = 0; i < 16; i++) { + if (nzc[scan8[i]]) + ff_h264_idct_add_8_lsx(dst + blk_offset[i], + block + i * 16 * sizeof(pixel), dst_stride); + else if (((dctcoef *) block)[i * 16]) + ff_h264_idct_dc_add_8_lsx(dst + blk_offset[i], + block + i * 16 * sizeof(pixel), + dst_stride); + } +} diff --git a/libavcodec/loongarch/h264intrapred.S b/libavcodec/loongarch/h264intrapred.S new file mode 100644 index 00000000000..a03f467b6ef --- /dev/null +++ b/libavcodec/loongarch/h264intrapred.S @@ -0,0 +1,299 @@ +/* + * Loongson LSX optimized h264intrapred + * + * Copyright (c) 2023 Loongson Technology Corporation Limited + * Contributed by Lu Wang + * + * 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 "loongson_asm.S" + +const shufa +.byte 6, 5, 4, 3, 2, 1, 0 +endconst + +const mulk +.byte 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0 +endconst + +const mulh +.byte 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0 +.byte 8, 0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0 +endconst + +.macro PRED16X16_PLANE + slli.d t6, a1, 1 + slli.d t4, a1, 3 + addi.d t0, a0, 7 + sub.d t0, t0, a1 + add.d t1, a0, t4 + addi.d t1, t1, -1 + sub.d t2, t1, t6 + + ld.bu t3, t0, 1 + ld.bu t4, t0, -1 + ld.bu t5, t1, 0 + ld.bu t7, t2, 0 + sub.d t3, t3, t4 + sub.d t4, t5, t7 + + la.local t5, mulk + vld vr0, t5, 0 + fld.d f1, t0, 2 + fld.d f2, t0, -8 + la.local t5, shufa + fld.d f3, t5, 0 + vshuf.b vr2, vr2, vr2, vr3 + vilvl.b vr1, vr1, vr2 + vhsubw.hu.bu vr1, vr1, vr1 + vmul.h vr0, vr0, vr1 + vhaddw.w.h vr1, vr0, vr0 + vhaddw.d.w vr0, vr1, vr1 + vhaddw.q.d vr1, vr0, vr0 + vpickve2gr.w t5, vr1, 0 + add.d t3, t3, t5 +//2 + sub.d t2, t2, a1 + ld.bu t8, t2, 0 + ldx.bu t7, t1, a1 + sub.d t5, t7, t8 + slli.d t5, t5, 1 + +//3&4 + add.d t1, t1, t6 + sub.d t2, t2, a1 + ld.bu t8, t2, 0 + ld.bu t7, t1, 0 + sub.d t7, t7, t8 + slli.d t8, t7, 1 + add.d t7, t7, t8 + add.d t5, t5, t7 + sub.d t2, t2, a1 + ld.bu t8, t2, 0 + ldx.bu t7, t1, a1 + sub.d t7, t7, t8 + slli.d t7, t7, 2 + add.d t5, t5, t7 + +//5&6 + add.d t1, t1, t6 + sub.d t2, t2, a1 + ld.bu t8, t2, 0 + ld.bu t7, t1, 0 + sub.d t7, t7, t8 + slli.d t8, t7, 2 + add.d t7, t7, t8 + add.d t5, t5, t7 + sub.d t2, t2, a1 + ld.bu t8, t2, 0 + ldx.bu t7, t1, a1 + sub.d t7, t7, t8 + slli.d t8, t7, 1 + slli.d t7, t7, 2 + add.d t7, t7, t8 + add.d t5, t5, t7 + +//7&8 + add.d t1, t1, t6 + sub.d t2, t2, a1 + ld.bu t8, t2, 0 + ld.bu t7, t1, 0 + sub.d t7, t7, t8 + slli.d t8, t7, 3 + sub.d t7, t8, t7 + add.d t5, t5, t7 + sub.d t2, t2, a1 + ld.bu t8, t2, 0 + ldx.bu t7, t1, a1 + sub.d t7, t7, t8 + slli.d t7, t7, 3 + add.d t5, t5, t7 + add.d t4, t4, t5 + add.d t1, t1, a1 +.endm + +.macro PRED16X16_PLANE_END + ld.bu t7, t1, 0 + ld.bu t8, t2, 16 + add.d t5, t7, t8 + addi.d t5, t5, 1 + slli.d t5, t5, 4 + add.d t7, t3, t4 + slli.d t8, t7, 3 + sub.d t7, t8, t7 + sub.d t5, t5, t7 + + la.local t8, mulh + vld vr3, t8, 0 + slli.d t8, t3, 3 + vreplgr2vr.h vr4, t3 + vreplgr2vr.h vr9, t8 + vmul.h vr5, vr3, vr4 + +.rept 16 + move t7, t5 + add.d t5, t5, t4 + vreplgr2vr.h vr6, t7 + vadd.h vr7, vr6, vr5 + vadd.h vr8, vr9, vr7 + vssrani.bu.h vr8, vr7, 5 + vst vr8, a0, 0 + add.d a0, a0, a1 +.endr +.endm + +.macro PRED16X16_PLANE_END_LASX + ld.bu t7, t1, 0 + ld.bu t8, t2, 16 + add.d t5, t7, t8 + addi.d t5, t5, 1 + slli.d t5, t5, 4 + add.d t7, t3, t4 + slli.d t8, t7, 3 + sub.d t7, t8, t7 + sub.d t5, t5, t7 + + la.local t8, mulh + xvld xr3, t8, 0 + xvreplgr2vr.h xr4, t3 + xvmul.h xr5, xr3, xr4 + +.rept 8 + move t7, t5 + add.d t5, t5, t4 + xvreplgr2vr.h xr6, t7 + xvreplgr2vr.h xr8, t5 + add.d t5, t5, t4 + xvadd.h xr7, xr6, xr5 + xvadd.h xr9, xr8, xr5 + + xvssrani.bu.h xr9, xr7, 5 + vstelm.d vr9, a0, 0, 0 + xvstelm.d xr9, a0, 8, 2 + add.d a0, a0, a1 + vstelm.d vr9, a0, 0, 1 + xvstelm.d xr9, a0, 8, 3 + add.d a0, a0, a1 +.endr +.endm + +/* void ff_h264_pred16x16_plane_h264_8_lsx(uint8_t *src, ptrdiff_t stride) + */ +function ff_h264_pred16x16_plane_h264_8_lsx + PRED16X16_PLANE + + slli.d t7, t3, 2 + add.d t3, t3, t7 + addi.d t3, t3, 32 + srai.d t3, t3, 6 + slli.d t7, t4, 2 + add.d t4, t4, t7 + addi.d t4, t4, 32 + srai.d t4, t4, 6 + + PRED16X16_PLANE_END +endfunc + +/* void ff_h264_pred16x16_plane_rv40_8_lsx(uint8_t *src, ptrdiff_t stride) + */ +function ff_h264_pred16x16_plane_rv40_8_lsx + PRED16X16_PLANE + + srai.d t7, t3, 2 + add.d t3, t3, t7 + srai.d t3, t3, 4 + srai.d t7, t4, 2 + add.d t4, t4, t7 + srai.d t4, t4, 4 + + PRED16X16_PLANE_END +endfunc + +/* void ff_h264_pred16x16_plane_svq3_8_lsx(uint8_t *src, ptrdiff_t stride) + */ +function ff_h264_pred16x16_plane_svq3_8_lsx + PRED16X16_PLANE + + li.d t6, 4 + li.d t7, 5 + li.d t8, 16 + div.d t3, t3, t6 + mul.d t3, t3, t7 + div.d t3, t3, t8 + div.d t4, t4, t6 + mul.d t4, t4, t7 + div.d t4, t4, t8 + move t7, t3 + move t3, t4 + move t4, t7 + + PRED16X16_PLANE_END +endfunc + +/* void ff_h264_pred16x16_plane_h264_8_lasx(uint8_t *src, ptrdiff_t stride) + */ +function ff_h264_pred16x16_plane_h264_8_lasx + PRED16X16_PLANE + + slli.d t7, t3, 2 + add.d t3, t3, t7 + addi.d t3, t3, 32 + srai.d t3, t3, 6 + slli.d t7, t4, 2 + add.d t4, t4, t7 + addi.d t4, t4, 32 + srai.d t4, t4, 6 + + PRED16X16_PLANE_END_LASX +endfunc + +/* void ff_h264_pred16x16_plane_rv40_8_lasx(uint8_t *src, ptrdiff_t stride) + */ +function ff_h264_pred16x16_plane_rv40_8_lasx + PRED16X16_PLANE + + srai.d t7, t3, 2 + add.d t3, t3, t7 + srai.d t3, t3, 4 + srai.d t7, t4, 2 + add.d t4, t4, t7 + srai.d t4, t4, 4 + + PRED16X16_PLANE_END_LASX +endfunc + +/* void ff_h264_pred16x16_plane_svq3_8_lasx(uint8_t *src, ptrdiff_t stride) + */ +function ff_h264_pred16x16_plane_svq3_8_lasx + PRED16X16_PLANE + + li.d t5, 4 + li.d t7, 5 + li.d t8, 16 + div.d t3, t3, t5 + mul.d t3, t3, t7 + div.d t3, t3, t8 + div.d t4, t4, t5 + mul.d t4, t4, t7 + div.d t4, t4, t8 + move t7, t3 + move t3, t4 + move t4, t7 + + PRED16X16_PLANE_END_LASX +endfunc diff --git a/libavcodec/loongarch/h264qpel.S b/libavcodec/loongarch/h264qpel.S new file mode 100644 index 00000000000..3f885b6ce23 --- /dev/null +++ b/libavcodec/loongarch/h264qpel.S @@ -0,0 +1,1686 @@ +/* + * Loongson LSX optimized h264qpel + * + * Copyright (c) 2023 Loongson Technology Corporation Limited + * Contributed by Hecai Yuan + * + * 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 "loongson_asm.S" + +.macro VLD_QPEL8_H_SSRANI_LSX in0, in1, in2, in3, in4 + vld vr0, \in4, 0 + vldx vr1, \in4, a2 + QPEL8_H_LSX \in0, \in1 + vssrani.bu.h \in0, \in2, 5 + vssrani.bu.h \in1, \in3, 5 +.endm + +.macro VLDX_QPEL8_H_SSRANI_LSX in0, in1, in2, in3, in4 + vldx vr0, \in4, t1 + vldx vr1, \in4, t2 + QPEL8_H_LSX \in0, \in1 + vssrani.bu.h \in0, \in2, 5 + vssrani.bu.h \in1, \in3, 5 +.endm + +.macro VLD_DOUBLE_QPEL8_H_SSRANI_LSX in0, in1, in2, in3, in4, in5, in6, in7, in8 + vld vr0, \in8, 0 + vldx vr1, \in8, a2 + QPEL8_H_LSX \in0, \in1 + vssrani.bu.h \in0, \in4, 5 + vssrani.bu.h \in1, \in5, 5 + vldx vr0, \in8, t1 + vldx vr1, \in8, t2 + QPEL8_H_LSX \in2, \in3 + vssrani.bu.h \in2, \in6, 5 + vssrani.bu.h \in3, \in7, 5 +.endm + +function ff_put_h264_qpel16_mc00_lsx + slli.d t0, a2, 1 + add.d t1, t0, a2 + slli.d t2, t0, 1 +.rept 4 + vld vr0, a1, 0 + vldx vr1, a1, a2 + vldx vr2, a1, t0 + vldx vr3, a1, t1 + add.d a1, a1, t2 + vst vr0, a0, 0 + vstx vr1, a0, a2 + vstx vr2, a0, t0 + vstx vr3, a0, t1 + add.d a0, a0, t2 +.endr +endfunc + +.macro QPEL8_H_LSX out0, out1 + vbsrl.v vr2, vr0, 1 + vbsrl.v vr3, vr1, 1 + vbsrl.v vr4, vr0, 2 + vbsrl.v vr5, vr1, 2 + vbsrl.v vr6, vr0, 3 + vbsrl.v vr7, vr1, 3 + vbsrl.v vr8, vr0, 4 + vbsrl.v vr9, vr1, 4 + vbsrl.v vr10, vr0, 5 + vbsrl.v vr11, vr1, 5 + + vilvl.b vr6, vr4, vr6 + vilvl.b vr7, vr5, vr7 + vilvl.b vr8, vr2, vr8 + vilvl.b vr9, vr3, vr9 + vilvl.b vr10, vr0, vr10 + vilvl.b vr11, vr1, vr11 + vhaddw.hu.bu vr6, vr6, vr6 + vhaddw.hu.bu vr7, vr7, vr7 + vhaddw.hu.bu vr8, vr8, vr8 + vhaddw.hu.bu vr9, vr9, vr9 + vhaddw.hu.bu vr10, vr10, vr10 + vhaddw.hu.bu vr11, vr11, vr11 + vmul.h vr2, vr6, vr20 + vmul.h vr3, vr7, vr20 + vmul.h vr4, vr8, vr21 + vmul.h vr5, vr9, vr21 + vssub.h vr2, vr2, vr4 + vssub.h vr3, vr3, vr5 + vsadd.h vr2, vr2, vr10 + vsadd.h vr3, vr3, vr11 + vsadd.h \out0, vr2, vr22 + vsadd.h \out1, vr3, vr22 +.endm + +.macro VLD_DOUBLE_QPEL8_H_LSX in0, in1, in2, in3, in4 + vld vr0, \in4, 0 + vldx vr1, \in4, a2 + QPEL8_H_LSX \in0, \in1 + vldx vr0, \in4, t1 + vldx vr1, \in4, t2 + QPEL8_H_LSX \in2, \in3 +.endm + +.macro put_h264_qpel16 in0 +function ff_put_h264_qpel16_mc\in0\()_lsx +.ifc \in0, 10 + addi.d t8, a1, 0 +.else + addi.d t8, a1, 1 +.endif + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 + slli.d t1, a2, 1 + add.d t2, t1, a2 + addi.d t0, a1, -2 // t0 = src - 2 + addi.d a1, t0, 8 // a1 = t0 + 8 +.rept 4 + VLD_DOUBLE_QPEL8_H_LSX vr12, vr13, vr14, vr15, t0 + VLD_QPEL8_H_SSRANI_LSX vr2, vr3, vr12, vr13, a1 + vld vr10, t8, 0 + vldx vr11, t8, a2 + vavgr.bu vr0, vr2, vr10 + vavgr.bu vr1, vr3, vr11 + vst vr0, a0, 0 + vstx vr1, a0, a2 + VLDX_QPEL8_H_SSRANI_LSX vr4, vr5, vr14, vr15, a1 + vldx vr12, t8, t1 + vldx vr13, t8, t2 + vavgr.bu vr2, vr4, vr12 + vavgr.bu vr3, vr5, vr13 + vstx vr2, a0, t1 + vstx vr3, a0, t2 + alsl.d a0, a2, a0, 2 + alsl.d t8, a2, t8, 2 + alsl.d a1, a2, a1, 2 + alsl.d t0, a2, t0, 2 +.endr +endfunc +.endm + +put_h264_qpel16 10 +put_h264_qpel16 30 + +function ff_put_h264_qpel16_mc20_lsx + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 + slli.d t1, a2, 1 + add.d t2, t1, a2 + addi.d t0, a1, -2 // t0 = src - 2 + addi.d a1, t0, 8 // a1 = t0 + 8 +.rept 4 + VLD_DOUBLE_QPEL8_H_LSX vr12, vr13, vr14, vr15, t0 + VLD_QPEL8_H_SSRANI_LSX vr2, vr3, vr12, vr13, a1 + vst vr2, a0, 0 + vstx vr3, a0, a2 + VLDX_QPEL8_H_SSRANI_LSX vr4, vr5, vr14, vr15, a1 + vstx vr4, a0, t1 + vstx vr5, a0, t2 + alsl.d a0, a2, a0, 2 + alsl.d a1, a2, a1, 2 + alsl.d t0, a2, t0, 2 +.endr +endfunc + +.macro QPEL8_V_LSX in0, in1, in2, in3, in4, in5, in6 + vilvl.b vr7, \in3, \in2 + vilvl.b vr8, \in4, \in3 + vilvl.b vr9, \in4, \in1 + vilvl.b vr10, \in5, \in2 + vilvl.b vr11, \in5, \in0 + vilvl.b vr12, \in6, \in1 + vhaddw.hu.bu vr7, vr7, vr7 + vhaddw.hu.bu vr8, vr8, vr8 + vhaddw.hu.bu vr9, vr9, vr9 + vhaddw.hu.bu vr10, vr10, vr10 + vhaddw.hu.bu vr11, vr11, vr11 + vhaddw.hu.bu vr12, vr12, vr12 + vmul.h vr7, vr7, vr20 + vmul.h vr8, vr8, vr20 + vmul.h vr9, vr9, vr21 + vmul.h vr10, vr10, vr21 + vssub.h vr7, vr7, vr9 + vssub.h vr8, vr8, vr10 + vsadd.h vr7, vr7, vr11 + vsadd.h vr8, vr8, vr12 + vsadd.h vr7, vr7, vr22 + vsadd.h vr8, vr8, vr22 + + vilvh.b vr13, \in3, \in2 + vilvh.b vr14, \in4, \in3 + vilvh.b vr15, \in4, \in1 + vilvh.b vr16, \in5, \in2 + vilvh.b vr17, \in5, \in0 + vilvh.b vr18, \in6, \in1 + vhaddw.hu.bu vr13, vr13, vr13 + vhaddw.hu.bu vr14, vr14, vr14 + vhaddw.hu.bu vr15, vr15, vr15 + vhaddw.hu.bu vr16, vr16, vr16 + vhaddw.hu.bu vr17, vr17, vr17 + vhaddw.hu.bu vr18, vr18, vr18 + vmul.h vr13, vr13, vr20 + vmul.h vr14, vr14, vr20 + vmul.h vr15, vr15, vr21 + vmul.h vr16, vr16, vr21 + vssub.h vr13, vr13, vr15 + vssub.h vr14, vr14, vr16 + vsadd.h vr13, vr13, vr17 + vsadd.h vr14, vr14, vr18 + vsadd.h vr13, vr13, vr22 + vsadd.h vr14, vr14, vr22 + vssrani.bu.h vr13, vr7, 5 + vssrani.bu.h vr14, vr8, 5 +.endm + +.macro put_h264_qpel16_mc1 in0 +function ff_put_h264_qpel16_mc\in0\()_lsx + slli.d t0, a2, 1 + add.d t1, t0, a2 + sub.d t2, a1, t0 // t2 = src - 2 * stride + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 + + vld vr0, t2, 0 + vldx vr1, t2, a2 + vldx vr2, t2, t0 + vldx vr3, t2, t1 + alsl.d t2, a2, t2, 2 // t2 = t2 + 4 * stride + vld vr4, t2, 0 + vldx vr5, t2, a2 + vldx vr6, t2, t0 + QPEL8_V_LSX vr0, vr1, vr2, vr3, vr4, vr5, vr6 +.ifc \in0, 01 + vavgr.bu vr13, vr2, vr13 + vavgr.bu vr14, vr3, vr14 +.else + vavgr.bu vr13, vr3, vr13 + vavgr.bu vr14, vr4, vr14 +.endif + vst vr13, a0, 0 + vstx vr14, a0, a2 + + vldx vr0, t2, t1 + alsl.d t2, a2, t2, 2 // t2 = t2 + 4 *stride + vld vr1, t2, 0 + QPEL8_V_LSX vr2, vr3, vr4, vr5, vr6, vr0, vr1 +.ifc \in0, 01 + vavgr.bu vr13, vr4, vr13 + vavgr.bu vr14, vr5, vr14 +.else + vavgr.bu vr13, vr5, vr13 + vavgr.bu vr14, vr6, vr14 +.endif + vstx vr13, a0, t0 + vstx vr14, a0, t1 + + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + + vldx vr2, t2, a2 + vldx vr3, t2, t0 + QPEL8_V_LSX vr4, vr5, vr6, vr0, vr1, vr2, vr3 +.ifc \in0, 01 + vavgr.bu vr13, vr6, vr13 + vavgr.bu vr14, vr0, vr14 +.else + vavgr.bu vr13, vr0, vr13 + vavgr.bu vr14, vr1, vr14 +.endif + vst vr13, a0, 0 + vstx vr14, a0, a2 + + vldx vr4, t2, t1 + alsl.d t2, a2, t2, 2 // t2 = t2 + 4 * stride + vld vr5, t2, 0 + QPEL8_V_LSX vr6, vr0, vr1, vr2, vr3, vr4, vr5 +.ifc \in0, 01 + vavgr.bu vr13, vr1, vr13 + vavgr.bu vr14, vr2, vr14 +.else + vavgr.bu vr13, vr2, vr13 + vavgr.bu vr14, vr3, vr14 +.endif + vstx vr13, a0, t0 + vstx vr14, a0, t1 + + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + + vldx vr6, t2, a2 + vldx vr0, t2, t0 + QPEL8_V_LSX vr1, vr2, vr3, vr4, vr5, vr6, vr0 +.ifc \in0, 01 + vavgr.bu vr13, vr3, vr13 + vavgr.bu vr14, vr4, vr14 +.else + vavgr.bu vr13, vr4, vr13 + vavgr.bu vr14, vr5, vr14 +.endif + vst vr13, a0, 0 + vstx vr14, a0, a2 + + vldx vr1, t2, t1 + alsl.d t2, a2, t2, 2 // t2 = t2 + 4 * stride + vld vr2, t2, 0 + QPEL8_V_LSX vr3, vr4, vr5, vr6, vr0, vr1, vr2 +.ifc \in0, 01 + vavgr.bu vr13, vr5, vr13 + vavgr.bu vr14, vr6, vr14 +.else + vavgr.bu vr13, vr6, vr13 + vavgr.bu vr14, vr0, vr14 +.endif + vstx vr13, a0, t0 + vstx vr14, a0, t1 + + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + + vldx vr3, t2, a2 + vldx vr4, t2, t0 + QPEL8_V_LSX vr5, vr6, vr0, vr1, vr2, vr3, vr4 +.ifc \in0, 01 + vavgr.bu vr13, vr0, vr13 + vavgr.bu vr14, vr1, vr14 +.else + vavgr.bu vr13, vr1, vr13 + vavgr.bu vr14, vr2, vr14 +.endif + vst vr13, a0, 0 + vstx vr14, a0, a2 + + vldx vr5, t2, t1 + alsl.d t2, a2, t2, 2 // t2 = t2 + 4 * stride + vld vr6, t2, 0 + QPEL8_V_LSX vr0, vr1, vr2, vr3, vr4, vr5, vr6 +.ifc \in0, 01 + vavgr.bu vr13, vr2, vr13 + vavgr.bu vr14, vr3, vr14 +.else + vavgr.bu vr13, vr3, vr13 + vavgr.bu vr14, vr4, vr14 +.endif + vstx vr13, a0, t0 + vstx vr14, a0, t1 +endfunc +.endm + +put_h264_qpel16_mc1 01 +put_h264_qpel16_mc1 03 + +.macro VST_QPEL8_V_LOWPASS_LSX in0, in1, in2, in3, in4, in5, in6, in7, in8 + QPEL8_V_LSX \in0, \in1, \in2, \in3, \in4, \in5, \in6 + vavgr.bu vr13, \in7, vr13 + vavgr.bu vr14, \in8, vr14 + vst vr13, a0, 0 + vstx vr14, a0, a2 +.endm + +.macro VSTX_QPEL8_V_LOWPASS_LSX in0, in1, in2, in3, in4, in5, in6, in7, in8 + QPEL8_V_LSX \in0, \in1, \in2, \in3, \in4, \in5, \in6 + vavgr.bu vr13, \in7, vr13 + vavgr.bu vr14, \in8, vr14 + vstx vr13, a0, t1 + vstx vr14, a0, t2 +.endm + +function ff_put_h264_qpel16_mc11_lsx + addi.d sp, sp, -64 + fst.d f24, sp, 0 + fst.d f25, sp, 8 + fst.d f26, sp, 16 + fst.d f27, sp, 24 + fst.d f28, sp, 32 + fst.d f29, sp, 40 + fst.d f30, sp, 48 + fst.d f31, sp, 56 + slli.d t1, a2, 1 + add.d t2, t1, a2 + slli.d t6, t1, 1 + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 + sub.d t4, a1, t1 // t4 = src - 2 * stride + addi.d t0, a1, -2 // t0 = src - 2 + addi.d a1, t0, 8 // a1 = t0 + 8 +.rept 2 + VLD_DOUBLE_QPEL8_H_LSX vr12, vr13, vr14, vr15, t0 + alsl.d t0, a2, t0, 2 + VLD_DOUBLE_QPEL8_H_LSX vr16, vr17, vr18, vr19, t0 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr23, vr24, vr25, vr26, vr12, vr13, \ + vr14, vr15, a1 + alsl.d a1, a2, a1, 2 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr27, vr28, vr29, vr30, vr16, vr17, \ + vr18, vr19, a1 + + vld vr0, t4, 0 // t4 = src - 2 * stride + vldx vr1, t4, a2 + vldx vr2, t4, t1 + vldx vr3, t4, t2 + alsl.d t4, a2, t4, 2 // src + 2 *stride + vld vr4, t4, 0 + vldx vr5, t4, a2 + vldx vr6, t4, t1 + VST_QPEL8_V_LOWPASS_LSX vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr23, vr24 + vldx vr0, t4, t2 + alsl.d t4, a2, t4, 2 // src + 6 *stride + vld vr1, t4, 0 + VSTX_QPEL8_V_LOWPASS_LSX vr2, vr3, vr4, vr5, vr6, vr0, vr1, vr25, vr26 + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + vldx vr2, t4, a2 + vldx vr3, t4, t1 + VST_QPEL8_V_LOWPASS_LSX vr4, vr5, vr6, vr0, vr1, vr2, vr3, vr27, vr28 + vldx vr4, t4, t2 + alsl.d t4, a2, t4, 2 // src + 10 *stride + vld vr5, t4, 0 + VSTX_QPEL8_V_LOWPASS_LSX vr6, vr0, vr1, vr2, vr3, vr4, vr5, vr29, vr30 + alsl.d t0, a2, t0, 2 + alsl.d a1, a2, a1, 2 // a1 = src + 8 * stride + alsl.d a0, a2, a0, 2 // dst = dst + 8 * stride + sub.d t4, t4, t6 +.endr + fld.d f24, sp, 0 + fld.d f25, sp, 8 + fld.d f26, sp, 16 + fld.d f27, sp, 24 + fld.d f28, sp, 32 + fld.d f29, sp, 40 + fld.d f30, sp, 48 + fld.d f31, sp, 56 + addi.d sp, sp, 64 +endfunc + +function ff_avg_h264_qpel16_mc00_lsx + slli.d t0, a2, 1 + add.d t1, t0, a2 + slli.d t2, t0, 1 + addi.d t3, a0, 0 +.rept 4 + vld vr0, a1, 0 + vldx vr1, a1, a2 + vldx vr2, a1, t0 + vldx vr3, a1, t1 + add.d a1, a1, t2 + vld vr8, t3, 0 + vldx vr9, t3, a2 + vldx vr10, t3, t0 + vldx vr11, t3, t1 + add.d t3, t3, t2 + vavgr.bu vr0, vr8, vr0 + vavgr.bu vr1, vr9, vr1 + vavgr.bu vr2, vr10, vr2 + vavgr.bu vr3, vr11, vr3 + vst vr0, a0, 0 + vstx vr1, a0, a2 + vstx vr2, a0, t0 + vstx vr3, a0, t1 + add.d a0, a0, t2 +.endr +endfunc + +.macro put_h264_qpel16_mc in0 +function ff_put_h264_qpel16_mc\in0\()_lsx + addi.d sp, sp, -64 + fst.d f24, sp, 0 + fst.d f25, sp, 8 + fst.d f26, sp, 16 + fst.d f27, sp, 24 + fst.d f28, sp, 32 + fst.d f29, sp, 40 + fst.d f30, sp, 48 + fst.d f31, sp, 56 + slli.d t1, a2, 1 + add.d t2, t1, a2 + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 + addi.d t0, a1, -2 // t0 = src - 2 + +.ifc \in0, 33 + add.d t0, t0, a2 +.endif + add.d t3, a1, zero // t3 = src + sub.d t4, a1, t1 // t4 = src - 2 * stride + addi.d t4, t4, 1 + + VLD_DOUBLE_QPEL8_H_LSX vr12, vr13, vr14, vr15, t0 + alsl.d a1, a2, t0, 2 + VLD_DOUBLE_QPEL8_H_LSX vr16, vr17, vr18, vr19, a1 + addi.d a1, t0, 8 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr23, vr24, vr25, vr26, vr12, vr13, \ + vr14, vr15, a1 + alsl.d a1, a2, a1, 2 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr27, vr28, vr29, vr30, vr16, vr17, \ + vr18, vr19, a1 + vld vr0, t4, 0 // t4 = src - 2 * stride + 1 + vldx vr1, t4, a2 + vldx vr2, t4, t1 + vldx vr3, t4, t2 + alsl.d t4, a2, t4, 2 + vld vr4, t4, 0 + vldx vr5, t4, a2 + vldx vr6, t4, t1 + VST_QPEL8_V_LOWPASS_LSX vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr23, vr24 + vldx vr0, t4, t2 + alsl.d t4, a2, t4, 2 + vld vr1, t4, 0 + VSTX_QPEL8_V_LOWPASS_LSX vr2, vr3, vr4, vr5, vr6, vr0, vr1, vr25, vr26 + add.d t6, t4, zero // t6 = src + 6 * stride + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + vldx vr2, t4, a2 + vldx vr3, t4, t1 + VST_QPEL8_V_LOWPASS_LSX vr4, vr5, vr6, vr0, vr1, vr2, vr3, vr27, vr28 + vldx vr4, t4, t2 + alsl.d t4, a2, t4, 2 + vld vr5, t4, 0 + VSTX_QPEL8_V_LOWPASS_LSX vr6, vr0, vr1, vr2, vr3, vr4, vr5, vr29, vr30 + alsl.d a1, a2, t0, 3 // a1 = src + 8 * stride + addi.d t5, a1, 8 // a1 = src + 8 * stride + 8 + VLD_DOUBLE_QPEL8_H_LSX vr12, vr13, vr14, vr15, a1 + alsl.d a1, a2, a1, 2 + VLD_DOUBLE_QPEL8_H_LSX vr16, vr17, vr18, vr19, a1 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr23, vr24, vr25, vr26, vr12, vr13, \ + vr14, vr15, t5 + alsl.d t5, a2, t5, 2 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr27, vr28, vr29, vr30, vr16, vr17, \ + vr18, vr19, t5 + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + + // t6 = src + 6 * stride + 1 + vld vr0, t6, 0 + vldx vr1, t6, a2 + vldx vr2, t6, t1 + vldx vr3, t6, t2 + alsl.d t6, a2, t6, 2 + vld vr4, t6, 0 + vldx vr5, t6, a2 + vldx vr6, t6, t1 + VST_QPEL8_V_LOWPASS_LSX vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr23, vr24 + vldx vr0, t6, t2 + alsl.d t6, a2, t6, 2 + vld vr1, t6, 0 + VSTX_QPEL8_V_LOWPASS_LSX vr2, vr3, vr4, vr5 ,vr6, vr0, vr1, vr25, vr26 + alsl.d a0, a2, a0, 2 // dst = dst + 4 *stride + vldx vr2, t6, a2 + vldx vr3, t6, t1 + VST_QPEL8_V_LOWPASS_LSX vr4, vr5, vr6, vr0, vr1, vr2, vr3, vr27, vr28 + vldx vr4, t6, t2 + alsl.d t6, a2, t6, 2 + vld vr5, t6, 0 + VSTX_QPEL8_V_LOWPASS_LSX vr6, vr0, vr1, vr2, vr3, vr4, vr5, vr29, vr30 + + fld.d f24, sp, 0 + fld.d f25, sp, 8 + fld.d f26, sp, 16 + fld.d f27, sp, 24 + fld.d f28, sp, 32 + fld.d f29, sp, 40 + fld.d f30, sp, 48 + fld.d f31, sp, 56 + addi.d sp, sp, 64 +endfunc +.endm + +put_h264_qpel16_mc 33 +put_h264_qpel16_mc 31 + +function ff_put_h264_qpel16_mc13_lsx + slli.d t1, a2, 1 + add.d t2, t1, a2 + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 + addi.d sp, sp, -64 + fst.d f24, sp, 0 + fst.d f25, sp, 8 + fst.d f26, sp, 16 + fst.d f27, sp, 24 + fst.d f28, sp, 32 + fst.d f29, sp, 40 + fst.d f30, sp, 48 + fst.d f31, sp, 56 + addi.d t0, a1, -2 // t0 = src - 2 + add.d t0, t0, a2 + add.d t3, a1, zero // t3 = src + sub.d t4, a1, t1 // t4 = src - 2 * stride + + VLD_DOUBLE_QPEL8_H_LSX vr12, vr13, vr14, vr15, t0 + alsl.d a1, a2, t0, 2 + VLD_DOUBLE_QPEL8_H_LSX vr16, vr17, vr18, vr19, a1 + addi.d a1, t0, 8 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr23, vr24, vr25, vr26, vr12, vr13, \ + vr14, vr15, a1 + alsl.d a1, a2, a1, 2 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr27, vr28, vr29, vr30, vr16, vr17, \ + vr18, vr19, a1 + vld vr0, t4, 0 // t4 = src - 2 * stride + 1 + vldx vr1, t4, a2 + vldx vr2, t4, t1 + vldx vr3, t4, t2 + alsl.d t4, a2, t4, 2 + vld vr4, t4, 0 + vldx vr5, t4, a2 + vldx vr6, t4, t1 + VST_QPEL8_V_LOWPASS_LSX vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr23, vr24 + vldx vr0, t4, t2 + alsl.d t4, a2, t4, 2 + vld vr1, t4, 0 + VSTX_QPEL8_V_LOWPASS_LSX vr2, vr3, vr4, vr5, vr6, vr0, vr1, vr25, vr26 + add.d t6, t4, zero + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + vldx vr2, t4, a2 + vldx vr3, t4, t1 + VST_QPEL8_V_LOWPASS_LSX vr4, vr5, vr6, vr0, vr1, vr2, vr3, vr27, vr28 + vldx vr4, t4, t2 + alsl.d t4, a2, t4, 2 + vld vr5, t4, 0 + VSTX_QPEL8_V_LOWPASS_LSX vr6, vr0, vr1, vr2, vr3, vr4, vr5, vr29, vr30 + alsl.d a1, a2, t0, 3 // a1 = src + 8 * stride + addi.d t5, a1, 8 // a1 = src + 8 * stride + 8 + VLD_DOUBLE_QPEL8_H_LSX vr12, vr13, vr14, vr15, a1 + alsl.d a1, a2, a1, 2 + VLD_DOUBLE_QPEL8_H_LSX vr16, vr17, vr18, vr19, a1 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr23, vr24, vr25, vr26, vr12, vr13, \ + vr14, vr15, t5 + alsl.d t5, a2, t5, 2 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr27, vr28, vr29, vr30, vr16, vr17, \ + vr18, vr19, t5 + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + + vld vr0, t6, 0 // // t6 = src + 6 * stride + 1 + vldx vr1, t6, a2 + vldx vr2, t6, t1 + vldx vr3, t6, t2 + alsl.d t6, a2, t6, 2 + vld vr4, t6, 0 + vldx vr5, t6, a2 + vldx vr6, t6, t1 + VST_QPEL8_V_LOWPASS_LSX vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr23, vr24 + vldx vr0, t6, t2 + alsl.d t6, a2, t6, 2 + vld vr1, t6, 0 + VSTX_QPEL8_V_LOWPASS_LSX vr2, vr3, vr4, vr5, vr6, vr0, vr1, vr25, vr26 + alsl.d a0, a2, a0, 2 // dst = dst + 4 *stride + vldx vr2, t6, a2 + vldx vr3, t6, t1 + VST_QPEL8_V_LOWPASS_LSX vr4, vr5, vr6, vr0, vr1, vr2, vr3, vr27, vr28 + vldx vr4, t6, t2 + alsl.d t6, a2, t6, 2 + vld vr5, t6, 0 + VSTX_QPEL8_V_LOWPASS_LSX vr6, vr0, vr1, vr2, vr3, vr4, vr5, vr29, vr30 + fld.d f24, sp, 0 + fld.d f25, sp, 8 + fld.d f26, sp, 16 + fld.d f27, sp, 24 + fld.d f28, sp, 32 + fld.d f29, sp, 40 + fld.d f30, sp, 48 + fld.d f31, sp, 56 + addi.d sp, sp, 64 +endfunc + +function ff_avg_h264_qpel16_mc10_lsx + addi.d t0, a0, 0 // t0 = dst + addi.d t4, a1, -2 // t1 = src - 2 + addi.d t5, t4, 8 + slli.d t1, a2, 1 + add.d t2, a2, t1 + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 +.rept 2 + VLD_DOUBLE_QPEL8_H_LSX vr12, vr13, vr14, vr15, t4 + alsl.d t4, a2, t4, 2 + VLD_DOUBLE_QPEL8_H_LSX vr16, vr17, vr18, vr19, t4 + VLD_QPEL8_H_SSRANI_LSX vr2, vr3, vr12, vr13, t5 + vld vr0, a1, 0 + vldx vr1, a1, a2 + vld vr12, t0, 0 + vldx vr13, t0, a2 + vavgr.bu vr0, vr0, vr2 + vavgr.bu vr1, vr1, vr3 + vavgr.bu vr0, vr0, vr12 + vavgr.bu vr1, vr1, vr13 + vst vr0, a0, 0 + vstx vr1, a0, a2 + VLDX_QPEL8_H_SSRANI_LSX vr2, vr3, vr14, vr15, t5 + vldx vr0, a1, t1 + vldx vr1, a1, t2 + vldx vr12, t0, t1 + vldx vr13, t0, t2 + vavgr.bu vr0, vr0, vr2 + vavgr.bu vr1, vr1, vr3 + vavgr.bu vr0, vr0, vr12 + vavgr.bu vr1, vr1, vr13 + vstx vr0, a0, t1 + vstx vr1, a0, t2 + alsl.d t5, a2, t5, 2 + alsl.d a1, a2, a1, 2 + alsl.d t0, a2, t0, 2 + alsl.d a0, a2, a0, 2 + VLD_QPEL8_H_SSRANI_LSX vr2, vr3, vr16, vr17, t5 + vld vr0, a1, 0 + vldx vr1, a1, a2 + vld vr12, t0, 0 + vldx vr13, t0, a2 + vavgr.bu vr0, vr0, vr2 + vavgr.bu vr1, vr1, vr3 + vavgr.bu vr0, vr0, vr12 + vavgr.bu vr1, vr1, vr13 + vst vr0, a0, 0 + vstx vr1, a0, a2 + VLDX_QPEL8_H_SSRANI_LSX vr2, vr3, vr18, vr19, t5 + vldx vr0, a1, t1 + vldx vr1, a1, t2 + vldx vr12, t0, t1 + vldx vr13, t0, t2 + vavgr.bu vr0, vr0, vr2 + vavgr.bu vr1, vr1, vr3 + vavgr.bu vr0, vr0, vr12 + vavgr.bu vr1, vr1, vr13 + vstx vr0, a0, t1 + vstx vr1, a0, t2 + alsl.d t5, a2, t5, 2 + alsl.d a1, a2, a1, 2 + alsl.d t0, a2, t0, 2 + alsl.d a0, a2, a0, 2 + alsl.d t4, a2, t4, 2 // src + 8 * stride -2 +.endr +endfunc + +function ff_avg_h264_qpel16_mc30_lsx + addi.d t0, a0, 0 // t0 = dst + addi.d t4, a1, -2 // t1 = src - 2 + addi.d t5, t4, 8 + addi.d a1, a1, 1 // a1 = a1 + 1 + slli.d t1, a2, 1 + add.d t2, a2, t1 + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 +.rept 2 + VLD_DOUBLE_QPEL8_H_LSX vr12, vr13, vr14, vr15, t4 + alsl.d t4, a2, t4, 2 + VLD_DOUBLE_QPEL8_H_LSX vr16, vr17, vr18, vr19, t4 + VLD_QPEL8_H_SSRANI_LSX vr2, vr3, vr12, vr13, t5 + vld vr0, a1, 0 + vldx vr1, a1, a2 + vld vr12, t0, 0 + vldx vr13, t0, a2 + vavgr.bu vr0, vr0, vr2 + vavgr.bu vr1, vr1, vr3 + vavgr.bu vr0, vr0, vr12 + vavgr.bu vr1, vr1, vr13 + vst vr0, a0, 0 + vstx vr1, a0, a2 + VLDX_QPEL8_H_SSRANI_LSX vr2, vr3, vr14, vr15, t5 + vldx vr0, a1, t1 + vldx vr1, a1, t2 + vldx vr12, t0, t1 + vldx vr13, t0, t2 + vavgr.bu vr0, vr0, vr2 + vavgr.bu vr1, vr1, vr3 + vavgr.bu vr0, vr0, vr12 + vavgr.bu vr1, vr1, vr13 + vstx vr0, a0, t1 + vstx vr1, a0, t2 + alsl.d t5, a2, t5, 2 + alsl.d a1, a2, a1, 2 + alsl.d t0, a2, t0, 2 + alsl.d a0, a2, a0, 2 + VLD_QPEL8_H_SSRANI_LSX vr2, vr3, vr16, vr17, t5 + vld vr0, a1, 0 + vldx vr1, a1, a2 + vld vr12, t0, 0 + vldx vr13, t0, a2 + vavgr.bu vr0, vr0, vr2 + vavgr.bu vr1, vr1, vr3 + vavgr.bu vr0, vr0, vr12 + vavgr.bu vr1, vr1, vr13 + vst vr0, a0, 0 + vstx vr1, a0, a2 + VLDX_QPEL8_H_SSRANI_LSX vr2, vr3, vr18, vr19, t5 + vldx vr0, a1, t1 + vldx vr1, a1, t2 + vldx vr12, t0, t1 + vldx vr13, t0, t2 + vavgr.bu vr0, vr0, vr2 + vavgr.bu vr1, vr1, vr3 + vavgr.bu vr0, vr0, vr12 + vavgr.bu vr1, vr1, vr13 + vstx vr0, a0, t1 + vstx vr1, a0, t2 + alsl.d t5, a2, t5, 2 + alsl.d a1, a2, a1, 2 + alsl.d t0, a2, t0, 2 + alsl.d a0, a2, a0, 2 + alsl.d t4, a2, t4, 2 // t1 = src + 8 * stride -2 +.endr +endfunc + +function ff_put_h264_qpel16_mc02_lsx + slli.d t0, a2, 1 + add.d t1, t0, a2 + sub.d t2, a1, t0 // t2 = src - 2 * stride + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 + + vld vr0, t2, 0 + vldx vr1, t2, a2 + vldx vr2, t2, t0 + vldx vr3, t2, t1 + alsl.d t2, a2, t2, 2 // t2 = t2 + 4 * stride + vld vr4, t2, 0 + vldx vr5, t2, a2 + vldx vr6, t2, t0 + QPEL8_V_LSX vr0, vr1, vr2, vr3, vr4, vr5, vr6 + vst vr13, a0, 0 + vstx vr14, a0, a2 + vldx vr0, t2, t1 + alsl.d t2, a2, t2, 2 // t2 = t2 + 4 *stride + vld vr1, t2, 0 + QPEL8_V_LSX vr2, vr3, vr4, vr5, vr6, vr0, vr1 + vstx vr13, a0, t0 + vstx vr14, a0, t1 + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + vldx vr2, t2, a2 + vldx vr3, t2, t0 + QPEL8_V_LSX vr4, vr5, vr6, vr0, vr1, vr2, vr3 + vst vr13, a0, 0 + vstx vr14, a0, a2 + vldx vr4, t2, t1 + alsl.d t2, a2, t2, 2 // t2 = t2 + 4 * stride + vld vr5, t2, 0 + QPEL8_V_LSX vr6, vr0, vr1, vr2, vr3, vr4, vr5 + vstx vr13, a0, t0 + vstx vr14, a0, t1 + + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + + vldx vr6, t2, a2 + vldx vr0, t2, t0 + QPEL8_V_LSX vr1, vr2, vr3, vr4, vr5, vr6, vr0 + vst vr13, a0, 0 + vstx vr14, a0, a2 + vldx vr1, t2, t1 + alsl.d t2, a2, t2, 2 // t2 = t2 + 4 * stride + vld vr2, t2, 0 + QPEL8_V_LSX vr3, vr4, vr5, vr6, vr0, vr1, vr2 + vstx vr13, a0, t0 + vstx vr14, a0, t1 + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + vldx vr3, t2, a2 + vldx vr4, t2, t0 + QPEL8_V_LSX vr5, vr6, vr0, vr1, vr2, vr3, vr4 + vst vr13, a0, 0 + vstx vr14, a0, a2 + vldx vr5, t2, t1 + alsl.d t2, a2, t2, 2 // t2 = t2 + 4 * stride + vld vr6, t2, 0 + QPEL8_V_LSX vr0, vr1, vr2, vr3, vr4, vr5, vr6 + vstx vr13, a0, t0 + vstx vr14, a0, t1 +endfunc + +.macro avc_luma_hv_qrt_and_aver_dst_16x16_lsx + addi.d sp, sp, -64 + fst.d f24, sp, 0 + fst.d f25, sp, 8 + fst.d f26, sp, 16 + fst.d f27, sp, 24 + fst.d f28, sp, 32 + fst.d f29, sp, 40 + fst.d f30, sp, 48 + fst.d f31, sp, 56 + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 + + VLD_DOUBLE_QPEL8_H_LSX vr12, vr13, vr14, vr15, t0 + alsl.d a1, a2, t0, 2 + VLD_DOUBLE_QPEL8_H_LSX vr16, vr17, vr18, vr19, a1 + addi.d a1, t0, 8 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr23, vr24, vr25, vr26, vr12, vr13, \ + vr14, vr15, a1 + alsl.d a1, a2, a1, 2 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr27, vr28, vr29, vr30, vr16, vr17, \ + vr18, vr19, a1 + vld vr0, t4, 0 // t4 = src - 2 * stride + 1 + vldx vr1, t4, a2 + vldx vr2, t4, t1 + vldx vr3, t4, t2 + alsl.d t4, a2, t4, 2 + vld vr4, t4, 0 + vldx vr5, t4, a2 + vldx vr6, t4, t1 + QPEL8_V_LSX vr0, vr1, vr2, vr3, vr4, vr5, vr6 + vld vr0, t8, 0 + vldx vr1, t8, a2 + vavgr.bu vr13, vr23, vr13 + vavgr.bu vr14, vr24, vr14 + vavgr.bu vr13, vr13, vr0 + vavgr.bu vr14, vr14, vr1 + vst vr13, a0, 0 + vstx vr14, a0, a2 + vldx vr0, t4, t2 + alsl.d t4, a2, t4, 2 + vld vr1, t4, 0 + QPEL8_V_LSX vr2, vr3, vr4, vr5, vr6, vr0, vr1 + vldx vr2, t8, t1 + vldx vr3, t8, t2 + vavgr.bu vr13, vr25, vr13 + vavgr.bu vr14, vr26, vr14 + vavgr.bu vr13, vr13, vr2 + vavgr.bu vr14, vr14, vr3 + add.d t6, t4, zero // t6 = src + 6 * stride + vstx vr13, a0, t1 + vstx vr14, a0, t2 + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + alsl.d t8, a2, t8, 2 + vldx vr2, t4, a2 + vldx vr3, t4, t1 + QPEL8_V_LSX vr4, vr5, vr6, vr0, vr1, vr2, vr3 + vld vr4, t8, 0 + vldx vr5, t8, a2 + vavgr.bu vr13, vr27, vr13 + vavgr.bu vr14, vr28, vr14 + vavgr.bu vr13, vr13, vr4 + vavgr.bu vr14, vr14, vr5 + vst vr13, a0, 0 + vstx vr14, a0, a2 + vldx vr4, t4, t2 + alsl.d t4, a2, t4, 2 + vld vr5, t4, 0 + QPEL8_V_LSX vr6, vr0, vr1, vr2, vr3, vr4, vr5 + vldx vr6, t8, t1 + vldx vr0, t8, t2 + vavgr.bu vr13, vr29, vr13 + vavgr.bu vr14, vr30, vr14 + vavgr.bu vr13, vr13, vr6 + vavgr.bu vr14, vr14, vr0 + vstx vr13, a0, t1 + vstx vr14, a0, t2 + alsl.d a1, a2, t0, 3 // a1 = src + 8 * stride + addi.d t5, a1, 8 // a1 = src + 8 * stride + 8 + VLD_DOUBLE_QPEL8_H_LSX vr12, vr13, vr14, vr15, a1 + alsl.d a1, a2, a1, 2 + VLD_DOUBLE_QPEL8_H_LSX vr16, vr17, vr18, vr19, a1 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr23, vr24, vr25, vr26, vr12, vr13, \ + vr14, vr15, t5 + alsl.d t5, a2, t5, 2 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr27, vr28, vr29, vr30, vr16, vr17, \ + vr18, vr19, t5 + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + alsl.d t8, a2, t8, 2 + // t6 = src + 6 * stride + 1 + vld vr0, t6, 0 + vldx vr1, t6, a2 + vldx vr2, t6, t1 + vldx vr3, t6, t2 + alsl.d t6, a2, t6, 2 + vld vr4, t6, 0 + vldx vr5, t6, a2 + vldx vr6, t6, t1 + QPEL8_V_LSX vr0, vr1, vr2, vr3, vr4, vr5, vr6 + vld vr0, t8, 0 + vldx vr1, t8, a2 + vavgr.bu vr13, vr23, vr13 + vavgr.bu vr14, vr24, vr14 + vavgr.bu vr13, vr13, vr0 + vavgr.bu vr14, vr14, vr1 + vst vr13, a0, 0 + vstx vr14, a0, a2 + vldx vr0, t6, t2 + alsl.d t6, a2, t6, 2 + vld vr1, t6, 0 + QPEL8_V_LSX vr2, vr3, vr4, vr5, vr6, vr0, vr1 + vldx vr2, t8, t1 + vldx vr3, t8, t2 + vavgr.bu vr13, vr25, vr13 + vavgr.bu vr14, vr26, vr14 + vavgr.bu vr13, vr13, vr2 + vavgr.bu vr14, vr14, vr3 + vstx vr13, a0, t1 + vstx vr14, a0, t2 + alsl.d a0, a2, a0, 2 // dst = dst + 4 *stride + alsl.d t8, a2, t8, 2 + vldx vr2, t6, a2 + vldx vr3, t6, t1 + QPEL8_V_LSX vr4, vr5, vr6, vr0, vr1, vr2, vr3 + vld vr4, t8, 0 + vldx vr5, t8, a2 + vavgr.bu vr13, vr27, vr13 + vavgr.bu vr14, vr28, vr14 + vavgr.bu vr13, vr13, vr4 + vavgr.bu vr14, vr14, vr5 + vst vr13, a0, 0 + vstx vr14, a0, a2 + vldx vr4, t6, t2 + alsl.d t6, a2, t6, 2 + vld vr5, t6, 0 + QPEL8_V_LSX vr6, vr0, vr1, vr2, vr3, vr4, vr5 + vldx vr6, t8, t1 + vldx vr0, t8, t2 + vavgr.bu vr13, vr29, vr13 + vavgr.bu vr14, vr30, vr14 + vavgr.bu vr13, vr13, vr6 + vavgr.bu vr14, vr14, vr0 + vstx vr13, a0, t1 + vstx vr14, a0, t2 + fld.d f24, sp, 0 + fld.d f25, sp, 8 + fld.d f26, sp, 16 + fld.d f27, sp, 24 + fld.d f28, sp, 32 + fld.d f29, sp, 40 + fld.d f30, sp, 48 + fld.d f31, sp, 56 + addi.d sp, sp, 64 +.endm + +function ff_avg_h264_qpel16_mc33_lsx + slli.d t1, a2, 1 + add.d t2, t1, a2 + addi.d t0, a1, -2 // t0 = src - 2 + add.d t0, t0, a2 // t0 = src + stride - 2 + add.d t3, a1, zero // t3 = src + sub.d t4, a1, t1 // t4 = src - 2 * stride + addi.d t4, t4, 1 + addi.d t8, a0, 0 + avc_luma_hv_qrt_and_aver_dst_16x16_lsx +endfunc + +function ff_avg_h264_qpel16_mc11_lsx + slli.d t1, a2, 1 + add.d t2, t1, a2 + addi.d t0, a1, -2 // t0 = src - 2 + add.d t3, a1, zero // t3 = src + sub.d t4, a1, t1 // t4 = src - 2 * stride + addi.d t8, a0, 0 + avc_luma_hv_qrt_and_aver_dst_16x16_lsx +endfunc + +function ff_avg_h264_qpel16_mc31_lsx + slli.d t1, a2, 1 + add.d t2, t1, a2 + addi.d t0, a1, -2 // t0 = src - 2 + add.d t3, a1, zero // t3 = src + sub.d t4, a1, t1 // t4 = src - 2 * stride + addi.d t4, t4, 1 + addi.d t8, a0, 0 + avc_luma_hv_qrt_and_aver_dst_16x16_lsx +endfunc + +function ff_avg_h264_qpel16_mc13_lsx + slli.d t1, a2, 1 + add.d t2, t1, a2 + addi.d t0, a1, -2 // t0 = src - 2 + add.d t0, t0, a2 + add.d t3, a1, zero // t3 = src + sub.d t4, a1, t1 // t4 = src - 2 * stride + addi.d t8, a0, 0 + avc_luma_hv_qrt_and_aver_dst_16x16_lsx +endfunc + +function ff_avg_h264_qpel16_mc20_lsx + slli.d t1, a2, 1 + add.d t2, t1, a2 + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 + addi.d t0, a1, -2 // t0 = src - 2 + addi.d t5, a0, 0 + addi.d a1, t0, 8 +.rept 4 + VLD_DOUBLE_QPEL8_H_LSX vr12, vr13, vr14, vr15, t0 + VLD_QPEL8_H_SSRANI_LSX vr2, vr3, vr12, vr13, a1 + vld vr0, t5, 0 + vldx vr1, t5, a2 + vavgr.bu vr0, vr0, vr2 + vavgr.bu vr1, vr1, vr3 + vst vr0, a0, 0 + vstx vr1, a0, a2 + add.d a1, a1, t1 + VLD_QPEL8_H_SSRANI_LSX vr2, vr3, vr14, vr15, a1 + vldx vr0, t5, t1 + vldx vr1, t5, t2 + vavgr.bu vr0, vr0, vr2 + vavgr.bu vr1, vr1, vr3 + vstx vr0, a0, t1 + vstx vr1, a0, t2 + alsl.d t0, a2, t0, 2 + alsl.d t5, a2, t5, 2 + alsl.d a0, a2, a0, 2 + alsl.d a1, a2, a1, 1 +.endr +endfunc + +.macro QPEL8_HV_H_LSX out0, out1 + vbsrl.v vr2, vr0, 1 + vbsrl.v vr3, vr1, 1 + vbsrl.v vr4, vr0, 2 + vbsrl.v vr5, vr1, 2 + vbsrl.v vr6, vr0, 3 + vbsrl.v vr7, vr1, 3 + vbsrl.v vr8, vr0, 4 + vbsrl.v vr9, vr1, 4 + vbsrl.v vr10, vr0, 5 + vbsrl.v vr11, vr1, 5 + vilvl.b vr6, vr4, vr6 + vilvl.b vr7, vr5, vr7 + vilvl.b vr8, vr2, vr8 + vilvl.b vr9, vr3, vr9 + vilvl.b vr10, vr0, vr10 + vilvl.b vr11, vr1, vr11 + vhaddw.hu.bu vr6, vr6, vr6 + vhaddw.hu.bu vr7, vr7, vr7 + vhaddw.hu.bu vr8, vr8, vr8 + vhaddw.hu.bu vr9, vr9, vr9 + vhaddw.hu.bu vr10, vr10, vr10 + vhaddw.hu.bu vr11, vr11, vr11 + vmul.h vr2, vr6, vr20 + vmul.h vr3, vr7, vr20 + vmul.h vr4, vr8, vr21 + vmul.h vr5, vr9, vr21 + vssub.h vr2, vr2, vr4 + vssub.h vr3, vr3, vr5 + vsadd.h \out0, vr2, vr10 + vsadd.h \out1, vr3, vr11 +.endm + +.macro QPEL8_HV_V_LSX in0, in1, in2, in3, in4, in5, in6, out0, out1, out2, out3 + vilvl.h vr0, \in2, \in3 + vilvl.h vr1, \in3, \in4 // tmp0 + vilvl.h vr2, \in1, \in4 + vilvl.h vr3, \in2, \in5 // tmp2 + vilvl.h vr4, \in0, \in5 + vilvl.h vr5, \in1, \in6 // tmp4 + vhaddw.w.h vr0, vr0, vr0 + vhaddw.w.h vr1, vr1, vr1 + vhaddw.w.h vr2, vr2, vr2 + vhaddw.w.h vr3, vr3, vr3 + vhaddw.w.h vr4, vr4, vr4 + vhaddw.w.h vr5, vr5, vr5 + vmul.w vr0, vr0, vr22 + vmul.w vr1, vr1, vr22 + vmul.w vr2, vr2, vr23 + vmul.w vr3, vr3, vr23 + vssub.w vr0, vr0, vr2 + vssub.w vr1, vr1, vr3 + vsadd.w vr0, vr0, vr4 + vsadd.w vr1, vr1, vr5 + vsadd.w \out0, vr0, vr24 + vsadd.w \out1, vr1, vr24 + vilvh.h vr0, \in2, \in3 + vilvh.h vr1, \in3, \in4 // tmp0 + vilvh.h vr2, \in1, \in4 + vilvh.h vr3, \in2, \in5 // tmp2 + vilvh.h vr4, \in0, \in5 + vilvh.h vr5, \in1, \in6 // tmp4 + vhaddw.w.h vr0, vr0, vr0 + vhaddw.w.h vr1, vr1, vr1 + vhaddw.w.h vr2, vr2, vr2 + vhaddw.w.h vr3, vr3, vr3 + vhaddw.w.h vr4, vr4, vr4 + vhaddw.w.h vr5, vr5, vr5 + vmul.w vr0, vr0, vr22 + vmul.w vr1, vr1, vr22 + vmul.w vr2, vr2, vr23 + vmul.w vr3, vr3, vr23 + vssub.w vr0, vr0, vr2 + vssub.w vr1, vr1, vr3 + vsadd.w vr0, vr0, vr4 + vsadd.w vr1, vr1, vr5 + vsadd.w \out2, vr0, vr24 + vsadd.w \out3, vr1, vr24 + vssrani.hu.w \out2, \out0, 10 + vssrani.hu.w \out3, \out1, 10 + vssrani.bu.h \out3, \out2, 0 +.endm + +.macro h264_qpel8_hv_lowpass_core_lsx in0, in1, type + vld vr0, \in0, 0 + vldx vr1, \in0, a3 + QPEL8_HV_H_LSX vr12, vr13 // a b$ + vldx vr0, \in0, t1 + vldx vr1, \in0, t2 + QPEL8_HV_H_LSX vr14, vr15 // c d$ + + alsl.d \in0, a3, \in0, 2 + + vld vr0, \in0, 0 + vldx vr1, \in0, a3 + QPEL8_HV_H_LSX vr16, vr17 // e f$ + vldx vr0, \in0, t1 + vldx vr1, \in0, t2 + QPEL8_HV_H_LSX vr18, vr19 // g h$ + QPEL8_HV_V_LSX vr12, vr13, vr14, vr15, vr16, vr17, vr18, vr6, vr7, vr0, vr1 +.ifc \type, avg + fld.d f2, t3, 0 + fldx.d f3, t3, a2 + vilvl.d vr2, vr3, vr2 + vavgr.bu vr1, vr2, vr1 +.endif + vstelm.d vr1, \in1, 0, 0 + add.d \in1, \in1, a2 + vstelm.d vr1, \in1, 0, 1 + + alsl.d \in0, a3, \in0, 2 + + // tmp8 + vld vr0, \in0, 0 + vldx vr1, \in0, a3 + QPEL8_HV_H_LSX vr12, vr13 + QPEL8_HV_V_LSX vr14, vr15, vr16, vr17, vr18, vr19, vr12, vr6, vr7, vr0, vr1 +.ifc \type, avg + fldx.d f2, t3, t5 + fldx.d f3, t3, t6 + vilvl.d vr2, vr3, vr2 + vavgr.bu vr1, vr2, vr1 +.endif + add.d \in1, \in1, a2 + vstelm.d vr1, \in1, 0, 0 + add.d \in1, \in1, a2 + vstelm.d vr1, \in1, 0, 1 + + // tmp10 + vldx vr0, \in0, t1 + vldx vr1, \in0, t2 + QPEL8_HV_H_LSX vr14, vr15 + QPEL8_HV_V_LSX vr16, vr17, vr18, vr19, vr12, vr13, vr14, vr6, vr7, vr0, vr1 +.ifc \type, avg + alsl.d t3, a2, t3, 2 + fld.d f2, t3, 0 + fldx.d f3, t3, a2 + vilvl.d vr2, vr3, vr2 + vavgr.bu vr1, vr2, vr1 +.endif + add.d \in1, \in1, a2 + vstelm.d vr1, \in1, 0, 0 + add.d \in1, \in1, a2 + vstelm.d vr1, \in1, 0, 1 + + // tmp12 + alsl.d \in0, a3, \in0, 2 + + vld vr0, \in0, 0 + vldx vr1, \in0, a3 + QPEL8_HV_H_LSX vr16, vr17 + QPEL8_HV_V_LSX vr18, vr19, vr12, vr13, vr14, vr15, vr16, vr6, vr7, vr0, vr1 +.ifc \type, avg + fldx.d f2, t3, t5 + fldx.d f3, t3, t6 + vilvl.d vr2, vr3, vr2 + vavgr.bu vr1, vr2, vr1 +.endif + add.d \in1, \in1, a2 + vstelm.d vr1, \in1, 0, 0 + add.d \in1, \in1, a2 + vstelm.d vr1, \in1, 0, 1 +.endm + +function put_h264_qpel8_hv_lowpass_lsx + slli.d t1, a3, 1 + add.d t2, t1, a3 + addi.d sp, sp, -8 + fst.d f24, sp, 0 + addi.d t0, a1, -2 // t0 = src - 2 + sub.d t0, t0, t1 // t0 = t0 - 2 * stride + vldi vr20, 0x414 // h_20 + vldi vr21, 0x405 // h_5 + vldi vr22, 0x814 // w_20 + vldi vr23, 0x805 // w_5 + addi.d t4, zero, 512 + vreplgr2vr.w vr24, t4 // w_512 + h264_qpel8_hv_lowpass_core_lsx t0, a0, put + fld.d f24, sp, 0 + addi.d sp, sp, 8 +endfunc + +function put_h264_qpel8_h_lowpass_lsx + slli.d t1, a3, 1 + add.d t2, t1, a3 + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 + addi.d t0, a1, -2 // t0 = src - 2 + add.d t3, a1, zero // t3 = src +.rept 2 + vld vr0, t0, 0 + vldx vr1, t0, a3 + QPEL8_H_LSX vr12, vr13 + vssrani.bu.h vr13, vr12, 5 + vstelm.d vr13, a0, 0, 0 + add.d a0, a0, a2 + vstelm.d vr13, a0, 0, 1 + add.d a0, a0, a2 + vldx vr0, t0, t1 + vldx vr1, t0, t2 + QPEL8_H_LSX vr12, vr13 + vssrani.bu.h vr13, vr12, 5 + vstelm.d vr13, a0, 0, 0 + add.d a0, a0, a2 + vstelm.d vr13, a0, 0, 1 + add.d a0, a0, a2 + alsl.d t0, a3, t0, 2 +.endr +endfunc + +function put_pixels16_l2_8_lsx + slli.d t0, a4, 1 + add.d t1, t0, a4 + slli.d t2, t0, 1 + slli.d t3, a3, 1 + add.d t4, t3, a3 + slli.d t5, t3, 1 +.rept 4 + vld vr0, a1, 0 + vldx vr1, a1, a4 + vldx vr2, a1, t0 + vldx vr3, a1, t1 + add.d a1, a1, t2 + vld vr8, a2, 0x00 + vld vr9, a2, 0x10 + vld vr10, a2, 0x20 + vld vr11, a2, 0x30 + addi.d a2, a2, 0x40 + vavgr.bu vr0, vr8, vr0 + vavgr.bu vr1, vr9, vr1 + vavgr.bu vr2, vr10, vr2 + vavgr.bu vr3, vr11, vr3 + vst vr0, a0, 0 + vstx vr1, a0, a3 + vstx vr2, a0, t3 + vstx vr3, a0, t4 + add.d a0, a0, t5 +.endr +endfunc + +.macro QPEL8_V1_LSX in0, in1, in2, in3, in4, in5, in6 + vilvl.b vr7, \in3, \in2 + vilvl.b vr8, \in4, \in3 + vilvl.b vr9, \in4, \in1 + vilvl.b vr10, \in5, \in2 + vilvl.b vr11, \in5, \in0 + vilvl.b vr12, \in6, \in1 + vhaddw.hu.bu vr7, vr7, vr7 + vhaddw.hu.bu vr8, vr8, vr8 + vhaddw.hu.bu vr9, vr9, vr9 + vhaddw.hu.bu vr10, vr10, vr10 + vhaddw.hu.bu vr11, vr11, vr11 + vhaddw.hu.bu vr12, vr12, vr12 + vmul.h vr7, vr7, vr20 + vmul.h vr8, vr8, vr20 + vmul.h vr9, vr9, vr21 + vmul.h vr10, vr10, vr21 + vssub.h vr7, vr7, vr9 + vssub.h vr8, vr8, vr10 + vsadd.h vr7, vr7, vr11 + vsadd.h vr8, vr8, vr12 + vsadd.h vr7, vr7, vr22 + vsadd.h vr8, vr8, vr22 + vssrani.bu.h vr8, vr7, 5 +.endm + +.macro h264_qpel8_v_lowpass_lsx type +function \type\()_h264_qpel8_v_lowpass_lsx + slli.d t0, a3, 1 + add.d t1, t0, a3 + sub.d t2, a1, t0 // t2 = src - 2 * stride +.ifc \type, avg + addi.d t3, a0, 0 + slli.d t4, a2, 1 + add.d t5, t4, a2 +.endif + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 + + fld.d f0, t2, 0 + fldx.d f1, t2, a3 + fldx.d f2, t2, t0 + fldx.d f3, t2, t1 + alsl.d t2, a3, t2, 2 // t2 = t2 + 4 * stride + fld.d f4, t2, 0 + fldx.d f5, t2, a3 + fldx.d f6, t2, t0 + QPEL8_V1_LSX vr0, vr1, vr2, vr3, vr4, vr5, vr6 +.ifc \type, avg + fld.d f0, t3, 0 + fldx.d f1, t3, a2 + vilvl.d vr0, vr1, vr0 + vavgr.bu vr8, vr8, vr0 +.endif + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + vstelm.d vr8, a0, 0, 1 + add.d a0, a0, a2 + + fldx.d f0, t2, t1 + alsl.d t2, a3, t2, 2 // t2 = t2 + 4 *stride + fld.d f1, t2, 0 + QPEL8_V1_LSX vr2, vr3, vr4, vr5, vr6, vr0, vr1 +.ifc \type, avg + fldx.d f2, t3, t4 + fldx.d f3, t3, t5 + vilvl.d vr2, vr3, vr2 + vavgr.bu vr8, vr8, vr2 +.endif + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + vstelm.d vr8, a0, 0, 1 + add.d a0, a0, a2 + + alsl.d t3, a2, t3, 2 + + fldx.d f2, t2, a3 + fldx.d f3, t2, t0 + QPEL8_V1_LSX vr4, vr5, vr6, vr0, vr1, vr2, vr3 +.ifc \type, avg + fld.d f4, t3, 0 + fldx.d f5, t3, a2 + vilvl.d vr4, vr5, vr4 + vavgr.bu vr8, vr8, vr4 +.endif + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + vstelm.d vr8, a0, 0, 1 + add.d a0, a0, a2 + + fldx.d f4, t2, t1 + alsl.d t2, a3, t2, 2 // t2 = t2 + 4 * stride + fld.d f5, t2, 0 + QPEL8_V1_LSX vr6, vr0, vr1, vr2, vr3, vr4, vr5 +.ifc \type, avg + fldx.d f6, t3, t4 + fldx.d f0, t3, t5 + vilvl.d vr6, vr0, vr6 + vavgr.bu vr8, vr8, vr6 +.endif + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + vstelm.d vr8, a0, 0, 1 +endfunc +.endm + +h264_qpel8_v_lowpass_lsx put +h264_qpel8_v_lowpass_lsx avg + +function avg_pixels16_l2_8_lsx + slli.d t0, a4, 1 + add.d t1, t0, a4 + slli.d t2, t0, 1 + slli.d t3, a3, 1 + add.d t4, t3, a3 + slli.d t5, t3, 1 + addi.d t6, a0, 0 +.rept 4 + vld vr0, a1, 0 + vldx vr1, a1, a4 + vldx vr2, a1, t0 + vldx vr3, a1, t1 + add.d a1, a1, t2 + vld vr8, a2, 0x00 + vld vr9, a2, 0x10 + vld vr10, a2, 0x20 + vld vr11, a2, 0x30 + addi.d a2, a2, 0x40 + vavgr.bu vr0, vr8, vr0 + vavgr.bu vr1, vr9, vr1 + vavgr.bu vr2, vr10, vr2 + vavgr.bu vr3, vr11, vr3 + vld vr8, t6, 0 + vldx vr9, t6, a3 + vldx vr10, t6, t3 + vldx vr11, t6, t4 + add.d t6, t6, t5 + vavgr.bu vr0, vr8, vr0 + vavgr.bu vr1, vr9, vr1 + vavgr.bu vr2, vr10, vr2 + vavgr.bu vr3, vr11, vr3 + vst vr0, a0, 0 + vstx vr1, a0, a3 + vstx vr2, a0, t3 + vstx vr3, a0, t4 + add.d a0, a0, t5 +.endr +endfunc + +function avg_h264_qpel8_hv_lowpass_lsx + slli.d t1, a3, 1 + add.d t2, t1, a3 + slli.d t5, a2, 1 + add.d t6, a2, t5 + addi.d sp, sp, -8 + fst.d f24, sp, 0 + vldi vr20, 0x414 // h_20 + vldi vr21, 0x405 // h_5 + vldi vr22, 0x814 // w_20 + vldi vr23, 0x805 // w_5 + addi.d t4, zero, 512 + vreplgr2vr.w vr24, t4 // w_512 + addi.d t0, a1, -2 // t0 = src - 2 + sub.d t0, t0, t1 // t0 = t0 - 2 * stride + addi.d t3, a0, 0 // t3 = dst + h264_qpel8_hv_lowpass_core_lsx t0, a0, avg + fld.d f24, sp, 0 + addi.d sp, sp, 8 +endfunc + +function put_pixels8_l2_8_lsx + slli.d t0, a4, 1 + add.d t1, t0, a4 + slli.d t2, t0, 1 +.rept 2 + vld vr0, a1, 0 + vldx vr1, a1, a4 + vldx vr2, a1, t0 + vldx vr3, a1, t1 + add.d a1, a1, t2 + vilvl.d vr0, vr1, vr0 + vilvl.d vr2, vr3, vr2 + vld vr8, a2, 0x00 + vld vr9, a2, 0x08 + vld vr10, a2, 0x10 + vld vr11, a2, 0x18 + vilvl.d vr8, vr9, vr8 + vilvl.d vr10, vr11, vr10 + addi.d a2, a2, 32 + vavgr.bu vr0, vr8, vr0 + vavgr.bu vr2, vr10, vr2 + vstelm.d vr0, a0, 0, 0 + add.d a0, a0, a3 + vstelm.d vr0, a0, 0, 1 + add.d a0, a0, a3 + vstelm.d vr2, a0, 0, 0 + add.d a0, a0, a3 + vstelm.d vr2, a0, 0, 1 + add.d a0, a0, a3 +.endr +endfunc + +function ff_put_h264_qpel8_mc00_lsx + slli.d t0, a2, 1 + add.d t1, t0, a2 + slli.d t2, t0, 1 + ld.d t3, a1, 0x0 + ldx.d t4, a1, a2 + ldx.d t5, a1, t0 + ldx.d t6, a1, t1 + st.d t3, a0, 0x0 + stx.d t4, a0, a2 + stx.d t5, a0, t0 + stx.d t6, a0, t1 + add.d a1, a1, t2 + add.d a0, a0, t2 + ld.d t3, a1, 0x0 + ldx.d t4, a1, a2 + ldx.d t5, a1, t0 + ldx.d t6, a1, t1 + st.d t3, a0, 0x0 + stx.d t4, a0, a2 + stx.d t5, a0, t0 + stx.d t6, a0, t1 +endfunc + +function ff_avg_h264_qpel8_mc00_lsx + slli.d t0, a2, 1 + add.d t1, t0, a2 + slli.d t2, t0, 1 + addi.d t3, a0, 0 +.rept 2 + vld vr0, a1, 0 + vldx vr1, a1, a2 + vldx vr2, a1, t0 + vldx vr3, a1, t1 + add.d a1, a1, t2 + vilvl.d vr0, vr1, vr0 + vilvl.d vr2, vr3, vr2 + vld vr8, t3, 0 + vldx vr9, t3, a2 + vldx vr10, t3, t0 + vldx vr11, t3, t1 + add.d t3, t3, t2 + vilvl.d vr8, vr9, vr8 + vilvl.d vr10, vr11, vr10 + vavgr.bu vr0, vr8, vr0 + vavgr.bu vr2, vr10, vr2 + vstelm.d vr0, a0, 0, 0 + add.d a0, a0, a2 + vstelm.d vr0, a0, 0, 1 + add.d a0, a0, a2 + vstelm.d vr2, a0, 0, 0 + add.d a0, a0, a2 + vstelm.d vr2, a0, 0, 1 + add.d a0, a0, a2 +.endr +endfunc + +function avg_pixels8_l2_8_lsx + slli.d t0, a4, 1 + add.d t1, t0, a4 + slli.d t2, t0, 1 + addi.d t3, a0, 0 + slli.d t4, a3, 1 + add.d t5, t4, a3 + slli.d t6, t4, 1 +.rept 2 + vld vr0, a1, 0 + vldx vr1, a1, a4 + vldx vr2, a1, t0 + vldx vr3, a1, t1 + add.d a1, a1, t2 + vilvl.d vr0, vr1, vr0 + vilvl.d vr2, vr3, vr2 + vld vr8, a2, 0x00 + vld vr9, a2, 0x08 + vld vr10, a2, 0x10 + vld vr11, a2, 0x18 + addi.d a2, a2, 0x20 + vilvl.d vr8, vr9, vr8 + vilvl.d vr10, vr11, vr10 + vavgr.bu vr0, vr8, vr0 + vavgr.bu vr2, vr10, vr2 + vld vr8, t3, 0 + vldx vr9, t3, a3 + vldx vr10, t3, t4 + vldx vr11, t3, t5 + add.d t3, t3, t6 + vilvl.d vr8, vr9, vr8 + vilvl.d vr10, vr11, vr10 + vavgr.bu vr0, vr8, vr0 + vavgr.bu vr2, vr10, vr2 + vstelm.d vr0, a0, 0, 0 + add.d a0, a0, a3 + vstelm.d vr0, a0, 0, 1 + add.d a0, a0, a3 + vstelm.d vr2, a0, 0, 0 + add.d a0, a0, a3 + vstelm.d vr2, a0, 0, 1 + add.d a0, a0, a3 +.endr +endfunc + +function avg_h264_qpel8_h_lowpass_lsx + slli.d t1, a3, 1 + add.d t2, t1, a3 + slli.d t5, a2, 1 + add.d t6, t5, a2 + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 + addi.d t0, a1, -2 // t0 = src - 2 + add.d t3, a1, zero // t3 = src + addi.d t4, a0, 0 // t4 = dst +.rept 4 + vld vr0, t0, 0 + vldx vr1, t0, a3 + QPEL8_H_LSX vr12, vr13 + vssrani.bu.h vr13, vr12, 5 + fld.d f0, t4, 0 + fldx.d f1, t4, a2 + vilvl.d vr0, vr1, vr0 + vavgr.bu vr13, vr13, vr0 + vstelm.d vr13, a0, 0, 0 + add.d a0, a0, a2 + vstelm.d vr13, a0, 0, 1 + add.d a0, a0, a2 + add.d t0, t0, t1 + add.d t4, t4, t1 +.endr +endfunc diff --git a/libavcodec/loongarch/h264qpel_init_loongarch.c b/libavcodec/loongarch/h264qpel_init_loongarch.c index 969c9c376c6..9d3a5cb1643 100644 --- a/libavcodec/loongarch/h264qpel_init_loongarch.c +++ b/libavcodec/loongarch/h264qpel_init_loongarch.c @@ -19,7 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "h264qpel_lasx.h" +#include "h264qpel_loongarch.h" #include "libavutil/attributes.h" #include "libavutil/loongarch/cpu.h" #include "libavcodec/h264qpel.h" @@ -27,6 +27,77 @@ av_cold void ff_h264qpel_init_loongarch(H264QpelContext *c, int bit_depth) { int cpu_flags = av_get_cpu_flags(); + + if (have_lsx(cpu_flags)) { + if (8 == bit_depth) { + c->put_h264_qpel_pixels_tab[0][0] = ff_put_h264_qpel16_mc00_lsx; + c->put_h264_qpel_pixels_tab[0][1] = ff_put_h264_qpel16_mc10_lsx; + c->put_h264_qpel_pixels_tab[0][2] = ff_put_h264_qpel16_mc20_lsx; + c->put_h264_qpel_pixels_tab[0][3] = ff_put_h264_qpel16_mc30_lsx; + c->put_h264_qpel_pixels_tab[0][4] = ff_put_h264_qpel16_mc01_lsx; + c->put_h264_qpel_pixels_tab[0][5] = ff_put_h264_qpel16_mc11_lsx; + c->put_h264_qpel_pixels_tab[0][6] = ff_put_h264_qpel16_mc21_lsx; + c->put_h264_qpel_pixels_tab[0][7] = ff_put_h264_qpel16_mc31_lsx; + c->put_h264_qpel_pixels_tab[0][8] = ff_put_h264_qpel16_mc02_lsx; + c->put_h264_qpel_pixels_tab[0][9] = ff_put_h264_qpel16_mc12_lsx; + c->put_h264_qpel_pixels_tab[0][10] = ff_put_h264_qpel16_mc22_lsx; + c->put_h264_qpel_pixels_tab[0][11] = ff_put_h264_qpel16_mc32_lsx; + c->put_h264_qpel_pixels_tab[0][12] = ff_put_h264_qpel16_mc03_lsx; + c->put_h264_qpel_pixels_tab[0][13] = ff_put_h264_qpel16_mc13_lsx; + c->put_h264_qpel_pixels_tab[0][14] = ff_put_h264_qpel16_mc23_lsx; + c->put_h264_qpel_pixels_tab[0][15] = ff_put_h264_qpel16_mc33_lsx; + + c->avg_h264_qpel_pixels_tab[0][0] = ff_avg_h264_qpel16_mc00_lsx; + c->avg_h264_qpel_pixels_tab[0][1] = ff_avg_h264_qpel16_mc10_lsx; + c->avg_h264_qpel_pixels_tab[0][2] = ff_avg_h264_qpel16_mc20_lsx; + c->avg_h264_qpel_pixels_tab[0][3] = ff_avg_h264_qpel16_mc30_lsx; + c->avg_h264_qpel_pixels_tab[0][4] = ff_avg_h264_qpel16_mc01_lsx; + c->avg_h264_qpel_pixels_tab[0][5] = ff_avg_h264_qpel16_mc11_lsx; + c->avg_h264_qpel_pixels_tab[0][6] = ff_avg_h264_qpel16_mc21_lsx; + c->avg_h264_qpel_pixels_tab[0][7] = ff_avg_h264_qpel16_mc31_lsx; + c->avg_h264_qpel_pixels_tab[0][8] = ff_avg_h264_qpel16_mc02_lsx; + c->avg_h264_qpel_pixels_tab[0][9] = ff_avg_h264_qpel16_mc12_lsx; + c->avg_h264_qpel_pixels_tab[0][10] = ff_avg_h264_qpel16_mc22_lsx; + c->avg_h264_qpel_pixels_tab[0][11] = ff_avg_h264_qpel16_mc32_lsx; + c->avg_h264_qpel_pixels_tab[0][12] = ff_avg_h264_qpel16_mc03_lsx; + c->avg_h264_qpel_pixels_tab[0][13] = ff_avg_h264_qpel16_mc13_lsx; + c->avg_h264_qpel_pixels_tab[0][14] = ff_avg_h264_qpel16_mc23_lsx; + c->avg_h264_qpel_pixels_tab[0][15] = ff_avg_h264_qpel16_mc33_lsx; + + c->put_h264_qpel_pixels_tab[1][0] = ff_put_h264_qpel8_mc00_lsx; + c->put_h264_qpel_pixels_tab[1][1] = ff_put_h264_qpel8_mc10_lsx; + c->put_h264_qpel_pixels_tab[1][2] = ff_put_h264_qpel8_mc20_lsx; + c->put_h264_qpel_pixels_tab[1][3] = ff_put_h264_qpel8_mc30_lsx; + c->put_h264_qpel_pixels_tab[1][4] = ff_put_h264_qpel8_mc01_lsx; + c->put_h264_qpel_pixels_tab[1][5] = ff_put_h264_qpel8_mc11_lsx; + c->put_h264_qpel_pixels_tab[1][6] = ff_put_h264_qpel8_mc21_lsx; + c->put_h264_qpel_pixels_tab[1][7] = ff_put_h264_qpel8_mc31_lsx; + c->put_h264_qpel_pixels_tab[1][8] = ff_put_h264_qpel8_mc02_lsx; + c->put_h264_qpel_pixels_tab[1][9] = ff_put_h264_qpel8_mc12_lsx; + c->put_h264_qpel_pixels_tab[1][10] = ff_put_h264_qpel8_mc22_lsx; + c->put_h264_qpel_pixels_tab[1][11] = ff_put_h264_qpel8_mc32_lsx; + c->put_h264_qpel_pixels_tab[1][12] = ff_put_h264_qpel8_mc03_lsx; + c->put_h264_qpel_pixels_tab[1][13] = ff_put_h264_qpel8_mc13_lsx; + c->put_h264_qpel_pixels_tab[1][14] = ff_put_h264_qpel8_mc23_lsx; + c->put_h264_qpel_pixels_tab[1][15] = ff_put_h264_qpel8_mc33_lsx; + + c->avg_h264_qpel_pixels_tab[1][0] = ff_avg_h264_qpel8_mc00_lsx; + c->avg_h264_qpel_pixels_tab[1][1] = ff_avg_h264_qpel8_mc10_lsx; + c->avg_h264_qpel_pixels_tab[1][2] = ff_avg_h264_qpel8_mc20_lsx; + c->avg_h264_qpel_pixels_tab[1][3] = ff_avg_h264_qpel8_mc30_lsx; + c->avg_h264_qpel_pixels_tab[1][5] = ff_avg_h264_qpel8_mc11_lsx; + c->avg_h264_qpel_pixels_tab[1][6] = ff_avg_h264_qpel8_mc21_lsx; + c->avg_h264_qpel_pixels_tab[1][7] = ff_avg_h264_qpel8_mc31_lsx; + c->avg_h264_qpel_pixels_tab[1][8] = ff_avg_h264_qpel8_mc02_lsx; + c->avg_h264_qpel_pixels_tab[1][9] = ff_avg_h264_qpel8_mc12_lsx; + c->avg_h264_qpel_pixels_tab[1][10] = ff_avg_h264_qpel8_mc22_lsx; + c->avg_h264_qpel_pixels_tab[1][11] = ff_avg_h264_qpel8_mc32_lsx; + c->avg_h264_qpel_pixels_tab[1][13] = ff_avg_h264_qpel8_mc13_lsx; + c->avg_h264_qpel_pixels_tab[1][14] = ff_avg_h264_qpel8_mc23_lsx; + c->avg_h264_qpel_pixels_tab[1][15] = ff_avg_h264_qpel8_mc33_lsx; + } + } +#if HAVE_LASX if (have_lasx(cpu_flags)) { if (8 == bit_depth) { c->put_h264_qpel_pixels_tab[0][0] = ff_put_h264_qpel16_mc00_lasx; @@ -95,4 +166,5 @@ av_cold void ff_h264qpel_init_loongarch(H264QpelContext *c, int bit_depth) c->avg_h264_qpel_pixels_tab[1][15] = ff_avg_h264_qpel8_mc33_lasx; } } +#endif } diff --git a/libavcodec/loongarch/h264qpel_lasx.c b/libavcodec/loongarch/h264qpel_lasx.c index 1c142e510eb..519bb03fe64 100644 --- a/libavcodec/loongarch/h264qpel_lasx.c +++ b/libavcodec/loongarch/h264qpel_lasx.c @@ -21,7 +21,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "h264qpel_lasx.h" +#include "h264qpel_loongarch.h" #include "libavutil/loongarch/loongson_intrinsics.h" #include "libavutil/attributes.h" @@ -418,157 +418,6 @@ avg_pixels8_8_lsx(uint8_t *dst, const uint8_t *src, ptrdiff_t stride) ); } -/* avg_pixels8_8_lsx : dst = avg(src, dst) - * put_pixels8_l2_8_lsx: dst = avg(src, half) , half stride is 8. - * avg_pixels8_l2_8_lsx: dst = avg(avg(src, half), dst) , half stride is 8.*/ -static av_always_inline void -put_pixels8_l2_8_lsx(uint8_t *dst, const uint8_t *src, const uint8_t *half, - ptrdiff_t dstStride, ptrdiff_t srcStride) -{ - ptrdiff_t stride_2, stride_3, stride_4; - __asm__ volatile ( - /* h0~h7 */ - "slli.d %[stride_2], %[srcStride], 1 \n\t" - "add.d %[stride_3], %[stride_2], %[srcStride] \n\t" - "slli.d %[stride_4], %[stride_2], 1 \n\t" - "vld $vr0, %[src], 0 \n\t" - "vldx $vr1, %[src], %[srcStride] \n\t" - "vldx $vr2, %[src], %[stride_2] \n\t" - "vldx $vr3, %[src], %[stride_3] \n\t" - "add.d %[src], %[src], %[stride_4] \n\t" - "vld $vr4, %[src], 0 \n\t" - "vldx $vr5, %[src], %[srcStride] \n\t" - "vldx $vr6, %[src], %[stride_2] \n\t" - "vldx $vr7, %[src], %[stride_3] \n\t" - - "vld $vr8, %[half], 0x00 \n\t" - "vld $vr9, %[half], 0x08 \n\t" - "vld $vr10, %[half], 0x10 \n\t" - "vld $vr11, %[half], 0x18 \n\t" - "vld $vr12, %[half], 0x20 \n\t" - "vld $vr13, %[half], 0x28 \n\t" - "vld $vr14, %[half], 0x30 \n\t" - "vld $vr15, %[half], 0x38 \n\t" - - "vavgr.bu $vr0, $vr8, $vr0 \n\t" - "vavgr.bu $vr1, $vr9, $vr1 \n\t" - "vavgr.bu $vr2, $vr10, $vr2 \n\t" - "vavgr.bu $vr3, $vr11, $vr3 \n\t" - "vavgr.bu $vr4, $vr12, $vr4 \n\t" - "vavgr.bu $vr5, $vr13, $vr5 \n\t" - "vavgr.bu $vr6, $vr14, $vr6 \n\t" - "vavgr.bu $vr7, $vr15, $vr7 \n\t" - - "vstelm.d $vr0, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr1, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr2, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr3, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr4, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr5, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr6, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr7, %[dst], 0, 0 \n\t" - : [dst]"+&r"(dst), [half]"+&r"(half), [src]"+&r"(src), - [stride_2]"=&r"(stride_2), [stride_3]"=&r"(stride_3), - [stride_4]"=&r"(stride_4) - : [srcStride]"r"(srcStride), [dstStride]"r"(dstStride) - : "memory" - ); -} - -/* avg_pixels8_8_lsx : dst = avg(src, dst) - * put_pixels8_l2_8_lsx: dst = avg(src, half) , half stride is 8. - * avg_pixels8_l2_8_lsx: dst = avg(avg(src, half), dst) , half stride is 8.*/ -static av_always_inline void -avg_pixels8_l2_8_lsx(uint8_t *dst, const uint8_t *src, const uint8_t *half, - ptrdiff_t dstStride, ptrdiff_t srcStride) -{ - uint8_t *tmp = dst; - ptrdiff_t stride_2, stride_3, stride_4; - __asm__ volatile ( - /* h0~h7 */ - "slli.d %[stride_2], %[srcStride], 1 \n\t" - "add.d %[stride_3], %[stride_2], %[srcStride] \n\t" - "slli.d %[stride_4], %[stride_2], 1 \n\t" - "vld $vr0, %[src], 0 \n\t" - "vldx $vr1, %[src], %[srcStride] \n\t" - "vldx $vr2, %[src], %[stride_2] \n\t" - "vldx $vr3, %[src], %[stride_3] \n\t" - "add.d %[src], %[src], %[stride_4] \n\t" - "vld $vr4, %[src], 0 \n\t" - "vldx $vr5, %[src], %[srcStride] \n\t" - "vldx $vr6, %[src], %[stride_2] \n\t" - "vldx $vr7, %[src], %[stride_3] \n\t" - - "vld $vr8, %[half], 0x00 \n\t" - "vld $vr9, %[half], 0x08 \n\t" - "vld $vr10, %[half], 0x10 \n\t" - "vld $vr11, %[half], 0x18 \n\t" - "vld $vr12, %[half], 0x20 \n\t" - "vld $vr13, %[half], 0x28 \n\t" - "vld $vr14, %[half], 0x30 \n\t" - "vld $vr15, %[half], 0x38 \n\t" - - "vavgr.bu $vr0, $vr8, $vr0 \n\t" - "vavgr.bu $vr1, $vr9, $vr1 \n\t" - "vavgr.bu $vr2, $vr10, $vr2 \n\t" - "vavgr.bu $vr3, $vr11, $vr3 \n\t" - "vavgr.bu $vr4, $vr12, $vr4 \n\t" - "vavgr.bu $vr5, $vr13, $vr5 \n\t" - "vavgr.bu $vr6, $vr14, $vr6 \n\t" - "vavgr.bu $vr7, $vr15, $vr7 \n\t" - - "slli.d %[stride_2], %[dstStride], 1 \n\t" - "add.d %[stride_3], %[stride_2], %[dstStride] \n\t" - "slli.d %[stride_4], %[stride_2], 1 \n\t" - "vld $vr8, %[tmp], 0 \n\t" - "vldx $vr9, %[tmp], %[dstStride] \n\t" - "vldx $vr10, %[tmp], %[stride_2] \n\t" - "vldx $vr11, %[tmp], %[stride_3] \n\t" - "add.d %[tmp], %[tmp], %[stride_4] \n\t" - "vld $vr12, %[tmp], 0 \n\t" - "vldx $vr13, %[tmp], %[dstStride] \n\t" - "vldx $vr14, %[tmp], %[stride_2] \n\t" - "vldx $vr15, %[tmp], %[stride_3] \n\t" - - "vavgr.bu $vr0, $vr8, $vr0 \n\t" - "vavgr.bu $vr1, $vr9, $vr1 \n\t" - "vavgr.bu $vr2, $vr10, $vr2 \n\t" - "vavgr.bu $vr3, $vr11, $vr3 \n\t" - "vavgr.bu $vr4, $vr12, $vr4 \n\t" - "vavgr.bu $vr5, $vr13, $vr5 \n\t" - "vavgr.bu $vr6, $vr14, $vr6 \n\t" - "vavgr.bu $vr7, $vr15, $vr7 \n\t" - - "vstelm.d $vr0, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr1, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr2, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr3, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr4, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr5, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr6, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr7, %[dst], 0, 0 \n\t" - : [dst]"+&r"(dst), [tmp]"+&r"(tmp), [half]"+&r"(half), - [src]"+&r"(src), [stride_2]"=&r"(stride_2), - [stride_3]"=&r"(stride_3), [stride_4]"=&r"(stride_4) - : [dstStride]"r"(dstStride), [srcStride]"r"(srcStride) - : "memory" - ); -} - /* put_pixels16_8_lsx: dst = src */ static av_always_inline void put_pixels16_8_lsx(uint8_t *dst, const uint8_t *src, ptrdiff_t stride) @@ -729,254 +578,6 @@ avg_pixels16_8_lsx(uint8_t *dst, const uint8_t *src, ptrdiff_t stride) ); } -/* avg_pixels16_8_lsx : dst = avg(src, dst) - * put_pixels16_l2_8_lsx: dst = avg(src, half) , half stride is 8. - * avg_pixels16_l2_8_lsx: dst = avg(avg(src, half), dst) , half stride is 8.*/ -static av_always_inline void -put_pixels16_l2_8_lsx(uint8_t *dst, const uint8_t *src, uint8_t *half, - ptrdiff_t dstStride, ptrdiff_t srcStride) -{ - ptrdiff_t stride_2, stride_3, stride_4; - ptrdiff_t dstride_2, dstride_3, dstride_4; - __asm__ volatile ( - "slli.d %[stride_2], %[srcStride], 1 \n\t" - "add.d %[stride_3], %[stride_2], %[srcStride] \n\t" - "slli.d %[stride_4], %[stride_2], 1 \n\t" - "slli.d %[dstride_2], %[dstStride], 1 \n\t" - "add.d %[dstride_3], %[dstride_2], %[dstStride] \n\t" - "slli.d %[dstride_4], %[dstride_2], 1 \n\t" - /* h0~h7 */ - "vld $vr0, %[src], 0 \n\t" - "vldx $vr1, %[src], %[srcStride] \n\t" - "vldx $vr2, %[src], %[stride_2] \n\t" - "vldx $vr3, %[src], %[stride_3] \n\t" - "add.d %[src], %[src], %[stride_4] \n\t" - "vld $vr4, %[src], 0 \n\t" - "vldx $vr5, %[src], %[srcStride] \n\t" - "vldx $vr6, %[src], %[stride_2] \n\t" - "vldx $vr7, %[src], %[stride_3] \n\t" - "add.d %[src], %[src], %[stride_4] \n\t" - - "vld $vr8, %[half], 0x00 \n\t" - "vld $vr9, %[half], 0x10 \n\t" - "vld $vr10, %[half], 0x20 \n\t" - "vld $vr11, %[half], 0x30 \n\t" - "vld $vr12, %[half], 0x40 \n\t" - "vld $vr13, %[half], 0x50 \n\t" - "vld $vr14, %[half], 0x60 \n\t" - "vld $vr15, %[half], 0x70 \n\t" - - "vavgr.bu $vr0, $vr8, $vr0 \n\t" - "vavgr.bu $vr1, $vr9, $vr1 \n\t" - "vavgr.bu $vr2, $vr10, $vr2 \n\t" - "vavgr.bu $vr3, $vr11, $vr3 \n\t" - "vavgr.bu $vr4, $vr12, $vr4 \n\t" - "vavgr.bu $vr5, $vr13, $vr5 \n\t" - "vavgr.bu $vr6, $vr14, $vr6 \n\t" - "vavgr.bu $vr7, $vr15, $vr7 \n\t" - - "vst $vr0, %[dst], 0 \n\t" - "vstx $vr1, %[dst], %[dstStride] \n\t" - "vstx $vr2, %[dst], %[dstride_2] \n\t" - "vstx $vr3, %[dst], %[dstride_3] \n\t" - "add.d %[dst], %[dst], %[dstride_4] \n\t" - "vst $vr4, %[dst], 0 \n\t" - "vstx $vr5, %[dst], %[dstStride] \n\t" - "vstx $vr6, %[dst], %[dstride_2] \n\t" - "vstx $vr7, %[dst], %[dstride_3] \n\t" - "add.d %[dst], %[dst], %[dstride_4] \n\t" - - /* h8~h15 */ - "vld $vr0, %[src], 0 \n\t" - "vldx $vr1, %[src], %[srcStride] \n\t" - "vldx $vr2, %[src], %[stride_2] \n\t" - "vldx $vr3, %[src], %[stride_3] \n\t" - "add.d %[src], %[src], %[stride_4] \n\t" - "vld $vr4, %[src], 0 \n\t" - "vldx $vr5, %[src], %[srcStride] \n\t" - "vldx $vr6, %[src], %[stride_2] \n\t" - "vldx $vr7, %[src], %[stride_3] \n\t" - - "vld $vr8, %[half], 0x80 \n\t" - "vld $vr9, %[half], 0x90 \n\t" - "vld $vr10, %[half], 0xa0 \n\t" - "vld $vr11, %[half], 0xb0 \n\t" - "vld $vr12, %[half], 0xc0 \n\t" - "vld $vr13, %[half], 0xd0 \n\t" - "vld $vr14, %[half], 0xe0 \n\t" - "vld $vr15, %[half], 0xf0 \n\t" - - "vavgr.bu $vr0, $vr8, $vr0 \n\t" - "vavgr.bu $vr1, $vr9, $vr1 \n\t" - "vavgr.bu $vr2, $vr10, $vr2 \n\t" - "vavgr.bu $vr3, $vr11, $vr3 \n\t" - "vavgr.bu $vr4, $vr12, $vr4 \n\t" - "vavgr.bu $vr5, $vr13, $vr5 \n\t" - "vavgr.bu $vr6, $vr14, $vr6 \n\t" - "vavgr.bu $vr7, $vr15, $vr7 \n\t" - - "vst $vr0, %[dst], 0 \n\t" - "vstx $vr1, %[dst], %[dstStride] \n\t" - "vstx $vr2, %[dst], %[dstride_2] \n\t" - "vstx $vr3, %[dst], %[dstride_3] \n\t" - "add.d %[dst], %[dst], %[dstride_4] \n\t" - "vst $vr4, %[dst], 0 \n\t" - "vstx $vr5, %[dst], %[dstStride] \n\t" - "vstx $vr6, %[dst], %[dstride_2] \n\t" - "vstx $vr7, %[dst], %[dstride_3] \n\t" - : [dst]"+&r"(dst), [half]"+&r"(half), [src]"+&r"(src), - [stride_2]"=&r"(stride_2), [stride_3]"=&r"(stride_3), - [stride_4]"=&r"(stride_4), [dstride_2]"=&r"(dstride_2), - [dstride_3]"=&r"(dstride_3), [dstride_4]"=&r"(dstride_4) - : [dstStride]"r"(dstStride), [srcStride]"r"(srcStride) - : "memory" - ); -} - -/* avg_pixels16_8_lsx : dst = avg(src, dst) - * put_pixels16_l2_8_lsx: dst = avg(src, half) , half stride is 8. - * avg_pixels16_l2_8_lsx: dst = avg(avg(src, half), dst) , half stride is 8.*/ -static av_always_inline void -avg_pixels16_l2_8_lsx(uint8_t *dst, const uint8_t *src, uint8_t *half, - ptrdiff_t dstStride, ptrdiff_t srcStride) -{ - uint8_t *tmp = dst; - ptrdiff_t stride_2, stride_3, stride_4; - ptrdiff_t dstride_2, dstride_3, dstride_4; - __asm__ volatile ( - "slli.d %[stride_2], %[srcStride], 1 \n\t" - "add.d %[stride_3], %[stride_2], %[srcStride] \n\t" - "slli.d %[stride_4], %[stride_2], 1 \n\t" - "slli.d %[dstride_2], %[dstStride], 1 \n\t" - "add.d %[dstride_3], %[dstride_2], %[dstStride] \n\t" - "slli.d %[dstride_4], %[dstride_2], 1 \n\t" - /* h0~h7 */ - "vld $vr0, %[src], 0 \n\t" - "vldx $vr1, %[src], %[srcStride] \n\t" - "vldx $vr2, %[src], %[stride_2] \n\t" - "vldx $vr3, %[src], %[stride_3] \n\t" - "add.d %[src], %[src], %[stride_4] \n\t" - "vld $vr4, %[src], 0 \n\t" - "vldx $vr5, %[src], %[srcStride] \n\t" - "vldx $vr6, %[src], %[stride_2] \n\t" - "vldx $vr7, %[src], %[stride_3] \n\t" - "add.d %[src], %[src], %[stride_4] \n\t" - - "vld $vr8, %[half], 0x00 \n\t" - "vld $vr9, %[half], 0x10 \n\t" - "vld $vr10, %[half], 0x20 \n\t" - "vld $vr11, %[half], 0x30 \n\t" - "vld $vr12, %[half], 0x40 \n\t" - "vld $vr13, %[half], 0x50 \n\t" - "vld $vr14, %[half], 0x60 \n\t" - "vld $vr15, %[half], 0x70 \n\t" - - "vavgr.bu $vr0, $vr8, $vr0 \n\t" - "vavgr.bu $vr1, $vr9, $vr1 \n\t" - "vavgr.bu $vr2, $vr10, $vr2 \n\t" - "vavgr.bu $vr3, $vr11, $vr3 \n\t" - "vavgr.bu $vr4, $vr12, $vr4 \n\t" - "vavgr.bu $vr5, $vr13, $vr5 \n\t" - "vavgr.bu $vr6, $vr14, $vr6 \n\t" - "vavgr.bu $vr7, $vr15, $vr7 \n\t" - - "vld $vr8, %[tmp], 0 \n\t" - "vldx $vr9, %[tmp], %[dstStride] \n\t" - "vldx $vr10, %[tmp], %[dstride_2] \n\t" - "vldx $vr11, %[tmp], %[dstride_3] \n\t" - "add.d %[tmp], %[tmp], %[dstride_4] \n\t" - "vld $vr12, %[tmp], 0 \n\t" - "vldx $vr13, %[tmp], %[dstStride] \n\t" - "vldx $vr14, %[tmp], %[dstride_2] \n\t" - "vldx $vr15, %[tmp], %[dstride_3] \n\t" - "add.d %[tmp], %[tmp], %[dstride_4] \n\t" - - "vavgr.bu $vr0, $vr8, $vr0 \n\t" - "vavgr.bu $vr1, $vr9, $vr1 \n\t" - "vavgr.bu $vr2, $vr10, $vr2 \n\t" - "vavgr.bu $vr3, $vr11, $vr3 \n\t" - "vavgr.bu $vr4, $vr12, $vr4 \n\t" - "vavgr.bu $vr5, $vr13, $vr5 \n\t" - "vavgr.bu $vr6, $vr14, $vr6 \n\t" - "vavgr.bu $vr7, $vr15, $vr7 \n\t" - - "vst $vr0, %[dst], 0 \n\t" - "vstx $vr1, %[dst], %[dstStride] \n\t" - "vstx $vr2, %[dst], %[dstride_2] \n\t" - "vstx $vr3, %[dst], %[dstride_3] \n\t" - "add.d %[dst], %[dst], %[dstride_4] \n\t" - "vst $vr4, %[dst], 0 \n\t" - "vstx $vr5, %[dst], %[dstStride] \n\t" - "vstx $vr6, %[dst], %[dstride_2] \n\t" - "vstx $vr7, %[dst], %[dstride_3] \n\t" - "add.d %[dst], %[dst], %[dstride_4] \n\t" - - /* h8~h15 */ - "vld $vr0, %[src], 0 \n\t" - "vldx $vr1, %[src], %[srcStride] \n\t" - "vldx $vr2, %[src], %[stride_2] \n\t" - "vldx $vr3, %[src], %[stride_3] \n\t" - "add.d %[src], %[src], %[stride_4] \n\t" - "vld $vr4, %[src], 0 \n\t" - "vldx $vr5, %[src], %[srcStride] \n\t" - "vldx $vr6, %[src], %[stride_2] \n\t" - "vldx $vr7, %[src], %[stride_3] \n\t" - - "vld $vr8, %[half], 0x80 \n\t" - "vld $vr9, %[half], 0x90 \n\t" - "vld $vr10, %[half], 0xa0 \n\t" - "vld $vr11, %[half], 0xb0 \n\t" - "vld $vr12, %[half], 0xc0 \n\t" - "vld $vr13, %[half], 0xd0 \n\t" - "vld $vr14, %[half], 0xe0 \n\t" - "vld $vr15, %[half], 0xf0 \n\t" - - "vavgr.bu $vr0, $vr8, $vr0 \n\t" - "vavgr.bu $vr1, $vr9, $vr1 \n\t" - "vavgr.bu $vr2, $vr10, $vr2 \n\t" - "vavgr.bu $vr3, $vr11, $vr3 \n\t" - "vavgr.bu $vr4, $vr12, $vr4 \n\t" - "vavgr.bu $vr5, $vr13, $vr5 \n\t" - "vavgr.bu $vr6, $vr14, $vr6 \n\t" - "vavgr.bu $vr7, $vr15, $vr7 \n\t" - - "vld $vr8, %[tmp], 0 \n\t" - "vldx $vr9, %[tmp], %[dstStride] \n\t" - "vldx $vr10, %[tmp], %[dstride_2] \n\t" - "vldx $vr11, %[tmp], %[dstride_3] \n\t" - "add.d %[tmp], %[tmp], %[dstride_4] \n\t" - "vld $vr12, %[tmp], 0 \n\t" - "vldx $vr13, %[tmp], %[dstStride] \n\t" - "vldx $vr14, %[tmp], %[dstride_2] \n\t" - "vldx $vr15, %[tmp], %[dstride_3] \n\t" - - "vavgr.bu $vr0, $vr8, $vr0 \n\t" - "vavgr.bu $vr1, $vr9, $vr1 \n\t" - "vavgr.bu $vr2, $vr10, $vr2 \n\t" - "vavgr.bu $vr3, $vr11, $vr3 \n\t" - "vavgr.bu $vr4, $vr12, $vr4 \n\t" - "vavgr.bu $vr5, $vr13, $vr5 \n\t" - "vavgr.bu $vr6, $vr14, $vr6 \n\t" - "vavgr.bu $vr7, $vr15, $vr7 \n\t" - - "vst $vr0, %[dst], 0 \n\t" - "vstx $vr1, %[dst], %[dstStride] \n\t" - "vstx $vr2, %[dst], %[dstride_2] \n\t" - "vstx $vr3, %[dst], %[dstride_3] \n\t" - "add.d %[dst], %[dst], %[dstride_4] \n\t" - "vst $vr4, %[dst], 0 \n\t" - "vstx $vr5, %[dst], %[dstStride] \n\t" - "vstx $vr6, %[dst], %[dstride_2] \n\t" - "vstx $vr7, %[dst], %[dstride_3] \n\t" - : [dst]"+&r"(dst), [tmp]"+&r"(tmp), [half]"+&r"(half), [src]"+&r"(src), - [stride_2]"=&r"(stride_2), [stride_3]"=&r"(stride_3), - [stride_4]"=&r"(stride_4), [dstride_2]"=&r"(dstride_2), - [dstride_3]"=&r"(dstride_3), [dstride_4]"=&r"(dstride_4) - : [dstStride]"r"(dstStride), [srcStride]"r"(srcStride) - : "memory" - ); -} - #define QPEL8_H_LOWPASS(out_v) \ src00 = __lasx_xvld(src, - 2); \ src += srcStride; \ diff --git a/libavcodec/loongarch/h264qpel_lasx.h b/libavcodec/loongarch/h264qpel_lasx.h deleted file mode 100644 index 32b6b509170..00000000000 --- a/libavcodec/loongarch/h264qpel_lasx.h +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (c) 2020 Loongson Technology Corporation Limited - * Contributed by Shiyou Yin - * - * 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_LOONGARCH_H264QPEL_LASX_H -#define AVCODEC_LOONGARCH_H264QPEL_LASX_H - -#include -#include -#include "libavcodec/h264.h" - -void ff_h264_h_lpf_luma_inter_lasx(uint8_t *src, int stride, - int alpha, int beta, int8_t *tc0); -void ff_h264_v_lpf_luma_inter_lasx(uint8_t *src, int stride, - int alpha, int beta, int8_t *tc0); -void ff_put_h264_qpel16_mc00_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc10_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc20_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc30_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc01_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc11_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc21_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc31_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc02_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc12_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc32_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc22_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc03_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc13_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc23_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc33_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc00_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc10_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc20_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc30_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc01_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc11_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc21_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc31_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc02_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc12_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc22_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc32_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc03_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc13_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc23_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc33_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); - -void ff_put_h264_qpel8_mc00_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc10_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc20_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc30_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc01_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc11_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc21_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc31_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc02_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc12_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc22_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc32_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc03_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc13_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc23_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc33_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_avg_h264_qpel8_mc00_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc10_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc20_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc30_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc11_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc21_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc31_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc02_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc12_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc22_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc32_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc13_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc23_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc33_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -#endif // #ifndef AVCODEC_LOONGARCH_H264QPEL_LASX_H diff --git a/libavcodec/loongarch/h264qpel_loongarch.h b/libavcodec/loongarch/h264qpel_loongarch.h new file mode 100644 index 00000000000..68232730dac --- /dev/null +++ b/libavcodec/loongarch/h264qpel_loongarch.h @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2023 Loongson Technology Corporation Limited + * Contributed by Shiyou Yin + * + * 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_LOONGARCH_H264QPEL_LOONGARCH_H +#define AVCODEC_LOONGARCH_H264QPEL_LOONGARCH_H + +#include +#include +#include "libavcodec/h264.h" +#include "config.h" + +void put_h264_qpel8_hv_lowpass_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dstStride, ptrdiff_t srcStride); +void put_h264_qpel8_h_lowpass_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dstStride, ptrdiff_t srcStride); +void put_h264_qpel8_v_lowpass_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dstStride, ptrdiff_t srcStride); +void put_pixels16_l2_8_lsx(uint8_t *dst, const uint8_t *src, uint8_t *half, + ptrdiff_t dstStride, ptrdiff_t srcStride); +void put_pixels8_l2_8_lsx(uint8_t *dst, const uint8_t *src, const uint8_t *half, + ptrdiff_t dstStride, ptrdiff_t srcStride); + +void avg_h264_qpel8_h_lowpass_lsx(uint8_t *dst, const uint8_t *src, int dstStride, + int srcStride); +void avg_h264_qpel8_v_lowpass_lsx(uint8_t *dst, uint8_t *src, int dstStride, + int srcStride); +void avg_pixels16_l2_8_lsx(uint8_t *dst, const uint8_t *src, uint8_t *half, + ptrdiff_t dstStride, ptrdiff_t srcStride); +void avg_h264_qpel8_hv_lowpass_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dstStride, ptrdiff_t srcStride); +void avg_pixels8_l2_8_lsx(uint8_t *dst, const uint8_t *src, const uint8_t *half, + ptrdiff_t dstStride, ptrdiff_t srcStride); + +void ff_put_h264_qpel16_mc00_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc10_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc20_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc30_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc01_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc11_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc13_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc31_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc33_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc03_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc02_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc22_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc21_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel16_mc12_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel16_mc32_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel16_mc23_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); + +void ff_avg_h264_qpel16_mc00_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc10_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc30_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc33_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc11_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc31_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc13_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc20_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc02_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel16_mc03_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel16_mc23_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel16_mc21_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel16_mc01_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel16_mc32_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel16_mc12_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel16_mc22_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); + +void ff_put_h264_qpel8_mc03_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc00_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc01_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc30_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc10_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc33_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc13_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc31_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc11_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc32_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc21_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc23_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc12_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc02_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc22_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc20_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); + +void ff_avg_h264_qpel8_mc00_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc10_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc20_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc30_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc11_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc21_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc31_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc02_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc12_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc22_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc32_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc13_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc23_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc33_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); + +#if HAVE_LASX +void ff_h264_h_lpf_luma_inter_lasx(uint8_t *src, int stride, + int alpha, int beta, int8_t *tc0); +void ff_h264_v_lpf_luma_inter_lasx(uint8_t *src, int stride, + int alpha, int beta, int8_t *tc0); +void ff_put_h264_qpel16_mc00_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc10_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc20_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc30_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc01_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc11_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc21_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc31_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc02_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc12_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc32_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc22_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc03_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc13_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc23_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc33_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc00_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc10_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc20_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc30_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc01_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc11_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc21_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc31_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc02_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc12_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc22_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc32_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc03_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc13_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc23_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc33_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); + +void ff_put_h264_qpel8_mc00_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc10_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc20_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc30_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc01_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc11_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc21_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc31_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc02_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc12_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc22_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc32_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc03_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc13_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc23_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc33_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc00_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc10_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc20_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc30_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc11_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc21_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc31_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc02_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc12_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc22_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc32_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc13_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc23_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc33_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +#endif + +#endif // #ifndef AVCODEC_LOONGARCH_H264QPEL_LOONGARCH_H diff --git a/libavcodec/loongarch/h264qpel_lsx.c b/libavcodec/loongarch/h264qpel_lsx.c new file mode 100644 index 00000000000..12b3bae6d1c --- /dev/null +++ b/libavcodec/loongarch/h264qpel_lsx.c @@ -0,0 +1,487 @@ +/* + * Loongson LSX optimized h264qpel + * + * Copyright (c) 2023 Loongson Technology Corporation Limited + * Contributed by Hecai Yuan + * + * 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 "h264qpel_loongarch.h" +#include "libavutil/loongarch/loongson_intrinsics.h" +#include "libavutil/attributes.h" + +static void put_h264_qpel16_hv_lowpass_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dstStride, ptrdiff_t srcStride) +{ + put_h264_qpel8_hv_lowpass_lsx(dst, src, dstStride, srcStride); + put_h264_qpel8_hv_lowpass_lsx(dst + 8, src + 8, dstStride, srcStride); + src += srcStride << 3; + dst += dstStride << 3; + put_h264_qpel8_hv_lowpass_lsx(dst, src, dstStride, srcStride); + put_h264_qpel8_hv_lowpass_lsx(dst + 8, src + 8, dstStride, srcStride); +} + +void ff_put_h264_qpel16_mc22_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + put_h264_qpel16_hv_lowpass_lsx(dst, src, stride, stride); +} + +static void put_h264_qpel16_h_lowpass_lsx(uint8_t *dst, const uint8_t *src, + int dstStride, int srcStride) +{ + put_h264_qpel8_h_lowpass_lsx(dst, src, dstStride, srcStride); + put_h264_qpel8_h_lowpass_lsx(dst+8, src+8, dstStride, srcStride); + src += srcStride << 3; + dst += dstStride << 3; + put_h264_qpel8_h_lowpass_lsx(dst, src, dstStride, srcStride); + put_h264_qpel8_h_lowpass_lsx(dst+8, src+8, dstStride, srcStride); +} + +static void put_h264_qpel16_v_lowpass_lsx(uint8_t *dst, const uint8_t *src, + int dstStride, int srcStride) +{ + put_h264_qpel8_v_lowpass_lsx(dst, (uint8_t*)src, dstStride, srcStride); + put_h264_qpel8_v_lowpass_lsx(dst+8, (uint8_t*)src+8, dstStride, srcStride); + src += 8*srcStride; + dst += 8*dstStride; + put_h264_qpel8_v_lowpass_lsx(dst, (uint8_t*)src, dstStride, srcStride); + put_h264_qpel8_v_lowpass_lsx(dst+8, (uint8_t*)src+8, dstStride, srcStride); +} + +void ff_put_h264_qpel16_mc21_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[512]; + uint8_t *const halfH = temp; + uint8_t *const halfHV = temp + 256; + + put_h264_qpel16_h_lowpass_lsx(halfH, src, 16, stride); + put_h264_qpel16_hv_lowpass_lsx(halfHV, src, 16, stride); + put_pixels16_l2_8_lsx(dst, halfH, halfHV, stride, 16); +} + +void ff_put_h264_qpel16_mc12_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[512]; + uint8_t *const halfHV = temp; + uint8_t *const halfH = temp + 256; + + put_h264_qpel16_hv_lowpass_lsx(halfHV, src, 16, stride); + put_h264_qpel16_v_lowpass_lsx(halfH, src, 16, stride); + put_pixels16_l2_8_lsx(dst, halfH, halfHV, stride, 16); +} + +void ff_put_h264_qpel16_mc32_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[512]; + uint8_t *const halfHV = temp; + uint8_t *const halfH = temp + 256; + + put_h264_qpel16_hv_lowpass_lsx(halfHV, src, 16, stride); + put_h264_qpel16_v_lowpass_lsx(halfH, src + 1, 16, stride); + put_pixels16_l2_8_lsx(dst, halfH, halfHV, stride, 16); +} + +void ff_put_h264_qpel16_mc23_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[512]; + uint8_t *const halfH = temp; + uint8_t *const halfHV = temp + 256; + + put_h264_qpel16_h_lowpass_lsx(halfH, src + stride, 16, stride); + put_h264_qpel16_hv_lowpass_lsx(halfHV, src, 16, stride); + put_pixels16_l2_8_lsx(dst, halfH, halfHV, stride, 16); +} + +static void avg_h264_qpel16_v_lowpass_lsx(uint8_t *dst, const uint8_t *src, + int dstStride, int srcStride) +{ + avg_h264_qpel8_v_lowpass_lsx(dst, (uint8_t*)src, dstStride, srcStride); + avg_h264_qpel8_v_lowpass_lsx(dst+8, (uint8_t*)src+8, dstStride, srcStride); + src += 8*srcStride; + dst += 8*dstStride; + avg_h264_qpel8_v_lowpass_lsx(dst, (uint8_t*)src, dstStride, srcStride); + avg_h264_qpel8_v_lowpass_lsx(dst+8, (uint8_t*)src+8, dstStride, srcStride); +} + +void ff_avg_h264_qpel16_mc02_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + avg_h264_qpel16_v_lowpass_lsx(dst, src, stride, stride); +} + +void ff_avg_h264_qpel16_mc03_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t half[256]; + + put_h264_qpel16_v_lowpass_lsx(half, src, 16, stride); + avg_pixels16_l2_8_lsx(dst, src + stride, half, stride, stride); +} + +void ff_avg_h264_qpel16_mc23_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[512]; + uint8_t *const halfH = temp; + uint8_t *const halfHV = temp + 256; + + put_h264_qpel16_h_lowpass_lsx(halfH, src + stride, 16, stride); + put_h264_qpel16_hv_lowpass_lsx(halfHV, src, 16, stride); + avg_pixels16_l2_8_lsx(dst, halfH, halfHV, stride, 16); +} + +void ff_avg_h264_qpel16_mc21_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[512]; + uint8_t *const halfH = temp; + uint8_t *const halfHV = temp + 256; + + put_h264_qpel16_h_lowpass_lsx(halfH, src, 16, stride); + put_h264_qpel16_hv_lowpass_lsx(halfHV, src, 16, stride); + avg_pixels16_l2_8_lsx(dst, halfH, halfHV, stride, 16); +} + +void ff_avg_h264_qpel16_mc01_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t half[256]; + + put_h264_qpel16_v_lowpass_lsx(half, src, 16, stride); + avg_pixels16_l2_8_lsx(dst, src, half, stride, stride); +} + +void ff_avg_h264_qpel16_mc32_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[512]; + uint8_t *const halfHV = temp; + uint8_t *const halfH = temp + 256; + + put_h264_qpel16_hv_lowpass_lsx(halfHV, src, 16, stride); + put_h264_qpel16_v_lowpass_lsx(halfH, src + 1, 16, stride); + avg_pixels16_l2_8_lsx(dst, halfH, halfHV, stride, 16); +} + +void ff_avg_h264_qpel16_mc12_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[512]; + uint8_t *const halfHV = temp; + uint8_t *const halfH = temp + 256; + + put_h264_qpel16_hv_lowpass_lsx(halfHV, src, 16, stride); + put_h264_qpel16_v_lowpass_lsx(halfH, src, 16, stride); + avg_pixels16_l2_8_lsx(dst, halfH, halfHV, stride, 16); +} + +static void avg_h264_qpel16_hv_lowpass_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dstStride, ptrdiff_t srcStride) +{ + avg_h264_qpel8_hv_lowpass_lsx(dst, src, dstStride, srcStride); + avg_h264_qpel8_hv_lowpass_lsx(dst + 8, src + 8, dstStride, srcStride); + src += srcStride << 3; + dst += dstStride << 3; + avg_h264_qpel8_hv_lowpass_lsx(dst, src, dstStride, srcStride); + avg_h264_qpel8_hv_lowpass_lsx(dst + 8, src + 8, dstStride, srcStride); +} + +void ff_avg_h264_qpel16_mc22_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + avg_h264_qpel16_hv_lowpass_lsx(dst, src, stride, stride); +} + +void ff_put_h264_qpel8_mc03_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t half[64]; + + put_h264_qpel8_v_lowpass_lsx(half, (uint8_t*)src, 8, stride); + put_pixels8_l2_8_lsx(dst, src + stride, half, stride, stride); +} + +void ff_put_h264_qpel8_mc01_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t half[64]; + + put_h264_qpel8_v_lowpass_lsx(half, (uint8_t*)src, 8, stride); + put_pixels8_l2_8_lsx(dst, src, half, stride, stride); +} + +void ff_put_h264_qpel8_mc30_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t half[64]; + + put_h264_qpel8_h_lowpass_lsx(half, src, 8, stride); + put_pixels8_l2_8_lsx(dst, src+1, half, stride, stride); +} + +void ff_put_h264_qpel8_mc10_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t half[64]; + + put_h264_qpel8_h_lowpass_lsx(half, src, 8, stride); + put_pixels8_l2_8_lsx(dst, src, half, stride, stride); +} + +void ff_put_h264_qpel8_mc33_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t halfH[64]; + uint8_t halfV[64]; + + put_h264_qpel8_h_lowpass_lsx(halfH, src + stride, 8, stride); + put_h264_qpel8_v_lowpass_lsx(halfV, (uint8_t*)src + 1, 8, stride); + put_pixels8_l2_8_lsx(dst, halfH, halfV, stride, 8); +} + +void ff_put_h264_qpel8_mc13_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t halfH[64]; + uint8_t halfV[64]; + + put_h264_qpel8_h_lowpass_lsx(halfH, src + stride, 8, stride); + put_h264_qpel8_v_lowpass_lsx(halfV, (uint8_t*)src, 8, stride); + put_pixels8_l2_8_lsx(dst, halfH, halfV, stride, 8); +} + +void ff_put_h264_qpel8_mc31_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t halfH[64]; + uint8_t halfV[64]; + + put_h264_qpel8_h_lowpass_lsx(halfH, src, 8, stride); + put_h264_qpel8_v_lowpass_lsx(halfV, (uint8_t*)src + 1, 8, stride); + put_pixels8_l2_8_lsx(dst, halfH, halfV, stride, 8); +} + +void ff_put_h264_qpel8_mc11_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t halfH[64]; + uint8_t halfV[64]; + + put_h264_qpel8_h_lowpass_lsx(halfH, src, 8, stride); + put_h264_qpel8_v_lowpass_lsx(halfV, (uint8_t*)src, 8, stride); + put_pixels8_l2_8_lsx(dst, halfH, halfV, stride, 8); +} + +void ff_put_h264_qpel8_mc32_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[128]; + uint8_t *const halfHV = temp; + uint8_t *const halfH = temp + 64; + + put_h264_qpel8_hv_lowpass_lsx(halfHV, src, 8, stride); + put_h264_qpel8_v_lowpass_lsx(halfH, (uint8_t*)src + 1, 8, stride); + put_pixels8_l2_8_lsx(dst, halfH, halfHV, stride, 8); +} + +void ff_put_h264_qpel8_mc21_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[128]; + uint8_t *const halfH = temp; + uint8_t *const halfHV = temp + 64; + + put_h264_qpel8_h_lowpass_lsx(halfH, src, 8, stride); + put_h264_qpel8_hv_lowpass_lsx(halfHV, src, 8, stride); + put_pixels8_l2_8_lsx(dst, halfH, halfHV, stride, 8); +} + +void ff_put_h264_qpel8_mc23_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[128]; + uint8_t *const halfH = temp; + uint8_t *const halfHV = temp + 64; + + put_h264_qpel8_h_lowpass_lsx(halfH, src + stride, 8, stride); + put_h264_qpel8_hv_lowpass_lsx(halfHV, src, 8, stride); + put_pixels8_l2_8_lsx(dst, halfH, halfHV, stride, 8); +} + +void ff_put_h264_qpel8_mc12_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[128]; + uint8_t *const halfHV = temp; + uint8_t *const halfH = temp + 64; + + put_h264_qpel8_hv_lowpass_lsx(halfHV, src, 8, stride); + put_h264_qpel8_v_lowpass_lsx(halfH, (uint8_t*)src, 8, stride); + put_pixels8_l2_8_lsx(dst, halfH, halfHV, stride, 8); +} + +void ff_put_h264_qpel8_mc02_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + put_h264_qpel8_v_lowpass_lsx(dst, (uint8_t*)src, stride, stride); +} + +void ff_put_h264_qpel8_mc22_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + put_h264_qpel8_hv_lowpass_lsx(dst, src, stride, stride); +} + +void ff_put_h264_qpel8_mc20_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + put_h264_qpel8_h_lowpass_lsx(dst, src, stride, stride); +} + +void ff_avg_h264_qpel8_mc10_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t half[64]; + + put_h264_qpel8_h_lowpass_lsx(half, src, 8, stride); + avg_pixels8_l2_8_lsx(dst, src, half, stride, stride); +} + +void ff_avg_h264_qpel8_mc20_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + avg_h264_qpel8_h_lowpass_lsx(dst, src, stride, stride); +} + +void ff_avg_h264_qpel8_mc30_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t half[64]; + + put_h264_qpel8_h_lowpass_lsx(half, src, 8, stride); + avg_pixels8_l2_8_lsx(dst, src+1, half, stride, stride); +} + +void ff_avg_h264_qpel8_mc11_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t halfH[64]; + uint8_t halfV[64]; + + put_h264_qpel8_h_lowpass_lsx(halfH, src, 8, stride); + put_h264_qpel8_v_lowpass_lsx(halfV, (uint8_t*)src, 8, stride); + avg_pixels8_l2_8_lsx(dst, halfH, halfV, stride, 8); +} + +void ff_avg_h264_qpel8_mc21_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[128]; + uint8_t *const halfH = temp; + uint8_t *const halfHV = temp + 64; + + put_h264_qpel8_h_lowpass_lsx(halfH, src, 8, stride); + put_h264_qpel8_hv_lowpass_lsx(halfHV, src, 8, stride); + avg_pixels8_l2_8_lsx(dst, halfH, halfHV, stride, 8); +} + +void ff_avg_h264_qpel8_mc31_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t halfH[64]; + uint8_t halfV[64]; + + put_h264_qpel8_h_lowpass_lsx(halfH, src, 8, stride); + put_h264_qpel8_v_lowpass_lsx(halfV, (uint8_t*)src + 1, 8, stride); + avg_pixels8_l2_8_lsx(dst, halfH, halfV, stride, 8); +} + +void ff_avg_h264_qpel8_mc02_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + avg_h264_qpel8_v_lowpass_lsx(dst, (uint8_t*)src, stride, stride); +} + +void ff_avg_h264_qpel8_mc12_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[128]; + uint8_t *const halfHV = temp; + uint8_t *const halfH = temp + 64; + + put_h264_qpel8_hv_lowpass_lsx(halfHV, src, 8, stride); + put_h264_qpel8_v_lowpass_lsx(halfH, (uint8_t*)src, 8, stride); + avg_pixels8_l2_8_lsx(dst, halfH, halfHV, stride, 8); +} + +void ff_avg_h264_qpel8_mc22_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + avg_h264_qpel8_hv_lowpass_lsx(dst, src, stride, stride); +} + +void ff_avg_h264_qpel8_mc32_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[128]; + uint8_t *const halfHV = temp; + uint8_t *const halfH = temp + 64; + + put_h264_qpel8_hv_lowpass_lsx(halfHV, src, 8, stride); + put_h264_qpel8_v_lowpass_lsx(halfH, (uint8_t*)src + 1, 8, stride); + avg_pixels8_l2_8_lsx(dst, halfH, halfHV, stride, 8); +} + +void ff_avg_h264_qpel8_mc13_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t halfH[64]; + uint8_t halfV[64]; + + put_h264_qpel8_h_lowpass_lsx(halfH, src + stride, 8, stride); + put_h264_qpel8_v_lowpass_lsx(halfV, (uint8_t*)src, 8, stride); + avg_pixels8_l2_8_lsx(dst, halfH, halfV, stride, 8); +} + +void ff_avg_h264_qpel8_mc23_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[128]; + uint8_t *const halfH = temp; + uint8_t *const halfHV = temp + 64; + + put_h264_qpel8_h_lowpass_lsx(halfH, src + stride, 8, stride); + put_h264_qpel8_hv_lowpass_lsx(halfHV, src, 8, stride); + avg_pixels8_l2_8_lsx(dst, halfH, halfHV, stride, 8); +} + +void ff_avg_h264_qpel8_mc33_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t halfH[64]; + uint8_t halfV[64]; + + put_h264_qpel8_h_lowpass_lsx(halfH, src + stride, 8, stride); + put_h264_qpel8_v_lowpass_lsx(halfV, (uint8_t*)src + 1, 8, stride); + avg_pixels8_l2_8_lsx(dst, halfH, halfV, stride, 8); +} diff --git a/libavcodec/loongarch/loongson_asm.S b/libavcodec/loongarch/loongson_asm.S new file mode 100644 index 00000000000..0a649f51c7f --- /dev/null +++ b/libavcodec/loongarch/loongson_asm.S @@ -0,0 +1,945 @@ +/* + * Loongson asm helper. + * + * Copyright (c) 2022 Loongson Technology Corporation Limited + * Contributed by Gu Xiwei(guxiwei-hf@loongson.cn) + * Shiyou Yin(yinshiyou-hf@loongson.cn) + * + * 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 + */ + +/** + * MAJOR version: Macro usage changes. + * MINOR version: Add new functions, or bug fixes. + * MICRO version: Comment changes or implementation changes. + */ +#define LML_VERSION_MAJOR 0 +#define LML_VERSION_MINOR 2 +#define LML_VERSION_MICRO 0 + +/* + *============================================================================ + * macros for specific projetc, set them as needed. + * Following LoongML macros for your reference. + *============================================================================ + */ +#define ASM_PREF +#define DEFAULT_ALIGN 5 + +.macro function name, align=DEFAULT_ALIGN +.macro endfunc + jirl $r0, $r1, 0x0 + .size ASM_PREF\name, . - ASM_PREF\name + .purgem endfunc +.endm +.text ; +.align \align ; +.globl ASM_PREF\name ; +.type ASM_PREF\name, @function ; +ASM_PREF\name: ; +.endm + +/** + * Attention: If align is not zero, the macro will use + * t7 until the end of function + */ +.macro alloc_stack size, align=0 +.if \align + .macro clean_stack + add.d sp, sp, t7 + .endm + addi.d sp, sp, - \size + andi.d t7, sp, \align - 1 + sub.d sp, sp, t7 + addi.d t7, t7, \size +.else + .macro clean_stack + addi.d sp, sp, \size + .endm + addi.d sp, sp, - \size +.endif +.endm + +.macro const name, align=DEFAULT_ALIGN + .macro endconst + .size \name, . - \name + .purgem endconst + .endm +.section .rodata +.align \align +\name: +.endm + +/* + *============================================================================ + * LoongArch register alias + *============================================================================ + */ + +#define a0 $a0 +#define a1 $a1 +#define a2 $a2 +#define a3 $a3 +#define a4 $a4 +#define a5 $a5 +#define a6 $a6 +#define a7 $a7 + +#define t0 $t0 +#define t1 $t1 +#define t2 $t2 +#define t3 $t3 +#define t4 $t4 +#define t5 $t5 +#define t6 $t6 +#define t7 $t7 +#define t8 $t8 + +#define s0 $s0 +#define s1 $s1 +#define s2 $s2 +#define s3 $s3 +#define s4 $s4 +#define s5 $s5 +#define s6 $s6 +#define s7 $s7 +#define s8 $s8 + +#define zero $zero +#define sp $sp +#define ra $ra + +#define f0 $f0 +#define f1 $f1 +#define f2 $f2 +#define f3 $f3 +#define f4 $f4 +#define f5 $f5 +#define f6 $f6 +#define f7 $f7 +#define f8 $f8 +#define f9 $f9 +#define f10 $f10 +#define f11 $f11 +#define f12 $f12 +#define f13 $f13 +#define f14 $f14 +#define f15 $f15 +#define f16 $f16 +#define f17 $f17 +#define f18 $f18 +#define f19 $f19 +#define f20 $f20 +#define f21 $f21 +#define f22 $f22 +#define f23 $f23 +#define f24 $f24 +#define f25 $f25 +#define f26 $f26 +#define f27 $f27 +#define f28 $f28 +#define f29 $f29 +#define f30 $f30 +#define f31 $f31 + +#define vr0 $vr0 +#define vr1 $vr1 +#define vr2 $vr2 +#define vr3 $vr3 +#define vr4 $vr4 +#define vr5 $vr5 +#define vr6 $vr6 +#define vr7 $vr7 +#define vr8 $vr8 +#define vr9 $vr9 +#define vr10 $vr10 +#define vr11 $vr11 +#define vr12 $vr12 +#define vr13 $vr13 +#define vr14 $vr14 +#define vr15 $vr15 +#define vr16 $vr16 +#define vr17 $vr17 +#define vr18 $vr18 +#define vr19 $vr19 +#define vr20 $vr20 +#define vr21 $vr21 +#define vr22 $vr22 +#define vr23 $vr23 +#define vr24 $vr24 +#define vr25 $vr25 +#define vr26 $vr26 +#define vr27 $vr27 +#define vr28 $vr28 +#define vr29 $vr29 +#define vr30 $vr30 +#define vr31 $vr31 + +#define xr0 $xr0 +#define xr1 $xr1 +#define xr2 $xr2 +#define xr3 $xr3 +#define xr4 $xr4 +#define xr5 $xr5 +#define xr6 $xr6 +#define xr7 $xr7 +#define xr8 $xr8 +#define xr9 $xr9 +#define xr10 $xr10 +#define xr11 $xr11 +#define xr12 $xr12 +#define xr13 $xr13 +#define xr14 $xr14 +#define xr15 $xr15 +#define xr16 $xr16 +#define xr17 $xr17 +#define xr18 $xr18 +#define xr19 $xr19 +#define xr20 $xr20 +#define xr21 $xr21 +#define xr22 $xr22 +#define xr23 $xr23 +#define xr24 $xr24 +#define xr25 $xr25 +#define xr26 $xr26 +#define xr27 $xr27 +#define xr28 $xr28 +#define xr29 $xr29 +#define xr30 $xr30 +#define xr31 $xr31 + +/* + *============================================================================ + * LSX/LASX synthesize instructions + *============================================================================ + */ + +/* + * Description : Dot product of byte vector elements + * Arguments : Inputs - vj, vk + * Outputs - vd + * Return Type - halfword + */ +.macro vdp2.h.bu vd, vj, vk + vmulwev.h.bu \vd, \vj, \vk + vmaddwod.h.bu \vd, \vj, \vk +.endm + +.macro vdp2.h.bu.b vd, vj, vk + vmulwev.h.bu.b \vd, \vj, \vk + vmaddwod.h.bu.b \vd, \vj, \vk +.endm + +.macro vdp2.w.h vd, vj, vk + vmulwev.w.h \vd, \vj, \vk + vmaddwod.w.h \vd, \vj, \vk +.endm + +.macro xvdp2.h.bu xd, xj, xk + xvmulwev.h.bu \xd, \xj, \xk + xvmaddwod.h.bu \xd, \xj, \xk +.endm + +.macro xvdp2.h.bu.b xd, xj, xk + xvmulwev.h.bu.b \xd, \xj, \xk + xvmaddwod.h.bu.b \xd, \xj, \xk +.endm + +.macro xvdp2.w.h xd, xj, xk + xvmulwev.w.h \xd, \xj, \xk + xvmaddwod.w.h \xd, \xj, \xk +.endm + +/* + * Description : Dot product & addition of halfword vector elements + * Arguments : Inputs - vj, vk + * Outputs - vd + * Return Type - twice size of input + */ +.macro vdp2add.h.bu vd, vj, vk + vmaddwev.h.bu \vd, \vj, \vk + vmaddwod.h.bu \vd, \vj, \vk +.endm + +.macro vdp2add.h.bu.b vd, vj, vk + vmaddwev.h.bu.b \vd, \vj, \vk + vmaddwod.h.bu.b \vd, \vj, \vk +.endm + +.macro vdp2add.w.h vd, vj, vk + vmaddwev.w.h \vd, \vj, \vk + vmaddwod.w.h \vd, \vj, \vk +.endm + +.macro xvdp2add.h.bu.b xd, xj, xk + xvmaddwev.h.bu.b \xd, \xj, \xk + xvmaddwod.h.bu.b \xd, \xj, \xk +.endm + +.macro xvdp2add.w.h xd, xj, xk + xvmaddwev.w.h \xd, \xj, \xk + xvmaddwod.w.h \xd, \xj, \xk +.endm + +/* + * Description : Range each element of vector + * clip: vj > vk ? vj : vk && vj < va ? vj : va + * clip255: vj < 255 ? vj : 255 && vj > 0 ? vj : 0 + */ +.macro vclip.h vd, vj, vk, va + vmax.h \vd, \vj, \vk + vmin.h \vd, \vd, \va +.endm + +.macro vclip255.w vd, vj + vmaxi.w \vd, \vj, 0 + vsat.wu \vd, \vd, 7 +.endm + +.macro vclip255.h vd, vj + vmaxi.h \vd, \vj, 0 + vsat.hu \vd, \vd, 7 +.endm + +.macro xvclip.h xd, xj, xk, xa + xvmax.h \xd, \xj, \xk + xvmin.h \xd, \xd, \xa +.endm + +.macro xvclip255.h xd, xj + xvmaxi.h \xd, \xj, 0 + xvsat.hu \xd, \xd, 7 +.endm + +.macro xvclip255.w xd, xj + xvmaxi.w \xd, \xj, 0 + xvsat.wu \xd, \xd, 7 +.endm + +/* + * Description : Store elements of vector + * vd : Data vector to be stroed + * rk : Address of data storage + * ra : Offset of address + * si : Index of data in vd + */ +.macro vstelmx.b vd, rk, ra, si + add.d \rk, \rk, \ra + vstelm.b \vd, \rk, 0, \si +.endm + +.macro vstelmx.h vd, rk, ra, si + add.d \rk, \rk, \ra + vstelm.h \vd, \rk, 0, \si +.endm + +.macro vstelmx.w vd, rk, ra, si + add.d \rk, \rk, \ra + vstelm.w \vd, \rk, 0, \si +.endm + +.macro vstelmx.d vd, rk, ra, si + add.d \rk, \rk, \ra + vstelm.d \vd, \rk, 0, \si +.endm + +.macro vmov xd, xj + vor.v \xd, \xj, \xj +.endm + +.macro xmov xd, xj + xvor.v \xd, \xj, \xj +.endm + +.macro xvstelmx.d xd, rk, ra, si + add.d \rk, \rk, \ra + xvstelm.d \xd, \rk, 0, \si +.endm + +/* + *============================================================================ + * LSX/LASX custom macros + *============================================================================ + */ + +/* + * Load 4 float, double, V128, v256 elements with stride. + */ +.macro FLDS_LOADX_4 src, stride, stride2, stride3, out0, out1, out2, out3 + fld.s \out0, \src, 0 + fldx.s \out1, \src, \stride + fldx.s \out2, \src, \stride2 + fldx.s \out3, \src, \stride3 +.endm + +.macro FLDD_LOADX_4 src, stride, stride2, stride3, out0, out1, out2, out3 + fld.d \out0, \src, 0 + fldx.d \out1, \src, \stride + fldx.d \out2, \src, \stride2 + fldx.d \out3, \src, \stride3 +.endm + +.macro LSX_LOADX_4 src, stride, stride2, stride3, out0, out1, out2, out3 + vld \out0, \src, 0 + vldx \out1, \src, \stride + vldx \out2, \src, \stride2 + vldx \out3, \src, \stride3 +.endm + +.macro LASX_LOADX_4 src, stride, stride2, stride3, out0, out1, out2, out3 + xvld \out0, \src, 0 + xvldx \out1, \src, \stride + xvldx \out2, \src, \stride2 + xvldx \out3, \src, \stride3 +.endm + +/* + * Description : Transpose 4x4 block with half-word elements in vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1, out2, out3 + */ +.macro LSX_TRANSPOSE4x4_H in0, in1, in2, in3, out0, out1, out2, out3, \ + tmp0, tmp1 + vilvl.h \tmp0, \in1, \in0 + vilvl.h \tmp1, \in3, \in2 + vilvl.w \out0, \tmp1, \tmp0 + vilvh.w \out2, \tmp1, \tmp0 + vilvh.d \out1, \out0, \out0 + vilvh.d \out3, \out0, \out2 +.endm + +/* + * Description : Transpose 4x4 block with word elements in vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1, out2, out3 + * Details : + * Example : + * 1, 2, 3, 4 1, 5, 9,13 + * 5, 6, 7, 8 to 2, 6,10,14 + * 9,10,11,12 =====> 3, 7,11,15 + * 13,14,15,16 4, 8,12,16 + */ +.macro LSX_TRANSPOSE4x4_W _in0, _in1, _in2, _in3, _out0, _out1, _out2, _out3, \ + _tmp0, _tmp1 + + vilvl.w \_tmp0, \_in1, \_in0 + vilvh.w \_out1, \_in1, \_in0 + vilvl.w \_tmp1, \_in3, \_in2 + vilvh.w \_out3, \_in3, \_in2 + + vilvl.d \_out0, \_tmp1, \_tmp0 + vilvl.d \_out2, \_out3, \_out1 + vilvh.d \_out3, \_out3, \_out1 + vilvh.d \_out1, \_tmp1, \_tmp0 +.endm + +/* + * Description : Transpose 8x8 block with half-word elements in vectors + * Arguments : Inputs - in0, in1, in2, in3, in4, in5, in6, in7 + * Outputs - out0, out1, out2, out3, out4, out5, out6, out7 + */ +.macro LSX_TRANSPOSE8x8_H in0, in1, in2, in3, in4, in5, in6, in7, out0, out1, \ + out2, out3, out4, out5, out6, out7, tmp0, tmp1, tmp2, \ + tmp3, tmp4, tmp5, tmp6, tmp7 + vilvl.h \tmp0, \in6, \in4 + vilvl.h \tmp1, \in7, \in5 + vilvl.h \tmp2, \in2, \in0 + vilvl.h \tmp3, \in3, \in1 + + vilvl.h \tmp4, \tmp1, \tmp0 + vilvh.h \tmp5, \tmp1, \tmp0 + vilvl.h \tmp6, \tmp3, \tmp2 + vilvh.h \tmp7, \tmp3, \tmp2 + + vilvh.h \tmp0, \in6, \in4 + vilvh.h \tmp1, \in7, \in5 + vilvh.h \tmp2, \in2, \in0 + vilvh.h \tmp3, \in3, \in1 + + vpickev.d \out0, \tmp4, \tmp6 + vpickod.d \out1, \tmp4, \tmp6 + vpickev.d \out2, \tmp5, \tmp7 + vpickod.d \out3, \tmp5, \tmp7 + + vilvl.h \tmp4, \tmp1, \tmp0 + vilvh.h \tmp5, \tmp1, \tmp0 + vilvl.h \tmp6, \tmp3, \tmp2 + vilvh.h \tmp7, \tmp3, \tmp2 + + vpickev.d \out4, \tmp4, \tmp6 + vpickod.d \out5, \tmp4, \tmp6 + vpickev.d \out6, \tmp5, \tmp7 + vpickod.d \out7, \tmp5, \tmp7 +.endm + +/* + * Description : Transpose 16x8 block with byte elements in vectors + * Arguments : Inputs - in0, in1, in2, in3, in4, in5, in6, in7 + * Outputs - out0, out1, out2, out3, out4, out5, out6, out7 + */ +.macro LASX_TRANSPOSE16X8_B in0, in1, in2, in3, in4, in5, in6, in7, \ + in8, in9, in10, in11, in12, in13, in14, in15, \ + out0, out1, out2, out3, out4, out5, out6, out7,\ + tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7 + xvilvl.b \tmp0, \in2, \in0 + xvilvl.b \tmp1, \in3, \in1 + xvilvl.b \tmp2, \in6, \in4 + xvilvl.b \tmp3, \in7, \in5 + xvilvl.b \tmp4, \in10, \in8 + xvilvl.b \tmp5, \in11, \in9 + xvilvl.b \tmp6, \in14, \in12 + xvilvl.b \tmp7, \in15, \in13 + xvilvl.b \out0, \tmp1, \tmp0 + xvilvh.b \out1, \tmp1, \tmp0 + xvilvl.b \out2, \tmp3, \tmp2 + xvilvh.b \out3, \tmp3, \tmp2 + xvilvl.b \out4, \tmp5, \tmp4 + xvilvh.b \out5, \tmp5, \tmp4 + xvilvl.b \out6, \tmp7, \tmp6 + xvilvh.b \out7, \tmp7, \tmp6 + xvilvl.w \tmp0, \out2, \out0 + xvilvh.w \tmp2, \out2, \out0 + xvilvl.w \tmp4, \out3, \out1 + xvilvh.w \tmp6, \out3, \out1 + xvilvl.w \tmp1, \out6, \out4 + xvilvh.w \tmp3, \out6, \out4 + xvilvl.w \tmp5, \out7, \out5 + xvilvh.w \tmp7, \out7, \out5 + xvilvl.d \out0, \tmp1, \tmp0 + xvilvh.d \out1, \tmp1, \tmp0 + xvilvl.d \out2, \tmp3, \tmp2 + xvilvh.d \out3, \tmp3, \tmp2 + xvilvl.d \out4, \tmp5, \tmp4 + xvilvh.d \out5, \tmp5, \tmp4 + xvilvl.d \out6, \tmp7, \tmp6 + xvilvh.d \out7, \tmp7, \tmp6 +.endm + +/* + * Description : Transpose 16x8 block with byte elements in vectors + * Arguments : Inputs - in0, in1, in2, in3, in4, in5, in6, in7 + * Outputs - out0, out1, out2, out3, out4, out5, out6, out7 + */ +.macro LSX_TRANSPOSE16X8_B in0, in1, in2, in3, in4, in5, in6, in7, \ + in8, in9, in10, in11, in12, in13, in14, in15, \ + out0, out1, out2, out3, out4, out5, out6, out7,\ + tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7 + vilvl.b \tmp0, \in2, \in0 + vilvl.b \tmp1, \in3, \in1 + vilvl.b \tmp2, \in6, \in4 + vilvl.b \tmp3, \in7, \in5 + vilvl.b \tmp4, \in10, \in8 + vilvl.b \tmp5, \in11, \in9 + vilvl.b \tmp6, \in14, \in12 + vilvl.b \tmp7, \in15, \in13 + + vilvl.b \out0, \tmp1, \tmp0 + vilvh.b \out1, \tmp1, \tmp0 + vilvl.b \out2, \tmp3, \tmp2 + vilvh.b \out3, \tmp3, \tmp2 + vilvl.b \out4, \tmp5, \tmp4 + vilvh.b \out5, \tmp5, \tmp4 + vilvl.b \out6, \tmp7, \tmp6 + vilvh.b \out7, \tmp7, \tmp6 + vilvl.w \tmp0, \out2, \out0 + vilvh.w \tmp2, \out2, \out0 + vilvl.w \tmp4, \out3, \out1 + vilvh.w \tmp6, \out3, \out1 + vilvl.w \tmp1, \out6, \out4 + vilvh.w \tmp3, \out6, \out4 + vilvl.w \tmp5, \out7, \out5 + vilvh.w \tmp7, \out7, \out5 + vilvl.d \out0, \tmp1, \tmp0 + vilvh.d \out1, \tmp1, \tmp0 + vilvl.d \out2, \tmp3, \tmp2 + vilvh.d \out3, \tmp3, \tmp2 + vilvl.d \out4, \tmp5, \tmp4 + vilvh.d \out5, \tmp5, \tmp4 + vilvl.d \out6, \tmp7, \tmp6 + vilvh.d \out7, \tmp7, \tmp6 +.endm + +/* + * Description : Transpose 4x4 block with half-word elements in vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1, out2, out3 + */ +.macro LASX_TRANSPOSE4x4_H in0, in1, in2, in3, out0, out1, out2, out3, \ + tmp0, tmp1 + xvilvl.h \tmp0, \in1, \in0 + xvilvl.h \tmp1, \in3, \in2 + xvilvl.w \out0, \tmp1, \tmp0 + xvilvh.w \out2, \tmp1, \tmp0 + xvilvh.d \out1, \out0, \out0 + xvilvh.d \out3, \out0, \out2 +.endm + +/* + * Description : Transpose 4x8 block with half-word elements in vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1, out2, out3 + */ +.macro LASX_TRANSPOSE4x8_H in0, in1, in2, in3, out0, out1, out2, out3, \ + tmp0, tmp1 + xvilvl.h \tmp0, \in2, \in0 + xvilvl.h \tmp1, \in3, \in1 + xvilvl.h \out2, \tmp1, \tmp0 + xvilvh.h \out3, \tmp1, \tmp0 + + xvilvl.d \out0, \out2, \out2 + xvilvh.d \out1, \out2, \out2 + xvilvl.d \out2, \out3, \out3 + xvilvh.d \out3, \out3, \out3 +.endm + +/* + * Description : Transpose 8x8 block with half-word elements in vectors + * Arguments : Inputs - in0, in1, in2, in3, in4, in5, in6, in7 + * Outputs - out0, out1, out2, out3, out4, out5, out6, out7 + */ +.macro LASX_TRANSPOSE8x8_H in0, in1, in2, in3, in4, in5, in6, in7, \ + out0, out1, out2, out3, out4, out5, out6, out7, \ + tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7 + xvilvl.h \tmp0, \in6, \in4 + xvilvl.h \tmp1, \in7, \in5 + xvilvl.h \tmp2, \in2, \in0 + xvilvl.h \tmp3, \in3, \in1 + + xvilvl.h \tmp4, \tmp1, \tmp0 + xvilvh.h \tmp5, \tmp1, \tmp0 + xvilvl.h \tmp6, \tmp3, \tmp2 + xvilvh.h \tmp7, \tmp3, \tmp2 + + xvilvh.h \tmp0, \in6, \in4 + xvilvh.h \tmp1, \in7, \in5 + xvilvh.h \tmp2, \in2, \in0 + xvilvh.h \tmp3, \in3, \in1 + + xvpickev.d \out0, \tmp4, \tmp6 + xvpickod.d \out1, \tmp4, \tmp6 + xvpickev.d \out2, \tmp5, \tmp7 + xvpickod.d \out3, \tmp5, \tmp7 + + xvilvl.h \tmp4, \tmp1, \tmp0 + xvilvh.h \tmp5, \tmp1, \tmp0 + xvilvl.h \tmp6, \tmp3, \tmp2 + xvilvh.h \tmp7, \tmp3, \tmp2 + + xvpickev.d \out4, \tmp4, \tmp6 + xvpickod.d \out5, \tmp4, \tmp6 + xvpickev.d \out6, \tmp5, \tmp7 + xvpickod.d \out7, \tmp5, \tmp7 +.endm + +/* + * Description : Transpose 2x4x4 block with half-word elements in vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1, out2, out3 + */ +.macro LASX_TRANSPOSE2x4x4_H in0, in1, in2, in3, out0, out1, out2, out3, \ + tmp0, tmp1, tmp2 + xvilvh.h \tmp1, \in0, \in1 + xvilvl.h \out1, \in0, \in1 + xvilvh.h \tmp0, \in2, \in3 + xvilvl.h \out3, \in2, \in3 + + xvilvh.w \tmp2, \out3, \out1 + xvilvl.w \out3, \out3, \out1 + + xvilvl.w \out2, \tmp0, \tmp1 + xvilvh.w \tmp1, \tmp0, \tmp1 + + xvilvh.d \out0, \out2, \out3 + xvilvl.d \out2, \out2, \out3 + xvilvh.d \out1, \tmp1, \tmp2 + xvilvl.d \out3, \tmp1, \tmp2 +.endm + +/* + * Description : Transpose 4x4 block with word elements in vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1, out2, out3 + * Details : + * Example : + * 1, 2, 3, 4, 1, 2, 3, 4 1,5, 9,13, 1,5, 9,13 + * 5, 6, 7, 8, 5, 6, 7, 8 to 2,6,10,14, 2,6,10,14 + * 9,10,11,12, 9,10,11,12 =====> 3,7,11,15, 3,7,11,15 + * 13,14,15,16, 13,14,15,16 4,8,12,16, 4,8,12,16 + */ +.macro LASX_TRANSPOSE4x4_W _in0, _in1, _in2, _in3, _out0, _out1, _out2, _out3, \ + _tmp0, _tmp1 + + xvilvl.w \_tmp0, \_in1, \_in0 + xvilvh.w \_out1, \_in1, \_in0 + xvilvl.w \_tmp1, \_in3, \_in2 + xvilvh.w \_out3, \_in3, \_in2 + + xvilvl.d \_out0, \_tmp1, \_tmp0 + xvilvl.d \_out2, \_out3, \_out1 + xvilvh.d \_out3, \_out3, \_out1 + xvilvh.d \_out1, \_tmp1, \_tmp0 +.endm + +/* + * Description : Transpose 8x8 block with word elements in vectors + * Arguments : Inputs - _in0, _in1, _in2, _in3, _in4, _in5, _in6, _in7 + * Outputs - _out0, _out1, _out2, _out3, _out4, _out5, _out6, + * _out7 + * Example : LASX_TRANSPOSE8x8_W + * _in0 : 1,2,3,4,5,6,7,8 + * _in1 : 2,2,3,4,5,6,7,8 + * _in2 : 3,2,3,4,5,6,7,8 + * _in3 : 4,2,3,4,5,6,7,8 + * _in4 : 5,2,3,4,5,6,7,8 + * _in5 : 6,2,3,4,5,6,7,8 + * _in6 : 7,2,3,4,5,6,7,8 + * _in7 : 8,2,3,4,5,6,7,8 + * + * _out0 : 1,2,3,4,5,6,7,8 + * _out1 : 2,2,2,2,2,2,2,2 + * _out2 : 3,3,3,3,3,3,3,3 + * _out3 : 4,4,4,4,4,4,4,4 + * _out4 : 5,5,5,5,5,5,5,5 + * _out5 : 6,6,6,6,6,6,6,6 + * _out6 : 7,7,7,7,7,7,7,7 + * _out7 : 8,8,8,8,8,8,8,8 + */ +.macro LASX_TRANSPOSE8x8_W _in0, _in1, _in2, _in3, _in4, _in5, _in6, _in7,\ + _out0, _out1, _out2, _out3, _out4, _out5, _out6, _out7,\ + _tmp0, _tmp1, _tmp2, _tmp3 + xvilvl.w \_tmp0, \_in2, \_in0 + xvilvl.w \_tmp1, \_in3, \_in1 + xvilvh.w \_tmp2, \_in2, \_in0 + xvilvh.w \_tmp3, \_in3, \_in1 + xvilvl.w \_out0, \_tmp1, \_tmp0 + xvilvh.w \_out1, \_tmp1, \_tmp0 + xvilvl.w \_out2, \_tmp3, \_tmp2 + xvilvh.w \_out3, \_tmp3, \_tmp2 + + xvilvl.w \_tmp0, \_in6, \_in4 + xvilvl.w \_tmp1, \_in7, \_in5 + xvilvh.w \_tmp2, \_in6, \_in4 + xvilvh.w \_tmp3, \_in7, \_in5 + xvilvl.w \_out4, \_tmp1, \_tmp0 + xvilvh.w \_out5, \_tmp1, \_tmp0 + xvilvl.w \_out6, \_tmp3, \_tmp2 + xvilvh.w \_out7, \_tmp3, \_tmp2 + + xmov \_tmp0, \_out0 + xmov \_tmp1, \_out1 + xmov \_tmp2, \_out2 + xmov \_tmp3, \_out3 + xvpermi.q \_out0, \_out4, 0x02 + xvpermi.q \_out1, \_out5, 0x02 + xvpermi.q \_out2, \_out6, 0x02 + xvpermi.q \_out3, \_out7, 0x02 + xvpermi.q \_out4, \_tmp0, 0x31 + xvpermi.q \_out5, \_tmp1, 0x31 + xvpermi.q \_out6, \_tmp2, 0x31 + xvpermi.q \_out7, \_tmp3, 0x31 +.endm + +/* + * Description : Transpose 4x4 block with double-word elements in vectors + * Arguments : Inputs - _in0, _in1, _in2, _in3 + * Outputs - _out0, _out1, _out2, _out3 + * Example : LASX_TRANSPOSE4x4_D + * _in0 : 1,2,3,4 + * _in1 : 1,2,3,4 + * _in2 : 1,2,3,4 + * _in3 : 1,2,3,4 + * + * _out0 : 1,1,1,1 + * _out1 : 2,2,2,2 + * _out2 : 3,3,3,3 + * _out3 : 4,4,4,4 + */ +.macro LASX_TRANSPOSE4x4_D _in0, _in1, _in2, _in3, _out0, _out1, _out2, _out3, \ + _tmp0, _tmp1 + xvilvl.d \_tmp0, \_in1, \_in0 + xvilvh.d \_out1, \_in1, \_in0 + xvilvh.d \_tmp1, \_in3, \_in2 + xvilvl.d \_out2, \_in3, \_in2 + + xvor.v \_out0, \_tmp0, \_tmp0 + xvor.v \_out3, \_tmp1, \_tmp1 + + xvpermi.q \_out0, \_out2, 0x02 + xvpermi.q \_out2, \_tmp0, 0x31 + xvpermi.q \_out3, \_out1, 0x31 + xvpermi.q \_out1, \_tmp1, 0x02 +.endm + +/* + * Description : Butterfly of 4 input vectors + * Arguments : Inputs - _in0, _in1, _in2, _in3 + * Outputs - _out0, _out1, _out2, _out3 + * Details : Butterfly operation + * Example : LSX_BUTTERFLY_4 + * _out0 = _in0 + _in3; + * _out1 = _in1 + _in2; + * _out2 = _in1 - _in2; + * _out3 = _in0 - _in3; + */ +.macro LSX_BUTTERFLY_4_B _in0, _in1, _in2, _in3, _out0, _out1, _out2, _out3 + vadd.b \_out0, \_in0, \_in3 + vadd.b \_out1, \_in1, \_in2 + vsub.b \_out2, \_in1, \_in2 + vsub.b \_out3, \_in0, \_in3 +.endm +.macro LSX_BUTTERFLY_4_H _in0, _in1, _in2, _in3, _out0, _out1, _out2, _out3 + vadd.h \_out0, \_in0, \_in3 + vadd.h \_out1, \_in1, \_in2 + vsub.h \_out2, \_in1, \_in2 + vsub.h \_out3, \_in0, \_in3 +.endm +.macro LSX_BUTTERFLY_4_W _in0, _in1, _in2, _in3, _out0, _out1, _out2, _out3 + vadd.w \_out0, \_in0, \_in3 + vadd.w \_out1, \_in1, \_in2 + vsub.w \_out2, \_in1, \_in2 + vsub.w \_out3, \_in0, \_in3 +.endm +.macro LSX_BUTTERFLY_4_D _in0, _in1, _in2, _in3, _out0, _out1, _out2, _out3 + vadd.d \_out0, \_in0, \_in3 + vadd.d \_out1, \_in1, \_in2 + vsub.d \_out2, \_in1, \_in2 + vsub.d \_out3, \_in0, \_in3 +.endm + +.macro LASX_BUTTERFLY_4_B _in0, _in1, _in2, _in3, _out0, _out1, _out2, _out3 + xvadd.b \_out0, \_in0, \_in3 + xvadd.b \_out1, \_in1, \_in2 + xvsub.b \_out2, \_in1, \_in2 + xvsub.b \_out3, \_in0, \_in3 +.endm +.macro LASX_BUTTERFLY_4_H _in0, _in1, _in2, _in3, _out0, _out1, _out2, _out3 + xvadd.h \_out0, \_in0, \_in3 + xvadd.h \_out1, \_in1, \_in2 + xvsub.h \_out2, \_in1, \_in2 + xvsub.h \_out3, \_in0, \_in3 +.endm +.macro LASX_BUTTERFLY_4_W _in0, _in1, _in2, _in3, _out0, _out1, _out2, _out3 + xvadd.w \_out0, \_in0, \_in3 + xvadd.w \_out1, \_in1, \_in2 + xvsub.w \_out2, \_in1, \_in2 + xvsub.w \_out3, \_in0, \_in3 +.endm +.macro LASX_BUTTERFLY_4_D _in0, _in1, _in2, _in3, _out0, _out1, _out2, _out3 + xvadd.d \_out0, \_in0, \_in3 + xvadd.d \_out1, \_in1, \_in2 + xvsub.d \_out2, \_in1, \_in2 + xvsub.d \_out3, \_in0, \_in3 +.endm + +/* + * Description : Butterfly of 8 input vectors + * Arguments : Inputs - _in0, _in1, _in2, _in3, ~ + * Outputs - _out0, _out1, _out2, _out3, ~ + * Details : Butterfly operation + * Example : LASX_BUTTERFLY_8 + * _out0 = _in0 + _in7; + * _out1 = _in1 + _in6; + * _out2 = _in2 + _in5; + * _out3 = _in3 + _in4; + * _out4 = _in3 - _in4; + * _out5 = _in2 - _in5; + * _out6 = _in1 - _in6; + * _out7 = _in0 - _in7; + */ +.macro LSX_BUTTERFLY_8_B _in0, _in1, _in2, _in3, _in4, _in5, _in6, _in7, \ + _out0, _out1, _out2, _out3, _out4, _out5, _out6, _out7 + vadd.b \_out0, \_in0, \_in7 + vadd.b \_out1, \_in1, \_in6 + vadd.b \_out2, \_in2, \_in5 + vadd.b \_out3, \_in3, \_in4 + vsub.b \_out4, \_in3, \_in4 + vsub.b \_out5, \_in2, \_in5 + vsub.b \_out6, \_in1, \_in6 + vsub.b \_out7, \_in0, \_in7 +.endm + +.macro LSX_BUTTERFLY_8_H _in0, _in1, _in2, _in3, _in4, _in5, _in6, _in7, \ + _out0, _out1, _out2, _out3, _out4, _out5, _out6, _out7 + vadd.h \_out0, \_in0, \_in7 + vadd.h \_out1, \_in1, \_in6 + vadd.h \_out2, \_in2, \_in5 + vadd.h \_out3, \_in3, \_in4 + vsub.h \_out4, \_in3, \_in4 + vsub.h \_out5, \_in2, \_in5 + vsub.h \_out6, \_in1, \_in6 + vsub.h \_out7, \_in0, \_in7 +.endm + +.macro LSX_BUTTERFLY_8_W _in0, _in1, _in2, _in3, _in4, _in5, _in6, _in7, \ + _out0, _out1, _out2, _out3, _out4, _out5, _out6, _out7 + vadd.w \_out0, \_in0, \_in7 + vadd.w \_out1, \_in1, \_in6 + vadd.w \_out2, \_in2, \_in5 + vadd.w \_out3, \_in3, \_in4 + vsub.w \_out4, \_in3, \_in4 + vsub.w \_out5, \_in2, \_in5 + vsub.w \_out6, \_in1, \_in6 + vsub.w \_out7, \_in0, \_in7 +.endm + +.macro LSX_BUTTERFLY_8_D _in0, _in1, _in2, _in3, _in4, _in5, _in6, _in7, \ + _out0, _out1, _out2, _out3, _out4, _out5, _out6, _out7 + vadd.d \_out0, \_in0, \_in7 + vadd.d \_out1, \_in1, \_in6 + vadd.d \_out2, \_in2, \_in5 + vadd.d \_out3, \_in3, \_in4 + vsub.d \_out4, \_in3, \_in4 + vsub.d \_out5, \_in2, \_in5 + vsub.d \_out6, \_in1, \_in6 + vsub.d \_out7, \_in0, \_in7 +.endm + +.macro LASX_BUTTERFLY_8_B _in0, _in1, _in2, _in3, _in4, _in5, _in6, _in7, \ + _out0, _out1, _out2, _out3, _out4, _out5, _out6, _out7 + xvadd.b \_out0, \_in0, \_in7 + xvadd.b \_out1, \_in1, \_in6 + xvadd.b \_out2, \_in2, \_in5 + xvadd.b \_out3, \_in3, \_in4 + xvsub.b \_out4, \_in3, \_in4 + xvsub.b \_out5, \_in2, \_in5 + xvsub.b \_out6, \_in1, \_in6 + xvsub.b \_out7, \_in0, \_in7 +.endm + +.macro LASX_BUTTERFLY_8_H _in0, _in1, _in2, _in3, _in4, _in5, _in6, _in7, \ + _out0, _out1, _out2, _out3, _out4, _out5, _out6, _out7 + xvadd.h \_out0, \_in0, \_in7 + xvadd.h \_out1, \_in1, \_in6 + xvadd.h \_out2, \_in2, \_in5 + xvadd.h \_out3, \_in3, \_in4 + xvsub.h \_out4, \_in3, \_in4 + xvsub.h \_out5, \_in2, \_in5 + xvsub.h \_out6, \_in1, \_in6 + xvsub.h \_out7, \_in0, \_in7 +.endm + +.macro LASX_BUTTERFLY_8_W _in0, _in1, _in2, _in3, _in4, _in5, _in6, _in7, \ + _out0, _out1, _out2, _out3, _out4, _out5, _out6, _out7 + xvadd.w \_out0, \_in0, \_in7 + xvadd.w \_out1, \_in1, \_in6 + xvadd.w \_out2, \_in2, \_in5 + xvadd.w \_out3, \_in3, \_in4 + xvsub.w \_out4, \_in3, \_in4 + xvsub.w \_out5, \_in2, \_in5 + xvsub.w \_out6, \_in1, \_in6 + xvsub.w \_out7, \_in0, \_in7 +.endm diff --git a/libavcodec/lscrdec.c b/libavcodec/lscrdec.c index 76a46751f07..415914bf0ac 100644 --- a/libavcodec/lscrdec.c +++ b/libavcodec/lscrdec.c @@ -154,10 +154,13 @@ static int decode_frame_lscr(AVCodecContext *avctx, AVFrame *rframe, size = bytestream2_get_le32(gb); - frame->key_frame = (nb_blocks == 1) && - (w == avctx->width) && - (h == avctx->height) && - (x == 0) && (y == 0); + if ((nb_blocks == 1) && + (w == avctx->width) && + (h == avctx->height) && + (x == 0) && (y == 0)) + frame->flags |= AV_FRAME_FLAG_KEY; + else + frame->flags &= ~AV_FRAME_FLAG_KEY; bytestream2_seek(gb, 2 + nb_blocks * 12 + offset, SEEK_SET); csize = bytestream2_get_be32(gb); @@ -199,7 +202,7 @@ static int decode_frame_lscr(AVCodecContext *avctx, AVFrame *rframe, } } - frame->pict_type = frame->key_frame ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; + frame->pict_type = (frame->flags & AV_FRAME_FLAG_KEY) ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; if ((ret = av_frame_ref(rframe, frame)) < 0) return ret; diff --git a/libavcodec/m101.c b/libavcodec/m101.c index 3def577b746..43a3c7bbe5b 100644 --- a/libavcodec/m101.c +++ b/libavcodec/m101.c @@ -67,15 +67,17 @@ static int m101_decode_frame(AVCodecContext *avctx, AVFrame *frame, if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) return ret; frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; - frame->interlaced_frame = ((avctx->extradata[3*4] & 3) != 3); - if (frame->interlaced_frame) - frame->top_field_first = avctx->extradata[3*4] & 1; + frame->flags |= AV_FRAME_FLAG_KEY; + if ((avctx->extradata[3*4] & 3) != 3) { + frame->flags |= AV_FRAME_FLAG_INTERLACED; + if (avctx->extradata[3*4] & 1) + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; + } for (y = 0; y < avctx->height; y++) { int src_y = y; - if (frame->interlaced_frame) - src_y = ((y&1)^frame->top_field_first) ? y/2 : (y/2 + avctx->height/2); + if (frame->flags & AV_FRAME_FLAG_INTERLACED) + src_y = ((y&1) ^ !!(frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)) ? y/2 : (y/2 + avctx->height/2); if (bits == 8) { uint8_t *line = frame->data[0] + y*frame->linesize[0]; memcpy(line, buf + src_y*stride, 2*avctx->width); diff --git a/libavcodec/magicyuv.c b/libavcodec/magicyuv.c index 62263409b1e..7898cd5be45 100644 --- a/libavcodec/magicyuv.c +++ b/libavcodec/magicyuv.c @@ -637,7 +637,7 @@ static int magy_decode_frame(AVCodecContext *avctx, AVFrame *p, return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; if ((ret = ff_thread_get_buffer(avctx, p, 0)) < 0) return ret; diff --git a/libavcodec/magicyuvenc.c b/libavcodec/magicyuvenc.c index 7f9ff72834f..082e2846c79 100644 --- a/libavcodec/magicyuvenc.c +++ b/libavcodec/magicyuvenc.c @@ -22,6 +22,7 @@ #include #include +#include "libavutil/cpu.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libavutil/qsort.h" @@ -63,9 +64,13 @@ typedef struct MagicYUVContext { int correlate; int hshift[4]; int vshift[4]; - uint8_t *slices[4]; - unsigned slice_pos[4]; + uint8_t **slices; + uint8_t **bitslices; + unsigned bitslice_size; + unsigned *slice_pos; + unsigned *slice_size; unsigned tables_size; + PTable *counts; uint8_t *decorrelate_buf[2]; HuffEntry he[4][256]; LLVidEncDSPContext llvidencdsp; @@ -150,7 +155,6 @@ static av_cold int magy_encode_init(AVCodecContext *avctx) { MagicYUVContext *s = avctx->priv_data; PutByteContext pb; - int i; switch (avctx->pix_fmt) { case AV_PIX_FMT_GBRP: @@ -201,14 +205,28 @@ static av_cold int magy_encode_init(AVCodecContext *avctx) s->planes = av_pix_fmt_count_planes(avctx->pix_fmt); - s->nb_slices = 1; + s->nb_slices = (avctx->slices <= 0) ? av_cpu_count() : avctx->slices; + s->nb_slices = FFMIN(s->nb_slices, avctx->height >> s->vshift[1]); + s->nb_slices = FFMAX(1, s->nb_slices); + s->slice_height = FFALIGN((avctx->height + s->nb_slices - 1) / s->nb_slices, 1 << s->vshift[1]); + s->slice_pos = av_calloc(s->nb_slices * s->planes, sizeof(*s->slice_pos)); + s->slice_size = av_calloc(s->nb_slices * s->planes, sizeof(*s->slice_size)); + s->slices = av_calloc(s->nb_slices * s->planes, sizeof(*s->slices)); + s->bitslices = av_calloc(s->nb_slices * s->planes, sizeof(*s->bitslices)); + s->counts = av_calloc(s->nb_slices * s->planes * 256, sizeof(*s->counts)); + if (!s->slices || !s->slice_pos || !s->counts || !s->slice_size) + return AVERROR(ENOMEM); - for (i = 0; i < s->planes; i++) { - s->slices[i] = av_malloc(avctx->width * (avctx->height + 2) + - AV_INPUT_BUFFER_PADDING_SIZE); - if (!s->slices[i]) { - av_log(avctx, AV_LOG_ERROR, "Cannot allocate temporary buffer.\n"); - return AVERROR(ENOMEM); + s->bitslice_size = avctx->width * (s->slice_height + 2) + AV_INPUT_BUFFER_PADDING_SIZE; + for (int n = 0; n < s->nb_slices; n++) { + for (int i = 0; i < s->planes; i++) { + s->bitslices[n * s->planes + i] = av_malloc(s->bitslice_size); + s->slices[n * s->planes + i] = av_malloc(avctx->width * (s->slice_height + 2) + + AV_INPUT_BUFFER_PADDING_SIZE); + if (!s->slices[n * s->planes + i] || !s->bitslices[n * s->planes + i]) { + av_log(avctx, AV_LOG_ERROR, "Cannot allocate temporary buffer.\n"); + return AVERROR(ENOMEM); + } } } @@ -263,15 +281,12 @@ static void calculate_codes(HuffEntry *he, uint16_t codes_count[33]) } } -static void count_usage(uint8_t *src, int width, +static void count_usage(const uint8_t *src, int width, int height, PTable *counts) { - int i, j; - - for (j = 0; j < height; j++) { - for (i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + for (int i = 0; i < width; i++) counts[src[i]].prob++; - } src += width; } } @@ -352,17 +367,35 @@ static void magy_huffman_compute_bits(PTable *prob_table, HuffEntry *distincts, } } -static int encode_table(AVCodecContext *avctx, uint8_t *dst, - int width, int height, - PutBitContext *pb, HuffEntry *he) +static int count_plane_slice(AVCodecContext *avctx, int n, int plane) { + MagicYUVContext *s = avctx->priv_data; + const uint8_t *dst = s->slices[n * s->planes + plane]; + PTable *counts = s->counts + 256 * (n * s->planes + plane); + + memset(counts, 0, sizeof(*counts) * 256); + + count_usage(dst, AV_CEIL_RSHIFT(avctx->width, s->hshift[plane]), + AV_CEIL_RSHIFT(s->slice_height, s->vshift[plane]), counts); + + return 0; +} + +static int encode_table(AVCodecContext *avctx, + PutBitContext *pb, HuffEntry *he, int plane) +{ + MagicYUVContext *s = avctx->priv_data; PTable counts[256] = { {0} }; uint16_t codes_counts[33] = { 0 }; - int i; - count_usage(dst, width, height, counts); + for (int n = 0; n < s->nb_slices; n++) { + PTable *slice_counts = s->counts + 256 * (n * s->planes + plane); + + for (int i = 0; i < 256; i++) + counts[i].prob = slice_counts[i].prob; + } - for (i = 0; i < 256; i++) { + for (int i = 0; i < 256; i++) { counts[i].prob++; counts[i].value = i; } @@ -371,7 +404,7 @@ static int encode_table(AVCodecContext *avctx, uint8_t *dst, calculate_codes(he, codes_counts); - for (i = 0; i < 256; i++) { + for (int i = 0; i < 256; i++) { put_bits(pb, 1, 0); put_bits(pb, 7, he[i].len); } @@ -379,8 +412,8 @@ static int encode_table(AVCodecContext *avctx, uint8_t *dst, return 0; } -static int encode_slice(uint8_t *src, uint8_t *dst, int dst_size, - int width, int height, HuffEntry *he, int prediction) +static int encode_plane_slice(uint8_t *src, uint8_t *dst, int dst_size, + int width, int height, HuffEntry *he, int prediction) { PutBitContext pb; int i, j; @@ -410,13 +443,94 @@ static int encode_slice(uint8_t *src, uint8_t *dst, int dst_size, return put_bytes_output(&pb); } +static int encode_slice(AVCodecContext *avctx, void *tdata, + int n, int threadnr) +{ + MagicYUVContext *s = avctx->priv_data; + const int slice_height = s->slice_height; + const int last_height = FFMIN(slice_height, avctx->height - n * slice_height); + const int height = (n < (s->nb_slices - 1)) ? slice_height : last_height; + PutByteContext pb; + + for (int i = 0; i < s->planes; i++) { + bytestream2_init_writer(&pb, s->bitslices[n + s->planes + i], + s->bitslice_size); + + s->slice_size[n * s->planes + i] = + encode_plane_slice(s->slices[n * s->planes + i], + s->bitslices[n * s->planes + i], + bytestream2_get_bytes_left_p(&pb), + AV_CEIL_RSHIFT(avctx->width, s->hshift[i]), + AV_CEIL_RSHIFT(height, s->vshift[i]), + s->he[i], s->frame_pred); + } + + return 0; +} + +static int predict_slice(AVCodecContext *avctx, void *tdata, + int n, int threadnr) +{ + const int aligned_width = FFALIGN(avctx->width, 16); + MagicYUVContext *s = avctx->priv_data; + const int slice_height = s->slice_height; + const int last_height = FFMIN(slice_height, avctx->height - n * slice_height); + const int height = (n < (s->nb_slices - 1)) ? slice_height : last_height; + const int width = avctx->width; + AVFrame *frame = tdata; + + if (s->correlate) { + uint8_t *decorrelated[2] = { s->decorrelate_buf[0] + n * slice_height * aligned_width, + s->decorrelate_buf[1] + n * slice_height * aligned_width }; + const int decorrelate_linesize = aligned_width; + const uint8_t *const data[4] = { decorrelated[0], frame->data[0] + n * slice_height * frame->linesize[0], + decorrelated[1], frame->data[3] + n * slice_height * frame->linesize[3] }; + const uint8_t *r, *g, *b; + const int linesize[4] = { decorrelate_linesize, frame->linesize[0], + decorrelate_linesize, frame->linesize[3] }; + + g = frame->data[0] + n * slice_height * frame->linesize[0]; + b = frame->data[1] + n * slice_height * frame->linesize[1]; + r = frame->data[2] + n * slice_height * frame->linesize[2]; + + for (int i = 0; i < slice_height; i++) { + s->llvidencdsp.diff_bytes(decorrelated[0], b, g, width); + s->llvidencdsp.diff_bytes(decorrelated[1], r, g, width); + g += frame->linesize[0]; + b += frame->linesize[1]; + r += frame->linesize[2]; + decorrelated[0] += decorrelate_linesize; + decorrelated[1] += decorrelate_linesize; + } + + for (int i = 0; i < s->planes; i++) { + s->predict(s, data[i], s->slices[n * s->planes + i], linesize[i], + frame->width, height); + } + } else { + for (int i = 0; i < s->planes; i++) { + s->predict(s, frame->data[i] + n * (slice_height >> s->vshift[i]) * frame->linesize[i], + s->slices[n * s->planes + i], + frame->linesize[i], + AV_CEIL_RSHIFT(frame->width, s->hshift[i]), + AV_CEIL_RSHIFT(height, s->vshift[i])); + } + } + + for (int p = 0; p < s->planes; p++) + count_plane_slice(avctx, n, p); + + return 0; +} + static int magy_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *frame, int *got_packet) { MagicYUVContext *s = avctx->priv_data; PutByteContext pb; const int width = avctx->width, height = avctx->height; - int pos, slice, i, j, ret = 0; + const int slice_height = s->slice_height; + int pos, ret = 0; ret = ff_alloc_packet(avctx, pkt, (256 + 4 * s->nb_slices + width * height) * s->planes + 256); @@ -439,92 +553,49 @@ static int magy_encode_frame(AVCodecContext *avctx, AVPacket *pkt, bytestream2_put_le32(&pb, avctx->width); bytestream2_put_le32(&pb, avctx->height); bytestream2_put_le32(&pb, avctx->width); - bytestream2_put_le32(&pb, avctx->height); + bytestream2_put_le32(&pb, slice_height); bytestream2_put_le32(&pb, 0); - for (i = 0; i < s->planes; i++) { + for (int i = 0; i < s->planes; i++) { bytestream2_put_le32(&pb, 0); - for (j = 1; j < s->nb_slices; j++) { + for (int j = 1; j < s->nb_slices; j++) bytestream2_put_le32(&pb, 0); - } } bytestream2_put_byte(&pb, s->planes); - for (i = 0; i < s->planes; i++) { - for (slice = 0; slice < s->nb_slices; slice++) { - bytestream2_put_byte(&pb, i); - } + for (int i = 0; i < s->planes; i++) { + for (int n = 0; n < s->nb_slices; n++) + bytestream2_put_byte(&pb, n * s->planes + i); } - if (s->correlate) { - uint8_t *decorrelated[2] = { s->decorrelate_buf[0], - s->decorrelate_buf[1] }; - const int decorrelate_linesize = FFALIGN(width, 16); - const uint8_t *const data[4] = { decorrelated[0], frame->data[0], - decorrelated[1], frame->data[3] }; - const uint8_t *r, *g, *b; - const int linesize[4] = { decorrelate_linesize, frame->linesize[0], - decorrelate_linesize, frame->linesize[3] }; - - g = frame->data[0]; - b = frame->data[1]; - r = frame->data[2]; - - for (i = 0; i < height; i++) { - s->llvidencdsp.diff_bytes(decorrelated[0], b, g, width); - s->llvidencdsp.diff_bytes(decorrelated[1], r, g, width); - g += frame->linesize[0]; - b += frame->linesize[1]; - r += frame->linesize[2]; - decorrelated[0] += decorrelate_linesize; - decorrelated[1] += decorrelate_linesize; - } - - for (i = 0; i < s->planes; i++) { - for (slice = 0; slice < s->nb_slices; slice++) { - s->predict(s, data[i], s->slices[i], linesize[i], - frame->width, frame->height); - } - } - } else { - for (i = 0; i < s->planes; i++) { - for (slice = 0; slice < s->nb_slices; slice++) { - s->predict(s, frame->data[i], s->slices[i], frame->linesize[i], - AV_CEIL_RSHIFT(frame->width, s->hshift[i]), - AV_CEIL_RSHIFT(frame->height, s->vshift[i])); - } - } - } + avctx->execute2(avctx, predict_slice, (void *)frame, NULL, s->nb_slices); init_put_bits(&s->pb, pkt->data + bytestream2_tell_p(&pb), bytestream2_get_bytes_left_p(&pb)); - for (i = 0; i < s->planes; i++) { - encode_table(avctx, s->slices[i], - AV_CEIL_RSHIFT(frame->width, s->hshift[i]), - AV_CEIL_RSHIFT(frame->height, s->vshift[i]), - &s->pb, s->he[i]); - } + for (int i = 0; i < s->planes; i++) + encode_table(avctx, &s->pb, s->he[i], i); + s->tables_size = put_bytes_count(&s->pb, 1); bytestream2_skip_p(&pb, s->tables_size); - for (i = 0; i < s->planes; i++) { - unsigned slice_size; + avctx->execute2(avctx, encode_slice, NULL, NULL, s->nb_slices); - s->slice_pos[i] = bytestream2_tell_p(&pb); - slice_size = encode_slice(s->slices[i], pkt->data + bytestream2_tell_p(&pb), - bytestream2_get_bytes_left_p(&pb), - AV_CEIL_RSHIFT(frame->width, s->hshift[i]), - AV_CEIL_RSHIFT(frame->height, s->vshift[i]), - s->he[i], s->frame_pred); - bytestream2_skip_p(&pb, slice_size); + for (int n = 0; n < s->nb_slices; n++) { + for (int i = 0; i < s->planes; i++) { + s->slice_pos[n * s->planes + i] = bytestream2_tell_p(&pb); + + bytestream2_put_buffer(&pb, s->bitslices[n * s->planes + i], + s->slice_size[n * s->planes + i]); + } } pos = bytestream2_tell_p(&pb); bytestream2_seek_p(&pb, 32, SEEK_SET); bytestream2_put_le32(&pb, s->slice_pos[0] - 32); - for (i = 0; i < s->planes; i++) { - bytestream2_put_le32(&pb, s->slice_pos[i] - 32); + for (int i = 0; i < s->planes; i++) { + for (int n = 0; n < s->nb_slices; n++) + bytestream2_put_le32(&pb, s->slice_pos[n * s->planes + i] - 32); } bytestream2_seek_p(&pb, pos, SEEK_SET); @@ -538,10 +609,16 @@ static int magy_encode_frame(AVCodecContext *avctx, AVPacket *pkt, static av_cold int magy_encode_close(AVCodecContext *avctx) { MagicYUVContext *s = avctx->priv_data; - int i; - for (i = 0; i < s->planes; i++) + av_freep(&s->slice_pos); + av_freep(&s->slice_size); + for (int i = 0; i < s->planes * s->nb_slices && s->slices; i++) av_freep(&s->slices[i]); + for (int i = 0; i < s->planes * s->nb_slices && s->bitslices; i++) + av_freep(&s->bitslices[i]); + av_freep(&s->counts); + av_freep(&s->slices); + av_freep(&s->bitslices); av_freep(&s->decorrelate_buf); return 0; @@ -569,7 +646,9 @@ const FFCodec ff_magicyuv_encoder = { CODEC_LONG_NAME("MagicYUV video"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_MAGICYUV, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_SLICE_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(MagicYUVContext), .p.priv_class = &magicyuv_class, .init = magy_encode_init, diff --git a/libavcodec/mdec.c b/libavcodec/mdec.c index 640b671a0fe..44b12471a9e 100644 --- a/libavcodec/mdec.c +++ b/libavcodec/mdec.c @@ -177,7 +177,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if ((ret = ff_thread_get_buffer(avctx, frame, 0)) < 0) return ret; frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; av_fast_padded_malloc(&a->bitstream_buffer, &a->bitstream_buffer_size, buf_size); if (!a->bitstream_buffer) diff --git a/libavcodec/me_cmp.c b/libavcodec/me_cmp.c index e2f9f84b055..cd05e63ffd6 100644 --- a/libavcodec/me_cmp.c +++ b/libavcodec/me_cmp.c @@ -473,8 +473,9 @@ static int zero_cmp(MpegEncContext *s, const uint8_t *a, const uint8_t *b, return 0; } -void ff_set_cmp(MECmpContext *c, me_cmp_func *cmp, int type) +int ff_set_cmp(MECmpContext *c, me_cmp_func *cmp, int type) { + int ret = 0; int i; memset(cmp, 0, sizeof(void *) * 6); @@ -533,9 +534,13 @@ void ff_set_cmp(MECmpContext *c, me_cmp_func *cmp, int type) #endif default: av_log(NULL, AV_LOG_ERROR, - "internal error in cmp function selection\n"); + "invalid cmp function selection\n"); + ret = -1; + break; } } + + return ret; } #define BUTTERFLY2(o1, o2, i1, i2) \ diff --git a/libavcodec/me_cmp.h b/libavcodec/me_cmp.h index 90ea76c891a..aefd32a7dc9 100644 --- a/libavcodec/me_cmp.h +++ b/libavcodec/me_cmp.h @@ -89,7 +89,7 @@ void ff_me_cmp_init_ppc(MECmpContext *c, AVCodecContext *avctx); void ff_me_cmp_init_x86(MECmpContext *c, AVCodecContext *avctx); void ff_me_cmp_init_mips(MECmpContext *c, AVCodecContext *avctx); -void ff_set_cmp(MECmpContext *c, me_cmp_func *cmp, int type); +int ff_set_cmp(MECmpContext *c, me_cmp_func *cmp, int type); void ff_dsputil_init_dwt(MECmpContext *c); diff --git a/libavcodec/media100.c b/libavcodec/media100.c deleted file mode 100644 index fdfce2cac1b..00000000000 --- a/libavcodec/media100.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Media 100 decoder - * Copyright (c) 2022 Paul B Mahol - * - * 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 - * Media 100 decoder. - */ - -#include - -#include "libavutil/intreadwrite.h" -#include "avcodec.h" -#include "bytestream.h" -#include "codec_internal.h" - -typedef struct Media100Context { - AVCodecContext *avctx; // wrapper context for mjpegb - AVPacket *pkt; -} Media100Context; - -static av_cold int media100_decode_init(AVCodecContext *avctx) -{ - Media100Context *ctx = avctx->priv_data; - const AVCodec *codec; - int ret; - - codec = avcodec_find_decoder(AV_CODEC_ID_MJPEGB); - if (!codec) - return AVERROR_BUG; - ctx->avctx = avcodec_alloc_context3(codec); - if (!ctx->avctx) - return AVERROR(ENOMEM); - ctx->avctx->thread_count = 1; - ctx->avctx->flags = avctx->flags; - ctx->avctx->flags2 = avctx->flags2; - ctx->avctx->width = ctx->avctx->coded_width = avctx->width; - ctx->avctx->height = ctx->avctx->coded_height = avctx->height; - - ret = avcodec_open2(ctx->avctx, codec, NULL); - if (ret < 0) - return ret; - - ctx->pkt = av_packet_alloc(); - if (!ctx->pkt) - return AVERROR(ENOMEM); - - return 0; -} - -static int media100_decode_frame(AVCodecContext *avctx, - AVFrame *frame, int *got_frame, - AVPacket *avpkt) -{ - Media100Context *ctx = avctx->priv_data; - unsigned second_field_offset = 0; - unsigned next_field = 0; - unsigned dht_offset[2]; - unsigned dqt_offset[2]; - unsigned sod_offset[2]; - unsigned sof_offset[2]; - unsigned sos_offset[2]; - unsigned field = 0; - GetByteContext gb; - PutByteContext pb; - AVPacket *pkt; - int ret; - - if (avpkt->size + 1024 > ctx->pkt->size) { - ret = av_grow_packet(ctx->pkt, avpkt->size + 1024 - ctx->pkt->size); - if (ret < 0) - return ret; - } - - ret = av_packet_make_writable(ctx->pkt); - if (ret < 0) - return ret; - - bytestream2_init(&gb, avpkt->data, avpkt->size); - bytestream2_init_writer(&pb, ctx->pkt->data, ctx->pkt->size); - -second_field: - bytestream2_put_be32(&pb, 0); - bytestream2_put_be32(&pb, AV_RB32("mjpg")); - bytestream2_put_be32(&pb, 0); - bytestream2_put_be32(&pb, 0); - for (int i = 0; i < 6; i++) - bytestream2_put_be32(&pb, 0); - - sof_offset[field] = bytestream2_tell_p(&pb); - bytestream2_put_be16(&pb, 17); - bytestream2_put_byte(&pb, 8); - bytestream2_put_be16(&pb, avctx->height / 2); - bytestream2_put_be16(&pb, avctx->width); - bytestream2_put_byte(&pb, 3); - bytestream2_put_byte(&pb, 1); - bytestream2_put_byte(&pb, 0x21); - bytestream2_put_byte(&pb, 0); - bytestream2_put_byte(&pb, 2); - bytestream2_put_byte(&pb, 0x11); - bytestream2_put_byte(&pb, 1); - bytestream2_put_byte(&pb, 3); - bytestream2_put_byte(&pb, 0x11); - bytestream2_put_byte(&pb, 1); - - sos_offset[field] = bytestream2_tell_p(&pb); - bytestream2_put_be16(&pb, 12); - bytestream2_put_byte(&pb, 3); - bytestream2_put_byte(&pb, 1); - bytestream2_put_byte(&pb, 0); - bytestream2_put_byte(&pb, 2); - bytestream2_put_byte(&pb, 0x11); - bytestream2_put_byte(&pb, 3); - bytestream2_put_byte(&pb, 0x11); - bytestream2_put_byte(&pb, 0); - bytestream2_put_byte(&pb, 0); - bytestream2_put_byte(&pb, 0); - - dqt_offset[field] = bytestream2_tell_p(&pb); - bytestream2_put_be16(&pb, 132); - bytestream2_put_byte(&pb, 0); - bytestream2_skip(&gb, 4); - for (int i = 0; i < 64; i++) - bytestream2_put_byte(&pb, bytestream2_get_be32(&gb)); - bytestream2_put_byte(&pb, 1); - for (int i = 0; i < 64; i++) - bytestream2_put_byte(&pb, bytestream2_get_be32(&gb)); - - dht_offset[field] = 0; - sod_offset[field] = bytestream2_tell_p(&pb); - - for (int i = bytestream2_tell(&gb) + 8; next_field == 0 && i < avpkt->size - 4; i++) { - if (AV_RB32(avpkt->data + i) == 0x00000001) { - next_field = i; - break; - } - } - - bytestream2_skip(&gb, 8); - bytestream2_copy_buffer(&pb, &gb, next_field - bytestream2_tell(&gb)); - bytestream2_put_be64(&pb, 0); - - if (field == 0) { - field = 1; - second_field_offset = bytestream2_tell_p(&pb); - next_field = avpkt->size; - goto second_field; - } - - pkt = ctx->pkt; - - AV_WB32(pkt->data + 8, second_field_offset); - AV_WB32(pkt->data + 12, second_field_offset); - AV_WB32(pkt->data + 16, second_field_offset); - AV_WB32(pkt->data + 20, dqt_offset[0]); - AV_WB32(pkt->data + 24, dht_offset[0]); - AV_WB32(pkt->data + 28, sof_offset[0]); - AV_WB32(pkt->data + 32, sos_offset[0]); - AV_WB32(pkt->data + 36, sod_offset[0]); - - AV_WB32(pkt->data + second_field_offset + 8, bytestream2_tell_p(&pb) - second_field_offset); - AV_WB32(pkt->data + second_field_offset + 12, bytestream2_tell_p(&pb) - second_field_offset); - AV_WB32(pkt->data + second_field_offset + 16, 0); - AV_WB32(pkt->data + second_field_offset + 20, dqt_offset[1] - second_field_offset); - AV_WB32(pkt->data + second_field_offset + 24, dht_offset[1]); - AV_WB32(pkt->data + second_field_offset + 28, sof_offset[1] - second_field_offset); - AV_WB32(pkt->data + second_field_offset + 32, sos_offset[1] - second_field_offset); - AV_WB32(pkt->data + second_field_offset + 36, sod_offset[1] - second_field_offset); - - pkt->size = bytestream2_tell_p(&pb); - - ret = avcodec_send_packet(ctx->avctx, pkt); - if (ret < 0) { - av_log(avctx, AV_LOG_ERROR, "Error submitting a packet for decoding\n"); - return ret; - } - - ret = avcodec_receive_frame(ctx->avctx, frame); - if (ret < 0) - return ret; - - *got_frame = 1; - - return avpkt->size; -} - -static av_cold int media100_decode_end(AVCodecContext *avctx) -{ - Media100Context *ctx = avctx->priv_data; - - avcodec_free_context(&ctx->avctx); - av_packet_free(&ctx->pkt); - - return 0; -} - -const FFCodec ff_media100_decoder = { - .p.name = "media100", - CODEC_LONG_NAME("Media 100"), - .p.type = AVMEDIA_TYPE_VIDEO, - .p.id = AV_CODEC_ID_MEDIA100, - .priv_data_size = sizeof(Media100Context), - .init = media100_decode_init, - .close = media100_decode_end, - FF_CODEC_DECODE_CB(media100_decode_frame), - .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, -}; diff --git a/libavcodec/media100_to_mjpegb_bsf.c b/libavcodec/media100_to_mjpegb_bsf.c new file mode 100644 index 00000000000..6e117ae20fb --- /dev/null +++ b/libavcodec/media100_to_mjpegb_bsf.c @@ -0,0 +1,168 @@ +/* + * Media 100 to MJPEGB bitstream filter + * Copyright (c) 2023 Paul B Mahol + * + * 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 + * Media 100 to MJPEGB bitstream filter. + */ + +#include "libavutil/intreadwrite.h" +#include "bsf.h" +#include "bsf_internal.h" +#include "bytestream.h" + +static av_cold int init(AVBSFContext *ctx) +{ + ctx->par_out->codec_id = AV_CODEC_ID_MJPEGB; + return 0; +} + +static int filter(AVBSFContext *ctx, AVPacket *out) +{ + unsigned second_field_offset = 0; + unsigned next_field = 0; + unsigned dht_offset[2]; + unsigned dqt_offset[2]; + unsigned sod_offset[2]; + unsigned sof_offset[2]; + unsigned sos_offset[2]; + unsigned field = 0; + GetByteContext gb; + PutByteContext pb; + AVPacket *in; + int ret; + + ret = ff_bsf_get_packet(ctx, &in); + if (ret < 0) + return ret; + + ret = av_new_packet(out, in->size + 1024); + if (ret < 0) + goto fail; + + bytestream2_init(&gb, in->data, in->size); + bytestream2_init_writer(&pb, out->data, out->size); + +second_field: + bytestream2_put_be32(&pb, 0); + bytestream2_put_be32(&pb, AV_RB32("mjpg")); + bytestream2_put_be32(&pb, 0); + bytestream2_put_be32(&pb, 0); + for (int i = 0; i < 6; i++) + bytestream2_put_be32(&pb, 0); + + sof_offset[field] = bytestream2_tell_p(&pb); + bytestream2_put_be16(&pb, 17); + bytestream2_put_byte(&pb, 8); + bytestream2_put_be16(&pb, ctx->par_in->height / 2); + bytestream2_put_be16(&pb, ctx->par_in->width); + bytestream2_put_byte(&pb, 3); + bytestream2_put_byte(&pb, 1); + bytestream2_put_byte(&pb, 0x21); + bytestream2_put_byte(&pb, 0); + bytestream2_put_byte(&pb, 2); + bytestream2_put_byte(&pb, 0x11); + bytestream2_put_byte(&pb, 1); + bytestream2_put_byte(&pb, 3); + bytestream2_put_byte(&pb, 0x11); + bytestream2_put_byte(&pb, 1); + + sos_offset[field] = bytestream2_tell_p(&pb); + bytestream2_put_be16(&pb, 12); + bytestream2_put_byte(&pb, 3); + bytestream2_put_byte(&pb, 1); + bytestream2_put_byte(&pb, 0); + bytestream2_put_byte(&pb, 2); + bytestream2_put_byte(&pb, 0x11); + bytestream2_put_byte(&pb, 3); + bytestream2_put_byte(&pb, 0x11); + bytestream2_put_byte(&pb, 0); + bytestream2_put_byte(&pb, 0); + bytestream2_put_byte(&pb, 0); + + dqt_offset[field] = bytestream2_tell_p(&pb); + bytestream2_put_be16(&pb, 132); + bytestream2_put_byte(&pb, 0); + bytestream2_skip(&gb, 4); + for (int i = 0; i < 64; i++) + bytestream2_put_byte(&pb, bytestream2_get_be32(&gb)); + bytestream2_put_byte(&pb, 1); + for (int i = 0; i < 64; i++) + bytestream2_put_byte(&pb, bytestream2_get_be32(&gb)); + + dht_offset[field] = 0; + sod_offset[field] = bytestream2_tell_p(&pb); + + for (int i = bytestream2_tell(&gb) + 8; next_field == 0 && i < in->size - 4; i++) { + if (AV_RB32(in->data + i) == 0x00000001) { + next_field = i; + break; + } + } + + bytestream2_skip(&gb, 8); + bytestream2_copy_buffer(&pb, &gb, next_field - bytestream2_tell(&gb)); + bytestream2_put_be64(&pb, 0); + + if (field == 0) { + field = 1; + second_field_offset = bytestream2_tell_p(&pb); + next_field = in->size; + goto second_field; + } + + AV_WB32(out->data + 8, second_field_offset); + AV_WB32(out->data + 12, second_field_offset); + AV_WB32(out->data + 16, second_field_offset); + AV_WB32(out->data + 20, dqt_offset[0]); + AV_WB32(out->data + 24, dht_offset[0]); + AV_WB32(out->data + 28, sof_offset[0]); + AV_WB32(out->data + 32, sos_offset[0]); + AV_WB32(out->data + 36, sod_offset[0]); + + AV_WB32(out->data + second_field_offset + 8, bytestream2_tell_p(&pb) - second_field_offset); + AV_WB32(out->data + second_field_offset + 12, bytestream2_tell_p(&pb) - second_field_offset); + AV_WB32(out->data + second_field_offset + 16, 0); + AV_WB32(out->data + second_field_offset + 20, dqt_offset[1] - second_field_offset); + AV_WB32(out->data + second_field_offset + 24, dht_offset[1]); + AV_WB32(out->data + second_field_offset + 28, sof_offset[1] - second_field_offset); + AV_WB32(out->data + second_field_offset + 32, sos_offset[1] - second_field_offset); + AV_WB32(out->data + second_field_offset + 36, sod_offset[1] - second_field_offset); + + out->size = bytestream2_tell_p(&pb); + + ret = av_packet_copy_props(out, in); + if (ret < 0) + goto fail; + +fail: + if (ret < 0) + av_packet_unref(out); + av_packet_free(&in); + return ret; +} + +const FFBitStreamFilter ff_media100_to_mjpegb_bsf = { + .p.name = "media100_to_mjpegb", + .p.codec_ids = (const enum AVCodecID []){ AV_CODEC_ID_MEDIA100, AV_CODEC_ID_NONE }, + .init = init, + .filter = filter, +}; diff --git a/libavcodec/mediacodec_wrapper.c b/libavcodec/mediacodec_wrapper.c index 34ec2134aa1..eb69ad7eaf3 100644 --- a/libavcodec/mediacodec_wrapper.c +++ b/libavcodec/mediacodec_wrapper.c @@ -319,10 +319,49 @@ int ff_AMediaCodecProfile_getProfileFromAVCodecContext(AVCodecContext *avctx) static const int HEVCProfileMain10HDR10 = 0x1000; static const int HEVCProfileMain10HDR10Plus = 0x2000; + static const int VP9Profile0 = 0x01; + static const int VP9Profile1 = 0x02; + static const int VP9Profile2 = 0x04; + static const int VP9Profile3 = 0x08; + static const int VP9Profile2HDR = 0x1000; + static const int VP9Profile3HDR = 0x2000; + static const int VP9Profile2HDR10Plus = 0x4000; + static const int VP9Profile3HDR10Plus = 0x8000; + + static const int MPEG4ProfileSimple = 0x01; + static const int MPEG4ProfileSimpleScalable = 0x02; + static const int MPEG4ProfileCore = 0x04; + static const int MPEG4ProfileMain = 0x08; + static const int MPEG4ProfileNbit = 0x10; + static const int MPEG4ProfileScalableTexture = 0x20; + static const int MPEG4ProfileSimpleFBA = 0x80; + static const int MPEG4ProfileSimpleFace = 0x40; + static const int MPEG4ProfileBasicAnimated = 0x100; + static const int MPEG4ProfileHybrid = 0x200; + static const int MPEG4ProfileAdvancedRealTime = 0x400; + static const int MPEG4ProfileCoreScalable = 0x800; + static const int MPEG4ProfileAdvancedCoding = 0x1000; + static const int MPEG4ProfileAdvancedCore = 0x2000; + static const int MPEG4ProfileAdvancedScalable = 0x4000; + static const int MPEG4ProfileAdvancedSimple = 0x8000; + + + static const int AV1ProfileMain8 = 0x1; + static const int AV1ProfileMain10 = 0x2; + static const int AV1ProfileMain10HDR10 = 0x1000; + static const int AV1ProfileMain10HDR10Plus = 0x2000; + // Unused yet. (void)AVCProfileConstrainedHigh; (void)HEVCProfileMain10HDR10; (void)HEVCProfileMain10HDR10Plus; + (void)VP9Profile2HDR; + (void)VP9Profile3HDR; + (void)VP9Profile2HDR10Plus; + (void)VP9Profile3HDR10Plus; + (void)AV1ProfileMain10; + (void)AV1ProfileMain10HDR10; + (void)AV1ProfileMain10HDR10Plus; if (avctx->codec_id == AV_CODEC_ID_H264) { switch(avctx->profile) { @@ -357,6 +396,65 @@ int ff_AMediaCodecProfile_getProfileFromAVCodecContext(AVCodecContext *avctx) case FF_PROFILE_HEVC_MAIN_10: return HEVCProfileMain10; } + } else if (avctx->codec_id == AV_CODEC_ID_VP9) { + switch (avctx->profile) { + case FF_PROFILE_VP9_0: + return VP9Profile0; + case FF_PROFILE_VP9_1: + return VP9Profile1; + case FF_PROFILE_VP9_2: + return VP9Profile2; + case FF_PROFILE_VP9_3: + return VP9Profile3; + } + } else if(avctx->codec_id == AV_CODEC_ID_MPEG4) { + switch (avctx->profile) + { + case FF_PROFILE_MPEG4_SIMPLE: + return MPEG4ProfileSimple; + case FF_PROFILE_MPEG4_SIMPLE_SCALABLE: + return MPEG4ProfileSimpleScalable; + case FF_PROFILE_MPEG4_CORE: + return MPEG4ProfileCore; + case FF_PROFILE_MPEG4_MAIN: + return MPEG4ProfileMain; + case FF_PROFILE_MPEG4_N_BIT: + return MPEG4ProfileNbit; + case FF_PROFILE_MPEG4_SCALABLE_TEXTURE: + return MPEG4ProfileScalableTexture; + case FF_PROFILE_MPEG4_SIMPLE_FACE_ANIMATION: + return MPEG4ProfileSimpleFBA; + case FF_PROFILE_MPEG4_BASIC_ANIMATED_TEXTURE: + return MPEG4ProfileBasicAnimated; + case FF_PROFILE_MPEG4_HYBRID: + return MPEG4ProfileHybrid; + case FF_PROFILE_MPEG4_ADVANCED_REAL_TIME: + return MPEG4ProfileAdvancedRealTime; + case FF_PROFILE_MPEG4_CORE_SCALABLE: + return MPEG4ProfileCoreScalable; + case FF_PROFILE_MPEG4_ADVANCED_CODING: + return MPEG4ProfileAdvancedCoding; + case FF_PROFILE_MPEG4_ADVANCED_CORE: + return MPEG4ProfileAdvancedCore; + case FF_PROFILE_MPEG4_ADVANCED_SCALABLE_TEXTURE: + return MPEG4ProfileAdvancedScalable; + case FF_PROFILE_MPEG4_ADVANCED_SIMPLE: + return MPEG4ProfileAdvancedSimple; + case FF_PROFILE_MPEG4_SIMPLE_STUDIO: + // Studio profiles are not supported by mediacodec. + default: + break; + } + } else if(avctx->codec_id == AV_CODEC_ID_AV1) { + switch (avctx->profile) + { + case FF_PROFILE_AV1_MAIN: + return AV1ProfileMain8; + case FF_PROFILE_AV1_HIGH: + case FF_PROFILE_AV1_PROFESSIONAL: + default: + break; + } } return -1; @@ -2542,3 +2640,105 @@ int ff_Build_SDK_INT(AVCodecContext *avctx) return ret; } + +static struct { + enum FFAMediaFormatColorRange mf_range; + enum AVColorRange range; +} color_range_map[] = { + { COLOR_RANGE_FULL, AVCOL_RANGE_JPEG }, + { COLOR_RANGE_LIMITED, AVCOL_RANGE_MPEG }, +}; + +static struct { + enum FFAMediaFormatColorStandard mf_standard; + enum AVColorSpace space; +} color_space_map[] = { + { COLOR_STANDARD_BT709, AVCOL_SPC_BT709 }, + { COLOR_STANDARD_BT601_PAL, AVCOL_SPC_BT470BG }, + { COLOR_STANDARD_BT601_NTSC, AVCOL_SPC_SMPTE170M }, + { COLOR_STANDARD_BT2020, AVCOL_SPC_BT2020_NCL }, +}; + +static struct { + enum FFAMediaFormatColorStandard mf_standard; + enum AVColorPrimaries primaries; +} color_primaries_map[] = { + { COLOR_STANDARD_BT709, AVCOL_PRI_BT709 }, + { COLOR_STANDARD_BT601_PAL, AVCOL_PRI_BT470BG }, + { COLOR_STANDARD_BT601_NTSC, AVCOL_PRI_SMPTE170M }, + { COLOR_STANDARD_BT2020, AVCOL_PRI_BT2020 }, +}; + +static struct { + enum FFAMediaFormatColorTransfer mf_transfer; + enum AVColorTransferCharacteristic transfer; +} color_transfer_map[] = { + { COLOR_TRANSFER_LINEAR, AVCOL_TRC_LINEAR }, + { COLOR_TRANSFER_SDR_VIDEO, AVCOL_TRC_SMPTE170M }, + { COLOR_TRANSFER_ST2084, AVCOL_TRC_SMPTEST2084 }, + { COLOR_TRANSFER_HLG, AVCOL_TRC_ARIB_STD_B67 }, +}; + +enum AVColorRange ff_AMediaFormatColorRange_to_AVColorRange(int color_range) +{ + for (int i = 0; i < FF_ARRAY_ELEMS(color_range_map); i++) + if (color_range_map[i].mf_range == color_range) + return color_range_map[i].range; + + return AVCOL_RANGE_UNSPECIFIED; +} + +int ff_AMediaFormatColorRange_from_AVColorRange(enum AVColorRange color_range) +{ + for (int i = 0; i < FF_ARRAY_ELEMS(color_range_map); i++) + if (color_range_map[i].range == color_range) + return color_range_map[i].mf_range; + return COLOR_RANGE_UNSPECIFIED; +} + +enum AVColorSpace ff_AMediaFormatColorStandard_to_AVColorSpace(int color_standard) +{ + for (int i = 0; i < FF_ARRAY_ELEMS(color_space_map); i++) + if (color_space_map[i].mf_standard == color_standard) + return color_space_map[i].space; + + return AVCOL_SPC_UNSPECIFIED; +} + +int ff_AMediaFormatColorStandard_from_AVColorSpace(enum AVColorSpace color_space) +{ + for (int i = 0; i < FF_ARRAY_ELEMS(color_space_map); i++) + if (color_space_map[i].space == color_space) + return color_space_map[i].mf_standard; + + return COLOR_STANDARD_UNSPECIFIED; +} + +enum AVColorPrimaries ff_AMediaFormatColorStandard_to_AVColorPrimaries(int color_standard) +{ + for (int i = 0; i < FF_ARRAY_ELEMS(color_primaries_map); i++) + if (color_primaries_map[i].mf_standard == color_standard) + return color_primaries_map[i].primaries; + + return AVCOL_PRI_UNSPECIFIED; +} + +enum AVColorTransferCharacteristic +ff_AMediaFormatColorTransfer_to_AVColorTransfer(int color_transfer) +{ + for (int i = 0; i < FF_ARRAY_ELEMS(color_transfer_map); i++) + if (color_transfer_map[i].mf_transfer == color_transfer) + return color_transfer_map[i].transfer; + + return AVCOL_TRC_UNSPECIFIED; +} + +int ff_AMediaFormatColorTransfer_from_AVColorTransfer( + enum AVColorTransferCharacteristic color_transfer) +{ + for (int i = 0; i < FF_ARRAY_ELEMS(color_transfer_map); i++) + if (color_transfer_map[i].transfer == color_transfer) + return color_transfer_map[i].mf_transfer; + + return COLOR_TRANSFER_UNSPECIFIED; +} diff --git a/libavcodec/mediacodec_wrapper.h b/libavcodec/mediacodec_wrapper.h index 1b81e6db843..11a42604979 100644 --- a/libavcodec/mediacodec_wrapper.h +++ b/libavcodec/mediacodec_wrapper.h @@ -345,4 +345,77 @@ static inline int ff_AMediaCodec_signalEndOfInputStream(FFAMediaCodec *codec) int ff_Build_SDK_INT(AVCodecContext *avctx); +enum FFAMediaFormatColorRange { + COLOR_RANGE_UNSPECIFIED = 0x0, + COLOR_RANGE_FULL = 0x1, + COLOR_RANGE_LIMITED = 0x2, +}; + +enum FFAMediaFormatColorStandard { + COLOR_STANDARD_UNSPECIFIED = 0x0, + COLOR_STANDARD_BT709 = 0x1, + COLOR_STANDARD_BT601_PAL = 0x2, + COLOR_STANDARD_BT601_NTSC = 0x4, + COLOR_STANDARD_BT2020 = 0x6, +}; + +enum FFAMediaFormatColorTransfer { + COLOR_TRANSFER_UNSPECIFIED = 0x0, + COLOR_TRANSFER_LINEAR = 0x1, + COLOR_TRANSFER_SDR_VIDEO = 0x3, + COLOR_TRANSFER_ST2084 = 0x6, + COLOR_TRANSFER_HLG = 0x7, +}; + +/** + * Map MediaFormat color range to AVColorRange. + * + * return AVCOL_RANGE_UNSPECIFIED when failed. + */ +enum AVColorRange ff_AMediaFormatColorRange_to_AVColorRange(int color_range); + +/** + * Map AVColorRange to MediaFormat color range. + * + * return COLOR_RANGE_UNSPECIFIED when failed. + */ +int ff_AMediaFormatColorRange_from_AVColorRange(enum AVColorRange color_range); + +/** + * Map MediaFormat color standard to AVColorSpace. + * + * return AVCOL_SPC_UNSPECIFIED when failed. + */ +enum AVColorSpace ff_AMediaFormatColorStandard_to_AVColorSpace(int color_standard); + +/** + * Map AVColorSpace to MediaFormat color standard. + * + * return COLOR_STANDARD_UNSPECIFIED when failed. + */ +int ff_AMediaFormatColorStandard_from_AVColorSpace(enum AVColorSpace color_space); + +/** + * Map MediaFormat color standard to AVColorPrimaries. + * + * return AVCOL_PRI_UNSPECIFIED when failed. + */ +enum AVColorPrimaries ff_AMediaFormatColorStandard_to_AVColorPrimaries(int color_standard); + +/** + * Map MediaFormat color transfer to AVColorTransferCharacteristic. + * + * return AVCOL_TRC_UNSPECIFIED when failed. + */ +enum AVColorTransferCharacteristic +ff_AMediaFormatColorTransfer_to_AVColorTransfer(int color_transfer); + +/** + * Map AVColorTransferCharacteristic to MediaFormat color transfer. + * + * return COLOR_TRANSFER_UNSPECIFIED when failed. + */ +int ff_AMediaFormatColorTransfer_from_AVColorTransfer( + enum AVColorTransferCharacteristic color_transfer); + #endif /* AVCODEC_MEDIACODEC_WRAPPER_H */ diff --git a/libavcodec/mediacodecdec.c b/libavcodec/mediacodecdec.c index 21464900d18..44f55947be8 100644 --- a/libavcodec/mediacodecdec.c +++ b/libavcodec/mediacodecdec.c @@ -577,8 +577,7 @@ const FFCodec ff_ ## short_name ## _mediacodec_decoder = { .flush = mediacodec_decode_flush, \ .close = mediacodec_decode_close, \ .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE, \ - .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | \ - FF_CODEC_CAP_SETS_PKT_DTS, \ + .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE, \ .bsfs = bsf, \ .hw_configs = mediacodec_hw_configs, \ .p.wrapper_name = "mediacodec", \ diff --git a/libavcodec/mediacodecdec_common.c b/libavcodec/mediacodecdec_common.c index 03bee119188..1151bb71f9b 100644 --- a/libavcodec/mediacodecdec_common.c +++ b/libavcodec/mediacodecdec_common.c @@ -85,85 +85,6 @@ #define OUTPUT_DEQUEUE_TIMEOUT_US 8000 #define OUTPUT_DEQUEUE_BLOCK_TIMEOUT_US 1000000 -enum { - COLOR_RANGE_FULL = 0x1, - COLOR_RANGE_LIMITED = 0x2, -}; - -static enum AVColorRange mcdec_get_color_range(int color_range) -{ - switch (color_range) { - case COLOR_RANGE_FULL: - return AVCOL_RANGE_JPEG; - case COLOR_RANGE_LIMITED: - return AVCOL_RANGE_MPEG; - default: - return AVCOL_RANGE_UNSPECIFIED; - } -} - -enum { - COLOR_STANDARD_BT709 = 0x1, - COLOR_STANDARD_BT601_PAL = 0x2, - COLOR_STANDARD_BT601_NTSC = 0x4, - COLOR_STANDARD_BT2020 = 0x6, -}; - -static enum AVColorSpace mcdec_get_color_space(int color_standard) -{ - switch (color_standard) { - case COLOR_STANDARD_BT709: - return AVCOL_SPC_BT709; - case COLOR_STANDARD_BT601_PAL: - return AVCOL_SPC_BT470BG; - case COLOR_STANDARD_BT601_NTSC: - return AVCOL_SPC_SMPTE170M; - case COLOR_STANDARD_BT2020: - return AVCOL_SPC_BT2020_NCL; - default: - return AVCOL_SPC_UNSPECIFIED; - } -} - -static enum AVColorPrimaries mcdec_get_color_pri(int color_standard) -{ - switch (color_standard) { - case COLOR_STANDARD_BT709: - return AVCOL_PRI_BT709; - case COLOR_STANDARD_BT601_PAL: - return AVCOL_PRI_BT470BG; - case COLOR_STANDARD_BT601_NTSC: - return AVCOL_PRI_SMPTE170M; - case COLOR_STANDARD_BT2020: - return AVCOL_PRI_BT2020; - default: - return AVCOL_PRI_UNSPECIFIED; - } -} - -enum { - COLOR_TRANSFER_LINEAR = 0x1, - COLOR_TRANSFER_SDR_VIDEO = 0x3, - COLOR_TRANSFER_ST2084 = 0x6, - COLOR_TRANSFER_HLG = 0x7, -}; - -static enum AVColorTransferCharacteristic mcdec_get_color_trc(int color_transfer) -{ - switch (color_transfer) { - case COLOR_TRANSFER_LINEAR: - return AVCOL_TRC_LINEAR; - case COLOR_TRANSFER_SDR_VIDEO: - return AVCOL_TRC_SMPTE170M; - case COLOR_TRANSFER_ST2084: - return AVCOL_TRC_SMPTEST2084; - case COLOR_TRANSFER_HLG: - return AVCOL_TRC_ARIB_STD_B67; - default: - return AVCOL_TRC_UNSPECIFIED; - } -} - enum { COLOR_FormatYUV420Planar = 0x13, COLOR_FormatYUV420SemiPlanar = 0x15, @@ -517,17 +438,17 @@ static int mediacodec_dec_parse_format(AVCodecContext *avctx, MediaCodecDecConte AMEDIAFORMAT_GET_INT32(color_range, "color-range", 0); if (color_range) - avctx->color_range = mcdec_get_color_range(color_range); + avctx->color_range = ff_AMediaFormatColorRange_to_AVColorRange(color_range); AMEDIAFORMAT_GET_INT32(color_standard, "color-standard", 0); if (color_standard) { - avctx->colorspace = mcdec_get_color_space(color_standard); - avctx->color_primaries = mcdec_get_color_pri(color_standard); + avctx->colorspace = ff_AMediaFormatColorStandard_to_AVColorSpace(color_standard); + avctx->color_primaries = ff_AMediaFormatColorStandard_to_AVColorPrimaries(color_standard); } AMEDIAFORMAT_GET_INT32(color_transfer, "color-transfer", 0); if (color_transfer) - avctx->color_trc = mcdec_get_color_trc(color_transfer); + avctx->color_trc = ff_AMediaFormatColorTransfer_to_AVColorTransfer(color_transfer); av_log(avctx, AV_LOG_INFO, "Output crop parameters top=%d bottom=%d left=%d right=%d, " diff --git a/libavcodec/mediacodecenc.c b/libavcodec/mediacodecenc.c index a92a8dc5a9c..1da705d1132 100644 --- a/libavcodec/mediacodecenc.c +++ b/libavcodec/mediacodecenc.c @@ -36,6 +36,7 @@ #include "mediacodec.h" #include "mediacodec_wrapper.h" #include "mediacodecdec_common.h" +#include "profiles.h" #define INPUT_DEQUEUE_TIMEOUT_US 8000 #define OUTPUT_DEQUEUE_TIMEOUT_US 8000 @@ -164,6 +165,18 @@ static av_cold int mediacodec_init(AVCodecContext *avctx) case AV_CODEC_ID_HEVC: codec_mime = "video/hevc"; break; + case AV_CODEC_ID_VP8: + codec_mime = "video/x-vnd.on2.vp8"; + break; + case AV_CODEC_ID_VP9: + codec_mime = "video/x-vnd.on2.vp9"; + break; + case AV_CODEC_ID_MPEG4: + codec_mime = "video/mp4v-es"; + break; + case AV_CODEC_ID_AV1: + codec_mime = "video/av01"; + break; default: av_assert0(0); } @@ -243,6 +256,16 @@ static av_cold int mediacodec_init(AVCodecContext *avctx) } } + ret = ff_AMediaFormatColorRange_from_AVColorRange(avctx->color_range); + if (ret != COLOR_RANGE_UNSPECIFIED) + ff_AMediaFormat_setInt32(format, "color-range", ret); + ret = ff_AMediaFormatColorStandard_from_AVColorSpace(avctx->colorspace); + if (ret != COLOR_STANDARD_UNSPECIFIED) + ff_AMediaFormat_setInt32(format, "color-standard", ret); + ret = ff_AMediaFormatColorTransfer_from_AVColorTransfer(avctx->color_trc); + if (ret != COLOR_TRANSFER_UNSPECIFIED) + ff_AMediaFormat_setInt32(format, "color-transfer", ret); + if (avctx->bit_rate) ff_AMediaFormat_setInt32(format, "bitrate", avctx->bit_rate); if (s->bitrate_mode >= 0) @@ -307,6 +330,10 @@ static av_cold int mediacodec_init(AVCodecContext *avctx) goto bailout; mediacodec_output_format(avctx); + if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) + av_log(avctx, AV_LOG_WARNING, + "Mediacodec encoder doesn't support AV_CODEC_FLAG_GLOBAL_HEADER. " + "Use extract_extradata bsf when necessary.\n"); s->frame = av_frame_alloc(); if (!s->frame) @@ -632,6 +659,16 @@ enum MediaCodecAvcLevel { static const AVOption h264_options[] = { COMMON_OPTION + + FF_AVCTX_PROFILE_OPTION("baseline", NULL, VIDEO, FF_PROFILE_H264_BASELINE) + FF_AVCTX_PROFILE_OPTION("constrained_baseline", NULL, VIDEO, FF_PROFILE_H264_CONSTRAINED_BASELINE) + FF_AVCTX_PROFILE_OPTION("main", NULL, VIDEO, FF_PROFILE_H264_MAIN) + FF_AVCTX_PROFILE_OPTION("extended", NULL, VIDEO, FF_PROFILE_H264_EXTENDED) + FF_AVCTX_PROFILE_OPTION("high", NULL, VIDEO, FF_PROFILE_H264_HIGH) + FF_AVCTX_PROFILE_OPTION("high10", NULL, VIDEO, FF_PROFILE_H264_HIGH_10) + FF_AVCTX_PROFILE_OPTION("high422", NULL, VIDEO, FF_PROFILE_H264_HIGH_422) + FF_AVCTX_PROFILE_OPTION("high444", NULL, VIDEO, FF_PROFILE_H264_HIGH_444) + { "level", "Specify level", OFFSET(level), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, VE, "level" }, { "1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel1 }, 0, 0, VE, "level" }, @@ -694,6 +731,10 @@ enum MediaCodecHevcLevel { static const AVOption hevc_options[] = { COMMON_OPTION + + FF_AVCTX_PROFILE_OPTION("main", NULL, VIDEO, FF_PROFILE_HEVC_MAIN) + FF_AVCTX_PROFILE_OPTION("main10", NULL, VIDEO, FF_PROFILE_HEVC_MAIN_10) + { "level", "Specify tier and level", OFFSET(level), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, VE, "level" }, { "m1", "Main tier level 1", @@ -754,3 +795,235 @@ static const AVOption hevc_options[] = { DECLARE_MEDIACODEC_ENCODER(hevc, "H.265", AV_CODEC_ID_HEVC) #endif // CONFIG_HEVC_MEDIACODEC_ENCODER + +#if CONFIG_VP8_MEDIACODEC_ENCODER + +enum MediaCodecVP8Level { + VP8Level_Version0 = 0x01, + VP8Level_Version1 = 0x02, + VP8Level_Version2 = 0x04, + VP8Level_Version3 = 0x08, +}; + +static const AVOption vp8_options[] = { + COMMON_OPTION + { "level", "Specify tier and level", + OFFSET(level), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, VE, "level" }, + { "V0", "Level Version 0", + 0, AV_OPT_TYPE_CONST, { .i64 = VP8Level_Version0 }, 0, 0, VE, "level" }, + { "V1", "Level Version 1", + 0, AV_OPT_TYPE_CONST, { .i64 = VP8Level_Version1 }, 0, 0, VE, "level" }, + { "V2", "Level Version 2", + 0, AV_OPT_TYPE_CONST, { .i64 = VP8Level_Version2 }, 0, 0, VE, "level" }, + { "V3", "Level Version 3", + 0, AV_OPT_TYPE_CONST, { .i64 = VP8Level_Version3 }, 0, 0, VE, "level" }, + { NULL, } +}; + +DECLARE_MEDIACODEC_ENCODER(vp8, "VP8", AV_CODEC_ID_VP8) + +#endif // CONFIG_VP8_MEDIACODEC_ENCODER + +#if CONFIG_VP9_MEDIACODEC_ENCODER + +enum MediaCodecVP9Level { + VP9Level1 = 0x1, + VP9Level11 = 0x2, + VP9Level2 = 0x4, + VP9Level21 = 0x8, + VP9Level3 = 0x10, + VP9Level31 = 0x20, + VP9Level4 = 0x40, + VP9Level41 = 0x80, + VP9Level5 = 0x100, + VP9Level51 = 0x200, + VP9Level52 = 0x400, + VP9Level6 = 0x800, + VP9Level61 = 0x1000, + VP9Level62 = 0x2000, +}; + +static const AVOption vp9_options[] = { + COMMON_OPTION + + FF_AVCTX_PROFILE_OPTION("profile0", NULL, VIDEO, FF_PROFILE_VP9_0) + FF_AVCTX_PROFILE_OPTION("profile1", NULL, VIDEO, FF_PROFILE_VP9_1) + FF_AVCTX_PROFILE_OPTION("profile2", NULL, VIDEO, FF_PROFILE_VP9_2) + FF_AVCTX_PROFILE_OPTION("profile3", NULL, VIDEO, FF_PROFILE_VP9_3) + + { "level", "Specify tier and level", + OFFSET(level), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, VE, "level" }, + { "1", "Level 1", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level1 }, 0, 0, VE, "level" }, + { "1.1", "Level 1.1", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level11 }, 0, 0, VE, "level" }, + { "2", "Level 2", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level2 }, 0, 0, VE, "level" }, + { "2.1", "Level 2.1", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level21 }, 0, 0, VE, "level" }, + { "3", "Level 3", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level3 }, 0, 0, VE, "level" }, + { "3.1", "Level 3.1", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level31 }, 0, 0, VE, "level" }, + { "4", "Level 4", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level4 }, 0, 0, VE, "level" }, + { "4.1", "Level 4.1", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level41 }, 0, 0, VE, "level" }, + { "5", "Level 5", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level5 }, 0, 0, VE, "level" }, + { "5.1", "Level 5.1", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level51 }, 0, 0, VE, "level" }, + { "5.2", "Level 5.2", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level52 }, 0, 0, VE, "level" }, + { "6", "Level 6", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level6 }, 0, 0, VE, "level" }, + { "6.1", "Level 4.1", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level61 }, 0, 0, VE, "level" }, + { "6.2", "Level 6.2", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level62 }, 0, 0, VE, "level" }, + { NULL, } +}; + +DECLARE_MEDIACODEC_ENCODER(vp9, "VP9", AV_CODEC_ID_VP9) + +#endif // CONFIG_VP9_MEDIACODEC_ENCODER + +#if CONFIG_MPEG4_MEDIACODEC_ENCODER + +enum MediaCodecMpeg4Level { + MPEG4Level0 = 0x01, + MPEG4Level0b = 0x02, + MPEG4Level1 = 0x04, + MPEG4Level2 = 0x08, + MPEG4Level3 = 0x10, + MPEG4Level3b = 0x18, + MPEG4Level4 = 0x20, + MPEG4Level4a = 0x40, + MPEG4Level5 = 0x80, + MPEG4Level6 = 0x100, +}; + +static const AVOption mpeg4_options[] = { + COMMON_OPTION + + FF_MPEG4_PROFILE_OPTS + + { "level", "Specify tier and level", + OFFSET(level), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, VE, "level" }, + { "0", "Level 0", + 0, AV_OPT_TYPE_CONST, { .i64 = MPEG4Level0 }, 0, 0, VE, "level" }, + { "0b", "Level 0b", + 0, AV_OPT_TYPE_CONST, { .i64 = MPEG4Level0b }, 0, 0, VE, "level" }, + { "1", "Level 1", + 0, AV_OPT_TYPE_CONST, { .i64 = MPEG4Level1 }, 0, 0, VE, "level" }, + { "2", "Level 2", + 0, AV_OPT_TYPE_CONST, { .i64 = MPEG4Level2 }, 0, 0, VE, "level" }, + { "3", "Level 3", + 0, AV_OPT_TYPE_CONST, { .i64 = MPEG4Level3 }, 0, 0, VE, "level" }, + { "3b", "Level 3b", + 0, AV_OPT_TYPE_CONST, { .i64 = MPEG4Level3b }, 0, 0, VE, "level" }, + { "4", "Level 4", + 0, AV_OPT_TYPE_CONST, { .i64 = MPEG4Level4 }, 0, 0, VE, "level" }, + { "4a", "Level 4a", + 0, AV_OPT_TYPE_CONST, { .i64 = MPEG4Level4a }, 0, 0, VE, "level" }, + { "5", "Level 5", + 0, AV_OPT_TYPE_CONST, { .i64 = MPEG4Level5 }, 0, 0, VE, "level" }, + { "6", "Level 6", + 0, AV_OPT_TYPE_CONST, { .i64 = MPEG4Level6 }, 0, 0, VE, "level" }, + { NULL, } +}; + +DECLARE_MEDIACODEC_ENCODER(mpeg4, "MPEG-4", AV_CODEC_ID_MPEG4) + +#endif // CONFIG_MPEG4_MEDIACODEC_ENCODER + +#if CONFIG_AV1_MEDIACODEC_ENCODER + +enum MediaCodecAV1Level { + AV1Level2 = 0x1, + AV1Level21 = 0x2, + AV1Level22 = 0x4, + AV1Level23 = 0x8, + AV1Level3 = 0x10, + AV1Level31 = 0x20, + AV1Level32 = 0x40, + AV1Level33 = 0x80, + AV1Level4 = 0x100, + AV1Level41 = 0x200, + AV1Level42 = 0x400, + AV1Level43 = 0x800, + AV1Level5 = 0x1000, + AV1Level51 = 0x2000, + AV1Level52 = 0x4000, + AV1Level53 = 0x8000, + AV1Level6 = 0x10000, + AV1Level61 = 0x20000, + AV1Level62 = 0x40000, + AV1Level63 = 0x80000, + AV1Level7 = 0x100000, + AV1Level71 = 0x200000, + AV1Level72 = 0x400000, + AV1Level73 = 0x800000, +}; + +static const AVOption av1_options[] = { + COMMON_OPTION + + FF_AV1_PROFILE_OPTS + + { "level", "Specify tier and level", + OFFSET(level), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, VE, "level" }, + { "2", "Level 2", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level2 }, 0, 0, VE, "level" }, + { "2.1", "Level 2.1", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level21 }, 0, 0, VE, "level" }, + { "2.2", "Level 2.2", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level22 }, 0, 0, VE, "level" }, + { "2.3", "Level 2.3", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level23 }, 0, 0, VE, "level" }, + { "3", "Level 3", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level3 }, 0, 0, VE, "level" }, + { "3.1", "Level 3.1", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level31 }, 0, 0, VE, "level" }, + { "3.2", "Level 3.2", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level32 }, 0, 0, VE, "level" }, + { "3.3", "Level 3.3", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level33 }, 0, 0, VE, "level" }, + { "4", "Level 4", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level4 }, 0, 0, VE, "level" }, + { "4.1", "Level 4.1", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level41 }, 0, 0, VE, "level" }, + { "4.2", "Level 4.2", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level42 }, 0, 0, VE, "level" }, + { "4.3", "Level 4.3", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level43 }, 0, 0, VE, "level" }, + { "5", "Level 5", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level5 }, 0, 0, VE, "level" }, + { "5.1", "Level 5.1", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level51 }, 0, 0, VE, "level" }, + { "5.2", "Level 5.2", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level52 }, 0, 0, VE, "level" }, + { "5.3", "Level 5.3", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level53 }, 0, 0, VE, "level" }, + { "6", "Level 6", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level6 }, 0, 0, VE, "level" }, + { "6.1", "Level 6.1", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level61 }, 0, 0, VE, "level" }, + { "6.2", "Level 6.2", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level62 }, 0, 0, VE, "level" }, + { "6.3", "Level 6.3", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level63 }, 0, 0, VE, "level" }, + { "7", "Level 7", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level7 }, 0, 0, VE, "level" }, + { "7.1", "Level 7.1", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level71 }, 0, 0, VE, "level" }, + { "7.2", "Level 7.2", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level72 }, 0, 0, VE, "level" }, + { "7.3", "Level 7.3", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level73 }, 0, 0, VE, "level" }, + { NULL, } +}; + +DECLARE_MEDIACODEC_ENCODER(av1, "AV1", AV_CODEC_ID_AV1) + +#endif // CONFIG_AV1_MEDIACODEC_ENCODER diff --git a/libavcodec/mfenc.c b/libavcodec/mfenc.c index 36a6d8482d1..8d950a3109f 100644 --- a/libavcodec/mfenc.c +++ b/libavcodec/mfenc.c @@ -659,7 +659,11 @@ static int mf_encv_output_adjust(AVCodecContext *avctx, IMFMediaType *type) framerate = avctx->framerate; } else { framerate = av_inv_q(avctx->time_base); +#if FF_API_TICKS_PER_FRAME +FF_DISABLE_DEPRECATION_WARNINGS framerate.den *= avctx->ticks_per_frame; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } ff_MFSetAttributeRatio((IMFAttributes *)type, &MF_MT_FRAME_RATE, framerate.num, framerate.den); @@ -1214,7 +1218,6 @@ static int mf_init(AVCodecContext *avctx) return 0; } } - mf_close(avctx); return ret; } diff --git a/libavcodec/midivid.c b/libavcodec/midivid.c index 599d5c8f8fa..70730231b5f 100644 --- a/libavcodec/midivid.c +++ b/libavcodec/midivid.c @@ -222,7 +222,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, return ret; frame->pict_type = key ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; - frame->key_frame = key; + if (key) + frame->flags |= AV_FRAME_FLAG_KEY; + else + frame->flags &= ~AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; diff --git a/libavcodec/mips/cabac.h b/libavcodec/mips/cabac.h index 39c308c7e0d..20ecab43207 100644 --- a/libavcodec/mips/cabac.h +++ b/libavcodec/mips/cabac.h @@ -30,6 +30,7 @@ #include "libavutil/mips/mmiutils.h" #include "config.h" +#if !HAVE_MIPS32R6 && !HAVE_MIPS64R6 #define get_cabac_inline get_cabac_inline_mips static av_always_inline int get_cabac_inline_mips(CABACContext *c, uint8_t * const state){ @@ -225,4 +226,5 @@ static av_always_inline int get_cabac_bypass_sign_mips(CABACContext *c, int val) return res; } +#endif /* !HAVE_MIPS32R6 && !HAVE_MIPS64R6 */ #endif /* AVCODEC_MIPS_CABAC_H */ diff --git a/libavcodec/mips/compute_antialias_fixed.h b/libavcodec/mips/compute_antialias_fixed.h index 1f395d23027..982002ad4cd 100644 --- a/libavcodec/mips/compute_antialias_fixed.h +++ b/libavcodec/mips/compute_antialias_fixed.h @@ -56,6 +56,7 @@ #define AVCODEC_MIPS_COMPUTE_ANTIALIAS_FIXED_H #if HAVE_INLINE_ASM +#if !HAVE_MIPS32R6 && !HAVE_MIPS64R6 static void compute_antialias_mips_fixed(MPADecodeContext *s, GranuleDef *g) { @@ -246,6 +247,7 @@ static void compute_antialias_mips_fixed(MPADecodeContext *s, } } #define compute_antialias compute_antialias_mips_fixed +#endif /* !HAVE_MIPS32R6 && !HAVE_MIPS64R6 */ #endif /* HAVE_INLINE_ASM */ #endif /* AVCODEC_MIPS_COMPUTE_ANTIALIAS_FIXED_H */ diff --git a/libavcodec/misc4.c b/libavcodec/misc4.c index 1bf162e1202..72ac944e54f 100644 --- a/libavcodec/misc4.c +++ b/libavcodec/misc4.c @@ -179,7 +179,10 @@ const FFCodec ff_misc4_decoder = { .priv_data_size = sizeof(MISC4Context), .init = misc4_init, FF_CODEC_DECODE_CB(misc4_decode), - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SUBFRAMES | + .p.capabilities = AV_CODEC_CAP_DR1 | +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif AV_CODEC_CAP_CHANNEL_CONF, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE }, diff --git a/libavcodec/mjpegbdec.c b/libavcodec/mjpegbdec.c index 6d422e7a29d..4db1d9a89d7 100644 --- a/libavcodec/mjpegbdec.c +++ b/libavcodec/mjpegbdec.c @@ -168,3 +168,18 @@ const FFCodec ff_mjpegb_decoder = { .p.max_lowres = 3, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, }; + +const FFCodec ff_media100_decoder = { + .p.name = "media100", + CODEC_LONG_NAME("Media 100"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MEDIA100, + .priv_data_size = sizeof(MJpegDecodeContext), + .init = ff_mjpeg_decode_init, + .close = ff_mjpeg_decode_end, + FF_CODEC_DECODE_CB(mjpegb_decode_frame), + .p.capabilities = AV_CODEC_CAP_DR1, + .p.max_lowres = 3, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, + .bsfs = "media100_to_mjpegb", +}; diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index f33911e1a86..cef8625791d 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -41,6 +41,7 @@ #include "codec_internal.h" #include "copy_block.h" #include "decode.h" +#include "hwaccel_internal.h" #include "hwconfig.h" #include "idctdsp.h" #include "internal.h" @@ -436,13 +437,13 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) /* test interlaced mode */ if (s->first_picture && - (s->multiscope != 2 || s->avctx->time_base.den >= 25 * s->avctx->time_base.num) && + (s->multiscope != 2 || s->avctx->pkt_timebase.den >= 25 * s->avctx->pkt_timebase.num) && s->orig_height != 0 && s->height < ((s->orig_height * 3) / 4)) { s->interlaced = 1; s->bottom_field = s->interlace_polarity; - s->picture_ptr->interlaced_frame = 1; - s->picture_ptr->top_field_first = !s->interlace_polarity; + s->picture_ptr->flags |= AV_FRAME_FLAG_INTERLACED; + s->picture_ptr->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST * !s->interlace_polarity; height *= 2; } @@ -545,6 +546,18 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) } av_assert0(s->nb_components == 4); break; + case 0x11412100: + if (s->bits > 8) + goto unk_pixfmt; + if (s->component_id[0] == 'R' && s->component_id[1] == 'G' && s->component_id[2] == 'B') { + s->avctx->pix_fmt = AV_PIX_FMT_GBRP; + s->upscale_h[0] = 4; + s->upscale_h[1] = 0; + s->upscale_h[2] = 1; + } else { + goto unk_pixfmt; + } + break; case 0x22111122: case 0x22111111: if (s->adobe_transform == 0 && s->bits <= 8) { @@ -571,10 +584,15 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) case 0x22221100: case 0x22112200: case 0x11222200: - if (s->bits <= 8) s->avctx->pix_fmt = s->cs_itu601 ? AV_PIX_FMT_YUV444P : AV_PIX_FMT_YUVJ444P; - else + if (s->bits > 8) goto unk_pixfmt; - s->avctx->color_range = s->cs_itu601 ? AVCOL_RANGE_MPEG : AVCOL_RANGE_JPEG; + if (s->adobe_transform == 0 || s->component_id[0] == 'R' && + s->component_id[1] == 'G' && s->component_id[2] == 'B') { + s->avctx->pix_fmt = AV_PIX_FMT_GBRP; + } else { + s->avctx->pix_fmt = s->cs_itu601 ? AV_PIX_FMT_YUV444P : AV_PIX_FMT_YUVJ444P; + s->avctx->color_range = s->cs_itu601 ? AVCOL_RANGE_MPEG : AVCOL_RANGE_JPEG; + } break; case 0x11000000: case 0x13000000: @@ -599,7 +617,7 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) if (s->bits <= 8) s->avctx->pix_fmt = AV_PIX_FMT_GBRP; else goto unk_pixfmt; - s->upscale_v[0] = s->upscale_v[1] = 1; + s->upscale_v[1] = s->upscale_v[2] = 1; } else { if (pix_fmt_id == 0x14111100) s->upscale_v[1] = s->upscale_v[2] = 1; @@ -614,13 +632,22 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) if (s->bits <= 8) s->avctx->pix_fmt = AV_PIX_FMT_GBRP; else goto unk_pixfmt; - s->upscale_h[0] = s->upscale_h[1] = 1; + s->upscale_h[1] = s->upscale_h[2] = 1; } else { if (s->bits <= 8) s->avctx->pix_fmt = s->cs_itu601 ? AV_PIX_FMT_YUV422P : AV_PIX_FMT_YUVJ422P; else s->avctx->pix_fmt = AV_PIX_FMT_YUV422P16; s->avctx->color_range = s->cs_itu601 ? AVCOL_RANGE_MPEG : AVCOL_RANGE_JPEG; } break; + case 0x11311100: + if (s->bits > 8) + goto unk_pixfmt; + if (s->component_id[0] == 'R' && s->component_id[1] == 'G' && s->component_id[2] == 'B') + s->avctx->pix_fmt = AV_PIX_FMT_GBRP; + else + goto unk_pixfmt; + s->upscale_h[0] = s->upscale_h[2] = 2; + break; case 0x31111100: if (s->bits > 8) goto unk_pixfmt; @@ -630,6 +657,7 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) break; case 0x22121100: case 0x22111200: + case 0x41211100: if (s->bits <= 8) s->avctx->pix_fmt = s->cs_itu601 ? AV_PIX_FMT_YUV422P : AV_PIX_FMT_YUVJ422P; else goto unk_pixfmt; @@ -673,10 +701,6 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) avpriv_report_missing_feature(s->avctx, "Lowres for weird subsampling"); return AVERROR_PATCHWELCOME; } - if ((AV_RB32(s->upscale_h) || AV_RB32(s->upscale_v)) && s->progressive && s->avctx->pix_fmt == AV_PIX_FMT_GBRP) { - avpriv_report_missing_feature(s->avctx, "progressive for weird subsampling"); - return AVERROR_PATCHWELCOME; - } if (s->ls) { memset(s->upscale_h, 0, sizeof(s->upscale_h)); memset(s->upscale_v, 0, sizeof(s->upscale_v)); @@ -722,7 +746,7 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) if (s->avctx->skip_frame == AVDISCARD_ALL) { s->picture_ptr->pict_type = AV_PICTURE_TYPE_I; - s->picture_ptr->key_frame = 1; + s->picture_ptr->flags |= AV_FRAME_FLAG_KEY; s->got_picture = 1; return 0; } @@ -731,7 +755,7 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) if (ff_get_buffer(s->avctx, s->picture_ptr, AV_GET_BUFFER_FLAG_REF) < 0) return -1; s->picture_ptr->pict_type = AV_PICTURE_TYPE_I; - s->picture_ptr->key_frame = 1; + s->picture_ptr->flags |= AV_FRAME_FLAG_KEY; s->got_picture = 1; // Lets clear the palette to avoid leaving uninitialized values in it @@ -773,13 +797,14 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) } if (s->avctx->hwaccel) { + const FFHWAccel *hwaccel = ffhwaccel(s->avctx->hwaccel); s->hwaccel_picture_private = - av_mallocz(s->avctx->hwaccel->frame_priv_data_size); + av_mallocz(hwaccel->frame_priv_data_size); if (!s->hwaccel_picture_private) return AVERROR(ENOMEM); - ret = s->avctx->hwaccel->start_frame(s->avctx, s->raw_image_buffer, - s->raw_image_buffer_size); + ret = hwaccel->start_frame(s->avctx, s->raw_image_buffer, + s->raw_image_buffer_size); if (ret < 0) return ret; } @@ -1697,9 +1722,6 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, s->h_scount[i] = s->h_count[index]; s->v_scount[i] = s->v_count[index]; - if((nb_components == 1 || nb_components == 3) && s->nb_components == 3 && s->avctx->pix_fmt == AV_PIX_FMT_GBR24P) - index = (index+2)%3; - s->comp_index[i] = index; s->dc_index[i] = get_bits(&s->gb, 4); @@ -1754,9 +1776,9 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, av_assert0(bytes_to_start >= 0 && s->raw_scan_buffer_size >= bytes_to_start); - ret = s->avctx->hwaccel->decode_slice(s->avctx, - s->raw_scan_buffer + bytes_to_start, - s->raw_scan_buffer_size - bytes_to_start); + ret = FF_HW_CALL(s->avctx, decode_slice, + s->raw_scan_buffer + bytes_to_start, + s->raw_scan_buffer_size - bytes_to_start); if (ret < 0) return ret; @@ -2527,7 +2549,7 @@ int ff_mjpeg_decode_frame_from_buf(AVCodecContext *avctx, AVFrame *frame, goto the_end_no_picture; } if (s->avctx->hwaccel) { - ret = s->avctx->hwaccel->end_frame(s->avctx); + ret = FF_HW_SIMPLE_CALL(s->avctx, end_frame); if (ret < 0) return ret; @@ -2607,6 +2629,8 @@ int ff_mjpeg_decode_frame_from_buf(AVCodecContext *avctx, AVFrame *frame, avctx->pix_fmt == AV_PIX_FMT_YUVJ440P || avctx->pix_fmt == AV_PIX_FMT_YUV440P || avctx->pix_fmt == AV_PIX_FMT_YUVA444P || + avctx->pix_fmt == AV_PIX_FMT_YUVJ422P || + avctx->pix_fmt == AV_PIX_FMT_YUV422P || avctx->pix_fmt == AV_PIX_FMT_YUVJ420P || avctx->pix_fmt == AV_PIX_FMT_YUV420P || avctx->pix_fmt == AV_PIX_FMT_YUV420P16|| @@ -2656,6 +2680,24 @@ int ff_mjpeg_decode_frame_from_buf(AVCodecContext *avctx, AVFrame *frame, for (index = w - 3; index > 0; index--) { line[index] = (line[index / 3] + line[(index + 1) / 3] + line[(index + 2) / 3] + 1) / 3; } + } else if (s->upscale_h[p] == 4){ + if (is16bit) { + uint16_t *line16 = (uint16_t *) line; + line16[w - 1] = line16[(w - 1) >> 2]; + if (w > 1) + line16[w - 2] = (line16[(w - 1) >> 2] * 3 + line16[(w - 2) >> 2]) >> 2; + if (w > 2) + line16[w - 3] = (line16[(w - 1) >> 2] + line16[(w - 2) >> 2]) >> 1; + } else { + line[w - 1] = line[(w - 1) >> 2]; + if (w > 1) + line[w - 2] = (line[(w - 1) >> 2] * 3 + line[(w - 2) >> 2]) >> 2; + if (w > 2) + line[w - 3] = (line[(w - 1) >> 2] + line[(w - 2) >> 2]) >> 1; + } + for (index = w - 4; index > 0; index--) + line[index] = (line[(index + 3) >> 2] + line[(index + 2) >> 2] + + line[(index + 1) >> 2] + line[index >> 2]) >> 2; } line += s->linesize[p]; } @@ -2722,6 +2764,15 @@ int ff_mjpeg_decode_frame_from_buf(AVCodecContext *avctx, AVFrame *frame, } } } + + if (s->avctx->pix_fmt == AV_PIX_FMT_GBRP) { + av_assert0(s->nb_components == 3); + FFSWAP(uint8_t *, frame->data[0], frame->data[2]); + FFSWAP(uint8_t *, frame->data[0], frame->data[1]); + FFSWAP(int, frame->linesize[0], frame->linesize[2]); + FFSWAP(int, frame->linesize[0], frame->linesize[1]); + } + if (s->adobe_transform == 0 && s->avctx->pix_fmt == AV_PIX_FMT_GBRAP) { int w = s->picture_ptr->width; int h = s->picture_ptr->height; @@ -2884,7 +2935,7 @@ av_cold int ff_mjpeg_decode_end(AVCodecContext *avctx) MJpegDecodeContext *s = avctx->priv_data; int i, j; - if (s->interlaced && s->bottom_field == !s->interlace_polarity && s->got_picture && !avctx->frame_number) { + if (s->interlaced && s->bottom_field == !s->interlace_polarity && s->got_picture && !avctx->frame_num) { av_log(avctx, AV_LOG_INFO, "Single field\n"); } @@ -3003,6 +3054,8 @@ static void smv_process_frame(AVCodecContext *avctx, AVFrame *frame) frame->crop_top = FFMIN(s->smv_next_frame * avctx->height, frame->height); frame->crop_bottom = frame->height - (s->smv_next_frame + 1) * avctx->height; + if (s->smv_frame->pts != AV_NOPTS_VALUE) + s->smv_frame->pts += s->smv_frame->duration; s->smv_next_frame = (s->smv_next_frame + 1) % s->smv_frames_per_jpeg; if (s->smv_next_frame == 0) @@ -3013,26 +3066,20 @@ static int smvjpeg_receive_frame(AVCodecContext *avctx, AVFrame *frame) { MJpegDecodeContext *s = avctx->priv_data; AVPacket *const pkt = avctx->internal->in_pkt; - int64_t pkt_dts; int got_frame = 0; int ret; - if (s->smv_next_frame > 0) { - av_assert0(s->smv_frame->buf[0]); - ret = av_frame_ref(frame, s->smv_frame); - if (ret < 0) - return ret; - - smv_process_frame(avctx, frame); - return 0; - } + if (s->smv_next_frame > 0) + goto return_frame; ret = ff_decode_get_packet(avctx, pkt); if (ret < 0) return ret; - ret = ff_mjpeg_decode_frame(avctx, frame, &got_frame, pkt); - pkt_dts = pkt->dts; + av_frame_unref(s->smv_frame); + + ret = ff_mjpeg_decode_frame(avctx, s->smv_frame, &got_frame, pkt); + s->smv_frame->pkt_dts = pkt->dts; av_packet_unref(pkt); if (ret < 0) return ret; @@ -3040,11 +3087,12 @@ static int smvjpeg_receive_frame(AVCodecContext *avctx, AVFrame *frame) if (!got_frame) return AVERROR(EAGAIN); - frame->pkt_dts = pkt_dts; + // packet duration covers all the frames in the packet + s->smv_frame->duration /= s->smv_frames_per_jpeg; - av_assert0(frame->buf[0]); - av_frame_unref(s->smv_frame); - ret = av_frame_ref(s->smv_frame, frame); +return_frame: + av_assert0(s->smv_frame->buf[0]); + ret = av_frame_ref(frame, s->smv_frame); if (ret < 0) return ret; @@ -3064,6 +3112,6 @@ const FFCodec ff_smvjpeg_decoder = { .flush = decode_flush, .p.capabilities = AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_EXPORTS_CROPPING | - FF_CODEC_CAP_SETS_PKT_DTS | FF_CODEC_CAP_INIT_CLEANUP, + FF_CODEC_CAP_INIT_CLEANUP, }; #endif diff --git a/libavcodec/mjpegenc.c b/libavcodec/mjpegenc.c index eafe7130e2c..508772987f3 100644 --- a/libavcodec/mjpegenc.c +++ b/libavcodec/mjpegenc.c @@ -651,7 +651,8 @@ const FFCodec ff_mjpeg_encoder = { .init = ff_mpv_encode_init, FF_CODEC_ENCODE_CB(ff_mpv_encode_picture), .close = mjpeg_encode_close, - .p.capabilities = AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_FRAME_THREADS, + .p.capabilities = AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_ICC_PROFILES, .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, @@ -685,5 +686,6 @@ const FFCodec ff_amv_encoder = { AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_NONE }, .p.priv_class = &amv_class, + .p.capabilities = AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, }; #endif diff --git a/libavcodec/mlp_parse.c b/libavcodec/mlp_parse.c index 45715352c28..924c731439d 100644 --- a/libavcodec/mlp_parse.c +++ b/libavcodec/mlp_parse.c @@ -159,7 +159,11 @@ int ff_mlp_read_major_sync(void *log, MLPHeaderInfo *mh, GetBitContext *gb) mh->num_substreams = get_bits(gb, 4); - skip_bits_long(gb, 4 + (header_size - 17) * 8); + skip_bits(gb, 2); + mh->extended_substream_info = get_bits(gb, 2); + mh->substream_info = get_bits(gb, 8); + + skip_bits_long(gb, (header_size - 18) * 8); return 0; } diff --git a/libavcodec/mlp_parse.h b/libavcodec/mlp_parse.h index f0d7b41c119..fa6e3d52dcc 100644 --- a/libavcodec/mlp_parse.h +++ b/libavcodec/mlp_parse.h @@ -58,6 +58,9 @@ typedef struct MLPHeaderInfo int peak_bitrate; ///< Peak bitrate for VBR, actual bitrate (==peak) for CBR int num_substreams; ///< Number of substreams within stream + + int extended_substream_info; ///< Which substream of substreams carry 16-channel presentation + int substream_info; ///< Which substream of substreams carry 2/6/8-channel presentation } MLPHeaderInfo; static const uint8_t thd_chancount[13] = { diff --git a/libavcodec/mlpdec.c b/libavcodec/mlpdec.c index 5b14a3b03ba..966ee0f0a2f 100644 --- a/libavcodec/mlpdec.c +++ b/libavcodec/mlpdec.c @@ -42,6 +42,7 @@ #include "mlpdsp.h" #include "mlp.h" #include "config.h" +#include "profiles.h" /** number of bits used for VLC lookup - longest Huffman code is 9 */ #if ARCH_ARM @@ -153,6 +154,12 @@ typedef struct MLPDecodeContext { /// Number of substreams contained within this stream. uint8_t num_substreams; + /// Which substream of substreams carry 16-channel presentation + uint8_t extended_substream_info; + + /// Which substream of substreams carry 2/6/8-channel presentation + uint8_t substream_info; + /// Index of the last substream to decode - further substreams are skipped. uint8_t max_decoded_substream; @@ -384,6 +391,15 @@ static int read_major_sync(MLPDecodeContext *m, GetBitContext *gb) m->access_unit_size_pow2 = mh.access_unit_size_pow2; m->num_substreams = mh.num_substreams; + m->substream_info = mh.substream_info; + + /* If there is a 4th substream and the MSB of substream_info is set, + * there is a 16-channel spatial presentation (Atmos in TrueHD). + */ + if (m->avctx->codec_id == AV_CODEC_ID_TRUEHD + && m->num_substreams == 4 && m->substream_info >> 7 == 1) { + m->avctx->profile = FF_PROFILE_TRUEHD_ATMOS; + } /* limit to decoding 3 substreams, as the 4th is used by Dolby Atmos for non-audio data */ m->max_decoded_substream = FFMIN(m->num_substreams - 1, 2); @@ -1286,7 +1302,13 @@ static int read_access_unit(AVCodecContext *avctx, AVFrame *frame, if (!s->restart_seen) goto next_substr; - if (substr > 0 && substr < m->max_decoded_substream && + if (((avctx->ch_layout.nb_channels == 6 && + ((m->substream_info >> 2) & 0x3) != 0x3) || + (avctx->ch_layout.nb_channels == 8 && + ((m->substream_info >> 4) & 0x7) != 0x7 && + ((m->substream_info >> 4) & 0x7) != 0x6 && + ((m->substream_info >> 4) & 0x7) != 0x3)) && + substr > 0 && substr < m->max_decoded_substream && (s->min_channel <= m->substream[substr - 1].max_channel)) { av_log(avctx, AV_LOG_DEBUG, "Previous substream(%d) channels overlaps current substream(%d) channels, skipping.\n", @@ -1439,5 +1461,6 @@ const FFCodec ff_truehd_decoder = { FF_CODEC_DECODE_CB(read_access_unit), .flush = mlp_decode_flush, .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_CHANNEL_CONF, + .p.profiles = NULL_IF_CONFIG_SMALL(ff_truehd_profiles), }; #endif /* CONFIG_TRUEHD_DECODER */ diff --git a/libavcodec/mlpenc.c b/libavcodec/mlpenc.c index 1bc8995c58b..5995a6b51c6 100644 --- a/libavcodec/mlpenc.c +++ b/libavcodec/mlpenc.c @@ -2118,7 +2118,7 @@ static int mlp_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, data = frame ? frame->data[0] : NULL; - ctx->frame_index = avctx->frame_number % ctx->max_restart_interval; + ctx->frame_index = avctx->frame_num % ctx->max_restart_interval; ctx->inout_buffer = ctx->major_inout_buffer + ctx->frame_index * ctx->one_sample_buffer_size; @@ -2128,7 +2128,7 @@ static int mlp_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, ctx->write_buffer = ctx->inout_buffer; - if (avctx->frame_number < ctx->max_restart_interval) { + if (avctx->frame_num < ctx->max_restart_interval) { if (data) goto input_and_return; } @@ -2199,7 +2199,7 @@ static int mlp_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, } if (!frame && ctx->last_frames < ctx->max_restart_interval - 1) - avctx->frame_number++; + avctx->frame_num++; if (bytes_written > 0) { ff_af_queue_remove(&ctx->afq, diff --git a/libavcodec/mmaldec.c b/libavcodec/mmaldec.c index 3092f585107..8d67d10cd92 100644 --- a/libavcodec/mmaldec.c +++ b/libavcodec/mmaldec.c @@ -622,8 +622,10 @@ static int ffmal_copy_frame(AVCodecContext *avctx, AVFrame *frame, MMALDecodeContext *ctx = avctx->priv_data; int ret = 0; - frame->interlaced_frame = ctx->interlaced_frame; - frame->top_field_first = ctx->top_field_first; + if (ctx->interlaced_frame) + frame->flags |= AV_FRAME_FLAG_INTERLACED; + if (ctx->top_field_first) + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; if (avctx->pix_fmt == AV_PIX_FMT_MMAL) { if (!ctx->pool_out) @@ -841,8 +843,7 @@ static const AVClass ffmmal_dec_class = { .flush = ffmmal_flush, \ .p.priv_class = &ffmmal_dec_class, \ .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE, \ - .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | \ - FF_CODEC_CAP_SETS_PKT_DTS, \ + .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE \ .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_MMAL, \ AV_PIX_FMT_YUV420P, \ AV_PIX_FMT_NONE}, \ diff --git a/libavcodec/mobiclip.c b/libavcodec/mobiclip.c index c3b2383dbc4..381b492de3a 100644 --- a/libavcodec/mobiclip.c +++ b/libavcodec/mobiclip.c @@ -1235,7 +1235,7 @@ static int mobiclip_decode(AVCodecContext *avctx, AVFrame *rframe, if (get_bits1(gb)) { frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; s->moflex = get_bits1(gb); s->dct_tab_idx = get_bits1(gb); @@ -1256,7 +1256,7 @@ static int mobiclip_decode(AVCodecContext *avctx, AVFrame *rframe, memset(motion, 0, s->motion_size); frame->pict_type = AV_PICTURE_TYPE_P; - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; s->dct_tab_idx = 0; ret = setup_qtables(avctx, s->quantizer + (int64_t)get_se_golomb(gb)); diff --git a/libavcodec/motion_est.c b/libavcodec/motion_est.c index d17ffe42b43..df9d1befa83 100644 --- a/libavcodec/motion_est.c +++ b/libavcodec/motion_est.c @@ -309,6 +309,7 @@ int ff_init_me(MpegEncContext *s){ MotionEstContext * const c= &s->me; int cache_size= FFMIN(ME_MAP_SIZE>>ME_MAP_SHIFT, 1<avctx->dia_size)&255, FFABS(s->avctx->pre_dia_size)&255); + int ret; if(FFMIN(s->avctx->dia_size, s->avctx->pre_dia_size) < -FFMIN(ME_MAP_SIZE, MAX_SAB_SIZE)){ av_log(s->avctx, AV_LOG_ERROR, "ME_MAP size is too small for SAB diamond\n"); @@ -324,10 +325,12 @@ int ff_init_me(MpegEncContext *s){ av_log(s->avctx, AV_LOG_INFO, "ME_MAP size may be a little small for the selected diamond size\n"); } - ff_set_cmp(&s->mecc, s->mecc.me_pre_cmp, c->avctx->me_pre_cmp); - ff_set_cmp(&s->mecc, s->mecc.me_cmp, c->avctx->me_cmp); - ff_set_cmp(&s->mecc, s->mecc.me_sub_cmp, c->avctx->me_sub_cmp); - ff_set_cmp(&s->mecc, s->mecc.mb_cmp, c->avctx->mb_cmp); + ret = ff_set_cmp(&s->mecc, s->mecc.me_pre_cmp, c->avctx->me_pre_cmp); + ret |= ff_set_cmp(&s->mecc, s->mecc.me_cmp, c->avctx->me_cmp); + ret |= ff_set_cmp(&s->mecc, s->mecc.me_sub_cmp, c->avctx->me_sub_cmp); + ret |= ff_set_cmp(&s->mecc, s->mecc.mb_cmp, c->avctx->mb_cmp); + if (ret < 0) + return ret; c->flags = get_flags(c, 0, c->avctx->me_cmp &FF_CMP_CHROMA); c->sub_flags= get_flags(c, 0, c->avctx->me_sub_cmp&FF_CMP_CHROMA); diff --git a/libavcodec/motionpixels.c b/libavcodec/motionpixels.c index 4141c5a4957..a947ca05de7 100644 --- a/libavcodec/motionpixels.c +++ b/libavcodec/motionpixels.c @@ -185,7 +185,7 @@ static YuvPixel mp_get_yuv_from_rgb(MotionPixelsContext *mp, int x, int y) int color; color = *(uint16_t *)&mp->frame->data[0][y * mp->frame->linesize[0] + x * 2]; - return mp_rgb_yuv_table[color]; + return mp_rgb_yuv_table[color & 0x7FFF]; } static void mp_set_rgb_from_yuv(MotionPixelsContext *mp, int x, int y, const YuvPixel *p) diff --git a/libavcodec/mpeg12.c b/libavcodec/mpeg12.c index 5d5f39388f9..a256d45c85b 100644 --- a/libavcodec/mpeg12.c +++ b/libavcodec/mpeg12.c @@ -167,72 +167,6 @@ av_cold void ff_mpeg12_init_vlcs(void) ff_thread_once(&init_static_once, mpeg12_init_vlcs); } -#if FF_API_FLAG_TRUNCATED -/** - * Find the end of the current frame in the bitstream. - * @return the position of the first byte of the next frame, or -1 - */ -int ff_mpeg1_find_frame_end(ParseContext *pc, const uint8_t *buf, int buf_size, AVCodecParserContext *s) -{ - int i; - uint32_t state = pc->state; - - /* EOF considered as end of frame */ - if (buf_size == 0) - return 0; - -/* - 0 frame start -> 1/4 - 1 first_SEQEXT -> 0/2 - 2 first field start -> 3/0 - 3 second_SEQEXT -> 2/0 - 4 searching end -*/ - - for (i = 0; i < buf_size; i++) { - av_assert1(pc->frame_start_found >= 0 && pc->frame_start_found <= 4); - if (pc->frame_start_found & 1) { - if (state == EXT_START_CODE && (buf[i] & 0xF0) != 0x80) - pc->frame_start_found--; - else if (state == EXT_START_CODE + 2) { - if ((buf[i] & 3) == 3) - pc->frame_start_found = 0; - else - pc->frame_start_found = (pc->frame_start_found + 1) & 3; - } - state++; - } else { - i = avpriv_find_start_code(buf + i, buf + buf_size, &state) - buf - 1; - if (pc->frame_start_found == 0 && state >= SLICE_MIN_START_CODE && state <= SLICE_MAX_START_CODE) { - i++; - pc->frame_start_found = 4; - } - if (state == SEQ_END_CODE) { - pc->frame_start_found = 0; - pc->state=-1; - return i+1; - } - if (pc->frame_start_found == 2 && state == SEQ_START_CODE) - pc->frame_start_found = 0; - if (pc->frame_start_found < 4 && state == EXT_START_CODE) - pc->frame_start_found++; - if (pc->frame_start_found == 4 && (state & 0xFFFFFF00) == 0x100) { - if (state < SLICE_MIN_START_CODE || state > SLICE_MAX_START_CODE) { - pc->frame_start_found = 0; - pc->state = -1; - return i - 3; - } - } - if (pc->frame_start_found == 0 && s && state == PICTURE_START_CODE) { - ff_fetch_timestamp(s, i - 3, 1, i > 3); - } - } - } - pc->state = state; - return END_NOT_FOUND; -} -#endif - #define MAX_INDEX (64 - 1) int ff_mpeg1_decode_block_intra(GetBitContext *gb, diff --git a/libavcodec/mpeg12.h b/libavcodec/mpeg12.h index 4e2e67eae18..86dd627e954 100644 --- a/libavcodec/mpeg12.h +++ b/libavcodec/mpeg12.h @@ -34,15 +34,6 @@ #define EXT_START_CODE 0x000001b5 #define USER_START_CODE 0x000001b2 -#include "version_major.h" -#if FF_API_FLAG_TRUNCATED -#include - -struct ParseContext; -struct AVCodecParserContext; -int ff_mpeg1_find_frame_end(struct ParseContext *pc, const uint8_t *buf, int buf_size, struct AVCodecParserContext *s); -#endif - void ff_mpeg12_find_best_frame_rate(AVRational frame_rate, int *code, int *ext_n, int *ext_d, int nonstandard); diff --git a/libavcodec/mpeg12dec.c b/libavcodec/mpeg12dec.c index 9999926f55d..e645b8e24ac 100644 --- a/libavcodec/mpeg12dec.c +++ b/libavcodec/mpeg12dec.c @@ -42,6 +42,7 @@ #include "codec_internal.h" #include "decode.h" #include "error_resilience.h" +#include "hwaccel_internal.h" #include "hwconfig.h" #include "idctdsp.h" #include "internal.h" @@ -1239,14 +1240,7 @@ static int mpeg_decode_postinit(AVCodecContext *avctx) (s1->save_progressive_seq != s->progressive_sequence && FFALIGN(s->height, 16) != FFALIGN(s->height, 32)) || 0) { if (s1->mpeg_enc_ctx_allocated) { -#if FF_API_FLAG_TRUNCATED - ParseContext pc = s->parse_context; - s->parse_context.buffer = 0; ff_mpv_common_end(s); - s->parse_context = pc; -#else - ff_mpv_common_end(s); -#endif s1->mpeg_enc_ctx_allocated = 0; } @@ -1272,7 +1266,11 @@ static int mpeg_decode_postinit(AVCodecContext *avctx) if (avctx->codec_id == AV_CODEC_ID_MPEG1VIDEO) { // MPEG-1 fps avctx->framerate = ff_mpeg12_frame_rate_tab[s1->frame_rate_index]; +#if FF_API_TICKS_PER_FRAME +FF_DISABLE_DEPRECATION_WARNINGS avctx->ticks_per_frame = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif avctx->chroma_sample_location = AVCHROMA_LOC_CENTER; } else { // MPEG-2 @@ -1282,7 +1280,11 @@ static int mpeg_decode_postinit(AVCodecContext *avctx) 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); +#if FF_API_TICKS_PER_FRAME +FF_DISABLE_DEPRECATION_WARNINGS avctx->ticks_per_frame = 2; +FF_ENABLE_DEPRECATION_WARNINGS +#endif switch (s->chroma_format) { case 1: avctx->chroma_sample_location = AVCHROMA_LOC_LEFT; break; @@ -1350,7 +1352,10 @@ static int mpeg1_decode_picture(AVCodecContext *avctx, const uint8_t *buf, s->mpeg_f_code[1][1] = f_code; } s->current_picture.f->pict_type = s->pict_type; - s->current_picture.f->key_frame = s->pict_type == AV_PICTURE_TYPE_I; + if (s->pict_type == AV_PICTURE_TYPE_I) + s->current_picture.f->flags |= AV_FRAME_FLAG_KEY; + else + s->current_picture.f->flags &= ~AV_FRAME_FLAG_KEY; if (avctx->debug & FF_DEBUG_PICT_INFO) av_log(avctx, AV_LOG_DEBUG, @@ -1532,7 +1537,10 @@ static int mpeg_decode_picture_coding_extension(Mpeg1Context *s1) } else s->pict_type = AV_PICTURE_TYPE_B; s->current_picture.f->pict_type = s->pict_type; - s->current_picture.f->key_frame = s->pict_type == AV_PICTURE_TYPE_I; + if (s->pict_type == AV_PICTURE_TYPE_I) + s->current_picture.f->flags |= AV_FRAME_FLAG_KEY; + else + s->current_picture.f->flags &= ~AV_FRAME_FLAG_KEY; } s->intra_dc_precision = get_bits(&s->gb, 2); @@ -1649,7 +1657,7 @@ static int mpeg_field_start(MpegEncContext *s, const uint8_t *buf, int buf_size) } if (s->avctx->hwaccel) { - if ((ret = s->avctx->hwaccel->end_frame(s->avctx)) < 0) { + 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; @@ -1665,7 +1673,7 @@ static int mpeg_field_start(MpegEncContext *s, const uint8_t *buf, int buf_size) } if (avctx->hwaccel) { - if ((ret = avctx->hwaccel->start_frame(avctx, buf, buf_size)) < 0) + if ((ret = FF_HW_CALL(avctx, start_frame, buf, buf_size)) < 0) return ret; } @@ -1743,14 +1751,14 @@ static int mpeg_decode_slice(MpegEncContext *s, int mb_y, return AVERROR_INVALIDDATA; } - if (avctx->hwaccel && avctx->hwaccel->decode_slice) { + 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->mb_y = mb_y; - if (avctx->hwaccel->decode_slice(avctx, buf_start, buf_end - buf_start) < 0) + 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; @@ -2019,7 +2027,7 @@ static int slice_end(AVCodecContext *avctx, AVFrame *pict) return 0; if (s->avctx->hwaccel) { - int ret = s->avctx->hwaccel->end_frame(s->avctx); + 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"); @@ -2482,11 +2490,7 @@ static int decode_chunks(AVCodecContext *avctx, AVFrame *picture, if (avctx->err_recognition & AV_EF_EXPLODE && s2->er.error_count) return AVERROR_INVALIDDATA; -#if FF_API_FLAG_TRUNCATED - return FFMAX(0, buf_ptr - buf - s2->parse_context.last_index); -#else return FFMAX(0, buf_ptr - buf); -#endif } input_size = buf_end - buf_ptr; @@ -2519,6 +2523,11 @@ static int decode_chunks(AVCodecContext *avctx, AVFrame *picture, } 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); @@ -2799,17 +2808,6 @@ static int mpeg_decode_frame(AVCodecContext *avctx, AVFrame *picture, return buf_size; } -#if FF_API_FLAG_TRUNCATED - if (s2->avctx->flags & AV_CODEC_FLAG_TRUNCATED) { - int next = ff_mpeg1_find_frame_end(&s2->parse_context, buf, - buf_size, NULL); - - if (ff_combine_frame(&s2->parse_context, next, - (const uint8_t **) &buf, &buf_size) < 0) - return buf_size; - } -#endif - if (s->mpeg_enc_ctx_allocated == 0 && ( s2->codec_tag == AV_RL32("VCR2") || s2->codec_tag == AV_RL32("BW10") )) @@ -2886,9 +2884,6 @@ const FFCodec ff_mpeg1video_decoder = { .close = mpeg_decode_end, FF_CODEC_DECODE_CB(mpeg_decode_frame), .p.capabilities = AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_DR1 | -#if FF_API_FLAG_TRUNCATED - AV_CODEC_CAP_TRUNCATED | -#endif AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS, .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM, .flush = flush, @@ -2918,9 +2913,6 @@ const FFCodec ff_mpeg2video_decoder = { .close = mpeg_decode_end, FF_CODEC_DECODE_CB(mpeg_decode_frame), .p.capabilities = AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_DR1 | -#if FF_API_FLAG_TRUNCATED - AV_CODEC_CAP_TRUNCATED | -#endif AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS, .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM, .flush = flush, @@ -2963,9 +2955,6 @@ const FFCodec ff_mpegvideo_decoder = { .close = mpeg_decode_end, FF_CODEC_DECODE_CB(mpeg_decode_frame), .p.capabilities = AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_DR1 | -#if FF_API_FLAG_TRUNCATED - AV_CODEC_CAP_TRUNCATED | -#endif AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS, .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM, .flush = flush, @@ -2987,6 +2976,10 @@ static int ipu_decode_frame(AVCodecContext *avctx, AVFrame *frame, GetBitContext *gb = &m->gb; 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) * (2 + 3*4 + 2*2 + 2*6)) + return AVERROR_INVALIDDATA; + ret = ff_get_buffer(avctx, frame, 0); if (ret < 0) return ret; @@ -3073,7 +3066,7 @@ static int ipu_decode_frame(AVCodecContext *avctx, AVFrame *frame, return AVERROR_INVALIDDATA; frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; diff --git a/libavcodec/mpeg12enc.c b/libavcodec/mpeg12enc.c index 3ad1cd84a16..4aa45e9e869 100644 --- a/libavcodec/mpeg12enc.c +++ b/libavcodec/mpeg12enc.c @@ -249,7 +249,6 @@ static av_cold int encode_init(AVCodecContext *avctx) } } - mpeg12->drop_frame_timecode = mpeg12->drop_frame_timecode || !!(avctx->flags2 & AV_CODEC_FLAG2_DROP_FRAME_TIMECODE); if (mpeg12->drop_frame_timecode) mpeg12->tc.flags |= AV_TIMECODE_FLAG_DROPFRAME; if (mpeg12->drop_frame_timecode && mpeg12->frame_rate_index != 4) { @@ -291,7 +290,7 @@ static void mpeg1_encode_sequence_header(MpegEncContext *s) AVRational aspect_ratio = s->avctx->sample_aspect_ratio; int aspect_ratio_info; - if (!s->current_picture.f->key_frame) + if (!(s->current_picture.f->flags & AV_FRAME_FLAG_KEY)) return; if (aspect_ratio.num == 0 || aspect_ratio.den == 0) @@ -420,10 +419,10 @@ static void mpeg1_encode_sequence_header(MpegEncContext *s) /* time code: we must convert from the real frame rate to a * fake MPEG frame rate in case of low frame rate */ fps = (framerate.num + framerate.den / 2) / framerate.den; - time_code = s->current_picture_ptr->f->coded_picture_number + + time_code = s->current_picture_ptr->coded_picture_number + mpeg12->timecode_frame_start; - mpeg12->gop_picture_number = s->current_picture_ptr->f->coded_picture_number; + mpeg12->gop_picture_number = s->current_picture_ptr->coded_picture_number; av_assert0(mpeg12->drop_frame_timecode == !!(mpeg12->tc.flags & AV_TIMECODE_FLAG_DROPFRAME)); if (mpeg12->drop_frame_timecode) @@ -468,7 +467,7 @@ void ff_mpeg1_encode_slice_header(MpegEncContext *s) put_bits(&s->pb, 1, 0); } -void ff_mpeg1_encode_picture_header(MpegEncContext *s, int picture_number) +void ff_mpeg1_encode_picture_header(MpegEncContext *s) { MPEG12EncContext *const mpeg12 = (MPEG12EncContext*)s; AVFrameSideData *side_data; @@ -531,7 +530,7 @@ void ff_mpeg1_encode_picture_header(MpegEncContext *s, int picture_number) if (s->progressive_sequence) put_bits(&s->pb, 1, 0); /* no repeat */ else - put_bits(&s->pb, 1, s->current_picture_ptr->f->top_field_first); + put_bits(&s->pb, 1, !!(s->current_picture_ptr->f->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)); /* XXX: optimize the generation of this flag with entropy measures */ s->frame_pred_frame_dct = s->progressive_sequence; @@ -1246,7 +1245,8 @@ const FFCodec ff_mpeg1video_encoder = { .p.supported_framerates = ff_mpeg12_frame_rate_tab + 1, .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }, - .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS, + .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .p.priv_class = &mpeg1_class, }; @@ -1264,7 +1264,8 @@ const FFCodec ff_mpeg2video_encoder = { .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_NONE }, - .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS, + .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .p.priv_class = &mpeg2_class, }; diff --git a/libavcodec/mpeg12enc.h b/libavcodec/mpeg12enc.h index 0455e5e4e21..0b35af8a307 100644 --- a/libavcodec/mpeg12enc.h +++ b/libavcodec/mpeg12enc.h @@ -26,7 +26,7 @@ #include "mpegvideo.h" -void ff_mpeg1_encode_picture_header(MpegEncContext *s, int picture_number); +void ff_mpeg1_encode_picture_header(MpegEncContext *s); void ff_mpeg1_encode_mb(MpegEncContext *s, int16_t block[8][64], int motion_x, int motion_y); void ff_mpeg1_encode_init(MpegEncContext *s); diff --git a/libavcodec/mpeg4video_parser.c b/libavcodec/mpeg4video_parser.c index e32a93d2964..28353aa146b 100644 --- a/libavcodec/mpeg4video_parser.c +++ b/libavcodec/mpeg4video_parser.c @@ -27,10 +27,6 @@ #include "mpegvideo.h" #include "mpeg4videodec.h" #include "mpeg4videodefs.h" -#if FF_API_FLAG_TRUNCATED -/* Nuke this header when removing FF_API_FLAG_TRUNCATED */ -#include "mpeg4video_parser.h" -#endif struct Mp4vParseContext { ParseContext pc; @@ -38,15 +34,11 @@ struct Mp4vParseContext { int first_picture; }; -#if FF_API_FLAG_TRUNCATED -int ff_mpeg4_find_frame_end(ParseContext *pc, const uint8_t *buf, int buf_size) -#else /** * Find the end of the current frame in the bitstream. * @return the position of the first byte of the next frame, or -1 */ static int mpeg4_find_frame_end(ParseContext *pc, const uint8_t *buf, int buf_size) -#endif { int vop_found, i; uint32_t state; @@ -114,11 +106,11 @@ static int mpeg4_decode_header(AVCodecParserContext *s1, AVCodecContext *avctx, if (ret < 0) return ret; } - if((s1->flags & PARSER_FLAG_USE_CODEC_TS) && s->avctx->time_base.den>0 && ret>=0){ + if((s1->flags & PARSER_FLAG_USE_CODEC_TS) && s->avctx->framerate.num>0 && ret>=0){ av_assert1(s1->pts == AV_NOPTS_VALUE); av_assert1(s1->dts == AV_NOPTS_VALUE); - s1->pts = av_rescale_q(s->time, (AVRational){1, s->avctx->time_base.den}, (AVRational){1, 1200000}); + s1->pts = av_rescale_q(s->time, (AVRational){1, s->avctx->framerate.num}, (AVRational){1, 1200000}); } s1->pict_type = s->pict_type; @@ -148,11 +140,7 @@ static int mpeg4video_parse(AVCodecParserContext *s, if (s->flags & PARSER_FLAG_COMPLETE_FRAMES) { next = buf_size; } else { -#if FF_API_FLAG_TRUNCATED - next = ff_mpeg4_find_frame_end(pc, buf, buf_size); -#else next = mpeg4_find_frame_end(pc, buf, buf_size); -#endif if (ff_combine_frame(pc, next, &buf, &buf_size) < 0) { *poutbuf = NULL; diff --git a/libavcodec/mpeg4videodec.c b/libavcodec/mpeg4videodec.c index 0a100d2064e..30aec5e5293 100644 --- a/libavcodec/mpeg4videodec.c +++ b/libavcodec/mpeg4videodec.c @@ -1437,7 +1437,7 @@ static inline int mpeg4_decode_block(Mpeg4DecContext *ctx, int16_t *block, if (SHOW_UBITS(re, &s->gb, 1) == 0) { av_log(s->avctx, AV_LOG_ERROR, "1. marker bit missing in 3. esc\n"); - if (!(s->avctx->err_recognition & AV_EF_IGNORE_ERR)) + if (!(s->avctx->err_recognition & AV_EF_IGNORE_ERR) || get_bits_left(&s->gb) <= 0) return AVERROR_INVALIDDATA; } SKIP_CACHE(re, &s->gb, 1); @@ -1448,7 +1448,7 @@ static inline int mpeg4_decode_block(Mpeg4DecContext *ctx, int16_t *block, if (SHOW_UBITS(re, &s->gb, 1) == 0) { av_log(s->avctx, AV_LOG_ERROR, "2. marker bit missing in 3. esc\n"); - if (!(s->avctx->err_recognition & AV_EF_IGNORE_ERR)) + if (!(s->avctx->err_recognition & AV_EF_IGNORE_ERR) || get_bits_left(&s->gb) <= 0) return AVERROR_INVALIDDATA; } @@ -2648,8 +2648,6 @@ static int decode_vol_header(Mpeg4DecContext *ctx, GetBitContext *gb) else s->avctx->framerate.den = 1; - s->avctx->time_base = av_inv_q(av_mul_q(s->avctx->framerate, (AVRational){s->avctx->ticks_per_frame, 1})); - ctx->t_frame = 0; if (ctx->shape != BIN_ONLY_SHAPE) { @@ -3145,7 +3143,6 @@ static int decode_vop_header(Mpeg4DecContext *ctx, GetBitContext *gb, "time_increment_bits set to %d bits, based on bitstream analysis\n", ctx->time_increment_bits); if (s->avctx->framerate.num && 4*s->avctx->framerate.num < 1<time_increment_bits) { s->avctx->framerate.num = 1<time_increment_bits; - s->avctx->time_base = av_inv_q(av_mul_q(s->avctx->framerate, (AVRational){s->avctx->ticks_per_frame, 1})); } } @@ -3862,9 +3859,6 @@ const FFCodec ff_mpeg4_decoder = { .close = ff_h263_decode_end, FF_CODEC_DECODE_CB(ff_h263_decode_frame), .p.capabilities = AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_DR1 | -#if FF_API_FLAG_TRUNCATED - AV_CODEC_CAP_TRUNCATED | -#endif AV_CODEC_CAP_DELAY | AV_CODEC_CAP_FRAME_THREADS, .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM | FF_CODEC_CAP_ALLOCATE_PROGRESS, diff --git a/libavcodec/mpeg4videoenc.c b/libavcodec/mpeg4videoenc.c index a2a14afbd08..777635c40cb 100644 --- a/libavcodec/mpeg4videoenc.c +++ b/libavcodec/mpeg4videoenc.c @@ -1056,7 +1056,7 @@ static void mpeg4_encode_vol_header(MpegEncContext *s, } /* write MPEG-4 VOP header */ -int ff_mpeg4_encode_picture_header(MpegEncContext *s, int picture_number) +int ff_mpeg4_encode_picture_header(MpegEncContext *s) { uint64_t time_incr; int64_t time_div, time_mod; @@ -1065,7 +1065,7 @@ int ff_mpeg4_encode_picture_header(MpegEncContext *s, int picture_number) if (!(s->avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)) { if (s->avctx->strict_std_compliance < FF_COMPLIANCE_VERY_STRICT) // HACK, the reference sw is buggy mpeg4_encode_visual_object_header(s); - if (s->avctx->strict_std_compliance < FF_COMPLIANCE_VERY_STRICT || picture_number == 0) // HACK, the reference sw is buggy + if (s->avctx->strict_std_compliance < FF_COMPLIANCE_VERY_STRICT || s->picture_number == 0) // HACK, the reference sw is buggy mpeg4_encode_vol_header(s, 0, 0); } if (!(s->workaround_bugs & FF_BUG_MS)) @@ -1101,7 +1101,7 @@ int ff_mpeg4_encode_picture_header(MpegEncContext *s, int picture_number) } put_bits(&s->pb, 3, 0); /* intra dc VLC threshold */ if (!s->progressive_sequence) { - put_bits(&s->pb, 1, s->current_picture_ptr->f->top_field_first); + put_bits(&s->pb, 1, !!(s->current_picture_ptr->f->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)); put_bits(&s->pb, 1, s->alternate_scan); } // FIXME sprite stuff @@ -1403,7 +1403,8 @@ const FFCodec ff_mpeg4_encoder = { FF_CODEC_ENCODE_CB(ff_mpv_encode_picture), .close = ff_mpv_encode_end, .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }, - .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS, + .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .p.priv_class = &mpeg4enc_class, }; diff --git a/libavcodec/mpeg4videoenc.h b/libavcodec/mpeg4videoenc.h index 243cd297a27..f0d5c3d0770 100644 --- a/libavcodec/mpeg4videoenc.h +++ b/libavcodec/mpeg4videoenc.h @@ -32,7 +32,7 @@ void ff_mpeg4_encode_mb(MpegEncContext *s, int16_t block[6][64], int motion_x, int motion_y); void ff_set_mpeg4_time(MpegEncContext *s); -int ff_mpeg4_encode_picture_header(MpegEncContext *s, int picture_number); +int ff_mpeg4_encode_picture_header(MpegEncContext *s); void ff_mpeg4_encode_video_packet_header(MpegEncContext *s); void ff_mpeg4_stuffing(PutBitContext *pbc); diff --git a/libavcodec/mpegaudioenc_fixed.c b/libavcodec/mpegaudioenc_fixed.c index afbffe766bb..a523b5d5338 100644 --- a/libavcodec/mpegaudioenc_fixed.c +++ b/libavcodec/mpegaudioenc_fixed.c @@ -28,7 +28,7 @@ const FFCodec ff_mp2fixed_encoder = { CODEC_LONG_NAME("MP2 fixed point (MPEG audio layer 2)"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_MP2, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(MpegAudioContext), .init = MPA_encode_init, FF_CODEC_ENCODE_CB(MPA_encode_frame), diff --git a/libavcodec/mpegaudioenc_float.c b/libavcodec/mpegaudioenc_float.c index 212709c2916..6a13d095738 100644 --- a/libavcodec/mpegaudioenc_float.c +++ b/libavcodec/mpegaudioenc_float.c @@ -29,7 +29,7 @@ const FFCodec ff_mp2_encoder = { CODEC_LONG_NAME("MP2 (MPEG audio layer 2)"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_MP2, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(MpegAudioContext), .init = MPA_encode_init, FF_CODEC_ENCODE_CB(MPA_encode_frame), diff --git a/libavcodec/mpegpicture.c b/libavcodec/mpegpicture.c index 977bc65191d..b7c804c8ecb 100644 --- a/libavcodec/mpegpicture.c +++ b/libavcodec/mpegpicture.c @@ -27,6 +27,8 @@ #include "avcodec.h" #include "encode.h" +#include "internal.h" +#include "decode.h" #include "motion_est.h" #include "mpegpicture.h" #include "mpegutils.h" @@ -169,17 +171,10 @@ static int alloc_frame_buffer(AVCodecContext *avctx, Picture *pic, pic->f->height = avctx->height; } - if (avctx->hwaccel) { - assert(!pic->hwaccel_picture_private); - if (avctx->hwaccel->frame_priv_data_size) { - pic->hwaccel_priv_buf = av_buffer_allocz(avctx->hwaccel->frame_priv_data_size); - if (!pic->hwaccel_priv_buf) { - av_log(avctx, AV_LOG_ERROR, "alloc_frame_buffer() failed (hwaccel private data allocation)\n"); - return -1; - } - pic->hwaccel_picture_private = pic->hwaccel_priv_buf->data; - } - } + ret = ff_hwaccel_frame_priv_alloc(avctx, &pic->hwaccel_picture_private, + &pic->hwaccel_priv_buf); + if (ret < 0) + return ret; if ((linesize && linesize != pic->f->linesize[0]) || (uvlinesize && uvlinesize != pic->f->linesize[1])) { @@ -332,6 +327,8 @@ void ff_mpeg_unref_picture(AVCodecContext *avctx, Picture *pic) pic->needs_realloc = 0; pic->reference = 0; pic->shared = 0; + pic->display_picture_number = 0; + pic->coded_picture_number = 0; } int ff_update_picture_tables(Picture *dst, const Picture *src) @@ -397,6 +394,8 @@ int ff_mpeg_ref_picture(AVCodecContext *avctx, Picture *dst, Picture *src) dst->needs_realloc = src->needs_realloc; dst->reference = src->reference; dst->shared = src->shared; + dst->display_picture_number = src->display_picture_number; + dst->coded_picture_number = src->coded_picture_number; return 0; fail: diff --git a/libavcodec/mpegpicture.h b/libavcodec/mpegpicture.h index a1455ee13c2..7919aa402ca 100644 --- a/libavcodec/mpegpicture.h +++ b/libavcodec/mpegpicture.h @@ -76,6 +76,9 @@ typedef struct Picture { int reference; int shared; + + int display_picture_number; + int coded_picture_number; } Picture; /** diff --git a/libavcodec/mpegutils.c b/libavcodec/mpegutils.c index 36d75b9633c..2d812a25bec 100644 --- a/libavcodec/mpegutils.c +++ b/libavcodec/mpegutils.c @@ -230,7 +230,7 @@ void ff_print_debug_info2(AVCodecContext *avctx, AVFrame *pict, if (mbcount) { AVFrameSideData *sd; - av_log(avctx, AV_LOG_DEBUG, "Adding %d MVs info to frame %d\n", mbcount, avctx->frame_number); + av_log(avctx, AV_LOG_DEBUG, "Adding %d MVs info to frame %"PRId64"\n", mbcount, avctx->frame_num); sd = av_frame_new_side_data(pict, AV_FRAME_DATA_MOTION_VECTORS, mbcount * sizeof(AVMotionVector)); if (!sd) { av_freep(&mvs); diff --git a/libavcodec/mpegvideo.c b/libavcodec/mpegvideo.c index 836869c1d92..fc73abab9cd 100644 --- a/libavcodec/mpegvideo.c +++ b/libavcodec/mpegvideo.c @@ -738,10 +738,6 @@ av_cold int ff_mpv_common_init(MpegEncContext *s) nb_slices = max_slices; } -#if FF_API_FLAG_TRUNCATED - s->parse_context.state = -1; -#endif - s->context_initialized = 1; memset(s->thread_context, 0, sizeof(s->thread_context)); s->thread_context[0] = s; @@ -791,11 +787,6 @@ void ff_mpv_common_end(MpegEncContext *s) if (s->slice_context_count > 1) s->slice_context_count = 1; -#if FF_API_FLAG_TRUNCATED - av_freep(&s->parse_context.buffer); - s->parse_context.buffer_size = 0; -#endif - av_freep(&s->bitstream_buffer); s->allocated_bitstream_buffer_size = 0; diff --git a/libavcodec/mpegvideo.h b/libavcodec/mpegvideo.h index 42275953b9e..55828e61027 100644 --- a/libavcodec/mpegvideo.h +++ b/libavcodec/mpegvideo.h @@ -44,9 +44,6 @@ #include "pixblockdsp.h" #include "put_bits.h" #include "ratecontrol.h" -#if FF_API_FLAG_TRUNCATED -#include "parser.h" -#endif #include "mpegutils.h" #include "qpeldsp.h" #include "videodsp.h" @@ -353,10 +350,6 @@ typedef struct MpegEncContext { GetBitContext last_resync_gb; ///< used to search for the next resync marker int mb_num_left; ///< number of MBs left in this video packet (for partitioned Slices only) -#if FF_API_FLAG_TRUNCATED - ParseContext parse_context; -#endif - /* H.263 specific */ int gob_index; int obmc; ///< overlapped block motion compensation diff --git a/libavcodec/mpegvideo_dec.c b/libavcodec/mpegvideo_dec.c index 12c7144ffb1..16e8b9193a4 100644 --- a/libavcodec/mpegvideo_dec.c +++ b/libavcodec/mpegvideo_dec.c @@ -154,21 +154,16 @@ do {\ s->divx_packed = s1->divx_packed; if (s1->bitstream_buffer) { - if (s1->bitstream_buffer_size + - AV_INPUT_BUFFER_PADDING_SIZE > s->allocated_bitstream_buffer_size) { - av_fast_malloc(&s->bitstream_buffer, - &s->allocated_bitstream_buffer_size, - s1->allocated_bitstream_buffer_size); - if (!s->bitstream_buffer) { - s->bitstream_buffer_size = 0; - return AVERROR(ENOMEM); - } + av_fast_padded_malloc(&s->bitstream_buffer, + &s->allocated_bitstream_buffer_size, + s1->bitstream_buffer_size); + if (!s->bitstream_buffer) { + s->bitstream_buffer_size = 0; + return AVERROR(ENOMEM); } s->bitstream_buffer_size = s1->bitstream_buffer_size; memcpy(s->bitstream_buffer, s1->bitstream_buffer, s1->bitstream_buffer_size); - memset(s->bitstream_buffer + s->bitstream_buffer_size, 0, - AV_INPUT_BUFFER_PADDING_SIZE); } // linesize-dependent scratch buffer allocation @@ -320,26 +315,33 @@ int ff_mpv_frame_start(MpegEncContext *s, AVCodecContext *avctx) pic->reference = 3; } +#if FF_API_FRAME_PICTURE_NUMBER +FF_DISABLE_DEPRECATION_WARNINGS pic->f->coded_picture_number = s->coded_picture_number++; +FF_ENABLE_DEPRECATION_WARNINGS +#endif if (alloc_picture(s, pic) < 0) return -1; s->current_picture_ptr = pic; // FIXME use only the vars from current_pic - s->current_picture_ptr->f->top_field_first = s->top_field_first; + s->current_picture_ptr->f->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST * !!s->top_field_first; if (s->codec_id == AV_CODEC_ID_MPEG1VIDEO || s->codec_id == AV_CODEC_ID_MPEG2VIDEO) { if (s->picture_structure != PICT_FRAME) - s->current_picture_ptr->f->top_field_first = - (s->picture_structure == PICT_TOP_FIELD) == s->first_field; + s->current_picture_ptr->f->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST * + ((s->picture_structure == PICT_TOP_FIELD) == s->first_field); } - s->current_picture_ptr->f->interlaced_frame = !s->progressive_frame && - !s->progressive_sequence; + s->current_picture_ptr->f->flags |= AV_FRAME_FLAG_INTERLACED * (!s->progressive_frame && + !s->progressive_sequence); s->current_picture_ptr->field_picture = s->picture_structure != PICT_FRAME; s->current_picture_ptr->f->pict_type = s->pict_type; - s->current_picture_ptr->f->key_frame = s->pict_type == AV_PICTURE_TYPE_I; + if (s->pict_type == AV_PICTURE_TYPE_I) + s->current_picture_ptr->f->flags |= AV_FRAME_FLAG_KEY; + else + s->current_picture_ptr->f->flags &= ~AV_FRAME_FLAG_KEY; if ((ret = ff_mpeg_ref_picture(s->avctx, &s->current_picture, s->current_picture_ptr)) < 0) @@ -378,7 +380,7 @@ int ff_mpv_frame_start(MpegEncContext *s, AVCodecContext *avctx) s->last_picture_ptr = &s->picture[idx]; s->last_picture_ptr->reference = 3; - s->last_picture_ptr->f->key_frame = 0; + s->last_picture_ptr->f->flags &= ~AV_FRAME_FLAG_KEY; s->last_picture_ptr->f->pict_type = AV_PICTURE_TYPE_P; if (alloc_picture(s, s->last_picture_ptr) < 0) { @@ -420,7 +422,7 @@ int ff_mpv_frame_start(MpegEncContext *s, AVCodecContext *avctx) s->next_picture_ptr = &s->picture[idx]; s->next_picture_ptr->reference = 3; - s->next_picture_ptr->f->key_frame = 0; + s->next_picture_ptr->f->flags &= ~AV_FRAME_FLAG_KEY; s->next_picture_ptr->f->pict_type = AV_PICTURE_TYPE_P; if (alloc_picture(s, s->next_picture_ptr) < 0) { @@ -554,14 +556,6 @@ void ff_mpeg_flush(AVCodecContext *avctx) s->mb_x = s->mb_y = 0; -#if FF_API_FLAG_TRUNCATED - s->parse_context.state = -1; - s->parse_context.frame_start_found = 0; - s->parse_context.overread = 0; - s->parse_context.overread_index = 0; - s->parse_context.index = 0; - s->parse_context.last_index = 0; -#endif s->bitstream_buffer_size = 0; s->pp_time = 0; } @@ -641,6 +635,7 @@ static av_always_inline void mpeg_motion_lowres(MpegEncContext *s, const int s_mask = (2 << lowres) - 1; const int h_edge_pos = s->h_edge_pos >> lowres; const int v_edge_pos = s->v_edge_pos >> lowres; + int hc = s->chroma_y_shift ? (h+1-bottom_field)>>1 : h; linesize = s->current_picture.f->linesize[0] << field_based; uvlinesize = s->current_picture.f->linesize[1] << field_based; @@ -703,7 +698,7 @@ static av_always_inline void mpeg_motion_lowres(MpegEncContext *s, ptr_cr = ref_picture[2] + uvsrc_y * uvlinesize + uvsrc_x; if ((unsigned) src_x > FFMAX( h_edge_pos - (!!sx) - 2 * block_s, 0) || uvsrc_y<0 || - (unsigned) src_y > FFMAX((v_edge_pos >> field_based) - (!!sy) - h, 0)) { + (unsigned) src_y > FFMAX((v_edge_pos >> field_based) - (!!sy) - FFMAX(h, hc<chroma_y_shift), 0)) { s->vdsp.emulated_edge_mc(s->sc.edge_emu_buffer, ptr_y, linesize >> field_based, linesize >> field_based, 17, 17 + field_based, @@ -748,7 +743,6 @@ static av_always_inline void mpeg_motion_lowres(MpegEncContext *s, pix_op[lowres - 1](dest_y, ptr_y, linesize, h, sx, sy); if (!CONFIG_GRAY || !(s->avctx->flags & AV_CODEC_FLAG_GRAY)) { - int hc = s->chroma_y_shift ? (h+1-bottom_field)>>1 : h; uvsx = (uvsx << 2) >> lowres; uvsy = (uvsy << 2) >> lowres; if (hc) { @@ -886,10 +880,9 @@ static inline void MPV_motion_lowres(MpegEncContext *s, s->mv[dir][1][0], s->mv[dir][1][1], block_s, mb_y); } else { - if (s->picture_structure != s->field_select[dir][0] + 1 && - s->pict_type != AV_PICTURE_TYPE_B && !s->first_field) { + if ( s->picture_structure != s->field_select[dir][0] + 1 && s->pict_type != AV_PICTURE_TYPE_B && !s->first_field + || !ref_picture[0]) { ref_picture = s->current_picture_ptr->f->data; - } mpeg_motion_lowres(s, dest_y, dest_cb, dest_cr, 0, 0, s->field_select[dir][0], @@ -902,8 +895,9 @@ static inline void MPV_motion_lowres(MpegEncContext *s, for (int i = 0; i < 2; i++) { uint8_t *const *ref2picture; - if (s->picture_structure == s->field_select[dir][i] + 1 || - s->pict_type == AV_PICTURE_TYPE_B || s->first_field) { + if ((s->picture_structure == s->field_select[dir][i] + 1 || + s->pict_type == AV_PICTURE_TYPE_B || s->first_field) && + ref_picture[0]) { ref2picture = ref_picture; } else { ref2picture = s->current_picture_ptr->f->data; @@ -934,6 +928,9 @@ static inline void MPV_motion_lowres(MpegEncContext *s, pix_op = s->h264chroma.avg_h264_chroma_pixels_tab; } } else { + if (!ref_picture[0]) { + ref_picture = s->current_picture_ptr->f->data; + } for (int i = 0; i < 2; i++) { mpeg_motion_lowres(s, dest_y, dest_cb, dest_cr, 0, 0, s->picture_structure != i + 1, diff --git a/libavcodec/mpegvideo_enc.c b/libavcodec/mpegvideo_enc.c index 9b11c5c05a0..64e66ae958c 100644 --- a/libavcodec/mpegvideo_enc.c +++ b/libavcodec/mpegvideo_enc.c @@ -43,7 +43,6 @@ #include "libavutil/opt.h" #include "libavutil/thread.h" #include "avcodec.h" -#include "dct.h" #include "encode.h" #include "idctdsp.h" #include "mpeg12codecs.h" @@ -83,7 +82,7 @@ #define QMAT_SHIFT_MMX 16 #define QMAT_SHIFT 21 -static int encode_picture(MpegEncContext *s, int picture_number); +static int encode_picture(MpegEncContext *s); static int dct_quantize_refine(MpegEncContext *s, int16_t *block, int16_t *weight, int16_t *orig, int n, int qscale); static int sse_mb(MpegEncContext *s); static void denoise_dct_c(MpegEncContext *s, int16_t *block); @@ -798,6 +797,11 @@ av_cold int ff_mpv_encode_init(AVCodecContext *avctx) AV_CODEC_FLAG_INTERLACED_ME) || s->alternate_scan); + if (s->lmin > s->lmax) { + av_log(avctx, AV_LOG_WARNING, "Clipping lmin value to %d\n", s->lmax); + s->lmin = s->lmax; + } + /* init */ ff_mpv_idct_init(s); if ((ret = ff_mpv_common_init(s)) < 0) @@ -902,8 +906,10 @@ av_cold int ff_mpv_encode_init(AVCodecContext *avctx) s->quant_precision = 5; - ff_set_cmp(&s->mecc, s->mecc.ildct_cmp, avctx->ildct_cmp); - ff_set_cmp(&s->mecc, s->mecc.frame_skip_cmp, s->frame_skip_cmp); + ret = ff_set_cmp(&s->mecc, s->mecc.ildct_cmp, avctx->ildct_cmp); + ret |= ff_set_cmp(&s->mecc, s->mecc.frame_skip_cmp, s->frame_skip_cmp); + if (ret < 0) + return AVERROR(EINVAL); if (CONFIG_H263_ENCODER && s->out_format == FMT_H263) { ff_h263_encode_init(s); @@ -1219,7 +1225,7 @@ static int load_input_picture(MpegEncContext *s, const AVFrame *pic_arg) if (ret < 0) return ret; - pic->f->display_picture_number = display_picture_number; + pic->display_picture_number = display_picture_number; pic->f->pts = pts; // we set this here to avoid modifying pic_arg } else { /* Flushing: When we have not received enough input frames, @@ -1477,14 +1483,14 @@ static int select_input_picture(MpegEncContext *s) !s->next_picture_ptr || s->intra_only) { s->reordered_input_picture[0] = s->input_picture[0]; s->reordered_input_picture[0]->f->pict_type = AV_PICTURE_TYPE_I; - s->reordered_input_picture[0]->f->coded_picture_number = + s->reordered_input_picture[0]->coded_picture_number = s->coded_picture_number++; } else { int b_frames = 0; if (s->avctx->flags & AV_CODEC_FLAG_PASS2) { for (i = 0; i < s->max_b_frames + 1; i++) { - int pict_num = s->input_picture[0]->f->display_picture_number + i; + int pict_num = s->input_picture[0]->display_picture_number + i; if (pict_num >= s->rc_context.num_entries) break; @@ -1563,13 +1569,13 @@ static int select_input_picture(MpegEncContext *s) s->reordered_input_picture[0] = s->input_picture[b_frames]; if (s->reordered_input_picture[0]->f->pict_type != AV_PICTURE_TYPE_I) s->reordered_input_picture[0]->f->pict_type = AV_PICTURE_TYPE_P; - s->reordered_input_picture[0]->f->coded_picture_number = + s->reordered_input_picture[0]->coded_picture_number = s->coded_picture_number++; for (i = 0; i < b_frames; i++) { s->reordered_input_picture[i + 1] = s->input_picture[i]; s->reordered_input_picture[i + 1]->f->pict_type = AV_PICTURE_TYPE_B; - s->reordered_input_picture[i + 1]->f->coded_picture_number = + s->reordered_input_picture[i + 1]->coded_picture_number = s->coded_picture_number++; } } @@ -1604,6 +1610,8 @@ static int select_input_picture(MpegEncContext *s) ret = av_frame_copy_props(pic->f, s->reordered_input_picture[0]->f); if (ret < 0) return ret; + pic->coded_picture_number = s->reordered_input_picture[0]->coded_picture_number; + pic->display_picture_number = s->reordered_input_picture[0]->display_picture_number; /* mark us unused / free shared pic */ av_frame_unref(s->reordered_input_picture[0]->f); @@ -1618,7 +1626,8 @@ static int select_input_picture(MpegEncContext *s) s->new_picture->data[i] += INPLACE_OFFSET; } } - s->picture_number = s->new_picture->display_picture_number; + s->picture_number = s->current_picture_ptr->display_picture_number; + } return 0; } @@ -1693,7 +1702,10 @@ static int frame_start(MpegEncContext *s) } s->current_picture_ptr->f->pict_type = s->pict_type; - s->current_picture_ptr->f->key_frame = s->pict_type == AV_PICTURE_TYPE_I; + if (s->pict_type == AV_PICTURE_TYPE_I) + s->current_picture.f->flags |= AV_FRAME_FLAG_KEY; + else + s->current_picture.f->flags &= ~AV_FRAME_FLAG_KEY; ff_mpeg_unref_picture(s->avctx, &s->current_picture); if ((ret = ff_mpeg_ref_picture(s->avctx, &s->current_picture, @@ -1795,7 +1807,7 @@ int ff_mpv_encode_picture(AVCodecContext *avctx, AVPacket *pkt, if (ret < 0) return ret; vbv_retry: - ret = encode_picture(s, s->picture_number); + ret = encode_picture(s); if (growing_buffer) { av_assert0(s->pb.buf == avctx->internal->byte_buffer); pkt->data = s->pb.buf; @@ -1952,15 +1964,24 @@ int ff_mpv_encode_picture(AVCodecContext *avctx, AVPacket *pkt, s->total_bits += s->frame_bits; pkt->pts = s->current_picture.f->pts; + pkt->duration = s->current_picture.f->duration; if (!s->low_delay && s->pict_type != AV_PICTURE_TYPE_B) { - if (!s->current_picture.f->coded_picture_number) + if (!s->current_picture.coded_picture_number) pkt->dts = pkt->pts - s->dts_delta; else pkt->dts = s->reordered_pts; s->reordered_pts = pkt->pts; } else pkt->dts = pkt->pts; - if (s->current_picture.f->key_frame) + + // the no-delay case is handled in generic code + if (avctx->codec->capabilities & AV_CODEC_CAP_DELAY) { + ret = ff_encode_reordered_opaque(avctx, pkt, s->current_picture.f); + if (ret < 0) + return ret; + } + + if (s->current_picture.f->flags & AV_FRAME_FLAG_KEY) pkt->flags |= AV_PKT_FLAG_KEY; if (s->mb_info) av_packet_shrink_side_data(pkt, AV_PKT_DATA_H263_MB_INFO, s->mb_info_size); @@ -3556,14 +3577,12 @@ static void set_frame_distances(MpegEncContext * s){ } } -static int encode_picture(MpegEncContext *s, int picture_number) +static int encode_picture(MpegEncContext *s) { int i, ret; int bits; int context_count = s->slice_context_count; - s->picture_number = picture_number; - /* Reset the average MB variance */ s->me.mb_var_sum_temp = s->me.mc_mb_var_sum_temp = 0; @@ -3766,12 +3785,17 @@ static int encode_picture(MpegEncContext *s, int picture_number) } //FIXME var duplication - s->current_picture_ptr->f->key_frame = - s->current_picture.f->key_frame = s->pict_type == AV_PICTURE_TYPE_I; //FIXME pic_ptr + if (s->pict_type == AV_PICTURE_TYPE_I) { + s->current_picture_ptr->f->flags |= AV_FRAME_FLAG_KEY; //FIXME pic_ptr + s->current_picture.f->flags |= AV_FRAME_FLAG_KEY; + } else { + s->current_picture_ptr->f->flags &= ~AV_FRAME_FLAG_KEY; //FIXME pic_ptr + s->current_picture.f->flags &= ~AV_FRAME_FLAG_KEY; + } s->current_picture_ptr->f->pict_type = s->current_picture.f->pict_type = s->pict_type; - if (s->current_picture.f->key_frame) + if (s->current_picture.f->flags & AV_FRAME_FLAG_KEY) s->picture_in_gop_number=0; s->mb_x = s->mb_y = 0; @@ -3788,32 +3812,32 @@ static int encode_picture(MpegEncContext *s, int picture_number) break; case FMT_H261: if (CONFIG_H261_ENCODER) - ff_h261_encode_picture_header(s, picture_number); + ff_h261_encode_picture_header(s); break; case FMT_H263: if (CONFIG_WMV2_ENCODER && s->codec_id == AV_CODEC_ID_WMV2) - ff_wmv2_encode_picture_header(s, picture_number); + ff_wmv2_encode_picture_header(s); else if (CONFIG_MSMPEG4ENC && s->msmpeg4_version) - ff_msmpeg4_encode_picture_header(s, picture_number); + ff_msmpeg4_encode_picture_header(s); else if (CONFIG_MPEG4_ENCODER && s->h263_pred) { - ret = ff_mpeg4_encode_picture_header(s, picture_number); + ret = ff_mpeg4_encode_picture_header(s); if (ret < 0) return ret; } else if (CONFIG_RV10_ENCODER && s->codec_id == AV_CODEC_ID_RV10) { - ret = ff_rv10_encode_picture_header(s, picture_number); + ret = ff_rv10_encode_picture_header(s); if (ret < 0) return ret; } else if (CONFIG_RV20_ENCODER && s->codec_id == AV_CODEC_ID_RV20) - ff_rv20_encode_picture_header(s, picture_number); + ff_rv20_encode_picture_header(s); else if (CONFIG_FLV_ENCODER && s->codec_id == AV_CODEC_ID_FLV1) - ff_flv_encode_picture_header(s, picture_number); + ff_flv_encode_picture_header(s); else if (CONFIG_H263_ENCODER) - ff_h263_encode_picture_header(s, picture_number); + ff_h263_encode_picture_header(s); break; case FMT_MPEG1: if (CONFIG_MPEG1VIDEO_ENCODER || CONFIG_MPEG2VIDEO_ENCODER) - ff_mpeg1_encode_picture_header(s, picture_number); + ff_mpeg1_encode_picture_header(s); break; default: av_assert0(0); diff --git a/libavcodec/mpegvideo_parser.c b/libavcodec/mpegvideo_parser.c index ac6efb69091..1204789c670 100644 --- a/libavcodec/mpegvideo_parser.c +++ b/libavcodec/mpegvideo_parser.c @@ -20,6 +20,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/avassert.h" #include "decode.h" #include "parser.h" #include "mpeg12.h" @@ -33,7 +34,6 @@ struct MpvParseContext { int width, height; }; -#if !FF_API_FLAG_TRUNCATED /** * Find the end of the current frame in the bitstream. * @return the position of the first byte of the next frame, or -1 @@ -98,7 +98,6 @@ static int mpeg1_find_frame_end(ParseContext *pc, const uint8_t *buf, pc->state = state; return END_NOT_FOUND; } -#endif static void mpegvideo_extract_headers(AVCodecParserContext *s, AVCodecContext *avctx, @@ -130,6 +129,7 @@ static void mpegvideo_extract_headers(AVCodecParserContext *s, s->pict_type = (buf[1] >> 3) & 7; if (bytes_left >= 4) vbv_delay = ((buf[1] & 0x07) << 13) | (buf[2] << 5) | (buf[3] >> 3); + s->repeat_pict = 1; } break; case SEQ_START_CODE: @@ -145,7 +145,11 @@ static void mpegvideo_extract_headers(AVCodecParserContext *s, pc->frame_rate = avctx->framerate = ff_mpeg12_frame_rate_tab[frame_rate_index]; bit_rate = (buf[4]<<10) | (buf[5]<<2) | (buf[6]>>6); avctx->codec_id = AV_CODEC_ID_MPEG1VIDEO; +#if FF_API_TICKS_PER_FRAME +FF_DISABLE_DEPRECATION_WARNINGS avctx->ticks_per_frame = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } break; case EXT_START_CODE: @@ -177,7 +181,11 @@ static void mpegvideo_extract_headers(AVCodecParserContext *s, avctx->framerate.num = pc->frame_rate.num * (frame_rate_ext_n + 1); avctx->framerate.den = pc->frame_rate.den * (frame_rate_ext_d + 1); avctx->codec_id = AV_CODEC_ID_MPEG2VIDEO; +#if FF_API_TICKS_PER_FRAME +FF_DISABLE_DEPRECATION_WARNINGS avctx->ticks_per_frame = 2; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } break; case 0x8: /* picture coding extension */ @@ -187,7 +195,6 @@ static void mpegvideo_extract_headers(AVCodecParserContext *s, progressive_frame = buf[4] & (1 << 7); /* check if we must repeat the frame */ - s->repeat_pict = 1; if (repeat_first_field) { if (pc->progressive_sequence) { if (top_field_first) @@ -241,11 +248,6 @@ static void mpegvideo_extract_headers(AVCodecParserContext *s, s->coded_width = FFALIGN(pc->width, 16); s->coded_height = FFALIGN(pc->height, 16); } - -#if FF_API_AVCTX_TIMEBASE - if (avctx->framerate.num) - avctx->time_base = av_inv_q(av_mul_q(avctx->framerate, (AVRational){avctx->ticks_per_frame, 1})); -#endif } static int mpegvideo_parse(AVCodecParserContext *s, @@ -260,11 +262,7 @@ static int mpegvideo_parse(AVCodecParserContext *s, if(s->flags & PARSER_FLAG_COMPLETE_FRAMES){ next= buf_size; }else{ -#if FF_API_FLAG_TRUNCATED - next= ff_mpeg1_find_frame_end(pc, buf, buf_size, s); -#else next = mpeg1_find_frame_end(pc, buf, buf_size, s); -#endif if (ff_combine_frame(pc, next, &buf, &buf_size) < 0) { *poutbuf = NULL; diff --git a/libavcodec/mscc.c b/libavcodec/mscc.c index e8406aa268c..d1d23e67514 100644 --- a/libavcodec/mscc.c +++ b/libavcodec/mscc.c @@ -150,7 +150,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, const uint8_t *pal = av_packet_get_side_data(avpkt, AV_PKT_DATA_PALETTE, &size); if (pal && size == AVPALETTE_SIZE) { +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif for (j = 0; j < 256; j++) s->pal[j] = 0xFF000000 | AV_RL32(pal + j * 4); } else if (pal) { @@ -200,7 +204,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, s->uncomp_buf + s->bpp * j * avctx->width, s->bpp * avctx->width); } - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; *got_frame = 1; diff --git a/libavcodec/msmpeg4enc.c b/libavcodec/msmpeg4enc.c index 9b6e5efa0ca..a8ddb8d8e1e 100644 --- a/libavcodec/msmpeg4enc.c +++ b/libavcodec/msmpeg4enc.c @@ -216,7 +216,7 @@ static void find_best_tables(MSMPEG4EncContext *ms) } /* write MSMPEG4 compatible frame header */ -void ff_msmpeg4_encode_picture_header(MpegEncContext * s, int picture_number) +void ff_msmpeg4_encode_picture_header(MpegEncContext * s) { MSMPEG4EncContext *const ms = (MSMPEG4EncContext*)s; @@ -280,7 +280,20 @@ void ff_msmpeg4_encode_picture_header(MpegEncContext * s, int picture_number) void ff_msmpeg4_encode_ext_header(MpegEncContext * s) { - unsigned fps = s->avctx->time_base.den / s->avctx->time_base.num / FFMAX(s->avctx->ticks_per_frame, 1); + unsigned fps; + + if (s->avctx->framerate.num > 0 && s->avctx->framerate.den > 0) + fps = s->avctx->framerate.num / s->avctx->framerate.den; + else { +FF_DISABLE_DEPRECATION_WARNINGS + fps = s->avctx->time_base.den / s->avctx->time_base.num +#if FF_API_TICKS_PER_FRAME + / FFMAX(s->avctx->ticks_per_frame, 1) +#endif + ; +FF_ENABLE_DEPRECATION_WARNINGS + } + put_bits(&s->pb, 5, FFMIN(fps, 31)); //yes 29.97 -> 29 put_bits(&s->pb, 11, FFMIN(s->bit_rate / 1024, 2047)); @@ -684,6 +697,7 @@ const FFCodec ff_msmpeg4v2_encoder = { .p.id = AV_CODEC_ID_MSMPEG4V2, .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }, .p.priv_class = &ff_mpv_enc_class, + .p.capabilities = AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .priv_data_size = sizeof(MSMPEG4EncContext), .init = ff_mpv_encode_init, @@ -698,6 +712,7 @@ const FFCodec ff_msmpeg4v3_encoder = { .p.id = AV_CODEC_ID_MSMPEG4V3, .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }, .p.priv_class = &ff_mpv_enc_class, + .p.capabilities = AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .priv_data_size = sizeof(MSMPEG4EncContext), .init = ff_mpv_encode_init, @@ -712,6 +727,7 @@ const FFCodec ff_wmv1_encoder = { .p.id = AV_CODEC_ID_WMV1, .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }, .p.priv_class = &ff_mpv_enc_class, + .p.capabilities = AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .priv_data_size = sizeof(MSMPEG4EncContext), .init = ff_mpv_encode_init, diff --git a/libavcodec/msmpeg4enc.h b/libavcodec/msmpeg4enc.h index 602e60112d4..da9a45b5896 100644 --- a/libavcodec/msmpeg4enc.h +++ b/libavcodec/msmpeg4enc.h @@ -34,7 +34,7 @@ typedef struct MSMPEG4EncContext { } MSMPEG4EncContext; void ff_msmpeg4_encode_init(MpegEncContext *s); -void ff_msmpeg4_encode_picture_header(MpegEncContext *s, int picture_number); +void ff_msmpeg4_encode_picture_header(MpegEncContext *s); void ff_msmpeg4_encode_ext_header(MpegEncContext *s); void ff_msmpeg4_encode_mb(MpegEncContext *s, int16_t block[6][64], int motion_x, int motion_y); diff --git a/libavcodec/msp2dec.c b/libavcodec/msp2dec.c index 9c51c35c61e..30a2825e47b 100644 --- a/libavcodec/msp2dec.c +++ b/libavcodec/msp2dec.c @@ -47,7 +47,7 @@ static int msp2_decode_frame(AVCodecContext *avctx, AVFrame *p, return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; bytestream2_init(&idx, buf, 2 * avctx->height); buf += 2 * avctx->height; diff --git a/libavcodec/msrle.c b/libavcodec/msrle.c index b6fa7f7abb9..51e843e4a69 100644 --- a/libavcodec/msrle.c +++ b/libavcodec/msrle.c @@ -95,7 +95,14 @@ static int msrle_decode_frame(AVCodecContext *avctx, AVFrame *rframe, return ret; if (avctx->bits_per_coded_sample > 1 && avctx->bits_per_coded_sample <= 8) { - s->frame->palette_has_changed = ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS + s->frame->palette_has_changed = +#endif + ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_ENABLE_DEPRECATION_WARNINGS +#endif /* make the palette available */ memcpy(s->frame->data[1], s->pal, AVPALETTE_SIZE); diff --git a/libavcodec/msrleenc.c b/libavcodec/msrleenc.c new file mode 100644 index 00000000000..931e7af0538 --- /dev/null +++ b/libavcodec/msrleenc.c @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2023 Tomas Härdin + * + * 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 + * MSRLE encoder + * @see https://wiki.multimedia.cx/index.php?title=Microsoft_RLE + */ + +// TODO: pal4 mode? + +#include "bytestream.h" +#include "codec_internal.h" +#include "encode.h" + +typedef struct MSRLEContext { + int curframe; + AVFrame *last_frame; +} MSRLEContext; + +static av_cold int msrle_encode_init(AVCodecContext *avctx) +{ + MSRLEContext *s = avctx->priv_data; + + avctx->bits_per_coded_sample = 8; + s->last_frame = av_frame_alloc(); + if (!s->last_frame) + return AVERROR(ENOMEM); + + return 0; +} + +static void write_run(AVCodecContext *avctx, uint8_t **data, int len, int value) +{ + // we're allowed to write odd runs + while (len >= 255) { + bytestream_put_byte(data, 255); + bytestream_put_byte(data, value); + len -= 255; + } + if (len >= 1) { + // this is wasteful when len == 1 and sometimes when len == 2 + // but sometimes we have no choice. also write_absolute() + // relies on this + bytestream_put_byte(data, len); + bytestream_put_byte(data, value); + } +} + +static void write_absolute(AVCodecContext *avctx, uint8_t **data, + const uint8_t *line, int len) +{ + // writing 255 would be wasteful here due to the padding requirement + while (len >= 254) { + bytestream_put_byte(data, 0); + bytestream_put_byte(data, 254); + bytestream_put_buffer(data, line, 254); + line += 254; + len -= 254; + } + if (len == 1) { + // it's less wasteful to write single pixels as runs + // not to mention that absolute mode requires >= 3 pixels + write_run(avctx, data, 1, line[0]); + } else if (len == 2) { + write_run(avctx, data, 1, line[0]); + write_run(avctx, data, 1, line[1]); + } else if (len > 0) { + bytestream_put_byte(data, 0); + bytestream_put_byte(data, len); + bytestream_put_buffer(data, line, len); + if (len & 1) + bytestream_put_byte(data, 0); + } +} + +static void write_delta(AVCodecContext *avctx, uint8_t **data, int delta) +{ + // we let the yskip logic handle the case where we want to delta + // to following lines. it's not perfect but it's easier than finding + // the optimal combination of end-of-lines and deltas to reach any + // following position including places where dx < 0 + while (delta >= 255) { + bytestream_put_byte(data, 0); + bytestream_put_byte(data, 2); + bytestream_put_byte(data, 255); + bytestream_put_byte(data, 0); + delta -= 255; + } + if (delta > 0) { + bytestream_put_byte(data, 0); + bytestream_put_byte(data, 2); + bytestream_put_byte(data, delta); + bytestream_put_byte(data, 0); + } +} + +static void write_yskip(AVCodecContext *avctx, uint8_t **data, int yskip) +{ + if (yskip < 4) + return; + // we have yskip*2 nul bytess + *data -= 2*yskip; + // the end-of-line counts as one skip + yskip--; + while (yskip >= 255) { + bytestream_put_byte(data, 0); + bytestream_put_byte(data, 2); + bytestream_put_byte(data, 0); + bytestream_put_byte(data, 255); + yskip -= 255; + } + if (yskip > 0) { + bytestream_put_byte(data, 0); + bytestream_put_byte(data, 2); + bytestream_put_byte(data, 0); + bytestream_put_byte(data, yskip); + } + bytestream_put_be16(data, 0x0000); +} + +// used both to encode lines in keyframes and to encode lines between deltas +static void encode_line(AVCodecContext *avctx, uint8_t **data, + const uint8_t *line, int length) +{ + int run = 0, last = -1, absstart = 0; + if (length == 0) + return; + for (int x = 0; x < length; x++) { + if (last == line[x]) { + run++; + if (run == 3) + write_absolute(avctx, data, &line[absstart], x - absstart - 2); + } else { + if (run >= 3) { + write_run(avctx, data, run, last); + absstart = x; + } + run = 1; + } + last = line[x]; + } + if (run >= 3) + write_run(avctx, data, run, last); + else + write_absolute(avctx, data, &line[absstart], length - absstart); +} + +static int encode(AVCodecContext *avctx, AVPacket *pkt, + const AVFrame *pict, int keyframe, int *got_keyframe) +{ + MSRLEContext *s = avctx->priv_data; + uint8_t *data = pkt->data; + + /* Compare the current frame to the last frame, or code the entire frame + if keyframe != 0. We're continually outputting pairs of bytes: + + 00 00 end of line + 00 01 end of bitmap + 00 02 dx dy delta. move pointer to x+dx, y+dy + 00 ll dd dd .. absolute (verbatim) mode. ll >= 3 + rr dd run. rr >= 1 + + For keyframes we only have absolute mode and runs at our disposal, and + we are not allowed to end a line early. If this happens when keyframe == 0 + then *got_keyframe is set to 1 and s->curframe is reset. + */ + *got_keyframe = 1; // set to zero whenever we use a feature that makes this a not-keyframe + + if (keyframe) { + for (int y = avctx->height-1; y >= 0; y--) { + uint8_t *line = &pict->data[0][y*pict->linesize[0]]; + encode_line(avctx, &data, line, avctx->width); + bytestream_put_be16(&data, 0x0000); // end of line + } + } else { + // compare to previous frame + int yskip = 0; // we can encode large skips using deltas + for (int y = avctx->height-1; y >= 0; y--) { + const uint8_t *line = &pict->data[0][y*pict->linesize[0]]; + const uint8_t *prev = &s->last_frame->data[0][y*s->last_frame->linesize[0]]; + // we need at least 5 pixels in a row for a delta to be worthwhile + int delta = 0, linestart = 0, encoded = 0; + for (int x = 0; x < avctx->width; x++) { + if (line[x] == prev[x]) { + delta++; + if (delta == 5) { + int len = x - linestart - 4; + if (len > 0) { + write_yskip(avctx, &data, yskip); + yskip = 0; + encode_line(avctx, &data, &line[linestart], len); + encoded = 1; + } + linestart = -1; + } + } else { + if (delta >= 5) { + write_yskip(avctx, &data, yskip); + yskip = 0; + write_delta(avctx, &data, delta); + *got_keyframe = 0; + encoded = 1; + } + delta = 0; + if (linestart == -1) + linestart = x; + } + } + if (delta < 5) { + write_yskip(avctx, &data, yskip); + yskip = 0; + encode_line(avctx, &data, &line[linestart], avctx->width - linestart); + encoded = 1; + } else + *got_keyframe = 0; + bytestream_put_be16(&data, 0x0000); // end of line + if (!encoded) + yskip++; + else + yskip = 0; + } + write_yskip(avctx, &data, yskip); + } + bytestream_put_be16(&data, 0x0001); // end of bitmap + pkt->size = data - pkt->data; + return 0; +} + +static int msrle_encode_frame(AVCodecContext *avctx, AVPacket *pkt, + const AVFrame *pict, int *got_packet) +{ + MSRLEContext *s = avctx->priv_data; + int ret, got_keyframe; + + if ((ret = ff_alloc_packet(avctx, pkt, ( + avctx->width*2 /* worst case = rle every pixel */ + 2 /*end of line */ + ) * avctx->height + 2 /* end of bitmap */ + AV_INPUT_BUFFER_MIN_SIZE))) + return ret; + + if (pict->data[1]) { + uint8_t *side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE); + if (!side_data) + return AVERROR(ENOMEM); + memcpy(side_data, pict->data[1], AVPALETTE_SIZE); + } + + if ((ret = encode(avctx, pkt, pict, s->curframe == 0, &got_keyframe))) + return ret; + + if (got_keyframe) { + pkt->flags |= AV_PKT_FLAG_KEY; + s->curframe = 0; + } + if (++s->curframe >= avctx->gop_size) + s->curframe = 0; + *got_packet = 1; + + return av_frame_replace(s->last_frame, pict); +} + +static int msrle_encode_close(AVCodecContext *avctx) +{ + MSRLEContext *s = avctx->priv_data; + av_frame_free(&s->last_frame); + return 0; +} + +const FFCodec ff_msrle_encoder = { + .p.name = "msrle", + CODEC_LONG_NAME("Microsoft RLE"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MSRLE, + .p.capabilities = AV_CODEC_CAP_DR1, + .priv_data_size = sizeof(MSRLEContext), + .init = msrle_encode_init, + FF_CODEC_ENCODE_CB(msrle_encode_frame), + .close = msrle_encode_close, + .p.pix_fmts = (const enum AVPixelFormat[]){ + AV_PIX_FMT_PAL8, AV_PIX_FMT_NONE + }, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, +}; diff --git a/libavcodec/mss1.c b/libavcodec/mss1.c index 775852102a9..fb5fc34ea14 100644 --- a/libavcodec/mss1.c +++ b/libavcodec/mss1.c @@ -165,12 +165,12 @@ static int mss1_decode_frame(AVCodecContext *avctx, AVFrame *rframe, c->corrupted = 0; ff_mss12_slicecontext_reset(&ctx->sc); pal_changed = decode_pal(c, &acoder); - ctx->pic->key_frame = 1; + ctx->pic->flags |= AV_FRAME_FLAG_KEY; ctx->pic->pict_type = AV_PICTURE_TYPE_I; } else { if (c->corrupted) return AVERROR_INVALIDDATA; - ctx->pic->key_frame = 0; + ctx->pic->flags &= ~AV_FRAME_FLAG_KEY; ctx->pic->pict_type = AV_PICTURE_TYPE_P; } c->corrupted = ff_mss12_decode_rect(&ctx->sc, &acoder, 0, 0, @@ -178,7 +178,11 @@ static int mss1_decode_frame(AVCodecContext *avctx, AVFrame *rframe, if (c->corrupted) return AVERROR_INVALIDDATA; memcpy(ctx->pic->data[1], c->pal, AVPALETTE_SIZE); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS ctx->pic->palette_has_changed = pal_changed; +FF_ENABLE_DEPRECATION_WARNINGS +#endif if ((ret = av_frame_ref(rframe, ctx->pic)) < 0) return ret; diff --git a/libavcodec/mss2.c b/libavcodec/mss2.c index 1d1ed11f54e..98103f7fed6 100644 --- a/libavcodec/mss2.c +++ b/libavcodec/mss2.c @@ -660,7 +660,10 @@ static int mss2_decode_frame(AVCodecContext *avctx, AVFrame *frame, frame->linesize[0] * (avctx->height - 1); c->rgb_stride = -frame->linesize[0]; - frame->key_frame = keyframe; + if (keyframe) + frame->flags |= AV_FRAME_FLAG_KEY; + else + frame->flags &= ~AV_FRAME_FLAG_KEY; frame->pict_type = keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; if (is_555) { diff --git a/libavcodec/mss3.c b/libavcodec/mss3.c index 023f110ec81..2701e9b912a 100644 --- a/libavcodec/mss3.c +++ b/libavcodec/mss3.c @@ -740,7 +740,10 @@ static int mss3_decode_frame(AVCodecContext *avctx, AVFrame *rframe, if ((ret = ff_reget_buffer(avctx, c->pic, 0)) < 0) return ret; - c->pic->key_frame = keyframe; + if (keyframe) + c->pic->flags |= AV_FRAME_FLAG_KEY; + else + c->pic->flags &= ~AV_FRAME_FLAG_KEY; c->pic->pict_type = keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; if (!bytestream2_get_bytes_left(&gb)) { if ((ret = av_frame_ref(rframe, c->pic)) < 0) diff --git a/libavcodec/mss4.c b/libavcodec/mss4.c index dceb42da25d..75f31beda64 100644 --- a/libavcodec/mss4.c +++ b/libavcodec/mss4.c @@ -503,7 +503,10 @@ static int mss4_decode_frame(AVCodecContext *avctx, AVFrame *rframe, if ((ret = ff_reget_buffer(avctx, c->pic, 0)) < 0) return ret; - c->pic->key_frame = (frame_type == INTRA_FRAME); + if (frame_type == INTRA_FRAME) + c->pic->flags |= AV_FRAME_FLAG_KEY; + else + c->pic->flags &= ~AV_FRAME_FLAG_KEY; c->pic->pict_type = (frame_type == INTRA_FRAME) ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; if (frame_type == SKIP_FRAME) { diff --git a/libavcodec/msvideo1.c b/libavcodec/msvideo1.c index 9903ff36a7f..ca4583d8415 100644 --- a/libavcodec/msvideo1.c +++ b/libavcodec/msvideo1.c @@ -312,7 +312,14 @@ static int msvideo1_decode_frame(AVCodecContext *avctx, AVFrame *rframe, return ret; if (s->mode_8bit) { - s->frame->palette_has_changed = ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS + s->frame->palette_has_changed = +#endif + ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_ENABLE_DEPRECATION_WARNINGS +#endif } if (s->mode_8bit) diff --git a/libavcodec/msvideo1enc.c b/libavcodec/msvideo1enc.c index a349b42ca0d..1fb8be88835 100644 --- a/libavcodec/msvideo1enc.c +++ b/libavcodec/msvideo1enc.c @@ -84,6 +84,8 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, if(!c->prev) c->prev = av_malloc(avctx->width * 3 * (avctx->height + 3)); + if (!c->prev) + return AVERROR(ENOMEM); prevptr = c->prev + avctx->width * 3 * (FFALIGN(avctx->height, 4) - 1); src = (const uint16_t*)(p->data[0] + p->linesize[0]*(FFALIGN(avctx->height, 4) - 1)); if(c->keyint >= avctx->keyint_min) @@ -307,6 +309,7 @@ const FFCodec ff_msvideo1_encoder = { CODEC_LONG_NAME("Microsoft Video-1"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_MSVIDEO1, + .p.capabilities = AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(Msvideo1EncContext), .init = encode_init, FF_CODEC_ENCODE_CB(encode_frame), diff --git a/libavcodec/mv30.c b/libavcodec/mv30.c index 24b04400fde..c2d05470534 100644 --- a/libavcodec/mv30.c +++ b/libavcodec/mv30.c @@ -623,9 +623,8 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, get_qtable(s->intraq_tab[0], s->intra_quant, luma_tab); get_qtable(s->intraq_tab[1], s->intra_quant, chroma_tab); - frame->key_frame = s->is_inter == 0; - - if (frame->key_frame) { + if (s->is_inter == 0) { + frame->flags |= AV_FRAME_FLAG_KEY; ret = decode_intra(avctx, gb, frame); if (ret < 0) return ret; @@ -638,6 +637,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, return AVERROR_INVALIDDATA; } + frame->flags &= ~AV_FRAME_FLAG_KEY; ret = decode_inter(avctx, gb, frame, s->prev_frame); if (ret < 0) return ret; diff --git a/libavcodec/mvcdec.c b/libavcodec/mvcdec.c index 1e99f44a7d1..6c971f709ed 100644 --- a/libavcodec/mvcdec.c +++ b/libavcodec/mvcdec.c @@ -247,7 +247,7 @@ static int mvc_decode_frame(AVCodecContext *avctx, AVFrame *frame, return ret; frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; diff --git a/libavcodec/mvha.c b/libavcodec/mvha.c index 55056c91cf4..8fb4a69e9ec 100644 --- a/libavcodec/mvha.c +++ b/libavcodec/mvha.c @@ -272,7 +272,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, } frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; diff --git a/libavcodec/mwsc.c b/libavcodec/mwsc.c index f57648bb15a..c7045ac793f 100644 --- a/libavcodec/mwsc.c +++ b/libavcodec/mwsc.c @@ -119,10 +119,13 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, bytestream2_init(&gbp, s->prev_frame->data[0], avctx->height * s->prev_frame->linesize[0]); bytestream2_init_writer(&pb, frame->data[0], avctx->height * frame->linesize[0]); - frame->key_frame = rle_uncompress(&gb, &pb, &gbp, avctx->width, avctx->height, avctx->width * 3, - frame->linesize[0], s->prev_frame->linesize[0]); + if (rle_uncompress(&gb, &pb, &gbp, avctx->width, avctx->height, avctx->width * 3, + frame->linesize[0], s->prev_frame->linesize[0])) + frame->flags |= AV_FRAME_FLAG_KEY; + else + frame->flags &= ~AV_FRAME_FLAG_KEY; - frame->pict_type = frame->key_frame ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; + frame->pict_type = (frame->flags & AV_FRAME_FLAG_KEY) ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; av_frame_unref(s->prev_frame); if ((ret = av_frame_ref(s->prev_frame, frame)) < 0) diff --git a/libavcodec/mxpegdec.c b/libavcodec/mxpegdec.c index 760b12f0cce..73df2ff9ffa 100644 --- a/libavcodec/mxpegdec.c +++ b/libavcodec/mxpegdec.c @@ -286,11 +286,11 @@ static int mxpeg_decode_frame(AVCodecContext *avctx, AVFrame *rframe, AV_GET_BUFFER_FLAG_REF)) < 0) return ret; jpg->picture_ptr->pict_type = AV_PICTURE_TYPE_P; - jpg->picture_ptr->key_frame = 0; + jpg->picture_ptr->flags &= ~AV_FRAME_FLAG_KEY; jpg->got_picture = 1; } else { jpg->picture_ptr->pict_type = AV_PICTURE_TYPE_I; - jpg->picture_ptr->key_frame = 1; + jpg->picture_ptr->flags |= AV_FRAME_FLAG_KEY; } if (s->got_mxm_bitmask) { diff --git a/libavcodec/noise_bsf.c b/libavcodec/noise_bsf.c index 168f3aa3735..7bdaa3c1db6 100644 --- a/libavcodec/noise_bsf.c +++ b/libavcodec/noise_bsf.c @@ -86,6 +86,12 @@ static int noise_init(AVBSFContext *ctx) return AVERROR(ENOMEM); } + if (ctx->par_in->codec_id == AV_CODEC_ID_WRAPPED_AVFRAME && + strcmp(s->amount_str, "0")) { + av_log(ctx, AV_LOG_ERROR, "Wrapped AVFrame noising is unsupported\n"); + return AVERROR_PATCHWELCOME; + } + ret = av_expr_parse(&s->amount_pexpr, s->amount_str, var_names, NULL, NULL, NULL, NULL, 0, ctx); if (ret < 0) { diff --git a/libavcodec/notchlc.c b/libavcodec/notchlc.c index 90704e1aeba..d973c83ac73 100644 --- a/libavcodec/notchlc.c +++ b/libavcodec/notchlc.c @@ -514,7 +514,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; diff --git a/libavcodec/null.c b/libavcodec/null.c new file mode 100644 index 00000000000..d8e334437c7 --- /dev/null +++ b/libavcodec/null.c @@ -0,0 +1,97 @@ +/* + * Null codecs + * + * 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 "codec_internal.h" +#include "decode.h" +#include "encode.h" + +#if CONFIG_VNULL_DECODER || CONFIG_ANULL_DECODER +static int null_decode(AVCodecContext *avctx, AVFrame *frame, + int *got_frame, AVPacket *avpkt) +{ + *got_frame = 0; + return avpkt->size; +} + +#if CONFIG_VNULL_DECODER +const FFCodec ff_vnull_decoder = { + .p.name = "vnull", + CODEC_LONG_NAME("null video"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VNULL, + .p.capabilities = AV_CODEC_CAP_DR1, + FF_CODEC_DECODE_CB(null_decode), +}; +#endif + +#if CONFIG_ANULL_DECODER +const FFCodec ff_anull_decoder = { + .p.name = "anull", + CODEC_LONG_NAME("null audio"), + .p.type = AVMEDIA_TYPE_AUDIO, + .p.id = AV_CODEC_ID_ANULL, + .p.capabilities = AV_CODEC_CAP_DR1, + FF_CODEC_DECODE_CB(null_decode), +}; +#endif + +#endif + +#if CONFIG_VNULL_ENCODER || CONFIG_ANULL_ENCODER +static int null_encode(AVCodecContext *avctx, AVPacket *pkt, + const AVFrame *frame, int *got_packet) +{ + *got_packet = 0; + return 0; +} + +#if CONFIG_VNULL_ENCODER +const FFCodec ff_vnull_encoder = { + .p.name = "vnull", + CODEC_LONG_NAME("null video"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VNULL, + FF_CODEC_ENCODE_CB(null_encode), +}; +#endif + +#if CONFIG_ANULL_ENCODER +const FFCodec ff_anull_encoder = { + .p.name = "anull", + CODEC_LONG_NAME("null audio"), + .p.type = AVMEDIA_TYPE_AUDIO, + .p.id = AV_CODEC_ID_ANULL, + .p.capabilities = AV_CODEC_CAP_VARIABLE_FRAME_SIZE, + .p.sample_fmts = (const enum AVSampleFormat[]){ + AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_U8P, + AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S16P, + AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_S32P, + AV_SAMPLE_FMT_S64, AV_SAMPLE_FMT_S64P, + AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP, + AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBLP, + AV_SAMPLE_FMT_NONE, + }, + FF_CODEC_ENCODE_CB(null_encode), +}; +#endif + +#endif diff --git a/libavcodec/nuv.c b/libavcodec/nuv.c index 1d4f02217c1..82048e93268 100644 --- a/libavcodec/nuv.c +++ b/libavcodec/nuv.c @@ -139,7 +139,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *picture, int size_change = 0; int minsize = 0; int flags = 0; - int result, init_frame = !avctx->frame_number; + int result, init_frame = !avctx->frame_num; enum { NUV_UNCOMPRESSED = '0', NUV_RTJPEG = '1', @@ -263,7 +263,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *picture, } c->pic->pict_type = keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; - c->pic->key_frame = keyframe; + if (keyframe) + c->pic->flags |= AV_FRAME_FLAG_KEY; + else + c->pic->flags &= ~AV_FRAME_FLAG_KEY; // decompress/copy/whatever data switch (comptype) { case NUV_LZO: diff --git a/libavcodec/nvdec_av1.c b/libavcodec/nvdec_av1.c index 3bbcd761232..74b04421778 100644 --- a/libavcodec/nvdec_av1.c +++ b/libavcodec/nvdec_av1.c @@ -23,6 +23,7 @@ #include "avcodec.h" #include "nvdec.h" #include "decode.h" +#include "hwaccel_internal.h" #include "internal.h" #include "av1dec.h" @@ -337,11 +338,11 @@ static int nvdec_av1_frame_params(AVCodecContext *avctx, AVBufferRef *hw_frames_ return ff_nvdec_frame_params(avctx, hw_frames_ctx, 8 * 2, 0); } -const AVHWAccel ff_av1_nvdec_hwaccel = { - .name = "av1_nvdec", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_AV1, - .pix_fmt = AV_PIX_FMT_CUDA, +const FFHWAccel ff_av1_nvdec_hwaccel = { + .p.name = "av1_nvdec", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_AV1, + .p.pix_fmt = AV_PIX_FMT_CUDA, .start_frame = nvdec_av1_start_frame, .end_frame = ff_nvdec_simple_end_frame, .decode_slice = nvdec_av1_decode_slice, diff --git a/libavcodec/nvdec_h264.c b/libavcodec/nvdec_h264.c index 116bd4fb5de..f022619b64a 100644 --- a/libavcodec/nvdec_h264.c +++ b/libavcodec/nvdec_h264.c @@ -28,6 +28,7 @@ #include "decode.h" #include "internal.h" #include "h264dec.h" +#include "hwaccel_internal.h" static void dpb_add(const H264Context *h, CUVIDH264DPBENTRY *dst, const H264Picture *src, int frame_idx) @@ -169,11 +170,11 @@ static int nvdec_h264_frame_params(AVCodecContext *avctx, return ff_nvdec_frame_params(avctx, hw_frames_ctx, sps->ref_frame_count + sps->num_reorder_frames, 0); } -const AVHWAccel ff_h264_nvdec_hwaccel = { - .name = "h264_nvdec", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_H264, - .pix_fmt = AV_PIX_FMT_CUDA, +const FFHWAccel ff_h264_nvdec_hwaccel = { + .p.name = "h264_nvdec", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_H264, + .p.pix_fmt = AV_PIX_FMT_CUDA, .start_frame = nvdec_h264_start_frame, .end_frame = ff_nvdec_end_frame, .decode_slice = nvdec_h264_decode_slice, diff --git a/libavcodec/nvdec_hevc.c b/libavcodec/nvdec_hevc.c index cd549d2ef68..91fb9f5384c 100644 --- a/libavcodec/nvdec_hevc.c +++ b/libavcodec/nvdec_hevc.c @@ -29,6 +29,7 @@ #include "internal.h" #include "hevcdec.h" #include "hevc_data.h" +#include "hwaccel_internal.h" static void dpb_add(CUVIDHEVCPICPARAMS *pp, int idx, const HEVCFrame *src) { @@ -308,11 +309,11 @@ static int nvdec_hevc_decode_init(AVCodecContext *avctx) { return ff_nvdec_decode_init(avctx); } -const AVHWAccel ff_hevc_nvdec_hwaccel = { - .name = "hevc_nvdec", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_HEVC, - .pix_fmt = AV_PIX_FMT_CUDA, +const FFHWAccel ff_hevc_nvdec_hwaccel = { + .p.name = "hevc_nvdec", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_HEVC, + .p.pix_fmt = AV_PIX_FMT_CUDA, .start_frame = nvdec_hevc_start_frame, .end_frame = ff_nvdec_end_frame, .decode_slice = nvdec_hevc_decode_slice, diff --git a/libavcodec/nvdec_mjpeg.c b/libavcodec/nvdec_mjpeg.c index fce464c1f82..850634bf1a1 100644 --- a/libavcodec/nvdec_mjpeg.c +++ b/libavcodec/nvdec_mjpeg.c @@ -25,6 +25,7 @@ #include "mjpegdec.h" #include "nvdec.h" #include "decode.h" +#include "hwaccel_internal.h" static int nvdec_mjpeg_start_frame(AVCodecContext *avctx, const uint8_t *buffer, uint32_t size) { @@ -69,11 +70,11 @@ static int nvdec_mjpeg_frame_params(AVCodecContext *avctx, return ff_nvdec_frame_params(avctx, hw_frames_ctx, 1, 0); } -AVHWAccel ff_mjpeg_nvdec_hwaccel = { - .name = "mjpeg_nvdec", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MJPEG, - .pix_fmt = AV_PIX_FMT_CUDA, +const FFHWAccel ff_mjpeg_nvdec_hwaccel = { + .p.name = "mjpeg_nvdec", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MJPEG, + .p.pix_fmt = AV_PIX_FMT_CUDA, .start_frame = nvdec_mjpeg_start_frame, .end_frame = ff_nvdec_simple_end_frame, .decode_slice = nvdec_mjpeg_decode_slice, diff --git a/libavcodec/nvdec_mpeg12.c b/libavcodec/nvdec_mpeg12.c index e10735587d4..a4603c7ea78 100644 --- a/libavcodec/nvdec_mpeg12.c +++ b/libavcodec/nvdec_mpeg12.c @@ -23,6 +23,7 @@ #include "config_components.h" #include "avcodec.h" +#include "hwaccel_internal.h" #include "internal.h" #include "mpegvideo.h" #include "nvdec.h" @@ -83,8 +84,9 @@ static int nvdec_mpeg12_start_frame(AVCodecContext *avctx, const uint8_t *buffer }; for (i = 0; i < 64; ++i) { - ppc->QuantMatrixIntra[i] = s->intra_matrix[i]; - ppc->QuantMatrixInter[i] = s->inter_matrix[i]; + int n = s->idsp.idct_permutation[i]; + ppc->QuantMatrixIntra[i] = s->intra_matrix[n]; + ppc->QuantMatrixInter[i] = s->inter_matrix[n]; } return 0; @@ -98,11 +100,11 @@ static int nvdec_mpeg12_frame_params(AVCodecContext *avctx, } #if CONFIG_MPEG2_NVDEC_HWACCEL -const AVHWAccel ff_mpeg2_nvdec_hwaccel = { - .name = "mpeg2_nvdec", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG2VIDEO, - .pix_fmt = AV_PIX_FMT_CUDA, +const FFHWAccel ff_mpeg2_nvdec_hwaccel = { + .p.name = "mpeg2_nvdec", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG2VIDEO, + .p.pix_fmt = AV_PIX_FMT_CUDA, .start_frame = nvdec_mpeg12_start_frame, .end_frame = ff_nvdec_simple_end_frame, .decode_slice = ff_nvdec_simple_decode_slice, @@ -114,11 +116,11 @@ const AVHWAccel ff_mpeg2_nvdec_hwaccel = { #endif #if CONFIG_MPEG1_NVDEC_HWACCEL -const AVHWAccel ff_mpeg1_nvdec_hwaccel = { - .name = "mpeg1_nvdec", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG1VIDEO, - .pix_fmt = AV_PIX_FMT_CUDA, +const FFHWAccel ff_mpeg1_nvdec_hwaccel = { + .p.name = "mpeg1_nvdec", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG1VIDEO, + .p.pix_fmt = AV_PIX_FMT_CUDA, .start_frame = nvdec_mpeg12_start_frame, .end_frame = ff_nvdec_simple_end_frame, .decode_slice = ff_nvdec_simple_decode_slice, diff --git a/libavcodec/nvdec_mpeg4.c b/libavcodec/nvdec_mpeg4.c index eac138cc382..20a0499437b 100644 --- a/libavcodec/nvdec_mpeg4.c +++ b/libavcodec/nvdec_mpeg4.c @@ -26,6 +26,7 @@ #include "mpeg4videodefs.h" #include "nvdec.h" #include "decode.h" +#include "hwaccel_internal.h" static int nvdec_mpeg4_start_frame(AVCodecContext *avctx, const uint8_t *buffer, uint32_t size) { @@ -88,8 +89,9 @@ static int nvdec_mpeg4_start_frame(AVCodecContext *avctx, const uint8_t *buffer, }; for (i = 0; i < 64; ++i) { - ppc->QuantMatrixIntra[i] = s->intra_matrix[i]; - ppc->QuantMatrixInter[i] = s->inter_matrix[i]; + int n = s->idsp.idct_permutation[i]; + ppc->QuantMatrixIntra[i] = s->intra_matrix[n]; + ppc->QuantMatrixInter[i] = s->inter_matrix[n]; } // We need to pass the full frame buffer and not just the slice @@ -108,11 +110,11 @@ static int nvdec_mpeg4_frame_params(AVCodecContext *avctx, return ff_nvdec_frame_params(avctx, hw_frames_ctx, 2, 0); } -const AVHWAccel ff_mpeg4_nvdec_hwaccel = { - .name = "mpeg4_nvdec", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG4, - .pix_fmt = AV_PIX_FMT_CUDA, +const FFHWAccel ff_mpeg4_nvdec_hwaccel = { + .p.name = "mpeg4_nvdec", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG4, + .p.pix_fmt = AV_PIX_FMT_CUDA, .start_frame = nvdec_mpeg4_start_frame, .end_frame = ff_nvdec_simple_end_frame, .decode_slice = nvdec_mpeg4_decode_slice, diff --git a/libavcodec/nvdec_vc1.c b/libavcodec/nvdec_vc1.c index fae1cb0ab23..5096d784dfe 100644 --- a/libavcodec/nvdec_vc1.c +++ b/libavcodec/nvdec_vc1.c @@ -23,6 +23,7 @@ #include "config_components.h" #include "avcodec.h" +#include "hwaccel_internal.h" #include "internal.h" #include "nvdec.h" #include "decode.h" @@ -113,11 +114,11 @@ static int nvdec_vc1_frame_params(AVCodecContext *avctx, return ff_nvdec_frame_params(avctx, hw_frames_ctx, 2, 0); } -const AVHWAccel ff_vc1_nvdec_hwaccel = { - .name = "vc1_nvdec", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VC1, - .pix_fmt = AV_PIX_FMT_CUDA, +const FFHWAccel ff_vc1_nvdec_hwaccel = { + .p.name = "vc1_nvdec", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VC1, + .p.pix_fmt = AV_PIX_FMT_CUDA, .start_frame = nvdec_vc1_start_frame, .end_frame = ff_nvdec_simple_end_frame, .decode_slice = ff_nvdec_simple_decode_slice, @@ -128,11 +129,11 @@ const AVHWAccel ff_vc1_nvdec_hwaccel = { }; #if CONFIG_WMV3_NVDEC_HWACCEL -const AVHWAccel ff_wmv3_nvdec_hwaccel = { - .name = "wmv3_nvdec", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_WMV3, - .pix_fmt = AV_PIX_FMT_CUDA, +const FFHWAccel ff_wmv3_nvdec_hwaccel = { + .p.name = "wmv3_nvdec", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_WMV3, + .p.pix_fmt = AV_PIX_FMT_CUDA, .start_frame = nvdec_vc1_start_frame, .end_frame = ff_nvdec_simple_end_frame, .decode_slice = ff_nvdec_simple_decode_slice, diff --git a/libavcodec/nvdec_vp8.c b/libavcodec/nvdec_vp8.c index f174ca430f5..ff3b3f259c0 100644 --- a/libavcodec/nvdec_vp8.c +++ b/libavcodec/nvdec_vp8.c @@ -23,6 +23,7 @@ #include "avcodec.h" #include "nvdec.h" #include "decode.h" +#include "hwaccel_internal.h" #include "internal.h" #include "vp8.h" @@ -90,11 +91,11 @@ static int nvdec_vp8_frame_params(AVCodecContext *avctx, return ff_nvdec_frame_params(avctx, hw_frames_ctx, 3, 0); } -AVHWAccel ff_vp8_nvdec_hwaccel = { - .name = "vp8_nvdec", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VP8, - .pix_fmt = AV_PIX_FMT_CUDA, +const FFHWAccel ff_vp8_nvdec_hwaccel = { + .p.name = "vp8_nvdec", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VP8, + .p.pix_fmt = AV_PIX_FMT_CUDA, .start_frame = nvdec_vp8_start_frame, .end_frame = ff_nvdec_simple_end_frame, .decode_slice = ff_nvdec_simple_decode_slice, diff --git a/libavcodec/nvdec_vp9.c b/libavcodec/nvdec_vp9.c index a76bcf99433..e196391c6d4 100644 --- a/libavcodec/nvdec_vp9.c +++ b/libavcodec/nvdec_vp9.c @@ -25,6 +25,7 @@ #include "avcodec.h" #include "nvdec.h" #include "decode.h" +#include "hwaccel_internal.h" #include "internal.h" #include "vp9shared.h" @@ -169,11 +170,11 @@ static int nvdec_vp9_frame_params(AVCodecContext *avctx, return ff_nvdec_frame_params(avctx, hw_frames_ctx, 8, 0); } -const AVHWAccel ff_vp9_nvdec_hwaccel = { - .name = "vp9_nvdec", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VP9, - .pix_fmt = AV_PIX_FMT_CUDA, +const FFHWAccel ff_vp9_nvdec_hwaccel = { + .p.name = "vp9_nvdec", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VP9, + .p.pix_fmt = AV_PIX_FMT_CUDA, .start_frame = nvdec_vp9_start_frame, .end_frame = ff_nvdec_simple_end_frame, .decode_slice = ff_nvdec_simple_decode_slice, diff --git a/libavcodec/nvenc.c b/libavcodec/nvenc.c index f6df7cb6ac1..505b95f93c5 100644 --- a/libavcodec/nvenc.c +++ b/libavcodec/nvenc.c @@ -43,9 +43,14 @@ #define CHECK_CU(x) FF_CUDA_CHECK_DL(avctx, dl_fn->cuda_dl, x) #define NVENC_CAP 0x30 + +#ifndef NVENC_NO_DEPRECATED_RC #define IS_CBR(rc) (rc == NV_ENC_PARAMS_RC_CBR || \ rc == NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ || \ rc == NV_ENC_PARAMS_RC_CBR_HQ) +#else +#define IS_CBR(rc) (rc == NV_ENC_PARAMS_RC_CBR) +#endif const enum AVPixelFormat ff_nvenc_pix_fmts[] = { AV_PIX_FMT_YUV420P, @@ -437,7 +442,7 @@ static int nvenc_check_cap(AVCodecContext *avctx, NV_ENC_CAPS cap) static int nvenc_check_capabilities(AVCodecContext *avctx) { NvencContext *ctx = avctx->priv_data; - int ret; + int tmp, ret; ret = nvenc_check_codec_support(avctx); if (ret < 0) { @@ -518,16 +523,18 @@ static int nvenc_check_capabilities(AVCodecContext *avctx) } #ifdef NVENC_HAVE_BFRAME_REF_MODE + tmp = (ctx->b_ref_mode >= 0) ? ctx->b_ref_mode : NV_ENC_BFRAME_REF_MODE_DISABLED; ret = nvenc_check_cap(avctx, NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE); - if (ctx->b_ref_mode == NV_ENC_BFRAME_REF_MODE_EACH && ret != 1 && ret != 3) { + if (tmp == NV_ENC_BFRAME_REF_MODE_EACH && ret != 1 && ret != 3) { av_log(avctx, AV_LOG_WARNING, "Each B frame as reference is not supported\n"); return AVERROR(ENOSYS); - } else if (ctx->b_ref_mode != NV_ENC_BFRAME_REF_MODE_DISABLED && ret == 0) { + } else if (tmp != NV_ENC_BFRAME_REF_MODE_DISABLED && ret == 0) { av_log(avctx, AV_LOG_WARNING, "B frames as references are not supported\n"); return AVERROR(ENOSYS); } #else - if (ctx->b_ref_mode != 0) { + tmp = (ctx->b_ref_mode >= 0) ? ctx->b_ref_mode : 0; + if (tmp > 0) { av_log(avctx, AV_LOG_WARNING, "B frames as references need SDK 8.1 at build time\n"); return AVERROR(ENOSYS); } @@ -902,6 +909,7 @@ static void nvenc_override_rate_control(AVCodecContext *avctx) case NV_ENC_PARAMS_RC_CONSTQP: set_constqp(avctx); return; +#ifndef NVENC_NO_DEPRECATED_RC case NV_ENC_PARAMS_RC_VBR_MINQP: if (avctx->qmin < 0) { av_log(avctx, AV_LOG_WARNING, @@ -912,12 +920,15 @@ static void nvenc_override_rate_control(AVCodecContext *avctx) } /* fall through */ case NV_ENC_PARAMS_RC_VBR_HQ: +#endif case NV_ENC_PARAMS_RC_VBR: set_vbr(avctx); break; case NV_ENC_PARAMS_RC_CBR: +#ifndef NVENC_NO_DEPRECATED_RC case NV_ENC_PARAMS_RC_CBR_HQ: case NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ: +#endif break; } @@ -960,6 +971,10 @@ static av_cold int nvenc_recalc_surfaces(AVCodecContext *avctx) ctx->nb_surfaces = FFMAX(1, FFMIN(MAX_REGISTERED_FRAMES, ctx->nb_surfaces)); ctx->async_depth = FFMIN(ctx->async_depth, ctx->nb_surfaces - 1); + // Output in the worst case will only start when the surface buffer is completely full. + // Hence we need to keep at least the max amount of surfaces plus the max reorder delay around. + ctx->frame_data_array_nb = ctx->nb_surfaces + ctx->encode_config.frameIntervalP - 1; + return 0; } @@ -1156,8 +1171,9 @@ static av_cold int nvenc_setup_h264_config(AVCodecContext *avctx) if (ctx->intra_refresh) { h264->enableIntraRefresh = 1; - h264->intraRefreshPeriod = avctx->gop_size; - h264->intraRefreshCnt = avctx->gop_size - 1; + h264->intraRefreshPeriod = cc->gopLength; + h264->intraRefreshCnt = cc->gopLength - 1; + cc->gopLength = NVENC_INFINITE_GOPLENGTH; #ifdef NVENC_HAVE_SINGLE_SLICE_INTRA_REFRESH h264->singleSliceIntraRefresh = ctx->single_slice_intra_refresh; #endif @@ -1175,11 +1191,7 @@ static av_cold int nvenc_setup_h264_config(AVCodecContext *avctx) h264->maxNumRefFrames = ctx->dpb_size; } - if (ctx->intra_refresh) { - h264->idrPeriod = NVENC_INFINITE_GOPLENGTH; - } else if (avctx->gop_size >= 0) { - h264->idrPeriod = avctx->gop_size; - } + h264->idrPeriod = cc->gopLength; if (IS_CBR(cc->rcParams.rateControlMode)) { h264->outputBufferingPeriodSEI = 1; @@ -1187,12 +1199,14 @@ static av_cold int nvenc_setup_h264_config(AVCodecContext *avctx) h264->outputPictureTimingSEI = 1; +#ifndef NVENC_NO_DEPRECATED_RC if (cc->rcParams.rateControlMode == NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ || cc->rcParams.rateControlMode == NV_ENC_PARAMS_RC_CBR_HQ || cc->rcParams.rateControlMode == NV_ENC_PARAMS_RC_VBR_HQ) { h264->adaptiveTransformMode = NV_ENC_H264_ADAPTIVE_TRANSFORM_ENABLE; h264->fmoMode = NV_ENC_H264_FMO_DISABLE; } +#endif if (ctx->flags & NVENC_LOSSLESS) { h264->qpPrimeYZeroTransformBypassFlag = 1; @@ -1278,8 +1292,9 @@ static av_cold int nvenc_setup_hevc_config(AVCodecContext *avctx) if (ctx->intra_refresh) { hevc->enableIntraRefresh = 1; - hevc->intraRefreshPeriod = avctx->gop_size; - hevc->intraRefreshCnt = avctx->gop_size - 1; + hevc->intraRefreshPeriod = cc->gopLength; + hevc->intraRefreshCnt = cc->gopLength - 1; + cc->gopLength = NVENC_INFINITE_GOPLENGTH; #ifdef NVENC_HAVE_SINGLE_SLICE_INTRA_REFRESH hevc->singleSliceIntraRefresh = ctx->single_slice_intra_refresh; #endif @@ -1299,11 +1314,7 @@ static av_cold int nvenc_setup_hevc_config(AVCodecContext *avctx) hevc->maxNumRefFramesInDPB = ctx->dpb_size; } - if (ctx->intra_refresh) { - hevc->idrPeriod = NVENC_INFINITE_GOPLENGTH; - } else if (avctx->gop_size >= 0) { - hevc->idrPeriod = avctx->gop_size; - } + hevc->idrPeriod = cc->gopLength; if (IS_CBR(cc->rcParams.rateControlMode)) { hevc->outputBufferingPeriodSEI = 1; @@ -1396,14 +1407,13 @@ static av_cold int nvenc_setup_av1_config(AVCodecContext *avctx) if (ctx->intra_refresh) { av1->enableIntraRefresh = 1; - av1->intraRefreshPeriod = avctx->gop_size; - av1->intraRefreshCnt = avctx->gop_size - 1; - - av1->idrPeriod = NVENC_INFINITE_GOPLENGTH; - } else if (avctx->gop_size >= 0) { - av1->idrPeriod = avctx->gop_size; + av1->intraRefreshPeriod = cc->gopLength; + av1->intraRefreshCnt = cc->gopLength - 1; + cc->gopLength = NVENC_INFINITE_GOPLENGTH; } + av1->idrPeriod = cc->gopLength; + if (IS_CBR(cc->rcParams.rateControlMode)) { av1->enableBitstreamPadding = 1; } @@ -1547,7 +1557,13 @@ static av_cold int nvenc_setup_encoder(AVCodecContext *avctx) ctx->init_encode_params.frameRateDen = avctx->framerate.den; } else { ctx->init_encode_params.frameRateNum = avctx->time_base.den; - ctx->init_encode_params.frameRateDen = avctx->time_base.num * avctx->ticks_per_frame; +FF_DISABLE_DEPRECATION_WARNINGS + ctx->init_encode_params.frameRateDen = avctx->time_base.num +#if FF_API_TICKS_PER_FRAME + * avctx->ticks_per_frame +#endif + ; +FF_ENABLE_DEPRECATION_WARNINGS } ctx->init_encode_params.enableEncodeAsync = 0; @@ -1580,24 +1596,22 @@ static av_cold int nvenc_setup_encoder(AVCodecContext *avctx) } if (avctx->gop_size > 0) { - if (avctx->max_b_frames >= 0) { - /* 0 is intra-only, 1 is I/P only, 2 is one B-Frame, 3 two B-frames, and so on. */ - ctx->encode_config.frameIntervalP = avctx->max_b_frames + 1; - } - + // only overwrite preset if a GOP size was selected as input ctx->encode_config.gopLength = avctx->gop_size; } else if (avctx->gop_size == 0) { ctx->encode_config.frameIntervalP = 0; ctx->encode_config.gopLength = 1; } + if (avctx->max_b_frames >= 0 && ctx->encode_config.gopLength > 1) { + /* 0 is intra-only, 1 is I/P only, 2 is one B-Frame, 3 two B-frames, and so on. */ + ctx->encode_config.frameIntervalP = avctx->max_b_frames + 1; + } + /* force to enable intra refresh */ if(ctx->single_slice_intra_refresh) ctx->intra_refresh = 1; - if (ctx->intra_refresh) - ctx->encode_config.gopLength = NVENC_INFINITE_GOPLENGTH; - nvenc_recalc_surfaces(avctx); nvenc_setup_rate_control(avctx); @@ -1748,6 +1762,10 @@ static av_cold int nvenc_setup_surfaces(AVCodecContext *avctx) if (!ctx->surfaces) return AVERROR(ENOMEM); + ctx->frame_data_array = av_calloc(ctx->frame_data_array_nb, sizeof(*ctx->frame_data_array)); + if (!ctx->frame_data_array) + return AVERROR(ENOMEM); + ctx->timestamp_list = av_fifo_alloc2(ctx->nb_surfaces, sizeof(int64_t), 0); if (!ctx->timestamp_list) return AVERROR(ENOMEM); @@ -1838,6 +1856,12 @@ av_cold int ff_nvenc_encode_close(AVCodecContext *avctx) av_fifo_freep2(&ctx->output_surface_queue); av_fifo_freep2(&ctx->unused_surface_queue); + if (ctx->frame_data_array) { + for (i = 0; i < ctx->nb_surfaces; i++) + av_buffer_unref(&ctx->frame_data_array[i].frame_opaque_ref); + av_freep(&ctx->frame_data_array); + } + if (ctx->surfaces && (avctx->pix_fmt == AV_PIX_FMT_CUDA || avctx->pix_fmt == AV_PIX_FMT_D3D11)) { for (i = 0; i < ctx->nb_registered_frames; i++) { if (ctx->registered_frames[i].mapped) @@ -2200,8 +2224,13 @@ static int nvenc_set_timestamp(AVCodecContext *avctx, pkt->pts = params->outputTimeStamp; if (avctx->codec_descriptor->props & AV_CODEC_PROP_REORDER) { - pkt->dts = timestamp_queue_dequeue(ctx->timestamp_list); - pkt->dts -= FFMAX(ctx->encode_config.frameIntervalP - 1, 0) * FFMAX(avctx->ticks_per_frame, 1); +FF_DISABLE_DEPRECATION_WARNINGS + pkt->dts = timestamp_queue_dequeue(ctx->timestamp_list) - +#if FF_API_TICKS_PER_FRAME + FFMAX(avctx->ticks_per_frame, 1) * +#endif + FFMAX(ctx->encode_config.frameIntervalP - 1, 0); +FF_ENABLE_DEPRECATION_WARNINGS } else { pkt->dts = pkt->pts; } @@ -2209,6 +2238,65 @@ static int nvenc_set_timestamp(AVCodecContext *avctx, return 0; } +static int nvenc_store_frame_data(AVCodecContext *avctx, NV_ENC_PIC_PARAMS *pic_params, const AVFrame *frame) +{ + NvencContext *ctx = avctx->priv_data; + int res = 0; + + int idx = ctx->frame_data_array_pos; + NvencFrameData *frame_data = &ctx->frame_data_array[idx]; + + // in case the encoder got reconfigured, there might be leftovers + av_buffer_unref(&frame_data->frame_opaque_ref); + + if (frame->opaque_ref && avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { + frame_data->frame_opaque_ref = av_buffer_ref(frame->opaque_ref); + if (!frame_data->frame_opaque_ref) + return AVERROR(ENOMEM); + } + + frame_data->duration = frame->duration; + frame_data->frame_opaque = frame->opaque; + +#if FF_API_REORDERED_OPAQUE +FF_DISABLE_DEPRECATION_WARNINGS + frame_data->reordered_opaque = frame->reordered_opaque; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + ctx->frame_data_array_pos = (ctx->frame_data_array_pos + 1) % ctx->frame_data_array_nb; + pic_params->inputDuration = idx; + + return res; +} + +static int nvenc_retrieve_frame_data(AVCodecContext *avctx, NV_ENC_LOCK_BITSTREAM *lock_params, AVPacket *pkt) +{ + NvencContext *ctx = avctx->priv_data; + int res = 0; + + int idx = lock_params->outputDuration; + NvencFrameData *frame_data = &ctx->frame_data_array[idx]; + + pkt->duration = frame_data->duration; + +#if FF_API_REORDERED_OPAQUE +FF_DISABLE_DEPRECATION_WARNINGS + avctx->reordered_opaque = frame_data->reordered_opaque; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { + pkt->opaque = frame_data->frame_opaque; + pkt->opaque_ref = frame_data->frame_opaque_ref; + frame_data->frame_opaque_ref = NULL; + } + + av_buffer_unref(&frame_data->frame_opaque_ref); + + return res; +} + static int process_output_surface(AVCodecContext *avctx, AVPacket *pkt, NvencSurface *tmpoutsurf) { NvencContext *ctx = avctx->priv_data; @@ -2295,6 +2383,10 @@ static int process_output_surface(AVCodecContext *avctx, AVPacket *pkt, NvencSur if (res < 0) goto error2; + res = nvenc_retrieve_frame_data(avctx, &lock_params, pkt); + if (res < 0) + goto error2; + return 0; error: @@ -2566,7 +2658,7 @@ static int nvenc_send_frame(AVCodecContext *avctx, const AVFrame *frame) pic_params.outputBitstream = in_surf->output_surface; if (avctx->flags & AV_CODEC_FLAG_INTERLACED_DCT) { - if (frame->top_field_first) + if (frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) pic_params.pictureStruct = NV_ENC_PIC_STRUCT_FIELD_TOP_BOTTOM; else pic_params.pictureStruct = NV_ENC_PIC_STRUCT_FIELD_BOTTOM_TOP; @@ -2590,6 +2682,10 @@ static int nvenc_send_frame(AVCodecContext *avctx, const AVFrame *frame) sei_count = res; } + res = nvenc_store_frame_data(avctx, &pic_params, frame); + if (res < 0) + return res; + nvenc_codec_specific_pic_params(avctx, &pic_params, ctx->sei_data, sei_count); } else { pic_params.encodePicFlags = NV_ENC_PIC_FLAG_EOS; diff --git a/libavcodec/nvenc.h b/libavcodec/nvenc.h index 05a7ac48b17..3a4b456a41d 100644 --- a/libavcodec/nvenc.h +++ b/libavcodec/nvenc.h @@ -31,6 +31,7 @@ typedef void ID3D11Device; #include #include "compat/cuda/dynlink_loader.h" +#include "libavutil/buffer.h" #include "libavutil/fifo.h" #include "libavutil/opt.h" #include "hwconfig.h" @@ -77,6 +78,11 @@ typedef void ID3D11Device; #define NVENC_HAVE_SINGLE_SLICE_INTRA_REFRESH #endif +// SDK 12.1 compile time feature checks +#if NVENCAPI_CHECK_VERSION(12, 1) +#define NVENC_NO_DEPRECATED_RC +#endif + typedef struct NvencSurface { NV_ENC_INPUT_PTR input_surface; @@ -90,6 +96,18 @@ typedef struct NvencSurface NV_ENC_BUFFER_FORMAT format; } NvencSurface; +typedef struct NvencFrameData +{ + int64_t duration; + +#if FF_API_REORDERED_OPAQUE + int64_t reordered_opaque; +#endif + + void *frame_opaque; + AVBufferRef *frame_opaque_ref; +} NvencFrameData; + typedef struct NvencDynLoadFunctions { CudaFunctions *cuda_dl; @@ -168,6 +186,10 @@ typedef struct NvencContext int nb_surfaces; NvencSurface *surfaces; + NvencFrameData *frame_data_array; + int frame_data_array_nb; + int frame_data_array_pos; + AVFifo *unused_surface_queue; AVFifo *output_surface_queue; AVFifo *output_surface_ready_queue; diff --git a/libavcodec/nvenc_av1.c b/libavcodec/nvenc_av1.c index 2ed99d948b6..43643f7bf1a 100644 --- a/libavcodec/nvenc_av1.c +++ b/libavcodec/nvenc_av1.c @@ -154,7 +154,7 @@ static const FFCodecDefault defaults[] = { { "qdiff", "-1" }, { "qblur", "-1" }, { "qcomp", "-1" }, - { "g", "250" }, + { "g", "-1" }, { "bf", "-1" }, { "refs", "0" }, { NULL }, @@ -181,7 +181,8 @@ const FFCodec ff_av1_nvenc_encoder = { .defaults = defaults, .p.pix_fmts = ff_nvenc_pix_fmts, .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE | - AV_CODEC_CAP_ENCODER_FLUSH | AV_CODEC_CAP_DR1, + AV_CODEC_CAP_ENCODER_FLUSH | AV_CODEC_CAP_DR1 | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, .p.wrapper_name = "nvenc", diff --git a/libavcodec/nvenc_h264.c b/libavcodec/nvenc_h264.c index a69358b03b8..a99860998e7 100644 --- a/libavcodec/nvenc_h264.c +++ b/libavcodec/nvenc_h264.c @@ -100,6 +100,7 @@ static const AVOption options[] = { { "constqp", "Constant QP mode", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_CONSTQP }, 0, 0, VE, "rc" }, { "vbr", "Variable bitrate mode", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_VBR }, 0, 0, VE, "rc" }, { "cbr", "Constant bitrate mode", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_CBR }, 0, 0, VE, "rc" }, +#ifndef NVENC_NO_DEPRECATED_RC { "vbr_minqp", "Variable bitrate mode with MinQP (deprecated)", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR_MINQP) }, 0, 0, VE, "rc" }, { "ll_2pass_quality", "Multi-pass optimized for image quality (deprecated)", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_2_PASS_QUALITY) }, 0, 0, VE, "rc" }, @@ -109,6 +110,17 @@ static const AVOption options[] = { { "cbr_ld_hq", "Constant bitrate low delay high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ) }, 0, 0, VE, "rc" }, { "cbr_hq", "Constant bitrate high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR_HQ) }, 0, 0, VE, "rc" }, { "vbr_hq", "Variable bitrate high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR_HQ) }, 0, 0, VE, "rc" }, +#else + { "vbr_minqp", "Variable bitrate mode with MinQP (deprecated)", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) }, 0, 0, VE, "rc" }, + { "ll_2pass_quality", "Multi-pass optimized for image quality (deprecated)", + 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) }, 0, 0, VE, "rc" }, + { "ll_2pass_size", "Multi-pass optimized for constant frame size (deprecated)", + 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR) }, 0, 0, VE, "rc" }, + { "vbr_2pass", "Multi-pass variable bitrate mode (deprecated)", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) }, 0, 0, VE, "rc" }, + { "cbr_ld_hq", "Constant bitrate low delay high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR) }, 0, 0, VE, "rc" }, + { "cbr_hq", "Constant bitrate high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR) }, 0, 0, VE, "rc" }, + { "vbr_hq", "Variable bitrate high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) }, 0, 0, VE, "rc" }, +#endif { "rc-lookahead", "Number of frames to look ahead for rate-control", OFFSET(rc_lookahead), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, { "surfaces", "Number of concurrent surfaces", OFFSET(nb_surfaces), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, MAX_REGISTERED_FRAMES, VE }, @@ -206,7 +218,7 @@ static const FFCodecDefault defaults[] = { { "qdiff", "-1" }, { "qblur", "-1" }, { "qcomp", "-1" }, - { "g", "250" }, + { "g", "-1" }, { "bf", "-1" }, { "refs", "0" }, { NULL }, @@ -232,7 +244,8 @@ const FFCodec ff_h264_nvenc_encoder = { .p.priv_class = &h264_nvenc_class, .defaults = defaults, .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE | - AV_CODEC_CAP_ENCODER_FLUSH | AV_CODEC_CAP_DR1, + AV_CODEC_CAP_ENCODER_FLUSH | AV_CODEC_CAP_DR1 | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, .p.pix_fmts = ff_nvenc_pix_fmts, diff --git a/libavcodec/nvenc_hevc.c b/libavcodec/nvenc_hevc.c index 5ad423444a5..a02f277888c 100644 --- a/libavcodec/nvenc_hevc.c +++ b/libavcodec/nvenc_hevc.c @@ -89,6 +89,7 @@ static const AVOption options[] = { { "constqp", "Constant QP mode", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_CONSTQP }, 0, 0, VE, "rc" }, { "vbr", "Variable bitrate mode", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_VBR }, 0, 0, VE, "rc" }, { "cbr", "Constant bitrate mode", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_CBR }, 0, 0, VE, "rc" }, +#ifndef NVENC_NO_DEPRECATED_RC { "vbr_minqp", "Variable bitrate mode with MinQP (deprecated)", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR_MINQP) }, 0, 0, VE, "rc" }, { "ll_2pass_quality", "Multi-pass optimized for image quality (deprecated)", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_2_PASS_QUALITY) }, 0, 0, VE, "rc" }, @@ -98,6 +99,17 @@ static const AVOption options[] = { { "cbr_ld_hq", "Constant bitrate low delay high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ) }, 0, 0, VE, "rc" }, { "cbr_hq", "Constant bitrate high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR_HQ) }, 0, 0, VE, "rc" }, { "vbr_hq", "Variable bitrate high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR_HQ) }, 0, 0, VE, "rc" }, +#else + { "vbr_minqp", "Variable bitrate mode with MinQP (deprecated)", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) }, 0, 0, VE, "rc" }, + { "ll_2pass_quality", "Multi-pass optimized for image quality (deprecated)", + 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) }, 0, 0, VE, "rc" }, + { "ll_2pass_size", "Multi-pass optimized for constant frame size (deprecated)", + 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR) }, 0, 0, VE, "rc" }, + { "vbr_2pass", "Multi-pass variable bitrate mode (deprecated)", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) }, 0, 0, VE, "rc" }, + { "cbr_ld_hq", "Constant bitrate low delay high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR) }, 0, 0, VE, "rc" }, + { "cbr_hq", "Constant bitrate high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR) }, 0, 0, VE, "rc" }, + { "vbr_hq", "Variable bitrate high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) }, 0, 0, VE, "rc" }, +#endif { "rc-lookahead", "Number of frames to look ahead for rate-control", OFFSET(rc_lookahead), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, { "surfaces", "Number of concurrent surfaces", OFFSET(nb_surfaces), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, MAX_REGISTERED_FRAMES, VE }, @@ -187,7 +199,7 @@ static const FFCodecDefault defaults[] = { { "qdiff", "-1" }, { "qblur", "-1" }, { "qcomp", "-1" }, - { "g", "250" }, + { "g", "-1" }, { "bf", "-1" }, { "refs", "0" }, { NULL }, @@ -214,7 +226,8 @@ const FFCodec ff_hevc_nvenc_encoder = { .defaults = defaults, .p.pix_fmts = ff_nvenc_pix_fmts, .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE | - AV_CODEC_CAP_ENCODER_FLUSH | AV_CODEC_CAP_DR1, + AV_CODEC_CAP_ENCODER_FLUSH | AV_CODEC_CAP_DR1 | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, .p.wrapper_name = "nvenc", diff --git a/libavcodec/on2avc.c b/libavcodec/on2avc.c index 74be1dcb605..b190f36e194 100644 --- a/libavcodec/on2avc.c +++ b/libavcodec/on2avc.c @@ -51,9 +51,9 @@ typedef struct On2AVCContext { AVCodecContext *avctx; AVFloatDSPContext *fdsp; AVTXContext *mdct, *mdct_half, *mdct_small; - AVTXContext *fft128, *fft256, *fft512, *fft1024; + AVTXContext *fft64, *fft128, *fft256, *fft512; av_tx_fn mdct_fn, mdct_half_fn, mdct_small_fn; - av_tx_fn fft128_fn, fft256_fn, fft512_fn, fft1024_fn; + av_tx_fn fft64_fn, fft128_fn, fft256_fn, fft512_fn; void (*wtf)(struct On2AVCContext *ctx, float *out, float *in, int size); int is_av500; @@ -475,16 +475,16 @@ static void wtf_end_512(On2AVCContext *c, float *out, float *src, zero_head_and_tail(tmp1 + 256, 128, 13, 7); zero_head_and_tail(tmp1 + 384, 128, 15, 5); - c->fft128_fn(c->fft128, src + 0, tmp1 + 0, sizeof(float)); - c->fft128_fn(c->fft128, src + 128, tmp1 + 128, sizeof(float)); - c->fft128_fn(c->fft128, src + 256, tmp1 + 256, sizeof(float)); - c->fft128_fn(c->fft128, src + 384, tmp1 + 384, sizeof(float)); + c->fft64_fn(c->fft64, src + 0, tmp1 + 0, sizeof(float)); + c->fft64_fn(c->fft64, src + 128, tmp1 + 128, sizeof(float)); + c->fft64_fn(c->fft64, src + 256, tmp1 + 256, sizeof(float)); + c->fft64_fn(c->fft64, src + 384, tmp1 + 384, sizeof(float)); combine_fft(src, src + 128, src + 256, src + 384, tmp1, ff_on2avc_ctab_1, ff_on2avc_ctab_2, ff_on2avc_ctab_3, ff_on2avc_ctab_4, 512, 2); - c->fft512_fn(c->fft512, src, tmp1, sizeof(float)); + c->fft256_fn(c->fft256, src, tmp1, sizeof(float)); pretwiddle(&tmp0[ 0], src, 512, 84, 4, 16, 4, ff_on2avc_tabs_20_84_1); pretwiddle(&tmp0[128], src, 512, 84, 4, 16, 4, ff_on2avc_tabs_20_84_2); @@ -503,16 +503,16 @@ static void wtf_end_1024(On2AVCContext *c, float *out, float *src, zero_head_and_tail(tmp1 + 512, 256, 13, 7); zero_head_and_tail(tmp1 + 768, 256, 15, 5); - c->fft256_fn(c->fft256, src + 0, tmp1 + 0, sizeof(float)); - c->fft256_fn(c->fft256, src + 256, tmp1 + 256, sizeof(float)); - c->fft256_fn(c->fft256, src + 512, tmp1 + 512, sizeof(float)); - c->fft256_fn(c->fft256, src + 768, tmp1 + 768, sizeof(float)); + c->fft128_fn(c->fft128, src + 0, tmp1 + 0, sizeof(float)); + c->fft128_fn(c->fft128, src + 256, tmp1 + 256, sizeof(float)); + c->fft128_fn(c->fft128, src + 512, tmp1 + 512, sizeof(float)); + c->fft128_fn(c->fft128, src + 768, tmp1 + 768, sizeof(float)); combine_fft(src, src + 256, src + 512, src + 768, tmp1, ff_on2avc_ctab_1, ff_on2avc_ctab_2, ff_on2avc_ctab_3, ff_on2avc_ctab_4, 1024, 1); - c->fft1024_fn(c->fft1024, src, tmp1, sizeof(float)); + c->fft512_fn(c->fft512, src, tmp1, sizeof(float)); pretwiddle(&tmp0[ 0], src, 1024, 84, 4, 16, 4, ff_on2avc_tabs_20_84_1); pretwiddle(&tmp0[256], src, 1024, 84, 4, 16, 4, ff_on2avc_tabs_20_84_2); @@ -700,7 +700,7 @@ static int on2avc_reconstruct_channel_ext(On2AVCContext *c, AVFrame *dst, int of break; case WINDOW_TYPE_EXT5: c->wtf(c, buf, in, 512); - c->mdct_half_fn(c->mdct, buf + 512, in + 512, sizeof(float)); + c->mdct_half_fn(c->mdct_half, buf + 512, in + 512, sizeof(float)); for (i = 0; i < 256; i++) { FFSWAP(float, buf[i + 512], buf[1023 - i]); } @@ -956,14 +956,14 @@ static av_cold int on2avc_decode_init(AVCodecContext *avctx) if ((ret = av_tx_init(&c->mdct_small, &c->mdct_small_fn, AV_TX_FLOAT_MDCT, 1, 128, &scale, 0)) < 0) return ret; - if ((ret = av_tx_init(&c->fft1024, &c->fft1024_fn, AV_TX_FLOAT_FFT, 1, 1024, NULL, 0)) < 0) - return ret; if ((ret = av_tx_init(&c->fft512, &c->fft512_fn, AV_TX_FLOAT_FFT, 1, 512, NULL, 0)) < 0) return ret; - if ((ret = av_tx_init(&c->fft256, &c->fft256_fn, AV_TX_FLOAT_FFT, 0, 256, NULL, 0)) < 0) + if ((ret = av_tx_init(&c->fft256, &c->fft256_fn, AV_TX_FLOAT_FFT, 1, 256, NULL, 0)) < 0) return ret; if ((ret = av_tx_init(&c->fft128, &c->fft128_fn, AV_TX_FLOAT_FFT, 0, 128, NULL, 0)) < 0) return ret; + if ((ret = av_tx_init(&c->fft64, &c->fft64_fn, AV_TX_FLOAT_FFT, 0, 64, NULL, 0)) < 0) + return ret; c->fdsp = avpriv_float_dsp_alloc(avctx->flags & AV_CODEC_FLAG_BITEXACT); if (!c->fdsp) @@ -998,10 +998,10 @@ static av_cold int on2avc_decode_close(AVCodecContext *avctx) av_tx_uninit(&c->mdct); av_tx_uninit(&c->mdct_half); av_tx_uninit(&c->mdct_small); + av_tx_uninit(&c->fft64); av_tx_uninit(&c->fft128); av_tx_uninit(&c->fft256); av_tx_uninit(&c->fft512); - av_tx_uninit(&c->fft1024); av_freep(&c->fdsp); diff --git a/libavcodec/options.c b/libavcodec/options.c index 2e05d29e1ee..a9b35ee1c34 100644 --- a/libavcodec/options.c +++ b/libavcodec/options.c @@ -124,7 +124,11 @@ static int init_context_defaults(AVCodecContext *s, const AVCodec *codec) s->sw_pix_fmt = AV_PIX_FMT_NONE; s->sample_fmt = AV_SAMPLE_FMT_NONE; +#if FF_API_REORDERED_OPAQUE +FF_DISABLE_DEPRECATION_WARNINGS s->reordered_opaque = AV_NOPTS_VALUE; +FF_ENABLE_DEPRECATION_WARNINGS +#endif if(codec && codec2->priv_data_size){ s->priv_data = av_mallocz(codec2->priv_data_size); if (!s->priv_data) @@ -185,39 +189,6 @@ const AVClass *avcodec_get_class(void) return &av_codec_context_class; } -#if FF_API_GET_FRAME_CLASS -FF_DISABLE_DEPRECATION_WARNINGS -#define FOFFSET(x) offsetof(AVFrame,x) - -static const AVOption frame_options[]={ -{"best_effort_timestamp", "", FOFFSET(best_effort_timestamp), AV_OPT_TYPE_INT64, {.i64 = AV_NOPTS_VALUE }, INT64_MIN, INT64_MAX, 0}, -{"pkt_pos", "", FOFFSET(pkt_pos), AV_OPT_TYPE_INT64, {.i64 = -1 }, INT64_MIN, INT64_MAX, 0}, -{"pkt_size", "", FOFFSET(pkt_size), AV_OPT_TYPE_INT64, {.i64 = -1 }, INT64_MIN, INT64_MAX, 0}, -{"sample_aspect_ratio", "", FOFFSET(sample_aspect_ratio), AV_OPT_TYPE_RATIONAL, {.dbl = 0 }, 0, INT_MAX, 0}, -{"width", "", FOFFSET(width), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, 0}, -{"height", "", FOFFSET(height), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, 0}, -{"format", "", FOFFSET(format), AV_OPT_TYPE_INT, {.i64 = -1 }, 0, INT_MAX, 0}, -#if FF_API_OLD_CHANNEL_LAYOUT -{"channel_layout", "", FOFFSET(channel_layout), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT64_MAX, 0}, -#endif -{"sample_rate", "", FOFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, 0}, -{NULL}, -}; - -static const AVClass av_frame_class = { - .class_name = "AVFrame", - .item_name = NULL, - .option = frame_options, - .version = LIBAVUTIL_VERSION_INT, -}; - -const AVClass *avcodec_get_frame_class(void) -{ - return &av_frame_class; -} -FF_ENABLE_DEPRECATION_WARNINGS -#endif - #define SROFFSET(x) offsetof(AVSubtitleRect,x) static const AVOption subtitle_rect_options[]={ diff --git a/libavcodec/options_table.h b/libavcodec/options_table.h index 7924ca61443..bb4b894b064 100644 --- a/libavcodec/options_table.h +++ b/libavcodec/options_table.h @@ -50,7 +50,7 @@ static const AVOption avcodec_options[] = { {"bt", "Set video bitrate tolerance (in bits/s). In 1-pass mode, bitrate tolerance specifies how far " "ratecontrol is willing to deviate from the target average bitrate value. This is not related " "to minimum/maximum bitrate. Lowering tolerance too much has an adverse effect on quality.", - OFFSET(bit_rate_tolerance), AV_OPT_TYPE_INT, {.i64 = AV_CODEC_DEFAULT_BITRATE*20 }, 1, INT_MAX, V|E}, + OFFSET(bit_rate_tolerance), AV_OPT_TYPE_INT, {.i64 = AV_CODEC_DEFAULT_BITRATE*20 }, 0, INT_MAX, A|V|E}, {"flags", NULL, OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, 0, UINT_MAX, V|A|S|E|D, "flags"}, {"unaligned", "allow decoders to produce unaligned output", 0, AV_OPT_TYPE_CONST, { .i64 = AV_CODEC_FLAG_UNALIGNED }, INT_MIN, INT_MAX, V | D, "flags" }, {"mv4", "use four motion vectors per macroblock (MPEG-4)", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_4MV }, INT_MIN, INT_MAX, V|E, "flags"}, @@ -58,13 +58,12 @@ static const AVOption avcodec_options[] = { {"loop", "use loop filter", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_LOOP_FILTER }, INT_MIN, INT_MAX, V|E, "flags"}, {"qscale", "use fixed qscale", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_QSCALE }, INT_MIN, INT_MAX, 0, "flags"}, {"recon_frame", "export reconstructed frames", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_RECON_FRAME}, .unit = "flags"}, +{"copy_opaque", "propagate opaque values", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_COPY_OPAQUE}, .unit = "flags"}, +{"frame_duration", "use frame durations", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_FRAME_DURATION}, .unit = "flags"}, {"pass1", "use internal 2-pass ratecontrol in first pass mode", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_PASS1 }, INT_MIN, INT_MAX, 0, "flags"}, {"pass2", "use internal 2-pass ratecontrol in second pass mode", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_PASS2 }, INT_MIN, INT_MAX, 0, "flags"}, {"gray", "only decode/encode grayscale", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_GRAY }, INT_MIN, INT_MAX, V|E|D, "flags"}, {"psnr", "error[?] variables will be set during encoding", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_PSNR }, INT_MIN, INT_MAX, V|E, "flags"}, -#if FF_API_FLAG_TRUNCATED -{"truncated", "(Deprecated, use parsers instead.) Input bitstream might be randomly truncated", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_TRUNCATED }, INT_MIN, INT_MAX, V|D | AV_OPT_FLAG_DEPRECATED, "flags"}, -#endif {"ildct", "use interlaced DCT", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_INTERLACED_DCT }, INT_MIN, INT_MAX, V|E, "flags"}, {"low_delay", "force low delay", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_LOW_DELAY }, INT_MIN, INT_MAX, V|D|E, "flags"}, {"global_header", "place global headers in extradata instead of every keyframe", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_GLOBAL_HEADER }, INT_MIN, INT_MAX, V|A|E, "flags"}, @@ -73,7 +72,9 @@ static const AVOption avcodec_options[] = { {"ilme", "interlaced motion estimation", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_INTERLACED_ME }, INT_MIN, INT_MAX, V|E, "flags"}, {"cgop", "closed GOP", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_CLOSED_GOP }, INT_MIN, INT_MAX, V|E, "flags"}, {"output_corrupt", "Output even potentially corrupted frames", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_OUTPUT_CORRUPT }, INT_MIN, INT_MAX, V|D, "flags"}, -{"drop_changed", "Drop frames whose parameters differ from first decoded frame", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_DROPCHANGED }, INT_MIN, INT_MAX, A|V|D, "flags"}, +#if FF_API_DROPCHANGED +{"drop_changed", "Drop frames whose parameters differ from first decoded frame", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_DROPCHANGED }, INT_MIN, INT_MAX, A|V|D | AV_OPT_FLAG_DEPRECATED, "flags"}, +#endif {"flags2", NULL, OFFSET(flags2), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT}, 0, UINT_MAX, V|A|E|D|S, "flags2"}, {"fast", "allow non-spec-compliant speedup tricks", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG2_FAST }, INT_MIN, INT_MAX, V|E, "flags2"}, {"noout", "skip bitstream encoding", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG2_NO_OUTPUT }, INT_MIN, INT_MAX, V|E, "flags2"}, @@ -98,7 +99,7 @@ static const AVOption avcodec_options[] = { #endif {"cutoff", "set cutoff bandwidth", OFFSET(cutoff), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, A|E}, {"frame_size", NULL, OFFSET(frame_size), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, INT_MAX, A|E}, -{"frame_number", NULL, OFFSET(frame_number), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX}, +{"frame_number", NULL, OFFSET(frame_num), AV_OPT_TYPE_INT64, {.i64 = DEFAULT }, INT_MIN, INT_MAX}, {"delay", NULL, OFFSET(delay), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX}, {"qcomp", "video quantizer scale compression (VBR). Constant of ratecontrol equation. " "Recommended range for default rc_eq: 0.0-1.0", @@ -178,7 +179,9 @@ static const AVOption avcodec_options[] = { {"xvidmmx", "deprecated, for compatibility only", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_XVID }, INT_MIN, INT_MAX, V|E|D, "idct"}, {"faani", "floating point AAN IDCT", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_FAAN }, INT_MIN, INT_MAX, V|D|E, "idct"}, {"simpleauto", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLEAUTO }, INT_MIN, INT_MAX, V|E|D, "idct"}, +#if FF_API_SLICE_OFFSET {"slice_count", NULL, OFFSET(slice_count), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX}, +#endif {"ec", "set error concealment strategy", OFFSET(error_concealment), AV_OPT_TYPE_FLAGS, {.i64 = 3 }, INT_MIN, INT_MAX, V|D, "ec"}, {"guess_mvs", "iterative motion vector (MV) search (slow)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_EC_GUESS_MVS }, INT_MIN, INT_MAX, V|D, "ec"}, {"deblock", "use strong deblock filter for damaged MBs", 0, AV_OPT_TYPE_CONST, {.i64 = FF_EC_DEBLOCK }, INT_MIN, INT_MAX, V|D, "ec"}, @@ -258,8 +261,8 @@ static const AVOption avcodec_options[] = { {"default" , "discard useless frames", 0, AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_DEFAULT }, INT_MIN, INT_MAX, V|D, "avdiscard"}, {"noref" , "discard all non-reference frames", 0, AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_NONREF }, INT_MIN, INT_MAX, V|D, "avdiscard"}, {"bidir" , "discard all bidirectional frames", 0, AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_BIDIR }, INT_MIN, INT_MAX, V|D, "avdiscard"}, -{"nokey" , "discard all frames except keyframes", 0, AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_NONKEY }, INT_MIN, INT_MAX, V|D, "avdiscard"}, {"nointra" , "discard all frames except I frames", 0, AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_NONINTRA}, INT_MIN, INT_MAX, V|D, "avdiscard"}, +{"nokey" , "discard all frames except keyframes", 0, AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_NONKEY }, INT_MIN, INT_MAX, V|D, "avdiscard"}, {"all" , "discard all frames", 0, AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_ALL }, INT_MIN, INT_MAX, V|D, "avdiscard"}, {"bidir_refine", "refine the two motion vectors used in bidirectional macroblocks", OFFSET(bidir_refine), AV_OPT_TYPE_INT, {.i64 = 1 }, 0, 4, V|E}, {"keyint_min", "minimum interval between IDR-frames", OFFSET(keyint_min), AV_OPT_TYPE_INT, {.i64 = 25 }, INT_MIN, INT_MAX, V|E}, @@ -275,7 +278,9 @@ static const AVOption avcodec_options[] = { #endif {"rc_max_vbv_use", NULL, OFFSET(rc_max_available_vbv_use), AV_OPT_TYPE_FLOAT, {.dbl = 0 }, 0.0, FLT_MAX, V|E}, {"rc_min_vbv_use", NULL, OFFSET(rc_min_vbv_overflow_use), AV_OPT_TYPE_FLOAT, {.dbl = 3 }, 0.0, FLT_MAX, V|E}, +#if FF_API_TICKS_PER_FRAME {"ticks_per_frame", NULL, OFFSET(ticks_per_frame), AV_OPT_TYPE_INT, {.i64 = 1 }, 1, INT_MAX, A|V|E|D}, +#endif {"color_primaries", "color primaries", OFFSET(color_primaries), AV_OPT_TYPE_INT, {.i64 = AVCOL_PRI_UNSPECIFIED }, 1, INT_MAX, V|E|D, "color_primaries_type"}, {"bt709", "BT.709", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_BT709 }, INT_MIN, INT_MAX, V|E|D, "color_primaries_type"}, {"unknown", "Unspecified", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_UNSPECIFIED }, INT_MIN, INT_MAX, V|E|D, "color_primaries_type"}, @@ -377,10 +382,6 @@ static const AVOption avcodec_options[] = { {"auto", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_SUB_CHARENC_MODE_AUTOMATIC}, INT_MIN, INT_MAX, S|D, "sub_charenc_mode"}, {"pre_decoder", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_SUB_CHARENC_MODE_PRE_DECODER}, INT_MIN, INT_MAX, S|D, "sub_charenc_mode"}, {"ignore", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_SUB_CHARENC_MODE_IGNORE}, INT_MIN, INT_MAX, S|D, "sub_charenc_mode"}, -#if FF_API_SUB_TEXT_FORMAT -{"sub_text_format", "Deprecated, does nothing", OFFSET(sub_text_format), AV_OPT_TYPE_INT, {.i64 = FF_SUB_TEXT_FMT_ASS}, 0, 1, S|D | AV_OPT_FLAG_DEPRECATED, "sub_text_format"}, -{"ass", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_SUB_TEXT_FMT_ASS}, INT_MIN, INT_MAX, S|D, "sub_text_format"}, -#endif {"apply_cropping", NULL, OFFSET(apply_cropping), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, V | D }, {"skip_alpha", "Skip processing alpha", OFFSET(skip_alpha), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, V|D }, {"field_order", "Field order", OFFSET(field_order), AV_OPT_TYPE_INT, {.i64 = AV_FIELD_UNKNOWN }, 0, 5, V|D|E, "field_order" }, diff --git a/libavcodec/opusenc.c b/libavcodec/opusenc.c index 8b86aa7a354..a2f74a347b4 100644 --- a/libavcodec/opusenc.c +++ b/libavcodec/opusenc.c @@ -554,7 +554,7 @@ static int opus_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, ff_bufqueue_add(avctx, &s->bufqueue, av_frame_clone(frame)); } else { ff_opus_psy_signal_eof(&s->psyctx); - if (!s->afq.remaining_samples || !avctx->frame_number) + if (!s->afq.remaining_samples || !avctx->frame_num) return 0; /* We've been flushed and there's nothing left to encode */ } diff --git a/libavcodec/packet_internal.h b/libavcodec/packet_internal.h index 92a0d4e6d53..52fa6d9be90 100644 --- a/libavcodec/packet_internal.h +++ b/libavcodec/packet_internal.h @@ -23,6 +23,8 @@ #include "packet.h" +#define AVPACKET_IS_EMPTY(pkt) (!(pkt)->data && !(pkt)->side_data_elems) + typedef struct PacketListEntry { struct PacketListEntry *next; AVPacket pkt; diff --git a/libavcodec/pafvideo.c b/libavcodec/pafvideo.c index 458fe9ff47c..14eb42435a3 100644 --- a/libavcodec/pafvideo.c +++ b/libavcodec/pafvideo.c @@ -296,10 +296,10 @@ static int paf_video_decode(AVCodecContext *avctx, AVFrame *rframe, if (code & 0x20) { // frame is keyframe memset(c->pic->data[1], 0, AVPALETTE_SIZE); c->current_frame = 0; - c->pic->key_frame = 1; + c->pic->flags |= AV_FRAME_FLAG_KEY; c->pic->pict_type = AV_PICTURE_TYPE_I; } else { - c->pic->key_frame = 0; + c->pic->flags &= ~AV_FRAME_FLAG_KEY; c->pic->pict_type = AV_PICTURE_TYPE_P; } @@ -327,7 +327,11 @@ static int paf_video_decode(AVCodecContext *avctx, AVFrame *rframe, b = b << 2 | b >> 4; *out++ = (0xFFU << 24) | (r << 16) | (g << 8) | b; } +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS c->pic->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } c->dirty[c->current_frame] = 1; diff --git a/libavcodec/pamenc.c b/libavcodec/pamenc.c index 7d01e89f742..45ec29ccb38 100644 --- a/libavcodec/pamenc.c +++ b/libavcodec/pamenc.c @@ -133,7 +133,7 @@ const FFCodec ff_pam_encoder = { CODEC_LONG_NAME("PAM (Portable AnyMap) image"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_PAM, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, FF_CODEC_ENCODE_CB(pam_encode_frame), .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_RGB24, AV_PIX_FMT_RGBA, diff --git a/libavcodec/parser.c b/libavcodec/parser.c index 49de7e6a57a..efc28b89183 100644 --- a/libavcodec/parser.c +++ b/libavcodec/parser.c @@ -166,6 +166,10 @@ int av_parser_parse2(AVCodecParserContext *s, AVCodecContext *avctx, #define FILL(name) if(s->name > 0 && avctx->name <= 0) avctx->name = s->name if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) { FILL(field_order); + FILL(coded_width); + FILL(coded_height); + FILL(width); + FILL(height); } /* update the file pointer */ diff --git a/libavcodec/parsers.c b/libavcodec/parsers.c index d3558080181..285f81a901b 100644 --- a/libavcodec/parsers.c +++ b/libavcodec/parsers.c @@ -41,6 +41,7 @@ extern const AVCodecParser ff_dvaudio_parser; extern const AVCodecParser ff_dvbsub_parser; extern const AVCodecParser ff_dvdsub_parser; extern const AVCodecParser ff_dvd_nav_parser; +extern const AVCodecParser ff_evc_parser; extern const AVCodecParser ff_flac_parser; extern const AVCodecParser ff_ftr_parser; extern const AVCodecParser ff_g723_1_parser; @@ -74,6 +75,7 @@ extern const AVCodecParser ff_vorbis_parser; extern const AVCodecParser ff_vp3_parser; extern const AVCodecParser ff_vp8_parser; extern const AVCodecParser ff_vp9_parser; +extern const AVCodecParser ff_vvc_parser; extern const AVCodecParser ff_webp_parser; extern const AVCodecParser ff_xbm_parser; extern const AVCodecParser ff_xma_parser; diff --git a/libavcodec/pcm-blurayenc.c b/libavcodec/pcm-blurayenc.c index 62e86e722f3..bfbfa91d7ac 100644 --- a/libavcodec/pcm-blurayenc.c +++ b/libavcodec/pcm-blurayenc.c @@ -304,5 +304,5 @@ const FFCodec ff_pcm_bluray_encoder = { { 0 } }, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_NONE }, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, }; diff --git a/libavcodec/pcm-dvdenc.c b/libavcodec/pcm-dvdenc.c index 011d0a2f00e..a2e5cbdc2e9 100644 --- a/libavcodec/pcm-dvdenc.c +++ b/libavcodec/pcm-dvdenc.c @@ -176,7 +176,8 @@ const FFCodec ff_pcm_dvd_encoder = { CODEC_LONG_NAME("PCM signed 16|20|24-bit big-endian for DVD media"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_PCM_DVD, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SMALL_LAST_FRAME, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SMALL_LAST_FRAME | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(PCMDVDContext), .init = pcm_dvd_encode_init, FF_CODEC_ENCODE_CB(pcm_dvd_encode_frame), diff --git a/libavcodec/pcm.c b/libavcodec/pcm.c index ee36a364c8d..467ecb4fe09 100644 --- a/libavcodec/pcm.c +++ b/libavcodec/pcm.c @@ -553,7 +553,8 @@ const FFCodec ff_ ## name_ ## _encoder = { \ CODEC_LONG_NAME(long_name_), \ .p.type = AVMEDIA_TYPE_AUDIO, \ .p.id = AV_CODEC_ID_ ## id_, \ - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_VARIABLE_FRAME_SIZE, \ + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_VARIABLE_FRAME_SIZE | \ + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, \ .init = pcm_encode_init, \ FF_CODEC_ENCODE_CB(pcm_encode_frame), \ .p.sample_fmts = (const enum AVSampleFormat[]){ sample_fmt_, \ @@ -577,7 +578,7 @@ const FFCodec ff_ ## name_ ## _decoder = { \ .priv_data_size = sizeof(PCMDecode), \ .init = pcm_decode_init, \ FF_CODEC_DECODE_CB(pcm_decode_frame), \ - .p.capabilities = AV_CODEC_CAP_DR1, \ + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_PARAM_CHANGE, \ .p.sample_fmts = (const enum AVSampleFormat[]){ sample_fmt_, \ AV_SAMPLE_FMT_NONE }, \ } diff --git a/libavcodec/pcm_rechunk_bsf.c b/libavcodec/pcm_rechunk_bsf.c index 108d9e90b99..28b5722ac97 100644 --- a/libavcodec/pcm_rechunk_bsf.c +++ b/libavcodec/pcm_rechunk_bsf.c @@ -139,6 +139,7 @@ static int rechunk_filter(AVBSFContext *ctx, AVPacket *pkt) av_packet_move_ref(pkt, s->out_pkt); return send_packet(s, nb_samples, pkt); } + av_assert0(!s->in_pkt->size); } else if (s->in_pkt->size > data_size) { ret = av_packet_ref(pkt, s->in_pkt); if (ret < 0) @@ -151,7 +152,8 @@ static int rechunk_filter(AVBSFContext *ctx, AVPacket *pkt) av_packet_move_ref(pkt, s->in_pkt); return send_packet(s, nb_samples, pkt); } - } + } else + av_packet_unref(s->in_pkt); ret = ff_bsf_get_packet_ref(ctx, s->in_pkt); if (ret == AVERROR_EOF && s->out_pkt->size) { diff --git a/libavcodec/pcxenc.c b/libavcodec/pcxenc.c index 509158ba0f2..cf9b41b752b 100644 --- a/libavcodec/pcxenc.c +++ b/libavcodec/pcxenc.c @@ -197,7 +197,7 @@ const FFCodec ff_pcx_encoder = { CODEC_LONG_NAME("PC Paintbrush PCX image"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_PCX, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, FF_CODEC_ENCODE_CB(pcx_encode_frame), .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_RGB24, diff --git a/libavcodec/pdvdec.c b/libavcodec/pdvdec.c new file mode 100644 index 00000000000..d50c4e729c8 --- /dev/null +++ b/libavcodec/pdvdec.c @@ -0,0 +1,141 @@ +/* + * PDV video format + * + * Copyright (c) 2023 Paul B Mahol + * + * 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 "avcodec.h" +#include "codec_internal.h" +#include "decode.h" +#include "zlib_wrapper.h" + +#include + +typedef struct PDVContext { + AVFrame *previous_frame; + FFZStream zstream; +} PDVContext; + +static av_cold int decode_init(AVCodecContext *avctx) +{ + PDVContext *s = avctx->priv_data; + + avctx->pix_fmt = AV_PIX_FMT_MONOBLACK; + + s->previous_frame = av_frame_alloc(); + if (!s->previous_frame) + return AVERROR(ENOMEM); + + return ff_inflate_init(&s->zstream, avctx); +} + +static av_cold int decode_end(AVCodecContext *avctx) +{ + PDVContext *s = avctx->priv_data; + + av_frame_free(&s->previous_frame); + ff_inflate_end(&s->zstream); + + return 0; +} + +static int decode_frame(AVCodecContext *avctx, AVFrame *frame, + int *got_frame, AVPacket *avpkt) +{ + PDVContext *s = avctx->priv_data; + AVFrame *prev_frame = s->previous_frame; + z_stream *const zstream = &s->zstream.zstream; + uint8_t *dst, *prev = prev_frame->data[0]; + int ret, zret; + + if (avctx->skip_frame >= AVDISCARD_ALL || + (avctx->skip_frame >= AVDISCARD_NONINTRA && + !(avpkt->flags & AV_PKT_FLAG_KEY))) + return avpkt->size; + + zret = inflateReset(zstream); + if (zret != Z_OK) { + av_log(avctx, AV_LOG_ERROR, "Could not reset inflate: %d.\n", zret); + return AVERROR_INVALIDDATA; + } + + if ((ret = ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF)) < 0) + return ret; + + zstream->next_in = avpkt->data; + zstream->avail_in = avpkt->size; + + dst = frame->data[0]; + for (int i = 0; i < avctx->height; i++) { + zstream->next_out = dst; + zstream->avail_out = (avctx->width + 7) >> 3; + + zret = inflate(zstream, Z_SYNC_FLUSH); + if (zret != Z_OK && zret != Z_STREAM_END) { + av_log(avctx, AV_LOG_ERROR, + "Inflate failed with return code: %d.\n", zret); + return AVERROR_INVALIDDATA; + } + + if (prev && !(avpkt->flags & AV_PKT_FLAG_KEY)) { + for (int j = 0; j < (avctx->width + 7) >> 3; j++) + dst[j] ^= prev[j]; + prev += prev_frame->linesize[0]; + } + + dst += frame->linesize[0]; + } + + av_frame_unref(s->previous_frame); + if ((ret = av_frame_ref(s->previous_frame, frame)) < 0) + return ret; + + if (avpkt->flags & AV_PKT_FLAG_KEY) { + frame->flags |= AV_FRAME_FLAG_KEY; + frame->pict_type = AV_PICTURE_TYPE_I; + } else { + frame->pict_type = AV_PICTURE_TYPE_P; + } + + *got_frame = 1; + + return avpkt->size; +} + +static void decode_flush(AVCodecContext *avctx) +{ + PDVContext *s = avctx->priv_data; + + av_frame_unref(s->previous_frame); +} + +const FFCodec ff_pdv_decoder = { + .p.name = "pdv", + CODEC_LONG_NAME("PDV (PlayDate Video)"), + .priv_data_size = sizeof(PDVContext), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_PDV, + .p.capabilities = AV_CODEC_CAP_DR1, + .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM | + FF_CODEC_CAP_INIT_CLEANUP, + .init = decode_init, + .close = decode_end, + .flush = decode_flush, + FF_CODEC_DECODE_CB(decode_frame), +}; diff --git a/libavcodec/pgxdec.c b/libavcodec/pgxdec.c index e5d1df784e6..cc7cdb8c9ac 100644 --- a/libavcodec/pgxdec.c +++ b/libavcodec/pgxdec.c @@ -140,7 +140,7 @@ static int pgx_decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; avctx->bits_per_raw_sample = depth; if (bpp == 8) write_frame_8(p, &g, width, height, sign, depth); diff --git a/libavcodec/photocd.c b/libavcodec/photocd.c index 3030a80e0d6..3a09b81908f 100644 --- a/libavcodec/photocd.c +++ b/libavcodec/photocd.c @@ -332,7 +332,7 @@ static int photocd_decode_frame(AVCodecContext *avctx, AVFrame *p, return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; bytestream2_init(gb, avpkt->data, avpkt->size); diff --git a/libavcodec/pictordec.c b/libavcodec/pictordec.c index aef3d3de76f..5aaa725bd3e 100644 --- a/libavcodec/pictordec.c +++ b/libavcodec/pictordec.c @@ -191,7 +191,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, return ret; memset(frame->data[0], 0, s->height * frame->linesize[0]); frame->pict_type = AV_PICTURE_TYPE_I; +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif pos_after_pal = bytestream2_tell(&s->g) + esize; palette = (uint32_t*)frame->data[1]; diff --git a/libavcodec/pixlet.c b/libavcodec/pixlet.c index b349d397f4c..6e925308b80 100644 --- a/libavcodec/pixlet.c +++ b/libavcodec/pixlet.c @@ -667,7 +667,7 @@ static int pixlet_decode_frame(AVCodecContext *avctx, AVFrame *p, bytestream2_skip(&ctx->gb, 8); p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; p->color_range = AVCOL_RANGE_JPEG; ret = ff_thread_get_buffer(avctx, p, 0); diff --git a/libavcodec/pngdec.c b/libavcodec/pngdec.c index f1cad26c522..0369d1c449f 100644 --- a/libavcodec/pngdec.c +++ b/libavcodec/pngdec.c @@ -26,10 +26,12 @@ #include "libavutil/avassert.h" #include "libavutil/bprint.h" #include "libavutil/crc.h" +#include "libavutil/csp.h" #include "libavutil/imgutils.h" #include "libavutil/intreadwrite.h" +#include "libavutil/pixfmt.h" +#include "libavutil/rational.h" #include "libavutil/stereo3d.h" -#include "libavutil/mastering_display_metadata.h" #include "avcodec.h" #include "bytestream.h" @@ -73,6 +75,12 @@ typedef struct PNGDecContext { int have_chrm; uint32_t white_point[2]; uint32_t display_primaries[3][2]; + int gamma; + int have_srgb; + int have_cicp; + enum AVColorPrimaries cicp_primaries; + enum AVColorTransferCharacteristic cicp_trc; + enum AVColorRange cicp_range; enum PNGHeaderState hdr_state; enum PNGImageState pic_state; @@ -90,6 +98,7 @@ typedef struct PNGDecContext { int bpp; int has_trns; uint8_t transparent_color_be[6]; + int significant_bits; uint32_t palette[256]; uint8_t *crow_buf; @@ -322,7 +331,7 @@ void ff_png_filter_row(PNGDSPContext *dsp, uint8_t *dst, int filter_type, static void deloco_ ## NAME(TYPE *dst, int size, int alpha) \ { \ int i; \ - for (i = 0; i < size; i += 3 + alpha) { \ + for (i = 0; i < size - 2; i += 3 + alpha) { \ int g = dst [i + 1]; \ dst[i + 0] += g; \ dst[i + 2] += g; \ @@ -634,12 +643,99 @@ static int decode_phys_chunk(AVCodecContext *avctx, PNGDecContext *s, return 0; } +/* + * This populates AVCodecContext fields so it must be called before + * ff_thread_finish_setup() to avoid a race condition with respect to the + * generic copying of avctx fields. + */ +static int populate_avctx_color_fields(AVCodecContext *avctx, AVFrame *frame) +{ + PNGDecContext *s = avctx->priv_data; + + if (s->have_cicp) { + if (s->cicp_primaries >= AVCOL_PRI_NB) + av_log(avctx, AV_LOG_WARNING, "unrecognized cICP primaries\n"); + else + avctx->color_primaries = frame->color_primaries = s->cicp_primaries; + if (s->cicp_trc >= AVCOL_TRC_NB) + av_log(avctx, AV_LOG_WARNING, "unrecognized cICP transfer\n"); + else + avctx->color_trc = frame->color_trc = s->cicp_trc; + if (s->cicp_range == 0) + av_log(avctx, AV_LOG_WARNING, "unsupported tv-range cICP chunk\n"); + } else if (s->iccp_data) { + AVFrameSideData *sd = av_frame_new_side_data(frame, AV_FRAME_DATA_ICC_PROFILE, s->iccp_data_len); + if (!sd) + return AVERROR(ENOMEM); + memcpy(sd->data, s->iccp_data, s->iccp_data_len); + av_dict_set(&sd->metadata, "name", s->iccp_name, 0); + } else if (s->have_srgb) { + avctx->color_primaries = frame->color_primaries = AVCOL_PRI_BT709; + avctx->color_trc = frame->color_trc = AVCOL_TRC_IEC61966_2_1; + } else if (s->have_chrm) { + AVColorPrimariesDesc desc; + enum AVColorPrimaries prim; + desc.wp.x = av_make_q(s->white_point[0], 100000); + desc.wp.y = av_make_q(s->white_point[1], 100000); + desc.prim.r.x = av_make_q(s->display_primaries[0][0], 100000); + desc.prim.r.y = av_make_q(s->display_primaries[0][1], 100000); + desc.prim.g.x = av_make_q(s->display_primaries[1][0], 100000); + desc.prim.g.y = av_make_q(s->display_primaries[1][1], 100000); + desc.prim.b.x = av_make_q(s->display_primaries[2][0], 100000); + desc.prim.b.y = av_make_q(s->display_primaries[2][1], 100000); + prim = av_csp_primaries_id_from_desc(&desc); + if (prim != AVCOL_PRI_UNSPECIFIED) + avctx->color_primaries = frame->color_primaries = prim; + else + av_log(avctx, AV_LOG_WARNING, "unknown cHRM primaries\n"); + } + + /* these chunks override gAMA */ + if (s->iccp_data || s->have_srgb || s->have_cicp) { + av_dict_set(&s->frame_metadata, "gamma", NULL, 0); + } else if (s->gamma) { + /* + * These values are 100000/2.2, 100000/2.8, 100000/2.6, and + * 100000/1.0 respectively. 45455, 35714, and 38462, and 100000. + * There's a 0.001 gamma tolerance here in case of floating + * point issues when the PNG was written. + * + * None of the other enums have a pure gamma curve so it makes + * sense to leave those to sRGB and cICP. + */ + if (s->gamma > 45355 && s->gamma < 45555) + avctx->color_trc = frame->color_trc = AVCOL_TRC_GAMMA22; + else if (s->gamma > 35614 && s->gamma < 35814) + avctx->color_trc = frame->color_trc = AVCOL_TRC_GAMMA28; + else if (s->gamma > 38362 && s->gamma < 38562) + avctx->color_trc = frame->color_trc = AVCOL_TRC_SMPTE428; + else if (s->gamma > 99900 && s->gamma < 100100) + avctx->color_trc = frame->color_trc = AVCOL_TRC_LINEAR; + } + + /* we only support pc-range RGB */ + avctx->colorspace = frame->colorspace = AVCOL_SPC_RGB; + avctx->color_range = frame->color_range = AVCOL_RANGE_JPEG; + + /* + * tRNS sets alpha depth to full, so we ignore sBIT if set. + * As a result we must wait until now to set + * avctx->bits_per_raw_sample in case tRNS appears after sBIT + */ + if (!s->has_trns && s->significant_bits > 0) + avctx->bits_per_raw_sample = s->significant_bits; + + return 0; +} + static int decode_idat_chunk(AVCodecContext *avctx, PNGDecContext *s, GetByteContext *gb, AVFrame *p) { int ret; size_t byte_depth = s->bit_depth > 8 ? 2 : 1; + if (!p) + return AVERROR_INVALIDDATA; if (!(s->hdr_state & PNG_IHDR)) { av_log(avctx, AV_LOG_ERROR, "IDAT without IHDR\n"); return AVERROR_INVALIDDATA; @@ -746,9 +842,11 @@ static int decode_idat_chunk(AVCodecContext *avctx, PNGDecContext *s, } p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; - p->interlaced_frame = !!s->interlace_type; + p->flags |= AV_FRAME_FLAG_KEY; + p->flags |= AV_FRAME_FLAG_INTERLACED * !!s->interlace_type; + if ((ret = populate_avctx_color_fields(avctx, p)) < 0) + return ret; ff_thread_finish_setup(avctx); /* compute the compressed row size */ @@ -876,7 +974,7 @@ static int decode_trns_chunk(AVCodecContext *avctx, PNGDecContext *s, return 0; } -static int decode_iccp_chunk(PNGDecContext *s, GetByteContext *gb, AVFrame *f) +static int decode_iccp_chunk(PNGDecContext *s, GetByteContext *gb) { int ret, cnt = 0; AVBPrint bp; @@ -909,6 +1007,41 @@ static int decode_iccp_chunk(PNGDecContext *s, GetByteContext *gb, AVFrame *f) return ret; } +static int decode_sbit_chunk(AVCodecContext *avctx, PNGDecContext *s, + GetByteContext *gb) +{ + int bits = 0; + int channels; + + if (!(s->hdr_state & PNG_IHDR)) { + av_log(avctx, AV_LOG_ERROR, "sBIT before IHDR\n"); + return AVERROR_INVALIDDATA; + } + + if (s->pic_state & PNG_IDAT) { + av_log(avctx, AV_LOG_ERROR, "sBIT after IDAT\n"); + return AVERROR_INVALIDDATA; + } + + channels = ff_png_get_nb_channels(s->color_type); + + if (bytestream2_get_bytes_left(gb) != channels) + return AVERROR_INVALIDDATA; + + for (int i = 0; i < channels; i++) { + int b = bytestream2_get_byteu(gb); + bits = FFMAX(b, bits); + } + + if (bits < 0 || bits > s->bit_depth) { + av_log(avctx, AV_LOG_ERROR, "Invalid significant bits: %d\n", bits); + return AVERROR_INVALIDDATA; + } + s->significant_bits = bits; + + return 0; +} + static void handle_small_bpp(PNGDecContext *s, AVFrame *p) { if (s->bits_per_pixel == 1 && s->color_type == PNG_COLOR_TYPE_PALETTE) { @@ -1231,6 +1364,7 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s, } av_log(avctx, AV_LOG_ERROR, ", skipping\n"); bytestream2_skip(&s->gb, length + 8); /* tag */ + continue; } } tag = bytestream2_get_le32(&s->gb); @@ -1249,6 +1383,10 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s, case MKTAG('t', 'E', 'X', 't'): case MKTAG('I', 'D', 'A', 'T'): case MKTAG('t', 'R', 'N', 'S'): + case MKTAG('s', 'R', 'G', 'B'): + case MKTAG('c', 'I', 'C', 'P'): + case MKTAG('c', 'H', 'R', 'M'): + case MKTAG('g', 'A', 'M', 'A'): break; default: continue; @@ -1311,8 +1449,26 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s, } break; } + case MKTAG('c', 'I', 'C', 'P'): + s->cicp_primaries = bytestream2_get_byte(&gb_chunk); + s->cicp_trc = bytestream2_get_byte(&gb_chunk); + if (bytestream2_get_byte(&gb_chunk) != 0) + av_log(avctx, AV_LOG_WARNING, "nonzero cICP matrix\n"); + s->cicp_range = bytestream2_get_byte(&gb_chunk); + if (s->cicp_range != 0 && s->cicp_range != 1) { + av_log(avctx, AV_LOG_ERROR, "invalid cICP range: %d\n", s->cicp_range); + ret = AVERROR_INVALIDDATA; + goto fail; + } + s->have_cicp = 1; + break; + case MKTAG('s', 'R', 'G', 'B'): + /* skip rendering intent byte */ + bytestream2_skip(&gb_chunk, 1); + s->have_srgb = 1; + break; case MKTAG('i', 'C', 'C', 'P'): { - if ((ret = decode_iccp_chunk(s, &gb_chunk, p)) < 0) + if ((ret = decode_iccp_chunk(s, &gb_chunk)) < 0) goto fail; break; } @@ -1330,13 +1486,17 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s, break; } + case MKTAG('s', 'B', 'I', 'T'): + if ((ret = decode_sbit_chunk(avctx, s, &gb_chunk)) < 0) + goto fail; + break; case MKTAG('g', 'A', 'M', 'A'): { AVBPrint bp; char *gamma_str; - int num = bytestream2_get_be32(&gb_chunk); + s->gamma = bytestream2_get_be32(&gb_chunk); av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED); - av_bprintf(&bp, "%i/%i", num, 100000); + av_bprintf(&bp, "%i/%i", s->gamma, 100000); ret = av_bprint_finalize(&bp, &gamma_str); if (ret < 0) return ret; @@ -1357,6 +1517,9 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s, } exit_loop: + if (!p) + return AVERROR_INVALIDDATA; + if (avctx->codec_id == AV_CODEC_ID_PNG && avctx->skip_frame == AVDISCARD_ALL) { return 0; @@ -1466,6 +1629,8 @@ static void clear_frame_metadata(PNGDecContext *s) s->stereo_mode = -1; s->have_chrm = 0; + s->have_srgb = 0; + s->have_cicp = 0; av_dict_free(&s->frame_metadata); } @@ -1474,17 +1639,6 @@ static int output_frame(PNGDecContext *s, AVFrame *f) { int ret; - if (s->iccp_data) { - AVFrameSideData *sd = av_frame_new_side_data(f, AV_FRAME_DATA_ICC_PROFILE, s->iccp_data_len); - if (!sd) { - ret = AVERROR(ENOMEM); - goto fail; - } - memcpy(sd->data, s->iccp_data, s->iccp_data_len); - - av_dict_set(&sd->metadata, "name", s->iccp_name, 0); - } - if (s->stereo_mode >= 0) { AVStereo3D *stereo3d = av_stereo3d_create_side_data(f); if (!stereo3d) { @@ -1496,25 +1650,6 @@ static int output_frame(PNGDecContext *s, AVFrame *f) stereo3d->flags = s->stereo_mode ? 0 : AV_STEREO3D_FLAG_INVERT; } - if (s->have_chrm) { - AVMasteringDisplayMetadata *mdm = av_mastering_display_metadata_create_side_data(f); - if (!mdm) { - ret = AVERROR(ENOMEM); - goto fail; - } - - mdm->white_point[0] = av_make_q(s->white_point[0], 100000); - mdm->white_point[1] = av_make_q(s->white_point[1], 100000); - - /* RGB Primaries */ - for (int i = 0; i < 3; i++) { - mdm->display_primaries[i][0] = av_make_q(s->display_primaries[i][0], 100000); - mdm->display_primaries[i][1] = av_make_q(s->display_primaries[i][1], 100000); - } - - mdm->has_primaries = 1; - } - FFSWAP(AVDictionary*, f->metadata, s->frame_metadata); return 0; @@ -1597,7 +1732,7 @@ static int decode_frame_apng(AVCodecContext *avctx, AVFrame *p, if ((ret = inflateReset(&s->zstream.zstream)) != Z_OK) return AVERROR_EXTERNAL; bytestream2_init(&s->gb, avctx->extradata, avctx->extradata_size); - if ((ret = decode_frame_common(avctx, s, p, avpkt)) < 0) + if ((ret = decode_frame_common(avctx, s, NULL, avpkt)) < 0) return ret; } diff --git a/libavcodec/pngenc.c b/libavcodec/pngenc.c index ca1a186ca86..21b033ea163 100644 --- a/libavcodec/pngenc.c +++ b/libavcodec/pngenc.c @@ -30,9 +30,10 @@ #include "libavutil/avassert.h" #include "libavutil/crc.h" +#include "libavutil/csp.h" #include "libavutil/libm.h" #include "libavutil/opt.h" -#include "libavutil/color_utils.h" +#include "libavutil/rational.h" #include "libavutil/stereo3d.h" #include @@ -250,7 +251,7 @@ static void png_write_image_data(AVCodecContext *avctx, const AVCRC *crc_table = av_crc_get_table(AV_CRC_32_IEEE_LE); uint32_t crc = ~0U; - if (avctx->codec_id == AV_CODEC_ID_PNG || avctx->frame_number == 0) { + if (avctx->codec_id == AV_CODEC_ID_PNG || avctx->frame_num == 0) { png_write_chunk(&s->bytestream, MKTAG('I', 'D', 'A', 'T'), buf, length); return; } @@ -294,51 +295,28 @@ static int png_write_row(AVCodecContext *avctx, const uint8_t *data, int size) } #define AV_WB32_PNG(buf, n) AV_WB32(buf, lrint((n) * 100000)) +#define AV_WB32_PNG_D(buf, d) AV_WB32_PNG(buf, av_q2d(d)) static int png_get_chrm(enum AVColorPrimaries prim, uint8_t *buf) { - double rx, ry, gx, gy, bx, by, wx = 0.3127, wy = 0.3290; - switch (prim) { - case AVCOL_PRI_BT709: - rx = 0.640; ry = 0.330; - gx = 0.300; gy = 0.600; - bx = 0.150; by = 0.060; - break; - case AVCOL_PRI_BT470M: - rx = 0.670; ry = 0.330; - gx = 0.210; gy = 0.710; - bx = 0.140; by = 0.080; - wx = 0.310; wy = 0.316; - break; - case AVCOL_PRI_BT470BG: - rx = 0.640; ry = 0.330; - gx = 0.290; gy = 0.600; - bx = 0.150; by = 0.060; - break; - case AVCOL_PRI_SMPTE170M: - case AVCOL_PRI_SMPTE240M: - rx = 0.630; ry = 0.340; - gx = 0.310; gy = 0.595; - bx = 0.155; by = 0.070; - break; - case AVCOL_PRI_BT2020: - rx = 0.708; ry = 0.292; - gx = 0.170; gy = 0.797; - bx = 0.131; by = 0.046; - break; - default: - return 0; - } + const AVColorPrimariesDesc *desc = av_csp_primaries_desc_from_id(prim); + if (!desc) + return 0; + + AV_WB32_PNG_D(buf, desc->wp.x); + AV_WB32_PNG_D(buf + 4, desc->wp.y); + AV_WB32_PNG_D(buf + 8, desc->prim.r.x); + AV_WB32_PNG_D(buf + 12, desc->prim.r.y); + AV_WB32_PNG_D(buf + 16, desc->prim.g.x); + AV_WB32_PNG_D(buf + 20, desc->prim.g.y); + AV_WB32_PNG_D(buf + 24, desc->prim.b.x); + AV_WB32_PNG_D(buf + 28, desc->prim.b.y); - AV_WB32_PNG(buf , wx); AV_WB32_PNG(buf + 4 , wy); - AV_WB32_PNG(buf + 8 , rx); AV_WB32_PNG(buf + 12, ry); - AV_WB32_PNG(buf + 16, gx); AV_WB32_PNG(buf + 20, gy); - AV_WB32_PNG(buf + 24, bx); AV_WB32_PNG(buf + 28, by); return 1; } static int png_get_gama(enum AVColorTransferCharacteristic trc, uint8_t *buf) { - double gamma = avpriv_get_gamma_from_trc(trc); + double gamma = av_csp_approximate_trc_gamma(trc); if (gamma <= 1e-6) return 0; @@ -433,11 +411,30 @@ static int encode_headers(AVCodecContext *avctx, const AVFrame *pict) } } + side_data = av_frame_get_side_data(pict, AV_FRAME_DATA_ICC_PROFILE); + if ((ret = png_write_iccp(s, side_data))) + return ret; + /* write colorspace information */ if (pict->color_primaries == AVCOL_PRI_BT709 && pict->color_trc == AVCOL_TRC_IEC61966_2_1) { s->buf[0] = 1; /* rendering intent, relative colorimetric by default */ png_write_chunk(&s->bytestream, MKTAG('s', 'R', 'G', 'B'), s->buf, 1); + } else if (pict->color_trc != AVCOL_TRC_UNSPECIFIED && !side_data) { + /* + * Avoid writing cICP if the transfer is unknown. Known primaries + * with unknown transfer can be handled by cHRM. + * + * We also avoid writing cICP if an ICC Profile is present, because + * the standard requires that cICP overrides iCCP. + * + * These values match H.273 so no translation is needed. + */ + s->buf[0] = pict->color_primaries; + s->buf[1] = pict->color_trc; + s->buf[2] = 0; /* colorspace = RGB */ + s->buf[3] = pict->color_range == AVCOL_RANGE_MPEG ? 0 : 1; + png_write_chunk(&s->bytestream, MKTAG('c', 'I', 'C', 'P'), s->buf, 4); } if (png_get_chrm(pict->color_primaries, s->buf)) @@ -445,9 +442,11 @@ static int encode_headers(AVCodecContext *avctx, const AVFrame *pict) if (png_get_gama(pict->color_trc, s->buf)) png_write_chunk(&s->bytestream, MKTAG('g', 'A', 'M', 'A'), s->buf, 4); - side_data = av_frame_get_side_data(pict, AV_FRAME_DATA_ICC_PROFILE); - if ((ret = png_write_iccp(s, side_data))) - return ret; + if (avctx->bits_per_raw_sample > 0 && avctx->bits_per_raw_sample < s->bit_depth) { + int len = ff_png_get_nb_channels(s->color_type); + memset(s->buf, avctx->bits_per_raw_sample, len); + png_write_chunk(&s->bytestream, MKTAG('s', 'B', 'I', 'T'), s->buf, len); + } /* put the palette if needed, must be after colorspace information */ if (s->color_type == PNG_COLOR_TYPE_PALETTE) { @@ -806,7 +805,7 @@ static int apng_encode_frame(AVCodecContext *avctx, const AVFrame *pict, APNGFctlChunk last_fctl_chunk = *best_last_fctl_chunk; APNGFctlChunk fctl_chunk = *best_fctl_chunk; - if (avctx->frame_number == 0) { + if (avctx->frame_num == 0) { best_fctl_chunk->width = pict->width; best_fctl_chunk->height = pict->height; best_fctl_chunk->x_offset = 0; @@ -931,7 +930,7 @@ static int encode_apng(AVCodecContext *avctx, AVPacket *pkt, if (pict && s->color_type == PNG_COLOR_TYPE_PALETTE) { uint32_t checksum = ~av_crc(av_crc_get_table(AV_CRC_32_IEEE_LE), ~0U, pict->data[1], 256 * sizeof(uint32_t)); - if (avctx->frame_number == 0) { + if (avctx->frame_num == 0) { s->palette_checksum = checksum; } else if (checksum != s->palette_checksum) { av_log(avctx, AV_LOG_ERROR, @@ -953,7 +952,7 @@ static int encode_apng(AVCodecContext *avctx, AVPacket *pkt, if (max_packet_size > INT_MAX) return AVERROR(ENOMEM); - if (avctx->frame_number == 0) { + if (avctx->frame_num == 0) { if (!pict) return AVERROR(EINVAL); @@ -976,7 +975,12 @@ static int encode_apng(AVCodecContext *avctx, AVPacket *pkt, return ret; memcpy(pkt->data, s->last_frame_packet, s->last_frame_packet_size); - pkt->pts = pkt->dts = s->last_frame->pts; + pkt->pts = s->last_frame->pts; + pkt->duration = s->last_frame->duration; + + ret = ff_encode_reordered_opaque(avctx, pkt, s->last_frame); + if (ret < 0) + return ret; } if (pict) { @@ -1196,7 +1200,8 @@ const FFCodec ff_png_encoder = { CODEC_LONG_NAME("PNG (Portable Network Graphics) image"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_PNG, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(PNGEncContext), .init = png_enc_init, .close = png_enc_close, @@ -1218,7 +1223,8 @@ const FFCodec ff_apng_encoder = { CODEC_LONG_NAME("APNG (Animated Portable Network Graphics) image"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_APNG, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(PNGEncContext), .init = png_enc_init, .close = png_enc_close, diff --git a/libavcodec/pnmdec.c b/libavcodec/pnmdec.c index 978e4c037ea..72bc83b272c 100644 --- a/libavcodec/pnmdec.c +++ b/libavcodec/pnmdec.c @@ -65,7 +65,7 @@ static int pnm_decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; avctx->bits_per_raw_sample = av_log2(s->maxval) + 1; switch (avctx->pix_fmt) { diff --git a/libavcodec/pnmenc.c b/libavcodec/pnmenc.c index 4bdd2e032f3..9e1b11382b1 100644 --- a/libavcodec/pnmenc.c +++ b/libavcodec/pnmenc.c @@ -229,7 +229,7 @@ const FFCodec ff_pgm_encoder = { CODEC_LONG_NAME("PGM (Portable GrayMap) image"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_PGM, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, FF_CODEC_ENCODE_CB(pnm_encode_frame), .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY16BE, AV_PIX_FMT_NONE @@ -243,7 +243,7 @@ const FFCodec ff_pgmyuv_encoder = { CODEC_LONG_NAME("PGMYUV (Portable GrayMap YUV) image"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_PGMYUV, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, FF_CODEC_ENCODE_CB(pnm_encode_frame), .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P16BE, AV_PIX_FMT_NONE @@ -257,7 +257,7 @@ const FFCodec ff_ppm_encoder = { CODEC_LONG_NAME("PPM (Portable PixelMap) image"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_PPM, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, FF_CODEC_ENCODE_CB(pnm_encode_frame), .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_RGB24, AV_PIX_FMT_RGB48BE, AV_PIX_FMT_NONE @@ -271,7 +271,7 @@ const FFCodec ff_pbm_encoder = { CODEC_LONG_NAME("PBM (Portable BitMap) image"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_PBM, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, FF_CODEC_ENCODE_CB(pnm_encode_frame), .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_MONOWHITE, AV_PIX_FMT_NONE }, @@ -284,7 +284,7 @@ const FFCodec ff_pfm_encoder = { CODEC_LONG_NAME("PFM (Portable FloatMap) image"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_PFM, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, FF_CODEC_ENCODE_CB(pnm_encode_frame), .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_GBRPF32LE, AV_PIX_FMT_GRAYF32LE, @@ -309,7 +309,7 @@ const FFCodec ff_phm_encoder = { CODEC_LONG_NAME("PHM (Portable HalfFloatMap) image"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_PHM, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(PHMEncContext), .init = phm_enc_init, FF_CODEC_ENCODE_CB(pnm_encode_frame), diff --git a/libavcodec/ppc/fdctdsp.c b/libavcodec/ppc/fdctdsp.c index 4ab516c6b35..ae3c1b18ff7 100644 --- a/libavcodec/ppc/fdctdsp.c +++ b/libavcodec/ppc/fdctdsp.c @@ -25,6 +25,7 @@ #include "libavutil/ppc/cpu.h" #include "libavutil/ppc/util_altivec.h" +#include "libavcodec/avcodec.h" #include "libavcodec/fdctdsp.h" #include "fdct.h" diff --git a/libavcodec/profiles.c b/libavcodec/profiles.c index 7af7fbeb130..c646a3f54df 100644 --- a/libavcodec/profiles.c +++ b/libavcodec/profiles.c @@ -36,15 +36,27 @@ const AVProfile ff_aac_profiles[] = { }; const AVProfile ff_dca_profiles[] = { - { FF_PROFILE_DTS, "DTS" }, - { FF_PROFILE_DTS_ES, "DTS-ES" }, - { FF_PROFILE_DTS_96_24, "DTS 96/24" }, - { FF_PROFILE_DTS_HD_HRA, "DTS-HD HRA" }, - { FF_PROFILE_DTS_HD_MA, "DTS-HD MA" }, - { FF_PROFILE_DTS_EXPRESS, "DTS Express" }, + { FF_PROFILE_DTS, "DTS" }, + { FF_PROFILE_DTS_ES, "DTS-ES" }, + { FF_PROFILE_DTS_96_24, "DTS 96/24" }, + { FF_PROFILE_DTS_HD_HRA, "DTS-HD HRA" }, + { FF_PROFILE_DTS_HD_MA, "DTS-HD MA" }, + { FF_PROFILE_DTS_HD_MA_X, "DTS-HD MA + DTS:X" }, + { FF_PROFILE_DTS_HD_MA_X_IMAX, "DTS-HD MA + DTS:X IMAX" }, + { FF_PROFILE_DTS_EXPRESS, "DTS Express" }, { FF_PROFILE_UNKNOWN }, }; +const AVProfile ff_eac3_profiles[] = { + { FF_PROFILE_EAC3_DDP_ATMOS, "Dolby Digital Plus + Dolby Atmos"}, + { FF_PROFILE_UNKNOWN }, +}; + +const AVProfile ff_truehd_profiles[] = { + { FF_PROFILE_TRUEHD_ATMOS, "Dolby TrueHD + Dolby Atmos"}, + { FF_PROFILE_UNKNOWN }, +}; + const AVProfile ff_dnxhd_profiles[] = { { FF_PROFILE_DNXHD, "DNXHD"}, { FF_PROFILE_DNXHR_LB, "DNXHR LB"}, @@ -85,6 +97,7 @@ const AVProfile ff_hevc_profiles[] = { { FF_PROFILE_HEVC_MAIN_10, "Main 10" }, { FF_PROFILE_HEVC_MAIN_STILL_PICTURE, "Main Still Picture" }, { FF_PROFILE_HEVC_REXT, "Rext" }, + { FF_PROFILE_HEVC_SCC, "Scc" }, { FF_PROFILE_UNKNOWN }, }; @@ -181,4 +194,10 @@ const AVProfile ff_arib_caption_profiles[] = { { FF_PROFILE_UNKNOWN } }; +const AVProfile ff_evc_profiles[] = { + { FF_PROFILE_EVC_BASELINE, "Baseline" }, + { FF_PROFILE_EVC_MAIN, "Main" }, + { FF_PROFILE_UNKNOWN }, +}; + #endif /* !CONFIG_SMALL */ diff --git a/libavcodec/profiles.h b/libavcodec/profiles.h index 41a19aa9add..c0eacae5c16 100644 --- a/libavcodec/profiles.h +++ b/libavcodec/profiles.h @@ -58,6 +58,8 @@ extern const AVProfile ff_aac_profiles[]; extern const AVProfile ff_dca_profiles[]; +extern const AVProfile ff_eac3_profiles[]; +extern const AVProfile ff_truehd_profiles[]; extern const AVProfile ff_dnxhd_profiles[]; extern const AVProfile ff_h264_profiles[]; extern const AVProfile ff_hevc_profiles[]; @@ -72,5 +74,6 @@ extern const AVProfile ff_sbc_profiles[]; extern const AVProfile ff_prores_profiles[]; extern const AVProfile ff_mjpeg_profiles[]; extern const AVProfile ff_arib_caption_profiles[]; +extern const AVProfile ff_evc_profiles[]; #endif /* AVCODEC_PROFILES_H */ diff --git a/libavcodec/proresdec2.c b/libavcodec/proresdec2.c index c821a078491..ae5838eca42 100644 --- a/libavcodec/proresdec2.c +++ b/libavcodec/proresdec2.c @@ -37,6 +37,7 @@ #include "codec_internal.h" #include "decode.h" #include "get_bits.h" +#include "hwaccel_internal.h" #include "hwconfig.h" #include "idctdsp.h" #include "profiles.h" @@ -44,13 +45,6 @@ #include "proresdata.h" #include "thread.h" -static void permute(uint8_t *dst, const uint8_t *src, const uint8_t permutation[64]) -{ - int i; - for (i = 0; i < 64; i++) - dst[i] = permutation[src[i]]; -} - #define ALPHA_SHIFT_16_TO_10(alpha_val) (alpha_val >> 6) #define ALPHA_SHIFT_8_TO_10(alpha_val) ((alpha_val << 2) | (alpha_val >> 6)) #define ALPHA_SHIFT_16_TO_12(alpha_val) (alpha_val >> 4) @@ -187,8 +181,8 @@ static av_cold int decode_init(AVCodecContext *avctx) ff_init_scantable_permutation(idct_permutation, ctx->prodsp.idct_permutation_type); - permute(ctx->progressive_scan, ff_prores_progressive_scan, idct_permutation); - permute(ctx->interlaced_scan, ff_prores_interlaced_scan, idct_permutation); + ff_permute_scantable(ctx->progressive_scan, ff_prores_progressive_scan, idct_permutation); + ff_permute_scantable(ctx->interlaced_scan, ff_prores_interlaced_scan, idct_permutation); ctx->pix_fmt = AV_PIX_FMT_NONE; @@ -252,8 +246,9 @@ static int decode_frame_header(ProresContext *ctx, const uint8_t *buf, ctx->scan = ctx->progressive_scan; // permuted } else { ctx->scan = ctx->interlaced_scan; // permuted - ctx->frame->interlaced_frame = 1; - ctx->frame->top_field_first = ctx->frame_type == 1; + ctx->frame->flags |= AV_FRAME_FLAG_INTERLACED; + if (ctx->frame_type == 1) + ctx->frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; } if (ctx->alpha_info) { @@ -303,7 +298,7 @@ static int decode_frame_header(ProresContext *ctx, const uint8_t *buf, av_log(avctx, AV_LOG_ERROR, "Header truncated\n"); return AVERROR_INVALIDDATA; } - permute(ctx->qmat_luma, ctx->prodsp.idct_permutation, ptr); + ff_permute_scantable(ctx->qmat_luma, ctx->prodsp.idct_permutation, ptr); ptr += 64; } else { memset(ctx->qmat_luma, 4, 64); @@ -314,7 +309,7 @@ static int decode_frame_header(ProresContext *ctx, const uint8_t *buf, av_log(avctx, AV_LOG_ERROR, "Header truncated\n"); return AVERROR_INVALIDDATA; } - permute(ctx->qmat_chroma, ctx->prodsp.idct_permutation, ptr); + ff_permute_scantable(ctx->qmat_chroma, ctx->prodsp.idct_permutation, ptr); } else { memcpy(ctx->qmat_chroma, ctx->qmat_luma, 64); } @@ -706,7 +701,7 @@ static int decode_slice_thread(AVCodecContext *avctx, void *arg, int jobnr, int dest_u = pic->data[1] + (slice->mb_y << 4) * chroma_stride + (slice->mb_x << mb_x_shift); dest_v = pic->data[2] + (slice->mb_y << 4) * chroma_stride + (slice->mb_x << mb_x_shift); - if (ctx->frame_type && ctx->first_field ^ ctx->frame->top_field_first) { + if (ctx->frame_type && ctx->first_field ^ !!(ctx->frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)) { dest_y += pic->linesize[0]; dest_u += pic->linesize[1]; dest_v += pic->linesize[2]; @@ -792,7 +787,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, ctx->frame = frame; ctx->frame->pict_type = AV_PICTURE_TYPE_I; - ctx->frame->key_frame = 1; + ctx->frame->flags |= AV_FRAME_FLAG_KEY; ctx->first_field = 1; buf += 8; @@ -810,13 +805,14 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, ff_thread_finish_setup(avctx); if (avctx->hwaccel) { - ret = avctx->hwaccel->start_frame(avctx, NULL, 0); + const FFHWAccel *hwaccel = ffhwaccel(avctx->hwaccel); + ret = hwaccel->start_frame(avctx, NULL, 0); if (ret < 0) return ret; - ret = avctx->hwaccel->decode_slice(avctx, avpkt->data, avpkt->size); + ret = hwaccel->decode_slice(avctx, avpkt->data, avpkt->size); if (ret < 0) return ret; - ret = avctx->hwaccel->end_frame(avctx); + ret = hwaccel->end_frame(avctx); if (ret < 0) return ret; goto finish; diff --git a/libavcodec/proresenc_anatoliy.c b/libavcodec/proresenc_anatoliy.c index bd1b70cc5dc..c701275e5db 100644 --- a/libavcodec/proresenc_anatoliy.c +++ b/libavcodec/proresenc_anatoliy.c @@ -32,7 +32,6 @@ #include "avcodec.h" #include "codec_internal.h" #include "encode.h" -#include "internal.h" #include "profiles.h" #include "proresdata.h" #include "put_bits.h" @@ -198,6 +197,35 @@ typedef struct { char *vendor; } ProresContext; +/** + * Check if a value is in the list. If not, return the default value + * + * @param ctx Context for the log msg + * @param val_name Name of the checked value, for log msg + * @param array_valid_values Array of valid int, ended with INT_MAX + * @param default_value Value return if checked value is not in the array + * @return Value or default_value. + */ +static int int_from_list_or_default(void *ctx, const char *val_name, int val, + const int *array_valid_values, int default_value) +{ + int i = 0; + + while (1) { + int ref_val = array_valid_values[i]; + if (ref_val == INT_MAX) + break; + if (val == ref_val) + return val; + i++; + } + /* val is not a valid value */ + av_log(ctx, AV_LOG_DEBUG, + "%s %d are not supported. Set to default value : %d\n", + val_name, val, default_value); + return default_value; +} + static void encode_codeword(PutBitContext *pb, int val, int codebook) { unsigned int rice_order, exp_order, switch_bits, first_exp, exp, zeros; @@ -746,7 +774,8 @@ static int prores_encode_frame(AVCodecContext *avctx, AVPacket *pkt, if (avctx->profile >= FF_PROFILE_PRORES_4444) /* 4444 or 4444 Xq */ frame_flags |= 0x40; /* 444 chroma */ if (ctx->is_interlaced) { - if (pict->top_field_first || !pict->interlaced_frame) { /* tff frame or progressive frame interpret as tff */ + if ((pict->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) || !(pict->flags & AV_FRAME_FLAG_INTERLACED)) { + /* tff frame or progressive frame interpret as tff */ av_log(avctx, AV_LOG_DEBUG, "use interlaced encoding, top field first\n"); frame_flags |= 0x04; /* interlaced tff */ is_top_field_first = 1; @@ -760,9 +789,12 @@ static int prores_encode_frame(AVCodecContext *avctx, AVPacket *pkt, *buf++ = frame_flags; *buf++ = 0; /* reserved */ /* only write color properties, if valid value. set to unspecified otherwise */ - *buf++ = ff_int_from_list_or_default(avctx, "frame color primaries", pict->color_primaries, valid_primaries, 0); - *buf++ = ff_int_from_list_or_default(avctx, "frame color trc", pict->color_trc, valid_trc, 0); - *buf++ = ff_int_from_list_or_default(avctx, "frame colorspace", pict->colorspace, valid_colorspace, 0); + *buf++ = int_from_list_or_default(avctx, "frame color primaries", + pict->color_primaries, valid_primaries, 0); + *buf++ = int_from_list_or_default(avctx, "frame color trc", + pict->color_trc, valid_trc, 0); + *buf++ = int_from_list_or_default(avctx, "frame colorspace", + pict->colorspace, valid_colorspace, 0); if (avctx->profile >= FF_PROFILE_PRORES_4444) { if (avctx->pix_fmt == AV_PIX_FMT_YUV444P10) { *buf++ = 0xA0;/* src b64a and no alpha */ @@ -944,7 +976,8 @@ const FFCodec ff_prores_aw_encoder = { CODEC_LONG_NAME("Apple ProRes"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_PRORES, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .p.pix_fmts = pix_fmts, .priv_data_size = sizeof(ProresContext), .init = prores_encode_init, @@ -960,7 +993,8 @@ const FFCodec ff_prores_encoder = { CODEC_LONG_NAME("Apple ProRes"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_PRORES, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .p.pix_fmts = pix_fmts, .priv_data_size = sizeof(ProresContext), .init = prores_encode_init, diff --git a/libavcodec/proresenc_kostya.c b/libavcodec/proresenc_kostya.c index 5b38437d0aa..52fe5639b19 100644 --- a/libavcodec/proresenc_kostya.c +++ b/libavcodec/proresenc_kostya.c @@ -585,7 +585,7 @@ static int encode_slice(AVCodecContext *avctx, const AVFrame *pic, if (ctx->pictures_per_frame == 1) line_add = 0; else - line_add = ctx->cur_picture_idx ^ !pic->top_field_first; + line_add = ctx->cur_picture_idx ^ !(pic->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST); if (ctx->force_quant) { qmat = ctx->quants[0]; @@ -838,7 +838,7 @@ static int find_slice_quant(AVCodecContext *avctx, if (ctx->pictures_per_frame == 1) line_add = 0; else - line_add = ctx->cur_picture_idx ^ !ctx->pic->top_field_first; + line_add = ctx->cur_picture_idx ^ !(ctx->pic->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST); mbs = x + mbs_per_slice; for (i = 0; i < ctx->num_planes; i++) { @@ -1045,7 +1045,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, frame_flags = ctx->chroma_factor << 6; if (avctx->flags & AV_CODEC_FLAG_INTERLACED_DCT) - frame_flags |= pic->top_field_first ? 0x04 : 0x08; + frame_flags |= (pic->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? 0x04 : 0x08; bytestream_put_byte (&buf, frame_flags); bytestream_put_byte (&buf, 0); // reserved @@ -1355,6 +1355,7 @@ static av_cold int encode_init(AVCodecContext *avctx) } avctx->codec_tag = ctx->profile_info->tag; + avctx->profile = ctx->profile; av_log(avctx, AV_LOG_DEBUG, "profile %d, %d slices, interlacing: %s, %d bits per MB\n", @@ -1428,7 +1429,8 @@ const FFCodec ff_prores_ks_encoder = { .init = encode_init, .close = encode_close, FF_CODEC_ENCODE_CB(encode_frame), - .p.capabilities = AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_FRAME_THREADS, + .p.capabilities = AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_NONE diff --git a/libavcodec/prosumer.c b/libavcodec/prosumer.c index e199d1aaa9e..a1ed6a9e538 100644 --- a/libavcodec/prosumer.c +++ b/libavcodec/prosumer.c @@ -195,7 +195,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, } frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; diff --git a/libavcodec/psd.c b/libavcodec/psd.c index ee96bd12376..d3456e6b3dc 100644 --- a/libavcodec/psd.c +++ b/libavcodec/psd.c @@ -532,7 +532,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *picture, } if (s->color_mode == PSD_INDEXED) { +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS picture->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif memcpy(picture->data[1], s->palette, AVPALETTE_SIZE); } diff --git a/libavcodec/pthread.c b/libavcodec/pthread.c index 60ba87dac48..ca84b81391d 100644 --- a/libavcodec/pthread.c +++ b/libavcodec/pthread.c @@ -48,9 +48,6 @@ static void validate_thread_parameters(AVCodecContext *avctx) { int frame_threading_supported = (avctx->codec->capabilities & AV_CODEC_CAP_FRAME_THREADS) -#if FF_API_FLAG_TRUNCATED - && !(avctx->flags & AV_CODEC_FLAG_TRUNCATED) -#endif && !(avctx->flags & AV_CODEC_FLAG_LOW_DELAY) && !(avctx->flags2 & AV_CODEC_FLAG2_CHUNKS); if (avctx->thread_count == 1) { diff --git a/libavcodec/pthread_frame.c b/libavcodec/pthread_frame.c index 62a0b18a8ac..6b792bfc39e 100644 --- a/libavcodec/pthread_frame.c +++ b/libavcodec/pthread_frame.c @@ -28,8 +28,10 @@ #include #include "avcodec.h" +#include "avcodec_internal.h" #include "codec_internal.h" #include "decode.h" +#include "hwaccel_internal.h" #include "hwconfig.h" #include "internal.h" #include "pthread_internal.h" @@ -99,27 +101,17 @@ typedef struct PerThreadContext { atomic_int state; -#if FF_API_THREAD_SAFE_CALLBACKS - /** - * Array of frames passed to ff_thread_release_buffer(). - * Frames are released after all threads referencing them are finished. - */ - AVFrame **released_buffers; - int num_released_buffers; - int released_buffers_allocated; - - AVFrame *requested_frame; ///< AVFrame the codec passed to get_buffer() - int requested_flags; ///< flags passed to get_buffer() for requested_frame - - const enum AVPixelFormat *available_formats; ///< Format array for get_format() - enum AVPixelFormat result_format; ///< get_format() result -#endif - int die; ///< Set when the thread should exit. int hwaccel_serializing; int async_serializing; + // set to 1 in ff_thread_finish_setup() when a threadsafe hwaccel is used; + // cannot check hwaccel caps directly, because + // worked threads clear hwaccel state for thread-unsafe hwaccels + // after each decode call + int hwaccel_threadsafe; + atomic_int debug_threads; ///< Set if the FF_DEBUG_THREADS option is set. } PerThreadContext; @@ -133,8 +125,8 @@ typedef struct FrameThreadContext { unsigned pthread_init_cnt; ///< Number of successfully initialized mutexes/conditions pthread_mutex_t buffer_mutex; ///< Mutex used to protect get/release_buffer(). /** - * This lock is used for ensuring threads run in serial when hwaccel - * is used. + * This lock is used for ensuring threads run in serial when thread-unsafe + * hwaccel is used. */ pthread_mutex_t hwaccel_mutex; pthread_mutex_t async_mutex; @@ -149,17 +141,18 @@ typedef struct FrameThreadContext { * While it is set, ff_thread_en/decode_frame won't return any results. */ - /* hwaccel state is temporarily stored here in order to transfer its ownership - * to the next decoding thread without the need for extra synchronization */ + /* hwaccel state for thread-unsafe hwaccels is temporarily stored here in + * order to transfer its ownership to the next decoding thread without the + * need for extra synchronization */ const AVHWAccel *stash_hwaccel; void *stash_hwaccel_context; void *stash_hwaccel_priv; } FrameThreadContext; -#if FF_API_THREAD_SAFE_CALLBACKS -#define THREAD_SAFE_CALLBACKS(avctx) \ -((avctx)->thread_safe_callbacks || (avctx)->get_buffer2 == avcodec_default_get_buffer2) -#endif +static int hwaccel_serial(const AVCodecContext *avctx) +{ + return avctx->hwaccel && !(ffhwaccel(avctx->hwaccel)->caps_internal & HWACCEL_CAP_THREAD_SAFE); +} static void async_lock(FrameThreadContext *fctx) { @@ -212,14 +205,8 @@ static attribute_align_arg void *frame_worker_thread(void *arg) if (p->die) break; -FF_DISABLE_DEPRECATION_WARNINGS - if (!codec->update_thread_context -#if FF_API_THREAD_SAFE_CALLBACKS - && THREAD_SAFE_CALLBACKS(avctx) -#endif - ) + if (!codec->update_thread_context) ff_thread_finish_setup(avctx); -FF_ENABLE_DEPRECATION_WARNINGS /* If a decoder supports hwaccel, then it must call ff_get_format(). * Since that call must happen before ff_thread_finish_setup(), the @@ -229,9 +216,9 @@ FF_ENABLE_DEPRECATION_WARNINGS * cannot be true here. */ av_assert0(!p->hwaccel_serializing); - /* if the previous thread uses hwaccel then we take the lock to ensure - * the threads don't run concurrently */ - if (avctx->hwaccel) { + /* if the previous thread uses thread-unsafe hwaccel then we take the + * lock to ensure the threads don't run concurrently */ + if (hwaccel_serial(avctx)) { pthread_mutex_lock(&p->parent->hwaccel_mutex); p->hwaccel_serializing = 1; } @@ -247,7 +234,8 @@ FF_ENABLE_DEPRECATION_WARNINGS ff_thread_finish_setup(avctx); if (p->hwaccel_serializing) { - /* wipe hwaccel state to avoid stale pointers lying around; + /* wipe hwaccel state for thread-unsafe hwaccels to avoid stale + * pointers lying around; * the state was transferred to FrameThreadContext in * ff_thread_finish_setup(), so nothing is leaked */ avctx->hwaccel = NULL; @@ -257,7 +245,8 @@ FF_ENABLE_DEPRECATION_WARNINGS p->hwaccel_serializing = 0; pthread_mutex_unlock(&p->parent->hwaccel_mutex); } - av_assert0(!avctx->hwaccel); + av_assert0(!avctx->hwaccel || + (ffhwaccel(avctx->hwaccel)->caps_internal & HWACCEL_CAP_THREAD_SAFE)); if (p->async_serializing) { p->async_serializing = 0; @@ -313,7 +302,11 @@ static int update_context_from_thread(AVCodecContext *dst, AVCodecContext *src, dst->level = src->level; dst->bits_per_raw_sample = src->bits_per_raw_sample; +#if FF_API_TICKS_PER_FRAME +FF_DISABLE_DEPRECATION_WARNINGS dst->ticks_per_frame = src->ticks_per_frame; +FF_ENABLE_DEPRECATION_WARNINGS +#endif dst->color_primaries = src->color_primaries; dst->color_trc = src->color_trc; @@ -355,8 +348,50 @@ FF_ENABLE_DEPRECATION_WARNINGS if (codec->update_thread_context_for_user) err = codec->update_thread_context_for_user(dst, src); } else { - if (codec->update_thread_context) + const PerThreadContext *p_src = src->internal->thread_ctx; + PerThreadContext *p_dst = dst->internal->thread_ctx; + + if (codec->update_thread_context) { err = codec->update_thread_context(dst, src); + if (err < 0) + return err; + } + + // reset dst hwaccel state if needed + av_assert0(p_dst->hwaccel_threadsafe || + (!dst->hwaccel && !dst->internal->hwaccel_priv_data)); + if (p_dst->hwaccel_threadsafe && + (!p_src->hwaccel_threadsafe || dst->hwaccel != src->hwaccel)) { + ff_hwaccel_uninit(dst); + p_dst->hwaccel_threadsafe = 0; + } + + // propagate hwaccel state for threadsafe hwaccels + if (p_src->hwaccel_threadsafe) { + const FFHWAccel *hwaccel = ffhwaccel(src->hwaccel); + if (!dst->hwaccel) { + if (hwaccel->priv_data_size) { + av_assert0(hwaccel->update_thread_context); + + dst->internal->hwaccel_priv_data = + av_mallocz(hwaccel->priv_data_size); + if (!dst->internal->hwaccel_priv_data) + return AVERROR(ENOMEM); + } + dst->hwaccel = src->hwaccel; + } + av_assert0(dst->hwaccel == src->hwaccel); + + if (hwaccel->update_thread_context) { + err = hwaccel->update_thread_context(dst, src); + if (err < 0) { + av_log(dst, AV_LOG_ERROR, "Error propagating hwaccel state\n"); + ff_hwaccel_uninit(dst); + return err; + } + } + p_dst->hwaccel_threadsafe = 1; + } } return err; @@ -389,14 +424,20 @@ static int update_context_from_user(AVCodecContext *dst, AVCodecContext *src) dst->skip_idct = src->skip_idct; dst->skip_frame = src->skip_frame; + dst->frame_num = src->frame_num; +#if FF_API_AVCTX_FRAME_NUMBER +FF_DISABLE_DEPRECATION_WARNINGS dst->frame_number = src->frame_number; - dst->reordered_opaque = src->reordered_opaque; -#if FF_API_THREAD_SAFE_CALLBACKS +FF_ENABLE_DEPRECATION_WARNINGS +#endif +#if FF_API_REORDERED_OPAQUE FF_DISABLE_DEPRECATION_WARNINGS - dst->thread_safe_callbacks = src->thread_safe_callbacks; + dst->reordered_opaque = src->reordered_opaque; FF_ENABLE_DEPRECATION_WARNINGS #endif +#if FF_API_SLICE_OFFSET +FF_DISABLE_DEPRECATION_WARNINGS if (src->slice_count && src->slice_offset) { if (dst->slice_count < src->slice_count) { int err = av_reallocp_array(&dst->slice_offset, src->slice_count, @@ -408,6 +449,8 @@ FF_ENABLE_DEPRECATION_WARNINGS src->slice_count * sizeof(*dst->slice_offset)); } dst->slice_count = src->slice_count; +FF_ENABLE_DEPRECATION_WARNINGS +#endif av_packet_unref(dst->internal->last_pkt_props); err = av_packet_copy_props(dst->internal->last_pkt_props, src->internal->last_pkt_props); @@ -417,29 +460,6 @@ FF_ENABLE_DEPRECATION_WARNINGS return 0; } -#if FF_API_THREAD_SAFE_CALLBACKS -/// Releases the buffers that this decoding thread was the last user of. -static void release_delayed_buffers(PerThreadContext *p) -{ - FrameThreadContext *fctx = p->parent; - - while (p->num_released_buffers > 0) { - AVFrame *f; - - pthread_mutex_lock(&fctx->buffer_mutex); - - // fix extended data in case the caller screwed it up - av_assert0(p->avctx->codec_type == AVMEDIA_TYPE_VIDEO || - p->avctx->codec_type == AVMEDIA_TYPE_AUDIO); - f = p->released_buffers[--p->num_released_buffers]; - f->extended_data = f->data; - av_frame_unref(f); - - pthread_mutex_unlock(&fctx->buffer_mutex); - } -} -#endif - static int submit_packet(PerThreadContext *p, AVCodecContext *user_avctx, AVPacket *avpkt) { @@ -462,10 +482,6 @@ static int submit_packet(PerThreadContext *p, AVCodecContext *user_avctx, (p->avctx->debug & FF_DEBUG_THREADS) != 0, memory_order_relaxed); -#if FF_API_THREAD_SAFE_CALLBACKS - release_delayed_buffers(p); -#endif - if (prev_thread) { int err; if (atomic_load(&prev_thread->state) == STATE_SETTING_UP) { @@ -483,10 +499,12 @@ static int submit_packet(PerThreadContext *p, AVCodecContext *user_avctx, } /* transfer the stashed hwaccel state, if any */ - av_assert0(!p->avctx->hwaccel); - FFSWAP(const AVHWAccel*, p->avctx->hwaccel, fctx->stash_hwaccel); - FFSWAP(void*, p->avctx->hwaccel_context, fctx->stash_hwaccel_context); - FFSWAP(void*, p->avctx->internal->hwaccel_priv_data, fctx->stash_hwaccel_priv); + av_assert0(!p->avctx->hwaccel || p->hwaccel_threadsafe); + if (!p->hwaccel_threadsafe) { + FFSWAP(const AVHWAccel*, p->avctx->hwaccel, fctx->stash_hwaccel); + FFSWAP(void*, p->avctx->hwaccel_context, fctx->stash_hwaccel_context); + FFSWAP(void*, p->avctx->internal->hwaccel_priv_data, fctx->stash_hwaccel_priv); + } av_packet_unref(p->avpkt); ret = av_packet_ref(p->avpkt, avpkt); @@ -500,44 +518,6 @@ static int submit_packet(PerThreadContext *p, AVCodecContext *user_avctx, pthread_cond_signal(&p->input_cond); pthread_mutex_unlock(&p->mutex); -#if FF_API_THREAD_SAFE_CALLBACKS -FF_DISABLE_DEPRECATION_WARNINGS - /* - * If the client doesn't have a thread-safe get_buffer(), - * then decoding threads call back to the main thread, - * and it calls back to the client here. - */ - - if (!p->avctx->thread_safe_callbacks && ( - p->avctx->get_format != avcodec_default_get_format || - p->avctx->get_buffer2 != avcodec_default_get_buffer2)) { - while (atomic_load(&p->state) != STATE_SETUP_FINISHED && atomic_load(&p->state) != STATE_INPUT_READY) { - int call_done = 1; - pthread_mutex_lock(&p->progress_mutex); - while (atomic_load(&p->state) == STATE_SETTING_UP) - pthread_cond_wait(&p->progress_cond, &p->progress_mutex); - - switch (atomic_load_explicit(&p->state, memory_order_acquire)) { - case STATE_GET_BUFFER: - p->result = ff_get_buffer(p->avctx, p->requested_frame, p->requested_flags); - break; - case STATE_GET_FORMAT: - p->result_format = ff_get_format(p->avctx, p->available_formats); - break; - default: - call_done = 0; - break; - } - if (call_done) { - atomic_store(&p->state, STATE_SETTING_UP); - pthread_cond_signal(&p->progress_cond); - } - pthread_mutex_unlock(&p->progress_mutex); - } - } -FF_ENABLE_DEPRECATION_WARNINGS -#endif - fctx->prev_thread = p; fctx->next_decoding++; @@ -678,26 +658,32 @@ void ff_thread_finish_setup(AVCodecContext *avctx) { if (!(avctx->active_thread_type&FF_THREAD_FRAME)) return; - if (avctx->hwaccel && !p->hwaccel_serializing) { + p->hwaccel_threadsafe = avctx->hwaccel && + (ffhwaccel(avctx->hwaccel)->caps_internal & HWACCEL_CAP_THREAD_SAFE); + + if (hwaccel_serial(avctx) && !p->hwaccel_serializing) { pthread_mutex_lock(&p->parent->hwaccel_mutex); p->hwaccel_serializing = 1; } /* this assumes that no hwaccel calls happen before ff_thread_finish_setup() */ if (avctx->hwaccel && - !(avctx->hwaccel->caps_internal & HWACCEL_CAP_ASYNC_SAFE)) { + !(ffhwaccel(avctx->hwaccel)->caps_internal & HWACCEL_CAP_ASYNC_SAFE)) { p->async_serializing = 1; async_lock(p->parent); } - /* save hwaccel state for passing to the next thread; + /* thread-unsafe hwaccels share a single private data instance, so we + * save hwaccel state for passing to the next thread; * this is done here so that this worker thread can wipe its own hwaccel * state after decoding, without requiring synchronization */ av_assert0(!p->parent->stash_hwaccel); - p->parent->stash_hwaccel = avctx->hwaccel; - p->parent->stash_hwaccel_context = avctx->hwaccel_context; - p->parent->stash_hwaccel_priv = avctx->internal->hwaccel_priv_data; + if (hwaccel_serial(avctx)) { + p->parent->stash_hwaccel = avctx->hwaccel; + p->parent->stash_hwaccel_context = avctx->hwaccel_context; + p->parent->stash_hwaccel_priv = avctx->internal->hwaccel_priv_data; + } pthread_mutex_lock(&p->progress_mutex); if(atomic_load(&p->state) == STATE_SETUP_FINISHED){ @@ -768,19 +754,21 @@ void ff_frame_thread_free(AVCodecContext *avctx, int thread_count) if (codec->close && p->thread_init != UNINITIALIZED) codec->close(ctx); -#if FF_API_THREAD_SAFE_CALLBACKS - release_delayed_buffers(p); - for (int j = 0; j < p->released_buffers_allocated; j++) - av_frame_free(&p->released_buffers[j]); - av_freep(&p->released_buffers); -#endif + /* When using a threadsafe hwaccel, this is where + * each thread's context is uninit'd and freed. */ + ff_hwaccel_uninit(ctx); + if (ctx->priv_data) { if (codec->p.priv_class) av_opt_free(ctx->priv_data); av_freep(&ctx->priv_data); } +#if FF_API_SLICE_OFFSET +FF_DISABLE_DEPRECATION_WARNINGS av_freep(&ctx->slice_offset); +FF_ENABLE_DEPRECATION_WARNINGS +#endif av_buffer_unref(&ctx->internal->pool); av_packet_free(&ctx->internal->last_pkt_props); @@ -830,7 +818,7 @@ static av_cold int init_thread(PerThreadContext *p, int *threads_to_free, p->parent = fctx; p->avctx = copy; - copy->internal = av_mallocz(sizeof(*copy->internal)); + copy->internal = ff_decode_internal_alloc(); if (!copy->internal) return AVERROR(ENOMEM); copy->internal->thread_ctx = p; @@ -971,10 +959,6 @@ void ff_thread_flush(AVCodecContext *avctx) av_frame_unref(p->frame); p->result = 0; -#if FF_API_THREAD_SAFE_CALLBACKS - release_delayed_buffers(p); -#endif - if (ffcodec(avctx->codec)->flush) ffcodec(avctx->codec)->flush(p->avctx); } @@ -983,16 +967,12 @@ void ff_thread_flush(AVCodecContext *avctx) int ff_thread_can_start_frame(AVCodecContext *avctx) { PerThreadContext *p = avctx->internal->thread_ctx; -FF_DISABLE_DEPRECATION_WARNINGS + if ((avctx->active_thread_type&FF_THREAD_FRAME) && atomic_load(&p->state) != STATE_SETTING_UP && - (ffcodec(avctx->codec)->update_thread_context -#if FF_API_THREAD_SAFE_CALLBACKS - || !THREAD_SAFE_CALLBACKS(avctx) -#endif - )) { + ffcodec(avctx->codec)->update_thread_context) { return 0; } -FF_ENABLE_DEPRECATION_WARNINGS + return 1; } @@ -1005,82 +985,20 @@ static int thread_get_buffer_internal(AVCodecContext *avctx, AVFrame *f, int fla return ff_get_buffer(avctx, f, flags); p = avctx->internal->thread_ctx; -FF_DISABLE_DEPRECATION_WARNINGS if (atomic_load(&p->state) != STATE_SETTING_UP && - (ffcodec(avctx->codec)->update_thread_context -#if FF_API_THREAD_SAFE_CALLBACKS - || !THREAD_SAFE_CALLBACKS(avctx) -#endif - )) { -FF_ENABLE_DEPRECATION_WARNINGS + ffcodec(avctx->codec)->update_thread_context) { av_log(avctx, AV_LOG_ERROR, "get_buffer() cannot be called after ff_thread_finish_setup()\n"); return -1; } pthread_mutex_lock(&p->parent->buffer_mutex); -#if !FF_API_THREAD_SAFE_CALLBACKS err = ff_get_buffer(avctx, f, flags); -#else -FF_DISABLE_DEPRECATION_WARNINGS - if (THREAD_SAFE_CALLBACKS(avctx)) { - err = ff_get_buffer(avctx, f, flags); - } else { - pthread_mutex_lock(&p->progress_mutex); - p->requested_frame = f; - p->requested_flags = flags; - atomic_store_explicit(&p->state, STATE_GET_BUFFER, memory_order_release); - pthread_cond_broadcast(&p->progress_cond); - - while (atomic_load(&p->state) != STATE_SETTING_UP) - pthread_cond_wait(&p->progress_cond, &p->progress_mutex); - - err = p->result; - - pthread_mutex_unlock(&p->progress_mutex); - - } - if (!THREAD_SAFE_CALLBACKS(avctx) && !ffcodec(avctx->codec)->update_thread_context) - ff_thread_finish_setup(avctx); -FF_ENABLE_DEPRECATION_WARNINGS -#endif pthread_mutex_unlock(&p->parent->buffer_mutex); return err; } -#if FF_API_THREAD_SAFE_CALLBACKS -FF_DISABLE_DEPRECATION_WARNINGS -enum AVPixelFormat ff_thread_get_format(AVCodecContext *avctx, const enum AVPixelFormat *fmt) -{ - enum AVPixelFormat res; - PerThreadContext *p; - if (!(avctx->active_thread_type & FF_THREAD_FRAME) || avctx->thread_safe_callbacks || - avctx->get_format == avcodec_default_get_format) - return ff_get_format(avctx, fmt); - - p = avctx->internal->thread_ctx; - if (atomic_load(&p->state) != STATE_SETTING_UP) { - av_log(avctx, AV_LOG_ERROR, "get_format() cannot be called after ff_thread_finish_setup()\n"); - return -1; - } - pthread_mutex_lock(&p->progress_mutex); - p->available_formats = fmt; - atomic_store(&p->state, STATE_GET_FORMAT); - pthread_cond_broadcast(&p->progress_cond); - - while (atomic_load(&p->state) != STATE_SETTING_UP) - pthread_cond_wait(&p->progress_cond, &p->progress_mutex); - - res = p->result_format; - - pthread_mutex_unlock(&p->progress_mutex); - - return res; -} -FF_ENABLE_DEPRECATION_WARNINGS -#endif - int ff_thread_get_buffer(AVCodecContext *avctx, AVFrame *f, int flags) { int ret = thread_get_buffer_internal(avctx, f, flags); @@ -1122,69 +1040,13 @@ int ff_thread_get_ext_buffer(AVCodecContext *avctx, ThreadFrame *f, int flags) void ff_thread_release_buffer(AVCodecContext *avctx, AVFrame *f) { -#if FF_API_THREAD_SAFE_CALLBACKS -FF_DISABLE_DEPRECATION_WARNINGS - PerThreadContext *p; - FrameThreadContext *fctx; - AVFrame *dst; - int ret = 0; - int can_direct_free = !(avctx->active_thread_type & FF_THREAD_FRAME) || - THREAD_SAFE_CALLBACKS(avctx); -FF_ENABLE_DEPRECATION_WARNINGS -#endif - if (!f) return; if (avctx->debug & FF_DEBUG_BUFFERS) av_log(avctx, AV_LOG_DEBUG, "thread_release_buffer called on pic %p\n", f); -#if !FF_API_THREAD_SAFE_CALLBACKS av_frame_unref(f); -#else - // when the frame buffers are not allocated, just reset it to clean state - if (can_direct_free || !f->buf[0]) { - av_frame_unref(f); - return; - } - - p = avctx->internal->thread_ctx; - fctx = p->parent; - pthread_mutex_lock(&fctx->buffer_mutex); - - if (p->num_released_buffers == p->released_buffers_allocated) { - AVFrame **tmp = av_realloc_array(p->released_buffers, p->released_buffers_allocated + 1, - sizeof(*p->released_buffers)); - if (tmp) { - tmp[p->released_buffers_allocated] = av_frame_alloc(); - p->released_buffers = tmp; - } - - if (!tmp || !tmp[p->released_buffers_allocated]) { - ret = AVERROR(ENOMEM); - goto fail; - } - p->released_buffers_allocated++; - } - - dst = p->released_buffers[p->num_released_buffers]; - av_frame_move_ref(dst, f); - - p->num_released_buffers++; - -fail: - pthread_mutex_unlock(&fctx->buffer_mutex); - - // make sure the frame is clean even if we fail to free it - // this leaks, but it is better than crashing - if (ret < 0) { - av_log(avctx, AV_LOG_ERROR, "Could not queue a frame for freeing, this will leak\n"); - memset(f->buf, 0, sizeof(f->buf)); - if (f->extended_buf) - memset(f->extended_buf, 0, f->nb_extended_buf * sizeof(*f->extended_buf)); - av_frame_unref(f); - } -#endif } void ff_thread_release_ext_buffer(AVCodecContext *avctx, ThreadFrame *f) diff --git a/libavcodec/qcelpdec.c b/libavcodec/qcelpdec.c index 277c55100a9..1435fecc2eb 100644 --- a/libavcodec/qcelpdec.c +++ b/libavcodec/qcelpdec.c @@ -646,8 +646,8 @@ static qcelp_packet_rate determine_bitrate(AVCodecContext *avctx, static void warn_insufficient_frame_quality(AVCodecContext *avctx, const char *message) { - av_log(avctx, AV_LOG_WARNING, "Frame #%d, IFQ: %s\n", - avctx->frame_number, message); + av_log(avctx, AV_LOG_WARNING, "Frame #%"PRId64", IFQ: %s\n", + avctx->frame_num, message); } static void postfilter(QCELPContext *q, float *samples, float *lpc) diff --git a/libavcodec/qdmc.c b/libavcodec/qdmc.c index 4b582dc3491..081c4dd46f0 100644 --- a/libavcodec/qdmc.c +++ b/libavcodec/qdmc.c @@ -25,6 +25,7 @@ #define BITSTREAM_READER_LE #include "libavutil/channel_layout.h" +#include "libavutil/mem_internal.h" #include "libavutil/thread.h" #include "libavutil/tx.h" diff --git a/libavcodec/qdrw.c b/libavcodec/qdrw.c index e41451e9a77..21a53b8e728 100644 --- a/libavcodec/qdrw.c +++ b/libavcodec/qdrw.c @@ -384,7 +384,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, ret = parse_palette(avctx, &gbc, (uint32_t *)p->data[1], colors, flags & 0x8000); if (ret < 0) return ret; +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS p->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif /* jump to image data */ bytestream2_skip(&gbc, 18); @@ -503,7 +507,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, if (*got_frame) { p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; return avpkt->size; } else { diff --git a/libavcodec/qoidec.c b/libavcodec/qoidec.c index 9414d2fbe9a..37bc2084c06 100644 --- a/libavcodec/qoidec.c +++ b/libavcodec/qoidec.c @@ -106,7 +106,7 @@ static int qoi_decode_frame(AVCodecContext *avctx, AVFrame *p, memcpy(&dst[off_x * channels], px, channels); } - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; p->pict_type = AV_PICTURE_TYPE_I; *got_frame = 1; diff --git a/libavcodec/qoienc.c b/libavcodec/qoienc.c index 6d574e0da9e..b9efdc2fa52 100644 --- a/libavcodec/qoienc.c +++ b/libavcodec/qoienc.c @@ -131,7 +131,8 @@ const FFCodec ff_qoi_encoder = { CODEC_LONG_NAME("QOI (Quite OK Image format) image"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_QOI, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, FF_CODEC_ENCODE_CB(qoi_encode_frame), .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_RGBA, AV_PIX_FMT_RGB24, diff --git a/libavcodec/qpeg.c b/libavcodec/qpeg.c index 5bca338acf5..1f76ebc5a1f 100644 --- a/libavcodec/qpeg.c +++ b/libavcodec/qpeg.c @@ -297,14 +297,24 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, } /* make the palette available on the way out */ - p->palette_has_changed = ff_copy_palette(a->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS + p->palette_has_changed = +#endif + ff_copy_palette(a->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_ENABLE_DEPRECATION_WARNINGS +#endif memcpy(p->data[1], a->pal, AVPALETTE_SIZE); av_frame_unref(ref); if ((ret = av_frame_ref(ref, p)) < 0) return ret; - p->key_frame = intra; + if (intra) + p->flags |= AV_FRAME_FLAG_KEY; + else + p->flags &= ~AV_FRAME_FLAG_KEY; p->pict_type = intra ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; *got_frame = 1; diff --git a/libavcodec/qsv.c b/libavcodec/qsv.c index 7af154202c4..75636256277 100644 --- a/libavcodec/qsv.c +++ b/libavcodec/qsv.c @@ -208,7 +208,6 @@ enum AVPixelFormat ff_qsv_map_fourcc(uint32_t fourcc) case MFX_FOURCC_P8: return AV_PIX_FMT_PAL8; case MFX_FOURCC_A2RGB10: return AV_PIX_FMT_X2RGB10; case MFX_FOURCC_RGB4: return AV_PIX_FMT_BGRA; -#if CONFIG_VAAPI case MFX_FOURCC_YUY2: return AV_PIX_FMT_YUYV422; case MFX_FOURCC_Y210: return AV_PIX_FMT_Y210; case MFX_FOURCC_AYUV: return AV_PIX_FMT_VUYX; @@ -217,7 +216,6 @@ enum AVPixelFormat ff_qsv_map_fourcc(uint32_t fourcc) case MFX_FOURCC_P016: return AV_PIX_FMT_P012; case MFX_FOURCC_Y216: return AV_PIX_FMT_Y212; case MFX_FOURCC_Y416: return AV_PIX_FMT_XV36; -#endif #endif } return AV_PIX_FMT_NONE; @@ -245,7 +243,6 @@ int ff_qsv_map_pixfmt(enum AVPixelFormat format, uint32_t *fourcc, uint16_t *shi *fourcc = MFX_FOURCC_RGB4; *shift = 0; return AV_PIX_FMT_BGRA; -#if CONFIG_VAAPI case AV_PIX_FMT_YUV422P: case AV_PIX_FMT_YUYV422: *fourcc = MFX_FOURCC_YUY2; @@ -277,7 +274,6 @@ int ff_qsv_map_pixfmt(enum AVPixelFormat format, uint32_t *fourcc, uint16_t *shi *fourcc = MFX_FOURCC_Y416; *shift = 1; return AV_PIX_FMT_XV36; -#endif #endif default: return AVERROR(ENOSYS); @@ -681,18 +677,31 @@ static int qsv_create_mfx_session(AVCodecContext *avctx, int ff_qsv_init_internal_session(AVCodecContext *avctx, QSVSession *qs, const char *load_plugins, int gpu_copy) { + mfxIMPL impls[] = { #if CONFIG_D3D11VA - mfxIMPL impl = MFX_IMPL_AUTO_ANY | MFX_IMPL_VIA_D3D11; -#else - mfxIMPL impl = MFX_IMPL_AUTO_ANY; + MFX_IMPL_AUTO_ANY | MFX_IMPL_VIA_D3D11, #endif + MFX_IMPL_AUTO_ANY + }; + mfxIMPL impl; mfxVersion ver = { { QSV_VERSION_MINOR, QSV_VERSION_MAJOR } }; const char *desc; - int ret = qsv_create_mfx_session(avctx, impl, &ver, gpu_copy, &qs->session, + int ret; + + for (int i = 0; i < FF_ARRAY_ELEMS(impls); i++) { + ret = qsv_create_mfx_session(avctx, impls[i], &ver, gpu_copy, &qs->session, &qs->loader); - if (ret) - return ret; + + if (ret == 0) + break; + + if (i == FF_ARRAY_ELEMS(impls) - 1) + return ret; + else + av_log(avctx, AV_LOG_ERROR, "The current mfx implementation is not " + "supported, try next mfx implementation.\n"); + } #ifdef AVCODEC_QSV_LINUX_SESSION_HANDLE ret = ff_qsv_set_display_handle(avctx, qs); diff --git a/libavcodec/qsv_internal.h b/libavcodec/qsv_internal.h index 5119ef4dffa..c2d301b4a22 100644 --- a/libavcodec/qsv_internal.h +++ b/libavcodec/qsv_internal.h @@ -23,9 +23,9 @@ #include "config.h" -#if CONFIG_VAAPI +#if CONFIG_VAAPI && !defined(_WIN32) // Do not enable for libva-win32 on Windows #define AVCODEC_QSV_LINUX_SESSION_HANDLE -#endif //CONFIG_VAAPI +#endif //CONFIG_VAAPI && !defined(_WIN32) #ifdef AVCODEC_QSV_LINUX_SESSION_HANDLE #include @@ -35,7 +35,6 @@ #endif #include #include -#include #include "libavutil/hwcontext_vaapi.h" #endif diff --git a/libavcodec/qsvdec.c b/libavcodec/qsvdec.c index 6bc85116adf..da700f25e9f 100644 --- a/libavcodec/qsvdec.c +++ b/libavcodec/qsvdec.c @@ -828,14 +828,18 @@ static int qsv_decode(AVCodecContext *avctx, QSVContext *q, outsurf->Info.PicStruct & MFX_PICSTRUCT_FRAME_TRIPLING ? 4 : outsurf->Info.PicStruct & MFX_PICSTRUCT_FRAME_DOUBLING ? 2 : outsurf->Info.PicStruct & MFX_PICSTRUCT_FIELD_REPEATED ? 1 : 0; - frame->top_field_first = - outsurf->Info.PicStruct & MFX_PICSTRUCT_FIELD_TFF; - frame->interlaced_frame = + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST * + !!(outsurf->Info.PicStruct & MFX_PICSTRUCT_FIELD_TFF); + frame->flags |= AV_FRAME_FLAG_INTERLACED * !(outsurf->Info.PicStruct & MFX_PICSTRUCT_PROGRESSIVE); frame->pict_type = ff_qsv_map_pictype(aframe.frame->dec_info.FrameType); //Key frame is IDR frame is only suitable for H264. For HEVC, IRAPs are key frames. - if (avctx->codec_id == AV_CODEC_ID_H264) - frame->key_frame = !!(aframe.frame->dec_info.FrameType & MFX_FRAMETYPE_IDR); + if (avctx->codec_id == AV_CODEC_ID_H264) { + if (aframe.frame->dec_info.FrameType & MFX_FRAMETYPE_IDR) + frame->flags |= AV_FRAME_FLAG_KEY; + else + frame->flags &= ~AV_FRAME_FLAG_KEY; + } /* update the surface properties */ if (avctx->pix_fmt == AV_PIX_FMT_QSV) diff --git a/libavcodec/qsvenc.c b/libavcodec/qsvenc.c index 93f1862a4b2..7ff9d333a27 100644 --- a/libavcodec/qsvenc.c +++ b/libavcodec/qsvenc.c @@ -21,12 +21,11 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "config_components.h" - #include #include #include +#include "libavutil/avassert.h" #include "libavutil/common.h" #include "libavutil/hwcontext.h" #include "libavutil/hwcontext_qsv.h" @@ -34,7 +33,6 @@ #include "libavutil/log.h" #include "libavutil/time.h" #include "libavutil/imgutils.h" -#include "libavcodec/bytestream.h" #include "avcodec.h" #include "internal.h" @@ -169,6 +167,8 @@ do { \ } \ } while (0) \ +#define MFX_IMPL_VIA_MASK(impl) (0x0f00 & (impl)) + static const char *print_ratecontrol(mfxU16 rc_mode) { int i; @@ -197,6 +197,10 @@ static void dump_video_param(AVCodecContext *avctx, QSVEncContext *q, mfxExtCodingOption2 *co2 = NULL; mfxExtCodingOption3 *co3 = NULL; mfxExtHEVCTiles *exthevctiles = NULL; +#if QSV_HAVE_HE + mfxExtHyperModeParam *exthypermodeparam = NULL; +#endif + const char *tmp_str = NULL; if (q->co2_idx > 0) @@ -208,6 +212,11 @@ static void dump_video_param(AVCodecContext *avctx, QSVEncContext *q, if (q->exthevctiles_idx > 0) exthevctiles = (mfxExtHEVCTiles *)coding_opts[q->exthevctiles_idx]; +#if QSV_HAVE_HE + if (q->exthypermodeparam_idx > 0) + exthypermodeparam = (mfxExtHyperModeParam *)coding_opts[q->exthypermodeparam_idx]; +#endif + av_log(avctx, AV_LOG_VERBOSE, "profile: %s; level: %"PRIu16"\n", print_profile(avctx->codec_id, info->CodecProfile), info->CodecLevel); @@ -373,6 +382,21 @@ static void dump_video_param(AVCodecContext *avctx, QSVEncContext *q, av_log(avctx, AV_LOG_VERBOSE, "NumTileColumns: %"PRIu16"; NumTileRows: %"PRIu16"\n", exthevctiles->NumTileColumns, exthevctiles->NumTileRows); } + +#if QSV_HAVE_HE + if (exthypermodeparam) { + av_log(avctx, AV_LOG_VERBOSE, "HyperEncode: "); + + if (exthypermodeparam->Mode == MFX_HYPERMODE_OFF) + av_log(avctx, AV_LOG_VERBOSE, "OFF"); + if (exthypermodeparam->Mode == MFX_HYPERMODE_ON) + av_log(avctx, AV_LOG_VERBOSE, "ON"); + if (exthypermodeparam->Mode == MFX_HYPERMODE_ADAPTIVE) + av_log(avctx, AV_LOG_VERBOSE, "Adaptive"); + + av_log(avctx, AV_LOG_VERBOSE, "\n"); + } +#endif } static void dump_video_vp9_param(AVCodecContext *avctx, QSVEncContext *q, @@ -808,7 +832,9 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q) // for progressive video, the height should be aligned to 16 for // H.264. For HEVC, depending on the version of MFX, it should be // either 32 or 16. The lower number is better if possible. - q->height_align = avctx->codec_id == AV_CODEC_ID_HEVC ? 32 : 16; + // For AV1, it is 32 + q->height_align = (avctx->codec_id == AV_CODEC_ID_HEVC || + avctx->codec_id == AV_CODEC_ID_AV1) ? 32 : 16; } q->param.mfx.FrameInfo.Height = FFALIGN(avctx->height, q->height_align); @@ -1092,11 +1118,16 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q) q->extco3.MaxFrameSizeI = q->max_frame_size_i; if (q->max_frame_size_p >= 0) q->extco3.MaxFrameSizeP = q->max_frame_size_p; + if (sw_format == AV_PIX_FMT_BGRA && + (q->profile == MFX_PROFILE_HEVC_REXT || + q->profile == MFX_PROFILE_UNKNOWN)) + q->extco3.TargetChromaFormatPlus1 = MFX_CHROMAFORMAT_YUV444 + 1; q->extco3.ScenarioInfo = q->scenario; } else if (avctx->codec_id == AV_CODEC_ID_AV1) { if (q->low_delay_brc >= 0) q->extco3.LowDelayBRC = q->low_delay_brc ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF; + q->old_low_delay_brc = q->low_delay_brc; } if (avctx->codec_id == AV_CODEC_ID_HEVC) { @@ -1159,7 +1190,12 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q) q->extvsi.ColourDescriptionPresent = 1; q->extvsi.ColourPrimaries = avctx->color_primaries; q->extvsi.TransferCharacteristics = avctx->color_trc; - q->extvsi.MatrixCoefficients = avctx->colorspace; + if (avctx->colorspace == AVCOL_SPC_RGB) + // RGB will be converted to YUV, so RGB colorspace is not supported + q->extvsi.MatrixCoefficients = AVCOL_SPC_UNSPECIFIED; + else + q->extvsi.MatrixCoefficients = avctx->colorspace; + } if ((avctx->codec_id != AV_CODEC_ID_VP9) && (q->extvsi.VideoFullRange || q->extvsi.ColourDescriptionPresent)) { @@ -1168,6 +1204,54 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q) q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->extvsi; } +#if QSV_HAVE_HE + if (q->dual_gfx) { + if (QSV_RUNTIME_VERSION_ATLEAST(q->ver, 2, 4)) { + mfxIMPL impl; + MFXQueryIMPL(q->session, &impl); + + if (MFX_IMPL_VIA_MASK(impl) != MFX_IMPL_VIA_D3D11) { + av_log(avctx, AV_LOG_ERROR, "Dual GFX mode requires D3D11VA \n"); + return AVERROR_UNKNOWN; + } + if (q->param.mfx.LowPower != MFX_CODINGOPTION_ON) { + av_log(avctx, AV_LOG_ERROR, "Dual GFX mode supports only low-power encoding mode \n"); + return AVERROR_UNKNOWN; + } + if (q->param.mfx.CodecId != MFX_CODEC_AVC && q->param.mfx.CodecId != MFX_CODEC_HEVC) { + av_log(avctx, AV_LOG_ERROR, "Not supported encoder for dual GFX mode. " + "Supported: h264_qsv and hevc_qsv \n"); + return AVERROR_UNKNOWN; + } + if (q->param.mfx.RateControlMethod != MFX_RATECONTROL_VBR && + q->param.mfx.RateControlMethod != MFX_RATECONTROL_CQP && + q->param.mfx.RateControlMethod != MFX_RATECONTROL_ICQ) { + av_log(avctx, AV_LOG_WARNING, "Not supported BRC for dual GFX mode. " + "Supported: VBR, CQP and ICQ \n"); + } + if ((q->param.mfx.CodecId == MFX_CODEC_AVC && q->param.mfx.IdrInterval != 0) || + (q->param.mfx.CodecId == MFX_CODEC_HEVC && q->param.mfx.IdrInterval != 1)) { + av_log(avctx, AV_LOG_WARNING, "Dual GFX mode requires closed GOP for AVC and strict GOP for HEVC, -idr_interval 0 \n"); + } + if (q->param.mfx.GopPicSize < 30) { + av_log(avctx, AV_LOG_WARNING, "For better performance in dual GFX mode GopPicSize must be >= 30 \n"); + } + if (q->param.AsyncDepth < 30) { + av_log(avctx, AV_LOG_WARNING, "For better performance in dual GFX mode AsyncDepth must be >= 30 \n"); + } + + q->exthypermodeparam.Header.BufferId = MFX_EXTBUFF_HYPER_MODE_PARAM; + q->exthypermodeparam.Header.BufferSz = sizeof(q->exthypermodeparam); + q->exthypermodeparam.Mode = q->dual_gfx; + q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->exthypermodeparam; + } else { + av_log(avctx, AV_LOG_ERROR, + "This version of runtime doesn't support Hyper Encode\n"); + return AVERROR_UNKNOWN; + } + } +#endif + if (!check_enc_param(avctx,q)) { av_log(avctx, AV_LOG_ERROR, "some encoding parameters are not supported by the QSV " @@ -1342,12 +1426,19 @@ static int qsv_retrieve_enc_params(AVCodecContext *avctx, QSVEncContext *q) .Header.BufferSz = sizeof(hevc_tile_buf), }; - mfxExtBuffer *ext_buffers[6]; +#if QSV_HAVE_HE + mfxExtHyperModeParam hyper_mode_param_buf = { + .Header.BufferId = MFX_EXTBUFF_HYPER_MODE_PARAM, + .Header.BufferSz = sizeof(hyper_mode_param_buf), + }; +#endif + + mfxExtBuffer *ext_buffers[6 + QSV_HAVE_HE]; int need_pps = avctx->codec_id != AV_CODEC_ID_MPEG2VIDEO; int ret, ext_buf_num = 0, extradata_offset = 0; - q->co2_idx = q->co3_idx = q->exthevctiles_idx = -1; + q->co2_idx = q->co3_idx = q->exthevctiles_idx = q->exthypermodeparam_idx = -1; ext_buffers[ext_buf_num++] = (mfxExtBuffer*)&extradata; ext_buffers[ext_buf_num++] = (mfxExtBuffer*)&co; @@ -1369,6 +1460,12 @@ static int qsv_retrieve_enc_params(AVCodecContext *avctx, QSVEncContext *q) q->exthevctiles_idx = ext_buf_num; ext_buffers[ext_buf_num++] = (mfxExtBuffer*)&hevc_tile_buf; } +#if QSV_HAVE_HE + if (q->dual_gfx && QSV_RUNTIME_VERSION_ATLEAST(q->ver, 2, 4)) { + q->exthypermodeparam_idx = ext_buf_num; + ext_buffers[ext_buf_num++] = (mfxExtBuffer*)&hyper_mode_param_buf; + } +#endif q->param.ExtParam = ext_buffers; q->param.NumExtParam = ext_buf_num; @@ -1513,7 +1610,7 @@ int ff_qsv_enc_init(AVCodecContext *avctx, QSVEncContext *q) q->param.AsyncDepth = q->async_depth; - q->async_fifo = av_fifo_alloc2(q->async_depth, sizeof(QSVPacket), 0); + q->async_fifo = av_fifo_alloc2(q->async_depth, sizeof(QSVPacket), AV_FIFO_FLAG_AUTO_GROW); if (!q->async_fifo) return AVERROR(ENOMEM); @@ -1785,6 +1882,62 @@ static int qsvenc_fill_padding_area(AVFrame *frame, int new_w, int new_h) return 0; } +/* frame width / height have been aligned with the alignment */ +static int qsvenc_get_continuous_buffer(AVFrame *frame) +{ + int total_size; + + switch (frame->format) { + case AV_PIX_FMT_NV12: + frame->linesize[0] = frame->width; + frame->linesize[1] = frame->linesize[0]; + total_size = frame->linesize[0] * frame->height + frame->linesize[1] * frame->height / 2; + break; + + case AV_PIX_FMT_P010: + case AV_PIX_FMT_P012: + frame->linesize[0] = 2 * frame->width; + frame->linesize[1] = frame->linesize[0]; + total_size = frame->linesize[0] * frame->height + frame->linesize[1] * frame->height / 2; + break; + + case AV_PIX_FMT_YUYV422: + frame->linesize[0] = 2 * frame->width; + frame->linesize[1] = 0; + total_size = frame->linesize[0] * frame->height; + break; + + case AV_PIX_FMT_Y210: + case AV_PIX_FMT_VUYX: + case AV_PIX_FMT_XV30: + case AV_PIX_FMT_BGRA: + case AV_PIX_FMT_X2RGB10: + frame->linesize[0] = 4 * frame->width; + frame->linesize[1] = 0; + total_size = frame->linesize[0] * frame->height; + break; + + default: + // This should never be reached + av_assert0(0); + return AVERROR(EINVAL); + } + + frame->buf[0] = av_buffer_alloc(total_size); + if (!frame->buf[0]) + return AVERROR(ENOMEM); + + frame->data[0] = frame->buf[0]->data; + frame->extended_data = frame->data; + + if (frame->format == AV_PIX_FMT_NV12 || + frame->format == AV_PIX_FMT_P010 || + frame->format == AV_PIX_FMT_P012) + frame->data[1] = frame->data[0] + frame->linesize[0] * frame->height; + + return 0; +} + static int submit_frame(QSVEncContext *q, const AVFrame *frame, QSVFrame **new_frame) { @@ -1812,8 +1965,9 @@ static int submit_frame(QSVEncContext *q, const AVFrame *frame, } else { /* make a copy if the input is not padded as libmfx requires */ /* and to make allocation continious for data[0]/data[1] */ - if ((frame->height & 31 || frame->linesize[0] & (q->width_align - 1)) || - (frame->data[1] - frame->data[0] != frame->linesize[0] * FFALIGN(qf->frame->height, q->height_align))) { + if ((frame->height & (q->height_align - 1) || frame->linesize[0] & (q->width_align - 1)) || + ((frame->format == AV_PIX_FMT_NV12 || frame->format == AV_PIX_FMT_P010 || frame->format == AV_PIX_FMT_P012) && + (frame->data[1] - frame->data[0] != frame->linesize[0] * FFALIGN(qf->frame->height, q->height_align)))) { int tmp_w, tmp_h; qf->frame->height = tmp_h = FFALIGN(frame->height, q->height_align); qf->frame->width = tmp_w = FFALIGN(frame->width, q->width_align); @@ -1821,7 +1975,7 @@ static int submit_frame(QSVEncContext *q, const AVFrame *frame, qf->frame->format = frame->format; if (!qf->frame->data[0]) { - ret = av_frame_get_buffer(qf->frame, q->width_align); + ret = qsvenc_get_continuous_buffer(qf->frame); if (ret < 0) return ret; } @@ -1850,8 +2004,8 @@ static int submit_frame(QSVEncContext *q, const AVFrame *frame, qf->surface.Info = q->param.mfx.FrameInfo; qf->surface.Info.PicStruct = - !frame->interlaced_frame ? MFX_PICSTRUCT_PROGRESSIVE : - frame->top_field_first ? MFX_PICSTRUCT_FIELD_TFF : + !(frame->flags & AV_FRAME_FLAG_INTERLACED) ? MFX_PICSTRUCT_PROGRESSIVE : + (frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? MFX_PICSTRUCT_FIELD_TFF : MFX_PICSTRUCT_FIELD_BFF; if (frame->repeat_pict == 1) qf->surface.Info.PicStruct |= MFX_PICSTRUCT_FIELD_REPEATED; @@ -2117,7 +2271,9 @@ static int update_low_delay_brc(AVCodecContext *avctx, QSVEncContext *q) { int updated = 0; - if (avctx->codec_id != AV_CODEC_ID_H264 && avctx->codec_id != AV_CODEC_ID_HEVC) + if (avctx->codec_id != AV_CODEC_ID_H264 && + avctx->codec_id != AV_CODEC_ID_HEVC && + avctx->codec_id != AV_CODEC_ID_AV1) return 0; UPDATE_PARAM(q->old_low_delay_brc, q->low_delay_brc); @@ -2209,58 +2365,6 @@ static int update_pic_timing_sei(AVCodecContext *avctx, QSVEncContext *q) return updated; } -static int update_parameters(AVCodecContext *avctx, QSVEncContext *q, - const AVFrame *frame) -{ - int needReset = 0, ret = 0; - - if (!frame || avctx->codec_id == AV_CODEC_ID_MJPEG) - return 0; - - needReset = update_qp(avctx, q); - needReset |= update_max_frame_size(avctx, q); - needReset |= update_gop_size(avctx, q); - needReset |= update_rir(avctx, q); - needReset |= update_low_delay_brc(avctx, q); - needReset |= update_frame_rate(avctx, q); - needReset |= update_bitrate(avctx, q); - needReset |= update_pic_timing_sei(avctx, q); - ret = update_min_max_qp(avctx, q); - if (ret < 0) - return ret; - needReset |= ret; - if (!needReset) - return 0; - - if (avctx->hwaccel_context) { - AVQSVContext *qsv = avctx->hwaccel_context; - int i, j; - q->param.ExtParam = q->extparam; - for (i = 0; i < qsv->nb_ext_buffers; i++) - q->param.ExtParam[i] = qsv->ext_buffers[i]; - q->param.NumExtParam = qsv->nb_ext_buffers; - - for (i = 0; i < q->nb_extparam_internal; i++) { - for (j = 0; j < qsv->nb_ext_buffers; j++) { - if (qsv->ext_buffers[j]->BufferId == q->extparam_internal[i]->BufferId) - break; - } - if (j < qsv->nb_ext_buffers) - continue; - q->param.ExtParam[q->param.NumExtParam++] = q->extparam_internal[i]; - } - } else { - q->param.ExtParam = q->extparam_internal; - q->param.NumExtParam = q->nb_extparam_internal; - } - av_log(avctx, AV_LOG_DEBUG, "Parameter change, call msdk reset.\n"); - ret = MFXVideoENCODE_Reset(q->session, &q->param); - if (ret < 0) - return ff_qsv_print_error(avctx, ret, "Error during resetting"); - - return 0; -} - static int encode_frame(AVCodecContext *avctx, QSVEncContext *q, const AVFrame *frame) { @@ -2351,17 +2455,19 @@ static int encode_frame(AVCodecContext *avctx, QSVEncContext *q, if (ret < 0) { ret = (ret == MFX_ERR_MORE_DATA) ? - 0 : ff_qsv_print_error(avctx, ret, "Error during encoding"); + AVERROR(EAGAIN) : ff_qsv_print_error(avctx, ret, "Error during encoding"); goto free; } - if (ret == MFX_WRN_INCOMPATIBLE_VIDEO_PARAM && frame && frame->interlaced_frame) + if (ret == MFX_WRN_INCOMPATIBLE_VIDEO_PARAM && frame && (frame->flags & AV_FRAME_FLAG_INTERLACED)) print_interlace_msg(avctx, q); ret = 0; if (*pkt.sync) { - av_fifo_write(q->async_fifo, &pkt, 1); + ret = av_fifo_write(q->async_fifo, &pkt, 1); + if (ret < 0) + goto free; } else { free: av_freep(&pkt.sync); @@ -2379,6 +2485,66 @@ static int encode_frame(AVCodecContext *avctx, QSVEncContext *q, goto free; } +static int update_parameters(AVCodecContext *avctx, QSVEncContext *q, + const AVFrame *frame) +{ + int needReset = 0, ret = 0; + + if (!frame || avctx->codec_id == AV_CODEC_ID_MJPEG) + return 0; + + needReset = update_qp(avctx, q); + needReset |= update_max_frame_size(avctx, q); + needReset |= update_gop_size(avctx, q); + needReset |= update_rir(avctx, q); + needReset |= update_low_delay_brc(avctx, q); + needReset |= update_frame_rate(avctx, q); + needReset |= update_bitrate(avctx, q); + needReset |= update_pic_timing_sei(avctx, q); + ret = update_min_max_qp(avctx, q); + if (ret < 0) + return ret; + needReset |= ret; + if (!needReset) + return 0; + + if (avctx->hwaccel_context) { + AVQSVContext *qsv = avctx->hwaccel_context; + int i, j; + q->param.ExtParam = q->extparam; + for (i = 0; i < qsv->nb_ext_buffers; i++) + q->param.ExtParam[i] = qsv->ext_buffers[i]; + q->param.NumExtParam = qsv->nb_ext_buffers; + + for (i = 0; i < q->nb_extparam_internal; i++) { + for (j = 0; j < qsv->nb_ext_buffers; j++) { + if (qsv->ext_buffers[j]->BufferId == q->extparam_internal[i]->BufferId) + break; + } + if (j < qsv->nb_ext_buffers) + continue; + q->param.ExtParam[q->param.NumExtParam++] = q->extparam_internal[i]; + } + } else { + q->param.ExtParam = q->extparam_internal; + q->param.NumExtParam = q->nb_extparam_internal; + } + + // Flush codec before reset configuration. + while (ret != AVERROR(EAGAIN)) { + ret = encode_frame(avctx, q, NULL); + if (ret < 0 && ret != AVERROR(EAGAIN)) + return ret; + } + + av_log(avctx, AV_LOG_DEBUG, "Parameter change, call msdk reset.\n"); + ret = MFXVideoENCODE_Reset(q->session, &q->param); + if (ret < 0) + return ff_qsv_print_error(avctx, ret, "Error during resetting"); + + return 0; +} + int ff_qsv_encode(AVCodecContext *avctx, QSVEncContext *q, AVPacket *pkt, const AVFrame *frame, int *got_packet) { @@ -2389,7 +2555,7 @@ int ff_qsv_encode(AVCodecContext *avctx, QSVEncContext *q, return ret; ret = encode_frame(avctx, q, frame); - if (ret < 0) + if (ret < 0 && ret != AVERROR(EAGAIN)) return ret; if ((av_fifo_can_read(q->async_fifo) >= q->async_depth) || diff --git a/libavcodec/qsvenc.h b/libavcodec/qsvenc.h index a7bbb3797e3..4a6fa2caedc 100644 --- a/libavcodec/qsvenc.h +++ b/libavcodec/qsvenc.h @@ -45,10 +45,12 @@ #define QSV_HAVE_AVBR 1 #define QSV_HAVE_VCM 1 #define QSV_HAVE_MF 0 +#define QSV_HAVE_HE QSV_VERSION_ATLEAST(2, 4) #else #define QSV_HAVE_AVBR 0 #define QSV_HAVE_VCM 0 #define QSV_HAVE_MF !QSV_ONEVPL +#define QSV_HAVE_HE 0 #endif #define QSV_COMMON_OPTS \ @@ -64,6 +66,14 @@ { "forced_idr", "Forcing I frames as IDR frames", OFFSET(qsv.forced_idr), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE }, \ { "low_power", "enable low power mode(experimental: many limitations by mfx version, BRC modes, etc.)", OFFSET(qsv.low_power), AV_OPT_TYPE_BOOL, { .i64 = -1}, -1, 1, VE}, +#if QSV_HAVE_HE +#define QSV_HE_OPTIONS \ +{ "dual_gfx", "Prefer processing on both iGfx and dGfx simultaneously", OFFSET(qsv.dual_gfx), AV_OPT_TYPE_INT, { .i64 = MFX_HYPERMODE_OFF }, MFX_HYPERMODE_OFF, MFX_HYPERMODE_ADAPTIVE, VE, "dual_gfx" }, \ +{ "off", "Disable HyperEncode mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_HYPERMODE_OFF }, INT_MIN, INT_MAX, VE, "dual_gfx" }, \ +{ "on", "Enable HyperEncode mode and return error if incompatible parameters during initialization", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_HYPERMODE_ON }, INT_MIN, INT_MAX, VE, "dual_gfx" }, \ +{ "adaptive", "Enable HyperEncode mode or fallback to single GPU if incompatible parameters during initialization", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_HYPERMODE_ADAPTIVE }, INT_MIN, INT_MAX, VE, "dual_gfx" }, +#endif + #define QSV_OPTION_RDO \ { "rdo", "Enable rate distortion optimization", OFFSET(qsv.rdo), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, VE }, @@ -171,7 +181,9 @@ typedef struct QSVEncContext { mfxExtAV1TileParam extav1tileparam; mfxExtAV1BitstreamParam extav1bsparam; #endif - +#if QSV_HAVE_HE + mfxExtHyperModeParam exthypermodeparam; +#endif #if QSV_HAVE_OPAQUE mfxExtOpaqueSurfaceAlloc opaque_alloc; mfxFrameSurface1 **opaque_surfaces; @@ -180,7 +192,7 @@ typedef struct QSVEncContext { mfxExtVideoSignalInfo extvsi; - mfxExtBuffer *extparam_internal[5 + (QSV_HAVE_MF * 2) + QSV_HAVE_EXT_AV1_PARAM * 2]; + mfxExtBuffer *extparam_internal[5 + (QSV_HAVE_MF * 2) + (QSV_HAVE_EXT_AV1_PARAM * 2) + QSV_HAVE_HE]; int nb_extparam_internal; mfxExtBuffer **extparam; @@ -255,6 +267,7 @@ typedef struct QSVEncContext { int co2_idx; int co3_idx; int exthevctiles_idx; + int exthypermodeparam_idx; int vp9_idx; int max_qp_i; @@ -299,6 +312,8 @@ typedef struct QSVEncContext { // This is used for SEI Timing reset int old_pic_timing_sei; int skip_frame; + // This is used for Hyper Encode + int dual_gfx; } QSVEncContext; int ff_qsv_enc_init(AVCodecContext *avctx, QSVEncContext *q); diff --git a/libavcodec/qsvenc_h264.c b/libavcodec/qsvenc_h264.c index bbdedc3335f..071a9a79e96 100644 --- a/libavcodec/qsvenc_h264.c +++ b/libavcodec/qsvenc_h264.c @@ -117,6 +117,9 @@ static const AVOption options[] = { QSV_OPTION_SCENARIO QSV_OPTION_AVBR QSV_OPTION_SKIP_FRAME +#if QSV_HAVE_HE + QSV_HE_OPTIONS +#endif { "cavlc", "Enable CAVLC", OFFSET(qsv.cavlc), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, #if QSV_HAVE_VCM diff --git a/libavcodec/qsvenc_hevc.c b/libavcodec/qsvenc_hevc.c index e042263bf51..5e23ca9647c 100644 --- a/libavcodec/qsvenc_hevc.c +++ b/libavcodec/qsvenc_hevc.c @@ -318,6 +318,9 @@ static const AVOption options[] = { QSV_OPTION_SCENARIO QSV_OPTION_AVBR QSV_OPTION_SKIP_FRAME +#if QSV_HAVE_HE + QSV_HE_OPTIONS +#endif { "idr_interval", "Distance (in I-frames) between IDR frames", OFFSET(qsv.idr_interval), AV_OPT_TYPE_INT, { .i64 = 0 }, -1, INT_MAX, VE, "idr_interval" }, { "begin_only", "Output an IDR-frame only at the beginning of the stream", 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, 0, 0, VE, "idr_interval" }, diff --git a/libavcodec/qsvenc_jpeg.c b/libavcodec/qsvenc_jpeg.c index 2469ef7c9fd..2add12adc90 100644 --- a/libavcodec/qsvenc_jpeg.c +++ b/libavcodec/qsvenc_jpeg.c @@ -89,6 +89,8 @@ const FFCodec ff_mjpeg_qsv_encoder = { .close = qsv_enc_close, .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HYBRID, .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_NV12, + AV_PIX_FMT_YUYV422, + AV_PIX_FMT_BGRA, AV_PIX_FMT_QSV, AV_PIX_FMT_NONE }, .p.priv_class = &class, diff --git a/libavcodec/qtrle.c b/libavcodec/qtrle.c index 5cb18c86c2b..9b016d7e83e 100644 --- a/libavcodec/qtrle.c +++ b/libavcodec/qtrle.c @@ -537,7 +537,14 @@ static int qtrle_decode_frame(AVCodecContext *avctx, AVFrame *rframe, } if(has_palette) { - s->frame->palette_has_changed = ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS + s->frame->palette_has_changed = +#endif + ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_ENABLE_DEPRECATION_WARNINGS +#endif /* make the palette available on the way out */ memcpy(s->frame->data[1], s->pal, AVPALETTE_SIZE); diff --git a/libavcodec/qtrleenc.c b/libavcodec/qtrleenc.c index 855494d3df8..3846762745a 100644 --- a/libavcodec/qtrleenc.c +++ b/libavcodec/qtrleenc.c @@ -374,7 +374,7 @@ static int qtrle_encode_frame(AVCodecContext *avctx, AVPacket *pkt, return ret; if (avctx->gop_size == 0 || !s->previous_frame->data[0] || - (s->avctx->frame_number % avctx->gop_size) == 0) { + (s->avctx->frame_num % avctx->gop_size) == 0) { /* I-Frame */ s->key_frame = 1; } else { @@ -404,7 +404,7 @@ const FFCodec ff_qtrle_encoder = { CODEC_LONG_NAME("QuickTime Animation (RLE) video"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_QTRLE, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(QtrleEncContext), .init = qtrle_encode_init, FF_CODEC_ENCODE_CB(qtrle_encode_frame), diff --git a/libavcodec/r210dec.c b/libavcodec/r210dec.c index ae80f46eb67..fe6a025988e 100644 --- a/libavcodec/r210dec.c +++ b/libavcodec/r210dec.c @@ -57,7 +57,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *pic, return ret; pic->pict_type = AV_PICTURE_TYPE_I; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; g_line = pic->data[0]; b_line = pic->data[1]; r_line = pic->data[2]; diff --git a/libavcodec/r210enc.c b/libavcodec/r210enc.c index d87f42ce4a8..91e34528741 100644 --- a/libavcodec/r210enc.c +++ b/libavcodec/r210enc.c @@ -96,7 +96,7 @@ const FFCodec ff_r210_encoder = { CODEC_LONG_NAME("Uncompressed RGB 10-bit"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_R210, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .init = encode_init, FF_CODEC_ENCODE_CB(encode_frame), .p.pix_fmts = pix_fmt, @@ -108,7 +108,7 @@ const FFCodec ff_r10k_encoder = { CODEC_LONG_NAME("AJA Kona 10-bit RGB Codec"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_R10K, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .init = encode_init, FF_CODEC_ENCODE_CB(encode_frame), .p.pix_fmts = pix_fmt, @@ -120,7 +120,7 @@ const FFCodec ff_avrp_encoder = { CODEC_LONG_NAME("Avid 1:1 10-bit RGB Packer"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_AVRP, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .init = encode_init, FF_CODEC_ENCODE_CB(encode_frame), .p.pix_fmts = pix_fmt, diff --git a/libavcodec/rasc.c b/libavcodec/rasc.c index cfa3d6b079d..4d057e80e72 100644 --- a/libavcodec/rasc.c +++ b/libavcodec/rasc.c @@ -740,7 +740,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if (!s->skip_cursor) draw_cursor(avctx); - s->frame->key_frame = intra; + if (intra) + s->frame->flags |= AV_FRAME_FLAG_KEY; + else + s->frame->flags &= ~AV_FRAME_FLAG_KEY; s->frame->pict_type = intra ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; *got_frame = 1; diff --git a/libavcodec/ratecontrol.c b/libavcodec/ratecontrol.c index 4829172c2cd..649f570c9d9 100644 --- a/libavcodec/ratecontrol.c +++ b/libavcodec/ratecontrol.c @@ -39,8 +39,8 @@ void ff_write_pass1_stats(MpegEncContext *s) snprintf(s->avctx->stats_out, 256, "in:%d out:%d type:%d q:%d itex:%d ptex:%d mv:%d misc:%d " "fcode:%d bcode:%d mc-var:%"PRId64" var:%"PRId64" icount:%d skipcount:%d hbits:%d;\n", - s->current_picture_ptr->f->display_picture_number, - s->current_picture_ptr->f->coded_picture_number, + s->current_picture_ptr->display_picture_number, + s->current_picture_ptr->coded_picture_number, s->pict_type, s->current_picture.f->quality, s->i_tex_bits, @@ -57,7 +57,16 @@ void ff_write_pass1_stats(MpegEncContext *s) static double get_fps(AVCodecContext *avctx) { - return 1.0 / av_q2d(avctx->time_base) / FFMAX(avctx->ticks_per_frame, 1); + if (avctx->framerate.num > 0 && avctx->framerate.den > 0) + return av_q2d(avctx->framerate); + +FF_DISABLE_DEPRECATION_WARNINGS + return 1.0 / av_q2d(avctx->time_base) +#if FF_API_TICKS_PER_FRAME + / FFMAX(avctx->ticks_per_frame, 1) +#endif + ; +FF_ENABLE_DEPRECATION_WARNINGS } static inline double qp2bits(RateControlEntry *rce, double qp) diff --git a/libavcodec/ratecontrol.h b/libavcodec/ratecontrol.h index 2a7aaec644c..4de80fad904 100644 --- a/libavcodec/ratecontrol.h +++ b/libavcodec/ratecontrol.h @@ -80,9 +80,6 @@ typedef struct RateControlContext{ int frame_count[5]; int last_non_b_pict_type; - void *non_lavc_opaque; ///< context for non lavc rc code (for example xvid) - float dry_run_qscale; ///< for xvid rc - int last_picture_number; ///< for xvid rc AVExpr * rc_eq_eval; }RateControlContext; diff --git a/libavcodec/rawdec.c b/libavcodec/rawdec.c index c20c317fed8..8e9358f95d1 100644 --- a/libavcodec/rawdec.c +++ b/libavcodec/rawdec.c @@ -227,15 +227,16 @@ static int raw_decode(AVCodecContext *avctx, AVFrame *frame, need_copy = !avpkt->buf || context->is_1_2_4_8_bpp || context->is_yuv2 || context->is_lt_16bpp; frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; res = ff_decode_frame_props(avctx, frame); if (res < 0) return res; if (context->tff >= 0) { - frame->interlaced_frame = 1; - frame->top_field_first = context->tff; + frame->flags |= AV_FRAME_FLAG_INTERLACED; + if (context->tff == 1) + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; } if ((res = av_image_check_size(avctx->width, avctx->height, 0, avctx)) < 0) @@ -372,7 +373,11 @@ static int raw_decode(AVCodecContext *avctx, AVFrame *frame, } if (ff_copy_palette(context->palette->data, avpkt, avctx)) { +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } else if (context->is_nut_pal8) { int vid_size = avctx->width * avctx->height; int pal_size = avpkt->size - vid_size; @@ -380,7 +385,11 @@ static int raw_decode(AVCodecContext *avctx, AVFrame *frame, if (avpkt->size > vid_size && pal_size <= AVPALETTE_SIZE) { const uint8_t *pal = avpkt->data + vid_size; memcpy(context->palette->data, pal, pal_size); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } } } @@ -459,9 +468,9 @@ static int raw_decode(AVCodecContext *avctx, AVFrame *frame, } if (avctx->field_order > AV_FIELD_PROGRESSIVE) { /* we have interlaced material flagged in container */ - frame->interlaced_frame = 1; + frame->flags |= AV_FRAME_FLAG_INTERLACED; if (avctx->field_order == AV_FIELD_TT || avctx->field_order == AV_FIELD_TB) - frame->top_field_first = 1; + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; } *got_frame = 1; diff --git a/libavcodec/rawenc.c b/libavcodec/rawenc.c index c2643e6d803..8c577006d92 100644 --- a/libavcodec/rawenc.c +++ b/libavcodec/rawenc.c @@ -86,7 +86,8 @@ const FFCodec ff_rawvideo_encoder = { CODEC_LONG_NAME("raw video"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_RAWVIDEO, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .init = raw_encode_init, FF_CODEC_ENCODE_CB(raw_encode), }; diff --git a/libavcodec/riscv/Makefile b/libavcodec/riscv/Makefile index 965942f4df8..ee17a521fd4 100644 --- a/libavcodec/riscv/Makefile +++ b/libavcodec/riscv/Makefile @@ -10,6 +10,8 @@ OBJS-$(CONFIG_BSWAPDSP) += riscv/bswapdsp_init.o \ RVV-OBJS-$(CONFIG_BSWAPDSP) += riscv/bswapdsp_rvv.o OBJS-$(CONFIG_FMTCONVERT) += riscv/fmtconvert_init.o RVV-OBJS-$(CONFIG_FMTCONVERT) += riscv/fmtconvert_rvv.o +OBJS-$(CONFIG_H264CHROMA) += riscv/h264_chroma_init_riscv.o +RVV-OBJS-$(CONFIG_H264CHROMA) += riscv/h264_mc_chroma.o OBJS-$(CONFIG_IDCTDSP) += riscv/idctdsp_init.o RVV-OBJS-$(CONFIG_IDCTDSP) += riscv/idctdsp_rvv.o OBJS-$(CONFIG_OPUS_DECODER) += riscv/opusdsp_init.o diff --git a/libavcodec/riscv/aacpsdsp_init.c b/libavcodec/riscv/aacpsdsp_init.c index f42baf4251a..c5ec7962322 100644 --- a/libavcodec/riscv/aacpsdsp_init.c +++ b/libavcodec/riscv/aacpsdsp_init.c @@ -43,13 +43,16 @@ av_cold void ff_psdsp_init_riscv(PSDSPContext *c) int flags = av_get_cpu_flags(); if (flags & AV_CPU_FLAG_RVV_F32) { - c->add_squares = ff_ps_add_squares_rvv; - c->mul_pair_single = ff_ps_mul_pair_single_rvv; c->hybrid_analysis = ff_ps_hybrid_analysis_rvv; - c->stereo_interpolate[0] = ff_ps_stereo_interpolate_rvv; + + if (flags & AV_CPU_FLAG_RVB_ADDR) { + c->add_squares = ff_ps_add_squares_rvv; + c->mul_pair_single = ff_ps_mul_pair_single_rvv; + c->stereo_interpolate[0] = ff_ps_stereo_interpolate_rvv; + } } - if (flags & AV_CPU_FLAG_RVV_I32) { + if ((flags & AV_CPU_FLAG_RVV_I32) && (flags & AV_CPU_FLAG_RVB_ADDR)) { c->hybrid_analysis_ileave = ff_ps_hybrid_analysis_ileave_rvv; c->hybrid_synthesis_deint = ff_ps_hybrid_synthesis_deint_rvv; } diff --git a/libavcodec/riscv/aacpsdsp_rvv.S b/libavcodec/riscv/aacpsdsp_rvv.S index 80bd19f6ad2..b581383f775 100644 --- a/libavcodec/riscv/aacpsdsp_rvv.S +++ b/libavcodec/riscv/aacpsdsp_rvv.S @@ -22,13 +22,13 @@ func ff_ps_add_squares_rvv, zve32f 1: - vsetvli t0, a2, e32, m1, ta, ma + vsetvli t0, a2, e32, m4, ta, ma vlseg2e32.v v24, (a1) sub a2, a2, t0 vle32.v v16, (a0) sh3add a1, t0, a1 vfmacc.vv v16, v24, v24 - vfmacc.vv v16, v25, v25 + vfmacc.vv v16, v28, v28 vse32.v v16, (a0) sh2add a0, t0, a0 bnez a2, 1b diff --git a/libavcodec/riscv/alacdsp_init.c b/libavcodec/riscv/alacdsp_init.c index fa8a7c81299..cd6dc4f8aea 100644 --- a/libavcodec/riscv/alacdsp_init.c +++ b/libavcodec/riscv/alacdsp_init.c @@ -41,7 +41,7 @@ av_cold void ff_alacdsp_init_riscv(ALACDSPContext *c) #if HAVE_RVV && (__riscv_xlen == 64) int flags = av_get_cpu_flags(); - if (flags & AV_CPU_FLAG_RVV_I32) { + if ((flags & AV_CPU_FLAG_RVV_I32) && (flags & AV_CPU_FLAG_RVB_ADDR)) { c->decorrelate_stereo = ff_alac_decorrelate_stereo_rvv; c->append_extra_bits[0] = ff_alac_append_extra_bits_mono_rvv; c->append_extra_bits[1] = ff_alac_append_extra_bits_stereo_rvv; diff --git a/libavcodec/riscv/alacdsp_rvv.S b/libavcodec/riscv/alacdsp_rvv.S index 8fbe3fbe778..8efb04e0c8d 100644 --- a/libavcodec/riscv/alacdsp_rvv.S +++ b/libavcodec/riscv/alacdsp_rvv.S @@ -25,7 +25,7 @@ func ff_alac_decorrelate_stereo_rvv, zve32x ld a4, 8(a0) ld a0, 0(a0) 1: - vsetvli t0, a1, e32, m1, ta, ma + vsetvli t0, a1, e32, m4, ta, ma vle32.v v24, (a4) sub a1, a1, t0 vle32.v v16, (a0) @@ -47,7 +47,7 @@ func ff_alac_append_extra_bits_mono_rvv, zve32x ld a0, (a0) ld a1, (a1) 1: - vsetvli t0, a4, e32, m1, ta, ma + vsetvli t0, a4, e32, m8, ta, ma vle32.v v16, (a0) sub a4, a4, t0 vle32.v v24, (a1) @@ -67,7 +67,7 @@ func ff_alac_append_extra_bits_stereo_rvv, zve32x ld a7, 8(a1) ld a1, (a1) 1: - vsetvli t0, a4, e32, m1, ta, ma + vsetvli t0, a4, e32, m8, ta, ma vle32.v v16, (a0) sub a4, a4, t0 vle32.v v0, (a6) diff --git a/libavcodec/riscv/audiodsp_init.c b/libavcodec/riscv/audiodsp_init.c index 32c3c6794da..9ab59c011eb 100644 --- a/libavcodec/riscv/audiodsp_init.c +++ b/libavcodec/riscv/audiodsp_init.c @@ -38,11 +38,13 @@ av_cold void ff_audiodsp_init_riscv(AudioDSPContext *c) if (flags & AV_CPU_FLAG_RVF) c->vector_clipf = ff_vector_clipf_rvf; #if HAVE_RVV - if (flags & AV_CPU_FLAG_RVV_I32) { - c->scalarproduct_int16 = ff_scalarproduct_int16_rvv; - c->vector_clip_int32 = ff_vector_clip_int32_rvv; + if (flags & AV_CPU_FLAG_RVB_ADDR) { + if (flags & AV_CPU_FLAG_RVV_I32) { + c->scalarproduct_int16 = ff_scalarproduct_int16_rvv; + c->vector_clip_int32 = ff_vector_clip_int32_rvv; + } + if (flags & AV_CPU_FLAG_RVV_F32) + c->vector_clipf = ff_vector_clipf_rvv; } - if (flags & AV_CPU_FLAG_RVV_F32) - c->vector_clipf = ff_vector_clipf_rvv; #endif } diff --git a/libavcodec/riscv/audiodsp_rvv.S b/libavcodec/riscv/audiodsp_rvv.S index af1e07bef9c..f7eba2114f8 100644 --- a/libavcodec/riscv/audiodsp_rvv.S +++ b/libavcodec/riscv/audiodsp_rvv.S @@ -21,21 +21,22 @@ #include "libavutil/riscv/asm.S" func ff_scalarproduct_int16_rvv, zve32x - vsetivli zero, 1, e32, m1, ta, ma - vmv.s.x v8, zero + vsetvli t0, zero, e32, m8, ta, ma + vmv.v.x v8, zero + vmv.s.x v0, zero 1: - vsetvli t0, a2, e16, m1, ta, ma + vsetvli t0, a2, e16, m4, tu, ma vle16.v v16, (a0) sub a2, a2, t0 vle16.v v24, (a1) sh1add a0, t0, a0 - vwmul.vv v0, v16, v24 + vwmacc.vv v8, v16, v24 sh1add a1, t0, a1 - vsetvli zero, t0, e32, m2, ta, ma - vredsum.vs v8, v0, v8 bnez a2, 1b - vmv.x.s a0, v8 + vsetvli t0, zero, e32, m8, ta, ma + vredsum.vs v0, v8, v0 + vmv.x.s a0, v0 ret endfunc diff --git a/libavcodec/riscv/bswapdsp_init.c b/libavcodec/riscv/bswapdsp_init.c index abe84ec1f7d..ed666c9b3a3 100644 --- a/libavcodec/riscv/bswapdsp_init.c +++ b/libavcodec/riscv/bswapdsp_init.c @@ -26,21 +26,20 @@ #include "libavcodec/bswapdsp.h" void ff_bswap32_buf_rvb(uint32_t *dst, const uint32_t *src, int len); -void ff_bswap32_buf_rvv(uint32_t *dst, const uint32_t *src, int len); void ff_bswap16_buf_rvv(uint16_t *dst, const uint16_t *src, int len); av_cold void ff_bswapdsp_init_riscv(BswapDSPContext *c) { - int cpu_flags = av_get_cpu_flags(); + int flags = av_get_cpu_flags(); + if (flags & AV_CPU_FLAG_RVB_ADDR) { #if (__riscv_xlen >= 64) - if (cpu_flags & AV_CPU_FLAG_RVB_BASIC) - c->bswap_buf = ff_bswap32_buf_rvb; + if (flags & AV_CPU_FLAG_RVB_BASIC) + c->bswap_buf = ff_bswap32_buf_rvb; #endif #if HAVE_RVV - if (cpu_flags & AV_CPU_FLAG_RVV_I32) { - c->bswap_buf = ff_bswap32_buf_rvv; - c->bswap16_buf = ff_bswap16_buf_rvv; - } + if (flags & AV_CPU_FLAG_RVV_I32) + c->bswap16_buf = ff_bswap16_buf_rvv; #endif + } } diff --git a/libavcodec/riscv/bswapdsp_rvv.S b/libavcodec/riscv/bswapdsp_rvv.S index ef2999c1be5..b37fe262559 100644 --- a/libavcodec/riscv/bswapdsp_rvv.S +++ b/libavcodec/riscv/bswapdsp_rvv.S @@ -21,42 +21,18 @@ #include "config.h" #include "libavutil/riscv/asm.S" -func ff_bswap32_buf_rvv, zve32x - li t4, 4 - addi t1, a0, 1 - addi t2, a0, 2 - addi t3, a0, 3 -1: - vsetvli t0, a2, e8, m1, ta, ma - vlseg4e8.v v8, (a1) - sub a2, a2, t0 - sh2add a1, t0, a1 - vsse8.v v8, (t3), t4 - sh2add t3, t0, t3 - vsse8.v v9, (t2), t4 - sh2add t2, t0, t2 - vsse8.v v10, (t1), t4 - sh2add t1, t0, t1 - vsse8.v v11, (a0), t4 - sh2add a0, t0, a0 - bnez a2, 1b - - ret -endfunc - func ff_bswap16_buf_rvv, zve32x - li t2, 2 - addi t1, a0, 1 1: - vsetvli t0, a2, e8, m1, ta, ma - vlseg2e8.v v8, (a1) - sub a2, a2, t0 - sh1add a1, t0, a1 - vsse8.v v8, (t1), t2 - sh1add t1, t0, t1 - vsse8.v v9, (a0), t2 - sh1add a0, t0, a0 - bnez a2, 1b + vsetvli t0, a2, e16, m8, ta, ma + vle16.v v8, (a1) + sub a2, a2, t0 + vsll.vi v16, v8, 8 + sh1add a1, t0, a1 + vsrl.vi v24, v8, 8 + vor.vv v8, v16, v24 + vse16.v v8, (a0) + sh1add a0, t0, a0 + bnez a2, 1b ret endfunc diff --git a/libavcodec/riscv/fmtconvert_init.c b/libavcodec/riscv/fmtconvert_init.c index 2ded9d36153..f5eeafba457 100644 --- a/libavcodec/riscv/fmtconvert_init.c +++ b/libavcodec/riscv/fmtconvert_init.c @@ -36,7 +36,7 @@ av_cold void ff_fmt_convert_init_riscv(FmtConvertContext *c) #if HAVE_RVV int flags = av_get_cpu_flags(); - if (flags & AV_CPU_FLAG_RVV_F32) { + if ((flags & AV_CPU_FLAG_RVV_F32) && (flags & AV_CPU_FLAG_RVB_ADDR)) { c->int32_to_float_fmul_scalar = ff_int32_to_float_fmul_scalar_rvv; c->int32_to_float_fmul_array8 = ff_int32_to_float_fmul_array8_rvv; } diff --git a/libavcodec/riscv/h264_chroma_init_riscv.c b/libavcodec/riscv/h264_chroma_init_riscv.c new file mode 100644 index 00000000000..e6fe5f6ed63 --- /dev/null +++ b/libavcodec/riscv/h264_chroma_init_riscv.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023 SiFive, Inc. All rights reserved. + * + * 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 + +#include "libavutil/attributes.h" +#include "libavutil/riscv/cpu.h" +#include "libavcodec/h264chroma.h" +#include "config.h" + +void h264_put_chroma_mc8_rvv(uint8_t *p_dst, const uint8_t *p_src, ptrdiff_t stride, int h, int x, int y); +void h264_avg_chroma_mc8_rvv(uint8_t *p_dst, const uint8_t *p_src, ptrdiff_t stride, int h, int x, int y); +void h264_put_chroma_mc4_rvv(uint8_t *p_dst, const uint8_t *p_src, ptrdiff_t stride, int h, int x, int y); +void h264_avg_chroma_mc4_rvv(uint8_t *p_dst, const uint8_t *p_src, ptrdiff_t stride, int h, int x, int y); +void h264_put_chroma_mc2_rvv(uint8_t *p_dst, const uint8_t *p_src, ptrdiff_t stride, int h, int x, int y); +void h264_avg_chroma_mc2_rvv(uint8_t *p_dst, const uint8_t *p_src, ptrdiff_t stride, int h, int x, int y); + +av_cold void ff_h264chroma_init_riscv(H264ChromaContext *c, int bit_depth) +{ +#if HAVE_RVV + int flags = av_get_cpu_flags(); + + if (bit_depth == 8 && (flags & AV_CPU_FLAG_RVV_I32) && + (flags & AV_CPU_FLAG_RVB_ADDR) && ff_get_rv_vlenb() >= 16) { + c->put_h264_chroma_pixels_tab[0] = h264_put_chroma_mc8_rvv; + c->avg_h264_chroma_pixels_tab[0] = h264_avg_chroma_mc8_rvv; + c->put_h264_chroma_pixels_tab[1] = h264_put_chroma_mc4_rvv; + c->avg_h264_chroma_pixels_tab[1] = h264_avg_chroma_mc4_rvv; + c->put_h264_chroma_pixels_tab[2] = h264_put_chroma_mc2_rvv; + c->avg_h264_chroma_pixels_tab[2] = h264_avg_chroma_mc2_rvv; + } +#endif +} diff --git a/libavcodec/riscv/h264_mc_chroma.S b/libavcodec/riscv/h264_mc_chroma.S new file mode 100644 index 00000000000..ce99bda44dc --- /dev/null +++ b/libavcodec/riscv/h264_mc_chroma.S @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2023 SiFive, Inc. All rights reserved. + * + * 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 "libavutil/riscv/asm.S" + +.macro do_chroma_mc type unroll + csrw vxrm, zero + slli t2, a5, 3 + mul t1, a5, a4 + sh3add a5, a4, t2 + slli a4, a4, 3 + sub a5, t1, a5 + sub a7, a4, t1 + addi a6, a5, 64 + sub t0, t2, t1 + vsetvli t3, t6, e8, m1, ta, mu + beqz t1, 2f + blez a3, 8f + li t4, 0 + li t2, 0 + li t5, 1 + addi a5, t3, 1 + slli t3, a2, (1 + \unroll) +1: # if (xy != 0) + add a4, a1, t4 + vsetvli zero, a5, e8, m1, ta, ma + .ifc \unroll,1 + addi t2, t2, 4 + .else + addi t2, t2, 2 + .endif + vle8.v v10, (a4) + add a4, a4, a2 + vslide1down.vx v11, v10, t5 + vsetvli zero, t6, e8, m1, ta, ma + vwmulu.vx v8, v10, a6 + vwmaccu.vx v8, a7, v11 + vsetvli zero, a5, e8, m1, ta, ma + vle8.v v12, (a4) + vsetvli zero, t6, e8, m1, ta, ma + add a4, a4, a2 + vwmaccu.vx v8, t0, v12 + vsetvli zero, a5, e8, m1, ta, ma + vslide1down.vx v13, v12, t5 + vsetvli zero, t6, e8, m1, ta, ma + vwmulu.vx v10, v12, a6 + vwmaccu.vx v8, t1, v13 + vwmaccu.vx v10, a7, v13 + vsetvli zero, a5, e8, m1, ta, ma + vle8.v v14, (a4) + vsetvli zero, t6, e8, m1, ta, ma + add a4, a4, a2 + vwmaccu.vx v10, t0, v14 + vsetvli zero, a5, e8, m1, ta, ma + vslide1down.vx v15, v14, t5 + vsetvli zero, t6, e8, m1, ta, ma + vwmulu.vx v12, v14, a6 + vwmaccu.vx v10, t1, v15 + vwmaccu.vx v12, a7, v15 + vnclipu.wi v15, v8, 6 + .ifc \type,avg + vle8.v v9, (a0) + vaaddu.vv v15, v15, v9 + .endif + vse8.v v15, (a0) + add a0, a0, a2 + vnclipu.wi v8, v10, 6 + .ifc \type,avg + vle8.v v9, (a0) + vaaddu.vv v8, v8, v9 + .endif + add t4, t4, t3 + vse8.v v8, (a0) + add a0, a0, a2 + .ifc \unroll,1 + vsetvli zero, a5, e8, m1, ta, ma + vle8.v v14, (a4) + vsetvli zero, t6, e8, m1, ta, ma + add a4, a4, a2 + vwmaccu.vx v12, t0, v14 + vsetvli zero, a5, e8, m1, ta, ma + vslide1down.vx v15, v14, t5 + vsetvli zero, t6, e8, m1, ta, ma + vwmulu.vx v16, v14, a6 + vwmaccu.vx v12, t1, v15 + vwmaccu.vx v16, a7, v15 + vsetvli zero, a5, e8, m1, ta, ma + vle8.v v14, (a4) + vsetvli zero, t6, e8, m1, ta, ma + vwmaccu.vx v16, t0, v14 + vsetvli zero, a5, e8, m1, ta, ma + vslide1down.vx v14, v14, t5 + vsetvli zero, t6, e8, m1, ta, ma + vwmaccu.vx v16, t1, v14 + vnclipu.wi v8, v12, 6 + .ifc \type,avg + vle8.v v9, (a0) + vaaddu.vv v8, v8, v9 + .endif + vse8.v v8, (a0) + add a0, a0, a2 + vnclipu.wi v8, v16, 6 + .ifc \type,avg + vle8.v v9, (a0) + vaaddu.vv v8, v8, v9 + .endif + vse8.v v8, (a0) + add a0, a0, a2 + .endif + blt t2, a3, 1b + j 8f +2: + bnez a4, 4f + beqz t2, 4f + blez a3, 8f + li a4, 0 + li t1, 0 + slli a7, a2, (1 + \unroll) +3: # if ((x8 - xy) == 0 && (y8 -xy) != 0) + add a5, a1, a4 + vsetvli zero, zero, e8, m1, ta, ma + .ifc \unroll,1 + addi t1, t1, 4 + .else + addi t1, t1, 2 + .endif + vle8.v v8, (a5) + add a5, a5, a2 + add t2, a5, a2 + vwmulu.vx v10, v8, a6 + vle8.v v8, (a5) + vwmulu.vx v12, v8, a6 + vle8.v v9, (t2) + add t2, t2, a2 + add a5, t2, a2 + vwmaccu.vx v10, t0, v8 + add a4, a4, a7 + vwmaccu.vx v12, t0, v9 + vnclipu.wi v15, v10, 6 + vwmulu.vx v10, v9, a6 + vnclipu.wi v9, v12, 6 + .ifc \type,avg + vle8.v v16, (a0) + vaaddu.vv v15, v15, v16 + .endif + vse8.v v15, (a0) + add a0, a0, a2 + .ifc \type,avg + vle8.v v16, (a0) + vaaddu.vv v9, v9, v16 + .endif + vse8.v v9, (a0) + add a0, a0, a2 + .ifc \unroll,1 + vle8.v v8, (t2) + vle8.v v14, (a5) + vwmaccu.vx v10, t0, v8 + vwmulu.vx v12, v8, a6 + vnclipu.wi v8, v10, 6 + vwmaccu.vx v12, t0, v14 + .ifc \type,avg + vle8.v v16, (a0) + vaaddu.vv v8, v8, v16 + .endif + vse8.v v8, (a0) + add a0, a0, a2 + vnclipu.wi v8, v12, 6 + .ifc \type,avg + vle8.v v16, (a0) + vaaddu.vv v8, v8, v16 + .endif + vse8.v v8, (a0) + add a0, a0, a2 + .endif + blt t1, a3, 3b + j 8f +4: + beqz a4, 6f + bnez t2, 6f + blez a3, 8f + li a4, 0 + li t2, 0 + addi t0, t3, 1 + slli t1, a2, (1 + \unroll) +5: # if ((x8 - xy) != 0 && (y8 -xy) == 0) + add a5, a1, a4 + vsetvli zero, t0, e8, m1, ta, ma + .ifc \unroll,1 + addi t2, t2, 4 + .else + addi t2, t2, 2 + .endif + vle8.v v8, (a5) + add a5, a5, a2 + vslide1down.vx v9, v8, t5 + vsetvli zero, t6, e8, m1, ta, ma + vwmulu.vx v10, v8, a6 + vwmaccu.vx v10, a7, v9 + vsetvli zero, t0, e8, m1, ta, ma + vle8.v v8, (a5) + add a5, a5, a2 + vslide1down.vx v9, v8, t5 + vsetvli zero, t6, e8, m1, ta, ma + vwmulu.vx v12, v8, a6 + vwmaccu.vx v12, a7, v9 + vnclipu.wi v16, v10, 6 + .ifc \type,avg + vle8.v v18, (a0) + vaaddu.vv v16, v16, v18 + .endif + vse8.v v16, (a0) + add a0, a0, a2 + vnclipu.wi v10, v12, 6 + .ifc \type,avg + vle8.v v18, (a0) + vaaddu.vv v10, v10, v18 + .endif + add a4, a4, t1 + vse8.v v10, (a0) + add a0, a0, a2 + .ifc \unroll,1 + vsetvli zero, t0, e8, m1, ta, ma + vle8.v v8, (a5) + add a5, a5, a2 + vslide1down.vx v9, v8, t5 + vsetvli zero, t6, e8, m1, ta, ma + vwmulu.vx v14, v8, a6 + vwmaccu.vx v14, a7, v9 + vsetvli zero, t0, e8, m1, ta, ma + vle8.v v8, (a5) + vslide1down.vx v9, v8, t5 + vsetvli zero, t6, e8, m1, ta, ma + vwmulu.vx v12, v8, a6 + vnclipu.wi v8, v14, 6 + vwmaccu.vx v12, a7, v9 + .ifc \type,avg + vle8.v v18, (a0) + vaaddu.vv v8, v8, v18 + .endif + vse8.v v8, (a0) + add a0, a0, a2 + vnclipu.wi v8, v12, 6 + .ifc \type,avg + vle8.v v18, (a0) + vaaddu.vv v8, v8, v18 + .endif + vse8.v v8, (a0) + add a0, a0, a2 + .endif + blt t2, a3, 5b + j 8f +6: + blez a3, 8f + li a4, 0 + li t2, 0 + slli a7, a2, (1 + \unroll) +7: # the final else, none of the above conditions are met + add t0, a1, a4 + vsetvli zero, zero, e8, m1, ta, ma + add a5, a0, a4 + add a4, a4, a7 + .ifc \unroll,1 + addi t2, t2, 4 + .else + addi t2, t2, 2 + .endif + vle8.v v8, (t0) + add t0, t0, a2 + add t1, t0, a2 + vwmulu.vx v10, v8, a6 + vle8.v v8, (t0) + add t0, t1, a2 + vnclipu.wi v13, v10, 6 + vwmulu.vx v10, v8, a6 + .ifc \type,avg + vle8.v v18, (a5) + vaaddu.vv v13, v13, v18 + .endif + vse8.v v13, (a5) + add a5, a5, a2 + vnclipu.wi v8, v10, 6 + .ifc \type,avg + vle8.v v18, (a5) + vaaddu.vv v8, v8, v18 + .endif + vse8.v v8, (a5) + add a5, a5, a2 + .ifc \unroll,1 + vle8.v v9, (t1) + vle8.v v12, (t0) + vwmulu.vx v10, v9, a6 + vnclipu.wi v8, v10, 6 + vwmulu.vx v10, v12, a6 + .ifc \type,avg + vle8.v v18, (a5) + vaaddu.vv v8, v8, v18 + .endif + vse8.v v8, (a5) + add a5, a5, a2 + vnclipu.wi v8, v10, 6 + .ifc \type,avg + vle8.v v18, (a5) + vaaddu.vv v8, v8, v18 + .endif + vse8.v v8, (a5) + .endif + blt t2, a3, 7b +8: + ret +.endm + +func h264_put_chroma_mc_rvv, zve32x +11: + li a7, 3 + blt a3, a7, 12f + do_chroma_mc put 1 +12: + do_chroma_mc put 0 +endfunc + +func h264_avg_chroma_mc_rvv, zve32x +21: + li a7, 3 + blt a3, a7, 22f + do_chroma_mc avg 1 +22: + do_chroma_mc avg 0 +endfunc + +func h264_put_chroma_mc8_rvv, zve32x + li t6, 8 + j 11b +endfunc + +func h264_put_chroma_mc4_rvv, zve32x + li t6, 4 + j 11b +endfunc + +func h264_put_chroma_mc2_rvv, zve32x + li t6, 2 + j 11b +endfunc + +func h264_avg_chroma_mc8_rvv, zve32x + li t6, 8 + j 21b +endfunc + +func h264_avg_chroma_mc4_rvv, zve32x + li t6, 4 + j 21b +endfunc + +func h264_avg_chroma_mc2_rvv, zve32x + li t6, 2 + j 21b +endfunc diff --git a/libavcodec/riscv/vorbisdsp_init.c b/libavcodec/riscv/vorbisdsp_init.c index 0c56ffcb9b7..0bbbcb68de6 100644 --- a/libavcodec/riscv/vorbisdsp_init.c +++ b/libavcodec/riscv/vorbisdsp_init.c @@ -31,7 +31,7 @@ av_cold void ff_vorbisdsp_init_riscv(VorbisDSPContext *c) #if HAVE_RVV int flags = av_get_cpu_flags(); - if (flags & AV_CPU_FLAG_RVV_I32) + if ((flags & AV_CPU_FLAG_RVV_I32) && (flags & AV_CPU_FLAG_RVB_ADDR)) c->vorbis_inverse_coupling = ff_vorbis_inverse_coupling_rvv; #endif } diff --git a/libavcodec/riscv/vorbisdsp_rvv.S b/libavcodec/riscv/vorbisdsp_rvv.S index f45e7dc2f13..81a6c62a657 100644 --- a/libavcodec/riscv/vorbisdsp_rvv.S +++ b/libavcodec/riscv/vorbisdsp_rvv.S @@ -23,7 +23,7 @@ func ff_vorbis_inverse_coupling_rvv, zve32f fmv.w.x ft0, zero 1: - vsetvli t0, a2, e32, m1, ta, ma + vsetvli t0, a2, e32, m4, ta, ma vle32.v v16, (a1) sub a2, a2, t0 vle32.v v24, (a0) diff --git a/libavcodec/rka.c b/libavcodec/rka.c new file mode 100644 index 00000000000..7646d776bea --- /dev/null +++ b/libavcodec/rka.c @@ -0,0 +1,989 @@ +/* + * RKA decoder + * Copyright (c) 2023 Paul B Mahol + * + * 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 "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" + +#include "avcodec.h" +#include "codec_internal.h" +#include "bytestream.h" +#include "decode.h" + +typedef struct ACoder { + GetByteContext gb; + uint32_t low, high; + uint32_t value; +} ACoder; + +typedef struct FiltCoeffs { + int32_t coeffs[257]; + unsigned size; +} FiltCoeffs; + +typedef struct Model64 { + uint32_t zero[2]; + uint32_t sign[2]; + unsigned size; + int bits; + + uint16_t val4[65]; + uint16_t val1[65]; +} Model64; + +typedef struct AdaptiveModel { + int last; + int total; + int buf_size; + int16_t sum; + uint16_t aprob0; + uint16_t aprob1; + uint16_t *prob[2]; +} AdaptiveModel; + +typedef struct ChContext { + int cmode; + int cmode2; + int last_nb_decoded; + unsigned srate_pad; + unsigned pos_idx; + + AdaptiveModel *filt_size; + AdaptiveModel *filt_bits; + + uint32_t *bprob[2]; + + AdaptiveModel position; + AdaptiveModel fshift; + AdaptiveModel nb_segments; + AdaptiveModel coeff_bits[11]; + + Model64 mdl64[4][11]; + + int32_t buf0[131072+2560]; + int32_t buf1[131072+2560]; +} ChContext; + +typedef struct RKAContext { + AVClass *class; + + ACoder ac; + ChContext ch[2]; + + int bps; + int align; + int channels; + int correlated; + int frame_samples; + int last_nb_samples; + uint32_t total_nb_samples; + uint32_t samples_left; + + uint32_t bprob[2][257]; + + AdaptiveModel filt_size; + AdaptiveModel filt_bits; +} RKAContext; + +static int adaptive_model_init(AdaptiveModel *am, int buf_size) +{ + am->buf_size = buf_size; + am->sum = 2000; + am->aprob0 = 0; + am->aprob1 = 0; + am->total = 0; + + if (!am->prob[0]) + am->prob[0] = av_malloc_array(buf_size + 5, sizeof(*am->prob[0])); + if (!am->prob[1]) + am->prob[1] = av_malloc_array(buf_size + 5, sizeof(*am->prob[1])); + + if (!am->prob[0] || !am->prob[1]) + return AVERROR(ENOMEM); + memset(am->prob[0], 0, (buf_size + 5) * sizeof(*am->prob[0])); + memset(am->prob[1], 0, (buf_size + 5) * sizeof(*am->prob[1])); + return 0; +} + +static void adaptive_model_free(AdaptiveModel *am) +{ + av_freep(&am->prob[0]); + av_freep(&am->prob[1]); +} + +static av_cold int rka_decode_init(AVCodecContext *avctx) +{ + RKAContext *s = avctx->priv_data; + int cmode; + + if (avctx->extradata_size < 16) + return AVERROR_INVALIDDATA; + + s->bps = avctx->bits_per_raw_sample = avctx->extradata[13]; + + switch (s->bps) { + case 8: + avctx->sample_fmt = AV_SAMPLE_FMT_U8P; + break; + case 16: + avctx->sample_fmt = AV_SAMPLE_FMT_S16P; + break; + default: + return AVERROR_INVALIDDATA; + } + + av_channel_layout_uninit(&avctx->ch_layout); + s->channels = avctx->ch_layout.nb_channels = avctx->extradata[12]; + if (s->channels < 1 || s->channels > 2) + return AVERROR_INVALIDDATA; + + s->align = (s->channels * (avctx->bits_per_raw_sample >> 3)); + s->samples_left = s->total_nb_samples = (AV_RL32(avctx->extradata + 4)) / s->align; + s->frame_samples = 131072 / s->align; + s->last_nb_samples = s->total_nb_samples % s->frame_samples; + s->correlated = avctx->extradata[15] & 1; + + cmode = avctx->extradata[14] & 0xf; + if ((avctx->extradata[15] & 4) != 0) + cmode = -cmode; + + s->ch[0].cmode = s->ch[1].cmode = cmode < 0 ? 2 : cmode; + s->ch[0].cmode2 = cmode < 0 ? FFABS(cmode) : 0; + s->ch[1].cmode2 = cmode < 0 ? FFABS(cmode) : 0; + av_log(avctx, AV_LOG_DEBUG, "cmode: %d\n", cmode); + + return 0; +} + +static void model64_init(Model64 *m, unsigned bits) +{ + unsigned x; + + m->bits = bits; + m->size = 64; + m->zero[0] = 1; + + x = (1 << (bits >> 1)) + 3; + x = FFMIN(x, 20); + + m->zero[1] = x; + m->sign[0] = 1; + m->sign[1] = 1; + + for (int i = 0; i < FF_ARRAY_ELEMS(m->val4); i++) { + m->val4[i] = 4; + m->val1[i] = 1; + } +} + +static int chctx_init(RKAContext *s, ChContext *c, + int sample_rate, int bps) +{ + int ret; + + memset(c->buf0, 0, sizeof(c->buf0)); + memset(c->buf1, 0, sizeof(c->buf1)); + + c->filt_size = &s->filt_size; + c->filt_bits = &s->filt_bits; + + c->bprob[0] = s->bprob[0]; + c->bprob[1] = s->bprob[1]; + + c->srate_pad = ((int64_t)sample_rate << 13) / 44100 & 0xFFFFFFFCU; + c->pos_idx = 1; + + for (int i = 0; i < FF_ARRAY_ELEMS(s->bprob[0]); i++) + c->bprob[0][i] = c->bprob[1][i] = 1; + + for (int i = 0; i < 11; i++) { + ret = adaptive_model_init(&c->coeff_bits[i], 32); + if (ret < 0) + return ret; + + model64_init(&c->mdl64[0][i], i); + model64_init(&c->mdl64[1][i], i); + model64_init(&c->mdl64[2][i], i+1); + model64_init(&c->mdl64[3][i], i+1); + } + + ret = adaptive_model_init(c->filt_size, 256); + if (ret < 0) + return ret; + ret = adaptive_model_init(c->filt_bits, 16); + if (ret < 0) + return ret; + ret = adaptive_model_init(&c->position, 16); + if (ret < 0) + return ret; + ret = adaptive_model_init(&c->nb_segments, 8); + if (ret < 0) + return ret; + return adaptive_model_init(&c->fshift, 32); +} + +static void init_acoder(ACoder *ac) +{ + ac->low = 0x0; + ac->high = 0xffffffff; + ac->value = bytestream2_get_be32(&ac->gb); +} + +static int ac_decode_bool(ACoder *ac, int freq1, int freq2) +{ + unsigned help, add, high, value; + int low; + + low = ac->low; + help = ac->high / (unsigned)(freq2 + freq1); + value = ac->value; + add = freq1 * help; + ac->high = help; + + if (value - low >= add) { + ac->low = low = add + low; + ac->high = high = freq2 * help; + while (1) { + if ((low ^ (high + low)) > 0xFFFFFF) { + if (high > 0xFFFF) + return 1; + ac->high = (uint16_t)-(int16_t)low; + } + + if (bytestream2_get_bytes_left(&ac->gb) <= 0) + break; + ac->value = bytestream2_get_byteu(&ac->gb) | (ac->value << 8); + ac->high = high = ac->high << 8; + low = ac->low = ac->low << 8; + } + return -1; + } + + ac->high = add; + while (1) { + if ((low ^ (add + low)) > 0xFFFFFF) { + if (add > 0xFFFF) + return 0; + ac->high = (uint16_t)-(int16_t)low; + } + + if (bytestream2_get_bytes_left(&ac->gb) <= 0) + break; + ac->value = bytestream2_get_byteu(&ac->gb) | (ac->value << 8); + ac->high = add = ac->high << 8; + low = ac->low = ac->low << 8; + } + return -1; +} + +static int decode_bool(ACoder *ac, ChContext *c, int idx) +{ + uint32_t x; + int b; + + x = c->bprob[0][idx]; + if (x + c->bprob[1][idx] > 4096) { + c->bprob[0][idx] = (x >> 1) + 1; + c->bprob[1][idx] = (c->bprob[1][idx] >> 1) + 1; + } + + b = ac_decode_bool(ac, c->bprob[0][idx], c->bprob[1][idx]); + if (b < 0) + return b; + + c->bprob[b][idx]++; + + return b; +} + +static int ac_get_freq(ACoder *ac, unsigned freq, int *result) +{ + uint32_t new_high; + + if (freq == 0) + return -1; + + new_high = ac->high / freq; + ac->high = new_high; + + if (new_high == 0) + return -1; + + *result = (ac->value - ac->low) / new_high; + + return 0; +} + +static int ac_update(ACoder *ac, int freq, int mul) +{ + uint32_t low, high; + + low = ac->low = ac->high * freq + ac->low; + high = ac->high = ac->high * mul; + + while (1) { + if (((high + low) ^ low) > 0xffffff) { + if (high > 0xffff) + return 0; + ac->high = (uint16_t)-(int16_t)low; + } + + if (bytestream2_get_bytes_left(&ac->gb) <= 0) + break; + + ac->value = (ac->value << 8) | bytestream2_get_byteu(&ac->gb); + low = ac->low = ac->low << 8; + high = ac->high = ac->high << 8; + } + + return -1; +} + +static void amdl_update_prob(AdaptiveModel *am, int val, int diff) +{ + am->aprob0 += diff; + if (val <= 0) { + am->prob[0][0] += diff; + } else { + do { + am->prob[0][val] += diff; + val += (val & -val); + } while (val < am->buf_size); + } +} + +static void update_ch_subobj(AdaptiveModel *am) +{ + int idx2, idx = am->buf_size - 1; + + if (idx >= 0) { + do { + uint16_t *prob = am->prob[0]; + int diff, prob_idx = prob[idx]; + + idx2 = idx - 1; + if (idx > 0) { + int idx3 = idx - 1; + + if ((idx2 & idx) != idx2) { + do { + prob_idx -= prob[idx3]; + idx3 &= idx3 - 1; + } while ((idx2 & idx) != idx3); + } + } + + diff = ((prob_idx > 0) - prob_idx) >> 1; + amdl_update_prob(am, idx, diff); + idx--; + } while (idx2 >= 0); + } + + if (am->sum < 8000) + am->sum += 200; + + am->aprob1 = (am->aprob1 + 1) >> 1; +} + +static int amdl_decode_int(AdaptiveModel *am, ACoder *ac, unsigned *dst, unsigned size) +{ + unsigned freq, size2, val, mul; + int j; + + size = FFMIN(size, am->buf_size - 1); + + if (am->aprob0 >= am->sum) + update_ch_subobj(am); + + if (am->aprob1 && (am->total == am->buf_size || + ac_decode_bool(ac, am->aprob0, am->aprob1) == 0)) { + if (am->total <= 1) { + dst[0] = am->last; + amdl_update_prob(am, dst[0], 1); + return 0; + } + if (size == am->buf_size - 1) { + freq = am->aprob0; + } else { + freq = am->prob[0][0]; + for (int j = size; j > 0; j &= (j - 1) ) + freq += am->prob[0][j]; + } + ac_get_freq(ac, freq, &freq); + size2 = am->buf_size >> 1; + val = am->prob[0][0]; + if (freq >= val) { + int sum = 0; + for (j = freq - val; size2; size2 >>= 1) { + unsigned v = am->prob[0][size2 + sum]; + if (j >= v) { + sum += size2; + j -= v; + } + } + freq -= j; + val = sum + 1; + } else { + freq = 0; + val = 0; + } + dst[0] = val; + mul = am->prob[0][val]; + if (val > 0) { + for (int k = val - 1; (val & (val - 1)) != k; k &= k - 1) + mul -= am->prob[0][k]; + } + ac_update(ac, freq, mul); + amdl_update_prob(am, dst[0], 1); + return 0; + } + am->aprob1++; + if (size == am->buf_size - 1) { + ac_get_freq(ac, am->buf_size - am->total, &val); + } else { + freq = 1; + for (dst[0] = 0; dst[0] < size; dst[0]++) { + if (!am->prob[1][dst[0]]) + freq++; + } + ac_get_freq(ac, freq, &val); + } + freq = 0; + dst[0] = 0; + if (val > 0 && am->buf_size > 0) { + for (dst[0] = 0; dst[0] < size & freq < val; dst[0]++) { + if (!am->prob[1][dst[0]]) + freq++; + } + } + if (am->prob[1][dst[0]]) { + do { + val = dst[0]++; + } while (val + 1 < am->buf_size && am->prob[1][val + 1]); + } + ac_update(ac, freq, 1); + am->prob[1][dst[0]]++; + am->total++; + amdl_update_prob(am, dst[0], 1); + am->last = dst[0]; + + return 0; +} + +static int decode_filt_coeffs(RKAContext *s, ChContext *ctx, ACoder *ac, FiltCoeffs *dst) +{ + unsigned val, bits; + int idx = 0; + + if (amdl_decode_int(ctx->filt_size, ac, &dst->size, 256) < 0) + return -1; + + if (dst->size == 0) + return 0; + + if (amdl_decode_int(ctx->filt_bits, ac, &bits, 10) < 0) + return -1; + + do { + if (((idx == 8) || (idx == 20)) && (0 < bits)) + bits--; + + if (bits > 10) + return -1; + + if (amdl_decode_int(&ctx->coeff_bits[bits], ac, &val, 31) < 0) + return -1; + + if (val == 31) { + ac_get_freq(ac, 65536, &val); + ac_update(ac, val, 1); + } + + if (val == 0) { + dst->coeffs[idx++] = 0; + } else { + unsigned freq = 0; + int sign; + + if (bits > 0) { + ac_get_freq(ac, 1 << bits, &freq); + ac_update(ac, freq, 1); + } + dst->coeffs[idx] = freq + 1 + ((val - 1U) << bits); + sign = decode_bool(ac, ctx, idx); + if (sign < 0) + return -1; + if (sign == 1) + dst->coeffs[idx] = -dst->coeffs[idx]; + idx++; + } + } while (idx < dst->size); + + return 0; +} + +static int ac_dec_bit(ACoder *ac) +{ + uint32_t high, low; + + low = ac->low; + ac->high = high = ac->high >> 1; + if (ac->value - low < high) { + do { + if (((high + low) ^ low) > 0xffffff) { + if (high > 0xffff) + return 0; + ac->high = (uint16_t)-(int16_t)low; + } + + if (bytestream2_get_bytes_left(&ac->gb) <= 0) + break; + + ac->value = (ac->value << 8) | bytestream2_get_byteu(&ac->gb); + ac->high = high = ac->high << 8; + ac->low = low = ac->low << 8; + } while (1); + + return -1; + } + ac->low = low = low + high; + do { + if (((high + low) ^ low) > 0xffffff) { + if (high > 0xffff) + return 1; + ac->high = (uint16_t)-(int16_t)low; + } + + if (bytestream2_get_bytes_left(&ac->gb) <= 0) + break; + + ac->value = (ac->value << 8) | bytestream2_get_byteu(&ac->gb); + ac->high = high = ac->high << 8; + ac->low = low = ac->low << 8; + } while (1); + + return -1; +} + +static int mdl64_decode(ACoder *ac, Model64 *ctx, int *dst) +{ + int sign, idx, bits; + unsigned val = 0; + + if (ctx->zero[0] + ctx->zero[1] > 4000U) { + ctx->zero[0] = (ctx->zero[0] >> 1) + 1; + ctx->zero[1] = (ctx->zero[1] >> 1) + 1; + } + if (ctx->sign[0] + ctx->sign[1] > 4000U) { + ctx->sign[0] = (ctx->sign[0] >> 1) + 1; + ctx->sign[1] = (ctx->sign[1] >> 1) + 1; + } + sign = ac_decode_bool(ac, ctx->zero[0], ctx->zero[1]); + if (sign == 0) { + ctx->zero[0] += 2; + dst[0] = 0; + return 0; + } else if (sign < 0) { + return -1; + } + + ctx->zero[1] += 2; + sign = ac_decode_bool(ac, ctx->sign[0], ctx->sign[1]); + if (sign < 0) + return -1; + ctx->sign[sign]++; + bits = ctx->bits; + if (bits > 0) { + if (bits < 13) { + ac_get_freq(ac, 1 << bits, &val); + ac_update(ac, val, 1); + } else { + int hbits = bits / 2; + ac_get_freq(ac, 1 << hbits, &val); + ac_update(ac, val, 1); + ac_get_freq(ac, 1 << (ctx->bits - (hbits)), &bits); + ac_update(ac, val, 1); + val += (bits << hbits); + } + } + bits = ctx->size; + idx = 0; + if (bits >= 0) { + do { + uint16_t *val4 = ctx->val4; + int b; + + if (val4[idx] + ctx->val1[idx] > 2000U) { + val4[idx] = (val4[idx] >> 1) + 1; + ctx->val1[idx] = (ctx->val1[idx] >> 1) + 1; + } + b = ac_decode_bool(ac, ctx->val4[idx], ctx->val1[idx]); + if (b == 1) { + ctx->val1[idx] += 4; + break; + } else if (b < 0) { + return -1; + } + ctx->val4[idx] += 4; + idx++; + } while (idx <= ctx->size); + bits = ctx->size; + if (idx <= bits) { + dst[0] = val + 1 + (idx << ctx->bits); + if (sign) + dst[0] = -dst[0]; + return 0; + } + } + bits++; + while (ac_dec_bit(ac) == 0) + bits += 64; + ac_get_freq(ac, 64, &idx); + ac_update(ac, idx, 1); + idx += bits; + dst[0] = val + 1 + (idx << ctx->bits); + if (sign) + dst[0] = -dst[0]; + + return 0; +} + +static const uint8_t tab[16] = { + 0, 3, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 +}; + +static int decode_filter(RKAContext *s, ChContext *ctx, ACoder *ac, int off, unsigned size) +{ + FiltCoeffs filt; + Model64 *mdl64; + int m = 0, split, val, last_val = 0, ret; + unsigned idx = 3, bits = 0; + + if (ctx->cmode == 0) { + if (amdl_decode_int(&ctx->fshift, ac, &bits, 15) < 0) + return -1; + bits &= 31U; + } + + ret = decode_filt_coeffs(s, ctx, ac, &filt); + if (ret < 0) + return ret; + + if (size < 512) + split = size / 2; + else + split = size >> 4; + + if (size <= 1) + return 0; + + for (int x = 0; x < size;) { + if (amdl_decode_int(&ctx->position, ac, &idx, 10) < 0) + return -1; + + idx = (ctx->pos_idx + idx) % 11; + ctx->pos_idx = idx; + + for (int y = 0; y < FFMIN(split, size - x); y++, off++) { + int midx, shift = idx, *src, sum = 16; + + if (off >= FF_ARRAY_ELEMS(ctx->buf0)) + return -1; + + midx = FFABS(last_val) >> shift; + if (midx >= 15) { + mdl64 = &ctx->mdl64[3][idx]; + } else if (midx >= 7) { + mdl64 = &ctx->mdl64[2][idx]; + } else if (midx >= 4) { + mdl64 = &ctx->mdl64[1][idx]; + } else { + mdl64 = &ctx->mdl64[0][idx]; + } + ret = mdl64_decode(ac, mdl64, &val); + if (ret < 0) + return -1; + last_val = val; + src = &ctx->buf1[off + -1]; + for (int i = 0; i < filt.size && i < 15; i++) + sum += filt.coeffs[i] * (unsigned)src[-i]; + sum = sum * 2U; + for (int i = 15; i < filt.size; i++) + sum += filt.coeffs[i] * (unsigned)src[-i]; + sum = sum >> 6; + if (ctx->cmode == 0) { + if (bits == 0) { + ctx->buf1[off] = sum + val; + } else { + ctx->buf1[off] = (val + (sum >> bits)) * (1U << bits) + + (((1U << bits) - 1U) & ctx->buf1[off + -1]); + } + ctx->buf0[off] = ctx->buf1[off] + (unsigned)ctx->buf0[off + -1]; + } else { + val *= 1U << ctx->cmode; + sum += ctx->buf0[off + -1] + (unsigned)val; + switch (s->bps) { + case 16: sum = av_clip_int16(sum); break; + case 8: sum = av_clip_int8(sum); break; + } + ctx->buf1[off] = sum - ctx->buf0[off + -1]; + ctx->buf0[off] = sum; + m += (unsigned)FFABS(ctx->buf1[off]); + } + } + if (ctx->cmode2 != 0) { + int sum = 0; + for (int i = (signed)((unsigned)m << 6) / split; i > 0; i = i >> 1) + sum++; + sum = sum - (ctx->cmode2 + 7); + ctx->cmode = FFMAX(sum, tab[ctx->cmode2]); + } + + x += split; + } + + return 0; +} + +static int decode_samples(AVCodecContext *avctx, ACoder *ac, ChContext *ctx, int offset) +{ + RKAContext *s = avctx->priv_data; + int segment_size, offset2, mode, ret; + + ret = amdl_decode_int(&ctx->nb_segments, ac, &mode, 5); + if (ret < 0) + return ret; + + if (mode == 5) { + ret = ac_get_freq(ac, ctx->srate_pad >> 2, &segment_size); + if (ret < 0) + return ret; + ac_update(ac, segment_size, 1); + segment_size *= 4; + ret = decode_filter(s, ctx, ac, offset, segment_size); + if (ret < 0) + return ret; + } else { + segment_size = ctx->srate_pad; + + if (mode) { + if (mode > 2) { + ret = decode_filter(s, ctx, ac, offset, segment_size / 4); + if (ret < 0) + return ret; + offset2 = segment_size / 4 + offset; + ret = decode_filter(s, ctx, ac, offset2, segment_size / 4); + if (ret < 0) + return ret; + offset2 = segment_size / 4 + offset2; + } else { + ret = decode_filter(s, ctx, ac, offset, segment_size / 2); + if (ret < 0) + return ret; + offset2 = segment_size / 2 + offset; + } + if (mode & 1) { + ret = decode_filter(s, ctx, ac, offset2, segment_size / 2); + if (ret < 0) + return ret; + } else { + ret = decode_filter(s, ctx, ac, offset2, segment_size / 4); + if (ret < 0) + return ret; + ret = decode_filter(s, ctx, ac, segment_size / 4 + offset2, segment_size / 4); + if (ret < 0) + return ret; + } + } else { + ret = decode_filter(s, ctx, ac, offset, ctx->srate_pad); + if (ret < 0) + return ret; + } + } + + return segment_size; +} + +static int decode_ch_samples(AVCodecContext *avctx, ChContext *c) +{ + RKAContext *s = avctx->priv_data; + ACoder *ac = &s->ac; + int nb_decoded = 0; + + if (bytestream2_get_bytes_left(&ac->gb) <= 0) + return 0; + + memmove(c->buf0, &c->buf0[c->last_nb_decoded], 2560 * sizeof(*c->buf0)); + memmove(c->buf1, &c->buf1[c->last_nb_decoded], 2560 * sizeof(*c->buf1)); + + nb_decoded = decode_samples(avctx, ac, c, 2560); + if (nb_decoded < 0) + return nb_decoded; + c->last_nb_decoded = nb_decoded; + + return nb_decoded; +} + +static int rka_decode_frame(AVCodecContext *avctx, AVFrame *frame, + int *got_frame_ptr, AVPacket *avpkt) +{ + RKAContext *s = avctx->priv_data; + ACoder *ac = &s->ac; + int ret; + + bytestream2_init(&ac->gb, avpkt->data, avpkt->size); + init_acoder(ac); + + for (int ch = 0; ch < s->channels; ch++) { + ret = chctx_init(s, &s->ch[ch], avctx->sample_rate, + avctx->bits_per_raw_sample); + if (ret < 0) + return ret; + } + + frame->nb_samples = s->frame_samples; + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) + return ret; + + if (s->channels == 2 && s->correlated) { + int16_t *l16 = (int16_t *)frame->extended_data[0]; + int16_t *r16 = (int16_t *)frame->extended_data[1]; + uint8_t *l8 = frame->extended_data[0]; + uint8_t *r8 = frame->extended_data[1]; + + for (int n = 0; n < frame->nb_samples;) { + ret = decode_ch_samples(avctx, &s->ch[0]); + if (ret == 0) { + frame->nb_samples = n; + break; + } + if (ret < 0 || n + ret > frame->nb_samples) + return AVERROR_INVALIDDATA; + + ret = decode_ch_samples(avctx, &s->ch[1]); + if (ret == 0) { + frame->nb_samples = n; + break; + } + if (ret < 0 || n + ret > frame->nb_samples) + return AVERROR_INVALIDDATA; + + switch (avctx->sample_fmt) { + case AV_SAMPLE_FMT_S16P: + for (int i = 0; i < ret; i++) { + int l = s->ch[0].buf0[2560 + i]; + int r = s->ch[1].buf0[2560 + i]; + + l16[n + i] = (l * 2 + r + 1) >> 1; + r16[n + i] = (l * 2 - r + 1) >> 1; + } + break; + case AV_SAMPLE_FMT_U8P: + for (int i = 0; i < ret; i++) { + int l = s->ch[0].buf0[2560 + i]; + int r = s->ch[1].buf0[2560 + i]; + + l8[n + i] = ((l * 2 + r + 1) >> 1) + 0x7f; + r8[n + i] = ((l * 2 - r + 1) >> 1) + 0x7f; + } + break; + default: + return AVERROR_INVALIDDATA; + } + + n += ret; + } + } else { + for (int n = 0; n < frame->nb_samples;) { + for (int ch = 0; ch < s->channels; ch++) { + int16_t *m16 = (int16_t *)frame->data[ch]; + uint8_t *m8 = frame->data[ch]; + + ret = decode_ch_samples(avctx, &s->ch[ch]); + if (ret == 0) { + frame->nb_samples = n; + break; + } + + if (ret < 0 || n + ret > frame->nb_samples) + return AVERROR_INVALIDDATA; + + switch (avctx->sample_fmt) { + case AV_SAMPLE_FMT_S16P: + for (int i = 0; i < ret; i++) { + int m = s->ch[ch].buf0[2560 + i]; + + m16[n + i] = m; + } + break; + case AV_SAMPLE_FMT_U8P: + for (int i = 0; i < ret; i++) { + int m = s->ch[ch].buf0[2560 + i]; + + m8[n + i] = m + 0x7f; + } + break; + default: + return AVERROR_INVALIDDATA; + } + } + + n += ret; + } + } + + *got_frame_ptr = 1; + + return avpkt->size; +} + +static av_cold int rka_decode_close(AVCodecContext *avctx) +{ + RKAContext *s = avctx->priv_data; + + for (int ch = 0; ch < 2; ch++) { + ChContext *c = &s->ch[ch]; + + for (int i = 0; i < 11; i++) + adaptive_model_free(&c->coeff_bits[i]); + + adaptive_model_free(&c->position); + adaptive_model_free(&c->nb_segments); + adaptive_model_free(&c->fshift); + } + + adaptive_model_free(&s->filt_size); + adaptive_model_free(&s->filt_bits); + + return 0; +} + +const FFCodec ff_rka_decoder = { + .p.name = "rka", + CODEC_LONG_NAME("RKA (RK Audio)"), + .p.type = AVMEDIA_TYPE_AUDIO, + .p.id = AV_CODEC_ID_RKA, + .priv_data_size = sizeof(RKAContext), + .init = rka_decode_init, + .close = rka_decode_close, + FF_CODEC_DECODE_CB(rka_decode_frame), + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_CHANNEL_CONF, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, +}; diff --git a/libavcodec/rkmppdec.c b/libavcodec/rkmppdec.c index 8bf7c6ed163..5768568b002 100644 --- a/libavcodec/rkmppdec.c +++ b/libavcodec/rkmppdec.c @@ -404,8 +404,10 @@ static int rkmpp_retrieve_frame(AVCodecContext *avctx, AVFrame *frame) frame->colorspace = mpp_frame_get_colorspace(mppframe); mode = mpp_frame_get_mode(mppframe); - frame->interlaced_frame = ((mode & MPP_FRAME_FLAG_FIELD_ORDER_MASK) == MPP_FRAME_FLAG_DEINTERLACED); - frame->top_field_first = ((mode & MPP_FRAME_FLAG_FIELD_ORDER_MASK) == MPP_FRAME_FLAG_TOP_FIRST); + if ((mode & MPP_FRAME_FLAG_FIELD_ORDER_MASK) == MPP_FRAME_FLAG_DEINTERLACED) + frame->flags |= AV_FRAME_FLAG_INTERLACED; + if ((mode & MPP_FRAME_FLAG_FIELD_ORDER_MASK) == MPP_FRAME_FLAG_TOP_FIRST) + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; mppformat = mpp_frame_get_fmt(mppframe); drmformat = rkmpp_get_frameformat(mppformat); diff --git a/libavcodec/roqaudioenc.c b/libavcodec/roqaudioenc.c index f0254adc700..81dccd09b58 100644 --- a/libavcodec/roqaudioenc.c +++ b/libavcodec/roqaudioenc.c @@ -174,7 +174,7 @@ static int roq_dpcm_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, /* Write the actual samples */ for (i = 0; i < data_size; i++) - *out++ = dpcm_predict(&context->lastSample[i & 1], *in++); + *out++ = dpcm_predict(&context->lastSample[(i & 1) & stereo], *in++); avpkt->pts = context->input_frames <= 7 ? context->first_pts : frame->pts; avpkt->duration = data_size / channels; diff --git a/libavcodec/roqvideoenc.c b/libavcodec/roqvideoenc.c index 273686a1476..c25aa39b735 100644 --- a/libavcodec/roqvideoenc.c +++ b/libavcodec/roqvideoenc.c @@ -1122,7 +1122,7 @@ const FFCodec ff_roq_encoder = { CODEC_LONG_NAME("id RoQ video"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_ROQ, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(RoqEncContext), .init = roq_encode_init, FF_CODEC_ENCODE_CB(roq_encode_frame), diff --git a/libavcodec/rpzaenc.c b/libavcodec/rpzaenc.c index 1ea579d2d38..da9500e4242 100644 --- a/libavcodec/rpzaenc.c +++ b/libavcodec/rpzaenc.c @@ -873,7 +873,7 @@ const FFCodec ff_rpza_encoder = { CODEC_LONG_NAME("QuickTime video (RPZA)"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_RPZA, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(RpzaContext), .p.priv_class = &rpza_class, .init = rpza_encode_init, diff --git a/libavcodec/rscc.c b/libavcodec/rscc.c index 61a25df3821..ace9aeeb405 100644 --- a/libavcodec/rscc.c +++ b/libavcodec/rscc.c @@ -339,14 +339,21 @@ static int rscc_decode_frame(AVCodecContext *avctx, AVFrame *frame, /* Keyframe when the number of pixels updated matches the whole surface */ if (pixel_size == ctx->inflated_size) { frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; } else { frame->pict_type = AV_PICTURE_TYPE_P; } /* Palette handling */ if (avctx->pix_fmt == AV_PIX_FMT_PAL8) { - frame->palette_has_changed = ff_copy_palette(ctx->palette, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS + frame->palette_has_changed = +#endif + ff_copy_palette(ctx->palette, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_ENABLE_DEPRECATION_WARNINGS +#endif memcpy(frame->data[1], ctx->palette, AVPALETTE_SIZE); } // We only return a picture when enough of it is undamaged, this avoids copying nearly broken frames around diff --git a/libavcodec/rtv1.c b/libavcodec/rtv1.c new file mode 100644 index 00000000000..4b202e6a214 --- /dev/null +++ b/libavcodec/rtv1.c @@ -0,0 +1,148 @@ +/* + * RTV1 decoder + * Copyright (c) 2023 Paul B Mahol + * + * 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 +#include + +#include "avcodec.h" +#include "bytestream.h" +#include "codec_internal.h" +#include "decode.h" +#include "texturedsp.h" +#include "thread.h" + +static av_cold int decode_init(AVCodecContext *avctx) +{ + TextureDSPContext *dsp = avctx->priv_data; + avctx->pix_fmt = AV_PIX_FMT_BGR0; + ff_texturedsp_init(dsp); + return 0; +} + +static int decode_rtv1(GetByteContext *gb, uint8_t *dst, ptrdiff_t linesize, + int width, int height, int flag, + int (*dxt1_block)(uint8_t *dst, ptrdiff_t stride, const uint8_t *block)) +{ + uint8_t block[8] = { 0 }; + int run = 0; + + for (int y = 0; y < height; y += 4) { + for (int x = 0; x < width * 4; x += 16) { + int mode = 0; + + if (run && --run > 0) { + dxt1_block(dst + x, linesize, block); + } else { + int a, b; + + if (bytestream2_get_bytes_left(gb) < 4) + break; + + a = bytestream2_get_le16u(gb); + b = bytestream2_get_le16u(gb); + if (a == b && flag) { + AV_WL32(block + 4, 0); + } else if (a == 1 && b == 0xffff) { + mode = 1; + } else if (b && a == 0) { + run = b; + } else { + AV_WL16(block, a); + AV_WL16(block + 2, b); + AV_WL32(block + 4, bytestream2_get_le32(gb)); + } + if (run && !mode) { + dxt1_block(dst + x, linesize, block); + } else if (!mode) { + AV_WL16(block, a); + AV_WL16(block + 2, b); + dxt1_block(dst + x, linesize, block); + } else { + if (bytestream2_get_bytes_left(gb) < 12 * 4) + break; + + for (int by = 0; by < 4; by++) { + for (int bx = 0; bx < 4; bx++) + AV_WL32(dst + x + bx * 4 + by * linesize, bytestream2_get_le24u(gb)); + } + } + } + } + + dst += linesize * 4; + } + + return 0; +} + +static int decode_frame(AVCodecContext *avctx, AVFrame *p, + int *got_frame, AVPacket *avpkt) +{ + int ret, width, height, flags; + TextureDSPContext *dsp = avctx->priv_data; + GetByteContext gb; + ptrdiff_t linesize; + uint8_t *dst; + + if (avpkt->size < 22) + return AVERROR_INVALIDDATA; + + bytestream2_init(&gb, avpkt->data, avpkt->size); + + if (bytestream2_get_le32(&gb) != MKTAG('D','X','T','1')) + return AVERROR_INVALIDDATA; + flags = bytestream2_get_le32(&gb); + + width = bytestream2_get_le32(&gb); + height = bytestream2_get_le32(&gb); + ret = ff_set_dimensions(avctx, FFALIGN(width, 4), FFALIGN(height, 4)); + if (ret < 0) + return ret; + + avctx->width = width; + avctx->height = height; + + if ((ret = ff_thread_get_buffer(avctx, p, 0)) < 0) + return ret; + + dst = p->data[0] + p->linesize[0] * (avctx->coded_height - 1); + linesize = -p->linesize[0]; + + decode_rtv1(&gb, dst, linesize, width, height, flags, dsp->dxt1_block); + + p->pict_type = AV_PICTURE_TYPE_I; + p->flags |= AV_FRAME_FLAG_KEY; + + *got_frame = 1; + + return avpkt->size; +} + +const FFCodec ff_rtv1_decoder = { + .p.name = "rtv1", + CODEC_LONG_NAME("RTV1 (RivaTuner Video)"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_RTV1, + .priv_data_size = sizeof(TextureDSPContext), + .init = decode_init, + FF_CODEC_DECODE_CB(decode_frame), + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, +}; diff --git a/libavcodec/rv10.c b/libavcodec/rv10.c index a45683228e3..bb1ead5002c 100644 --- a/libavcodec/rv10.c +++ b/libavcodec/rv10.c @@ -587,10 +587,7 @@ static int rv10_decode_packet(AVCodecContext *avctx, const uint8_t *buf, static int get_slice_offset(AVCodecContext *avctx, const uint8_t *buf, int n) { - if (avctx->slice_count) - return avctx->slice_offset[n]; - else - return AV_RL32(buf + n * 8); + return AV_RL32(buf + n * 8); } static int rv10_decode_frame(AVCodecContext *avctx, AVFrame *pict, @@ -603,28 +600,25 @@ static int rv10_decode_frame(AVCodecContext *avctx, AVFrame *pict, int slice_count; const uint8_t *slices_hdr = NULL; - ff_dlog(avctx, "*****frame %d size=%d\n", avctx->frame_number, buf_size); + ff_dlog(avctx, "*****frame %"PRId64" size=%d\n", avctx->frame_num, buf_size); /* no supplementary picture */ if (buf_size == 0) { return 0; } - if (!avctx->slice_count) { - slice_count = (*buf++) + 1; - buf_size--; + slice_count = (*buf++) + 1; + buf_size--; - if (!slice_count || buf_size <= 8 * slice_count) { - av_log(avctx, AV_LOG_ERROR, "Invalid slice count: %d.\n", - slice_count); - return AVERROR_INVALIDDATA; - } + if (!slice_count || buf_size <= 8 * slice_count) { + av_log(avctx, AV_LOG_ERROR, "Invalid slice count: %d.\n", + slice_count); + return AVERROR_INVALIDDATA; + } - slices_hdr = buf + 4; - buf += 8 * slice_count; - buf_size -= 8 * slice_count; - } else - slice_count = avctx->slice_count; + slices_hdr = buf + 4; + buf += 8 * slice_count; + buf_size -= 8 * slice_count; for (i = 0; i < slice_count; i++) { unsigned offset = get_slice_offset(avctx, slices_hdr, i); diff --git a/libavcodec/rv10enc.c b/libavcodec/rv10enc.c index d0704c5a4b7..8a405b86867 100644 --- a/libavcodec/rv10enc.c +++ b/libavcodec/rv10enc.c @@ -31,7 +31,7 @@ #include "put_bits.h" #include "rv10enc.h" -int ff_rv10_encode_picture_header(MpegEncContext *s, int picture_number) +int ff_rv10_encode_picture_header(MpegEncContext *s) { int full_frame= 0; @@ -71,6 +71,7 @@ const FFCodec ff_rv10_encoder = { .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_RV10, .p.priv_class = &ff_mpv_enc_class, + .p.capabilities = AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(MpegEncContext), .init = ff_mpv_encode_init, FF_CODEC_ENCODE_CB(ff_mpv_encode_picture), diff --git a/libavcodec/rv10enc.h b/libavcodec/rv10enc.h index 66672f80876..fc3665e8395 100644 --- a/libavcodec/rv10enc.h +++ b/libavcodec/rv10enc.h @@ -23,7 +23,7 @@ #include "mpegvideo.h" -int ff_rv10_encode_picture_header(MpegEncContext *s, int picture_number); -void ff_rv20_encode_picture_header(MpegEncContext *s, int picture_number); +int ff_rv10_encode_picture_header(MpegEncContext *s); +void ff_rv20_encode_picture_header(MpegEncContext *s); #endif /* AVCODEC_RV10ENC_H */ diff --git a/libavcodec/rv20enc.c b/libavcodec/rv20enc.c index a6bacacb48a..dc26877d5e6 100644 --- a/libavcodec/rv20enc.c +++ b/libavcodec/rv20enc.c @@ -34,12 +34,12 @@ #include "put_bits.h" #include "rv10enc.h" -void ff_rv20_encode_picture_header(MpegEncContext *s, int picture_number){ +void ff_rv20_encode_picture_header(MpegEncContext *s) { put_bits(&s->pb, 2, s->pict_type); //I 0 vs. 1 ? put_bits(&s->pb, 1, 0); /* unknown bit */ put_bits(&s->pb, 5, s->qscale); - put_sbits(&s->pb, 8, picture_number); //FIXME wrong, but correct is not known + put_sbits(&s->pb, 8, s->picture_number); //FIXME wrong, but correct is not known s->mb_x= s->mb_y= 0; ff_h263_encode_mba(s); @@ -68,6 +68,7 @@ const FFCodec ff_rv20_encoder = { .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_RV20, .p.priv_class = &ff_mpv_enc_class, + .p.capabilities = AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(MpegEncContext), .init = ff_mpv_encode_init, FF_CODEC_ENCODE_CB(ff_mpv_encode_picture), diff --git a/libavcodec/rv34.c b/libavcodec/rv34.c index be188edc475..51f18147af8 100644 --- a/libavcodec/rv34.c +++ b/libavcodec/rv34.c @@ -1549,8 +1549,7 @@ int ff_rv34_decode_update_thread_context(AVCodecContext *dst, const AVCodecConte static int get_slice_offset(AVCodecContext *avctx, const uint8_t *buf, int n, int slice_count, int buf_size) { if (n < slice_count) { - if(avctx->slice_count) return avctx->slice_offset[n]; - else return AV_RL32(buf + n*8 - 4) == 1 ? AV_RL32(buf + n*8) : AV_RB32(buf + n*8); + return AV_RL32(buf + n*8 - 4) == 1 ? AV_RL32(buf + n*8) : AV_RB32(buf + n*8); } else return buf_size; } @@ -1623,13 +1622,10 @@ int ff_rv34_decode_frame(AVCodecContext *avctx, AVFrame *pict, return 0; } - if(!avctx->slice_count){ - slice_count = (*buf++) + 1; - slices_hdr = buf + 4; - buf += 8 * slice_count; - buf_size -= 1 + 8 * slice_count; - }else - slice_count = avctx->slice_count; + slice_count = (*buf++) + 1; + slices_hdr = buf + 4; + buf += 8 * slice_count; + buf_size -= 1 + 8 * slice_count; offset = get_slice_offset(avctx, slices_hdr, 0, slice_count, buf_size); //parse first slice header to check whether this frame can be decoded @@ -1696,6 +1692,8 @@ int ff_rv34_decode_frame(AVCodecContext *avctx, AVFrame *pict, int i; r->tmp_b_block_base = av_malloc(s->linesize * 48); + if (!r->tmp_b_block_base) + return AVERROR(ENOMEM); for (i = 0; i < 2; i++) r->tmp_b_block_y[i] = r->tmp_b_block_base + i * 16 * s->linesize; diff --git a/libavcodec/s302menc.c b/libavcodec/s302menc.c index 3bd657f9458..4b8996f9ac8 100644 --- a/libavcodec/s302menc.c +++ b/libavcodec/s302menc.c @@ -176,7 +176,8 @@ const FFCodec ff_s302m_encoder = { .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_S302M, .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_EXPERIMENTAL | - AV_CODEC_CAP_VARIABLE_FRAME_SIZE, + AV_CODEC_CAP_VARIABLE_FRAME_SIZE | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(S302MEncContext), .init = s302m_encode_init, FF_CODEC_ENCODE_CB(s302m_encode2_frame), diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c index bec3c770ec4..b70daab7ac9 100644 --- a/libavcodec/sanm.c +++ b/libavcodec/sanm.c @@ -1484,11 +1484,13 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, return ret; ctx->rotate_code = header.rotate_code; - if ((ctx->frame->key_frame = !header.seq_num)) { + if (!header.seq_num) { + ctx->frame->flags |= AV_FRAME_FLAG_KEY; ctx->frame->pict_type = AV_PICTURE_TYPE_I; fill_frame(ctx->frm1, ctx->npixels, header.bg_color); fill_frame(ctx->frm2, ctx->npixels, header.bg_color); } else { + ctx->frame->flags &= ~AV_FRAME_FLAG_KEY; ctx->frame->pict_type = AV_PICTURE_TYPE_P; } diff --git a/libavcodec/sbcenc.c b/libavcodec/sbcenc.c index 721c97e1ea3..fccb0e3ea3f 100644 --- a/libavcodec/sbcenc.c +++ b/libavcodec/sbcenc.c @@ -348,7 +348,8 @@ const FFCodec ff_sbc_encoder = { CODEC_LONG_NAME("SBC (low-complexity subband codec)"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_SBC, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SMALL_LAST_FRAME, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SMALL_LAST_FRAME | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(SBCEncContext), .init = sbc_encode_init, FF_CODEC_ENCODE_CB(sbc_encode_frame), diff --git a/libavcodec/scpr.c b/libavcodec/scpr.c index 7630adb3e09..b096965de54 100644 --- a/libavcodec/scpr.c +++ b/libavcodec/scpr.c @@ -516,18 +516,18 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, s->version = 1; s->get_freq = get_freq0; s->decode = decode0; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; ret = decompress_i(avctx, (uint32_t *)s->current_frame->data[0], s->current_frame->linesize[0] / 4); } else if (type == 18) { s->version = 2; s->get_freq = get_freq; s->decode = decode; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; ret = decompress_i(avctx, (uint32_t *)s->current_frame->data[0], s->current_frame->linesize[0] / 4); } else if (type == 34) { - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; s->version = 3; ret = decompress_i3(avctx, (uint32_t *)s->current_frame->data[0], s->current_frame->linesize[0] / 4); @@ -538,7 +538,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if (bytestream2_get_bytes_left(gb) < 3) return AVERROR_INVALIDDATA; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; bytestream2_skip(gb, 1); if (avctx->bits_per_coded_sample == 16) { uint16_t value = bytestream2_get_le16(gb); @@ -557,7 +557,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, dst += s->current_frame->linesize[0] / 4; } } else if (type == 0 || type == 1) { - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; if (s->version == 1 || s->version == 2) ret = decompress_p(avctx, (uint32_t *)s->current_frame->data[0], @@ -612,7 +612,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, } } - frame->pict_type = frame->key_frame ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; + frame->pict_type = (frame->flags & AV_FRAME_FLAG_KEY) ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; FFSWAP(AVFrame *, s->current_frame, s->last_frame); diff --git a/libavcodec/scpr3.c b/libavcodec/scpr3.c index d9ea6af1c18..5271717ac79 100644 --- a/libavcodec/scpr3.c +++ b/libavcodec/scpr3.c @@ -1167,6 +1167,9 @@ static int decompress_p3(AVCodecContext *avctx, int run, bx = x * 16 + sx1, by = y * 16 + sy1; uint32_t clr, ptype = 0, r, g, b; + if (bx >= avctx->width) + return AVERROR_INVALIDDATA; + for (; by < y * 16 + sy2 && by < avctx->height;) { ret = decode_value3(s, 5, &s->op_model3[ptype].cntsum, s->op_model3[ptype].freqs[0], diff --git a/libavcodec/screenpresso.c b/libavcodec/screenpresso.c index 0d9e485043a..b27154991c9 100644 --- a/libavcodec/screenpresso.c +++ b/libavcodec/screenpresso.c @@ -173,7 +173,7 @@ static int screenpresso_decode_frame(AVCodecContext *avctx, AVFrame *frame, /* Usual properties */ if (keyframe) { frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; } else { frame->pict_type = AV_PICTURE_TYPE_P; } diff --git a/libavcodec/sga.c b/libavcodec/sga.c index d3f49242984..4ced6e98900 100644 --- a/libavcodec/sga.c +++ b/libavcodec/sga.c @@ -127,19 +127,18 @@ static int decode_index_palmap(SGAVideoContext *s, AVFrame *frame) static int decode_index_tilemap(SGAVideoContext *s, AVFrame *frame) { - GetByteContext *gb = &s->gb; - GetBitContext pm; + GetByteContext *gb = &s->gb, gb2; bytestream2_seek(gb, s->tilemapdata_offset, SEEK_SET); if (bytestream2_get_bytes_left(gb) < s->tilemapdata_size) return AVERROR_INVALIDDATA; - init_get_bits8(&pm, gb->buffer, s->tilemapdata_size); + gb2 = *gb; for (int y = 0; y < s->tiles_h; y++) { for (int x = 0; x < s->tiles_w; x++) { uint8_t tile[64]; - int tilemap = get_bits(&pm, 16); + int tilemap = bytestream2_get_be16u(&gb2); int flip_x = (tilemap >> 11) & 1; int flip_y = (tilemap >> 12) & 1; int tindex = av_clip((tilemap & 511) - 1, 0, s->nb_tiles - 1); @@ -497,9 +496,13 @@ static int sga_decode_frame(AVCodecContext *avctx, AVFrame *frame, } memcpy(frame->data[1], s->pal, AVPALETTE_SIZE); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; diff --git a/libavcodec/sgidec.c b/libavcodec/sgidec.c index 92083f23de9..04a347c51e1 100644 --- a/libavcodec/sgidec.c +++ b/libavcodec/sgidec.c @@ -249,7 +249,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, break; } p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; /* Skip header. */ bytestream2_seek(&g, SGI_HEADER_SIZE, SEEK_SET); diff --git a/libavcodec/sgienc.c b/libavcodec/sgienc.c index 901e0a74f94..5bbb72c03a6 100644 --- a/libavcodec/sgienc.c +++ b/libavcodec/sgienc.c @@ -275,7 +275,7 @@ const FFCodec ff_sgi_encoder = { CODEC_LONG_NAME("SGI image"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_SGI, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(SgiContext), .p.priv_class = &sgi_class, .init = encode_init, diff --git a/libavcodec/sgirledec.c b/libavcodec/sgirledec.c index 9e3a220ad41..18bf8081fc6 100644 --- a/libavcodec/sgirledec.c +++ b/libavcodec/sgirledec.c @@ -124,7 +124,7 @@ static int sgirle_decode_frame(AVCodecContext *avctx, AVFrame *frame, return ret; frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; diff --git a/libavcodec/sheervideo.c b/libavcodec/sheervideo.c index eee60147422..d9c94ea9eb1 100644 --- a/libavcodec/sheervideo.c +++ b/libavcodec/sheervideo.c @@ -1973,7 +1973,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, } p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; if ((ret = ff_thread_get_buffer(avctx, p, 0)) < 0) return ret; diff --git a/libavcodec/shorten.c b/libavcodec/shorten.c index 1b2abd76b14..eca0c4e85ad 100644 --- a/libavcodec/shorten.c +++ b/libavcodec/shorten.c @@ -814,8 +814,10 @@ const FFCodec ff_shorten_decoder = { FF_CODEC_DECODE_CB(shorten_decode_frame), .p.capabilities = AV_CODEC_CAP_CHANNEL_CONF | AV_CODEC_CAP_DELAY | - AV_CODEC_CAP_DR1 | - AV_CODEC_CAP_SUBFRAMES , +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_U8P, AV_SAMPLE_FMT_NONE }, diff --git a/libavcodec/smacker.c b/libavcodec/smacker.c index ecc27e9b672..b98e7275f61 100644 --- a/libavcodec/smacker.c +++ b/libavcodec/smacker.c @@ -107,16 +107,16 @@ enum SmkBlockTypes { * Can read SMKTREE_DECODE_MAX_RECURSION before the first check; * does not overread gb on success. */ -static int smacker_decode_tree(GetBitContext *gb, HuffContext *hc, int length) +static int smacker_decode_tree(AVCodecContext *avctx, GetBitContext *gb, HuffContext *hc, int length) { if (length > SMKTREE_DECODE_MAX_RECURSION || length > 3 * SMKTREE_BITS) { - av_log(NULL, AV_LOG_ERROR, "Maximum tree recursion level exceeded.\n"); + av_log(avctx, AV_LOG_ERROR, "Maximum tree recursion level exceeded.\n"); return AVERROR_INVALIDDATA; } if(!get_bits1(gb)){ //Leaf if (hc->current >= 256) { - av_log(NULL, AV_LOG_ERROR, "Tree size exceeded!\n"); + av_log(avctx, AV_LOG_ERROR, "Tree size exceeded!\n"); return AVERROR_INVALIDDATA; } if (get_bits_left(gb) < 8) @@ -126,10 +126,10 @@ static int smacker_decode_tree(GetBitContext *gb, HuffContext *hc, int length) } else { //Node int r; length++; - r = smacker_decode_tree(gb, hc, length); + r = smacker_decode_tree(avctx, gb, hc, length); if(r) return r; - return smacker_decode_tree(gb, hc, length); + return smacker_decode_tree(avctx, gb, hc, length); } } @@ -138,7 +138,7 @@ static int smacker_decode_tree(GetBitContext *gb, HuffContext *hc, int length) * * Checks before the first read, can overread by 6 * SMKTREE_BITS on success. */ -static int smacker_decode_bigtree(GetBitContext *gb, DBCtx *ctx, int length) +static int smacker_decode_bigtree(AVCodecContext *avctx, GetBitContext *gb, DBCtx *ctx, int length) { // Larger length can cause segmentation faults due to too deep recursion. if (length > SMKTREE_DECODE_BIG_MAX_RECURSION) { @@ -176,12 +176,12 @@ static int smacker_decode_bigtree(GetBitContext *gb, DBCtx *ctx, int length) int r = 0, r_new, t; t = ctx->current++; - r = smacker_decode_bigtree(gb, ctx, length + 1); + r = smacker_decode_bigtree(avctx, gb, ctx, length + 1); if(r < 0) return r; ctx->values[t] = SMK_NODE | r; r++; - r_new = smacker_decode_bigtree(gb, ctx, length + 1); + r_new = smacker_decode_bigtree(avctx, gb, ctx, length + 1); if (r_new < 0) return r_new; return r + r_new; @@ -215,7 +215,7 @@ static int smacker_decode_header_tree(SmackVContext *smk, GetBitContext *gb, int i ? "high" : "low"); continue; } - err = smacker_decode_tree(gb, &h, 0); + err = smacker_decode_tree(smk->avctx, gb, &h, 0); if (err < 0) goto error; skip_bits1(gb); @@ -253,7 +253,7 @@ static int smacker_decode_header_tree(SmackVContext *smk, GetBitContext *gb, int } *recodes = ctx.values; - err = smacker_decode_bigtree(gb, &ctx, 0); + err = smacker_decode_bigtree(smk->avctx, gb, &ctx, 0); if (err < 0) goto error; skip_bits1(gb); @@ -392,12 +392,18 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, pal = (uint32_t*)smk->pic->data[1]; bytestream2_init(&gb2, avpkt->data, avpkt->size); flags = bytestream2_get_byteu(&gb2); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS smk->pic->palette_has_changed = flags & 1; - smk->pic->key_frame = !!(flags & 2); - if (smk->pic->key_frame) +FF_ENABLE_DEPRECATION_WARNINGS +#endif + if (flags & 2) { + smk->pic->flags |= AV_FRAME_FLAG_KEY; smk->pic->pict_type = AV_PICTURE_TYPE_I; - else + } else { + smk->pic->flags &= ~AV_FRAME_FLAG_KEY; smk->pic->pict_type = AV_PICTURE_TYPE_P; + } for(i = 0; i < 256; i++) *pal++ = 0xFFU << 24 | bytestream2_get_be24u(&gb2); @@ -649,7 +655,7 @@ static int smka_decode_frame(AVCodecContext *avctx, AVFrame *frame, HuffContext h; h.current = 0; skip_bits1(&gb); - if ((ret = smacker_decode_tree(&gb, &h, 0)) < 0) + if ((ret = smacker_decode_tree(avctx, &gb, &h, 0)) < 0) goto error; skip_bits1(&gb); if (h.current > 1) { diff --git a/libavcodec/smc.c b/libavcodec/smc.c index 2b10e74386f..3e8a89ced13 100644 --- a/libavcodec/smc.c +++ b/libavcodec/smc.c @@ -437,7 +437,14 @@ static int smc_decode_frame(AVCodecContext *avctx, AVFrame *rframe, if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) return ret; - s->frame->palette_has_changed = ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS + s->frame->palette_has_changed = +#endif + ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_ENABLE_DEPRECATION_WARNINGS +#endif bytestream2_init(&gb, buf, buf_size); ret = smc_decode_stream(s, &gb); diff --git a/libavcodec/smcenc.c b/libavcodec/smcenc.c index e3ea7e5e9f4..40b53c40eef 100644 --- a/libavcodec/smcenc.c +++ b/libavcodec/smcenc.c @@ -542,7 +542,7 @@ static int smc_encode_frame(AVCodecContext *avctx, AVPacket *pkt, return ret; if (avctx->gop_size == 0 || !s->prev_frame->data[0] || - (avctx->frame_number % avctx->gop_size) == 0) { + (avctx->frame_num % avctx->gop_size) == 0) { s->key_frame = 1; } else { s->key_frame = 0; @@ -595,7 +595,7 @@ const FFCodec ff_smc_encoder = { CODEC_LONG_NAME("QuickTime Graphics (SMC)"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_SMC, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(SMCContext), .init = smc_encode_init, FF_CODEC_ENCODE_CB(smc_encode_frame), diff --git a/libavcodec/snow.c b/libavcodec/snow.c index b6c8d5e2568..5eb3ee1e9e7 100644 --- a/libavcodec/snow.c +++ b/libavcodec/snow.c @@ -606,7 +606,7 @@ int ff_snow_frame_start(SnowContext *s){ }else{ int i; for(i=0; imax_ref_frames && s->last_picture[i]->data[0]; i++) - if(i && s->last_picture[i-1]->key_frame) + if(i && (s->last_picture[i-1]->flags & AV_FRAME_FLAG_KEY)) break; s->ref_frames= i; if(s->ref_frames==0){ @@ -617,7 +617,10 @@ int ff_snow_frame_start(SnowContext *s){ if ((ret = ff_snow_get_buffer(s, s->current_picture)) < 0) return ret; - s->current_picture->key_frame= s->keyframe; + if (s->keyframe) + s->current_picture->flags |= AV_FRAME_FLAG_KEY; + else + s->current_picture->flags &= ~AV_FRAME_FLAG_KEY; return 0; } diff --git a/libavcodec/snowenc.c b/libavcodec/snowenc.c index 7fad95b69a8..1360343aec1 100644 --- a/libavcodec/snowenc.c +++ b/libavcodec/snowenc.c @@ -26,6 +26,7 @@ #include "avcodec.h" #include "codec_internal.h" #include "encode.h" +#include "internal.h" //For AVCodecInternal.recon_frame #include "me_cmp.h" #include "packet_internal.h" #include "snow_dwt.h" @@ -129,8 +130,10 @@ static av_cold int encode_init(AVCodecContext *avctx) if (ret) return ret; - ff_set_cmp(&s->mecc, s->mecc.me_cmp, s->avctx->me_cmp); - ff_set_cmp(&s->mecc, s->mecc.me_sub_cmp, s->avctx->me_sub_cmp); + ret = ff_set_cmp(&s->mecc, s->mecc.me_cmp, s->avctx->me_cmp); + ret |= ff_set_cmp(&s->mecc, s->mecc.me_sub_cmp, s->avctx->me_sub_cmp); + if (ret < 0) + return AVERROR(EINVAL); s->input_picture = av_frame_alloc(); if (!s->input_picture) @@ -1551,10 +1554,10 @@ static void calculate_visual_weight(SnowContext *s, Plane *p){ int level, orientation, x, y; for(level=0; levelspatial_decomposition_count; level++){ + int64_t error=0; for(orientation=level ? 1 : 0; orientation<4; orientation++){ SubBand *b= &p->band[level][orientation]; IDWTELEM *ibuf= b->ibuf; - int64_t error=0; memset(s->spatial_idwt_buffer, 0, sizeof(*s->spatial_idwt_buffer)*width*height); ibuf[b->width/2 + b->height/2*b->stride]= 256*16; @@ -1565,9 +1568,13 @@ static void calculate_visual_weight(SnowContext *s, Plane *p){ error += d*d; } } - + if (orientation == 2) + error /= 2; b->qlog= (int)(QROOT * log2(352256.0/sqrt(error)) + 0.5); + if (orientation != 1) + error = 0; } + p->band[level][1].qlog = p->band[level][2].qlog; } } @@ -1576,6 +1583,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, { SnowContext *s = avctx->priv_data; RangeCoder * const c= &s->c; + AVCodecInternal *avci = avctx->internal; AVFrame *pic; const int width= s->avctx->width; const int height= s->avctx->height; @@ -1607,9 +1615,9 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, pic->pict_type = pict->pict_type; pic->quality = pict->quality; - s->m.picture_number= avctx->frame_number; + s->m.picture_number= avctx->frame_num; if(avctx->flags&AV_CODEC_FLAG_PASS2){ - s->m.pict_type = pic->pict_type = s->m.rc_context.entry[avctx->frame_number].new_pict_type; + s->m.pict_type = pic->pict_type = s->m.rc_context.entry[avctx->frame_num].new_pict_type; s->keyframe = pic->pict_type == AV_PICTURE_TYPE_I; if(!(avctx->flags&AV_CODEC_FLAG_QSCALE)) { pic->quality = ff_rate_estimate_qscale(&s->m, 0); @@ -1617,11 +1625,11 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, return -1; } }else{ - s->keyframe= avctx->gop_size==0 || avctx->frame_number % avctx->gop_size == 0; + s->keyframe= avctx->gop_size==0 || avctx->frame_num % avctx->gop_size == 0; s->m.pict_type = pic->pict_type = s->keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; } - if(s->pass1_rc && avctx->frame_number == 0) + if(s->pass1_rc && avctx->frame_num == 0) pic->quality = 2*FF_QP2LAMBDA; if (pic->quality) { s->qlog = qscale2qlog(pic->quality); @@ -1755,7 +1763,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, ff_build_rac_states(c, (1LL<<32)/20, 256-8); pic->pict_type= AV_PICTURE_TYPE_I; s->keyframe=1; - s->current_picture->key_frame=1; + s->current_picture->flags |= AV_FRAME_FLAG_KEY; goto redo_frame; } @@ -1856,13 +1864,12 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, ff_snow_release_buffer(avctx); - s->current_picture->coded_picture_number = avctx->frame_number; s->current_picture->pict_type = pic->pict_type; s->current_picture->quality = pic->quality; s->m.frame_bits = 8*(s->c.bytestream - s->c.bytestream_start); s->m.p_tex_bits = s->m.frame_bits - s->m.misc_bits - s->m.mv_bits; - s->m.current_picture.f->display_picture_number = - s->m.current_picture.f->coded_picture_number = avctx->frame_number; + s->m.current_picture.display_picture_number = + s->m.current_picture.coded_picture_number = avctx->frame_num; s->m.current_picture.f->quality = pic->quality; s->m.total_bits += 8*(s->c.bytestream - s->c.bytestream_start); if(s->pass1_rc) @@ -1878,9 +1885,13 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, s->encoding_error, (s->avctx->flags&AV_CODEC_FLAG_PSNR) ? SNOW_MAX_PLANES : 0, s->current_picture->pict_type); + if (s->avctx->flags & AV_CODEC_FLAG_RECON_FRAME) { + av_frame_unref(avci->recon_frame); + av_frame_ref(avci->recon_frame, s->current_picture); + } pkt->size = ff_rac_terminate(c, 0); - if (s->current_picture->key_frame) + if (s->current_picture->flags & AV_FRAME_FLAG_KEY) pkt->flags |= AV_PKT_FLAG_KEY; *got_packet = 1; @@ -1935,7 +1946,9 @@ const FFCodec ff_snow_encoder = { CODEC_LONG_NAME("Snow"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_SNOW, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE | + AV_CODEC_CAP_ENCODER_RECON_FRAME, .priv_data_size = sizeof(SnowContext), .init = encode_init, FF_CODEC_ENCODE_CB(encode_frame), diff --git a/libavcodec/sonic.c b/libavcodec/sonic.c index 77bdb418a7e..0544fecf469 100644 --- a/libavcodec/sonic.c +++ b/libavcodec/sonic.c @@ -473,7 +473,7 @@ static void predictor_init_state(int *k, int *state, int order) static int predictor_calc_error(int *k, int *state, int order, int error) { - int i, x = error - shift_down(k[order-1] * (unsigned)state[order-1], LATTICE_SHIFT); + int i, x = error - (unsigned)shift_down(k[order-1] * (unsigned)state[order-1], LATTICE_SHIFT); #if 1 int *k_ptr = &(k[order-2]), @@ -1013,7 +1013,7 @@ static int sonic_decode_frame(AVCodecContext *avctx, AVFrame *frame, if (s->lossless) quant = 1; else - quant = get_symbol(&c, state, 0) * SAMPLE_FACTOR; + quant = get_symbol(&c, state, 0) * (unsigned)SAMPLE_FACTOR; // av_log(NULL, AV_LOG_INFO, "quant: %d\n", quant); @@ -1096,7 +1096,8 @@ const FFCodec ff_sonic_encoder = { CODEC_LONG_NAME("Sonic"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_SONIC, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_EXPERIMENTAL, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_EXPERIMENTAL | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(SonicContext), .init = sonic_encode_init, FF_CODEC_ENCODE_CB(sonic_encode_frame), @@ -1112,7 +1113,8 @@ const FFCodec ff_sonic_ls_encoder = { CODEC_LONG_NAME("Sonic lossless"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_SONIC_LS, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_EXPERIMENTAL, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_EXPERIMENTAL | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(SonicContext), .init = sonic_encode_init, FF_CODEC_ENCODE_CB(sonic_encode_frame), diff --git a/libavcodec/speedhqdec.c b/libavcodec/speedhqdec.c index e1e6f9a5f54..ff106009a97 100644 --- a/libavcodec/speedhqdec.c +++ b/libavcodec/speedhqdec.c @@ -436,7 +436,7 @@ static int speedhq_decode_frame(AVCodecContext *avctx, AVFrame *frame, if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) { return ret; } - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; if (second_field_offset == 4 || second_field_offset == (buf_size-4)) { /* diff --git a/libavcodec/speedhqenc.c b/libavcodec/speedhqenc.c index 7269e345d37..5b4ff4c139e 100644 --- a/libavcodec/speedhqenc.c +++ b/libavcodec/speedhqenc.c @@ -288,6 +288,7 @@ const FFCodec ff_speedhq_encoder = { .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_SPEEDHQ, .p.priv_class = &ff_mpv_enc_class, + .p.capabilities = AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(SpeedHQEncContext), .init = ff_mpv_encode_init, FF_CODEC_ENCODE_CB(ff_mpv_encode_picture), diff --git a/libavcodec/sunrastenc.c b/libavcodec/sunrastenc.c index 9b82f9921a3..b2d57f7235d 100644 --- a/libavcodec/sunrastenc.c +++ b/libavcodec/sunrastenc.c @@ -213,7 +213,7 @@ const FFCodec ff_sunrast_encoder = { CODEC_LONG_NAME("Sun Rasterfile image"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_SUNRAST, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(SUNRASTContext), .init = sunrast_encode_init, FF_CODEC_ENCODE_CB(sunrast_encode_frame), diff --git a/libavcodec/svq1enc.c b/libavcodec/svq1enc.c index 8f09e634a59..5e7f4102141 100644 --- a/libavcodec/svq1enc.c +++ b/libavcodec/svq1enc.c @@ -118,7 +118,7 @@ static void svq1_write_header(SVQ1EncContext *s, PutBitContext *pb, int frame_ty /* output 5 unknown bits (2 + 2 + 1) */ put_bits(pb, 5, 2); /* 2 needed by quicktime decoder */ - i = ff_match_2uint16((void*)ff_svq1_frame_size_table, + i = ff_match_2uint16(ff_svq1_frame_size_table, FF_ARRAY_ELEMS(ff_svq1_frame_size_table), s->frame_width, s->frame_height); put_bits(pb, 3, i); @@ -548,10 +548,10 @@ static av_cold int svq1_encode_end(AVCodecContext *avctx) SVQ1EncContext *const s = avctx->priv_data; int i; - if (avctx->frame_number) + if (avctx->frame_num) av_log(avctx, AV_LOG_DEBUG, "RD: %f\n", s->rd_total / (double)(avctx->width * avctx->height * - avctx->frame_number)); + avctx->frame_num)); s->m.mb_type = NULL; ff_mpv_common_end(&s->m); @@ -684,7 +684,7 @@ static int svq1_encode_frame(AVCodecContext *avctx, AVPacket *pkt, FFSWAP(AVFrame*, s->current_picture, s->last_picture); - if (avctx->gop_size && (avctx->frame_number % avctx->gop_size)) + if (avctx->gop_size && (avctx->frame_num % avctx->gop_size)) s->pict_type = AV_PICTURE_TYPE_P; else s->pict_type = AV_PICTURE_TYPE_I; @@ -752,7 +752,7 @@ const FFCodec ff_svq1_encoder = { CODEC_LONG_NAME("Sorenson Vector Quantizer 1 / Sorenson Video 1 / SVQ1"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_SVQ1, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(SVQ1EncContext), .p.priv_class = &svq1enc_class, .init = svq1_encode_init, diff --git a/libavcodec/svq3.c b/libavcodec/svq3.c index b96c4f61f66..2d03dbc457b 100644 --- a/libavcodec/svq3.c +++ b/libavcodec/svq3.c @@ -1408,7 +1408,10 @@ static int svq3_decode_frame(AVCodecContext *avctx, AVFrame *rframe, /* for skipping the frame */ s->cur_pic->f->pict_type = s->pict_type; - s->cur_pic->f->key_frame = (s->pict_type == AV_PICTURE_TYPE_I); + if (s->pict_type == AV_PICTURE_TYPE_I) + s->cur_pic->f->flags |= AV_FRAME_FLAG_KEY; + else + s->cur_pic->f->flags &= ~AV_FRAME_FLAG_KEY; ret = get_buffer(avctx, s->cur_pic); if (ret < 0) @@ -1542,12 +1545,12 @@ static int svq3_decode_frame(AVCodecContext *avctx, AVFrame *rframe, left = buf_size*8 - get_bits_count(&s->gb_slice); if (s->mb_y != s->mb_height || s->mb_x != s->mb_width) { - av_log(avctx, AV_LOG_INFO, "frame num %d incomplete pic x %d y %d left %d\n", avctx->frame_number, s->mb_y, s->mb_x, left); + av_log(avctx, AV_LOG_INFO, "frame num %"PRId64" incomplete pic x %d y %d left %d\n", avctx->frame_num, s->mb_y, s->mb_x, left); //av_hex_dump(stderr, buf+buf_size-8, 8); } if (left < 0) { - av_log(avctx, AV_LOG_ERROR, "frame num %d left %d\n", avctx->frame_number, left); + av_log(avctx, AV_LOG_ERROR, "frame num %"PRId64" left %d\n", avctx->frame_num, left); return -1; } diff --git a/libavcodec/tak.c b/libavcodec/tak.c index f26574c968e..91feac5451d 100644 --- a/libavcodec/tak.c +++ b/libavcodec/tak.c @@ -92,10 +92,10 @@ int ff_tak_check_crc(const uint8_t *buf, unsigned int buf_size) return 0; } -void ff_tak_parse_streaminfo(TAKStreamInfo *s, GetBitContext *gb) +static int tak_parse_streaminfo(TAKStreamInfo *s, GetBitContext *gb) { uint64_t channel_mask = 0; - int frame_type, i; + int frame_type, i, ret; s->codec = get_bits(gb, TAK_ENCODER_CODEC_BITS); skip_bits(gb, TAK_ENCODER_PROFILE_BITS); @@ -124,7 +124,13 @@ void ff_tak_parse_streaminfo(TAKStreamInfo *s, GetBitContext *gb) } s->ch_layout = channel_mask; - s->frame_samples = tak_get_nb_samples(s->sample_rate, frame_type); + + ret = tak_get_nb_samples(s->sample_rate, frame_type); + if (ret < 0) + return ret; + s->frame_samples = ret; + + return 0; } int avpriv_tak_parse_streaminfo(TAKStreamInfo *s, const uint8_t *buf, int size) @@ -135,16 +141,14 @@ int avpriv_tak_parse_streaminfo(TAKStreamInfo *s, const uint8_t *buf, int size) if (ret < 0) return AVERROR_INVALIDDATA; - ff_tak_parse_streaminfo(s, &gb); - - return 0; + return tak_parse_streaminfo(s, &gb); } -int ff_tak_decode_frame_header(AVCodecContext *avctx, GetBitContext *gb, +int ff_tak_decode_frame_header(void *logctx, GetBitContext *gb, TAKStreamInfo *ti, int log_level_offset) { if (get_bits(gb, TAK_FRAME_HEADER_SYNC_ID_BITS) != TAK_FRAME_HEADER_SYNC_ID) { - av_log(avctx, AV_LOG_ERROR + log_level_offset, "missing sync id\n"); + av_log(logctx, AV_LOG_ERROR + log_level_offset, "missing sync id\n"); return AVERROR_INVALIDDATA; } @@ -159,7 +163,9 @@ int ff_tak_decode_frame_header(AVCodecContext *avctx, GetBitContext *gb, } if (ti->flags & TAK_FRAME_FLAG_HAS_INFO) { - ff_tak_parse_streaminfo(ti, gb); + int ret = tak_parse_streaminfo(ti, gb); + if (ret < 0) + return ret; if (get_bits(gb, 6)) skip_bits(gb, 25); @@ -169,6 +175,9 @@ int ff_tak_decode_frame_header(AVCodecContext *avctx, GetBitContext *gb, if (ti->flags & TAK_FRAME_FLAG_HAS_METADATA) return AVERROR_INVALIDDATA; + if (get_bits_left(gb) < 24) + return AVERROR_INVALIDDATA; + skip_bits(gb, 24); return 0; diff --git a/libavcodec/tak.h b/libavcodec/tak.h index 60691189710..1d1ee645e83 100644 --- a/libavcodec/tak.h +++ b/libavcodec/tak.h @@ -29,7 +29,6 @@ #include -#include "avcodec.h" #include "get_bits.h" #define TAK_FORMAT_DATA_TYPE_BITS 3 @@ -149,17 +148,15 @@ int ff_tak_check_crc(const uint8_t *buf, unsigned int buf_size); */ int avpriv_tak_parse_streaminfo(TAKStreamInfo *s, const uint8_t *buf, int size); -void ff_tak_parse_streaminfo(TAKStreamInfo *s, GetBitContext *gb); - /** * Validate and decode a frame header. - * @param avctx AVCodecContext to use as av_log() context + * @param logctx for use as av_log() context * @param[in] gb GetBitContext from which to read frame header * @param[out] s frame information * @param log_level_offset log level offset, can be used to silence * error messages. * @return non-zero on error, 0 if OK */ -int ff_tak_decode_frame_header(AVCodecContext *avctx, GetBitContext *gb, +int ff_tak_decode_frame_header(void *logctx, GetBitContext *gb, TAKStreamInfo *s, int log_level_offset); #endif /* AVCODEC_TAK_H */ diff --git a/libavcodec/takdsp.c b/libavcodec/takdsp.c index 881d7be5f27..b646a063db7 100644 --- a/libavcodec/takdsp.c +++ b/libavcodec/takdsp.c @@ -28,8 +28,8 @@ static void decorrelate_ls(int32_t *p1, int32_t *p2, int length) int i; for (i = 0; i < length; i++) { - int32_t a = p1[i]; - int32_t b = p2[i]; + uint32_t a = p1[i]; + uint32_t b = p2[i]; p2[i] = a + b; } } @@ -39,8 +39,8 @@ static void decorrelate_sr(int32_t *p1, int32_t *p2, int length) int i; for (i = 0; i < length; i++) { - int32_t a = p1[i]; - int32_t b = p2[i]; + uint32_t a = p1[i]; + uint32_t b = p2[i]; p1[i] = b - a; } } @@ -50,7 +50,7 @@ static void decorrelate_sm(int32_t *p1, int32_t *p2, int length) int i; for (i = 0; i < length; i++) { - int32_t a = p1[i]; + uint32_t a = p1[i]; int32_t b = p2[i]; a -= b >> 1; p1[i] = a; @@ -63,7 +63,7 @@ static void decorrelate_sf(int32_t *p1, int32_t *p2, int length, int dshift, int int i; for (i = 0; i < length; i++) { - int32_t a = p1[i]; + uint32_t a = p1[i]; int32_t b = p2[i]; b = (unsigned)((int)(dfactor * (unsigned)(b >> dshift) + 128) >> 8) << dshift; p1[i] = b - a; diff --git a/libavcodec/targa.c b/libavcodec/targa.c index 07005f2be69..59fdc428d95 100644 --- a/libavcodec/targa.c +++ b/libavcodec/targa.c @@ -249,7 +249,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, } break; } +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS p->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } } diff --git a/libavcodec/targa_y216dec.c b/libavcodec/targa_y216dec.c index d5234c16ae9..2874a51aaed 100644 --- a/libavcodec/targa_y216dec.c +++ b/libavcodec/targa_y216dec.c @@ -47,7 +47,7 @@ static int y216_decode_frame(AVCodecContext *avctx, AVFrame *pic, if ((ret = ff_get_buffer(avctx, pic, 0)) < 0) return ret; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; pic->pict_type = AV_PICTURE_TYPE_I; y = (uint16_t *)pic->data[0]; diff --git a/libavcodec/targaenc.c b/libavcodec/targaenc.c index bb3cb931878..d9c500b97de 100644 --- a/libavcodec/targaenc.c +++ b/libavcodec/targaenc.c @@ -207,7 +207,7 @@ const FFCodec ff_targa_encoder = { CODEC_LONG_NAME("Truevision Targa image"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_TARGA, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(TargaContext), .p.priv_class = &targa_class, .init = targa_encode_init, diff --git a/libavcodec/tdsc.c b/libavcodec/tdsc.c index b5ab2e171ba..739738d9b12 100644 --- a/libavcodec/tdsc.c +++ b/libavcodec/tdsc.c @@ -612,7 +612,7 @@ static int tdsc_decode_frame(AVCodecContext *avctx, AVFrame *frame, /* Frame is ready to be output */ if (keyframe) { frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; } else { frame->pict_type = AV_PICTURE_TYPE_P; } diff --git a/libavcodec/tests/avcodec.c b/libavcodec/tests/avcodec.c index 3288a85f64b..08ca507bf0e 100644 --- a/libavcodec/tests/avcodec.c +++ b/libavcodec/tests/avcodec.c @@ -20,7 +20,6 @@ #include "libavcodec/codec.h" #include "libavcodec/codec_desc.h" #include "libavcodec/codec_internal.h" -#include "libavcodec/internal.h" static const char *get_type_string(enum AVMediaType type) { @@ -149,8 +148,7 @@ int main(void){ FF_CODEC_CAP_SETS_FRAME_PROPS) || codec->capabilities & (AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_CHANNEL_CONF | - AV_CODEC_CAP_DRAW_HORIZ_BAND | - AV_CODEC_CAP_SUBFRAMES)) + AV_CODEC_CAP_DRAW_HORIZ_BAND)) ERR("Encoder %s has decoder-only capabilities set\n"); if (codec->capabilities & AV_CODEC_CAP_FRAME_THREADS && codec->capabilities & AV_CODEC_CAP_ENCODER_FLUSH) @@ -158,6 +156,10 @@ int main(void){ if (codec->capabilities & AV_CODEC_CAP_FRAME_THREADS && codec->capabilities & AV_CODEC_CAP_DELAY) ERR("Frame-threaded encoder %s claims to have delay\n"); + + if (codec2->caps_internal & FF_CODEC_CAP_EOF_FLUSH && + !(codec->capabilities & AV_CODEC_CAP_DELAY)) + ERR("EOF_FLUSH encoder %s is not marked as having delay\n"); } else { if ((codec->type == AVMEDIA_TYPE_SUBTITLE) != (codec2->cb_type == FF_CODEC_CB_TYPE_DECODE_SUB)) ERR("Subtitle decoder %s does not implement decode_sub callback\n"); @@ -173,6 +175,10 @@ int main(void){ !(codec->capabilities & AV_CODEC_CAP_FRAME_THREADS)) ERR("Decoder %s wants allocated progress without supporting" "frame threads\n"); + if (codec2->cb_type != FF_CODEC_CB_TYPE_DECODE && + codec2->caps_internal & FF_CODEC_CAP_SETS_PKT_DTS) + ERR("Decoder %s is marked as setting pkt_dts when it doesn't have" + "any effect\n"); } if (priv_data_size_wrong(codec2)) ERR_EXT("Private context of codec %s is impossibly-sized (size %d).", diff --git a/libavcodec/tests/bitstream_template.c b/libavcodec/tests/bitstream_template.c index 13e92a31c6f..ef59845154d 100644 --- a/libavcodec/tests/bitstream_template.c +++ b/libavcodec/tests/bitstream_template.c @@ -42,6 +42,7 @@ enum Op { OP_READ_63, OP_READ_64, OP_READ_SIGNED, + OP_READ_SIGNED_NZ, OP_APPLY_SIGN, OP_ALIGN, OP_NB, @@ -58,7 +59,7 @@ int main(int argc, char **argv) uint32_t random_seed; uint64_t val, val1; - int32_t sval; + int32_t sval, sval1; unsigned count; /* generate random input, using a given or random seed */ @@ -130,10 +131,26 @@ int main(int argc, char **argv) break; case OP_READ_SIGNED: count = av_lfg_get(&lfg) % FFMIN(33, bits_left(&bc) + 1); + sval1 = bits_peek_signed(&bc, count); sval = bits_read_signed(&bc, count); fprintf(stderr, "%d read_signed %u: %"PRId32"\n", bits_tell(&bc) - count, count, sval); + av_assert0(sval == sval1); + + if (count == 32) put_bits32(&pb, sval); + else put_sbits(&pb, count, sval); + break; + case OP_READ_SIGNED_NZ: + count = av_lfg_get(&lfg) % FFMIN(33, bits_left(&bc) + 1); + count = FFMAX(count, 1); + sval1 = bits_peek_signed_nz(&bc, count); + sval = bits_read_signed_nz(&bc, count); + + fprintf(stderr, "%d read_signed_nz %u: %"PRId32"\n", bits_tell(&bc) - count, count, sval); + + av_assert0(sval == sval1); + if (count == 32) put_bits32(&pb, sval); else put_sbits(&pb, count, sval); break; diff --git a/libavcodec/tests/dct.c b/libavcodec/tests/dct.c index c847af2f110..e8d0b8dd1db 100644 --- a/libavcodec/tests/dct.c +++ b/libavcodec/tests/dct.c @@ -43,6 +43,7 @@ #include "libavutil/time.h" #include "libavcodec/dct.h" +#include "libavcodec/fdctdsp.h" #include "libavcodec/idctdsp.h" #include "libavcodec/simple_idct.h" #include "libavcodec/xvididct.h" diff --git a/libavcodec/tests/snowenc.c b/libavcodec/tests/snowenc.c index e423ab0541b..37198cd4e3b 100644 --- a/libavcodec/tests/snowenc.c +++ b/libavcodec/tests/snowenc.c @@ -31,11 +31,13 @@ int main(void){ #define width 256 #define height 256 int buffer[2][width*height]; + short obuffer[width*height]; SnowContext s; int i; AVLFG prng; s.spatial_decomposition_count=6; s.spatial_decomposition_type=1; + int ret = 0; s.temp_dwt_buffer = av_calloc(width, sizeof(*s.temp_dwt_buffer)); s.temp_idwt_buffer = av_calloc(width, sizeof(*s.temp_idwt_buffer)); @@ -49,24 +51,34 @@ int main(void){ printf("testing 5/3 DWT\n"); for(i=0; i20) printf("fsck: %6d %12d %7d\n",i, buffer[0][i], buffer[1][i]); + if(FFABS(buffer[1][i] - obuffer[i])>20) { + printf("fsck: %4dx%4d %12d %7d\n",i%width, i/width, buffer[1][i], obuffer[i]); + ret = 1; + } { int level, orientation, x, y; @@ -81,18 +93,18 @@ int main(void){ int w= width >> (s.spatial_decomposition_count-level); int h= height >> (s.spatial_decomposition_count-level); int stride= width << (s.spatial_decomposition_count-level); - DWTELEM *buf= buffer[0]; + IDWTELEM *buf= obuffer; int64_t error=0; if(orientation&1) buf+=w; if(orientation>1) buf+=stride>>1; - memset(buffer[0], 0, sizeof(int)*width*height); - buf[w/2 + h/2*stride]= 256*256; - ff_spatial_idwt((IDWTELEM*)buffer[0], s.temp_idwt_buffer, width, height, width, s.spatial_decomposition_type, s.spatial_decomposition_count); + memset(obuffer, 0, sizeof(short)*width*height); + buf[w/2 + h/2*stride]= 8*256; + ff_spatial_idwt(obuffer, s.temp_idwt_buffer, width, height, width, s.spatial_decomposition_type, s.spatial_decomposition_count); for(y=0; yget_format(). - * Cannot be called after the codec has called ff_thread_finish_setup(). - * - * @param avctx The current context. - * @param fmt The list of available formats. - */ -enum AVPixelFormat ff_thread_get_format(AVCodecContext *avctx, const enum AVPixelFormat *fmt); -#else #define ff_thread_get_format ff_get_format -#endif /** * Wrapper around get_buffer() for frame-multithreaded codecs. diff --git a/libavcodec/threadframe.h b/libavcodec/threadframe.h index d2f93c5cd09..d581c408a5f 100644 --- a/libavcodec/threadframe.h +++ b/libavcodec/threadframe.h @@ -84,6 +84,9 @@ void ff_thread_release_ext_buffer(AVCodecContext *avctx, ThreadFrame *f); int ff_thread_ref_frame(ThreadFrame *dst, const ThreadFrame *src); +int ff_thread_replace_frame(AVCodecContext *avctx, ThreadFrame *dst, + const ThreadFrame *src); + int ff_thread_can_start_frame(AVCodecContext *avctx); #endif diff --git a/libavcodec/tiertexseqv.c b/libavcodec/tiertexseqv.c index 19c0671bf6b..cdc885558b8 100644 --- a/libavcodec/tiertexseqv.c +++ b/libavcodec/tiertexseqv.c @@ -182,7 +182,11 @@ static int seqvideo_decode(SeqVideoContext *seq, const unsigned char *data, int c[j] = (*data << 2) | (*data >> 4); palette[i] = 0xFFU << 24 | AV_RB24(c); } +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS seq->frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } if (flags & 2) { diff --git a/libavcodec/tiff.c b/libavcodec/tiff.c index 1a1879de890..148964590bc 100644 --- a/libavcodec/tiff.c +++ b/libavcodec/tiff.c @@ -1036,7 +1036,7 @@ static int dng_decode_tiles(AVCodecContext *avctx, AVFrame *frame, /* Frame is ready to be output */ frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; return avpkt->size; } @@ -1451,7 +1451,7 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) break; case TIFF_GRAY_RESPONSE_CURVE: case DNG_LINEARIZATION_TABLE: - if (count > FF_ARRAY_ELEMS(s->dng_lut)) + if (count < 1 || count > FF_ARRAY_ELEMS(s->dng_lut)) return AVERROR_INVALIDDATA; for (int i = 0; i < count; i++) s->dng_lut[i] = ff_tget(&s->gb, type, s->le); @@ -2379,6 +2379,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, } } + p->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; diff --git a/libavcodec/tiffenc.c b/libavcodec/tiffenc.c index 6dfbdaeb409..06d7dcc99d8 100644 --- a/libavcodec/tiffenc.c +++ b/libavcodec/tiffenc.c @@ -574,7 +574,8 @@ const FFCodec ff_tiff_encoder = { CODEC_LONG_NAME("TIFF image"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_TIFF, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(TiffEncoderContext), .init = encode_init, .close = encode_close, diff --git a/libavcodec/tmv.c b/libavcodec/tmv.c index cdb83452e50..2a7e1a105f1 100644 --- a/libavcodec/tmv.c +++ b/libavcodec/tmv.c @@ -57,10 +57,14 @@ static int tmv_decode_frame(AVCodecContext *avctx, AVFrame *frame, } frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; dst = frame->data[0]; +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif memcpy(frame->data[1], ff_cga_palette, 16 * 4); memset(frame->data[1] + 16 * 4, 0, AVPALETTE_SIZE - 16 * 4); diff --git a/libavcodec/truemotion2.c b/libavcodec/truemotion2.c index b168b9cda14..73c93359dac 100644 --- a/libavcodec/truemotion2.c +++ b/libavcodec/truemotion2.c @@ -930,11 +930,13 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, } offset += t; } - p->key_frame = tm2_decode_blocks(l, p); - if (p->key_frame) + if (tm2_decode_blocks(l, p)) { + p->flags |= AV_FRAME_FLAG_KEY; p->pict_type = AV_PICTURE_TYPE_I; - else + } else { + p->flags &= ~AV_FRAME_FLAG_KEY; p->pict_type = AV_PICTURE_TYPE_P; + } l->cur = !l->cur; *got_frame = 1; diff --git a/libavcodec/truemotion2rt.c b/libavcodec/truemotion2rt.c index c6015b278a8..4f8590fc82c 100644 --- a/libavcodec/truemotion2rt.c +++ b/libavcodec/truemotion2rt.c @@ -202,7 +202,7 @@ static int truemotion2rt_decode_frame(AVCodecContext *avctx, AVFrame *p, } p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; diff --git a/libavcodec/tscc.c b/libavcodec/tscc.c index 0ebe641ab1b..346d93e1f24 100644 --- a/libavcodec/tscc.c +++ b/libavcodec/tscc.c @@ -106,7 +106,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, /* make the palette available on the way out */ if (c->avctx->pix_fmt == AV_PIX_FMT_PAL8) { +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = palette_has_changed; +FF_ENABLE_DEPRECATION_WARNINGS +#endif memcpy(frame->data[1], c->pal, AVPALETTE_SIZE); } diff --git a/libavcodec/tta.c b/libavcodec/tta.c index e63d08bb441..3e89571f16d 100644 --- a/libavcodec/tta.c +++ b/libavcodec/tta.c @@ -160,7 +160,8 @@ static av_cold int tta_decode_init(AVCodecContext * avctx) av_channel_layout_uninit(&avctx->ch_layout); if (s->channels > 1 && s->channels < 9) { av_channel_layout_from_mask(&avctx->ch_layout, tta_channel_layouts[s->channels-2]); - } else { + } + if (avctx->ch_layout.nb_channels == 0) { avctx->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC; avctx->ch_layout.nb_channels = s->channels; } diff --git a/libavcodec/ttaenc.c b/libavcodec/ttaenc.c index d41d2e6fa58..db79c38b43f 100644 --- a/libavcodec/ttaenc.c +++ b/libavcodec/ttaenc.c @@ -204,7 +204,8 @@ const FFCodec ff_tta_encoder = { CODEC_LONG_NAME("TTA (True Audio)"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_TTA, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SMALL_LAST_FRAME, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SMALL_LAST_FRAME | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(TTAEncContext), .init = tta_encode_init, .close = tta_encode_close, diff --git a/libavcodec/utils.c b/libavcodec/utils.c index 2b63a498b9a..bd4131db628 100644 --- a/libavcodec/utils.c +++ b/libavcodec/utils.c @@ -243,6 +243,8 @@ void avcodec_align_dimensions2(AVCodecContext *s, int *width, int *height, case AV_PIX_FMT_GBRAP16BE: w_align = 16; //FIXME assume 16 pixel per macroblock h_align = 16 * 2; // interlaced needs 2 macroblocks height + if (s->codec_id == AV_CODEC_ID_BINKVIDEO) + w_align = 16*2; break; case AV_PIX_FMT_YUV411P: case AV_PIX_FMT_YUVJ411P: @@ -315,12 +317,13 @@ void avcodec_align_dimensions2(AVCodecContext *s, int *width, int *height, } if (s->codec_id == AV_CODEC_ID_IFF_ILBM) { - w_align = FFMAX(w_align, 8); + w_align = FFMAX(w_align, 16); } *width = FFALIGN(*width, w_align); *height = FFALIGN(*height, h_align); if (s->codec_id == AV_CODEC_ID_H264 || s->lowres || + s->codec_id == AV_CODEC_ID_VC1 || s->codec_id == AV_CODEC_ID_WMV3 || s->codec_id == AV_CODEC_ID_VP5 || s->codec_id == AV_CODEC_ID_VP6 || s->codec_id == AV_CODEC_ID_VP6F || s->codec_id == AV_CODEC_ID_VP6A ) { @@ -334,6 +337,9 @@ void avcodec_align_dimensions2(AVCodecContext *s, int *width, int *height, // the next rounded up width is 32 *width = FFMAX(*width, 32); } + if (s->codec_id == AV_CODEC_ID_SVQ3) { + *width = FFMAX(*width, 32); + } for (i = 0; i < 4; i++) linesize_align[i] = STRIDE_ALIGN; @@ -400,34 +406,6 @@ int avcodec_fill_audio_frame(AVFrame *frame, int nb_channels, return ret; } -void ff_color_frame(AVFrame *frame, const int c[4]) -{ - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format); - int p, y; - - av_assert0(desc->flags & AV_PIX_FMT_FLAG_PLANAR); - - for (p = 0; pnb_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) { - ((uint16_t*)dst)[0] = c[p]; - av_memcpy_backptr(dst + 2, 2, bytes - 2); - dst += frame->linesize[p]; - for (y = 1; y < height; y++) { - memcpy(dst, frame->data[p], 2*bytes); - dst += frame->linesize[p]; - } - } else { - for (y = 0; y < height; y++) { - memset(dst, c[p], bytes); - dst += frame->linesize[p]; - } - } - } -} int avpriv_codec_get_cap_skip_frame_fill_param(const AVCodec *codec){ return !!(ffcodec(codec)->caps_internal & FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM); @@ -514,7 +492,9 @@ int av_get_exact_bits_per_sample(enum AVCodecID codec_id) case AV_CODEC_ID_PCM_SGA: case AV_CODEC_ID_PCM_U8: case AV_CODEC_ID_SDX2_DPCM: + case AV_CODEC_ID_CBD2_DPCM: case AV_CODEC_ID_DERF_DPCM: + case AV_CODEC_ID_WADY_DPCM: return 8; case AV_CODEC_ID_PCM_S16BE: case AV_CODEC_ID_PCM_S16BE_PLANAR: @@ -633,9 +613,9 @@ static int get_audio_frame_duration(enum AVCodecID id, int sr, int ch, int ba, if (sr > 0) { /* calc from sample rate */ if (id == AV_CODEC_ID_TTA) - return 256 * sr / 245; + return 256ll * sr / 245; else if (id == AV_CODEC_ID_DST) - return 588 * sr / 44100; + return 588ll * sr / 44100; else if (id == AV_CODEC_ID_BINKAUDIO_DCT) { if (sr / 22050 > 22) return 0; @@ -765,6 +745,9 @@ static int get_audio_frame_duration(enum AVCodecID id, int sr, int ch, int ba, case AV_CODEC_ID_ADPCM_MTAF: tmp = blocks * (ba - 16LL) * 2 / ch; break; + case AV_CODEC_ID_ADPCM_XMD: + tmp = blocks * 32; + break; } if (tmp) { if (tmp != (int)tmp) @@ -904,13 +887,29 @@ int ff_thread_ref_frame(ThreadFrame *dst, const ThreadFrame *src) return 0; } -#if !HAVE_THREADS - -enum AVPixelFormat ff_thread_get_format(AVCodecContext *avctx, const enum AVPixelFormat *fmt) +int ff_thread_replace_frame(AVCodecContext *avctx, ThreadFrame *dst, + const ThreadFrame *src) { - return ff_get_format(avctx, fmt); + int ret; + + dst->owner[0] = src->owner[0]; + dst->owner[1] = src->owner[1]; + + ret = av_frame_replace(dst->f, src->f); + if (ret < 0) + return ret; + + ret = av_buffer_replace(&dst->progress, src->progress); + if (ret < 0) { + ff_thread_release_ext_buffer(dst->owner[0], dst); + return ret; + } + + return 0; } +#if !HAVE_THREADS + int ff_thread_get_buffer(AVCodecContext *avctx, AVFrame *f, int flags) { return ff_get_buffer(avctx, f, flags); @@ -1143,22 +1142,3 @@ int64_t ff_guess_coded_bitrate(AVCodecContext *avctx) return bitrate; } - -int ff_int_from_list_or_default(void *ctx, const char * val_name, int val, - const int * array_valid_values, int default_value) -{ - int i = 0, ref_val; - - while (1) { - ref_val = array_valid_values[i]; - if (ref_val == INT_MAX) - break; - if (val == ref_val) - return val; - i++; - } - /* val is not a valid value */ - av_log(ctx, AV_LOG_DEBUG, - "%s %d are not supported. Set to default value : %d\n", val_name, val, default_value); - return default_value; -} diff --git a/libavcodec/utvideodec.c b/libavcodec/utvideodec.c index 83120d1b22c..1f00c58950a 100644 --- a/libavcodec/utvideodec.c +++ b/libavcodec/utvideodec.c @@ -869,9 +869,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, break; } - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; - frame->interlaced_frame = !!c->interlaced; + if (c->interlaced) + frame->flags |= AV_FRAME_FLAG_INTERLACED; *got_frame = 1; diff --git a/libavcodec/utvideoenc.c b/libavcodec/utvideoenc.c index d4388da8ba1..6e87bbc2b6c 100644 --- a/libavcodec/utvideoenc.c +++ b/libavcodec/utvideoenc.c @@ -648,7 +648,8 @@ const FFCodec ff_utvideo_encoder = { CODEC_LONG_NAME("Ut Video"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_UTVIDEO, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(UtvideoContext), .p.priv_class = &utvideo_class, .init = utvideo_encode_init, diff --git a/libavcodec/v210dec.c b/libavcodec/v210dec.c index 43b92f6ec93..814d65bbda0 100644 --- a/libavcodec/v210dec.c +++ b/libavcodec/v210dec.c @@ -187,7 +187,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *pic, return ret; pic->pict_type = AV_PICTURE_TYPE_I; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; if (stride) { td.stride = stride; @@ -207,9 +207,9 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *pic, if (avctx->field_order > AV_FIELD_PROGRESSIVE) { /* we have interlaced material flagged in container */ - pic->interlaced_frame = 1; + pic->flags |= AV_FRAME_FLAG_INTERLACED; if (avctx->field_order == AV_FIELD_TT || avctx->field_order == AV_FIELD_TB) - pic->top_field_first = 1; + pic->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; } *got_frame = 1; diff --git a/libavcodec/v210enc.c b/libavcodec/v210enc.c index abbbf4ff9db..2a30ed77da3 100644 --- a/libavcodec/v210enc.c +++ b/libavcodec/v210enc.c @@ -112,7 +112,8 @@ const FFCodec ff_v210_encoder = { CODEC_LONG_NAME("Uncompressed 4:2:2 10-bit"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_V210, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(V210EncContext), .init = encode_init, FF_CODEC_ENCODE_CB(encode_frame), diff --git a/libavcodec/v210x.c b/libavcodec/v210x.c index 96594e2a43c..55630fa2fb9 100644 --- a/libavcodec/v210x.c +++ b/libavcodec/v210x.c @@ -62,7 +62,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *pic, vdst = (uint16_t *)pic->data[2]; yend = ydst + width; pic->pict_type = AV_PICTURE_TYPE_I; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; for (;;) { uint32_t v = av_be2ne32(*src++); diff --git a/libavcodec/v308dec.c b/libavcodec/v308dec.c index a81771fc5f4..4bc4ea4e21a 100644 --- a/libavcodec/v308dec.c +++ b/libavcodec/v308dec.c @@ -48,7 +48,7 @@ static int v308_decode_frame(AVCodecContext *avctx, AVFrame *pic, if ((ret = ff_get_buffer(avctx, pic, 0)) < 0) return ret; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; pic->pict_type = AV_PICTURE_TYPE_I; y = pic->data[0]; diff --git a/libavcodec/v308enc.c b/libavcodec/v308enc.c index 78e33c0a8ab..68f9c3310b8 100644 --- a/libavcodec/v308enc.c +++ b/libavcodec/v308enc.c @@ -75,7 +75,7 @@ const FFCodec ff_v308_encoder = { CODEC_LONG_NAME("Uncompressed packed 4:4:4"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_V308, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .init = v308_encode_init, FF_CODEC_ENCODE_CB(v308_encode_frame), .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV444P, AV_PIX_FMT_NONE }, diff --git a/libavcodec/v408dec.c b/libavcodec/v408dec.c index edc9976d94f..191c050fb24 100644 --- a/libavcodec/v408dec.c +++ b/libavcodec/v408dec.c @@ -51,7 +51,7 @@ static int v408_decode_frame(AVCodecContext *avctx, AVFrame *pic, if ((ret = ff_get_buffer(avctx, pic, 0)) < 0) return ret; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; pic->pict_type = AV_PICTURE_TYPE_I; y = pic->data[0]; diff --git a/libavcodec/v408enc.c b/libavcodec/v408enc.c index 514f41be4e3..1faac7cc363 100644 --- a/libavcodec/v408enc.c +++ b/libavcodec/v408enc.c @@ -94,7 +94,7 @@ const FFCodec ff_ayuv_encoder = { CODEC_LONG_NAME("Uncompressed packed MS 4:4:4:4"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_AYUV, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .init = v408_encode_init, FF_CODEC_ENCODE_CB(v408_encode_frame), .p.pix_fmts = pix_fmt, @@ -107,7 +107,7 @@ const FFCodec ff_v408_encoder = { CODEC_LONG_NAME("Uncompressed packed QT 4:4:4:4"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_V408, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .init = v408_encode_init, FF_CODEC_ENCODE_CB(v408_encode_frame), .p.pix_fmts = pix_fmt, diff --git a/libavcodec/v410dec.c b/libavcodec/v410dec.c index fb859e8cca8..35e4a8ae032 100644 --- a/libavcodec/v410dec.c +++ b/libavcodec/v410dec.c @@ -102,7 +102,7 @@ static int v410_decode_frame(AVCodecContext *avctx, AVFrame *pic, if ((ret = ff_thread_get_buffer(avctx, pic, 0)) < 0) return ret; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; pic->pict_type = AV_PICTURE_TYPE_I; td.buf = src; diff --git a/libavcodec/v410enc.c b/libavcodec/v410enc.c index bad13c371ae..89ee3a72786 100644 --- a/libavcodec/v410enc.c +++ b/libavcodec/v410enc.c @@ -79,7 +79,7 @@ const FFCodec ff_v410_encoder = { CODEC_LONG_NAME("Uncompressed 4:4:4 10-bit"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_V410, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .init = v410_encode_init, FF_CODEC_ENCODE_CB(v410_encode_frame), .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV444P10, AV_PIX_FMT_NONE }, diff --git a/libavcodec/v4l2_buffers.c b/libavcodec/v4l2_buffers.c index 3f5471067a1..22771356998 100644 --- a/libavcodec/v4l2_buffers.c +++ b/libavcodec/v4l2_buffers.c @@ -427,7 +427,8 @@ int ff_v4l2_buffer_buf_to_avframe(AVFrame *frame, V4L2Buffer *avbuf) return ret; /* 2. get frame information */ - frame->key_frame = !!(avbuf->buf.flags & V4L2_BUF_FLAG_KEYFRAME); + if (avbuf->buf.flags & V4L2_BUF_FLAG_KEYFRAME) + frame->flags |= AV_FRAME_FLAG_KEY; frame->color_primaries = v4l2_get_color_primaries(avbuf); frame->colorspace = v4l2_get_color_space(avbuf); frame->color_range = v4l2_get_color_range(avbuf); diff --git a/libavcodec/v4l2_context.c b/libavcodec/v4l2_context.c index a40be946904..f20f713e1d5 100644 --- a/libavcodec/v4l2_context.c +++ b/libavcodec/v4l2_context.c @@ -325,9 +325,13 @@ static V4L2Buffer* v4l2_dequeue_v4l2buf(V4L2Context *ctx, int timeout) /* 0. handle errors */ if (pfd.revents & POLLERR) { - /* if we are trying to get free buffers but none have been queued yet - no need to raise a warning */ + /* if we are trying to get free buffers but none have been queued yet, + * or if no buffers have been allocated yet, no need to raise a warning + */ if (timeout == 0) { + if (!ctx->buffers) + return NULL; + for (i = 0; i < ctx->num_buffers; i++) { if (ctx->buffers[i].status != V4L2BUF_AVAILABLE) av_log(logger(ctx), AV_LOG_WARNING, "%s POLLERR\n", ctx->name); diff --git a/libavcodec/v4l2_m2m_dec.c b/libavcodec/v4l2_m2m_dec.c index 4944d085119..aa2d759e1ea 100644 --- a/libavcodec/v4l2_m2m_dec.c +++ b/libavcodec/v4l2_m2m_dec.c @@ -253,7 +253,7 @@ static const AVOption options[] = { .bsfs = bsf_name, \ .p.capabilities = AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING, \ .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | \ - FF_CODEC_CAP_SETS_PKT_DTS | FF_CODEC_CAP_INIT_CLEANUP, \ + FF_CODEC_CAP_INIT_CLEANUP, \ .p.wrapper_name = "v4l2m2m", \ } diff --git a/libavcodec/vaapi_av1.c b/libavcodec/vaapi_av1.c index d0339b2705a..85b508438e1 100644 --- a/libavcodec/vaapi_av1.c +++ b/libavcodec/vaapi_av1.c @@ -19,8 +19,7 @@ */ #include "libavutil/frame.h" -#include "libavutil/pixdesc.h" -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "vaapi_decode.h" #include "internal.h" #include "av1dec.h" @@ -434,11 +433,11 @@ static int vaapi_av1_decode_slice(AVCodecContext *avctx, return 0; } -const AVHWAccel ff_av1_vaapi_hwaccel = { - .name = "av1_vaapi", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_AV1, - .pix_fmt = AV_PIX_FMT_VAAPI, +const FFHWAccel ff_av1_vaapi_hwaccel = { + .p.name = "av1_vaapi", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_AV1, + .p.pix_fmt = AV_PIX_FMT_VAAPI, .start_frame = vaapi_av1_start_frame, .end_frame = vaapi_av1_end_frame, .decode_slice = vaapi_av1_decode_slice, diff --git a/libavcodec/vaapi_decode.c b/libavcodec/vaapi_decode.c index 134f10eca5f..dd55cbd6f1c 100644 --- a/libavcodec/vaapi_decode.c +++ b/libavcodec/vaapi_decode.c @@ -398,6 +398,11 @@ static const struct { MAP(MPEG4, MPEG4_ADVANCED_SIMPLE, MPEG4AdvancedSimple), MAP(MPEG4, MPEG4_MAIN, MPEG4Main ), +#if VA_CHECK_VERSION(1, 18, 0) + MAP(H264, H264_HIGH_10_INTRA, + H264High10 ), + MAP(H264, H264_HIGH_10, H264High10 ), +#endif MAP(H264, H264_CONSTRAINED_BASELINE, H264ConstrainedBaseline), MAP(H264, H264_MAIN, H264Main ), @@ -410,7 +415,9 @@ static const struct { #endif #if VA_CHECK_VERSION(1, 2, 0) && CONFIG_HEVC_VAAPI_HWACCEL MAP(HEVC, HEVC_REXT, None, - ff_vaapi_parse_hevc_rext_profile ), + ff_vaapi_parse_hevc_rext_scc_profile ), + MAP(HEVC, HEVC_SCC, None, + ff_vaapi_parse_hevc_rext_scc_profile ), #endif MAP(MJPEG, MJPEG_HUFFMAN_BASELINE_DCT, JPEGBaseline), diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c index 9a58661b51a..bfca315a7ad 100644 --- a/libavcodec/vaapi_encode.c +++ b/libavcodec/vaapi_encode.c @@ -695,6 +695,7 @@ static int vaapi_encode_output(AVCodecContext *avctx, pkt->flags |= AV_PKT_FLAG_KEY; pkt->pts = pic->pts; + pkt->duration = pic->duration; vas = vaUnmapBuffer(ctx->hwctx->display, pic->output_buffer); if (vas != VA_STATUS_SUCCESS) { @@ -704,6 +705,14 @@ static int vaapi_encode_output(AVCodecContext *avctx, goto fail; } + // for no-delay encoders this is handled in generic codec + if (avctx->codec->capabilities & AV_CODEC_CAP_DELAY && + avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { + pkt->opaque = pic->opaque; + pkt->opaque_ref = pic->opaque_ref; + pic->opaque_ref = NULL; + } + av_buffer_unref(&pic->output_buffer_ref); pic->output_buffer = VA_INVALID_ID; @@ -777,6 +786,8 @@ static int vaapi_encode_free(AVCodecContext *avctx, av_frame_free(&pic->input_image); av_frame_free(&pic->recon_image); + av_buffer_unref(&pic->opaque_ref); + av_freep(&pic->param_buffers); av_freep(&pic->slices); // Output buffer should already be destroyed. @@ -1144,6 +1155,15 @@ static int vaapi_encode_send_frame(AVCodecContext *avctx, AVFrame *frame) pic->input_surface = (VASurfaceID)(uintptr_t)frame->data[3]; pic->pts = frame->pts; + pic->duration = frame->duration; + + if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { + err = av_buffer_replace(&pic->opaque_ref, frame->opaque_ref); + if (err < 0) + goto fail; + + pic->opaque = frame->opaque; + } av_frame_move_ref(pic->input_image, frame); diff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h index 359f954ffff..a1e639f56b0 100644 --- a/libavcodec/vaapi_encode.h +++ b/libavcodec/vaapi_encode.h @@ -75,8 +75,12 @@ typedef struct VAAPIEncodePicture { int64_t display_order; int64_t encode_order; int64_t pts; + int64_t duration; int force_idr; + void *opaque; + AVBufferRef *opaque_ref; + #if VA_CHECK_VERSION(1, 0, 0) // ROI regions. VAEncROI *roi; diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c index dd17be2190a..9ad017d5402 100644 --- a/libavcodec/vaapi_encode_h264.c +++ b/libavcodec/vaapi_encode_h264.c @@ -23,9 +23,11 @@ #include "libavutil/avassert.h" #include "libavutil/common.h" +#include "libavutil/pixdesc.h" #include "libavutil/internal.h" #include "libavutil/opt.h" +#include "atsc_a53.h" #include "avcodec.h" #include "cbs.h" #include "cbs_h264.h" @@ -41,6 +43,7 @@ enum { SEI_TIMING = 0x01, SEI_IDENTIFIER = 0x02, SEI_RECOVERY_POINT = 0x04, + SEI_A53_CC = 0x08, }; // Random (version 4) ISO 11578 UUID. @@ -99,6 +102,8 @@ typedef struct VAAPIEncodeH264Context { H264RawSEIRecoveryPoint sei_recovery_point; SEIRawUserDataUnregistered sei_identifier; char *sei_identifier_string; + SEIRawUserDataRegistered sei_a53cc; + void *sei_a53cc_data; int aud_needed; int sei_needed; @@ -249,6 +254,13 @@ static int vaapi_encode_h264_write_extra_header(AVCodecContext *avctx, if (err < 0) goto fail; } + if (priv->sei_needed & SEI_A53_CC) { + err = ff_cbs_sei_add_message(priv->cbc, au, 1, + SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35, + &priv->sei_a53cc, NULL); + if (err < 0) + goto fail; + } priv->sei_needed = 0; @@ -290,10 +302,21 @@ static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx) H264RawPPS *pps = &priv->raw_pps; VAEncSequenceParameterBufferH264 *vseq = ctx->codec_sequence_params; VAEncPictureParameterBufferH264 *vpic = ctx->codec_picture_params; + const AVPixFmtDescriptor *desc; + int bit_depth; memset(sps, 0, sizeof(*sps)); memset(pps, 0, sizeof(*pps)); + desc = av_pix_fmt_desc_get(priv->common.input_frames->sw_format); + av_assert0(desc); + if (desc->nb_components == 1 || desc->log2_chroma_w != 1 || desc->log2_chroma_h != 1) { + av_log(avctx, AV_LOG_ERROR, "Chroma format of input pixel format " + "%s is not supported.\n", desc->name); + return AVERROR(EINVAL); + } + bit_depth = desc->comp[0].depth; + sps->nal_unit_header.nal_ref_idc = 3; sps->nal_unit_header.nal_unit_type = H264_NAL_SPS; @@ -303,11 +326,11 @@ static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx) avctx->profile == FF_PROFILE_H264_MAIN) sps->constraint_set1_flag = 1; - if (avctx->profile == FF_PROFILE_H264_HIGH) + if (avctx->profile == FF_PROFILE_H264_HIGH || avctx->profile == FF_PROFILE_H264_HIGH_10) sps->constraint_set3_flag = ctx->gop_size == 1; if (avctx->profile == FF_PROFILE_H264_MAIN || - avctx->profile == FF_PROFILE_H264_HIGH) { + avctx->profile == FF_PROFILE_H264_HIGH || avctx->profile == FF_PROFILE_H264_HIGH_10) { sps->constraint_set4_flag = 1; sps->constraint_set5_flag = ctx->b_per_p == 0; } @@ -348,10 +371,14 @@ static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx) sps->seq_parameter_set_id = 0; sps->chroma_format_idc = 1; + sps->bit_depth_luma_minus8 = bit_depth - 8; + sps->bit_depth_chroma_minus8 = bit_depth - 8; sps->log2_max_frame_num_minus4 = 4; - sps->pic_order_cnt_type = 0; - sps->log2_max_pic_order_cnt_lsb_minus4 = 4; + sps->pic_order_cnt_type = ctx->max_b_depth ? 0 : 2; + if (sps->pic_order_cnt_type == 0) { + sps->log2_max_pic_order_cnt_lsb_minus4 = 4; + } sps->max_num_ref_frames = priv->dpb_frames; @@ -632,6 +659,10 @@ static int vaapi_encode_h264_init_picture_params(AVCodecContext *avctx, } } hpic->pic_order_cnt = pic->display_order - hpic->last_idr_frame; + if (priv->raw_sps.pic_order_cnt_type == 2) { + hpic->pic_order_cnt *= 2; + } + hpic->dpb_delay = pic->display_order - pic->encode_order + ctx->max_b_depth; hpic->cpb_delay = pic->encode_order - hpic->last_idr_frame; @@ -675,6 +706,22 @@ static int vaapi_encode_h264_init_picture_params(AVCodecContext *avctx, priv->sei_needed |= SEI_RECOVERY_POINT; } + if (priv->sei & SEI_A53_CC) { + int err; + size_t sei_a53cc_len; + av_freep(&priv->sei_a53cc_data); + err = ff_alloc_a53_sei(pic->input_image, 0, &priv->sei_a53cc_data, &sei_a53cc_len); + if (err < 0) + return err; + if (priv->sei_a53cc_data != NULL) { + priv->sei_a53cc.itu_t_t35_country_code = 181; + priv->sei_a53cc.data = (uint8_t *)priv->sei_a53cc_data + 1; + priv->sei_a53cc.data_length = sei_a53cc_len - 1; + + priv->sei_needed |= SEI_A53_CC; + } + } + vpic->CurrPic = (VAPictureH264) { .picture_id = pic->recon_surface, .frame_idx = hpic->frame_num, @@ -1111,6 +1158,9 @@ static av_cold int vaapi_encode_h264_configure(AVCodecContext *avctx) } static const VAAPIEncodeProfile vaapi_encode_h264_profiles[] = { +#if VA_CHECK_VERSION(1, 18, 0) + { FF_PROFILE_H264_HIGH_10, 10, 3, 1, 1, VAProfileH264High10 }, +#endif { FF_PROFILE_H264_HIGH, 8, 3, 1, 1, VAProfileH264High }, { FF_PROFILE_H264_MAIN, 8, 3, 1, 1, VAProfileH264Main }, { FF_PROFILE_H264_CONSTRAINED_BASELINE, @@ -1175,10 +1225,9 @@ static av_cold int vaapi_encode_h264_init(AVCodecContext *avctx) av_log(avctx, AV_LOG_ERROR, "H.264 extended profile " "is not supported.\n"); return AVERROR_PATCHWELCOME; - case FF_PROFILE_H264_HIGH_10: case FF_PROFILE_H264_HIGH_10_INTRA: - av_log(avctx, AV_LOG_ERROR, "H.264 10-bit profiles " - "are not supported.\n"); + av_log(avctx, AV_LOG_ERROR, "H.264 high 10 intra profile " + "is not supported.\n"); return AVERROR_PATCHWELCOME; case FF_PROFILE_H264_HIGH_422: case FF_PROFILE_H264_HIGH_422_INTRA: @@ -1220,6 +1269,7 @@ static av_cold int vaapi_encode_h264_close(AVCodecContext *avctx) ff_cbs_fragment_free(&priv->current_access_unit); ff_cbs_close(&priv->cbc); av_freep(&priv->sei_identifier_string); + av_freep(&priv->sei_a53cc_data); return ff_vaapi_encode_close(avctx); } @@ -1246,7 +1296,7 @@ static const AVOption vaapi_encode_h264_options[] = { { "sei", "Set SEI to include", OFFSET(sei), AV_OPT_TYPE_FLAGS, - { .i64 = SEI_IDENTIFIER | SEI_TIMING | SEI_RECOVERY_POINT }, + { .i64 = SEI_IDENTIFIER | SEI_TIMING | SEI_RECOVERY_POINT | SEI_A53_CC }, 0, INT_MAX, FLAGS, "sei" }, { "identifier", "Include encoder version identifier", 0, AV_OPT_TYPE_CONST, { .i64 = SEI_IDENTIFIER }, @@ -1257,6 +1307,9 @@ static const AVOption vaapi_encode_h264_options[] = { { "recovery_point", "Include recovery points where appropriate", 0, AV_OPT_TYPE_CONST, { .i64 = SEI_RECOVERY_POINT }, INT_MIN, INT_MAX, FLAGS, "sei" }, + { "a53_cc", "Include A/53 caption data", + 0, AV_OPT_TYPE_CONST, { .i64 = SEI_A53_CC }, + INT_MIN, INT_MAX, FLAGS, "sei" }, { "profile", "Set profile (profile_idc and constraint_set*_flag)", OFFSET(profile), AV_OPT_TYPE_INT, @@ -1267,6 +1320,7 @@ static const AVOption vaapi_encode_h264_options[] = { { PROFILE("constrained_baseline", FF_PROFILE_H264_CONSTRAINED_BASELINE) }, { PROFILE("main", FF_PROFILE_H264_MAIN) }, { PROFILE("high", FF_PROFILE_H264_HIGH) }, + { PROFILE("high10", FF_PROFILE_H264_HIGH_10) }, #undef PROFILE { "level", "Set level (level_idc)", @@ -1330,7 +1384,7 @@ const FFCodec ff_h264_vaapi_encoder = { .close = &vaapi_encode_h264_close, .p.priv_class = &vaapi_encode_h264_class, .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE | - AV_CODEC_CAP_DR1, + AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, .defaults = vaapi_encode_h264_defaults, diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c index 0833053c745..aa7e532f9a4 100644 --- a/libavcodec/vaapi_encode_h265.c +++ b/libavcodec/vaapi_encode_h265.c @@ -27,6 +27,7 @@ #include "libavutil/opt.h" #include "libavutil/mastering_display_metadata.h" +#include "atsc_a53.h" #include "avcodec.h" #include "cbs.h" #include "cbs_h265.h" @@ -41,6 +42,7 @@ enum { SEI_MASTERING_DISPLAY = 0x08, SEI_CONTENT_LIGHT_LEVEL = 0x10, + SEI_A53_CC = 0x20, }; typedef struct VAAPIEncodeH265Picture { @@ -85,6 +87,8 @@ typedef struct VAAPIEncodeH265Context { SEIRawMasteringDisplayColourVolume sei_mastering_display; SEIRawContentLightLevelInfo sei_content_light_level; + SEIRawUserDataRegistered sei_a53cc; + void *sei_a53cc_data; CodedBitstreamContext *cbc; CodedBitstreamFragment current_access_unit; @@ -227,6 +231,13 @@ static int vaapi_encode_h265_write_extra_header(AVCodecContext *avctx, if (err < 0) goto fail; } + if (priv->sei_needed & SEI_A53_CC) { + err = ff_cbs_sei_add_message(priv->cbc, au, 1, + SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35, + &priv->sei_a53cc, NULL); + if (err < 0) + goto fail; + } priv->sei_needed = 0; @@ -882,6 +893,22 @@ static int vaapi_encode_h265_init_picture_params(AVCodecContext *avctx, } } + if (priv->sei & SEI_A53_CC) { + int err; + size_t sei_a53cc_len; + av_freep(&priv->sei_a53cc_data); + err = ff_alloc_a53_sei(pic->input_image, 0, &priv->sei_a53cc_data, &sei_a53cc_len); + if (err < 0) + return err; + if (priv->sei_a53cc_data != NULL) { + priv->sei_a53cc.itu_t_t35_country_code = 181; + priv->sei_a53cc.data = (uint8_t *)priv->sei_a53cc_data + 1; + priv->sei_a53cc.data_length = sei_a53cc_len - 1; + + priv->sei_needed |= SEI_A53_CC; + } + } + vpic->decoded_curr_pic = (VAPictureHEVC) { .picture_id = pic->recon_surface, .pic_order_cnt = hpic->pic_order_cnt, @@ -1349,6 +1376,7 @@ static av_cold int vaapi_encode_h265_close(AVCodecContext *avctx) ff_cbs_fragment_free(&priv->current_access_unit); ff_cbs_close(&priv->cbc); + av_freep(&priv->sei_a53cc_data); return ff_vaapi_encode_close(avctx); } @@ -1407,7 +1435,7 @@ static const AVOption vaapi_encode_h265_options[] = { { "sei", "Set SEI to include", OFFSET(sei), AV_OPT_TYPE_FLAGS, - { .i64 = SEI_MASTERING_DISPLAY | SEI_CONTENT_LIGHT_LEVEL }, + { .i64 = SEI_MASTERING_DISPLAY | SEI_CONTENT_LIGHT_LEVEL | SEI_A53_CC }, 0, INT_MAX, FLAGS, "sei" }, { "hdr", "Include HDR metadata for mastering display colour volume " @@ -1415,6 +1443,11 @@ static const AVOption vaapi_encode_h265_options[] = { 0, AV_OPT_TYPE_CONST, { .i64 = SEI_MASTERING_DISPLAY | SEI_CONTENT_LIGHT_LEVEL }, INT_MIN, INT_MAX, FLAGS, "sei" }, + { "a53_cc", + "Include A/53 caption data", + 0, AV_OPT_TYPE_CONST, + { .i64 = SEI_A53_CC }, + INT_MIN, INT_MAX, FLAGS, "sei" }, { "tiles", "Tile columns x rows", OFFSET(common.tile_cols), AV_OPT_TYPE_IMAGE_SIZE, @@ -1454,7 +1487,7 @@ const FFCodec ff_hevc_vaapi_encoder = { .close = &vaapi_encode_h265_close, .p.priv_class = &vaapi_encode_h265_class, .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE | - AV_CODEC_CAP_DR1, + AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, .defaults = vaapi_encode_h265_defaults, diff --git a/libavcodec/vaapi_encode_mjpeg.c b/libavcodec/vaapi_encode_mjpeg.c index 5ef93cd102f..cb7588b94ba 100644 --- a/libavcodec/vaapi_encode_mjpeg.c +++ b/libavcodec/vaapi_encode_mjpeg.c @@ -574,7 +574,8 @@ const FFCodec ff_mjpeg_vaapi_encoder = { FF_CODEC_RECEIVE_PACKET_CB(&ff_vaapi_encode_receive_packet), .close = &vaapi_encode_mjpeg_close, .p.priv_class = &vaapi_encode_mjpeg_class, - .p.capabilities = AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_DR1 | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, .defaults = vaapi_encode_mjpeg_defaults, diff --git a/libavcodec/vaapi_encode_mpeg2.c b/libavcodec/vaapi_encode_mpeg2.c index 38e1d83f682..9261d19a831 100644 --- a/libavcodec/vaapi_encode_mpeg2.c +++ b/libavcodec/vaapi_encode_mpeg2.c @@ -698,7 +698,7 @@ const FFCodec ff_mpeg2_vaapi_encoder = { .close = &vaapi_encode_mpeg2_close, .p.priv_class = &vaapi_encode_mpeg2_class, .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE | - AV_CODEC_CAP_DR1, + AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, .defaults = vaapi_encode_mpeg2_defaults, diff --git a/libavcodec/vaapi_encode_vp8.c b/libavcodec/vaapi_encode_vp8.c index 93e543d7984..ae6a8d313ca 100644 --- a/libavcodec/vaapi_encode_vp8.c +++ b/libavcodec/vaapi_encode_vp8.c @@ -253,7 +253,7 @@ const FFCodec ff_vp8_vaapi_encoder = { .close = &ff_vaapi_encode_close, .p.priv_class = &vaapi_encode_vp8_class, .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE | - AV_CODEC_CAP_DR1, + AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, .defaults = vaapi_encode_vp8_defaults, diff --git a/libavcodec/vaapi_encode_vp9.c b/libavcodec/vaapi_encode_vp9.c index b4c5588730a..af1353cea80 100644 --- a/libavcodec/vaapi_encode_vp9.c +++ b/libavcodec/vaapi_encode_vp9.c @@ -308,7 +308,7 @@ const FFCodec ff_vp9_vaapi_encoder = { .close = &ff_vaapi_encode_close, .p.priv_class = &vaapi_encode_vp9_class, .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE | - AV_CODEC_CAP_DR1, + AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, .defaults = vaapi_encode_vp9_defaults, diff --git a/libavcodec/vaapi_h264.c b/libavcodec/vaapi_h264.c index 9332aa6f310..55cf5a05ee3 100644 --- a/libavcodec/vaapi_h264.c +++ b/libavcodec/vaapi_h264.c @@ -22,7 +22,7 @@ #include "h264dec.h" #include "h264_ps.h" -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "vaapi_decode.h" /** @@ -385,11 +385,11 @@ static int vaapi_h264_decode_slice(AVCodecContext *avctx, return 0; } -const AVHWAccel ff_h264_vaapi_hwaccel = { - .name = "h264_vaapi", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_H264, - .pix_fmt = AV_PIX_FMT_VAAPI, +const FFHWAccel ff_h264_vaapi_hwaccel = { + .p.name = "h264_vaapi", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_H264, + .p.pix_fmt = AV_PIX_FMT_VAAPI, .start_frame = &vaapi_h264_start_frame, .end_frame = &vaapi_h264_end_frame, .decode_slice = &vaapi_h264_decode_slice, diff --git a/libavcodec/vaapi_hevc.c b/libavcodec/vaapi_hevc.c index 20fb36adfa8..d464fda18c0 100644 --- a/libavcodec/vaapi_hevc.c +++ b/libavcodec/vaapi_hevc.c @@ -25,7 +25,7 @@ #include "avcodec.h" #include "hevcdec.h" -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "vaapi_decode.h" #include "vaapi_hevc.h" #include "h265_profile_level.h" @@ -60,10 +60,10 @@ static void fill_vaapi_pic(VAPictureHEVC *va_pic, const HEVCFrame *pic, int rps_ if (pic->flags & HEVC_FRAME_FLAG_LONG_REF) va_pic->flags |= VA_PICTURE_HEVC_LONG_TERM_REFERENCE; - if (pic->frame->interlaced_frame) { + if (pic->frame->flags & AV_FRAME_FLAG_INTERLACED) { va_pic->flags |= VA_PICTURE_HEVC_FIELD_PIC; - if (!pic->frame->top_field_first) + if (!(pic->frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)) va_pic->flags |= VA_PICTURE_HEVC_BOTTOM_FIELD; } } @@ -71,6 +71,7 @@ static void fill_vaapi_pic(VAPictureHEVC *va_pic, const HEVCFrame *pic, int rps_ static int find_frame_rps_type(const HEVCContext *h, const HEVCFrame *pic) { VASurfaceID pic_surf = ff_vaapi_get_surface_id(pic->frame); + const HEVCFrame *current_picture = h->ref; int i; for (i = 0; i < h->rps[ST_CURR_BEF].nb_refs; i++) { @@ -88,6 +89,9 @@ static int find_frame_rps_type(const HEVCContext *h, const HEVCFrame *pic) return VA_PICTURE_HEVC_RPS_LT_CURR; } + if (h->ps.pps->pps_curr_pic_ref_enabled_flag && current_picture->poc == pic->poc) + return VA_PICTURE_HEVC_LONG_TERM_REFERENCE; + return 0; } @@ -100,7 +104,8 @@ static void fill_vaapi_reference_frames(const HEVCContext *h, VAPictureParameter const HEVCFrame *frame = NULL; while (!frame && j < FF_ARRAY_ELEMS(h->DPB)) { - if (&h->DPB[j] != current_picture && (h->DPB[j].flags & (HEVC_FRAME_FLAG_LONG_REF | HEVC_FRAME_FLAG_SHORT_REF))) + if ((&h->DPB[j] != current_picture || h->ps.pps->pps_curr_pic_ref_enabled_flag) && + (h->DPB[j].flags & (HEVC_FRAME_FLAG_LONG_REF | HEVC_FRAME_FLAG_SHORT_REF))) frame = &h->DPB[j]; j++; } @@ -126,6 +131,10 @@ static int vaapi_hevc_start_frame(AVCodecContext *avctx, const ScalingList *scaling_list = NULL; int pic_param_size, err, i; +#if VA_CHECK_VERSION(1, 2, 0) + int num_comps, pre_palette_size; +#endif + VAPictureParameterBufferHEVC *pic_param = (VAPictureParameterBufferHEVC *)&pic->pic_param; pic->pic.output_surface = ff_vaapi_get_surface_id(h->ref->frame); @@ -218,7 +227,8 @@ static int vaapi_hevc_start_frame(AVCodecContext *avctx, } #if VA_CHECK_VERSION(1, 2, 0) - if (avctx->profile == FF_PROFILE_HEVC_REXT) { + if (avctx->profile == FF_PROFILE_HEVC_REXT || + avctx->profile == FF_PROFILE_HEVC_SCC) { pic->pic_param.rext = (VAPictureParameterBufferHEVCRext) { .range_extension_pic_fields.bits = { .transform_skip_rotation_enabled_flag = sps->transform_skip_rotation_enabled_flag, @@ -245,8 +255,46 @@ static int vaapi_hevc_start_frame(AVCodecContext *avctx, for (i = 0; i < 6; i++) pic->pic_param.rext.cr_qp_offset_list[i] = pps->cr_qp_offset_list[i]; } + + pre_palette_size = pps->pps_palette_predictor_initializers_present_flag ? + pps->pps_num_palette_predictor_initializers : + (sps->sps_palette_predictor_initializers_present_flag ? + sps->sps_num_palette_predictor_initializers : + 0); + + if (avctx->profile == FF_PROFILE_HEVC_SCC) { + pic->pic_param.scc = (VAPictureParameterBufferHEVCScc) { + .screen_content_pic_fields.bits = { + .pps_curr_pic_ref_enabled_flag = pps->pps_curr_pic_ref_enabled_flag, + .palette_mode_enabled_flag = sps->palette_mode_enabled_flag, + .motion_vector_resolution_control_idc = sps->motion_vector_resolution_control_idc, + .intra_boundary_filtering_disabled_flag = sps->intra_boundary_filtering_disabled_flag, + .residual_adaptive_colour_transform_enabled_flag + = pps->residual_adaptive_colour_transform_enabled_flag, + .pps_slice_act_qp_offsets_present_flag = pps->pps_slice_act_qp_offsets_present_flag, + }, + .palette_max_size = sps->palette_max_size, + .delta_palette_max_predictor_size = sps->delta_palette_max_predictor_size, + .predictor_palette_size = pre_palette_size, + .pps_act_y_qp_offset_plus5 = pps->residual_adaptive_colour_transform_enabled_flag ? + pps->pps_act_y_qp_offset + 5 : 0, + .pps_act_cb_qp_offset_plus5 = pps->residual_adaptive_colour_transform_enabled_flag ? + pps->pps_act_cb_qp_offset + 5 : 0, + .pps_act_cr_qp_offset_plus3 = pps->residual_adaptive_colour_transform_enabled_flag ? + pps->pps_act_cr_qp_offset + 3 : 0, + }; + + num_comps = pps->monochrome_palette_flag ? 1 : 3; + for (int comp = 0; comp < num_comps; comp++) + for (int j = 0; j < pre_palette_size; j++) + pic->pic_param.scc.predictor_palette_entries[comp][j] = + pps->pps_palette_predictor_initializers_present_flag ? + pps->pps_palette_predictor_initializer[comp][j]: + sps->sps_palette_predictor_initializer[comp][j]; + } + #endif - pic_param_size = avctx->profile == FF_PROFILE_HEVC_REXT ? + pic_param_size = avctx->profile >= FF_PROFILE_HEVC_REXT ? sizeof(pic->pic_param) : sizeof(VAPictureParameterBufferHEVC); err = ff_vaapi_decode_make_param_buffer(avctx, &pic->pic, @@ -299,7 +347,7 @@ static int vaapi_hevc_end_frame(AVCodecContext *avctx) VASliceParameterBufferHEVC *last_slice_param = (VASliceParameterBufferHEVC *)&pic->last_slice_param; int ret; - int slice_param_size = avctx->profile == FF_PROFILE_HEVC_REXT ? + int slice_param_size = avctx->profile >= FF_PROFILE_HEVC_REXT ? sizeof(pic->last_slice_param) : sizeof(VASliceParameterBufferHEVC); if (pic->last_size) { @@ -413,7 +461,7 @@ static int vaapi_hevc_decode_slice(AVCodecContext *avctx, VAAPIDecodePictureHEVC *pic = h->ref->hwaccel_picture_private; VASliceParameterBufferHEVC *last_slice_param = (VASliceParameterBufferHEVC *)&pic->last_slice_param; - int slice_param_size = avctx->profile == FF_PROFILE_HEVC_REXT ? + int slice_param_size = avctx->profile >= FF_PROFILE_HEVC_REXT ? sizeof(pic->last_slice_param) : sizeof(VASliceParameterBufferHEVC); int nb_list = (sh->slice_type == HEVC_SLICE_B) ? @@ -478,11 +526,15 @@ static int vaapi_hevc_decode_slice(AVCodecContext *avctx, fill_pred_weight_table(avctx, h, sh, last_slice_param); #if VA_CHECK_VERSION(1, 2, 0) - if (avctx->profile == FF_PROFILE_HEVC_REXT) { + if (avctx->profile >= FF_PROFILE_HEVC_REXT) { pic->last_slice_param.rext = (VASliceParameterBufferHEVCRext) { .slice_ext_flags.bits = { .cu_chroma_qp_offset_enabled_flag = sh->cu_chroma_qp_offset_enabled_flag, + .use_integer_mv_flag = sh->use_integer_mv_flag, }, + .slice_act_y_qp_offset = sh->slice_act_y_qp_offset, + .slice_act_cb_qp_offset = sh->slice_act_cb_qp_offset, + .slice_act_cr_qp_offset = sh->slice_act_cr_qp_offset, }; for (i = 0; i < 15 && i < sh->nb_refs[L0]; i++) { pic->last_slice_param.rext.luma_offset_l0[i] = sh->luma_offset_l0[i]; @@ -490,12 +542,6 @@ static int vaapi_hevc_decode_slice(AVCodecContext *avctx, pic->last_slice_param.rext.ChromaOffsetL0[i][1] = sh->chroma_offset_l0[i][1]; } - for (i = 0; i < 15 && i < sh->nb_refs[L0]; i++) { - pic->last_slice_param.rext.luma_offset_l0[i] = sh->luma_offset_l0[i]; - pic->last_slice_param.rext.ChromaOffsetL0[i][0] = sh->chroma_offset_l0[i][0]; - pic->last_slice_param.rext.ChromaOffsetL0[i][1] = sh->chroma_offset_l0[i][1]; - } - if (sh->slice_type == HEVC_SLICE_B) { for (i = 0; i < 15 && i < sh->nb_refs[L1]; i++) { pic->last_slice_param.rext.luma_offset_l1[i] = sh->luma_offset_l1[i]; @@ -544,9 +590,9 @@ static int ptl_convert(const PTLCommon *general_ptl, H265RawProfileTierLevel *h2 } /* - * Find exact va_profile for HEVC Range Extension + * Find exact va_profile for HEVC Range Extension and Screen Content Coding Extension */ -VAProfile ff_vaapi_parse_hevc_rext_profile(AVCodecContext *avctx) +VAProfile ff_vaapi_parse_hevc_rext_scc_profile(AVCodecContext *avctx) { const HEVCContext *h = avctx->priv_data; const HEVCSPS *sps = h->ps.sps; @@ -585,6 +631,16 @@ VAProfile ff_vaapi_parse_hevc_rext_profile(AVCodecContext *avctx) else if (!strcmp(profile->name, "Main 4:4:4 12") || !strcmp(profile->name, "Main 4:4:4 12 Intra")) return VAProfileHEVCMain444_12; + else if (!strcmp(profile->name, "Screen-Extended Main")) + return VAProfileHEVCSccMain; + else if (!strcmp(profile->name, "Screen-Extended Main 10")) + return VAProfileHEVCSccMain10; + else if (!strcmp(profile->name, "Screen-Extended Main 4:4:4")) + return VAProfileHEVCSccMain444; +#if VA_CHECK_VERSION(1, 8, 0) + else if (!strcmp(profile->name, "Screen-Extended Main 4:4:4 10")) + return VAProfileHEVCSccMain444_10; +#endif #else av_log(avctx, AV_LOG_WARNING, "HEVC profile %s is " "not supported with this VA version.\n", profile->name); @@ -598,11 +654,11 @@ VAProfile ff_vaapi_parse_hevc_rext_profile(AVCodecContext *avctx) return VAProfileNone; } -const AVHWAccel ff_hevc_vaapi_hwaccel = { - .name = "hevc_vaapi", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_HEVC, - .pix_fmt = AV_PIX_FMT_VAAPI, +const FFHWAccel ff_hevc_vaapi_hwaccel = { + .p.name = "hevc_vaapi", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_HEVC, + .p.pix_fmt = AV_PIX_FMT_VAAPI, .start_frame = vaapi_hevc_start_frame, .end_frame = vaapi_hevc_end_frame, .decode_slice = vaapi_hevc_decode_slice, diff --git a/libavcodec/vaapi_hevc.h b/libavcodec/vaapi_hevc.h index b3b0e6fc1e9..449635d0d70 100644 --- a/libavcodec/vaapi_hevc.h +++ b/libavcodec/vaapi_hevc.h @@ -22,6 +22,6 @@ #include #include "avcodec.h" -VAProfile ff_vaapi_parse_hevc_rext_profile(AVCodecContext *avctx); +VAProfile ff_vaapi_parse_hevc_rext_scc_profile(AVCodecContext *avctx); #endif /* AVCODEC_VAAPI_HEVC_H */ diff --git a/libavcodec/vaapi_mjpeg.c b/libavcodec/vaapi_mjpeg.c index 81582114b64..5b8d47bb2a5 100644 --- a/libavcodec/vaapi_mjpeg.c +++ b/libavcodec/vaapi_mjpeg.c @@ -19,7 +19,7 @@ #include #include -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "vaapi_decode.h" #include "mjpegdec.h" @@ -142,11 +142,11 @@ static int vaapi_mjpeg_decode_slice(AVCodecContext *avctx, return err; } -const AVHWAccel ff_mjpeg_vaapi_hwaccel = { - .name = "mjpeg_vaapi", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MJPEG, - .pix_fmt = AV_PIX_FMT_VAAPI, +const FFHWAccel ff_mjpeg_vaapi_hwaccel = { + .p.name = "mjpeg_vaapi", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MJPEG, + .p.pix_fmt = AV_PIX_FMT_VAAPI, .start_frame = &vaapi_mjpeg_start_frame, .end_frame = &vaapi_mjpeg_end_frame, .decode_slice = &vaapi_mjpeg_decode_slice, diff --git a/libavcodec/vaapi_mpeg2.c b/libavcodec/vaapi_mpeg2.c index 5e2b8891375..eeb4e873210 100644 --- a/libavcodec/vaapi_mpeg2.c +++ b/libavcodec/vaapi_mpeg2.c @@ -20,7 +20,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "mpegutils.h" #include "mpegvideo.h" #include "mpegvideodec.h" @@ -172,11 +172,11 @@ static int vaapi_mpeg2_decode_slice(AVCodecContext *avctx, const uint8_t *buffer return 0; } -const AVHWAccel ff_mpeg2_vaapi_hwaccel = { - .name = "mpeg2_vaapi", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG2VIDEO, - .pix_fmt = AV_PIX_FMT_VAAPI, +const FFHWAccel ff_mpeg2_vaapi_hwaccel = { + .p.name = "mpeg2_vaapi", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG2VIDEO, + .p.pix_fmt = AV_PIX_FMT_VAAPI, .start_frame = &vaapi_mpeg2_start_frame, .end_frame = &vaapi_mpeg2_end_frame, .decode_slice = &vaapi_mpeg2_decode_slice, diff --git a/libavcodec/vaapi_mpeg4.c b/libavcodec/vaapi_mpeg4.c index 4e74e0382b6..363b686e42e 100644 --- a/libavcodec/vaapi_mpeg4.c +++ b/libavcodec/vaapi_mpeg4.c @@ -23,7 +23,7 @@ #include "config_components.h" #include "h263.h" -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "mpeg4videodec.h" #include "mpegvideo.h" #include "mpegvideodec.h" @@ -180,11 +180,11 @@ static int vaapi_mpeg4_decode_slice(AVCodecContext *avctx, const uint8_t *buffer } #if CONFIG_MPEG4_VAAPI_HWACCEL -const AVHWAccel ff_mpeg4_vaapi_hwaccel = { - .name = "mpeg4_vaapi", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG4, - .pix_fmt = AV_PIX_FMT_VAAPI, +const FFHWAccel ff_mpeg4_vaapi_hwaccel = { + .p.name = "mpeg4_vaapi", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG4, + .p.pix_fmt = AV_PIX_FMT_VAAPI, .start_frame = &vaapi_mpeg4_start_frame, .end_frame = &vaapi_mpeg4_end_frame, .decode_slice = &vaapi_mpeg4_decode_slice, @@ -198,11 +198,11 @@ const AVHWAccel ff_mpeg4_vaapi_hwaccel = { #endif #if CONFIG_H263_VAAPI_HWACCEL -const AVHWAccel ff_h263_vaapi_hwaccel = { - .name = "h263_vaapi", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_H263, - .pix_fmt = AV_PIX_FMT_VAAPI, +const FFHWAccel ff_h263_vaapi_hwaccel = { + .p.name = "h263_vaapi", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_H263, + .p.pix_fmt = AV_PIX_FMT_VAAPI, .start_frame = &vaapi_mpeg4_start_frame, .end_frame = &vaapi_mpeg4_end_frame, .decode_slice = &vaapi_mpeg4_decode_slice, diff --git a/libavcodec/vaapi_vc1.c b/libavcodec/vaapi_vc1.c index fb2132e814b..09a5c852fc2 100644 --- a/libavcodec/vaapi_vc1.c +++ b/libavcodec/vaapi_vc1.c @@ -22,11 +22,10 @@ #include "config_components.h" -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "mpegvideodec.h" #include "vaapi_decode.h" #include "vc1.h" -#include "vc1data.h" /** Translate FFmpeg MV modes to VA API */ static int get_VAMvModeVC1(enum MVModes mv_mode) @@ -501,11 +500,11 @@ static int vaapi_vc1_decode_slice(AVCodecContext *avctx, const uint8_t *buffer, } #if CONFIG_WMV3_VAAPI_HWACCEL -const AVHWAccel ff_wmv3_vaapi_hwaccel = { - .name = "wmv3_vaapi", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_WMV3, - .pix_fmt = AV_PIX_FMT_VAAPI, +const FFHWAccel ff_wmv3_vaapi_hwaccel = { + .p.name = "wmv3_vaapi", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_WMV3, + .p.pix_fmt = AV_PIX_FMT_VAAPI, .start_frame = &vaapi_vc1_start_frame, .end_frame = &vaapi_vc1_end_frame, .decode_slice = &vaapi_vc1_decode_slice, @@ -518,11 +517,11 @@ const AVHWAccel ff_wmv3_vaapi_hwaccel = { }; #endif -const AVHWAccel ff_vc1_vaapi_hwaccel = { - .name = "vc1_vaapi", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VC1, - .pix_fmt = AV_PIX_FMT_VAAPI, +const FFHWAccel ff_vc1_vaapi_hwaccel = { + .p.name = "vc1_vaapi", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VC1, + .p.pix_fmt = AV_PIX_FMT_VAAPI, .start_frame = &vaapi_vc1_start_frame, .end_frame = &vaapi_vc1_end_frame, .decode_slice = &vaapi_vc1_decode_slice, diff --git a/libavcodec/vaapi_vp8.c b/libavcodec/vaapi_vp8.c index 5b18bf8f347..31137a45bd1 100644 --- a/libavcodec/vaapi_vp8.c +++ b/libavcodec/vaapi_vp8.c @@ -19,7 +19,7 @@ #include #include -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "vaapi_decode.h" #include "vp8.h" @@ -220,11 +220,11 @@ static int vaapi_vp8_decode_slice(AVCodecContext *avctx, return err; } -const AVHWAccel ff_vp8_vaapi_hwaccel = { - .name = "vp8_vaapi", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VP8, - .pix_fmt = AV_PIX_FMT_VAAPI, +const FFHWAccel ff_vp8_vaapi_hwaccel = { + .p.name = "vp8_vaapi", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VP8, + .p.pix_fmt = AV_PIX_FMT_VAAPI, .start_frame = &vaapi_vp8_start_frame, .end_frame = &vaapi_vp8_end_frame, .decode_slice = &vaapi_vp8_decode_slice, diff --git a/libavcodec/vaapi_vp9.c b/libavcodec/vaapi_vp9.c index 776382f6837..9dc7d5e72b9 100644 --- a/libavcodec/vaapi_vp9.c +++ b/libavcodec/vaapi_vp9.c @@ -22,7 +22,7 @@ #include "libavutil/pixdesc.h" -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "vaapi_decode.h" #include "vp9shared.h" @@ -168,11 +168,11 @@ static int vaapi_vp9_decode_slice(AVCodecContext *avctx, return 0; } -const AVHWAccel ff_vp9_vaapi_hwaccel = { - .name = "vp9_vaapi", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VP9, - .pix_fmt = AV_PIX_FMT_VAAPI, +const FFHWAccel ff_vp9_vaapi_hwaccel = { + .p.name = "vp9_vaapi", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VP9, + .p.pix_fmt = AV_PIX_FMT_VAAPI, .start_frame = vaapi_vp9_start_frame, .end_frame = vaapi_vp9_end_frame, .decode_slice = vaapi_vp9_decode_slice, diff --git a/libavcodec/vb.c b/libavcodec/vb.c index 8b0e2164734..5744faa983f 100644 --- a/libavcodec/vb.c +++ b/libavcodec/vb.c @@ -230,7 +230,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, } memcpy(frame->data[1], c->pal, AVPALETTE_SIZE); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = flags & VB_HAS_PALETTE; +FF_ENABLE_DEPRECATION_WARNINGS +#endif outptr = frame->data[0]; srcptr = c->frame; diff --git a/libavcodec/vble.c b/libavcodec/vble.c index 9307b0d165c..7711bf1bb10 100644 --- a/libavcodec/vble.c +++ b/libavcodec/vble.c @@ -135,7 +135,7 @@ static int vble_decode_frame(AVCodecContext *avctx, AVFrame *pic, return ret; /* Set flags */ - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; pic->pict_type = AV_PICTURE_TYPE_I; /* Version should always be 1 */ diff --git a/libavcodec/vbndec.c b/libavcodec/vbndec.c index d92dcd46b92..02ed43f874f 100644 --- a/libavcodec/vbndec.c +++ b/libavcodec/vbndec.c @@ -151,7 +151,7 @@ static int vbn_decode_frame(AVCodecContext *avctx, goto out; frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; if (format == VBN_FORMAT_RAW) { uint8_t *flipped = frame->data[0] + frame->linesize[0] * (frame->height - 1); diff --git a/libavcodec/vbnenc.c b/libavcodec/vbnenc.c index 45101382a3f..7ce91863d7f 100644 --- a/libavcodec/vbnenc.c +++ b/libavcodec/vbnenc.c @@ -153,7 +153,8 @@ const FFCodec ff_vbn_encoder = { CODEC_LONG_NAME("Vizrt Binary Image"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_VBN, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SLICE_THREADS, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SLICE_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .p.priv_class = &vbnenc_class, .init = vbn_init, FF_CODEC_ENCODE_CB(vbn_encode), diff --git a/libavcodec/vc1.c b/libavcodec/vc1.c index d4014d25abe..795c6db5418 100644 --- a/libavcodec/vc1.c +++ b/libavcodec/vc1.c @@ -418,6 +418,14 @@ static int decode_sequence_header_adv(VC1Context *v, GetBitContext *gb) v->s.loop_filter, v->chromaformat, v->broadcast, v->interlace, v->tfcntrflag, v->finterpflag); +#if FF_API_TICKS_PER_FRAME +FF_DISABLE_DEPRECATION_WARNINGS + if (v->broadcast) { // Pulldown may be present + v->s.avctx->ticks_per_frame = 2; + } +FF_ENABLE_DEPRECATION_WARNINGS +#endif + v->psf = get_bits1(gb); if (v->psf) { //PsF, 6.1.13 av_log(v->s.avctx, AV_LOG_ERROR, "Progressive Segmented Frame mode: not supported (yet)\n"); @@ -467,9 +475,6 @@ static int decode_sequence_header_adv(VC1Context *v, GetBitContext *gb) v->s.avctx->framerate.num = ff_vc1_fps_nr[nr - 1] * 1000; } } - if (v->broadcast) { // Pulldown may be present - v->s.avctx->ticks_per_frame = 2; - } } if (get_bits1(gb)) { diff --git a/libavcodec/vc1_mc.c b/libavcodec/vc1_mc.c index 1b8d8799b3c..8f0b3f6fab1 100644 --- a/libavcodec/vc1_mc.c +++ b/libavcodec/vc1_mc.c @@ -233,7 +233,7 @@ void ff_vc1_mc_1mv(VC1Context *v, int dir) luty = v->last_luty; lutuv = v->last_lutuv; use_ic = v->last_use_ic; - interlace = s->last_picture.f->interlaced_frame; + interlace = !!(s->last_picture.f->flags & AV_FRAME_FLAG_INTERLACED); } } else { srcY = s->next_picture.f->data[0]; @@ -242,7 +242,7 @@ void ff_vc1_mc_1mv(VC1Context *v, int dir) luty = v->next_luty; lutuv = v->next_lutuv; use_ic = v->next_use_ic; - interlace = s->next_picture.f->interlaced_frame; + interlace = !!(s->next_picture.f->flags & AV_FRAME_FLAG_INTERLACED); } if (!srcY || !srcU) { @@ -482,13 +482,13 @@ void ff_vc1_mc_4mv_luma(VC1Context *v, int n, int dir, int avg) srcY = s->last_picture.f->data[0]; luty = v->last_luty; use_ic = v->last_use_ic; - interlace = s->last_picture.f->interlaced_frame; + interlace = !!(s->last_picture.f->flags & AV_FRAME_FLAG_INTERLACED); } } else { srcY = s->next_picture.f->data[0]; luty = v->next_luty; use_ic = v->next_use_ic; - interlace = s->next_picture.f->interlaced_frame; + interlace = !!(s->next_picture.f->flags & AV_FRAME_FLAG_INTERLACED); } if (!srcY) { @@ -708,14 +708,14 @@ void ff_vc1_mc_4mv_chroma(VC1Context *v, int dir) srcV = s->last_picture.f->data[2]; lutuv = v->last_lutuv; use_ic = v->last_use_ic; - interlace = s->last_picture.f->interlaced_frame; + interlace = !!(s->last_picture.f->flags & AV_FRAME_FLAG_INTERLACED); } } else { srcU = s->next_picture.f->data[1]; srcV = s->next_picture.f->data[2]; lutuv = v->next_lutuv; use_ic = v->next_use_ic; - interlace = s->next_picture.f->interlaced_frame; + interlace = !!(s->next_picture.f->flags & AV_FRAME_FLAG_INTERLACED); } if (!srcU) { @@ -884,13 +884,13 @@ void ff_vc1_mc_4mv_chroma4(VC1Context *v, int dir, int dir2, int avg) srcV = s->next_picture.f->data[2]; lutuv = v->next_lutuv; use_ic = v->next_use_ic; - interlace = s->next_picture.f->interlaced_frame; + interlace = !!(s->next_picture.f->flags & AV_FRAME_FLAG_INTERLACED); } else { srcU = s->last_picture.f->data[1]; srcV = s->last_picture.f->data[2]; lutuv = v->last_lutuv; use_ic = v->last_use_ic; - interlace = s->last_picture.f->interlaced_frame; + interlace = !!(s->last_picture.f->flags & AV_FRAME_FLAG_INTERLACED); } if (!srcU) return; @@ -1034,7 +1034,7 @@ void ff_vc1_interp_mc(VC1Context *v) srcU = s->next_picture.f->data[1]; srcV = s->next_picture.f->data[2]; - interlace = s->next_picture.f->interlaced_frame; + interlace = !!(s->next_picture.f->flags & AV_FRAME_FLAG_INTERLACED); src_x = s->mb_x * 16 + (mx >> 2); src_y = s->mb_y * 16 + (my >> 2); diff --git a/libavcodec/vc1_parser.c b/libavcodec/vc1_parser.c index a459a2aa7d7..ec284dca009 100644 --- a/libavcodec/vc1_parser.c +++ b/libavcodec/vc1_parser.c @@ -89,11 +89,10 @@ static void vc1_extract_header(AVCodecParserContext *s, AVCodecContext *avctx, else s->pict_type = vpc->v.s.pict_type; - if (avctx->ticks_per_frame > 1){ + if (vpc->v.broadcast){ // process pulldown flags s->repeat_pict = 1; // Pulldown flags are only valid when 'broadcast' has been set. - // So ticks_per_frame will be 2 if (vpc->v.rff){ // repeat field s->repeat_pict = 2; @@ -112,8 +111,6 @@ static void vc1_extract_header(AVCodecParserContext *s, AVCodecContext *avctx, break; } - if (avctx->framerate.num) - avctx->time_base = av_inv_q(av_mul_q(avctx->framerate, (AVRational){avctx->ticks_per_frame, 1})); s->format = vpc->v.chromaformat == 1 ? AV_PIX_FMT_YUV420P : AV_PIX_FMT_NONE; if (avctx->width && avctx->height) { diff --git a/libavcodec/vc1dec.c b/libavcodec/vc1dec.c index 5cb4c544c9a..a2a0e8f56fe 100644 --- a/libavcodec/vc1dec.c +++ b/libavcodec/vc1dec.c @@ -34,6 +34,7 @@ #include "decode.h" #include "get_bits.h" #include "h263dec.h" +#include "hwaccel_internal.h" #include "hwconfig.h" #include "mpeg_er.h" #include "mpegvideo.h" @@ -836,6 +837,7 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, const uint8_t *rawbuf; int raw_size; } *slices = NULL, *tmp; + unsigned slices_allocated = 0; v->second_field = 0; @@ -859,6 +861,7 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, //for advanced profile we may need to parse and unescape data if (avctx->codec_id == AV_CODEC_ID_VC1 || avctx->codec_id == AV_CODEC_ID_VC1IMAGE) { int buf_size2 = 0; + size_t next_allocated = 0; buf2 = av_mallocz(buf_size + AV_INPUT_BUFFER_PADDING_SIZE); if (!buf2) return AVERROR(ENOMEM); @@ -882,7 +885,8 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, int buf_size3; if (avctx->hwaccel) buf_start_second_field = start; - tmp = av_realloc_array(slices, sizeof(*slices), n_slices+1); + av_size_mult(sizeof(*slices), n_slices+1, &next_allocated); + tmp = next_allocated ? av_fast_realloc(slices, &slices_allocated, next_allocated) : NULL; if (!tmp) { ret = AVERROR(ENOMEM); goto err; @@ -911,7 +915,8 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, break; case VC1_CODE_SLICE: { int buf_size3; - tmp = av_realloc_array(slices, sizeof(*slices), n_slices+1); + av_size_mult(sizeof(*slices), n_slices+1, &next_allocated); + tmp = next_allocated ? av_fast_realloc(slices, &slices_allocated, next_allocated) : NULL; if (!tmp) { ret = AVERROR(ENOMEM); goto err; @@ -946,7 +951,8 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, } else { // found field marker, unescape second field if (avctx->hwaccel) buf_start_second_field = divider; - tmp = av_realloc_array(slices, sizeof(*slices), n_slices+1); + av_size_mult(sizeof(*slices), n_slices+1, &next_allocated); + tmp = next_allocated ? av_fast_realloc(slices, &slices_allocated, next_allocated) : NULL; if (!tmp) { ret = AVERROR(ENOMEM); goto err; @@ -1055,7 +1061,10 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, // for skipping the frame s->current_picture.f->pict_type = s->pict_type; - s->current_picture.f->key_frame = s->pict_type == AV_PICTURE_TYPE_I; + if (s->pict_type == AV_PICTURE_TYPE_I) + s->current_picture.f->flags |= AV_FRAME_FLAG_KEY; + else + s->current_picture.f->flags &= ~AV_FRAME_FLAG_KEY; /* skip B-frames if we don't have reference frames */ if (!s->last_picture_ptr && s->pict_type == AV_PICTURE_TYPE_B) { @@ -1073,13 +1082,12 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, } v->s.current_picture_ptr->field_picture = v->field_mode; - v->s.current_picture_ptr->f->interlaced_frame = (v->fcm != PROGRESSIVE); - v->s.current_picture_ptr->f->top_field_first = v->tff; + v->s.current_picture_ptr->f->flags |= AV_FRAME_FLAG_INTERLACED * (v->fcm != PROGRESSIVE); + v->s.current_picture_ptr->f->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST * !!v->tff; // process pulldown flags s->current_picture_ptr->f->repeat_pict = 0; // Pulldown flags are only valid when 'broadcast' has been set. - // So ticks_per_frame will be 2 if (v->rff) { // repeat field s->current_picture_ptr->f->repeat_pict = 1; @@ -1089,19 +1097,26 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, } if (avctx->hwaccel) { + const FFHWAccel *hwaccel = ffhwaccel(avctx->hwaccel); s->mb_y = 0; if (v->field_mode && buf_start_second_field) { // decode first field s->picture_structure = PICT_BOTTOM_FIELD - v->tff; - if ((ret = avctx->hwaccel->start_frame(avctx, buf_start, buf_start_second_field - buf_start)) < 0) + ret = hwaccel->start_frame(avctx, buf_start, + buf_start_second_field - buf_start); + if (ret < 0) goto err; if (n_slices1 == -1) { // no slices, decode the field as-is - if ((ret = avctx->hwaccel->decode_slice(avctx, buf_start, buf_start_second_field - buf_start)) < 0) + ret = hwaccel->decode_slice(avctx, buf_start, + buf_start_second_field - buf_start); + if (ret < 0) goto err; } else { - if ((ret = avctx->hwaccel->decode_slice(avctx, buf_start, slices[0].rawbuf - buf_start)) < 0) + ret = hwaccel->decode_slice(avctx, buf_start, + slices[0].rawbuf - buf_start); + if (ret < 0) goto err; for (i = 0 ; i < n_slices1 + 1; i++) { @@ -1119,12 +1134,14 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, } } - if ((ret = avctx->hwaccel->decode_slice(avctx, slices[i].rawbuf, slices[i].raw_size)) < 0) + ret = hwaccel->decode_slice(avctx, slices[i].rawbuf, + slices[i].raw_size); + if (ret < 0) goto err; } } - if ((ret = avctx->hwaccel->end_frame(avctx)) < 0) + if ((ret = hwaccel->end_frame(avctx)) < 0) goto err; // decode second field @@ -1140,15 +1157,21 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, } v->s.current_picture_ptr->f->pict_type = v->s.pict_type; - if ((ret = avctx->hwaccel->start_frame(avctx, buf_start_second_field, (buf + buf_size) - buf_start_second_field)) < 0) + ret = hwaccel->start_frame(avctx, buf_start_second_field, + (buf + buf_size) - buf_start_second_field); + if (ret < 0) goto err; if (n_slices - n_slices1 == 2) { // no slices, decode the field as-is - if ((ret = avctx->hwaccel->decode_slice(avctx, buf_start_second_field, (buf + buf_size) - buf_start_second_field)) < 0) + ret = hwaccel->decode_slice(avctx, buf_start_second_field, + (buf + buf_size) - buf_start_second_field); + if (ret < 0) goto err; } else { - if ((ret = avctx->hwaccel->decode_slice(avctx, buf_start_second_field, slices[n_slices1 + 2].rawbuf - buf_start_second_field)) < 0) + ret = hwaccel->decode_slice(avctx, buf_start_second_field, + slices[n_slices1 + 2].rawbuf - buf_start_second_field); + if (ret < 0) goto err; for (i = n_slices1 + 2; i < n_slices; i++) { @@ -1166,25 +1189,33 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, } } - if ((ret = avctx->hwaccel->decode_slice(avctx, slices[i].rawbuf, slices[i].raw_size)) < 0) + ret = hwaccel->decode_slice(avctx, slices[i].rawbuf, + slices[i].raw_size); + if (ret < 0) goto err; } } - if ((ret = avctx->hwaccel->end_frame(avctx)) < 0) + if ((ret = hwaccel->end_frame(avctx)) < 0) goto err; } else { s->picture_structure = PICT_FRAME; - if ((ret = avctx->hwaccel->start_frame(avctx, buf_start, (buf + buf_size) - buf_start)) < 0) + ret = hwaccel->start_frame(avctx, buf_start, + (buf + buf_size) - buf_start); + if (ret < 0) goto err; if (n_slices == 0) { // no slices, decode the frame as-is - if ((ret = avctx->hwaccel->decode_slice(avctx, buf_start, (buf + buf_size) - buf_start)) < 0) + ret = hwaccel->decode_slice(avctx, buf_start, + (buf + buf_size) - buf_start); + if (ret < 0) goto err; } else { // decode the frame part as the first slice - if ((ret = avctx->hwaccel->decode_slice(avctx, buf_start, slices[0].rawbuf - buf_start)) < 0) + ret = hwaccel->decode_slice(avctx, buf_start, + slices[0].rawbuf - buf_start); + if (ret < 0) goto err; // and process the slices as additional slices afterwards @@ -1203,11 +1234,13 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, } } - if ((ret = avctx->hwaccel->decode_slice(avctx, slices[i].rawbuf, slices[i].raw_size)) < 0) + ret = hwaccel->decode_slice(avctx, slices[i].rawbuf, + slices[i].raw_size); + if (ret < 0) goto err; } } - if ((ret = avctx->hwaccel->end_frame(avctx)) < 0) + if ((ret = hwaccel->end_frame(avctx)) < 0) goto err; } } else { diff --git a/libavcodec/vc2enc.c b/libavcodec/vc2enc.c index 82d11462aaa..d978c67a3b0 100644 --- a/libavcodec/vc2enc.c +++ b/libavcodec/vc2enc.c @@ -1228,7 +1228,8 @@ const FFCodec ff_vc2_encoder = { CODEC_LONG_NAME("SMPTE VC-2"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_DIRAC, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SLICE_THREADS, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SLICE_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .priv_data_size = sizeof(VC2EncContext), .init = vc2_encode_init, diff --git a/libavcodec/vcr1.c b/libavcodec/vcr1.c index 853d4459a80..771337e2624 100644 --- a/libavcodec/vcr1.c +++ b/libavcodec/vcr1.c @@ -63,7 +63,7 @@ static int vcr1_decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; for (i = 0; i < 16; i++) { a->delta[i] = *bytestream++; diff --git a/libavcodec/vdpau.c b/libavcodec/vdpau.c index 0bb793c0104..2b9b78d8d07 100644 --- a/libavcodec/vdpau.c +++ b/libavcodec/vdpau.c @@ -27,6 +27,7 @@ #include "avcodec.h" #include "decode.h" +#include "hwaccel_internal.h" #include "internal.h" #include "mpegvideodec.h" #include "vc1.h" @@ -324,8 +325,8 @@ static int ff_vdpau_common_reinit(AVCodecContext *avctx) avctx->coded_height == vdctx->height && (!hwctx || !hwctx->reset)) return 0; - avctx->hwaccel->uninit(avctx); - return avctx->hwaccel->init(avctx); + FF_HW_SIMPLE_CALL(avctx, uninit); + return FF_HW_SIMPLE_CALL(avctx, init); } int ff_vdpau_common_start_frame(struct vdpau_picture_context *pic_ctx, diff --git a/libavcodec/vdpau_av1.c b/libavcodec/vdpau_av1.c index 3c3c8e61d10..0ae62301cbf 100644 --- a/libavcodec/vdpau_av1.c +++ b/libavcodec/vdpau_av1.c @@ -25,7 +25,7 @@ #include "avcodec.h" #include "internal.h" #include "av1dec.h" -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "vdpau.h" #include "vdpau_internal.h" @@ -353,11 +353,11 @@ static int vdpau_av1_init(AVCodecContext *avctx) return ff_vdpau_common_init(avctx, profile, level); } -const AVHWAccel ff_av1_vdpau_hwaccel = { - .name = "av1_vdpau", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_AV1, - .pix_fmt = AV_PIX_FMT_VDPAU, +const FFHWAccel ff_av1_vdpau_hwaccel = { + .p.name = "av1_vdpau", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_AV1, + .p.pix_fmt = AV_PIX_FMT_VDPAU, .start_frame = vdpau_av1_start_frame, .end_frame = vdpau_av1_end_frame, .decode_slice = vdpau_av1_decode_slice, diff --git a/libavcodec/vdpau_h264.c b/libavcodec/vdpau_h264.c index 525e2084958..8694292fe33 100644 --- a/libavcodec/vdpau_h264.c +++ b/libavcodec/vdpau_h264.c @@ -26,7 +26,7 @@ #include "avcodec.h" #include "h264dec.h" #include "h264_ps.h" -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "mpegutils.h" #include "vdpau.h" #include "vdpau_internal.h" @@ -261,11 +261,11 @@ static int vdpau_h264_init(AVCodecContext *avctx) return ff_vdpau_common_init(avctx, profile, level); } -const AVHWAccel ff_h264_vdpau_hwaccel = { - .name = "h264_vdpau", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_H264, - .pix_fmt = AV_PIX_FMT_VDPAU, +const FFHWAccel ff_h264_vdpau_hwaccel = { + .p.name = "h264_vdpau", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_H264, + .p.pix_fmt = AV_PIX_FMT_VDPAU, .start_frame = vdpau_h264_start_frame, .end_frame = vdpau_h264_end_frame, .decode_slice = vdpau_h264_decode_slice, diff --git a/libavcodec/vdpau_hevc.c b/libavcodec/vdpau_hevc.c index 2669040f781..fea98805d59 100644 --- a/libavcodec/vdpau_hevc.c +++ b/libavcodec/vdpau_hevc.c @@ -25,7 +25,7 @@ #include "avcodec.h" #include "hevc_data.h" #include "hevcdec.h" -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "vdpau.h" #include "vdpau_internal.h" #include "h265_profile_level.h" @@ -540,11 +540,11 @@ static int vdpau_hevc_init(AVCodecContext *avctx) return ff_vdpau_common_init(avctx, profile, level); } -const AVHWAccel ff_hevc_vdpau_hwaccel = { - .name = "hevc_vdpau", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_HEVC, - .pix_fmt = AV_PIX_FMT_VDPAU, +const FFHWAccel ff_hevc_vdpau_hwaccel = { + .p.name = "hevc_vdpau", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_HEVC, + .p.pix_fmt = AV_PIX_FMT_VDPAU, .start_frame = vdpau_hevc_start_frame, .end_frame = vdpau_hevc_end_frame, .decode_slice = vdpau_hevc_decode_slice, diff --git a/libavcodec/vdpau_mpeg12.c b/libavcodec/vdpau_mpeg12.c index 354239cad54..a5f21ca3a5c 100644 --- a/libavcodec/vdpau_mpeg12.c +++ b/libavcodec/vdpau_mpeg12.c @@ -26,7 +26,7 @@ #include #include "avcodec.h" -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "mpegvideo.h" #include "vdpau.h" #include "vdpau_internal.h" @@ -75,8 +75,9 @@ static int vdpau_mpeg_start_frame(AVCodecContext *avctx, info->f_code[1][0] = s->mpeg_f_code[1][0]; info->f_code[1][1] = s->mpeg_f_code[1][1]; for (i = 0; i < 64; ++i) { - info->intra_quantizer_matrix[i] = s->intra_matrix[i]; - info->non_intra_quantizer_matrix[i] = s->inter_matrix[i]; + int n = s->idsp.idct_permutation[i]; + info->intra_quantizer_matrix[i] = s->intra_matrix[n]; + info->non_intra_quantizer_matrix[i] = s->inter_matrix[n]; } return ff_vdpau_common_start_frame(pic_ctx, buffer, size); @@ -105,11 +106,11 @@ static int vdpau_mpeg1_init(AVCodecContext *avctx) VDP_DECODER_LEVEL_MPEG1_NA); } -const AVHWAccel ff_mpeg1_vdpau_hwaccel = { - .name = "mpeg1_vdpau", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG1VIDEO, - .pix_fmt = AV_PIX_FMT_VDPAU, +const FFHWAccel ff_mpeg1_vdpau_hwaccel = { + .p.name = "mpeg1_vdpau", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG1VIDEO, + .p.pix_fmt = AV_PIX_FMT_VDPAU, .start_frame = vdpau_mpeg_start_frame, .end_frame = ff_vdpau_mpeg_end_frame, .decode_slice = vdpau_mpeg_decode_slice, @@ -140,11 +141,11 @@ static int vdpau_mpeg2_init(AVCodecContext *avctx) return ff_vdpau_common_init(avctx, profile, VDP_DECODER_LEVEL_MPEG2_HL); } -const AVHWAccel ff_mpeg2_vdpau_hwaccel = { - .name = "mpeg2_vdpau", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG2VIDEO, - .pix_fmt = AV_PIX_FMT_VDPAU, +const FFHWAccel ff_mpeg2_vdpau_hwaccel = { + .p.name = "mpeg2_vdpau", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG2VIDEO, + .p.pix_fmt = AV_PIX_FMT_VDPAU, .start_frame = vdpau_mpeg_start_frame, .end_frame = ff_vdpau_mpeg_end_frame, .decode_slice = vdpau_mpeg_decode_slice, diff --git a/libavcodec/vdpau_mpeg4.c b/libavcodec/vdpau_mpeg4.c index 6e082eefc68..0c3852efdf9 100644 --- a/libavcodec/vdpau_mpeg4.c +++ b/libavcodec/vdpau_mpeg4.c @@ -24,7 +24,7 @@ #include #include "avcodec.h" -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "mpeg4videodec.h" #include "vdpau.h" #include "vdpau_internal.h" @@ -74,8 +74,9 @@ static int vdpau_mpeg4_start_frame(AVCodecContext *avctx, info->alternate_vertical_scan_flag = s->alternate_scan; info->top_field_first = s->top_field_first; for (i = 0; i < 64; ++i) { - info->intra_quantizer_matrix[i] = s->intra_matrix[i]; - info->non_intra_quantizer_matrix[i] = s->inter_matrix[i]; + int n = s->idsp.idct_permutation[i]; + info->intra_quantizer_matrix[i] = s->intra_matrix[n]; + info->non_intra_quantizer_matrix[i] = s->inter_matrix[n]; } ff_vdpau_common_start_frame(pic_ctx, buffer, size); @@ -110,11 +111,11 @@ static int vdpau_mpeg4_init(AVCodecContext *avctx) return ff_vdpau_common_init(avctx, profile, avctx->level); } -const AVHWAccel ff_mpeg4_vdpau_hwaccel = { - .name = "mpeg4_vdpau", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG4, - .pix_fmt = AV_PIX_FMT_VDPAU, +const FFHWAccel ff_mpeg4_vdpau_hwaccel = { + .p.name = "mpeg4_vdpau", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG4, + .p.pix_fmt = AV_PIX_FMT_VDPAU, .start_frame = vdpau_mpeg4_start_frame, .end_frame = ff_vdpau_mpeg_end_frame, .decode_slice = vdpau_mpeg4_decode_slice, diff --git a/libavcodec/vdpau_vc1.c b/libavcodec/vdpau_vc1.c index 00b33f420b0..a7ab36e431d 100644 --- a/libavcodec/vdpau_vc1.c +++ b/libavcodec/vdpau_vc1.c @@ -26,7 +26,7 @@ #include #include "avcodec.h" -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "vc1.h" #include "vdpau.h" #include "vdpau_internal.h" @@ -138,11 +138,11 @@ static int vdpau_vc1_init(AVCodecContext *avctx) } #if CONFIG_WMV3_VDPAU_HWACCEL -const AVHWAccel ff_wmv3_vdpau_hwaccel = { - .name = "wm3_vdpau", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_WMV3, - .pix_fmt = AV_PIX_FMT_VDPAU, +const FFHWAccel ff_wmv3_vdpau_hwaccel = { + .p.name = "wm3_vdpau", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_WMV3, + .p.pix_fmt = AV_PIX_FMT_VDPAU, .start_frame = vdpau_vc1_start_frame, .end_frame = ff_vdpau_mpeg_end_frame, .decode_slice = vdpau_vc1_decode_slice, @@ -155,11 +155,11 @@ const AVHWAccel ff_wmv3_vdpau_hwaccel = { }; #endif -const AVHWAccel ff_vc1_vdpau_hwaccel = { - .name = "vc1_vdpau", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VC1, - .pix_fmt = AV_PIX_FMT_VDPAU, +const FFHWAccel ff_vc1_vdpau_hwaccel = { + .p.name = "vc1_vdpau", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VC1, + .p.pix_fmt = AV_PIX_FMT_VDPAU, .start_frame = vdpau_vc1_start_frame, .end_frame = ff_vdpau_mpeg_end_frame, .decode_slice = vdpau_vc1_decode_slice, diff --git a/libavcodec/vdpau_vp9.c b/libavcodec/vdpau_vp9.c index 49fe18189be..fa551015366 100644 --- a/libavcodec/vdpau_vp9.c +++ b/libavcodec/vdpau_vp9.c @@ -23,9 +23,8 @@ #include #include "libavutil/pixdesc.h" #include "avcodec.h" -#include "vp9data.h" +#include "hwaccel_internal.h" #include "vp9dec.h" -#include "hwconfig.h" #include "vdpau.h" #include "vdpau_internal.h" @@ -223,11 +222,11 @@ static int vdpau_vp9_init(AVCodecContext *avctx) return ff_vdpau_common_init(avctx, profile, level); } -const AVHWAccel ff_vp9_vdpau_hwaccel = { - .name = "vp9_vdpau", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VP9, - .pix_fmt = AV_PIX_FMT_VDPAU, +const FFHWAccel ff_vp9_vdpau_hwaccel = { + .p.name = "vp9_vdpau", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VP9, + .p.pix_fmt = AV_PIX_FMT_VDPAU, .start_frame = vdpau_vp9_start_frame, .end_frame = vdpau_vp9_end_frame, .decode_slice = vdpau_vp9_decode_slice, diff --git a/libavcodec/version.h b/libavcodec/version.h index 6b8a1dbb793..728ab8839d8 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -29,7 +29,7 @@ #include "version_major.h" -#define LIBAVCODEC_VERSION_MINOR 56 +#define LIBAVCODEC_VERSION_MINOR 23 #define LIBAVCODEC_VERSION_MICRO 100 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ diff --git a/libavcodec/version_major.h b/libavcodec/version_major.h index 12f863deb79..95c5aec0c58 100644 --- a/libavcodec/version_major.h +++ b/libavcodec/version_major.h @@ -25,7 +25,7 @@ * Libavcodec version macros. */ -#define LIBAVCODEC_VERSION_MAJOR 59 +#define LIBAVCODEC_VERSION_MAJOR 60 /** * FF_API_* defines may be placed below to indicate public API that will be @@ -37,21 +37,20 @@ * at once through the bump. This improves the git bisect-ability of the change. */ -#define FF_API_OPENH264_SLICE_MODE (LIBAVCODEC_VERSION_MAJOR < 60) -#define FF_API_OPENH264_CABAC (LIBAVCODEC_VERSION_MAJOR < 60) -#define FF_API_UNUSED_CODEC_CAPS (LIBAVCODEC_VERSION_MAJOR < 60) -#define FF_API_THREAD_SAFE_CALLBACKS (LIBAVCODEC_VERSION_MAJOR < 60) -#define FF_API_DEBUG_MV (LIBAVCODEC_VERSION_MAJOR < 60) -#define FF_API_GET_FRAME_CLASS (LIBAVCODEC_VERSION_MAJOR < 60) -#define FF_API_AUTO_THREADS (LIBAVCODEC_VERSION_MAJOR < 60) -#define FF_API_INIT_PACKET (LIBAVCODEC_VERSION_MAJOR < 60) -#define FF_API_AVCTX_TIMEBASE (LIBAVCODEC_VERSION_MAJOR < 60) -#define FF_API_FLAG_TRUNCATED (LIBAVCODEC_VERSION_MAJOR < 60) -#define FF_API_SUB_TEXT_FORMAT (LIBAVCODEC_VERSION_MAJOR < 60) -#define FF_API_IDCT_NONE (LIBAVCODEC_VERSION_MAJOR < 60) -#define FF_API_SVTAV1_OPTS (LIBAVCODEC_VERSION_MAJOR < 60) -#define FF_API_AYUV_CODECID (LIBAVCODEC_VERSION_MAJOR < 60) -#define FF_API_VT_OUTPUT_CALLBACK (LIBAVCODEC_VERSION_MAJOR < 60) -#define FF_API_AVCODEC_CHROMA_POS (LIBAVCODEC_VERSION_MAJOR < 60) +#define FF_API_INIT_PACKET (LIBAVCODEC_VERSION_MAJOR < 61) +#define FF_API_IDCT_NONE (LIBAVCODEC_VERSION_MAJOR < 61) +#define FF_API_SVTAV1_OPTS (LIBAVCODEC_VERSION_MAJOR < 61) +#define FF_API_AYUV_CODECID (LIBAVCODEC_VERSION_MAJOR < 61) +#define FF_API_VT_OUTPUT_CALLBACK (LIBAVCODEC_VERSION_MAJOR < 61) +#define FF_API_AVCODEC_CHROMA_POS (LIBAVCODEC_VERSION_MAJOR < 61) +#define FF_API_VT_HWACCEL_CONTEXT (LIBAVCODEC_VERSION_MAJOR < 61) +#define FF_API_AVCTX_FRAME_NUMBER (LIBAVCODEC_VERSION_MAJOR < 61) +#define FF_API_SLICE_OFFSET (LIBAVCODEC_VERSION_MAJOR < 61) +#define FF_API_SUBFRAMES (LIBAVCODEC_VERSION_MAJOR < 61) +#define FF_API_TICKS_PER_FRAME (LIBAVCODEC_VERSION_MAJOR < 61) +#define FF_API_DROPCHANGED (LIBAVCODEC_VERSION_MAJOR < 61) + +// reminder to remove CrystalHD decoders on next major bump +#define FF_CODEC_CRYSTAL_HD (LIBAVCODEC_VERSION_MAJOR < 61) #endif /* AVCODEC_VERSION_MAJOR_H */ diff --git a/libavcodec/videodsp_template.c b/libavcodec/videodsp_template.c index 324d70f2cb2..d653f4d524d 100644 --- a/libavcodec/videodsp_template.c +++ b/libavcodec/videodsp_template.c @@ -64,7 +64,7 @@ void FUNC(ff_emulated_edge_mc)(uint8_t *buf, const uint8_t *src, av_assert2(start_x < end_x && block_w); w = end_x - start_x; - src += start_y * src_linesize + start_x * sizeof(pixel); + src += start_y * src_linesize + start_x * (ptrdiff_t)sizeof(pixel); buf += start_x * sizeof(pixel); // top @@ -87,7 +87,7 @@ void FUNC(ff_emulated_edge_mc)(uint8_t *buf, const uint8_t *src, buf += buf_linesize; } - buf -= block_h * buf_linesize + start_x * sizeof(pixel); + buf -= block_h * buf_linesize + start_x * (ptrdiff_t)sizeof(pixel); while (block_h--) { pixel *bufp = (pixel *) buf; diff --git a/libavcodec/videotoolbox.c b/libavcodec/videotoolbox.c index 1b1be8ddb41..963379d4831 100644 --- a/libavcodec/videotoolbox.c +++ b/libavcodec/videotoolbox.c @@ -33,6 +33,7 @@ #include "internal.h" #include "h264dec.h" #include "hevcdec.h" +#include "hwaccel_internal.h" #include "mpegvideo.h" #include "proresdec.h" #include @@ -799,6 +800,9 @@ static CFDictionaryRef videotoolbox_buffer_attributes_create(int width, static CFDictionaryRef videotoolbox_decoder_config_create(CMVideoCodecType codec_type, AVCodecContext *avctx) { + CFMutableDictionaryRef avc_info; + CFDataRef data = NULL; + CFMutableDictionaryRef config_info = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, @@ -810,9 +814,6 @@ static CFDictionaryRef videotoolbox_decoder_config_create(CMVideoCodecType codec kVTVideoDecoderSpecification_RequireHardwareAcceleratedVideoDecoder, kCFBooleanTrue); - CFMutableDictionaryRef avc_info; - CFDataRef data = NULL; - avc_info = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, @@ -1072,13 +1073,14 @@ static int videotoolbox_hevc_end_frame(AVCodecContext *avctx) HEVCContext *h = avctx->priv_data; AVFrame *frame = h->ref->frame; VTContext *vtctx = avctx->internal->hwaccel_priv_data; + int ret; h->output_frame->crop_right = 0; h->output_frame->crop_left = 0; h->output_frame->crop_top = 0; h->output_frame->crop_bottom = 0; - int ret = ff_videotoolbox_common_end_frame(avctx, frame); + ret = ff_videotoolbox_common_end_frame(avctx, frame); vtctx->bitstream_size = 0; return ret; } @@ -1132,15 +1134,17 @@ static int videotoolbox_prores_end_frame(AVCodecContext *avctx) } static enum AVPixelFormat videotoolbox_best_pixel_format(AVCodecContext *avctx) { + int depth; const AVPixFmtDescriptor *descriptor = av_pix_fmt_desc_get(avctx->sw_pix_fmt); if (!descriptor) return AV_PIX_FMT_NV12; // same as av_videotoolbox_alloc_context() - int depth = descriptor->comp[0].depth; if (descriptor->flags & AV_PIX_FMT_FLAG_ALPHA) return AV_PIX_FMT_AYUV64; + depth = descriptor->comp[0].depth; + #if HAVE_KCVPIXELFORMATTYPE_444YPCBCR16BIPLANARVIDEORANGE if (depth > 10) return descriptor->log2_chroma_w == 0 ? AV_PIX_FMT_P416 : AV_PIX_FMT_P216; @@ -1173,17 +1177,34 @@ static enum AVPixelFormat videotoolbox_best_pixel_format(AVCodecContext *avctx) return AV_PIX_FMT_NV12; } +static AVVideotoolboxContext *videotoolbox_alloc_context_with_pix_fmt(enum AVPixelFormat pix_fmt, + bool full_range) +{ + AVVideotoolboxContext *ret = av_mallocz(sizeof(*ret)); + + if (ret) { + OSType cv_pix_fmt_type = av_map_videotoolbox_format_from_pixfmt2(pix_fmt, full_range); + if (cv_pix_fmt_type == 0) { + cv_pix_fmt_type = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange; + } + ret->cv_pix_fmt_type = cv_pix_fmt_type; + } + + return ret; +} + int ff_videotoolbox_common_init(AVCodecContext *avctx) { VTContext *vtctx = avctx->internal->hwaccel_priv_data; AVHWFramesContext *hw_frames; int err; + bool full_range; vtctx->logctx = avctx; - // Old API - do nothing. - if (avctx->hwaccel_context) - return 0; + if (!avctx->hw_frames_ctx && !avctx->hw_device_ctx && + avctx->hwaccel_context) + return videotoolbox_start(avctx); if (!avctx->hw_frames_ctx && !avctx->hw_device_ctx) { av_log(avctx, AV_LOG_ERROR, @@ -1191,7 +1212,7 @@ int ff_videotoolbox_common_init(AVCodecContext *avctx) return AVERROR(EINVAL); } - vtctx->vt_ctx = av_videotoolbox_alloc_context(); + vtctx->vt_ctx = videotoolbox_alloc_context_with_pix_fmt(AV_PIX_FMT_NONE, false); if (!vtctx->vt_ctx) { err = AVERROR(ENOMEM); goto fail; @@ -1225,7 +1246,7 @@ int ff_videotoolbox_common_init(AVCodecContext *avctx) goto fail; } - bool full_range = avctx->color_range == AVCOL_RANGE_JPEG; + full_range = avctx->color_range == AVCOL_RANGE_JPEG; vtctx->vt_ctx->cv_pix_fmt_type = av_map_videotoolbox_format_from_pixfmt2(hw_frames->sw_format, full_range); if (!vtctx->vt_ctx->cv_pix_fmt_type) { @@ -1264,11 +1285,11 @@ int ff_videotoolbox_frame_params(AVCodecContext *avctx, return 0; } -const AVHWAccel ff_h263_videotoolbox_hwaccel = { - .name = "h263_videotoolbox", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_H263, - .pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, +const FFHWAccel ff_h263_videotoolbox_hwaccel = { + .p.name = "h263_videotoolbox", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_H263, + .p.pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, .alloc_frame = ff_videotoolbox_alloc_frame, .start_frame = videotoolbox_mpeg_start_frame, .decode_slice = videotoolbox_mpeg_decode_slice, @@ -1279,11 +1300,11 @@ const AVHWAccel ff_h263_videotoolbox_hwaccel = { .priv_data_size = sizeof(VTContext), }; -const AVHWAccel ff_hevc_videotoolbox_hwaccel = { - .name = "hevc_videotoolbox", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_HEVC, - .pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, +const FFHWAccel ff_hevc_videotoolbox_hwaccel = { + .p.name = "hevc_videotoolbox", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_HEVC, + .p.pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, .alloc_frame = ff_videotoolbox_alloc_frame, .start_frame = videotoolbox_hevc_start_frame, .decode_slice = videotoolbox_hevc_decode_slice, @@ -1295,11 +1316,11 @@ const AVHWAccel ff_hevc_videotoolbox_hwaccel = { .priv_data_size = sizeof(VTContext), }; -const AVHWAccel ff_h264_videotoolbox_hwaccel = { - .name = "h264_videotoolbox", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_H264, - .pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, +const FFHWAccel ff_h264_videotoolbox_hwaccel = { + .p.name = "h264_videotoolbox", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_H264, + .p.pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, .alloc_frame = ff_videotoolbox_alloc_frame, .start_frame = ff_videotoolbox_h264_start_frame, .decode_slice = ff_videotoolbox_h264_decode_slice, @@ -1311,11 +1332,11 @@ const AVHWAccel ff_h264_videotoolbox_hwaccel = { .priv_data_size = sizeof(VTContext), }; -const AVHWAccel ff_mpeg1_videotoolbox_hwaccel = { - .name = "mpeg1_videotoolbox", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG1VIDEO, - .pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, +const FFHWAccel ff_mpeg1_videotoolbox_hwaccel = { + .p.name = "mpeg1_videotoolbox", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG1VIDEO, + .p.pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, .alloc_frame = ff_videotoolbox_alloc_frame, .start_frame = videotoolbox_mpeg_start_frame, .decode_slice = videotoolbox_mpeg_decode_slice, @@ -1326,11 +1347,11 @@ const AVHWAccel ff_mpeg1_videotoolbox_hwaccel = { .priv_data_size = sizeof(VTContext), }; -const AVHWAccel ff_mpeg2_videotoolbox_hwaccel = { - .name = "mpeg2_videotoolbox", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG2VIDEO, - .pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, +const FFHWAccel ff_mpeg2_videotoolbox_hwaccel = { + .p.name = "mpeg2_videotoolbox", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG2VIDEO, + .p.pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, .alloc_frame = ff_videotoolbox_alloc_frame, .start_frame = videotoolbox_mpeg_start_frame, .decode_slice = videotoolbox_mpeg_decode_slice, @@ -1341,11 +1362,11 @@ const AVHWAccel ff_mpeg2_videotoolbox_hwaccel = { .priv_data_size = sizeof(VTContext), }; -const AVHWAccel ff_mpeg4_videotoolbox_hwaccel = { - .name = "mpeg4_videotoolbox", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG4, - .pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, +const FFHWAccel ff_mpeg4_videotoolbox_hwaccel = { + .p.name = "mpeg4_videotoolbox", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG4, + .p.pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, .alloc_frame = ff_videotoolbox_alloc_frame, .start_frame = videotoolbox_mpeg_start_frame, .decode_slice = videotoolbox_mpeg_decode_slice, @@ -1356,11 +1377,11 @@ const AVHWAccel ff_mpeg4_videotoolbox_hwaccel = { .priv_data_size = sizeof(VTContext), }; -const AVHWAccel ff_prores_videotoolbox_hwaccel = { - .name = "prores_videotoolbox", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_PRORES, - .pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, +const FFHWAccel ff_prores_videotoolbox_hwaccel = { + .p.name = "prores_videotoolbox", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_PRORES, + .p.pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, .alloc_frame = ff_videotoolbox_alloc_frame, .start_frame = videotoolbox_prores_start_frame, .decode_slice = videotoolbox_prores_decode_slice, @@ -1371,25 +1392,12 @@ const AVHWAccel ff_prores_videotoolbox_hwaccel = { .priv_data_size = sizeof(VTContext), }; -static AVVideotoolboxContext *av_videotoolbox_alloc_context_with_pix_fmt(enum AVPixelFormat pix_fmt, - bool full_range) -{ - AVVideotoolboxContext *ret = av_mallocz(sizeof(*ret)); - if (ret) { - OSType cv_pix_fmt_type = av_map_videotoolbox_format_from_pixfmt2(pix_fmt, full_range); - if (cv_pix_fmt_type == 0) { - cv_pix_fmt_type = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange; - } - ret->cv_pix_fmt_type = cv_pix_fmt_type; - } - - return ret; -} +#if FF_API_VT_HWACCEL_CONTEXT AVVideotoolboxContext *av_videotoolbox_alloc_context(void) { - return av_videotoolbox_alloc_context_with_pix_fmt(AV_PIX_FMT_NONE, false); + return videotoolbox_alloc_context_with_pix_fmt(AV_PIX_FMT_NONE, false); } int av_videotoolbox_default_init(AVCodecContext *avctx) @@ -1401,10 +1409,10 @@ int av_videotoolbox_default_init2(AVCodecContext *avctx, AVVideotoolboxContext * { enum AVPixelFormat pix_fmt = videotoolbox_best_pixel_format(avctx); bool full_range = avctx->color_range == AVCOL_RANGE_JPEG; - avctx->hwaccel_context = vtctx ?: av_videotoolbox_alloc_context_with_pix_fmt(pix_fmt, full_range); + avctx->hwaccel_context = vtctx ?: videotoolbox_alloc_context_with_pix_fmt(pix_fmt, full_range); if (!avctx->hwaccel_context) return AVERROR(ENOMEM); - return videotoolbox_start(avctx); + return 0; } void av_videotoolbox_default_free(AVCodecContext *avctx) @@ -1413,4 +1421,6 @@ void av_videotoolbox_default_free(AVCodecContext *avctx) videotoolbox_stop(avctx); av_freep(&avctx->hwaccel_context); } +#endif /* FF_API_VT_HWACCEL_CONTEXT */ + #endif /* CONFIG_VIDEOTOOLBOX */ diff --git a/libavcodec/videotoolbox.h b/libavcodec/videotoolbox.h index 25a747a49f3..ba5eddbf460 100644 --- a/libavcodec/videotoolbox.h +++ b/libavcodec/videotoolbox.h @@ -57,7 +57,6 @@ typedef struct AVVideotoolboxContext { /** * Videotoolbox decompression session object. - * Created and freed the caller. */ VTDecompressionSessionRef session; @@ -79,17 +78,17 @@ typedef struct AVVideotoolboxContext { /** * CoreMedia Format Description that Videotoolbox will use to create the decompression session. - * Set by the caller. */ CMVideoFormatDescriptionRef cm_fmt_desc; /** * CoreMedia codec type that Videotoolbox will use to create the decompression session. - * Set by the caller. */ int cm_codec_type; } AVVideotoolboxContext; +#if FF_API_VT_HWACCEL_CONTEXT + /** * Allocate and initialize a Videotoolbox context. * @@ -102,7 +101,9 @@ typedef struct AVVideotoolboxContext { * object and free the Videotoolbox context using av_free(). * * @return the newly allocated context or NULL on failure + * @deprecated Use AVCodecContext.hw_frames_ctx or hw_device_ctx instead. */ +attribute_deprecated AVVideotoolboxContext *av_videotoolbox_alloc_context(void); /** @@ -112,7 +113,9 @@ AVVideotoolboxContext *av_videotoolbox_alloc_context(void); * @param avctx the corresponding codec context * * @return >= 0 on success, a negative AVERROR code on failure + * @deprecated Use AVCodecContext.hw_frames_ctx or hw_device_ctx instead. */ +attribute_deprecated int av_videotoolbox_default_init(AVCodecContext *avctx); /** @@ -123,7 +126,9 @@ int av_videotoolbox_default_init(AVCodecContext *avctx); * @param vtctx the Videotoolbox context to use * * @return >= 0 on success, a negative AVERROR code on failure + * @deprecated Use AVCodecContext.hw_frames_ctx or hw_device_ctx instead. */ +attribute_deprecated int av_videotoolbox_default_init2(AVCodecContext *avctx, AVVideotoolboxContext *vtctx); /** @@ -131,9 +136,13 @@ int av_videotoolbox_default_init2(AVCodecContext *avctx, AVVideotoolboxContext * * av_videotoolbox_default_init(). * * @param avctx the corresponding codec context + * @deprecated Use AVCodecContext.hw_frames_ctx or hw_device_ctx instead. */ +attribute_deprecated void av_videotoolbox_default_free(AVCodecContext *avctx); +#endif /* FF_API_VT_HWACCEL_CONTEXT */ + /** * @} */ diff --git a/libavcodec/videotoolbox_vp9.c b/libavcodec/videotoolbox_vp9.c index a998f36d1ad..f5489854e38 100644 --- a/libavcodec/videotoolbox_vp9.c +++ b/libavcodec/videotoolbox_vp9.c @@ -31,6 +31,7 @@ #include "libavutil/intreadwrite.h" #include "libavutil/pixdesc.h" #include "decode.h" +#include "hwaccel_internal.h" #include "internal.h" #include "vp9shared.h" @@ -125,11 +126,11 @@ static int videotoolbox_vp9_end_frame(AVCodecContext *avctx) return ff_videotoolbox_common_end_frame(avctx, frame); } -const AVHWAccel ff_vp9_videotoolbox_hwaccel = { - .name = "vp9_videotoolbox", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VP9, - .pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, +const FFHWAccel ff_vp9_videotoolbox_hwaccel = { + .p.name = "vp9_videotoolbox", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VP9, + .p.pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, .alloc_frame = ff_videotoolbox_alloc_frame, .start_frame = videotoolbox_vp9_start_frame, .decode_slice = videotoolbox_vp9_decode_slice, diff --git a/libavcodec/videotoolboxenc.c b/libavcodec/videotoolboxenc.c index 56971471e4e..8e493c4f7a7 100644 --- a/libavcodec/videotoolboxenc.c +++ b/libavcodec/videotoolboxenc.c @@ -63,7 +63,21 @@ typedef OSStatus (*getParameterSetAtIndex)(CMFormatDescriptionRef videoDesc, size_t *parameterSetCountOut, int *NALUnitHeaderLengthOut); -//These symbols may not be present +/* + * Symbols that aren't available in MacOS 10.8 and iOS 8.0 need to be accessed + * from compat_keys, or it will cause compiler errors when compiling for older + * OS versions. + * + * For example, kVTCompressionPropertyKey_H264EntropyMode was added in + * MacOS 10.9. If this constant were used directly, a compiler would generate + * an error when it has access to the MacOS 10.8 headers, but does not have + * 10.9 headers. + * + * Runtime errors will still occur when unknown keys are set. A warning is + * logged and encoding continues where possible. + * + * When adding new symbols, they should be loaded/set in loadVTEncSymbols(). + */ static struct{ CFStringRef kCVImageBufferColorPrimaries_ITU_R_2020; CFStringRef kCVImageBufferTransferFunction_ITU_R_2020; @@ -94,6 +108,8 @@ static struct{ CFStringRef kVTProfileLevel_H264_High_AutoLevel; CFStringRef kVTProfileLevel_H264_Extended_5_0; CFStringRef kVTProfileLevel_H264_Extended_AutoLevel; + CFStringRef kVTProfileLevel_H264_ConstrainedBaseline_AutoLevel; + CFStringRef kVTProfileLevel_H264_ConstrainedHigh_AutoLevel; CFStringRef kVTProfileLevel_HEVC_Main_AutoLevel; CFStringRef kVTProfileLevel_HEVC_Main10_AutoLevel; @@ -105,6 +121,12 @@ static struct{ CFStringRef kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder; CFStringRef kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder; + CFStringRef kVTVideoEncoderSpecification_EnableLowLatencyRateControl; + CFStringRef kVTCompressionPropertyKey_AllowOpenGOP; + CFStringRef kVTCompressionPropertyKey_MaximizePowerEfficiency; + CFStringRef kVTCompressionPropertyKey_ReferenceBufferCount; + CFStringRef kVTCompressionPropertyKey_MaxAllowedFrameQP; + CFStringRef kVTCompressionPropertyKey_MinAllowedFrameQP; getParameterSetAtIndex CMVideoFormatDescriptionGetHEVCParameterSetAtIndex; } compat_keys; @@ -120,7 +142,7 @@ do{ \ static pthread_once_t once_ctrl = PTHREAD_ONCE_INIT; -static void loadVTEncSymbols(){ +static void loadVTEncSymbols(void){ compat_keys.CMVideoFormatDescriptionGetHEVCParameterSetAtIndex = (getParameterSetAtIndex)dlsym( RTLD_DEFAULT, @@ -156,6 +178,8 @@ static void loadVTEncSymbols(){ GET_SYM(kVTProfileLevel_H264_High_AutoLevel, "H264_High_AutoLevel"); GET_SYM(kVTProfileLevel_H264_Extended_5_0, "H264_Extended_5_0"); GET_SYM(kVTProfileLevel_H264_Extended_AutoLevel, "H264_Extended_AutoLevel"); + GET_SYM(kVTProfileLevel_H264_ConstrainedBaseline_AutoLevel, "H264_ConstrainedBaseline_AutoLevel"); + GET_SYM(kVTProfileLevel_H264_ConstrainedHigh_AutoLevel, "H264_ConstrainedHigh_AutoLevel"); GET_SYM(kVTProfileLevel_HEVC_Main_AutoLevel, "HEVC_Main_AutoLevel"); GET_SYM(kVTProfileLevel_HEVC_Main10_AutoLevel, "HEVC_Main10_AutoLevel"); @@ -171,16 +195,18 @@ static void loadVTEncSymbols(){ "EnableHardwareAcceleratedVideoEncoder"); GET_SYM(kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder, "RequireHardwareAcceleratedVideoEncoder"); + GET_SYM(kVTVideoEncoderSpecification_EnableLowLatencyRateControl, + "EnableLowLatencyRateControl"); + GET_SYM(kVTCompressionPropertyKey_AllowOpenGOP, "AllowOpenGOP"); + GET_SYM(kVTCompressionPropertyKey_MaximizePowerEfficiency, + "MaximizePowerEfficiency"); + GET_SYM(kVTCompressionPropertyKey_ReferenceBufferCount, + "ReferenceBufferCount"); + GET_SYM(kVTCompressionPropertyKey_MaxAllowedFrameQP, "MaxAllowedFrameQP"); + GET_SYM(kVTCompressionPropertyKey_MinAllowedFrameQP, "MinAllowedFrameQP"); } -typedef enum VT_H264Profile { - H264_PROF_AUTO, - H264_PROF_BASELINE, - H264_PROF_MAIN, - H264_PROF_HIGH, - H264_PROF_EXTENDED, - H264_PROF_COUNT -} VT_H264Profile; +#define H264_PROFILE_CONSTRAINED_HIGH (FF_PROFILE_H264_HIGH | FF_PROFILE_H264_CONSTRAINED) typedef enum VTH264Entropy{ VT_ENTROPY_NOT_SET, @@ -188,13 +214,6 @@ typedef enum VTH264Entropy{ VT_CABAC } VTH264Entropy; -typedef enum VT_HEVCProfile { - HEVC_PROF_AUTO, - HEVC_PROF_MAIN, - HEVC_PROF_MAIN10, - HEVC_PROF_COUNT -} VT_HEVCProfile; - static const uint8_t start_code[] = { 0, 0, 0, 1 }; typedef struct ExtraSEI { @@ -232,7 +251,7 @@ typedef struct VTEncContext { int64_t first_pts; int64_t dts_delta; - int64_t profile; + int profile; int level; int entropy; int realtime; @@ -251,6 +270,10 @@ typedef struct VTEncContext { /* can't be bool type since AVOption will access it as int */ int a53_cc; + + int max_slice_bytes; + int power_efficient; + int max_ref_frames; } VTEncContext; static int vtenc_populate_extradata(AVCodecContext *avctx, @@ -419,7 +442,7 @@ static int count_nalus(size_t length_code_size, } static CMVideoCodecType get_cm_codec_type(AVCodecContext *avctx, - int64_t profile, + int profile, double alpha_quality) { const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(avctx->pix_fmt == AV_PIX_FMT_VIDEOTOOLBOX ? avctx->sw_pix_fmt : avctx->pix_fmt); @@ -446,7 +469,7 @@ static CMVideoCodecType get_cm_codec_type(AVCodecContext *avctx, return MKBETAG('a','p','4','x'); // kCMVideoCodecType_AppleProRes4444XQ default: - av_log(avctx, AV_LOG_ERROR, "Unknown profile ID: %"PRId64", using auto\n", profile); + av_log(avctx, AV_LOG_ERROR, "Unknown profile ID: %d, using auto\n", profile); case FF_PROFILE_UNKNOWN: if (desc && ((desc->flags & AV_PIX_FMT_FLAG_ALPHA) || @@ -711,20 +734,20 @@ static bool get_vt_h264_profile_level(AVCodecContext *avctx, CFStringRef *profile_level_val) { VTEncContext *vtctx = avctx->priv_data; - int64_t profile = vtctx->profile; + int profile = vtctx->profile; - if (profile == H264_PROF_AUTO && vtctx->level) { + if (profile == FF_PROFILE_UNKNOWN && vtctx->level) { //Need to pick a profile if level is not auto-selected. - profile = vtctx->has_b_frames ? H264_PROF_MAIN : H264_PROF_BASELINE; + profile = vtctx->has_b_frames ? FF_PROFILE_H264_MAIN : FF_PROFILE_H264_BASELINE; } *profile_level_val = NULL; switch (profile) { - case H264_PROF_AUTO: + case FF_PROFILE_UNKNOWN: return true; - case H264_PROF_BASELINE: + case FF_PROFILE_H264_BASELINE: switch (vtctx->level) { case 0: *profile_level_val = compat_keys.kVTProfileLevel_H264_Baseline_AutoLevel; break; @@ -746,7 +769,19 @@ static bool get_vt_h264_profile_level(AVCodecContext *avctx, } break; - case H264_PROF_MAIN: + case FF_PROFILE_H264_CONSTRAINED_BASELINE: + *profile_level_val = compat_keys.kVTProfileLevel_H264_ConstrainedBaseline_AutoLevel; + + if (vtctx->level != 0) { + av_log(avctx, + AV_LOG_WARNING, + "Level is auto-selected when constrained-baseline " + "profile is used. The output may be encoded with a " + "different level.\n"); + } + break; + + case FF_PROFILE_H264_MAIN: switch (vtctx->level) { case 0: *profile_level_val = compat_keys.kVTProfileLevel_H264_Main_AutoLevel; break; @@ -765,7 +800,19 @@ static bool get_vt_h264_profile_level(AVCodecContext *avctx, } break; - case H264_PROF_HIGH: + case H264_PROFILE_CONSTRAINED_HIGH: + *profile_level_val = compat_keys.kVTProfileLevel_H264_ConstrainedHigh_AutoLevel; + + if (vtctx->level != 0) { + av_log(avctx, + AV_LOG_WARNING, + "Level is auto-selected when constrained-high profile " + "is used. The output may be encoded with a different " + "level.\n"); + } + break; + + case FF_PROFILE_H264_HIGH: switch (vtctx->level) { case 0: *profile_level_val = compat_keys.kVTProfileLevel_H264_High_AutoLevel; break; @@ -788,7 +835,7 @@ static bool get_vt_h264_profile_level(AVCodecContext *avctx, compat_keys.kVTProfileLevel_H264_High_5_2; break; } break; - case H264_PROF_EXTENDED: + case FF_PROFILE_H264_EXTENDED: switch (vtctx->level) { case 0: *profile_level_val = compat_keys.kVTProfileLevel_H264_Extended_AutoLevel; break; @@ -816,18 +863,18 @@ static bool get_vt_hevc_profile_level(AVCodecContext *avctx, CFStringRef *profile_level_val) { VTEncContext *vtctx = avctx->priv_data; - int64_t profile = vtctx->profile; + int profile = vtctx->profile; *profile_level_val = NULL; switch (profile) { - case HEVC_PROF_AUTO: + case FF_PROFILE_UNKNOWN: return true; - case HEVC_PROF_MAIN: + case FF_PROFILE_HEVC_MAIN: *profile_level_val = compat_keys.kVTProfileLevel_HEVC_Main_AutoLevel; break; - case HEVC_PROF_MAIN10: + case FF_PROFILE_HEVC_MAIN_10: *profile_level_val = compat_keys.kVTProfileLevel_HEVC_Main10_AutoLevel; break; @@ -954,141 +1001,70 @@ static int create_cv_pixel_buffer_info(AVCodecContext* avctx, return AVERROR(ENOMEM); } -static int get_cv_color_primaries(AVCodecContext *avctx, - CFStringRef *primaries) +static int get_cv_gamma(AVCodecContext *avctx, + CFNumberRef *gamma_level) { - enum AVColorPrimaries pri = avctx->color_primaries; - switch (pri) { - case AVCOL_PRI_UNSPECIFIED: - *primaries = NULL; - break; - - case AVCOL_PRI_BT470BG: - *primaries = kCVImageBufferColorPrimaries_EBU_3213; - break; - - case AVCOL_PRI_SMPTE170M: - *primaries = kCVImageBufferColorPrimaries_SMPTE_C; - break; - - case AVCOL_PRI_BT709: - *primaries = kCVImageBufferColorPrimaries_ITU_R_709_2; - break; - - case AVCOL_PRI_BT2020: - *primaries = compat_keys.kCVImageBufferColorPrimaries_ITU_R_2020; - break; + enum AVColorTransferCharacteristic trc = avctx->color_trc; + Float32 gamma = 0; + *gamma_level = NULL; - default: - av_log(avctx, AV_LOG_ERROR, "Color primaries %s is not supported.\n", av_color_primaries_name(pri)); - *primaries = NULL; - return -1; - } + if (trc == AVCOL_TRC_GAMMA22) + gamma = 2.2; + else if (trc == AVCOL_TRC_GAMMA28) + gamma = 2.8; + if (gamma != 0) + *gamma_level = CFNumberCreate(NULL, kCFNumberFloat32Type, &gamma); return 0; } -static int get_cv_transfer_function(AVCodecContext *avctx, - CFStringRef *transfer_fnc, - CFNumberRef *gamma_level) +// constant quality only on Macs with Apple Silicon +static bool vtenc_qscale_enabled(void) { - enum AVColorTransferCharacteristic trc = avctx->color_trc; - Float32 gamma; - *gamma_level = NULL; - - switch (trc) { - case AVCOL_TRC_UNSPECIFIED: - *transfer_fnc = NULL; - break; - - case AVCOL_TRC_BT709: - *transfer_fnc = kCVImageBufferTransferFunction_ITU_R_709_2; - break; - - case AVCOL_TRC_SMPTE240M: - *transfer_fnc = kCVImageBufferTransferFunction_SMPTE_240M_1995; - break; - -#if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_SMPTE_ST_2084_PQ - case AVCOL_TRC_SMPTE2084: - *transfer_fnc = kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ; - break; -#endif -#if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_LINEAR - case AVCOL_TRC_LINEAR: - *transfer_fnc = kCVImageBufferTransferFunction_Linear; - break; -#endif -#if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_ITU_R_2100_HLG - case AVCOL_TRC_ARIB_STD_B67: - *transfer_fnc = kCVImageBufferTransferFunction_ITU_R_2100_HLG; - break; -#endif - - case AVCOL_TRC_GAMMA22: - gamma = 2.2; - *transfer_fnc = kCVImageBufferTransferFunction_UseGamma; - *gamma_level = CFNumberCreate(NULL, kCFNumberFloat32Type, &gamma); - break; - - case AVCOL_TRC_GAMMA28: - gamma = 2.8; - *transfer_fnc = kCVImageBufferTransferFunction_UseGamma; - *gamma_level = CFNumberCreate(NULL, kCFNumberFloat32Type, &gamma); - break; + return !TARGET_OS_IPHONE && TARGET_CPU_ARM64; +} - case AVCOL_TRC_BT2020_10: - case AVCOL_TRC_BT2020_12: - *transfer_fnc = compat_keys.kCVImageBufferTransferFunction_ITU_R_2020; - break; +static void set_encoder_property_or_log(AVCodecContext *avctx, + CFStringRef key, + const char *print_option_name, + CFTypeRef value) { + int status; + VTEncContext *vtctx = avctx->priv_data; - default: - *transfer_fnc = NULL; - av_log(avctx, AV_LOG_ERROR, "Transfer function %s is not supported.\n", av_color_transfer_name(trc)); - return -1; + status = VTSessionSetProperty(vtctx->session, key, value); + if (status == kVTPropertyNotSupportedErr) { + av_log(avctx, + AV_LOG_INFO, + "This device does not support the %s option. Value ignored.\n", + print_option_name); + } else if (status != 0) { + av_log(avctx, + AV_LOG_ERROR, + "Error setting %s: Error %d\n", + print_option_name, + status); } - - return 0; } -static int get_cv_ycbcr_matrix(AVCodecContext *avctx, CFStringRef *matrix) { - switch(avctx->colorspace) { - case AVCOL_SPC_BT709: - *matrix = kCVImageBufferYCbCrMatrix_ITU_R_709_2; - break; +static int set_encoder_int_property_or_log(AVCodecContext* avctx, + CFStringRef key, + const char* print_option_name, + int value) { + CFNumberRef value_cfnum = CFNumberCreate(kCFAllocatorDefault, + kCFNumberIntType, + &value); - case AVCOL_SPC_UNSPECIFIED: - case AVCOL_SPC_RGB: - *matrix = NULL; - break; + if (value_cfnum == NULL) { + return AVERROR(ENOMEM); + } - case AVCOL_SPC_BT470BG: - case AVCOL_SPC_SMPTE170M: - *matrix = kCVImageBufferYCbCrMatrix_ITU_R_601_4; - break; + set_encoder_property_or_log(avctx, key, print_option_name, value_cfnum); - case AVCOL_SPC_SMPTE240M: - *matrix = kCVImageBufferYCbCrMatrix_SMPTE_240M_1995; - break; - - case AVCOL_SPC_BT2020_NCL: - *matrix = compat_keys.kCVImageBufferYCbCrMatrix_ITU_R_2020; - break; - - default: - av_log(avctx, AV_LOG_ERROR, "Color space %s is not supported.\n", av_color_space_name(avctx->colorspace)); - return -1; - } + CFRelease(value_cfnum); return 0; } -// constant quality only on Macs with Apple Silicon -static bool vtenc_qscale_enabled(void) -{ - return !TARGET_OS_IPHONE && TARGET_CPU_ARM64; -} - static int vtenc_create_encoder(AVCodecContext *avctx, CMVideoCodecType codec_type, CFStringRef profile_level, @@ -1245,6 +1221,13 @@ static int vtenc_create_encoder(AVCodecContext *avctx, compat_keys.kVTCompressionPropertyKey_TargetQualityForAlpha, alpha_quality_num); CFRelease(alpha_quality_num); + + if (status) { + av_log(avctx, + AV_LOG_ERROR, + "Error setting alpha quality: %d\n", + status); + } } } @@ -1441,6 +1424,64 @@ static int vtenc_create_encoder(AVCodecContext *avctx, } } + if ((avctx->flags & AV_CODEC_FLAG_CLOSED_GOP) != 0) { + set_encoder_property_or_log(avctx, + compat_keys.kVTCompressionPropertyKey_AllowOpenGOP, + "AllowOpenGop", + kCFBooleanFalse); + } + + if (avctx->qmin >= 0) { + status = set_encoder_int_property_or_log(avctx, + compat_keys.kVTCompressionPropertyKey_MinAllowedFrameQP, + "qmin", + avctx->qmin); + + if (status != 0) { + return status; + } + } + + if (avctx->qmax >= 0) { + status = set_encoder_int_property_or_log(avctx, + compat_keys.kVTCompressionPropertyKey_MaxAllowedFrameQP, + "qmax", + avctx->qmax); + + if (status != 0) { + return status; + } + } + + if (vtctx->max_slice_bytes >= 0 && avctx->codec_id == AV_CODEC_ID_H264) { + status = set_encoder_int_property_or_log(avctx, + kVTCompressionPropertyKey_MaxH264SliceBytes, + "max_slice_bytes", + vtctx->max_slice_bytes); + + if (status != 0) { + return status; + } + } + + if (vtctx->power_efficient >= 0) { + set_encoder_property_or_log(avctx, + compat_keys.kVTCompressionPropertyKey_MaximizePowerEfficiency, + "power_efficient", + vtctx->power_efficient ? kCFBooleanTrue : kCFBooleanFalse); + } + + if (vtctx->max_ref_frames > 0) { + status = set_encoder_int_property_or_log(avctx, + compat_keys.kVTCompressionPropertyKey_ReferenceBufferCount, + "max_ref_frames", + vtctx->max_ref_frames); + + if (status != 0) { + return status; + } + } + status = VTCompressionSessionPrepareToEncodeFrames(vtctx->session); if (status) { av_log(avctx, AV_LOG_ERROR, "Error: cannot prepare encoder: %d\n", status); @@ -1480,12 +1521,12 @@ static int vtenc_configure_encoder(AVCodecContext *avctx) vtctx->get_param_set_func = CMVideoFormatDescriptionGetH264ParameterSetAtIndex; vtctx->has_b_frames = avctx->max_b_frames > 0; - if(vtctx->has_b_frames && vtctx->profile == H264_PROF_BASELINE){ + if(vtctx->has_b_frames && (0xFF & vtctx->profile) == FF_PROFILE_H264_BASELINE){ av_log(avctx, AV_LOG_WARNING, "Cannot use B-frames with baseline profile. Output will not contain B-frames.\n"); vtctx->has_b_frames = 0; } - if (vtctx->entropy == VT_CABAC && vtctx->profile == H264_PROF_BASELINE) { + if (vtctx->entropy == VT_CABAC && (0xFF & vtctx->profile) == FF_PROFILE_H264_BASELINE) { av_log(avctx, AV_LOG_WARNING, "CABAC entropy requires 'main' or 'high' profile, but baseline was requested. Encode will not use CABAC entropy.\n"); vtctx->entropy = VT_ENTROPY_NOT_SET; } @@ -1526,6 +1567,13 @@ static int vtenc_configure_encoder(AVCodecContext *avctx) } #endif + // low-latency mode: eliminate frame reordering, follow a one-in-one-out encoding mode + if ((avctx->flags & AV_CODEC_FLAG_LOW_DELAY) && avctx->codec_id == AV_CODEC_ID_H264) { + CFDictionarySetValue(enc_info, + compat_keys.kVTVideoEncoderSpecification_EnableLowLatencyRateControl, + kCFBooleanTrue); + } + if (avctx->pix_fmt != AV_PIX_FMT_VIDEOTOOLBOX) { status = create_cv_pixel_buffer_info(avctx, &pixel_buffer_info); if (status) @@ -1534,9 +1582,10 @@ static int vtenc_configure_encoder(AVCodecContext *avctx) vtctx->dts_delta = vtctx->has_b_frames ? -1 : 0; - get_cv_transfer_function(avctx, &vtctx->transfer_function, &gamma_level); - get_cv_ycbcr_matrix(avctx, &vtctx->ycbcr_matrix); - get_cv_color_primaries(avctx, &vtctx->color_primaries); + get_cv_gamma(avctx, &gamma_level); + vtctx->transfer_function = av_map_videotoolbox_color_trc_from_av(avctx->color_trc); + vtctx->ycbcr_matrix = av_map_videotoolbox_color_matrix_from_av(avctx->colorspace); + vtctx->color_primaries = av_map_videotoolbox_color_primaries_from_av(avctx->color_primaries); if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) { @@ -1582,6 +1631,9 @@ static av_cold int vtenc_init(AVCodecContext *avctx) pthread_mutex_init(&vtctx->lock, NULL); pthread_cond_init(&vtctx->cv_sample_sent, NULL); + // It can happen when user set avctx->profile directly. + if (vtctx->profile == FF_PROFILE_UNKNOWN) + vtctx->profile = avctx->profile; vtctx->session = NULL; status = vtenc_configure_encoder(avctx); if (status) return status; @@ -1650,8 +1702,8 @@ static int find_sei_end(AVCodecContext *avctx, { int nal_type; size_t sei_payload_size = 0; - *sei_end = NULL; uint8_t *nal_start = nal_data; + *sei_end = NULL; if (!nal_size) return 0; @@ -2041,7 +2093,7 @@ static int vtenc_cm_to_avpacket( return AVERROR_EXTERNAL; } - int status = get_params_size(avctx, vid_fmt, &header_size); + status = get_params_size(avctx, vid_fmt, &header_size); if (status) return status; } @@ -2718,14 +2770,21 @@ static const enum AVPixelFormat prores_pix_fmts[] = { OFFSET(frames_after), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, \ { "prio_speed", "prioritize encoding speed", OFFSET(prio_speed), AV_OPT_TYPE_BOOL, \ { .i64 = -1 }, -1, 1, VE }, \ + { "power_efficient", "Set to 1 to enable more power-efficient encoding if supported.", \ + OFFSET(power_efficient), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, VE }, \ + { "max_ref_frames", \ + "Sets the maximum number of reference frames. This only has an effect when the value is less than the maximum allowed by the profile/level.", \ + OFFSET(max_ref_frames), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, #define OFFSET(x) offsetof(VTEncContext, x) static const AVOption h264_options[] = { - { "profile", "Profile", OFFSET(profile), AV_OPT_TYPE_INT64, { .i64 = H264_PROF_AUTO }, H264_PROF_AUTO, H264_PROF_COUNT, VE, "profile" }, - { "baseline", "Baseline Profile", 0, AV_OPT_TYPE_CONST, { .i64 = H264_PROF_BASELINE }, INT_MIN, INT_MAX, VE, "profile" }, - { "main", "Main Profile", 0, AV_OPT_TYPE_CONST, { .i64 = H264_PROF_MAIN }, INT_MIN, INT_MAX, VE, "profile" }, - { "high", "High Profile", 0, AV_OPT_TYPE_CONST, { .i64 = H264_PROF_HIGH }, INT_MIN, INT_MAX, VE, "profile" }, - { "extended", "Extend Profile", 0, AV_OPT_TYPE_CONST, { .i64 = H264_PROF_EXTENDED }, INT_MIN, INT_MAX, VE, "profile" }, + { "profile", "Profile", OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = FF_PROFILE_UNKNOWN }, FF_PROFILE_UNKNOWN, INT_MAX, VE, "profile" }, + { "baseline", "Baseline Profile", 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_H264_BASELINE }, INT_MIN, INT_MAX, VE, "profile" }, + { "constrained_baseline", "Constrained Baseline Profile", 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_H264_CONSTRAINED_BASELINE }, INT_MIN, INT_MAX, VE, "profile" }, + { "main", "Main Profile", 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_H264_MAIN }, INT_MIN, INT_MAX, VE, "profile" }, + { "high", "High Profile", 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_H264_HIGH }, INT_MIN, INT_MAX, VE, "profile" }, + { "constrained_high", "Constrained High Profile", 0, AV_OPT_TYPE_CONST, { .i64 = H264_PROFILE_CONSTRAINED_HIGH }, INT_MIN, INT_MAX, VE, "profile" }, + { "extended", "Extend Profile", 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_H264_EXTENDED }, INT_MIN, INT_MAX, VE, "profile" }, { "level", "Level", OFFSET(level), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 52, VE, "level" }, { "1.3", "Level 1.3, only available with Baseline Profile", 0, AV_OPT_TYPE_CONST, { .i64 = 13 }, INT_MIN, INT_MAX, VE, "level" }, @@ -2748,7 +2807,7 @@ static const AVOption h264_options[] = { { "a53cc", "Use A53 Closed Captions (if available)", OFFSET(a53_cc), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, VE }, { "constant_bit_rate", "Require constant bit rate (macOS 13 or newer)", OFFSET(constant_bit_rate), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, - + { "max_slice_bytes", "Set the maximum number of bytes in an H.264 slice.", OFFSET(max_slice_bytes), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE }, COMMON_OPTIONS { NULL }, }; @@ -2776,9 +2835,9 @@ const FFCodec ff_h264_videotoolbox_encoder = { }; static const AVOption hevc_options[] = { - { "profile", "Profile", OFFSET(profile), AV_OPT_TYPE_INT64, { .i64 = HEVC_PROF_AUTO }, HEVC_PROF_AUTO, HEVC_PROF_COUNT, VE, "profile" }, - { "main", "Main Profile", 0, AV_OPT_TYPE_CONST, { .i64 = HEVC_PROF_MAIN }, INT_MIN, INT_MAX, VE, "profile" }, - { "main10", "Main10 Profile", 0, AV_OPT_TYPE_CONST, { .i64 = HEVC_PROF_MAIN10 }, INT_MIN, INT_MAX, VE, "profile" }, + { "profile", "Profile", OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = FF_PROFILE_UNKNOWN }, FF_PROFILE_UNKNOWN, INT_MAX, VE, "profile" }, + { "main", "Main Profile", 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_HEVC_MAIN }, INT_MIN, INT_MAX, VE, "profile" }, + { "main10", "Main10 Profile", 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_HEVC_MAIN_10 }, INT_MIN, INT_MAX, VE, "profile" }, { "alpha_quality", "Compression quality for the alpha channel", OFFSET(alpha_quality), AV_OPT_TYPE_DOUBLE, { .dbl = 0.0 }, 0.0, 1.0, VE }, @@ -2813,7 +2872,7 @@ const FFCodec ff_hevc_videotoolbox_encoder = { }; static const AVOption prores_options[] = { - { "profile", "Profile", OFFSET(profile), AV_OPT_TYPE_INT64, { .i64 = FF_PROFILE_UNKNOWN }, FF_PROFILE_UNKNOWN, FF_PROFILE_PRORES_XQ, VE, "profile" }, + { "profile", "Profile", OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = FF_PROFILE_UNKNOWN }, FF_PROFILE_UNKNOWN, FF_PROFILE_PRORES_XQ, VE, "profile" }, { "auto", "Automatically determine based on input format", 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_UNKNOWN }, INT_MIN, INT_MAX, VE, "profile" }, { "proxy", "ProRes 422 Proxy", 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_PRORES_PROXY }, INT_MIN, INT_MAX, VE, "profile" }, { "lt", "ProRes 422 LT", 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_PRORES_LT }, INT_MIN, INT_MAX, VE, "profile" }, diff --git a/libavcodec/vmixdec.c b/libavcodec/vmixdec.c new file mode 100644 index 00000000000..b77c90929a1 --- /dev/null +++ b/libavcodec/vmixdec.c @@ -0,0 +1,288 @@ +/* + * vMix decoder + * Copyright (c) 2023 Paul B Mahol + * + * 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 +#include +#include + +#include "libavutil/avassert.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/mem_internal.h" + +#include "avcodec.h" +#include "codec_internal.h" +#include "decode.h" +#define CACHED_BITSTREAM_READER !ARCH_X86_32 +#include "golomb.h" +#include "get_bits.h" +#include "idctdsp.h" +#include "thread.h" + +typedef struct SliceContext { + const uint8_t *dc_ptr; + const uint8_t *ac_ptr; + unsigned dc_size; + unsigned ac_size; +} SliceContext; + +typedef struct VMIXContext { + int nb_slices; + + int16_t factors[64]; + uint8_t scan[64]; + + SliceContext slices[255]; + + IDCTDSPContext idsp; +} VMIXContext; + +static const uint8_t quality[25] = { + 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, + 18, 20, 22, 24, 28, 32, 36, 40, 44, 48, 52, 56, 64, +}; + +static const uint8_t quant[64] = { + 16, 16, 19, 22, 22, 26, 26, 27, + 16, 16, 22, 22, 26, 27, 27, 29, + 19, 22, 26, 26, 27, 29, 29, 35, + 22, 24, 27, 27, 29, 32, 34, 38, + 26, 27, 29, 29, 32, 35, 38, 46, + 27, 29, 34, 34, 35, 40, 46, 56, + 29, 34, 34, 37, 40, 48, 56, 69, + 34, 37, 38, 40, 48, 58, 69, 83, +}; + +static av_cold int decode_init(AVCodecContext *avctx) +{ + VMIXContext *s = avctx->priv_data; + + avctx->bits_per_raw_sample = 8; + avctx->pix_fmt = AV_PIX_FMT_YUV422P; + + avctx->coded_width = FFALIGN(avctx->width, 16); + avctx->coded_height = FFALIGN(avctx->height, 16); + + ff_idctdsp_init(&s->idsp, avctx); + ff_permute_scantable(s->scan, ff_zigzag_direct, + s->idsp.idct_permutation); + return 0; +} + +static inline int get_se_golomb_vmix(GetBitContext *gb) +{ + unsigned int buf = get_ue_golomb_long(gb); + int sign = (buf & 1) - 1; + return ((buf >> 1) ^ (~sign)); +} + +static int decode_dcac(AVCodecContext *avctx, + GetBitContext *dc_gb, GetBitContext *ac_gb, + unsigned *dcrun, unsigned *acrun, + AVFrame *frame, int width, int by, int plane) +{ + const ptrdiff_t linesize = frame->linesize[plane]; + uint8_t *dst = frame->data[plane] + by * linesize; + unsigned dc_run = *dcrun, ac_run = *acrun; + LOCAL_ALIGNED_32(int16_t, block, [64]); + VMIXContext *s = avctx->priv_data; + const int16_t *factors = s->factors; + const uint8_t *scan = s->scan; + const int add = plane ? 0 : 1024; + int i, dc_v = 0, ac_v = 0, dc = 0; + + for (int y = 0; y < 2; y++) { + for (int x = 0; x < width; x += 8) { + memset(block, 0, sizeof(*block)*64); + + if (dc_run > 0) { + dc_run--; + } else { + dc_v = get_se_golomb_vmix(dc_gb); + dc += (unsigned)dc_v; + if (!dc_v) + dc_run = get_ue_golomb_long(dc_gb); + } + + for (int n = 0; n < 64; n++) { + if (ac_run > 0) { + ac_run--; + continue; + } + + ac_v = get_se_golomb_vmix(ac_gb); + i = scan[n]; + block[i] = ((unsigned)ac_v * factors[i]) >> 4; + if (!ac_v) + ac_run = get_ue_golomb_long(ac_gb); + } + + block[0] = dc + add; + s->idsp.idct_put(dst + x, linesize, block); + } + + dst += 8 * linesize; + } + + *dcrun = dc_run; + *acrun = ac_run; + + return 0; +} + +static int decode_slice(AVCodecContext *avctx, AVFrame *frame, + const uint8_t *dc_src, unsigned dc_slice_size, + const uint8_t *ac_src, unsigned ac_slice_size, + int by) +{ + unsigned dc_run = 0, ac_run = 0; + GetBitContext dc_gb, ac_gb; + int ret; + + ret = init_get_bits8(&dc_gb, dc_src, dc_slice_size); + if (ret < 0) + return ret; + + ret = init_get_bits8(&ac_gb, ac_src, ac_slice_size); + if (ret < 0) + return ret; + + for (int p = 0; p < 3; p++) { + const int rshift = !!p; + ret = decode_dcac(avctx, &dc_gb, &ac_gb, + &dc_run, &ac_run, frame, + frame->width >> rshift, by, p); + if (ret < 0) + return ret; + + if (get_bits_left(&dc_gb) < 0) + return AVERROR_INVALIDDATA; + if (get_bits_left(&ac_gb) < 0) + return AVERROR_INVALIDDATA; + + align_get_bits(&dc_gb); + align_get_bits(&ac_gb); + } + + if (get_bits_left(&dc_gb) > 0) + return AVERROR_INVALIDDATA; + if (get_bits_left(&ac_gb) > 0) + return AVERROR_INVALIDDATA; + + return 0; +} + +static int decode_slices(AVCodecContext *avctx, void *arg, + int n, int thread_nb) +{ + VMIXContext *s = avctx->priv_data; + const uint8_t *dc_slice_ptr = s->slices[n].dc_ptr; + const uint8_t *ac_slice_ptr = s->slices[n].ac_ptr; + unsigned dc_slice_size = s->slices[n].dc_size; + unsigned ac_slice_size = s->slices[n].ac_size; + AVFrame *frame = arg; + + return decode_slice(avctx, frame, dc_slice_ptr, dc_slice_size, + ac_slice_ptr, ac_slice_size, n * 16); +} + +static int decode_frame(AVCodecContext *avctx, + AVFrame *frame, int *got_frame, + AVPacket *avpkt) +{ + VMIXContext *s = avctx->priv_data; + unsigned offset = 3, q; + int ret; + + if (avpkt->size <= 7) + return AVERROR_INVALIDDATA; + + if (avpkt->data[0] != 0x01) + return AVERROR_INVALIDDATA; + + q = av_clip(99 - av_clip(avpkt->data[1], 0, 99), 0, FF_ARRAY_ELEMS(quality) - 1); + for (int n = 0; n < 64; n++) + s->factors[n] = quant[n] * quality[q]; + + s->nb_slices = avpkt->data[2]; + if (!s->nb_slices || s->nb_slices > (avctx->height + 15) / 16) + return AVERROR_INVALIDDATA; + + for (int n = 0; n < s->nb_slices; n++) { + unsigned slice_size; + + if (offset + 4 > avpkt->size) + return AVERROR_INVALIDDATA; + + slice_size = AV_RL32(avpkt->data + offset); + if (slice_size > avpkt->size) + return AVERROR_INVALIDDATA; + + if (avpkt->size - slice_size - 4LL < offset) + return AVERROR_INVALIDDATA; + + s->slices[n].dc_size = slice_size; + s->slices[n].dc_ptr = avpkt->data + offset + 4; + offset += slice_size + 4; + } + + for (int n = 0; n < s->nb_slices; n++) { + unsigned slice_size; + + if (offset + 4 > avpkt->size) + return AVERROR_INVALIDDATA; + + slice_size = AV_RL32(avpkt->data + offset); + if (slice_size > avpkt->size) + return AVERROR_INVALIDDATA; + + if (avpkt->size - slice_size - 4LL < offset) + return AVERROR_INVALIDDATA; + + s->slices[n].ac_size = slice_size; + s->slices[n].ac_ptr = avpkt->data + offset + 4; + offset += slice_size + 4; + } + + ret = ff_thread_get_buffer(avctx, frame, 0); + if (ret < 0) + return ret; + + avctx->execute2(avctx, decode_slices, frame, NULL, s->nb_slices); + + frame->pict_type = AV_PICTURE_TYPE_I; + frame->flags |= AV_FRAME_FLAG_KEY; + + *got_frame = 1; + + return avpkt->size; +} + +const FFCodec ff_vmix_decoder = { + .p.name = "vmix", + CODEC_LONG_NAME("vMix Video"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VMIX, + .priv_data_size = sizeof(VMIXContext), + .init = decode_init, + FF_CODEC_DECODE_CB(decode_frame), + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_SLICE_THREADS, +}; diff --git a/libavcodec/vmnc.c b/libavcodec/vmnc.c index 8daaf08c5d3..17e3a2f3c18 100644 --- a/libavcodec/vmnc.c +++ b/libavcodec/vmnc.c @@ -339,7 +339,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, if ((ret = ff_reget_buffer(avctx, c->pic, 0)) < 0) return ret; - c->pic->key_frame = 0; + c->pic->flags &= ~AV_FRAME_FLAG_KEY; c->pic->pict_type = AV_PICTURE_TYPE_P; // restore screen after cursor @@ -441,7 +441,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, bytestream2_skip(gb, 4); break; case MAGIC_WMVi: // ServerInitialization struct - c->pic->key_frame = 1; + c->pic->flags |= AV_FRAME_FLAG_KEY; c->pic->pict_type = AV_PICTURE_TYPE_I; depth = bytestream2_get_byte(gb); if (depth != c->bpp) { diff --git a/libavcodec/vorbis_parser.c b/libavcodec/vorbis_parser.c index a7d15d4ce99..d2c9e647ce5 100644 --- a/libavcodec/vorbis_parser.c +++ b/libavcodec/vorbis_parser.c @@ -234,7 +234,8 @@ int av_vorbis_parse_frame_flags(AVVorbisParseContext *s, const uint8_t *buf, else if (buf[0] == 5) *flags |= VORBIS_FLAG_SETUP; else - goto bad_packet; + av_log(s, AV_LOG_VERBOSE, "Ignoring packet with unknown type %u\n", + buf[0]); /* Special packets have no duration. */ return 0; diff --git a/libavcodec/vorbisdec.c b/libavcodec/vorbisdec.c index dd856a6dfee..1d2a0997607 100644 --- a/libavcodec/vorbisdec.c +++ b/libavcodec/vorbisdec.c @@ -38,6 +38,7 @@ #include "codec_internal.h" #include "decode.h" #include "get_bits.h" +#include "internal.h" #include "vorbis.h" #include "vorbisdsp.h" #include "vorbis_data.h" @@ -134,7 +135,6 @@ typedef struct vorbis_context_s { av_tx_fn mdct_fn[2]; uint8_t first_frame; - int64_t initial_pts; uint32_t version; uint8_t audio_channels; uint32_t audio_samplerate; @@ -368,6 +368,10 @@ static int vorbis_parse_setup_hdr_codebooks(vorbis_context *vc) unsigned codebook_value_bits = get_bits(gb, 4) + 1; unsigned codebook_sequence_p = get_bits1(gb); + if (!isfinite(codebook_minimum_value) || !isfinite(codebook_delta_value)) { + ret = AVERROR_INVALIDDATA; + goto error; + } ff_dlog(NULL, " We expect %d numbers for building the codevectors. \n", codebook_lookup_values); ff_dlog(NULL, " delta %f minmum %f \n", @@ -1839,13 +1843,7 @@ static int vorbis_decode_frame(AVCodecContext *avctx, AVFrame *frame, if (!vc->first_frame) { vc->first_frame = 1; - vc->initial_pts = frame->pts; - } - - if (frame->pts == vc->initial_pts) { - *got_frame_ptr = 0; - av_frame_unref(frame); - return buf_size; + avctx->internal->skip_samples = len; } ff_dlog(NULL, "parsed %d bytes %d bits, returned %d samples (*ch*bits) \n", @@ -1877,6 +1875,7 @@ static av_cold void vorbis_decode_flush(AVCodecContext *avctx) sizeof(*vc->saved)); } vc->previous_window = -1; + vc->first_frame = 0; } const FFCodec ff_vorbis_decoder = { diff --git a/libavcodec/vp3.c b/libavcodec/vp3.c index b731bc0669c..9e097c8905d 100644 --- a/libavcodec/vp3.c +++ b/libavcodec/vp3.c @@ -2353,6 +2353,8 @@ static av_cold int vp3_decode_init(AVCodecContext *avctx) s->avctx = avctx; s->width = FFALIGN(avctx->coded_width, 16); s->height = FFALIGN(avctx->coded_height, 16); + if (s->width < 18) + return AVERROR_PATCHWELCOME; if (avctx->codec_id != AV_CODEC_ID_THEORA) avctx->pix_fmt = AV_PIX_FMT_YUV420P; avctx->chroma_sample_location = AVCHROMA_LOC_CENTER; @@ -2654,8 +2656,8 @@ static int vp3_decode_frame(AVCodecContext *avctx, AVFrame *frame, s->qps[i] = -1; if (s->avctx->debug & FF_DEBUG_PICT_INFO) - av_log(s->avctx, AV_LOG_INFO, " VP3 %sframe #%d: Q index = %d\n", - s->keyframe ? "key" : "", avctx->frame_number + 1, s->qps[0]); + av_log(s->avctx, AV_LOG_INFO, " VP3 %sframe #%"PRId64": Q index = %d\n", + s->keyframe ? "key" : "", avctx->frame_num + 1, s->qps[0]); s->skip_loop_filter = !s->filter_limit_values[s->qps[0]] || avctx->skip_loop_filter >= (s->keyframe ? AVDISCARD_ALL @@ -2675,7 +2677,10 @@ static int vp3_decode_frame(AVCodecContext *avctx, AVFrame *frame, s->current_frame.f->pict_type = s->keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; - s->current_frame.f->key_frame = s->keyframe; + if (s->keyframe) + s->current_frame.f->flags |= AV_FRAME_FLAG_KEY; + else + s->current_frame.f->flags &= ~AV_FRAME_FLAG_KEY; if ((ret = ff_thread_get_ext_buffer(avctx, &s->current_frame, AV_GET_BUFFER_FLAG_REF)) < 0) goto error; @@ -2701,7 +2706,7 @@ static int vp3_decode_frame(AVCodecContext *avctx, AVFrame *frame, } #endif s->version = version; - if (avctx->frame_number == 0) + if (avctx->frame_num == 0) av_log(s->avctx, AV_LOG_DEBUG, "VP version: %d\n", s->version); } @@ -2919,7 +2924,9 @@ static int theora_decode_header(AVCodecContext *avctx, GetBitContext *gb) /* sanity check */ if (av_image_check_size(visible_width, visible_height, 0, avctx) < 0 || visible_width + offset_x > s->width || - visible_height + offset_y > s->height) { + visible_height + offset_y > s->height || + visible_width < 18 + ) { av_log(avctx, AV_LOG_ERROR, "Invalid frame dimensions - w:%d h:%d x:%d y:%d (%dx%d).\n", visible_width, visible_height, offset_x, offset_y, @@ -2965,6 +2972,8 @@ static int theora_decode_header(AVCodecContext *avctx, GetBitContext *gb) } else avctx->pix_fmt = AV_PIX_FMT_YUV420P; + if (s->width < 18) + return AVERROR_PATCHWELCOME; ret = ff_set_dimensions(avctx, s->width, s->height); if (ret < 0) return ret; diff --git a/libavcodec/vp5.c b/libavcodec/vp5.c index 579333506af..78d4b38ce3e 100644 --- a/libavcodec/vp5.c +++ b/libavcodec/vp5.c @@ -44,10 +44,13 @@ static int vp5_parse_header(VP56Context *s, const uint8_t *buf, int buf_size) ret = ff_vpx_init_range_decoder(&s->c, buf, buf_size); if (ret < 0) return ret; - s->frames[VP56_FRAME_CURRENT]->key_frame = !vpx_rac_get(c); + if (!vpx_rac_get(c)) + s->frames[VP56_FRAME_CURRENT]->flags |= AV_FRAME_FLAG_KEY; + else + s->frames[VP56_FRAME_CURRENT]->flags &= ~AV_FRAME_FLAG_KEY; vpx_rac_get(c); ff_vp56_init_dequant(s, vp56_rac_gets(c, 6)); - if (s->frames[VP56_FRAME_CURRENT]->key_frame) + if (s->frames[VP56_FRAME_CURRENT]->flags & AV_FRAME_FLAG_KEY) { int render_x, render_y; @@ -148,7 +151,7 @@ static int vp5_parse_coeff_models(VP56Context *s) if (vpx_rac_get_prob_branchy(c, vp5_dccv_pct[pt][node])) { def_prob[node] = vp56_rac_gets_nn(c, 7); model->coeff_dccv[pt][node] = def_prob[node]; - } else if (s->frames[VP56_FRAME_CURRENT]->key_frame) { + } else if (s->frames[VP56_FRAME_CURRENT]->flags & AV_FRAME_FLAG_KEY) { model->coeff_dccv[pt][node] = def_prob[node]; } @@ -159,7 +162,7 @@ static int vp5_parse_coeff_models(VP56Context *s) if (vpx_rac_get_prob_branchy(c, vp5_ract_pct[ct][pt][cg][node])) { def_prob[node] = vp56_rac_gets_nn(c, 7); model->coeff_ract[pt][ct][cg][node] = def_prob[node]; - } else if (s->frames[VP56_FRAME_CURRENT]->key_frame) { + } else if (s->frames[VP56_FRAME_CURRENT]->flags & AV_FRAME_FLAG_KEY) { model->coeff_ract[pt][ct][cg][node] = def_prob[node]; } diff --git a/libavcodec/vp56.c b/libavcodec/vp56.c index bd994428a42..a5c5b23622a 100644 --- a/libavcodec/vp56.c +++ b/libavcodec/vp56.c @@ -350,7 +350,7 @@ static void vp56_mc(VP56Context *s, int b, int plane, uint8_t *src, if (s->avctx->skip_loop_filter >= AVDISCARD_ALL || (s->avctx->skip_loop_filter >= AVDISCARD_NONKEY - && !s->frames[VP56_FRAME_CURRENT]->key_frame)) + && !(s->frames[VP56_FRAME_CURRENT]->flags & AV_FRAME_FLAG_KEY))) deblock_filtering = 0; dx = s->mv[b].x / s->vp56_coord_div[b]; @@ -493,7 +493,7 @@ static int vp56_decode_mb(VP56Context *s, int row, int col, int is_alpha) VP56mb mb_type; int ret; - if (s->frames[VP56_FRAME_CURRENT]->key_frame) + if (s->frames[VP56_FRAME_CURRENT]->flags & AV_FRAME_FLAG_KEY) mb_type = VP56_MB_INTRA; else mb_type = vp56_decode_mv(s, row, col); @@ -511,7 +511,7 @@ static int vp56_conceal_mb(VP56Context *s, int row, int col, int is_alpha) { VP56mb mb_type; - if (s->frames[VP56_FRAME_CURRENT]->key_frame) + if (s->frames[VP56_FRAME_CURRENT]->flags & AV_FRAME_FLAG_KEY) mb_type = VP56_MB_INTRA; else mb_type = vp56_conceal_mv(s, row, col); @@ -596,6 +596,7 @@ int ff_vp56_decode_frame(AVCodecContext *avctx, AVFrame *rframe, if (s->alpha_context) av_frame_unref(s->alpha_context->frames[i]); } + s->frames[VP56_FRAME_CURRENT]->flags |= AV_FRAME_FLAG_KEY; //FIXME } ret = ff_get_buffer(avctx, p, AV_GET_BUFFER_FLAG_REF); @@ -670,7 +671,7 @@ static int ff_vp56_decode_mbs(AVCodecContext *avctx, void *data, int res; int damaged = 0; - if (p->key_frame) { + if (p->flags & AV_FRAME_FLAG_KEY) { p->pict_type = AV_PICTURE_TYPE_I; s->default_models_init(s); for (block=0; blockmb_height*s->mb_width; block++) @@ -762,7 +763,7 @@ static int ff_vp56_decode_mbs(AVCodecContext *avctx, void *data, s->have_undamaged_frame = 1; next: - if (p->key_frame || s->golden_frame) { + if ((p->flags & AV_FRAME_FLAG_KEY) || s->golden_frame) { av_frame_unref(s->frames[VP56_FRAME_GOLDEN]); if ((res = av_frame_ref(s->frames[VP56_FRAME_GOLDEN], p)) < 0) return res; diff --git a/libavcodec/vp6.c b/libavcodec/vp6.c index 9bbfa0eb5d7..7a519cf10dd 100644 --- a/libavcodec/vp6.c +++ b/libavcodec/vp6.c @@ -57,10 +57,13 @@ static int vp6_parse_header(VP56Context *s, const uint8_t *buf, int buf_size) int ret; int separated_coeff = buf[0] & 1; - s->frames[VP56_FRAME_CURRENT]->key_frame = !(buf[0] & 0x80); + if (!(buf[0] & 0x80)) + s->frames[VP56_FRAME_CURRENT]->flags |= AV_FRAME_FLAG_KEY; + else + s->frames[VP56_FRAME_CURRENT]->flags &= ~AV_FRAME_FLAG_KEY; ff_vp56_init_dequant(s, (buf[0] >> 1) & 0x3F); - if (s->frames[VP56_FRAME_CURRENT]->key_frame) { + if (s->frames[VP56_FRAME_CURRENT]->flags & AV_FRAME_FLAG_KEY) { sub_version = buf[1] >> 3; if (sub_version > 8) return AVERROR_INVALIDDATA; @@ -299,7 +302,7 @@ static int vp6_parse_coeff_models(VP56Context *s) if (vpx_rac_get_prob_branchy(c, vp6_dccv_pct[pt][node])) { def_prob[node] = vp56_rac_gets_nn(c, 7); model->coeff_dccv[pt][node] = def_prob[node]; - } else if (s->frames[VP56_FRAME_CURRENT]->key_frame) { + } else if (s->frames[VP56_FRAME_CURRENT]->flags & AV_FRAME_FLAG_KEY) { model->coeff_dccv[pt][node] = def_prob[node]; } @@ -322,7 +325,7 @@ static int vp6_parse_coeff_models(VP56Context *s) if (vpx_rac_get_prob_branchy(c, vp6_ract_pct[ct][pt][cg][node])) { def_prob[node] = vp56_rac_gets_nn(c, 7); model->coeff_ract[pt][ct][cg][node] = def_prob[node]; - } else if (s->frames[VP56_FRAME_CURRENT]->key_frame) { + } else if (s->frames[VP56_FRAME_CURRENT]->flags & AV_FRAME_FLAG_KEY) { model->coeff_ract[pt][ct][cg][node] = def_prob[node]; } diff --git a/libavcodec/vp8.c b/libavcodec/vp8.c index db2419deaf7..64b1c7f60e3 100644 --- a/libavcodec/vp8.c +++ b/libavcodec/vp8.c @@ -31,6 +31,7 @@ #include "avcodec.h" #include "codec_internal.h" #include "decode.h" +#include "hwaccel_internal.h" #include "hwconfig.h" #include "mathops.h" #include "thread.h" @@ -104,23 +105,21 @@ static int vp8_alloc_frame(VP8Context *s, VP8Frame *f, int ref) if ((ret = ff_thread_get_ext_buffer(s->avctx, &f->tf, ref ? AV_GET_BUFFER_FLAG_REF : 0)) < 0) return ret; - if (!(f->seg_map = av_buffer_allocz(s->mb_width * s->mb_height))) + if (!(f->seg_map = av_buffer_allocz(s->mb_width * s->mb_height))) { + ret = AVERROR(ENOMEM); goto fail; - if (s->avctx->hwaccel) { - const AVHWAccel *hwaccel = s->avctx->hwaccel; - if (hwaccel->frame_priv_data_size) { - f->hwaccel_priv_buf = av_buffer_allocz(hwaccel->frame_priv_data_size); - if (!f->hwaccel_priv_buf) - goto fail; - f->hwaccel_picture_private = f->hwaccel_priv_buf->data; - } } + ret = ff_hwaccel_frame_priv_alloc(s->avctx, &f->hwaccel_picture_private, + &f->hwaccel_priv_buf); + if (ret < 0) + goto fail; + return 0; fail: av_buffer_unref(&f->seg_map); ff_thread_release_ext_buffer(s->avctx, &f->tf); - return AVERROR(ENOMEM); + return ret; } static void vp8_release_frame(VP8Context *s, VP8Frame *f) @@ -167,6 +166,9 @@ static void vp8_decode_flush_impl(AVCodecContext *avctx, int free_mem) if (free_mem) free_buffers(s); + + if (FF_HW_HAS_CB(avctx, flush)) + FF_HW_SIMPLE_CALL(avctx, flush); } static void vp8_decode_flush(AVCodecContext *avctx) @@ -2732,7 +2734,10 @@ int vp78_decode_frame(AVCodecContext *avctx, AVFrame *rframe, int *got_frame, goto err; } - curframe->tf.f->key_frame = s->keyframe; + 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; if ((ret = vp8_alloc_frame(s, curframe, referenced)) < 0) @@ -2760,15 +2765,16 @@ int vp78_decode_frame(AVCodecContext *avctx, AVFrame *rframe, int *got_frame, ff_thread_finish_setup(avctx); if (avctx->hwaccel) { - ret = avctx->hwaccel->start_frame(avctx, avpkt->data, avpkt->size); + const FFHWAccel *hwaccel = ffhwaccel(avctx->hwaccel); + ret = hwaccel->start_frame(avctx, avpkt->data, avpkt->size); if (ret < 0) goto err; - ret = avctx->hwaccel->decode_slice(avctx, avpkt->data, avpkt->size); + ret = hwaccel->decode_slice(avctx, avpkt->data, avpkt->size); if (ret < 0) goto err; - ret = avctx->hwaccel->end_frame(avctx); + ret = hwaccel->end_frame(avctx); if (ret < 0) goto err; diff --git a/libavcodec/vp8data.c b/libavcodec/vp8data.c new file mode 100644 index 00000000000..a41fc872f58 --- /dev/null +++ b/libavcodec/vp8data.c @@ -0,0 +1,42 @@ +/* + * 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 "stdint.h" + +// cat 1 and 2 are defined in vp8data.h +static const uint8_t vp8_dct_cat3_prob[] = { + 173, 148, 140, 0 +}; +static const uint8_t vp8_dct_cat4_prob[] = { + 176, 155, 140, 135, 0 +}; +static const uint8_t vp8_dct_cat5_prob[] = { + 180, 157, 141, 134, 130, 0 +}; +static const uint8_t vp8_dct_cat6_prob[] = { + 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129, 0 +}; + +// only used for cat3 and above; cat 1 and 2 are referenced directly. +const uint8_t *const ff_vp8_dct_cat_prob[] = { + vp8_dct_cat3_prob, + vp8_dct_cat4_prob, + vp8_dct_cat5_prob, + vp8_dct_cat6_prob, +}; + diff --git a/libavcodec/vp8data.h b/libavcodec/vp8data.h index 1fcce134eb2..8b8f1ed1118 100644 --- a/libavcodec/vp8data.h +++ b/libavcodec/vp8data.h @@ -339,26 +339,8 @@ static const uint8_t vp8_dct_cat1_prob[] = { static const uint8_t vp8_dct_cat2_prob[] = { 165, 145, 0 }; -static const uint8_t vp8_dct_cat3_prob[] = { - 173, 148, 140, 0 -}; -static const uint8_t vp8_dct_cat4_prob[] = { - 176, 155, 140, 135, 0 -}; -static const uint8_t vp8_dct_cat5_prob[] = { - 180, 157, 141, 134, 130, 0 -}; -static const uint8_t vp8_dct_cat6_prob[] = { - 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129, 0 -}; -// only used for cat3 and above; cat 1 and 2 are referenced directly -const uint8_t *const ff_vp8_dct_cat_prob[] = { - vp8_dct_cat3_prob, - vp8_dct_cat4_prob, - vp8_dct_cat5_prob, - vp8_dct_cat6_prob, -}; +extern const uint8_t *const ff_vp8_dct_cat_prob[]; static const uint8_t vp8_token_default_probs[4][8][3][NUM_DCT_TOKENS - 1] = { { diff --git a/libavcodec/vp9.c b/libavcodec/vp9.c index 7c0a2464467..89f7549ef0b 100644 --- a/libavcodec/vp9.c +++ b/libavcodec/vp9.c @@ -27,6 +27,7 @@ #include "codec_internal.h" #include "decode.h" #include "get_bits.h" +#include "hwaccel_internal.h" #include "hwconfig.h" #include "profiles.h" #include "thread.h" @@ -119,12 +120,14 @@ static int vp9_frame_alloc(AVCodecContext *avctx, VP9Frame *f) s->frame_extradata_pool = av_buffer_pool_init(sz * (1 + sizeof(VP9mvrefPair)), NULL); 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_buffer_pool_get(s->frame_extradata_pool); if (!f->extradata) { + ret = AVERROR(ENOMEM); goto fail; } memset(f->extradata->data, 0, f->extradata->size); @@ -132,22 +135,16 @@ static int vp9_frame_alloc(AVCodecContext *avctx, VP9Frame *f) f->segmentation_map = f->extradata->data; f->mv = (VP9mvrefPair *) (f->extradata->data + sz); - if (avctx->hwaccel) { - const AVHWAccel *hwaccel = avctx->hwaccel; - av_assert0(!f->hwaccel_picture_private); - if (hwaccel->frame_priv_data_size) { - f->hwaccel_priv_buf = av_buffer_allocz(hwaccel->frame_priv_data_size); - if (!f->hwaccel_priv_buf) - goto fail; - f->hwaccel_picture_private = f->hwaccel_priv_buf->data; - } - } + ret = ff_hwaccel_frame_priv_alloc(avctx, &f->hwaccel_picture_private, + &f->hwaccel_priv_buf); + if (ret < 0) + goto fail; return 0; fail: vp9_frame_unref(avctx, f); - return AVERROR(ENOMEM); + return ret; } static int vp9_frame_ref(AVCodecContext *avctx, VP9Frame *dst, VP9Frame *src) @@ -239,6 +236,13 @@ static int update_size(AVCodecContext *avctx, int w, int h) case AV_PIX_FMT_YUV444P12: #if CONFIG_VP9_VAAPI_HWACCEL *fmtp++ = AV_PIX_FMT_VAAPI; +#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 break; } @@ -1606,7 +1610,10 @@ static int vp9_decode_frame(AVCodecContext *avctx, AVFrame *frame, if ((ret = vp9_frame_alloc(avctx, &s->s.frames[CUR_FRAME])) < 0) return ret; f = s->s.frames[CUR_FRAME].tf.f; - f->key_frame = s->s.h.keyframe; + if (s->s.h.keyframe) + f->flags |= AV_FRAME_FLAG_KEY; + else + f->flags &= ~AV_FRAME_FLAG_KEY; f->pict_type = (s->s.h.keyframe || s->s.h.intraonly) ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; if (s->s.frames[REF_FRAME_SEGMAP].tf.f->buf[0] && @@ -1629,13 +1636,14 @@ static int vp9_decode_frame(AVCodecContext *avctx, AVFrame *frame, } if (avctx->hwaccel) { - ret = avctx->hwaccel->start_frame(avctx, NULL, 0); + const FFHWAccel *hwaccel = ffhwaccel(avctx->hwaccel); + ret = hwaccel->start_frame(avctx, NULL, 0); if (ret < 0) return ret; - ret = avctx->hwaccel->decode_slice(avctx, pkt->data, pkt->size); + ret = hwaccel->decode_slice(avctx, pkt->data, pkt->size); if (ret < 0) return ret; - ret = avctx->hwaccel->end_frame(avctx); + ret = hwaccel->end_frame(avctx); if (ret < 0) return ret; goto finish; @@ -1791,6 +1799,9 @@ static void vp9_decode_flush(AVCodecContext *avctx) vp9_frame_unref(avctx, &s->s.frames[i]); for (i = 0; i < 8; i++) ff_thread_release_ext_buffer(avctx, &s->s.refs[i]); + + if (FF_HW_HAS_CB(avctx, flush)) + FF_HW_SIMPLE_CALL(avctx, flush); } static av_cold int vp9_decode_init(AVCodecContext *avctx) diff --git a/libavcodec/vqavideo.c b/libavcodec/vqavideo.c index 0573696d94d..2977cf9a526 100644 --- a/libavcodec/vqavideo.c +++ b/libavcodec/vqavideo.c @@ -809,7 +809,11 @@ static int vqa_decode_frame(AVCodecContext *avctx, AVFrame *rframe, /* make the palette available on the way out */ memcpy(s->frame->data[1], s->palette, PALETTE_COUNT * 4); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS s->frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } else if (avctx->pix_fmt == AV_PIX_FMT_RGB555LE) { if ((res = vqa_decode_frame_hicolor(s, s->frame)) < 0) return res; diff --git a/libavcodec/mpeg4video_parser.h b/libavcodec/vulkan.c similarity index 62% rename from libavcodec/mpeg4video_parser.h rename to libavcodec/vulkan.c index 8008e693b43..fc8a1fa47ba 100644 --- a/libavcodec/mpeg4video_parser.h +++ b/libavcodec/vulkan.c @@ -1,8 +1,4 @@ /* - * MPEG-4 video parser prototypes - * Copyright (c) 2003 Fabrice Bellard - * Copyright (c) 2003 Michael Niedermayer - * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -20,15 +16,4 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef AVCODEC_MPEG4VIDEO_PARSER_H -#define AVCODEC_MPEG4VIDEO_PARSER_H - -#include "parser.h" - -/** - * Find the end of the current frame in the bitstream. - * @return the position of the first byte of the next frame, or -1 - */ -int ff_mpeg4_find_frame_end(ParseContext *pc, const uint8_t *buf, int buf_size); - -#endif /* AVCODEC_MPEG4VIDEO_PARSER_H */ +#include "libavutil/vulkan.c" diff --git a/libavutil/bfin/timer.h b/libavcodec/vulkan.h similarity index 62% rename from libavutil/bfin/timer.h rename to libavcodec/vulkan.h index 644573daec9..b15efd4addb 100644 --- a/libavutil/bfin/timer.h +++ b/libavcodec/vulkan.h @@ -1,6 +1,4 @@ /* - * Copyright (C) 2007 Marc Hoffman - * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -18,24 +16,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef AVUTIL_BFIN_TIMER_H -#define AVUTIL_BFIN_TIMER_H - -#include - -#define AV_READ_TIME read_time +#ifndef AVCODEC_VULKAN_H +#define AVCODEC_VULKAN_H -static inline uint64_t read_time(void) -{ - union { - struct { - unsigned lo; - unsigned hi; - } p; - unsigned long long c; - } t; - __asm__ volatile ("%0=cycles; %1=cycles2;" : "=d" (t.p.lo), "=d" (t.p.hi)); - return t.c; -} +#include "libavutil/vulkan.h" -#endif /* AVUTIL_BFIN_TIMER_H */ +#endif /* AVCODEC_VULKAN_H */ diff --git a/libavcodec/vulkan_av1.c b/libavcodec/vulkan_av1.c new file mode 100644 index 00000000000..adaebb28180 --- /dev/null +++ b/libavcodec/vulkan_av1.c @@ -0,0 +1,605 @@ +/* + * 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 "av1dec.h" + +#include "vulkan_decode.h" + +/* Maximum number of tiles specified by any defined level */ +#define MAX_TILES 256 + +const VkExtensionProperties ff_vk_dec_av1_ext = { + .extensionName = VK_STD_VULKAN_VIDEO_CODEC_AV1_DECODE_EXTENSION_NAME, + .specVersion = VK_STD_VULKAN_VIDEO_CODEC_AV1_DECODE_SPEC_VERSION, +}; + +typedef struct AV1VulkanDecodePicture { + FFVulkanDecodePicture vp; + + /* Workaround for a spec issue. + *Can be removed once no longer needed, and threading can be enabled. */ + FFVulkanDecodeContext *dec; + + StdVideoAV1MESATile tiles[MAX_TILES]; + StdVideoAV1MESATileList tile_list; + const uint32_t *tile_offsets; + + /* Current picture */ + VkVideoDecodeAV1DpbSlotInfoMESA vkav1_ref; + StdVideoAV1MESAFrameHeader av1_frame_header; + VkVideoDecodeAV1PictureInfoMESA av1_pic_info; + + /* Picture refs */ + const AV1Frame *ref_src [AV1_NUM_REF_FRAMES]; + VkVideoDecodeAV1DpbSlotInfoMESA vkav1_refs[AV1_NUM_REF_FRAMES]; + + uint8_t frame_id_set; + uint8_t frame_id; +} AV1VulkanDecodePicture; + +static int vk_av1_fill_pict(AVCodecContext *avctx, const AV1Frame **ref_src, + VkVideoReferenceSlotInfoKHR *ref_slot, /* Main structure */ + VkVideoPictureResourceInfoKHR *ref, /* Goes in ^ */ + VkVideoDecodeAV1DpbSlotInfoMESA *vkav1_ref, /* Goes in ^ */ + const AV1Frame *pic, int is_current, int has_grain, + int dpb_slot_index) +{ + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + AV1VulkanDecodePicture *hp = pic->hwaccel_picture_private; + FFVulkanDecodePicture *vkpic = &hp->vp; + + int err = ff_vk_decode_prepare_frame(dec, pic->f, vkpic, is_current, + has_grain || dec->dedicated_dpb); + if (err < 0) + return err; + + *vkav1_ref = (VkVideoDecodeAV1DpbSlotInfoMESA) { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_DPB_SLOT_INFO_MESA, + .frameIdx = hp->frame_id, + }; + + for (unsigned i = 0; i < 7; i++) { + const int idx = pic->raw_frame_header->ref_frame_idx[i]; + vkav1_ref->ref_order_hint[i] = pic->raw_frame_header->ref_order_hint[idx]; + } + + vkav1_ref->disable_frame_end_update_cdf = pic->raw_frame_header->disable_frame_end_update_cdf; + + *ref = (VkVideoPictureResourceInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_INFO_KHR, + .codedOffset = (VkOffset2D){ 0, 0 }, + .codedExtent = (VkExtent2D){ pic->f->width, pic->f->height }, + .baseArrayLayer = ((has_grain || dec->dedicated_dpb) && dec->layered_dpb) ? + dpb_slot_index : 0, + .imageViewBinding = vkpic->img_view_ref, + }; + + *ref_slot = (VkVideoReferenceSlotInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_REFERENCE_SLOT_INFO_KHR, + .pNext = vkav1_ref, + .slotIndex = dpb_slot_index, + .pPictureResource = ref, + }; + + if (ref_src) + *ref_src = pic; + + return 0; +} + +static int vk_av1_create_params(AVCodecContext *avctx, AVBufferRef **buf) +{ + VkResult ret; + + const AV1DecContext *s = avctx->priv_data; + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + FFVulkanDecodeShared *ctx = (FFVulkanDecodeShared *)dec->shared_ref->data; + FFVulkanFunctions *vk = &ctx->s.vkfn; + + const AV1RawSequenceHeader *seq = s->raw_seq; + + StdVideoAV1MESASequenceHeader av1_sequence_header; + VkVideoDecodeAV1SessionParametersAddInfoMESA av1_params_info; + VkVideoDecodeAV1SessionParametersCreateInfoMESA av1_params; + VkVideoSessionParametersCreateInfoKHR session_params_create; + + AVBufferRef *tmp; + VkVideoSessionParametersKHR *par = av_malloc(sizeof(*par)); + if (!par) + return AVERROR(ENOMEM); + + av1_sequence_header = (StdVideoAV1MESASequenceHeader) { + .flags = (StdVideoAV1MESASequenceHeaderFlags) { + .still_picture = seq->still_picture, + .reduced_still_picture_header = seq->reduced_still_picture_header, + .use_128x128_superblock = seq->use_128x128_superblock, + .enable_filter_intra = seq->enable_filter_intra, + .enable_intra_edge_filter = seq->enable_intra_edge_filter, + .enable_interintra_compound = seq->enable_interintra_compound, + .enable_masked_compound = seq->enable_masked_compound, + .enable_warped_motion = seq->enable_warped_motion, + .enable_dual_filter = seq->enable_dual_filter, + .enable_order_hint = seq->enable_order_hint, + .enable_jnt_comp = seq->enable_jnt_comp, + .enable_ref_frame_mvs = seq->enable_ref_frame_mvs, + .frame_id_numbers_present_flag = seq->frame_id_numbers_present_flag, + .enable_superres = seq->enable_superres, + .enable_cdef = seq->enable_cdef, + .enable_restoration = seq->enable_restoration, + .film_grain_params_present = seq->film_grain_params_present, + .timing_info_present_flag = seq->timing_info_present_flag, + .initial_display_delay_present_flag = seq->initial_display_delay_present_flag, + }, + .seq_profile = seq->seq_profile, + .frame_width_bits_minus_1 = seq->frame_width_bits_minus_1, + .frame_height_bits_minus_1 = seq->frame_height_bits_minus_1, + .max_frame_width_minus_1 = seq->max_frame_width_minus_1, + .max_frame_height_minus_1 = seq->max_frame_height_minus_1, + .delta_frame_id_length_minus_2 = seq->delta_frame_id_length_minus_2, + .additional_frame_id_length_minus_1 = seq->additional_frame_id_length_minus_1, + .order_hint_bits_minus_1 = seq->order_hint_bits_minus_1, + .timing_info = (StdVideoAV1MESATimingInfo) { + .flags = (StdVideoAV1MESATimingInfoFlags) { + .equal_picture_interval = seq->timing_info.equal_picture_interval, + }, + .num_units_in_display_tick = seq->timing_info.num_units_in_display_tick, + .time_scale = seq->timing_info.time_scale, + .num_ticks_per_picture_minus_1 = seq->timing_info.num_ticks_per_picture_minus_1, + }, + .color_config = (StdVideoAV1MESAColorConfig) { + .flags = (StdVideoAV1MESAColorConfigFlags) { + .mono_chrome = seq->color_config.mono_chrome, + .color_range = seq->color_config.color_range, + .separate_uv_delta_q = seq->color_config.separate_uv_delta_q, + }, + .bit_depth = seq->color_config.twelve_bit ? 12 : + seq->color_config.high_bitdepth ? 10 : 8, + .subsampling_x = seq->color_config.subsampling_x, + .subsampling_y = seq->color_config.subsampling_y, + }, + }; + + av1_params_info = (VkVideoDecodeAV1SessionParametersAddInfoMESA) { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_SESSION_PARAMETERS_ADD_INFO_MESA, + .sequence_header = &av1_sequence_header, + }; + av1_params = (VkVideoDecodeAV1SessionParametersCreateInfoMESA) { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_SESSION_PARAMETERS_CREATE_INFO_MESA, + .pParametersAddInfo = &av1_params_info, + }; + session_params_create = (VkVideoSessionParametersCreateInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_SESSION_PARAMETERS_CREATE_INFO_KHR, + .pNext = &av1_params, + .videoSession = ctx->common.session, + .videoSessionParametersTemplate = NULL, + }; + + /* Create session parameters */ + ret = vk->CreateVideoSessionParametersKHR(ctx->s.hwctx->act_dev, &session_params_create, + ctx->s.hwctx->alloc, par); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Unable to create Vulkan video session parameters: %s!\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + tmp = av_buffer_create((uint8_t *)par, sizeof(*par), ff_vk_decode_free_params, + ctx, 0); + if (!tmp) { + ff_vk_decode_free_params(ctx, (uint8_t *)par); + return AVERROR(ENOMEM); + } + + av_log(avctx, AV_LOG_DEBUG, "Created frame parameters\n"); + + *buf = tmp; + + return 0; +} + +static int vk_av1_start_frame(AVCodecContext *avctx, + av_unused const uint8_t *buffer, + av_unused uint32_t size) +{ + int err; + int ref_count = 0; + AV1DecContext *s = avctx->priv_data; + const AV1Frame *pic = &s->cur_frame; + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + AV1VulkanDecodePicture *ap = pic->hwaccel_picture_private; + FFVulkanDecodePicture *vp = &ap->vp; + + const AV1RawFrameHeader *frame_header = s->raw_frame_header; + const AV1RawFilmGrainParams *film_grain = &s->cur_frame.film_grain; + const int apply_grain = !(avctx->export_side_data & AV_CODEC_EXPORT_DATA_FILM_GRAIN) && + film_grain->apply_grain; + + if (!dec->session_params) { + err = vk_av1_create_params(avctx, &dec->session_params); + if (err < 0) + return err; + } + + if (!ap->frame_id_set) { + unsigned slot_idx = 0; + for (unsigned i = 0; i < 32; i++) { + if (!(dec->frame_id_alloc_mask & (1 << i))) { + slot_idx = i; + break; + } + } + ap->frame_id = slot_idx; + ap->frame_id_set = 1; + dec->frame_id_alloc_mask |= (1 << slot_idx); + } + + /* Fill in references */ + for (int i = 0; i < AV1_NUM_REF_FRAMES; i++) { + const AV1Frame *ref_frame = &s->ref[i]; + if (s->ref[i].f->pict_type == AV_PICTURE_TYPE_NONE) + continue; + + err = vk_av1_fill_pict(avctx, &ap->ref_src[i], &vp->ref_slots[i], + &vp->refs[i], &ap->vkav1_refs[i], + ref_frame, 0, 0, i); + if (err < 0) + return err; + + ref_count++; + } + + err = vk_av1_fill_pict(avctx, NULL, &vp->ref_slot, &vp->ref, + &ap->vkav1_ref, + pic, 1, apply_grain, 8); + if (err < 0) + return err; + + ap->tile_list.nb_tiles = 0; + ap->tile_list.tile_list = ap->tiles; + + ap->av1_pic_info = (VkVideoDecodeAV1PictureInfoMESA) { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_PICTURE_INFO_MESA, + .frame_header = &ap->av1_frame_header, + .tile_list = &ap->tile_list, + }; + + vp->decode_info = (VkVideoDecodeInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_INFO_KHR, + .pNext = &ap->av1_pic_info, + .flags = 0x0, + .pSetupReferenceSlot = &vp->ref_slot, + .referenceSlotCount = ref_count, + .pReferenceSlots = vp->ref_slots, + .dstPictureResource = (VkVideoPictureResourceInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_INFO_KHR, + .codedOffset = (VkOffset2D){ 0, 0 }, + .codedExtent = (VkExtent2D){ pic->f->width, pic->f->height }, + .baseArrayLayer = 0, + .imageViewBinding = vp->img_view_out, + }, + }; + + /* Setup frame header */ + ap->av1_frame_header = (StdVideoAV1MESAFrameHeader) { + .flags = (StdVideoAV1MESAFrameHeaderFlags) { + .error_resilient_mode = frame_header->error_resilient_mode, + .disable_cdf_update = frame_header->disable_cdf_update, + .use_superres = frame_header->use_superres, + .render_and_frame_size_different = frame_header->render_and_frame_size_different, + .allow_screen_content_tools = frame_header->allow_screen_content_tools, + .is_filter_switchable = frame_header->is_filter_switchable, + .force_integer_mv = frame_header->force_integer_mv, + .frame_size_override_flag = frame_header->frame_size_override_flag, + .buffer_removal_time_present_flag = frame_header->buffer_removal_time_present_flag, + .allow_intrabc = frame_header->allow_intrabc, + .frame_refs_short_signaling = frame_header->frame_refs_short_signaling, + .allow_high_precision_mv = frame_header->allow_high_precision_mv, + .is_motion_mode_switchable = frame_header->is_motion_mode_switchable, + .use_ref_frame_mvs = frame_header->use_ref_frame_mvs, + .disable_frame_end_update_cdf = frame_header->disable_frame_end_update_cdf, + .allow_warped_motion = frame_header->allow_warped_motion, + .reduced_tx_set = frame_header->reduced_tx_set, + .reference_select = frame_header->reference_select, + .skip_mode_present = frame_header->skip_mode_present, + .delta_q_present = frame_header->delta_q_present, + }, + .frame_to_show_map_idx = frame_header->frame_to_show_map_idx, + .frame_presentation_time = frame_header->frame_presentation_time, + .display_frame_id = frame_header->display_frame_id, + .frame_type = frame_header->frame_type, + .current_frame_id = frame_header->current_frame_id, + .order_hint = frame_header->order_hint, + .primary_ref_frame = frame_header->primary_ref_frame, + .frame_width_minus_1 = frame_header->frame_width_minus_1, + .frame_height_minus_1 = frame_header->frame_height_minus_1, + .coded_denom = frame_header->coded_denom, + .render_width_minus_1 = frame_header->render_width_minus_1, + .render_height_minus_1 = frame_header->render_height_minus_1, + .refresh_frame_flags = frame_header->refresh_frame_flags, + .interpolation_filter = frame_header->interpolation_filter, + .tx_mode = frame_header->tx_mode, + .tiling = (StdVideoAV1MESATileInfo) { + .flags = (StdVideoAV1MESATileInfoFlags) { + .uniform_tile_spacing_flag = frame_header->uniform_tile_spacing_flag, + }, + .tile_cols = frame_header->tile_cols, + .tile_rows = frame_header->tile_rows, + .context_update_tile_id = frame_header->context_update_tile_id, + .tile_size_bytes_minus1 = frame_header->tile_size_bytes_minus1, + }, + .quantization = (StdVideoAV1MESAQuantization) { + .flags.using_qmatrix = frame_header->using_qmatrix, + .base_q_idx = frame_header->base_q_idx, + .delta_q_y_dc = frame_header->delta_q_y_dc, + .diff_uv_delta = frame_header->diff_uv_delta, + .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 = (StdVideoAV1MESADeltaQ) { + .flags = (StdVideoAV1MESADeltaQFlags) { + .delta_lf_present = frame_header->delta_lf_present, + .delta_lf_multi = frame_header->delta_lf_multi, + }, + .delta_q_res = frame_header->delta_q_res, + .delta_lf_res = frame_header->delta_lf_res, + }, + .loop_filter = (StdVideoAV1MESALoopFilter) { + .flags = (StdVideoAV1MESALoopFilterFlags) { + .delta_enabled = frame_header->loop_filter_delta_enabled, + .delta_update = frame_header->loop_filter_delta_update, + }, + .level = { + frame_header->loop_filter_level[0], frame_header->loop_filter_level[1], + frame_header->loop_filter_level[2], frame_header->loop_filter_level[3], + }, + .sharpness = frame_header->loop_filter_sharpness, + .mode_deltas = { + frame_header->loop_filter_mode_deltas[0], frame_header->loop_filter_mode_deltas[1], + }, + }, + .cdef = (StdVideoAV1MESACDEF) { + .damping_minus_3 = frame_header->cdef_damping_minus_3, + .bits = frame_header->cdef_bits, + }, + .lr = (StdVideoAV1MESALoopRestoration) { + .lr_unit_shift = frame_header->lr_unit_shift, + .lr_uv_shift = frame_header->lr_uv_shift, + .lr_type = { frame_header->lr_type[0], frame_header->lr_type[1], frame_header->lr_type[2] }, + }, + .segmentation = (StdVideoAV1MESASegmentation) { + .flags = (StdVideoAV1MESASegmentationFlags) { + .enabled = frame_header->segmentation_enabled, + .update_map = frame_header->segmentation_update_map, + .temporal_update = frame_header->segmentation_temporal_update, + .update_data = frame_header->segmentation_update_data, + }, + }, + .film_grain = (StdVideoAV1MESAFilmGrainParameters) { + .flags = (StdVideoAV1MESAFilmGrainFlags) { + .apply_grain = apply_grain, + .chroma_scaling_from_luma = film_grain->chroma_scaling_from_luma, + .overlap_flag = film_grain->overlap_flag, + .clip_to_restricted_range = film_grain->clip_to_restricted_range, + }, + .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, + .grain_seed = film_grain->grain_seed, + .num_y_points = film_grain->num_y_points, + .num_cb_points = film_grain->num_cb_points, + .num_cr_points = film_grain->num_cr_points, + .cb_mult = film_grain->cb_mult, + .cb_luma_mult = film_grain->cb_luma_mult, + .cb_offset = film_grain->cb_offset, + .cr_mult = film_grain->cr_mult, + .cr_luma_mult = film_grain->cr_luma_mult, + .cr_offset = film_grain->cr_offset, + }, + }; + + for (int i = 0; i < 64; i++) { + ap->av1_frame_header.tiling.width_in_sbs_minus_1[i] = frame_header->width_in_sbs_minus_1[i]; + ap->av1_frame_header.tiling.height_in_sbs_minus_1[i] = frame_header->height_in_sbs_minus_1[i]; + ap->av1_frame_header.tiling.tile_start_col_sb[i] = frame_header->tile_start_col_sb[i]; + ap->av1_frame_header.tiling.tile_start_row_sb[i] = frame_header->tile_start_row_sb[i]; + } + + for (int i = 0; i < 8; i++) { + ap->av1_frame_header.segmentation.feature_enabled_bits[i] = 0; + for (int j = 0; j < 8; j++) { + ap->av1_frame_header.segmentation.feature_enabled_bits[i] |= (frame_header->feature_enabled[i][j] << j); + ap->av1_frame_header.segmentation.feature_data[i][j] = frame_header->feature_value[i][j]; + } + + ap->av1_frame_header.loop_filter.ref_deltas[i] = frame_header->loop_filter_ref_deltas[i]; + + ap->av1_frame_header.cdef.y_pri_strength[i] = frame_header->cdef_y_pri_strength[i]; + ap->av1_frame_header.cdef.y_sec_strength[i] = frame_header->cdef_y_sec_strength[i]; + ap->av1_frame_header.cdef.uv_pri_strength[i] = frame_header->cdef_uv_pri_strength[i]; + ap->av1_frame_header.cdef.uv_sec_strength[i] = frame_header->cdef_uv_sec_strength[i]; + + ap->av1_frame_header.ref_order_hint[i] = frame_header->ref_order_hint[i]; + ap->av1_frame_header.global_motion[i] = (StdVideoAV1MESAGlobalMotion) { + .flags = (StdVideoAV1MESAGlobalMotionFlags) { + .gm_invalid = s->cur_frame.gm_invalid[i], + }, + .gm_type = s->cur_frame.gm_type[i], + .gm_params = { + s->cur_frame.gm_params[i][0], s->cur_frame.gm_params[i][1], + s->cur_frame.gm_params[i][2], s->cur_frame.gm_params[i][3], + s->cur_frame.gm_params[i][4], s->cur_frame.gm_params[i][5], + }, + }; + } + + for (int i = 0; i < 7; i++) { + ap->av1_frame_header.ref_frame_idx[i] = frame_header->ref_frame_idx[i]; + ap->av1_frame_header.delta_frame_id_minus1[i] = frame_header->delta_frame_id_minus1[i]; + } + + ap->av1_pic_info.skip_mode_frame_idx[0] = s->cur_frame.skip_mode_frame_idx[0]; + ap->av1_pic_info.skip_mode_frame_idx[1] = s->cur_frame.skip_mode_frame_idx[1]; + + if (apply_grain) { + for (int i = 0; i < 14; i++) { + ap->av1_frame_header.film_grain.point_y_value[i] = film_grain->point_y_value[i]; + ap->av1_frame_header.film_grain.point_y_scaling[i] = film_grain->point_y_scaling[i]; + } + + for (int i = 0; i < 10; i++) { + ap->av1_frame_header.film_grain.point_cb_value[i] = film_grain->point_cb_value[i]; + ap->av1_frame_header.film_grain.point_cb_scaling[i] = film_grain->point_cb_scaling[i]; + ap->av1_frame_header.film_grain.point_cr_value[i] = film_grain->point_cr_value[i]; + ap->av1_frame_header.film_grain.point_cr_scaling[i] = film_grain->point_cr_scaling[i]; + } + + for (int i = 0; i < 24; i++) { + ap->av1_frame_header.film_grain.ar_coeffs_y_plus_128[i] = film_grain->ar_coeffs_y_plus_128[i]; + ap->av1_frame_header.film_grain.ar_coeffs_cb_plus_128[i] = film_grain->ar_coeffs_cb_plus_128[i]; + ap->av1_frame_header.film_grain.ar_coeffs_cr_plus_128[i] = film_grain->ar_coeffs_cr_plus_128[i]; + } + + ap->av1_frame_header.film_grain.ar_coeffs_cb_plus_128[24] = film_grain->ar_coeffs_cb_plus_128[24]; + ap->av1_frame_header.film_grain.ar_coeffs_cr_plus_128[24] = film_grain->ar_coeffs_cr_plus_128[24]; + } + + /* Workaround for a spec issue. */ + ap->dec = dec; + + return 0; +} + +static int vk_av1_decode_slice(AVCodecContext *avctx, + const uint8_t *data, + uint32_t size) +{ + int err; + const AV1DecContext *s = avctx->priv_data; + AV1VulkanDecodePicture *ap = s->cur_frame.hwaccel_picture_private; + FFVulkanDecodePicture *vp = &ap->vp; + + for (int i = s->tg_start; i <= s->tg_end; i++) { + ap->tiles[ap->tile_list.nb_tiles] = (StdVideoAV1MESATile) { + .size = s->tile_group_info[i].tile_size, + .offset = s->tile_group_info[i].tile_offset, + .row = s->tile_group_info[i].tile_row, + .column = s->tile_group_info[i].tile_column, + .tg_start = s->tg_start, + .tg_end = s->tg_end, + }; + + err = ff_vk_decode_add_slice(avctx, vp, + data + s->tile_group_info[i].tile_offset, + s->tile_group_info[i].tile_size, 0, + &ap->tile_list.nb_tiles, + &ap->tile_offsets); + if (err < 0) + return err; + + ap->tiles[ap->tile_list.nb_tiles - 1].offset = ap->tile_offsets[ap->tile_list.nb_tiles - 1]; + } + + return 0; +} + +static int vk_av1_end_frame(AVCodecContext *avctx) +{ + const AV1DecContext *s = avctx->priv_data; + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + const AV1Frame *pic = &s->cur_frame; + AV1VulkanDecodePicture *ap = pic->hwaccel_picture_private; + FFVulkanDecodePicture *vp = &ap->vp; + FFVulkanDecodePicture *rvp[AV1_NUM_REF_FRAMES] = { 0 }; + AVFrame *rav[AV1_NUM_REF_FRAMES] = { 0 }; + + if (!ap->tile_list.nb_tiles) + return 0; + + if (!dec->session_params) { + int err = vk_av1_create_params(avctx, &dec->session_params); + if (err < 0) + return err; + } + + for (int i = 0; i < vp->decode_info.referenceSlotCount; i++) { + const AV1Frame *rp = ap->ref_src[i]; + AV1VulkanDecodePicture *rhp = rp->hwaccel_picture_private; + + rvp[i] = &rhp->vp; + rav[i] = ap->ref_src[i]->f; + } + + av_log(avctx, AV_LOG_VERBOSE, "Decoding frame, %"SIZE_SPECIFIER" bytes, %i tiles\n", + vp->slices_size, ap->tile_list.nb_tiles); + + return ff_vk_decode_frame(avctx, pic->f, vp, rav, rvp); +} + +static void vk_av1_free_frame_priv(void *_hwctx, uint8_t *data) +{ + AVHWDeviceContext *hwctx = _hwctx; + AV1VulkanDecodePicture *ap = (AV1VulkanDecodePicture *)data; + + /* Workaround for a spec issue. */ + if (ap->frame_id_set) + ap->dec->frame_id_alloc_mask &= ~(1 << ap->frame_id); + + /* Free frame resources, this also destroys the session parameters. */ + ff_vk_decode_free_frame(hwctx, &ap->vp); + + /* Free frame context */ + av_free(ap); +} + +const FFHWAccel ff_av1_vulkan_hwaccel = { + .p.name = "av1_vulkan", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_AV1, + .p.pix_fmt = AV_PIX_FMT_VULKAN, + .start_frame = &vk_av1_start_frame, + .decode_slice = &vk_av1_decode_slice, + .end_frame = &vk_av1_end_frame, + .free_frame_priv = &vk_av1_free_frame_priv, + .frame_priv_data_size = sizeof(AV1VulkanDecodePicture), + .init = &ff_vk_decode_init, + .update_thread_context = &ff_vk_update_thread_context, + .decode_params = &ff_vk_params_invalidate, + .flush = &ff_vk_decode_flush, + .uninit = &ff_vk_decode_uninit, + .frame_params = &ff_vk_frame_params, + .priv_data_size = sizeof(FFVulkanDecodeContext), + + /* NOTE: Threading is intentionally disabled here. Due to the design of Vulkan, + * where frames are opaque to users, and mostly opaque for driver developers, + * there's an issue with current hardware accelerator implementations of AV1, + * where they require an internal index. With regular hwaccel APIs, this index + * is given to users as an opaque handle directly. With Vulkan, due to increased + * flexibility, this index cannot be present anywhere. + * The current implementation tracks the index for the driver and submits it + * as necessary information. Due to needing to modify the decoding context, + * which is not thread-safe, on frame free, threading is disabled. + * In the future, once this is fixed in the spec, the workarounds may be removed + * and threading enabled. */ + .caps_internal = HWACCEL_CAP_ASYNC_SAFE, +}; diff --git a/libavcodec/vulkan_decode.c b/libavcodec/vulkan_decode.c new file mode 100644 index 00000000000..f20733fb391 --- /dev/null +++ b/libavcodec/vulkan_decode.c @@ -0,0 +1,1260 @@ +/* + * 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 "vulkan_video.h" +#include "vulkan_decode.h" +#include "config_components.h" + +#if CONFIG_H264_VULKAN_HWACCEL +extern const VkExtensionProperties ff_vk_dec_h264_ext; +#endif +#if CONFIG_HEVC_VULKAN_HWACCEL +extern const VkExtensionProperties ff_vk_dec_hevc_ext; +#endif +#if CONFIG_AV1_VULKAN_HWACCEL +extern const VkExtensionProperties ff_vk_dec_av1_ext; +#endif + +static const VkExtensionProperties *dec_ext[] = { +#if CONFIG_H264_VULKAN_HWACCEL + [AV_CODEC_ID_H264] = &ff_vk_dec_h264_ext, +#endif +#if CONFIG_HEVC_VULKAN_HWACCEL + [AV_CODEC_ID_HEVC] = &ff_vk_dec_hevc_ext, +#endif +#if CONFIG_AV1_VULKAN_HWACCEL + [AV_CODEC_ID_AV1] = &ff_vk_dec_av1_ext, +#endif +}; + +static const VkVideoProfileInfoKHR *get_video_profile(FFVulkanDecodeShared *ctx, enum AVCodecID codec_id) +{ + const VkVideoProfileListInfoKHR *profile_list; + + VkStructureType profile_struct_type = + codec_id == AV_CODEC_ID_H264 ? VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_PROFILE_INFO_KHR : + codec_id == AV_CODEC_ID_HEVC ? VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_PROFILE_INFO_KHR : + codec_id == AV_CODEC_ID_AV1 ? VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_PROFILE_INFO_MESA : + 0; + + profile_list = ff_vk_find_struct(ctx->s.hwfc->create_pnext, + VK_STRUCTURE_TYPE_VIDEO_PROFILE_LIST_INFO_KHR); + if (!profile_list) + return NULL; + + for (int i = 0; i < profile_list->profileCount; i++) + if (ff_vk_find_struct(profile_list->pProfiles[i].pNext, profile_struct_type)) + return &profile_list->pProfiles[i]; + + return NULL; +} + +int ff_vk_update_thread_context(AVCodecContext *dst, const AVCodecContext *src) +{ + int err; + FFVulkanDecodeContext *src_ctx = src->internal->hwaccel_priv_data; + FFVulkanDecodeContext *dst_ctx = dst->internal->hwaccel_priv_data; + + if (!dst_ctx->exec_pool.cmd_bufs) { + FFVulkanDecodeShared *ctx = (FFVulkanDecodeShared *)src_ctx->shared_ref->data; + + const VkVideoProfileInfoKHR *profile = get_video_profile(ctx, dst->codec_id); + if (!profile) { + av_log(dst, AV_LOG_ERROR, "Video profile missing from frames context!"); + return AVERROR(EINVAL); + } + + err = ff_vk_exec_pool_init(&ctx->s, &ctx->qf, + &dst_ctx->exec_pool, + src_ctx->exec_pool.pool_size, + src_ctx->exec_pool.nb_queries, + VK_QUERY_TYPE_RESULT_STATUS_ONLY_KHR, 0, + profile); + if (err < 0) + return err; + } + + err = av_buffer_replace(&dst_ctx->shared_ref, src_ctx->shared_ref); + if (err < 0) + return err; + + if (src_ctx->session_params) { + err = av_buffer_replace(&dst_ctx->session_params, src_ctx->session_params); + if (err < 0) + return err; + } + + dst_ctx->dedicated_dpb = src_ctx->dedicated_dpb; + dst_ctx->layered_dpb = src_ctx->layered_dpb; + dst_ctx->external_fg = src_ctx->external_fg; + dst_ctx->frame_id_alloc_mask = src_ctx->frame_id_alloc_mask; + + return 0; +} + +int ff_vk_params_invalidate(AVCodecContext *avctx, int t, const uint8_t *b, uint32_t s) +{ + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + av_buffer_unref(&dec->session_params); + return 0; +} + +static int vk_decode_create_view(FFVulkanDecodeShared *ctx, VkImageView *dst_view, + VkImageAspectFlags *aspect, AVVkFrame *src, + VkFormat vkf) +{ + VkResult ret; + FFVulkanFunctions *vk = &ctx->s.vkfn; + VkImageAspectFlags aspect_mask = ff_vk_aspect_bits_from_vkfmt(vkf); + + VkSamplerYcbcrConversionInfo yuv_sampler_info = { + .sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO, + .conversion = ctx->yuv_sampler, + }; + VkImageViewCreateInfo img_view_create_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .pNext = &yuv_sampler_info, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = vkf, + .image = src->img[0], + .components = (VkComponentMapping) { + .r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY, + }, + .subresourceRange = (VkImageSubresourceRange) { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + .levelCount = 1, + }, + }; + + ret = vk->CreateImageView(ctx->s.hwctx->act_dev, &img_view_create_info, + ctx->s.hwctx->alloc, dst_view); + if (ret != VK_SUCCESS) + return AVERROR_EXTERNAL; + + *aspect = aspect_mask; + + return 0; +} + +static AVFrame *vk_get_dpb_pool(FFVulkanDecodeShared *ctx) +{ + int err; + AVFrame *avf = av_frame_alloc(); + if (!avf) + return NULL; + + err = av_hwframe_get_buffer(ctx->dpb_hwfc_ref, avf, 0x0); + if (err < 0) + av_frame_free(&avf); + + return avf; +} + +int ff_vk_decode_prepare_frame(FFVulkanDecodeContext *dec, AVFrame *pic, + FFVulkanDecodePicture *vkpic, int is_current, + int alloc_dpb) +{ + int err; + FFVulkanDecodeShared *ctx = (FFVulkanDecodeShared *)dec->shared_ref->data; + + vkpic->slices_size = 0; + + /* If the decoder made a blank frame to make up for a missing ref, or the + * frame is the current frame so it's missing one, create a re-representation */ + if (vkpic->img_view_ref) + return 0; + + vkpic->dpb_frame = NULL; + vkpic->img_view_ref = NULL; + vkpic->img_view_out = NULL; + vkpic->img_view_dest = NULL; + + if (dec->layered_dpb && alloc_dpb) { + vkpic->img_view_ref = ctx->layered_view; + vkpic->img_aspect_ref = ctx->layered_aspect; + } else if (alloc_dpb) { + AVHWFramesContext *dpb_frames = (AVHWFramesContext *)ctx->dpb_hwfc_ref->data; + AVVulkanFramesContext *dpb_hwfc = dpb_frames->hwctx; + + vkpic->dpb_frame = vk_get_dpb_pool(ctx); + if (!vkpic->dpb_frame) + return AVERROR(ENOMEM); + + err = vk_decode_create_view(ctx, &vkpic->img_view_ref, + &vkpic->img_aspect_ref, + (AVVkFrame *)vkpic->dpb_frame->data[0], + dpb_hwfc->format[0]); + if (err < 0) + return err; + + vkpic->img_view_dest = vkpic->img_view_ref; + } + + if (!alloc_dpb || is_current) { + AVHWFramesContext *frames = (AVHWFramesContext *)pic->hw_frames_ctx->data; + AVVulkanFramesContext *hwfc = frames->hwctx; + + err = vk_decode_create_view(ctx, &vkpic->img_view_out, + &vkpic->img_aspect, + (AVVkFrame *)pic->data[0], + hwfc->format[0]); + if (err < 0) + return err; + + if (!alloc_dpb) { + vkpic->img_view_ref = vkpic->img_view_out; + vkpic->img_aspect_ref = vkpic->img_aspect; + } + } + + return 0; +} + +int ff_vk_decode_add_slice(AVCodecContext *avctx, FFVulkanDecodePicture *vp, + const uint8_t *data, size_t size, int add_startcode, + uint32_t *nb_slices, const uint32_t **offsets) +{ + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + FFVulkanDecodeShared *ctx = (FFVulkanDecodeShared *)dec->shared_ref->data; + + static const uint8_t startcode_prefix[3] = { 0x0, 0x0, 0x1 }; + const size_t startcode_len = add_startcode ? sizeof(startcode_prefix) : 0; + const int nb = *nb_slices; + uint8_t *slices; + uint32_t *slice_off; + FFVkVideoBuffer *vkbuf; + + size_t new_size = vp->slices_size + startcode_len + size + + ctx->caps.minBitstreamBufferSizeAlignment; + new_size = FFALIGN(new_size, ctx->caps.minBitstreamBufferSizeAlignment); + + slice_off = av_fast_realloc(dec->slice_off, &dec->slice_off_max, + (nb + 1)*sizeof(slice_off)); + if (!slice_off) + return AVERROR(ENOMEM); + + *offsets = dec->slice_off = slice_off; + slice_off[nb] = vp->slices_size; + + vkbuf = vp->slices_buf ? (FFVkVideoBuffer *)vp->slices_buf->data : NULL; + if (!vkbuf || vkbuf->buf.size < new_size) { + int err; + AVBufferRef *new_ref; + FFVkVideoBuffer *new_buf; + err = ff_vk_video_get_buffer(&ctx->s, &ctx->common, &new_ref, + VK_BUFFER_USAGE_VIDEO_DECODE_SRC_BIT_KHR, + ctx->s.hwfc->create_pnext, new_size); + if (err < 0) + return err; + + new_buf = (FFVkVideoBuffer *)new_ref->data; + + /* Copy data from the old buffer */ + if (vkbuf) { + memcpy(new_buf->mem, vkbuf->mem, vp->slices_size); + av_buffer_unref(&vp->slices_buf); + } + + vp->slices_buf = new_ref; + vkbuf = new_buf; + } + slices = vkbuf->mem; + + /* Startcode */ + memcpy(slices + vp->slices_size, startcode_prefix, startcode_len); + + /* Slice data */ + memcpy(slices + vp->slices_size + startcode_len, data, size); + + *nb_slices = nb + 1; + vp->slices_size += startcode_len + size; + + return 0; +} + +void ff_vk_decode_flush(AVCodecContext *avctx) +{ + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + FFVulkanDecodeShared *ctx = (FFVulkanDecodeShared *)dec->shared_ref->data; + + FFVulkanFunctions *vk = &ctx->s.vkfn; + VkVideoBeginCodingInfoKHR decode_start = { + .sType = VK_STRUCTURE_TYPE_VIDEO_BEGIN_CODING_INFO_KHR, + .videoSession = ctx->common.session, + .videoSessionParameters = ctx->empty_session_params, + }; + VkVideoCodingControlInfoKHR decode_ctrl = { + .sType = VK_STRUCTURE_TYPE_VIDEO_CODING_CONTROL_INFO_KHR, + .flags = VK_VIDEO_CODING_CONTROL_RESET_BIT_KHR, + }; + VkVideoEndCodingInfoKHR decode_end = { + .sType = VK_STRUCTURE_TYPE_VIDEO_END_CODING_INFO_KHR, + }; + + VkCommandBuffer cmd_buf; + FFVkExecContext *exec = ff_vk_exec_get(&dec->exec_pool); + ff_vk_exec_start(&ctx->s, exec); + cmd_buf = exec->buf; + + vk->CmdBeginVideoCodingKHR(cmd_buf, &decode_start); + vk->CmdControlVideoCodingKHR(cmd_buf, &decode_ctrl); + vk->CmdEndVideoCodingKHR(cmd_buf, &decode_end); + ff_vk_exec_submit(&ctx->s, exec); +} + +int ff_vk_decode_frame(AVCodecContext *avctx, + AVFrame *pic, FFVulkanDecodePicture *vp, + AVFrame *rpic[], FFVulkanDecodePicture *rvkp[]) +{ + int err; + VkResult ret; + VkCommandBuffer cmd_buf; + FFVkVideoBuffer *sd_buf; + + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + FFVulkanDecodeShared *ctx = (FFVulkanDecodeShared *)dec->shared_ref->data; + FFVulkanFunctions *vk = &ctx->s.vkfn; + + /* Output */ + AVVkFrame *vkf = (AVVkFrame *)pic->buf[0]->data; + + /* Quirks */ + const int layered_dpb = dec->layered_dpb; + + VkVideoSessionParametersKHR *par = (VkVideoSessionParametersKHR *)dec->session_params->data; + VkVideoBeginCodingInfoKHR decode_start = { + .sType = VK_STRUCTURE_TYPE_VIDEO_BEGIN_CODING_INFO_KHR, + .videoSession = ctx->common.session, + .videoSessionParameters = *par, + .referenceSlotCount = vp->decode_info.referenceSlotCount, + .pReferenceSlots = vp->decode_info.pReferenceSlots, + }; + VkVideoEndCodingInfoKHR decode_end = { + .sType = VK_STRUCTURE_TYPE_VIDEO_END_CODING_INFO_KHR, + }; + + VkImageMemoryBarrier2 img_bar[37]; + int nb_img_bar = 0; + size_t data_size = FFALIGN(vp->slices_size, + ctx->caps.minBitstreamBufferSizeAlignment); + + FFVkExecContext *exec = ff_vk_exec_get(&dec->exec_pool); + + /* The current decoding reference has to be bound as an inactive reference */ + VkVideoReferenceSlotInfoKHR *cur_vk_ref; + cur_vk_ref = (void *)&decode_start.pReferenceSlots[decode_start.referenceSlotCount]; + cur_vk_ref[0] = vp->ref_slot; + cur_vk_ref[0].slotIndex = -1; + decode_start.referenceSlotCount++; + + if (dec->exec_pool.nb_queries) { + int64_t prev_sub_res = 0; + ff_vk_exec_wait(&ctx->s, exec); + ret = ff_vk_exec_get_query(&ctx->s, exec, NULL, &prev_sub_res); + if (ret != VK_NOT_READY && ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Unable to perform query: %s!\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + if (ret == VK_SUCCESS) + av_log(avctx, prev_sub_res < 0 ? AV_LOG_ERROR : AV_LOG_DEBUG, + "Result of previous frame decoding: %"PRId64"\n", prev_sub_res); + } + + sd_buf = (FFVkVideoBuffer *)vp->slices_buf->data; + + /* Flush if needed */ + if (!(sd_buf->buf.flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) { + VkMappedMemoryRange flush_buf = { + .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, + .memory = sd_buf->buf.mem, + .offset = 0, + .size = FFALIGN(vp->slices_size, + ctx->s.props.properties.limits.nonCoherentAtomSize), + }; + + ret = vk->FlushMappedMemoryRanges(ctx->s.hwctx->act_dev, 1, &flush_buf); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to flush memory: %s\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + } + + vp->decode_info.srcBuffer = sd_buf->buf.buf; + vp->decode_info.srcBufferOffset = 0; + vp->decode_info.srcBufferRange = data_size; + + /* Start command buffer recording */ + err = ff_vk_exec_start(&ctx->s, exec); + if (err < 0) + return err; + cmd_buf = exec->buf; + + /* Slices */ + err = ff_vk_exec_add_dep_buf(&ctx->s, exec, &vp->slices_buf, 1, 0); + if (err < 0) + return err; + vp->slices_buf = NULL; /* Owned by the exec buffer from now on */ + + /* Parameters */ + err = ff_vk_exec_add_dep_buf(&ctx->s, exec, &dec->session_params, 1, 1); + if (err < 0) + return err; + + err = ff_vk_exec_add_dep_frame(&ctx->s, exec, pic, + VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR, + VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR); + if (err < 0) + return err; + + err = ff_vk_exec_mirror_sem_value(&ctx->s, exec, &vp->sem, &vp->sem_value, + pic); + if (err < 0) + return err; + + /* Output image - change layout, as it comes from a pool */ + img_bar[nb_img_bar] = (VkImageMemoryBarrier2) { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .pNext = NULL, + .srcStageMask = VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR, + .dstStageMask = VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR, + .srcAccessMask = VK_ACCESS_2_NONE, + .dstAccessMask = VK_ACCESS_2_VIDEO_DECODE_WRITE_BIT_KHR, + .oldLayout = vkf->layout[0], + .newLayout = vp->dpb_frame ? VK_IMAGE_LAYOUT_VIDEO_DECODE_DST_KHR : + VK_IMAGE_LAYOUT_VIDEO_DECODE_DPB_KHR, /* Spec, 07252 utter madness */ + .srcQueueFamilyIndex = vkf->queue_family[0], + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = vkf->img[0], + .subresourceRange = (VkImageSubresourceRange) { + .aspectMask = vp->img_aspect, + .layerCount = 1, + .levelCount = 1, + }, + }; + ff_vk_exec_update_frame(&ctx->s, exec, pic, + &img_bar[nb_img_bar], &nb_img_bar); + + /* Reference for the current image, if existing and not layered */ + if (vp->dpb_frame) { + err = ff_vk_exec_add_dep_frame(&ctx->s, exec, vp->dpb_frame, + VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR, + VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR); + if (err < 0) + return err; + } + + if (!layered_dpb) { + /* All references (apart from the current) for non-layered refs */ + + for (int i = 0; i < vp->decode_info.referenceSlotCount; i++) { + AVFrame *ref_frame = rpic[i]; + FFVulkanDecodePicture *rvp = rvkp[i]; + AVFrame *ref = rvp->dpb_frame ? rvp->dpb_frame : ref_frame; + + err = ff_vk_exec_add_dep_frame(&ctx->s, exec, ref, + VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR, + VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR); + if (err < 0) + return err; + + if (err == 0) { + err = ff_vk_exec_mirror_sem_value(&ctx->s, exec, + &rvp->sem, &rvp->sem_value, + ref); + if (err < 0) + return err; + } + + if (!rvp->dpb_frame) { + AVVkFrame *rvkf = (AVVkFrame *)ref->data[0]; + + img_bar[nb_img_bar] = (VkImageMemoryBarrier2) { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .pNext = NULL, + .srcStageMask = VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR, + .dstStageMask = VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR, + .srcAccessMask = VK_ACCESS_2_NONE, + .dstAccessMask = VK_ACCESS_2_VIDEO_DECODE_READ_BIT_KHR | + VK_ACCESS_2_VIDEO_DECODE_WRITE_BIT_KHR, + .oldLayout = rvkf->layout[0], + .newLayout = VK_IMAGE_LAYOUT_VIDEO_DECODE_DPB_KHR, + .srcQueueFamilyIndex = rvkf->queue_family[0], + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = rvkf->img[0], + .subresourceRange = (VkImageSubresourceRange) { + .aspectMask = rvp->img_aspect_ref, + .layerCount = 1, + .levelCount = 1, + }, + }; + ff_vk_exec_update_frame(&ctx->s, exec, ref, + &img_bar[nb_img_bar], &nb_img_bar); + } + } + } else if (vp->decode_info.referenceSlotCount || + vp->img_view_out != vp->img_view_ref) { + /* Single barrier for a single layered ref */ + err = ff_vk_exec_add_dep_frame(&ctx->s, exec, ctx->layered_frame, + VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR, + VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR); + if (err < 0) + return err; + } + + /* Change image layout */ + vk->CmdPipelineBarrier2(cmd_buf, &(VkDependencyInfo) { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT, + .pImageMemoryBarriers = img_bar, + .imageMemoryBarrierCount = nb_img_bar, + }); + + /* Start, use parameters, decode and end decoding */ + vk->CmdBeginVideoCodingKHR(cmd_buf, &decode_start); + + /* Start status query */ + if (dec->exec_pool.nb_queries) + vk->CmdBeginQuery(cmd_buf, dec->exec_pool.query_pool, exec->query_idx + 0, 0); + + vk->CmdDecodeVideoKHR(cmd_buf, &vp->decode_info); + + /* End status query */ + if (dec->exec_pool.nb_queries) + vk->CmdEndQuery(cmd_buf, dec->exec_pool.query_pool, exec->query_idx + 0); + + vk->CmdEndVideoCodingKHR(cmd_buf, &decode_end); + + /* End recording and submit for execution */ + return ff_vk_exec_submit(&ctx->s, exec); +} + +void ff_vk_decode_free_frame(AVHWDeviceContext *dev_ctx, FFVulkanDecodePicture *vp) +{ + AVVulkanDeviceContext *hwctx = dev_ctx->hwctx; + PFN_vkGetDeviceProcAddr device_proc_addr; + PFN_vkWaitSemaphores wait_semaphores; + PFN_vkDestroyImageView destroy_image_view; + + VkSemaphoreWaitInfo sem_wait = (VkSemaphoreWaitInfo) { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO, + .pSemaphores = &vp->sem, + .pValues = &vp->sem_value, + .semaphoreCount = 1, + }; + + /* Guaranteed to exist */ + device_proc_addr = (PFN_vkGetDeviceProcAddr)hwctx->get_proc_addr(hwctx->inst, "vkGetDeviceProcAddr"); + destroy_image_view = (PFN_vkDestroyImageView)device_proc_addr(hwctx->act_dev, "vkDestroyImageView"); + wait_semaphores = (PFN_vkWaitSemaphores)device_proc_addr(hwctx->act_dev, "vkWaitSemaphores"); + + /* We do not have to lock the frame here because we're not interested + * in the actual current semaphore value, but only that it's later than + * the time we submitted the image for decoding. */ + if (vp->sem) + wait_semaphores(hwctx->act_dev, &sem_wait, UINT64_MAX); + + /* Free slices data */ + av_buffer_unref(&vp->slices_buf); + + /* Destroy image view (out) */ + if (vp->img_view_out && vp->img_view_out != vp->img_view_dest) + destroy_image_view(hwctx->act_dev, vp->img_view_out, hwctx->alloc); + + /* Destroy image view (ref, unlayered) */ + if (vp->img_view_dest) + destroy_image_view(hwctx->act_dev, vp->img_view_dest, hwctx->alloc); + + av_frame_free(&vp->dpb_frame); +} + +static void free_common(void *opaque, uint8_t *data) +{ + FFVulkanDecodeShared *ctx = (FFVulkanDecodeShared *)data; + FFVulkanContext *s = &ctx->s; + FFVulkanFunctions *vk = &ctx->s.vkfn; + + /* Destroy layered view */ + if (ctx->layered_view) + vk->DestroyImageView(s->hwctx->act_dev, ctx->layered_view, s->hwctx->alloc); + + /* This also frees all references from this pool */ + av_frame_free(&ctx->layered_frame); + av_buffer_unref(&ctx->dpb_hwfc_ref); + + /* Destroy parameters */ + if (ctx->empty_session_params) + vk->DestroyVideoSessionParametersKHR(s->hwctx->act_dev, + ctx->empty_session_params, + s->hwctx->alloc); + + ff_vk_video_common_uninit(s, &ctx->common); + + if (ctx->yuv_sampler) + vk->DestroySamplerYcbcrConversion(s->hwctx->act_dev, ctx->yuv_sampler, + s->hwctx->alloc); + + ff_vk_uninit(s); + + av_free(ctx); +} + +static int vulkan_decode_bootstrap(AVCodecContext *avctx, AVBufferRef *frames_ref) +{ + int err; + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + AVHWFramesContext *frames = (AVHWFramesContext *)frames_ref->data; + AVHWDeviceContext *device = (AVHWDeviceContext *)frames->device_ref->data; + AVVulkanDeviceContext *hwctx = device->hwctx; + FFVulkanDecodeShared *ctx; + + if (dec->shared_ref) + return 0; + + ctx = av_mallocz(sizeof(*ctx)); + if (!ctx) + return AVERROR(ENOMEM); + + dec->shared_ref = av_buffer_create((uint8_t *)ctx, sizeof(*ctx), + free_common, NULL, 0); + if (!dec->shared_ref) { + av_free(ctx); + return AVERROR(ENOMEM); + } + + ctx = (FFVulkanDecodeShared *)dec->shared_ref->data; + + ctx->s.extensions = ff_vk_extensions_to_mask(hwctx->enabled_dev_extensions, + hwctx->nb_enabled_dev_extensions); + + if (!(ctx->s.extensions & FF_VK_EXT_VIDEO_DECODE_QUEUE)) { + av_log(avctx, AV_LOG_ERROR, "Device does not support the %s extension!\n", + VK_KHR_VIDEO_DECODE_QUEUE_EXTENSION_NAME); + av_buffer_unref(&dec->shared_ref); + return AVERROR(ENOSYS); + } + + err = ff_vk_load_functions(device, &ctx->s.vkfn, ctx->s.extensions, 1, 1); + if (err < 0) { + av_buffer_unref(&dec->shared_ref); + return err; + } + + return 0; +} + +static VkResult vulkan_setup_profile(AVCodecContext *avctx, + FFVulkanDecodeProfileData *prof, + AVVulkanDeviceContext *hwctx, + FFVulkanFunctions *vk, + const struct FFVkCodecMap *vk_codec, + VkVideoDecodeH264CapabilitiesKHR *h264_caps, + VkVideoDecodeH265CapabilitiesKHR *h265_caps, + VkVideoDecodeAV1CapabilitiesMESA *av1_caps, + VkVideoCapabilitiesKHR *caps, + VkVideoDecodeCapabilitiesKHR *dec_caps, + int cur_profile) +{ + VkVideoDecodeUsageInfoKHR *usage = &prof->usage; + VkVideoProfileInfoKHR *profile = &prof->profile; + VkVideoProfileListInfoKHR *profile_list = &prof->profile_list; + + VkVideoDecodeH264ProfileInfoKHR *h264_profile = &prof->h264_profile; + VkVideoDecodeH264ProfileInfoKHR *h265_profile = &prof->h265_profile; + VkVideoDecodeAV1ProfileInfoMESA *av1_profile = &prof->av1_profile; + + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(avctx->sw_pix_fmt); + if (!desc) + return AVERROR(EINVAL); + + if (avctx->codec_id == AV_CODEC_ID_H264) { + dec_caps->pNext = h264_caps; + usage->pNext = h264_profile; + h264_profile->sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_PROFILE_INFO_KHR; + + /* Vulkan transmits all the constrant_set flags, rather than wanting them + * merged in the profile IDC */ + h264_profile->stdProfileIdc = cur_profile & ~(FF_PROFILE_H264_CONSTRAINED | + FF_PROFILE_H264_INTRA); + + h264_profile->pictureLayout = avctx->field_order == AV_FIELD_UNKNOWN || + avctx->field_order == AV_FIELD_PROGRESSIVE ? + VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_PROGRESSIVE_KHR : + VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_INTERLACED_INTERLEAVED_LINES_BIT_KHR; + } else if (avctx->codec_id == AV_CODEC_ID_H265) { + dec_caps->pNext = h265_caps; + usage->pNext = h265_profile; + h265_profile->sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_PROFILE_INFO_KHR; + h265_profile->stdProfileIdc = cur_profile; + } else if (avctx->codec_id == AV_CODEC_ID_AV1) { + dec_caps->pNext = av1_caps; + usage->pNext = av1_profile; + av1_profile->sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_PROFILE_INFO_MESA; + av1_profile->stdProfileIdc = cur_profile; + } + + usage->sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_USAGE_INFO_KHR; + usage->videoUsageHints = VK_VIDEO_DECODE_USAGE_DEFAULT_KHR; + + profile->sType = VK_STRUCTURE_TYPE_VIDEO_PROFILE_INFO_KHR; + profile->pNext = usage; + profile->videoCodecOperation = vk_codec->decode_op; + profile->chromaSubsampling = ff_vk_subsampling_from_av_desc(desc); + profile->lumaBitDepth = ff_vk_depth_from_av_depth(desc->comp[0].depth); + profile->chromaBitDepth = profile->lumaBitDepth; + + profile_list->sType = VK_STRUCTURE_TYPE_VIDEO_PROFILE_LIST_INFO_KHR; + profile_list->profileCount = 1; + profile_list->pProfiles = profile; + + /* Get the capabilities of the decoder for the given profile */ + caps->sType = VK_STRUCTURE_TYPE_VIDEO_CAPABILITIES_KHR; + caps->pNext = dec_caps; + dec_caps->sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_CAPABILITIES_KHR; + /* dec_caps->pNext already filled in */ + + return vk->GetPhysicalDeviceVideoCapabilitiesKHR(hwctx->phys_dev, profile, + caps); +} + +static int vulkan_decode_get_profile(AVCodecContext *avctx, AVBufferRef *frames_ref, + enum AVPixelFormat *pix_fmt, VkFormat *vk_fmt, + FFVulkanDecodeProfileData *prof, + int *dpb_dedicate) +{ + VkResult ret; + int max_level, base_profile, cur_profile; + const struct FFVkCodecMap *vk_codec = &ff_vk_codec_map[avctx->codec_id]; + AVHWFramesContext *frames = (AVHWFramesContext *)frames_ref->data; + AVHWDeviceContext *device = (AVHWDeviceContext *)frames->device_ref->data; + AVVulkanDeviceContext *hwctx = device->hwctx; + enum AVPixelFormat source_format; + enum AVPixelFormat best_format; + VkFormat best_vkfmt; + + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + FFVulkanDecodeShared *ctx = (FFVulkanDecodeShared *)dec->shared_ref->data; + FFVulkanFunctions *vk = &ctx->s.vkfn; + + VkVideoCapabilitiesKHR *caps = &ctx->caps; + VkVideoDecodeCapabilitiesKHR *dec_caps = &ctx->dec_caps; + + VkVideoDecodeH264CapabilitiesKHR h264_caps = { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_CAPABILITIES_KHR, + }; + VkVideoDecodeH265CapabilitiesKHR h265_caps = { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_CAPABILITIES_KHR, + }; + VkVideoDecodeAV1CapabilitiesMESA av1_caps = { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_CAPABILITIES_MESA, + }; + + VkPhysicalDeviceVideoFormatInfoKHR fmt_info = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_FORMAT_INFO_KHR, + .pNext = &prof->profile_list, + }; + VkVideoFormatPropertiesKHR *ret_info; + uint32_t nb_out_fmts = 0; + + if (!vk_codec->decode_op || !vk_codec->decode_extension) { + av_log(avctx, AV_LOG_ERROR, "Unsupported codec for Vulkan decoding: %s!\n", + avcodec_get_name(avctx->codec_id)); + return AVERROR(ENOSYS); + } else if (!(vk_codec->decode_extension & ctx->s.extensions)) { + av_log(avctx, AV_LOG_ERROR, "Device does not support decoding %s!\n", + avcodec_get_name(avctx->codec_id)); + return AVERROR(ENOSYS); + } + + cur_profile = avctx->profile; + base_profile = avctx->codec_id == AV_CODEC_ID_H264 ? FF_PROFILE_H264_CONSTRAINED_BASELINE : + avctx->codec_id == AV_CODEC_ID_H265 ? FF_PROFILE_HEVC_MAIN : + avctx->codec_id == AV_CODEC_ID_AV1 ? STD_VIDEO_AV1_MESA_PROFILE_MAIN : + 0; + + ret = vulkan_setup_profile(avctx, prof, hwctx, vk, vk_codec, + &h264_caps, + &h265_caps, + &av1_caps, + caps, + dec_caps, + cur_profile); + if (ret == VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR && + avctx->flags & AV_HWACCEL_FLAG_ALLOW_PROFILE_MISMATCH && + avctx->profile != base_profile) { + av_log(avctx, AV_LOG_VERBOSE, "%s profile %s not supported, attempting " + "again with profile %s\n", + avcodec_get_name(avctx->codec_id), + avcodec_profile_name(avctx->codec_id, cur_profile), + avcodec_profile_name(avctx->codec_id, base_profile)); + cur_profile = base_profile; + ret = vulkan_setup_profile(avctx, prof, hwctx, vk, vk_codec, + &h264_caps, + &h265_caps, + &av1_caps, + caps, + dec_caps, + cur_profile); + } + + if (ret == VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR) { + av_log(avctx, AV_LOG_VERBOSE, "Unable to initialize video session: " + "%s profile \"%s\" not supported!\n", + avcodec_get_name(avctx->codec_id), + avcodec_profile_name(avctx->codec_id, cur_profile)); + return AVERROR(EINVAL); + } else if (ret == VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR) { + av_log(avctx, AV_LOG_VERBOSE, "Unable to initialize video session: " + "format (%s) not supported!\n", + av_get_pix_fmt_name(avctx->sw_pix_fmt)); + return AVERROR(EINVAL); + } else if (ret == VK_ERROR_FEATURE_NOT_PRESENT || + ret == VK_ERROR_FORMAT_NOT_SUPPORTED) { + return AVERROR(EINVAL); + } else if (ret != VK_SUCCESS) { + return AVERROR_EXTERNAL; + } + + max_level = avctx->codec_id == AV_CODEC_ID_H264 ? h264_caps.maxLevelIdc : + avctx->codec_id == AV_CODEC_ID_H265 ? h265_caps.maxLevelIdc : + avctx->codec_id == AV_CODEC_ID_AV1 ? av1_caps.maxLevelIdc : + 0; + + av_log(avctx, AV_LOG_VERBOSE, "Decoder capabilities for %s profile \"%s\":\n", + avcodec_get_name(avctx->codec_id), + avcodec_profile_name(avctx->codec_id, cur_profile)); + av_log(avctx, AV_LOG_VERBOSE, " Maximum level: %i (stream %i)\n", + max_level, avctx->level); + av_log(avctx, AV_LOG_VERBOSE, " Width: from %i to %i\n", + caps->minCodedExtent.width, caps->maxCodedExtent.width); + av_log(avctx, AV_LOG_VERBOSE, " Height: from %i to %i\n", + caps->minCodedExtent.height, caps->maxCodedExtent.height); + av_log(avctx, AV_LOG_VERBOSE, " Width alignment: %i\n", + caps->pictureAccessGranularity.width); + av_log(avctx, AV_LOG_VERBOSE, " Height alignment: %i\n", + caps->pictureAccessGranularity.height); + av_log(avctx, AV_LOG_VERBOSE, " Bitstream offset alignment: %"PRIu64"\n", + caps->minBitstreamBufferOffsetAlignment); + av_log(avctx, AV_LOG_VERBOSE, " Bitstream size alignment: %"PRIu64"\n", + caps->minBitstreamBufferSizeAlignment); + av_log(avctx, AV_LOG_VERBOSE, " Maximum references: %u\n", + caps->maxDpbSlots); + av_log(avctx, AV_LOG_VERBOSE, " Maximum active references: %u\n", + caps->maxActiveReferencePictures); + av_log(avctx, AV_LOG_VERBOSE, " Codec header version: %i.%i.%i (driver), %i.%i.%i (compiled)\n", + CODEC_VER(caps->stdHeaderVersion.specVersion), + CODEC_VER(dec_ext[avctx->codec_id]->specVersion)); + av_log(avctx, AV_LOG_VERBOSE, " Decode modes:%s%s%s\n", + dec_caps->flags ? "" : + " invalid", + dec_caps->flags & VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR ? + " reuse_dst_dpb" : "", + dec_caps->flags & VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_DISTINCT_BIT_KHR ? + " dedicated_dpb" : ""); + av_log(avctx, AV_LOG_VERBOSE, " Capability flags:%s%s%s\n", + caps->flags ? "" : + " none", + caps->flags & VK_VIDEO_CAPABILITY_PROTECTED_CONTENT_BIT_KHR ? + " protected" : "", + caps->flags & VK_VIDEO_CAPABILITY_SEPARATE_REFERENCE_IMAGES_BIT_KHR ? + " separate_references" : ""); + + /* Check if decoding is possible with the given parameters */ + if (avctx->width < caps->minCodedExtent.width || + avctx->height < caps->minCodedExtent.height || + avctx->width > caps->maxCodedExtent.width || + avctx->height > caps->maxCodedExtent.height) + return AVERROR(EINVAL); + + if (!(avctx->hwaccel_flags & AV_HWACCEL_FLAG_IGNORE_LEVEL) && + avctx->level > max_level) + return AVERROR(EINVAL); + + /* Some basic sanity checking */ + if (!(dec_caps->flags & (VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR | + VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_DISTINCT_BIT_KHR))) { + av_log(avctx, AV_LOG_ERROR, "Buggy driver signals invalid decoding mode: neither " + "VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR nor " + "VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_DISTINCT_BIT_KHR are set!\n"); + return AVERROR_EXTERNAL; + } else if ((dec_caps->flags & (VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR | + VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_DISTINCT_BIT_KHR) == + VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR) && + !(caps->flags & VK_VIDEO_CAPABILITY_SEPARATE_REFERENCE_IMAGES_BIT_KHR)) { + av_log(avctx, AV_LOG_ERROR, "Cannot initialize Vulkan decoding session, buggy driver: " + "VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR set " + "but VK_VIDEO_CAPABILITY_SEPARATE_REFERENCE_IMAGES_BIT_KHR is unset!\n"); + return AVERROR_EXTERNAL; + } else if (!(dec_caps->flags & VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_DISTINCT_BIT_KHR) && + avctx->codec_id == AV_CODEC_ID_AV1) { + av_log(avctx, AV_LOG_ERROR, "Cannot initialize Vulkan decoding session, buggy driver: " + "codec is AV1, but VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_DISTINCT_BIT_KHR isn't set!\n"); + return AVERROR_EXTERNAL; + } + + /* TODO: make dedicated_dpb tunable */ + dec->dedicated_dpb = !(dec_caps->flags & VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR); + dec->layered_dpb = !(caps->flags & VK_VIDEO_CAPABILITY_SEPARATE_REFERENCE_IMAGES_BIT_KHR); + dec->external_fg = av1_caps.flags & VK_VIDEO_DECODE_AV1_CAPABILITY_EXTERNAL_FILM_GRAIN_MESA; + + if (dec->dedicated_dpb) { + fmt_info.imageUsage = VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR; + } else { + fmt_info.imageUsage = VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR | + VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR | + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT; + } + + /* Get the format of the images necessary */ + ret = vk->GetPhysicalDeviceVideoFormatPropertiesKHR(hwctx->phys_dev, + &fmt_info, + &nb_out_fmts, NULL); + if (ret == VK_ERROR_FORMAT_NOT_SUPPORTED || + (!nb_out_fmts && ret == VK_SUCCESS)) { + return AVERROR(EINVAL); + } else if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Unable to get Vulkan format properties: %s!\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + ret_info = av_mallocz(sizeof(*ret_info)*nb_out_fmts); + if (!ret_info) + return AVERROR(ENOMEM); + + for (int i = 0; i < nb_out_fmts; i++) + ret_info[i].sType = VK_STRUCTURE_TYPE_VIDEO_FORMAT_PROPERTIES_KHR; + + ret = vk->GetPhysicalDeviceVideoFormatPropertiesKHR(hwctx->phys_dev, + &fmt_info, + &nb_out_fmts, ret_info); + if (ret == VK_ERROR_FORMAT_NOT_SUPPORTED || + (!nb_out_fmts && ret == VK_SUCCESS)) { + av_free(ret_info); + return AVERROR(EINVAL); + } else if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Unable to get Vulkan format properties: %s!\n", + ff_vk_ret2str(ret)); + av_free(ret_info); + return AVERROR_EXTERNAL; + } + + /* Find a format to use */ + *pix_fmt = best_format = AV_PIX_FMT_NONE; + *vk_fmt = best_vkfmt = VK_FORMAT_UNDEFINED; + source_format = avctx->sw_pix_fmt; + + av_log(avctx, AV_LOG_DEBUG, "Choosing best pixel format for decoding from %i:\n", nb_out_fmts); + for (int i = 0; i < nb_out_fmts; i++) { + enum AVPixelFormat tmp = ff_vk_pix_fmt_from_vkfmt(ret_info[i].format); + if (tmp == AV_PIX_FMT_NONE) { + av_log(avctx, AV_LOG_WARNING, "Invalid/unknown Vulkan format %i!\n", ret_info[i].format); + continue; + } + + best_format = av_find_best_pix_fmt_of_2(tmp, best_format, source_format, 0, NULL); + if (tmp == best_format) + best_vkfmt = ret_info[i].format; + + av_log(avctx, AV_LOG_DEBUG, " %s%s (Vulkan ID: %i)\n", + av_get_pix_fmt_name(tmp), tmp == best_format ? "*" : "", + ret_info[i].format); + } + + av_free(ret_info); + + if (best_format == AV_PIX_FMT_NONE) { + av_log(avctx, AV_LOG_ERROR, "No valid/compatible pixel format found for decoding!\n"); + return AVERROR(EINVAL); + } else { + av_log(avctx, AV_LOG_VERBOSE, "Chosen frame pixfmt: %s (Vulkan ID: %i)\n", + av_get_pix_fmt_name(best_format), best_vkfmt); + } + + *pix_fmt = best_format; + *vk_fmt = best_vkfmt; + + *dpb_dedicate = dec->dedicated_dpb; + + return 0; +} + +static void free_profile_data(AVHWFramesContext *hwfc) +{ + av_free(hwfc->user_opaque); +} + +int ff_vk_frame_params(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx) +{ + VkFormat vkfmt; + int err, dedicated_dpb; + AVHWFramesContext *frames_ctx = (AVHWFramesContext*)hw_frames_ctx->data; + AVVulkanFramesContext *hwfc = frames_ctx->hwctx; + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + FFVulkanDecodeProfileData *prof; + + frames_ctx->sw_format = AV_PIX_FMT_NONE; + + err = vulkan_decode_bootstrap(avctx, hw_frames_ctx); + if (err < 0) + return err; + + prof = av_mallocz(sizeof(FFVulkanDecodeProfileData)); + if (!prof) + return AVERROR(ENOMEM); + + err = vulkan_decode_get_profile(avctx, hw_frames_ctx, + &frames_ctx->sw_format, &vkfmt, + prof, &dedicated_dpb); + if (err < 0) { + av_free(prof); + return err; + } + + frames_ctx->user_opaque = prof; + frames_ctx->free = free_profile_data; + + frames_ctx->width = avctx->width; + frames_ctx->height = avctx->height; + frames_ctx->format = AV_PIX_FMT_VULKAN; + + hwfc->format[0] = vkfmt; + hwfc->create_pnext = &prof->profile_list; + hwfc->tiling = VK_IMAGE_TILING_OPTIMAL; + hwfc->usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR; + + if (!dec->dedicated_dpb) + hwfc->usage |= VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR; + + return err; +} + +void ff_vk_decode_free_params(void *opaque, uint8_t *data) +{ + FFVulkanDecodeShared *ctx = opaque; + FFVulkanFunctions *vk = &ctx->s.vkfn; + VkVideoSessionParametersKHR *par = (VkVideoSessionParametersKHR *)data; + vk->DestroyVideoSessionParametersKHR(ctx->s.hwctx->act_dev, *par, + ctx->s.hwctx->alloc); + av_free(par); +} + +int ff_vk_decode_uninit(AVCodecContext *avctx) +{ + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + FFVulkanDecodeShared *ctx = (FFVulkanDecodeShared *)dec->shared_ref->data; + + /* Wait on and free execution pool */ + ff_vk_exec_pool_free(&ctx->s, &dec->exec_pool); + + av_buffer_pool_uninit(&dec->tmp_pool); + av_buffer_unref(&dec->session_params); + av_buffer_unref(&dec->shared_ref); + av_freep(&dec->slice_off); + return 0; +} + +int ff_vk_decode_init(AVCodecContext *avctx) +{ + int err, qf, cxpos = 0, cypos = 0, nb_q = 0; + VkResult ret; + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + FFVulkanDecodeShared *ctx; + FFVulkanContext *s; + FFVulkanFunctions *vk; + const VkVideoProfileInfoKHR *profile; + + VkVideoDecodeH264SessionParametersCreateInfoKHR h264_params = { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_SESSION_PARAMETERS_CREATE_INFO_KHR, + }; + VkVideoDecodeH265SessionParametersCreateInfoKHR h265_params = { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_SESSION_PARAMETERS_CREATE_INFO_KHR, + }; + VkVideoDecodeAV1SessionParametersCreateInfoMESA av1_params = { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_SESSION_PARAMETERS_CREATE_INFO_MESA, + }; + VkVideoSessionParametersCreateInfoKHR session_params_create = { + .sType = VK_STRUCTURE_TYPE_VIDEO_SESSION_PARAMETERS_CREATE_INFO_KHR, + .pNext = avctx->codec_id == AV_CODEC_ID_H264 ? (void *)&h264_params : + avctx->codec_id == AV_CODEC_ID_HEVC ? (void *)&h265_params : + avctx->codec_id == AV_CODEC_ID_AV1 ? (void *)&av1_params : + NULL, + }; + VkVideoSessionCreateInfoKHR session_create = { + .sType = VK_STRUCTURE_TYPE_VIDEO_SESSION_CREATE_INFO_KHR, + }; + VkSamplerYcbcrConversionCreateInfo yuv_sampler_info = { + .sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO, + .components = ff_comp_identity_map, + .ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY, + .ycbcrRange = avctx->color_range == AVCOL_RANGE_MPEG, /* Ignored */ + }; + + err = ff_decode_get_hw_frames_ctx(avctx, AV_HWDEVICE_TYPE_VULKAN); + if (err < 0) + return err; + + /* Initialize contexts */ + ctx = (FFVulkanDecodeShared *)dec->shared_ref->data; + s = &ctx->s; + vk = &ctx->s.vkfn; + + s->frames_ref = av_buffer_ref(avctx->hw_frames_ctx); + s->frames = (AVHWFramesContext *)s->frames_ref->data; + s->hwfc = s->frames->hwctx; + + s->device = (AVHWDeviceContext *)s->frames->device_ref->data; + s->hwctx = s->device->hwctx; + + profile = get_video_profile(ctx, avctx->codec_id); + if (!profile) { + av_log(avctx, AV_LOG_ERROR, "Video profile missing from frames context!"); + return AVERROR(EINVAL); + } + + err = ff_vk_load_props(s); + if (err < 0) + goto fail; + + /* Create queue context */ + qf = ff_vk_qf_init(s, &ctx->qf, VK_QUEUE_VIDEO_DECODE_BIT_KHR); + + /* Check for support */ + if (!(s->video_props[qf].videoCodecOperations & + ff_vk_codec_map[avctx->codec_id].decode_op)) { + av_log(avctx, AV_LOG_ERROR, "Decoding %s not supported on the given " + "queue family %i!\n", avcodec_get_name(avctx->codec_id), qf); + return AVERROR(EINVAL); + } + + /* Enable queries if supported */ + if (s->query_props[qf].queryResultStatusSupport) + nb_q = 1; + + session_create.flags = 0x0; + session_create.queueFamilyIndex = s->hwctx->queue_family_decode_index; + session_create.maxCodedExtent = ctx->caps.maxCodedExtent; + session_create.maxDpbSlots = ctx->caps.maxDpbSlots; + session_create.maxActiveReferencePictures = ctx->caps.maxActiveReferencePictures; + session_create.pictureFormat = s->hwfc->format[0]; + session_create.referencePictureFormat = session_create.pictureFormat; + session_create.pStdHeaderVersion = dec_ext[avctx->codec_id]; + session_create.pVideoProfile = profile; + + /* Create decode exec context for this specific main thread. + * 2 async contexts per thread was experimentally determined to be optimal + * for a majority of streams. */ + err = ff_vk_exec_pool_init(s, &ctx->qf, &dec->exec_pool, 2, + nb_q, VK_QUERY_TYPE_RESULT_STATUS_ONLY_KHR, 0, + profile); + if (err < 0) + goto fail; + + err = ff_vk_video_common_init(avctx, s, &ctx->common, &session_create); + if (err < 0) + goto fail; + + /* Get sampler */ + av_chroma_location_enum_to_pos(&cxpos, &cypos, avctx->chroma_sample_location); + yuv_sampler_info.xChromaOffset = cxpos >> 7; + yuv_sampler_info.yChromaOffset = cypos >> 7; + yuv_sampler_info.format = s->hwfc->format[0]; + ret = vk->CreateSamplerYcbcrConversion(s->hwctx->act_dev, &yuv_sampler_info, + s->hwctx->alloc, &ctx->yuv_sampler); + if (ret != VK_SUCCESS) { + err = AVERROR_EXTERNAL; + goto fail; + } + + /* If doing an out-of-place decoding, create a DPB pool */ + if (dec->dedicated_dpb || avctx->codec_id == AV_CODEC_ID_AV1) { + AVHWFramesContext *dpb_frames; + AVVulkanFramesContext *dpb_hwfc; + + ctx->dpb_hwfc_ref = av_hwframe_ctx_alloc(s->frames->device_ref); + if (!ctx->dpb_hwfc_ref) { + err = AVERROR(ENOMEM); + goto fail; + } + + dpb_frames = (AVHWFramesContext *)ctx->dpb_hwfc_ref->data; + dpb_frames->format = s->frames->format; + dpb_frames->sw_format = s->frames->sw_format; + dpb_frames->width = s->frames->width; + dpb_frames->height = s->frames->height; + + dpb_hwfc = dpb_frames->hwctx; + dpb_hwfc->create_pnext = (void *)ff_vk_find_struct(ctx->s.hwfc->create_pnext, + VK_STRUCTURE_TYPE_VIDEO_PROFILE_LIST_INFO_KHR); + dpb_hwfc->format[0] = s->hwfc->format[0]; + dpb_hwfc->tiling = VK_IMAGE_TILING_OPTIMAL; + dpb_hwfc->usage = VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR | + VK_IMAGE_USAGE_SAMPLED_BIT; /* Shuts validator up. */ + + if (dec->layered_dpb) + dpb_hwfc->nb_layers = ctx->caps.maxDpbSlots; + + err = av_hwframe_ctx_init(ctx->dpb_hwfc_ref); + if (err < 0) + goto fail; + + if (dec->layered_dpb) { + ctx->layered_frame = vk_get_dpb_pool(ctx); + if (!ctx->layered_frame) { + err = AVERROR(ENOMEM); + goto fail; + } + + err = vk_decode_create_view(ctx, &ctx->layered_view, &ctx->layered_aspect, + (AVVkFrame *)ctx->layered_frame->data[0], + s->hwfc->format[0]); + if (err < 0) + goto fail; + } + } + + session_params_create.videoSession = ctx->common.session; + ret = vk->CreateVideoSessionParametersKHR(s->hwctx->act_dev, &session_params_create, + s->hwctx->alloc, &ctx->empty_session_params); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Unable to create empty Vulkan video session parameters: %s!\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + ff_vk_decode_flush(avctx); + + av_log(avctx, AV_LOG_VERBOSE, "Vulkan decoder initialization sucessful\n"); + + return 0; + +fail: + ff_vk_decode_uninit(avctx); + + return err; +} diff --git a/libavcodec/vulkan_decode.h b/libavcodec/vulkan_decode.h new file mode 100644 index 00000000000..0aaa2e2de09 --- /dev/null +++ b/libavcodec/vulkan_decode.h @@ -0,0 +1,173 @@ +/* + * 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_VULKAN_DECODE_H +#define AVCODEC_VULKAN_DECODE_H + +#include "decode.h" +#include "hwaccel_internal.h" +#include "internal.h" + +#include "vulkan_video.h" + +typedef struct FFVulkanDecodeProfileData { + VkVideoDecodeH264ProfileInfoKHR h264_profile; + VkVideoDecodeH264ProfileInfoKHR h265_profile; + VkVideoDecodeAV1ProfileInfoMESA av1_profile; + VkVideoDecodeUsageInfoKHR usage; + VkVideoProfileInfoKHR profile; + VkVideoProfileListInfoKHR profile_list; +} FFVulkanDecodeProfileData; + +typedef struct FFVulkanDecodeShared { + FFVulkanContext s; + FFVkVideoCommon common; + FFVkQueueFamilyCtx qf; + + VkVideoCapabilitiesKHR caps; + VkVideoDecodeCapabilitiesKHR dec_caps; + + AVBufferRef *dpb_hwfc_ref; /* Only used for dedicated_dpb */ + + AVFrame *layered_frame; /* Only used for layered_dpb */ + VkImageView layered_view; + VkImageAspectFlags layered_aspect; + + VkVideoSessionParametersKHR empty_session_params; + + VkSamplerYcbcrConversion yuv_sampler; +} FFVulkanDecodeShared; + +typedef struct FFVulkanDecodeContext { + AVBufferRef *shared_ref; + AVBufferRef *session_params; + FFVkExecPool exec_pool; + + int dedicated_dpb; /* Oddity #1 - separate DPB images */ + int layered_dpb; /* Madness #1 - layered DPB images */ + int external_fg; /* Oddity #2 - hardware can't apply film grain */ + uint32_t frame_id_alloc_mask; /* For AV1 only */ + + /* Thread-local state below */ + AVBufferPool *tmp_pool; /* Pool for temporary data, if needed (HEVC) */ + size_t tmp_pool_ele_size; + + uint32_t *slice_off; + unsigned int slice_off_max; +} FFVulkanDecodeContext; + +typedef struct FFVulkanDecodePicture { + AVFrame *dpb_frame; /* Only used for out-of-place decoding. */ + + VkImageView img_view_ref; /* Image representation view (reference) */ + VkImageView img_view_out; /* Image representation view (output-only) */ + VkImageView img_view_dest; /* Set to img_view_out if no layered refs are used */ + VkImageAspectFlags img_aspect; /* Image plane mask bits */ + VkImageAspectFlags img_aspect_ref; /* Only used for out-of-place decoding */ + + VkSemaphore sem; + uint64_t sem_value; + + /* Current picture */ + VkVideoPictureResourceInfoKHR ref; + VkVideoReferenceSlotInfoKHR ref_slot; + + /* Picture refs. H264 has the maximum number of refs (36) of any supported codec. */ + VkVideoPictureResourceInfoKHR refs [36]; + VkVideoReferenceSlotInfoKHR ref_slots[36]; + + /* Main decoding struct */ + VkVideoDecodeInfoKHR decode_info; + + /* Slice data */ + AVBufferRef *slices_buf; + size_t slices_size; +} FFVulkanDecodePicture; + +/** + * Initialize decoder. + */ +int ff_vk_decode_init(AVCodecContext *avctx); + +/** + * Synchronize the contexts between 2 threads. + */ +int ff_vk_update_thread_context(AVCodecContext *dst, const AVCodecContext *src); + +/** + * Initialize hw_frames_ctx with the parameters needed to decode the stream + * using the parameters from avctx. + * + * NOTE: if avctx->internal->hwaccel_priv_data exists, will partially initialize + * the context. + */ +int ff_vk_frame_params(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx); + +/** + * Removes current session parameters to recreate them + */ +int ff_vk_params_invalidate(AVCodecContext *avctx, int t, const uint8_t *b, uint32_t s); + +/** + * Prepare a frame, creates the image view, and sets up the dpb fields. + */ +int ff_vk_decode_prepare_frame(FFVulkanDecodeContext *dec, AVFrame *pic, + FFVulkanDecodePicture *vkpic, int is_current, + int alloc_dpb); + +/** + * Add slice data to frame. + */ +int ff_vk_decode_add_slice(AVCodecContext *avctx, FFVulkanDecodePicture *vp, + const uint8_t *data, size_t size, int add_startcode, + uint32_t *nb_slices, const uint32_t **offsets); + +/** + * Decode a frame. + */ +int ff_vk_decode_frame(AVCodecContext *avctx, + AVFrame *pic, FFVulkanDecodePicture *vp, + AVFrame *rpic[], FFVulkanDecodePicture *rvkp[]); + +/** + * Free a frame and its state. + */ +void ff_vk_decode_free_frame(AVHWDeviceContext *dev_ctx, FFVulkanDecodePicture *vp); + +/** + * Get an FFVkBuffer suitable for decoding from. + */ +int ff_vk_get_decode_buffer(FFVulkanDecodeContext *ctx, AVBufferRef **buf, + void *create_pNext, size_t size); + +/** + * Free VkVideoSessionParametersKHR. + */ +void ff_vk_decode_free_params(void *opaque, uint8_t *data); + +/** + * Flush decoder. + */ +void ff_vk_decode_flush(AVCodecContext *avctx); + +/** + * Free decoder. + */ +int ff_vk_decode_uninit(AVCodecContext *avctx); + +#endif /* AVCODEC_VULKAN_DECODE_H */ diff --git a/libavcodec/vulkan_h264.c b/libavcodec/vulkan_h264.c new file mode 100644 index 00000000000..f38bb9057ba --- /dev/null +++ b/libavcodec/vulkan_h264.c @@ -0,0 +1,581 @@ +/* + * 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 "h264dec.h" +#include "h264_ps.h" + +#include "vulkan_decode.h" + +const VkExtensionProperties ff_vk_dec_h264_ext = { + .extensionName = VK_STD_VULKAN_VIDEO_CODEC_H264_DECODE_EXTENSION_NAME, + .specVersion = VK_STD_VULKAN_VIDEO_CODEC_H264_DECODE_SPEC_VERSION, +}; + +typedef struct H264VulkanDecodePicture { + FFVulkanDecodePicture vp; + + /* Current picture */ + StdVideoDecodeH264ReferenceInfo h264_ref; + VkVideoDecodeH264DpbSlotInfoKHR vkh264_ref; + + /* Picture refs */ + H264Picture *ref_src [H264_MAX_PICTURE_COUNT]; + StdVideoDecodeH264ReferenceInfo h264_refs [H264_MAX_PICTURE_COUNT]; + VkVideoDecodeH264DpbSlotInfoKHR vkh264_refs[H264_MAX_PICTURE_COUNT]; + + /* Current picture (contd.) */ + StdVideoDecodeH264PictureInfo h264pic; + VkVideoDecodeH264PictureInfoKHR h264_pic_info; +} H264VulkanDecodePicture; + +const static int h264_scaling_list8_order[] = { 0, 3, 1, 4, 2, 5 }; + +static int vk_h264_fill_pict(AVCodecContext *avctx, H264Picture **ref_src, + VkVideoReferenceSlotInfoKHR *ref_slot, /* Main structure */ + VkVideoPictureResourceInfoKHR *ref, /* Goes in ^ */ + VkVideoDecodeH264DpbSlotInfoKHR *vkh264_ref, /* Goes in ^ */ + StdVideoDecodeH264ReferenceInfo *h264_ref, /* Goes in ^ */ + H264Picture *pic, int is_current, + int is_field, int picture_structure, + int dpb_slot_index) +{ + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + H264VulkanDecodePicture *hp = pic->hwaccel_picture_private; + FFVulkanDecodePicture *vkpic = &hp->vp; + + int err = ff_vk_decode_prepare_frame(dec, pic->f, vkpic, is_current, + dec->dedicated_dpb); + if (err < 0) + return err; + + *h264_ref = (StdVideoDecodeH264ReferenceInfo) { + .FrameNum = pic->long_ref ? pic->pic_id : pic->frame_num, + .PicOrderCnt = { pic->field_poc[0], pic->field_poc[1] }, + .flags = (StdVideoDecodeH264ReferenceInfoFlags) { + .top_field_flag = is_field ? !!(picture_structure & PICT_TOP_FIELD) : 0, + .bottom_field_flag = is_field ? !!(picture_structure & PICT_BOTTOM_FIELD) : 0, + .used_for_long_term_reference = pic->reference && pic->long_ref, + /* + * flags.is_non_existing is used to indicate whether the picture is marked as + * “non-existing” as defined in section 8.2.5.2 of the ITU-T H.264 Specification; + * 8.2.5.2 Decoding process for gaps in frame_num + * corresponds to the code in h264_slice.c:h264_field_start, + * which sets the invalid_gap flag when decoding. + */ + .is_non_existing = pic->invalid_gap, + }, + }; + + *vkh264_ref = (VkVideoDecodeH264DpbSlotInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_DPB_SLOT_INFO_KHR, + .pStdReferenceInfo = h264_ref, + }; + + *ref = (VkVideoPictureResourceInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_INFO_KHR, + .codedOffset = (VkOffset2D){ 0, 0 }, + .codedExtent = (VkExtent2D){ pic->f->width, pic->f->height }, + .baseArrayLayer = dec->layered_dpb ? dpb_slot_index : 0, + .imageViewBinding = vkpic->img_view_ref, + }; + + *ref_slot = (VkVideoReferenceSlotInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_REFERENCE_SLOT_INFO_KHR, + .pNext = vkh264_ref, + .slotIndex = dpb_slot_index, + .pPictureResource = ref, + }; + + if (ref_src) + *ref_src = pic; + + return 0; +} + +static StdVideoH264LevelIdc convert_to_vk_level_idc(int level_idc) +{ + switch (level_idc) { + case 10: return STD_VIDEO_H264_LEVEL_IDC_1_0; + case 11: return STD_VIDEO_H264_LEVEL_IDC_1_1; + case 12: return STD_VIDEO_H264_LEVEL_IDC_1_2; + case 13: return STD_VIDEO_H264_LEVEL_IDC_1_3; + case 20: return STD_VIDEO_H264_LEVEL_IDC_2_0; + case 21: return STD_VIDEO_H264_LEVEL_IDC_2_1; + case 22: return STD_VIDEO_H264_LEVEL_IDC_2_2; + case 30: return STD_VIDEO_H264_LEVEL_IDC_3_0; + case 31: return STD_VIDEO_H264_LEVEL_IDC_3_1; + case 32: return STD_VIDEO_H264_LEVEL_IDC_3_2; + case 40: return STD_VIDEO_H264_LEVEL_IDC_4_0; + case 41: return STD_VIDEO_H264_LEVEL_IDC_4_1; + case 42: return STD_VIDEO_H264_LEVEL_IDC_4_2; + case 50: return STD_VIDEO_H264_LEVEL_IDC_5_0; + case 51: return STD_VIDEO_H264_LEVEL_IDC_5_1; + case 52: return STD_VIDEO_H264_LEVEL_IDC_5_2; + case 60: return STD_VIDEO_H264_LEVEL_IDC_6_0; + case 61: return STD_VIDEO_H264_LEVEL_IDC_6_1; + default: + case 62: return STD_VIDEO_H264_LEVEL_IDC_6_2; + } +} + +static void set_sps(const SPS *sps, + StdVideoH264ScalingLists *vksps_scaling, + StdVideoH264HrdParameters *vksps_vui_header, + StdVideoH264SequenceParameterSetVui *vksps_vui, + StdVideoH264SequenceParameterSet *vksps) +{ + *vksps_scaling = (StdVideoH264ScalingLists) { + .scaling_list_present_mask = sps->scaling_matrix_present_mask, + .use_default_scaling_matrix_mask = 0, /* We already fill in the default matrix */ + }; + + for (int i = 0; i < STD_VIDEO_H264_SCALING_LIST_4X4_NUM_LISTS; i++) + memcpy(vksps_scaling->ScalingList4x4[i], sps->scaling_matrix4[i], + STD_VIDEO_H264_SCALING_LIST_4X4_NUM_ELEMENTS * sizeof(**sps->scaling_matrix4)); + + for (int i = 0; i < STD_VIDEO_H264_SCALING_LIST_8X8_NUM_LISTS; i++) + memcpy(vksps_scaling->ScalingList8x8[i], sps->scaling_matrix8[h264_scaling_list8_order[i]], + STD_VIDEO_H264_SCALING_LIST_8X8_NUM_ELEMENTS * sizeof(**sps->scaling_matrix8)); + + *vksps_vui_header = (StdVideoH264HrdParameters) { + .cpb_cnt_minus1 = sps->cpb_cnt - 1, + .bit_rate_scale = sps->bit_rate_scale, + .initial_cpb_removal_delay_length_minus1 = sps->initial_cpb_removal_delay_length - 1, + .cpb_removal_delay_length_minus1 = sps->cpb_removal_delay_length - 1, + .dpb_output_delay_length_minus1 = sps->dpb_output_delay_length - 1, + .time_offset_length = sps->time_offset_length, + }; + + for (int i = 0; i < sps->cpb_cnt; i++) { + vksps_vui_header->bit_rate_value_minus1[i] = sps->bit_rate_value[i] - 1; + vksps_vui_header->cpb_size_value_minus1[i] = sps->cpb_size_value[i] - 1; + vksps_vui_header->cbr_flag[i] = (sps->cpr_flag >> i) & 0x1; + } + + *vksps_vui = (StdVideoH264SequenceParameterSetVui) { + .aspect_ratio_idc = sps->vui.aspect_ratio_idc, + .sar_width = sps->vui.sar.num, + .sar_height = sps->vui.sar.den, + .video_format = sps->vui.video_format, + .colour_primaries = sps->vui.colour_primaries, + .transfer_characteristics = sps->vui.transfer_characteristics, + .matrix_coefficients = sps->vui.matrix_coeffs, + .num_units_in_tick = sps->num_units_in_tick, + .time_scale = sps->time_scale, + .pHrdParameters = vksps_vui_header, + .max_num_reorder_frames = sps->num_reorder_frames, + .max_dec_frame_buffering = sps->max_dec_frame_buffering, + .flags = (StdVideoH264SpsVuiFlags) { + .aspect_ratio_info_present_flag = sps->vui.aspect_ratio_info_present_flag, + .overscan_info_present_flag = sps->vui.overscan_info_present_flag, + .overscan_appropriate_flag = sps->vui.overscan_appropriate_flag, + .video_signal_type_present_flag = sps->vui.video_signal_type_present_flag, + .video_full_range_flag = sps->vui.video_full_range_flag, + .color_description_present_flag = sps->vui.colour_description_present_flag, + .chroma_loc_info_present_flag = sps->vui.chroma_location, + .timing_info_present_flag = sps->timing_info_present_flag, + .fixed_frame_rate_flag = sps->fixed_frame_rate_flag, + .bitstream_restriction_flag = sps->bitstream_restriction_flag, + .nal_hrd_parameters_present_flag = sps->nal_hrd_parameters_present_flag, + .vcl_hrd_parameters_present_flag = sps->vcl_hrd_parameters_present_flag, + }, + }; + + *vksps = (StdVideoH264SequenceParameterSet) { + .profile_idc = sps->profile_idc, + .level_idc = convert_to_vk_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->poc_type ? 0 : sps->log2_max_poc_lsb - 4, + .offset_for_non_ref_pic = sps->offset_for_non_ref_pic, + .offset_for_top_to_bottom_field = sps->offset_for_top_to_bottom_field, + .num_ref_frames_in_pic_order_cnt_cycle = sps->poc_cycle_length, + .max_num_ref_frames = sps->ref_frame_count, + .pic_width_in_mbs_minus1 = sps->mb_width - 1, + .pic_height_in_map_units_minus1 = (sps->mb_height/(2 - sps->frame_mbs_only_flag)) - 1, + .frame_crop_left_offset = sps->crop_left, + .frame_crop_right_offset = sps->crop_right, + .frame_crop_top_offset = sps->crop_top, + .frame_crop_bottom_offset = sps->crop_bottom, + .flags = (StdVideoH264SpsFlags) { + .constraint_set0_flag = (sps->constraint_set_flags >> 0) & 0x1, + .constraint_set1_flag = (sps->constraint_set_flags >> 1) & 0x1, + .constraint_set2_flag = (sps->constraint_set_flags >> 2) & 0x1, + .constraint_set3_flag = (sps->constraint_set_flags >> 3) & 0x1, + .constraint_set4_flag = (sps->constraint_set_flags >> 4) & 0x1, + .constraint_set5_flag = (sps->constraint_set_flags >> 5) & 0x1, + .direct_8x8_inference_flag = sps->direct_8x8_inference_flag, + .mb_adaptive_frame_field_flag = sps->mb_aff, + .frame_mbs_only_flag = sps->frame_mbs_only_flag, + .delta_pic_order_always_zero_flag = sps->delta_pic_order_always_zero_flag, + .separate_colour_plane_flag = sps->residual_color_transform_flag, + .gaps_in_frame_num_value_allowed_flag = sps->gaps_in_frame_num_allowed_flag, + .qpprime_y_zero_transform_bypass_flag = sps->transform_bypass, + .frame_cropping_flag = sps->crop, + .seq_scaling_matrix_present_flag = sps->scaling_matrix_present, + .vui_parameters_present_flag = sps->vui_parameters_present_flag, + }, + .pOffsetForRefFrame = sps->offset_for_ref_frame, + .pScalingLists = vksps_scaling, + .pSequenceParameterSetVui = vksps_vui, + }; +} + +static void set_pps(const PPS *pps, const SPS *sps, + StdVideoH264ScalingLists *vkpps_scaling, + StdVideoH264PictureParameterSet *vkpps) +{ + *vkpps_scaling = (StdVideoH264ScalingLists) { + .scaling_list_present_mask = pps->pic_scaling_matrix_present_mask, + .use_default_scaling_matrix_mask = 0, /* We already fill in the default matrix */ + }; + + for (int i = 0; i < STD_VIDEO_H264_SCALING_LIST_4X4_NUM_LISTS; i++) + memcpy(vkpps_scaling->ScalingList4x4[i], pps->scaling_matrix4[i], + STD_VIDEO_H264_SCALING_LIST_4X4_NUM_ELEMENTS * sizeof(**pps->scaling_matrix4)); + + for (int i = 0; i < STD_VIDEO_H264_SCALING_LIST_8X8_NUM_LISTS; i++) + memcpy(vkpps_scaling->ScalingList8x8[i], pps->scaling_matrix8[h264_scaling_list8_order[i]], + STD_VIDEO_H264_SCALING_LIST_8X8_NUM_ELEMENTS * sizeof(**pps->scaling_matrix8)); + + *vkpps = (StdVideoH264PictureParameterSet) { + .seq_parameter_set_id = pps->sps_id, + .pic_parameter_set_id = pps->pps_id, + .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, + .pic_init_qs_minus26 = pps->init_qs - 26, + .chroma_qp_index_offset = pps->chroma_qp_index_offset[0], + .second_chroma_qp_index_offset = pps->chroma_qp_index_offset[1], + .flags = (StdVideoH264PpsFlags) { + .transform_8x8_mode_flag = pps->transform_8x8_mode, + .redundant_pic_cnt_present_flag = pps->redundant_pic_cnt_present, + .constrained_intra_pred_flag = pps->constrained_intra_pred, + .deblocking_filter_control_present_flag = pps->deblocking_filter_parameters_present, + .weighted_pred_flag = pps->weighted_pred, + .bottom_field_pic_order_in_frame_present_flag = pps->pic_order_present, + .entropy_coding_mode_flag = pps->cabac, + .pic_scaling_matrix_present_flag = pps->pic_scaling_matrix_present_flag, + }, + .pScalingLists = vkpps_scaling, + }; +} + +static int vk_h264_create_params(AVCodecContext *avctx, AVBufferRef **buf) +{ + VkResult ret; + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + FFVulkanDecodeShared *ctx = (FFVulkanDecodeShared *)dec->shared_ref->data; + FFVulkanFunctions *vk = &ctx->s.vkfn; + const H264Context *h = avctx->priv_data; + + /* SPS */ + StdVideoH264ScalingLists vksps_scaling[MAX_SPS_COUNT]; + StdVideoH264HrdParameters vksps_vui_header[MAX_SPS_COUNT]; + StdVideoH264SequenceParameterSetVui vksps_vui[MAX_SPS_COUNT]; + StdVideoH264SequenceParameterSet vksps[MAX_SPS_COUNT]; + + /* PPS */ + StdVideoH264ScalingLists vkpps_scaling[MAX_PPS_COUNT]; + StdVideoH264PictureParameterSet vkpps[MAX_PPS_COUNT]; + + VkVideoDecodeH264SessionParametersAddInfoKHR h264_params_info = { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_SESSION_PARAMETERS_ADD_INFO_KHR, + .pStdSPSs = vksps, + .stdSPSCount = 0, + .pStdPPSs = vkpps, + .stdPPSCount = 0, + }; + VkVideoDecodeH264SessionParametersCreateInfoKHR h264_params = { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_SESSION_PARAMETERS_CREATE_INFO_KHR, + .pParametersAddInfo = &h264_params_info, + }; + VkVideoSessionParametersCreateInfoKHR session_params_create = { + .sType = VK_STRUCTURE_TYPE_VIDEO_SESSION_PARAMETERS_CREATE_INFO_KHR, + .pNext = &h264_params, + .videoSession = ctx->common.session, + .videoSessionParametersTemplate = NULL, + }; + + AVBufferRef *tmp; + VkVideoSessionParametersKHR *par = av_malloc(sizeof(*par)); + if (!par) + return AVERROR(ENOMEM); + + /* SPS list */ + for (int i = 0; i < FF_ARRAY_ELEMS(h->ps.sps_list); i++) { + if (h->ps.sps_list[i]) { + const SPS *sps_l = (const SPS *)h->ps.sps_list[i]->data; + int idx = h264_params_info.stdSPSCount; + set_sps(sps_l, &vksps_scaling[idx], &vksps_vui_header[idx], &vksps_vui[idx], &vksps[idx]); + h264_params_info.stdSPSCount++; + } + } + + /* PPS list */ + for (int i = 0; i < FF_ARRAY_ELEMS(h->ps.pps_list); i++) { + if (h->ps.pps_list[i]) { + const PPS *pps_l = (const PPS *)h->ps.pps_list[i]->data; + int idx = h264_params_info.stdPPSCount; + set_pps(pps_l, pps_l->sps, &vkpps_scaling[idx], &vkpps[idx]); + h264_params_info.stdPPSCount++; + } + } + + h264_params.maxStdSPSCount = h264_params_info.stdSPSCount; + h264_params.maxStdPPSCount = h264_params_info.stdPPSCount; + + /* Create session parameters */ + ret = vk->CreateVideoSessionParametersKHR(ctx->s.hwctx->act_dev, &session_params_create, + ctx->s.hwctx->alloc, par); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Unable to create Vulkan video session parameters: %s!\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + tmp = av_buffer_create((uint8_t *)par, sizeof(*par), ff_vk_decode_free_params, + ctx, 0); + if (!tmp) { + ff_vk_decode_free_params(ctx, (uint8_t *)par); + return AVERROR(ENOMEM); + } + + av_log(avctx, AV_LOG_DEBUG, "Created frame parameters: %i SPS %i PPS\n", + h264_params_info.stdSPSCount, h264_params_info.stdPPSCount); + + *buf = tmp; + + return 0; +} + +static int vk_h264_start_frame(AVCodecContext *avctx, + av_unused const uint8_t *buffer, + av_unused uint32_t size) +{ + int err; + int dpb_slot_index = 0; + H264Context *h = avctx->priv_data; + H264Picture *pic = h->cur_pic_ptr; + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + H264VulkanDecodePicture *hp = pic->hwaccel_picture_private; + FFVulkanDecodePicture *vp = &hp->vp; + + if (!dec->session_params) { + err = vk_h264_create_params(avctx, &dec->session_params); + if (err < 0) + return err; + } + + /* Fill in main slot */ + dpb_slot_index = 0; + for (unsigned slot = 0; slot < H264_MAX_PICTURE_COUNT; slot++) { + if (pic == &h->DPB[slot]) { + dpb_slot_index = slot; + break; + } + } + + err = vk_h264_fill_pict(avctx, NULL, &vp->ref_slot, &vp->ref, + &hp->vkh264_ref, &hp->h264_ref, pic, 1, + h->DPB[dpb_slot_index].field_picture, + h->DPB[dpb_slot_index].reference, + dpb_slot_index); + if (err < 0) + return err; + + /* Fill in short-term references */ + for (int i = 0; i < h->short_ref_count; i++) { + dpb_slot_index = 0; + for (unsigned slot = 0; slot < H264_MAX_PICTURE_COUNT; slot++) { + if (h->short_ref[i] == &h->DPB[slot]) { + dpb_slot_index = slot; + break; + } + } + err = vk_h264_fill_pict(avctx, &hp->ref_src[i], &vp->ref_slots[i], + &vp->refs[i], &hp->vkh264_refs[i], + &hp->h264_refs[i], h->short_ref[i], 0, + h->DPB[dpb_slot_index].field_picture, + h->DPB[dpb_slot_index].reference, + dpb_slot_index); + if (err < 0) + return err; + } + + /* Fill in long-term refs */ + for (int r = 0, i = h->short_ref_count; i < h->short_ref_count + h->long_ref_count; i++, r++) { + dpb_slot_index = 0; + for (unsigned slot = 0; slot < H264_MAX_PICTURE_COUNT; slot++) { + if (h->long_ref[i] == &h->DPB[slot]) { + dpb_slot_index = slot; + break; + } + } + err = vk_h264_fill_pict(avctx, &hp->ref_src[i], &vp->ref_slots[i], + &vp->refs[i], &hp->vkh264_refs[i], + &hp->h264_refs[i], h->long_ref[r], 0, + h->DPB[dpb_slot_index].field_picture, + h->DPB[dpb_slot_index].reference, + dpb_slot_index); + if (err < 0) + return err; + } + + hp->h264pic = (StdVideoDecodeH264PictureInfo) { + .seq_parameter_set_id = pic->pps->sps_id, + .pic_parameter_set_id = pic->pps->pps_id, + .frame_num = 0, /* Set later */ + .idr_pic_id = 0, /* Set later */ + .PicOrderCnt[0] = pic->field_poc[0], + .PicOrderCnt[1] = pic->field_poc[1], + .flags = (StdVideoDecodeH264PictureInfoFlags) { + .field_pic_flag = FIELD_PICTURE(h), + .is_intra = 1, /* Set later */ + .IdrPicFlag = h->picture_idr, + .bottom_field_flag = h->picture_structure != PICT_FRAME && + h->picture_structure & PICT_BOTTOM_FIELD, + .is_reference = h->nal_ref_idc != 0, + .complementary_field_pair = h->first_field && FIELD_PICTURE(h), + }, + }; + + hp->h264_pic_info = (VkVideoDecodeH264PictureInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_PICTURE_INFO_KHR, + .pStdPictureInfo = &hp->h264pic, + }; + + vp->decode_info = (VkVideoDecodeInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_INFO_KHR, + .pNext = &hp->h264_pic_info, + .flags = 0x0, + .pSetupReferenceSlot = &vp->ref_slot, + .referenceSlotCount = h->short_ref_count + h->long_ref_count, + .pReferenceSlots = vp->ref_slots, + .dstPictureResource = (VkVideoPictureResourceInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_INFO_KHR, + .codedOffset = (VkOffset2D){ 0, 0 }, + .codedExtent = (VkExtent2D){ pic->f->width, pic->f->height }, + .baseArrayLayer = 0, + .imageViewBinding = vp->img_view_out, + }, + }; + + return 0; +} + +static int vk_h264_decode_slice(AVCodecContext *avctx, + const uint8_t *data, + uint32_t size) +{ + const H264Context *h = avctx->priv_data; + const H264SliceContext *sl = &h->slice_ctx[0]; + H264VulkanDecodePicture *hp = h->cur_pic_ptr->hwaccel_picture_private; + FFVulkanDecodePicture *vp = &hp->vp; + + int err = ff_vk_decode_add_slice(avctx, vp, data, size, 1, + &hp->h264_pic_info.sliceCount, + &hp->h264_pic_info.pSliceOffsets); + if (err < 0) + return err; + + hp->h264pic.frame_num = sl->frame_num; + hp->h264pic.idr_pic_id = sl->idr_pic_id; + + /* Frame is only intra of all slices are marked as intra */ + if (sl->slice_type != AV_PICTURE_TYPE_I && sl->slice_type != AV_PICTURE_TYPE_SI) + hp->h264pic.flags.is_intra = 0; + + return 0; +} + +static int vk_h264_end_frame(AVCodecContext *avctx) +{ + const H264Context *h = avctx->priv_data; + H264Picture *pic = h->cur_pic_ptr; + H264VulkanDecodePicture *hp = pic->hwaccel_picture_private; + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + FFVulkanDecodePicture *vp = &hp->vp; + FFVulkanDecodePicture *rvp[H264_MAX_PICTURE_COUNT] = { 0 }; + AVFrame *rav[H264_MAX_PICTURE_COUNT] = { 0 }; + + if (!hp->h264_pic_info.sliceCount) + return 0; + + if (!vp->slices_buf) + return AVERROR(EINVAL); + + if (!dec->session_params) { + int err = vk_h264_create_params(avctx, &dec->session_params); + if (err < 0) + return err; + + hp->h264pic.seq_parameter_set_id = pic->pps->sps_id; + hp->h264pic.pic_parameter_set_id = pic->pps->pps_id; + } + + for (int i = 0; i < vp->decode_info.referenceSlotCount; i++) { + H264Picture *rp = hp->ref_src[i]; + H264VulkanDecodePicture *rhp = rp->hwaccel_picture_private; + + rvp[i] = &rhp->vp; + rav[i] = hp->ref_src[i]->f; + } + + av_log(avctx, AV_LOG_VERBOSE, "Decoding frame, %"SIZE_SPECIFIER" bytes, %i slices\n", + vp->slices_size, hp->h264_pic_info.sliceCount); + + return ff_vk_decode_frame(avctx, pic->f, vp, rav, rvp); +} + +static void vk_h264_free_frame_priv(void *_hwctx, uint8_t *data) +{ + AVHWDeviceContext *hwctx = _hwctx; + H264VulkanDecodePicture *hp = (H264VulkanDecodePicture *)data; + + /* Free frame resources, this also destroys the session parameters. */ + ff_vk_decode_free_frame(hwctx, &hp->vp); + + /* Free frame context */ + av_free(hp); +} + +const FFHWAccel ff_h264_vulkan_hwaccel = { + .p.name = "h264_vulkan", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_H264, + .p.pix_fmt = AV_PIX_FMT_VULKAN, + .start_frame = &vk_h264_start_frame, + .decode_slice = &vk_h264_decode_slice, + .end_frame = &vk_h264_end_frame, + .free_frame_priv = &vk_h264_free_frame_priv, + .frame_priv_data_size = sizeof(H264VulkanDecodePicture), + .init = &ff_vk_decode_init, + .update_thread_context = &ff_vk_update_thread_context, + .decode_params = &ff_vk_params_invalidate, + .flush = &ff_vk_decode_flush, + .uninit = &ff_vk_decode_uninit, + .frame_params = &ff_vk_frame_params, + .priv_data_size = sizeof(FFVulkanDecodeContext), + .caps_internal = HWACCEL_CAP_ASYNC_SAFE | HWACCEL_CAP_THREAD_SAFE, +}; diff --git a/libavcodec/vulkan_hevc.c b/libavcodec/vulkan_hevc.c new file mode 100644 index 00000000000..672694a19d2 --- /dev/null +++ b/libavcodec/vulkan_hevc.c @@ -0,0 +1,973 @@ +/* + * 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 "hevcdec.h" +#include "hevc_data.h" +#include "hevc_ps.h" + +#include "vulkan_decode.h" + +const VkExtensionProperties ff_vk_dec_hevc_ext = { + .extensionName = VK_STD_VULKAN_VIDEO_CODEC_H265_DECODE_EXTENSION_NAME, + .specVersion = VK_STD_VULKAN_VIDEO_CODEC_H265_DECODE_SPEC_VERSION, +}; + +typedef struct HEVCHeaderSPS { + StdVideoH265ScalingLists scaling; + StdVideoH265HrdParameters vui_header; + StdVideoH265SequenceParameterSetVui vui; + StdVideoH265ProfileTierLevel ptl; + StdVideoH265DecPicBufMgr dpbm; + StdVideoH265PredictorPaletteEntries pal; + StdVideoH265SubLayerHrdParameters nal_hdr[HEVC_MAX_SUB_LAYERS]; + StdVideoH265SubLayerHrdParameters vcl_hdr[HEVC_MAX_SUB_LAYERS]; + StdVideoH265ShortTermRefPicSet str[HEVC_MAX_SHORT_TERM_REF_PIC_SETS]; + StdVideoH265LongTermRefPicsSps ltr; +} HEVCHeaderSPS; + +typedef struct HEVCHeaderPPS { + StdVideoH265ScalingLists scaling; + StdVideoH265PredictorPaletteEntries pal; +} HEVCHeaderPPS; + +typedef struct HEVCHeaderVPSSet { + StdVideoH265SubLayerHrdParameters nal_hdr[HEVC_MAX_SUB_LAYERS]; + StdVideoH265SubLayerHrdParameters vcl_hdr[HEVC_MAX_SUB_LAYERS]; +} HEVCHeaderVPSSet; + +typedef struct HEVCHeaderVPS { + StdVideoH265ProfileTierLevel ptl; + StdVideoH265DecPicBufMgr dpbm; + StdVideoH265HrdParameters hdr[HEVC_MAX_LAYER_SETS]; + HEVCHeaderVPSSet *sls; +} HEVCHeaderVPS; + +typedef struct HEVCHeaderSet { + StdVideoH265SequenceParameterSet sps[HEVC_MAX_SPS_COUNT]; + HEVCHeaderSPS hsps[HEVC_MAX_SPS_COUNT]; + + StdVideoH265PictureParameterSet pps[HEVC_MAX_PPS_COUNT]; + HEVCHeaderPPS hpps[HEVC_MAX_PPS_COUNT]; + + StdVideoH265VideoParameterSet vps[HEVC_MAX_PPS_COUNT]; + HEVCHeaderVPS *hvps; +} HEVCHeaderSet; + +static int get_data_set_buf(FFVulkanDecodeContext *s, AVBufferRef **data_buf, + int nb_vps, AVBufferRef * const vps_list[HEVC_MAX_VPS_COUNT]) +{ + uint8_t *data_ptr; + HEVCHeaderSet *hdr; + + size_t base_size = sizeof(StdVideoH265SequenceParameterSet)*HEVC_MAX_SPS_COUNT + + sizeof(HEVCHeaderSPS)*HEVC_MAX_SPS_COUNT + + sizeof(StdVideoH265PictureParameterSet)*HEVC_MAX_PPS_COUNT + + sizeof(HEVCHeaderPPS)*HEVC_MAX_PPS_COUNT + + sizeof(StdVideoH265VideoParameterSet)*HEVC_MAX_VPS_COUNT + + sizeof(HEVCHeaderVPS *); + + size_t vps_size = sizeof(StdVideoH265ProfileTierLevel) + + sizeof(StdVideoH265DecPicBufMgr) + + sizeof(StdVideoH265HrdParameters)*HEVC_MAX_LAYER_SETS + + sizeof(HEVCHeaderVPSSet *); + + size_t buf_size = base_size + vps_size*nb_vps; + + for (int i = 0; i < nb_vps; i++) { + const HEVCVPS *vps = (const HEVCVPS *)vps_list[i]->data; + buf_size += sizeof(HEVCHeaderVPSSet)*vps->vps_num_hrd_parameters; + } + + if (buf_size > s->tmp_pool_ele_size) { + av_buffer_pool_uninit(&s->tmp_pool); + s->tmp_pool_ele_size = 0; + s->tmp_pool = av_buffer_pool_init(buf_size, NULL); + if (!s->tmp_pool) + return AVERROR(ENOMEM); + s->tmp_pool_ele_size = buf_size; + } + + *data_buf = av_buffer_pool_get(s->tmp_pool); + if (!(*data_buf)) + return AVERROR(ENOMEM); + + /* Setup pointers */ + data_ptr = (*data_buf)->data; + hdr = (HEVCHeaderSet *)data_ptr; + hdr->hvps = (HEVCHeaderVPS *)(data_ptr + base_size); + data_ptr += base_size + vps_size*nb_vps; + for (int i = 0; i < nb_vps; i++) { + const HEVCVPS *vps = (const HEVCVPS *)vps_list[i]->data; + hdr->hvps[i].sls = (HEVCHeaderVPSSet *)data_ptr; + data_ptr += sizeof(HEVCHeaderVPSSet)*vps->vps_num_hrd_parameters; + } + + return 0; +} + +typedef struct HEVCVulkanDecodePicture { + FFVulkanDecodePicture vp; + + /* Current picture */ + StdVideoDecodeH265ReferenceInfo h265_ref; + VkVideoDecodeH265DpbSlotInfoKHR vkh265_ref; + + /* Picture refs */ + HEVCFrame *ref_src [HEVC_MAX_REFS]; + StdVideoDecodeH265ReferenceInfo h265_refs [HEVC_MAX_REFS]; + VkVideoDecodeH265DpbSlotInfoKHR vkh265_refs[HEVC_MAX_REFS]; + + /* Current picture (contd.) */ + StdVideoDecodeH265PictureInfo h265pic; + VkVideoDecodeH265PictureInfoKHR h265_pic_info; +} HEVCVulkanDecodePicture; + +static int vk_hevc_fill_pict(AVCodecContext *avctx, HEVCFrame **ref_src, + VkVideoReferenceSlotInfoKHR *ref_slot, /* Main structure */ + VkVideoPictureResourceInfoKHR *ref, /* Goes in ^ */ + VkVideoDecodeH265DpbSlotInfoKHR *vkh265_ref, /* Goes in ^ */ + StdVideoDecodeH265ReferenceInfo *h265_ref, /* Goes in ^ */ + HEVCFrame *pic, int is_current, int pic_id) +{ + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + HEVCVulkanDecodePicture *hp = pic->hwaccel_picture_private; + FFVulkanDecodePicture *vkpic = &hp->vp; + + int err = ff_vk_decode_prepare_frame(dec, pic->frame, vkpic, is_current, + dec->dedicated_dpb); + if (err < 0) + return err; + + *h265_ref = (StdVideoDecodeH265ReferenceInfo) { + .flags = (StdVideoDecodeH265ReferenceInfoFlags) { + .used_for_long_term_reference = pic->flags & HEVC_FRAME_FLAG_LONG_REF, + .unused_for_reference = 0, + }, + .PicOrderCntVal = pic->poc, + }; + + *vkh265_ref = (VkVideoDecodeH265DpbSlotInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_DPB_SLOT_INFO_KHR, + .pStdReferenceInfo = h265_ref, + }; + + *ref = (VkVideoPictureResourceInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_INFO_KHR, + .codedOffset = (VkOffset2D){ 0, 0 }, + .codedExtent = (VkExtent2D){ pic->frame->width, pic->frame->height }, + .baseArrayLayer = dec->layered_dpb ? pic_id : 0, + .imageViewBinding = vkpic->img_view_ref, + }; + + *ref_slot = (VkVideoReferenceSlotInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_REFERENCE_SLOT_INFO_KHR, + .pNext = vkh265_ref, + .slotIndex = pic_id, + .pPictureResource = ref, + }; + + if (ref_src) + *ref_src = pic; + + return 0; +} + +static StdVideoH265LevelIdc convert_to_vk_level_idc(int level_idc) +{ + switch (level_idc) { + case 10: return STD_VIDEO_H265_LEVEL_IDC_1_0; + case 20: return STD_VIDEO_H265_LEVEL_IDC_2_0; + case 21: return STD_VIDEO_H265_LEVEL_IDC_2_1; + case 30: return STD_VIDEO_H265_LEVEL_IDC_3_0; + case 31: return STD_VIDEO_H265_LEVEL_IDC_3_1; + case 40: return STD_VIDEO_H265_LEVEL_IDC_4_0; + case 41: return STD_VIDEO_H265_LEVEL_IDC_4_1; + case 50: return STD_VIDEO_H265_LEVEL_IDC_5_0; + case 51: return STD_VIDEO_H265_LEVEL_IDC_5_1; + case 60: return STD_VIDEO_H265_LEVEL_IDC_6_0; + case 61: return STD_VIDEO_H265_LEVEL_IDC_6_1; + default: + case 62: return STD_VIDEO_H265_LEVEL_IDC_6_2; + } +} + +static void copy_scaling_list(const ScalingList *sl, StdVideoH265ScalingLists *vksl) +{ + for (int i = 0; i < STD_VIDEO_H265_SCALING_LIST_4X4_NUM_LISTS; i++) { + for (int j = 0; j < STD_VIDEO_H265_SCALING_LIST_4X4_NUM_ELEMENTS; j++) { + uint8_t pos = 4 * ff_hevc_diag_scan4x4_y[j] + ff_hevc_diag_scan4x4_x[j]; + vksl->ScalingList4x4[i][j] = sl->sl[0][i][pos]; + } + } + + for (int i = 0; i < STD_VIDEO_H265_SCALING_LIST_8X8_NUM_LISTS; i++) { + for (int j = 0; j < STD_VIDEO_H265_SCALING_LIST_8X8_NUM_ELEMENTS; j++) { + uint8_t pos = 8 * ff_hevc_diag_scan8x8_y[j] + ff_hevc_diag_scan8x8_x[j]; + vksl->ScalingList8x8[i][j] = sl->sl[1][i][pos]; + } + } + + for (int i = 0; i < STD_VIDEO_H265_SCALING_LIST_16X16_NUM_LISTS; i++) { + for (int j = 0; j < STD_VIDEO_H265_SCALING_LIST_16X16_NUM_ELEMENTS; j++) { + uint8_t pos = 8 * ff_hevc_diag_scan8x8_y[j] + ff_hevc_diag_scan8x8_x[j]; + vksl->ScalingList16x16[i][j] = sl->sl[2][i][pos]; + } + } + + for (int i = 0; i < STD_VIDEO_H265_SCALING_LIST_32X32_NUM_LISTS; i++) { + for (int j = 0; j < STD_VIDEO_H265_SCALING_LIST_32X32_NUM_ELEMENTS; j++) { + uint8_t pos = 8 * ff_hevc_diag_scan8x8_y[j] + ff_hevc_diag_scan8x8_x[j]; + vksl->ScalingList32x32[i][j] = sl->sl[3][i * 3][pos]; + } + } + + memcpy(vksl->ScalingListDCCoef16x16, sl->sl_dc[0], + STD_VIDEO_H265_SCALING_LIST_16X16_NUM_LISTS * sizeof(*vksl->ScalingListDCCoef16x16)); + + for (int i = 0; i < STD_VIDEO_H265_SCALING_LIST_32X32_NUM_LISTS; i++) + vksl->ScalingListDCCoef32x32[i] = sl->sl_dc[1][i * 3]; +} + +static void set_sps(const HEVCSPS *sps, int sps_idx, + StdVideoH265ScalingLists *vksps_scaling, + StdVideoH265HrdParameters *vksps_vui_header, + StdVideoH265SequenceParameterSetVui *vksps_vui, + StdVideoH265SequenceParameterSet *vksps, + StdVideoH265SubLayerHrdParameters *slhdrnal, + StdVideoH265SubLayerHrdParameters *slhdrvcl, + StdVideoH265ProfileTierLevel *ptl, + StdVideoH265DecPicBufMgr *dpbm, + StdVideoH265PredictorPaletteEntries *pal, + StdVideoH265ShortTermRefPicSet *str, + StdVideoH265LongTermRefPicsSps *ltr) +{ + copy_scaling_list(&sps->scaling_list, vksps_scaling); + + *vksps_vui_header = (StdVideoH265HrdParameters) { + .flags = (StdVideoH265HrdFlags) { + .nal_hrd_parameters_present_flag = sps->hdr.flags.nal_hrd_parameters_present_flag, + .vcl_hrd_parameters_present_flag = sps->hdr.flags.vcl_hrd_parameters_present_flag, + .sub_pic_hrd_params_present_flag = sps->hdr.flags.sub_pic_hrd_params_present_flag, + .sub_pic_cpb_params_in_pic_timing_sei_flag = sps->hdr.flags.sub_pic_cpb_params_in_pic_timing_sei_flag, + .fixed_pic_rate_general_flag = sps->hdr.flags.fixed_pic_rate_general_flag, + .fixed_pic_rate_within_cvs_flag = sps->hdr.flags.fixed_pic_rate_within_cvs_flag, + .low_delay_hrd_flag = sps->hdr.flags.low_delay_hrd_flag, + }, + .tick_divisor_minus2 = sps->hdr.tick_divisor_minus2, + .du_cpb_removal_delay_increment_length_minus1 = sps->hdr.du_cpb_removal_delay_increment_length_minus1, + .dpb_output_delay_du_length_minus1 = sps->hdr.dpb_output_delay_du_length_minus1, + .bit_rate_scale = sps->hdr.bit_rate_scale, + .cpb_size_scale = sps->hdr.cpb_size_scale, + .cpb_size_du_scale = sps->hdr.cpb_size_du_scale, + .initial_cpb_removal_delay_length_minus1 = sps->hdr.initial_cpb_removal_delay_length_minus1, + .au_cpb_removal_delay_length_minus1 = sps->hdr.au_cpb_removal_delay_length_minus1, + .dpb_output_delay_length_minus1 = sps->hdr.dpb_output_delay_length_minus1, + /* Reserved - 3*16 bits */ + .pSubLayerHrdParametersNal = slhdrnal, + .pSubLayerHrdParametersVcl = slhdrvcl, + }; + + memcpy(vksps_vui_header->cpb_cnt_minus1, sps->hdr.cpb_cnt_minus1, + STD_VIDEO_H265_SUBLAYERS_LIST_SIZE*sizeof(*vksps_vui_header->cpb_cnt_minus1)); + memcpy(vksps_vui_header->elemental_duration_in_tc_minus1, sps->hdr.elemental_duration_in_tc_minus1, + STD_VIDEO_H265_SUBLAYERS_LIST_SIZE*sizeof(*vksps_vui_header->elemental_duration_in_tc_minus1)); + + memcpy(slhdrnal, sps->hdr.nal_params, HEVC_MAX_SUB_LAYERS*sizeof(*slhdrnal)); + memcpy(slhdrvcl, sps->hdr.vcl_params, HEVC_MAX_SUB_LAYERS*sizeof(*slhdrvcl)); + + *vksps_vui = (StdVideoH265SequenceParameterSetVui) { + .flags = (StdVideoH265SpsVuiFlags) { + .aspect_ratio_info_present_flag = sps->vui.common.aspect_ratio_info_present_flag, + .overscan_info_present_flag = sps->vui.common.overscan_info_present_flag, + .overscan_appropriate_flag = sps->vui.common.overscan_appropriate_flag, + .video_signal_type_present_flag = sps->vui.common.video_signal_type_present_flag, + .video_full_range_flag = sps->vui.common.video_full_range_flag, + .colour_description_present_flag = sps->vui.common.colour_description_present_flag, + .chroma_loc_info_present_flag = sps->vui.common.chroma_loc_info_present_flag, + .neutral_chroma_indication_flag = sps->vui.neutra_chroma_indication_flag, + .field_seq_flag = sps->vui.field_seq_flag, + .frame_field_info_present_flag = sps->vui.frame_field_info_present_flag, + .default_display_window_flag = sps->vui.default_display_window_flag, + .vui_timing_info_present_flag = sps->vui.vui_timing_info_present_flag, + .vui_poc_proportional_to_timing_flag = sps->vui.vui_poc_proportional_to_timing_flag, + .vui_hrd_parameters_present_flag = sps->vui.vui_hrd_parameters_present_flag, + .bitstream_restriction_flag = sps->vui.bitstream_restriction_flag, + .tiles_fixed_structure_flag = sps->vui.tiles_fixed_structure_flag, + .motion_vectors_over_pic_boundaries_flag = sps->vui.motion_vectors_over_pic_boundaries_flag, + .restricted_ref_pic_lists_flag = sps->vui.restricted_ref_pic_lists_flag, + }, + .aspect_ratio_idc = sps->vui.common.aspect_ratio_idc, + .sar_width = sps->vui.common.sar.num, + .sar_height = sps->vui.common.sar.den, + .video_format = sps->vui.common.video_format, + .colour_primaries = sps->vui.common.colour_primaries, + .transfer_characteristics = sps->vui.common.transfer_characteristics, + .matrix_coeffs = sps->vui.common.matrix_coeffs, + .chroma_sample_loc_type_top_field = sps->vui.common.chroma_sample_loc_type_top_field, + .chroma_sample_loc_type_bottom_field = sps->vui.common.chroma_sample_loc_type_bottom_field, + /* Reserved */ + /* Reserved */ + .def_disp_win_left_offset = sps->vui.def_disp_win.left_offset, + .def_disp_win_right_offset = sps->vui.def_disp_win.right_offset, + .def_disp_win_top_offset = sps->vui.def_disp_win.top_offset, + .def_disp_win_bottom_offset = sps->vui.def_disp_win.bottom_offset, + .vui_num_units_in_tick = sps->vui.vui_num_units_in_tick, + .vui_time_scale = sps->vui.vui_time_scale, + .vui_num_ticks_poc_diff_one_minus1 = sps->vui.vui_num_ticks_poc_diff_one_minus1, + .min_spatial_segmentation_idc = sps->vui.min_spatial_segmentation_idc, + .max_bytes_per_pic_denom = sps->vui.max_bytes_per_pic_denom, + .max_bits_per_min_cu_denom = sps->vui.max_bits_per_min_cu_denom, + .log2_max_mv_length_horizontal = sps->vui.log2_max_mv_length_horizontal, + .log2_max_mv_length_vertical = sps->vui.log2_max_mv_length_vertical, + .pHrdParameters = vksps_vui_header, + }; + + *ptl = (StdVideoH265ProfileTierLevel) { + .flags = (StdVideoH265ProfileTierLevelFlags) { + .general_tier_flag = sps->ptl.general_ptl.tier_flag, + .general_progressive_source_flag = sps->ptl.general_ptl.progressive_source_flag, + .general_interlaced_source_flag = sps->ptl.general_ptl.interlaced_source_flag, + .general_non_packed_constraint_flag = sps->ptl.general_ptl.non_packed_constraint_flag, + .general_frame_only_constraint_flag = sps->ptl.general_ptl.frame_only_constraint_flag, + }, + .general_profile_idc = sps->ptl.general_ptl.profile_idc, + .general_level_idc = convert_to_vk_level_idc(sps->ptl.general_ptl.level_idc), + }; + + for (int i = 0; i < sps->max_sub_layers; i++) { + dpbm->max_latency_increase_plus1[i] = sps->temporal_layer[i].max_latency_increase + 1; + dpbm->max_dec_pic_buffering_minus1[i] = sps->temporal_layer[i].max_dec_pic_buffering - 1; + dpbm->max_num_reorder_pics[i] = sps->temporal_layer[i].num_reorder_pics; + } + + for (int i = 0; i < (sps->chroma_format_idc ? 3 : 1); i++) + for (int j = 0; j < sps->sps_num_palette_predictor_initializers; j++) + pal->PredictorPaletteEntries[i][j] = sps->sps_palette_predictor_initializer[i][j]; + + for (int i = 0; i < sps->nb_st_rps; i++) { + str[i] = (StdVideoH265ShortTermRefPicSet) { + .flags = (StdVideoH265ShortTermRefPicSetFlags) { + .inter_ref_pic_set_prediction_flag = sps->st_rps[i].rps_predict, + .delta_rps_sign = sps->st_rps[i].delta_rps_sign, + }, + .delta_idx_minus1 = sps->st_rps[i].delta_idx - 1, + .use_delta_flag = sps->st_rps[i].use_delta_flag, + .abs_delta_rps_minus1 = sps->st_rps[i].abs_delta_rps - 1, + .used_by_curr_pic_flag = 0x0, + .used_by_curr_pic_s0_flag = 0x0, + .used_by_curr_pic_s1_flag = 0x0, + /* Reserved */ + /* Reserved */ + /* Reserved */ + .num_negative_pics = sps->st_rps[i].num_negative_pics, + .num_positive_pics = sps->st_rps[i].num_delta_pocs - sps->st_rps[i].num_negative_pics, + }; + + /* NOTE: This is the predicted, and *reordered* version. + * Probably incorrect, but the spec doesn't say which version to use. */ + for (int j = 0; j < sps->st_rps[i].num_delta_pocs; j++) + str[i].used_by_curr_pic_flag |= sps->st_rps[i].used[j] << j; + + for (int j = 0; j < str[i].num_negative_pics; j++) { + str[i].delta_poc_s0_minus1[j] = sps->st_rps[i].delta_poc_s0[j] - 1; + str[i].used_by_curr_pic_s0_flag |= sps->st_rps[i].used[j] << j; + } + + for (int j = 0; j < str[i].num_positive_pics; j++) { + str[i].delta_poc_s1_minus1[j] = sps->st_rps[i].delta_poc_s1[j] - 1; + str[i].used_by_curr_pic_s0_flag |= sps->st_rps[i].used[str[i].num_negative_pics + j] << j; + } + } + + *ltr = (StdVideoH265LongTermRefPicsSps) { + .used_by_curr_pic_lt_sps_flag = 0x0, + }; + + for (int i = 0; i < sps->num_long_term_ref_pics_sps; i++) { + ltr->used_by_curr_pic_lt_sps_flag |= sps->used_by_curr_pic_lt_sps_flag[i] << i; + ltr->lt_ref_pic_poc_lsb_sps[i] = sps->lt_ref_pic_poc_lsb_sps[i]; + } + + *vksps = (StdVideoH265SequenceParameterSet) { + .flags = (StdVideoH265SpsFlags) { + .sps_temporal_id_nesting_flag = sps->temporal_id_nesting_flag, + .separate_colour_plane_flag = sps->separate_colour_plane_flag, + .conformance_window_flag = sps->conformance_window_flag, + .sps_sub_layer_ordering_info_present_flag = sps->sublayer_ordering_info_flag, + .scaling_list_enabled_flag = sps->scaling_list_enable_flag, + .sps_scaling_list_data_present_flag = sps->scaling_list_enable_flag, + .amp_enabled_flag = sps->amp_enabled_flag, + .sample_adaptive_offset_enabled_flag = sps->sao_enabled, + .pcm_enabled_flag = sps->pcm_enabled_flag, + .pcm_loop_filter_disabled_flag = sps->pcm.loop_filter_disable_flag, + .long_term_ref_pics_present_flag = sps->long_term_ref_pics_present_flag, + .sps_temporal_mvp_enabled_flag = sps->sps_temporal_mvp_enabled_flag, + .strong_intra_smoothing_enabled_flag = sps->sps_strong_intra_smoothing_enable_flag, + .vui_parameters_present_flag = sps->vui_present, + .sps_extension_present_flag = sps->sps_extension_present_flag, + .sps_range_extension_flag = sps->sps_range_extension_flag, + .transform_skip_rotation_enabled_flag = sps->transform_skip_rotation_enabled_flag, + .transform_skip_context_enabled_flag = sps->transform_skip_context_enabled_flag, + .implicit_rdpcm_enabled_flag = sps->implicit_rdpcm_enabled_flag, + .explicit_rdpcm_enabled_flag = sps->explicit_rdpcm_enabled_flag, + .extended_precision_processing_flag = sps->extended_precision_processing_flag, + .intra_smoothing_disabled_flag = sps->intra_smoothing_disabled_flag, + .high_precision_offsets_enabled_flag = sps->high_precision_offsets_enabled_flag, + .persistent_rice_adaptation_enabled_flag = sps->persistent_rice_adaptation_enabled_flag, + .cabac_bypass_alignment_enabled_flag = sps->cabac_bypass_alignment_enabled_flag, + .sps_scc_extension_flag = sps->sps_scc_extension_flag, + .sps_curr_pic_ref_enabled_flag = sps->sps_curr_pic_ref_enabled_flag, + .palette_mode_enabled_flag = sps->palette_mode_enabled_flag, + .sps_palette_predictor_initializers_present_flag = sps->sps_palette_predictor_initializers_present_flag, + .intra_boundary_filtering_disabled_flag = sps->intra_boundary_filtering_disabled_flag, + }, + .chroma_format_idc = sps->chroma_format_idc, + .pic_width_in_luma_samples = sps->width, + .pic_height_in_luma_samples = sps->height, + .sps_video_parameter_set_id = sps->vps_id, + .sps_max_sub_layers_minus1 = sps->max_sub_layers - 1, + .sps_seq_parameter_set_id = sps_idx, + .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, + .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_diff_max_min_transform_block_size, + .max_transform_hierarchy_depth_inter = sps->max_transform_hierarchy_depth_inter, + .max_transform_hierarchy_depth_intra = sps->max_transform_hierarchy_depth_intra, + .num_short_term_ref_pic_sets = sps->nb_st_rps, + .num_long_term_ref_pics_sps = sps->num_long_term_ref_pics_sps, + .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, + /* Reserved */ + /* Reserved */ + .palette_max_size = sps->palette_max_size, + .delta_palette_max_predictor_size = sps->delta_palette_max_predictor_size, + .motion_vector_resolution_control_idc = sps->motion_vector_resolution_control_idc, + .sps_num_palette_predictor_initializers_minus1 = sps->sps_num_palette_predictor_initializers - 1, + .conf_win_left_offset = sps->pic_conf_win.left_offset, + .conf_win_right_offset = sps->pic_conf_win.right_offset, + .conf_win_top_offset = sps->pic_conf_win.top_offset, + .conf_win_bottom_offset = sps->pic_conf_win.bottom_offset, + .pProfileTierLevel = ptl, + .pDecPicBufMgr = dpbm, + .pScalingLists = vksps_scaling, + .pShortTermRefPicSet = str, + .pLongTermRefPicsSps = ltr, + .pSequenceParameterSetVui = vksps_vui, + .pPredictorPaletteEntries = pal, + }; +} + +static void set_pps(const HEVCPPS *pps, const HEVCSPS *sps, + StdVideoH265ScalingLists *vkpps_scaling, + StdVideoH265PictureParameterSet *vkpps, + StdVideoH265PredictorPaletteEntries *pal) +{ + copy_scaling_list(&pps->scaling_list, vkpps_scaling); + + *vkpps = (StdVideoH265PictureParameterSet) { + .flags = (StdVideoH265PpsFlags) { + .dependent_slice_segments_enabled_flag = pps->dependent_slice_segments_enabled_flag, + .output_flag_present_flag = pps->output_flag_present_flag, + .sign_data_hiding_enabled_flag = pps->sign_data_hiding_flag, + .cabac_init_present_flag = pps->cabac_init_present_flag, + .constrained_intra_pred_flag = pps->constrained_intra_pred_flag, + .transform_skip_enabled_flag = pps->transform_skip_enabled_flag, + .cu_qp_delta_enabled_flag = pps->cu_qp_delta_enabled_flag, + .pps_slice_chroma_qp_offsets_present_flag = pps->pic_slice_level_chroma_qp_offsets_present_flag, + .weighted_pred_flag = pps->weighted_pred_flag, + .weighted_bipred_flag = pps->weighted_bipred_flag, + .transquant_bypass_enabled_flag = pps->transquant_bypass_enable_flag, + .tiles_enabled_flag = pps->tiles_enabled_flag, + .entropy_coding_sync_enabled_flag = pps->entropy_coding_sync_enabled_flag, + .uniform_spacing_flag = pps->uniform_spacing_flag, + .loop_filter_across_tiles_enabled_flag = pps->loop_filter_across_tiles_enabled_flag, + .pps_loop_filter_across_slices_enabled_flag = pps->seq_loop_filter_across_slices_enabled_flag, + .deblocking_filter_control_present_flag = pps->deblocking_filter_control_present_flag, + .deblocking_filter_override_enabled_flag = pps->deblocking_filter_override_enabled_flag, + .pps_deblocking_filter_disabled_flag = pps->disable_dbf, + .pps_scaling_list_data_present_flag = pps->scaling_list_data_present_flag, + .lists_modification_present_flag = pps->lists_modification_present_flag, + .slice_segment_header_extension_present_flag = pps->slice_header_extension_present_flag, + .pps_extension_present_flag = pps->pps_extension_present_flag, + .cross_component_prediction_enabled_flag = pps->cross_component_prediction_enabled_flag, + .chroma_qp_offset_list_enabled_flag = pps->chroma_qp_offset_list_enabled_flag, + .pps_curr_pic_ref_enabled_flag = pps->pps_curr_pic_ref_enabled_flag, + .residual_adaptive_colour_transform_enabled_flag = pps->residual_adaptive_colour_transform_enabled_flag, + .pps_slice_act_qp_offsets_present_flag = pps->pps_slice_act_qp_offsets_present_flag, + .pps_palette_predictor_initializers_present_flag = pps->pps_palette_predictor_initializers_present_flag, + .monochrome_palette_flag = pps->monochrome_palette_flag, + .pps_range_extension_flag = pps->pps_range_extensions_flag, + }, + .pps_pic_parameter_set_id = pps->pps_id, + .pps_seq_parameter_set_id = pps->sps_id, + .sps_video_parameter_set_id = sps->vps_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 >> 1, + .pps_tc_offset_div2 = pps->tc_offset >> 1, + .log2_parallel_merge_level_minus2 = pps->log2_parallel_merge_level - 2, + .log2_max_transform_skip_block_size_minus2 = pps->log2_max_transform_skip_block_size - 2, + .diff_cu_chroma_qp_offset_depth = pps->diff_cu_chroma_qp_offset_depth, + .chroma_qp_offset_list_len_minus1 = pps->chroma_qp_offset_list_len_minus1, + .log2_sao_offset_scale_luma = pps->log2_sao_offset_scale_luma, + .log2_sao_offset_scale_chroma = pps->log2_sao_offset_scale_chroma, + .pps_act_y_qp_offset_plus5 = pps->pps_act_y_qp_offset + 5, + .pps_act_cb_qp_offset_plus5 = pps->pps_act_cb_qp_offset + 5, + .pps_act_cr_qp_offset_plus3 = pps->pps_act_cr_qp_offset + 3, + .pps_num_palette_predictor_initializers = pps->pps_num_palette_predictor_initializers, + .luma_bit_depth_entry_minus8 = pps->luma_bit_depth_entry - 8, + .chroma_bit_depth_entry_minus8 = pps->chroma_bit_depth_entry - 8, + .num_tile_columns_minus1 = pps->num_tile_columns - 1, + .num_tile_rows_minus1 = pps->num_tile_rows - 1, + .pScalingLists = vkpps_scaling, + .pPredictorPaletteEntries = pal, + }; + + for (int i = 0; i < (pps->monochrome_palette_flag ? 1 : 3); i++) { + for (int j = 0; j < pps->pps_num_palette_predictor_initializers; j++) + pal->PredictorPaletteEntries[i][j] = pps->pps_palette_predictor_initializer[i][j]; + } + + for (int i = 0; i < pps->num_tile_columns - 1; i++) + vkpps->column_width_minus1[i] = pps->column_width[i] - 1; + + for (int i = 0; i < pps->num_tile_rows - 1; i++) + vkpps->row_height_minus1[i] = pps->row_height[i] - 1; + + for (int i = 0; i <= pps->chroma_qp_offset_list_len_minus1; i++) { + vkpps->cb_qp_offset_list[i] = pps->cb_qp_offset_list[i]; + vkpps->cr_qp_offset_list[i] = pps->cr_qp_offset_list[i]; + } +} + +static void set_vps(const HEVCVPS *vps, + StdVideoH265VideoParameterSet *vkvps, + StdVideoH265ProfileTierLevel *ptl, + StdVideoH265DecPicBufMgr *dpbm, + StdVideoH265HrdParameters *sls_hdr, + HEVCHeaderVPSSet sls[]) +{ + for (int i = 0; i < vps->vps_num_hrd_parameters; i++) { + const HEVCHdrParams *src = &vps->hdr[i]; + + sls_hdr[i] = (StdVideoH265HrdParameters) { + .flags = (StdVideoH265HrdFlags) { + .nal_hrd_parameters_present_flag = src->flags.nal_hrd_parameters_present_flag, + .vcl_hrd_parameters_present_flag = src->flags.vcl_hrd_parameters_present_flag, + .sub_pic_hrd_params_present_flag = src->flags.sub_pic_hrd_params_present_flag, + .sub_pic_cpb_params_in_pic_timing_sei_flag = src->flags.sub_pic_cpb_params_in_pic_timing_sei_flag, + .fixed_pic_rate_general_flag = src->flags.fixed_pic_rate_general_flag, + .fixed_pic_rate_within_cvs_flag = src->flags.fixed_pic_rate_within_cvs_flag, + .low_delay_hrd_flag = src->flags.low_delay_hrd_flag, + }, + .tick_divisor_minus2 = src->tick_divisor_minus2, + .du_cpb_removal_delay_increment_length_minus1 = src->du_cpb_removal_delay_increment_length_minus1, + .dpb_output_delay_du_length_minus1 = src->dpb_output_delay_du_length_minus1, + .bit_rate_scale = src->bit_rate_scale, + .cpb_size_scale = src->cpb_size_scale, + .cpb_size_du_scale = src->cpb_size_du_scale, + .initial_cpb_removal_delay_length_minus1 = src->initial_cpb_removal_delay_length_minus1, + .au_cpb_removal_delay_length_minus1 = src->au_cpb_removal_delay_length_minus1, + .dpb_output_delay_length_minus1 = src->dpb_output_delay_length_minus1, + /* Reserved - 3*16 bits */ + .pSubLayerHrdParametersNal = sls[i].nal_hdr, + .pSubLayerHrdParametersVcl = sls[i].vcl_hdr, + }; + + memcpy(sls_hdr[i].cpb_cnt_minus1, src->cpb_cnt_minus1, + STD_VIDEO_H265_SUBLAYERS_LIST_SIZE*sizeof(*sls_hdr[i].cpb_cnt_minus1)); + memcpy(sls_hdr[i].elemental_duration_in_tc_minus1, src->elemental_duration_in_tc_minus1, + STD_VIDEO_H265_SUBLAYERS_LIST_SIZE*sizeof(*sls_hdr[i].elemental_duration_in_tc_minus1)); + + memcpy(sls[i].nal_hdr, src->nal_params, HEVC_MAX_SUB_LAYERS*sizeof(*sls[i].nal_hdr)); + memcpy(sls[i].vcl_hdr, src->vcl_params, HEVC_MAX_SUB_LAYERS*sizeof(*sls[i].vcl_hdr)); + } + + *ptl = (StdVideoH265ProfileTierLevel) { + .flags = (StdVideoH265ProfileTierLevelFlags) { + .general_tier_flag = vps->ptl.general_ptl.tier_flag, + .general_progressive_source_flag = vps->ptl.general_ptl.progressive_source_flag, + .general_interlaced_source_flag = vps->ptl.general_ptl.interlaced_source_flag, + .general_non_packed_constraint_flag = vps->ptl.general_ptl.non_packed_constraint_flag, + .general_frame_only_constraint_flag = vps->ptl.general_ptl.frame_only_constraint_flag, + }, + .general_profile_idc = vps->ptl.general_ptl.profile_idc, + .general_level_idc = convert_to_vk_level_idc(vps->ptl.general_ptl.level_idc), + }; + + for (int i = 0; i < vps->vps_max_sub_layers; i++) { + dpbm->max_latency_increase_plus1[i] = vps->vps_max_latency_increase[i] + 1; + dpbm->max_dec_pic_buffering_minus1[i] = vps->vps_max_dec_pic_buffering[i] - 1; + dpbm->max_num_reorder_pics[i] = vps->vps_num_reorder_pics[i]; + } + + *vkvps = (StdVideoH265VideoParameterSet) { + .flags = (StdVideoH265VpsFlags) { + .vps_temporal_id_nesting_flag = vps->vps_temporal_id_nesting_flag, + .vps_sub_layer_ordering_info_present_flag = vps->vps_sub_layer_ordering_info_present_flag, + .vps_timing_info_present_flag = vps->vps_timing_info_present_flag, + .vps_poc_proportional_to_timing_flag = vps->vps_poc_proportional_to_timing_flag, + }, + .vps_video_parameter_set_id = vps->vps_id, + .vps_max_sub_layers_minus1 = vps->vps_max_sub_layers - 1, + /* Reserved */ + /* Reserved */ + .vps_num_units_in_tick = vps->vps_num_units_in_tick, + .vps_time_scale = vps->vps_time_scale, + .vps_num_ticks_poc_diff_one_minus1 = vps->vps_num_ticks_poc_diff_one - 1, + /* Reserved */ + .pDecPicBufMgr = dpbm, + .pHrdParameters = sls_hdr, + .pProfileTierLevel = ptl, + }; +} + +static int vk_hevc_create_params(AVCodecContext *avctx, AVBufferRef **buf) +{ + int err; + VkResult ret; + const HEVCContext *h = avctx->priv_data; + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + FFVulkanDecodeShared *ctx = (FFVulkanDecodeShared *)dec->shared_ref->data; + FFVulkanFunctions *vk = &ctx->s.vkfn; + + VkVideoDecodeH265SessionParametersAddInfoKHR h265_params_info = { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_SESSION_PARAMETERS_ADD_INFO_KHR, + .stdSPSCount = 0, + .stdPPSCount = 0, + .stdVPSCount = 0, + }; + VkVideoDecodeH265SessionParametersCreateInfoKHR h265_params = { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_SESSION_PARAMETERS_CREATE_INFO_KHR, + .pParametersAddInfo = &h265_params_info, + }; + VkVideoSessionParametersCreateInfoKHR session_params_create = { + .sType = VK_STRUCTURE_TYPE_VIDEO_SESSION_PARAMETERS_CREATE_INFO_KHR, + .pNext = &h265_params, + .videoSession = ctx->common.session, + .videoSessionParametersTemplate = NULL, + }; + + int nb_vps = 0; + AVBufferRef *data_set; + HEVCHeaderSet *hdr; + + AVBufferRef *tmp; + VkVideoSessionParametersKHR *par = av_malloc(sizeof(*par)); + if (!par) + return AVERROR(ENOMEM); + + for (int i = 0; h->ps.vps_list[i]; i++) + nb_vps++; + + err = get_data_set_buf(dec, &data_set, nb_vps, h->ps.vps_list); + if (err < 0) + return err; + + hdr = (HEVCHeaderSet *)data_set->data; + + h265_params_info.pStdSPSs = hdr->sps; + h265_params_info.pStdPPSs = hdr->pps; + h265_params_info.pStdVPSs = hdr->vps; + + /* SPS list */ + for (int i = 0; h->ps.sps_list[i]; i++) { + const HEVCSPS *sps_l = (const HEVCSPS *)h->ps.sps_list[i]->data; + set_sps(sps_l, i, &hdr->hsps[i].scaling, &hdr->hsps[i].vui_header, + &hdr->hsps[i].vui, &hdr->sps[i], hdr->hsps[i].nal_hdr, + hdr->hsps[i].vcl_hdr, &hdr->hsps[i].ptl, &hdr->hsps[i].dpbm, + &hdr->hsps[i].pal, hdr->hsps[i].str, &hdr->hsps[i].ltr); + h265_params_info.stdSPSCount++; + } + + /* PPS list */ + for (int i = 0; h->ps.pps_list[i]; i++) { + const HEVCPPS *pps_l = (const HEVCPPS *)h->ps.pps_list[i]->data; + const HEVCSPS *sps_l = (const HEVCSPS *)h->ps.sps_list[pps_l->sps_id]->data; + set_pps(pps_l, sps_l, &hdr->hpps[i].scaling, &hdr->pps[i], &hdr->hpps[i].pal); + h265_params_info.stdPPSCount++; + } + + /* VPS list */ + for (int i = 0; i < nb_vps; i++) { + const HEVCVPS *vps_l = (const HEVCVPS *)h->ps.vps_list[i]->data; + set_vps(vps_l, &hdr->vps[i], &hdr->hvps[i].ptl, &hdr->hvps[i].dpbm, + hdr->hvps[i].hdr, hdr->hvps[i].sls); + h265_params_info.stdVPSCount++; + } + + h265_params.maxStdSPSCount = h265_params_info.stdSPSCount; + h265_params.maxStdPPSCount = h265_params_info.stdPPSCount; + h265_params.maxStdVPSCount = h265_params_info.stdVPSCount; + + /* Create session parameters */ + ret = vk->CreateVideoSessionParametersKHR(ctx->s.hwctx->act_dev, &session_params_create, + ctx->s.hwctx->alloc, par); + av_buffer_unref(&data_set); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Unable to create Vulkan video session parameters: %s!\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + tmp = av_buffer_create((uint8_t *)par, sizeof(*par), ff_vk_decode_free_params, + ctx, 0); + if (!tmp) { + ff_vk_decode_free_params(ctx, (uint8_t *)par); + return AVERROR(ENOMEM); + } + + av_log(avctx, AV_LOG_DEBUG, "Created frame parameters: %i SPS %i PPS %i VPS\n", + h265_params_info.stdSPSCount, h265_params_info.stdPPSCount, + h265_params_info.stdVPSCount); + + *buf = tmp; + + return 0; +} + +static int vk_hevc_start_frame(AVCodecContext *avctx, + av_unused const uint8_t *buffer, + av_unused uint32_t size) +{ + int err; + HEVCContext *h = avctx->priv_data; + HEVCFrame *pic = h->ref; + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + HEVCVulkanDecodePicture *hp = pic->hwaccel_picture_private; + FFVulkanDecodePicture *vp = &hp->vp; + const HEVCSPS *sps = h->ps.sps; + const HEVCPPS *pps = h->ps.pps; + int nb_refs = 0; + + if (!dec->session_params) { + err = vk_hevc_create_params(avctx, &dec->session_params); + if (err < 0) + return err; + } + + hp->h265pic = (StdVideoDecodeH265PictureInfo) { + .flags = (StdVideoDecodeH265PictureInfoFlags) { + .IrapPicFlag = IS_IRAP(h), + .IdrPicFlag = IS_IDR(h), + .IsReference = h->nal_unit_type < 16 ? h->nal_unit_type & 1 : 1, + .short_term_ref_pic_set_sps_flag = h->sh.short_term_ref_pic_set_sps_flag, + }, + .sps_video_parameter_set_id = sps->vps_id, + .pps_seq_parameter_set_id = pps->sps_id, + .pps_pic_parameter_set_id = pps->pps_id, + .NumDeltaPocsOfRefRpsIdx = h->sh.short_term_rps ? h->sh.short_term_rps->rps_idx_num_delta_pocs : 0, + .PicOrderCntVal = h->poc, + .NumBitsForSTRefPicSetInSlice = !h->sh.short_term_ref_pic_set_sps_flag ? + h->sh.short_term_ref_pic_set_size : 0, + }; + + /* Fill in references */ + for (int i = 0; i < FF_ARRAY_ELEMS(h->DPB); i++) { + const HEVCFrame *ref = &h->DPB[i]; + int idx = nb_refs; + + if (!(ref->flags & (HEVC_FRAME_FLAG_SHORT_REF | HEVC_FRAME_FLAG_LONG_REF))) + continue; + + if (ref == pic) { + err = vk_hevc_fill_pict(avctx, NULL, &vp->ref_slot, &vp->ref, + &hp->vkh265_ref, &hp->h265_ref, pic, 1, i); + if (err < 0) + return err; + + continue; + } + + err = vk_hevc_fill_pict(avctx, &hp->ref_src[idx], &vp->ref_slots[idx], + &vp->refs[idx], &hp->vkh265_refs[idx], + &hp->h265_refs[idx], (HEVCFrame *)ref, 0, i); + if (err < 0) + return err; + + nb_refs++; + } + + memset(hp->h265pic.RefPicSetStCurrBefore, 0xff, 8); + for (int i = 0; i < h->rps[ST_CURR_BEF].nb_refs; i++) { + HEVCFrame *frame = h->rps[ST_CURR_BEF].ref[i]; + for (int j = 0; j < FF_ARRAY_ELEMS(h->DPB); j++) { + const HEVCFrame *ref = &h->DPB[j]; + if (ref == frame) { + hp->h265pic.RefPicSetStCurrBefore[i] = j; + break; + } + } + } + memset(hp->h265pic.RefPicSetStCurrAfter, 0xff, 8); + for (int i = 0; i < h->rps[ST_CURR_AFT].nb_refs; i++) { + HEVCFrame *frame = h->rps[ST_CURR_AFT].ref[i]; + for (int j = 0; j < FF_ARRAY_ELEMS(h->DPB); j++) { + const HEVCFrame *ref = &h->DPB[j]; + if (ref == frame) { + hp->h265pic.RefPicSetStCurrAfter[i] = j; + break; + } + } + } + memset(hp->h265pic.RefPicSetLtCurr, 0xff, 8); + for (int i = 0; i < h->rps[LT_CURR].nb_refs; i++) { + HEVCFrame *frame = h->rps[LT_CURR].ref[i]; + for (int j = 0; j < FF_ARRAY_ELEMS(h->DPB); j++) { + const HEVCFrame *ref = &h->DPB[j]; + if (ref == frame) { + hp->h265pic.RefPicSetLtCurr[i] = j; + break; + } + } + } + + hp->h265_pic_info = (VkVideoDecodeH265PictureInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_PICTURE_INFO_KHR, + .pStdPictureInfo = &hp->h265pic, + .sliceSegmentCount = 0, + }; + + vp->decode_info = (VkVideoDecodeInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_INFO_KHR, + .pNext = &hp->h265_pic_info, + .flags = 0x0, + .pSetupReferenceSlot = &vp->ref_slot, + .referenceSlotCount = nb_refs, + .pReferenceSlots = vp->ref_slots, + .dstPictureResource = (VkVideoPictureResourceInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_INFO_KHR, + .codedOffset = (VkOffset2D){ 0, 0 }, + .codedExtent = (VkExtent2D){ pic->frame->width, pic->frame->height }, + .baseArrayLayer = 0, + .imageViewBinding = vp->img_view_out, + }, + }; + + return 0; +} + +static int vk_hevc_decode_slice(AVCodecContext *avctx, + const uint8_t *data, + uint32_t size) +{ + const HEVCContext *h = avctx->priv_data; + HEVCVulkanDecodePicture *hp = h->ref->hwaccel_picture_private; + FFVulkanDecodePicture *vp = &hp->vp; + + int err = ff_vk_decode_add_slice(avctx, vp, data, size, 1, + &hp->h265_pic_info.sliceSegmentCount, + &hp->h265_pic_info.pSliceSegmentOffsets); + if (err < 0) + return err; + + return 0; +} + +static int vk_hevc_end_frame(AVCodecContext *avctx) +{ + const HEVCContext *h = avctx->priv_data; + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + HEVCFrame *pic = h->ref; + HEVCVulkanDecodePicture *hp = pic->hwaccel_picture_private; + FFVulkanDecodePicture *vp = &hp->vp; + FFVulkanDecodePicture *rvp[HEVC_MAX_REFS] = { 0 }; + AVFrame *rav[HEVC_MAX_REFS] = { 0 }; + int err; + + if (!hp->h265_pic_info.sliceSegmentCount) + return 0; + + if (!dec->session_params) { + const HEVCSPS *sps = h->ps.sps; + const HEVCPPS *pps = h->ps.pps; + + if (!pps) { + unsigned int pps_id = h->sh.pps_id; + if (pps_id < HEVC_MAX_PPS_COUNT && h->ps.pps_list[pps_id] != NULL) + pps = (const HEVCPPS *)h->ps.pps_list[pps_id]->data; + } + + if (!pps) { + av_log(avctx, AV_LOG_ERROR, + "Encountered frame without a valid active PPS reference.\n"); + return AVERROR_INVALIDDATA; + } + + err = vk_hevc_create_params(avctx, &dec->session_params); + if (err < 0) + return err; + + hp->h265pic.sps_video_parameter_set_id = sps->vps_id; + hp->h265pic.pps_seq_parameter_set_id = pps->sps_id; + hp->h265pic.pps_pic_parameter_set_id = pps->pps_id; + } + + for (int i = 0; i < vp->decode_info.referenceSlotCount; i++) { + HEVCVulkanDecodePicture *rfhp = hp->ref_src[i]->hwaccel_picture_private; + rav[i] = hp->ref_src[i]->frame; + rvp[i] = &rfhp->vp; + } + + av_log(avctx, AV_LOG_VERBOSE, "Decoding frame, %"SIZE_SPECIFIER" bytes, %i slices\n", + vp->slices_size, hp->h265_pic_info.sliceSegmentCount); + + return ff_vk_decode_frame(avctx, pic->frame, vp, rav, rvp); +} + +static void vk_hevc_free_frame_priv(void *_hwctx, uint8_t *data) +{ + AVHWDeviceContext *hwctx = _hwctx; + HEVCVulkanDecodePicture *hp = (HEVCVulkanDecodePicture *)data; + + /* Free frame resources */ + ff_vk_decode_free_frame(hwctx, &hp->vp); + + /* Free frame context */ + av_free(hp); +} + +const FFHWAccel ff_hevc_vulkan_hwaccel = { + .p.name = "hevc_vulkan", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_HEVC, + .p.pix_fmt = AV_PIX_FMT_VULKAN, + .start_frame = &vk_hevc_start_frame, + .decode_slice = &vk_hevc_decode_slice, + .end_frame = &vk_hevc_end_frame, + .free_frame_priv = &vk_hevc_free_frame_priv, + .frame_priv_data_size = sizeof(HEVCVulkanDecodePicture), + .init = &ff_vk_decode_init, + .update_thread_context = &ff_vk_update_thread_context, + .decode_params = &ff_vk_params_invalidate, + .flush = &ff_vk_decode_flush, + .uninit = &ff_vk_decode_uninit, + .frame_params = &ff_vk_frame_params, + .priv_data_size = sizeof(FFVulkanDecodeContext), + .caps_internal = HWACCEL_CAP_ASYNC_SAFE | HWACCEL_CAP_THREAD_SAFE, +}; diff --git a/libavcodec/vulkan_video.c b/libavcodec/vulkan_video.c new file mode 100644 index 00000000000..9a363aab024 --- /dev/null +++ b/libavcodec/vulkan_video.c @@ -0,0 +1,367 @@ +/* + * 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 "codec_id.h" + +#include "vulkan_video.h" + +const FFVkCodecMap ff_vk_codec_map[AV_CODEC_ID_FIRST_AUDIO] = { + [AV_CODEC_ID_H264] = { + 0, + 0, + FF_VK_EXT_VIDEO_DECODE_H264, + VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR, + }, + [AV_CODEC_ID_HEVC] = { + 0, + 0, + FF_VK_EXT_VIDEO_DECODE_H265, + VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR + }, + [AV_CODEC_ID_AV1] = { + 0, + 0, + FF_VK_EXT_VIDEO_DECODE_AV1, + 0x01000000 /* TODO fix this */ + }, +}; + +#define ASPECT_2PLANE (VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT) +#define ASPECT_3PLANE (VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT | VK_IMAGE_ASPECT_PLANE_2_BIT) + +static const struct FFVkFormatMapEntry { + VkFormat vkf; + enum AVPixelFormat pixfmt; + VkImageAspectFlags aspect; +} vk_format_map[] = { + /* Gray formats */ + { VK_FORMAT_R8_UNORM, AV_PIX_FMT_GRAY8, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_R16_UNORM, AV_PIX_FMT_GRAY16, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_R32_SFLOAT, AV_PIX_FMT_GRAYF32, VK_IMAGE_ASPECT_COLOR_BIT }, + + /* RGB formats */ + { VK_FORMAT_R16G16B16A16_UNORM, AV_PIX_FMT_XV36, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_B8G8R8A8_UNORM, AV_PIX_FMT_BGRA, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_R8G8B8A8_UNORM, AV_PIX_FMT_RGBA, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_R8G8B8_UNORM, AV_PIX_FMT_RGB24, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_B8G8R8_UNORM, AV_PIX_FMT_BGR24, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_R16G16B16_UNORM, AV_PIX_FMT_RGB48, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_R16G16B16A16_UNORM, AV_PIX_FMT_RGBA64, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_R5G6B5_UNORM_PACK16, AV_PIX_FMT_RGB565, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_B5G6R5_UNORM_PACK16, AV_PIX_FMT_BGR565, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_B8G8R8A8_UNORM, AV_PIX_FMT_BGR0, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_R8G8B8A8_UNORM, AV_PIX_FMT_RGB0, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_A2R10G10B10_UNORM_PACK32, AV_PIX_FMT_X2RGB10, VK_IMAGE_ASPECT_COLOR_BIT }, + + /* Planar RGB */ + { VK_FORMAT_R8_UNORM, AV_PIX_FMT_GBRAP, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_R16_UNORM, AV_PIX_FMT_GBRAP16, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_R32_SFLOAT, AV_PIX_FMT_GBRPF32, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_R32_SFLOAT, AV_PIX_FMT_GBRAPF32, VK_IMAGE_ASPECT_COLOR_BIT }, + + /* Two-plane 420 YUV at 8, 10, 12 and 16 bits */ + { VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, AV_PIX_FMT_NV12, ASPECT_2PLANE }, + { VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16, AV_PIX_FMT_P010, ASPECT_2PLANE }, + { VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16, AV_PIX_FMT_P012, ASPECT_2PLANE }, + { VK_FORMAT_G16_B16R16_2PLANE_420_UNORM, AV_PIX_FMT_P016, ASPECT_2PLANE }, + + /* Two-plane 422 YUV at 8, 10 and 16 bits */ + { VK_FORMAT_G8_B8R8_2PLANE_422_UNORM, AV_PIX_FMT_NV16, ASPECT_2PLANE }, + { VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16, AV_PIX_FMT_P210, ASPECT_2PLANE }, + { VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16, AV_PIX_FMT_P212, ASPECT_2PLANE }, + { VK_FORMAT_G16_B16R16_2PLANE_422_UNORM, AV_PIX_FMT_P216, ASPECT_2PLANE }, + + /* Two-plane 444 YUV at 8, 10 and 16 bits */ + { VK_FORMAT_G8_B8R8_2PLANE_444_UNORM, AV_PIX_FMT_NV24, ASPECT_2PLANE }, + { VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16, AV_PIX_FMT_P410, ASPECT_2PLANE }, + { VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16, AV_PIX_FMT_P412, ASPECT_2PLANE }, + { VK_FORMAT_G16_B16R16_2PLANE_444_UNORM, AV_PIX_FMT_P416, ASPECT_2PLANE }, + + /* Three-plane 420, 422, 444 at 8, 10, 12 and 16 bits */ + { VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, AV_PIX_FMT_YUV420P, ASPECT_3PLANE }, + { VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM, AV_PIX_FMT_YUV420P10, ASPECT_3PLANE }, + { VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM, AV_PIX_FMT_YUV420P12, ASPECT_3PLANE }, + { VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM, AV_PIX_FMT_YUV420P16, ASPECT_3PLANE }, + { VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM, AV_PIX_FMT_YUV422P, ASPECT_3PLANE }, + { VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM, AV_PIX_FMT_YUV422P10, ASPECT_3PLANE }, + { VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM, AV_PIX_FMT_YUV422P12, ASPECT_3PLANE }, + { VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM, AV_PIX_FMT_YUV422P16, ASPECT_3PLANE }, + { VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM, AV_PIX_FMT_YUV444P, ASPECT_3PLANE }, + { VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM, AV_PIX_FMT_YUV444P10, ASPECT_3PLANE }, + { VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM, AV_PIX_FMT_YUV444P12, ASPECT_3PLANE }, + { VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM, AV_PIX_FMT_YUV444P16, ASPECT_3PLANE }, + + /* Single plane 422 at 8, 10 and 12 bits */ + { VK_FORMAT_G8B8G8R8_422_UNORM, AV_PIX_FMT_YUYV422, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_B8G8R8G8_422_UNORM, AV_PIX_FMT_UYVY422, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16, AV_PIX_FMT_Y210, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16, AV_PIX_FMT_Y212, VK_IMAGE_ASPECT_COLOR_BIT }, +}; +static const int nb_vk_format_map = FF_ARRAY_ELEMS(vk_format_map); + +enum AVPixelFormat ff_vk_pix_fmt_from_vkfmt(VkFormat vkf) +{ + for (int i = 0; i < nb_vk_format_map; i++) + if (vk_format_map[i].vkf == vkf) + return vk_format_map[i].pixfmt; + return AV_PIX_FMT_NONE; +} + +VkImageAspectFlags ff_vk_aspect_bits_from_vkfmt(VkFormat vkf) +{ + for (int i = 0; i < nb_vk_format_map; i++) + if (vk_format_map[i].vkf == vkf) + return vk_format_map[i].aspect; + return VK_IMAGE_ASPECT_NONE; +} + +VkVideoChromaSubsamplingFlagBitsKHR ff_vk_subsampling_from_av_desc(const AVPixFmtDescriptor *desc) +{ + if (desc->nb_components == 1) + return VK_VIDEO_CHROMA_SUBSAMPLING_MONOCHROME_BIT_KHR; + else if (!desc->log2_chroma_w && !desc->log2_chroma_h) + return VK_VIDEO_CHROMA_SUBSAMPLING_444_BIT_KHR; + else if (!desc->log2_chroma_w && desc->log2_chroma_h == 1) + return VK_VIDEO_CHROMA_SUBSAMPLING_422_BIT_KHR; + else if (desc->log2_chroma_w == 1 && desc->log2_chroma_h == 1) + return VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR; + return VK_VIDEO_CHROMA_SUBSAMPLING_INVALID_KHR; +} + +VkVideoComponentBitDepthFlagBitsKHR ff_vk_depth_from_av_depth(int depth) +{ + switch (depth) { + case 8: return VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR; + case 10: return VK_VIDEO_COMPONENT_BIT_DEPTH_10_BIT_KHR; + case 12: return VK_VIDEO_COMPONENT_BIT_DEPTH_12_BIT_KHR; + default: break; + } + return VK_VIDEO_COMPONENT_BIT_DEPTH_INVALID_KHR; +} + +static void free_data_buf(void *opaque, uint8_t *data) +{ + FFVulkanContext *ctx = opaque; + FFVkVideoBuffer *buf = (FFVkVideoBuffer *)data; + ff_vk_unmap_buffer(ctx, &buf->buf, 0); + ff_vk_free_buf(ctx, &buf->buf); + av_free(data); +} + +static AVBufferRef *alloc_data_buf(void *opaque, size_t size) +{ + AVBufferRef *ref; + uint8_t *buf = av_mallocz(size); + if (!buf) + return NULL; + + ref = av_buffer_create(buf, size, free_data_buf, opaque, 0); + if (!ref) + av_free(buf); + return ref; +} + +int ff_vk_video_get_buffer(FFVulkanContext *ctx, FFVkVideoCommon *s, + AVBufferRef **buf, VkBufferUsageFlags usage, + void *create_pNext, size_t size) +{ + int err; + AVBufferRef *ref; + FFVkVideoBuffer *data; + + if (!s->buf_pool) { + s->buf_pool = av_buffer_pool_init2(sizeof(FFVkVideoBuffer), ctx, + alloc_data_buf, NULL); + if (!s->buf_pool) + return AVERROR(ENOMEM); + } + + *buf = ref = av_buffer_pool_get(s->buf_pool); + if (!ref) + return AVERROR(ENOMEM); + + data = (FFVkVideoBuffer *)ref->data; + + if (data->buf.size >= size) + return 0; + + /* No point in requesting anything smaller. */ + size = FFMAX(size, 1024*1024); + + /* Align buffer to nearest power of two. Makes fragmentation management + * easier, and gives us ample headroom. */ + size--; + size |= size >> 1; + size |= size >> 2; + size |= size >> 4; + size |= size >> 8; + size |= size >> 16; + size++; + + ff_vk_free_buf(ctx, &data->buf); + memset(data, 0, sizeof(FFVkVideoBuffer)); + + err = ff_vk_create_buf(ctx, &data->buf, size, + create_pNext, NULL, usage, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); + if (err < 0) { + av_buffer_unref(&ref); + return err; + } + + /* Map the buffer */ + err = ff_vk_map_buffer(ctx, &data->buf, &data->mem, 0); + if (err < 0) { + av_buffer_unref(&ref); + return err; + } + + return 0; +} + +av_cold void ff_vk_video_common_uninit(FFVulkanContext *s, + FFVkVideoCommon *common) +{ + FFVulkanFunctions *vk = &s->vkfn; + + if (common->session) { + vk->DestroyVideoSessionKHR(s->hwctx->act_dev, common->session, + s->hwctx->alloc); + common->session = NULL; + } + + if (common->nb_mem && common->mem) + for (int i = 0; i < common->nb_mem; i++) + vk->FreeMemory(s->hwctx->act_dev, common->mem[i], s->hwctx->alloc); + + av_freep(&common->mem); + + av_buffer_pool_uninit(&common->buf_pool); +} + +av_cold int ff_vk_video_common_init(void *log, FFVulkanContext *s, + FFVkVideoCommon *common, + VkVideoSessionCreateInfoKHR *session_create) +{ + int err; + VkResult ret; + FFVulkanFunctions *vk = &s->vkfn; + VkMemoryRequirements2 *mem_req = NULL; + VkVideoSessionMemoryRequirementsKHR *mem = NULL; + VkBindVideoSessionMemoryInfoKHR *bind_mem = NULL; + + /* Create session */ + ret = vk->CreateVideoSessionKHR(s->hwctx->act_dev, session_create, + s->hwctx->alloc, &common->session); + if (ret != VK_SUCCESS) + return AVERROR_EXTERNAL; + + /* Get memory requirements */ + ret = vk->GetVideoSessionMemoryRequirementsKHR(s->hwctx->act_dev, + common->session, + &common->nb_mem, + NULL); + if (ret != VK_SUCCESS) { + err = AVERROR_EXTERNAL; + goto fail; + } + + /* Allocate all memory needed to actually allocate memory */ + common->mem = av_mallocz(sizeof(*common->mem)*common->nb_mem); + if (!common->mem) { + err = AVERROR(ENOMEM); + goto fail; + } + mem = av_mallocz(sizeof(*mem)*common->nb_mem); + if (!mem) { + err = AVERROR(ENOMEM); + goto fail; + } + mem_req = av_mallocz(sizeof(*mem_req)*common->nb_mem); + if (!mem_req) { + err = AVERROR(ENOMEM); + goto fail; + } + bind_mem = av_mallocz(sizeof(*bind_mem)*common->nb_mem); + if (!bind_mem) { + err = AVERROR(ENOMEM); + goto fail; + } + + /* Set the needed fields to get the memory requirements */ + for (int i = 0; i < common->nb_mem; i++) { + mem_req[i] = (VkMemoryRequirements2) { + .sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, + }; + mem[i] = (VkVideoSessionMemoryRequirementsKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_SESSION_MEMORY_REQUIREMENTS_KHR, + .memoryRequirements = mem_req[i].memoryRequirements, + }; + } + + /* Finally get the memory requirements */ + ret = vk->GetVideoSessionMemoryRequirementsKHR(s->hwctx->act_dev, + common->session, &common->nb_mem, + mem); + if (ret != VK_SUCCESS) { + err = AVERROR_EXTERNAL; + goto fail; + } + + /* Now allocate each requested memory. + * For ricing, could pool together memory that ends up in the same index. */ + for (int i = 0; i < common->nb_mem; i++) { + err = ff_vk_alloc_mem(s, &mem[i].memoryRequirements, + UINT32_MAX, NULL, NULL, &common->mem[i]); + if (err < 0) + goto fail; + + bind_mem[i] = (VkBindVideoSessionMemoryInfoKHR) { + .sType = VK_STRUCTURE_TYPE_BIND_VIDEO_SESSION_MEMORY_INFO_KHR, + .memory = common->mem[i], + .memoryBindIndex = mem[i].memoryBindIndex, + .memoryOffset = 0, + .memorySize = mem[i].memoryRequirements.size, + }; + + av_log(log, AV_LOG_VERBOSE, "Allocating %"SIZE_SPECIFIER" bytes in bind index %i for video session\n", + bind_mem[i].memorySize, bind_mem[i].memoryBindIndex); + } + + /* Bind the allocated memory */ + ret = vk->BindVideoSessionMemoryKHR(s->hwctx->act_dev, common->session, + common->nb_mem, bind_mem); + if (ret != VK_SUCCESS) { + err = AVERROR_EXTERNAL; + goto fail; + } + + av_freep(&mem); + av_freep(&mem_req); + av_freep(&bind_mem); + + return 0; + +fail: + av_freep(&mem); + av_freep(&mem_req); + av_freep(&bind_mem); + + ff_vk_video_common_uninit(s, common); + return err; +} diff --git a/libavcodec/vulkan_video.h b/libavcodec/vulkan_video.h new file mode 100644 index 00000000000..183ce89bf0a --- /dev/null +++ b/libavcodec/vulkan_video.h @@ -0,0 +1,99 @@ +/* + * 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_VULKAN_VIDEO_H +#define AVCODEC_VULKAN_VIDEO_H + +#include "codec_id.h" +#include "vulkan.h" + +#include +#include "vulkan_video_codec_av1std.h" +#include "vulkan_video_codec_av1std_decode.h" + +#define CODEC_VER_MAJ(ver) (ver >> 22) +#define CODEC_VER_MIN(ver) ((ver >> 12) & ((1 << 10) - 1)) +#define CODEC_VER_PAT(ver) (ver & ((1 << 12) - 1)) +#define CODEC_VER(ver) CODEC_VER_MAJ(ver), CODEC_VER_MIN(ver), CODEC_VER_PAT(ver) + +typedef struct FFVkCodecMap { + FFVulkanExtensions encode_extension; + VkVideoCodecOperationFlagBitsKHR encode_op; + FFVulkanExtensions decode_extension; + VkVideoCodecOperationFlagBitsKHR decode_op; +} FFVkCodecMap; + +typedef struct FFVkVideoSession { + VkVideoSessionKHR session; + VkDeviceMemory *mem; + uint32_t nb_mem; + + AVBufferPool *buf_pool; +} FFVkVideoCommon; + +/** + * Index is codec_id. + */ +extern const FFVkCodecMap ff_vk_codec_map[AV_CODEC_ID_FIRST_AUDIO]; + +/** + * Get pixfmt from a Vulkan format. + */ +enum AVPixelFormat ff_vk_pix_fmt_from_vkfmt(VkFormat vkf); + +/** + * Get aspect bits which include all planes from a VkFormat. + */ +VkImageAspectFlags ff_vk_aspect_bits_from_vkfmt(VkFormat vkf); + +/** + * Get Vulkan's chroma subsampling from a pixfmt descriptor. + */ +VkVideoChromaSubsamplingFlagBitsKHR ff_vk_subsampling_from_av_desc(const AVPixFmtDescriptor *desc); + +/** + * Get Vulkan's bit depth from an [8:12] integer. + */ +VkVideoComponentBitDepthFlagBitsKHR ff_vk_depth_from_av_depth(int depth); + +typedef struct FFVkVideoBuffer { + FFVkBuffer buf; + uint8_t *mem; +} FFVkVideoBuffer; + +/** + * Get a mapped FFVkPooledBuffer with a specific guaranteed minimum size + * from a pool. + */ +int ff_vk_video_get_buffer(FFVulkanContext *ctx, FFVkVideoCommon *s, + AVBufferRef **buf, VkBufferUsageFlags usage, + void *create_pNext, size_t size); + +/** + * Initialize video session, allocating and binding necessary memory. + */ +int ff_vk_video_common_init(void *log, FFVulkanContext *s, + FFVkVideoCommon *common, + VkVideoSessionCreateInfoKHR *session_create); + +/** + * Free video session and required resources. + */ +void ff_vk_video_common_uninit(FFVulkanContext *s, FFVkVideoCommon *common); + +#endif /* AVCODEC_VULKAN_VIDEO_H */ diff --git a/libavcodec/vulkan_video_codec_av1std.h b/libavcodec/vulkan_video_codec_av1std.h new file mode 100644 index 00000000000..c46236c4572 --- /dev/null +++ b/libavcodec/vulkan_video_codec_av1std.h @@ -0,0 +1,403 @@ +/* Copyright 2023 Lynne + * Copyright 2023 Dave Airlie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VULKAN_VIDEO_CODEC_AV1STD_H_ +#define VULKAN_VIDEO_CODEC_AV1STD_H_ 1 + +/* +** This header is NOT YET generated from the Khronos Vulkan XML API Registry. +** +*/ + +#ifdef __cplusplus +extern "C" { +#endif +#define vulkan_video_codec_av1std 1 + +#define VK_MAKE_VIDEO_STD_VERSION(major, minor, patch) \ + ((((uint32_t)(major)) << 22) | (((uint32_t)(minor)) << 12) | ((uint32_t)(patch))) +#define VK_STD_VULKAN_VIDEO_CODEC_AV1_DECODE_API_VERSION_0_1_0 VK_MAKE_VIDEO_STD_VERSION(0, 1, 0) +#define VK_STD_VULKAN_VIDEO_CODEC_AV1_DECODE_SPEC_VERSION VK_STD_VULKAN_VIDEO_CODEC_AV1_DECODE_API_VERSION_0_1_0 +#define VK_STD_VULKAN_VIDEO_CODEC_AV1_DECODE_EXTENSION_NAME "VK_STD_vulkan_video_codec_av1_decode" + +typedef enum StdVideoAV1MESAProfile { + STD_VIDEO_AV1_MESA_PROFILE_MAIN = 0, + STD_VIDEO_AV1_MESA_PROFILE_HIGH = 1, + STD_VIDEO_AV1_MESA_PROFILE_PROFESSIONAL = 2, +} StdVideoAV1MESAProfile; + +typedef enum StdVideoAV1MESALevel { + STD_VIDEO_AV1_MESA_LEVEL_2_0 = 0, + STD_VIDEO_AV1_MESA_LEVEL_2_1 = 1, + STD_VIDEO_AV1_MESA_LEVEL_2_2 = 2, + STD_VIDEO_AV1_MESA_LEVEL_2_3 = 3, + STD_VIDEO_AV1_MESA_LEVEL_3_0 = 4, + STD_VIDEO_AV1_MESA_LEVEL_3_1 = 5, + STD_VIDEO_AV1_MESA_LEVEL_3_2 = 6, + STD_VIDEO_AV1_MESA_LEVEL_3_3 = 7, + STD_VIDEO_AV1_MESA_LEVEL_4_0 = 8, + STD_VIDEO_AV1_MESA_LEVEL_4_1 = 9, + STD_VIDEO_AV1_MESA_LEVEL_4_2 = 10, + STD_VIDEO_AV1_MESA_LEVEL_4_3 = 11, + STD_VIDEO_AV1_MESA_LEVEL_5_0 = 12, + STD_VIDEO_AV1_MESA_LEVEL_5_1 = 13, + STD_VIDEO_AV1_MESA_LEVEL_5_2 = 14, + STD_VIDEO_AV1_MESA_LEVEL_5_3 = 15, + STD_VIDEO_AV1_MESA_LEVEL_6_0 = 16, + STD_VIDEO_AV1_MESA_LEVEL_6_1 = 17, + STD_VIDEO_AV1_MESA_LEVEL_6_2 = 18, + STD_VIDEO_AV1_MESA_LEVEL_6_3 = 19, + STD_VIDEO_AV1_MESA_LEVEL_7_0 = 20, + STD_VIDEO_AV1_MESA_LEVEL_7_1 = 21, + STD_VIDEO_AV1_MESA_LEVEL_7_2 = 22, + STD_VIDEO_AV1_MESA_LEVEL_7_3 = 23, + STD_VIDEO_AV1_MESA_LEVEL_MAX = 31, +} StdVideoAV1MESALevel; + +typedef struct StdVideoAV1MESAFilmGrainFlags { + uint8_t apply_grain; + uint8_t chroma_scaling_from_luma; + uint8_t overlap_flag; + uint8_t clip_to_restricted_range; +} StdVideoAV1MESAFilmGrainFlags; + +typedef struct StdVideoAV1MESAFilmGrainParameters { + StdVideoAV1MESAFilmGrainFlags flags; + uint32_t grain_scaling_minus_8; + uint32_t ar_coeff_lag; + uint32_t ar_coeff_shift_minus_6; + uint32_t grain_scale_shift; + + uint16_t grain_seed; + uint8_t num_y_points; + uint8_t point_y_value[14]; + uint8_t point_y_scaling[14]; + + uint8_t num_cb_points; + uint8_t point_cb_value[10]; + uint8_t point_cb_scaling[10]; + + uint8_t num_cr_points; + uint8_t point_cr_value[10]; + uint8_t point_cr_scaling[10]; + + int8_t ar_coeffs_y_plus_128[24]; + int8_t ar_coeffs_cb_plus_128[25]; + int8_t ar_coeffs_cr_plus_128[25]; + uint8_t cb_mult; + uint8_t cb_luma_mult; + uint16_t cb_offset; + uint8_t cr_mult; + uint8_t cr_luma_mult; + uint16_t cr_offset; +} StdVideoAV1MESAFilmGrainParameters; + +typedef struct StdVideoAV1MESAGlobalMotionFlags { + uint8_t gm_invalid; +} StdVideoAV1MESAGlobalMotionFlags; + +typedef struct StdVideoAV1MESAGlobalMotion { + StdVideoAV1MESAGlobalMotionFlags flags; + uint8_t gm_type; + uint32_t gm_params[6]; +} StdVideoAV1MESAGlobalMotion; + +typedef struct StdVideoAV1MESALoopRestoration { + uint8_t lr_type[3]; + uint8_t lr_unit_shift; + uint8_t lr_uv_shift; +} StdVideoAV1MESALoopRestoration; + +typedef struct StdVideoAV1MESATileInfoFlags { + uint8_t uniform_tile_spacing_flag; +} StdVideoAV1MESATileInfoFlags; + +typedef struct StdVideoAV1MESATileInfo { + StdVideoAV1MESATileInfoFlags flags; + uint8_t tile_cols; + uint8_t tile_rows; + uint8_t tile_start_col_sb[64]; + uint8_t tile_start_row_sb[64]; + uint8_t width_in_sbs_minus_1[64]; + uint8_t height_in_sbs_minus_1[64]; + uint16_t context_update_tile_id; + uint8_t tile_size_bytes_minus1; +} StdVideoAV1MESATileInfo; + +typedef struct StdVideoAV1MESAQuantizationFlags { + uint8_t using_qmatrix; +} StdVideoAV1MESAQuantizationFlags; + +typedef struct StdVideoAV1MESAQuantization { + StdVideoAV1MESAQuantizationFlags flags; + uint8_t base_q_idx; + int8_t delta_q_y_dc; + uint8_t diff_uv_delta; + int8_t delta_q_u_dc; + int8_t delta_q_u_ac; + int8_t delta_q_v_dc; + int8_t delta_q_v_ac; + uint8_t qm_y; + uint8_t qm_u; + uint8_t qm_v; +} StdVideoAV1MESAQuantization; + +typedef struct StdVideoAV1MESACDEF { + uint8_t damping_minus_3; + uint8_t bits; + uint8_t y_pri_strength[8]; + uint8_t y_sec_strength[8]; + uint8_t uv_pri_strength[8]; + uint8_t uv_sec_strength[8]; +} StdVideoAV1MESACDEF; + +typedef struct StdVideoAV1MESADeltaQFlags { + uint8_t delta_lf_present; + uint8_t delta_lf_multi; +} StdVideoAV1MESADeltaQFlags; + +typedef struct StdVideoAV1MESADeltaQ { + StdVideoAV1MESADeltaQFlags flags; + uint8_t delta_q_res; + uint8_t delta_lf_res; +} StdVideoAV1MESADeltaQ; + +typedef struct StdVideoAV1MESASegmentationFlags { + uint8_t enabled; + uint8_t update_map; + uint8_t temporal_update; + uint8_t update_data; +} StdVideoAV1MESASegmentationFlags; + +typedef struct StdVideoAV1MESASegmentation { + StdVideoAV1MESASegmentationFlags flags; + uint8_t feature_enabled_bits[8]; + int16_t feature_data[8][8]; +} StdVideoAV1MESASegmentation; + +typedef struct StdVideoAV1MESALoopFilterFlags { + uint8_t delta_enabled; + uint8_t delta_update; +} StdVideoAV1MESALoopFilterFlags; + +typedef struct StdVideoAV1MESALoopFilter { + StdVideoAV1MESALoopFilterFlags flags; + uint8_t level[4]; + uint8_t sharpness; + int8_t ref_deltas[8]; + int8_t mode_deltas[2]; +} StdVideoAV1MESALoopFilter; + +typedef struct StdVideoAV1MESAFrameHeaderFlags { + uint8_t error_resilient_mode; + uint8_t disable_cdf_update; + uint8_t use_superres; + uint8_t render_and_frame_size_different; + uint8_t allow_screen_content_tools; + uint8_t is_filter_switchable; + uint8_t force_integer_mv; + uint8_t frame_size_override_flag; + uint8_t buffer_removal_time_present_flag; + uint8_t allow_intrabc; + uint8_t frame_refs_short_signaling; + uint8_t allow_high_precision_mv; + uint8_t is_motion_mode_switchable; + uint8_t use_ref_frame_mvs; + uint8_t disable_frame_end_update_cdf; + uint8_t allow_warped_motion; + uint8_t reduced_tx_set; + uint8_t reference_select; + uint8_t skip_mode_present; + uint8_t delta_q_present; + uint8_t UsesLr; +} StdVideoAV1MESAFrameHeaderFlags; + +typedef struct StdVideoAV1MESAFrameHeader { + StdVideoAV1MESAFrameHeaderFlags flags; + + uint32_t frame_presentation_time; + uint32_t display_frame_id; + uint32_t current_frame_id; + uint8_t frame_to_show_map_idx; + uint8_t frame_type; + uint8_t order_hint; + uint8_t primary_ref_frame; + uint16_t frame_width_minus_1; + uint16_t frame_height_minus_1; + uint16_t render_width_minus_1; + uint16_t render_height_minus_1; + uint8_t coded_denom; + + uint8_t refresh_frame_flags; + uint8_t ref_order_hint[8]; + int8_t ref_frame_idx[7]; + uint32_t delta_frame_id_minus1[7]; + + uint8_t interpolation_filter; + uint8_t tx_mode; + + StdVideoAV1MESATileInfo tiling; + StdVideoAV1MESAQuantization quantization; + StdVideoAV1MESASegmentation segmentation; + StdVideoAV1MESADeltaQ delta_q; + StdVideoAV1MESALoopFilter loop_filter; + StdVideoAV1MESACDEF cdef; + StdVideoAV1MESALoopRestoration lr; + StdVideoAV1MESAGlobalMotion global_motion[8]; // One per ref frame + StdVideoAV1MESAFilmGrainParameters film_grain; +} StdVideoAV1MESAFrameHeader; + +typedef struct StdVideoAV1MESAScreenCoding { + uint8_t seq_force_screen_content_tools; +} StdVideoAV1MESAScreenCoding; + +typedef struct StdVideoAV1MESATimingInfoFlags { + uint8_t equal_picture_interval; +} StdVideoAV1MESATimingInfoFlags; + +typedef struct StdVideoAV1MESATimingInfo { + StdVideoAV1MESATimingInfoFlags flags; + uint32_t num_units_in_display_tick; + uint32_t time_scale; + uint32_t num_ticks_per_picture_minus_1; +} StdVideoAV1MESATimingInfo; + +typedef struct StdVideoAV1MESAColorConfigFlags { + uint8_t mono_chrome; + uint8_t color_range; + uint8_t separate_uv_delta_q; +} StdVideoAV1MESAColorConfigFlags; + +typedef struct StdVideoAV1MESAColorConfig { + StdVideoAV1MESAColorConfigFlags flags; + uint8_t bit_depth; + uint8_t subsampling_x; + uint8_t subsampling_y; +} StdVideoAV1MESAColorConfig; + +typedef struct StdVideoAV1MESASequenceHeaderFlags { + uint8_t still_picture; + uint8_t reduced_still_picture_header; + uint8_t use_128x128_superblock; + uint8_t enable_filter_intra; + uint8_t enable_intra_edge_filter; + uint8_t enable_interintra_compound; + uint8_t enable_masked_compound; + uint8_t enable_warped_motion; + uint8_t enable_dual_filter; + uint8_t enable_order_hint; + uint8_t enable_jnt_comp; + uint8_t enable_ref_frame_mvs; + uint8_t frame_id_numbers_present_flag; + uint8_t enable_superres; + uint8_t enable_cdef; + uint8_t enable_restoration; + uint8_t film_grain_params_present; + uint8_t timing_info_present_flag; + uint8_t initial_display_delay_present_flag; +} StdVideoAV1MESASequenceHeaderFlags; + +typedef struct StdVideoAV1MESASequenceHeader { + StdVideoAV1MESASequenceHeaderFlags flags; + + StdVideoAV1MESAProfile seq_profile; + uint8_t frame_width_bits_minus_1; + uint8_t frame_height_bits_minus_1; + uint16_t max_frame_width_minus_1; + uint16_t max_frame_height_minus_1; + uint8_t delta_frame_id_length_minus_2; + uint8_t additional_frame_id_length_minus_1; + uint8_t order_hint_bits_minus_1; + uint8_t seq_choose_integer_mv; + uint8_t seq_force_integer_mv; + + StdVideoAV1MESATimingInfo timing_info; + StdVideoAV1MESAColorConfig color_config; +} StdVideoAV1MESASequenceHeader; + +typedef struct StdVideoAV1MESATile { + uint16_t tg_start; + uint16_t tg_end; + uint16_t row; + uint16_t column; + uint32_t size; + uint32_t offset; +} StdVideoAV1MESATile; + +typedef struct StdVideoAV1MESATileList { + StdVideoAV1MESATile *tile_list; + uint32_t nb_tiles; +} StdVideoAV1MESATileList; + +typedef struct VkVideoDecodeAV1PictureInfoMESA { + VkStructureType sType; + const void *pNext; + StdVideoAV1MESAFrameHeader *frame_header; + StdVideoAV1MESATileList *tile_list; + uint8_t skip_mode_frame_idx[2]; +} VkVideoDecodeAV1PictureInfoMESA; + +typedef struct VkVideoDecodeAV1DpbSlotInfoMESA { + VkStructureType sType; + const void *pNext; + uint8_t frameIdx; + uint8_t ref_order_hint[7]; + uint8_t disable_frame_end_update_cdf; +} VkVideoDecodeAV1DpbSlotInfoMESA; + +typedef struct VkVideoDecodeAV1SessionParametersAddInfoMESA { + VkStructureType sType; + const void *pNext; + StdVideoAV1MESASequenceHeader *sequence_header; +} VkVideoDecodeAV1SessionParametersAddInfoMESA; + +typedef struct VkVideoDecodeAV1SessionParametersCreateInfoMESA { + VkStructureType sType; + const void *pNext; + const VkVideoDecodeAV1SessionParametersAddInfoMESA *pParametersAddInfo; +} VkVideoDecodeAV1SessionParametersCreateInfoMESA; + +typedef struct VkVideoDecodeAV1ProfileInfoMESA { + VkStructureType sType; + const void *pNext; + StdVideoAV1MESAProfile stdProfileIdc; +} VkVideoDecodeAV1ProfileInfoMESA; + +typedef enum VkVideoDecodeAV1CapabilityFlagBitsMESA { + VK_VIDEO_DECODE_AV1_CAPABILITY_EXTERNAL_FILM_GRAIN_MESA = 0x00000001, + VK_VIDEO_DECODE_AV1_CAPABILITY_FLAG_BITS_MAX_ENUM_MESA = 0x7FFFFFFF +} VkVideoDecodeAV1CapabilityFlagBitsMESA; +typedef VkFlags VkVideoDecodeAV1CapabilityFlagsMESA; + +typedef struct VkVideoDecodeAV1CapabilitiesMESA { + VkStructureType sType; + const void *pNext; + VkVideoDecodeAV1CapabilityFlagsMESA flags; + StdVideoAV1MESALevel maxLevelIdc; +} VkVideoDecodeAV1CapabilitiesMESA; + +#define VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_PICTURE_INFO_MESA 1000509000 +#define VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_SESSION_PARAMETERS_CREATE_INFO_MESA 1000509001 +#define VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_SESSION_PARAMETERS_ADD_INFO_MESA 1000509002 +#define VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_DPB_SLOT_INFO_MESA 1000509003 +#define VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_CAPABILITIES_MESA 1000509004 +#define VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_PROFILE_INFO_MESA 1000509005 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libavcodec/vulkan_video_codec_av1std_decode.h b/libavcodec/vulkan_video_codec_av1std_decode.h new file mode 100644 index 00000000000..a697c00593c --- /dev/null +++ b/libavcodec/vulkan_video_codec_av1std_decode.h @@ -0,0 +1,36 @@ +/* Copyright 2023 Lynne + * Copyright 2023 Dave Airlie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VULKAN_VIDEO_CODEC_AV1STD_DECODE_H_ +#define VULKAN_VIDEO_CODEC_AV1STD_DECODE_H_ 1 + +/* +** This header is NOT YET generated from the Khronos Vulkan XML API Registry. +** +*/ + +#ifdef __cplusplus +extern "C" { +#endif +#define vulkan_video_codec_av1std_decode 1 + + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libavcodec/vvc.h b/libavcodec/vvc.h new file mode 100644 index 00000000000..7d165cdb861 --- /dev/null +++ b/libavcodec/vvc.h @@ -0,0 +1,156 @@ +/* + * H.266 / VVC shared code + * + * 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_VVC_H +#define AVCODEC_VVC_H + +/** + * Table 5 – NAL unit type codes and NAL unit type classes + * in T-REC-H.266-202008 + */ +enum VVCNALUnitType { + VVC_TRAIL_NUT = 0, + VVC_STSA_NUT = 1, + VVC_RADL_NUT = 2, + VVC_RASL_NUT = 3, + VVC_RSV_VCL_4 = 4, + VVC_RSV_VCL_5 = 5, + VVC_RSV_VCL_6 = 6, + VVC_IDR_W_RADL = 7, + VVC_IDR_N_LP = 8, + VVC_CRA_NUT = 9, + VVC_GDR_NUT = 10, + VVC_RSV_IRAP_11 = 11, + VVC_OPI_NUT = 12, + VVC_DCI_NUT = 13, + VVC_VPS_NUT = 14, + VVC_SPS_NUT = 15, + VVC_PPS_NUT = 16, + VVC_PREFIX_APS_NUT = 17, + VVC_SUFFIX_APS_NUT = 18, + VVC_PH_NUT = 19, + VVC_AUD_NUT = 20, + VVC_EOS_NUT = 21, + VVC_EOB_NUT = 22, + VVC_PREFIX_SEI_NUT = 23, + VVC_SUFFIX_SEI_NUT = 24, + VVC_FD_NUT = 25, + VVC_RSV_NVCL_26 = 26, + VVC_RSV_NVCL_27 = 27, + VVC_UNSPEC_28 = 28, + VVC_UNSPEC_29 = 29, + VVC_UNSPEC_30 = 30, + VVC_UNSPEC_31 = 31, +}; + +enum VVCSliceType { + VVC_SLICE_TYPE_B = 0, + VVC_SLICE_TYPE_P = 1, + VVC_SLICE_TYPE_I = 2, +}; + +enum VVCAPSType { + VVC_ASP_TYPE_ALF = 0, + VVC_ASP_TYPE_LMCS = 1, + VVC_ASP_TYPE_SCALING = 2, +}; + +enum { + //6.2 we can have 3 sample arrays + VVC_MAX_SAMPLE_ARRAYS = 3, + + //7.4.3.3 vps_max_layers_minus1 is u(6) + VVC_MAX_LAYERS = 64, + + //7.4.3.3 The value of vps_max_sublayers_minus1 shall be in the range of 0 to 6, inclusive + VVC_MAX_SUBLAYERS = 7, + + //7.3.2.1 dci_num_ptls_minus1 is u(4) + VVC_MAX_DCI_PTLS = 16, + + //7.4.3.3 vps_num_ptls_minus1 is u(8) + VVC_MAX_PTLS = 256, + + //7.4.3.3 vps_num_output_layer_sets_minus2 is u(8) + VVC_MAX_TOTAL_NUM_OLSS = 257, + + // 7.3.2.3: vps_video_parameter_set_id is u(4). + VVC_MAX_VPS_COUNT = 16, + // 7.3.2.4: sps_seq_parameter_set_id is u(4) + VVC_MAX_SPS_COUNT = 16, + // 7.3.2.5: pps_pic_parameter_set_id is u(6) + VVC_MAX_PPS_COUNT = 64, + + // 7.4.4.1: ptl_num_sub_profiles is u(8) + VVC_MAX_SUB_PROFILES = 256, + + // 7.4.3.18: The variable NumAlfFilters specifying the number of different adaptive loop + // filters is set equal to 25. + VVC_NUM_ALF_FILTERS = 25, + + // A.4.2: according to (1577), MaxDpbSize is bounded above by 2 * maxDpbPicBuf(8) + VVC_MAX_DPB_SIZE = 16, + + //7.4.3.4 sps_num_ref_pic_lists in range [0, 64] + VVC_MAX_REF_PIC_LISTS = 64, + + //7.4.11 num_ref_entries in range [0, MaxDpbSize + 13] + VVC_MAX_REF_ENTRIES = VVC_MAX_DPB_SIZE + 13, + + //7.4.3.3 sps_num_points_in_qp_table_minus1[i] in range [0, 36 − sps_qp_table_start_minus26[i]], + //and sps_qp_table_start_minus26[i] in range [−26 − QpBdOffset, 36]. + //so sps_num_points_in_qp_table_minus1[i] should in range [0, 62 + QpBdOffset] + //since 16 bits QpBdOffset is 48, sps_num_points_in_qp_table_minus1[i] should range [0, 110] + VVC_MAX_POINTS_IN_QP_TABLE = 111, + + // 7.4.6.1: hrd_cpb_cnt_minus1 is in [0, 31]. + VVC_MAX_CPB_CNT = 32, + + // A.4.1: the highest level allows a MaxLumaPs of 35 651 584. + VVC_MAX_LUMA_PS = 35651584, + + // A.4.1: pic_width_in_luma_samples and pic_height_in_luma_samples are + // constrained to be not greater than sqrt(MaxLumaPs * 8). Hence height/ + // width are bounded above by sqrt(8 * 35651584) = 16888.2 samples. + VVC_MAX_WIDTH = 16888, + VVC_MAX_HEIGHT = 16888, + + // A.4.1: table A.1 allows at most 440 tiles per au for any level. + VVC_MAX_TILES_PER_AU = 440, + // A.4.1: table A.1 did not define max tile rows. + // in worest a case, we can have 1x440 tiles picture. + VVC_MAX_TILE_ROWS = VVC_MAX_TILES_PER_AU, + // A.4.1: table A.1 allows at most 20 tile columns for any level. + VVC_MAX_TILE_COLUMNS = 20, + + // A.4.1 table A.1 allows at most 600 slice for any level. + VVC_MAX_SLICES = 600, + + // 7.4.8: in the worst case (!pps_no_pic_partition_flag and + // sps_entropy_coding_sync_enabled_flag are both true), entry points can be + // placed at the beginning of every Ctb row in every tile, giving an + // upper bound of (num_tile_columns_minus1 + 1) * PicHeightInCtbsY - 1. + // Only a stream with very high resolution and perverse parameters could + // get near that, though, so set a lower limit here with the maximum + // possible value for 8K video (at most 135 32x32 Ctb rows). + VVC_MAX_ENTRY_POINTS = VVC_MAX_TILE_COLUMNS * 135, +}; + +#endif /* AVCODEC_VVC_H */ diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile new file mode 100644 index 00000000000..5dfd9c85615 --- /dev/null +++ b/libavcodec/vvc/Makefile @@ -0,0 +1,16 @@ +clean:: + $(RM) $(CLEANSUFFIXES:%=libavcodec/vvc/%) + +OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \ + vvc/vvcdsp.o \ + vvc/vvc_cabac.o \ + vvc/vvc_ctu.o \ + vvc/vvc_data.o \ + vvc/vvc_filter.o \ + vvc/vvc_inter.o \ + vvc/vvc_intra.o \ + vvc/vvc_itx_1d.o \ + vvc/vvc_mvs.o \ + vvc/vvc_ps.o \ + vvc/vvc_refs.o \ + vvc/vvc_thread.o diff --git a/libavcodec/vvc/vvc_cabac.c b/libavcodec/vvc/vvc_cabac.c new file mode 100644 index 00000000000..7275f44c4dd --- /dev/null +++ b/libavcodec/vvc/vvc_cabac.c @@ -0,0 +1,2484 @@ +/* + * VVC CABAC decoder + * + * Copyright (C) 2021 Nuo Mi + * + * 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 "libavcodec/cabac_functions.h" + +#include "vvc_cabac.h" +#include "vvc_ctu.h" +#include "vvc_data.h" + +#define CABAC_MAX_BIN 31 + +#define CNU 35 + +enum SyntaxElement { + ALF_CTB_FLAG = 0, + ALF_USE_APS_FLAG = ALF_CTB_FLAG + 9, + ALF_CTB_CC_CB_IDC, + ALF_CTB_CC_CR_IDC = ALF_CTB_CC_CB_IDC + 3, + ALF_CTB_FILTER_ALT_IDX = ALF_CTB_CC_CR_IDC + 3, + SAO_MERGE_FLAG = ALF_CTB_FILTER_ALT_IDX + 2, + SAO_TYPE_IDX, + SPLIT_CU_FLAG, + SPLIT_QT_FLAG = SPLIT_CU_FLAG + 9, + MTT_SPLIT_CU_VERTICAL_FLAG = SPLIT_QT_FLAG + 6, + MTT_SPLIT_CU_BINARY_FLAG = MTT_SPLIT_CU_VERTICAL_FLAG + 5, + NON_INTER_FLAG = MTT_SPLIT_CU_BINARY_FLAG + 4, + CU_SKIP_FLAG = NON_INTER_FLAG + 2, + PRED_MODE_IBC_FLAG = CU_SKIP_FLAG + 3, + PRED_MODE_FLAG = PRED_MODE_IBC_FLAG + 3, + PRED_MODE_PLT_FLAG = PRED_MODE_FLAG + 2, + CU_ACT_ENABLED_FLAG, + INTRA_BDPCM_LUMA_FLAG, + INTRA_BDPCM_LUMA_DIR_FLAG, + INTRA_MIP_FLAG, + INTRA_LUMA_REF_IDX = INTRA_MIP_FLAG + 4, + INTRA_SUBPARTITIONS_MODE_FLAG = INTRA_LUMA_REF_IDX + 2, + INTRA_SUBPARTITIONS_SPLIT_FLAG, + INTRA_LUMA_MPM_FLAG, + INTRA_LUMA_NOT_PLANAR_FLAG, + INTRA_BDPCM_CHROMA_FLAG = INTRA_LUMA_NOT_PLANAR_FLAG + 2, + INTRA_BDPCM_CHROMA_DIR_FLAG, + CCLM_MODE_FLAG, + CCLM_MODE_IDX, + INTRA_CHROMA_PRED_MODE, + GENERAL_MERGE_FLAG, + INTER_PRED_IDC, + INTER_AFFINE_FLAG = INTER_PRED_IDC + 6, + CU_AFFINE_TYPE_FLAG = INTER_AFFINE_FLAG + 3, + SYM_MVD_FLAG, + REF_IDX_LX, + MVP_LX_FLAG = REF_IDX_LX + 2, + AMVR_FLAG, + AMVR_PRECISION_IDX = AMVR_FLAG + 2, + BCW_IDX = AMVR_PRECISION_IDX + 3, + CU_CODED_FLAG, + CU_SBT_FLAG, + CU_SBT_QUAD_FLAG = CU_SBT_FLAG + 2, + CU_SBT_HORIZONTAL_FLAG, + CU_SBT_POS_FLAG = CU_SBT_HORIZONTAL_FLAG + 3, + LFNST_IDX, + MTS_IDX = LFNST_IDX + 3, + COPY_ABOVE_PALETTE_INDICES_FLAG = MTS_IDX + 4, + PALETTE_TRANSPOSE_FLAG, + RUN_COPY_FLAG, + REGULAR_MERGE_FLAG = RUN_COPY_FLAG + 8, + MMVD_MERGE_FLAG = REGULAR_MERGE_FLAG + 2, + MMVD_CAND_FLAG, + MMVD_DISTANCE_IDX, + CIIP_FLAG, + MERGE_SUBBLOCK_FLAG, + MERGE_SUBBLOCK_IDX = MERGE_SUBBLOCK_FLAG + 3, + MERGE_IDX, + ABS_MVD_GREATER0_FLAG, + ABS_MVD_GREATER1_FLAG, + TU_Y_CODED_FLAG, + TU_CB_CODED_FLAG = TU_Y_CODED_FLAG + 4, + TU_CR_CODED_FLAG = TU_CB_CODED_FLAG + 2, + CU_QP_DELTA_ABS = TU_CR_CODED_FLAG + 3, + CU_CHROMA_QP_OFFSET_FLAG = CU_QP_DELTA_ABS + 2, + CU_CHROMA_QP_OFFSET_IDX, + TRANSFORM_SKIP_FLAG, + TU_JOINT_CBCR_RESIDUAL_FLAG = TRANSFORM_SKIP_FLAG + 2, + LAST_SIG_COEFF_X_PREFIX = TU_JOINT_CBCR_RESIDUAL_FLAG + 3, + LAST_SIG_COEFF_Y_PREFIX = LAST_SIG_COEFF_X_PREFIX +23, + SB_CODED_FLAG = LAST_SIG_COEFF_Y_PREFIX +23, + SIG_COEFF_FLAG = SB_CODED_FLAG + 7, + PAR_LEVEL_FLAG = SIG_COEFF_FLAG +63, + ABS_LEVEL_GTX_FLAG = PAR_LEVEL_FLAG +33, + COEFF_SIGN_FLAG = ABS_LEVEL_GTX_FLAG +72, + SYNTAX_ELEMENT_LAST = COEFF_SIGN_FLAG + 6, +}; + +static const uint8_t init_values[4][SYNTAX_ELEMENT_LAST] = { + { + //alf_ctb_flag + 62, 39, 39, 54, 39, 39, 31, 39, 39, + //alf_use_aps_flag + 46, + //alf_ctb_cc_cb_idc + 18, 30, 31, + //alf_ctb_cc_cr_idc + 18, 30, 31, + //alf_ctb_filter_alt_idx + 11, 11, + //sao_merge_left_flag and sao_merge_up_flag + 60, + //sao_type_idx_luma and sao_type_idx_chroma + 13, + //split_cu_flag + 19, 28, 38, 27, 29, 38, 20, 30, 31, + //split_qt_flag + 27, 6, 15, 25, 19, 37, + //mtt_split_cu_vertical_flag + 43, 42, 29, 27, 44, + //mtt_split_cu_binary_flag + 36, 45, 36, 45, + //non_inter_flag + CNU, CNU, + //cu_skip_flag + 0, 26, 28, + //pred_mode_ibc_flag + 17, 42, 36, + //pred_mode_flag + CNU, CNU, + //pred_mode_plt_flag + 25, + //cu_act_enabled_flag + 52, + //intra_bdpcm_luma_flag + 19, + //intra_bdpcm_luma_dir_flag + 35, + //intra_mip_flag + 33, 49, 50, 25, + //intra_luma_ref_idx + 25, 60, + //intra_subpartitions_mode_flag + 33, + //intra_subpartitions_split_flag + 43, + //intra_luma_mpm_flag + 45, + //intra_luma_not_planar_flag + 13, 28, + //intra_bdpcm_chroma_flag + 1, + //intra_bdpcm_chroma_dir_flag + 27, + //cclm_mode_flag + 59, + //cclm_mode_idx + 27, + //intra_chroma_pred_mode + 34, + //general_merge_flag + 26, + //inter_pred_idc + CNU, CNU, CNU, CNU, CNU, CNU, + //inter_affine_flag + CNU, CNU, CNU, + //cu_affine_type_flag + CNU, + //sym_mvd_flag + CNU, + //ref_idx_l0 and ref_idx_l1 + CNU, CNU, + //mvp_l0_flag and mvp_l1_flag + 42, + //amvr_flag + CNU, CNU, + //amvr_precision_idx + 35, 34, 35, + //bcw_idx + CNU, + //cu_coded_flag + 6, + //cu_sbt_flag + CNU, CNU, + //cu_sbt_quad_flag + CNU, + //cu_sbt_horizontal_flag + CNU, CNU, CNU, + //cu_sbt_pos_flag + CNU, + //lfnst_idx + 28, 52, 42, + //mts_idx + 29, 0, 28, 0, + //copy_above_palette_indices_flag + 42, + //palette_transpose_flag + 42, + //run_copy_flag + 50, 37, 45, 30, 46, 45, 38, 46, + //regular_merge_flag + CNU, CNU, + //mmvd_merge_flag + CNU, + //mmvd_cand_flag + CNU, + //mmvd_distance_idx + CNU, + //ciip_flag + CNU, + //merge_subblock_flag + CNU, CNU, CNU, + //merge_subblock_idx + CNU, + //merge_idx, merge_gpm_idx0, and merge_gpm_idx1 + 34, + //abs_mvd_greater0_flag + 14, + //abs_mvd_greater1_flag + 45, + //tu_y_coded_flag + 15, 12, 5, 7, + //tu_cb_coded_flag + 12, 21, + //tu_cr_coded_flag + 33, 28, 36, + //cu_qp_delta_abs + CNU, CNU, + //cu_chroma_qp_offset_flag + CNU, + //cu_chroma_qp_offset_idx + CNU, + //transform_skip_flag + 25, 9, + //tu_joint_cbcr_residual_flag + 12, 21, 35, + //last_sig_coeff_x_prefix + 13, 5, 4, 21, 14, 4, 6, 14, 21, 11, 14, 7, 14, 5, 11, 21, + 30, 22, 13, 42, 12, 4, 3, + //last_sig_coeff_y_prefix + 13, 5, 4, 6, 13, 11, 14, 6, 5, 3, 14, 22, 6, 4, 3, 6, + 22, 29, 20, 34, 12, 4, 3, + //sb_coded_flag + 18, 31, 25, 15, 18, 20, 38, + //sig_coeff_flag + 25, 19, 28, 14, 25, 20, 29, 30, 19, 37, 30, 38, 11, 38, 46, 54, + 27, 39, 39, 39, 44, 39, 39, 39, 18, 39, 39, 39, 27, 39, 39, 39, + 0, 39, 39, 39, 25, 27, 28, 37, 34, 53, 53, 46, 19, 46, 38, 39, + 52, 39, 39, 39, 11, 39, 39, 39, 19, 39, 39, 39, 25, 28, 38, + //par_level_flag + 33, 25, 18, 26, 34, 27, 25, 26, 19, 42, 35, 33, 19, 27, 35, 35, + 34, 42, 20, 43, 20, 33, 25, 26, 42, 19, 27, 26, 50, 35, 20, 43, + 11, + //abs_level_gtx_flag + 25, 25, 11, 27, 20, 21, 33, 12, 28, 21, 22, 34, 28, 29, 29, 30, + 36, 29, 45, 30, 23, 40, 33, 27, 28, 21, 37, 36, 37, 45, 38, 46, + 25, 1, 40, 25, 33, 11, 17, 25, 25, 18, 4, 17, 33, 26, 19, 13, + 33, 19, 20, 28, 22, 40, 9, 25, 18, 26, 35, 25, 26, 35, 28, 37, + 11, 5, 5, 14, 10, 3, 3, 3, + //coeff_sign_flag + 12, 17, 46, 28, 25, 46, + }, + { + //alf_ctb_flag + 13, 23, 46, 4, 61, 54, 19, 46, 54, + //alf_use_aps_flag + 46, + //alf_ctb_cc_cb_idc + 18, 21, 38, + //alf_ctb_cc_cr_idc + 18, 21, 38, + //alf_ctb_filter_alt_idx + 20, 12, + //sao_merge_left_flag and sao_merge_up_flag + 60, + //sao_type_idx_luma and sao_type_idx_chroma + 5, + //split_cu_flag + 11, 35, 53, 12, 6, 30, 13, 15, 31, + //split_qt_flag + 20, 14, 23, 18, 19, 6, + //mtt_split_cu_vertical_flag + 43, 35, 37, 34, 52, + //mtt_split_cu_binary_flag + 43, 37, 21, 22, + //non_inter_flag + 25, 12, + //cu_skip_flag + 57, 59, 45, + //pred_mode_ibc_flag + 0, 57, 44, + //pred_mode_flag + 40, 35, + //pred_mode_plt_flag + 0, + //cu_act_enabled_flag + 46, + //intra_bdpcm_luma_flag + 40, + //intra_bdpcm_luma_dir_flag + 36, + //intra_mip_flag + 41, 57, 58, 26, + //intra_luma_ref_idx + 25, 58, + //intra_subpartitions_mode_flag + 33, + //intra_subpartitions_split_flag + 36, + //intra_luma_mpm_flag + 36, + //intra_luma_not_planar_flag + 12, 20, + //intra_bdpcm_chroma_flag + 0, + //intra_bdpcm_chroma_dir_flag + 13, + //cclm_mode_flag + 34, + //cclm_mode_idx + 27, + //intra_chroma_pred_mode + 25, + //general_merge_flag + 21, + //inter_pred_idc + 7, 6, 5, 12, 4, 40, + //inter_affine_flag + 12, 13, 14, + //cu_affine_type_flag + 35, + //sym_mvd_flag + 28, + //ref_idx_l0 and ref_idx_l1 + 20, 35, + //mvp_l0_flag and mvp_l1_flag + 34, + //amvr_flag + 59, 58, + //amvr_precision_idx + 60, 48, 60, + //bcw_idx + 4, + //cu_coded_flag + 5, + //cu_sbt_flag + 56, 57, + //cu_sbt_quad_flag + 42, + //cu_sbt_horizontal_flag + 20, 43, 12, + //cu_sbt_pos_flag + 28, + //lfnst_idx + 37, 45, 27, + //mts_idx + 45, 40, 27, 0, + //copy_above_palette_indices_flag + 59, + //palette_transpose_flag + 42, + //run_copy_flag + 51, 30, 30, 38, 23, 38, 53, 46, + //regular_merge_flag + 38, 7, + //mmvd_merge_flag + 26, + //mmvd_cand_flag + 43, + //mmvd_distance_idx + 60, + //ciip_flag + 57, + //merge_subblock_flag + 48, 57, 44, + //merge_subblock_idx + 5, + //merge_idx, merge_gpm_idx0, and merge_gpm_idx1 + 20, + //abs_mvd_greater0_flag + 44, + //abs_mvd_greater1_flag + 43, + //tu_y_coded_flag + 23, 5, 20, 7, + //tu_cb_coded_flag + 25, 28, + //tu_cr_coded_flag + 25, 29, 45, + //cu_qp_delta_abs + CNU, CNU, + //cu_chroma_qp_offset_flag + CNU, + //cu_chroma_qp_offset_idx + CNU, + //transform_skip_flag + 25, 9, + //tu_joint_cbcr_residual_flag + 27, 36, 45, + //last_sig_coeff_x_prefix + 6, 13, 12, 6, 6, 12, 14, 14, 13, 12, 29, 7, 6, 13, 36, 28, + 14, 13, 5, 26, 12, 4, 18, + //last_sig_coeff_y_prefix + 5, 5, 12, 6, 6, 4, 6, 14, 5, 12, 14, 7, 13, 5, 13, 21, + 14, 20, 12, 34, 11, 4, 18, + //sb_coded_flag + 25, 30, 25, 45, 18, 12, 29, + //sig_coeff_flag + 17, 41, 42, 29, 25, 49, 43, 37, 33, 58, 51, 30, 19, 38, 38, 46, + 34, 54, 54, 39, 6, 39, 39, 39, 19, 39, 54, 39, 19, 39, 39, 39, + 56, 39, 39, 39, 17, 34, 35, 21, 41, 59, 60, 38, 35, 45, 53, 54, + 44, 39, 39, 39, 34, 38, 62, 39, 26, 39, 39, 39, 40, 35, 44, + //par_level_flag + 18, 17, 33, 18, 26, 42, 25, 33, 26, 42, 27, 25, 34, 42, 42, 35, + 26, 27, 42, 20, 20, 25, 25, 26, 11, 19, 27, 33, 42, 35, 35, 43, + 3, + //abs_level_gtx_flag + 0, 17, 26, 19, 35, 21, 25, 34, 20, 28, 29, 33, 27, 28, 29, 22, + 34, 28, 44, 37, 38, 0, 25, 19, 20, 13, 14, 57, 44, 30, 30, 23, + 17, 0, 1, 17, 25, 18, 0, 9, 25, 33, 34, 9, 25, 18, 26, 20, + 25, 18, 19, 27, 29, 17, 9, 25, 10, 18, 4, 17, 33, 19, 20, 29, + 18, 11, 4, 28, 2, 10, 3, 3, + //coeff_sign_flag + 5, 10, 53, 43, 25, 46, + }, + { + //alf_ctb_flag + 33, 52, 46, 25, 61, 54, 25, 61, 54, + //alf_use_aps_flag + 46, + //alf_ctb_cc_cb_idc + 25, 35, 38, + //alf_ctb_cc_cr_idc + 25, 28, 38, + //alf_ctb_filter_alt_idx + 11, 26, + //sao_merge_left_flag and sao_merge_up_flag + 2, + //sao_type_idx_luma and sao_type_idx_chroma + 2, + //split_cu_flag + 18, 27, 15, 18, 28, 45, 26, 7, 23, + //split_qt_flag + 26, 36, 38, 18, 34, 21, + //mtt_split_cu_vertical_flag + 43, 42, 37, 42, 44, + //mtt_split_cu_binary_flag + 28, 29, 28, 29, + //non_inter_flag + 25, 20, + //cu_skip_flag + 57, 60, 46, + //pred_mode_ibc_flag + 0, 43, 45, + //pred_mode_flag + 40, 35, + //pred_mode_plt_flag + 17, + //cu_act_enabled_flag + 46, + //intra_bdpcm_luma_flag + 19, + //intra_bdpcm_luma_dir_flag + 21, + //intra_mip_flag + 56, 57, 50, 26, + //intra_luma_ref_idx + 25, 59, + //intra_subpartitions_mode_flag + 33, + //intra_subpartitions_split_flag + 43, + //intra_luma_mpm_flag + 44, + //intra_luma_not_planar_flag + 13, 6, + //intra_bdpcm_chroma_flag + 0, + //intra_bdpcm_chroma_dir_flag + 28, + //cclm_mode_flag + 26, + //cclm_mode_idx + 27, + //intra_chroma_pred_mode + 25, + //general_merge_flag + 6, + //inter_pred_idc + 14, 13, 5, 4, 3, 40, + //inter_affine_flag + 19, 13, 6, + //cu_affine_type_flag + 35, + //sym_mvd_flag + 28, + //ref_idx_l0 and ref_idx_l1 + 5, 35, + //mvp_l0_flag and mvp_l1_flag + 34, + //amvr_flag + 59, 50, + //amvr_precision_idx + 38, 26, 60, + //bcw_idx + 5, + //cu_coded_flag + 12, + //cu_sbt_flag + 41, 57, + //cu_sbt_quad_flag + 42, + //cu_sbt_horizontal_flag + 35, 51, 27, + //cu_sbt_pos_flag + 28, + //lfnst_idx + 52, 37, 27, + //mts_idx + 45, 25, 27, 0, + //copy_above_palette_indices_flag + 50, + //palette_transpose_flag + 35, + //run_copy_flag + 58, 45, 45, 30, 38, 45, 38, 46, + //regular_merge_flag + 46, 15, + //mmvd_merge_flag + 25, + //mmvd_cand_flag + 43, + //mmvd_distance_idx + 59, + //ciip_flag + 57, + //merge_subblock_flag + 25, 58, 45, + //merge_subblock_idx + 4, + //merge_idx, merge_gpm_idx0, and merge_gpm_idx1 + 18, + //abs_mvd_greater0_flag + 51, + //abs_mvd_greater1_flag + 36, + //tu_y_coded_flag + 15, 6, 5, 14, + //tu_cb_coded_flag + 25, 37, + //tu_cr_coded_flag + 9, 36, 45, + //cu_qp_delta_abs + CNU, CNU, + //cu_chroma_qp_offset_flag + CNU, + //cu_chroma_qp_offset_idx + CNU, + //transform_skip_flag + 25, 17, + //tu_joint_cbcr_residual_flag + 42, 43, 52, + //last_sig_coeff_x_prefix + 6, 6, 12, 14, 6, 4, 14, 7, 6, 4, 29, 7, 6, 6, 12, 28, + 7, 13, 13, 35, 19, 5, 4, + //last_sig_coeff_y_prefix + 5, 5, 20, 13, 13, 19, 21, 6, 12, 12, 14, 14, 5, 4, 12, 13, + 7, 13, 12, 41, 11, 5, 27, + //sb_coded_flag + 25, 45, 25, 14, 18, 35, 45, + //sig_coeff_flag + 17, 41, 49, 36, 1, 49, 50, 37, 48, 51, 58, 45, 26, 45, 53, 46, + 49, 54, 61, 39, 35, 39, 39, 39, 19, 54, 39, 39, 50, 39, 39, 39, + 0, 39, 39, 39, 9, 49, 50, 36, 48, 59, 59, 38, 34, 45, 38, 31, + 58, 39, 39, 39, 34, 38, 54, 39, 41, 39, 39, 39, 25, 50, 37, + //par_level_flag + 33, 40, 25, 41, 26, 42, 25, 33, 26, 34, 27, 25, 41, 42, 42, 35, + 33, 27, 35, 42, 43, 33, 25, 26, 34, 19, 27, 33, 42, 43, 35, 43, + 11, + //abs_level_gtx_flag + 0, 0, 33, 34, 35, 21, 25, 34, 35, 28, 29, 40, 42, 43, 29, 30, + 49, 36, 37, 45, 38, 0, 40, 34, 43, 36, 37, 57, 52, 45, 38, 46, + 25, 0, 0, 17, 25, 26, 0, 9, 25, 33, 19, 0, 25, 33, 26, 20, + 25, 33, 27, 35, 22, 25, 1, 25, 33, 26, 12, 25, 33, 27, 28, 37, + 19, 11, 4, 6, 3, 4, 4, 5, + //coeff_sign_flag + 35, 25, 46, 28, 33, 38, + }, + //shiftIdx + { + //alf_ctb_flag + 0, 0, 0, 4, 0, 0, 1, 0, 0, + //alf_use_aps_flag + 0, + //alf_ctb_cc_cb_idc + 4, 1, 4, + //alf_ctb_cc_cr_idc + 4, 1, 4, + //alf_ctb_filter_alt_idx + 0, 0, + //sao_merge_left_flag and sao_merge_up_flag + 0, + //sao_type_idx_luma and sao_type_idx_chroma + 4, + //split_cu_flag + 12, 13, 8, 8, 13, 12, 5, 9, 9, + //split_qt_flag + 0, 8, 8, 12, 12, 8, + //mtt_split_cu_vertical_flag + 9, 8, 9, 8, 5, + //mtt_split_cu_binary_flag + 12, 13, 12, 13, + //non_inter_flag + 1, 0, + //cu_skip_flag + 5, 4, 8, + //pred_mode_ibc_flag + 1, 5, 8, + //pred_mode_flag + 5, 1, + //pred_mode_plt_flag + 1, + //cu_act_enabled_flag + 1, + //intra_bdpcm_luma_flag + 1, + //intra_bdpcm_luma_dir_flag + 4, + //intra_mip_flag + 9, 10, 9, 6, + //intra_luma_ref_idx + 5, 8, + //intra_subpartitions_mode_flag + 9, + //intra_subpartitions_split_flag + 2, + //intra_luma_mpm_flag + 6, + //intra_luma_not_planar_flag + 1, 5, + //intra_bdpcm_chroma_flag + 1, + //intra_bdpcm_chroma_dir_flag + 0, + //cclm_mode_flag + 4, + //cclm_mode_idx + 9, + //intra_chroma_pred_mode + 5, + //general_merge_flag + 4, + //inter_pred_idc + 0, 0, 1, 4, 4, 0, + //inter_affine_flag + 4, 0, 0, + //cu_affine_type_flag + 4, + //sym_mvd_flag + 5, + //ref_idx_l0 and ref_idx_l1 + 0, 4, + //mvp_l0_flag and mvp_l1_flag + 12, + //amvr_flag + 0, 0, + //amvr_precision_idx + 4, 5, 0, + //bcw_idx + 1, + //cu_coded_flag + 4, + //cu_sbt_flag + 1, 5, + //cu_sbt_quad_flag + 10, + //cu_sbt_horizontal_flag + 8, 4, 1, + //cu_sbt_pos_flag + 13, + //lfnst_idx + 9, 9, 10, + //mts_idx + 8, 0, 9, 0, + //copy_above_palette_indices_flag + 9, + //palette_transpose_flag + 5, + //run_copy_flag + 9, 6, 9, 10, 5, 0, 9, 5, + //regular_merge_flag + 5, 5, + //mmvd_merge_flag + 4, + //mmvd_cand_flag + 10, + //mmvd_distance_idx + 0, + //ciip_flag + 1, + //merge_subblock_flag + 4, 4, 4, + //merge_subblock_idx + 0, + //merge_idx, merge_gpm_idx0, and merge_gpm_idx1 + 4, + //abs_mvd_greater0_flag + 9, + //abs_mvd_greater1_flag + 5, + //tu_y_coded_flag + 5, 1, 8, 9, + //tu_cb_coded_flag + 5, 0, + //tu_cr_coded_flag + 2, 1, 0, + //cu_qp_delta_abs + 8, 8, + //cu_chroma_qp_offset_flag + 8, + //cu_chroma_qp_offset_idx + 8, + //transform_skip_flag + 1, 1, + //tu_joint_cbcr_residual_flag + 1, 1, 0, + //last_sig_coeff_x_prefix + 8, 5, 4, 5, 4, 4, 5, 4, 1, 0, 4, 1, 0, 0, 0, 0, + 1, 0, 0, 0, 5, 4, 4, + //last_sig_coeff_y_prefix + 8, 5, 8, 5, 5, 4, 5, 5, 4, 0, 5, 4, 1, 0, 0, 1, + 4, 0, 0, 0, 6, 5, 5, + //sb_coded_flag + 8, 5, 5, 8, 5, 8, 8, + //sig_coeff_flag + 12, 9, 9, 10, 9, 9, 9, 10, 8, 8, 8, 10, 9, 13, 8, 8, + 8, 8, 8, 5, 8, 0, 0, 0, 8, 8, 8, 8, 8, 0, 4, 4, + 0, 0, 0, 0, 12, 12, 9, 13, 4, 5, 8, 9, 8, 12, 12, 8, + 4, 0, 0, 0, 8, 8, 8, 8, 4, 0, 0, 0, 13, 13, 8, + //par_level_flag + 8, 9, 12, 13, 13, 13, 10, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 10, 13, 13, 13, 13, 8, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, + 6, + //abs_level_gtx_flag + 9, 5, 10, 13, 13, 10, 9, 10, 13, 13, 13, 9, 10, 10, 10, 13, + 8, 9, 10, 10, 13, 8, 8, 9, 12, 12, 10, 5, 9, 9, 9, 13, + 1, 5, 9, 9, 9, 6, 5, 9, 10, 10, 9, 9, 9, 9, 9, 9, + 6, 8, 9, 9, 10, 1, 5, 8, 8, 9, 6, 6, 9, 8, 8, 9, + 4, 2, 1, 6, 1, 1, 1, 1, + //coeff_sign_flag + 1, 4, 4, 5, 8, 8, + } +}; + +#define MAX_SUB_BLOCKS 16 +#define MAX_SUB_BLOCK_SIZE 4 +#define MAX_TB_SIZE 64 + +typedef struct ResidualCoding { + //common for ts and non ts + TransformBlock *tb; + + int log2_sb_w; + int log2_sb_h; + int last_sub_block; + int hist_value; + int update_hist; + int num_sb_coeff; + int rem_bins_pass1; + + int width_in_sbs; + int height_in_sbs; + int nb_sbs; + + const uint8_t *sb_scan_x_off; + const uint8_t *sb_scan_y_off; + const uint8_t *scan_x_off; + const uint8_t *scan_y_off; + + uint8_t sb_coded_flag[MAX_SUB_BLOCKS * MAX_SUB_BLOCKS]; + int sig_coeff_flag[MAX_TB_SIZE * MAX_TB_SIZE]; + int abs_level_pass1[MAX_TB_SIZE * MAX_TB_SIZE]; ///< AbsLevelPass1[][] + int abs_level[MAX_TB_SIZE * MAX_TB_SIZE]; + + //for ts only + uint8_t infer_sb_cbf; + int coeff_sign_level[MAX_TB_SIZE * MAX_TB_SIZE]; ///< CoeffSignLevel[][] + + //for non ts only + int qstate; + int last_scan_pos; + int last_significant_coeff_x; + int last_significant_coeff_y; +} ResidualCoding; + +static int cabac_reinit(VVCLocalContext *lc) +{ + return skip_bytes(&lc->ep->cc, 0) == NULL ? AVERROR_INVALIDDATA : 0; +} + +static void cabac_init_state(VVCLocalContext *lc) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int qp = av_clip_uintp2(lc->sc->sh.slice_qp_y, 6); + int init_type = 2 - rsh->sh_slice_type; + + av_assert0(VVC_CONTEXTS == SYNTAX_ELEMENT_LAST); + + ff_vvc_ep_init_stat_coeff(lc->ep, sps->bit_depth, sps->r->sps_persistent_rice_adaptation_enabled_flag); + + if (rsh->sh_cabac_init_flag && !IS_I(rsh)) + init_type ^= 3; + + for (int i = 0; i < VVC_CONTEXTS; i++) { + VVCCabacState *state = &lc->ep->cabac_state[i]; + const int init_value = init_values[init_type][i]; + const int shift_idx = init_values[3][i]; + const int m = (init_value >> 3) - 4; + const int n = ((init_value & 7) * 18) + 1; + const int pre = av_clip(((m * (qp - 16)) >> 1) + n, 1, 127); + + state->state[0] = pre << 3; + state->state[1] = pre << 7; + state->shift[0] = (shift_idx >> 2 ) + 2; + state->shift[1] = (shift_idx & 3 ) + 3 + state->shift[0]; + } +} + +int ff_vvc_cabac_init(VVCLocalContext *lc, + const int ctu_idx, const int rx, const int ry) +{ + int ret = 0; + const VVCPPS *pps = lc->fc->ps.pps; + const int first_ctb_in_slice = !ctu_idx; + const int first_ctb_in_tile = rx == pps->ctb_to_col_bd[rx] && ry == pps->ctb_to_row_bd[ry]; + + if (first_ctb_in_slice|| first_ctb_in_tile) { + if (lc->sc->nb_eps == 1 && !first_ctb_in_slice) + ret = cabac_reinit(lc); + if (ret == 0) + cabac_init_state(lc); + } + return ret; +} + +//fixme +static void vvc_refill2(CABACContext* c) { + int i; + unsigned x; +#if !HAVE_FAST_CLZ + x = c->low ^ (c->low - 1); + i = 7 - ff_h264_norm_shift[x >> (CABAC_BITS - 1)]; +#else + i = ff_ctz(c->low) - CABAC_BITS; +#endif + + x = -CABAC_MASK; + +#if CABAC_BITS == 16 + x += (c->bytestream[0] << 9) + (c->bytestream[1] << 1); +#else + x += c->bytestream[0] << 1; +#endif + + c->low += x << i; +#if !UNCHECKED_BITSTREAM_READER + if (c->bytestream < c->bytestream_end) +#endif + c->bytestream += CABAC_BITS / 8; +} + +static int inline vvc_get_cabac(CABACContext *c, VVCCabacState* base, const int ctx) +{ + VVCCabacState *s = base + ctx; + const int qRangeIdx = c->range >> 5; + const int pState = s->state[1] + (s->state[0] << 4); + const int valMps = pState >> 14; + const int RangeLPS = (qRangeIdx * ((valMps ? 32767 - pState : pState) >> 9 ) >> 1) + 4; + int bit, lps_mask; + + c->range -= RangeLPS; + lps_mask = ((c->range<<(CABAC_BITS+1)) - c->low)>>31; + + c->low -= (c->range<<(CABAC_BITS+1)) & lps_mask; + c->range += (RangeLPS - c->range) & lps_mask; + + bit = valMps ^ (lps_mask & 1); + + lps_mask = ff_h264_norm_shift[c->range]; + c->range <<= lps_mask; + c->low <<= lps_mask; + + if (!(c->low & CABAC_MASK)) + vvc_refill2(c); + s->state[0] = s->state[0] - (s->state[0] >> s->shift[0]) + (1023 * bit >> s->shift[0]); + s->state[1] = s->state[1] - (s->state[1] >> s->shift[1]) + (16383 * bit >> s->shift[1]); + return bit; +} + +#define GET_CABAC(ctx) vvc_get_cabac(&lc->ep->cc, lc->ep->cabac_state, ctx) + +static av_always_inline int vvc_get_cabac_bypass(CABACContext *c) +{ + return get_cabac_bypass(c); +} + +//9.3.3.4 Truncated binary (TB) binarization process +static int truncated_binary_decode(VVCLocalContext *lc, const int c_max) +{ + const int n = c_max + 1; + const int k = av_log2(n); + const int u = (1 << (k+1)) - n; + int v = 0; + for (int i = 0; i < k; i++) + v = (v << 1) | vvc_get_cabac_bypass(&lc->ep->cc); + if (v >= u) { + v = (v << 1) | vvc_get_cabac_bypass(&lc->ep->cc); + v -= u; + } + return v; +} + +// 9.3.3.6 Limited k-th order Exp-Golomb binarization process +static int limited_kth_order_egk_decode(CABACContext *c, const int k, const int max_pre_ext_len, const int trunc_suffix_len) +{ + int pre_ext_len = 0; + int escape_length; + int val = 0; + while ((pre_ext_len < max_pre_ext_len) && vvc_get_cabac_bypass(c)) + pre_ext_len++; + if (pre_ext_len == max_pre_ext_len) + escape_length = trunc_suffix_len; + else + escape_length = pre_ext_len + k; + while (escape_length-- > 0) { + val = (val << 1) + vvc_get_cabac_bypass(c); + } + val += ((1 << pre_ext_len) - 1) << k; + return val; +} + +static av_always_inline +void get_left_top(const VVCLocalContext *lc, uint8_t *left, uint8_t *top, + const int x0, const int y0, const uint8_t *left_ctx, const uint8_t *top_ctx) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int x0b = av_mod_uintp2(x0, sps->ctb_log2_size_y); + const int y0b = av_mod_uintp2(y0, sps->ctb_log2_size_y); + const int x_cb = x0 >> sps->min_cb_log2_size_y; + const int y_cb = y0 >> sps->min_cb_log2_size_y; + + if (lc->ctb_left_flag || x0b) + *left = SAMPLE_CTB(left_ctx, x_cb - 1, y_cb); + if (lc->ctb_up_flag || y0b) + *top = SAMPLE_CTB(top_ctx, x_cb, y_cb - 1); +} + +static av_always_inline +uint8_t get_inc(VVCLocalContext *lc, const uint8_t *ctx) +{ + uint8_t left = 0, top = 0; + get_left_top(lc, &left, &top, lc->cu->x0, lc->cu->y0, ctx, ctx); + return left + top; +} + +int ff_vvc_sao_merge_flag_decode(VVCLocalContext *lc) +{ + return GET_CABAC(SAO_MERGE_FLAG); +} + +int ff_vvc_sao_type_idx_decode(VVCLocalContext *lc) +{ + if (!GET_CABAC(SAO_TYPE_IDX)) + return SAO_NOT_APPLIED; + + if (!vvc_get_cabac_bypass(&lc->ep->cc)) + return SAO_BAND; + return SAO_EDGE; +} + +int ff_vvc_sao_band_position_decode(VVCLocalContext *lc) +{ + int i; + int value = vvc_get_cabac_bypass(&lc->ep->cc); + + for (i = 0; i < 4; i++) + value = (value << 1) | vvc_get_cabac_bypass(&lc->ep->cc); + return value; +} + +int ff_vvc_sao_offset_abs_decode(VVCLocalContext *lc) +{ + int i = 0; + int length = (1 << (FFMIN(lc->fc->ps.sps->bit_depth, 10) - 5)) - 1; + + while (i < length && vvc_get_cabac_bypass(&lc->ep->cc)) + i++; + return i; +} + +int ff_vvc_sao_offset_sign_decode(VVCLocalContext *lc) +{ + return vvc_get_cabac_bypass(&lc->ep->cc); +} + +int ff_vvc_sao_eo_class_decode(VVCLocalContext *lc) +{ + int ret = vvc_get_cabac_bypass(&lc->ep->cc) << 1; + ret |= vvc_get_cabac_bypass(&lc->ep->cc); + return ret; +} + +int ff_vvc_alf_ctb_flag(VVCLocalContext *lc, const int rx, const int ry, const int c_idx) +{ + int inc = c_idx * 3; + const VVCFrameContext *fc = lc->fc; + if (lc->ctb_left_flag) { + const ALFParams *left = &CTB(fc->tab.alf, rx - 1, ry); + inc += left->ctb_flag[c_idx]; + } + if (lc->ctb_up_flag) { + const ALFParams *above = &CTB(fc->tab.alf, rx, ry - 1); + inc += above->ctb_flag[c_idx]; + } + return GET_CABAC(ALF_CTB_FLAG + inc); +} + +int ff_vvc_alf_use_aps_flag(VVCLocalContext *lc) +{ + return GET_CABAC(ALF_USE_APS_FLAG); +} + +int ff_vvc_alf_luma_prev_filter_idx(VVCLocalContext *lc) +{ + return truncated_binary_decode(lc, lc->sc->sh.r->sh_num_alf_aps_ids_luma - 1); +} + +int ff_vvc_alf_luma_fixed_filter_idx(VVCLocalContext *lc) +{ + return truncated_binary_decode(lc, 15); +} + +int ff_vvc_alf_ctb_filter_alt_idx(VVCLocalContext *lc, const int c_idx, const int num_chroma_filters) +{ + int i = 0; + const int length = num_chroma_filters - 1; + + while (i < length && GET_CABAC(ALF_CTB_FILTER_ALT_IDX + c_idx - 1)) + i++; + return i; +} + +int ff_vvc_alf_ctb_cc_idc(VVCLocalContext *lc, const int rx, const int ry, const int idx, const int cc_filters_signalled) +{ + int inc = !idx ? ALF_CTB_CC_CB_IDC : ALF_CTB_CC_CR_IDC; + int i = 0; + const VVCFrameContext *fc = lc->fc; + if (lc->ctb_left_flag) { + const ALFParams *left = &CTB(fc->tab.alf, rx - 1, ry); + inc += left->ctb_cc_idc[idx] != 0; + } + if (lc->ctb_up_flag) { + const ALFParams *above = &CTB(fc->tab.alf, rx, ry - 1); + inc += above->ctb_cc_idc[idx] != 0; + } + + if (!GET_CABAC(inc)) + return 0; + i++; + while (i < cc_filters_signalled && vvc_get_cabac_bypass(&lc->ep->cc)) + i++; + return i; +} + +int ff_vvc_split_cu_flag(VVCLocalContext *lc, const int x0, const int y0, + const int cb_width, const int cb_height, const int is_chroma, const VVCAllowedSplit *a) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPPS *pps = fc->ps.pps; + const int is_inside = (x0 + cb_width <= pps->width) && (y0 + cb_height <= pps->height); + + if ((a->btv || a->bth || a->ttv || a->tth || a->qt) && is_inside) + { + uint8_t inc = 0, left_height = cb_height, top_width = cb_width; + + get_left_top(lc, &left_height, &top_width, x0, y0, fc->tab.cb_height[is_chroma], fc->tab.cb_width[is_chroma]); + inc += left_height < cb_height; + inc += top_width < cb_width; + inc += (a->btv + a->bth + a->ttv + a->tth + 2 * a->qt - 1) / 2 * 3; + + return GET_CABAC(SPLIT_CU_FLAG + inc); + + } + return !is_inside; +} + +static int split_qt_flag_decode(VVCLocalContext *lc, const int x0, const int y0, const int ch_type, const int cqt_depth) +{ + const VVCFrameContext *fc = lc->fc; + int inc = 0; + uint8_t depth_left = 0, depth_top = 0; + + get_left_top(lc, &depth_left, &depth_top, x0, y0, fc->tab.cqt_depth[ch_type], fc->tab.cqt_depth[ch_type]); + inc += depth_left > cqt_depth; + inc += depth_top > cqt_depth; + inc += (cqt_depth >= 2) * 3; + + return GET_CABAC(SPLIT_QT_FLAG + inc); +} + +static int mtt_split_cu_vertical_flag_decode(VVCLocalContext *lc, const int x0, const int y0, + const int cb_width, const int cb_height, const int ch_type, const VVCAllowedSplit* a) +{ + if ((a->bth || a->tth) && (a->btv || a->ttv)) { + int inc; + const int v = a->btv + a->ttv; + const int h = a->bth + a->tth; + if (v > h) + inc = 4; + else if (v < h) + inc = 3; + else { + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int x0b = av_mod_uintp2(x0, sps->ctb_log2_size_y); + const int y0b = av_mod_uintp2(y0, sps->ctb_log2_size_y); + const int x_cb = x0 >> sps->min_cb_log2_size_y; + const int y_cb = y0 >> sps->min_cb_log2_size_y; + const int available_a = lc->ctb_up_flag || y0b; + const int available_l = lc->ctb_left_flag || x0b; + const int da = cb_width / (available_a ? SAMPLE_CTB(fc->tab.cb_width[ch_type], x_cb, y_cb - 1) : 1); + const int dl = cb_height / (available_l ? SAMPLE_CTB(fc->tab.cb_height[ch_type], x_cb - 1, y_cb) : 1); + + if (da == dl || !available_a || !available_l) + inc = 0; + else if (da < dl) + inc = 1; + else + inc = 2; + } + return GET_CABAC(MTT_SPLIT_CU_VERTICAL_FLAG + inc); + } + return !(a->bth || a->tth); +} + +static int mtt_split_cu_binary_flag_decode(VVCLocalContext *lc, const int mtt_split_cu_vertical_flag, const int mtt_depth) +{ + int inc = (2 * mtt_split_cu_vertical_flag) + ((mtt_depth <= 1) ? 1 : 0); + return GET_CABAC(MTT_SPLIT_CU_BINARY_FLAG + inc); +} + +VVCSplitMode ff_vvc_split_mode(VVCLocalContext *lc, const int x0, const int y0, const int cb_width, const int cb_height, + const int cqt_depth, const int mtt_depth, const int ch_type, const VVCAllowedSplit *a) +{ + int allow_no_qt = a->btv || a->bth || a->ttv || a->tth; + int split_qt_flag; + int mtt_split_cu_vertical_flag; + int mtt_split_cu_binary_flag; + const VVCSplitMode mtt_split_modes[] = { + SPLIT_TT_HOR, SPLIT_BT_HOR, SPLIT_TT_VER, SPLIT_BT_VER, + }; + if (allow_no_qt && a->qt) { + split_qt_flag = split_qt_flag_decode(lc, x0, y0, ch_type, cqt_depth); + } else { + split_qt_flag = !allow_no_qt || a->qt; + } + if (split_qt_flag) + return SPLIT_QT; + mtt_split_cu_vertical_flag = mtt_split_cu_vertical_flag_decode(lc, x0, y0, cb_width, cb_height, ch_type, a); + if ((a->btv && a->ttv && mtt_split_cu_vertical_flag) || + (a->bth && a->tth && !mtt_split_cu_vertical_flag)) { + mtt_split_cu_binary_flag = mtt_split_cu_binary_flag_decode(lc, mtt_split_cu_vertical_flag, mtt_depth); + } else { + if (!a->btv && !a->bth) + mtt_split_cu_binary_flag = 0; + else if (!a->ttv && !a->tth) + mtt_split_cu_binary_flag = 1; + else if (a->bth && a->ttv) + mtt_split_cu_binary_flag = 1 - mtt_split_cu_vertical_flag; + else + mtt_split_cu_binary_flag = mtt_split_cu_vertical_flag; + } + return mtt_split_modes[(mtt_split_cu_vertical_flag << 1) + mtt_split_cu_binary_flag]; +} + +int ff_vvc_non_inter_flag(VVCLocalContext *lc, const int x0, const int y0, const int ch_type) +{ + const VVCFrameContext *fc = lc->fc; + uint8_t inc, left = 0, top = 0; + + get_left_top(lc, &left, &top, x0, y0, fc->tab.cpm[ch_type], fc->tab.cpm[ch_type]); + inc = left || top; + return GET_CABAC(NON_INTER_FLAG + inc); +} + +int ff_vvc_pred_mode_flag(VVCLocalContext *lc, const int is_chroma) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + uint8_t inc, left = 0, top = 0; + + get_left_top(lc, &left, &top, cu->x0, cu->y0, fc->tab.cpm[is_chroma], fc->tab.cpm[is_chroma]); + inc = left || top; + return GET_CABAC(PRED_MODE_FLAG + inc); +} + +int ff_vvc_pred_mode_plt_flag(VVCLocalContext *lc) +{ + return GET_CABAC(PRED_MODE_PLT_FLAG); +} + +int ff_vvc_intra_bdpcm_luma_flag(VVCLocalContext *lc) +{ + return GET_CABAC(INTRA_BDPCM_LUMA_FLAG); +} + +int ff_vvc_intra_bdpcm_luma_dir_flag(VVCLocalContext *lc) +{ + return GET_CABAC(INTRA_BDPCM_LUMA_DIR_FLAG); +} + +int ff_vvc_intra_bdpcm_chroma_flag(VVCLocalContext *lc) +{ + return GET_CABAC(INTRA_BDPCM_CHROMA_FLAG); +} + +int ff_vvc_intra_bdpcm_chroma_dir_flag(VVCLocalContext *lc) +{ + return GET_CABAC(INTRA_BDPCM_CHROMA_DIR_FLAG); +} + +int ff_vvc_cu_skip_flag(VVCLocalContext *lc, const uint8_t *cu_skip_flag) +{ + const int inc = get_inc(lc, cu_skip_flag); + return GET_CABAC(CU_SKIP_FLAG + inc); +} + +int ff_vvc_pred_mode_ibc_flag(VVCLocalContext *lc, const int is_chroma) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + uint8_t left_mode = MODE_INTER, top_mode = MODE_INTER; + int inc; + + get_left_top(lc, &left_mode, &top_mode, cu->x0, cu->y0, fc->tab.cpm[is_chroma], fc->tab.cpm[is_chroma]); + inc = (left_mode == MODE_IBC) + (top_mode == MODE_IBC); + return GET_CABAC(PRED_MODE_IBC_FLAG + inc); +} + +int ff_vvc_intra_mip_flag(VVCLocalContext *lc, const uint8_t *intra_mip_flag) +{ + const int w = lc->cu->cb_width; + const int h = lc->cu->cb_height; + const int inc = (w > h * 2 || h > w * 2) ? 3 : get_inc(lc, intra_mip_flag); + return GET_CABAC(INTRA_MIP_FLAG + inc); +} + +int ff_vvc_intra_mip_transposed_flag(VVCLocalContext *lc) +{ + return vvc_get_cabac_bypass(&lc->ep->cc); +} + +int ff_vvc_intra_mip_mode(VVCLocalContext *lc) +{ + const int w = lc->cu->cb_width; + const int h = lc->cu->cb_height; + const int c_max = (w == 4 && h == 4) ? 15 : + ((w == 4 || h == 4) || (w == 8 && h == 8)) ? 7: 5; + return truncated_binary_decode(lc, c_max); +} + +int ff_vvc_intra_luma_ref_idx(VVCLocalContext *lc) +{ + int i; + for (i = 0; i < 2; i++) { + if (!GET_CABAC(INTRA_LUMA_REF_IDX + i)) + return i; + } + return i; +} + +int ff_vvc_intra_subpartitions_mode_flag(VVCLocalContext *lc) +{ + return GET_CABAC(INTRA_SUBPARTITIONS_MODE_FLAG); +} + +enum IspType ff_vvc_isp_split_type(VVCLocalContext *lc, const int intra_subpartitions_mode_flag) +{ + if (!intra_subpartitions_mode_flag) + return ISP_NO_SPLIT; + return 1 + GET_CABAC(INTRA_SUBPARTITIONS_SPLIT_FLAG); +} + +int ff_vvc_intra_luma_mpm_flag(VVCLocalContext *lc) +{ + return GET_CABAC(INTRA_LUMA_MPM_FLAG); +} + +int ff_vvc_intra_luma_not_planar_flag(VVCLocalContext *lc, const int intra_subpartitions_mode_flag) +{ + return GET_CABAC(INTRA_LUMA_NOT_PLANAR_FLAG + !intra_subpartitions_mode_flag); +} + +int ff_vvc_intra_luma_mpm_idx(VVCLocalContext *lc) +{ + int i; + for (i = 0; i < 4 && vvc_get_cabac_bypass(&lc->ep->cc); i++) + /* nothing */; + return i; +} + +int ff_vvc_intra_luma_mpm_remainder(VVCLocalContext *lc) +{ + return truncated_binary_decode(lc, 60); +} + +int ff_vvc_cclm_mode_flag(VVCLocalContext *lc) +{ + return GET_CABAC(CCLM_MODE_FLAG); +} + +int ff_vvc_cclm_mode_idx(VVCLocalContext *lc) +{ + if (!GET_CABAC(CCLM_MODE_IDX)) + return 0; + return vvc_get_cabac_bypass(&lc->ep->cc) + 1; +} + +int ff_vvc_intra_chroma_pred_mode(VVCLocalContext *lc) +{ + if (!GET_CABAC(INTRA_CHROMA_PRED_MODE)) + return 4; + return (vvc_get_cabac_bypass(&lc->ep->cc) << 1) | vvc_get_cabac_bypass(&lc->ep->cc); +} + +int ff_vvc_general_merge_flag(VVCLocalContext *lc) +{ + return GET_CABAC(GENERAL_MERGE_FLAG); +} + +static int get_inter_flag_inc(VVCLocalContext *lc, const int x0, const int y0) +{ + uint8_t left_merge = 0, top_merge = 0; + uint8_t left_affine = 0, top_affine = 0; + const VVCFrameContext *fc = lc->fc; + + get_left_top(lc, &left_merge, &top_merge, x0, y0, fc->tab.msf, fc->tab.msf); + get_left_top(lc, &left_affine, &top_affine, x0, y0, fc->tab.iaf, fc->tab.iaf); + return (left_merge || left_affine) + (top_merge + top_affine); +} + +int ff_vvc_merge_subblock_flag(VVCLocalContext *lc) +{ + const int inc = get_inter_flag_inc(lc, lc->cu->x0, lc->cu->y0); + return GET_CABAC(MERGE_SUBBLOCK_FLAG + inc); +} + +int ff_vvc_merge_subblock_idx(VVCLocalContext *lc, const int max_num_subblock_merge_cand) +{ + int i; + if (!GET_CABAC(MERGE_SUBBLOCK_IDX)) + return 0; + for (i = 1; i < max_num_subblock_merge_cand - 1 && vvc_get_cabac_bypass(&lc->ep->cc); i++) + /* nothing */; + return i; +} + +int ff_vvc_regular_merge_flag(VVCLocalContext *lc, const int cu_skip_flag) +{ + int inc = !cu_skip_flag; + return GET_CABAC(REGULAR_MERGE_FLAG + inc); +} + +int ff_vvc_mmvd_merge_flag(VVCLocalContext *lc) +{ + return GET_CABAC(MMVD_MERGE_FLAG); +} + +int ff_vvc_mmvd_cand_flag(VVCLocalContext *lc) +{ + return GET_CABAC(MMVD_CAND_FLAG); +} + +static int mmvd_distance_idx_decode(VVCLocalContext *lc) +{ + int i; + if (!GET_CABAC(MMVD_DISTANCE_IDX)) + return 0; + for (i = 1; i < 7 && vvc_get_cabac_bypass(&lc->ep->cc); i++) + /* nothing */; + return i; +} + +static int mmvd_direction_idx_decode(VVCLocalContext *lc) +{ + return (vvc_get_cabac_bypass(&lc->ep->cc) << 1) | vvc_get_cabac_bypass(&lc->ep->cc); +} + +void ff_vvc_mmvd_offset_coding(VVCLocalContext *lc, Mv *mmvd_offset, const int ph_mmvd_fullpel_only_flag) +{ + const int shift = ph_mmvd_fullpel_only_flag ? 4 : 2; + const int mmvd_distance = 1 << (mmvd_distance_idx_decode(lc) + shift); + const int mmvd_direction_idx = mmvd_direction_idx_decode(lc); + const int mmvd_signs[][2] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} }; + mmvd_offset->x = mmvd_distance * mmvd_signs[mmvd_direction_idx][0]; + mmvd_offset->y = mmvd_distance * mmvd_signs[mmvd_direction_idx][1]; +} + +static PredMode get_luma_pred_mode(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + PredMode pred_mode; + if (cu->tree_type != DUAL_TREE_CHROMA) { + pred_mode = cu->pred_mode; + } else { + const int x_cb = cu->x0 >> fc->ps.sps->min_cb_log2_size_y; + const int y_cb = cu->y0 >> fc->ps.sps->min_cb_log2_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + pred_mode = SAMPLE_CTB(fc->tab.cpm[0], x_cb, y_cb); + } + return pred_mode; +} + +int ff_vvc_merge_idx(VVCLocalContext *lc) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const int is_ibc = get_luma_pred_mode(lc) == MODE_IBC; + const int c_max = (is_ibc ? sps->max_num_ibc_merge_cand : sps->max_num_merge_cand) - 1; + int i; + + if (!GET_CABAC(MERGE_IDX)) + return 0; + + for (i = 1; i < c_max && vvc_get_cabac_bypass(&lc->ep->cc); i++) + /* nothing */; + return i; +} + +int ff_vvc_merge_gpm_partition_idx(VVCLocalContext *lc) +{ + int i = 0; + + for (int j = 0; j < 6; j++) + i = (i << 1) | vvc_get_cabac_bypass(&lc->ep->cc); + + return i; +} + +int ff_vvc_merge_gpm_idx(VVCLocalContext *lc, const int idx) +{ + const int c_max = lc->fc->ps.sps->max_num_gpm_merge_cand - idx - 1; + int i; + + if (!GET_CABAC(MERGE_IDX)) + return 0; + + for (i = 1; i < c_max && vvc_get_cabac_bypass(&lc->ep->cc); i++) + /* nothing */; + + return i; +} + +int ff_vvc_ciip_flag(VVCLocalContext *lc) +{ + return GET_CABAC(CIIP_FLAG); +} + +PredFlag ff_vvc_pred_flag(VVCLocalContext *lc, const int is_b) +{ + const int w = lc->cu->cb_width; + const int h = lc->cu->cb_height; + if (!is_b) + return PF_L0; + if (w + h > 12) { + const int log2 = av_log2(w) + av_log2(h); + const int inc = 7 - ((1 + log2)>>1); + if (GET_CABAC(INTER_PRED_IDC + inc)) + return PF_BI; + } + return PF_L0 + GET_CABAC(INTER_PRED_IDC + 5); +} + +int ff_vvc_inter_affine_flag(VVCLocalContext *lc) +{ + const int inc = get_inter_flag_inc(lc, lc->cu->x0, lc->cu->y0); + return GET_CABAC(INTER_AFFINE_FLAG + inc); +} + +int ff_vvc_cu_affine_type_flag(VVCLocalContext *lc) +{ + return GET_CABAC(CU_AFFINE_TYPE_FLAG); +} + +int ff_vvc_sym_mvd_flag(VVCLocalContext *lc) +{ + return GET_CABAC(SYM_MVD_FLAG); +} + +int ff_vvc_ref_idx_lx(VVCLocalContext *lc, const uint8_t nb_refs) +{ + const int c_max = nb_refs - 1; + const int max_ctx = FFMIN(c_max, 2); + int i = 0; + + while (i < max_ctx && GET_CABAC(REF_IDX_LX + i)) + i++; + if (i == 2) { + while (i < c_max && vvc_get_cabac_bypass(&lc->ep->cc)) + i++; + } + return i; +} + +int ff_vvc_abs_mvd_greater0_flag(VVCLocalContext *lc) +{ + return GET_CABAC(ABS_MVD_GREATER0_FLAG); +} + +int ff_vvc_abs_mvd_greater1_flag(VVCLocalContext *lc) +{ + return GET_CABAC(ABS_MVD_GREATER1_FLAG); +} + +int ff_vvc_abs_mvd_minus2(VVCLocalContext *lc) +{ + return limited_kth_order_egk_decode(&lc->ep->cc, 1, 15, 17); +} + +int ff_vvc_mvd_sign_flag(VVCLocalContext *lc) +{ + return vvc_get_cabac_bypass(&lc->ep->cc); +} + +int ff_vvc_mvp_lx_flag(VVCLocalContext *lc) +{ + return GET_CABAC(MVP_LX_FLAG); +} + +static int amvr_flag(VVCLocalContext *lc, const int inter_affine_flag) +{ + return GET_CABAC(AMVR_FLAG + inter_affine_flag); +} + +static int amvr_precision_idx(VVCLocalContext *lc, const int inc, const int c_max) +{ + int i = 0; + if (!GET_CABAC(AMVR_PRECISION_IDX + inc)) + return 0; + i++; + if (i < c_max && GET_CABAC(AMVR_PRECISION_IDX + 1)) + i++; + return i; +} + +int ff_vvc_amvr_shift(VVCLocalContext *lc, const int inter_affine_flag, + const PredMode pred_mode, const int has_amvr_flag) +{ + int amvr_shift = 2; + if (has_amvr_flag) { + if (amvr_flag(lc, inter_affine_flag)) { + int idx; + if (inter_affine_flag) { + idx = amvr_precision_idx(lc, 2, 1); + amvr_shift = idx * 4; + } else if (pred_mode == MODE_IBC) { + idx = amvr_precision_idx(lc, 1, 1); + amvr_shift = 4 + idx * 2; + } else { + static const int shifts[] = {3, 4, 6}; + idx = amvr_precision_idx(lc, 0, 2); + amvr_shift = shifts[idx]; + } + } + } + return amvr_shift; +} + +int ff_vvc_bcw_idx(VVCLocalContext *lc, const int no_backward_pred_flag) +{ + const int c_max = no_backward_pred_flag ? 4 : 2; + int i = 1; + if (!GET_CABAC(BCW_IDX)) + return 0; + while (i < c_max && vvc_get_cabac_bypass(&lc->ep->cc)) + i++; + return i; +} + +int ff_vvc_tu_cb_coded_flag(VVCLocalContext *lc) +{ + return GET_CABAC(TU_CB_CODED_FLAG + lc->cu->bdpcm_flag[1]); +} + +int ff_vvc_tu_cr_coded_flag(VVCLocalContext *lc, int tu_cb_coded_flag) +{ + return GET_CABAC(TU_CR_CODED_FLAG + (lc->cu->bdpcm_flag[1] ? 2 : tu_cb_coded_flag)); +} + +int ff_vvc_tu_y_coded_flag(VVCLocalContext *lc) +{ + const CodingUnit *cu = lc->cu; + int inc; + if (cu->bdpcm_flag[0]) + inc = 1; + else if (cu->isp_split_type == ISP_NO_SPLIT) + inc = 0; + else + inc = 2 + lc->parse.prev_tu_cbf_y; + lc->parse.prev_tu_cbf_y = GET_CABAC(TU_Y_CODED_FLAG + inc); + return lc->parse.prev_tu_cbf_y; +} + +int ff_vvc_cu_qp_delta_abs(VVCLocalContext *lc) +{ + int v, i, k; + if (!GET_CABAC(CU_QP_DELTA_ABS)) + return 0; + + // prefixVal + for (v = 1; v < 5 && GET_CABAC(CU_QP_DELTA_ABS + 1); v++) + /* nothing */; + if (v < 5) + return v; + + // 9.3.3.5 k-th order Exp-Golomb binarization process + // suffixVal + + // CuQpDeltaVal shall in the range of −( 32 + QpBdOffset / 2 ) to +( 31 + QpBdOffset / 2 ) + // so k = 6 should enough + for (k = 0; k < 6 && vvc_get_cabac_bypass(&lc->ep->cc); k++) + /* nothing */; + i = (1 << k) - 1; + v = 0; + while (k--) + v = (v << 1) + vvc_get_cabac_bypass(&lc->ep->cc); + v += i; + + return v + 5; +} + +int ff_vvc_cu_qp_delta_sign_flag(VVCLocalContext *lc) +{ + return vvc_get_cabac_bypass(&lc->ep->cc); +} + +int ff_vvc_cu_chroma_qp_offset_flag(VVCLocalContext *lc) +{ + return GET_CABAC(CU_CHROMA_QP_OFFSET_FLAG); +} +int ff_vvc_cu_chroma_qp_offset_idx(VVCLocalContext *lc) +{ + const int c_max = lc->fc->ps.pps->r->pps_chroma_qp_offset_list_len_minus1; + int i; + for (i = 0; i < c_max && GET_CABAC(CU_CHROMA_QP_OFFSET_IDX); i++) + /* nothing */; + return i; +} + +static av_always_inline int last_significant_coeff_xy_prefix(VVCLocalContext *lc, + const int log2_tb_size, const int log2_zo_tb_size, const int c_idx, const int ctx) +{ + int i = 0; + int max = (log2_zo_tb_size << 1) - 1; + int ctx_offset, ctx_shift; + if (!log2_tb_size) + return 0; + if (!c_idx) { + const int offset_y[] = {0, 0, 3, 6, 10, 15}; + ctx_offset = offset_y[log2_tb_size - 1]; + ctx_shift = (log2_tb_size + 1) >> 2; + } else { + const int shifts[] = {0, 0, 0, 1, 2, 2, 2}; + ctx_offset = 20; + ctx_shift = shifts[log2_tb_size]; + } + while (i < max && GET_CABAC(ctx + (i >> ctx_shift) + ctx_offset)) + i++; + return i; +} + +static av_always_inline int last_significant_coeff_x_prefix_decode(VVCLocalContext *lc, + const int log2_tb_width, const int log2_zo_tb_width, const int c_idx) +{ + return last_significant_coeff_xy_prefix(lc, log2_tb_width, log2_zo_tb_width, c_idx, LAST_SIG_COEFF_X_PREFIX); +} + +static av_always_inline int last_significant_coeff_y_prefix_decode(VVCLocalContext *lc, + const int log2_tb_height, const int log2_zo_tb_height, const int c_idx) +{ + return last_significant_coeff_xy_prefix(lc, log2_tb_height, log2_zo_tb_height, c_idx, LAST_SIG_COEFF_Y_PREFIX); +} + +static av_always_inline int last_sig_coeff_suffix_decode(VVCLocalContext *lc, + const int last_significant_coeff_y_prefix) +{ + int i; + const int length = (last_significant_coeff_y_prefix >> 1) - 1; + int value = vvc_get_cabac_bypass(&lc->ep->cc); + + for (i = 1; i < length; i++) + value = (value << 1) | vvc_get_cabac_bypass(&lc->ep->cc); + return value; +} + +int ff_vvc_tu_joint_cbcr_residual_flag(VVCLocalContext *lc, const int tu_cb_coded_flag, const int tu_cr_coded_flag) +{ + return GET_CABAC(TU_JOINT_CBCR_RESIDUAL_FLAG + 2 * tu_cb_coded_flag + tu_cr_coded_flag - 1); +} + +int ff_vvc_transform_skip_flag(VVCLocalContext *lc, const int inc) +{ + return GET_CABAC(TRANSFORM_SKIP_FLAG + inc); +} + +//9.3.4.2.7 Derivation process for the variables locNumSig, locSumAbsPass1 +static int get_local_sum(const int *level, const int w, const int h, + const int xc, const int yc, const int hist_value) +{ + int loc_sum = 3 * hist_value; + level += w * yc + xc; + if (xc < w - 1) { + loc_sum += level[1]; + if (xc < w - 2) + loc_sum += level[2] - hist_value; + if (yc < h - 1) + loc_sum += level[w + 1] - hist_value; + } + if (yc < h - 1) { + loc_sum += level[w]; + if (yc < h - 2) + loc_sum += level[w << 1] - hist_value; + } + return loc_sum; +} + +//9.3.4.2.7 Derivation process for the variables locNumSig, locSumAbsPass1 +static int get_local_sum_ts(const int *level, const int w, const int h, const int xc, const int yc) +{ + int loc_sum = 0; + level += w * yc + xc; + if (xc > 0) + loc_sum += level[-1]; + if (yc > 0) + loc_sum += level[-w]; + return loc_sum; +} + +static int get_gtx_flag_inc(const ResidualCoding* rc, const int xc, const int yc, const int last) +{ + const TransformBlock *tb = rc->tb; + int inc; + if (last) { + const int incs[] = {0, 21, 21}; + inc = incs[tb->c_idx]; + } else { + const int d = xc + yc; + const int local_sum_sig = get_local_sum(rc->sig_coeff_flag, + tb->tb_width,tb->tb_height, xc, yc, rc->hist_value); + const int loc_sum_abs_pass1 = get_local_sum(rc->abs_level_pass1, + tb->tb_width, tb->tb_height, xc, yc, rc->hist_value); + const int offset = FFMIN(loc_sum_abs_pass1 - local_sum_sig, 4); + + if (!tb->c_idx) + inc = 1 + offset + (!d ? 15 : (d < 3 ? 10 : (d < 10 ? 5 : 0))); + else + inc = 22 + offset + (!d ? 5 : 0); + } + return inc; +} + +static int abs_level_gtx_flag_decode(VVCLocalContext *lc, const int inc) +{ + return GET_CABAC(ABS_LEVEL_GTX_FLAG + inc); +} + +static int par_level_flag_decode(VVCLocalContext *lc, const int inc) +{ + return GET_CABAC(PAR_LEVEL_FLAG + inc); +} + +static int par_level_flag_ts_decode(VVCLocalContext *lc) +{ + const int inc = 32; + return GET_CABAC(PAR_LEVEL_FLAG + inc); +} + +static int sb_coded_flag_decode(VVCLocalContext *lc, const uint8_t *sb_coded_flag, + const ResidualCoding *rc, const int xs, const int ys) +{ + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const TransformBlock *tb = rc->tb; + const int w = rc->width_in_sbs; + const int h = rc->height_in_sbs; + int inc; + + if (tb->ts && !rsh->sh_ts_residual_coding_disabled_flag) { + const int left = xs > 0 ? sb_coded_flag[-1] : 0; + const int above = ys > 0 ? sb_coded_flag[-w] : 0; + inc = left + above + 4; + } else { + const int right = (xs < w - 1) ? sb_coded_flag[1] : 0; + const int bottom = (ys < h - 1) ? sb_coded_flag[w] : 0; + inc = (right | bottom) + (tb->c_idx ? 2 : 0); + } + return GET_CABAC(SB_CODED_FLAG + inc); +} + +static int sig_coeff_flag_decode(VVCLocalContext *lc, const ResidualCoding* rc, const int xc, const int yc) +{ + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const TransformBlock *tb = rc->tb; + int inc; + + if (tb->ts && !rsh->sh_ts_residual_coding_disabled_flag) { + const int local_num_sig = get_local_sum_ts(rc->sig_coeff_flag, tb->tb_width, tb->tb_height, xc, yc); + inc = 60 + local_num_sig; + } else { + const int d = xc + yc; + const int loc_sum_abs_pass1 = get_local_sum(rc->abs_level_pass1, + tb->tb_width, tb->tb_height, xc, yc, 0); + + if (!tb->c_idx) { + inc = 12 * FFMAX(0, rc->qstate - 1) + FFMIN((loc_sum_abs_pass1 + 1) >> 1, 3) + ((d < 2) ? 8 : (d < 5 ? 4 : 0)); + } else { + inc = 36 + 8 * FFMAX(0, rc->qstate - 1) + FFMIN((loc_sum_abs_pass1 + 1) >> 1, 3) + (d < 2 ? 4 : 0); + } + } + return GET_CABAC(SIG_COEFF_FLAG + inc); +} + +static int abs_get_rice_param(VVCLocalContext *lc, const ResidualCoding* rc, + const int xc, const int yc, const int base_level) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const TransformBlock* tb = rc->tb; + const int rice_params[] = { + 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, + }; + int loc_sum_abs; + int shift_val; + + loc_sum_abs = get_local_sum(rc->abs_level, tb->tb_width, tb->tb_height, xc, + yc, rc->hist_value); + + if (!sps->r->sps_rrc_rice_extension_flag) { + shift_val = 0; + } else { + shift_val = (av_log2(FFMAX(FFMIN(loc_sum_abs, 2048), 8)) - 3) & ~1; + } + + loc_sum_abs = av_clip_uintp2((loc_sum_abs >> shift_val) - base_level * 5, 5); + + return rice_params[loc_sum_abs] + shift_val; +} + +static int abs_decode(VVCLocalContext *lc, const int c_rice_param) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const int MAX_BIN = 6; + int prefix = 0; + int suffix = 0; + int i; + + while (prefix < MAX_BIN && vvc_get_cabac_bypass(&lc->ep->cc)) + prefix++; + if (prefix < MAX_BIN) { + for (i = 0; i < c_rice_param; i++) { + suffix = (suffix << 1) | vvc_get_cabac_bypass(&lc->ep->cc); + } + } else { + suffix = limited_kth_order_egk_decode(&lc->ep->cc, + c_rice_param + 1, + 26 - sps->log2_transform_range, + sps->log2_transform_range); + } + return suffix + (prefix << c_rice_param); +} + +static int abs_remainder_decode(VVCLocalContext *lc, const ResidualCoding* rc, const int xc, const int yc) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int base_level[][2][2] = { + { {4, 4}, {4, 4} }, + { {3, 2}, {2, 1} } + }; + const int c_rice_param = abs_get_rice_param(lc, rc, xc, yc, + base_level[sps->r->sps_rrc_rice_extension_flag][sps->bit_depth > 12][IS_I(rsh)]); + const int rem = abs_decode(lc, c_rice_param); + return rem; +} + +static int abs_remainder_ts_decode(VVCLocalContext *lc, const ResidualCoding* rc, const int xc, const int yc) +{ + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int c_rice_param = rsh->sh_ts_residual_coding_rice_idx_minus1 + 1; + const int rem = abs_decode(lc, c_rice_param); + + return rem; +} + +static int coeff_sign_flag_decode(VVCLocalContext *lc) +{ + return vvc_get_cabac_bypass(&lc->ep->cc); +} + +//9.3.4.2.10 Derivation process of ctxInc for the syntax element coeff_sign_flag for transform skip mode +static int coeff_sign_flag_ts_decode(VVCLocalContext *lc, const CodingUnit *cu, const ResidualCoding *rc, const int xc, const int yc) +{ + const TransformBlock *tb = rc->tb; + const int w = tb->tb_width; + const int *level = rc->coeff_sign_level + yc * w + xc; + const int left_sign = (xc == 0) ? 0 : level[-1]; + const int above_sign = (yc == 0) ? 0 : level[-w]; + const int bdpcm_flag = cu->bdpcm_flag[tb->c_idx]; + int inc; + + if (left_sign == -above_sign) + inc = bdpcm_flag == 0 ? 0 : 3; + else if (left_sign >= 0 && above_sign >= 0) + inc = bdpcm_flag == 0 ? 1 : 4; + else + inc = bdpcm_flag == 0 ? 2 : 5; + return GET_CABAC(COEFF_SIGN_FLAG + inc); +} + +static int abs_level_gt1_flag_ts_decode(VVCLocalContext *lc, const CodingUnit *cu, const ResidualCoding *rc, const int xc, const int yc) +{ + const TransformBlock *tb = rc->tb; + const int *sig_coeff_flag = rc->sig_coeff_flag + yc * tb->tb_width + xc; + int inc; + + if (cu->bdpcm_flag[tb->c_idx]) { + inc = 67; + } else { + const int l = xc > 0 ? sig_coeff_flag[-1] : 0; + const int a = yc > 0 ? sig_coeff_flag[-tb->tb_width] : 0; + inc = 64 + a + l; + } + return GET_CABAC(ABS_LEVEL_GTX_FLAG + inc); +} + +static int abs_level_gtx_flag_ts_decode(VVCLocalContext *lc, const int j) +{ + const int inc = 67 + j; + return GET_CABAC(ABS_LEVEL_GTX_FLAG + inc); +} + +static const uint8_t qstate_translate_table[][2] = { + { 0, 2 }, { 2, 0 }, { 1, 3 }, { 3, 1 } +}; + +static int dec_abs_level_decode(VVCLocalContext *lc, const ResidualCoding *rc, + const int xc, const int yc, int *abs_level) +{ + const int c_rice_param = abs_get_rice_param(lc, rc, xc, yc, 0); + const int dec_abs_level = abs_decode(lc, c_rice_param); + const int zero_pos = (rc->qstate < 2 ? 1 : 2) << c_rice_param; + + *abs_level = 0; + if (dec_abs_level != zero_pos) { + *abs_level = dec_abs_level; + if (dec_abs_level < zero_pos) + *abs_level += 1; + } + return dec_abs_level; +} + +static void ep_update_hist(EntryPoint *ep, ResidualCoding *rc, + const int remainder, const int addin) +{ + int *stat = ep->stat_coeff + rc->tb->c_idx; + if (rc->update_hist && remainder > 0) { + *stat = (*stat + av_log2(remainder) + addin) >> 1; + rc->update_hist = 0; + } +} + +static void init_residual_coding(VVCLocalContext *lc, ResidualCoding *rc, + const int log2_zo_tb_width, const int log2_zo_tb_height, + TransformBlock *tb) +{ + const VVCSPS *sps = lc->fc->ps.sps; + int log2_sb_w = (FFMIN(log2_zo_tb_width, log2_zo_tb_height ) < 2 ? 1 : 2 ); + int log2_sb_h = log2_sb_w; + + if ( log2_zo_tb_width + log2_zo_tb_height > 3 ) { + if ( log2_zo_tb_width < 2 ) { + log2_sb_w = log2_zo_tb_width; + log2_sb_h = 4 - log2_sb_w; + } else if ( log2_zo_tb_height < 2 ) { + log2_sb_h = log2_zo_tb_height; + log2_sb_w = 4 - log2_sb_h; + } + } + rc->log2_sb_w = log2_sb_w; + rc->log2_sb_h = log2_sb_h; + rc->num_sb_coeff = 1 << (log2_sb_w + log2_sb_h); + rc->last_sub_block = ( 1 << ( log2_zo_tb_width + log2_zo_tb_height - (log2_sb_w + log2_sb_h))) - 1; + rc->hist_value = sps->r->sps_persistent_rice_adaptation_enabled_flag ? (1 << lc->ep->stat_coeff[tb->c_idx]) : 0; + rc->update_hist = sps->r->sps_persistent_rice_adaptation_enabled_flag ? 1 : 0; + rc->rem_bins_pass1 = (( 1 << ( log2_zo_tb_width + log2_zo_tb_height)) * 7 ) >> 2; + + + rc->sb_scan_x_off = ff_vvc_diag_scan_x[log2_zo_tb_width - log2_sb_w][log2_zo_tb_height - log2_sb_h]; + rc->sb_scan_y_off = ff_vvc_diag_scan_y[log2_zo_tb_width - log2_sb_w][log2_zo_tb_height - log2_sb_h]; + + rc->scan_x_off = ff_vvc_diag_scan_x[log2_sb_w][log2_sb_h]; + rc->scan_y_off = ff_vvc_diag_scan_y[log2_sb_w][log2_sb_h]; + + rc->infer_sb_cbf = 1; + + rc->width_in_sbs = (1 << (log2_zo_tb_width - log2_sb_w)); + rc->height_in_sbs = (1 << (log2_zo_tb_height - log2_sb_h)); + rc->nb_sbs = rc->width_in_sbs * rc->height_in_sbs; + + rc->last_scan_pos = rc->num_sb_coeff; + rc->qstate = 0; + + rc->tb = tb; +} + +static int residual_ts_coding_subblock(VVCLocalContext *lc, ResidualCoding* rc, const int i) +{ + const CodingUnit *cu = lc->cu; + TransformBlock *tb = rc->tb; + const int bdpcm_flag = cu->bdpcm_flag[tb->c_idx]; + const int xs = rc->sb_scan_x_off[i]; + const int ys = rc->sb_scan_y_off[i]; + uint8_t *sb_coded_flag = rc->sb_coded_flag + ys * rc->width_in_sbs + xs; + int infer_sb_sig_coeff_flag = 1; + int last_scan_pos_pass1 = -1, last_scan_pos_pass2 = -1, n; + int abs_level_gtx_flag[MAX_SUB_BLOCK_SIZE * MAX_SUB_BLOCK_SIZE]; + int abs_level_pass2[MAX_SUB_BLOCK_SIZE * MAX_SUB_BLOCK_SIZE]; ///< AbsLevelPass2 + + if (i != rc->last_sub_block || !rc->infer_sb_cbf) + *sb_coded_flag = sb_coded_flag_decode(lc, sb_coded_flag, rc, xs, ys); + else + *sb_coded_flag = 1; + if (*sb_coded_flag && i < rc->last_sub_block) + rc->infer_sb_cbf = 0; + + //first scan pass + for (n = 0; n < rc->num_sb_coeff && rc->rem_bins_pass1 >= 4; n++) { + const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n]; + const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n]; + const int off = yc * tb->tb_width + xc; + int *sig_coeff_flag = rc->sig_coeff_flag + off; + int *abs_level_pass1 = rc->abs_level_pass1 + off; + int *coeff_sign_level = rc->coeff_sign_level + off; + int par_level_flag = 0; + + abs_level_gtx_flag[n] = 0; + last_scan_pos_pass1 = n; + if (*sb_coded_flag && (n != rc->num_sb_coeff - 1 || !infer_sb_sig_coeff_flag)) { + *sig_coeff_flag = sig_coeff_flag_decode(lc, rc, xc, yc); + rc->rem_bins_pass1--; + if (*sig_coeff_flag) + infer_sb_sig_coeff_flag = 0; + } else { + *sig_coeff_flag = (n == rc->num_sb_coeff - 1) && infer_sb_sig_coeff_flag && *sb_coded_flag; + } + *coeff_sign_level = 0; + if (*sig_coeff_flag) { + *coeff_sign_level = 1 - 2 * coeff_sign_flag_ts_decode(lc, cu, rc, xc, yc); + abs_level_gtx_flag[n] = abs_level_gt1_flag_ts_decode(lc, cu, rc, xc, yc); + rc->rem_bins_pass1 -= 2; + if (abs_level_gtx_flag[n]) { + par_level_flag = par_level_flag_ts_decode(lc); + rc->rem_bins_pass1--; + } + } + *abs_level_pass1 = *sig_coeff_flag + par_level_flag + abs_level_gtx_flag[n]; + } + + //greater than x scan pass + for (n = 0; n < rc->num_sb_coeff && rc->rem_bins_pass1 >= 4; n++) { + const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n]; + const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n]; + const int off = yc * tb->tb_width + xc; + + abs_level_pass2[n] = rc->abs_level_pass1[off]; + for (int j = 1; j < 5 && abs_level_gtx_flag[n]; j++) { + abs_level_gtx_flag[n] = abs_level_gtx_flag_ts_decode(lc, j); + abs_level_pass2[n] += abs_level_gtx_flag[n] << 1; + rc->rem_bins_pass1--; + } + last_scan_pos_pass2 = n; + } + + /* remainder scan pass */ + for (n = 0; n < rc->num_sb_coeff; n++) { + const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n]; + const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n]; + const int off = yc * tb->tb_width + xc; + const int *abs_level_pass1 = rc->abs_level_pass1 + off; + int *abs_level = rc->abs_level + off; + int *coeff_sign_level = rc->coeff_sign_level + off; + int abs_remainder = 0; + + if ((n <= last_scan_pos_pass2 && abs_level_pass2[n] >= 10) || + (n > last_scan_pos_pass2 && n <= last_scan_pos_pass1 && + *abs_level_pass1 >= 2) || + (n > last_scan_pos_pass1 && *sb_coded_flag)) + abs_remainder = abs_remainder_ts_decode(lc, rc, xc, yc); + if (n <= last_scan_pos_pass2) { + *abs_level = abs_level_pass2[n] + 2 * abs_remainder; + } else if (n <= last_scan_pos_pass1) { + *abs_level = *abs_level_pass1 + 2 * abs_remainder; + } else { + *abs_level = abs_remainder; + if (abs_remainder) { + //n > lastScanPosPass1 + *coeff_sign_level = 1 - 2 * coeff_sign_flag_decode(lc); + } + } + if (!bdpcm_flag && n <= last_scan_pos_pass1) { + const int left = xc > 0 ? abs_level[-1] : 0; + const int above = yc > 0 ? abs_level[-tb->tb_width] : 0; + const int pred = FFMAX(left, above); + + if (*abs_level == 1 && pred > 0) + *abs_level = pred; + else if (*abs_level > 0 && *abs_level <= pred) + (*abs_level)--; + } + if (*abs_level) { + tb->coeffs[off] = *coeff_sign_level * *abs_level; + tb->max_scan_x = FFMAX(xc, tb->max_scan_x); + tb->max_scan_y = FFMAX(yc, tb->max_scan_y); + tb->min_scan_x = FFMIN(xc, tb->min_scan_x); + tb->min_scan_y = FFMIN(yc, tb->min_scan_y); + } else { + tb->coeffs[off] = 0; + } + } + + return 0; +} + +static int hls_residual_ts_coding(VVCLocalContext *lc, TransformBlock *tb) +{ + ResidualCoding rc; + tb->min_scan_x = tb->min_scan_y = INT_MAX; + init_residual_coding(lc, &rc, tb->log2_tb_width, tb->log2_tb_height, tb); + for (int i = 0; i <= rc.last_sub_block; i++) { + int ret = residual_ts_coding_subblock(lc, &rc, i); + if (ret < 0) + return ret; + } + + return 0; +} + +static inline int residual_coding_subblock(VVCLocalContext *lc, ResidualCoding *rc, const int i) +{ + const H266RawSliceHeader *rsh = lc->sc->sh.r; + TransformBlock *tb = rc->tb; + int first_sig_scan_pos_sb, last_sig_scan_pos_sb; + int first_pos_mode0, first_pos_mode1; + int infer_sb_dc_sig_coeff_flag = 0; + int n, sig_hidden_flag, sum = 0; + int abs_level_gt2_flag[MAX_SUB_BLOCK_SIZE * MAX_SUB_BLOCK_SIZE]; + const int start_qstate_sb = rc->qstate; + const int xs = rc->sb_scan_x_off[i]; + const int ys = rc->sb_scan_y_off[i]; + uint8_t *sb_coded_flag = rc->sb_coded_flag + ys * rc->width_in_sbs + xs; + + + av_assert0(rc->num_sb_coeff <= MAX_SUB_BLOCK_SIZE * MAX_SUB_BLOCK_SIZE); + if (i < rc->last_sub_block && i > 0) { + *sb_coded_flag = sb_coded_flag_decode(lc, sb_coded_flag, rc, xs, ys); + infer_sb_dc_sig_coeff_flag = 1; + } else { + *sb_coded_flag = 1; + } + if (*sb_coded_flag && (xs > 3 || ys > 3) && tb->c_idx == 0) + lc->parse.mts_zero_out_sig_coeff_flag = 0; + + if (!*sb_coded_flag) + return 0; + + first_sig_scan_pos_sb = rc->num_sb_coeff; + last_sig_scan_pos_sb = -1; + first_pos_mode0 = (i == rc->last_sub_block ? rc->last_scan_pos : rc->num_sb_coeff -1); + first_pos_mode1 = first_pos_mode0; + for (n = first_pos_mode0; n >= 0 && rc->rem_bins_pass1 >= 4; n--) { + const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n]; + const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n]; + const int last = (xc == rc->last_significant_coeff_x && yc == rc->last_significant_coeff_y); + int *abs_level_pass1 = rc->abs_level_pass1 + yc * tb->tb_width + xc; + int *sig_coeff_flag = rc->sig_coeff_flag + yc * tb->tb_width + xc; + + if ((n > 0 || !infer_sb_dc_sig_coeff_flag ) && !last) { + *sig_coeff_flag = sig_coeff_flag_decode(lc, rc, xc, yc); + rc->rem_bins_pass1--; + if (*sig_coeff_flag) + infer_sb_dc_sig_coeff_flag = 0; + } else { + *sig_coeff_flag = last || (!rc->scan_x_off[n] && !rc ->scan_y_off[n] && + infer_sb_dc_sig_coeff_flag); + } + *abs_level_pass1 = 0; + if (*sig_coeff_flag) { + int abs_level_gt1_flag, par_level_flag = 0; + const int inc = get_gtx_flag_inc(rc, xc, yc, last); + abs_level_gt1_flag = abs_level_gtx_flag_decode(lc, inc); + rc->rem_bins_pass1--; + if (abs_level_gt1_flag) { + par_level_flag = par_level_flag_decode(lc, inc); + abs_level_gt2_flag[n] = abs_level_gtx_flag_decode(lc, inc + 32); + rc->rem_bins_pass1 -= 2; + } else { + abs_level_gt2_flag[n] = 0; + } + if (last_sig_scan_pos_sb == -1) + last_sig_scan_pos_sb = n; + first_sig_scan_pos_sb = n; + + *abs_level_pass1 = + 1 + par_level_flag + abs_level_gt1_flag + (abs_level_gt2_flag[n] << 1); + } else { + abs_level_gt2_flag[n] = 0; + } + + if (rsh->sh_dep_quant_used_flag) + rc->qstate = qstate_translate_table[rc->qstate][*abs_level_pass1 & 1]; + + first_pos_mode1 = n - 1; + } + for (n = first_pos_mode0; n > first_pos_mode1; n--) { + const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n]; + const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n]; + const int *abs_level_pass1 = rc->abs_level_pass1 + yc * tb->tb_width + xc; + int *abs_level = rc->abs_level + yc * tb->tb_width + xc; + + *abs_level = *abs_level_pass1; + if (abs_level_gt2_flag[n]) { + const int abs_remainder = abs_remainder_decode(lc, rc, xc, yc); + ep_update_hist(lc->ep, rc, abs_remainder, 2); + *abs_level += 2 * abs_remainder; + } + } + for (n = first_pos_mode1; n >= 0; n--) { + const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n]; + const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n]; + int *abs_level = rc->abs_level + yc * tb->tb_width + xc; + + if (*sb_coded_flag) { + const int dec_abs_level = dec_abs_level_decode(lc, rc, xc, yc, abs_level); + ep_update_hist(lc->ep, rc, dec_abs_level, 0); + } + if (*abs_level > 0) { + if (last_sig_scan_pos_sb == -1) + last_sig_scan_pos_sb = n; + first_sig_scan_pos_sb = n; + } + if (rsh->sh_dep_quant_used_flag) + rc->qstate = qstate_translate_table[rc->qstate][*abs_level & 1]; + } + sig_hidden_flag = rsh->sh_sign_data_hiding_used_flag && + (last_sig_scan_pos_sb - first_sig_scan_pos_sb > 3 ? 1 : 0); + + if (rsh->sh_dep_quant_used_flag) + rc->qstate = start_qstate_sb; + n = (i == rc->last_sub_block ? rc->last_scan_pos : rc->num_sb_coeff -1); + for (/* nothing */; n >= 0; n--) { + int trans_coeff_level; + const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n]; + const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n]; + const int off = yc * tb->tb_width + xc; + const int *abs_level = rc->abs_level + off; + + if (*abs_level > 0) { + int sign = 1; + if (!sig_hidden_flag || (n != first_sig_scan_pos_sb)) + sign = 1 - 2 * coeff_sign_flag_decode(lc); + if (rsh->sh_dep_quant_used_flag) { + trans_coeff_level = (2 * *abs_level - (rc->qstate > 1)) * sign; + } else { + trans_coeff_level = *abs_level * sign; + if (sig_hidden_flag) { + sum += *abs_level; + if (n == first_sig_scan_pos_sb && (sum % 2)) + trans_coeff_level = -trans_coeff_level; + } + } + tb->coeffs[off] = trans_coeff_level; + tb->max_scan_x = FFMAX(xc, tb->max_scan_x); + tb->max_scan_y = FFMAX(yc, tb->max_scan_y); + } + if (rsh->sh_dep_quant_used_flag) + rc->qstate = qstate_translate_table[rc->qstate][*abs_level & 1]; + } + + return 0; +} + +static void derive_last_scan_pos(ResidualCoding *rc, + const int log2_zo_tb_width, int log2_zo_tb_height) +{ + int xc, yc, xs, ys; + do { + if (rc->last_scan_pos == 0) { + rc->last_scan_pos = rc->num_sb_coeff; + rc->last_sub_block--; + } + rc->last_scan_pos--; + xs = rc->sb_scan_x_off[rc->last_sub_block]; + ys = rc->sb_scan_y_off[rc->last_sub_block]; + xc = (xs << rc->log2_sb_w) + rc->scan_x_off[rc->last_scan_pos]; + yc = (ys << rc->log2_sb_h) + rc->scan_y_off[rc->last_scan_pos]; + } while ((xc != rc->last_significant_coeff_x) || (yc != rc->last_significant_coeff_y)); +} + +static void last_significant_coeff_x_y_decode(ResidualCoding *rc, VVCLocalContext *lc, + const int log2_zo_tb_width, const int log2_zo_tb_height) +{ + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const TransformBlock *tb = rc->tb; + int last_significant_coeff_x, last_significant_coeff_y; + + last_significant_coeff_x = last_significant_coeff_x_prefix_decode(lc, + tb->log2_tb_width, log2_zo_tb_width, tb->c_idx); + + last_significant_coeff_y = last_significant_coeff_y_prefix_decode(lc, + tb->log2_tb_height, log2_zo_tb_height, tb->c_idx); + + if (last_significant_coeff_x > 3) { + int suffix = last_sig_coeff_suffix_decode(lc, last_significant_coeff_x); + last_significant_coeff_x = (1 << ((last_significant_coeff_x >> 1) - 1)) * + (2 + (last_significant_coeff_x & 1)) + suffix; + } + if (last_significant_coeff_y > 3) { + int suffix = last_sig_coeff_suffix_decode(lc, last_significant_coeff_y); + last_significant_coeff_y = (1 << ((last_significant_coeff_y >> 1) - 1)) * + (2 + (last_significant_coeff_y & 1)) + suffix; + } + if (rsh->sh_reverse_last_sig_coeff_flag) { + last_significant_coeff_x = (1 << log2_zo_tb_width) - 1 - last_significant_coeff_x; + last_significant_coeff_y = (1 << log2_zo_tb_height) - 1 - last_significant_coeff_y; + } + rc->last_significant_coeff_x = last_significant_coeff_x; + rc->last_significant_coeff_y = last_significant_coeff_y; +} + +static int hls_residual_coding(VVCLocalContext *lc, TransformBlock *tb) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const CodingUnit *cu = lc->cu; + const int log2_tb_width = tb->log2_tb_width; + const int log2_tb_height = tb->log2_tb_height; + const int c_idx = tb->c_idx; + int log2_zo_tb_width, log2_zo_tb_height; + ResidualCoding rc; + + if (sps->r->sps_mts_enabled_flag && cu->sbt_flag && c_idx == 0 && log2_tb_width == 5 && log2_tb_height < 6) + log2_zo_tb_width = 4; + else + log2_zo_tb_width = FFMIN(log2_tb_width, 5 ); + + if (sps->r->sps_mts_enabled_flag && cu->sbt_flag && c_idx == 0 && log2_tb_width < 6 && log2_tb_height == 5 ) + log2_zo_tb_height = 4; + else + log2_zo_tb_height = FFMIN(log2_tb_height, 5); + + init_residual_coding(lc, &rc, log2_zo_tb_width, log2_zo_tb_height, tb); + last_significant_coeff_x_y_decode(&rc, lc, log2_zo_tb_width, log2_zo_tb_height); + derive_last_scan_pos(&rc, log2_zo_tb_width, log2_zo_tb_height); + + if (rc.last_sub_block == 0 && log2_tb_width >= 2 && log2_tb_height >= 2 && !tb->ts && rc.last_scan_pos > 0) + lc->parse.lfnst_dc_only = 0; + if ((rc.last_sub_block > 0 && log2_tb_width >= 2 && log2_tb_height >= 2 ) || + (rc.last_scan_pos > 7 && (log2_tb_width == 2 || log2_tb_width == 3 ) && + log2_tb_width == log2_tb_height)) + lc->parse.lfnst_zero_out_sig_coeff_flag = 0; + if ((rc.last_sub_block > 0 || rc.last_scan_pos > 0 ) && c_idx == 0) + lc->parse.mts_dc_only = 0; + + memset(tb->coeffs, 0, tb->tb_width * tb->tb_height * sizeof(*tb->coeffs)); + memset(rc.abs_level, 0, tb->tb_width * tb->tb_height * sizeof(rc.abs_level[0])); + memset(rc.sb_coded_flag, 0, rc.nb_sbs); + memset(rc.abs_level_pass1, 0, tb->tb_width * tb->tb_height * sizeof(rc.abs_level_pass1[0])); + memset(rc.sig_coeff_flag, 0, tb->tb_width * tb->tb_height * sizeof(rc.sig_coeff_flag[0])); + + for (int i = rc.last_sub_block; i >= 0; i--) { + int ret = residual_coding_subblock(lc, &rc, i); + if (ret < 0) + return ret; + } + + return 0; +} + +int ff_vvc_residual_coding(VVCLocalContext *lc, TransformBlock *tb) +{ + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int ts = !rsh->sh_ts_residual_coding_disabled_flag && tb->ts; + + return ts ? hls_residual_ts_coding(lc, tb) : hls_residual_coding(lc, tb); +} + +int ff_vvc_cu_coded_flag(VVCLocalContext *lc) +{ + return GET_CABAC(CU_CODED_FLAG); +} + +int ff_vvc_sbt_flag(VVCLocalContext *lc) +{ + const int w = lc->cu->cb_width; + const int h = lc->cu->cb_height; + const int inc = w * h <= 256; + return GET_CABAC(CU_SBT_FLAG + inc); +} + +int ff_vvc_sbt_quad_flag(VVCLocalContext *lc) +{ + return GET_CABAC(CU_SBT_QUAD_FLAG); +} + +int ff_vvc_sbt_horizontal_flag(VVCLocalContext *lc) +{ + const int w = lc->cu->cb_width; + const int h = lc->cu->cb_height; + const int inc = (w == h) ? 0 : ((w < h) ? 1 : 2); + return GET_CABAC(CU_SBT_HORIZONTAL_FLAG + inc); +} + +int ff_vvc_sbt_pos_flag(VVCLocalContext *lc) +{ + return GET_CABAC(CU_SBT_POS_FLAG); +} + +int ff_vvc_lfnst_idx(VVCLocalContext *lc, const int inc) +{ + if (!GET_CABAC(LFNST_IDX + inc)) + return 0; + if (!GET_CABAC(LFNST_IDX + 2)) + return 1; + return 2; +} + +int ff_vvc_mts_idx(VVCLocalContext *lc) +{ + int i; + for (i = 0; i < 4; i++) { + if (!GET_CABAC(MTS_IDX + i)) + return i; + } + return i; +} + +int ff_vvc_end_of_slice_flag_decode(VVCLocalContext *lc) +{ + return get_cabac_terminate(&lc->ep->cc); +} + +int ff_vvc_end_of_tile_one_bit(VVCLocalContext *lc) +{ + return get_cabac_terminate(&lc->ep->cc); +} + +int ff_vvc_end_of_subset_one_bit(VVCLocalContext *lc) +{ + return get_cabac_terminate(&lc->ep->cc); +} diff --git a/libavcodec/vvc/vvc_cabac.h b/libavcodec/vvc/vvc_cabac.h new file mode 100644 index 00000000000..5bb1ae83f74 --- /dev/null +++ b/libavcodec/vvc/vvc_cabac.h @@ -0,0 +1,126 @@ +/* + * VVC CABAC decoder + * + * Copyright (C) 2022 Nuo Mi + * + * 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_VVC_CABAC_H +#define AVCODEC_VVC_CABAC_H + +#include "vvc_ctu.h" + +int ff_vvc_cabac_init(VVCLocalContext *lc, int ctu_idx, int rx, int ry); + +//sao +int ff_vvc_sao_merge_flag_decode(VVCLocalContext *lc); +int ff_vvc_sao_type_idx_decode(VVCLocalContext *lc); +int ff_vvc_sao_band_position_decode(VVCLocalContext *lc); +int ff_vvc_sao_offset_abs_decode(VVCLocalContext *lc); +int ff_vvc_sao_offset_sign_decode(VVCLocalContext *lc); +int ff_vvc_sao_eo_class_decode(VVCLocalContext *lc); + +//alf +int ff_vvc_alf_ctb_flag(VVCLocalContext *lc, int rx, int ry, int c_idx); +int ff_vvc_alf_use_aps_flag(VVCLocalContext *lc); +int ff_vvc_alf_luma_prev_filter_idx(VVCLocalContext *lc); +int ff_vvc_alf_luma_fixed_filter_idx(VVCLocalContext *lc); +int ff_vvc_alf_ctb_filter_alt_idx(VVCLocalContext *lc, int c_idx, int num_chroma_filters); +int ff_vvc_alf_ctb_cc_idc(VVCLocalContext *lc, int rx, int ry, int idx, int cc_filters_signalled); + +//coding_tree +int ff_vvc_split_cu_flag(VVCLocalContext* lc, int x0, int y0, int cb_width, int cb_height, + int ch_type, const VVCAllowedSplit *a); +VVCSplitMode ff_vvc_split_mode(VVCLocalContext *lc, int x0, int y0, int cb_width, int cb_height, + int cqt_depth, int mtt_depth, int ch_type, const VVCAllowedSplit *a); +int ff_vvc_non_inter_flag(VVCLocalContext *lc, int x0, int y0, int ch_type); + +//coding unit +int ff_vvc_pred_mode_flag(VVCLocalContext *lc, int is_chroma); +int ff_vvc_pred_mode_plt_flag(VVCLocalContext *lc); +int ff_vvc_intra_bdpcm_luma_flag(VVCLocalContext *lc); +int ff_vvc_intra_bdpcm_luma_dir_flag(VVCLocalContext *lc); +int ff_vvc_intra_bdpcm_chroma_flag(VVCLocalContext *lc); +int ff_vvc_intra_bdpcm_chroma_dir_flag(VVCLocalContext *lc); +int ff_vvc_cu_skip_flag(VVCLocalContext *lc, const uint8_t *cu_skip_flag); +int ff_vvc_pred_mode_ibc_flag(VVCLocalContext *lc, int ch_type); +int ff_vvc_cu_coded_flag(VVCLocalContext *lc); +int ff_vvc_cu_qp_delta_abs(VVCLocalContext *lc); +int ff_vvc_cu_qp_delta_sign_flag(VVCLocalContext *lc); +int ff_vvc_sbt_flag(VVCLocalContext *lc); +int ff_vvc_sbt_quad_flag(VVCLocalContext *lc); +int ff_vvc_sbt_horizontal_flag(VVCLocalContext *lc); +int ff_vvc_sbt_pos_flag(VVCLocalContext *lc); + +//intra +int ff_vvc_intra_mip_flag(VVCLocalContext *lc, const uint8_t *intra_mip_flag); +int ff_vvc_intra_mip_transposed_flag(VVCLocalContext *lc); +int ff_vvc_intra_mip_mode(VVCLocalContext *lc); +int ff_vvc_intra_luma_ref_idx(VVCLocalContext *lc); +int ff_vvc_intra_subpartitions_mode_flag(VVCLocalContext *lc); +enum IspType ff_vvc_isp_split_type(VVCLocalContext *lc, int intra_subpartitions_mode_flag); +int ff_vvc_intra_luma_mpm_flag(VVCLocalContext *lc); +int ff_vvc_intra_luma_not_planar_flag(VVCLocalContext *lc, int intra_subpartitions_mode_flag); +int ff_vvc_intra_luma_mpm_idx(VVCLocalContext *lc); +int ff_vvc_intra_luma_mpm_remainder(VVCLocalContext *lc); +int ff_vvc_cclm_mode_flag(VVCLocalContext *lc); +int ff_vvc_cclm_mode_idx(VVCLocalContext *lc); +int ff_vvc_intra_chroma_pred_mode(VVCLocalContext *lc); + +//inter +int ff_vvc_general_merge_flag(VVCLocalContext *lc); +int ff_vvc_merge_subblock_flag(VVCLocalContext *lc); +int ff_vvc_merge_subblock_idx(VVCLocalContext *lc, int max_num_subblock_merge_cand); +int ff_vvc_regular_merge_flag(VVCLocalContext *lc, int cu_skip_flag); +int ff_vvc_merge_idx(VVCLocalContext *lc); +int ff_vvc_mmvd_merge_flag(VVCLocalContext *lc); +int ff_vvc_mmvd_cand_flag(VVCLocalContext *lc); +void ff_vvc_mmvd_offset_coding(VVCLocalContext *lc, Mv *mvd_offset, int ph_mmvd_fullpel_only_flag); +int ff_vvc_ciip_flag(VVCLocalContext *lc); +int ff_vvc_merge_gpm_partition_idx(VVCLocalContext *lc); +int ff_vvc_merge_gpm_idx(VVCLocalContext *lc, int idx); +PredFlag ff_vvc_pred_flag(VVCLocalContext *lc, int is_b); +int ff_vvc_inter_affine_flag(VVCLocalContext *lc); +int ff_vvc_cu_affine_type_flag(VVCLocalContext *lc); +int ff_vvc_sym_mvd_flag(VVCLocalContext *lc); +int ff_vvc_ref_idx_lx(VVCLocalContext *lc, uint8_t nb_refs); +int ff_vvc_abs_mvd_greater0_flag(VVCLocalContext *lc); +int ff_vvc_abs_mvd_greater1_flag(VVCLocalContext *lc); +int ff_vvc_abs_mvd_minus2(VVCLocalContext *lc); +int ff_vvc_mvd_sign_flag(VVCLocalContext *lc); +int ff_vvc_mvp_lx_flag(VVCLocalContext *lc); +int ff_vvc_amvr_shift(VVCLocalContext *lc, int inter_affine_flag, PredMode pred_mode, int has_amvr_flag); +int ff_vvc_bcw_idx(VVCLocalContext *lc, int no_backward_pred_flag); + +//transform +int ff_vvc_tu_cb_coded_flag(VVCLocalContext *lc); +int ff_vvc_tu_cr_coded_flag(VVCLocalContext *lc, int tu_cb_coded_flag); +int ff_vvc_tu_y_coded_flag(VVCLocalContext *lc); +int ff_vvc_cu_chroma_qp_offset_flag(VVCLocalContext *lc); +int ff_vvc_cu_chroma_qp_offset_idx(VVCLocalContext *lc); +int ff_vvc_tu_joint_cbcr_residual_flag(VVCLocalContext *lc, int tu_cb_coded_flag, int tu_cr_coded_flag); +int ff_vvc_transform_skip_flag(VVCLocalContext *lc, int ctx); +int ff_vvc_residual_coding(VVCLocalContext *lc, TransformBlock *tb); +int ff_vvc_lfnst_idx(VVCLocalContext *lc, int inc); +int ff_vvc_mts_idx(VVCLocalContext *lc); + +int ff_vvc_end_of_slice_flag_decode(VVCLocalContext *lc); +int ff_vvc_end_of_tile_one_bit(VVCLocalContext *lc); +int ff_vvc_end_of_subset_one_bit(VVCLocalContext *lc); + +#endif //AVCODEC_VVC_CABAC_H diff --git a/libavcodec/vvc/vvc_ctu.c b/libavcodec/vvc/vvc_ctu.c new file mode 100644 index 00000000000..a212d3a44a1 --- /dev/null +++ b/libavcodec/vvc/vvc_ctu.c @@ -0,0 +1,2477 @@ +/* + * VVC CTU(Coding Tree Unit) parser + * + * Copyright (C) 2022 Nuo Mi + * + * 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 "vvc_cabac.h" +#include "vvc_ctu.h" +#include "vvc_inter.h" +#include "vvc_mvs.h" + +#define PROF_TEMP_SIZE (PROF_BLOCK_SIZE) * sizeof(int16_t) + +#define TAB_MSM(fc, depth, x, y) fc->tab.msm[(depth)][((y) >> 5) * fc->ps.pps->width32 + ((x) >> 5)] +#define TAB_ISPMF(fc, x, y) fc->tab.ispmf[((y) >> 6) * fc->ps.pps->width64 + ((x) >> 6)] + +typedef enum VVCModeType { + MODE_TYPE_ALL, + MODE_TYPE_INTER, + MODE_TYPE_INTRA, +} VVCModeType; + +static void set_tb_pos(const VVCFrameContext *fc, const TransformBlock *tb) +{ + const int x_tb = tb->x0 >> MIN_TU_LOG2; + const int y_tb = tb->y0 >> MIN_TU_LOG2; + const int hs = fc->ps.sps->hshift[tb->c_idx]; + const int vs = fc->ps.sps->vshift[tb->c_idx]; + const int is_chroma = tb->c_idx != 0; + const int width = FFMAX(1, tb->tb_width >> (MIN_TU_LOG2 - hs)); + const int end = y_tb + FFMAX(1, tb->tb_height >> (MIN_TU_LOG2 - vs)); + + for (int y = y_tb; y < end; y++) { + const int off = y * fc->ps.pps->min_tu_width + x_tb; + for (int i = 0; i < width; i++) { + fc->tab.tb_pos_x0[is_chroma][off + i] = tb->x0; + fc->tab.tb_pos_y0[is_chroma][off + i] = tb->y0; + } + memset(fc->tab.tb_width [is_chroma] + off, tb->tb_width, width); + memset(fc->tab.tb_height[is_chroma] + off, tb->tb_height, width); + } +} + +static void set_tb_tab(uint8_t *tab, uint8_t v, const VVCFrameContext *fc, + const TransformBlock *tb) +{ + const int width = tb->tb_width << fc->ps.sps->hshift[tb->c_idx]; + const int height = tb->tb_height << fc->ps.sps->vshift[tb->c_idx]; + + for (int h = 0; h < height; h += MIN_TU_SIZE) { + const int y = (tb->y0 + h) >> MIN_TU_LOG2; + const int off = y * fc->ps.pps->min_tu_width + (tb->x0 >> MIN_TU_LOG2); + const int w = FFMAX(1, width >> MIN_TU_LOG2); + memset(tab + off, v, w); + } +} + +// 8.7.1 Derivation process for quantization parameters +static int get_qp_y_pred(const VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const CodingUnit *cu = lc->cu; + const int ctb_log2_size = sps->ctb_log2_size_y; + const int ctb_size_mask = (1 << ctb_log2_size) - 1; + const int xQg = lc->parse.cu_qg_top_left_x; + const int yQg = lc->parse.cu_qg_top_left_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int x_cb = cu->x0 >> sps->min_cb_log2_size_y; + const int y_cb = cu->y0 >> sps->min_cb_log2_size_y; + const int x_ctb = cu->x0 >> ctb_log2_size; + const int y_ctb = cu->y0 >> ctb_log2_size; + const int in_same_ctb_a = ((xQg - 1) >> ctb_log2_size) == x_ctb && (yQg >> ctb_log2_size) == y_ctb; + const int in_same_ctb_b = (xQg >> ctb_log2_size) == x_ctb && ((yQg - 1) >> ctb_log2_size) == y_ctb; + int qPy_pred, qPy_a, qPy_b; + + if (lc->na.cand_up) { + const int first_qg_in_ctu = !(xQg & ctb_size_mask) && !(yQg & ctb_size_mask); + const int qPy_up = fc->tab.qp[LUMA][x_cb + (y_cb - 1) * min_cb_width]; + if (first_qg_in_ctu && pps->ctb_to_col_bd[xQg >> ctb_log2_size] == xQg) + return qPy_up; + } + + // qPy_pred + qPy_pred = lc->ep->is_first_qg ? lc->sc->sh.slice_qp_y : lc->ep->qp_y; + + // qPy_b + if (!lc->na.cand_up || !in_same_ctb_b) + qPy_b = qPy_pred; + else + qPy_b = fc->tab.qp[LUMA][x_cb + (y_cb - 1) * min_cb_width]; + + // qPy_a + if (!lc->na.cand_left || !in_same_ctb_a) + qPy_a = qPy_pred; + else + qPy_a = fc->tab.qp[LUMA][(x_cb - 1) + y_cb * min_cb_width]; + + av_assert2(qPy_a >= -fc->ps.sps->qp_bd_offset && qPy_a < 63); + av_assert2(qPy_b >= -fc->ps.sps->qp_bd_offset && qPy_b < 63); + + return (qPy_a + qPy_b + 1) >> 1; +} + +static void set_cb_tab(const VVCLocalContext *lc, uint8_t *tab, const uint8_t v) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPPS *pps = fc->ps.pps; + const CodingUnit *cu = lc->cu; + const int log2_min_cb_size = fc->ps.sps->min_cb_log2_size_y; + const int x_cb = cu->x0 >> log2_min_cb_size; + const int y_cb = cu->y0 >> log2_min_cb_size; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + int x = y_cb * pps->min_cb_width + x_cb; + + for (int y = 0; y < (cb_height >> log2_min_cb_size); y++) { + const int width = cb_width >> log2_min_cb_size; + + memset(&tab[x], v, width); + x += pps->min_cb_width; + } +} + +static int set_qp_y(VVCLocalContext *lc, const int x0, const int y0, const int has_qp_delta) +{ + const VVCSPS *sps = lc->fc->ps.sps; + EntryPoint *ep = lc->ep; + CodingUnit *cu = lc->cu; + int cu_qp_delta = 0; + + if (!lc->fc->ps.pps->r->pps_cu_qp_delta_enabled_flag) { + ep->qp_y = lc->sc->sh.slice_qp_y; + } else if (ep->is_first_qg || (lc->parse.cu_qg_top_left_x == x0 && lc->parse.cu_qg_top_left_y == y0)) { + ep->qp_y = get_qp_y_pred(lc); + ep->is_first_qg = 0; + } + + if (has_qp_delta) { + const int cu_qp_delta_abs = ff_vvc_cu_qp_delta_abs(lc); + + if (cu_qp_delta_abs) + cu_qp_delta = ff_vvc_cu_qp_delta_sign_flag(lc) ? -cu_qp_delta_abs : cu_qp_delta_abs; + if (cu_qp_delta > (31 + sps->qp_bd_offset / 2) || cu_qp_delta < -(32 + sps->qp_bd_offset / 2)) + return AVERROR_INVALIDDATA; + lc->parse.is_cu_qp_delta_coded = 1; + + if (cu_qp_delta) { + int off = sps->qp_bd_offset; + ep->qp_y = FFUMOD(ep->qp_y + cu_qp_delta + 64 + 2 * off, 64 + off) - off; + } + } + + set_cb_tab(lc, lc->fc->tab.qp[LUMA], ep->qp_y); + cu->qp[LUMA] = ep->qp_y; + + return 0; +} + +static void set_qp_c_tab(const VVCLocalContext *lc, const TransformUnit *tu, const TransformBlock *tb) +{ + const int is_jcbcr = tu->joint_cbcr_residual_flag && tu->coded_flag[CB] && tu->coded_flag[CR]; + const int idx = is_jcbcr ? JCBCR : tb->c_idx; + + set_tb_tab(lc->fc->tab.qp[tb->c_idx], lc->cu->qp[idx], lc->fc, tb); +} + +static void set_qp_c(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + CodingUnit *cu = lc->cu; + const int x_center = cu->x0 + cu->cb_width / 2; + const int y_center = cu->y0 + cu->cb_height / 2; + const int single_tree = cu->tree_type == SINGLE_TREE; + const int qp_luma = (single_tree ? lc->ep->qp_y : ff_vvc_get_qPy(fc, x_center, y_center)) + sps->qp_bd_offset; + const int qp_chroma = av_clip(qp_luma, 0, MAX_QP + sps->qp_bd_offset); + const int sh_chroma_qp_offset[] = { + rsh->sh_cb_qp_offset, + rsh->sh_cr_qp_offset, + rsh->sh_joint_cbcr_qp_offset, + }; + int qp; + + for (int i = CB - 1; i < CR + sps->r->sps_joint_cbcr_enabled_flag; i++) { + qp = sps->chroma_qp_table[i][qp_chroma]; + qp = qp + pps->chroma_qp_offset[i] + sh_chroma_qp_offset[i] + lc->parse.chroma_qp_offset[i]; + qp = av_clip(qp, -sps->qp_bd_offset, MAX_QP) + sps->qp_bd_offset; + cu->qp[i + 1] = qp; + } +} + +static TransformUnit* alloc_tu(VVCFrameContext *fc, CodingUnit *cu) +{ + TransformUnit *tu; + AVBufferRef *buf = av_buffer_pool_get(fc->tu_pool); + if (!buf) + return NULL; + + tu = (TransformUnit *)buf->data; + tu->next = NULL; + tu->buf = buf; + + if (cu->tus.tail) + cu->tus.tail->next = tu; + else + cu->tus.head = tu; + cu->tus.tail = tu; + + return tu; +} + +static TransformUnit* add_tu(VVCFrameContext *fc, CodingUnit *cu, const int x0, const int y0, const int tu_width, const int tu_height) +{ + TransformUnit *tu = alloc_tu(fc, cu); + + if (!tu) + return NULL; + + tu->x0 = x0; + tu->y0 = y0; + tu->width = tu_width; + tu->height = tu_height; + tu->joint_cbcr_residual_flag = 0; + memset(tu->coded_flag, 0, sizeof(tu->coded_flag)); + tu->nb_tbs = 0; + + return tu; +} + +static TransformBlock* add_tb(TransformUnit *tu, VVCLocalContext *lc, + const int x0, const int y0, const int tb_width, const int tb_height, const int c_idx) +{ + TransformBlock *tb; + + tb = &tu->tbs[tu->nb_tbs++]; + tb->has_coeffs = 0; + tb->x0 = x0; + tb->y0 = y0; + tb->tb_width = tb_width; + tb->tb_height = tb_height; + tb->log2_tb_width = log2(tb_width); + tb->log2_tb_height = log2(tb_height); + + tb->max_scan_x = tb->max_scan_y = 0; + tb->min_scan_x = tb->min_scan_y = 0; + + tb->c_idx = c_idx; + tb->ts = 0; + tb->coeffs = lc->coeffs; + lc->coeffs += tb_width * tb_height; + return tb; +} + +static uint8_t tu_y_coded_flag_decode(VVCLocalContext *lc, const int is_sbt_not_coded, + const int sub_tu_index, const int is_isp, const int is_chroma_coded) +{ + uint8_t tu_y_coded_flag = 0; + const VVCSPS *sps = lc->fc->ps.sps; + CodingUnit *cu = lc->cu; + + if (!is_sbt_not_coded) { + int has_y_coded_flag = sub_tu_index < cu->num_intra_subpartitions - 1 || !lc->parse.infer_tu_cbf_luma; + if (!is_isp) { + const int is_large = cu->cb_width > sps->max_tb_size_y || cu->cb_height > sps->max_tb_size_y; + has_y_coded_flag = (cu->pred_mode == MODE_INTRA && !cu->act_enabled_flag) || is_chroma_coded || is_large; + } + tu_y_coded_flag = has_y_coded_flag ? ff_vvc_tu_y_coded_flag(lc) : 1; + } + if (is_isp) + lc->parse.infer_tu_cbf_luma = lc->parse.infer_tu_cbf_luma && !tu_y_coded_flag; + return tu_y_coded_flag; +} + +static void chroma_qp_offset_decode(VVCLocalContext *lc, const int is_128, const int is_chroma_coded) +{ + const VVCPPS *pps = lc->fc->ps.pps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + + if ((is_128 || is_chroma_coded) && + rsh->sh_cu_chroma_qp_offset_enabled_flag && !lc->parse.is_cu_chroma_qp_offset_coded) { + const int cu_chroma_qp_offset_flag = ff_vvc_cu_chroma_qp_offset_flag(lc); + if (cu_chroma_qp_offset_flag) { + int cu_chroma_qp_offset_idx = 0; + if (pps->r->pps_chroma_qp_offset_list_len_minus1 > 0) + cu_chroma_qp_offset_idx = ff_vvc_cu_chroma_qp_offset_idx(lc); + for (int i = CB - 1; i < JCBCR; i++) + lc->parse.chroma_qp_offset[i] = pps->chroma_qp_offset_list[cu_chroma_qp_offset_idx][i]; + } else { + memset(lc->parse.chroma_qp_offset, 0, sizeof(lc->parse.chroma_qp_offset)); + } + lc->parse.is_cu_chroma_qp_offset_coded = 1; + } +} + +static int hls_transform_unit(VVCLocalContext *lc, int x0, int y0,int tu_width, int tu_height, int sub_tu_index, int ch_type) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + CodingUnit *cu = lc->cu; + TransformUnit *tu = add_tu(fc, cu, x0, y0, tu_width, tu_height); + const int min_cb_width = pps->min_cb_width; + const VVCTreeType tree_type = cu->tree_type; + const int is_128 = cu->cb_width > 64 || cu->cb_height > 64; + const int is_isp = cu->isp_split_type != ISP_NO_SPLIT; + const int is_isp_last_tu = is_isp && (sub_tu_index == cu->num_intra_subpartitions - 1); + const int is_sbt_not_coded = cu->sbt_flag && + ((sub_tu_index == 0 && cu->sbt_pos_flag) || (sub_tu_index == 1 && !cu->sbt_pos_flag)); + const int chroma_available = tree_type != DUAL_TREE_LUMA && sps->r->sps_chroma_format_idc && + (!is_isp || is_isp_last_tu); + int ret, xc, yc, wc, hc, is_chroma_coded; + + if (!tu) + return AVERROR_INVALIDDATA; + + if (tree_type == SINGLE_TREE && is_isp_last_tu) { + const int x_cu = x0 >> fc->ps.sps->min_cb_log2_size_y; + const int y_cu = y0 >> fc->ps.sps->min_cb_log2_size_y; + xc = SAMPLE_CTB(fc->tab.cb_pos_x[ch_type], x_cu, y_cu); + yc = SAMPLE_CTB(fc->tab.cb_pos_y[ch_type], x_cu, y_cu); + wc = SAMPLE_CTB(fc->tab.cb_width[ch_type], x_cu, y_cu); + hc = SAMPLE_CTB(fc->tab.cb_height[ch_type], x_cu, y_cu); + } else { + xc = x0, yc = y0, wc = tu_width, hc = tu_height; + } + + if (chroma_available && !is_sbt_not_coded) { + tu->coded_flag[CB] = ff_vvc_tu_cb_coded_flag(lc); + tu->coded_flag[CR] = ff_vvc_tu_cr_coded_flag(lc, tu->coded_flag[CB]); + } + + is_chroma_coded = chroma_available && (tu->coded_flag[CB] || tu->coded_flag[CR]); + + if (tree_type != DUAL_TREE_CHROMA) { + int has_qp_delta; + tu->coded_flag[LUMA] = tu_y_coded_flag_decode(lc, is_sbt_not_coded, sub_tu_index, is_isp, is_chroma_coded); + has_qp_delta = (is_128 || tu->coded_flag[LUMA] || is_chroma_coded) && + pps->r->pps_cu_qp_delta_enabled_flag && !lc->parse.is_cu_qp_delta_coded; + ret = set_qp_y(lc, x0, y0, has_qp_delta); + if (ret < 0) + return ret; + add_tb(tu, lc, x0, y0, tu_width, tu_height, LUMA); + } + if (tree_type != DUAL_TREE_LUMA) { + chroma_qp_offset_decode(lc, is_128, is_chroma_coded); + if (chroma_available) { + const int hs = sps->hshift[CHROMA]; + const int vs = sps->vshift[CHROMA]; + add_tb(tu, lc, xc, yc, wc >> hs, hc >> vs, CB); + add_tb(tu, lc, xc, yc, wc >> hs, hc >> vs, CR); + } + } + if (sps->r->sps_joint_cbcr_enabled_flag && ((cu->pred_mode == MODE_INTRA && + (tu->coded_flag[CB] || tu->coded_flag[CR])) || + (tu->coded_flag[CB] && tu->coded_flag[CR])) && + chroma_available) { + tu->joint_cbcr_residual_flag = ff_vvc_tu_joint_cbcr_residual_flag(lc, tu->coded_flag[1], tu->coded_flag[2]); + } + + for (int i = 0; i < tu->nb_tbs; i++) { + TransformBlock *tb = &tu->tbs[i]; + const int is_chroma = tb->c_idx != LUMA; + tb->has_coeffs = tu->coded_flag[tb->c_idx]; + if (tb->has_coeffs && is_chroma) + tb->has_coeffs = tb->c_idx == CB ? 1 : !(tu->coded_flag[CB] && tu->joint_cbcr_residual_flag); + if (tb->has_coeffs) { + tb->ts = cu->bdpcm_flag[tb->c_idx]; + if (sps->r->sps_transform_skip_enabled_flag && !cu->bdpcm_flag[tb->c_idx] && + tb->tb_width <= sps->max_ts_size && tb->tb_height <= sps->max_ts_size && + !cu->sbt_flag && (is_chroma || !is_isp)) { + tb->ts = ff_vvc_transform_skip_flag(lc, is_chroma); + } + ret = ff_vvc_residual_coding(lc, tb); + if (ret < 0) + return ret; + set_tb_tab(fc->tab.tu_coded_flag[tb->c_idx], tu->coded_flag[tb->c_idx], fc, tb); + } + if (tb->c_idx != CR) + set_tb_pos(fc, tb); + if (tb->c_idx == CB) + set_tb_tab(fc->tab.tu_joint_cbcr_residual_flag, tu->joint_cbcr_residual_flag, fc, tb); + } + + return 0; +} + +static int hls_transform_tree(VVCLocalContext *lc, int x0, int y0,int tu_width, int tu_height, int ch_type) +{ + const CodingUnit *cu = lc->cu; + const VVCSPS *sps = lc->fc->ps.sps; + int ret; + + lc->parse.infer_tu_cbf_luma = 1; + if (cu->isp_split_type == ISP_NO_SPLIT && !cu->sbt_flag) { + if (tu_width > sps->max_tb_size_y || tu_height > sps->max_tb_size_y) { + const int ver_split_first = tu_width > sps->max_tb_size_y && tu_width > tu_height; + const int trafo_width = ver_split_first ? (tu_width / 2) : tu_width; + const int trafo_height = !ver_split_first ? (tu_height / 2) : tu_height; + + #define TRANSFORM_TREE(x, y) do { \ + ret = hls_transform_tree(lc, x, y, trafo_width, trafo_height, ch_type); \ + if (ret < 0) \ + return ret; \ + } while (0) + + TRANSFORM_TREE(x0, y0); + if (ver_split_first) + TRANSFORM_TREE(x0 + trafo_width, y0); + else + TRANSFORM_TREE(x0, y0 + trafo_height); + + } else { + ret = hls_transform_unit(lc, x0, y0, tu_width, tu_height, 0, ch_type); + if (ret < 0) + return ret; + + } + } else if (cu->sbt_flag) { + if (!cu->sbt_horizontal_flag) { + #define TRANSFORM_UNIT(x, width, idx) do { \ + ret = hls_transform_unit(lc, x, y0, width, tu_height, idx, ch_type); \ + if (ret < 0) \ + return ret; \ + } while (0) + + const int trafo_width = tu_width * lc->parse.sbt_num_fourths_tb0 / 4; + TRANSFORM_UNIT(x0, trafo_width, 0); + TRANSFORM_UNIT(x0 + trafo_width, tu_width - trafo_width, 1); + + #undef TRANSFORM_UNIT + } else { + #define TRANSFORM_UNIT(y, height, idx) do { \ + ret = hls_transform_unit(lc, x0, y, tu_width, height, idx, ch_type); \ + if (ret < 0) \ + return ret; \ + } while (0) + + const int trafo_height = tu_height * lc->parse.sbt_num_fourths_tb0 / 4; + TRANSFORM_UNIT(y0, trafo_height, 0); + TRANSFORM_UNIT(y0 + trafo_height, tu_height - trafo_height, 1); + + #undef TRANSFORM_UNIT + } + } else if (cu->isp_split_type == ISP_HOR_SPLIT) { + const int trafo_height = tu_height / cu->num_intra_subpartitions; + for (int i = 0; i < cu->num_intra_subpartitions; i++) { + ret = hls_transform_unit(lc, x0, y0 + trafo_height * i, tu_width, trafo_height, i, 0); + if (ret < 0) + return ret; + } + } else if (cu->isp_split_type == ISP_VER_SPLIT) { + const int trafo_width = tu_width / cu->num_intra_subpartitions; + for (int i = 0; i < cu->num_intra_subpartitions; i++) { + ret = hls_transform_unit(lc, x0 + trafo_width * i , y0, trafo_width, tu_height, i, 0); + if (ret < 0) + return ret; + } + } + + return 0; +} + +static int skipped_transform_tree(VVCLocalContext *lc, int x0, int y0,int tu_width, int tu_height) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + + if (tu_width > sps->max_tb_size_y || tu_height > sps->max_tb_size_y) { + const int ver_split_first = tu_width > sps->max_tb_size_y && tu_width > tu_height; + const int trafo_width = ver_split_first ? (tu_width / 2) : tu_width; + const int trafo_height = !ver_split_first ? (tu_height / 2) : tu_height; + + #define SKIPPED_TRANSFORM_TREE(x, y) do { \ + int ret = skipped_transform_tree(lc, x, y, trafo_width, trafo_height); \ + if (ret < 0) \ + return ret; \ + } while (0) + + SKIPPED_TRANSFORM_TREE(x0, y0); + if (ver_split_first) + SKIPPED_TRANSFORM_TREE(x0 + trafo_width, y0); + else + SKIPPED_TRANSFORM_TREE(x0, y0 + trafo_height); + } else { + TransformUnit *tu = add_tu(fc, lc->cu, x0, y0, tu_width, tu_height); + const int c_end = sps->r->sps_chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : (LUMA + 1); + if (!tu) + return AVERROR_INVALIDDATA; + for (int i = LUMA; i < c_end; i++) { + TransformBlock *tb = add_tb(tu, lc, x0, y0, tu_width >> sps->hshift[i], tu_height >> sps->vshift[i], i); + if (i != CR) + set_tb_pos(fc, tb); + } + } + + return 0; +} + +//6.4.1 Allowed quad split process +//6.4.2 Allowed binary split process +//6.4.3 Allowed ternary split process +static void can_split(const VVCLocalContext *lc, int x0, int y0,int cb_width, int cb_height, + int mtt_depth, int depth_offset, int part_idx, VVCSplitMode last_split_mode, + VVCTreeType tree_type, VVCModeType mode_type, VVCAllowedSplit* split) +{ + int min_qt_size, max_bt_size, max_tt_size, max_mtt_depth; + const VVCFrameContext *fc = lc->fc; + const VVCSH *sh = &lc->sc->sh; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int chroma = tree_type == DUAL_TREE_CHROMA; + int min_cb_size_y = sps->min_cb_size_y; + int *qt = &split->qt; + int *btv = &split->btv; + int *bth = &split->bth; + int *ttv = &split->ttv; + int *tth = &split->tth; + + *qt = *bth = *btv = *tth = *ttv = 1; + + if (mtt_depth) + *qt = 0; + + min_qt_size = sh->min_qt_size[chroma]; + if (cb_width <= min_qt_size) + *qt = 0; + + if (chroma) { + int chroma_area = (cb_width >> sps->hshift[1]) * (cb_height >> sps->vshift[1]); + int chroma_width = cb_width >> sps->hshift[1]; + + if (chroma_width == 8) + *ttv = 0; + else if (chroma_width <= 4) { + if (chroma_width == 4) + *btv = 0; + *qt = 0; + } + if (mode_type == MODE_TYPE_INTRA) + *qt = *btv = *bth = *ttv = *tth = 0; + if (chroma_area <= 32) { + *ttv = *tth = 0; + if (chroma_area <= 16) + *btv = *bth = 0; + } + } + max_bt_size = sh->max_bt_size[chroma]; + max_tt_size = sh->max_tt_size[chroma]; + max_mtt_depth = sh->max_mtt_depth[chroma] + depth_offset; + + if (mode_type == MODE_TYPE_INTER) { + int area = cb_width * cb_height; + if (area == 32) + *btv = *bth = 0; + else if (area == 64) + *ttv = *tth = 0; + } + if (cb_width <= 2 * min_cb_size_y) { + *ttv = 0; + if (cb_width <= min_cb_size_y) + *btv = 0; + } + if (cb_height <= 2 * min_cb_size_y) { + *tth = 0; + if (cb_height <= min_cb_size_y) + *bth = 0; + } + if (cb_width > max_bt_size || cb_height > max_bt_size) + *btv = *bth = 0; + max_tt_size = FFMIN(64, max_tt_size); + if (cb_width > max_tt_size || cb_height > max_tt_size) + *ttv = *tth = 0; + if (mtt_depth >= max_mtt_depth) + *btv = *bth = *ttv = *tth = 0; + if (x0 + cb_width > pps->width) { + *ttv = *tth = 0; + if (cb_height > 64) + *btv = 0; + if (y0 + cb_height <= pps->height) + *bth = 0; + else if (cb_width > min_qt_size) + *btv = *bth = 0; + } + if (y0 + cb_height > pps->height) { + *btv = *ttv = *tth = 0; + if (cb_width > 64) + *bth = 0; + } + if (mtt_depth > 0 && part_idx == 1) { + if (last_split_mode == SPLIT_TT_VER) + *btv = 0; + else if (last_split_mode == SPLIT_TT_HOR) + *bth = 0; + } + if (cb_width <= 64 && cb_height > 64) + *btv = 0; + if (cb_width > 64 && cb_height <= 64) + *bth = 0; +} + +static int get_num_intra_subpartitions(enum IspType isp_split_type, int cb_width, int cb_height) +{ + if (isp_split_type == ISP_NO_SPLIT) + return 1; + if ((cb_width == 4 && cb_height == 8) || (cb_width == 8 && cb_height == 4)) + return 2; + return 4; +} + +static int get_cclm_enabled(const VVCLocalContext *lc, const int x0, const int y0) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + int enabled = 0; + + if (!sps->r->sps_cclm_enabled_flag) + return 0; + if (!sps->r->sps_qtbtt_dual_tree_intra_flag || !IS_I(lc->sc->sh.r) || sps->ctb_log2_size_y < 6) + return 1; + else { + const int x64 = x0 >> 6 << 6; + const int y64 = y0 >> 6 << 6; + const int y32 = y0 >> 5 << 5; + const int x64_cu = x64 >> fc->ps.sps->min_cb_log2_size_y; + const int y64_cu = y64 >> fc->ps.sps->min_cb_log2_size_y; + const int y32_cu = y32 >> fc->ps.sps->min_cb_log2_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int depth = SAMPLE_CTB(fc->tab.cqt_depth[1], x64_cu, y64_cu); + const int min_depth = fc->ps.sps->ctb_log2_size_y - 6; + const VVCSplitMode msm64 = (VVCSplitMode)TAB_MSM(fc, 0, x64, y64); + const VVCSplitMode msm32 = (VVCSplitMode)TAB_MSM(fc, 1, x64, y32); + + enabled = SAMPLE_CTB(fc->tab.cb_width[1], x64_cu, y64_cu) == 64 && + SAMPLE_CTB(fc->tab.cb_height[1], x64_cu, y64_cu) == 64; + enabled |= depth == min_depth && msm64 == SPLIT_BT_HOR && + SAMPLE_CTB(fc->tab.cb_width[1], x64_cu, y32_cu) == 64 && + SAMPLE_CTB(fc->tab.cb_height[1], x64_cu, y32_cu) == 32; + enabled |= depth > min_depth; + enabled |= depth == min_depth && msm64 == SPLIT_BT_HOR && msm32 == SPLIT_BT_VER; + + if (enabled) { + const int w = SAMPLE_CTB(fc->tab.cb_width[0], x64_cu, y64_cu); + const int h = SAMPLE_CTB(fc->tab.cb_height[0], x64_cu, y64_cu); + const int depth0 = SAMPLE_CTB(fc->tab.cqt_depth[0], x64_cu, y64_cu); + if ((w == 64 && h == 64 && TAB_ISPMF(fc, x64, y64)) || + ((w < 64 || h < 64) && depth0 == min_depth)) + return 0; + } + + } + + return enabled; +} + +static int less(const void *a, const void *b) +{ + return *(const int*)a - *(const int*)b; +} + +//8.4.2 Derivation process for luma intra prediction mode +static enum IntraPredMode luma_intra_pred_mode(VVCLocalContext* lc, const int intra_subpartitions_mode_flag) +{ + VVCFrameContext *fc = lc->fc; + CodingUnit *cu = lc->cu; + const int x0 = cu->x0; + const int y0 = cu->y0; + enum IntraPredMode pred; + int intra_luma_not_planar_flag = 1; + int intra_luma_mpm_remainder = 0; + int intra_luma_mpm_flag = 1; + int intra_luma_mpm_idx = 0; + + if (!cu->intra_luma_ref_idx) + intra_luma_mpm_flag = ff_vvc_intra_luma_mpm_flag(lc); + if (intra_luma_mpm_flag) { + if (!cu->intra_luma_ref_idx) + intra_luma_not_planar_flag = ff_vvc_intra_luma_not_planar_flag(lc, intra_subpartitions_mode_flag); + if (intra_luma_not_planar_flag) + intra_luma_mpm_idx = ff_vvc_intra_luma_mpm_idx(lc); + } else { + intra_luma_mpm_remainder = ff_vvc_intra_luma_mpm_remainder(lc); + } + + if (!intra_luma_not_planar_flag) { + pred = INTRA_PLANAR; + } else { + const VVCSPS *sps = fc->ps.sps; + const int x_a = (x0 - 1) >> sps->min_cb_log2_size_y; + const int y_a = (y0 + cu->cb_height - 1) >> sps->min_cb_log2_size_y; + const int x_b = (x0 + cu->cb_width - 1) >> sps->min_cb_log2_size_y; + const int y_b = (y0 - 1) >> sps->min_cb_log2_size_y; + int min_cb_width = fc->ps.pps->min_cb_width; + int x0b = av_mod_uintp2(x0, sps->ctb_log2_size_y); + int y0b = av_mod_uintp2(y0, sps->ctb_log2_size_y); + const int available_l = lc->ctb_left_flag || x0b; + const int available_u = lc->ctb_up_flag || y0b; + + int a, b, cand[5]; + + if (!available_l || (SAMPLE_CTB(fc->tab.cpm[0], x_a, y_a) != MODE_INTRA) || + SAMPLE_CTB(fc->tab.imf, x_a, y_a)) { + a = INTRA_PLANAR; + } else { + a = SAMPLE_CTB(fc->tab.ipm, x_a, y_a); + } + + if (!available_u || (SAMPLE_CTB(fc->tab.cpm[0], x_b, y_b) != MODE_INTRA) || + SAMPLE_CTB(fc->tab.imf, x_b, y_b) || !y0b) { + b = INTRA_PLANAR; + } else { + b = SAMPLE_CTB(fc->tab.ipm, x_b, y_b); + } + + if (a == b && a > INTRA_DC) { + cand[0] = a; + cand[1] = 2 + ((a + 61) % 64); + cand[2] = 2 + ((a - 1) % 64); + cand[3] = 2 + ((a + 60) % 64); + cand[4] = 2 + (a % 64); + } else { + const int minab = FFMIN(a, b); + const int maxab = FFMAX(a, b); + if (a > INTRA_DC && b > INTRA_DC) { + const int diff = maxab - minab; + cand[0] = a; + cand[1] = b; + if (diff == 1) { + cand[2] = 2 + ((minab + 61) % 64); + cand[3] = 2 + ((maxab - 1) % 64); + cand[4] = 2 + ((minab + 60) % 64); + } else if (diff >= 62) { + cand[2] = 2 + ((minab - 1) % 64); + cand[3] = 2 + ((maxab + 61) % 64); + cand[4] = 2 + (minab % 64); + } else if (diff == 2) { + cand[2] = 2 + ((minab - 1) % 64); + cand[3] = 2 + ((minab + 61) % 64); + cand[4] = 2 + ((maxab - 1) % 64); + } else { + cand[2] = 2 + ((minab + 61) % 64); + cand[3] = 2 + ((minab - 1) % 64); + cand[4] = 2 + ((maxab + 61) % 64); + } + } else if (a > INTRA_DC || b > INTRA_DC) { + cand[0] = maxab; + cand[1] = 2 + ((maxab + 61 ) % 64); + cand[2] = 2 + ((maxab - 1) % 64); + cand[3] = 2 + ((maxab + 60 ) % 64); + cand[4] = 2 + (maxab % 64); + } else { + cand[0] = INTRA_DC; + cand[1] = INTRA_VERT; + cand[2] = INTRA_HORZ; + cand[3] = INTRA_VERT - 4; + cand[4] = INTRA_VERT + 4; + } + } + if (intra_luma_mpm_flag) { + pred = cand[intra_luma_mpm_idx]; + } else { + qsort(cand, FF_ARRAY_ELEMS(cand), sizeof(cand[0]), less); + pred = intra_luma_mpm_remainder + 1; + for (int i = 0; i < FF_ARRAY_ELEMS(cand); i++) { + if (pred >= cand[i]) + pred++; + } + } + } + return pred; +} + +static int lfnst_idx_decode(VVCLocalContext *lc) +{ + CodingUnit *cu = lc->cu; + const VVCTreeType tree_type = cu->tree_type; + const VVCSPS *sps = lc->fc->ps.sps; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + const TransformUnit *tu = cu->tus.head; + int lfnst_width, lfnst_height, min_lfnst; + int lfnst_idx = 0; + + memset(cu->apply_lfnst_flag, 0, sizeof(cu->apply_lfnst_flag)); + + if (!sps->r->sps_lfnst_enabled_flag || cu->pred_mode != MODE_INTRA || FFMAX(cb_width, cb_height) > sps->max_tb_size_y) + return 0; + + while (tu) { + for (int j = 0; j < tu->nb_tbs; j++) { + const TransformBlock *tb = tu->tbs + j; + if (tu->coded_flag[tb->c_idx] && tb->ts) + return 0; + } + tu = tu->next; + } + + if (tree_type == DUAL_TREE_CHROMA) { + lfnst_width = cb_width >> sps->hshift[1]; + lfnst_height = cb_height >> sps->vshift[1]; + } else { + const int vs = cu->isp_split_type == ISP_VER_SPLIT; + const int hs = cu->isp_split_type == ISP_HOR_SPLIT; + lfnst_width = vs ? cb_width / cu->num_intra_subpartitions : cb_width; + lfnst_height = hs ? cb_height / cu->num_intra_subpartitions : cb_height; + } + min_lfnst = FFMIN(lfnst_width, lfnst_height); + if (tree_type != DUAL_TREE_CHROMA && cu->intra_mip_flag && min_lfnst < 16) + return 0; + + if (min_lfnst >= 4) { + if ((cu->isp_split_type != ISP_NO_SPLIT || !lc->parse.lfnst_dc_only) && lc->parse.lfnst_zero_out_sig_coeff_flag) + lfnst_idx = ff_vvc_lfnst_idx(lc, tree_type != SINGLE_TREE); + } + + if (lfnst_idx) { + cu->apply_lfnst_flag[LUMA] = tree_type != DUAL_TREE_CHROMA; + cu->apply_lfnst_flag[CB] = cu->apply_lfnst_flag[CR] = tree_type == DUAL_TREE_CHROMA; + } + + return lfnst_idx; +} + +static MtsIdx mts_idx_decode(VVCLocalContext *lc) +{ + const CodingUnit *cu = lc->cu; + const VVCSPS *sps = lc->fc->ps.sps; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + const uint8_t transform_skip_flag = cu->tus.head->tbs[0].ts; //fix me + int mts_idx = MTS_DCT2_DCT2; + if (cu->tree_type != DUAL_TREE_CHROMA && !cu->lfnst_idx && + !transform_skip_flag && FFMAX(cb_width, cb_height) <= 32 && + cu->isp_split_type == ISP_NO_SPLIT && !cu->sbt_flag && + lc->parse.mts_zero_out_sig_coeff_flag && !lc->parse.mts_dc_only) { + if ((cu->pred_mode == MODE_INTER && sps->r->sps_explicit_mts_inter_enabled_flag) || + (cu->pred_mode == MODE_INTRA && sps->r->sps_explicit_mts_intra_enabled_flag)) { + mts_idx = ff_vvc_mts_idx(lc); + } + } + + return mts_idx; +} + +static enum IntraPredMode derive_center_luma_intra_pred_mode(const VVCFrameContext *fc, const VVCSPS *sps, const VVCPPS *pps, const CodingUnit *cu) +{ + const int x_center = (cu->x0 + cu->cb_width / 2) >> sps->min_cb_log2_size_y; + const int y_center = (cu->y0 + cu->cb_height / 2) >> sps->min_cb_log2_size_y; + const int min_cb_width = pps->min_cb_width; + const int intra_mip_flag = SAMPLE_CTB(fc->tab.imf, x_center, y_center); + const int cu_pred_mode = SAMPLE_CTB(fc->tab.cpm[0], x_center, y_center); + const int intra_pred_mode_y = SAMPLE_CTB(fc->tab.ipm, x_center, y_center); + + if (intra_mip_flag) { + if (cu->tree_type == SINGLE_TREE && sps->r->sps_chroma_format_idc == CHROMA_FORMAT_444) + return INTRA_INVALID; + return INTRA_PLANAR; + } + if (cu_pred_mode == MODE_IBC || cu_pred_mode == MODE_PLT) + return INTRA_DC; + return intra_pred_mode_y; +} + +static void derive_chroma_intra_pred_mode(VVCLocalContext *lc, + const int cclm_mode_flag, const int cclm_mode_idx, const int intra_chroma_pred_mode) +{ + const VVCFrameContext *fc = lc->fc; + CodingUnit *cu = lc->cu; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int x_cb = cu->x0 >> sps->min_cb_log2_size_y; + const int y_cb = cu->y0 >> sps->min_cb_log2_size_y; + const int min_cb_width = pps->min_cb_width; + const int intra_mip_flag = SAMPLE_CTB(fc->tab.imf, x_cb, y_cb); + enum IntraPredMode luma_intra_pred_mode = SAMPLE_CTB(fc->tab.ipm, x_cb, y_cb); + + if (cu->tree_type == SINGLE_TREE && sps->r->sps_chroma_format_idc == CHROMA_FORMAT_444 && + intra_chroma_pred_mode == 4 && intra_mip_flag) { + cu->mip_chroma_direct_flag = 1; + cu->intra_pred_mode_c = luma_intra_pred_mode; + return; + } + luma_intra_pred_mode = derive_center_luma_intra_pred_mode(fc, sps, pps, cu); + + if (cu->act_enabled_flag) { + cu->intra_pred_mode_c = luma_intra_pred_mode; + return; + } + if (cclm_mode_flag) { + cu->intra_pred_mode_c = INTRA_LT_CCLM + cclm_mode_idx; + } else if (intra_chroma_pred_mode == 4){ + cu->intra_pred_mode_c = luma_intra_pred_mode; + } else { + const static IntraPredMode pred_mode_c[][4 + 1] = { + {INTRA_VDIAG, INTRA_PLANAR, INTRA_PLANAR, INTRA_PLANAR, INTRA_PLANAR}, + {INTRA_VERT, INTRA_VDIAG, INTRA_VERT, INTRA_VERT, INTRA_VERT}, + {INTRA_HORZ, INTRA_HORZ, INTRA_VDIAG, INTRA_HORZ, INTRA_HORZ}, + {INTRA_DC, INTRA_DC, INTRA_DC, INTRA_VDIAG, INTRA_DC}, + }; + const int modes[4] = {INTRA_PLANAR, INTRA_VERT, INTRA_HORZ, INTRA_DC}; + int idx; + + // This workaround is necessary to have 4:4:4 video decode correctly + // See VVC ticket https://jvet.hhi.fraunhofer.de/trac/vvc/ticket/1602 + // and VTM source https://vcgit.hhi.fraunhofer.de/jvet/VVCSoftware_VTM/-/blob/master/source/Lib/CommonLib/UnitTools.cpp#L736 + if (cu->tree_type == SINGLE_TREE && sps->r->sps_chroma_format_idc == CHROMA_FORMAT_444 && intra_mip_flag) { + idx = 4; + } else { + for (idx = 0; idx < FF_ARRAY_ELEMS(modes); idx++) { + if (modes[idx] == luma_intra_pred_mode) + break; + } + } + + cu->intra_pred_mode_c = pred_mode_c[intra_chroma_pred_mode][idx]; + } + if (sps->r->sps_chroma_format_idc == CHROMA_FORMAT_422 && cu->intra_pred_mode_c <= INTRA_VDIAG) { + const static int mode_map_422[INTRA_VDIAG + 1] = { + 0, 1, 61, 62, 63, 64, 65, 66, 2, 3, 5, 6, 8, 10, 12, 13, + 14, 16, 18, 20, 22, 23, 24, 26, 28, 30, 31, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 41, 42, 43, 43, 44, 44, 45, 45, 46, 47, 48, 48, + 49, 49, 50, 51, 51, 52, 52, 53, 54, 55, 55, 56, 56, 57, 57, 58, + 59, 59, 60, + }; + cu->intra_pred_mode_c = mode_map_422[cu->intra_pred_mode_c]; + } +} + +static void intra_luma_pred_modes(VVCLocalContext *lc) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + CodingUnit *cu = lc->cu; + const int log2_min_cb_size = sps->min_cb_log2_size_y; + const int x0 = cu->x0; + const int y0 = cu->y0; + const int x_cb = x0 >> log2_min_cb_size; + const int y_cb = y0 >> log2_min_cb_size; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + + cu->intra_luma_ref_idx = 0; + if (sps->r->sps_bdpcm_enabled_flag && cb_width <= sps->max_ts_size && cb_height <= sps->max_ts_size) + cu->bdpcm_flag[LUMA] = ff_vvc_intra_bdpcm_luma_flag(lc); + if (cu->bdpcm_flag[LUMA]) { + cu->intra_pred_mode_y = ff_vvc_intra_bdpcm_luma_dir_flag(lc) ? INTRA_VERT : INTRA_HORZ; + } else { + if (sps->r->sps_mip_enabled_flag) + cu->intra_mip_flag = ff_vvc_intra_mip_flag(lc, fc->tab.imf); + if (cu->intra_mip_flag) { + int intra_mip_transposed_flag = ff_vvc_intra_mip_transposed_flag(lc); + int intra_mip_mode = ff_vvc_intra_mip_mode(lc); + int x = y_cb * pps->min_cb_width + x_cb; + for (int y = 0; y < (cb_height>>log2_min_cb_size); y++) { + int width = cb_width>>log2_min_cb_size; + memset(&fc->tab.imf[x], cu->intra_mip_flag, width); + fc->tab.imtf[x] = intra_mip_transposed_flag; + fc->tab.imm[x] = intra_mip_mode; + x += pps->min_cb_width; + } + cu->intra_pred_mode_y = intra_mip_mode; + } else { + int intra_subpartitions_mode_flag = 0; + if (sps->r->sps_mrl_enabled_flag && ((y0 % sps->ctb_size_y) > 0)) + cu->intra_luma_ref_idx = ff_vvc_intra_luma_ref_idx(lc); + if (sps->r->sps_isp_enabled_flag && !cu->intra_luma_ref_idx && + (cb_width <= sps->max_tb_size_y && cb_height <= sps->max_tb_size_y) && + (cb_width * cb_height > MIN_TU_SIZE * MIN_TU_SIZE) && + !cu->act_enabled_flag) + intra_subpartitions_mode_flag = ff_vvc_intra_subpartitions_mode_flag(lc); + if (!(x0 & 63) && !(y0 & 63)) + TAB_ISPMF(fc, x0, y0) = intra_subpartitions_mode_flag; + cu->isp_split_type = ff_vvc_isp_split_type(lc, intra_subpartitions_mode_flag); + cu->num_intra_subpartitions = get_num_intra_subpartitions(cu->isp_split_type, cb_width, cb_height); + cu->intra_pred_mode_y = luma_intra_pred_mode(lc, intra_subpartitions_mode_flag); + } + } + set_cb_tab(lc, fc->tab.ipm, cu->intra_pred_mode_y); +} + +static void intra_chroma_pred_modes(VVCLocalContext *lc) +{ + const VVCSPS *sps = lc->fc->ps.sps; + CodingUnit *cu = lc->cu; + const int hs = sps->hshift[CHROMA]; + const int vs = sps->vshift[CHROMA]; + + cu->mip_chroma_direct_flag = 0; + if (sps->r->sps_bdpcm_enabled_flag && + (cu->cb_width >> hs) <= sps->max_ts_size && + (cu->cb_height >> vs) <= sps->max_ts_size) { + cu->bdpcm_flag[CB] = cu->bdpcm_flag[CR] = ff_vvc_intra_bdpcm_chroma_flag(lc); + } + if (cu->bdpcm_flag[CHROMA]) { + cu->intra_pred_mode_c = ff_vvc_intra_bdpcm_chroma_dir_flag(lc) ? INTRA_VERT : INTRA_HORZ; + } else { + const int cclm_enabled = get_cclm_enabled(lc, cu->x0, cu->y0); + int cclm_mode_flag = 0; + int cclm_mode_idx = 0; + int intra_chroma_pred_mode = 0; + + if (cclm_enabled) + cclm_mode_flag = ff_vvc_cclm_mode_flag(lc); + + if (cclm_mode_flag) + cclm_mode_idx = ff_vvc_cclm_mode_idx(lc); + else + intra_chroma_pred_mode = ff_vvc_intra_chroma_pred_mode(lc); + derive_chroma_intra_pred_mode(lc, cclm_mode_flag, cclm_mode_idx, intra_chroma_pred_mode); + } +} + +static PredMode pred_mode_decode(VVCLocalContext *lc, + const VVCTreeType tree_type, + const VVCModeType mode_type) +{ + const VVCFrameContext *fc = lc->fc; + CodingUnit *cu = lc->cu; + const VVCSPS *sps = fc->ps.sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int ch_type = tree_type == DUAL_TREE_CHROMA ? 1 : 0; + const int is_4x4 = cu->cb_width == 4 && cu->cb_height == 4; + int pred_mode_flag; + int pred_mode_ibc_flag; + PredMode pred_mode; + + cu->skip_flag = 0; + if (!IS_I(rsh) || sps->r->sps_ibc_enabled_flag) { + const int is_128 = cu->cb_width == 128 || cu->cb_height == 128; + if (tree_type != DUAL_TREE_CHROMA && + ((!is_4x4 && mode_type != MODE_TYPE_INTRA) || + (sps->r->sps_ibc_enabled_flag && !is_128))) { + cu->skip_flag = ff_vvc_cu_skip_flag(lc, fc->tab.skip); + } + + if (is_4x4 || mode_type == MODE_TYPE_INTRA || IS_I(rsh)) { + pred_mode_flag = 1; + } else if (mode_type == MODE_TYPE_INTER || cu->skip_flag) { + pred_mode_flag = 0; + } else { + pred_mode_flag = ff_vvc_pred_mode_flag(lc, ch_type); + } + pred_mode = pred_mode_flag ? MODE_INTRA : MODE_INTER; + + if (((IS_I(rsh) && !cu->skip_flag) || + (!IS_I(rsh) && (pred_mode != MODE_INTRA || + ((is_4x4 || mode_type == MODE_TYPE_INTRA) && !cu->skip_flag)))) && + !is_128 && mode_type != MODE_TYPE_INTER && sps->r->sps_ibc_enabled_flag && + tree_type != DUAL_TREE_CHROMA) { + pred_mode_ibc_flag = ff_vvc_pred_mode_ibc_flag(lc, ch_type); + } else if (cu->skip_flag && (is_4x4 || mode_type == MODE_TYPE_INTRA)) { + pred_mode_ibc_flag = 1; + } else if (is_128 || mode_type == MODE_TYPE_INTER || tree_type == DUAL_TREE_CHROMA) { + pred_mode_ibc_flag = 0; + } else { + pred_mode_ibc_flag = (IS_I(rsh)) ? sps->r->sps_ibc_enabled_flag : 0; + } + if (pred_mode_ibc_flag) + pred_mode = MODE_IBC; + } else { + pred_mode_flag = is_4x4 || mode_type == MODE_TYPE_INTRA || + mode_type != MODE_TYPE_INTER || IS_I(rsh); + pred_mode = pred_mode_flag ? MODE_INTRA : MODE_INTER; + } + return pred_mode; +} + +static void sbt_info(VVCLocalContext *lc, const VVCSPS *sps) +{ + CodingUnit *cu = lc->cu; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + + if (cu->pred_mode == MODE_INTER && sps->r->sps_sbt_enabled_flag && !cu->ciip_flag + && cb_width <= sps->max_tb_size_y && cb_height <= sps->max_tb_size_y) { + const int sbt_ver_h = cb_width >= 8; + const int sbt_hor_h = cb_height >= 8; + cu->sbt_flag = 0; + if (sbt_ver_h || sbt_hor_h) + cu->sbt_flag = ff_vvc_sbt_flag(lc); + if (cu->sbt_flag) { + const int sbt_ver_q = cb_width >= 16; + const int sbt_hor_q = cb_height >= 16; + int cu_sbt_quad_flag = 0; + + if ((sbt_ver_h || sbt_hor_h) && (sbt_ver_q || sbt_hor_q)) + cu_sbt_quad_flag = ff_vvc_sbt_quad_flag(lc); + if (cu_sbt_quad_flag) { + cu->sbt_horizontal_flag = sbt_hor_q; + if (sbt_ver_q && sbt_hor_q) + cu->sbt_horizontal_flag = ff_vvc_sbt_horizontal_flag(lc); + } else { + cu->sbt_horizontal_flag = sbt_hor_h; + if (sbt_ver_h && sbt_hor_h) + cu->sbt_horizontal_flag = ff_vvc_sbt_horizontal_flag(lc); + } + cu->sbt_pos_flag = ff_vvc_sbt_pos_flag(lc); + + { + const int sbt_min = cu_sbt_quad_flag ? 1 : 2; + lc->parse.sbt_num_fourths_tb0 = cu->sbt_pos_flag ? (4 - sbt_min) : sbt_min; + } + } + } +} + +static int skipped_transform_tree_unit(VVCLocalContext *lc) +{ + const CodingUnit *cu = lc->cu; + int ret; + + set_qp_y(lc, cu->x0, cu->y0, 0); + set_qp_c(lc); + ret = skipped_transform_tree(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + if (ret < 0) + return ret; + return 0; +} + +static void set_cb_pos(const VVCFrameContext *fc, const CodingUnit *cu) +{ + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int log2_min_cb_size = sps->min_cb_log2_size_y; + const int x_cb = cu->x0 >> log2_min_cb_size; + const int y_cb = cu->y0 >> log2_min_cb_size; + const int ch_type = cu->ch_type; + int x, y; + + x = y_cb * pps->min_cb_width + x_cb; + for (y = 0; y < (cu->cb_height >> log2_min_cb_size); y++) { + const int width = cu->cb_width >> log2_min_cb_size; + + for (int i = 0; i < width; i++) { + fc->tab.cb_pos_x[ch_type][x + i] = cu->x0; + fc->tab.cb_pos_y[ch_type][x + i] = cu->y0; + } + memset(&fc->tab.cb_width[ch_type][x], cu->cb_width, width); + memset(&fc->tab.cb_height[ch_type][x], cu->cb_height, width); + memset(&fc->tab.cqt_depth[ch_type][x], cu->cqt_depth, width); + + x += pps->min_cb_width; + } +} + +static CodingUnit* alloc_cu(VVCLocalContext *lc, const int x0, const int y0) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int rx = x0 >> sps->ctb_log2_size_y; + const int ry = y0 >> sps->ctb_log2_size_y; + CTU *ctu = fc->tab.ctus + ry * pps->ctb_width + rx; + CodingUnit *cu; + + AVBufferRef *buf = av_buffer_pool_get(fc->cu_pool); + if (!buf) + return NULL; + cu = (CodingUnit *)buf->data; + cu->next = NULL; + cu->buf = buf; + + if (lc->cu) + lc->cu->next = cu; + else + ctu->cus = cu; + lc->cu = cu; + + return cu; +} + +static CodingUnit* add_cu(VVCLocalContext *lc, const int x0, const int y0, + const int cb_width, const int cb_height, const int cqt_depth, const VVCTreeType tree_type) +{ + VVCFrameContext *fc = lc->fc; + const int ch_type = tree_type == DUAL_TREE_CHROMA ? 1 : 0; + CodingUnit *cu = alloc_cu(lc, x0, y0); + + if (!cu) + return NULL; + + memset(&cu->pu, 0, sizeof(cu->pu)); + + lc->parse.prev_tu_cbf_y = 0; + + cu->sbt_flag = 0; + cu->act_enabled_flag = 0; + + cu->tree_type = tree_type; + cu->x0 = x0; + cu->y0 = y0; + cu->cb_width = cb_width; + cu->cb_height = cb_height; + cu->ch_type = ch_type; + cu->cqt_depth = cqt_depth; + cu->tus.head = cu->tus.tail = NULL; + cu->bdpcm_flag[LUMA] = cu->bdpcm_flag[CB] = cu->bdpcm_flag[CR] = 0; + cu->isp_split_type = ISP_NO_SPLIT; + cu->intra_mip_flag = 0; + cu->ciip_flag = 0; + cu->coded_flag = 1; + cu->num_intra_subpartitions = 1; + + set_cb_pos(fc, cu); + return cu; +} + +static void set_cu_tabs(const VVCLocalContext *lc, const CodingUnit *cu) +{ + const VVCFrameContext *fc = lc->fc; + const TransformUnit *tu = cu->tus.head; + + set_cb_tab(lc, fc->tab.cpm[cu->ch_type], cu->pred_mode); + if (cu->tree_type != DUAL_TREE_CHROMA) + set_cb_tab(lc, fc->tab.skip, cu->skip_flag); + + while (tu) { + for (int j = 0; j < tu->nb_tbs; j++) { + const TransformBlock *tb = tu->tbs + j; + if (tb->c_idx != LUMA) + set_qp_c_tab(lc, tu, tb); + if (tb->c_idx != CR && cu->bdpcm_flag[tb->c_idx]) + set_tb_tab(fc->tab.pcmf[tb->c_idx], 1, fc, tb); + } + tu = tu->next; + } +} + +//8.5.2.7 Derivation process for merge motion vector difference +static void derive_mmvd(const VVCLocalContext *lc, MvField *mvf, const Mv *mmvd_offset) +{ + const SliceContext *sc = lc->sc; + Mv mmvd[2]; + + if (mvf->pred_flag == PF_BI) { + const RefPicList *rpl = sc->rpl; + const int poc = lc->fc->ps.ph.poc; + const int diff[] = { + poc - rpl[0].list[mvf->ref_idx[0]], + poc - rpl[1].list[mvf->ref_idx[1]] + }; + const int sign = FFSIGN(diff[0]) != FFSIGN(diff[1]); + + if (diff[0] == diff[1]) { + mmvd[1] = mmvd[0] = *mmvd_offset; + } + else { + const int i = FFABS(diff[0]) < FFABS(diff[1]); + const int o = !i; + mmvd[i] = *mmvd_offset; + if (!rpl[0].isLongTerm[mvf->ref_idx[0]] && !rpl[1].isLongTerm[mvf->ref_idx[1]]) { + ff_vvc_mv_scale(&mmvd[o], mmvd_offset, diff[i], diff[o]); + } + else { + mmvd[o].x = sign ? -mmvd[i].x : mmvd[i].x; + mmvd[o].y = sign ? -mmvd[i].y : mmvd[i].y; + } + } + mvf->mv[0].x += mmvd[0].x; + mvf->mv[0].y += mmvd[0].y; + mvf->mv[1].x += mmvd[1].x; + mvf->mv[1].y += mmvd[1].y; + } else { + const int idx = mvf->pred_flag - PF_L0; + mvf->mv[idx].x += mmvd_offset->x; + mvf->mv[idx].y += mmvd_offset->y; + } + +} + +static void mvf_to_mi(const MvField *mvf, MotionInfo *mi) +{ + mi->pred_flag = mvf->pred_flag; + mi->bcw_idx = mvf->bcw_idx; + mi->hpel_if_idx = mvf->hpel_if_idx; + for (int i = 0; i < 2; i++) { + const PredFlag mask = i + 1; + if (mvf->pred_flag & mask) { + mi->mv[i][0] = mvf->mv[i]; + mi->ref_idx[i] = mvf->ref_idx[i]; + } + } +} + +static void mv_merge_refine_pred_flag(MvField *mvf, const int width, const int height) +{ + if (mvf->pred_flag == PF_BI && (width + height) == 12) { + mvf->pred_flag = PF_L0; + mvf->bcw_idx = 0; + } +} + +// subblock-based inter prediction data +static void merge_data_subblock(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPH *ph = &fc->ps.ph; + CodingUnit* cu = lc->cu; + PredictionUnit *pu = &cu->pu; + int merge_subblock_idx = 0; + + set_cb_tab(lc, fc->tab.msf, pu->merge_subblock_flag); + if (ph->max_num_subblock_merge_cand > 1) { + merge_subblock_idx = ff_vvc_merge_subblock_idx(lc, ph->max_num_subblock_merge_cand); + } + ff_vvc_sb_mv_merge_mode(lc, merge_subblock_idx, pu); +} + +static void merge_data_regular(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPH *ph = &fc->ps.ph; + const CodingUnit* cu = lc->cu; + PredictionUnit *pu = &lc->cu->pu; + int merge_idx = 0; + Mv mmvd_offset; + MvField mvf; + + if (sps->r->sps_mmvd_enabled_flag) + pu->mmvd_merge_flag = ff_vvc_mmvd_merge_flag(lc); + if (pu->mmvd_merge_flag) { + int mmvd_cand_flag = 0; + if (sps->max_num_merge_cand > 1) + mmvd_cand_flag = ff_vvc_mmvd_cand_flag(lc); + ff_vvc_mmvd_offset_coding(lc, &mmvd_offset, ph->r->ph_mmvd_fullpel_only_flag); + merge_idx = mmvd_cand_flag; + } else if (sps->max_num_merge_cand > 1) { + merge_idx = ff_vvc_merge_idx(lc); + } + ff_vvc_luma_mv_merge_mode(lc, merge_idx, 0, &mvf); + if (pu->mmvd_merge_flag) + derive_mmvd(lc, &mvf, &mmvd_offset); + mv_merge_refine_pred_flag(&mvf, cu->cb_width, cu->cb_height); + ff_vvc_store_mvf(lc, &mvf); + mvf_to_mi(&mvf, &pu->mi); +} + +static int ciip_flag_decode(VVCLocalContext *lc, const int ciip_avaiable, const int gpm_avaiable, const int is_128) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const CodingUnit *cu = lc->cu; + + if (ciip_avaiable && gpm_avaiable) + return ff_vvc_ciip_flag(lc); + return sps->r->sps_ciip_enabled_flag && !cu->skip_flag && + !is_128 && (cu->cb_width * cu->cb_height >= 64); +} + +static void merge_data_gpm(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + PredictionUnit *pu = &lc->cu->pu; + int merge_gpm_idx[2]; + + pu->merge_gpm_flag = 1; + pu->gpm_partition_idx = ff_vvc_merge_gpm_partition_idx(lc); + merge_gpm_idx[0] = ff_vvc_merge_gpm_idx(lc, 0); + merge_gpm_idx[1] = 0; + if (sps->max_num_gpm_merge_cand > 2) + merge_gpm_idx[1] = ff_vvc_merge_gpm_idx(lc, 1); + + ff_vvc_luma_mv_merge_gpm(lc, merge_gpm_idx, pu->gpm_mv); + ff_vvc_store_gpm_mvf(lc, pu); +} + +static void merge_data_ciip(VVCLocalContext *lc) +{ + const VVCFrameContext* fc = lc->fc; + const VVCSPS* sps = fc->ps.sps; + CodingUnit *cu = lc->cu; + MotionInfo *mi = &cu->pu.mi; + int merge_idx = 0; + MvField mvf; + + if (sps->max_num_merge_cand > 1) + merge_idx = ff_vvc_merge_idx(lc); + ff_vvc_luma_mv_merge_mode(lc, merge_idx, 1, &mvf); + mv_merge_refine_pred_flag(&mvf, cu->cb_width, cu->cb_height); + ff_vvc_store_mvf(lc, &mvf); + mvf_to_mi(&mvf, mi); + cu->intra_pred_mode_y = cu->intra_pred_mode_c = INTRA_PLANAR; + cu->intra_luma_ref_idx = 0; + cu->intra_mip_flag = 0; +} + +// block-based inter prediction data +static void merge_data_block(VVCLocalContext *lc) +{ + const VVCFrameContext* fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + CodingUnit *cu = lc->cu; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + const int is_128 = cb_width == 128 || cb_height == 128; + const int ciip_avaiable = sps->r->sps_ciip_enabled_flag && + !cu->skip_flag && (cb_width * cb_height >= 64); + const int gpm_avaiable = sps->r->sps_gpm_enabled_flag && IS_B(rsh) && + (cb_width >= 8) && (cb_height >=8) && + (cb_width < 8 * cb_height) && (cb_height < 8 *cb_width); + + int regular_merge_flag = 1; + + if (!is_128 && (ciip_avaiable || gpm_avaiable)) + regular_merge_flag = ff_vvc_regular_merge_flag(lc, cu->skip_flag); + if (regular_merge_flag) { + merge_data_regular(lc); + } else { + cu->ciip_flag = ciip_flag_decode(lc, ciip_avaiable, gpm_avaiable, is_128); + if (cu->ciip_flag) + merge_data_ciip(lc); + else + merge_data_gpm(lc); + } +} + +static int hls_merge_data(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPH *ph = &fc->ps.ph; + const CodingUnit *cu = lc->cu; + PredictionUnit *pu = &lc->cu->pu; + + pu->merge_gpm_flag = 0; + pu->mi.num_sb_x = pu->mi.num_sb_y = 1; + if (cu->pred_mode == MODE_IBC) { + avpriv_report_missing_feature(lc->fc->avctx, "Intra Block Copy"); + return AVERROR_PATCHWELCOME; + } else { + if (ph->max_num_subblock_merge_cand > 0 && cu->cb_width >= 8 && cu->cb_height >= 8) + pu->merge_subblock_flag = ff_vvc_merge_subblock_flag(lc); + if (pu->merge_subblock_flag) + merge_data_subblock(lc); + else + merge_data_block(lc); + } + return 0; +} + +static void hls_mvd_coding(VVCLocalContext *lc, Mv* mvd) +{ + int16_t mv[2]; + int i; + + for (i = 0; i < 2; i++) { + mv[i] = ff_vvc_abs_mvd_greater0_flag(lc); + } + for (i = 0; i < 2; i++) { + if (mv[i]) + mv[i] += ff_vvc_abs_mvd_greater1_flag(lc); + } + for (i = 0; i < 2; i++) { + if (mv[i] > 0) { + if (mv[i] == 2) + mv[i] += ff_vvc_abs_mvd_minus2(lc); + mv[i] = (1 - 2 * ff_vvc_mvd_sign_flag(lc)) * mv[i]; + } + } + mvd->x = mv[0]; + mvd->y = mv[1]; +} + +static int bcw_idx_decode(VVCLocalContext *lc, const MotionInfo *mi, const int cb_width, const int cb_height) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const VVCPH *ph = &fc->ps.ph; + const VVCSH *sh = &lc->sc->sh; + const PredWeightTable *w = pps->r->pps_wp_info_in_ph_flag ? &ph->pwt : &sh->pwt; + int bcw_idx = 0; + + if (sps->r->sps_bcw_enabled_flag && mi->pred_flag == PF_BI && + !w->weight_flag[L0][LUMA][mi->ref_idx[0]] && + !w->weight_flag[L1][LUMA][mi->ref_idx[1]] && + !w->weight_flag[L0][CHROMA][mi->ref_idx[0]] && + !w->weight_flag[L1][CHROMA][mi->ref_idx[1]] && + cb_width * cb_height >= 256) { + bcw_idx = ff_vvc_bcw_idx(lc, ff_vvc_no_backward_pred_flag(lc)); + } + return bcw_idx; +} + +static int8_t ref_idx_decode(VVCLocalContext *lc, const VVCSH *sh, const int sym_mvd_flag, const int lx) +{ + const H266RawSliceHeader *rsh = sh->r; + int ref_idx = 0; + + if (rsh->num_ref_idx_active[lx] > 1 && !sym_mvd_flag) + ref_idx = ff_vvc_ref_idx_lx(lc, rsh->num_ref_idx_active[lx]); + else if (sym_mvd_flag) + ref_idx = sh->ref_idx_sym[lx]; + return ref_idx; +} + +static int mvds_decode(VVCLocalContext *lc, Mv mvds[2][MAX_CONTROL_POINTS], + const int num_cp_mv, const int lx) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPH *ph = &fc->ps.ph; + const PredictionUnit *pu = &lc->cu->pu; + const MotionInfo *mi = &pu->mi; + int has_no_zero_mvd = 0; + + if (lx == L1 && ph->r->ph_mvd_l1_zero_flag && mi->pred_flag == PF_BI) { + for (int j = 0; j < num_cp_mv; j++) + AV_ZERO64(&mvds[lx][j]); + } else { + Mv *mvd0 = &mvds[lx][0]; + if (lx == L1 && pu->sym_mvd_flag) { + mvd0->x = -mvds[L0][0].x; + mvd0->y = -mvds[L0][0].y; + } else { + hls_mvd_coding(lc, mvd0); + } + has_no_zero_mvd |= (mvd0->x || mvd0->y); + for (int j = 1; j < num_cp_mv; j++) { + Mv *mvd = &mvds[lx][j]; + hls_mvd_coding(lc, mvd); + mvd->x += mvd0->x; + mvd->y += mvd0->y; + has_no_zero_mvd |= (mvd->x || mvd->y); + } + } + return has_no_zero_mvd; +} + +static void mvp_add_difference(MotionInfo *mi, const int num_cp_mv, + const Mv mvds[2][MAX_CONTROL_POINTS], const int amvr_shift) +{ + for (int i = 0; i < 2; i++) { + const PredFlag mask = i + PF_L0; + if (mi->pred_flag & mask) { + for (int j = 0; j < num_cp_mv; j++) { + const Mv *mvd = &mvds[i][j]; + mi->mv[i][j].x += mvd->x << amvr_shift; + mi->mv[i][j].y += mvd->y << amvr_shift; + } + } + } +} + +static int mvp_data(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + PredictionUnit *pu = &lc->cu->pu; + const VVCSPS *sps = fc->ps.sps; + const VVCPH *ph = &fc->ps.ph; + const VVCSH *sh = &lc->sc->sh; + const H266RawSliceHeader *rsh = sh->r; + MotionInfo *mi = &pu->mi; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + + int mvp_lx_flag[2] = {0}; + int cu_affine_type_flag = 0; + int num_cp_mv; + int amvr_enabled, has_no_zero_mvd = 0, amvr_shift; + Mv mvds[2][MAX_CONTROL_POINTS]; + + mi->pred_flag = ff_vvc_pred_flag(lc, IS_B(rsh)); + if (sps->r->sps_affine_enabled_flag && cb_width >= 16 && cb_height >= 16) { + pu->inter_affine_flag = ff_vvc_inter_affine_flag(lc); + set_cb_tab(lc, fc->tab.iaf, pu->inter_affine_flag); + if (sps->r->sps_6param_affine_enabled_flag && pu->inter_affine_flag) + cu_affine_type_flag = ff_vvc_cu_affine_type_flag(lc); + } + mi->motion_model_idc = pu->inter_affine_flag + cu_affine_type_flag; + num_cp_mv = mi->motion_model_idc + 1; + + if (sps->r->sps_smvd_enabled_flag && !ph->r->ph_mvd_l1_zero_flag && + mi->pred_flag == PF_BI && !pu->inter_affine_flag && + sh->ref_idx_sym[0] > -1 && sh->ref_idx_sym[1] > -1) + pu->sym_mvd_flag = ff_vvc_sym_mvd_flag(lc); + + for (int i = L0; i <= L1; i++) { + const PredFlag pred_flag = PF_L0 + !i; + if (mi->pred_flag != pred_flag) { + mi->ref_idx[i] = ref_idx_decode(lc, sh, pu->sym_mvd_flag, i); + has_no_zero_mvd |= mvds_decode(lc, mvds, num_cp_mv, i); + mvp_lx_flag[i] = ff_vvc_mvp_lx_flag(lc); + } + } + + amvr_enabled = mi->motion_model_idc == MOTION_TRANSLATION ? + sps->r->sps_amvr_enabled_flag : sps->r->sps_affine_amvr_enabled_flag; + amvr_enabled &= has_no_zero_mvd; + + amvr_shift = ff_vvc_amvr_shift(lc, pu->inter_affine_flag, cu->pred_mode, amvr_enabled); + + mi->hpel_if_idx = amvr_shift == 3; + mi->bcw_idx = bcw_idx_decode(lc, mi, cb_width, cb_height); + + if (mi->motion_model_idc) + ff_vvc_affine_mvp(lc, mvp_lx_flag, amvr_shift, mi); + else + ff_vvc_mvp(lc, mvp_lx_flag, amvr_shift, mi); + + mvp_add_difference(mi, num_cp_mv, mvds, amvr_shift); + + if (mi->motion_model_idc) + ff_vvc_store_sb_mvs(lc, pu); + else + ff_vvc_store_mv(lc, &pu->mi); + + return 0; +} + +// derive bdofFlag from 8.5.6 Decoding process for inter blocks +// derive dmvr from 8.5.1 General decoding process for coding units coded in inter prediction mode +static void derive_dmvr_bdof_flag(const VVCLocalContext *lc, PredictionUnit *pu) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPPS *pps = fc->ps.pps; + const VVCPH *ph = &fc->ps.ph; + const VVCSH *sh = &lc->sc->sh; + const int poc = ph->poc; + const RefPicList *rpl0 = lc->sc->rpl + L0; + const RefPicList *rpl1 = lc->sc->rpl + L1; + const int8_t *ref_idx = pu->mi.ref_idx; + const MotionInfo *mi = &pu->mi; + const CodingUnit *cu = lc->cu; + const PredWeightTable *w = pps->r->pps_wp_info_in_ph_flag ? &fc->ps.ph.pwt : &sh->pwt; + + pu->dmvr_flag = 0; + pu->bdof_flag = 0; + + if (mi->pred_flag == PF_BI && + (poc - rpl0->list[ref_idx[L0]] == rpl1->list[ref_idx[L1]] - poc) && + !rpl0->isLongTerm[ref_idx[L0]] && !rpl1->isLongTerm[ref_idx[L1]] && + !cu->ciip_flag && + !mi->bcw_idx && + !w->weight_flag[L0][LUMA][mi->ref_idx[L0]] && !w->weight_flag[L1][LUMA][mi->ref_idx[L1]] && + !w->weight_flag[L0][CHROMA][mi->ref_idx[L0]] && !w->weight_flag[L1][CHROMA][mi->ref_idx[L1]] && + cu->cb_width >= 8 && cu->cb_height >= 8 && + (cu->cb_width * cu->cb_height >= 128)) { + // fixme: for RprConstraintsActiveFlag + if (!ph->r->ph_bdof_disabled_flag && + mi->motion_model_idc == MOTION_TRANSLATION && + !pu->merge_subblock_flag && + !pu->sym_mvd_flag) + pu->bdof_flag = 1; + if (!ph->r->ph_dmvr_disabled_flag && + pu->general_merge_flag && + !pu->mmvd_merge_flag) + pu->dmvr_flag = 1; + } +} + +// part of 8.5.1 General decoding process for coding units coded in inter prediction mode +static void refine_regular_subblock(const VVCLocalContext *lc) +{ + const CodingUnit *cu = lc->cu; + PredictionUnit *pu = &lc->cu->pu; + + derive_dmvr_bdof_flag(lc, pu); + if (pu->dmvr_flag || pu->bdof_flag) { + pu->mi.num_sb_x = (cu->cb_width > 16) ? (cu->cb_width >> 4) : 1; + pu->mi.num_sb_y = (cu->cb_height > 16) ? (cu->cb_height >> 4) : 1; + } +} + +static int vvc_inter_data(VVCLocalContext *lc) +{ + const CodingUnit *cu = lc->cu; + PredictionUnit *pu = &lc->cu->pu; + const MotionInfo *mi = &pu->mi; + int ret = 0; + + pu->general_merge_flag = 1; + if (!cu->skip_flag) + pu->general_merge_flag = ff_vvc_general_merge_flag(lc); + + if (pu->general_merge_flag) { + hls_merge_data(lc); + } else if (cu->pred_mode == MODE_IBC){ + avpriv_report_missing_feature(lc->fc->avctx, "Intra Block Copy"); + return AVERROR_PATCHWELCOME; + } else { + ret = mvp_data(lc); + } + if (!pu->merge_gpm_flag && !pu->inter_affine_flag && !pu->merge_subblock_flag) { + refine_regular_subblock(lc); + ff_vvc_update_hmvp(lc, mi); + } + return ret; +} + +static int hls_coding_unit(VVCLocalContext *lc, int x0, int y0, int cb_width, int cb_height, + int cqt_depth, const VVCTreeType tree_type, VVCModeType mode_type) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int hs = sps->hshift[CHROMA]; + const int vs = sps->vshift[CHROMA]; + const int is_128 = cb_width > 64 || cb_height > 64; + int pred_mode_plt_flag = 0; + int ret; + + CodingUnit *cu = add_cu(lc, x0, y0, cb_width, cb_height, cqt_depth, tree_type); + + if (!cu) + return AVERROR(ENOMEM); + + ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + + if (IS_I(rsh) && is_128) + mode_type = MODE_TYPE_INTRA; + cu->pred_mode = pred_mode_decode(lc, tree_type, mode_type); + + if (cu->pred_mode == MODE_INTRA && sps->r->sps_palette_enabled_flag && !is_128 && !cu->skip_flag && + mode_type != MODE_TYPE_INTER && ((cb_width * cb_height) > + (tree_type != DUAL_TREE_CHROMA ? 16 : (16 << hs << vs))) && + (mode_type != MODE_TYPE_INTRA || tree_type != DUAL_TREE_CHROMA)) { + pred_mode_plt_flag = ff_vvc_pred_mode_plt_flag(lc); + if (pred_mode_plt_flag) { + avpriv_report_missing_feature(lc->fc->avctx, "Palette"); + return AVERROR_PATCHWELCOME; + } + } + if (cu->pred_mode == MODE_INTRA && sps->r->sps_act_enabled_flag && tree_type == SINGLE_TREE) { + avpriv_report_missing_feature(fc->avctx, "Adaptive Color Transform"); + return AVERROR_PATCHWELCOME; + } + if (cu->pred_mode == MODE_INTRA || cu->pred_mode == MODE_PLT) { + if (tree_type == SINGLE_TREE || tree_type == DUAL_TREE_LUMA) { + if (pred_mode_plt_flag) { + avpriv_report_missing_feature(lc->fc->avctx, "Palette"); + return AVERROR_PATCHWELCOME; + } else { + intra_luma_pred_modes(lc); + } + ff_vvc_set_intra_mvf(lc); + } + if ((tree_type == SINGLE_TREE || tree_type == DUAL_TREE_CHROMA) && sps->r->sps_chroma_format_idc) { + if (pred_mode_plt_flag && tree_type == DUAL_TREE_CHROMA) { + avpriv_report_missing_feature(lc->fc->avctx, "Palette"); + return AVERROR_PATCHWELCOME; + } else if (!pred_mode_plt_flag) { + if (!cu->act_enabled_flag) + intra_chroma_pred_modes(lc); + } + } + } else if (tree_type != DUAL_TREE_CHROMA) { /* MODE_INTER or MODE_IBC */ + if ((ret = vvc_inter_data(lc)) < 0) + return ret; + } + if (cu->pred_mode != MODE_INTRA && !pred_mode_plt_flag && !lc->cu->pu.general_merge_flag) + cu->coded_flag = ff_vvc_cu_coded_flag(lc); + else + cu->coded_flag = !(cu->skip_flag || pred_mode_plt_flag); + + if (cu->coded_flag) { + sbt_info(lc, sps); + if (sps->r->sps_act_enabled_flag && cu->pred_mode != MODE_INTRA && tree_type == SINGLE_TREE) { + avpriv_report_missing_feature(fc->avctx, "Adaptive Color Transform"); + return AVERROR_PATCHWELCOME; + } + lc->parse.lfnst_dc_only = 1; + lc->parse.lfnst_zero_out_sig_coeff_flag = 1; + lc->parse.mts_dc_only = 1; + lc->parse.mts_zero_out_sig_coeff_flag = 1; + ret = hls_transform_tree(lc, x0, y0, cb_width, cb_height, cu->ch_type); + if (ret < 0) + return ret; + cu->lfnst_idx = lfnst_idx_decode(lc); + cu->mts_idx = mts_idx_decode(lc); + set_qp_c(lc); + if (ret < 0) + return ret; + } else { + av_assert0(tree_type == SINGLE_TREE); + ret = skipped_transform_tree_unit(lc); + if (ret < 0) + return ret; + } + set_cu_tabs(lc, cu); + + return 0; +} + +static int derive_mode_type_condition(const VVCLocalContext *lc, + const VVCSplitMode split, const int cb_width, const int cb_height, const VVCModeType mode_type_curr) +{ + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const VVCSPS *sps = lc->fc->ps.sps; + const int area = cb_width * cb_height; + + if ((IS_I(rsh) && sps->r->sps_qtbtt_dual_tree_intra_flag) || + mode_type_curr != MODE_TYPE_ALL || !sps->r->sps_chroma_format_idc || + sps->r->sps_chroma_format_idc == CHROMA_FORMAT_444) + return 0; + if ((area == 64 && (split == SPLIT_QT || split == SPLIT_TT_HOR || split == SPLIT_TT_VER)) || + (area == 32 && (split == SPLIT_BT_HOR || split == SPLIT_BT_VER))) + return 1; + if ((area == 64 && (split == SPLIT_BT_HOR || split == SPLIT_BT_VER) && sps->r->sps_chroma_format_idc == CHROMA_FORMAT_420) || + (area == 128 && (split == SPLIT_TT_HOR || split == SPLIT_TT_VER) && sps->r->sps_chroma_format_idc == CHROMA_FORMAT_420) || + (cb_width == 8 && split == SPLIT_BT_VER) || (cb_width == 16 && split == SPLIT_TT_VER)) + return 1 + !IS_I(rsh); + + return 0; +} + +static VVCModeType mode_type_decode(VVCLocalContext *lc, const int x0, const int y0, + const int cb_width, const int cb_height, const VVCSplitMode split, const int ch_type, + const VVCModeType mode_type_curr) +{ + VVCModeType mode_type; + const int mode_type_condition = derive_mode_type_condition(lc, split, cb_width, cb_height, mode_type_curr); + + if (mode_type_condition == 1) + mode_type = MODE_TYPE_INTRA; + else if (mode_type_condition == 2) { + mode_type = ff_vvc_non_inter_flag(lc, x0, y0, ch_type) ? MODE_TYPE_INTRA : MODE_TYPE_INTER; + } else { + mode_type = mode_type_curr; + } + + return mode_type; +} + +static int hls_coding_tree(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, int part_idx, + VVCSplitMode last_split_mode, VVCTreeType tree_type_curr, VVCModeType mode_type_curr); + +static int coding_tree_btv(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, + VVCTreeType tree_type, VVCModeType mode_type) +{ +#define CODING_TREE(x, idx) do { \ + ret = hls_coding_tree(lc, x, y0, cb_width / 2, cb_height, \ + qg_on_y, qg_on_c, cb_sub_div + 1, cqt_depth, mtt_depth + 1, \ + depth_offset, idx, SPLIT_BT_VER, tree_type, mode_type); \ + if (ret < 0) \ + return ret; \ +} while (0); + + const VVCPPS *pps = lc->fc->ps.pps; + const int x1 = x0 + cb_width / 2; + int ret = 0; + + depth_offset += (x0 + cb_width > pps->width) ? 1 : 0; + CODING_TREE(x0, 0); + if (x1 < pps->width) + CODING_TREE(x1, 1); + + return 0; + +#undef CODING_TREE +} + +static int coding_tree_bth(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, + VVCTreeType tree_type, VVCModeType mode_type) +{ +#define CODING_TREE(y, idx) do { \ + ret = hls_coding_tree(lc, x0, y, cb_width , cb_height / 2, \ + qg_on_y, qg_on_c, cb_sub_div + 1, cqt_depth, mtt_depth + 1, \ + depth_offset, idx, SPLIT_BT_HOR, tree_type, mode_type); \ + if (ret < 0) \ + return ret; \ + } while (0); + + const VVCPPS *pps = lc->fc->ps.pps; + const int y1 = y0 + (cb_height / 2); + int ret = 0; + + depth_offset += (y0 + cb_height > pps->height) ? 1 : 0; + CODING_TREE(y0, 0); + if (y1 < pps->height) + CODING_TREE(y1, 1); + + return 0; + +#undef CODING_TREE +} + +static int coding_tree_ttv(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, + VVCTreeType tree_type, VVCModeType mode_type) +{ +#define CODING_TREE(x, w, sub_div, idx) do { \ + ret = hls_coding_tree(lc, x, y0, w, cb_height, \ + qg_on_y, qg_on_c, sub_div, cqt_depth, mtt_depth + 1, \ + depth_offset, idx, SPLIT_TT_VER, tree_type, mode_type); \ + if (ret < 0) \ + return ret; \ + } while (0); + + const VVCSH *sh = &lc->sc->sh; + const int x1 = x0 + cb_width / 4; + const int x2 = x0 + cb_width * 3 / 4; + int ret; + + qg_on_y = qg_on_y && (cb_sub_div + 2 <= sh->cu_qp_delta_subdiv); + qg_on_c = qg_on_c && (cb_sub_div + 2 <= sh->cu_chroma_qp_offset_subdiv); + + CODING_TREE(x0, cb_width / 4, cb_sub_div + 2, 0); + CODING_TREE(x1, cb_width / 2, cb_sub_div + 1, 1); + CODING_TREE(x2, cb_width / 4, cb_sub_div + 2, 2); + + return 0; + +#undef CODING_TREE +} + +static int coding_tree_tth(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, + VVCTreeType tree_type, VVCModeType mode_type) +{ +#define CODING_TREE(y, h, sub_div, idx) do { \ + ret = hls_coding_tree(lc, x0, y, cb_width, h, \ + qg_on_y, qg_on_c, sub_div, cqt_depth, mtt_depth + 1, \ + depth_offset, idx, SPLIT_TT_HOR, tree_type, mode_type); \ + if (ret < 0) \ + return ret; \ + } while (0); + + const VVCSH *sh = &lc->sc->sh; + const int y1 = y0 + (cb_height / 4); + const int y2 = y0 + (3 * cb_height / 4); + int ret; + + qg_on_y = qg_on_y && (cb_sub_div + 2 <= sh->cu_qp_delta_subdiv); + qg_on_c = qg_on_c && (cb_sub_div + 2 <= sh->cu_chroma_qp_offset_subdiv); + + CODING_TREE(y0, cb_height / 4, cb_sub_div + 2, 0); + CODING_TREE(y1, cb_height / 2, cb_sub_div + 1, 1); + CODING_TREE(y2, cb_height / 4, cb_sub_div + 2, 2); + + return 0; + +#undef CODING_TREE +} + +static int coding_tree_qt(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, + VVCTreeType tree_type, VVCModeType mode_type) +{ +#define CODING_TREE(x, y, idx) do { \ + ret = hls_coding_tree(lc, x, y, cb_width / 2, cb_height / 2, \ + qg_on_y, qg_on_c, cb_sub_div + 2, cqt_depth + 1, 0, 0, \ + idx, SPLIT_QT, tree_type, mode_type); \ + if (ret < 0) \ + return ret; \ + } while (0); + + const VVCPPS *pps = lc->fc->ps.pps; + const int x1 = x0 + cb_width / 2; + const int y1 = y0 + cb_height / 2; + int ret = 0; + + CODING_TREE(x0, y0, 0); + if (x1 < pps->width) + CODING_TREE(x1, y0, 1); + if (y1 < pps->height) + CODING_TREE(x0, y1, 2); + if (x1 < pps->width && + y1 < pps->height) + CODING_TREE(x1, y1, 3); + + return 0; + +#undef CODING_TREE +} + +typedef int (*coding_tree_fn)(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, + VVCTreeType tree_type, VVCModeType mode_type); + +const static coding_tree_fn coding_tree[] = { + coding_tree_tth, + coding_tree_bth, + coding_tree_ttv, + coding_tree_btv, + coding_tree_qt, +}; + +static int hls_coding_tree(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, int part_idx, + VVCSplitMode last_split_mode, VVCTreeType tree_type_curr, VVCModeType mode_type_curr) +{ + VVCFrameContext *fc = lc->fc; + const VVCPPS *pps = fc->ps.pps; + const VVCSH *sh = &lc->sc->sh; + const H266RawSliceHeader *rsh = sh->r; + const int ch_type = tree_type_curr == DUAL_TREE_CHROMA; + int ret; + VVCAllowedSplit allowed; + + if (pps->r->pps_cu_qp_delta_enabled_flag && qg_on_y && cb_sub_div <= sh->cu_qp_delta_subdiv) { + lc->parse.is_cu_qp_delta_coded = 0; + lc->parse.cu_qg_top_left_x = x0; + lc->parse.cu_qg_top_left_y = y0; + } + if (rsh->sh_cu_chroma_qp_offset_enabled_flag && qg_on_c && + cb_sub_div <= sh->cu_chroma_qp_offset_subdiv) { + lc->parse.is_cu_chroma_qp_offset_coded = 0; + memset(lc->parse.chroma_qp_offset, 0, sizeof(lc->parse.chroma_qp_offset)); + } + + can_split(lc, x0, y0, cb_width, cb_height, mtt_depth, depth_offset, part_idx, + last_split_mode, tree_type_curr, mode_type_curr, &allowed); + if (ff_vvc_split_cu_flag(lc, x0, y0, cb_width, cb_height, ch_type, &allowed)) { + VVCSplitMode split = ff_vvc_split_mode(lc, x0, y0, cb_width, cb_height, cqt_depth, mtt_depth, ch_type, &allowed); + VVCModeType mode_type = mode_type_decode(lc, x0, y0, cb_width, cb_height, split, ch_type, mode_type_curr); + + VVCTreeType tree_type = (mode_type == MODE_TYPE_INTRA) ? DUAL_TREE_LUMA : tree_type_curr; + + if (split != SPLIT_QT) { + if (!(x0 & 31) && !(y0 & 31) && mtt_depth <= 1) + TAB_MSM(fc, mtt_depth, x0, y0) = split; + } + ret = coding_tree[split - 1](lc, x0, y0, cb_width, cb_height, qg_on_y, qg_on_c, + cb_sub_div, cqt_depth, mtt_depth, depth_offset, tree_type, mode_type); + if (ret < 0) + return ret; + if (mode_type_curr == MODE_TYPE_ALL && mode_type == MODE_TYPE_INTRA) { + ret = hls_coding_tree(lc, x0, y0, cb_width, cb_height, 0, qg_on_c, cb_sub_div, + cqt_depth, mtt_depth, 0, 0, split, DUAL_TREE_CHROMA, mode_type); + if (ret < 0) + return ret; + } + } else { + ret = hls_coding_unit(lc, x0, y0, cb_width, cb_height, cqt_depth, tree_type_curr, mode_type_curr); + if (ret < 0) + return ret; + } + + return 0; +} + +static int dual_tree_implicit_qt_split(VVCLocalContext *lc, + const int x0, const int y0, const int cb_size, const int cqt_depth) +{ + const VVCSH *sh = &lc->sc->sh; + const H266RawSliceHeader *rsh = sh->r; + const VVCPPS *pps = lc->fc->ps.pps; + const int cb_subdiv = 2 * cqt_depth; + int ret; + + if (cb_size > 64) { + #define DUAL_TREE(x, y) do { \ + ret = dual_tree_implicit_qt_split(lc, x, y, cb_size / 2, cqt_depth + 1); \ + if (ret < 0) \ + return ret; \ + } while (0) + + const int x1 = x0 + (cb_size / 2); + const int y1 = y0 + (cb_size / 2); + if (pps->r->pps_cu_qp_delta_enabled_flag && cb_subdiv <= sh->cu_qp_delta_subdiv) { + lc->parse.is_cu_qp_delta_coded = 0; + lc->parse.cu_qg_top_left_x = x0; + lc->parse.cu_qg_top_left_y = y0; + } + if (rsh->sh_cu_chroma_qp_offset_enabled_flag && cb_subdiv <= sh->cu_chroma_qp_offset_subdiv) { + lc->parse.is_cu_chroma_qp_offset_coded = 0; + memset(lc->parse.chroma_qp_offset, 0, sizeof(lc->parse.chroma_qp_offset)); + } + DUAL_TREE(x0, y0); + if (x1 < pps->width) + DUAL_TREE(x1, y0); + if (y1 < pps->height) + DUAL_TREE(x0, y1); + if (x1 < pps->width && y1 < pps->height) + DUAL_TREE(x1, y1); + #undef DUAL_TREE + } else { + #define CODING_TREE(tree_type) do { \ + const int qg_on_y = tree_type == DUAL_TREE_LUMA; \ + ret = hls_coding_tree(lc, x0, y0, cb_size, cb_size, qg_on_y, !qg_on_y, \ + cb_subdiv, cqt_depth, 0, 0, 0, SPLIT_NONE, tree_type, MODE_TYPE_ALL); \ + if (ret < 0) \ + return ret; \ + } while (0) + CODING_TREE(DUAL_TREE_LUMA); + CODING_TREE(DUAL_TREE_CHROMA); + #undef CODING_TREE + } + return 0; +} + +#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(fc->tab.sao, rx-1, ry).elem; \ + else if (sao_merge_up_flag) \ + sao->elem = CTB(fc->tab.sao, rx, ry-1).elem; \ + else \ + sao->elem = 0; \ +} while (0) + +static void hls_sao(VVCLocalContext *lc, const int rx, const int ry) +{ + VVCFrameContext *fc = lc->fc; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + int sao_merge_left_flag = 0; + int sao_merge_up_flag = 0; + SAOParams *sao = &CTB(fc->tab.sao, rx, ry); + int c_idx, i; + + if (rsh->sh_sao_luma_used_flag || rsh->sh_sao_chroma_used_flag) { + if (rx > 0) { + if (lc->ctb_left_flag) + sao_merge_left_flag = ff_vvc_sao_merge_flag_decode(lc); + } + if (ry > 0 && !sao_merge_left_flag) { + if (lc->ctb_up_flag) + sao_merge_up_flag = ff_vvc_sao_merge_flag_decode(lc); + } + } + + for (c_idx = 0; c_idx < (fc->ps.sps->r->sps_chroma_format_idc ? 3 : 1); c_idx++) { + const int sao_used_flag = !c_idx ? rsh->sh_sao_luma_used_flag : rsh->sh_sao_chroma_used_flag; + if (!sao_used_flag) { + 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_vvc_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_vvc_sao_offset_abs_decode(lc)); + + 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_vvc_sao_offset_sign_decode(lc)); + } else { + sao->offset_sign[c_idx][i] = 0; + } + } + SET_SAO(band_position[c_idx], ff_vvc_sao_band_position_decode(lc)); + } else if (c_idx != 2) { + SET_SAO(eo_class[c_idx], ff_vvc_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 << (fc->ps.sps->bit_depth - FFMIN(10, fc->ps.sps->bit_depth)); + } + } +} + +static void alf_params(VVCLocalContext *lc, const int rx, const int ry) +{ + const VVCFrameContext *fc = lc->fc; + const H266RawSliceHeader *sh = lc->sc->sh.r; + ALFParams *alf = &CTB(fc->tab.alf, rx, ry); + + alf->ctb_flag[LUMA] = alf->ctb_flag[CB] = alf->ctb_flag[CR] = 0; + if (sh->sh_alf_enabled_flag) { + alf->ctb_flag[LUMA] = ff_vvc_alf_ctb_flag(lc, rx, ry, LUMA); + if (alf->ctb_flag[LUMA]) { + uint8_t alf_use_aps_flag = 0; + if (sh->sh_num_alf_aps_ids_luma > 0) + alf_use_aps_flag = ff_vvc_alf_use_aps_flag(lc); + if (alf_use_aps_flag) { + alf->ctb_filt_set_idx_y = 16; + if (sh->sh_num_alf_aps_ids_luma > 1) + alf->ctb_filt_set_idx_y += ff_vvc_alf_luma_prev_filter_idx(lc); + } else { + alf->ctb_filt_set_idx_y = ff_vvc_alf_luma_fixed_filter_idx(lc); + } + } + for (int c_idx = CB; c_idx <= CR; c_idx++) { + const uint8_t alf_enabled_flag = + c_idx == CB ? sh->sh_alf_cb_enabled_flag : sh->sh_alf_cr_enabled_flag; + if (alf_enabled_flag) { + const VVCALF *aps = (VVCALF*)fc->ps.alf_list[sh->sh_alf_aps_id_chroma]->data; + alf->ctb_flag[c_idx] = ff_vvc_alf_ctb_flag(lc, rx, ry, c_idx); + alf->alf_ctb_filter_alt_idx[c_idx - 1] = 0; + if (alf->ctb_flag[c_idx] && aps->num_chroma_filters > 1) + alf->alf_ctb_filter_alt_idx[c_idx - 1] = ff_vvc_alf_ctb_filter_alt_idx(lc, c_idx, aps->num_chroma_filters); + } + } + } + if (fc->ps.sps->r->sps_ccalf_enabled_flag) { + const uint8_t cc_enabled[] = { sh->sh_alf_cc_cb_enabled_flag, sh->sh_alf_cc_cr_enabled_flag }; + const uint8_t cc_aps_id[] = { sh->sh_alf_cc_cb_aps_id, sh->sh_alf_cc_cr_aps_id }; + for (int i = 0; i < 2; i++) { + alf->ctb_cc_idc[i] = 0; + if (cc_enabled[i]) { + const VVCALF *aps = (VVCALF*)fc->ps.alf_list[cc_aps_id[i]]->data; + alf->ctb_cc_idc[i] = ff_vvc_alf_ctb_cc_idc(lc, rx, ry, i, aps->num_cc_filters[i]); + } + } + } +} + +static void deblock_params(VVCLocalContext *lc, const int rx, const int ry) +{ + VVCFrameContext *fc = lc->fc; + const VVCSH *sh = &lc->sc->sh; + CTB(fc->tab.deblock, rx, ry) = sh->deblock; +} + +static int hls_coding_tree_unit(VVCLocalContext *lc, + const int x0, const int y0, const int ctu_idx, const int rx, const int ry) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const VVCSH *sh = &lc->sc->sh; + const H266RawSliceHeader *rsh = sh->r; + const unsigned int ctb_size = sps->ctb_size_y; + int ret = 0; + + memset(lc->parse.chroma_qp_offset, 0, sizeof(lc->parse.chroma_qp_offset)); + + hls_sao(lc, x0 >> sps->ctb_log2_size_y, y0 >> sps->ctb_log2_size_y); + alf_params(lc, x0 >> sps->ctb_log2_size_y, y0 >> sps->ctb_log2_size_y); + deblock_params(lc, x0 >> sps->ctb_log2_size_y, y0 >> sps->ctb_log2_size_y); + + if (IS_I(rsh) && sps->r->sps_qtbtt_dual_tree_intra_flag) + ret = dual_tree_implicit_qt_split(lc, x0, y0, ctb_size, 0); + else + ret = hls_coding_tree(lc, x0, y0, ctb_size, ctb_size, + 1, 1, 0, 0, 0, 0, 0, SPLIT_NONE, SINGLE_TREE, MODE_TYPE_ALL); + if (ret < 0) + return ret; + + if (rx == pps->ctb_to_col_bd[rx + 1] - 1) { + if (ctu_idx == sh->num_ctus_in_curr_slice - 1) { + const int end_of_slice_one_bit = ff_vvc_end_of_slice_flag_decode(lc); + if (!end_of_slice_one_bit) + return AVERROR_INVALIDDATA; + } else { + if (ry == pps->ctb_to_row_bd[ry + 1] - 1) { + const int end_of_tile_one_bit = ff_vvc_end_of_tile_one_bit(lc); + if (!end_of_tile_one_bit) + return AVERROR_INVALIDDATA; + } else { + if (fc->ps.sps->r->sps_entropy_coding_sync_enabled_flag) { + const int end_of_subset_one_bit = ff_vvc_end_of_subset_one_bit(lc); + if (!end_of_subset_one_bit) + return AVERROR_INVALIDDATA; + } + } + } + } + + return 0; +} + +static int has_inter_luma(const CodingUnit *cu) +{ + return cu->pred_mode != MODE_INTRA && cu->pred_mode != MODE_PLT && cu->tree_type != DUAL_TREE_CHROMA; +} + +static int pred_get_y(const int y0, const Mv *mv, const int height) +{ + return FFMAX(0, y0 + (mv->y >> 4) + height); +} + +static void cu_get_max_y(const CodingUnit *cu, int max_y[2][VVC_MAX_REF_ENTRIES], const VVCFrameContext *fc) +{ + const PredictionUnit *pu = &cu->pu; + + if (pu->merge_gpm_flag) { + for (int i = 0; i < FF_ARRAY_ELEMS(pu->gpm_mv); i++) { + const MvField *mvf = pu->gpm_mv + i; + const int lx = mvf->pred_flag - PF_L0; + const int idx = mvf->ref_idx[lx]; + const int y = pred_get_y(cu->y0, mvf->mv + lx, cu->cb_height); + + max_y[lx][idx] = FFMAX(max_y[lx][idx], y); + } + } else { + const MotionInfo *mi = &pu->mi; + const int max_dmvr_off = (!pu->inter_affine_flag && pu->dmvr_flag) ? 2 : 0; + const int sbw = cu->cb_width / mi->num_sb_x; + const int sbh = cu->cb_height / mi->num_sb_y; + for (int sby = 0; sby < mi->num_sb_y; sby++) { + for (int sbx = 0; sbx < mi->num_sb_x; sbx++) { + const int x0 = cu->x0 + sbx * sbw; + const int y0 = cu->y0 + sby * sbh; + const MvField *mvf = ff_vvc_get_mvf(fc, x0, y0); + for (int lx = 0; lx < 2; lx++) { + const PredFlag mask = 1 << lx; + if (mvf->pred_flag & mask) { + const int idx = mvf->ref_idx[lx]; + const int y = pred_get_y(y0, mvf->mv + lx, sbh); + + max_y[lx][idx] = FFMAX(max_y[lx][idx], y + max_dmvr_off); + } + } + } + } + } +} + +static void pred_get_max_y(VVCLocalContext *lc, const int rs) +{ + const VVCFrameContext *fc = lc->fc; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + CTU *ctu = fc->tab.ctus + rs; + const CodingUnit *cu = ctu->cus; + + if (IS_I(rsh)) + return; + + for (int lx = 0; lx < 2; lx++) + memset(ctu->max_y[lx], -1, sizeof(ctu->max_y[0][0]) * rsh->num_ref_idx_active[lx]); + + while (cu) { + if (has_inter_luma(cu)) + cu_get_max_y(cu, ctu->max_y, fc); + cu = cu->next; + } + ctu->max_y_idx[0] = ctu->max_y_idx[1] = 0; +} + +int ff_vvc_coding_tree_unit(VVCLocalContext *lc, + const int ctu_idx, const int rs, const int rx, const int ry) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int x_ctb = rx << sps->ctb_log2_size_y; + const int y_ctb = ry << sps->ctb_log2_size_y; + const int ctb_size = 1 << sps->ctb_log2_size_y << sps->ctb_log2_size_y; + EntryPoint* ep = lc->ep; + int ret; + + if (rx == pps->ctb_to_col_bd[rx]) { + //fix me for ibc + ep->num_hmvp = 0; + ep->is_first_qg = ry == pps->ctb_to_row_bd[ry] || !ctu_idx; + } + + lc->coeffs = fc->tab.coeffs + rs * ctb_size * VVC_MAX_SAMPLE_ARRAYS; + lc->cu = NULL; + + ff_vvc_cabac_init(lc, ctu_idx, rx, ry); + fc->tab.slice_idx[rs] = lc->sc->slice_idx; + ff_vvc_decode_neighbour(lc, x_ctb, y_ctb, rx, ry, rs); + ret = hls_coding_tree_unit(lc, x_ctb, y_ctb, ctu_idx, rx, ry); + if (ret < 0) + return ret; + pred_get_max_y(lc, rs); + + return 0; +} + +void ff_vvc_decode_neighbour(VVCLocalContext *lc, const int x_ctb, const int y_ctb, + const int rx, const int ry, const int rs) +{ + VVCFrameContext *fc = lc->fc; + const int ctb_size = fc->ps.sps->ctb_size_y; + + lc->end_of_tiles_x = fc->ps.sps->width; + lc->end_of_tiles_y = fc->ps.sps->height; + if (fc->ps.pps->ctb_to_col_bd[rx] != fc->ps.pps->ctb_to_col_bd[rx + 1]) + lc->end_of_tiles_x = FFMIN(x_ctb + ctb_size, lc->end_of_tiles_x); + if (fc->ps.pps->ctb_to_row_bd[ry] != fc->ps.pps->ctb_to_row_bd[ry + 1]) + lc->end_of_tiles_y = FFMIN(y_ctb + ctb_size, lc->end_of_tiles_y); + + lc->boundary_flags = 0; + if (rx > 0 && fc->ps.pps->ctb_to_col_bd[rx] != fc->ps.pps->ctb_to_col_bd[rx - 1]) + lc->boundary_flags |= BOUNDARY_LEFT_TILE; + if (rx > 0 && fc->tab.slice_idx[rs] != fc->tab.slice_idx[rs - 1]) + lc->boundary_flags |= BOUNDARY_LEFT_SLICE; + if (ry > 0 && fc->ps.pps->ctb_to_row_bd[ry] != fc->ps.pps->ctb_to_row_bd[ry - 1]) + lc->boundary_flags |= BOUNDARY_UPPER_TILE; + if (ry > 0 && fc->tab.slice_idx[rs] != fc->tab.slice_idx[rs - fc->ps.pps->ctb_width]) + lc->boundary_flags |= BOUNDARY_UPPER_SLICE; + lc->ctb_left_flag = rx > 0 && !(lc->boundary_flags & BOUNDARY_LEFT_TILE); + lc->ctb_up_flag = ry > 0 && !(lc->boundary_flags & BOUNDARY_UPPER_TILE) && !(lc->boundary_flags & BOUNDARY_UPPER_SLICE); + lc->ctb_up_right_flag = lc->ctb_up_flag && (fc->ps.pps->ctb_to_col_bd[rx] == fc->ps.pps->ctb_to_col_bd[rx + 1]) && + (fc->ps.pps->ctb_to_row_bd[ry] == fc->ps.pps->ctb_to_row_bd[ry - 1]); + lc->ctb_up_left_flag = lc->ctb_left_flag && lc->ctb_up_flag; +} + +void ff_vvc_set_neighbour_available(VVCLocalContext *lc, + const int x0, const int y0, const int w, const int h) +{ + const int log2_ctb_size = lc->fc->ps.sps->ctb_log2_size_y; + const int x0b = av_mod_uintp2(x0, log2_ctb_size); + const int y0b = av_mod_uintp2(y0, log2_ctb_size); + + lc->na.cand_up = (lc->ctb_up_flag || y0b); + lc->na.cand_left = (lc->ctb_left_flag || x0b); + lc->na.cand_up_left = (x0b || y0b) ? lc->na.cand_left && lc->na.cand_up : lc->ctb_up_left_flag; + lc->na.cand_up_right_sap = + (x0b + w == 1 << log2_ctb_size) ? lc->ctb_up_right_flag && !y0b : lc->na.cand_up; + lc->na.cand_up_right = lc->na.cand_up_right_sap && (x0 + w) < lc->end_of_tiles_x; +} + +void ff_vvc_ctu_free_cus(CTU *ctu) +{ + CodingUnit *cu = ctu->cus; + while (cu) { + AVBufferRef *cu_buf = cu->buf; + TransformUnit *tu = cu->tus.head; + + while (tu) { + AVBufferRef *buf = tu->buf; + tu = tu->next; + av_buffer_unref(&buf); + } + cu->tus.head = cu->tus.tail = NULL; + + cu = cu->next; + av_buffer_unref(&cu_buf); + } + ctu->cus = NULL; +} + +int ff_vvc_get_qPy(const VVCFrameContext *fc, const int xc, const int yc) +{ + const int min_cb_log2_size_y = fc->ps.sps->min_cb_log2_size_y; + const int x = xc >> min_cb_log2_size_y; + const int y = yc >> min_cb_log2_size_y; + return fc->tab.qp[LUMA][x + y * fc->ps.pps->min_cb_width]; +} + +void ff_vvc_ep_init_stat_coeff(EntryPoint *ep, + const int bit_depth, const int persistent_rice_adaptation_enabled_flag) +{ + for (size_t i = 0; i < FF_ARRAY_ELEMS(ep->stat_coeff); ++i) { + ep->stat_coeff[i] = + persistent_rice_adaptation_enabled_flag ? 2 * (av_log2(bit_depth - 10)) : 0; + } +} \ No newline at end of file diff --git a/libavcodec/vvc/vvc_ctu.h b/libavcodec/vvc/vvc_ctu.h new file mode 100644 index 00000000000..50f081a4c47 --- /dev/null +++ b/libavcodec/vvc/vvc_ctu.h @@ -0,0 +1,422 @@ +/* + * VVC CTU(Coding Tree Unit) parser + * + * Copyright (C) 2022 Nuo Mi + * + * 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_VVC_CTU_H +#define AVCODEC_VVC_CTU_H + +#include "libavcodec/cabac.h" +#include "libavutil/mem_internal.h" + +#include "vvcdec.h" + +enum SAOType { + SAO_NOT_APPLIED = 0, + SAO_BAND, + SAO_EDGE, + SAO_APPLIED +}; + +enum SAOEOClass { + SAO_EO_HORIZ = 0, + SAO_EO_VERT, + SAO_EO_135D, + SAO_EO_45D, +}; + +typedef struct NeighbourAvailable { + int cand_left; + int cand_up; + int cand_up_left; + int cand_up_right; + int cand_up_right_sap; +} NeighbourAvailable; + +enum IspType{ + ISP_NO_SPLIT, + ISP_HOR_SPLIT, + ISP_VER_SPLIT, +}; + +typedef enum VVCSplitMode { + SPLIT_NONE, + SPLIT_TT_HOR, + SPLIT_BT_HOR, + SPLIT_TT_VER, + SPLIT_BT_VER, + SPLIT_QT, +} VVCSplitMode; + +typedef enum MtsIdx { + MTS_DCT2_DCT2, + MTS_DST7_DST7, + MTS_DST7_DCT8, + MTS_DCT8_DST7, + MTS_DCT8_DCT8, +} MtsIdx; + +typedef struct TransformBlock { + uint8_t has_coeffs; + uint8_t c_idx; + uint8_t ts; ///< transform_skip_flag + int x0; + int y0; + + int tb_width; + int tb_height; + int log2_tb_width; + int log2_tb_height; + + int max_scan_x; + int max_scan_y; + int min_scan_x; + int min_scan_y; + + int qp; + int rect_non_ts_flag; + int bd_shift; + int bd_offset; + + int *coeffs; +} TransformBlock; + +typedef enum VVCTreeType { + SINGLE_TREE, + DUAL_TREE_LUMA, + DUAL_TREE_CHROMA, +} VVCTreeType; + +typedef struct TransformUnit { + AVBufferRef *buf; + + int x0; + int y0; + int width; + int height; + + uint8_t joint_cbcr_residual_flag; ///< tu_joint_cbcr_residual_flag + + uint8_t coded_flag[VVC_MAX_SAMPLE_ARRAYS]; ///< tu_y_coded_flag, tu_cb_coded_flag, tu_cr_coded_flag + uint8_t nb_tbs; + TransformBlock tbs[VVC_MAX_SAMPLE_ARRAYS]; + + struct TransformUnit *next; +} TransformUnit; + +typedef enum PredMode { + MODE_INTER, + MODE_INTRA, + MODE_SKIP, + MODE_PLT, + MODE_IBC, +} PredMode; + +typedef struct Mv { + int x; ///< horizontal component of motion vector + int y; ///< vertical component of motion vector +} Mv; + +typedef struct MvField { + DECLARE_ALIGNED(4, Mv, mv)[2]; ///< mvL0, vvL1 + int8_t ref_idx[2]; ///< refIdxL0, refIdxL1 + uint8_t hpel_if_idx; ///< hpelIfIdx + uint8_t bcw_idx; ///< bcwIdx + uint8_t pred_flag; + uint8_t ciip_flag; ///< ciip_flag +} MvField; + +typedef struct DMVRInfo { + DECLARE_ALIGNED(4, Mv, mv)[2]; ///< mvL0, vvL1 + uint8_t dmvr_enabled; +} DMVRInfo; + +typedef enum MotionModelIdc { + MOTION_TRANSLATION, + MOTION_4_PARAMS_AFFINE, + MOTION_6_PARAMS_AFFINE, +} MotionModelIdc; + +typedef enum PredFlag { + PF_INTRA = 0x0, + PF_L0 = 0x1, + PF_L1 = 0x2, + PF_BI = 0x3, +} PredFlag; + +typedef enum IntraPredMode { + INTRA_INVALID = -1, + INTRA_PLANAR = 0, + INTRA_DC, + INTRA_HORZ = 18, + INTRA_DIAG = 34, + INTRA_VERT = 50, + INTRA_VDIAG = 66, + INTRA_LT_CCLM = 81, + INTRA_L_CCLM, + INTRA_T_CCLM +} IntraPredMode; + +typedef struct MotionInfo { + MotionModelIdc motion_model_idc; ///< MotionModelIdc + int8_t ref_idx[2]; ///< refIdxL0, refIdxL1 + uint8_t hpel_if_idx; ///< hpelIfIdx + uint8_t bcw_idx; ///< bcwIdx + PredFlag pred_flag; + + Mv mv[2][MAX_CONTROL_POINTS]; + + int num_sb_x, num_sb_y; +} MotionInfo; + +typedef struct PredictionUnit { + uint8_t general_merge_flag; + uint8_t mmvd_merge_flag; + //InterPredIdc inter_pred_idc; + uint8_t inter_affine_flag; + + //subblock predict + uint8_t merge_subblock_flag; + + uint8_t merge_gpm_flag; + uint8_t gpm_partition_idx; + MvField gpm_mv[2]; + + int sym_mvd_flag; + + MotionInfo mi; + + // for regular prediction only + uint8_t dmvr_flag; + uint8_t bdof_flag; + + int16_t diff_mv_x[2][AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; ///< diffMvLX + int16_t diff_mv_y[2][AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; ///< diffMvLX + int cb_prof_flag[2]; +} PredictionUnit; + +typedef struct CodingUnit { + AVBufferRef *buf; + + VVCTreeType tree_type; + int x0; + int y0; + int cb_width; + int cb_height; + int ch_type; + int cqt_depth; + + uint8_t coded_flag; + + uint8_t sbt_flag; + uint8_t sbt_horizontal_flag; + uint8_t sbt_pos_flag; + + int lfnst_idx; + MtsIdx mts_idx; + + uint8_t act_enabled_flag; + + uint8_t intra_luma_ref_idx; ///< IntraLumaRefLineIdx[][] + uint8_t intra_mip_flag; ///< intra_mip_flag + uint8_t skip_flag; ///< cu_skip_flag; + + //inter + uint8_t ciip_flag; + + // Inferred parameters + enum IspType isp_split_type; ///< IntraSubPartitionsSplitType + + enum PredMode pred_mode; ///< PredMode + + int num_intra_subpartitions; + + IntraPredMode intra_pred_mode_y; ///< IntraPredModeY + IntraPredMode intra_pred_mode_c; ///< IntraPredModeC + int mip_chroma_direct_flag; ///< MipChromaDirectFlag + + int bdpcm_flag[VVC_MAX_SAMPLE_ARRAYS]; ///< BdpcmFlag + + int apply_lfnst_flag[VVC_MAX_SAMPLE_ARRAYS]; ///< ApplyLfnstFlag[] + + struct { + TransformUnit *head; + TransformUnit *tail; + } tus; + + int8_t qp[4]; ///< QpY, Qp′Cb, Qp′Cr, Qp′CbCr + + PredictionUnit pu; + + struct CodingUnit *next; +} CodingUnit; + +struct CTU { + CodingUnit *cus; + int max_y[2][VVC_MAX_REF_ENTRIES]; + int max_y_idx[2]; +}; + +typedef struct ReconstructedArea { + int x; + int y; + int w; + int h; +} ReconstructedArea; + +typedef struct VVCCabacState { + uint16_t state[2]; + uint8_t shift[2]; +} VVCCabacState; + +// VVC_CONTEXTS matched with SYNTAX_ELEMENT_LAST, it's checked by cabac_init_state. +#define VVC_CONTEXTS 378 +typedef struct EntryPoint { + int8_t qp_y; ///< QpY + + int stat_coeff[VVC_MAX_SAMPLE_ARRAYS]; ///< StatCoeff + + VVCCabacState cabac_state[VVC_CONTEXTS]; + CABACContext cc; + + VVCTask *parse_task; + int ctu_end; + + uint8_t is_first_qg; // first quantization group + MvField hmvp[MAX_NUM_HMVP_CANDS]; ///< HmvpCandList + int num_hmvp; ///< NumHmvpCand +} EntryPoint; + +struct VVCLocalContext { + uint8_t ctb_left_flag; + uint8_t ctb_up_flag; + uint8_t ctb_up_right_flag; + uint8_t ctb_up_left_flag; + int end_of_tiles_x; + int end_of_tiles_y; + + /* +7 is for subpixel interpolation, *2 for high bit depths */ + DECLARE_ALIGNED(32, uint8_t, edge_emu_buffer)[(MAX_PB_SIZE + 7) * EDGE_EMU_BUFFER_STRIDE * 2]; + /* The extended size between the new edge emu buffer is abused by SAO */ + DECLARE_ALIGNED(32, uint8_t, edge_emu_buffer2)[(MAX_PB_SIZE + 7) * EDGE_EMU_BUFFER_STRIDE * 2]; + DECLARE_ALIGNED(32, int16_t, tmp)[MAX_PB_SIZE * MAX_PB_SIZE]; + DECLARE_ALIGNED(32, int16_t, tmp1)[MAX_PB_SIZE * MAX_PB_SIZE]; + DECLARE_ALIGNED(32, uint8_t, ciip_tmp1)[MAX_PB_SIZE * MAX_PB_SIZE * 2]; + DECLARE_ALIGNED(32, uint8_t, ciip_tmp2)[MAX_PB_SIZE * MAX_PB_SIZE * 2]; + DECLARE_ALIGNED(32, uint8_t, sao_buffer)[(MAX_CTU_SIZE + 2 * SAO_PADDING_SIZE) * EDGE_EMU_BUFFER_STRIDE * 2]; + DECLARE_ALIGNED(32, uint8_t, alf_buffer_luma)[(MAX_CTU_SIZE + 2 * ALF_PADDING_SIZE) * EDGE_EMU_BUFFER_STRIDE * 2]; + DECLARE_ALIGNED(32, uint8_t, alf_buffer_chroma)[(MAX_CTU_SIZE + 2 * ALF_PADDING_SIZE) * EDGE_EMU_BUFFER_STRIDE * 2]; + DECLARE_ALIGNED(32, int32_t, alf_gradient_tmp)[ALF_GRADIENT_SIZE * ALF_GRADIENT_SIZE * ALF_NUM_DIR]; + + struct { + int sbt_num_fourths_tb0; ///< SbtNumFourthsTb0 + + uint8_t is_cu_qp_delta_coded; ///< IsCuQpDeltaCoded + int cu_qg_top_left_x; ///< CuQgTopLeftX + int cu_qg_top_left_y; ///< CuQgTopLeftY + int is_cu_chroma_qp_offset_coded; ///< IsCuChromaQpOffsetCoded + int chroma_qp_offset[3]; ///< CuQpOffsetCb, CuQpOffsetCr, CuQpOffsetCbCr + + int infer_tu_cbf_luma; ///< InferTuCbfLuma + int prev_tu_cbf_y; ///< prevTuCbfY; + + int lfnst_dc_only; ///< LfnstDcOnly + int lfnst_zero_out_sig_coeff_flag; ///< LfnstZeroOutSigCoeffFlag + + int mts_dc_only; ///< MtsDcOnly + int mts_zero_out_sig_coeff_flag; ///< MtsZeroOutSigCoeffFlag; + } parse; + + struct { + // lmcs cache, for recon only + int chroma_scale; + int x_vpdu; + int y_vpdu; + } lmcs; + + CodingUnit *cu; + ReconstructedArea ras[2][MAX_PARTS_IN_CTU]; + int num_ras[2]; + + NeighbourAvailable na; + +#define BOUNDARY_LEFT_SLICE (1 << 0) +#define BOUNDARY_LEFT_TILE (1 << 1) +#define BOUNDARY_UPPER_SLICE (1 << 2) +#define BOUNDARY_UPPER_TILE (1 << 3) + /* properties of the boundary of the current CTB for the purposes + * of the deblocking filter */ + int boundary_flags; + + SliceContext *sc; + VVCFrameContext *fc; + EntryPoint *ep; + int *coeffs; +} ; + +typedef struct VVCAllowedSplit { + int qt; + int btv; + int bth; + int ttv; + int tth; +} VVCAllowedSplit; + +struct SAOParams { + int offset_abs[3][4]; ///< sao_offset_abs + int offset_sign[3][4]; ///< sao_offset_sign + + uint8_t band_position[3]; ///< sao_band_position + + int eo_class[3]; ///< sao_eo_class + + int16_t offset_val[3][5]; /// + +#include "libavutil/avassert.h" + +#include "vvc_data.h" + +const uint8_t ff_vvc_diag_scan_x[5][5][16*16] = { + { + //1x1 + { 0, }, + //1x2 + { 0, 0, }, + //1x4 + { 0, 0, 0, 0, }, + //1x8 + { 0, 0, 0, 0, 0, 0, 0, 0, }, + //1x16 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + }, + { + //2x1 + { 0, 1, }, + //2x2 + { 0, 0, 1, 1, }, + //2x4 + { 0, 0, 1, 0, 1, 0, 1, 1, }, + //2x8 + { 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, }, + //2x16 + { + 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, + }, + }, + { + //4x1 + { 0, 1, 2, 3, }, + //4x2 + { 0, 0, 1, 1, 2, 2, 3, 3, }, + //4x4 + { 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 2, 3, 3, }, + //4x8 + { + 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, + 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 2, 3, 3, + }, + //4x16 + { + 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, + 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, + 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, + 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 2, 3, 3, + }, + }, + { + //8x1 + { 0, 1, 2, 3, 4, 5, 6, 7, }, + //8x2 + { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, }, + //8x4 + { + 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, + 4, 5, 3, 4, 5, 6, 4, 5, 6, 7, 5, 6, 7, 6, 7, 7, + }, + //8x8 + { + 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0, + 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, + 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, + 7, 3, 4, 5, 6, 7, 4, 5, 6, 7, 5, 6, 7, 6, 7, 7, + }, + //8x16 + { + 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0, + 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, + 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, + 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, + 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, + 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, + 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, + 7, 3, 4, 5, 6, 7, 4, 5, 6, 7, 5, 6, 7, 6, 7, 7, + }, + }, + { + //16x1 + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, }, + //16x2 + { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, + }, + //16x4 + { + 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, + 4, 5, 3, 4, 5, 6, 4, 5, 6, 7, 5, 6, 7, 8, 6, 7, + 8, 9, 7, 8, 9, 10, 8, 9, 10, 11, 9, 10, 11, 12, 10, 11, + 12, 13, 11, 12, 13, 14, 12, 13, 14, 15, 13, 14, 15, 14, 15, 15, + }, + //16x8 + { + 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0, + 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, + 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 8, 2, 3, 4, 5, + 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 10, 4, 5, 6, 7, + 8, 9, 10, 11, 5, 6, 7, 8, 9, 10, 11, 12, 6, 7, 8, 9, + 10, 11, 12, 13, 7, 8, 9, 10, 11, 12, 13, 14, 8, 9, 10, 11, + 12, 13, 14, 15, 9, 10, 11, 12, 13, 14, 15, 10, 11, 12, 13, 14, + 15, 11, 12, 13, 14, 15, 12, 13, 14, 15, 13, 14, 15, 14, 15, 15, + }, + //16x16 + { + 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0, + 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, + 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 7, 8, 9, 10, 11, 12, 13, 14, 15, 8, 9, 10, 11, + 12, 13, 14, 15, 9, 10, 11, 12, 13, 14, 15, 10, 11, 12, 13, 14, + 15, 11, 12, 13, 14, 15, 12, 13, 14, 15, 13, 14, 15, 14, 15, 15, + }, + }, +}; + +const uint8_t ff_vvc_diag_scan_y[5][5][16*16] = { + { + //1x1 + { 0, }, + //1x2 + { 0, 1, }, + //1x4 + { 0, 1, 2, 3, }, + //1x8 + { 0, 1, 2, 3, 4, 5, 6, 7, }, + //1x16 + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, }, + }, + { + //2x1 + { 0, 0, }, + //2x2 + { 0, 1, 0, 1, }, + //2x4 + { 0, 1, 0, 2, 1, 3, 2, 3, }, + //2x8 + { 0, 1, 0, 2, 1, 3, 2, 4, 3, 5, 4, 6, 5, 7, 6, 7, }, + //2x16 + { + 0, 1, 0, 2, 1, 3, 2, 4, 3, 5, 4, 6, 5, 7, 6, 8, + 7, 9, 8, 10, 9, 11, 10, 12, 11, 13, 12, 14, 13, 15, 14, 15, + }, + }, + { + //4x1 + { 0, 0, 0, 0, }, + //4x2 + { 0, 1, 0, 1, 0, 1, 0, 1, }, + //4x4 + { 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 3, 2, 3, }, + //4x8 + { + 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 5, 4, + 3, 2, 6, 5, 4, 3, 7, 6, 5, 4, 7, 6, 5, 7, 6, 7, + }, + //4x16 + { + 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 5, 4, + 3, 2, 6, 5, 4, 3, 7, 6, 5, 4, 8, 7, 6, 5, 9, 8, + 7, 6, 10, 9, 8, 7, 11, 10, 9, 8, 12, 11, 10, 9, 13, 12, + 11, 10, 14, 13, 12, 11, 15, 14, 13, 12, 15, 14, 13, 15, 14, 15, + }, + }, + { + //8x1 + { 0, 0, 0, 0, 0, 0, 0, 0, }, + //8x2 + { 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, }, + //8x4 + { + 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, + 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 3, 2, 3, + }, + //8x8 + { + 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 0, 5, + 4, 3, 2, 1, 0, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, + 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, + 2, 7, 6, 5, 4, 3, 7, 6, 5, 4, 7, 6, 5, 7, 6, 7, + }, + //8x16 + { + 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 0, 5, + 4, 3, 2, 1, 0, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, + 3, 2, 1, 0, 8, 7, 6, 5, 4, 3, 2, 1, 9, 8, 7, 6, + 5, 4, 3, 2, 10, 9, 8, 7, 6, 5, 4, 3, 11, 10, 9, 8, + 7, 6, 5, 4, 12, 11, 10, 9, 8, 7, 6, 5, 13, 12, 11, 10, + 9, 8, 7, 6, 14, 13, 12, 11, 10, 9, 8, 7, 15, 14, 13, 12, + 11, 10, 9, 8, 15, 14, 13, 12, 11, 10, 9, 15, 14, 13, 12, 11, + 10, 15, 14, 13, 12, 11, 15, 14, 13, 12, 15, 14, 13, 15, 14, 15, + }, + }, + { + //16x1 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + //16x2 + { + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, + }, + //16x4 + { + 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, + 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, + 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, + 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 3, 2, 3, + }, + //16x8 + { + 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 0, 5, + 4, 3, 2, 1, 0, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, + 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, + 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, + 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, + 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, + 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, + 2, 7, 6, 5, 4, 3, 7, 6, 5, 4, 7, 6, 5, 7, 6, 7, + }, + //16x16 + { + 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 0, 5, + 4, 3, 2, 1, 0, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, + 3, 2, 1, 0, 8, 7, 6, 5, 4, 3, 2, 1, 0, 9, 8, 7, + 6, 5, 4, 3, 2, 1, 0, 10, 9, 8, 7, 6, 5, 4, 3, 2, + 1, 0, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 12, 11, + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 13, 12, 11, 10, 9, + 8, 7, 6, 5, 4, 3, 2, 1, 0, 14, 13, 12, 11, 10, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 15, 14, 13, 12, 11, 10, 9, 8, 7, + 6, 5, 4, 3, 2, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, + 4, 3, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 15, 14, + 13, 12, 11, 10, 9, 8, 7, 6, 5, 15, 14, 13, 12, 11, 10, 9, + 8, 7, 6, 15, 14, 13, 12, 11, 10, 9, 8, 7, 15, 14, 13, 12, + 11, 10, 9, 8, 15, 14, 13, 12, 11, 10, 9, 15, 14, 13, 12, 11, + 10, 15, 14, 13, 12, 11, 15, 14, 13, 12, 15, 14, 13, 15, 14, 15, + }, + }, +}; + +const uint8_t ff_vvc_scaling_pred_8[8 * 8] = { + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, +}; + +const uint8_t ff_vvc_scaling_pred_16[8 * 8] = { + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, +}; + +const int ff_vvc_scaling_list0[8 * 8] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static const uint8_t mip_matrix_4x4[16][16][4] = { + { + { 32, 30, 90, 28 }, + { 32, 32, 72, 28 }, + { 34, 77, 53, 30 }, + { 51, 124, 36, 37 }, + { 31, 31, 95, 37 }, + { 33, 31, 70, 50 }, + { 52, 80, 25, 60 }, + { 78, 107, 1, 65 }, + { 31, 29, 37, 95 }, + { 38, 34, 19, 101 }, + { 73, 85, 0, 81 }, + { 92, 99, 0, 65 }, + { 34, 29, 14, 111 }, + { 48, 48, 7, 100 }, + { 80, 91, 0, 74 }, + { 89, 97, 0, 64 } + }, + { + { 31, 23, 34, 29 }, + { 31, 43, 34, 31 }, + { 30, 95, 34, 32 }, + { 29, 100, 35, 33 }, + { 31, 23, 34, 29 }, + { 31, 43, 34, 31 }, + { 30, 95, 34, 32 }, + { 29, 99, 35, 33 }, + { 31, 24, 35, 29 }, + { 31, 44, 34, 31 }, + { 30, 95, 35, 32 }, + { 29, 99, 35, 33 }, + { 31, 24, 35, 30 }, + { 31, 44, 35, 31 }, + { 30, 95, 35, 32 }, + { 29, 99, 35, 33 } + }, + { + { 32, 32, 36, 58 }, + { 32, 29, 26, 66 }, + { 36, 37, 23, 61 }, + { 79, 84, 3, 37 }, + { 32, 32, 30, 69 }, + { 33, 29, 24, 71 }, + { 44, 16, 21, 70 }, + { 96, 18, 0, 57 }, + { 32, 31, 24, 74 }, + { 33, 30, 23, 71 }, + { 36, 24, 24, 71 }, + { 59, 9, 16, 68 }, + { 32, 32, 23, 75 }, + { 33, 30, 24, 70 }, + { 32, 30, 25, 71 }, + { 36, 26, 25, 70 } + }, + { + { 32, 33, 34, 32 }, + { 32, 30, 22, 38 }, + { 29, 46, 25, 38 }, + { 53, 123, 28, 22 }, + { 32, 33, 30, 37 }, + { 32, 30, 21, 38 }, + { 32, 40, 24, 38 }, + { 64, 116, 26, 17 }, + { 32, 32, 23, 49 }, + { 32, 30, 21, 39 }, + { 34, 39, 24, 37 }, + { 72, 109, 23, 16 }, + { 33, 31, 17, 60 }, + { 32, 31, 21, 39 }, + { 35, 41, 24, 37 }, + { 72, 106, 22, 18 } + }, + { + { 34, 25, 89, 20 }, + { 38, 32, 47, 24 }, + { 40, 86, 29, 27 }, + { 38, 98, 32, 29 }, + { 34, 31, 94, 40 }, + { 44, 25, 83, 27 }, + { 54, 72, 43, 16 }, + { 47, 94, 33, 22 }, + { 33, 31, 36, 94 }, + { 43, 23, 51, 76 }, + { 62, 55, 64, 25 }, + { 57, 89, 38, 15 }, + { 32, 32, 28, 101 }, + { 38, 26, 33, 94 }, + { 55, 38, 68, 47 }, + { 59, 80, 52, 16 } + }, + { + { 28, 30, 68, 29 }, + { 23, 48, 23, 48 }, + { 39, 98, 16, 42 }, + { 84, 86, 20, 17 }, + { 25, 31, 52, 74 }, + { 38, 68, 5, 70 }, + { 95, 78, 7, 21 }, + { 127, 54, 12, 0 }, + { 30, 47, 14, 107 }, + { 79, 76, 0, 53 }, + { 127, 59, 7, 1 }, + { 127, 51, 9, 0 }, + { 50, 71, 1, 96 }, + { 109, 69, 7, 25 }, + { 127, 56, 9, 0 }, + { 123, 53, 13, 0 } + }, + { + { 40, 20, 72, 18 }, + { 48, 29, 44, 18 }, + { 53, 81, 35, 18 }, + { 48, 96, 33, 22 }, + { 45, 23, 79, 49 }, + { 61, 21, 56, 49 }, + { 72, 52, 32, 48 }, + { 65, 69, 20, 50 }, + { 41, 27, 29, 96 }, + { 49, 22, 28, 94 }, + { 52, 22, 28, 93 }, + { 49, 27, 27, 92 }, + { 37, 29, 26, 98 }, + { 39, 28, 28, 97 }, + { 38, 28, 30, 97 }, + { 38, 29, 30, 95 } + }, + { + { 33, 27, 43, 27 }, + { 32, 29, 31, 31 }, + { 31, 73, 33, 31 }, + { 35, 104, 34, 28 }, + { 32, 30, 63, 22 }, + { 33, 26, 33, 29 }, + { 33, 57, 33, 30 }, + { 37, 100, 35, 27 }, + { 32, 31, 85, 25 }, + { 34, 25, 39, 25 }, + { 35, 39, 32, 28 }, + { 40, 91, 35, 25 }, + { 32, 30, 77, 50 }, + { 34, 26, 54, 22 }, + { 37, 31, 34, 27 }, + { 45, 75, 34, 23 } + }, + { + { 34, 25, 77, 19 }, + { 36, 34, 56, 24 }, + { 41, 83, 39, 30 }, + { 47, 96, 28, 35 }, + { 34, 31, 70, 65 }, + { 38, 29, 53, 77 }, + { 43, 36, 37, 83 }, + { 48, 39, 28, 83 }, + { 33, 31, 31, 98 }, + { 33, 31, 30, 99 }, + { 34, 30, 31, 98 }, + { 36, 29, 31, 96 }, + { 32, 32, 30, 97 }, + { 32, 32, 31, 96 }, + { 31, 33, 33, 96 }, + { 32, 33, 34, 94 } + }, + { + { 30, 30, 93, 19 }, + { 31, 59, 67, 34 }, + { 31, 79, 36, 59 }, + { 30, 67, 17, 79 }, + { 30, 38, 68, 69 }, + { 29, 40, 43, 91 }, + { 26, 35, 32, 101 }, + { 23, 32, 30, 101 }, + { 26, 34, 30, 101 }, + { 23, 33, 30, 102 }, + { 20, 32, 31, 102 }, + { 18, 33, 32, 102 }, + { 23, 33, 31, 100 }, + { 20, 34, 32, 100 }, + { 18, 35, 33, 100 }, + { 18, 35, 33, 100 } + }, + { + { 31, 54, 90, 26 }, + { 32, 60, 53, 61 }, + { 34, 49, 37, 84 }, + { 34, 39, 35, 89 }, + { 35, 38, 41, 88 }, + { 35, 35, 32, 96 }, + { 35, 31, 33, 96 }, + { 35, 32, 35, 94 }, + { 34, 34, 30, 97 }, + { 35, 32, 33, 95 }, + { 35, 32, 34, 94 }, + { 35, 34, 34, 93 }, + { 34, 34, 34, 93 }, + { 35, 34, 34, 93 }, + { 35, 34, 34, 92 }, + { 36, 34, 35, 91 } + }, + { + { 32, 29, 54, 24 }, + { 31, 32, 34, 29 }, + { 31, 43, 34, 29 }, + { 32, 67, 36, 28 }, + { 31, 34, 69, 37 }, + { 31, 35, 46, 33 }, + { 30, 35, 39, 33 }, + { 30, 42, 39, 36 }, + { 31, 35, 39, 88 }, + { 30, 38, 41, 84 }, + { 30, 39, 40, 81 }, + { 39, 46, 38, 78 }, + { 31, 36, 34, 96 }, + { 34, 38, 37, 93 }, + { 55, 42, 38, 82 }, + { 89, 53, 38, 65 } + }, + { + { 32, 33, 43, 29 }, + { 32, 30, 29, 33 }, + { 31, 47, 31, 33 }, + { 33, 100, 31, 31 }, + { 32, 33, 74, 25 }, + { 32, 32, 34, 31 }, + { 32, 33, 30, 33 }, + { 32, 68, 30, 32 }, + { 32, 31, 91, 40 }, + { 32, 32, 58, 26 }, + { 31, 31, 30, 32 }, + { 31, 42, 30, 33 }, + { 32, 31, 49, 85 }, + { 32, 31, 83, 35 }, + { 31, 33, 48, 29 }, + { 31, 36, 32, 33 } + }, + { + { 31, 29, 81, 35 }, + { 32, 28, 34, 50 }, + { 31, 75, 16, 43 }, + { 34, 103, 29, 32 }, + { 32, 32, 53, 78 }, + { 31, 28, 36, 88 }, + { 30, 52, 18, 73 }, + { 52, 88, 17, 35 }, + { 32, 32, 35, 94 }, + { 30, 31, 35, 95 }, + { 36, 29, 31, 92 }, + { 100, 43, 16, 40 }, + { 32, 32, 35, 93 }, + { 30, 32, 38, 93 }, + { 55, 18, 37, 83 }, + { 127, 0, 30, 40 } + }, + { + { 31, 22, 47, 30 }, + { 31, 48, 25, 34 }, + { 30, 95, 31, 32 }, + { 32, 103, 33, 32 }, + { 30, 24, 57, 31 }, + { 30, 47, 26, 34 }, + { 31, 95, 31, 32 }, + { 43, 97, 35, 25 }, + { 29, 26, 44, 63 }, + { 37, 38, 24, 47 }, + { 74, 63, 28, 20 }, + { 110, 58, 34, 3 }, + { 46, 22, 5, 108 }, + { 93, 5, 9, 77 }, + { 127, 0, 17, 52 }, + { 127, 0, 15, 50 } + }, + { + { 32, 27, 68, 24 }, + { 35, 23, 35, 28 }, + { 35, 64, 29, 29 }, + { 37, 104, 33, 28 }, + { 32, 32, 91, 40 }, + { 36, 23, 67, 36 }, + { 49, 23, 39, 28 }, + { 60, 67, 30, 20 }, + { 32, 32, 36, 95 }, + { 35, 29, 38, 93 }, + { 50, 16, 30, 84 }, + { 72, 16, 15, 65 }, + { 32, 32, 27, 100 }, + { 33, 32, 29, 100 }, + { 37, 29, 30, 98 }, + { 48, 21, 29, 90 } + } +}; + +static const uint8_t mip_matrix_8x8[8][16][8] = { + { + { 30, 63, 46, 37, 25, 33, 33, 34 }, + { 30, 60, 66, 38, 32, 31, 32, 33 }, + { 29, 45, 74, 42, 32, 32, 32, 33 }, + { 30, 39, 62, 58, 32, 33, 32, 33 }, + { 30, 66, 55, 39, 32, 30, 30, 36 }, + { 29, 54, 69, 40, 33, 31, 31, 33 }, + { 28, 48, 71, 43, 32, 33, 32, 33 }, + { 28, 41, 72, 46, 32, 34, 32, 33 }, + { 30, 66, 56, 40, 32, 33, 28, 33 }, + { 29, 55, 69, 39, 33, 33, 30, 32 }, + { 27, 46, 72, 43, 33, 33, 32, 33 }, + { 27, 42, 69, 48, 32, 34, 32, 33 }, + { 30, 63, 55, 40, 32, 33, 35, 30 }, + { 29, 56, 66, 40, 33, 33, 33, 30 }, + { 27, 47, 69, 44, 33, 33, 33, 32 }, + { 27, 42, 65, 50, 32, 34, 32, 33 } + }, + { + { 32, 33, 30, 31, 74, 30, 31, 32 }, + { 33, 56, 28, 30, 41, 29, 32, 32 }, + { 33, 77, 52, 26, 29, 34, 30, 32 }, + { 33, 37, 80, 41, 31, 34, 30, 32 }, + { 32, 32, 33, 31, 59, 76, 28, 31 }, + { 33, 31, 31, 30, 78, 40, 28, 32 }, + { 33, 47, 28, 29, 53, 27, 31, 31 }, + { 33, 61, 44, 28, 34, 32, 31, 31 }, + { 32, 31, 34, 30, 26, 64, 76, 27 }, + { 32, 31, 34, 29, 45, 86, 36, 29 }, + { 33, 27, 34, 29, 73, 55, 25, 32 }, + { 33, 33, 34, 30, 62, 33, 30, 31 }, + { 32, 31, 34, 30, 30, 29, 58, 74 }, + { 32, 31, 35, 29, 27, 53, 77, 35 }, + { 32, 30, 36, 29, 40, 80, 44, 31 }, + { 33, 28, 37, 30, 58, 60, 31, 33 } + }, + { + { 32, 51, 27, 32, 27, 50, 29, 32 }, + { 32, 95, 42, 29, 29, 42, 30, 32 }, + { 32, 27, 99, 34, 31, 41, 29, 32 }, + { 32, 34, 21, 104, 31, 42, 30, 32 }, + { 32, 45, 30, 32, 9, 88, 40, 30 }, + { 32, 77, 38, 30, 9, 76, 38, 30 }, + { 32, 38, 78, 33, 14, 67, 37, 30 }, + { 32, 30, 30, 87, 20, 59, 38, 31 }, + { 33, 37, 32, 32, 27, 18, 106, 34 }, + { 34, 44, 34, 31, 25, 17, 108, 31 }, + { 36, 39, 45, 31, 24, 15, 108, 30 }, + { 37, 31, 31, 54, 25, 14, 101, 32 }, + { 36, 33, 32, 30, 29, 37, 13, 110 }, + { 39, 32, 32, 29, 27, 37, 15, 108 }, + { 44, 33, 31, 27, 25, 37, 16, 106 }, + { 47, 30, 31, 32, 25, 34, 19, 102 } + }, + { + { 32, 48, 35, 35, 47, 68, 31, 31 }, + { 32, 33, 59, 40, 27, 71, 33, 30 }, + { 32, 29, 47, 65, 24, 62, 37, 30 }, + { 33, 33, 31, 81, 26, 50, 42, 32 }, + { 32, 30, 40, 38, 30, 70, 55, 31 }, + { 32, 20, 46, 50, 26, 55, 64, 31 }, + { 33, 30, 29, 66, 25, 41, 72, 33 }, + { 36, 34, 27, 69, 26, 31, 67, 39 }, + { 33, 28, 36, 40, 30, 26, 85, 47 }, + { 36, 27, 33, 50, 31, 20, 79, 53 }, + { 43, 30, 26, 57, 28, 17, 67, 62 }, + { 51, 27, 28, 55, 22, 23, 49, 70 }, + { 38, 29, 32, 39, 28, 30, 22, 104 }, + { 51, 31, 28, 43, 24, 31, 17, 102 }, + { 69, 23, 30, 40, 15, 38, 10, 95 }, + { 77, 13, 35, 38, 8, 43, 8, 90 } + }, + { + { 32, 38, 32, 33, 101, 40, 29, 32 }, + { 32, 40, 37, 32, 100, 36, 30, 32 }, + { 32, 37, 46, 35, 94, 33, 30, 31 }, + { 33, 34, 30, 62, 81, 35, 30, 31 }, + { 32, 32, 33, 32, 22, 102, 39, 29 }, + { 32, 31, 33, 33, 26, 104, 34, 28 }, + { 33, 33, 33, 33, 31, 103, 32, 28 }, + { 33, 32, 34, 36, 37, 94, 33, 28 }, + { 32, 33, 32, 32, 34, 24, 99, 36 }, + { 32, 34, 33, 33, 33, 30, 98, 32 }, + { 33, 33, 34, 33, 31, 37, 95, 29 }, + { 33, 33, 33, 36, 30, 46, 85, 31 }, + { 32, 33, 32, 33, 30, 34, 23, 104 }, + { 32, 34, 33, 33, 31, 32, 30, 98 }, + { 32, 33, 34, 34, 31, 29, 39, 91 }, + { 33, 33, 32, 37, 32, 30, 47, 82 } + }, + { + { 32, 52, 48, 31, 38, 76, 26, 32 }, + { 33, 19, 62, 50, 25, 50, 51, 31 }, + { 33, 30, 20, 74, 29, 29, 54, 51 }, + { 34, 35, 23, 56, 31, 25, 41, 76 }, + { 33, 25, 38, 39, 28, 39, 83, 35 }, + { 35, 28, 25, 47, 31, 23, 57, 74 }, + { 37, 35, 22, 38, 31, 27, 30, 101 }, + { 38, 32, 33, 29, 30, 31, 27, 103 }, + { 34, 32, 27, 37, 32, 25, 41, 92 }, + { 38, 33, 28, 32, 30, 31, 18, 111 }, + { 40, 32, 33, 27, 29, 33, 18, 111 }, + { 40, 32, 34, 27, 28, 33, 23, 105 }, + { 35, 32, 30, 33, 31, 33, 20, 107 }, + { 38, 31, 33, 30, 29, 33, 21, 106 }, + { 40, 32, 33, 29, 29, 34, 22, 105 }, + { 40, 32, 33, 30, 29, 34, 24, 101 } + }, + { + { 32, 28, 31, 33, 92, 33, 30, 31 }, + { 33, 30, 28, 33, 71, 26, 32, 30 }, + { 33, 60, 26, 33, 47, 28, 33, 30 }, + { 33, 63, 44, 36, 37, 31, 33, 30 }, + { 33, 30, 31, 33, 43, 90, 33, 29 }, + { 33, 28, 29, 34, 71, 71, 26, 30 }, + { 33, 30, 26, 33, 86, 45, 28, 30 }, + { 33, 38, 29, 32, 74, 32, 33, 29 }, + { 33, 32, 30, 32, 29, 41, 95, 27 }, + { 34, 31, 29, 33, 26, 71, 73, 22 }, + { 34, 31, 29, 33, 37, 88, 46, 25 }, + { 33, 32, 28, 34, 55, 75, 36, 28 }, + { 34, 31, 30, 32, 33, 27, 43, 89 }, + { 35, 32, 28, 33, 33, 23, 77, 59 }, + { 34, 33, 28, 33, 30, 35, 91, 37 }, + { 34, 34, 28, 34, 33, 53, 74, 31 } + }, + { + { 33, 49, 26, 32, 26, 52, 28, 31 }, + { 33, 71, 72, 24, 30, 32, 34, 31 }, + { 32, 23, 70, 68, 32, 32, 32, 32 }, + { 31, 33, 21, 106, 33, 32, 32, 33 }, + { 34, 47, 32, 29, 5, 86, 44, 26 }, + { 34, 44, 89, 28, 28, 37, 33, 30 }, + { 32, 27, 46, 89, 33, 31, 31, 32 }, + { 30, 33, 20, 107, 33, 33, 32, 33 }, + { 35, 39, 42, 27, 26, 24, 92, 35 }, + { 34, 27, 87, 43, 30, 34, 38, 31 }, + { 31, 31, 32, 100, 32, 33, 30, 32 }, + { 29, 32, 22, 106, 33, 33, 32, 33 }, + { 35, 29, 47, 32, 32, 32, 17, 100 }, + { 34, 24, 69, 60, 34, 33, 28, 44 }, + { 31, 33, 31, 99, 32, 33, 32, 31 }, + { 29, 33, 25, 103, 33, 33, 32, 35 } + } +}; + +static const uint8_t mip_matrix_16x16[6][64][7] = { + { + { 42, 37, 33, 27, 44, 33, 35 }, + { 71, 39, 34, 24, 36, 35, 36 }, + { 77, 46, 35, 33, 30, 34, 36 }, + { 64, 60, 35, 33, 31, 32, 36 }, + { 49, 71, 38, 32, 32, 31, 36 }, + { 42, 66, 50, 33, 31, 32, 36 }, + { 40, 52, 67, 33, 31, 32, 35 }, + { 38, 43, 75, 33, 32, 32, 35 }, + { 56, 40, 33, 26, 43, 38, 36 }, + { 70, 49, 34, 30, 28, 38, 38 }, + { 65, 57, 36, 34, 28, 33, 39 }, + { 59, 60, 39, 33, 30, 31, 38 }, + { 55, 60, 43, 33, 30, 31, 38 }, + { 51, 61, 47, 33, 30, 32, 37 }, + { 46, 62, 51, 34, 30, 32, 37 }, + { 42, 60, 55, 33, 31, 32, 37 }, + { 60, 42, 34, 30, 37, 43, 38 }, + { 68, 52, 35, 35, 22, 37, 40 }, + { 62, 58, 37, 34, 28, 31, 40 }, + { 58, 59, 41, 33, 30, 30, 39 }, + { 56, 59, 44, 34, 30, 31, 38 }, + { 53, 60, 45, 33, 30, 31, 38 }, + { 49, 65, 45, 33, 30, 31, 38 }, + { 45, 64, 47, 33, 31, 32, 38 }, + { 59, 44, 35, 31, 34, 43, 41 }, + { 66, 53, 36, 35, 25, 31, 43 }, + { 61, 58, 38, 34, 29, 30, 40 }, + { 59, 57, 41, 33, 30, 31, 39 }, + { 57, 58, 43, 33, 30, 31, 39 }, + { 54, 61, 43, 33, 31, 31, 39 }, + { 51, 64, 43, 33, 31, 31, 39 }, + { 48, 64, 45, 33, 32, 31, 39 }, + { 57, 45, 35, 30, 35, 40, 44 }, + { 65, 54, 37, 33, 33, 24, 44 }, + { 63, 56, 38, 34, 30, 29, 39 }, + { 61, 56, 41, 34, 30, 32, 39 }, + { 58, 58, 42, 33, 31, 31, 39 }, + { 54, 62, 41, 33, 31, 31, 39 }, + { 51, 65, 42, 33, 31, 31, 39 }, + { 48, 63, 43, 33, 32, 31, 39 }, + { 55, 46, 35, 30, 36, 38, 47 }, + { 65, 53, 37, 32, 36, 26, 40 }, + { 65, 54, 38, 33, 31, 30, 38 }, + { 63, 55, 39, 33, 30, 32, 38 }, + { 59, 58, 40, 33, 31, 31, 39 }, + { 54, 64, 40, 33, 31, 30, 40 }, + { 49, 66, 40, 32, 32, 30, 41 }, + { 48, 64, 42, 32, 32, 30, 41 }, + { 54, 46, 35, 30, 34, 39, 49 }, + { 64, 52, 36, 32, 34, 34, 35 }, + { 65, 53, 37, 33, 32, 32, 37 }, + { 63, 55, 38, 33, 31, 31, 39 }, + { 59, 60, 38, 33, 31, 31, 40 }, + { 54, 64, 38, 33, 32, 30, 40 }, + { 49, 66, 39, 33, 32, 29, 41 }, + { 47, 64, 42, 32, 33, 29, 42 }, + { 51, 46, 35, 31, 33, 37, 54 }, + { 61, 51, 36, 32, 33, 38, 36 }, + { 63, 53, 37, 32, 32, 34, 37 }, + { 62, 55, 37, 33, 32, 32, 39 }, + { 58, 59, 37, 33, 32, 31, 40 }, + { 53, 63, 38, 33, 32, 31, 40 }, + { 49, 64, 40, 33, 33, 30, 41 }, + { 46, 62, 42, 33, 33, 30, 42 } + }, + { + { 39, 34, 33, 58, 44, 31, 32 }, + { 60, 38, 32, 40, 51, 30, 31 }, + { 73, 49, 31, 39, 48, 32, 31 }, + { 60, 73, 30, 39, 46, 33, 32 }, + { 43, 87, 35, 38, 45, 33, 32 }, + { 35, 78, 54, 36, 45, 33, 32 }, + { 33, 47, 86, 35, 44, 33, 32 }, + { 31, 17, 114, 34, 44, 34, 33 }, + { 43, 37, 32, 53, 70, 30, 31 }, + { 53, 50, 30, 42, 72, 31, 30 }, + { 52, 66, 30, 39, 70, 32, 30 }, + { 46, 78, 35, 37, 68, 34, 30 }, + { 43, 75, 48, 37, 66, 34, 30 }, + { 40, 62, 68, 35, 65, 35, 30 }, + { 33, 37, 97, 33, 62, 37, 31 }, + { 26, 14, 122, 32, 59, 38, 33 }, + { 40, 39, 33, 34, 87, 37, 30 }, + { 45, 54, 32, 34, 84, 41, 29 }, + { 41, 70, 35, 33, 83, 40, 29 }, + { 37, 73, 44, 32, 82, 40, 30 }, + { 37, 65, 60, 31, 81, 41, 29 }, + { 35, 48, 82, 30, 79, 43, 29 }, + { 28, 27, 108, 28, 76, 45, 30 }, + { 19, 11, 127, 27, 70, 46, 32 }, + { 38, 40, 34, 27, 73, 62, 28 }, + { 39, 54, 35, 30, 73, 62, 28 }, + { 33, 65, 41, 29, 75, 59, 28 }, + { 30, 65, 53, 27, 76, 58, 29 }, + { 29, 53, 72, 26, 77, 58, 29 }, + { 27, 35, 95, 24, 77, 60, 28 }, + { 19, 19, 117, 23, 74, 61, 30 }, + { 9, 16, 127, 23, 68, 60, 34 }, + { 35, 40, 35, 29, 44, 89, 30 }, + { 33, 51, 39, 29, 49, 86, 30 }, + { 28, 57, 49, 28, 53, 83, 30 }, + { 24, 52, 65, 26, 56, 82, 30 }, + { 22, 39, 86, 24, 58, 82, 30 }, + { 18, 22, 108, 23, 59, 82, 31 }, + { 10, 13, 125, 22, 58, 80, 33 }, + { 0, 19, 127, 22, 56, 74, 40 }, + { 33, 40, 36, 31, 28, 90, 45 }, + { 29, 46, 44, 29, 31, 92, 43 }, + { 24, 45, 58, 28, 34, 91, 43 }, + { 19, 37, 78, 26, 37, 91, 43 }, + { 15, 22, 99, 25, 38, 91, 42 }, + { 11, 11, 118, 24, 39, 90, 44 }, + { 2, 11, 127, 23, 41, 85, 48 }, + { 0, 17, 127, 23, 43, 75, 55 }, + { 31, 37, 39, 30, 28, 54, 82 }, + { 27, 37, 52, 28, 30, 58, 79 }, + { 22, 30, 70, 27, 32, 58, 79 }, + { 15, 19, 91, 26, 33, 58, 79 }, + { 10, 8, 111, 25, 34, 58, 79 }, + { 5, 2, 125, 25, 35, 57, 80 }, + { 0, 9, 127, 25, 36, 53, 84 }, + { 0, 13, 127, 25, 39, 47, 88 }, + { 28, 29, 46, 28, 39, 2, 123 }, + { 24, 24, 62, 27, 41, 1, 125 }, + { 19, 14, 81, 25, 43, 0, 126 }, + { 13, 4, 101, 24, 44, 0, 127 }, + { 6, 0, 116, 23, 45, 0, 127 }, + { 0, 0, 126, 23, 45, 1, 127 }, + { 0, 4, 127, 25, 44, 2, 127 }, + { 0, 9, 127, 25, 44, 3, 127 } + }, + { + { 30, 32, 32, 42, 34, 32, 32 }, + { 63, 26, 34, 16, 38, 32, 32 }, + { 98, 26, 34, 25, 34, 33, 32 }, + { 75, 61, 30, 31, 32, 33, 32 }, + { 36, 94, 32, 30, 33, 32, 32 }, + { 26, 76, 58, 30, 33, 32, 32 }, + { 30, 39, 91, 31, 32, 33, 31 }, + { 32, 23, 105, 32, 32, 32, 32 }, + { 34, 30, 33, 31, 52, 29, 32 }, + { 66, 24, 34, 11, 41, 33, 32 }, + { 97, 28, 34, 24, 34, 33, 32 }, + { 71, 65, 30, 30, 32, 33, 32 }, + { 34, 92, 35, 30, 33, 32, 32 }, + { 26, 70, 64, 29, 34, 32, 32 }, + { 30, 37, 94, 30, 33, 32, 31 }, + { 32, 23, 105, 31, 33, 33, 31 }, + { 37, 29, 33, 8, 79, 27, 32 }, + { 71, 22, 35, 5, 50, 32, 32 }, + { 98, 29, 34, 23, 34, 34, 32 }, + { 66, 70, 30, 31, 31, 33, 32 }, + { 31, 92, 38, 30, 33, 32, 32 }, + { 26, 66, 68, 29, 34, 32, 31 }, + { 30, 34, 97, 30, 34, 33, 31 }, + { 31, 22, 106, 30, 34, 33, 31 }, + { 40, 28, 34, 0, 76, 46, 28 }, + { 76, 21, 35, 0, 55, 35, 32 }, + { 97, 32, 34, 21, 37, 33, 33 }, + { 61, 75, 29, 30, 32, 32, 32 }, + { 29, 92, 40, 29, 33, 32, 32 }, + { 26, 62, 73, 29, 34, 32, 31 }, + { 29, 32, 99, 30, 34, 33, 30 }, + { 31, 22, 107, 30, 34, 33, 31 }, + { 42, 27, 34, 1, 48, 79, 25 }, + { 80, 20, 35, 0, 48, 47, 31 }, + { 94, 36, 32, 17, 40, 33, 33 }, + { 55, 80, 29, 27, 35, 31, 32 }, + { 27, 90, 43, 28, 34, 32, 31 }, + { 26, 58, 76, 29, 33, 33, 30 }, + { 29, 30, 101, 29, 34, 34, 30 }, + { 31, 21, 108, 29, 35, 34, 30 }, + { 44, 26, 34, 6, 30, 80, 40 }, + { 81, 21, 35, 0, 41, 52, 35 }, + { 90, 41, 31, 14, 41, 35, 33 }, + { 51, 82, 29, 24, 37, 32, 32 }, + { 27, 87, 47, 27, 35, 32, 31 }, + { 26, 54, 79, 29, 34, 33, 30 }, + { 29, 29, 102, 28, 34, 33, 30 }, + { 31, 21, 108, 28, 35, 33, 31 }, + { 47, 26, 34, 7, 34, 44, 75 }, + { 80, 24, 34, 0, 41, 41, 50 }, + { 84, 45, 31, 12, 40, 36, 36 }, + { 49, 81, 31, 22, 37, 33, 32 }, + { 28, 81, 51, 26, 35, 33, 31 }, + { 28, 51, 81, 28, 34, 33, 30 }, + { 29, 30, 101, 28, 35, 33, 31 }, + { 31, 22, 107, 28, 35, 33, 32 }, + { 48, 27, 34, 10, 40, 16, 97 }, + { 75, 27, 34, 3, 42, 26, 66 }, + { 77, 47, 33, 12, 40, 32, 43 }, + { 49, 75, 36, 21, 37, 33, 35 }, + { 32, 72, 55, 25, 36, 33, 32 }, + { 30, 49, 81, 27, 35, 33, 31 }, + { 30, 32, 98, 28, 35, 32, 32 }, + { 31, 24, 104, 28, 35, 32, 33 } + }, + { + { 36, 29, 33, 43, 47, 29, 31 }, + { 74, 20, 35, 19, 47, 34, 32 }, + { 92, 35, 32, 29, 31, 40, 34 }, + { 53, 80, 26, 33, 28, 36, 37 }, + { 24, 91, 41, 31, 31, 31, 38 }, + { 25, 57, 74, 31, 32, 30, 37 }, + { 32, 28, 99, 32, 32, 29, 36 }, + { 34, 20, 105, 33, 32, 30, 35 }, + { 50, 26, 34, 33, 74, 30, 31 }, + { 75, 28, 33, 23, 46, 47, 33 }, + { 64, 58, 29, 30, 26, 46, 40 }, + { 31, 85, 37, 31, 27, 33, 44 }, + { 22, 67, 64, 30, 31, 28, 42 }, + { 29, 35, 93, 31, 32, 27, 40 }, + { 33, 20, 105, 32, 33, 27, 37 }, + { 34, 19, 106, 33, 32, 29, 36 }, + { 51, 29, 33, 25, 72, 51, 30 }, + { 61, 42, 31, 30, 31, 60, 39 }, + { 40, 70, 34, 32, 24, 41, 50 }, + { 22, 72, 54, 30, 31, 27, 50 }, + { 25, 44, 83, 30, 33, 25, 44 }, + { 32, 23, 102, 32, 33, 26, 40 }, + { 34, 18, 107, 32, 33, 28, 37 }, + { 34, 19, 105, 33, 32, 30, 35 }, + { 45, 35, 32, 30, 39, 79, 33 }, + { 43, 53, 33, 35, 24, 53, 55 }, + { 27, 67, 45, 32, 29, 27, 61 }, + { 22, 53, 72, 30, 33, 22, 52 }, + { 28, 31, 95, 31, 33, 25, 43 }, + { 32, 20, 105, 32, 33, 27, 38 }, + { 34, 18, 107, 32, 32, 29, 36 }, + { 34, 20, 105, 33, 31, 31, 35 }, + { 38, 40, 32, 35, 23, 72, 54 }, + { 31, 55, 39, 34, 29, 32, 73 }, + { 22, 57, 60, 31, 35, 18, 64 }, + { 25, 39, 86, 31, 35, 22, 49 }, + { 30, 24, 101, 32, 33, 27, 40 }, + { 33, 19, 106, 32, 32, 30, 36 }, + { 34, 18, 107, 33, 31, 31, 35 }, + { 34, 20, 104, 33, 31, 32, 34 }, + { 33, 42, 35, 34, 28, 39, 82 }, + { 26, 51, 50, 33, 34, 18, 80 }, + { 23, 46, 74, 31, 35, 20, 59 }, + { 27, 32, 93, 32, 34, 26, 44 }, + { 31, 22, 103, 32, 32, 30, 37 }, + { 33, 19, 106, 33, 31, 31, 35 }, + { 34, 19, 106, 33, 31, 32, 34 }, + { 35, 21, 103, 34, 31, 32, 34 }, + { 29, 41, 41, 33, 34, 20, 92 }, + { 24, 44, 62, 34, 35, 18, 73 }, + { 24, 37, 83, 34, 33, 25, 52 }, + { 28, 28, 97, 33, 32, 30, 40 }, + { 32, 23, 103, 33, 31, 32, 36 }, + { 34, 20, 105, 34, 30, 33, 34 }, + { 35, 20, 104, 34, 30, 33, 33 }, + { 35, 22, 102, 34, 30, 33, 34 }, + { 27, 38, 51, 34, 34, 20, 86 }, + { 26, 37, 71, 35, 34, 24, 64 }, + { 27, 33, 87, 35, 32, 30, 47 }, + { 30, 28, 96, 34, 31, 32, 39 }, + { 32, 24, 100, 35, 30, 32, 36 }, + { 34, 23, 101, 34, 30, 33, 34 }, + { 35, 23, 101, 34, 30, 32, 34 }, + { 34, 24, 99, 35, 30, 33, 34 } + }, + { + { 39, 30, 31, 67, 33, 34, 31 }, + { 72, 21, 32, 43, 39, 33, 31 }, + { 100, 23, 32, 35, 39, 34, 31 }, + { 75, 63, 24, 32, 38, 34, 32 }, + { 32, 98, 26, 29, 37, 35, 32 }, + { 22, 77, 55, 29, 36, 35, 31 }, + { 31, 37, 90, 31, 35, 35, 32 }, + { 35, 22, 100, 33, 33, 36, 33 }, + { 47, 29, 32, 74, 54, 32, 31 }, + { 71, 24, 32, 60, 50, 36, 30 }, + { 86, 31, 30, 46, 48, 37, 30 }, + { 65, 63, 25, 34, 46, 39, 30 }, + { 33, 85, 32, 28, 43, 40, 30 }, + { 26, 64, 60, 27, 39, 41, 30 }, + { 33, 33, 87, 29, 35, 41, 31 }, + { 37, 23, 93, 32, 33, 41, 32 }, + { 41, 32, 32, 45, 84, 32, 32 }, + { 55, 31, 32, 50, 70, 40, 30 }, + { 62, 37, 31, 45, 61, 45, 29 }, + { 53, 55, 31, 36, 55, 48, 29 }, + { 38, 63, 40, 29, 48, 50, 28 }, + { 34, 49, 60, 27, 43, 51, 29 }, + { 38, 30, 78, 28, 38, 50, 31 }, + { 40, 24, 83, 30, 36, 48, 33 }, + { 35, 33, 33, 29, 75, 58, 29 }, + { 39, 35, 33, 34, 68, 59, 29 }, + { 41, 39, 34, 36, 61, 62, 29 }, + { 41, 43, 37, 33, 54, 64, 28 }, + { 41, 43, 45, 30, 48, 65, 29 }, + { 42, 36, 56, 27, 44, 63, 30 }, + { 42, 30, 65, 27, 41, 60, 33 }, + { 42, 28, 68, 28, 37, 56, 36 }, + { 33, 34, 33, 31, 42, 88, 30 }, + { 31, 36, 34, 31, 44, 84, 31 }, + { 31, 37, 35, 32, 43, 83, 31 }, + { 35, 35, 39, 32, 40, 82, 31 }, + { 40, 32, 44, 31, 38, 81, 31 }, + { 44, 30, 48, 30, 37, 78, 33 }, + { 44, 30, 52, 28, 37, 72, 36 }, + { 43, 30, 55, 29, 35, 66, 40 }, + { 32, 33, 33, 34, 25, 85, 48 }, + { 30, 34, 34, 33, 25, 88, 44 }, + { 30, 34, 36, 34, 25, 90, 41 }, + { 33, 32, 38, 34, 25, 90, 40 }, + { 38, 29, 41, 34, 26, 88, 40 }, + { 42, 29, 41, 33, 27, 85, 41 }, + { 43, 30, 42, 31, 28, 80, 43 }, + { 42, 31, 45, 31, 30, 72, 47 }, + { 32, 33, 33, 33, 26, 54, 79 }, + { 31, 32, 34, 35, 20, 68, 68 }, + { 32, 32, 35, 36, 17, 76, 62 }, + { 34, 31, 36, 36, 17, 79, 59 }, + { 37, 29, 37, 36, 18, 78, 58 }, + { 39, 29, 37, 35, 20, 77, 58 }, + { 41, 30, 37, 34, 22, 74, 58 }, + { 40, 31, 40, 32, 26, 68, 59 }, + { 33, 31, 34, 33, 29, 31, 98 }, + { 34, 30, 34, 35, 23, 45, 88 }, + { 34, 31, 34, 36, 20, 54, 82 }, + { 35, 31, 34, 36, 18, 59, 78 }, + { 36, 31, 34, 37, 19, 60, 76 }, + { 38, 30, 34, 36, 20, 61, 74 }, + { 39, 31, 35, 35, 22, 60, 73 }, + { 39, 31, 37, 34, 24, 59, 71 } + }, + { + { 30, 33, 32, 55, 32, 32, 32 }, + { 47, 30, 31, 29, 36, 32, 32 }, + { 81, 28, 32, 28, 34, 32, 32 }, + { 85, 46, 29, 32, 32, 33, 32 }, + { 54, 82, 26, 32, 32, 33, 32 }, + { 30, 90, 38, 31, 32, 33, 32 }, + { 30, 56, 73, 31, 33, 32, 32 }, + { 37, 21, 102, 32, 32, 32, 32 }, + { 33, 32, 31, 68, 39, 31, 31 }, + { 38, 32, 31, 43, 34, 33, 31 }, + { 63, 30, 31, 29, 34, 32, 32 }, + { 82, 37, 30, 29, 33, 32, 32 }, + { 71, 63, 27, 31, 32, 33, 32 }, + { 44, 86, 30, 30, 33, 33, 32 }, + { 33, 72, 55, 30, 32, 32, 31 }, + { 37, 37, 86, 31, 32, 33, 31 }, + { 34, 33, 32, 60, 61, 29, 32 }, + { 36, 33, 31, 56, 38, 32, 31 }, + { 51, 30, 31, 38, 33, 33, 32 }, + { 75, 31, 31, 30, 33, 33, 32 }, + { 80, 47, 29, 30, 32, 33, 31 }, + { 60, 73, 27, 30, 33, 33, 31 }, + { 41, 78, 41, 30, 33, 32, 31 }, + { 38, 53, 68, 30, 32, 33, 31 }, + { 33, 33, 32, 43, 77, 35, 30 }, + { 35, 33, 31, 55, 54, 29, 32 }, + { 43, 32, 31, 46, 39, 31, 32 }, + { 64, 30, 31, 35, 34, 33, 32 }, + { 79, 37, 30, 31, 32, 33, 31 }, + { 73, 57, 28, 30, 32, 33, 31 }, + { 54, 73, 33, 30, 32, 33, 31 }, + { 43, 64, 52, 30, 32, 33, 31 }, + { 33, 33, 32, 34, 68, 58, 28 }, + { 34, 33, 31, 45, 70, 33, 31 }, + { 38, 33, 31, 48, 52, 29, 32 }, + { 54, 31, 31, 40, 39, 31, 32 }, + { 73, 32, 31, 34, 34, 33, 31 }, + { 77, 45, 29, 31, 32, 32, 32 }, + { 65, 63, 30, 31, 31, 33, 31 }, + { 51, 66, 42, 30, 32, 33, 31 }, + { 33, 32, 32, 34, 44, 81, 31 }, + { 34, 33, 31, 38, 66, 52, 28 }, + { 36, 33, 30, 44, 62, 34, 31 }, + { 47, 31, 31, 43, 48, 30, 32 }, + { 64, 31, 31, 38, 38, 32, 32 }, + { 75, 38, 30, 33, 34, 32, 32 }, + { 71, 53, 30, 31, 32, 33, 32 }, + { 59, 61, 37, 30, 32, 33, 32 }, + { 33, 32, 31, 35, 31, 71, 54 }, + { 34, 33, 31, 37, 49, 70, 33 }, + { 36, 33, 31, 41, 60, 48, 30 }, + { 43, 32, 31, 43, 54, 35, 31 }, + { 56, 31, 31, 40, 44, 32, 32 }, + { 68, 35, 30, 36, 37, 32, 32 }, + { 70, 45, 30, 33, 34, 33, 32 }, + { 63, 55, 35, 31, 33, 33, 32 }, + { 33, 32, 31, 33, 34, 36, 87 }, + { 34, 32, 31, 36, 38, 62, 52 }, + { 36, 33, 31, 39, 50, 57, 36 }, + { 41, 33, 31, 41, 53, 43, 33 }, + { 50, 33, 31, 41, 48, 36, 32 }, + { 59, 35, 31, 37, 41, 34, 32 }, + { 65, 42, 31, 35, 36, 33, 32 }, + { 62, 49, 35, 33, 34, 34, 33 } + } +}; + +const uint8_t* ff_vvc_get_mip_matrix(const int size_id, const int mode_id) +{ + av_assert0(size_id < 3); + if (size_id == 0) + return &mip_matrix_4x4[mode_id][0][0]; + if (size_id == 1) + return &mip_matrix_8x8[mode_id][0][0]; + return &mip_matrix_16x16[mode_id][0][0]; +} + +// DCT-8 +#define DEFINE_DCT8_P4_MATRIX(a,b,c,d) \ +{ \ + { a, b, c, d }, \ + { b, 0, -b, -b }, \ + { c, -b, -d, a }, \ + { d, -b, a, -c }, \ +} + +#define DEFINE_DCT8_P8_MATRIX(a,b,c,d,e,f,g,h) \ +{ \ + { a, b, c, d, e, f, g, h }, \ + { b, e, h, -g, -d, -a, -c, -f }, \ + { c, h, -e, -a, -f, g, b, d }, \ + { d, -g, -a, -h, c, e, -f, -b }, \ + { e, -d, -f, c, g, -b, -h, a }, \ + { f, -a, g, e, -b, h, d, -c }, \ + { g, -c, b, -f, -h, d, -a, e }, \ + { h, -f, d, -b, a, -c, e, -g }, \ +} + +#define DEFINE_DCT8_P16_MATRIX(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) \ +{ \ + { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p }, \ + { b, e, h, k, n, 0, -n, -k, -h, -e, -b, -b, -e, -h, -k, -n }, \ + { c, h, m, -p, -k, -f, -a, -e, -j, -o, n, i, d, b, g, l }, \ + { d, k, -p, -i, -b, -f, -m, n, g, a, h, o, -l, -e, -c, -j }, \ + { e, n, -k, -b, -h, 0, h, b, k, -n, -e, -e, -n, k, b, h }, \ + { f, 0, -f, -f, 0, f, f, 0, -f, -f, 0, f, f, 0, -f, -f }, \ + { g, -n, -a, -m, h, f, -o, -b, -l, i, e, -p, -c, -k, j, d }, \ + { h, -k, -e, n, b, 0, -b, -n, e, k, -h, -h, k, e, -n, -b }, \ + { i, -h, -j, g, k, -f, -l, e, m, -d, -n, c, o, -b, -p, a }, \ + { j, -e, -o, a, -n, -f, i, k, -d, -p, b, -m, -g, h, l, -c }, \ + { k, -b, n, h, -e, 0, e, -h, -n, b, -k, -k, b, -n, -h, e }, \ + { l, -b, i, o, -e, f, -p, -h, c, -m, -k, a, -j, -n, d, -g }, \ + { m, -e, d, -l, -n, f, -c, k, o, -g, b, -j, -p, h, -a, i }, \ + { n, -h, b, -e, k, 0, -k, e, -b, h, -n, -n, h, -b, e, -k }, \ + { o, -k, g, -c, b, -f, j, -n, -p, l, -h, d, -a, e, -i, m }, \ + { p, -n, l, -j, h, -f, d, -b, a, -c, e, -g, i, -k, m, -o }, \ +} + +#define DEFINE_DCT8_P32_MATRIX(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F) \ +{ \ + { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F }, \ + { b, e, h, k, n, q, t, w, z, C, F, -E, -B, -y, -v, -s, -p, -m, -j, -g, -d, -a, -c, -f, -i, -l, -o, -r, -u, -x, -A, -D }, \ + { c, h, m, r, w, B, 0, -B, -w, -r, -m, -h, -c, -c, -h, -m, -r, -w, -B, 0, B, w, r, m, h, c, c, h, m, r, w, B }, \ + { d, k, r, y, F, -A, -t, -m, -f, -b, -i, -p, -w, -D, C, v, o, h, a, g, n, u, B, -E, -x, -q, -j, -c, -e, -l, -s, -z }, \ + { e, n, w, F, -y, -p, -g, -c, -l, -u, -D, A, r, i, a, j, s, B, -C, -t, -k, -b, -h, -q, -z, E, v, m, d, f, o, x }, \ + { f, q, B, -A, -p, -e, -g, -r, -C, z, o, d, h, s, D, -y, -n, -c, -i, -t, -E, x, m, b, j, u, F, -w, -l, -a, -k, -v }, \ + { g, t, 0, -t, -g, -g, -t, 0, t, g, g, t, 0, -t, -g, -g, -t, 0, t, g, g, t, 0, -t, -g, -g, -t, 0, t, g, g, t }, \ + { h, w, -B, -m, -c, -r, 0, r, c, m, B, -w, -h, -h, -w, B, m, c, r, 0, -r, -c, -m, -B, w, h, h, w, -B, -m, -c, -r }, \ + { i, z, -w, -f, -l, -C, t, c, o, F, -q, -a, -r, E, n, d, u, -B, -k, -g, -x, y, h, j, A, -v, -e, -m, -D, s, b, p }, \ + { j, C, -r, -b, -u, z, g, m, F, -o, -e, -x, w, d, p, -E, -l, -h, -A, t, a, s, -B, -i, -k, -D, q, c, v, -y, -f, -n }, \ + { k, F, -m, -i, -D, o, g, B, -q, -e, -z, s, c, x, -u, -a, -v, w, b, t, -y, -d, -r, A, f, p, -C, -h, -n, E, j, l }, \ + { l, -E, -h, -p, A, d, t, -w, -a, -x, s, e, B, -o, -i, -F, k, m, -D, -g, -q, z, c, u, -v, -b, -y, r, f, C, -n, -j }, \ + { m, -B, -c, -w, r, h, 0, -h, -r, w, c, B, -m, -m, B, c, w, -r, -h, 0, h, r, -w, -c, -B, m, m, -B, -c, -w, r, h }, \ + { n, -y, -c, -D, i, s, -t, -h, E, d, x, -o, -m, z, b, C, -j, -r, u, g, -F, -e, -w, p, l, -A, -a, -B, k, q, -v, -f }, \ + { o, -v, -h, C, a, D, -g, -w, n, p, -u, -i, B, b, E, -f, -x, m, q, -t, -j, A, c, F, -e, -y, l, r, -s, -k, z, d }, \ + { p, -s, -m, v, j, -y, -g, B, d, -E, -a, -F, c, C, -f, -z, i, w, -l, -t, o, q, -r, -n, u, k, -x, -h, A, e, -D, -b }, \ + { q, -p, -r, o, s, -n, -t, m, u, -l, -v, k, w, -j, -x, i, y, -h, -z, g, A, -f, -B, e, C, -d, -D, c, E, -b, -F, a }, \ + { r, -m, -w, h, B, -c, 0, c, -B, -h, w, m, -r, -r, m, w, -h, -B, c, 0, -c, B, h, -w, -m, r, r, -m, -w, h, B, -c }, \ + { s, -j, -B, a, -C, -i, t, r, -k, -A, b, -D, -h, u, q, -l, -z, c, -E, -g, v, p, -m, -y, d, -F, -f, w, o, -n, -x, e }, \ + { t, -g, 0, g, -t, -t, g, 0, -g, t, t, -g, 0, g, -t, -t, g, 0, -g, t, t, -g, 0, g, -t, -t, g, 0, -g, t, t, -g }, \ + { u, -d, B, n, -k, -E, g, -r, -x, a, -y, -q, h, -F, -j, o, A, -c, v, t, -e, C, m, -l, -D, f, -s, -w, b, -z, -p, i }, \ + { v, -a, w, u, -b, x, t, -c, y, s, -d, z, r, -e, A, q, -f, B, p, -g, C, o, -h, D, n, -i, E, m, -j, F, l, -k }, \ + { w, -c, r, B, -h, m, 0, -m, h, -B, -r, c, -w, -w, c, -r, -B, h, -m, 0, m, -h, B, r, -c, w, w, -c, r, B, -h, m }, \ + { x, -f, m, -E, -q, b, -t, -B, j, -i, A, u, -c, p, F, -n, e, -w, -y, g, -l, D, r, -a, s, C, -k, h, -z, -v, d, -o }, \ + { y, -i, h, -x, -z, j, -g, w, A, -k, f, -v, -B, l, -e, u, C, -m, d, -t, -D, n, -c, s, E, -o, b, -r, -F, p, -a, q }, \ + { z, -l, c, -q, E, u, -g, h, -v, -D, p, -b, m, -A, -y, k, -d, r, -F, -t, f, -i, w, C, -o, a, -n, B, x, -j, e, -s }, \ + { A, -o, c, -j, v, F, -t, h, -e, q, -C, -y, m, -a, l, -x, -D, r, -f, g, -s, E, w, -k, b, -n, z, B, -p, d, -i, u }, \ + { B, -r, h, -c, m, -w, 0, w, -m, c, -h, r, -B, -B, r, -h, c, -m, w, 0, -w, m, -c, h, -r, B, B, -r, h, -c, m, -w }, \ + { C, -u, m, -e, d, -l, t, -B, -D, v, -n, f, -c, k, -s, A, E, -w, o, -g, b, -j, r, -z, -F, x, -p, h, -a, i, -q, y }, \ + { D, -x, r, -l, f, -a, g, -m, s, -y, E, C, -w, q, -k, e, -b, h, -n, t, -z, F, B, -v, p, -j, d, -c, i, -o, u, -A }, \ + { E, -A, w, -s, o, -k, g, -c, b, -f, j, -n, r, -v, z, -D, -F, B, -x, t, -p, l, -h, d, -a, e, -i, m, -q, u, -y, C }, \ + { F, -D, B, -z, x, -v, t, -r, p, -n, l, -j, h, -f, d, -b, a, -c, e, -g, i, -k, m, -o, q, -s, u, -w, y, -A, C, -E }, \ +} + +const int8_t ff_vvc_dct8_4x4[4][4] = DEFINE_DCT8_P4_MATRIX(84, 74, 55, 29); +const int8_t ff_vvc_dct8_8x8[8][8] = DEFINE_DCT8_P8_MATRIX(86, 85, 78, 71, 60, 46, 32, 17); +const int8_t ff_vvc_dct8_16x16[16][16] = DEFINE_DCT8_P16_MATRIX(88, 88, 87, 85, 81, 77, 73, 68, 62, 55, 48, 40, 33, 25, 17, 8); +const int8_t ff_vvc_dct8_32x32[32][32] = DEFINE_DCT8_P32_MATRIX(90, 90, 89, 88, 87, 86, 85, 84, 82, 80, 78, 77, 74, 72, 68, 66, 63, 60, 56, 53, 50, 46, 42, 38, 34, 30, 26, 21, 17, 13, 9, 4); + +// DST-7 +#define DEFINE_DST7_P4_MATRIX(a,b,c,d) \ +{ \ + { a, b, c, d }, \ + { c, c, 0, -c }, \ + { d, -a, -c, b }, \ + { b, -d, c, -a }, \ +} + +#define DEFINE_DST7_P8_MATRIX(a,b,c,d,e,f,g,h) \ +{ \ + { a, b, c, d, e, f, g, h }, \ + { c, f, h, e, b, -a, -d, -g }, \ + { e, g, b, -c, -h, -d, a, f }, \ + { g, c, -d, -f, a, h, b, -e }, \ + { h, -a, -g, b, f, -c, -e, d }, \ + { f, -e, -a, g, -d, -b, h, -c }, \ + { d, -h, e, -a, -c, g, -f, b }, \ + { b, -d, f, -h, g, -e, c, -a }, \ +} + +#define DEFINE_DST7_P16_MATRIX(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) \ +{ \ + { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p }, \ + { c, f, i, l, o, o, l, i, f, c, 0, -c, -f, -i, -l, -o }, \ + { e, j, o, m, h, c, -b, -g, -l, -p, -k, -f, -a, d, i, n }, \ + { g, n, l, e, -b, -i, -p, -j, -c, d, k, o, h, a, -f, -m }, \ + { i, o, f, -c, -l, -l, -c, f, o, i, 0, -i, -o, -f, c, l }, \ + { k, k, 0, -k, -k, 0, k, k, 0, -k, -k, 0, k, k, 0, -k }, \ + { m, g, -f, -n, -a, l, h, -e, -o, -b, k, i, -d, -p, -c, j }, \ + { o, c, -l, -f, i, i, -f, -l, c, o, 0, -o, -c, l, f, -i }, \ + { p, -a, -o, b, n, -c, -m, d, l, -e, -k, f, j, -g, -i, h }, \ + { n, -e, -i, j, d, -o, a, m, -f, -h, k, c, -p, b, l, -g }, \ + { l, -i, -c, o, -f, -f, o, -c, -i, l, 0, -l, i, c, -o, f }, \ + { j, -m, c, g, -p, f, d, -n, i, a, -k, l, -b, -h, o, -e }, \ + { h, -p, i, -a, -g, o, -j, b, f, -n, k, -c, -e, m, -l, d }, \ + { f, -l, o, -i, c, c, -i, o, -l, f, 0, -f, l, -o, i, -c }, \ + { d, -h, l, -p, m, -i, e, -a, -c, g, -k, o, -n, j, -f, b }, \ + { b, -d, f, -h, j, -l, n, -p, o, -m, k, -i, g, -e, c, -a }, \ +} + +#define DEFINE_DST7_P32_MATRIX(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F) \ +{ \ + { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F }, \ + { c, f, i, l, o, r, u, x, A, D, F, C, z, w, t, q, n, k, h, e, b, -a, -d, -g, -j, -m, -p, -s, -v, -y, -B, -E }, \ + { e, j, o, t, y, D, D, y, t, o, j, e, 0, -e, -j, -o, -t, -y, -D, -D, -y, -t, -o, -j, -e, 0, e, j, o, t, y, D }, \ + { g, n, u, B, D, w, p, i, b, -e, -l, -s, -z, -F, -y, -r, -k, -d, c, j, q, x, E, A, t, m, f, -a, -h, -o, -v, -C }, \ + { i, r, A, C, t, k, b, -g, -p, -y, -E, -v, -m, -d, e, n, w, F, x, o, f, -c, -l, -u, -D, -z, -q, -h, a, j, s, B }, \ + { k, v, F, u, j, -a, -l, -w, -E, -t, -i, b, m, x, D, s, h, -c, -n, -y, -C, -r, -g, d, o, z, B, q, f, -e, -p, -A }, \ + { m, z, z, m, 0, -m, -z, -z, -m, 0, m, z, z, m, 0, -m, -z, -z, -m, 0, m, z, z, m, 0, -m, -z, -z, -m, 0, m, z }, \ + { o, D, t, e, -j, -y, -y, -j, e, t, D, o, 0, -o, -D, -t, -e, j, y, y, j, -e, -t, -D, -o, 0, o, D, t, e, -j, -y }, \ + { q, E, n, -c, -t, -B, -k, f, w, y, h, -i, -z, -v, -e, l, C, s, b, -o, -F, -p, a, r, D, m, -d, -u, -A, -j, g, x }, \ + { s, A, h, -k, -D, -p, c, v, x, e, -n, -F, -m, f, y, u, b, -q, -C, -j, i, B, r, -a, -t, -z, -g, l, E, o, -d, -w }, \ + { u, w, b, -s, -y, -d, q, A, f, -o, -C, -h, m, E, j, -k, -F, -l, i, D, n, -g, -B, -p, e, z, r, -c, -x, -t, a, v }, \ + { w, s, -d, -A, -o, h, E, k, -l, -D, -g, p, z, c, -t, -v, a, x, r, -e, -B, -n, i, F, j, -m, -C, -f, q, y, b, -u }, \ + { y, o, -j, -D, -e, t, t, -e, -D, -j, o, y, 0, -y, -o, j, D, e, -t, -t, e, D, j, -o, -y, 0, y, o, -j, -D, -e, t }, \ + { A, k, -p, -v, e, F, f, -u, -q, j, B, a, -z, -l, o, w, -d, -E, -g, t, r, -i, -C, -b, y, m, -n, -x, c, D, h, -s }, \ + { C, g, -v, -n, o, u, -h, -B, a, D, f, -w, -m, p, t, -i, -A, b, E, e, -x, -l, q, s, -j, -z, c, F, d, -y, -k, r }, \ + { E, c, -B, -f, y, i, -v, -l, s, o, -p, -r, m, u, -j, -x, g, A, -d, -D, a, F, b, -C, -e, z, h, -w, -k, t, n, -q }, \ + { F, -a, -E, b, D, -c, -C, d, B, -e, -A, f, z, -g, -y, h, x, -i, -w, j, v, -k, -u, l, t, -m, -s, n, r, -o, -q, p }, \ + { D, -e, -y, j, t, -o, -o, t, j, -y, -e, D, 0, -D, e, y, -j, -t, o, o, -t, -j, y, e, -D, 0, D, -e, -y, j, t, -o }, \ + { B, -i, -s, r, j, -A, -a, C, -h, -t, q, k, -z, -b, D, -g, -u, p, l, -y, -c, E, -f, -v, o, m, -x, -d, F, -e, -w, n }, \ + { z, -m, -m, z, 0, -z, m, m, -z, 0, z, -m, -m, z, 0, -z, m, m, -z, 0, z, -m, -m, z, 0, -z, m, m, -z, 0, z, -m }, \ + { x, -q, -g, E, -j, -n, A, -c, -u, t, d, -B, m, k, -D, f, r, -w, -a, y, -p, -h, F, -i, -o, z, -b, -v, s, e, -C, l }, \ + { v, -u, -a, w, -t, -b, x, -s, -c, y, -r, -d, z, -q, -e, A, -p, -f, B, -o, -g, C, -n, -h, D, -m, -i, E, -l, -j, F, -k }, \ + { t, -y, e, o, -D, j, j, -D, o, e, -y, t, 0, -t, y, -e, -o, D, -j, -j, D, -o, -e, y, -t, 0, t, -y, e, o, -D, j }, \ + { r, -C, k, g, -y, v, -d, -n, F, -o, -c, u, -z, h, j, -B, s, -a, -q, D, -l, -f, x, -w, e, m, -E, p, b, -t, A, -i }, \ + { p, -F, q, -a, -o, E, -r, b, n, -D, s, -c, -m, C, -t, d, l, -B, u, -e, -k, A, -v, f, j, -z, w, -g, -i, y, -x, h }, \ + { n, -B, w, -i, -e, s, -F, r, -d, -j, x, -A, m, a, -o, C, -v, h, f, -t, E, -q, c, k, -y, z, -l, -b, p, -D, u, -g }, \ + { l, -x, C, -q, e, g, -s, E, -v, j, b, -n, z, -A, o, -c, -i, u, -F, t, -h, -d, p, -B, y, -m, a, k, -w, D, -r, f }, \ + { j, -t, D, -y, o, -e, -e, o, -y, D, -t, j, 0, -j, t, -D, y, -o, e, e, -o, y, -D, t, -j, 0, j, -t, D, -y, o, -e }, \ + { h, -p, x, -F, y, -q, i, -a, -g, o, -w, E, -z, r, -j, b, f, -n, v, -D, A, -s, k, -c, -e, m, -u, C, -B, t, -l, d }, \ + { f, -l, r, -x, D, -C, w, -q, k, -e, -a, g, -m, s, -y, E, -B, v, -p, j, -d, -b, h, -n, t, -z, F, -A, u, -o, i, -c }, \ + { d, -h, l, -p, t, -x, B, -F, C, -y, u, -q, m, -i, e, -a, -c, g, -k, o, -s, w, -A, E, -D, z, -v, r, -n, j, -f, b }, \ + { b, -d, f, -h, j, -l, n, -p, r, -t, v, -x, z, -B, D, -F, E, -C, A, -y, w, -u, s, -q, o, -m, k, -i, g, -e, c, -a }, \ +} + +const int8_t ff_vvc_dst7_4x4[4][4] = DEFINE_DST7_P4_MATRIX (29, 55, 74, 84); +const int8_t ff_vvc_dst7_8x8[8][8] = DEFINE_DST7_P8_MATRIX (17, 32, 46, 60, 71, 78, 85, 86); +const int8_t ff_vvc_dst7_16x16[16][16] = DEFINE_DST7_P16_MATRIX( 8, 17, 25, 33, 40, 48, 55, 62, 68, 73, 77, 81, 85, 87, 88, 88); +const int8_t ff_vvc_dst7_32x32[32][32] = DEFINE_DST7_P32_MATRIX( 4, 9, 13, 17, 21, 26, 30, 34, 38, 42, 46, 50, 53, 56, 60, 63, 66, 68, 72, 74, 77, 78, 80, 82, 84, 85, 86, 87, 88, 89, 90, 90); + +const int8_t ff_vvc_lfnst_8x8[4][2][16][48] = { + { //0 + { + { -117, 28, 18, 2, 4, 1, 2, 1, 32, -18, -2, 0, -1, 0, 0, 0, 14, -1, -3, 0, -1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, -1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 }, + { -29, -91, 47, 1, 9, 0, 3, 0, -54, 26, -8, 3, 0, 1, 0, 0, 33, 5, -9, -1, -2, 0, -1, 0, -3, 3, 0, 0, 0, 0, 0, 0, 7, 2, -2, 0, -1, 1, 0, 0, 2, 1, -1, 0, 0, 0, 0, 0 }, + { -10, 62, -11, -8, -2, -2, -1, -1, -95, 3, 32, 0, 4, 0, 2, 0, 32, -30, -4, 4, -1, 1, 0, 0, 6, 2, -5, 0, 0, 0, 0, 0, 6, -3, 0, 0, 2, 0, -1, 0, 2, -1, 0, 0, 1, 0, 0, 0 }, + { -15, 15, -10, -2, 1, 0, 1, 0, 10, 112, -20, -17, -4, -4, -1, -2, -20, -26, 31, 1, 0, 0, 0, 0, 2, -16, -1, 6, 0, 1, 0, 0, 1, -4, 0, 0, 0, -3, 0, 1, 0, -1, 0, 0, 0, -2, 0, 0 }, + { 32, 39, 92, -44, 4, -10, 1, -4, 26, 12, -15, 13, -5, 2, -2, 0, 29, -16, -22, 8, 0, 1, 0, 1, -20, 6, 4, -3, 1, 0, 0, 0, 1, -4, -3, 2, -4, 1, 0, 0, 1, -1, -2, 1, -2, 0, 0, 0 }, + { -10, 1, 50, -15, 2, -3, 1, -1, -28, -15, 14, 6, 1, 1, 1, 0, -99, -4, 9, 5, 5, 2, 2, 1, 44, -10, -11, 1, -2, 0, -1, 0, -5, 4, -3, 0, 8, -1, -2, 0, -2, 1, -1, 0, 4, 0, -1, 0 }, + { 1, -33, -11, -14, 7, -2, 2, 0, 29, -12, 37, -7, -4, 0, -1, 0, 6, -99, 3, 26, -1, 5, 0, 2, 14, 30, -27, -2, 1, -1, 0, -1, -6, 6, 6, -3, 1, 3, -3, 0, -1, 1, 1, 0, 0, 1, -1, 0 }, + { 0, 6, -6, 21, -4, 2, 0, 0, -20, -24, -104, 30, 5, 5, 1, 2, -7, -46, 10, -14, 7, 0, 1, 0, 9, 21, 7, -6, -2, -1, 0, -1, 2, 2, 5, -2, 0, 3, 4, -1, 0, 0, 1, 0, 0, 1, 2, -1 }, + { -13, -13, -37, -101, 29, -11, 8, -3, -12, -15, -20, 2, -11, 5, -2, 1, -12, 10, 26, 12, -6, 0, -1, 0, -32, -2, 11, 3, 3, -1, 1, 0, 11, -5, -1, 6, -4, 2, 1, 0, 3, -1, 1, 2, -1, 0, 0, 0 }, + { 6, 1, -14, -36, 9, -3, 2, 0, 10, 9, -18, -1, -3, 1, 0, 0, 38, 26, -13, -1, -5, -1, -1, 0, 102, 3, -14, -1, -5, -1, -2, 0, -29, 10, 10, 0, 10, -4, -1, 1, -7, 1, 2, 1, 2, -1, 0, 0 }, + { -12, -2, -26, -12, -9, 2, -1, 1, -3, 30, 4, 34, -4, 0, -1, 0, -30, 3, -92, 14, 19, 0, 3, 0, -11, 34, 21, -33, 1, -2, 0, -1, -9, -4, 18, 3, 2, 0, 0, -2, -1, -1, 3, 0, 0, 0, 0, -1 }, + { 0, -3, 0, -4, -15, 6, -3, 1, -7, -15, -28, -86, 19, -5, 4, -1, -5, -17, -41, 42, -6, 2, -1, 1, -1, -40, 37, 13, -4, 2, -1, 1, -10, 13, -1, -4, 4, -4, 3, 4, -2, 2, -1, -1, 1, -1, 1, 2 }, + { -1, 9, 13, 5, 14, -2, 2, -1, -8, 3, -4, -62, 4, 1, 1, 0, -12, 23, 16, -11, -17, 0, -1, 0, -11, 97, -3, -3, 0, -6, 0, -2, -21, -5, 23, 0, 2, -2, -1, 6, -3, -3, 1, 0, 0, 0, 0, 2 }, + { 6, 2, -3, 2, 10, -1, 2, 0, 8, 3, -1, -20, 0, 1, 0, 0, -4, 4, -16, 0, -2, 0, 1, 0, 34, 23, 6, -7, -4, -2, -1, 0, 108, -5, -30, 6, -27, 10, 7, -2, 11, -3, -1, 1, -4, 1, 0, 1 }, + { 6, 9, -2, 35, 110, -22, 11, -4, -2, 0, -3, 1, -18, 12, -3, 2, -5, -4, -22, 8, -25, 3, 0, 0, -3, -21, 2, -3, 9, -2, 1, 0, -7, 1, 3, -5, 3, 0, -1, 0, 0, 1, 0, -1, 1, 0, 0, 0 }, + { -1, 7, -2, 9, -11, 5, -1, 1, -7, 2, -22, 4, -13, 0, -1, 0, 0, 28, 0, 76, 4, -6, 0, -2, -13, 5, -76, -4, 33, -1, 3, 0, 9, 18, -3, -35, -4, -1, 6, 1, 1, 2, 0, -3, -1, 0, 2, 0 }, + }, + { + { -108, 48, 9, 1, 1, 1, 0, 0, 44, -6, -9, -1, -1, 0, -1, 0, 9, -9, -1, 1, 0, 0, 0, 0, 3, -1, 1, 0, 0, 0, 0, 0, 1, -1, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0 }, + { 55, 66, -37, -5, -6, -1, -2, 0, 67, -30, -20, 4, -2, 0, -1, 0, -31, -19, 14, 4, 1, 1, 1, 0, -6, 3, 5, -2, 0, 0, 0, 0, -7, -1, 1, 0, -1, 1, 1, 0, -2, -1, 1, 0, 0, 0, 0, 0 }, + { 2, 86, -21, -13, -4, -2, -1, -1, -88, 5, 6, 4, 5, 1, 1, 0, 14, -5, 0, 3, 0, 0, 0, 0, 10, -5, -2, 0, -1, 0, 0, 0, 6, -5, 0, 1, 2, -1, 0, 0, 1, -1, 0, 0, 1, 0, 0, 0 }, + { -24, -21, -38, 19, 0, 4, -1, 2, -23, -89, 31, 20, 2, 3, 1, 1, -30, 26, 36, -8, -2, -2, 0, -1, 14, 18, -7, -9, -1, -1, 0, 0, 1, 3, -2, -1, 3, 2, -2, -1, 0, 1, 0, 0, 1, 1, -1, 0 }, + { 9, 20, 98, -26, -3, -5, 0, -2, -9, -26, 15, -16, 2, 0, 1, 0, -61, -3, -2, 3, 7, 1, 1, 0, 12, 16, -6, -1, 0, -1, 0, 0, 2, 0, -8, 1, 3, 1, -1, 1, 0, -1, -2, 0, 1, 0, -1, 0 }, + { -21, -7, -37, 10, 2, 2, -1, 1, -10, 69, -5, -7, -2, -2, 0, -1, -93, 2, 19, 0, 3, 0, 2, 0, 17, 4, 0, 0, -1, 0, 0, 0, 5, -4, -2, 0, 4, -2, 0, 1, 0, 0, 0, 0, 2, -1, 0, 0 }, + { -10, -25, 4, -17, 8, -2, 2, -1, -27, -17, -71, 25, 8, 2, 1, 1, -4, -66, 28, 36, -5, 3, 0, 1, -10, 20, 33, -13, -8, 0, 0, -1, 3, 6, -3, -7, -1, 3, 3, -1, 1, 0, -1, 0, 0, 1, 1, -1 }, + { 2, 5, 10, 64, -9, 4, -3, 1, -4, 8, 62, 3, -17, 1, -2, 0, -3, -75, 5, -14, 1, 4, 0, 1, -36, 3, 18, -4, 4, 0, 1, 0, 1, 14, -2, -8, -2, 1, -3, 0, 2, 2, -1, -2, 0, 1, -1, 0 }, + { -11, -15, -28, -97, 6, -1, 4, -1, 7, 3, 57, -15, 10, -2, 0, -1, -1, -27, 13, 6, 1, -1, 0, 0, -34, -6, 0, 3, 4, 1, 2, 0, -2, 8, 1, 5, -2, 0, -3, 1, 1, 1, 0, 2, -1, 0, -1, 0 }, + { 9, 13, 24, -6, 7, -2, 1, -1, 16, 39, 20, 47, -2, -2, -2, 0, 28, 23, 76, -5, -25, -3, -3, -1, 6, 36, -7, -39, -4, -1, 0, -1, 2, -4, -18, -3, -1, -1, -2, -2, 1, -2, -2, 0, 0, 0, -1, -1 }, + { -7, 11, 12, 7, 2, -1, 0, -1, -14, -1, -24, 11, 2, 0, 0, 0, -20, 48, 11, -13, -5, -2, 0, -1, -105, -19, 17, 0, 6, 2, 3, 0, -14, 8, 8, 2, 1, 2, -1, -2, 3, 0, -1, 0, 0, 0, 0, 0 }, + { 0, 0, 7, -6, 23, -3, 3, -1, 5, 1, 18, 96, 13, -9, -1, -1, -21, -7, -42, 14, -24, -3, 0, 0, 11, -47, -7, 3, -5, 9, 1, 2, 0, -1, 19, -1, 1, 0, -1, -6, -1, 1, 2, 0, 1, 0, 0, -2 }, + { -2, -6, -1, -10, 0, 1, 1, 0, -7, -2, -28, 20, -15, 4, -3, 1, -2, -32, -2, -66, 3, 7, 1, 2, -11, 13, -70, 5, 43, -2, 3, 0, 8, -14, -3, 43, -1, 2, 7, -1, 1, -2, 1, 3, -1, 1, 1, 0 }, + { -1, 6, -16, 0, 24, -3, 1, -1, 2, 6, 6, 16, 18, -7, 1, -1, -3, 11, -63, 9, 4, -5, 2, -1, -22, 94, -4, -6, -4, -4, 1, -2, 10, 23, -19, -5, 0, -6, -4, 6, 3, -2, 1, 1, 0, -1, 0, 0 }, + { -5, -6, -3, -19, -104, 18, -4, 3, 0, 6, 0, 35, -41, 20, -2, 2, -2, 10, -18, 16, 21, 3, -2, 0, -2, 11, 6, -10, 6, -3, -1, 0, -1, 5, -1, -6, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, -1 }, + { -1, -2, 0, 23, -9, 0, -2, 0, 1, 1, 8, -1, 29, 1, 1, 0, 3, -6, 13, 76, 30, -11, -1, -2, -26, -8, -69, 7, -9, -7, 3, -1, -10, -34, -25, 13, -1, 0, 11, 5, 1, -1, 1, -2, 0, 0, 2, 0 }, + } + }, + { //1 + { + { 110, -49, -3, -4, -1, -1, 0, -1, -38, -1, 10, 0, 2, 0, 1, 0, -9, 13, 1, -2, 0, 0, 0, 0, -4, 2, -3, 0, 0, 0, 0, 0, -2, 2, 0, 1, -1, 1, 0, 0, -1, 1, 0, 0, -1, 0, 0, 0 }, + { -43, -19, 17, -1, 3, 0, 1, 0, -98, 46, 14, -1, 2, 0, 1, 0, 26, 26, -15, -3, -2, -1, -1, 0, 11, -7, -9, 2, 0, 0, 0, 0, 9, -3, -1, 2, 3, -3, 0, 0, 4, -1, 0, 0, 2, -1, 0, 0 }, + { -19, 17, -7, 3, -2, 1, -1, 0, -32, -59, 29, 3, 4, 0, 2, 0, -72, 43, 34, -9, 3, -2, 1, -1, 13, 36, -18, -10, 0, -2, 0, -1, 3, 0, -12, 3, 6, 1, -3, 2, 1, -1, -2, 0, 3, 1, -1, 1 }, + { -35, -103, 39, 1, 7, 0, 2, 0, 38, -13, 25, -6, 1, -1, 0, 0, -1, 7, 6, -7, 1, -1, 0, 0, -13, 14, 2, -4, 2, -1, 0, 0, -2, 11, -6, -2, -2, 4, -3, 0, 0, 3, -2, 0, -1, 1, -1, 0 }, + { 9, 5, -6, -1, -1, 0, -1, 0, 42, 4, 21, -11, 1, -3, 1, -1, 21, 70, -32, -21, 0, -4, -1, -1, 34, -26, -57, 11, 4, 2, 0, 1, -4, -32, 5, 24, 1, -6, 12, 4, -3, -2, 4, -2, 0, -1, 0, 0 }, + { -5, -5, -28, 9, -3, 2, -1, 1, -20, -78, 22, 16, 1, 3, 0, 1, 80, -6, 25, -5, -4, -1, -1, 0, 6, -24, 7, -9, 0, 0, 0, 0, -7, 3, 13, -4, -3, 5, 1, -5, -2, 3, 1, -2, -1, 2, -1, -2 }, + { 14, 17, 27, -12, 1, -3, 1, -1, 8, 19, -13, 4, -2, 1, -1, 0, 48, -1, 48, -15, -4, -2, -1, -1, 1, 60, -28, -42, 5, -6, 1, -2, 11, -11, -51, 11, -2, -10, -2, 13, 2, -6, -4, 4, -2, -3, 2, 2 }, + { 7, 35, 17, -4, -1, 0, 0, 0, 3, 8, 54, -17, 1, -2, 1, -1, 10, 14, -11, -34, 4, -4, 1, -1, -80, -7, -6, 2, 15, 0, 3, 0, -16, 46, 1, 3, 2, 7, -24, 0, 2, -2, -5, 8, 1, -1, -2, 2 }, + { -13, -27, -101, 24, -8, 6, -3, 2, 11, 43, 6, 28, -6, 3, -1, 1, -3, 14, 21, -12, -7, -2, -1, -1, -23, 10, -4, -12, 3, 0, 1, 0, 2, 9, -10, 0, 1, -5, -4, 4, 2, -2, 2, 2, 0, -2, 1, 0 }, + { -11, -13, -3, -10, 3, -1, 1, 0, -19, -19, -37, 8, 4, 2, 0, 1, -12, -30, 3, -9, 5, 0, 1, 0, -56, -9, -47, 8, 21, 1, 4, 1, -11, -30, 10, 59, -2, 8, 41, 8, 2, 5, 6, -7, -1, 3, 5, -2 }, + { -4, -10, -24, -11, 3, -2, 0, -1, -6, -37, -45, -17, 8, -2, 2, -1, 17, 14, -58, 14, 15, 0, 2, 0, -10, 34, -7, 28, 4, -1, 1, 0, 23, 34, -31, 4, 10, -22, -30, 22, 4, -15, 9, 20, 2, -5, 9, 4 }, + { -2, 1, 13, -17, 3, -5, 1, -2, 3, 0, -55, 22, 6, 1, 1, 0, 8, 74, 21, 40, -14, 0, -2, 0, -36, -8, 11, -13, -23, 1, -3, 0, -36, 6, 16, -14, 2, 19, -4, -12, -1, 0, -7, -3, 0, 2, -2, -1 }, + { 3, 1, 5, -15, 1, -2, 1, -1, 7, 4, -7, 29, -1, 2, -1, 1, 8, 3, 12, -14, -9, -1, -1, 0, 4, 29, -15, 31, 10, 4, 1, 1, 61, 22, 55, 14, 13, 3, -9, -65, 1, -11, -21, -7, 0, 0, -1, 3 }, + { -4, -8, -1, -50, 6, -4, 2, -2, -1, 5, -22, 20, 6, 1, 0, 0, -16, -15, 18, -29, -11, 2, -2, 1, 40, -45, -19, -22, 31, 2, 4, 1, -25, 41, 0, 12, 9, 7, -42, 12, -3, -14, 2, 28, 5, 1, 6, 2 }, + { 5, -1, 26, 102, -13, 12, -4, 4, -4, -2, -40, -7, -23, 3, -5, 1, -1, 5, 8, -23, 7, 2, 1, 1, 10, -11, -13, -3, 12, -3, 2, 0, -9, 23, 4, 9, 14, 9, -14, -4, 0, -12, -7, 6, 3, 0, 6, 3 }, + { -5, -6, -27, -22, -12, 0, -3, 0, -5, 8, -20, -83, 0, 0, 0, 0, 9, 7, 24, -20, 41, 3, 6, 1, 15, 20, 12, 11, 17, -9, 1, -2, -26, -1, 18, -1, -12, 32, 3, -18, -5, 10, -25, -5, -2, 1, -8, 10 }, + }, + { + { 80, -49, 6, -4, 1, -1, 1, -1, -72, 36, 4, 0, 1, 0, 0, 0, 26, 0, -12, 2, -2, 1, -1, 0, -7, -9, 6, 1, 0, 0, 0, 0, 3, 5, -1, -2, -2, -2, -1, 1, 1, 1, 0, 0, -1, -1, 0, 0 }, + { -72, -6, 17, 0, 3, 0, 1, 0, -23, 58, -21, 2, -3, 1, -1, 0, 55, -46, -1, 6, -2, 1, -1, 0, -22, 7, 17, -7, 2, -1, 1, 0, 9, 5, -12, 1, -3, -4, 4, 2, 4, 1, -2, -1, -1, -1, 1, 0 }, + { -50, 19, -15, 4, -1, 1, -1, 1, -58, -2, 30, -3, 4, -1, 2, 0, 6, 57, -34, 0, -2, 0, -1, 0, 34, -48, -2, 14, -4, 3, -1, 1, -10, 7, 21, -10, 6, 1, -11, 0, -1, -1, 4, 2, 3, 0, -2, -1 }, + { -33, -43, 28, -7, 4, -2, 2, -1, -38, 11, -8, 4, 1, 1, 0, 0, -55, 24, 26, -5, 2, -1, 1, 0, 15, 46, -40, -1, -1, 0, -1, 0, 17, -38, 1, 17, -3, 11, 15, -11, 3, -1, -10, 1, 0, 1, 3, 2 }, + { 10, 66, -21, -3, -3, 0, -1, 0, -53, -41, -2, 16, -1, 4, -1, 1, 36, -5, 41, -20, 3, -3, 1, -1, -30, 26, -32, -3, 7, -2, 2, -1, 15, -8, 1, 17, -1, -2, 4, -8, 2, 0, -1, 3, 0, 0, 0, -1 }, + { 18, 14, 13, -9, 2, -2, 1, -1, 34, 32, -31, 12, -5, 2, -2, 1, 40, 4, -4, -9, -3, -2, -1, -1, 27, -31, -43, 19, -2, 3, -1, 1, 7, -49, 52, 10, -11, 22, 7, -26, -1, -6, -9, 6, -2, 2, 4, -2 }, + { 21, 66, -1, 9, -4, 2, -1, 1, -21, 41, -30, -10, 0, -2, 0, -1, -35, -17, -3, 26, -6, 5, -2, 2, 56, 3, 18, -25, -1, -2, -1, -1, -15, -13, -27, 9, 9, -6, 20, 5, -3, 2, -6, -9, 3, -3, 1, 5 }, + { 1, -6, -24, 17, -5, 3, -2, 1, 24, 10, 39, -21, 5, -4, 2, -1, 33, 32, -30, 4, -3, -1, -1, 0, -4, 13, -16, -10, 0, -1, 0, 0, 24, -26, -37, 33, 5, -32, 55, -5, -7, 22, -14, -22, 1, -9, -3, 13 }, + { 9, 33, -24, 1, 4, 0, 1, 0, 6, 50, 26, 1, -10, 0, -2, 0, -27, 1, -28, -21, 16, -5, 3, -2, -23, 36, -2, 40, -17, 4, -3, 1, 43, -13, 4, -41, -19, -2, -24, 17, 11, -4, 8, 4, -3, -3, -3, -3 }, + { -7, -9, -32, 14, -3, 3, -1, 1, -23, -28, 0, -5, -1, 0, 0, 0, -36, -59, -24, 14, 4, 2, 1, 1, -23, -26, 23, 26, -3, 5, 0, 2, 10, -26, 38, 7, -12, 11, 42, -22, -5, 20, -14, -15, -1, -2, 1, 6 }, + { 6, 30, 69, -18, 5, -4, 3, -1, -3, -11, -34, -16, 9, -4, 2, -1, -16, 35, -35, 30, -9, 3, -2, 1, -57, -13, 6, 4, -5, 5, -1, 1, 28, 10, 4, 7, 0, -15, 7, -10, -1, 7, -2, 2, 1, -3, 0, 0 }, + { 1, -8, 24, -3, 7, -2, 2, -1, -6, -51, -6, -4, -5, 0, -1, 0, 38, -1, 0, 25, 6, 2, 1, 1, 47, 20, 35, 1, -27, 1, -5, 0, 37, -37, -9, -47, -28, 5, 0, 18, 8, 6, 0, -8, -4, -3, -3, 1 }, + { 4, 10, 4, 17, -9, 4, -2, 1, 5, 14, 32, -15, 9, -3, 2, -1, 7, 13, 19, 15, -8, 1, -1, 0, 3, 25, 30, -18, 1, -2, 0, -1, 11, 24, 22, -11, -3, 37, -13, -58, -5, 12, -63, 26, 9, -15, 11, 8 }, + { -3, -9, -23, 10, -10, 3, -3, 1, -5, -14, -16, -27, 13, -5, 2, -1, -1, -13, -30, 11, -5, 2, -1, 0, -5, -8, -22, -16, 10, 0, 1, 0, 0, -29, -27, 6, -27, -10, -30, 9, -3, -10, -7, 77, 9, -13, 45, -8 }, + { 2, 11, 22, 2, 9, -2, 2, 0, -6, -7, 20, -32, -3, -4, 0, -1, 13, -5, -28, 6, 18, -4, 3, -1, -26, 27, -14, 6, -20, 0, -2, 0, -76, -26, -4, -7, 12, 51, 5, 24, 7, -17, -16, -12, -5, 4, 2, 13 }, + { 2, -3, 8, 14, -5, 3, -1, 1, -2, -11, 5, -18, 8, -3, 2, -1, 12, -23, -19, 22, 2, 0, 1, 0, 23, 41, -7, 35, -10, 4, -1, 1, 5, 7, 23, 5, 69, -38, -8, -32, -15, -31, 24, 11, 2, 18, 11, -15 }, + } + }, + { //2 + { + { -121, 33, 4, 4, 1, 2, 0, 1, -1, -1, 1, 0, 0, 0, 0, 0, 24, -5, -1, -1, 0, 0, 0, 0, 5, -1, 0, 0, 0, 0, 0, 0, 3, -1, 0, 0, 2, -1, 0, 0, 2, -1, 0, 0, 1, 0, 0, 0 }, + { 0, -2, 0, 0, 0, 0, 0, 0, 121, -23, -7, -3, -2, -1, -1, 0, 17, 1, -2, 0, 0, 0, 0, 0, -27, 4, 2, 0, 0, 0, 0, 0, -12, 2, 1, 0, -5, 1, 0, 0, -1, 0, 0, 0, -2, 0, 0, 0 }, + { -20, 19, -5, 2, -1, 1, 0, 0, 16, 3, -2, 0, 0, 0, 0, 0, -120, 14, 8, 1, 3, 1, 1, 0, -18, -2, 3, 0, 1, 0, 0, 0, 17, -3, -1, 0, 6, -1, -1, 0, 2, 0, 0, 0, 2, 0, 0, 0 }, + { 32, 108, -43, 10, -9, 3, -3, 1, 4, 19, -7, 1, -1, 0, 0, 0, 11, -30, 9, -2, 1, -1, 0, 0, 0, -8, 2, 0, 0, 0, 0, 0, -7, -1, 2, 0, -3, -1, 1, 0, -2, -2, 1, 0, 0, 0, 0, 0 }, + { -3, 0, -1, 0, 0, 0, 0, 0, -29, 11, -2, 1, 0, 0, 0, 0, 12, 7, -1, 0, 0, 0, 0, 0, -117, 12, 9, 1, 3, 0, 1, 0, -32, -3, 3, 0, 12, -2, -1, 0, 7, 0, 0, 0, 1, 0, 0, 0 }, + { -4, -12, -3, 1, -1, 0, 0, 0, 19, 105, -31, 7, -6, 1, -2, 0, 9, 46, -6, 0, 0, 0, 0, 0, 8, -29, 9, -3, 1, 0, 0, 0, -3, -19, 3, 0, -4, -6, 1, 0, 0, 0, 0, 0, 0, -1, 0, 0 }, + { 7, 1, 2, 0, 0, 0, 0, 0, 4, 3, -2, 0, 0, 0, 0, 0, 22, -8, 1, -1, 0, 0, 0, 0, -28, -9, 4, 0, 1, 0, 0, 0, 117, -10, -8, 0, 32, 1, -4, 0, 3, 1, -1, 0, -3, 1, 0, 0 }, + { -8, -31, 14, -4, 3, -1, 1, 0, 9, 43, 0, 1, -1, 0, 0, 0, -13, -105, 17, -2, 2, 0, 0, 0, -8, -25, -3, 0, 0, 0, 0, 0, -7, 32, -5, 1, -1, 4, 0, 0, 2, -1, 0, 0, 1, 0, -1, 0 }, + { -15, -43, -100, 23, -12, 6, -4, 2, -6, -17, -48, 10, -5, 2, -1, 1, 1, -5, 19, -6, 3, -1, 1, 0, 2, 7, 15, -3, 1, -1, 0, 0, 4, 10, 5, -1, 0, 3, 1, 0, -2, 1, 2, 0, -1, 1, 1, 0 }, + { -3, 1, 2, 0, 0, 0, 0, 0, -6, 3, 1, 0, 0, 0, 0, 0, 0, 3, -2, 0, 0, 0, 0, 0, -20, 8, -2, 0, 0, 0, 0, 0, 30, 13, -3, 0, -116, 6, 10, 0, -35, -5, 4, 0, -3, -1, 0, 0 }, + { -1, -6, -3, 2, -1, 0, 0, 0, -6, -35, 9, 0, 2, 0, 0, 0, 1, -6, 11, -2, 2, 0, 1, 0, -9, -100, 17, -1, 1, 0, 0, 0, -10, -63, 1, 2, -17, 3, -4, 0, -1, 9, -1, 0, 3, 4, -1, 0 }, + { -5, -14, -48, 2, -5, 1, -2, 0, 10, 24, 99, -17, 10, -4, 3, -1, 4, 14, 32, 0, 2, 0, 1, 0, -4, 0, -39, 6, -4, 1, -1, 0, 2, -3, -4, 0, 2, -2, -2, 0, 0, 0, -1, 0, 0, -1, -1, 0 }, + { -2, 0, 2, 0, 0, 0, 0, 0, -2, 0, 1, 0, 0, 0, 0, 0, -1, -1, 1, -1, 0, 0, 0, 0, -1, -4, 2, 0, 0, 0, 0, 0, -8, -2, -1, 1, 30, 4, -4, 1, -102, 4, 8, -1, -69, -2, 6, -1 }, + { -2, -10, -4, 0, 0, 0, 0, 0, 3, 11, -1, -1, 0, 0, 0, 0, -6, -40, -15, 6, -2, 1, 0, 0, 5, 57, -6, 2, 0, 0, 0, 0, 1, -95, 18, -6, -10, -34, -2, 0, -4, 17, -2, 0, 0, 2, 1, 0 }, + { -2, -3, -25, -2, -3, 0, -1, 0, -1, -3, -1, 4, -2, 2, 0, 1, -7, -8, -97, 17, -9, 3, -3, 1, -8, -26, -61, -1, -3, -1, -1, -1, 2, 10, 24, -7, 5, 9, 19, -1, 0, 1, 4, 0, -2, 0, 1, 0 }, + { 4, -4, 28, 103, -42, 24, -9, 7, 1, 2, 4, 0, 3, -1, 0, 0, -1, 0, -9, -42, 17, -9, 3, -2, -1, 1, -14, 6, -4, 2, -1, 0, -1, -2, -4, 4, 0, 3, 1, -1, 0, 2, 0, -2, 2, 0, 0, 0 }, + }, + { + { 87, -41, 3, -4, 1, -1, 0, -1, -73, 28, 2, 1, 1, 1, 0, 0, 30, -5, -6, 1, -1, 0, 0, 0, -8, -3, 3, 0, 0, 0, 0, 0, 3, 2, -1, 0, -2, -1, 0, 0, 1, 1, 0, 0, -1, 0, 0, 0 }, + { -75, 4, 7, 0, 2, 0, 1, 0, -41, 36, -7, 3, -1, 1, 0, 0, 72, -29, -2, 0, -1, 0, -1, 0, -37, 6, 7, -2, 1, 0, 0, 0, 12, 3, -4, 0, -3, -2, 1, 0, 4, 0, 0, 0, -1, 0, 0, 0 }, + { 26, -44, 22, -6, 4, -2, 1, -1, 77, 24, -22, 2, -4, 0, -1, 0, 7, -38, 10, 0, 1, 0, 0, 0, -51, 27, 4, -3, 2, -1, 1, 0, 31, -5, -8, 3, -14, 0, 5, -1, 6, 1, -3, 0, -4, -1, 1, 0 }, + { -39, -68, 37, -7, 6, -2, 2, 0, -9, 56, -21, 1, -2, 0, -1, 0, -45, 4, -3, 6, -1, 2, 0, 1, 49, -13, 3, -3, -1, 0, 0, 0, -19, 2, 0, 0, 5, 1, 1, 0, -2, 0, -1, 0, 1, 0, 0, 0 }, + { 10, -20, 2, 0, 1, 0, 0, 0, 50, -1, 8, -5, 1, -1, 0, 0, 66, 17, -24, 4, -3, 1, -1, 0, 13, -49, 15, 1, 0, 0, 0, 0, -53, 34, 6, -5, 30, -7, -11, 3, -11, -2, 5, 1, 4, 2, -1, -1 }, + { -21, -45, 8, -2, 3, -1, 1, 0, -7, -30, 26, -8, 3, -1, 1, -1, -9, 69, -33, 5, -2, 0, -1, 0, -44, -31, 10, 7, -2, 2, 0, 1, 49, 7, 2, -6, -23, -3, -2, 2, 9, 4, 0, 0, -2, -1, -1, 0 }, + { -4, -2, -55, 28, -8, 5, -3, 2, -2, 37, 43, -19, 1, -2, 1, -1, -47, -34, -27, 5, 4, -1, 1, 0, -39, -2, 27, 4, -2, 1, 0, 0, -11, 32, -8, -7, 27, -12, -6, 6, -13, 0, 4, -3, 3, -1, -2, 1 }, + { 2, 19, 47, -23, 6, -4, 2, -1, -23, -22, -44, 17, -2, 2, -1, 0, -33, 3, 22, -2, -4, 1, -1, 0, -58, -17, 6, -6, 7, -1, 1, 0, -23, 40, -2, 5, 43, -11, -8, -1, -18, -4, 5, 2, 4, 3, 0, -1 }, + { -19, -62, -9, 3, 0, 0, 0, 0, -12, -56, 27, -7, 3, -1, 1, 0, 7, -8, 16, -6, 4, -2, 1, -1, -15, 54, -23, 2, -1, 0, 0, 0, -42, -25, 4, 6, 34, 8, 2, -2, -15, -1, 0, -1, 3, 2, 0, 1 }, + { 1, 9, -5, 0, -1, 0, 0, 0, 0, 22, -1, 2, 0, 1, 0, 0, -13, 17, 0, -2, 0, -1, 0, 0, -46, -10, -10, 4, -1, 1, 0, 0, -80, -27, 20, -4, -66, 23, -2, -2, 20, -3, -2, 3, -14, 2, 3, -1 }, + { 5, 17, -9, 0, -2, 1, 0, 0, 13, 54, -2, 7, -1, 1, 0, 0, 4, 51, -3, -6, -1, -1, 0, 0, -20, 6, -34, 9, -2, 2, -1, 0, 16, -52, 28, 1, 59, 15, -8, -5, -28, -7, 2, 2, 10, 3, 0, -1 }, + { 7, 27, 56, -2, 10, -3, 3, -1, -2, -6, 8, -28, 3, -4, 1, -1, -1, -4, -68, 35, -5, 5, -2, 1, 0, 35, 43, -4, -6, 1, -1, 0, -14, -38, -12, -10, 9, 5, 7, 6, -9, 7, -4, -3, 4, -4, 0, 3 }, + { 0, 0, 19, -4, 3, -2, 2, -1, -3, -13, 10, -4, 1, 0, 0, 0, -6, -37, -18, -5, 2, -2, 1, -1, 6, -6, -7, 25, -6, 4, -1, 1, 16, 10, 55, -24, 15, 46, -52, 1, 35, -43, 10, 12, -23, 13, 5, -8 }, + { -3, 0, -27, -80, 40, -16, 6, -4, 4, 3, 31, 61, -22, 7, -1, 1, -4, -7, -26, -6, -10, 6, -4, 1, 3, 8, 14, -18, 15, -5, 2, -1, -2, -4, -1, 13, 0, 2, -4, -3, 3, -1, 2, 1, -2, 0, -2, -1 }, + { 1, 2, -8, 6, -1, 1, 0, 0, 2, 8, -5, -1, 0, 0, 0, 0, 1, 24, 3, 5, -1, 1, 0, 0, -3, 12, 6, -10, 1, -1, 0, 0, -9, -1, -25, 10, 45, -11, 18, 2, 86, 1, -13, -4, -65, -6, 7, 2 }, + { -4, -18, -57, 8, -8, 1, -3, 0, -5, -20, -69, 7, -6, 2, -2, 1, 1, 4, 0, 33, -7, 5, -2, 1, 0, -9, 53, -22, 3, -1, 0, 0, 4, -27, -2, -9, 5, 36, -13, 5, -7, -17, 1, 2, 4, 6, 4, -1 }, + } + }, + { //3 + { + { -115, 37, 9, 2, 2, 1, 1, 0, 10, -29, 8, 0, 1, 0, 1, 0, 23, -8, -8, 1, -1, 0, 0, 0, 3, 3, -2, -1, 0, 0, 0, 0, 4, 0, 0, -1, 1, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0 }, + { 15, 51, -18, 0, -3, 0, -1, 0, -95, 7, 34, -3, 5, -1, 2, 0, 23, -47, 1, 6, 0, 1, 0, 1, 8, 5, -12, 0, -1, 0, 0, 0, 3, -3, 1, -1, 2, 1, -2, 0, 1, -1, 0, 0, 1, 1, -1, 0 }, + { 29, -22, 16, -6, 3, -2, 1, -1, -4, -80, 12, 15, 0, 3, 0, 1, 45, 7, -59, 7, -2, 1, -1, 0, -15, 41, -3, -16, 2, -3, 0, -1, 1, 0, 7, -2, -3, 6, 1, -2, 0, 0, 1, 0, -1, 2, 0, -1 }, + { -36, -98, 25, 5, 4, 1, 2, 1, -59, 11, -17, 1, 1, 1, 0, 0, 6, -13, 7, -3, 0, 0, 0, 0, 14, -4, -14, 3, -1, 0, 0, 0, 2, 8, -3, -5, 2, 0, 0, 0, 0, 3, 0, -1, 1, 0, 0, 0 }, + { -6, 18, 3, -3, -1, 0, 0, 0, -50, -5, -38, 12, 0, 2, 0, 1, 3, 67, -7, -40, 3, -6, 1, -3, -12, -13, 65, -3, -10, 0, -1, 0, 9, -20, -5, 22, -2, 0, 0, -1, 2, -3, -2, 3, -1, 0, 1, 0 }, + { 4, 15, 52, -13, 5, -3, 2, -1, -17, -45, 16, 24, -2, 4, -1, 2, -87, -8, -14, 7, 8, 1, 2, 0, 23, -35, -6, -3, 1, 1, 0, 0, 2, 5, -17, 0, 3, -1, -1, -5, 0, 1, -4, 0, 1, 0, 0, -2 }, + { -20, -7, -43, 4, 0, 1, -1, 1, -7, 35, 0, 12, -4, 1, -1, 0, -51, -2, -57, 5, 15, 0, 4, 0, 7, 39, 5, -55, 1, -7, 1, -3, 1, -10, 41, 2, 4, -3, -2, 3, -1, -2, 7, 1, 1, -1, -1, 0 }, + { 4, 29, 1, 26, -5, 4, -2, 1, -17, -7, -73, 6, 6, 2, 1, 1, -5, 21, -3, 5, -1, -3, 0, -1, -11, 2, -52, -3, 27, -2, 5, 0, 0, 27, 8, -58, 2, -5, 25, 3, 0, 3, 0, -5, 0, -2, 7, 0 }, + { 12, 13, 10, 2, -1, 3, -1, 1, 17, -2, -46, 12, 7, 0, 2, 0, 16, -45, -9, -53, 6, 1, 1, 0, 70, 16, 8, -4, -37, 1, -7, 0, -12, 29, 3, 21, 4, 0, 5, -1, -3, 4, 1, 4, 2, 0, 1, 0 }, + { 5, 20, 90, -17, 4, -3, 2, -1, 6, 66, 8, 28, -7, 3, -1, 1, 29, 5, -19, 12, 9, -1, 1, 0, -10, 14, -1, -13, 7, 0, 1, 0, 0, -6, 13, -4, 0, -4, 1, 5, 0, -1, -1, 1, 0, -1, 0, 0 }, + { -3, -4, -34, -12, 2, -1, -1, 0, 5, 25, 11, 43, -10, 4, -2, 1, 23, 20, -40, 12, 21, -3, 4, -1, 25, -28, -10, 5, 8, 6, 0, 2, -4, 21, -64, -8, -5, 19, 10, -48, 3, -1, 10, -3, 0, 4, 3, -6 }, + { -1, -3, 2, 19, -2, 4, -1, 2, 9, 3, -35, 22, 11, 1, 2, 0, -7, -65, -19, -22, 11, 4, 2, 1, -75, -18, 3, -1, -10, 2, 0, 1, 2, -35, -27, 4, 1, 8, -17, -19, 3, 0, 3, -6, 0, 2, -1, -2 }, + { 10, -4, -6, 12, 5, 1, 1, 0, 11, -9, -12, -2, -7, 0, -1, 0, 33, -10, -4, 18, 18, -4, 4, -1, 28, -72, 1, -49, 15, 2, 2, 1, 56, -23, 22, -1, 4, -1, -15, 26, 6, 4, -10, 0, 0, 2, -3, 2 }, + { 4, 6, 14, 53, -4, 4, 0, 2, 0, -1, -20, -13, 3, 2, -1, 1, -3, 1, -5, 35, -16, -6, -1, -2, 46, 29, 13, 21, 37, -5, 4, -1, -10, -53, -18, 8, 9, 12, -41, -25, -2, 2, 13, -16, 4, 1, -5, 1 }, + { 2, 9, 13, 37, 19, 6, 2, 2, -9, -3, -9, -28, -20, -4, -3, -1, 1, 18, 9, 28, 24, 6, 2, 2, -20, -5, -25, -33, -36, 9, -2, 2, -13, 42, 1, 57, -22, -2, -25, -28, 5, 6, 19, -12, -5, -3, -2, 4 }, + { 3, -3, 12, 84, -12, 8, -2, 3, 6, 13, 50, -1, 45, 1, 7, 0, -2, 18, -22, -37, -13, 14, 0, 3, 1, -12, -3, 2, -15, -8, 1, -1, 19, 14, -4, -12, -4, 5, 17, 8, 2, -4, -4, 4, -2, 2, 1, 0 }, + }, + { + { 109, -26, -8, -3, -2, -1, -1, 0, -50, 28, 2, 1, 0, 0, 0, 0, -18, -8, 6, 0, 1, 0, 1, 0, 6, -2, -3, 0, 0, 0, 0, 0, -3, 2, 1, -1, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0 }, + { -39, 31, -5, 2, -1, 1, 0, 0, -95, 6, 18, 0, 4, 0, 1, 0, 32, -49, 5, 1, 1, 0, 0, 0, 27, -1, -14, 2, -2, 1, -1, 0, 3, 5, -3, -2, 4, 1, -1, -1, 2, 0, 0, 0, 2, 0, 0, 0 }, + { 29, -3, -2, -2, 0, 0, 0, 0, 0, -41, 9, 0, 2, 0, 1, 0, 86, 4, -33, 2, -6, 1, -2, 0, -32, 58, 1, -7, 0, -2, 0, -1, -14, -8, 20, 0, -2, -3, 0, 4, -1, -1, 0, 0, -1, 1, 0, 0 }, + { 18, 96, -23, 2, -5, 1, -2, 0, -10, 6, 10, -2, 1, -1, 1, 0, -14, 26, 2, -4, 1, -1, 0, 0, -43, -9, 35, -2, 4, -1, 1, 0, 14, -40, 1, 10, 2, 1, -10, 1, 2, -4, -1, -1, 0, 0, -1, 0 }, + { -29, -60, 16, -2, 3, -1, 1, 0, -52, 9, -17, 5, -2, 1, -1, 1, 13, 56, -2, -9, 0, -2, 0, -1, -34, -18, 41, 0, 3, 0, 1, 0, 19, -36, -10, 13, 3, 6, -14, -1, 3, 1, -1, -3, 1, 1, -1, -1 }, + { -23, -5, -15, 5, -2, 1, -1, 1, 2, 79, -13, -4, -2, -1, -1, 0, -9, 1, 5, -1, 1, 0, 0, 0, -4, 49, 2, -14, 1, -3, 0, -1, -31, -14, 56, -1, 13, -37, -4, 20, -2, 2, -10, 0, 2, -4, 0, -1 }, + { -7, -3, 12, -3, 3, -1, 1, 0, -31, -62, 8, 7, 0, 2, 0, 1, -75, 9, -45, 5, -1, 1, -1, 0, 14, 35, 0, -23, 2, -5, 1, -2, 1, -8, 32, -1, 7, -12, -4, 10, 0, 2, -6, -1, 2, 0, 0, -2 }, + { 1, -26, 5, 0, 1, 0, 1, 0, 24, -3, 43, -6, 4, -2, 1, -1, -7, -64, 9, 14, 0, 3, 0, 1, -12, -4, 5, 3, -1, 1, 0, 0, 8, -59, -3, 26, 14, 6, -58, 6, -5, 17, -7, -18, 3, 3, -1, -5 }, + { 11, 14, 6, -3, 1, -1, 1, 0, 10, -7, -9, 3, -2, 1, -1, 0, 22, 21, 1, -21, 2, -4, 1, -2, 92, 1, 53, 0, -9, 1, -2, 0, -21, -11, 1, 40, -5, -4, -24, 5, -4, 5, -6, -5, 0, 0, 0, -3 }, + { -10, -11, -47, 3, -4, 1, -1, 0, 5, 28, 11, -2, -1, 0, 0, 0, -12, -2, -38, 2, 0, 1, 0, 0, 16, 38, 11, -16, -1, -3, 0, -2, 12, -9, -22, 7, -8, 60, 4, -36, -6, -15, 54, 7, 3, -7, -8, 14 }, + { -8, -24, -99, 11, -10, 3, -4, 1, -5, -36, 19, -26, 4, -5, 1, -2, 0, 25, 41, 5, -3, 1, 0, 0, 10, -5, -7, 12, 2, 1, 0, 0, -1, 1, 9, -3, -3, -14, -3, 12, 2, 4, -13, -2, -1, 3, 2, -4 }, + { -5, 1, -1, 0, 1, 0, 0, 0, -10, -14, -6, 8, 0, 1, 0, 0, -17, -2, 7, -5, 3, -1, 0, 0, -16, 13, 3, 31, -1, 6, 0, 2, -93, -15, -46, -3, 23, -19, 0, -47, 8, 4, 8, 3, 2, 3, 0, 0 }, + { 1, 12, -20, 21, -4, 5, -2, 2, -5, -2, -75, 9, -1, 2, -1, 1, -1, -2, -16, -4, 0, -1, 0, 0, -7, 7, -31, 0, 3, 0, 0, 0, 4, 11, -12, 4, -12, 14, -50, -1, -8, 32, -4, -54, 2, 0, 30, -15 }, + { 2, -9, -18, 8, -3, 3, -1, 1, 3, -25, -62, -6, 0, -2, 0, -1, -6, -61, 14, -51, 2, -6, 0, -2, -19, 0, 40, -7, -17, 0, -3, 0, 13, -4, 11, 9, 17, 0, 24, 5, 1, -12, 4, 28, 0, 0, -15, 8 }, + { 4, 9, 39, 18, 0, 2, 0, 1, -6, -16, -22, -37, 5, -5, 1, -2, -5, 15, 63, 9, -16, 0, -3, 0, 18, 42, -18, 27, 15, 1, 3, 1, 12, -34, 9, -24, 4, 28, -2, 4, -11, -4, 30, 2, 5, -13, -4, 18 }, + { -7, -2, 15, -6, 1, -1, 1, -1, -11, -3, 22, -14, 0, -2, 1, -1, -18, -7, 30, -9, -4, 0, -1, 0, -35, 23, 23, 10, -17, 1, -3, 0, -19, 53, 6, 48, -65, 12, -12, 11, -8, -16, 10, -21, -2, -12, 6, 2 }, + } + } +}; + +const int8_t ff_vvc_lfnst_4x4[4][2][16][16] = { + { //0 + { + { 108, -44, -15, 1, -44, 19, 7, -1, -11, 6, 2, -1, 0, -1, -1, 0 }, + { -40, -97, 56, 12, -11, 29, -12, -3, 18, 18, -15, -3, -1, -3, 2, 1 }, + { 25, -31, -1, 7, 100, -16, -29, 1, -54, 21, 14, -4, -7, 2, 4, 0 }, + { -32, -39, -92, 51, -6, -16, 36, -8, 3, 22, 18, -15, 4, 1, -5, 2 }, + { 8, -9, 33, -8, -16, -102, 36, 23, -4, 38, -27, -5, 5, 16, -8, -6 }, + { -25, 5, 16, -3, -38, 14, 11, -3, -97, 7, 26, 1, 55, -10, -19, 3 }, + { 8, 9, 16, 1, 37, 36, 94, -38, -7, 3, -47, 11, -6, -13, -17, 10 }, + { 2, 34, -5, 1, -7, 24, -25, -3, 8, 99, -28, -29, 6, -43, 21, 11 }, + { -16, -27, -39, -109, 6, 10, 16, 24, 3, 19, 10, 24, -4, -7, -2, -3 }, + { -9, -10, -34, 4, -9, -5, -29, 5, -33, -26, -96, 33, 14, 4, 39, -14 }, + { -13, 1, 4, -9, -30, -17, -3, -64, -35, 11, 17, 19, -86, 6, 36, 14 }, + { 8, -7, -5, -15, 7, -30, -28, -87, 31, 4, 4, 33, 61, -5, -17, 22 }, + { -2, 13, -6, -4, -2, 28, -13, -14, -3, 37, -15, -3, -2, 107, -36, -24 }, + { 4, 9, 11, 31, 4, 9, 16, 19, 12, 33, 32, 94, 12, 0, 34, -45 }, + { 2, -2, 8, -16, 8, 5, 28, -17, 6, -7, 18, -45, 40, 36, 97, -8 }, + { 0, -2, 0, -10, -1, -7, -3, -35, -1, -7, -2, -32, -6, -33, -16, -112 }, + }, + { + { 119, -30, -22, -3, -23, -2, 3, 2, -16, 3, 6, 0, -3, 2, 1, 0 }, + { -27, -101, 31, 17, -47, 2, 22, 3, 19, 30, -7, -9, 5, 3, -5, -1 }, + { 0, 58, 22, -15, -102, 2, 38, 2, 10, -13, -5, 4, 14, -1, -9, 0 }, + { 23, 4, 66, -11, 22, 89, -2, -26, 13, -8, -38, -1, -9, -20, -2, 8 }, + { -19, -5, -89, 2, -26, 76, -11, -17, 20, 13, 18, -4, 1, -15, 3, 5 }, + { -10, -1, -1, 6, 23, 25, 87, -7, -74, 4, 39, -5, 0, -1, -20, -1 }, + { -17, -28, 12, -8, -32, 14, -53, -6, -68, -67, 17, 29, 2, 6, 25, 4 }, + { 1, -24, -23, 1, 17, -7, 52, 9, 50, -92, -15, 27, -15, -10, -6, 3 }, + { -6, -17, -2, -111, 7, -17, 8, -42, 9, 18, 16, 25, -4, 2, -1, 11 }, + { 9, 5, 35, 0, 6, 21, -9, 34, 44, -3, 102, 11, -7, 13, 11, -20 }, + { 4, -5, -5, -10, 15, 19, -2, 6, 6, -12, -13, 6, 95, 69, -29, -24 }, + { -6, -4, -9, -39, 1, 22, 0, 102, -19, 19, -32, 30, -16, -14, -8, -23 }, + { 4, -4, 7, 8, 4, -13, -18, 5, 0, 0, 21, 22, 58, -88, -54, 28 }, + { -4, -7, 0, -24, -7, 0, -25, 3, -3, -30, 8, -76, -34, 4, -80, -26 }, + { 0, 6, 0, 30, -6, 1, -13, -23, 1, 20, -2, 80, -44, 37, -68, 1 }, + { 0, 0, -1, 5, -1, -7, 1, -34, -2, 3, -6, 19, 5, -38, 11, -115 }, + } + }, + { //1 + { + { -111, 39, 4, 3, 44, 11, -12, -1, 7, -16, -5, 2, 3, -1, 4, 2 }, + { -47, -27, 15, -1, -92, 43, 20, -2, 20, 39, -16, -5, 10, -5, -13, 2 }, + { -35, -23, 4, 4, -17, -72, 32, 6, -59, 18, 50, -6, 0, 40, 0, -13 }, + { 13, 93, -27, -4, -48, 13, -34, 4, -52, 11, 1, 10, 3, 16, -3, 1 }, + { -11, -27, 1, 2, -47, -4, -36, 10, -2, -85, 14, 29, -20, -2, 57, 4 }, + { 0, -35, 32, -2, 26, 60, -3, -17, -82, 1, -30, 0, -37, 21, 3, 12 }, + { -17, -46, -92, 14, 7, -10, -39, 29, -17, 27, -28, 17, 1, -15, -13, 17 }, + { 4, -10, -23, 4, 16, 58, -17, 26, 30, 21, 67, 2, -13, 59, 13, -40 }, + { 5, -20, 32, -5, 8, -3, -46, -7, -4, 2, -15, 24, 100, 44, 0, 5 }, + { -4, -1, 38, -18, -7, -42, -63, -6, 33, 34, -23, 15, -65, 33, -20, 2 }, + { -2, -10, 35, -19, 5, 8, -44, 14, -25, 25, 58, 17, 7, -84, -16, -18 }, + { 5, 13, 18, 34, 11, -4, 18, 18, 5, 58, -3, 42, -2, -10, 85, 38 }, + { -5, -7, -34, -83, 2, -1, -4, -73, 4, 20, 15, -12, 4, -3, 44, 12 }, + { 0, 4, -2, -60, 5, 9, 42, 34, 5, -14, 9, 80, -5, 13, -38, 37 }, + { -1, 2, 7, -57, 3, -7, 9, 68, -9, 6, -49, -20, 6, -4, 36, -64 }, + { -1, 0, -12, 23, 1, -4, 17, -53, -3, 4, -21, 72, -4, -8, -3, -83 }, + }, + { + { 88, -55, 6, -3, -66, 27, 9, -2, 11, 11, -13, 1, -2, -7, 1, 2 }, + { -58, -20, 27, -2, -27, 75, -29, 0, 47, -42, -11, 11, -9, -3, 19, -4 }, + { -51, 23, -22, 5, -63, 3, 37, -5, 1, 64, -35, -4, 29, -31, -11, 13 }, + { -27, -76, 49, -2, 40, 14, 9, -17, -56, 36, -25, 6, 14, 3, -6, 8 }, + { 19, -4, -36, 22, 52, 7, 36, -23, 28, -17, -64, 15, -5, -44, 48, 9 }, + { 29, 50, 13, -10, 1, 34, -59, 1, -51, 4, -16, 30, 52, -33, 24, -5 }, + { -12, -21, -74, 43, -13, 39, 18, -5, -58, -35, 27, -5, 19, 26, 6, -5 }, + { 19, 38, -10, -5, 28, 66, 0, -5, -4, 19, -30, -26, -40, 28, -60, 37 }, + { -6, 27, 18, -5, -37, -18, 12, -25, -44, -10, -38, 37, -66, 45, 40, -7 }, + { -13, -28, -45, -39, 0, -5, -39, 69, -23, 16, -12, -18, -50, -31, 24, 13 }, + { -1, 8, 24, -51, -15, -9, 44, 10, -28, -70, -12, -39, 24, -18, -4, 51 }, + { -8, -22, -17, 33, -18, -45, -57, -27, 0, -31, -30, 29, -2, -13, -53, 49 }, + { 1, 12, 32, 51, -8, 8, -2, -31, -22, 4, 46, -39, -49, -67, 14, 17 }, + { 4, 5, 24, 60, -5, -14, -23, 38, 9, 8, -34, -59, 24, 47, 42, 28 }, + { -1, -5, -20, -34, 4, 4, -15, -46, 18, 31, 42, 10, 10, 27, 49, 78 }, + { -3, -7, -22, -34, -5, -11, -36, -69, -1, -3, -25, -73, 5, 4, 4, -49 }, + } + }, + { //2 + { + { -112, 47, -2, 2, -34, 13, 2, 0, 15, -7, 1, 0, 8, -3, -1, 0 }, + { 29, -7, 1, -1, -108, 40, 2, 0, -45, 13, 4, -1, 8, -5, 1, 0 }, + { -36, -87, 69, -10, -17, -33, 26, -2, 7, 14, -11, 2, 6, 8, -7, 0 }, + { 28, -5, 2, -2, -29, 13, -2, 0, 103, -36, -4, 1, 48, -16, -4, 1 }, + { -12, -24, 15, -3, 26, 80, -61, 9, 15, 54, -36, 2, 0, -4, 6, -2 }, + { 18, 53, 69, -74, 14, 24, 28, -30, -6, -7, -11, 12, -5, -7, -6, 8 }, + { 5, -1, 2, 0, -26, 6, 0, 1, 45, -9, -1, 0, -113, 28, 8, -1 }, + { -13, -32, 18, -2, 15, 34, -27, 7, -25, -80, 47, -1, -16, -50, 28, 2 }, + { -4, -13, -10, 19, 18, 46, 60, -48, 16, 33, 60, -48, 1, 0, 5, -2 }, + { 15, 33, 63, 89, 8, 15, 25, 40, -4, -8, -15, -8, -2, -6, -9, -7 }, + { -8, -24, -27, 15, 12, 41, 26, -29, -17, -50, -39, 27, 0, 35, -67, 26 }, + { -2, -6, -24, 13, -1, -8, 37, -22, 3, 18, -51, 22, -23, -95, 17, 17 }, + { -3, -7, -16, -21, 10, 24, 46, 75, 8, 20, 38, 72, 1, 2, 1, 7 }, + { 2, 6, 10, -3, -5, -16, -31, 12, 7, 24, 41, -16, -16, -41, -89, 49 }, + { 4, 8, 21, 40, -4, -11, -28, -57, 5, 14, 31, 70, 7, 18, 32, 52 }, + { 0, 1, 4, 11, -2, -4, -13, -34, 3, 7, 20, 47, -6, -19, -42, -101 }, + }, + { + { -99, 39, -1, 2, 65, -20, -5, 0, -15, -2, 5, -1, 0, 3, -1, 0 }, + { 58, 42, -33, 3, 33, -63, 23, -1, -55, 32, 3, -5, 21, -2, -8, 3 }, + { -15, 71, -44, 5, -58, -29, 25, 3, 62, -7, -4, -4, -19, 4, 0, 1 }, + { 46, 5, 4, -6, 71, -12, -15, 5, 52, -38, 13, -2, -63, 23, 3, -3 }, + { -14, -54, -29, 29, 25, -9, 61, -29, 27, 44, -48, 5, -27, -21, 12, 7 }, + { -3, 3, 69, -42, -11, -50, -26, 26, 24, 63, -19, -5, -18, -22, 12, 0 }, + { 17, 16, -2, 1, 38, 18, -12, 0, 62, 1, -14, 5, 89, -42, 8, -2 }, + { 15, 54, -8, 6, 6, 60, -26, -8, -30, 17, -38, 22, -43, -45, 42, -7 }, + { -6, -17, -55, -28, 9, 30, -8, 58, 4, 34, 41, -52, -16, -36, -20, 16 }, + { -2, -1, -9, -79, 7, 11, 48, 44, -13, -34, -55, 6, 12, 23, 20, -11 }, + { 7, 29, 14, -6, 12, 53, 10, -11, 14, 59, -15, -3, 5, 71, -54, 13 }, + { -5, -24, -53, 15, -3, -15, -61, 26, 6, 30, -16, 23, 13, 56, 44, -35 }, + { 4, 8, 21, 52, -1, -1, -5, 29, -7, -17, -44, -84, 8, 20, 31, 39 }, + { -2, -11, -25, -4, -4, -21, -53, 2, -5, -26, -64, 19, -8, -19, -73, 39 }, + { -3, -5, -23, -57, -2, -4, -24, -75, 1, 3, 9, -25, 6, 15, 41, 61 }, + { 1, 1, 7, 18, 1, 2, 16, 47, 2, 5, 24, 67, 3, 9, 25, 88 }, + } + }, + { //3 + { + { -114, 37, 3, 2, -22, -23, 14, 0, 21, -17, -5, 2, 5, 2, -4, -1 }, + { -19, -41, 19, -2, 85, -60, -11, 7, 17, 31, -34, 2, -11, 19, 2, -8 }, + { 36, -25, 18, -2, -42, -53, 35, 5, 46, -60, -25, 19, 8, 21, -33, -1 }, + { -27, -80, 44, -3, -58, 1, -29, 19, -41, 18, -12, -7, 12, -17, 7, -6 }, + { -11, -21, 37, -10, 44, -4, 47, -12, -37, -41, 58, 18, 10, -46, -16, 31 }, + { 15, 47, 10, -6, -16, -44, 42, 10, -80, 25, -40, 21, -23, -2, 3, -14 }, + { 13, 25, 79, -39, -13, 10, 31, -4, 49, 45, 12, -8, 3, -1, 43, 7 }, + { 16, 11, -26, 13, -13, -74, -20, -1, 5, -6, 29, -47, 26, -49, 54, 2 }, + { -8, -34, -26, 7, -26, -19, 29, -37, 1, 22, 46, -9, -81, 37, 14, 20 }, + { -6, -30, -42, -12, -3, 5, 57, -52, -2, 37, -12, 6, 74, 10, 6, -15 }, + { 5, 9, -6, 42, -15, -18, -9, 26, 15, 58, 14, 43, 23, -10, -37, 75 }, + { -5, -23, -23, 36, 3, 22, 36, 40, 27, -4, -16, 56, -25, -46, 56, -24 }, + { 1, 3, 23, 73, 8, 5, 34, 46, -12, 2, 35, -38, 26, 52, 2, -31 }, + { -3, -2, -21, -52, 1, -10, -17, 44, -19, -20, 30, 45, 27, 61, 49, 21 }, + { -2, -7, -33, -56, -4, -6, 21, 63, 15, 31, 32, -22, -10, -26, -52, -38 }, + { -5, -12, -18, -12, 8, 22, 38, 36, -5, -15, -51, -63, -5, 0, 15, 73 }, + }, + { + { -102, 22, 7, 2, 66, -25, -6, -1, -15, 14, 1, -1, 2, -2, 1, 0 }, + { 12, 93, -27, -6, -27, -64, 36, 6, 13, 5, -23, 0, -2, 6, 5, -3 }, + { -59, -24, 17, 1, -62, -2, -3, 2, 83, -12, -17, -2, -24, 14, 7, -2 }, + { -33, 23, -36, 11, -21, 50, 35, -16, -23, -78, 16, 19, 22, 15, -30, -5 }, + { 0, -38, -81, 30, 27, 5, 51, -32, 24, 36, -16, 12, -24, -8, 9, 1 }, + { 28, 38, 8, -9, 62, 32, -13, 2, 51, -32, 15, 5, -66, 28, 0, -1 }, + { 11, -35, 21, -17, 30, -18, 31, 18, -11, -36, -80, 12, 16, 49, 13, -32 }, + { -13, 23, 22, -36, -12, 64, 39, 25, -19, 23, -36, 9, -30, -58, 33, -7 }, + { -9, -20, -55, -83, 3, -2, 1, 62, 8, 2, 27, -28, 7, 15, -11, 5 }, + { -6, 24, -38, 23, -8, 40, -49, 0, -7, 9, -25, -44, 23, 39, 70, -3 }, + { 12, 17, 17, 0, 32, 27, 21, 2, 67, 11, -6, -10, 89, -22, -12, 16 }, + { 2, -9, 8, 45, 7, -8, 27, 35, -9, -31, -17, -87, -23, -22, -19, 44 }, + { -1, -9, 28, -24, -1, -10, 49, -30, -8, -7, 40, 1, 4, 33, 65, 67 }, + { 5, -12, -24, -17, 13, -34, -32, -16, 14, -67, -7, 9, 7, -74, 49, 1 }, + { 2, -6, 11, 45, 3, -10, 33, 55, 8, -5, 59, 4, 7, -4, 44, -66 }, + { -1, 1, -14, 36, -1, 2, -20, 69, 0, 0, -15, 72, 3, 4, 5, 65 }, + } + } +}; + +const uint8_t ff_vvc_lfnst_tr_set_index[95] = +{ + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 +}; + +uint8_t ff_vvc_default_scale_m[64 * 64]; + +//< AlfFixFiltCoeff +const int16_t ff_vvc_alf_fix_filt_coeff[64][12] = { + { 0, 0, 2, -3, 1, -4, 1, 7, -1, 1, -1, 5 }, + { 0, 0, 0, 0, 0, -1, 0, 1, 0, 0, -1, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 1 }, + { 2, 2, -7, -3, 0, -5, 13, 22, 12, -3, -3, 17 }, + { -1, 0, 6, -8, 1, -5, 1, 23, 0, 2, -5, 10 }, + { 0, 0, -1, -1, 0, -1, 2, 1, 0, 0, -1, 4 }, + { 0, 0, 3, -11, 1, 0, -1, 35, 5, 2, -9, 9 }, + { 0, 0, 8, -8, -2, -7, 4, 4, 2, 1, -1, 25 }, + { 0, 0, 1, -1, 0, -3, 1, 3, -1, 1, -1, 3 }, + { 0, 0, 3, -3, 0, -6, 5, -1, 2, 1, -4, 21 }, + { -7, 1, 5, 4, -3, 5, 11, 13, 12, -8, 11, 12 }, + { -5, -3, 6, -2, -3, 8, 14, 15, 2, -7, 11, 16 }, + { 2, -1, -6, -5, -2, -2, 20, 14, -4, 0, -3, 25 }, + { 3, 1, -8, -4, 0, -8, 22, 5, -3, 2, -10, 29 }, + { 2, 1, -7, -1, 2, -11, 23, -5, 0, 2, -10, 29 }, + { -6, -3, 8, 9, -4, 8, 9, 7, 14, -2, 8, 9 }, + { 2, 1, -4, -7, 0, -8, 17, 22, 1, -1, -4, 23 }, + { 3, 0, -5, -7, 0, -7, 15, 18, -5, 0, -5, 27 }, + { 2, 0, 0, -7, 1, -10, 13, 13, -4, 2, -7, 24 }, + { 3, 3, -13, 4, -2, -5, 9, 21, 25, -2, -3, 12 }, + { -5, -2, 7, -3, -7, 9, 8, 9, 16, -2, 15, 12 }, + { 0, -1, 0, -7, -5, 4, 11, 11, 8, -6, 12, 21 }, + { 3, -2, -3, -8, -4, -1, 16, 15, -2, -3, 3, 26 }, + { 2, 1, -5, -4, -1, -8, 16, 4, -2, 1, -7, 33 }, + { 2, 1, -4, -2, 1, -10, 17, -2, 0, 2, -11, 33 }, + { 1, -2, 7, -15, -16, 10, 8, 8, 20, 11, 14, 11 }, + { 2, 2, 3, -13, -13, 4, 8, 12, 2, -3, 16, 24 }, + { 1, 4, 0, -7, -8, -4, 9, 9, -2, -2, 8, 29 }, + { 1, 1, 2, -4, -1, -6, 6, 3, -1, -1, -3, 30 }, + { -7, 3, 2, 10, -2, 3, 7, 11, 19, -7, 8, 10 }, + { 0, -2, -5, -3, -2, 4, 20, 15, -1, -3, -1, 22 }, + { 3, -1, -8, -4, -1, -4, 22, 8, -4, 2, -8, 28 }, + { 0, 3, -14, 3, 0, 1, 19, 17, 8, -3, -7, 20 }, + { 0, 2, -1, -8, 3, -6, 5, 21, 1, 1, -9, 13 }, + { -4, -2, 8, 20, -2, 2, 3, 5, 21, 4, 6, 1 }, + { 2, -2, -3, -9, -4, 2, 14, 16, 3, -6, 8, 24 }, + { 2, 1, 5, -16, -7, 2, 3, 11, 15, -3, 11, 22 }, + { 1, 2, 3, -11, -2, -5, 4, 8, 9, -3, -2, 26 }, + { 0, -1, 10, -9, -1, -8, 2, 3, 4, 0, 0, 29 }, + { 1, 2, 0, -5, 1, -9, 9, 3, 0, 1, -7, 20 }, + { -2, 8, -6, -4, 3, -9, -8, 45, 14, 2, -13, 7 }, + { 1, -1, 16, -19, -8, -4, -3, 2, 19, 0, 4, 30 }, + { 1, 1, -3, 0, 2, -11, 15, -5, 1, 2, -9, 24 }, + { 0, 1, -2, 0, 1, -4, 4, 0, 0, 1, -4, 7 }, + { 0, 1, 2, -5, 1, -6, 4, 10, -2, 1, -4, 10 }, + { 3, 0, -3, -6, -2, -6, 14, 8, -1, -1, -3, 31 }, + { 0, 1, 0, -2, 1, -6, 5, 1, 0, 1, -5, 13 }, + { 3, 1, 9, -19, -21, 9, 7, 6, 13, 5, 15, 21 }, + { 2, 4, 3, -12, -13, 1, 7, 8, 3, 0, 12, 26 }, + { 3, 1, -8, -2, 0, -6, 18, 2, -2, 3, -10, 23 }, + { 1, 1, -4, -1, 1, -5, 8, 1, -1, 2, -5, 10 }, + { 0, 1, -1, 0, 0, -2, 2, 0, 0, 1, -2, 3 }, + { 1, 1, -2, -7, 1, -7, 14, 18, 0, 0, -7, 21 }, + { 0, 1, 0, -2, 0, -7, 8, 1, -2, 0, -3, 24 }, + { 0, 1, 1, -2, 2, -10, 10, 0, -2, 1, -7, 23 }, + { 0, 2, 2, -11, 2, -4, -3, 39, 7, 1, -10, 9 }, + { 1, 0, 13, -16, -5, -6, -1, 8, 6, 0, 6, 29 }, + { 1, 3, 1, -6, -4, -7, 9, 6, -3, -2, 3, 33 }, + { 4, 0, -17, -1, -1, 5, 26, 8, -2, 3, -15, 30 }, + { 0, 1, -2, 0, 2, -8, 12, -6, 1, 1, -6, 16 }, + { 0, 0, 0, -1, 1, -4, 4, 0, 0, 0, -3, 11 }, + { 0, 1, 2, -8, 2, -6, 5, 15, 0, 2, -7, 9 }, + { 1, -1, 12, -15, -7, -2, 3, 6, 6, -1, 7, 30 }, +}; + +//< AlfClassToFiltMap +const uint8_t ff_vvc_alf_class_to_filt_map[16][25] = { + { 8, 2, 2, 2, 3, 4, 53, 9, 9, 52, 4, 4, 5, 9, 2, 8, 10, 9, 1, 3, 39, 39, 10, 9, 52 }, + { 11, 12, 13, 14, 15, 30, 11, 17, 18, 19, 16, 20, 20, 4, 53, 21, 22, 23, 14, 25, 26, 26, 27, 28, 10 }, + { 16, 12, 31, 32, 14, 16, 30, 33, 53, 34, 35, 16, 20, 4, 7, 16, 21, 36, 18, 19, 21, 26, 37, 38, 39 }, + { 35, 11, 13, 14, 43, 35, 16, 4, 34, 62, 35, 35, 30, 56, 7, 35, 21, 38, 24, 40, 16, 21, 48, 57, 39 }, + { 11, 31, 32, 43, 44, 16, 4, 17, 34, 45, 30, 20, 20, 7, 5, 21, 22, 46, 40, 47, 26, 48, 63, 58, 10 }, + { 12, 13, 50, 51, 52, 11, 17, 53, 45, 9, 30, 4, 53, 19, 0, 22, 23, 25, 43, 44, 37, 27, 28, 10, 55 }, + { 30, 33, 62, 51, 44, 20, 41, 56, 34, 45, 20, 41, 41, 56, 5, 30, 56, 38, 40, 47, 11, 37, 42, 57, 8 }, + { 35, 11, 23, 32, 14, 35, 20, 4, 17, 18, 21, 20, 20, 20, 4, 16, 21, 36, 46, 25, 41, 26, 48, 49, 58 }, + { 12, 31, 59, 59, 3, 33, 33, 59, 59, 52, 4, 33, 17, 59, 55, 22, 36, 59, 59, 60, 22, 36, 59, 25, 55 }, + { 31, 25, 15, 60, 60, 22, 17, 19, 55, 55, 20, 20, 53, 19, 55, 22, 46, 25, 43, 60, 37, 28, 10, 55, 52 }, + { 12, 31, 32, 50, 51, 11, 33, 53, 19, 45, 16, 4, 4, 53, 5, 22, 36, 18, 25, 43, 26, 27, 27, 28, 10 }, + { 5, 2, 44, 52, 3, 4, 53, 45, 9, 3, 4, 56, 5, 0, 2, 5, 10, 47, 52, 3, 63, 39, 10, 9, 52 }, + { 12, 34, 44, 44, 3, 56, 56, 62, 45, 9, 56, 56, 7, 5, 0, 22, 38, 40, 47, 52, 48, 57, 39, 10, 9 }, + { 35, 11, 23, 14, 51, 35, 20, 41, 56, 62, 16, 20, 41, 56, 7, 16, 21, 38, 24, 40, 26, 26, 42, 57, 39 }, + { 33, 34, 51, 51, 52, 41, 41, 34, 62, 0, 41, 41, 56, 7, 5, 56, 38, 38, 40, 44, 37, 42, 57, 39, 10 }, + { 16, 31, 32, 15, 60, 30, 4, 17, 19, 25, 22, 20, 4, 53, 19, 21, 22, 46, 25, 55, 26, 48, 63, 58, 55 }, +}; + +const uint8_t ff_vvc_alf_aps_class_to_filt_map[25] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, +}; + +const int8_t ff_vvc_filter_c[NUM_INTRA_LUMA_FACTS][NUM_INTRA_LUMA_TAPS] = +{ + { 0, 64, 0, 0 }, + { -1, 63, 2, 0 }, + { -2, 62, 4, 0 }, + { -2, 60, 7, -1 }, + { -2, 58, 10, -2 }, + { -3, 57, 12, -2 }, + { -4, 56, 14, -2 }, + { -4, 55, 15, -2 }, + { -4, 54, 16, -2 }, + { -5, 53, 18, -2 }, + { -6, 52, 20, -2 }, + { -6, 49, 24, -3 }, + { -6, 46, 28, -4 }, + { -5, 44, 29, -4 }, + { -4, 42, 30, -4 }, + { -4, 39, 33, -4 }, + { -4, 36, 36, -4 }, + { -4, 33, 39, -4 }, + { -4, 30, 42, -4 }, + { -4, 29, 44, -5 }, + { -4, 28, 46, -6 }, + { -3, 24, 49, -6 }, + { -2, 20, 52, -6 }, + { -2, 18, 53, -5 }, + { -2, 16, 54, -4 }, + { -2, 15, 55, -4 }, + { -2, 14, 56, -4 }, + { -2, 12, 57, -3 }, + { -2, 10, 58, -2 }, + { -1, 7, 60, -2 }, + { 0, 4, 62, -2 }, + { 0, 2, 63, -1 }, +}; + +#define FILTER_G(fact) { 16 - (fact >> 1), 32 - (fact >> 1), 16 + (fact >> 1), fact >> 1} +const int8_t ff_vvc_filter_g[NUM_INTRA_LUMA_FACTS][NUM_INTRA_LUMA_TAPS] = { + FILTER_G(0), + FILTER_G(1), + FILTER_G(2), + FILTER_G(3), + FILTER_G(4), + FILTER_G(5), + FILTER_G(6), + FILTER_G(7), + FILTER_G(8), + FILTER_G(9), + FILTER_G(10), + FILTER_G(11), + FILTER_G(12), + FILTER_G(13), + FILTER_G(14), + FILTER_G(15), + FILTER_G(16), + FILTER_G(17), + FILTER_G(18), + FILTER_G(19), + FILTER_G(20), + FILTER_G(21), + FILTER_G(22), + FILTER_G(23), + FILTER_G(24), + FILTER_G(25), + FILTER_G(26), + FILTER_G(27), + FILTER_G(28), + FILTER_G(29), + FILTER_G(30), + FILTER_G(31), +}; + +const uint8_t ff_vvc_gpm_angle_idx[VVC_GPM_NUM_PARTITION] = { + 0, 0, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, + 5, 5, 8, 8, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, + 14, 14, 14, 14, 16, 16, 18, 18, 18, 19, 19, 19, 20, 20, 20, 21, + 21, 21, 24, 24, 27, 27, 27, 28, 28, 28, 29, 29, 29, 30, 30, 30, +}; + +const uint8_t ff_vvc_gpm_distance_idx[VVC_GPM_NUM_PARTITION] = { + 1, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, + 2, 3, 1, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, + 0, 1, 2, 3, 1, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, + 2, 3, 1, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, +}; + +const int8_t ff_vvc_gpm_distance_lut[VVC_GPM_NUM_ANGLES] = { + 8, 8, 8, 8, 4, 4, 2, 1, 0, -1, -2, -4, -4, -8, -8, -8, -8, -8, -8, -8, -4, -4, -2, -1, 0, 1, 2, 4, 4, 8, 8, 8, +}; + +const uint8_t ff_vvc_gpm_angle_to_mirror[VVC_GPM_NUM_ANGLES] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, +}; + +#define INV -1 +const uint8_t ff_vvc_gpm_angle_to_weights_idx[32] = { + 0, INV, 1, 2, 3, 4, INV, INV, 5, INV, INV, 4, 3, 2, 1, INV, + 0, INV, 1, 2, 3, 4, INV, INV, 5, INV, INV, 4, 3, 2, 1, INV, +}; +#undef INV + +const uint8_t ff_vvc_gpm_weights_offset_x[VVC_GPM_NUM_PARTITION][4][4] = { + { + { 53, 50, 44, 32 }, + { 53, 50, 44, 32 }, + { 53, 50, 44, 32 }, + { 53, 50, 44, 32 }, + }, + { + { 55, 54, 52, 48 }, + { 55, 54, 52, 48 }, + { 55, 54, 52, 48 }, + { 55, 54, 52, 48 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 50, 44, 32 }, + { 52, 48, 44, 32 }, + { 52, 48, 40, 32 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 52, 48, 40 }, + { 52, 48, 48, 40 }, + { 52, 48, 40, 40 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 54, 52, 48 }, + { 52, 48, 52, 48 }, + { 52, 48, 40, 48 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 50, 44, 32 }, + { 52, 48, 44, 32 }, + { 52, 48, 40, 32 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 52, 48, 40 }, + { 52, 48, 48, 40 }, + { 52, 48, 40, 40 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 54, 52, 48 }, + { 52, 48, 52, 48 }, + { 52, 48, 40, 48 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 50, 44, 32 }, + { 52, 48, 44, 32 }, + { 52, 48, 40, 32 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 52, 48, 40 }, + { 52, 48, 48, 40 }, + { 52, 48, 40, 40 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 54, 52, 48 }, + { 52, 48, 52, 48 }, + { 52, 48, 40, 48 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 50, 44, 32 }, + { 52, 48, 44, 32 }, + { 52, 48, 40, 32 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 52, 48, 40 }, + { 52, 48, 48, 40 }, + { 52, 48, 40, 40 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 54, 52, 48 }, + { 52, 48, 52, 48 }, + { 52, 48, 40, 48 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 50, 44, 32 }, + { 52, 48, 44, 32 }, + { 52, 48, 40, 32 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 52, 48, 40 }, + { 52, 48, 48, 40 }, + { 52, 48, 40, 40 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 54, 52, 48 }, + { 52, 48, 52, 48 }, + { 52, 48, 40, 48 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 50, 44, 32 }, + { 52, 48, 44, 32 }, + { 52, 48, 40, 32 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 52, 48, 40 }, + { 52, 48, 48, 40 }, + { 52, 48, 40, 40 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 54, 52, 48 }, + { 52, 48, 52, 48 }, + { 52, 48, 40, 48 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 50, 44, 32 }, + { 52, 48, 44, 32 }, + { 52, 48, 40, 32 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 52, 48, 40 }, + { 52, 48, 48, 40 }, + { 52, 48, 40, 40 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 54, 52, 48 }, + { 52, 48, 52, 48 }, + { 52, 48, 40, 48 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 50, 44, 32 }, + { 52, 48, 44, 32 }, + { 52, 48, 40, 32 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 52, 48, 40 }, + { 52, 48, 48, 40 }, + { 52, 48, 40, 40 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 54, 52, 48 }, + { 52, 48, 52, 48 }, + { 52, 48, 40, 48 }, + { 52, 48, 40, 24 }, + }, + { + { 51, 46, 36, 16 }, + { 51, 46, 36, 16 }, + { 51, 46, 36, 16 }, + { 51, 46, 36, 16 }, + }, + { + { 49, 42, 28, 0 }, + { 49, 42, 28, 0 }, + { 49, 42, 28, 0 }, + { 49, 42, 28, 0 }, + }, + { + { 52, 46, 36, 16 }, + { 52, 48, 36, 16 }, + { 52, 48, 40, 16 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 44, 32, 8 }, + { 52, 48, 32, 8 }, + { 52, 48, 40, 8 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 42, 28, 0 }, + { 52, 48, 28, 0 }, + { 52, 48, 40, 0 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 46, 36, 16 }, + { 52, 48, 36, 16 }, + { 52, 48, 40, 16 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 44, 32, 8 }, + { 52, 48, 32, 8 }, + { 52, 48, 40, 8 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 42, 28, 0 }, + { 52, 48, 28, 0 }, + { 52, 48, 40, 0 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 46, 36, 16 }, + { 52, 48, 36, 16 }, + { 52, 48, 40, 16 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 44, 32, 8 }, + { 52, 48, 32, 8 }, + { 52, 48, 40, 8 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 42, 28, 0 }, + { 52, 48, 28, 0 }, + { 52, 48, 40, 0 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 46, 36, 16 }, + { 52, 48, 36, 16 }, + { 52, 48, 40, 16 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 44, 32, 8 }, + { 52, 48, 32, 8 }, + { 52, 48, 40, 8 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 42, 28, 0 }, + { 52, 48, 28, 0 }, + { 52, 48, 40, 0 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 46, 36, 16 }, + { 52, 48, 36, 16 }, + { 52, 48, 40, 16 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 44, 32, 8 }, + { 52, 48, 32, 8 }, + { 52, 48, 40, 8 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 42, 28, 0 }, + { 52, 48, 28, 0 }, + { 52, 48, 40, 0 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 46, 36, 16 }, + { 52, 48, 36, 16 }, + { 52, 48, 40, 16 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 44, 32, 8 }, + { 52, 48, 32, 8 }, + { 52, 48, 40, 8 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 42, 28, 0 }, + { 52, 48, 28, 0 }, + { 52, 48, 40, 0 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 46, 36, 16 }, + { 52, 48, 36, 16 }, + { 52, 48, 40, 16 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 44, 32, 8 }, + { 52, 48, 32, 8 }, + { 52, 48, 40, 8 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 42, 28, 0 }, + { 52, 48, 28, 0 }, + { 52, 48, 40, 0 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 46, 36, 16 }, + { 52, 48, 36, 16 }, + { 52, 48, 40, 16 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 44, 32, 8 }, + { 52, 48, 32, 8 }, + { 52, 48, 40, 8 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 42, 28, 0 }, + { 52, 48, 28, 0 }, + { 52, 48, 40, 0 }, + { 52, 48, 40, 24 }, + }, +}; + +const uint8_t ff_vvc_gpm_weights_offset_y[VVC_GPM_NUM_PARTITION][4][4] = { + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 53, 52, 52, 52 }, + { 50, 50, 48, 48 }, + { 44, 44, 44, 40 }, + { 32, 32, 32, 32 }, + }, + { + { 54, 52, 52, 52 }, + { 52, 52, 48, 48 }, + { 48, 48, 48, 40 }, + { 40, 40, 40, 40 }, + }, + { + { 55, 52, 52, 52 }, + { 54, 54, 48, 48 }, + { 52, 52, 52, 40 }, + { 48, 48, 48, 48 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 53, 52, 52, 52 }, + { 50, 50, 48, 48 }, + { 44, 44, 44, 40 }, + { 32, 32, 32, 32 }, + }, + { + { 54, 52, 52, 52 }, + { 52, 52, 48, 48 }, + { 48, 48, 48, 40 }, + { 40, 40, 40, 40 }, + }, + { + { 55, 52, 52, 52 }, + { 54, 54, 48, 48 }, + { 52, 52, 52, 40 }, + { 48, 48, 48, 48 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 53, 52, 52, 52 }, + { 50, 50, 48, 48 }, + { 44, 44, 44, 40 }, + { 32, 32, 32, 32 }, + }, + { + { 54, 52, 52, 52 }, + { 52, 52, 48, 48 }, + { 48, 48, 48, 40 }, + { 40, 40, 40, 40 }, + }, + { + { 55, 52, 52, 52 }, + { 54, 54, 48, 48 }, + { 52, 52, 52, 40 }, + { 48, 48, 48, 48 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 53, 52, 52, 52 }, + { 50, 50, 48, 48 }, + { 44, 44, 44, 40 }, + { 32, 32, 32, 32 }, + }, + { + { 54, 52, 52, 52 }, + { 52, 52, 48, 48 }, + { 48, 48, 48, 40 }, + { 40, 40, 40, 40 }, + }, + { + { 55, 52, 52, 52 }, + { 54, 54, 48, 48 }, + { 52, 52, 52, 40 }, + { 48, 48, 48, 48 }, + }, + { + { 53, 53, 53, 53 }, + { 50, 50, 50, 50 }, + { 44, 44, 44, 44 }, + { 32, 32, 32, 32 }, + }, + { + { 55, 55, 55, 55 }, + { 54, 54, 54, 54 }, + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 53, 52, 52, 52 }, + { 50, 50, 48, 48 }, + { 44, 44, 44, 40 }, + { 32, 32, 32, 32 }, + }, + { + { 54, 52, 52, 52 }, + { 52, 52, 48, 48 }, + { 48, 48, 48, 40 }, + { 40, 40, 40, 40 }, + }, + { + { 55, 52, 52, 52 }, + { 54, 54, 48, 48 }, + { 52, 52, 52, 40 }, + { 48, 48, 48, 48 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 53, 52, 52, 52 }, + { 50, 50, 48, 48 }, + { 44, 44, 44, 40 }, + { 32, 32, 32, 32 }, + }, + { + { 54, 52, 52, 52 }, + { 52, 52, 48, 48 }, + { 48, 48, 48, 40 }, + { 40, 40, 40, 40 }, + }, + { + { 55, 52, 52, 52 }, + { 54, 54, 48, 48 }, + { 52, 52, 52, 40 }, + { 48, 48, 48, 48 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 53, 52, 52, 52 }, + { 50, 50, 48, 48 }, + { 44, 44, 44, 40 }, + { 32, 32, 32, 32 }, + }, + { + { 54, 52, 52, 52 }, + { 52, 52, 48, 48 }, + { 48, 48, 48, 40 }, + { 40, 40, 40, 40 }, + }, + { + { 55, 52, 52, 52 }, + { 54, 54, 48, 48 }, + { 52, 52, 52, 40 }, + { 48, 48, 48, 48 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 53, 52, 52, 52 }, + { 50, 50, 48, 48 }, + { 44, 44, 44, 40 }, + { 32, 32, 32, 32 }, + }, + { + { 54, 52, 52, 52 }, + { 52, 52, 48, 48 }, + { 48, 48, 48, 40 }, + { 40, 40, 40, 40 }, + }, + { + { 55, 52, 52, 52 }, + { 54, 54, 48, 48 }, + { 52, 52, 52, 40 }, + { 48, 48, 48, 48 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 51, 52, 52, 52 }, + { 46, 46, 48, 48 }, + { 36, 36, 36, 40 }, + { 16, 16, 16, 16 }, + }, + { + { 50, 52, 52, 52 }, + { 44, 44, 48, 48 }, + { 32, 32, 32, 40 }, + { 8, 8, 8, 8 }, + }, + { + { 49, 52, 52, 52 }, + { 42, 42, 48, 48 }, + { 28, 28, 28, 40 }, + { 0, 0, 0, 0 }, + }, + { + { 51, 52, 52, 52 }, + { 46, 46, 48, 48 }, + { 36, 36, 36, 40 }, + { 16, 16, 16, 16 }, + }, + { + { 50, 52, 52, 52 }, + { 44, 44, 48, 48 }, + { 32, 32, 32, 40 }, + { 8, 8, 8, 8 }, + }, + { + { 49, 52, 52, 52 }, + { 42, 42, 48, 48 }, + { 28, 28, 28, 40 }, + { 0, 0, 0, 0 }, + }, + { + { 51, 52, 52, 52 }, + { 46, 46, 48, 48 }, + { 36, 36, 36, 40 }, + { 16, 16, 16, 16 }, + }, + { + { 50, 52, 52, 52 }, + { 44, 44, 48, 48 }, + { 32, 32, 32, 40 }, + { 8, 8, 8, 8 }, + }, + { + { 49, 52, 52, 52 }, + { 42, 42, 48, 48 }, + { 28, 28, 28, 40 }, + { 0, 0, 0, 0 }, + }, + { + { 51, 52, 52, 52 }, + { 46, 46, 48, 48 }, + { 36, 36, 36, 40 }, + { 16, 16, 16, 16 }, + }, + { + { 50, 52, 52, 52 }, + { 44, 44, 48, 48 }, + { 32, 32, 32, 40 }, + { 8, 8, 8, 8 }, + }, + { + { 49, 52, 52, 52 }, + { 42, 42, 48, 48 }, + { 28, 28, 28, 40 }, + { 0, 0, 0, 0 }, + }, + { + { 51, 51, 51, 51 }, + { 46, 46, 46, 46 }, + { 36, 36, 36, 36 }, + { 16, 16, 16, 16 }, + }, + { + { 49, 49, 49, 49 }, + { 42, 42, 42, 42 }, + { 28, 28, 28, 28 }, + { 0, 0, 0, 0 }, + }, + { + { 51, 52, 52, 52 }, + { 46, 46, 48, 48 }, + { 36, 36, 36, 40 }, + { 16, 16, 16, 16 }, + }, + { + { 50, 52, 52, 52 }, + { 44, 44, 48, 48 }, + { 32, 32, 32, 40 }, + { 8, 8, 8, 8 }, + }, + { + { 49, 52, 52, 52 }, + { 42, 42, 48, 48 }, + { 28, 28, 28, 40 }, + { 0, 0, 0, 0 }, + }, + { + { 51, 52, 52, 52 }, + { 46, 46, 48, 48 }, + { 36, 36, 36, 40 }, + { 16, 16, 16, 16 }, + }, + { + { 50, 52, 52, 52 }, + { 44, 44, 48, 48 }, + { 32, 32, 32, 40 }, + { 8, 8, 8, 8 }, + }, + { + { 49, 52, 52, 52 }, + { 42, 42, 48, 48 }, + { 28, 28, 28, 40 }, + { 0, 0, 0, 0 }, + }, + { + { 51, 52, 52, 52 }, + { 46, 46, 48, 48 }, + { 36, 36, 36, 40 }, + { 16, 16, 16, 16 }, + }, + { + { 50, 52, 52, 52 }, + { 44, 44, 48, 48 }, + { 32, 32, 32, 40 }, + { 8, 8, 8, 8 }, + }, + { + { 49, 52, 52, 52 }, + { 42, 42, 48, 48 }, + { 28, 28, 28, 40 }, + { 0, 0, 0, 0 }, + }, + { + { 51, 52, 52, 52 }, + { 46, 46, 48, 48 }, + { 36, 36, 36, 40 }, + { 16, 16, 16, 16 }, + }, + { + { 50, 52, 52, 52 }, + { 44, 44, 48, 48 }, + { 32, 32, 32, 40 }, + { 8, 8, 8, 8 }, + }, + { + { 49, 52, 52, 52 }, + { 42, 42, 48, 48 }, + { 28, 28, 28, 40 }, + { 0, 0, 0, 0 }, + }, +}; + +const uint8_t ff_vvc_gpm_weights[6][VVC_GPM_WEIGHT_SIZE * VVC_GPM_WEIGHT_SIZE] = { + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + }, + { + 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, + }, + { + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + { + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 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, 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, 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, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, +}; diff --git a/libavcodec/vvc/vvc_data.h b/libavcodec/vvc/vvc_data.h new file mode 100644 index 00000000000..9e8d0f051da --- /dev/null +++ b/libavcodec/vvc/vvc_data.h @@ -0,0 +1,69 @@ +/* + * VVC shared tables + * + * 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_VVC_DATA_H +#define AVCODEC_VVC_DATA_H + +#include + +extern const uint8_t ff_vvc_diag_scan_x[5][5][16 * 16]; +extern const uint8_t ff_vvc_diag_scan_y[5][5][16 * 16]; + +extern const uint8_t ff_vvc_scaling_pred_8[8 * 8]; +extern const uint8_t ff_vvc_scaling_pred_16[8 * 8]; +extern const int ff_vvc_scaling_list0[8 * 8]; + +extern const int8_t ff_vvc_dct8_4x4[4][4]; +extern const int8_t ff_vvc_dct8_8x8[8][8]; +extern const int8_t ff_vvc_dct8_16x16[16][16]; +extern const int8_t ff_vvc_dct8_32x32[32][32]; +extern const int8_t ff_vvc_dst7_4x4[4][4]; +extern const int8_t ff_vvc_dst7_8x8[8][8]; +extern const int8_t ff_vvc_dst7_16x16[16][16]; +extern const int8_t ff_vvc_dst7_32x32[32][32]; +extern const int8_t ff_vvc_lfnst_4x4[4][2][16][16]; +extern const int8_t ff_vvc_lfnst_8x8[4][2][16][48]; +extern const uint8_t ff_vvc_lfnst_tr_set_index[95]; +extern uint8_t ff_vvc_default_scale_m[64 * 64]; + +#define NUM_INTRA_LUMA_TAPS 4 +#define NUM_INTRA_LUMA_FACTS 32 +extern const int8_t ff_vvc_filter_c[NUM_INTRA_LUMA_FACTS][NUM_INTRA_LUMA_TAPS]; +extern const int8_t ff_vvc_filter_g[NUM_INTRA_LUMA_FACTS][NUM_INTRA_LUMA_TAPS]; + +#define VVC_GPM_NUM_PARTITION 64 +#define VVC_GPM_NUM_ANGLES 32 +#define VVC_GPM_WEIGHT_SIZE 112 +extern const uint8_t ff_vvc_gpm_angle_idx[VVC_GPM_NUM_PARTITION]; +extern const uint8_t ff_vvc_gpm_distance_idx[VVC_GPM_NUM_PARTITION]; +extern const int8_t ff_vvc_gpm_distance_lut[VVC_GPM_NUM_ANGLES]; +extern const uint8_t ff_vvc_gpm_angle_to_mirror[VVC_GPM_NUM_ANGLES]; +extern const uint8_t ff_vvc_gpm_angle_to_weights_idx[VVC_GPM_NUM_ANGLES]; +extern const uint8_t ff_vvc_gpm_weights_offset_x[VVC_GPM_NUM_PARTITION][4][4]; +extern const uint8_t ff_vvc_gpm_weights_offset_y[VVC_GPM_NUM_PARTITION][4][4]; +extern const uint8_t ff_vvc_gpm_weights[6][VVC_GPM_WEIGHT_SIZE * VVC_GPM_WEIGHT_SIZE]; + +extern const int16_t ff_vvc_alf_fix_filt_coeff[64][12]; +extern const uint8_t ff_vvc_alf_class_to_filt_map[16][25]; +extern const uint8_t ff_vvc_alf_aps_class_to_filt_map[25]; + +const uint8_t* ff_vvc_get_mip_matrix(const int size_id, const int mode_idx); + +#endif /* AVCODEC_VVC_DATA_H */ diff --git a/libavcodec/vvc/vvc_filter.c b/libavcodec/vvc/vvc_filter.c new file mode 100644 index 00000000000..ab6d75d06f7 --- /dev/null +++ b/libavcodec/vvc/vvc_filter.c @@ -0,0 +1,1330 @@ +/* + * VVC filters + * + * Copyright (C) 2021 Nuo Mi + * + * 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 "vvc_ctu.h" +#include "vvc_data.h" +#include "vvc_filter.h" +#include "vvc_refs.h" + +#define LEFT 0 +#define TOP 1 +#define RIGHT 2 +#define BOTTOM 3 +#define MAX_EDGES 4 + +//Table 43 Derivation of threshold variables beta' and tc' from input Q +static const uint16_t tctable[66] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 4, 4, 4, 4, 5, 5, 5, 5, 7, 7, 8, 9, 10, + 10, 11, 13, 14, 15, 17, 19, 21, 24, 25, 29, 33, 36, 41, 45, 51, + 57, 64, 71, 80, 89, 100, 112, 125, 141, 157, 177, 198, 222, 250, 280, 314, + 352, 395, + +}; + +//Table 43 Derivation of threshold variables beta' and tc' from input Q +static const uint8_t betatable[64] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 22, 24, + 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, + 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, +}; + + +static int get_qPc(const VVCFrameContext *fc, const int x0, const int y0, const int chroma) +{ + const int x = x0 >> MIN_TU_LOG2; + const int y = y0 >> MIN_TU_LOG2; + const int min_tu_width = fc->ps.pps->min_tu_width; + return fc->tab.qp[chroma][x + y * min_tu_width]; +} + +static void copy_ctb(uint8_t *dst, const uint8_t *src, const int width, const int height, + const ptrdiff_t dst_stride, const ptrdiff_t src_stride) +{ + for (int y = 0; y < height; y++) { + memcpy(dst, src, width); + + dst += dst_stride; + src += src_stride; + } +} + +static void copy_pixel(uint8_t *dst, const uint8_t *src, const int pixel_shift) +{ + if (pixel_shift) + *(uint16_t *)dst = *(uint16_t *)src; + else + *dst = *src; +} + +static void copy_vert(uint8_t *dst, const uint8_t *src, const int pixel_shift, const int height, + const ptrdiff_t dst_stride, const ptrdiff_t src_stride) +{ + int i; + if (pixel_shift == 0) { + for (i = 0; i < height; i++) { + *dst = *src; + dst += dst_stride; + src += src_stride; + } + } else { + for (i = 0; i < height; i++) { + *(uint16_t *)dst = *(uint16_t *)src; + dst += dst_stride; + src += src_stride; + } + } +} + +static void copy_ctb_to_hv(VVCFrameContext *fc, const uint8_t *src, + const ptrdiff_t src_stride, const int x, const int y, const int width, const int height, + const int c_idx, const int x_ctb, const int y_ctb) +{ + int ps = fc->ps.sps->pixel_shift; + int w = fc->ps.sps->width >> fc->ps.sps->hshift[c_idx]; + int h = fc->ps.sps->height >> fc->ps.sps->vshift[c_idx]; + + /* copy horizontal edges */ + memcpy(fc->tab.sao_pixel_buffer_h[c_idx] + (((2 * y_ctb) * w + x) << ps), + src, width << ps); + memcpy(fc->tab.sao_pixel_buffer_h[c_idx] + (((2 * y_ctb + 1) * w + x) << ps), + src + src_stride * (height - 1), width << ps); + + /* copy vertical edges */ + copy_vert(fc->tab.sao_pixel_buffer_v[c_idx] + (((2 * x_ctb) * h + y) << ps), src, ps, height, 1 << ps, src_stride); + + copy_vert(fc->tab.sao_pixel_buffer_v[c_idx] + (((2 * x_ctb + 1) * h + y) << ps), src + ((width - 1) << ps), ps, height, 1 << ps, src_stride); +} + +void ff_vvc_sao_filter(VVCLocalContext *lc, int x, int y) +{ + VVCFrameContext *fc = lc->fc; + const int ctb_size_y = fc->ps.sps->ctb_size_y; + static const uint8_t sao_tab[16] = { 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8 }; + int c_idx; + int edges[4]; // 0 left 1 top 2 right 3 bottom + int x_ctb = x >> fc->ps.sps->ctb_log2_size_y; + int y_ctb = y >> fc->ps.sps->ctb_log2_size_y; + SAOParams *sao = &CTB(fc->tab.sao, x_ctb, y_ctb); + // flags indicating unfilterable edges + uint8_t vert_edge[] = { 0, 0 }; + uint8_t horiz_edge[] = { 0, 0 }; + uint8_t diag_edge[] = { 0, 0, 0, 0 }; + uint8_t lfase = fc->ps.pps->r->pps_loop_filter_across_slices_enabled_flag; + uint8_t no_tile_filter = fc->ps.pps->r->num_tiles_in_pic > 1 && + !fc->ps.pps->r->pps_loop_filter_across_tiles_enabled_flag; + uint8_t restore = no_tile_filter || !lfase; + uint8_t left_tile_edge = 0; + uint8_t right_tile_edge = 0; + uint8_t up_tile_edge = 0; + uint8_t bottom_tile_edge = 0; + + edges[LEFT] = x_ctb == 0; + edges[TOP] = y_ctb == 0; + edges[RIGHT] = x_ctb == fc->ps.pps->ctb_width - 1; + edges[BOTTOM] = y_ctb == fc->ps.pps->ctb_height - 1; + + if (restore) { + if (!edges[LEFT]) { + left_tile_edge = no_tile_filter && fc->ps.pps->ctb_to_col_bd[x_ctb] == x_ctb; + vert_edge[0] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb - 1, y_ctb)) || left_tile_edge; + } + if (!edges[RIGHT]) { + right_tile_edge = no_tile_filter && fc->ps.pps->ctb_to_col_bd[x_ctb] != fc->ps.pps->ctb_to_col_bd[x_ctb + 1]; + vert_edge[1] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb + 1, y_ctb)) || right_tile_edge; + } + if (!edges[TOP]) { + up_tile_edge = no_tile_filter && fc->ps.pps->ctb_to_row_bd[y_ctb] == y_ctb; + horiz_edge[0] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb, y_ctb - 1)) || up_tile_edge; + } + if (!edges[BOTTOM]) { + bottom_tile_edge = no_tile_filter && fc->ps.pps->ctb_to_row_bd[y_ctb] != fc->ps.pps->ctb_to_row_bd[y_ctb + 1]; + horiz_edge[1] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb, y_ctb + 1)) || bottom_tile_edge; + } + if (!edges[LEFT] && !edges[TOP]) { + diag_edge[0] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb - 1, y_ctb - 1)) || left_tile_edge || up_tile_edge; + } + if (!edges[TOP] && !edges[RIGHT]) { + diag_edge[1] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb + 1, y_ctb - 1)) || right_tile_edge || up_tile_edge; + } + if (!edges[RIGHT] && !edges[BOTTOM]) { + diag_edge[2] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb + 1, y_ctb + 1)) || right_tile_edge || bottom_tile_edge; + } + if (!edges[LEFT] && !edges[BOTTOM]) { + diag_edge[3] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb - 1, y_ctb + 1)) || left_tile_edge || bottom_tile_edge; + } + } + + for (c_idx = 0; c_idx < (fc->ps.sps->r->sps_chroma_format_idc ? 3 : 1); c_idx++) { + int x0 = x >> fc->ps.sps->hshift[c_idx]; + int y0 = y >> fc->ps.sps->vshift[c_idx]; + ptrdiff_t src_stride = fc->frame->linesize[c_idx]; + int ctb_size_h = ctb_size_y >> fc->ps.sps->hshift[c_idx]; + int ctb_size_v = ctb_size_y >> fc->ps.sps->vshift[c_idx]; + int width = FFMIN(ctb_size_h, (fc->ps.sps->width >> fc->ps.sps->hshift[c_idx]) - x0); + int height = FFMIN(ctb_size_v, (fc->ps.sps->height >> fc->ps.sps->vshift[c_idx]) - y0); + int tab = sao_tab[(FFALIGN(width, 8) >> 3) - 1]; + uint8_t *src = &fc->frame->data[c_idx][y0 * src_stride + (x0 << fc->ps.sps->pixel_shift)]; + ptrdiff_t dst_stride; + uint8_t *dst; + + switch (sao->type_idx[c_idx]) { + case SAO_BAND: + copy_ctb_to_hv(fc, src, src_stride, x0, y0, width, height, c_idx, x_ctb, y_ctb); + fc->vvcdsp.sao.band_filter[tab](src, src, src_stride, src_stride, + sao->offset_val[c_idx], sao->band_position[c_idx], width, height); + sao->type_idx[c_idx] = SAO_APPLIED; + break; + case SAO_EDGE: + { + int w = fc->ps.sps->width >> fc->ps.sps->hshift[c_idx]; + int h = fc->ps.sps->height >> fc->ps.sps->vshift[c_idx]; + int sh = fc->ps.sps->pixel_shift; + int left_pixels, right_pixels; + + dst_stride = 2*MAX_PB_SIZE + AV_INPUT_BUFFER_PADDING_SIZE; + dst = lc->sao_buffer + dst_stride + AV_INPUT_BUFFER_PADDING_SIZE; + + if (!edges[TOP]) { + int left = 1 - edges[LEFT]; + int right = 1 - edges[RIGHT]; + const uint8_t *src1[2]; + uint8_t *dst1; + int src_idx, pos; + + dst1 = dst - dst_stride - (left << sh); + src1[0] = src - src_stride - (left << sh); + src1[1] = fc->tab.sao_pixel_buffer_h[c_idx] + (((2 * y_ctb - 1) * w + x0 - left) << sh); + pos = 0; + if (left) { + src_idx = (CTB(fc->tab.sao, x_ctb-1, y_ctb-1).type_idx[c_idx] == + SAO_APPLIED); + copy_pixel(dst1, src1[src_idx], sh); + pos += (1 << sh); + } + src_idx = (CTB(fc->tab.sao, x_ctb, y_ctb-1).type_idx[c_idx] == + SAO_APPLIED); + memcpy(dst1 + pos, src1[src_idx] + pos, width << sh); + if (right) { + pos += width << sh; + src_idx = (CTB(fc->tab.sao, x_ctb+1, y_ctb-1).type_idx[c_idx] == + SAO_APPLIED); + copy_pixel(dst1 + pos, src1[src_idx] + pos, sh); + } + } + if (!edges[BOTTOM]) { + int left = 1 - edges[LEFT]; + int right = 1 - edges[RIGHT]; + const uint8_t *src1[2]; + uint8_t *dst1; + int src_idx, pos; + + dst1 = dst + height * dst_stride - (left << sh); + src1[0] = src + height * src_stride - (left << sh); + src1[1] = fc->tab.sao_pixel_buffer_h[c_idx] + (((2 * y_ctb + 2) * w + x0 - left) << sh); + pos = 0; + if (left) { + src_idx = (CTB(fc->tab.sao, x_ctb-1, y_ctb+1).type_idx[c_idx] == + SAO_APPLIED); + copy_pixel(dst1, src1[src_idx], sh); + pos += (1 << sh); + } + src_idx = (CTB(fc->tab.sao, x_ctb, y_ctb+1).type_idx[c_idx] == + SAO_APPLIED); + memcpy(dst1 + pos, src1[src_idx] + pos, width << sh); + if (right) { + pos += width << sh; + src_idx = (CTB(fc->tab.sao, x_ctb+1, y_ctb+1).type_idx[c_idx] == + SAO_APPLIED); + copy_pixel(dst1 + pos, src1[src_idx] + pos, sh); + } + } + left_pixels = 0; + if (!edges[LEFT]) { + if (CTB(fc->tab.sao, x_ctb-1, y_ctb).type_idx[c_idx] == SAO_APPLIED) { + copy_vert(dst - (1 << sh), + fc->tab.sao_pixel_buffer_v[c_idx] + (((2 * x_ctb - 1) * h + y0) << sh), + sh, height, dst_stride, 1 << sh); + } else { + left_pixels = 1; + } + } + right_pixels = 0; + if (!edges[RIGHT]) { + if (CTB(fc->tab.sao, x_ctb+1, y_ctb).type_idx[c_idx] == SAO_APPLIED) { + copy_vert(dst + (width << sh), + fc->tab.sao_pixel_buffer_v[c_idx] + (((2 * x_ctb + 2) * h + y0) << sh), + sh, height, dst_stride, 1 << sh); + } else { + right_pixels = 1; + } + } + + copy_ctb(dst - (left_pixels << sh), + src - (left_pixels << sh), + (width + left_pixels + right_pixels) << sh, + height, dst_stride, src_stride); + + copy_ctb_to_hv(fc, src, src_stride, x0, y0, width, height, c_idx, + x_ctb, y_ctb); + fc->vvcdsp.sao.edge_filter[tab](src, dst, src_stride, sao->offset_val[c_idx], + sao->eo_class[c_idx], width, height); + fc->vvcdsp.sao.edge_restore[restore](src, dst, + src_stride, dst_stride, + sao, + edges, width, + height, c_idx, + vert_edge, + horiz_edge, + diag_edge); + sao->type_idx[c_idx] = SAO_APPLIED; + break; + } + } + } +} + +#define TAB_BS(t, x, y) (t)[((y) >> 2) * (fc->tab.bs_width) + ((x) >> 2)] +#define TAB_MAX_LEN(t, x, y) (t)[((y) >> 2) * (fc->tab.bs_width) + ((x) >> 2)] +#define DEBLOCK_STEP 4 +#define MAX_FILTER_LEN 8 +#define LUMA_GRID 4 +#define CHROMA_GRID 8 + +static int boundary_strength(const VVCLocalContext *lc, MvField *curr, MvField *neigh, + const RefPicList *neigh_rpl) +{ + RefPicList *rpl = lc->sc->rpl; + if (curr->pred_flag == PF_BI && neigh->pred_flag == PF_BI) { + // same L0 and L1 + if (rpl[0].list[curr->ref_idx[0]] == neigh_rpl[0].list[neigh->ref_idx[0]] && + rpl[0].list[curr->ref_idx[0]] == rpl[1].list[curr->ref_idx[1]] && + neigh_rpl[0].list[neigh->ref_idx[0]] == neigh_rpl[1].list[neigh->ref_idx[1]]) { + if ((FFABS(neigh->mv[0].x - curr->mv[0].x) >= 8 || FFABS(neigh->mv[0].y - curr->mv[0].y) >= 8 || + FFABS(neigh->mv[1].x - curr->mv[1].x) >= 8 || FFABS(neigh->mv[1].y - curr->mv[1].y) >= 8) && + (FFABS(neigh->mv[1].x - curr->mv[0].x) >= 8 || FFABS(neigh->mv[1].y - curr->mv[0].y) >= 8 || + FFABS(neigh->mv[0].x - curr->mv[1].x) >= 8 || FFABS(neigh->mv[0].y - curr->mv[1].y) >= 8)) + return 1; + else + return 0; + } else if (neigh_rpl[0].list[neigh->ref_idx[0]] == rpl[0].list[curr->ref_idx[0]] && + neigh_rpl[1].list[neigh->ref_idx[1]] == rpl[1].list[curr->ref_idx[1]]) { + if (FFABS(neigh->mv[0].x - curr->mv[0].x) >= 8 || FFABS(neigh->mv[0].y - curr->mv[0].y) >= 8 || + FFABS(neigh->mv[1].x - curr->mv[1].x) >= 8 || FFABS(neigh->mv[1].y - curr->mv[1].y) >= 8) + return 1; + else + return 0; + } else if (neigh_rpl[1].list[neigh->ref_idx[1]] == rpl[0].list[curr->ref_idx[0]] && + neigh_rpl[0].list[neigh->ref_idx[0]] == rpl[1].list[curr->ref_idx[1]]) { + if (FFABS(neigh->mv[1].x - curr->mv[0].x) >= 8 || FFABS(neigh->mv[1].y - curr->mv[0].y) >= 8 || + FFABS(neigh->mv[0].x - curr->mv[1].x) >= 8 || FFABS(neigh->mv[0].y - curr->mv[1].y) >= 8) + return 1; + else + return 0; + } else { + return 1; + } + } else if ((curr->pred_flag != PF_BI) && (neigh->pred_flag != PF_BI)){ // 1 MV + Mv A, B; + int ref_A, ref_B; + + if (curr->pred_flag & 1) { + A = curr->mv[0]; + ref_A = rpl[0].list[curr->ref_idx[0]]; + } else { + A = curr->mv[1]; + ref_A = rpl[1].list[curr->ref_idx[1]]; + } + + if (neigh->pred_flag & 1) { + B = neigh->mv[0]; + ref_B = neigh_rpl[0].list[neigh->ref_idx[0]]; + } else { + B = neigh->mv[1]; + ref_B = neigh_rpl[1].list[neigh->ref_idx[1]]; + } + + if (ref_A == ref_B) { + if (FFABS(A.x - B.x) >= 8 || FFABS(A.y - B.y) >= 8) + return 1; + else + return 0; + } else + return 1; + } + + return 1; +} + +//part of 8.8.3.3 Derivation process of transform block boundary +static void derive_max_filter_length_luma(const VVCFrameContext *fc, const int qx, const int qy, + const int is_intra, const int has_subblock, const int vertical, uint8_t *max_len_p, uint8_t *max_len_q) +{ + const int px = vertical ? qx - 1 : qx; + const int py = !vertical ? qy - 1 : qy; + const uint8_t *tb_size = vertical ? fc->tab.tb_width[LUMA] : fc->tab.tb_height[LUMA]; + const int size_p = tb_size[(py >> MIN_TU_LOG2) * fc->ps.pps->min_tu_width + (px >> MIN_TU_LOG2)]; + const int size_q = tb_size[(qy >> MIN_TU_LOG2) * fc->ps.pps->min_tu_width + (qx >> MIN_TU_LOG2)]; + const int min_cb_log2 = fc->ps.sps->min_cb_log2_size_y; + const int off_p = (py >> min_cb_log2) * fc->ps.pps->min_cb_width + (px >> min_cb_log2); + if (size_p <= 4 || size_q <= 4) { + *max_len_p = *max_len_q = 1; + } else { + *max_len_p = *max_len_q = 3; + if (size_p >= 32) + *max_len_p = 7; + if (size_q >= 32) + *max_len_q = 7; + } + if (has_subblock) + *max_len_q = FFMIN(5, *max_len_q); + if (fc->tab.msf[off_p] || fc->tab.iaf[off_p]) + *max_len_p = FFMIN(5, *max_len_p); +} + +static void vvc_deblock_subblock_bs_vertical(const VVCLocalContext *lc, + const int cb_x, const int cb_y, const int x0, const int y0, const int width, const int height) +{ + const VVCFrameContext *fc = lc->fc; + MvField *tab_mvf = fc->tab.mvf; + RefPicList *rpl = lc->sc->rpl; + const int min_pu_width = fc->ps.pps->min_pu_width; + const int log2_min_pu_size = MIN_PU_LOG2; + uint8_t max_len_p, max_len_q; + int bs, i, j; + + // bs for TU internal vertical PU boundaries + for (j = 0; j < height; j += 4) { + int y_pu = (y0 + j) >> log2_min_pu_size; + + for (i = 8 - ((x0 - cb_x) % 8); i < width; i += 8) { + int xp_pu = (x0 + i - 1) >> log2_min_pu_size; + int xq_pu = (x0 + i) >> log2_min_pu_size; + MvField *left = &tab_mvf[y_pu * min_pu_width + xp_pu]; + MvField *curr = &tab_mvf[y_pu * min_pu_width + xq_pu]; + const int x = x0 + i; + const int y = y0 + j; + + bs = boundary_strength(lc, curr, left, rpl); + TAB_BS(fc->tab.vertical_bs[LUMA], x, y) = bs; + + + max_len_p = max_len_q = 0; + if (i == 4 || i == width - 4) + max_len_p = max_len_q = 1; + else if (i == 8 || i == width - 8) + max_len_p = max_len_q = 2; + else + max_len_p = max_len_q = 3; + + TAB_MAX_LEN(fc->tab.vertical_p, x, y) = max_len_p; + TAB_MAX_LEN(fc->tab.vertical_q, x, y) = max_len_q; + } + } +} + +static void vvc_deblock_subblock_bs_horizontal(const VVCLocalContext *lc, + const int cb_x, const int cb_y, const int x0, const int y0, const int width, const int height) +{ + const VVCFrameContext *fc = lc->fc; + MvField* tab_mvf = fc->tab.mvf; + RefPicList* rpl = lc->sc->rpl; + const int min_pu_width = fc->ps.pps->min_pu_width; + const int log2_min_pu_size = MIN_PU_LOG2; + uint8_t max_len_p, max_len_q; + int bs, i, j; + + // bs for TU internal horizontal PU boundaries + for (j = 8 - ((y0 - cb_y) % 8); j < height; j += 8) { + int yp_pu = (y0 + j - 1) >> log2_min_pu_size; + int yq_pu = (y0 + j) >> log2_min_pu_size; + + for (i = 0; i < width; i += 4) { + int x_pu = (x0 + i) >> log2_min_pu_size; + MvField *top = &tab_mvf[yp_pu * min_pu_width + x_pu]; + MvField *curr = &tab_mvf[yq_pu * min_pu_width + x_pu]; + const int x = x0 + i; + const int y = y0 + j; + + bs = boundary_strength(lc, curr, top, rpl); + TAB_BS(fc->tab.horizontal_bs[LUMA], x, y) = bs; + + //fixme: + //edgeTbFlags[ x − sbW ][ y ] is equal to 1 + //edgeTbFlags[ x + sbW ][ y ] is equal to 1 + max_len_p = max_len_q = 0; + if (j == 4 || j == height - 4) + max_len_p = max_len_q = 1; + else if (j == 8 || j == height - 8) + max_len_p = max_len_q = 2; + else + max_len_p = max_len_q = 3; + TAB_MAX_LEN(fc->tab.horizontal_p, x, y) = max_len_p; + TAB_MAX_LEN(fc->tab.horizontal_q, x, y) = max_len_q; + } + } + +} + +static void vvc_deblock_bs_luma_vertical(const VVCLocalContext *lc, + const int x0, const int y0, const int width, const int height) +{ + const VVCFrameContext *fc = lc->fc; + MvField *tab_mvf = fc->tab.mvf; + const int log2_min_pu_size = MIN_PU_LOG2; + const int log2_min_tu_size = MIN_TU_LOG2; + const int min_pu_width = fc->ps.pps->min_pu_width; + const int min_tu_width = fc->ps.pps->min_tu_width; + const int min_cb_log2 = fc->ps.sps->min_cb_log2_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + int is_intra = tab_mvf[(y0 >> log2_min_pu_size) * min_pu_width + + (x0 >> log2_min_pu_size)].pred_flag == PF_INTRA; + int boundary_left; + int i, bs, has_vertical_sb = 0; + uint8_t max_len_p, max_len_q; + + const int off_q = (y0 >> min_cb_log2) * min_cb_width + (x0 >> min_cb_log2); + const int cb_x = fc->tab.cb_pos_x[LUMA][off_q]; + const int cb_y = fc->tab.cb_pos_y[LUMA][off_q]; + const int cb_width = fc->tab.cb_width[LUMA][off_q]; + + if (!is_intra) { + if (fc->tab.msf[off_q] || fc->tab.iaf[off_q]) + has_vertical_sb = cb_width > 8; + } + + // bs for vertical TU boundaries + boundary_left = x0 > 0 && !(x0 & 3); + if (boundary_left && + ((!fc->ps.pps->r->pps_loop_filter_across_slices_enabled_flag && + lc->boundary_flags & BOUNDARY_LEFT_SLICE && + (x0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0) || + (!fc->ps.pps->r->pps_loop_filter_across_tiles_enabled_flag && + lc->boundary_flags & BOUNDARY_LEFT_TILE && + (x0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0))) + boundary_left = 0; + + if (boundary_left) { + const RefPicList *rpl_left = + (lc->boundary_flags & BOUNDARY_LEFT_SLICE) ? ff_vvc_get_ref_list(fc, fc->ref, x0 - 1, y0) : lc->sc->rpl; + int xp_pu = (x0 - 1) >> log2_min_pu_size; + int xq_pu = x0 >> log2_min_pu_size; + int xp_tu = (x0 - 1) >> log2_min_tu_size; + int xq_tu = x0 >> log2_min_tu_size; + + for (i = 0; i < height; i += 4) { + const int off_x = cb_x - x0; + int y_pu = (y0 + i) >> log2_min_pu_size; + int y_tu = (y0 + i) >> log2_min_tu_size; + MvField *left = &tab_mvf[y_pu * min_pu_width + xp_pu]; + MvField *curr = &tab_mvf[y_pu * min_pu_width + xq_pu]; + uint8_t left_cbf_luma = fc->tab.tu_coded_flag[LUMA][y_tu * min_tu_width + xp_tu]; + uint8_t curr_cbf_luma = fc->tab.tu_coded_flag[LUMA][y_tu * min_tu_width + xq_tu]; + uint8_t pcmf = fc->tab.pcmf[LUMA][y_tu * min_tu_width + xp_tu] && + fc->tab.pcmf[LUMA][y_tu * min_tu_width + xq_tu]; + + if (pcmf) + bs = 0; + else if (curr->pred_flag == PF_INTRA || left->pred_flag == PF_INTRA || curr->ciip_flag || left->ciip_flag) + bs = 2; + else if (curr_cbf_luma || left_cbf_luma) + bs = 1; + else if (off_x && ((off_x % 8) || !has_vertical_sb)) + bs = 0; ////inside a cu, not aligned to 8 or with no subblocks + else + bs = boundary_strength(lc, curr, left, rpl_left); + + TAB_BS(fc->tab.vertical_bs[LUMA], x0, (y0 + i)) = bs; + + derive_max_filter_length_luma(fc, x0, y0 + i, is_intra, has_vertical_sb, 1, &max_len_p, &max_len_q); + TAB_MAX_LEN(fc->tab.vertical_p, x0, y0 + i) = max_len_p; + TAB_MAX_LEN(fc->tab.vertical_q, x0, y0 + i) = max_len_q; + } + } + + if (!is_intra) { + if (fc->tab.msf[off_q] || fc->tab.iaf[off_q]) + vvc_deblock_subblock_bs_vertical(lc, cb_x, cb_y, x0, y0, width, height); + } + +} +static void vvc_deblock_bs_luma_horizontal(const VVCLocalContext *lc, + const int x0, const int y0, const int width, const int height) +{ + const VVCFrameContext *fc = lc->fc; + MvField *tab_mvf = fc->tab.mvf; + const int log2_min_pu_size = MIN_PU_LOG2; + const int log2_min_tu_size = MIN_TU_LOG2; + const int min_pu_width = fc->ps.pps->min_pu_width; + const int min_tu_width = fc->ps.pps->min_tu_width; + const int min_cb_log2 = fc->ps.sps->min_cb_log2_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + int is_intra = tab_mvf[(y0 >> log2_min_pu_size) * min_pu_width + + (x0 >> log2_min_pu_size)].pred_flag == PF_INTRA; + int boundary_upper; + int i, bs, has_horizontal_sb = 0; + uint8_t max_len_p, max_len_q; + + const int off_q = (y0 >> min_cb_log2) * min_cb_width + (x0 >> min_cb_log2); + const int cb_x = fc->tab.cb_pos_x[LUMA][off_q]; + const int cb_y = fc->tab.cb_pos_y[LUMA][off_q]; + const int cb_height = fc->tab.cb_height[LUMA][off_q]; + + if (!is_intra) { + if (fc->tab.msf[off_q] || fc->tab.iaf[off_q]) + has_horizontal_sb = cb_height > 8; + } + + boundary_upper = y0 > 0 && !(y0 & 3); + if (boundary_upper && + ((!fc->ps.pps->r->pps_loop_filter_across_slices_enabled_flag && + lc->boundary_flags & BOUNDARY_UPPER_SLICE && + (y0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0) || + (!fc->ps.pps->r->pps_loop_filter_across_tiles_enabled_flag && + lc->boundary_flags & BOUNDARY_UPPER_TILE && + (y0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0))) + boundary_upper = 0; + + if (boundary_upper) { + const RefPicList *rpl_top = + (lc->boundary_flags & BOUNDARY_UPPER_SLICE) ? ff_vvc_get_ref_list(fc, fc->ref, x0, y0 - 1) : lc->sc->rpl; + int yp_pu = (y0 - 1) >> log2_min_pu_size; + int yq_pu = y0 >> log2_min_pu_size; + int yp_tu = (y0 - 1) >> log2_min_tu_size; + int yq_tu = y0 >> log2_min_tu_size; + + for (i = 0; i < width; i += 4) { + const int off_y = y0 - cb_y; + int x_pu = (x0 + i) >> log2_min_pu_size; + int x_tu = (x0 + i) >> log2_min_tu_size; + MvField *top = &tab_mvf[yp_pu * min_pu_width + x_pu]; + MvField *curr = &tab_mvf[yq_pu * min_pu_width + x_pu]; + uint8_t top_cbf_luma = fc->tab.tu_coded_flag[LUMA][yp_tu * min_tu_width + x_tu]; + uint8_t curr_cbf_luma = fc->tab.tu_coded_flag[LUMA][yq_tu * min_tu_width + x_tu]; + const uint8_t pcmf = fc->tab.pcmf[LUMA][yp_tu * min_tu_width + x_tu] && + fc->tab.pcmf[LUMA][yq_tu * min_tu_width + x_tu]; + + if (pcmf) + bs = 0; + else if (curr->pred_flag == PF_INTRA || top->pred_flag == PF_INTRA || curr->ciip_flag || top->ciip_flag) + bs = 2; + else if (curr_cbf_luma || top_cbf_luma) + bs = 1; + else if (off_y && ((off_y % 8) || !has_horizontal_sb)) + bs = 0; //inside a cu, not aligned to 8 or with no subblocks + else + bs = boundary_strength(lc, curr, top, rpl_top); + + TAB_BS(fc->tab.horizontal_bs[LUMA], x0 + i, y0) = bs; + + derive_max_filter_length_luma(fc, x0 + i, y0, is_intra, has_horizontal_sb, 0, &max_len_p, &max_len_q); + TAB_MAX_LEN(fc->tab.horizontal_p, x0 + i, y0) = max_len_p; + TAB_MAX_LEN(fc->tab.horizontal_q, x0 + i, y0) = max_len_q; + } + } + + if (!is_intra) { + if (fc->tab.msf[off_q] || fc->tab.iaf[off_q]) + vvc_deblock_subblock_bs_horizontal(lc, cb_x, cb_y, x0, y0, width, height); + } +} + +static void vvc_deblock_bs_chroma_vertical(const VVCLocalContext *lc, + const int x0, const int y0, const int width, const int height) +{ + const VVCFrameContext *fc = lc->fc; + MvField *tab_mvf = fc->tab.mvf; + const int log2_min_pu_size = MIN_PU_LOG2; + const int log2_min_tu_size = MIN_PU_LOG2; + const int min_pu_width = fc->ps.pps->min_pu_width; + const int min_tu_width = fc->ps.pps->min_tu_width; + int boundary_left, i; + + // bs for vertical TU boundaries + boundary_left = x0 > 0 && !(x0 & ((CHROMA_GRID << fc->ps.sps->hshift[1]) - 1)); + if (boundary_left && + ((!fc->ps.pps->r->pps_loop_filter_across_slices_enabled_flag && + lc->boundary_flags & BOUNDARY_LEFT_SLICE && + (x0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0) || + (!fc->ps.pps->r->pps_loop_filter_across_tiles_enabled_flag && + lc->boundary_flags & BOUNDARY_LEFT_TILE && + (x0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0))) + boundary_left = 0; + + if (boundary_left) { + int xp_pu = (x0 - 1) >> log2_min_pu_size; + int xq_pu = x0 >> log2_min_pu_size; + int xp_tu = (x0 - 1) >> log2_min_tu_size; + int xq_tu = x0 >> log2_min_tu_size; + + for (i = 0; i < height; i += 2) { + int y_pu = (y0 + i) >> log2_min_pu_size; + int y_tu = (y0 + i) >> log2_min_tu_size; + MvField *left = &tab_mvf[y_pu * min_pu_width + xp_pu]; + MvField *curr = &tab_mvf[y_pu * min_pu_width + xq_pu]; + const int left_tu = y_tu * min_tu_width + xp_tu; + const int curr_tu = y_tu * min_tu_width + xq_tu; + const uint8_t pcmf = fc->tab.pcmf[CHROMA][left_tu] && fc->tab.pcmf[CHROMA][curr_tu]; + + for (int c = CB; c <= CR; c++) { + uint8_t cbf = fc->tab.tu_coded_flag[c][left_tu] | + fc->tab.tu_coded_flag[c][curr_tu] | + fc->tab.tu_joint_cbcr_residual_flag[left_tu] | + fc->tab.tu_joint_cbcr_residual_flag[curr_tu]; + int bs = 0; + + if (pcmf) + bs = 0; + else if (curr->pred_flag == PF_INTRA || left->pred_flag == PF_INTRA || curr->ciip_flag || left->ciip_flag) + bs = 2; + else if (cbf) + bs = 1; + TAB_BS(fc->tab.vertical_bs[c], x0, (y0 + i)) = bs; + } + } + } +} + +static void vvc_deblock_bs_chroma_horizontal(const VVCLocalContext *lc, + const int x0, const int y0, const int width, const int height) +{ + const VVCFrameContext *fc = lc->fc; + MvField *tab_mvf = fc->tab.mvf; + const int log2_min_pu_size = MIN_PU_LOG2; + const int log2_min_tu_size = MIN_PU_LOG2; + const int min_pu_width = fc->ps.pps->min_pu_width; + const int min_tu_width = fc->ps.pps->min_tu_width; + int boundary_upper; + int i; + + boundary_upper = y0 > 0 && !(y0 & ((CHROMA_GRID << fc->ps.sps->vshift[1]) - 1)); + if (boundary_upper && + ((!fc->ps.pps->r->pps_loop_filter_across_slices_enabled_flag && + lc->boundary_flags & BOUNDARY_UPPER_SLICE && + (y0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0) || + (!fc->ps.pps->r->pps_loop_filter_across_tiles_enabled_flag && + lc->boundary_flags & BOUNDARY_UPPER_TILE && + (y0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0))) + boundary_upper = 0; + + if (boundary_upper) { + int yp_pu = (y0 - 1) >> log2_min_pu_size; + int yq_pu = y0 >> log2_min_pu_size; + int yp_tu = (y0 - 1) >> log2_min_tu_size; + int yq_tu = y0 >> log2_min_tu_size; + + for (i = 0; i < width; i += 2) { + int x_pu = (x0 + i) >> log2_min_pu_size; + int x_tu = (x0 + i) >> log2_min_tu_size; + MvField *top = &tab_mvf[yp_pu * min_pu_width + x_pu]; + MvField *curr = &tab_mvf[yq_pu * min_pu_width + x_pu]; + const int top_tu = yp_tu * min_tu_width + x_tu; + const int curr_tu = yq_tu * min_tu_width + x_tu; + const uint8_t pcmf = fc->tab.pcmf[CHROMA][top_tu] && fc->tab.pcmf[CHROMA][curr_tu]; + + for (int c = CB; c <= CR; c++) { + uint8_t cbf = fc->tab.tu_coded_flag[c][top_tu] | + fc->tab.tu_coded_flag[c][curr_tu] | + fc->tab.tu_joint_cbcr_residual_flag[top_tu] | + fc->tab.tu_joint_cbcr_residual_flag[curr_tu]; + int bs = 0; + + if (pcmf) + bs = 0; + else if (curr->pred_flag == PF_INTRA || top->pred_flag == PF_INTRA || curr->ciip_flag || top->ciip_flag) + bs = 2; + else if (cbf) + bs = 1; + TAB_BS(fc->tab.horizontal_bs[c], x0 + i, y0) = bs; + } + } + } +} + +typedef void (*deblock_bs_fn)(const VVCLocalContext *lc, const int x0, const int y0, + const int width, const int height); + +static void vvc_deblock_bs(const VVCLocalContext *lc, const int x0, const int y0, const int vertical) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int ctb_size = sps->ctb_size_y; + const int x_end = FFMIN(x0 + ctb_size, pps->width) >> MIN_TU_LOG2; + const int y_end = FFMIN(y0 + ctb_size, pps->height) >> MIN_TU_LOG2; + deblock_bs_fn deblock_bs[2][2] = { + { vvc_deblock_bs_luma_horizontal, vvc_deblock_bs_chroma_horizontal }, + { vvc_deblock_bs_luma_vertical, vvc_deblock_bs_chroma_vertical } + }; + + for (int is_chroma = 0; is_chroma <= 1; is_chroma++) { + const int hs = sps->hshift[is_chroma]; + const int vs = sps->vshift[is_chroma]; + for (int y = y0 >> MIN_TU_LOG2; y < y_end; y++) { + for (int x = x0 >> MIN_TU_LOG2; x < x_end; x++) { + const int off = y * fc->ps.pps->min_tu_width + x; + if ((fc->tab.tb_pos_x0[is_chroma][off] >> MIN_TU_LOG2) == x && (fc->tab.tb_pos_y0[is_chroma][off] >> MIN_TU_LOG2) == y) { + deblock_bs[vertical][is_chroma](lc, x << MIN_TU_LOG2, y << MIN_TU_LOG2, + fc->tab.tb_width[is_chroma][off] << hs, fc->tab.tb_height[is_chroma][off] << vs); + } + } + } + } +} + +//part of 8.8.3.3 Derivation process of transform block boundary +static void max_filter_length_luma(const VVCFrameContext *fc, const int qx, const int qy, + const int vertical, uint8_t *max_len_p, uint8_t *max_len_q) +{ + const uint8_t *tab_len_p = vertical ? fc->tab.vertical_p : fc->tab.horizontal_p; + const uint8_t *tab_len_q = vertical ? fc->tab.vertical_q : fc->tab.horizontal_q; + *max_len_p = TAB_MAX_LEN(tab_len_p, qx, qy); + *max_len_q = TAB_MAX_LEN(tab_len_q, qx, qy); +} + +//part of 8.8.3.3 Derivation process of transform block boundary +static void max_filter_length_chroma(const VVCFrameContext *fc, const int qx, const int qy, + const int vertical, const int horizontal_ctu_edge, const int bs, uint8_t *max_len_p, uint8_t *max_len_q) +{ + const int px = vertical ? qx - 1 : qx; + const int py = !vertical ? qy - 1 : qy; + const uint8_t *tb_size = vertical ? fc->tab.tb_width[CHROMA] : fc->tab.tb_height[CHROMA]; + + const int size_p = tb_size[(py >> MIN_TU_LOG2) * fc->ps.pps->min_tu_width + (px >> MIN_TU_LOG2)]; + const int size_q = tb_size[(qy >> MIN_TU_LOG2) * fc->ps.pps->min_tu_width + (qx >> MIN_TU_LOG2)]; + if (size_p >= 8 && size_q >= 8) { + *max_len_p = *max_len_q = 3; + if (horizontal_ctu_edge) + *max_len_p = 1; + } else { + //part of 8.8.3.6.4 Decision process for chroma block edges + *max_len_p = *max_len_q = (bs == 2); + } +} + +static void max_filter_length(const VVCFrameContext *fc, const int qx, const int qy, + const int c_idx, const int vertical, const int horizontal_ctu_edge, const int bs, uint8_t *max_len_p, uint8_t *max_len_q) +{ + if (!c_idx) + max_filter_length_luma(fc, qx, qy, vertical, max_len_p, max_len_q); + else + max_filter_length_chroma(fc, qx, qy, vertical, horizontal_ctu_edge, bs, max_len_p, max_len_q); +} + +#define TC_CALC(qp, bs) \ + tctable[av_clip((qp) + DEFAULT_INTRA_TC_OFFSET * ((bs) - 1) + \ + (tc_offset & -2), \ + 0, MAX_QP + DEFAULT_INTRA_TC_OFFSET)] + +// part of 8.8.3.6.2 Decision process for luma block edges +static int get_qp_y(const VVCFrameContext *fc, const uint8_t *src, const int x, const int y, const int vertical) +{ + const VVCSPS *sps = fc->ps.sps; + const int qp = (ff_vvc_get_qPy(fc, x - vertical, y - !vertical) + ff_vvc_get_qPy(fc, x, y) + 1) >> 1; + int qp_offset = 0; + int level; + + if (!sps->r->sps_ladf_enabled_flag) + return qp; + + level = fc->vvcdsp.lf.ladf_level[vertical](src, fc->frame->linesize[LUMA]); + qp_offset = sps->r->sps_ladf_lowest_interval_qp_offset; + for (int i = 0; i < sps->num_ladf_intervals - 1 && level > sps->ladf_interval_lower_bound[i + 1]; i++) + qp_offset = sps->r->sps_ladf_qp_offset[i]; + + return qp + qp_offset; +} + +// part of 8.8.3.6.2 Decision process for luma block edges +static int get_qp_c(const VVCFrameContext *fc, const int x, const int y, const int c_idx, const int vertical) +{ + const VVCSPS *sps = fc->ps.sps; + return (get_qPc(fc, x - vertical, y - !vertical, c_idx) + get_qPc(fc, x, y, c_idx) - 2 * sps->qp_bd_offset + 1) >> 1; +} + +static int get_qp(const VVCFrameContext *fc, const uint8_t *src, const int x, const int y, const int c_idx, const int vertical) +{ + if (!c_idx) + return get_qp_y(fc, src, x, y, vertical); + return get_qp_c(fc, x, y, c_idx, vertical); +} + +void ff_vvc_deblock_vertical(const VVCLocalContext *lc, int x0, int y0) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int c_end = fc->ps.sps->r->sps_chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : 1; + uint8_t *src; + int x, y, beta, tc, qp; + uint8_t no_p = 0; + uint8_t no_q = 0; + + int ctb_log2_size_y = fc->ps.sps->ctb_log2_size_y; + int x_end, y_end; + int ctb_size = 1 << ctb_log2_size_y; + int ctb = (x0 >> ctb_log2_size_y) + + (y0 >> ctb_log2_size_y) * fc->ps.pps->ctb_width; + DBParams *params = fc->tab.deblock + ctb; + + vvc_deblock_bs(lc, x0, y0, 1); + + x_end = x0 + ctb_size; + if (x_end > fc->ps.sps->width) + x_end = fc->ps.sps->width; + y_end = y0 + ctb_size; + if (y_end > fc->ps.sps->height) + y_end = fc->ps.sps->height; + + for (int c_idx = 0; c_idx < c_end; c_idx++) { + const int hs = sps->hshift[c_idx]; + const int vs = sps->vshift[c_idx]; + const int grid = c_idx ? (CHROMA_GRID << hs) : LUMA_GRID; + const int tc_offset = params->tc_offset[c_idx]; + const int beta_offset = params->beta_offset[c_idx]; + + for (y = y0; y < y_end; y += DEBLOCK_STEP) { + for (x = x0 ? x0 : grid; x < x_end; x += grid) { + const int bs = TAB_BS(fc->tab.vertical_bs[c_idx], x, y); + if (bs) { + uint8_t max_len_p, max_len_q; + + src = &fc->frame->data[c_idx][(y >> vs) * fc->frame->linesize[c_idx] + ((x >> hs) << fc->ps.sps->pixel_shift)]; + qp = get_qp(fc, src, x, y, c_idx, 1); + + beta = betatable[av_clip(qp + beta_offset, 0, MAX_QP)]; + + tc = TC_CALC(qp, bs); + + max_filter_length(fc, x, y, c_idx, 1, 0, bs, &max_len_p, &max_len_q); + + if (!c_idx) { + fc->vvcdsp.lf.filter_luma[1](src, + fc->frame->linesize[c_idx], beta, tc, no_p, no_q, max_len_p, max_len_q, 0); + } else { + fc->vvcdsp.lf.filter_chroma[1](src, + fc->frame->linesize[c_idx], beta, tc, no_p, no_q, vs, max_len_p, max_len_q); + } + } + } + } + } +} + +void ff_vvc_deblock_horizontal(const VVCLocalContext *lc, int x0, int y0) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int c_end = fc->ps.sps->r->sps_chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : 1; + uint8_t* src; + int x, y, beta, tc, qp; + uint8_t no_p = 0; + uint8_t no_q = 0; + + int ctb_log2_size_y = fc->ps.sps->ctb_log2_size_y; + int x_end, x_end2, y_end; + int ctb_size = 1 << ctb_log2_size_y; + int ctb = (x0 >> ctb_log2_size_y) + + (y0 >> ctb_log2_size_y) * fc->ps.pps->ctb_width; + int tc_offset, beta_offset; + + vvc_deblock_bs(lc, x0, y0, 0); + + x_end = x0 + ctb_size; + if (x_end > fc->ps.sps->width) + x_end = fc->ps.sps->width; + y_end = y0 + ctb_size; + if (y_end > fc->ps.sps->height) + y_end = fc->ps.sps->height; + + for (int c_idx = 0; c_idx < c_end; c_idx++) { + const int hs = sps->hshift[c_idx]; + const int vs = sps->vshift[c_idx]; + const int grid = c_idx ? (CHROMA_GRID << vs) : LUMA_GRID; + + x_end2 = x_end == fc->ps.sps->width ? x_end : x_end - (MAX_FILTER_LEN << hs); + + for (y = y0; y < y_end; y += grid) { + if (!y) + continue; + + // horizontal filtering luma + for (x = x0 ? x0 - (MAX_FILTER_LEN << hs) : 0; x < x_end2; x += DEBLOCK_STEP) { + const int bs = TAB_BS(fc->tab.horizontal_bs[c_idx], x, y); + if (bs) { + const DBParams *params = fc->tab.deblock + ctb - (x < x0); + const int horizontal_ctu_edge = !(y % fc->ps.sps->ctb_size_y); + uint8_t max_len_p, max_len_q; + + src = &fc->frame->data[c_idx][(y >> vs) * fc->frame->linesize[c_idx] + ((x >> hs) << fc->ps.sps->pixel_shift)]; + qp = get_qp(fc, src, x, y, c_idx, 0); + + tc_offset = params->tc_offset[c_idx]; + beta_offset = params->beta_offset[c_idx]; + + beta = betatable[av_clip(qp + beta_offset, 0, MAX_QP)]; + tc = TC_CALC(qp, bs); + + max_filter_length(fc, x, y, c_idx, 0, horizontal_ctu_edge, bs, &max_len_p, &max_len_q); + + if (!c_idx) { + fc->vvcdsp.lf.filter_luma[0](src, fc->frame->linesize[c_idx], + beta, tc, no_p, no_q, max_len_p, max_len_q, horizontal_ctu_edge); + } else { + fc->vvcdsp.lf.filter_chroma[0](src, fc->frame->linesize[c_idx], beta, + tc, no_p, no_q, hs, max_len_p, max_len_q); + } + } + } + } + } +} + +static void alf_copy_border(uint8_t *dst, const uint8_t *src, + const int pixel_shift, int width, const int height, const ptrdiff_t dst_stride, const ptrdiff_t src_stride) +{ + width <<= pixel_shift; + for (int i = 0; i < height; i++) { + memcpy(dst, src, width); + dst += dst_stride; + src += src_stride; + } +} + +static void alf_extend_vert(uint8_t *_dst, const uint8_t *_src, + const int pixel_shift, const int width, const int height, ptrdiff_t stride) +{ + if (pixel_shift == 0) { + for (int i = 0; i < height; i++) { + memset(_dst, *_src, width); + _src += stride; + _dst += stride; + } + } else { + const uint16_t *src = (const uint16_t *)_src; + uint16_t *dst = (uint16_t *)_dst; + stride >>= pixel_shift; + + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) + dst[j] = *src; + src += stride; + dst += stride; + } + } +} + +static void alf_extend_horz(uint8_t *dst, const uint8_t *src, + const int pixel_shift, int width, const int height, const ptrdiff_t stride) +{ + width <<= pixel_shift; + for (int i = 0; i < height; i++) { + memcpy(dst, src, width); + dst += stride; + } +} + +static void alf_copy_ctb_to_hv(VVCFrameContext *fc, const uint8_t *src, const ptrdiff_t src_stride, + const int x, const int y, const int width, const int height, const int x_ctb, const int y_ctb, const int c_idx) +{ + const int ps = fc->ps.sps->pixel_shift; + const int w = fc->ps.sps->width >> fc->ps.sps->hshift[c_idx]; + const int h = fc->ps.sps->height >> fc->ps.sps->vshift[c_idx]; + const int border_pixels = (c_idx == 0) ? ALF_BORDER_LUMA : ALF_BORDER_CHROMA; + const int offset_h[] = { 0, height - border_pixels }; + const int offset_v[] = { 0, width - border_pixels }; + + /* copy horizontal edges */ + for (int i = 0; i < FF_ARRAY_ELEMS(offset_h); i++) { + alf_copy_border(fc->tab.alf_pixel_buffer_h[c_idx][i] + ((border_pixels * y_ctb * w + x)<< ps), + src + offset_h[i] * src_stride, ps, width, border_pixels, w << ps, src_stride); + } + /* copy vertical edges */ + for (int i = 0; i < FF_ARRAY_ELEMS(offset_v); i++) { + alf_copy_border(fc->tab.alf_pixel_buffer_v[c_idx][i] + ((h * x_ctb + y) * (border_pixels << ps)), + src + (offset_v[i] << ps), ps, border_pixels, height, border_pixels << ps, src_stride); + } +} + +static void alf_fill_border_h(uint8_t *dst, const ptrdiff_t dst_stride, const uint8_t *src, const ptrdiff_t src_stride, + const uint8_t *border, const int width, const int border_pixels, const int ps, const int edge) +{ + if (edge) + alf_extend_horz(dst, border, ps, width, border_pixels, dst_stride); + else + alf_copy_border(dst, src, ps, width, border_pixels, dst_stride, src_stride); +} + +static void alf_fill_border_v(uint8_t *dst, const ptrdiff_t dst_stride, const uint8_t *src, + const uint8_t *border, const int border_pixels, const int height, const int pixel_shift, const int *edges, const int edge) +{ + const ptrdiff_t src_stride = (border_pixels << pixel_shift); + + if (edge) { + alf_extend_vert(dst, border, pixel_shift, border_pixels, height + 2 * border_pixels, dst_stride); + return; + } + + //left/right + alf_copy_border(dst + dst_stride * border_pixels * edges[TOP], src + src_stride * border_pixels * edges[TOP], + pixel_shift, border_pixels, height + (!edges[TOP] + !edges[BOTTOM]) * border_pixels, dst_stride, src_stride); + + //top left/right + if (edges[TOP]) + alf_extend_horz(dst, dst + dst_stride * border_pixels, pixel_shift, border_pixels, border_pixels, dst_stride); + + //bottom left/right + if (edges[BOTTOM]) { + dst += dst_stride * (border_pixels + height); + alf_extend_horz(dst, dst - dst_stride, pixel_shift, border_pixels, border_pixels, dst_stride); + } +} + +static void alf_prepare_buffer(VVCFrameContext *fc, uint8_t *_dst, const uint8_t *_src, const int x, const int y, + const int x_ctb, const int y_ctb, const int width, const int height, const ptrdiff_t dst_stride, const ptrdiff_t src_stride, + const int c_idx, const int *edges) +{ + const int ps = fc->ps.sps->pixel_shift; + const int w = fc->ps.sps->width >> fc->ps.sps->hshift[c_idx]; + const int h = fc->ps.sps->height >> fc->ps.sps->vshift[c_idx]; + const int border_pixels = c_idx == 0 ? ALF_BORDER_LUMA : ALF_BORDER_CHROMA; + uint8_t *dst, *src; + + copy_ctb(_dst, _src, width << ps, height, dst_stride, src_stride); + + //top + src = fc->tab.alf_pixel_buffer_h[c_idx][1] + (((border_pixels * (y_ctb - 1)) * w + x) << ps); + dst = _dst - border_pixels * dst_stride; + alf_fill_border_h(dst, dst_stride, src, w << ps, _dst, width, border_pixels, ps, edges[TOP]); + + //bottom + src = fc->tab.alf_pixel_buffer_h[c_idx][0] + ((border_pixels * (y_ctb + 1) * w + x) << ps); + dst = _dst + height * dst_stride; + alf_fill_border_h(dst, dst_stride, src, w << ps, _dst + (height - 1) * dst_stride, width, border_pixels, ps, edges[BOTTOM]); + + + //left + src = fc->tab.alf_pixel_buffer_v[c_idx][1] + (h * (x_ctb - 1) + y - border_pixels) * (border_pixels << ps); + dst = _dst - (border_pixels << ps) - border_pixels * dst_stride; + alf_fill_border_v(dst, dst_stride, src, dst + (border_pixels << ps), border_pixels, height, ps, edges, edges[LEFT]); + + //right + src = fc->tab.alf_pixel_buffer_v[c_idx][0] + (h * (x_ctb + 1) + y - border_pixels) * (border_pixels << ps); + dst = _dst + (width << ps) - border_pixels * dst_stride; + alf_fill_border_v(dst, dst_stride, src, dst - (1 << ps), border_pixels, height, ps, edges, edges[RIGHT]); +} + +#define ALF_MAX_BLOCKS_IN_CTU (MAX_CTU_SIZE * MAX_CTU_SIZE / ALF_BLOCK_SIZE / ALF_BLOCK_SIZE) +#define ALF_MAX_FILTER_SIZE (ALF_MAX_BLOCKS_IN_CTU * ALF_NUM_COEFF_LUMA) + +static void alf_get_coeff_and_clip(VVCLocalContext *lc, int16_t *coeff, int16_t *clip, + const uint8_t *src, ptrdiff_t src_stride, int width, int height, int vb_pos, ALFParams *alf) +{ + const VVCFrameContext *fc = lc->fc; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + uint8_t fixed_clip_set[ALF_NUM_FILTERS_LUMA][ALF_NUM_COEFF_LUMA] = { 0 }; + const int16_t *coeff_set; + const uint8_t *clip_idx_set; + const uint8_t *class_to_filt; + const int size = width * height / ALF_BLOCK_SIZE / ALF_BLOCK_SIZE; + int class_idx[ALF_MAX_BLOCKS_IN_CTU]; + int transpose_idx[ALF_MAX_BLOCKS_IN_CTU]; + + if (alf->ctb_filt_set_idx_y < 16) { + coeff_set = &ff_vvc_alf_fix_filt_coeff[0][0]; + clip_idx_set = &fixed_clip_set[0][0]; + class_to_filt = ff_vvc_alf_class_to_filt_map[alf->ctb_filt_set_idx_y]; + } else { + const int id = rsh->sh_alf_aps_id_luma[alf->ctb_filt_set_idx_y - 16]; + const VVCALF *aps = (VVCALF *)fc->ps.alf_list[id]->data; + coeff_set = &aps->luma_coeff[0][0]; + clip_idx_set = &aps->luma_clip_idx[0][0]; + class_to_filt = ff_vvc_alf_aps_class_to_filt_map; + } + fc->vvcdsp.alf.classify(class_idx, transpose_idx, src, src_stride, width, height, + vb_pos, lc->alf_gradient_tmp); + fc->vvcdsp.alf.recon_coeff_and_clip(coeff, clip, class_idx, transpose_idx, size, + coeff_set, clip_idx_set, class_to_filt); +} + +static void alf_filter_luma(VVCLocalContext *lc, uint8_t *dst, const uint8_t *src, + const ptrdiff_t dst_stride, const ptrdiff_t src_stride, const int x0, const int y0, + const int width, const int height, const int _vb_pos, ALFParams *alf) +{ + const VVCFrameContext *fc = lc->fc; + int vb_pos = _vb_pos - y0; + int16_t *coeff = (int16_t*)lc->tmp; + int16_t *clip = (int16_t *)lc->tmp1; + + av_assert0(ALF_MAX_FILTER_SIZE <= sizeof(lc->tmp)); + av_assert0(ALF_MAX_FILTER_SIZE * sizeof(int16_t) <= sizeof(lc->tmp1)); + + alf_get_coeff_and_clip(lc, coeff, clip, src, src_stride, width, height, vb_pos, alf); + fc->vvcdsp.alf.filter[LUMA](dst, dst_stride, src, src_stride, width, height, coeff, clip, vb_pos); +} + +static int alf_clip_from_idx(const VVCFrameContext *fc, const int idx) +{ + const VVCSPS *sps = fc->ps.sps; + const int offset[] = {0, 3, 5, 7}; + + return 1 << (sps->bit_depth - offset[idx]); +} + +static void alf_filter_chroma(VVCLocalContext *lc, uint8_t *dst, const uint8_t *src, + const ptrdiff_t dst_stride, const ptrdiff_t src_stride, const int c_idx, + const int width, const int height, const int vb_pos, ALFParams *alf) +{ + VVCFrameContext *fc = lc->fc; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const VVCALF *aps = (VVCALF *)fc->ps.alf_list[rsh->sh_alf_aps_id_chroma]->data; + const int idx = alf->alf_ctb_filter_alt_idx[c_idx - 1]; + const int16_t *coeff = aps->chroma_coeff[idx]; + int16_t clip[ALF_NUM_COEFF_CHROMA]; + + for (int i = 0; i < ALF_NUM_COEFF_CHROMA; i++) + clip[i] = alf_clip_from_idx(fc, aps->chroma_clip_idx[idx][i]); + + fc->vvcdsp.alf.filter[CHROMA](dst, dst_stride, src, src_stride, width, height, coeff, clip, vb_pos); +} + +static void alf_filter_cc(VVCLocalContext *lc, uint8_t *dst, const uint8_t *luma, + const ptrdiff_t dst_stride, const ptrdiff_t luma_stride, const int c_idx, + const int width, const int height, const int hs, const int vs, const int vb_pos, ALFParams *alf) +{ + VVCFrameContext *fc = lc->fc; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int idx = c_idx - 1; + const int cc_aps_id = c_idx == CB ? rsh->sh_alf_cc_cb_aps_id : rsh->sh_alf_cc_cr_aps_id; + AVBufferRef *aps_buf = fc->ps.alf_list[cc_aps_id]; + + if (aps_buf) { + const VVCALF *aps = (VVCALF *)aps_buf->data; + const int16_t *coeff = aps->cc_coeff[idx][alf->ctb_cc_idc[idx] - 1]; + + fc->vvcdsp.alf.filter_cc(dst, dst_stride, luma, luma_stride, width, height, hs, vs, coeff, vb_pos); + } +} + +void ff_vvc_alf_copy_ctu_to_hv(VVCLocalContext* lc, const int x0, const int y0) +{ + VVCFrameContext *fc = lc->fc; + const int x_ctb = x0 >> fc->ps.sps->ctb_log2_size_y; + const int y_ctb = y0 >> fc->ps.sps->ctb_log2_size_y; + const int ctb_size_y = fc->ps.sps->ctb_size_y; + const int ps = fc->ps.sps->pixel_shift; + const int c_end = fc->ps.sps->r->sps_chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : 1; + + for (int c_idx = 0; c_idx < c_end; c_idx++) { + const int hs = fc->ps.sps->hshift[c_idx]; + const int vs = fc->ps.sps->vshift[c_idx]; + const int x = x0 >> hs; + const int y = y0 >> vs; + const int width = FFMIN(fc->ps.sps->width - x0, ctb_size_y) >> hs; + const int height = FFMIN(fc->ps.sps->height - y0, ctb_size_y) >> vs; + + const int src_stride = fc->frame->linesize[c_idx]; + uint8_t* src = &fc->frame->data[c_idx][y * src_stride + (x << ps)]; + + alf_copy_ctb_to_hv(fc, src, src_stride, x, y, width, height, x_ctb, y_ctb, c_idx); + } +} + +void ff_vvc_alf_filter(VVCLocalContext *lc, const int x0, const int y0) +{ + VVCFrameContext *fc = lc->fc; + const VVCPPS *pps = fc->ps.pps; + const int x_ctb = x0 >> fc->ps.sps->ctb_log2_size_y; + const int y_ctb = y0 >> fc->ps.sps->ctb_log2_size_y; + const int ctb_size_y = fc->ps.sps->ctb_size_y; + const int ps = fc->ps.sps->pixel_shift; + const int padded_stride = EDGE_EMU_BUFFER_STRIDE << ps; + const int padded_offset = padded_stride * ALF_PADDING_SIZE + (ALF_PADDING_SIZE << ps); + const int c_end = fc->ps.sps->r->sps_chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : 1; + ALFParams *alf = &CTB(fc->tab.alf, x_ctb, y_ctb); + int edges[MAX_EDGES] = { x_ctb == 0, y_ctb == 0, x_ctb == pps->ctb_width - 1, y_ctb == pps->ctb_height - 1 }; + + if (!pps->r->pps_loop_filter_across_tiles_enabled_flag) { + edges[LEFT] = edges[LEFT] || (lc->boundary_flags & BOUNDARY_LEFT_TILE); + edges[TOP] = edges[TOP] || (lc->boundary_flags & BOUNDARY_UPPER_TILE); + edges[RIGHT] = edges[RIGHT] || pps->ctb_to_col_bd[x_ctb] != pps->ctb_to_col_bd[x_ctb + 1]; + edges[BOTTOM] = edges[BOTTOM] || pps->ctb_to_row_bd[y_ctb] != pps->ctb_to_row_bd[y_ctb + 1]; + } + + if (!pps->r->pps_loop_filter_across_slices_enabled_flag) { + edges[LEFT] = edges[LEFT] || (lc->boundary_flags & BOUNDARY_LEFT_SLICE); + edges[TOP] = edges[TOP] || (lc->boundary_flags & BOUNDARY_UPPER_SLICE); + edges[RIGHT] = edges[RIGHT] || CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb + 1, y_ctb); + edges[BOTTOM] = edges[BOTTOM] || CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb, y_ctb + 1); + } + + for (int c_idx = 0; c_idx < c_end; c_idx++) { + const int hs = fc->ps.sps->hshift[c_idx]; + const int vs = fc->ps.sps->vshift[c_idx]; + const int ctb_size_h = ctb_size_y >> hs; + const int ctb_size_v = ctb_size_y >> vs; + const int x = x0 >> hs; + const int y = y0 >> vs; + const int pic_width = fc->ps.sps->width >> hs; + const int pic_height = fc->ps.sps->height >> vs; + const int width = FFMIN(pic_width - x, ctb_size_h); + const int height = FFMIN(pic_height - y, ctb_size_v); + const int src_stride = fc->frame->linesize[c_idx]; + uint8_t *src = &fc->frame->data[c_idx][y * src_stride + (x << ps)]; + uint8_t *padded; + + if (alf->ctb_flag[c_idx] || (!c_idx && (alf->ctb_cc_idc[0] || alf->ctb_cc_idc[1]))) { + padded = (c_idx ? lc->alf_buffer_chroma : lc->alf_buffer_luma) + padded_offset; + alf_prepare_buffer(fc, padded, src, x, y, x_ctb, y_ctb, width, height, + padded_stride, src_stride, c_idx, edges); + } + if (alf->ctb_flag[c_idx]) { + if (!c_idx) { + alf_filter_luma(lc, src, padded, src_stride, padded_stride, x, y, + width, height, y + ctb_size_v - ALF_VB_POS_ABOVE_LUMA, alf); + } else { + alf_filter_chroma(lc, src, padded, src_stride, padded_stride, c_idx, + width, height, ctb_size_v - ALF_VB_POS_ABOVE_CHROMA, alf); + } + } + if (c_idx && alf->ctb_cc_idc[c_idx - 1]) { + padded = lc->alf_buffer_luma + padded_offset; + alf_filter_cc(lc, src, padded, src_stride, padded_stride, c_idx, + width, height, hs, vs, (ctb_size_v << vs) - ALF_VB_POS_ABOVE_LUMA, alf); + } + + alf->applied[c_idx] = 1; + } +} + + +void ff_vvc_lmcs_filter(const VVCLocalContext *lc, const int x, const int y) +{ + const SliceContext *sc = lc->sc; + const VVCFrameContext *fc = lc->fc; + const int ctb_size = fc->ps.sps->ctb_size_y; + const int width = FFMIN(fc->ps.pps->width - x, ctb_size); + const int height = FFMIN(fc->ps.pps->height - y, ctb_size); + uint8_t *data = fc->frame->data[LUMA] + y * fc->frame->linesize[LUMA] + (x << fc->ps.sps->pixel_shift); + if (sc->sh.r->sh_lmcs_used_flag) + fc->vvcdsp.lmcs.filter(data, fc->frame->linesize[LUMA], width, height, fc->ps.lmcs.inv_lut); +} diff --git a/libavcodec/vvc/vvc_filter.h b/libavcodec/vvc/vvc_filter.h new file mode 100644 index 00000000000..2fbfaa23b16 --- /dev/null +++ b/libavcodec/vvc/vvc_filter.h @@ -0,0 +1,70 @@ +/* + * VVC filters + * + * Copyright (C) 2022 Nuo Mi + * + * + * 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_VVC_FILTER_H +#define AVCODEC_VVC_FILTER_H + +#include "vvcdec.h" + +/** + * lmcs filter for the CTU + * @param lc local context for CTU + * @param x0 x position for the CTU + * @param y0 y position for the CTU + */ +void ff_vvc_lmcs_filter(const VVCLocalContext *lc, const int x0, const int y0); + +/** + * vertical deblock filter for the CTU + * @param lc local context for CTU + * @param x0 x position for the CTU + * @param y0 y position for the CTU + */ +void ff_vvc_deblock_vertical(const VVCLocalContext *lc, int x0, int y0); + +/** + * horizontal deblock filter for the CTU + * @param lc local context for CTU + * @param x0 x position for the CTU + * @param y0 y position for the CTU + */ +void ff_vvc_deblock_horizontal(const VVCLocalContext *lc, int x0, int y0); + +/** + * sao filter for the CTU + * @param lc local context for CTU + * @param x0 x position for the CTU + * @param y0 y position for the CTU + */ +void ff_vvc_sao_filter(VVCLocalContext *lc, const int x0, const int y0); + +void ff_vvc_alf_copy_ctu_to_hv(VVCLocalContext* lc, int x0, int y0); + +/** + * alf filter for the CTU + * @param lc local context for CTU + * @param x0 x position for the CTU + * @param y0 y position for the CTU + */ +void ff_vvc_alf_filter(VVCLocalContext *lc, const int x0, const int y0); + +#endif // AVCODEC_VVC_CTU_H diff --git a/libavcodec/vvc/vvc_filter_template.c b/libavcodec/vvc/vvc_filter_template.c new file mode 100644 index 00000000000..2ce3e93ff1e --- /dev/null +++ b/libavcodec/vvc/vvc_filter_template.c @@ -0,0 +1,1088 @@ +/* + * VVC filters DSP + * + * Copyright (C) 2022 Nuo Mi + * + * 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 + */ + +static void FUNC(lmcs_filter_luma)(uint8_t *_dst, ptrdiff_t dst_stride, const int width, const int height, const uint8_t *_lut) +{ + const pixel *lut = (const pixel *)_lut; + pixel *dst = (pixel*)_dst; + dst_stride /= sizeof(pixel); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = lut[dst[x]]; + dst += dst_stride; + } +} + +static void FUNC(sao_band_filter)(uint8_t *_dst, const uint8_t *_src, ptrdiff_t dst_stride, ptrdiff_t src_stride, + const int16_t *sao_offset_val, const int sao_left_class, const int width, const int height) +{ + pixel *dst = (pixel *)_dst; + const pixel *src = (pixel *)_src; + int offset_table[32] = { 0 }; + int k, y, x; + int shift = BIT_DEPTH - 5; + + dst_stride /= sizeof(pixel); + src_stride /= sizeof(pixel); + + for (k = 0; k < 4; k++) + offset_table[(k + sao_left_class) & 31] = sao_offset_val[k + 1]; + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) + dst[x] = av_clip_pixel(src[x] + offset_table[src[x] >> shift]); + dst += dst_stride; + src += src_stride; + } +} + +#define CMP(a, b) (((a) > (b)) - ((a) < (b))) + +static void FUNC(sao_edge_filter)(uint8_t *_dst, const uint8_t *_src, ptrdiff_t dst_stride, + const int16_t *sao_offset_val, const int eo, const int width, const int height) +{ + static const uint8_t edge_idx[] = { 1, 2, 0, 3, 4 }; + static const int8_t pos[4][2][2] = { + { { -1, 0 }, { 1, 0 } }, // horizontal + { { 0, -1 }, { 0, 1 } }, // vertical + { { -1, -1 }, { 1, 1 } }, // 45 degree + { { 1, -1 }, { -1, 1 } }, // 135 degree + }; + pixel *dst = (pixel *)_dst; + const pixel *src = (pixel *)_src; + int a_stride, b_stride; + int x, y; + ptrdiff_t src_stride = (2 * MAX_PB_SIZE + AV_INPUT_BUFFER_PADDING_SIZE) / sizeof(pixel); + dst_stride /= sizeof(pixel); + + a_stride = pos[eo][0][0] + pos[eo][0][1] * src_stride; + b_stride = pos[eo][1][0] + pos[eo][1][1] * src_stride; + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + int diff0 = CMP(src[x], src[x + a_stride]); + int diff1 = CMP(src[x], src[x + b_stride]); + int offset_val = edge_idx[2 + diff0 + diff1]; + dst[x] = av_clip_pixel(src[x] + sao_offset_val[offset_val]); + } + src += src_stride; + dst += dst_stride; + } +} + +static void FUNC(sao_edge_restore_0)(uint8_t *_dst, const uint8_t *_src, + ptrdiff_t dst_stride, ptrdiff_t src_stride, const SAOParams *sao, + const int *borders, const int _width, const int _height, const int c_idx, + const uint8_t *vert_edge, const uint8_t *horiz_edge, const uint8_t *diag_edge) +{ + int x, y; + pixel *dst = (pixel *)_dst; + const pixel *src = (pixel *)_src; + const int16_t *sao_offset_val = sao->offset_val[c_idx]; + const int sao_eo_class = sao->eo_class[c_idx]; + int init_x = 0, width = _width, height = _height; + + dst_stride /= sizeof(pixel); + src_stride /= sizeof(pixel); + + if (sao_eo_class != SAO_EO_VERT) { + if (borders[0]) { + int offset_val = sao_offset_val[0]; + for (y = 0; y < height; y++) { + dst[y * dst_stride] = av_clip_pixel(src[y * src_stride] + offset_val); + } + init_x = 1; + } + if (borders[2]) { + int offset_val = sao_offset_val[0]; + int offset = width - 1; + for (x = 0; x < height; x++) { + dst[x * dst_stride + offset] = av_clip_pixel(src[x * src_stride + offset] + offset_val); + } + width--; + } + } + if (sao_eo_class != SAO_EO_HORIZ) { + if (borders[1]) { + int offset_val = sao_offset_val[0]; + for (x = init_x; x < width; x++) + dst[x] = av_clip_pixel(src[x] + offset_val); + } + if (borders[3]) { + int offset_val = sao_offset_val[0]; + ptrdiff_t y_dst_stride = dst_stride * (height - 1); + ptrdiff_t y_src_stride = src_stride * (height - 1); + for (x = init_x; x < width; x++) + dst[x + y_dst_stride] = av_clip_pixel(src[x + y_src_stride] + offset_val); + height--; + } + } +} + +static void FUNC(sao_edge_restore_1)(uint8_t *_dst, const uint8_t *_src, + ptrdiff_t dst_stride, ptrdiff_t src_stride, const SAOParams *sao, + const int *borders, const int _width, const int _height, const int c_idx, + const uint8_t *vert_edge, const uint8_t *horiz_edge, const uint8_t *diag_edge) +{ + int x, y; + pixel *dst = (pixel *)_dst; + const pixel *src = (pixel *)_src; + const int16_t *sao_offset_val = sao->offset_val[c_idx]; + const int sao_eo_class = sao->eo_class[c_idx]; + int init_x = 0, init_y = 0, width = _width, height = _height; + + dst_stride /= sizeof(pixel); + src_stride /= sizeof(pixel); + + if (sao_eo_class != SAO_EO_VERT) { + if (borders[0]) { + int offset_val = sao_offset_val[0]; + for (y = 0; y < height; y++) { + dst[y * dst_stride] = av_clip_pixel(src[y * src_stride] + offset_val); + } + init_x = 1; + } + if (borders[2]) { + int offset_val = sao_offset_val[0]; + int offset = width - 1; + for (x = 0; x < height; x++) { + dst[x * dst_stride + offset] = av_clip_pixel(src[x * src_stride + offset] + offset_val); + } + width--; + } + } + if (sao_eo_class != SAO_EO_HORIZ) { + if (borders[1]) { + int offset_val = sao_offset_val[0]; + for (x = init_x; x < width; x++) + dst[x] = av_clip_pixel(src[x] + offset_val); + init_y = 1; + } + if (borders[3]) { + int offset_val = sao_offset_val[0]; + ptrdiff_t y_dst_stride = dst_stride * (height - 1); + ptrdiff_t y_src_stride = src_stride * (height - 1); + for (x = init_x; x < width; x++) + dst[x + y_dst_stride] = av_clip_pixel(src[x + y_src_stride] + offset_val); + height--; + } + } + + { + int save_upper_left = !diag_edge[0] && sao_eo_class == SAO_EO_135D && !borders[0] && !borders[1]; + int save_upper_right = !diag_edge[1] && sao_eo_class == SAO_EO_45D && !borders[1] && !borders[2]; + int save_lower_right = !diag_edge[2] && sao_eo_class == SAO_EO_135D && !borders[2] && !borders[3]; + int save_lower_left = !diag_edge[3] && sao_eo_class == SAO_EO_45D && !borders[0] && !borders[3]; + + // Restore pixels that can't be modified + if (vert_edge[0] && sao_eo_class != SAO_EO_VERT) { + for (y = init_y + save_upper_left; y < height - save_lower_left; y++) + dst[y * dst_stride] = src[y * src_stride]; + } + if (vert_edge[1] && sao_eo_class != SAO_EO_VERT) { + for (y = init_y + save_upper_right; y < height - save_lower_right; y++) + dst[y * dst_stride + width - 1] = src[y * src_stride + width - 1]; + } + + if (horiz_edge[0] && sao_eo_class != SAO_EO_HORIZ) { + for (x = init_x + save_upper_left; x < width - save_upper_right; x++) + dst[x] = src[x]; + } + if (horiz_edge[1] && sao_eo_class != SAO_EO_HORIZ) { + for (x = init_x + save_lower_left; x < width - save_lower_right; x++) + dst[(height - 1) * dst_stride + x] = src[(height - 1) * src_stride + x]; + } + if (diag_edge[0] && sao_eo_class == SAO_EO_135D) + dst[0] = src[0]; + if (diag_edge[1] && sao_eo_class == SAO_EO_45D) + dst[width - 1] = src[width - 1]; + if (diag_edge[2] && sao_eo_class == SAO_EO_135D) + dst[dst_stride * (height - 1) + width - 1] = src[src_stride * (height - 1) + width - 1]; + if (diag_edge[3] && sao_eo_class == SAO_EO_45D) + dst[dst_stride * (height - 1)] = src[src_stride * (height - 1)]; + + } +} + +#undef CMP + +static av_always_inline int16_t FUNC(alf_clip)(pixel curr, pixel v0, pixel v1, int16_t clip) +{ + return av_clip(v0 - curr, -clip, clip) + av_clip(v1 - curr, -clip, clip); +} + +static void FUNC(alf_filter_luma)(uint8_t *_dst, ptrdiff_t dst_stride, const uint8_t *_src, ptrdiff_t src_stride, + const int width, const int height, const int16_t *filter, const int16_t *clip, const int vb_pos) +{ + const pixel *src = (pixel *)_src; + const int shift = 7; + const int offset = 1 << ( shift - 1 ); + const int vb_above = vb_pos - 4; + const int vb_below = vb_pos + 3; + + dst_stride /= sizeof(pixel); + src_stride /= sizeof(pixel); + + for (int y = 0; y < height; y += ALF_BLOCK_SIZE) { + for (int x = 0; x < width; x += ALF_BLOCK_SIZE) { + const pixel *s0 = src + y * src_stride + x; + const pixel *s1 = s0 + src_stride; + const pixel *s2 = s0 - src_stride; + const pixel *s3 = s1 + src_stride; + const pixel *s4 = s2 - src_stride; + const pixel *s5 = s3 + src_stride; + const pixel *s6 = s4 - src_stride; + + for (int i = 0; i < ALF_BLOCK_SIZE; i++) { + pixel *dst = (pixel *)_dst + (y + i) * dst_stride + x; + + const pixel *p0 = s0 + i * src_stride; + const pixel *p1 = s1 + i * src_stride; + const pixel *p2 = s2 + i * src_stride; + const pixel *p3 = s3 + i * src_stride; + const pixel *p4 = s4 + i * src_stride; + const pixel *p5 = s5 + i * src_stride; + const pixel *p6 = s6 + i * src_stride; + + const int is_near_vb_above = (y + i < vb_pos) && (y + i >= vb_pos - 1); + const int is_near_vb_below = (y + i >= vb_pos) && (y + i <= vb_pos); + const int is_near_vb = is_near_vb_above || is_near_vb_below; + + if ((y + i < vb_pos) && ((y + i) >= vb_above)) { + p1 = (y + i == vb_pos - 1) ? p0 : p1; + p3 = (y + i >= vb_pos - 2) ? p1 : p3; + p5 = (y + i >= vb_pos - 3) ? p3 : p5; + + p2 = (y + i == vb_pos - 1) ? p0 : p2; + p4 = (y + i >= vb_pos - 2) ? p2 : p4; + p6 = (y + i >= vb_pos - 3) ? p4 : p6; + } else if ((y + i >= vb_pos) && ((y + i) <= vb_below)) { + p2 = (y + i == vb_pos ) ? p0 : p2; + p4 = (y + i <= vb_pos + 1) ? p2 : p4; + p6 = (y + i <= vb_pos + 2) ? p4 : p6; + + p1 = (y + i == vb_pos ) ? p0 : p1; + p3 = (y + i <= vb_pos + 1) ? p1 : p3; + p5 = (y + i <= vb_pos + 2) ? p3 : p5; + } + + for (int j = 0; j < ALF_BLOCK_SIZE; j++) { + int sum = 0; + const pixel curr = *p0; + + sum += filter[0] * FUNC(alf_clip)(curr, p5[+0], p6[+0], clip[0]); + sum += filter[1] * FUNC(alf_clip)(curr, p3[+1], p4[-1], clip[1]); + sum += filter[2] * FUNC(alf_clip)(curr, p3[+0], p4[+0], clip[2]); + sum += filter[3] * FUNC(alf_clip)(curr, p3[-1], p4[+1], clip[3]); + sum += filter[4] * FUNC(alf_clip)(curr, p1[+2], p2[-2], clip[4]); + sum += filter[5] * FUNC(alf_clip)(curr, p1[+1], p2[-1], clip[5]); + sum += filter[6] * FUNC(alf_clip)(curr, p1[+0], p2[+0], clip[6]); + sum += filter[7] * FUNC(alf_clip)(curr, p1[-1], p2[+1], clip[7]); + sum += filter[8] * FUNC(alf_clip)(curr, p1[-2], p2[+2], clip[8]); + sum += filter[9] * FUNC(alf_clip)(curr, p0[+3], p0[-3], clip[9]); + sum += filter[10] * FUNC(alf_clip)(curr, p0[+2], p0[-2], clip[10]); + sum += filter[11] * FUNC(alf_clip)(curr, p0[+1], p0[-1], clip[11]); + + if (!is_near_vb) + sum = (sum + offset) >> shift; + else + sum = (sum + (1 << ((shift + 3) - 1))) >> (shift + 3); + sum += curr; + dst[j] = CLIP(sum); + + p0++; + p1++; + p2++; + p3++; + p4++; + p5++; + p6++; + } + } + filter += ALF_NUM_COEFF_LUMA; + clip += ALF_NUM_COEFF_LUMA; + } + } +} + +static void FUNC(alf_filter_chroma)(uint8_t* _dst, ptrdiff_t dst_stride, const uint8_t* _src, ptrdiff_t src_stride, + const int width, const int height, const int16_t* filter, const int16_t* clip, const int vb_pos) +{ + const pixel *src = (pixel *)_src; + const int shift = 7; + const int offset = 1 << ( shift - 1 ); + const int vb_above = vb_pos - 2; + const int vb_below = vb_pos + 1; + + dst_stride /= sizeof(pixel); + src_stride /= sizeof(pixel); + + for (int y = 0; y < height; y += ALF_BLOCK_SIZE) { + for (int x = 0; x < width; x += ALF_BLOCK_SIZE) { + const pixel *s0 = src + y * src_stride + x; + const pixel *s1 = s0 + src_stride; + const pixel *s2 = s0 - src_stride; + const pixel *s3 = s1 + src_stride; + const pixel *s4 = s2 - src_stride; + const pixel *s5 = s3 + src_stride; + const pixel *s6 = s4 - src_stride; + + for (int i = 0; i < ALF_BLOCK_SIZE; i++) { + pixel *dst = (pixel *)_dst + (y + i) * dst_stride + x; + + const pixel *p0 = s0 + i * src_stride; + const pixel *p1 = s1 + i * src_stride; + const pixel *p2 = s2 + i * src_stride; + const pixel *p3 = s3 + i * src_stride; + const pixel *p4 = s4 + i * src_stride; + const pixel *p5 = s5 + i * src_stride; + const pixel *p6 = s6 + i * src_stride; + + const int is_near_vb_above = (y + i < vb_pos) && (y + i >= vb_pos - 1); + const int is_near_vb_below = (y + i >= vb_pos) && (y + i <= vb_pos); + const int is_near_vb = is_near_vb_above || is_near_vb_below; + + if ((y + i < vb_pos) && ((y + i) >= vb_above)) { + p1 = (y + i == vb_pos - 1) ? p0 : p1; + p3 = (y + i >= vb_pos - 2) ? p1 : p3; + p5 = (y + i >= vb_pos - 3) ? p3 : p5; + + p2 = (y + i == vb_pos - 1) ? p0 : p2; + p4 = (y + i >= vb_pos - 2) ? p2 : p4; + p6 = (y + i >= vb_pos - 3) ? p4 : p6; + } else if ((y + i >= vb_pos) && ((y + i) <= vb_below)) { + p2 = (y + i == vb_pos ) ? p0 : p2; + p4 = (y + i <= vb_pos + 1) ? p2 : p4; + p6 = (y + i <= vb_pos + 2) ? p4 : p6; + + p1 = (y + i == vb_pos ) ? p0 : p1; + p3 = (y + i <= vb_pos + 1) ? p1 : p3; + p5 = (y + i <= vb_pos + 2) ? p3 : p5; + } + + for (int j = 0; j < ALF_BLOCK_SIZE; j++) { + int sum = 0; + const pixel curr = *p0; + + sum += filter[0] * FUNC(alf_clip)(curr, p3[+0], p4[+0], clip[0]); + sum += filter[1] * FUNC(alf_clip)(curr, p1[+1], p2[-1], clip[1]); + sum += filter[2] * FUNC(alf_clip)(curr, p1[+0], p2[+0], clip[2]); + sum += filter[3] * FUNC(alf_clip)(curr, p1[-1], p2[+1], clip[3]); + sum += filter[4] * FUNC(alf_clip)(curr, p0[+2], p0[-2], clip[4]); + sum += filter[5] * FUNC(alf_clip)(curr, p0[+1], p0[-1], clip[5]); + + if (!is_near_vb) + sum = (sum + offset) >> shift; + else + sum = (sum + (1 << ((shift + 3) - 1))) >> (shift + 3); + sum += curr; + dst[j] = CLIP(sum); + + p0++; + p1++; + p2++; + p3++; + p4++; + p5++; + p6++; + } + } + } + } +} + +static void FUNC(alf_filter_cc)(uint8_t *_dst, ptrdiff_t dst_stride, const uint8_t *_luma, const ptrdiff_t luma_stride, + const int width, const int height, const int hs, const int vs, const int16_t *filter, const int vb_pos) +{ + const ptrdiff_t stride = luma_stride / sizeof(pixel); + + dst_stride /= sizeof(pixel); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int sum = 0; + pixel *dst = (pixel *)_dst + y * dst_stride + x; + const pixel *src = (pixel *)_luma + (y << vs) * stride + (x << hs); + + const pixel *s0 = src - stride; + const pixel *s1 = src; + const pixel *s2 = src + stride; + const pixel *s3 = src + 2 * stride; + + const int pos = y << vs; + if (!vs && (pos == vb_pos || pos == vb_pos + 1)) + continue; + + if (pos == (vb_pos - 2) || pos == (vb_pos + 1)) + s3 = s2; + else if (pos == (vb_pos - 1) || pos == vb_pos) + s3 = s2 = s0 = s1; + + + sum += filter[0] * (*s0 - *src); + sum += filter[1] * (*(s1 - 1) - *src); + sum += filter[2] * (*(s1 + 1) - *src); + sum += filter[3] * (*(s2 - 1) - *src); + sum += filter[4] * (*s2 - *src); + sum += filter[5] * (*(s2 + 1) - *src); + sum += filter[6] * (*s3 - *src); + sum = av_clip((sum + 64) >> 7, -(1 << (BIT_DEPTH - 1)), (1 << (BIT_DEPTH - 1)) - 1); + sum += *dst; + *dst = av_clip_pixel(sum); + } + } +} + +#define ALF_DIR_VERT 0 +#define ALF_DIR_HORZ 1 +#define ALF_DIR_DIGA0 2 +#define ALF_DIR_DIGA1 3 + +static void FUNC(alf_get_idx)(int *class_idx, int *transpose_idx, const int *sum, const int ac) +{ + static const int arg_var[] = {0, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4 }; + + int hv0, hv1, dir_hv, d0, d1, dir_d, hvd1, hvd0, sum_hv, dir1; + + dir_hv = sum[ALF_DIR_VERT] <= sum[ALF_DIR_HORZ]; + hv1 = FFMAX(sum[ALF_DIR_VERT], sum[ALF_DIR_HORZ]); + hv0 = FFMIN(sum[ALF_DIR_VERT], sum[ALF_DIR_HORZ]); + + dir_d = sum[ALF_DIR_DIGA0] <= sum[ALF_DIR_DIGA1]; + d1 = FFMAX(sum[ALF_DIR_DIGA0], sum[ALF_DIR_DIGA1]); + d0 = FFMIN(sum[ALF_DIR_DIGA0], sum[ALF_DIR_DIGA1]); + + //promote to avoid overflow + dir1 = (uint64_t)d1 * hv0 <= (uint64_t)hv1 * d0; + hvd1 = dir1 ? hv1 : d1; + hvd0 = dir1 ? hv0 : d0; + + sum_hv = sum[ALF_DIR_HORZ] + sum[ALF_DIR_VERT]; + *class_idx = arg_var[av_clip_uintp2(sum_hv * ac >> (BIT_DEPTH - 1), 4)]; + if (hvd1 * 2 > 9 * hvd0) + *class_idx += ((dir1 << 1) + 2) * 5; + else if (hvd1 > 2 * hvd0) + *class_idx += ((dir1 << 1) + 1) * 5; + + *transpose_idx = dir_d * 2 + dir_hv; +} + +static void FUNC(alf_classify)(int *class_idx, int *transpose_idx, + const uint8_t *_src, const ptrdiff_t _src_stride, const int width, const int height, + const int vb_pos, int *gradient_tmp) +{ + int *grad; + + const int h = height + ALF_GRADIENT_BORDER * 2; + const int w = width + ALF_GRADIENT_BORDER * 2; + const int size = (ALF_BLOCK_SIZE + ALF_GRADIENT_BORDER * 2) / ALF_GRADIENT_STEP; + const int gstride = (w / ALF_GRADIENT_STEP) * ALF_NUM_DIR; + + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + src -= (ALF_GRADIENT_BORDER + 1) * src_stride + ALF_GRADIENT_BORDER; + + grad = gradient_tmp; + for (int y = 0; y < h; y += ALF_GRADIENT_STEP) { + const pixel *s0 = src + y * src_stride; + const pixel *s1 = s0 + src_stride; + const pixel *s2 = s1 + src_stride; + const pixel *s3 = s2 + src_stride; + + if (y == vb_pos) //above + s3 = s2; + else if (y == vb_pos + ALF_GRADIENT_BORDER) + s0 = s1; + + for (int x = 0; x < w; x += ALF_GRADIENT_STEP) { + //two points a time + const pixel *a0 = s0 + x; + const pixel *p0 = s1 + x; + const pixel *b0 = s2 + x; + const int val0 = (*p0) << 1; + + const pixel *a1 = s1 + x + 1; + const pixel *p1 = s2 + x + 1; + const pixel *b1 = s3 + x + 1; + const int val1 = (*p1) << 1; + + grad[ALF_DIR_VERT] = FFABS(val0 - *a0 - *b0) + FFABS(val1 - *a1 - *b1); + grad[ALF_DIR_HORZ] = FFABS(val0 - *(p0 - 1) - *(p0 + 1)) + FFABS(val1 - *(p1 - 1) - *(p1 + 1)); + grad[ALF_DIR_DIGA0] = FFABS(val0 - *(a0 - 1) - *(b0 + 1)) + FFABS(val1 - *(a1 - 1) - *(b1 + 1)); + grad[ALF_DIR_DIGA1] = FFABS(val0 - *(a0 + 1) - *(b0 - 1)) + FFABS(val1 - *(a1 + 1) - *(b1 - 1)); + grad += ALF_NUM_DIR; + } + } + + for (int y = 0; y < height ; y += ALF_BLOCK_SIZE ) { + int start = 0; + int end = (ALF_BLOCK_SIZE + ALF_GRADIENT_BORDER * 2) / ALF_GRADIENT_STEP; + int ac = 2; + if (y + ALF_BLOCK_SIZE == vb_pos) { + end -= ALF_GRADIENT_BORDER / ALF_GRADIENT_STEP; + ac = 3; + } else if (y == vb_pos) { + start += ALF_GRADIENT_BORDER / ALF_GRADIENT_STEP; + ac = 3; + } + for (int x = 0; x < width; x += ALF_BLOCK_SIZE) { + const int xg = x / ALF_GRADIENT_STEP; + const int yg = y / ALF_GRADIENT_STEP; + int sum[ALF_NUM_DIR] = { 0 }; + + grad = gradient_tmp + (yg + start) * gstride + xg * ALF_NUM_DIR; + //todo: optimize this loop + for (int i = start; i < end; i++) { + for (int j = 0; j < size; j++) { + sum[ALF_DIR_VERT] += grad[ALF_DIR_VERT]; + sum[ALF_DIR_HORZ] += grad[ALF_DIR_HORZ]; + sum[ALF_DIR_DIGA0] += grad[ALF_DIR_DIGA0]; + sum[ALF_DIR_DIGA1] += grad[ALF_DIR_DIGA1]; + grad += ALF_NUM_DIR; + } + grad += gstride - size * ALF_NUM_DIR; + } + FUNC(alf_get_idx)(class_idx, transpose_idx, sum, ac); + + class_idx++; + transpose_idx++; + } + } + +} + +static void FUNC(alf_recon_coeff_and_clip)(int16_t *coeff, int16_t *clip, + const int *class_idx, const int *transpose_idx, const int size, + const int16_t *coeff_set, const uint8_t *clip_idx_set, const uint8_t *class_to_filt) +{ + const static int index[][ALF_NUM_COEFF_LUMA] = { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }, + { 9, 4, 10, 8, 1, 5, 11, 7, 3, 0, 2, 6 }, + { 0, 3, 2, 1, 8, 7, 6, 5, 4, 9, 10, 11 }, + { 9, 8, 10, 4, 3, 7, 11, 5, 1, 0, 2, 6 }, + }; + + const int16_t clip_set[] = { + 1 << BIT_DEPTH, 1 << (BIT_DEPTH - 3), 1 << (BIT_DEPTH - 5), 1 << (BIT_DEPTH - 7) + }; + + for (int i = 0; i < size; i++) { + const int16_t *src_coeff = coeff_set + class_to_filt[class_idx[i]] * ALF_NUM_COEFF_LUMA; + const uint8_t *clip_idx = clip_idx_set + class_idx[i] * ALF_NUM_COEFF_LUMA; + + for (int j = 0; j < ALF_NUM_COEFF_LUMA; j++) { + const int idx = index[transpose_idx[i]][j]; + *coeff++ = src_coeff[idx]; + *clip++ = clip_set[clip_idx[idx]]; + } + } +} + +#undef ALF_DIR_HORZ +#undef ALF_DIR_VERT +#undef ALF_DIR_DIGA0 +#undef ALF_DIR_DIGA1 + +// line zero +#define P7 pix[-8 * xstride] +#define P6 pix[-7 * xstride] +#define P5 pix[-6 * xstride] +#define P4 pix[-5 * xstride] +#define P3 pix[-4 * xstride] +#define P2 pix[-3 * xstride] +#define P1 pix[-2 * xstride] +#define P0 pix[-1 * xstride] +#define Q0 pix[0 * xstride] +#define Q1 pix[1 * xstride] +#define Q2 pix[2 * xstride] +#define Q3 pix[3 * xstride] +#define Q4 pix[4 * xstride] +#define Q5 pix[5 * xstride] +#define Q6 pix[6 * xstride] +#define Q7 pix[7 * xstride] +#define P(x) pix[(-(x)-1) * xstride] +#define Q(x) pix[(x) * xstride] + +// line three. used only for deblocking decision +#define TP7 pix[-8 * xstride + 3 * ystride] +#define TP6 pix[-7 * xstride + 3 * ystride] +#define TP5 pix[-6 * xstride + 3 * ystride] +#define TP4 pix[-5 * xstride + 3 * ystride] +#define TP3 pix[-4 * xstride + 3 * ystride] +#define TP2 pix[-3 * xstride + 3 * ystride] +#define TP1 pix[-2 * xstride + 3 * ystride] +#define TP0 pix[-1 * xstride + 3 * ystride] +#define TQ0 pix[0 * xstride + 3 * ystride] +#define TQ1 pix[1 * xstride + 3 * ystride] +#define TQ2 pix[2 * xstride + 3 * ystride] +#define TQ3 pix[3 * xstride + 3 * ystride] +#define TQ4 pix[4 * xstride + 3 * ystride] +#define TQ5 pix[5 * xstride + 3 * ystride] +#define TQ6 pix[6 * xstride + 3 * ystride] +#define TQ7 pix[7 * xstride + 3 * ystride] +#define TP(x) pix[(-(x)-1) * xstride + 3 * ystride] +#define TQ(x) pix[(x) * xstride + 3 * ystride] + +#define FP3 pix[-4 * xstride + 1 * ystride] +#define FP2 pix[-3 * xstride + 1 * ystride] +#define FP1 pix[-2 * xstride + 1 * ystride] +#define FP0 pix[-1 * xstride + 1 * ystride] +#define FQ0 pix[0 * xstride + 1 * ystride] +#define FQ1 pix[1 * xstride + 1 * ystride] +#define FQ2 pix[2 * xstride + 1 * ystride] +#define FQ3 pix[3 * xstride + 1 * ystride] + +static void FUNC(vvc_loop_filter_luma)(uint8_t* _pix, + ptrdiff_t _xstride, ptrdiff_t _ystride, + int _beta, int _tc, + uint8_t _no_p, uint8_t _no_q, + uint8_t max_len_p, uint8_t max_len_q, + int hor_ctu_edge) +{ + int d; + pixel* pix = (pixel*)_pix; + ptrdiff_t xstride = _xstride / sizeof(pixel); + ptrdiff_t ystride = _ystride / sizeof(pixel); + + const int dp0 = abs(P2 - 2 * P1 + P0); + const int dq0 = abs(Q2 - 2 * Q1 + Q0); + const int dp3 = abs(TP2 - 2 * TP1 + TP0); + const int dq3 = abs(TQ2 - 2 * TQ1 + TQ0); + const int d0 = dp0 + dq0; + const int d3 = dp3 + dq3; +#if BIT_DEPTH < 10 + const int tc = (_tc + (1 << (9 - BIT_DEPTH))) >> (10 - BIT_DEPTH); +#else + const int tc = _tc << (BIT_DEPTH - 10); +#endif + const int no_p = _no_p; + const int no_q = _no_q; + + const int large_p = (max_len_p > 3 && !hor_ctu_edge); + const int large_q = max_len_q > 3; + const int beta = _beta << BIT_DEPTH - 8; + + const int beta_3 = beta >> 3; + const int beta_2 = beta >> 2; + const int tc25 = ((tc * 5 + 1) >> 1); + + + if (large_p || large_q) { + const int dp0l = large_p ? ((dp0 + abs(P5 - 2 * P4 + P3) + 1) >> 1) : dp0; + const int dq0l = large_q ? ((dq0 + abs(Q5 - 2 * Q4 + Q3) + 1) >> 1) : dq0; + const int dp3l = large_p ? ((dp3 + abs(TP5 - 2 * TP4 + TP3) + 1) >> 1) : dp3; + const int dq3l = large_q ? ((dq3 + abs(TQ5 - 2 * TQ4 + TQ3) + 1) >> 1) : dq3; + const int d0l = dp0l + dq0l; + const int d3l = dp3l + dq3l; + const int beta53 = beta * 3 >> 5; + const int beta_4 = beta >> 4; + max_len_p = large_p ? max_len_p : 3; + max_len_q = large_q ? max_len_q : 3; + + if (d0l + d3l < beta) { + const int sp0l = abs(P3 - P0) + (max_len_p == 7 ? abs(P7 - P6 - P5 + P4) : 0); + const int sq0l = abs(Q0 - Q3) + (max_len_q == 7 ? abs(Q4 - Q5 - Q6 + Q7) : 0); + const int sp3l = abs(TP3 - TP0) + (max_len_p == 7 ? abs(TP7 - TP6 - TP5 + TP4) : 0); + const int sq3l = abs(TQ0 - TQ3) + (max_len_q == 7 ? abs(TQ4 - TQ5 - TQ6 + TQ7) : 0); + const int sp0 = large_p ? ((sp0l + abs(P3 - P(max_len_p)) + 1) >> 1) : sp0l; + const int sp3 = large_p ? ((sp3l + abs(TP3 - TP(max_len_p)) + 1) >> 1) : sp3l; + const int sq0 = large_q ? ((sq0l + abs(Q3 - Q(max_len_q)) + 1) >> 1) : sq0l; + const int sq3 = large_q ? ((sq3l + abs(TQ3 - TQ(max_len_q)) + 1) >> 1) : sq3l; + if (sp0 + sq0 < beta53 && abs(P0 - Q0) < tc25 && + sp3 + sq3 < beta53 && abs(TP0 - TQ0) < tc25 && + (d0l << 1) < beta_4 && (d3l << 1) < beta_4) { + for (d = 0; d < 4; d++) { + const int p6 = P6; + const int p5 = P5; + const int p4 = P4; + const int p3 = P3; + const int p2 = P2; + const int p1 = P1; + const int p0 = P0; + const int q0 = Q0; + const int q1 = Q1; + const int q2 = Q2; + const int q3 = Q3; + const int q4 = Q4; + const int q5 = Q5; + const int q6 = Q6; + int m; + if (max_len_p == 5 && max_len_q == 5) + m = (p4 + p3 + 2 * (p2 + p1 + p0 + q0 + q1 + q2) + q3 + q4 + 8) >> 4; + else if (max_len_p == max_len_q) + m = (p6 + p5 + p4 + p3 + p2 + p1 + 2 * (p0 + q0) + q1 + q2 + q3 + q4 + q5 + q6 + 8) >> 4; + else if (max_len_p + max_len_q == 12) + m = (p5 + p4 + p3 + p2 + 2 * (p1 + p0 + q0 + q1) + q2 + q3 + q4 + q5 + 8) >> 4; + else if (max_len_p + max_len_q == 8) + m = (p3 + p2 + p1 + p0 + q0 + q1 + q2 + q3 + 4) >> 3; + else if (max_len_q == 7) + m = (2 * (p2 + p1 + p0 + q0) + p0 + p1 + q1 + q2 + q3 + q4 + q5 + q6 + 8) >> 4; + else + m = (p6 + p5 + p4 + p3 + p2 + p1 + 2 * (q2 + q1 + q0 + p0) + q0 + q1 + 8) >> 4; + if (!no_p) { + const int refp = (P(max_len_p) + P(max_len_p - 1) + 1) >> 1; + if (max_len_p == 3) { + P0 = p0 + av_clip(((m * 53 + refp * 11 + 32) >> 6) - p0, -(tc * 6 >> 1), (tc * 6 >> 1)); + P1 = p1 + av_clip(((m * 32 + refp * 32 + 32) >> 6) - p1, -(tc * 4 >> 1), (tc * 4 >> 1)); + P2 = p2 + av_clip(((m * 11 + refp * 53 + 32) >> 6) - p2, -(tc * 2 >> 1), (tc * 2 >> 1)); + } else if (max_len_p == 5) { + P0 = p0 + av_clip(((m * 58 + refp * 6 + 32) >> 6) - p0, -(tc * 6 >> 1), (tc * 6 >> 1)); + P1 = p1 + av_clip(((m * 45 + refp * 19 + 32) >> 6) - p1, -(tc * 5 >> 1), (tc * 5 >> 1)); + P2 = p2 + av_clip(((m * 32 + refp * 32 + 32) >> 6) - p2, -(tc * 4 >> 1), (tc * 4 >> 1)); + P3 = p3 + av_clip(((m * 19 + refp * 45 + 32) >> 6) - p3, -(tc * 3 >> 1), (tc * 3 >> 1)); + P4 = p4 + av_clip(((m * 6 + refp * 58 + 32) >> 6) - p4, -(tc * 2 >> 1), (tc * 2 >> 1)); + } else { + P0 = p0 + av_clip(((m * 59 + refp * 5 + 32) >> 6) - p0, -(tc * 6 >> 1), (tc * 6 >> 1)); + P1 = p1 + av_clip(((m * 50 + refp * 14 + 32) >> 6) - p1, -(tc * 5 >> 1), (tc * 5 >> 1)); + P2 = p2 + av_clip(((m * 41 + refp * 23 + 32) >> 6) - p2, -(tc * 4 >> 1), (tc * 4 >> 1)); + P3 = p3 + av_clip(((m * 32 + refp * 32 + 32) >> 6) - p3, -(tc * 3 >> 1), (tc * 3 >> 1)); + P4 = p4 + av_clip(((m * 23 + refp * 41 + 32) >> 6) - p4, -(tc * 2 >> 1), (tc * 2 >> 1)); + P5 = p5 + av_clip(((m * 14 + refp * 50 + 32) >> 6) - p5, -(tc * 1 >> 1), (tc * 1 >> 1)); + P6 = p6 + av_clip(((m * 5 + refp * 59 + 32) >> 6) - p6, -(tc * 1 >> 1), (tc * 1 >> 1)); + } + } + if (!no_q) { + const int refq = (Q(max_len_q) + Q(max_len_q - 1) + 1) >> 1; + if (max_len_q == 3) { + Q0 = q0 + av_clip(((m * 53 + refq * 11 + 32) >> 6) - q0, -(tc * 6 >> 1), (tc * 6 >> 1)); + Q1 = q1 + av_clip(((m * 32 + refq * 32 + 32) >> 6) - q1, -(tc * 4 >> 1), (tc * 4 >> 1)); + Q2 = q2 + av_clip(((m * 11 + refq * 53 + 32) >> 6) - q2, -(tc * 2 >> 1), (tc * 2 >> 1)); + } else if (max_len_q == 5) { + Q0 = q0 + av_clip(((m * 58 + refq * 6 + 32) >> 6) - q0, -(tc * 6 >> 1), (tc * 6 >> 1)); + Q1 = q1 + av_clip(((m * 45 + refq * 19 + 32) >> 6) - q1, -(tc * 5 >> 1), (tc * 5 >> 1)); + Q2 = q2 + av_clip(((m * 32 + refq * 32 + 32) >> 6) - q2, -(tc * 4 >> 1), (tc * 4 >> 1)); + Q3 = q3 + av_clip(((m * 19 + refq * 45 + 32) >> 6) - q3, -(tc * 3 >> 1), (tc * 3 >> 1)); + Q4 = q4 + av_clip(((m * 6 + refq * 58 + 32) >> 6) - q4, -(tc * 2 >> 1), (tc * 2 >> 1)); + } else { + Q0 = q0 + av_clip(((m * 59 + refq * 5 + 32) >> 6) - q0, -(tc * 6 >> 1), (tc * 6 >> 1)); + Q1 = q1 + av_clip(((m * 50 + refq * 14 + 32) >> 6) - q1, -(tc * 5 >> 1), (tc * 5 >> 1)); + Q2 = q2 + av_clip(((m * 41 + refq * 23 + 32) >> 6) - q2, -(tc * 4 >> 1), (tc * 4 >> 1)); + Q3 = q3 + av_clip(((m * 32 + refq * 32 + 32) >> 6) - q3, -(tc * 3 >> 1), (tc * 3 >> 1)); + Q4 = q4 + av_clip(((m * 23 + refq * 41 + 32) >> 6) - q4, -(tc * 2 >> 1), (tc * 2 >> 1)); + Q5 = q5 + av_clip(((m * 14 + refq * 50 + 32) >> 6) - q5, -(tc * 1 >> 1), (tc * 1 >> 1)); + Q6 = q6 + av_clip(((m * 5 + refq * 59 + 32) >> 6) - q6, -(tc * 1 >> 1), (tc * 1 >> 1)); + } + + } + + pix += ystride; + } + return; + + } + } + } + if (d0 + d3 < beta) { + if (max_len_p > 2 && max_len_q > 2 && + abs(P3 - P0) + abs(Q3 - Q0) < beta_3 && abs(P0 - Q0) < tc25 && + abs(TP3 - TP0) + abs(TQ3 - TQ0) < beta_3 && abs(TP0 - TQ0) < tc25 && + (d0 << 1) < beta_2 && (d3 << 1) < beta_2) { + // strong filtering + const int tc2 = tc << 1; + const int tc3 = tc * 3; + for (d = 0; d < 4; d++) { + const int p3 = P3; + const int p2 = P2; + const int p1 = P1; + const int p0 = P0; + const int q0 = Q0; + const int q1 = Q1; + const int q2 = Q2; + const int q3 = Q3; + if (!no_p) { + P0 = p0 + av_clip(((p2 + 2 * p1 + 2 * p0 + 2 * q0 + q1 + 4) >> 3) - p0, -tc3, tc3); + P1 = p1 + av_clip(((p2 + p1 + p0 + q0 + 2) >> 2) - p1, -tc2, tc2); + P2 = p2 + av_clip(((2 * p3 + 3 * p2 + p1 + p0 + q0 + 4) >> 3) - p2, -tc, tc); + } + if (!no_q) { + Q0 = q0 + av_clip(((p1 + 2 * p0 + 2 * q0 + 2 * q1 + q2 + 4) >> 3) - q0, -tc3, tc3); + Q1 = q1 + av_clip(((p0 + q0 + q1 + q2 + 2) >> 2) - q1, -tc2, tc2); + Q2 = q2 + av_clip(((2 * q3 + 3 * q2 + q1 + q0 + p0 + 4) >> 3) - q2, -tc, tc); + } + pix += ystride; + } + } else { // weak filtering + int nd_p = 1; + int nd_q = 1; + const int tc_2 = tc >> 1; + if (max_len_p > 1 && max_len_q > 1) { + if (dp0 + dp3 < ((beta + (beta >> 1)) >> 3)) + nd_p = 2; + if (dq0 + dq3 < ((beta + (beta >> 1)) >> 3)) + nd_q = 2; + } + + for (d = 0; d < 4; d++) { + const int p2 = P2; + const int p1 = P1; + const int p0 = P0; + const int q0 = Q0; + const int q1 = Q1; + const int q2 = Q2; + int delta0 = (9 * (q0 - p0) - 3 * (q1 - p1) + 8) >> 4; + if (abs(delta0) < 10 * tc) { + delta0 = av_clip(delta0, -tc, tc); + if (!no_p) + P0 = av_clip_pixel(p0 + delta0); + if (!no_q) + Q0 = av_clip_pixel(q0 - delta0); + if (!no_p && nd_p > 1) { + const int deltap1 = av_clip((((p2 + p0 + 1) >> 1) - p1 + delta0) >> 1, -tc_2, tc_2); + P1 = av_clip_pixel(p1 + deltap1); + } + if (!no_q && nd_q > 1) { + const int deltaq1 = av_clip((((q2 + q0 + 1) >> 1) - q1 - delta0) >> 1, -tc_2, tc_2); + Q1 = av_clip_pixel(q1 + deltaq1); + } + } + pix += ystride; + } + } + } +} + +static void FUNC(vvc_loop_filter_chroma)(uint8_t *_pix, const ptrdiff_t _xstride, + const ptrdiff_t _ystride, const int _beta, const int _tc, const uint8_t no_p, const uint8_t no_q, + const int shift, int max_len_p, int max_len_q) +{ + pixel *pix = (pixel *)_pix; + const ptrdiff_t xstride = _xstride / sizeof(pixel); + const ptrdiff_t ystride = _ystride / sizeof(pixel); + const int end = shift ? 2 : 4; + const int beta = _beta << (BIT_DEPTH - 8); + const int beta_3 = beta >> 3; + const int beta_2 = beta >> 2; + +#if BIT_DEPTH < 10 + const int tc = (_tc + (1 << (9 - BIT_DEPTH))) >> (10 - BIT_DEPTH); +#else + const int tc = _tc << (BIT_DEPTH - 10); +#endif + const int tc25 = ((tc * 5 + 1) >> 1); + + if (!max_len_p || !max_len_q) + return; + + if (max_len_q == 3){ + const int p1n = shift ? FP1 : TP1; + const int p2n = max_len_p == 1 ? p1n : (shift ? FP2 : TP2); + const int p0n = shift ? FP0 : TP0; + const int q0n = shift ? FQ0 : TQ0; + const int q1n = shift ? FQ1 : TQ1; + const int q2n = shift ? FQ2 : TQ2; + const int p3 = max_len_p == 1 ? P1 : P3; + const int p2 = max_len_p == 1 ? P1 : P2; + const int p1 = P1; + const int p0 = P0; + const int dp0 = abs(p2 - 2 * p1 + p0); + const int dq0 = abs(Q2 - 2 * Q1 + Q0); + + const int dp1 = abs(p2n - 2 * p1n + p0n); + const int dq1 = abs(q2n - 2 * q1n + q0n); + const int d0 = dp0 + dq0; + const int d1 = dp1 + dq1; + + if (d0 + d1 < beta) { + const int p3n = max_len_p == 1 ? p1n : (shift ? FP3 : TP3); + const int q3n = shift ? FQ3 : TQ3; + const int dsam0 = (d0 << 1) < beta_2 && (abs(p3 - p0) + abs(Q0 - Q3) < beta_3) && + abs(p0 - Q0) < tc25; + const int dsam1 = (d1 << 1) < beta_2 && (abs(p3n - p0n) + abs(q0n - q3n) < beta_3) && + abs(p0n - q0n) < tc25; + if (!dsam0 || !dsam1) + max_len_p = max_len_q = 1; + } else { + max_len_p = max_len_q = 1; + } + } + + if (max_len_p == 3 && max_len_q == 3) { + //strong + for (int d = 0; d < end; d++) { + const int p3 = P3; + const int p2 = P2; + const int p1 = P1; + const int p0 = P0; + const int q0 = Q0; + const int q1 = Q1; + const int q2 = Q2; + const int q3 = Q3; + if (!no_p) { + P0 = av_clip((p3 + p2 + p1 + 2 * p0 + q0 + q1 + q2 + 4) >> 3, p0 - tc, p0 + tc); + P1 = av_clip((2 * p3 + p2 + 2 * p1 + p0 + q0 + q1 + 4) >> 3, p1 - tc, p1 + tc); + P2 = av_clip((3 * p3 + 2 * p2 + p1 + p0 + q0 + 4) >> 3, p2 - tc, p2 + tc ); + } + if (!no_q) { + Q0 = av_clip((p2 + p1 + p0 + 2 * q0 + q1 + q2 + q3 + 4) >> 3, q0 - tc, q0 + tc); + Q1 = av_clip((p1 + p0 + q0 + 2 * q1 + q2 + 2 * q3 + 4) >> 3, q1 - tc, q1 + tc); + Q2 = av_clip((p0 + q0 + q1 + 2 * q2 + 3 * q3 + 4) >> 3, q2 - tc, q2 + tc); + } + pix += ystride; + } + } else if (max_len_q == 3) { + for (int d = 0; d < end; d++) { + const int p1 = P1; + const int p0 = P0; + const int q0 = Q0; + const int q1 = Q1; + const int q2 = Q2; + const int q3 = Q3; + if (!no_p) { + P0 = av_clip((3 * p1 + 2 * p0 + q0 + q1 + q2 + 4) >> 3, p0 - tc, p0 + tc); + } + if (!no_q) { + Q0 = av_clip((2 * p1 + p0 + 2 * q0 + q1 + q2 + q3 + 4) >> 3, q0 - tc, q0 + tc); + Q1 = av_clip((p1 + p0 + q0 + 2 * q1 + q2 + 2 * q3 + 4) >> 3, q1 - tc, q1 + tc); + Q2 = av_clip((p0 + q0 + q1 + 2 * q2 + 3 * q3 + 4) >> 3, q2 - tc, q2 + tc); + } + pix += ystride; + } + } else { + //weak + for (int d = 0; d < end; d++) { + int delta0; + const int p1 = P1; + const int p0 = P0; + const int q0 = Q0; + const int q1 = Q1; + delta0 = av_clip((((q0 - p0) * 4) + p1 - q1 + 4) >> 3, -tc, tc); + if (!no_p) + P0 = av_clip_pixel(p0 + delta0); + if (!no_q) + Q0 = av_clip_pixel(q0 - delta0); + pix += ystride; + } + } +} + +static void FUNC(vvc_h_loop_filter_chroma)(uint8_t *pix, ptrdiff_t stride, + int beta, int32_t tc, uint8_t no_p, uint8_t no_q, int shift, int max_len_p, int max_len_q) +{ + FUNC(vvc_loop_filter_chroma)(pix, stride, sizeof(pixel), beta, tc, no_p, no_q, shift, max_len_p, max_len_q); +} + +static void FUNC(vvc_v_loop_filter_chroma)(uint8_t *pix, ptrdiff_t stride, + int beta, int32_t tc, uint8_t no_p, uint8_t no_q, int shift, int max_len_p, int max_len_q) +{ + FUNC(vvc_loop_filter_chroma)(pix, sizeof(pixel), stride, beta, tc, no_p, no_q, shift, max_len_p, max_len_q); +} + +static void FUNC(vvc_h_loop_filter_luma)(uint8_t *pix, ptrdiff_t stride, + int beta, int32_t tc, uint8_t no_p, + uint8_t no_q, uint8_t max_len_p, uint8_t max_len_q, int hor_ctu_edge) +{ + FUNC(vvc_loop_filter_luma)(pix, stride, sizeof(pixel), + beta, tc, no_p, no_q, max_len_p, max_len_q, hor_ctu_edge); +} + +static void FUNC(vvc_v_loop_filter_luma)(uint8_t *pix, ptrdiff_t stride, + int beta, int32_t tc, uint8_t no_p, + uint8_t no_q, uint8_t max_len_p, uint8_t max_len_q, int hor_ctu_edge) +{ + FUNC(vvc_loop_filter_luma)(pix, sizeof(pixel), stride, + beta, tc, no_p, no_q, max_len_p, max_len_q, hor_ctu_edge); +} + +static int FUNC(vvc_loop_ladf_level)(const uint8_t *_pix, const ptrdiff_t _xstride, const ptrdiff_t _ystride) +{ + const pixel *pix = (pixel *)_pix; + const ptrdiff_t xstride = _xstride / sizeof(pixel); + const ptrdiff_t ystride = _ystride / sizeof(pixel); + return (P0 + TP0 + Q0 + TQ0) >> 2; +} + +static int FUNC(vvc_h_loop_ladf_level)(const uint8_t *pix, ptrdiff_t stride) +{ + return FUNC(vvc_loop_ladf_level)(pix, stride, sizeof(pixel)); +} + +static int FUNC(vvc_v_loop_ladf_level)(const uint8_t *pix, ptrdiff_t stride) +{ + return FUNC(vvc_loop_ladf_level)(pix, sizeof(pixel), stride); +} + +#undef P7 +#undef P6 +#undef P5 +#undef P4 +#undef P3 +#undef P2 +#undef P1 +#undef P0 +#undef Q0 +#undef Q1 +#undef Q2 +#undef Q3 +#undef Q4 +#undef Q5 +#undef Q6 +#undef Q7 + +#undef TP7 +#undef TP6 +#undef TP5 +#undef TP4 +#undef TP3 +#undef TP2 +#undef TP1 +#undef TP0 +#undef TQ0 +#undef TQ1 +#undef TQ2 +#undef TQ3 +#undef TQ4 +#undef TQ5 +#undef TQ6 +#undef TQ7 + +static void FUNC(ff_vvc_lmcs_dsp_init)(VVCLMCSDSPContext *const lmcs) +{ + lmcs->filter = FUNC(lmcs_filter_luma); +} + +static void FUNC(ff_vvc_lf_dsp_init)(VVCLFDSPContext *const lf) +{ + lf->ladf_level[0] = FUNC(vvc_h_loop_ladf_level); + lf->ladf_level[1] = FUNC(vvc_v_loop_ladf_level); + lf->filter_luma[0] = FUNC(vvc_h_loop_filter_luma); + lf->filter_luma[1] = FUNC(vvc_v_loop_filter_luma); + lf->filter_chroma[0] = FUNC(vvc_h_loop_filter_chroma); + lf->filter_chroma[1] = FUNC(vvc_v_loop_filter_chroma); +} + +static void FUNC(ff_vvc_sao_dsp_init)(VVCSAODSPContext *const sao) +{ + for (int i = 0; i < FF_ARRAY_ELEMS(sao->band_filter); i++) + sao->band_filter[i] = FUNC(sao_band_filter); + for (int i = 0; i < FF_ARRAY_ELEMS(sao->edge_filter); i++) + sao->edge_filter[i] = FUNC(sao_edge_filter); + sao->edge_restore[0] = FUNC(sao_edge_restore_0); + sao->edge_restore[1] = FUNC(sao_edge_restore_1); +} + +static void FUNC(ff_vvc_alf_dsp_init)(VVCALFDSPContext *const alf) +{ + alf->filter[LUMA] = FUNC(alf_filter_luma); + alf->filter[CHROMA] = FUNC(alf_filter_chroma); + alf->filter_cc = FUNC(alf_filter_cc); + alf->classify = FUNC(alf_classify); + alf->recon_coeff_and_clip = FUNC(alf_recon_coeff_and_clip); +} diff --git a/libavcodec/vvc/vvc_inter.c b/libavcodec/vvc/vvc_inter.c new file mode 100644 index 00000000000..ef8727bbb0a --- /dev/null +++ b/libavcodec/vvc/vvc_inter.c @@ -0,0 +1,992 @@ +/* + * VVC inter prediction + * + * Copyright (C) 2022 Nuo Mi + * + * 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 "vvc_data.h" +#include "vvc_inter.h" +#include "vvc_mvs.h" +#include "vvc_refs.h" + +static const int bcw_w_lut[] = {4, 5, 3, 10, -2}; + +static int emulated_edge(const VVCFrameContext *fc, uint8_t *dst, const uint8_t **src, ptrdiff_t *src_stride, + const int x_off, const int y_off, const int block_w, const int block_h, const int is_luma) +{ + const int extra_before = is_luma ? LUMA_EXTRA_BEFORE : CHROMA_EXTRA_BEFORE; + const int extra_after = is_luma ? LUMA_EXTRA_AFTER : CHROMA_EXTRA_AFTER; + const int extra = is_luma ? LUMA_EXTRA : CHROMA_EXTRA; + const int pic_width = is_luma ? fc->ps.pps->width : (fc->ps.pps->width >> fc->ps.sps->hshift[1]); + const int pic_height = is_luma ? fc->ps.pps->height : (fc->ps.pps->height >> fc->ps.sps->vshift[1]); + + if (x_off < extra_before || y_off < extra_before || + x_off >= pic_width - block_w - extra_after || + y_off >= pic_height - block_h - extra_after) { + const ptrdiff_t edge_emu_stride = EDGE_EMU_BUFFER_STRIDE << fc->ps.sps->pixel_shift; + int offset = extra_before * *src_stride + (extra_before << fc->ps.sps->pixel_shift); + int buf_offset = extra_before * edge_emu_stride + (extra_before << fc->ps.sps->pixel_shift); + + fc->vdsp.emulated_edge_mc(dst, *src - offset, edge_emu_stride, *src_stride, + block_w + extra, block_h + extra, x_off - extra_before, y_off - extra_before, + pic_width, pic_height); + + *src = dst + buf_offset; + *src_stride = edge_emu_stride; + return 1; + } + return 0; +} + +static void emulated_edge_dmvr(const VVCFrameContext *fc, uint8_t *dst, const uint8_t **src, ptrdiff_t *src_stride, + const int x_sb, const int y_sb, const int x_off, const int y_off, const int block_w, const int block_h, const int is_luma) +{ + const int extra_before = is_luma ? LUMA_EXTRA_BEFORE : CHROMA_EXTRA_BEFORE; + const int extra_after = is_luma ? LUMA_EXTRA_AFTER : CHROMA_EXTRA_AFTER; + const int extra = is_luma ? LUMA_EXTRA : CHROMA_EXTRA; + const int pic_width = is_luma ? fc->ps.pps->width : (fc->ps.pps->width >> fc->ps.sps->hshift[1]); + const int pic_height = is_luma ? fc->ps.pps->height : (fc->ps.pps->height >> fc->ps.sps->vshift[1]); + + if (x_off < extra_before || y_off < extra_before || + x_off >= pic_width - block_w - extra_after || + y_off >= pic_height - block_h - extra_after|| + (x_off != x_sb || y_off != y_sb)) { + const int ps = fc->ps.sps->pixel_shift; + const ptrdiff_t edge_emu_stride = EDGE_EMU_BUFFER_STRIDE << ps; + const int offset = extra_before * *src_stride + (extra_before << ps); + const int buf_offset = extra_before * edge_emu_stride + (extra_before << ps); + + const int start_x = FFMIN(FFMAX(x_sb - extra_before, 0), pic_width - 1); + const int start_y = FFMIN(FFMAX(y_sb - extra_before, 0), pic_height - 1); + const int width = FFMAX(FFMIN(pic_width, x_sb + block_w + extra_after) - start_x, 1); + const int height = FFMAX(FFMIN(pic_height, y_sb + block_h + extra_after) - start_y, 1); + + fc->vdsp.emulated_edge_mc(dst, *src - offset, edge_emu_stride, *src_stride, block_w + extra, block_h + extra, + x_off - start_x - extra_before, y_off - start_y - extra_before, width, height); + + *src = dst + buf_offset; + *src_stride = edge_emu_stride; + } +} + +static void emulated_edge_bilinear(const VVCFrameContext *fc, uint8_t *dst, uint8_t **src, ptrdiff_t *src_stride, + const int x_off, const int y_off, const int block_w, const int block_h) +{ + int pic_width = fc->ps.pps->width; + int pic_height = fc->ps.pps->height; + + if (x_off < BILINEAR_EXTRA_BEFORE || y_off < BILINEAR_EXTRA_BEFORE || + x_off >= pic_width - block_w - BILINEAR_EXTRA_AFTER || + y_off >= pic_height - block_h - BILINEAR_EXTRA_AFTER) { + const ptrdiff_t edge_emu_stride = EDGE_EMU_BUFFER_STRIDE << fc->ps.sps->pixel_shift; + const int offset = BILINEAR_EXTRA_BEFORE * *src_stride + (BILINEAR_EXTRA_BEFORE << fc->ps.sps->pixel_shift); + const int buf_offset = BILINEAR_EXTRA_BEFORE * edge_emu_stride + (BILINEAR_EXTRA_BEFORE << fc->ps.sps->pixel_shift); + + fc->vdsp.emulated_edge_mc(dst, *src - offset, edge_emu_stride, *src_stride, block_w + BILINEAR_EXTRA, block_h + BILINEAR_EXTRA, + x_off - BILINEAR_EXTRA_BEFORE, y_off - BILINEAR_EXTRA_BEFORE, pic_width, pic_height); + + *src = dst + buf_offset; + *src_stride = edge_emu_stride; + } +} + + +#define EMULATED_EDGE_LUMA(dst, src, src_stride, x_off, y_off) \ + emulated_edge(fc, dst, src, src_stride, x_off, y_off, block_w, block_h, 1) + +#define EMULATED_EDGE_CHROMA(dst, src, src_stride, x_off, y_off) \ + emulated_edge(fc, dst, src, src_stride, x_off, y_off, block_w, block_h, 0) + +#define EMULATED_EDGE_DMVR_LUMA(dst, src, src_stride, x_sb, y_sb, x_off, y_off) \ + emulated_edge_dmvr(fc, dst, src, src_stride, x_sb, y_sb, x_off, y_off, block_w, block_h, 1) + +#define EMULATED_EDGE_DMVR_CHROMA(dst, src, src_stride, x_sb, y_sb, x_off, y_off) \ + emulated_edge_dmvr(fc, dst, src, src_stride, x_sb, y_sb, x_off, y_off, block_w, block_h, 0) + +#define EMULATED_EDGE_BILINEAR(dst, src, src_stride, x_off, y_off) \ + emulated_edge_bilinear(fc, dst, src, src_stride, x_off, y_off, pred_w, pred_h) + +// part of 8.5.6.6 Weighted sample prediction process +static int derive_weight_uni(int *denom, int *wx, int *ox, + const VVCLocalContext *lc, const MvField *mvf, const int c_idx) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPPS *pps = fc->ps.pps; + const VVCSH *sh = &lc->sc->sh; + const int weight_flag = (IS_P(sh->r) && pps->r->pps_weighted_pred_flag) || + (IS_B(sh->r) && pps->r->pps_weighted_bipred_flag); + if (weight_flag) { + const int lx = mvf->pred_flag - PF_L0; + const PredWeightTable *w = pps->r->pps_wp_info_in_ph_flag ? &fc->ps.ph.pwt : &sh->pwt; + + *denom = w->log2_denom[c_idx > 0]; + *wx = w->weight[lx][c_idx][mvf->ref_idx[lx]]; + *ox = w->offset[lx][c_idx][mvf->ref_idx[lx]]; + } + return weight_flag; +} + +// part of 8.5.6.6 Weighted sample prediction process +static int derive_weight(int *denom, int *w0, int *w1, int *o0, int *o1, + const VVCLocalContext *lc, const MvField *mvf, const int c_idx, const int dmvr_flag) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPPS *pps = fc->ps.pps; + const VVCSH *sh = &lc->sc->sh; + const int bcw_idx = mvf->bcw_idx; + const int weight_flag = (IS_P(sh->r) && pps->r->pps_weighted_pred_flag) || + (IS_B(sh->r) && pps->r->pps_weighted_bipred_flag && !dmvr_flag); + if ((!weight_flag && !bcw_idx) || (bcw_idx && lc->cu->ciip_flag)) + return 0; + + if (bcw_idx) { + *denom = 2; + *w1 = bcw_w_lut[bcw_idx]; + *w0 = 8 - *w1; + *o0 = *o1 = 0; + } else { + const VVCPPS *pps = fc->ps.pps; + const PredWeightTable *w = pps->r->pps_wp_info_in_ph_flag ? &fc->ps.ph.pwt : &sh->pwt; + + *denom = w->log2_denom[c_idx > 0]; + *w0 = w->weight[L0][c_idx][mvf->ref_idx[L0]]; + *w1 = w->weight[L1][c_idx][mvf->ref_idx[L1]]; + *o0 = w->offset[L0][c_idx][mvf->ref_idx[L0]]; + *o1 = w->offset[L1][c_idx][mvf->ref_idx[L1]]; + } + return 1; +} + +static void luma_mc(VVCLocalContext *lc, int16_t *dst, const AVFrame *ref, const Mv *mv, + int x_off, int y_off, const int block_w, const int block_h) +{ + const VVCFrameContext *fc = lc->fc; + const uint8_t *src = ref->data[0]; + ptrdiff_t src_stride = ref->linesize[0]; + + const int mx = mv->x & 0xf; + const int my = mv->y & 0xf; + + x_off += mv->x >> 4; + y_off += mv->y >> 4; + src += y_off * src_stride + (x_off * (1 << fc->ps.sps->pixel_shift)); + + EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off); + + fc->vvcdsp.inter.put[LUMA][!!my][!!mx](dst, src, src_stride, block_h, mx, my, block_w, 0, 0); +} + +static void chroma_mc(VVCLocalContext *lc, int16_t *dst, const AVFrame *ref, const Mv *mv, + int x_off, int y_off, const int block_w, const int block_h, const int c_idx) +{ + const VVCFrameContext *fc = lc->fc; + const uint8_t *src = ref->data[c_idx]; + ptrdiff_t src_stride = ref->linesize[c_idx]; + int hs = fc->ps.sps->hshift[c_idx]; + int vs = fc->ps.sps->vshift[c_idx]; + + const intptr_t mx = av_mod_uintp2(mv->x, 4 + hs); + const intptr_t my = av_mod_uintp2(mv->y, 4 + vs); + const intptr_t _mx = mx << (1 - hs); + const intptr_t _my = my << (1 - vs); + + x_off += mv->x >> (4 + hs); + y_off += mv->y >> (4 + vs); + src += y_off * src_stride + (x_off * (1 << fc->ps.sps->pixel_shift)); + + EMULATED_EDGE_CHROMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off); + fc->vvcdsp.inter.put[CHROMA][!!my][!!mx](dst, src, src_stride, block_h, _mx, _my, block_w, 0, 0); +} + +static void luma_mc_uni(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride, + const AVFrame *ref, const MvField *mvf, int x_off, int y_off, const int block_w, const int block_h, + const int hf_idx, const int vf_idx) +{ + const VVCFrameContext *fc = lc->fc; + const int lx = mvf->pred_flag - PF_L0; + const Mv *mv = mvf->mv + lx; + const uint8_t *src = ref->data[0]; + ptrdiff_t src_stride = ref->linesize[0]; + const int mx = mv->x & 0xf; + const int my = mv->y & 0xf; + int denom, wx, ox; + + x_off += mv->x >> 4; + y_off += mv->y >> 4; + src += y_off * src_stride + (x_off * (1 << fc->ps.sps->pixel_shift)); + + EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off); + + if (derive_weight_uni(&denom, &wx, &ox, lc, mvf, LUMA)) { + fc->vvcdsp.inter.put_uni_w[LUMA][!!my][!!mx](dst, dst_stride, src, src_stride, + block_h, denom, wx, ox, mx, my, block_w, hf_idx, vf_idx); + } else { + fc->vvcdsp.inter.put_uni[LUMA][!!my][!!mx](dst, dst_stride, src, src_stride, + block_h, mx, my, block_w, hf_idx, vf_idx); + } +} + +static void luma_bdof(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride, + const uint8_t *_src0, const ptrdiff_t src0_stride, const int mx0, const int my0, + const uint8_t *_src1, const ptrdiff_t src1_stride, const int mx1, const int my1, + const int block_w, const int block_h, const int hf_idx, const int vf_idx) +{ + const VVCFrameContext *fc = lc->fc; + int16_t *tmp0 = lc->tmp + 1 + MAX_PB_SIZE; + int16_t *tmp1 = lc->tmp1 + 1 + MAX_PB_SIZE; + + fc->vvcdsp.inter.put[LUMA][!!my0][!!mx0](tmp0, _src0, src0_stride, + block_h, mx0, my0, block_w, hf_idx, vf_idx); + fc->vvcdsp.inter.bdof_fetch_samples(tmp0, _src0, src0_stride, mx0, my0, block_w, block_h); + + fc->vvcdsp.inter.put[LUMA][!!my1][!!mx1](tmp1, _src1, src1_stride, + block_h, mx1, my1, block_w, hf_idx, vf_idx); + fc->vvcdsp.inter.bdof_fetch_samples(tmp1, _src1, src1_stride, mx1, my1, block_w, block_h); + fc->vvcdsp.inter.apply_bdof(dst, dst_stride, tmp0, tmp1, block_w, block_h); +} + + static void luma_mc_bi(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride, + const AVFrame *ref0, const Mv *mv0, const int x_off, const int y_off, const int block_w, const int block_h, + const AVFrame *ref1, const Mv *mv1, const MvField *mvf, const int hf_idx, const int vf_idx, + const MvField *orig_mv, const int sb_bdof_flag) +{ + const VVCFrameContext *fc = lc->fc; + const PredictionUnit *pu = &lc->cu->pu; + ptrdiff_t src0_stride = ref0->linesize[0]; + ptrdiff_t src1_stride = ref1->linesize[0]; + const int mx0 = mv0->x & 0xf; + const int my0 = mv0->y & 0xf; + const int mx1 = mv1->x & 0xf; + const int my1 = mv1->y & 0xf; + + const int x_off0 = x_off + (mv0->x >> 4); + const int y_off0 = y_off + (mv0->y >> 4); + const int x_off1 = x_off + (mv1->x >> 4); + const int y_off1 = y_off + (mv1->y >> 4); + + const uint8_t *src0 = ref0->data[0] + y_off0 * src0_stride + (int)((unsigned)x_off0 << fc->ps.sps->pixel_shift); + const uint8_t *src1 = ref1->data[0] + y_off1 * src1_stride + (int)((unsigned)x_off1 << fc->ps.sps->pixel_shift); + + if (pu->dmvr_flag) { + const int x_sb0 = x_off + (orig_mv->mv[L0].x >> 4); + const int y_sb0 = y_off + (orig_mv->mv[L0].y >> 4); + const int x_sb1 = x_off + (orig_mv->mv[L1].x >> 4); + const int y_sb1 = y_off + (orig_mv->mv[L1].y >> 4); + EMULATED_EDGE_DMVR_LUMA(lc->edge_emu_buffer, &src0, &src0_stride, x_sb0, y_sb0, x_off0, y_off0); + EMULATED_EDGE_DMVR_LUMA(lc->edge_emu_buffer2, &src1, &src1_stride, x_sb1, y_sb1, x_off1, y_off1); + } else { + EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src0, &src0_stride, x_off0, y_off0); + EMULATED_EDGE_LUMA(lc->edge_emu_buffer2, &src1, &src1_stride, x_off1, y_off1); + } + if (sb_bdof_flag) { + luma_bdof(lc, dst, dst_stride, src0, src0_stride, mx0, my0, src1, src1_stride, mx1, my1, + block_w, block_h, hf_idx, vf_idx); + } else { + int denom, w0, w1, o0, o1; + fc->vvcdsp.inter.put[LUMA][!!my0][!!mx0](lc->tmp, src0, src0_stride, + block_h, mx0, my0, block_w, hf_idx, vf_idx); + if (derive_weight(&denom, &w0, &w1, &o0, &o1, lc, mvf, LUMA, pu->dmvr_flag)) { + fc->vvcdsp.inter.put_bi_w[LUMA][!!my1][!!mx1](dst, dst_stride, src1, src1_stride, lc->tmp, + block_h, denom, w0, w1, o0, o1, mx1, my1, block_w, hf_idx, vf_idx); + } else { + fc->vvcdsp.inter.put_bi[LUMA][!!my1][!!mx1](dst, dst_stride, src1, src1_stride, lc->tmp, + block_h, mx1, my1, block_w, hf_idx, vf_idx); + } + } +} + +static void chroma_mc_uni(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride, + const uint8_t *src, ptrdiff_t src_stride, int x_off, int y_off, + const int block_w, const int block_h, const MvField *mvf, const int c_idx, + const int hf_idx, const int vf_idx) +{ + const VVCFrameContext *fc = lc->fc; + const int lx = mvf->pred_flag - PF_L0; + const int hs = fc->ps.sps->hshift[1]; + const int vs = fc->ps.sps->vshift[1]; + const Mv *mv = &mvf->mv[lx]; + const intptr_t mx = av_mod_uintp2(mv->x, 4 + hs); + const intptr_t my = av_mod_uintp2(mv->y, 4 + vs); + const intptr_t _mx = mx << (1 - hs); + const intptr_t _my = my << (1 - vs); + int denom, wx, ox; + + x_off += mv->x >> (4 + hs); + y_off += mv->y >> (4 + vs); + src += y_off * src_stride + (x_off * (1 << fc->ps.sps->pixel_shift)); + + EMULATED_EDGE_CHROMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off); + if (derive_weight_uni(&denom, &wx, &ox, lc, mvf, c_idx)) { + fc->vvcdsp.inter.put_uni_w[CHROMA][!!my][!!mx](dst, dst_stride, src, src_stride, + block_h, denom, wx, ox, _mx, _my, block_w, hf_idx, vf_idx); + } else { + fc->vvcdsp.inter.put_uni[CHROMA][!!my][!!mx](dst, dst_stride, src, src_stride, + block_h, _mx, _my, block_w, hf_idx, vf_idx); + } +} + +static void chroma_mc_bi(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride, + const AVFrame *ref0, const AVFrame *ref1, const int x_off, const int y_off, + const int block_w, const int block_h, const MvField *mvf, const int c_idx, + const int hf_idx, const int vf_idx, const MvField *orig_mv, const int dmvr_flag, const int ciip_flag) +{ + const VVCFrameContext *fc = lc->fc; + const uint8_t *src0 = ref0->data[c_idx]; + const uint8_t *src1 = ref1->data[c_idx]; + ptrdiff_t src0_stride = ref0->linesize[c_idx]; + ptrdiff_t src1_stride = ref1->linesize[c_idx]; + const Mv *mv0 = &mvf->mv[0]; + const Mv *mv1 = &mvf->mv[1]; + const int hs = fc->ps.sps->hshift[1]; + const int vs = fc->ps.sps->vshift[1]; + + const intptr_t mx0 = av_mod_uintp2(mv0->x, 4 + hs); + const intptr_t my0 = av_mod_uintp2(mv0->y, 4 + vs); + const intptr_t mx1 = av_mod_uintp2(mv1->x, 4 + hs); + const intptr_t my1 = av_mod_uintp2(mv1->y, 4 + vs); + const intptr_t _mx0 = mx0 << (1 - hs); + const intptr_t _my0 = my0 << (1 - vs); + const intptr_t _mx1 = mx1 << (1 - hs); + const intptr_t _my1 = my1 << (1 - vs); + + const int x_off0 = x_off + (mv0->x >> (4 + hs)); + const int y_off0 = y_off + (mv0->y >> (4 + vs)); + const int x_off1 = x_off + (mv1->x >> (4 + hs)); + const int y_off1 = y_off + (mv1->y >> (4 + vs)); + int denom, w0, w1, o0, o1; + + src0 += y_off0 * src0_stride + (int)((unsigned)x_off0 << fc->ps.sps->pixel_shift); + src1 += y_off1 * src1_stride + (int)((unsigned)x_off1 << fc->ps.sps->pixel_shift); + + if (dmvr_flag) { + const int x_sb0 = x_off + (orig_mv->mv[L0].x >> (4 + hs)); + const int y_sb0 = y_off + (orig_mv->mv[L0].y >> (4 + vs)); + const int x_sb1 = x_off + (orig_mv->mv[L1].x >> (4 + hs)); + const int y_sb1 = y_off + (orig_mv->mv[L1].y >> (4 + vs)); + EMULATED_EDGE_DMVR_CHROMA(lc->edge_emu_buffer, &src0, &src0_stride, x_sb0, y_sb0, x_off0, y_off0); + EMULATED_EDGE_DMVR_CHROMA(lc->edge_emu_buffer2, &src1, &src1_stride, x_sb1, y_sb1, x_off1, y_off1); + } else { + EMULATED_EDGE_CHROMA(lc->edge_emu_buffer, &src0, &src0_stride, x_off0, y_off0); + EMULATED_EDGE_CHROMA(lc->edge_emu_buffer2, &src1, &src1_stride, x_off1, y_off1); + } + + fc->vvcdsp.inter.put[CHROMA][!!my0][!!mx0](lc->tmp, src0, src0_stride, + block_h, _mx0, _my0, block_w, hf_idx, vf_idx); + if (derive_weight(&denom, &w0, &w1, &o0, &o1, lc, mvf, c_idx, dmvr_flag)) { + fc->vvcdsp.inter.put_bi_w[CHROMA][!!my1][!!mx1](dst, dst_stride, src1, src1_stride, lc->tmp, + block_h, denom, w0, w1, o0, o1, _mx1, _my1, block_w, hf_idx, vf_idx); + } else { + fc->vvcdsp.inter.put_bi[CHROMA][!!my1][!!mx1](dst, dst_stride, src1, src1_stride, lc->tmp, + block_h, _mx1, _my1, block_w, hf_idx, vf_idx); + } +} + +static void luma_prof_uni(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride, + const AVFrame *ref, const MvField *mvf, int x_off, int y_off, const int block_w, const int block_h, + const int cb_prof_flag, const int16_t *diff_mv_x, const int16_t *diff_mv_y) +{ + const VVCFrameContext *fc = lc->fc; + const uint8_t *src = ref->data[0]; + ptrdiff_t src_stride = ref->linesize[0]; + uint16_t *prof_tmp = lc->tmp + 1 + MAX_PB_SIZE; + const int lx = mvf->pred_flag - PF_L0; + const Mv *mv = mvf->mv + lx; + const int mx = mv->x & 0xf; + const int my = mv->y & 0xf; + int denom, wx, ox; + const int weight_flag = derive_weight_uni(&denom, &wx, &ox, lc, mvf, LUMA); + + x_off += mv->x >> 4; + y_off += mv->y >> 4; + src += y_off * src_stride + (x_off * (1 << fc->ps.sps->pixel_shift)); + + EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off); + if (cb_prof_flag) { + fc->vvcdsp.inter.put[LUMA][!!my][!!mx](prof_tmp, src, src_stride, AFFINE_MIN_BLOCK_SIZE, mx, my, AFFINE_MIN_BLOCK_SIZE, 2, 2); + fc->vvcdsp.inter.fetch_samples(prof_tmp, src, src_stride, mx, my); + if (!weight_flag) + fc->vvcdsp.inter.apply_prof_uni(dst, dst_stride, prof_tmp, diff_mv_x, diff_mv_y); + else + fc->vvcdsp.inter.apply_prof_uni_w(dst, dst_stride, prof_tmp, diff_mv_x, diff_mv_y, denom, wx, ox); + } else { + if (!weight_flag) + fc->vvcdsp.inter.put_uni[LUMA][!!my][!!mx](dst, dst_stride, src, src_stride, block_h, mx, my, block_w, 2, 2); + else + fc->vvcdsp.inter.put_uni_w[LUMA][!!my][!!mx](dst, dst_stride, src, src_stride, block_h, denom, wx, ox, mx, my, block_w, 2, 2); + } +} + +static void luma_prof_bi(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride, + const AVFrame *ref0, const AVFrame *ref1, const MvField *mvf, const int x_off, const int y_off, + const int block_w, const int block_h) +{ + const VVCFrameContext *fc = lc->fc; + const PredictionUnit *pu = &lc->cu->pu; + ptrdiff_t src0_stride = ref0->linesize[0]; + ptrdiff_t src1_stride = ref1->linesize[0]; + uint16_t *prof_tmp = lc->tmp1 + 1 + MAX_PB_SIZE; + const Mv *mv0 = mvf->mv + L0; + const Mv *mv1 = mvf->mv + L1; + const int mx0 = mv0->x & 0xf; + const int my0 = mv0->y & 0xf; + const int mx1 = mv1->x & 0xf; + const int my1 = mv1->y & 0xf; + const int x_off0 = x_off + (mv0->x >> 4); + const int y_off0 = y_off + (mv0->y >> 4); + const int x_off1 = x_off + (mv1->x >> 4); + const int y_off1 = y_off + (mv1->y >> 4); + + const uint8_t *src0 = ref0->data[0] + y_off0 * src0_stride + (int)((unsigned)x_off0 << fc->ps.sps->pixel_shift); + const uint8_t *src1 = ref1->data[0] + y_off1 * src1_stride + (int)((unsigned)x_off1 << fc->ps.sps->pixel_shift); + + int denom, w0, w1, o0, o1; + const int weight_flag = derive_weight(&denom, &w0, &w1, &o0, &o1, lc, mvf, LUMA, 0); + + EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src0, &src0_stride, x_off0, y_off0); + EMULATED_EDGE_LUMA(lc->edge_emu_buffer2, &src1, &src1_stride, x_off1, y_off1); + + if (!pu->cb_prof_flag[L0]) { + fc->vvcdsp.inter.put[LUMA][!!my0][!!mx0](lc->tmp, src0, src0_stride, + block_h, mx0, my0, block_w, 2, 2); + } else { + fc->vvcdsp.inter.put[LUMA][!!my0][!!mx0](prof_tmp, src0, src0_stride, AFFINE_MIN_BLOCK_SIZE, mx0, my0, AFFINE_MIN_BLOCK_SIZE, 2, 2); + fc->vvcdsp.inter.fetch_samples(prof_tmp, src0, src0_stride, mx0, my0); + fc->vvcdsp.inter.apply_prof(lc->tmp, prof_tmp, pu->diff_mv_x[L0], pu->diff_mv_y[L0]); + } + if (!pu->cb_prof_flag[L1]) { + if (weight_flag) { + fc->vvcdsp.inter.put_bi_w[LUMA][!!my1][!!mx1](dst, dst_stride, src1, src1_stride, lc->tmp, + block_h, denom, w0, w1, o0, o1, mx1, my1, block_w, 2, 2); + } else { + fc->vvcdsp.inter.put_bi[LUMA][!!my1][!!mx1](dst, dst_stride, src1, src1_stride, lc->tmp, + block_h, mx1, my1, block_w, 2, 2); + } + } else { + fc->vvcdsp.inter.put[LUMA][!!my1][!!mx1](prof_tmp, src1, src1_stride, AFFINE_MIN_BLOCK_SIZE, mx1, my1, AFFINE_MIN_BLOCK_SIZE, 2, 2); + fc->vvcdsp.inter.fetch_samples(prof_tmp, src1, src1_stride, mx1, my1); + if (weight_flag) { + fc->vvcdsp.inter.apply_prof_bi_w(dst, dst_stride, lc->tmp, prof_tmp, pu->diff_mv_x[L1], pu->diff_mv_y[L1], + denom, w0, w1, o0, o1); + } else { + fc->vvcdsp.inter.apply_prof_bi(dst, dst_stride, lc->tmp, prof_tmp, pu->diff_mv_x[L1], pu->diff_mv_y[L1]); + } + } + +} + +static int pred_get_refs(const VVCLocalContext *lc, VVCFrame *ref[2], const MvField *mv) +{ + const RefPicList *rpl = lc->sc->rpl; + + for (int mask = PF_L0; mask <= PF_L1; mask++) { + if (mv->pred_flag & mask) { + const int lx = mask - PF_L0; + ref[lx] = rpl[lx].ref[mv->ref_idx[lx]]; + if (!ref[lx]) + return AVERROR_INVALIDDATA; + } + } + return 0; +} + +#define POS(c_idx, x, y) \ + &fc->frame->data[c_idx][((y) >> fc->ps.sps->vshift[c_idx]) * fc->frame->linesize[c_idx] + \ + (((x) >> fc->ps.sps->hshift[c_idx]) << fc->ps.sps->pixel_shift)] + +static void pred_gpm_blk(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const PredictionUnit *pu = &cu->pu; + + const uint8_t angle_idx = ff_vvc_gpm_angle_idx[pu->gpm_partition_idx]; + const uint8_t weights_idx = ff_vvc_gpm_angle_to_weights_idx[angle_idx]; + const int w = av_log2(cu->cb_width) - 3; + const int h = av_log2(cu->cb_height) - 3; + const uint8_t off_x = ff_vvc_gpm_weights_offset_x[pu->gpm_partition_idx][h][w]; + const uint8_t off_y = ff_vvc_gpm_weights_offset_y[pu->gpm_partition_idx][h][w]; + const uint8_t mirror_type = ff_vvc_gpm_angle_to_mirror[angle_idx]; + const uint8_t *weights; + + const int c_end = fc->ps.sps->r->sps_chroma_format_idc ? 3 : 1; + + int16_t *tmp[2] = {lc->tmp, lc->tmp1}; + const ptrdiff_t tmp_stride = MAX_PB_SIZE; + + for (int c_idx = 0; c_idx < c_end; c_idx++) { + const int hs = fc->ps.sps->hshift[c_idx]; + const int vs = fc->ps.sps->vshift[c_idx]; + const int x = lc->cu->x0 >> hs; + const int y = lc->cu->y0 >> vs; + const int width = cu->cb_width >> hs; + const int height = cu->cb_height >> vs; + uint8_t *dst = POS(c_idx, lc->cu->x0, lc->cu->y0); + ptrdiff_t dst_stride = fc->frame->linesize[c_idx]; + + int step_x = 1 << hs; + int step_y = VVC_GPM_WEIGHT_SIZE << vs; + if (!mirror_type) { + weights = &ff_vvc_gpm_weights[weights_idx][off_y * VVC_GPM_WEIGHT_SIZE + off_x]; + } else if (mirror_type == 1) { + step_x = -step_x; + weights = &ff_vvc_gpm_weights[weights_idx][off_y * VVC_GPM_WEIGHT_SIZE + VVC_GPM_WEIGHT_SIZE - 1- off_x]; + } else { + step_y = -step_y; + weights = &ff_vvc_gpm_weights[weights_idx][(VVC_GPM_WEIGHT_SIZE - 1 - off_y) * VVC_GPM_WEIGHT_SIZE + off_x]; + } + + for (int i = 0; i < 2; i++) { + const MvField *mv = pu->gpm_mv + i; + const int lx = mv->pred_flag - PF_L0; + VVCFrame *ref = lc->sc->rpl[lx].ref[mv->ref_idx[lx]]; + if (!ref) + return; + if (c_idx) + chroma_mc(lc, tmp[i], ref->frame, mv->mv + lx, x, y, width, height, c_idx); + else + luma_mc(lc, tmp[i], ref->frame, mv->mv + lx, x, y, width, height); + } + fc->vvcdsp.inter.put_gpm(dst, dst_stride, width, height, tmp[0], tmp[1], tmp_stride, weights, step_x, step_y); + } + return; +} + +static int ciip_derive_intra_weight(const VVCLocalContext *lc, const int x0, const int y0, + const int width, const int height) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int x0b = av_mod_uintp2(x0, sps->ctb_log2_size_y); + const int y0b = av_mod_uintp2(y0, sps->ctb_log2_size_y); + const int available_l = lc->ctb_left_flag || x0b; + const int available_u = lc->ctb_up_flag || y0b; + const int min_pu_width = fc->ps.pps->min_pu_width; + + int w = 1; + + if (available_u &&fc->tab.mvf[((y0 - 1) >> MIN_PU_LOG2) * min_pu_width + ((x0 - 1 + width)>> MIN_PU_LOG2)].pred_flag == PF_INTRA) + w++; + + if (available_l && fc->tab.mvf[((y0 - 1 + height)>> MIN_PU_LOG2) * min_pu_width + ((x0 - 1) >> MIN_PU_LOG2)].pred_flag == PF_INTRA) + w++; + + return w; +} + +static void pred_regular_luma(VVCLocalContext *lc, const int hf_idx, const int vf_idx, const MvField *mv, + const int x0, const int y0, const int sbw, const int sbh, const MvField *orig_mv, const int sb_bdof_flag) +{ + const SliceContext *sc = lc->sc; + const VVCFrameContext *fc = lc->fc; + const int ciip_flag = lc->cu->ciip_flag; + uint8_t *dst = POS(0, x0, y0); + const ptrdiff_t dst_stride = fc->frame->linesize[0]; + uint8_t *inter = ciip_flag ? (uint8_t *)lc->ciip_tmp1 : dst; + const ptrdiff_t inter_stride = ciip_flag ? (MAX_PB_SIZE * sizeof(uint16_t)) : dst_stride; + VVCFrame *ref[2]; + + if (pred_get_refs(lc, ref, mv) < 0) + return; + + if (mv->pred_flag != PF_BI) { + const int lx = mv->pred_flag - PF_L0; + luma_mc_uni(lc, inter, inter_stride, ref[lx]->frame, + mv, x0, y0, sbw, sbh, hf_idx, vf_idx); + } else { + luma_mc_bi(lc, inter, inter_stride, ref[0]->frame, + &mv->mv[0], x0, y0, sbw, sbh, ref[1]->frame, &mv->mv[1], mv, + hf_idx, vf_idx, orig_mv, sb_bdof_flag); + } + + if (ciip_flag) { + const int intra_weight = ciip_derive_intra_weight(lc, x0, y0, sbw, sbh); + fc->vvcdsp.intra.intra_pred(lc, x0, y0, sbw, sbh, 0); + if (sc->sh.r->sh_lmcs_used_flag) + fc->vvcdsp.lmcs.filter(inter, inter_stride, sbw, sbh, fc->ps.lmcs.fwd_lut); + fc->vvcdsp.inter.put_ciip(dst, dst_stride, sbw, sbh, inter, inter_stride, intra_weight); + + } +} + +static void pred_regular_chroma(VVCLocalContext *lc, const MvField *mv, + const int x0, const int y0, const int sbw, const int sbh, const MvField *orig_mv, const int dmvr_flag) +{ + const VVCFrameContext *fc = lc->fc; + const int hs = fc->ps.sps->hshift[1]; + const int vs = fc->ps.sps->vshift[1]; + const int x0_c = x0 >> hs; + const int y0_c = y0 >> vs; + const int w_c = sbw >> hs; + const int h_c = sbh >> vs; + const int do_ciip = lc->cu->ciip_flag && (w_c > 2); + + uint8_t* dst1 = POS(1, x0, y0); + uint8_t* dst2 = POS(2, x0, y0); + const ptrdiff_t dst1_stride = fc->frame->linesize[1]; + const ptrdiff_t dst2_stride = fc->frame->linesize[2]; + + uint8_t *inter1 = do_ciip ? (uint8_t *)lc->ciip_tmp1 : dst1; + const ptrdiff_t inter1_stride = do_ciip ? (MAX_PB_SIZE * sizeof(uint16_t)) : dst1_stride; + + uint8_t *inter2 = do_ciip ? (uint8_t *)lc->ciip_tmp2 : dst2; + const ptrdiff_t inter2_stride = do_ciip ? (MAX_PB_SIZE * sizeof(uint16_t)) : dst2_stride; + + //fix me + const int hf_idx = 0; + const int vf_idx = 0; + VVCFrame *ref[2]; + + if (pred_get_refs(lc, ref, mv) < 0) + return; + + if (mv->pred_flag != PF_BI) { + const int lx = mv->pred_flag - PF_L0; + if (!ref[lx]) + return; + + chroma_mc_uni(lc, inter1, inter1_stride, ref[lx]->frame->data[1], ref[lx]->frame->linesize[1], + x0_c, y0_c, w_c, h_c, mv, CB, hf_idx, vf_idx); + chroma_mc_uni(lc, inter2, inter2_stride, ref[lx]->frame->data[2], ref[lx]->frame->linesize[2], + x0_c, y0_c, w_c, h_c, mv, CR, hf_idx, vf_idx); + } else { + if (!ref[0] || !ref[1]) + return; + + chroma_mc_bi(lc, inter1, inter1_stride, ref[0]->frame, ref[1]->frame, + x0_c, y0_c, w_c, h_c, mv, CB, hf_idx, vf_idx, orig_mv, dmvr_flag, lc->cu->ciip_flag); + + chroma_mc_bi(lc, inter2, inter2_stride, ref[0]->frame, ref[1]->frame, + x0_c, y0_c, w_c, h_c, mv, CR, hf_idx, vf_idx, orig_mv, dmvr_flag, lc->cu->ciip_flag); + + } + if (do_ciip) { + const int intra_weight = ciip_derive_intra_weight(lc, x0, y0, sbw, sbh); + fc->vvcdsp.intra.intra_pred(lc, x0, y0, sbw, sbh, 1); + fc->vvcdsp.intra.intra_pred(lc, x0, y0, sbw, sbh, 2); + fc->vvcdsp.inter.put_ciip(dst1, dst1_stride, w_c, h_c, inter1, inter1_stride, intra_weight); + fc->vvcdsp.inter.put_ciip(dst2, dst2_stride, w_c, h_c, inter2, inter2_stride, intra_weight); + + } +} + +// 8.5.3.5 Parametric motion vector refinement process +static int parametric_mv_refine(const int *sad, const int stride) +{ + const int sad_minus = sad[-stride]; + const int sad_center = sad[0]; + const int sad_plus = sad[stride]; + int dmvc; + int denom = (( sad_minus + sad_plus) - (sad_center << 1 ) ) << 3; + if (!denom) + dmvc = 0; + else { + if (sad_minus == sad_center) + dmvc = -8; + else if (sad_plus == sad_center) + dmvc = 8; + else { + int num = ( sad_minus - sad_plus ) << 4; + int sign_num = 0; + int quotient = 0; + int counter = 3; + if (num < 0 ) { + num = - num; + sign_num = 1; + } + while (counter > 0) { + counter = counter - 1; + quotient = quotient << 1; + if ( num >= denom ) { + num = num - denom; + quotient = quotient + 1; + } + denom = (denom >> 1); + } + if (sign_num == 1 ) + dmvc = -quotient; + else + dmvc = quotient; + } + } + return dmvc; +} + +#define SAD_ARRAY_SIZE 5 +//8.5.3 Decoder-side motion vector refinement process +static void dmvr_mv_refine(VVCLocalContext *lc, MvField *mv, MvField *orig_mv, int *sb_bdof_flag, + const AVFrame *ref0, const AVFrame *ref1, const int x_off, const int y_off, const int block_w, const int block_h) +{ + const VVCFrameContext *fc = lc->fc; + ptrdiff_t src0_stride = ref0->linesize[0]; + ptrdiff_t src1_stride = ref1->linesize[0]; + Mv *mv0 = mv->mv + L0; + Mv *mv1 = mv->mv + L1; + const int sr_range = 2; + const int mx0 = mv0->x & 0xf; + const int my0 = mv0->y & 0xf; + const int mx1 = mv1->x & 0xf; + const int my1 = mv1->y & 0xf; + const int x_off0 = x_off + (mv0->x >> 4) - sr_range; + const int y_off0 = y_off + (mv0->y >> 4) - sr_range; + const int x_off1 = x_off + (mv1->x >> 4) - sr_range; + const int y_off1 = y_off + (mv1->y >> 4) - sr_range; + const int pred_w = block_w + 2 * sr_range; + const int pred_h = block_h + 2 * sr_range; + + uint8_t *src0 = ref0->data[0] + y_off0 * src0_stride + (int)((unsigned)x_off0 << fc->ps.sps->pixel_shift); + uint8_t *src1 = ref1->data[0] + y_off1 * src1_stride + (int)((unsigned)x_off1 << fc->ps.sps->pixel_shift); + + int sad[SAD_ARRAY_SIZE][SAD_ARRAY_SIZE]; + int min_dx, min_dy, min_sad, dx, dy; + + *orig_mv = *mv; + min_dx = min_dy = dx = dy = 2; + + EMULATED_EDGE_BILINEAR(lc->edge_emu_buffer, &src0, &src0_stride, x_off0, y_off0); + EMULATED_EDGE_BILINEAR(lc->edge_emu_buffer2, &src1, &src1_stride, x_off1, y_off1); + fc->vvcdsp.inter.dmvr[!!my0][!!mx0](lc->tmp, src0, src0_stride, pred_h, mx0, my0, pred_w); + fc->vvcdsp.inter.dmvr[!!my1][!!mx1](lc->tmp1, src1, src1_stride, pred_h, mx1, my1, pred_w); + + min_sad = fc->vvcdsp.inter.sad(lc->tmp, lc->tmp1, dx, dy, block_w, block_h); + min_sad -= min_sad >> 2; + sad[dy][dx] = min_sad; + + if (min_sad >= block_w * block_h) { + int dmv[2]; + // 8.5.3.4 Array entry selection process + for (dy = 0; dy < SAD_ARRAY_SIZE; dy++) { + for (dx = 0; dx < SAD_ARRAY_SIZE; dx++) { + if (dx != sr_range || dy != sr_range) { + sad[dy][dx] = fc->vvcdsp.inter.sad(lc->tmp, lc->tmp1, dx, dy, block_w, block_h); + if (sad[dy][dx] < min_sad) { + min_sad = sad[dy][dx]; + min_dx = dx; + min_dy = dy; + } + } + } + } + dmv[0] = (min_dx - sr_range) << 4; + dmv[1] = (min_dy - sr_range) << 4; + if (min_dx != 0 && min_dx != 4 && min_dy != 0 && min_dy != 4) { + dmv[0] += parametric_mv_refine(&sad[min_dy][min_dx], 1); + dmv[1] += parametric_mv_refine(&sad[min_dy][min_dx], SAD_ARRAY_SIZE); + } + mv0->x += dmv[0]; + mv0->y += dmv[1]; + mv1->x += -dmv[0]; + mv1->y += -dmv[1]; + ff_vvc_clip_mv(mv0); + ff_vvc_clip_mv(mv1); + } + if (min_sad < 2 * block_w * block_h) { + *sb_bdof_flag = 0; + } +} + +static void set_dmvr_info(VVCFrameContext *fc, const int x0, const int y0, + const int width, const int height, const MvField *mvf) + +{ + const VVCPPS *pps = fc->ps.pps; + + for (int y = y0; y < y0 + height; y += MIN_PU_SIZE) { + for (int x = x0; x < x0 + width; x += MIN_PU_SIZE) { + const int idx = pps->min_pu_width * (y >> MIN_PU_LOG2) + (x >> MIN_PU_LOG2); + fc->ref->tab_dmvr_mvf[idx] = *mvf; + } + } +} + +static void fill_dmvr_info(const VVCFrameContext *fc, const int x0, const int y0, + const int width, const int height) +{ + const VVCPPS *pps = fc->ps.pps; + const int w = width >> MIN_PU_LOG2; + + for (int y = y0 >> MIN_PU_LOG2; y < (y0 + height) >> MIN_PU_LOG2; y++) { + const int idx = pps->min_pu_width * y + (x0 >> MIN_PU_LOG2); + const MvField *mvf = fc->tab.mvf + idx; + MvField *dmvr_mvf = fc->ref->tab_dmvr_mvf + idx; + memcpy(dmvr_mvf, mvf, sizeof(MvField) * w); + } +} + +static void derive_sb_mv(VVCLocalContext *lc, MvField *mv, MvField *orig_mv, int *sb_bdof_flag, + const int x0, const int y0, const int sbw, const int sbh) +{ + VVCFrameContext *fc = lc->fc; + const PredictionUnit *pu = &lc->cu->pu; + + *orig_mv = *mv = *ff_vvc_get_mvf(fc, x0, y0); + if (pu->bdof_flag) + *sb_bdof_flag = 1; + if (pu->dmvr_flag) { + VVCFrame* ref[2]; + if (pred_get_refs(lc, ref, mv) < 0) + return; + dmvr_mv_refine(lc, mv, orig_mv, sb_bdof_flag, ref[0]->frame, ref[1]->frame, x0, y0, sbw, sbh); + set_dmvr_info(fc, x0, y0, sbw, sbh, mv); + } +} + +static void pred_regular_blk(VVCLocalContext *lc, const int skip_ciip) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + PredictionUnit *pu = &lc->cu->pu; + const MotionInfo *mi = &pu->mi; + MvField mv, orig_mv; + int sbw, sbh, sb_bdof_flag = 0; + + if (cu->ciip_flag && skip_ciip) + return; + + sbw = cu->cb_width / mi->num_sb_x; + sbh = cu->cb_height / mi->num_sb_y; + + for (int sby = 0; sby < mi->num_sb_y; sby++) { + for (int sbx = 0; sbx < mi->num_sb_x; sbx++) { + const int x0 = cu->x0 + sbx * sbw; + const int y0 = cu->y0 + sby * sbh; + + if (cu->ciip_flag) + ff_vvc_set_neighbour_available(lc, x0, y0, sbw, sbh); + + derive_sb_mv(lc, &mv, &orig_mv, &sb_bdof_flag, x0, y0, sbw, sbh); + pred_regular_luma(lc, mi->hpel_if_idx, mi->hpel_if_idx, &mv, x0, y0, sbw, sbh, &orig_mv, sb_bdof_flag); + if (fc->ps.sps->r->sps_chroma_format_idc) + pred_regular_chroma(lc, &mv, x0, y0, sbw, sbh, &orig_mv, pu->dmvr_flag); + } + } +} + +static void derive_affine_mvc(MvField *mvc, const VVCFrameContext *fc, const MvField *mv, + const int x0, const int y0, const int sbw, const int sbh) +{ + const int hs = fc->ps.sps->hshift[1]; + const int vs = fc->ps.sps->vshift[1]; + const MvField* mv2 = ff_vvc_get_mvf(fc, x0 + hs * sbw, y0 + vs * sbh); + *mvc = *mv; + mvc->mv[0].x += mv2->mv[0].x; + mvc->mv[0].y += mv2->mv[0].y; + mvc->mv[1].x += mv2->mv[1].x; + mvc->mv[1].y += mv2->mv[1].y; + ff_vvc_round_mv(mvc->mv + 0, 0, 1); + ff_vvc_round_mv(mvc->mv + 1, 0, 1); +} + +static void pred_affine_blk(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const PredictionUnit *pu = &cu->pu; + const MotionInfo *mi = &pu->mi; + const int x0 = cu->x0; + const int y0 = cu->y0; + const int sbw = cu->cb_width / mi->num_sb_x; + const int sbh = cu->cb_height / mi->num_sb_y; + const int hs = fc->ps.sps->hshift[1]; + const int vs = fc->ps.sps->vshift[1]; + + for (int sby = 0; sby < mi->num_sb_y; sby++) { + for (int sbx = 0; sbx < mi->num_sb_x; sbx++) { + const int x = x0 + sbx * sbw; + const int y = y0 + sby * sbh; + + uint8_t *dst0 = POS(0, x, y); + const MvField *mv = ff_vvc_get_mvf(fc, x, y); + VVCFrame *ref[2]; + + if (pred_get_refs(lc, ref, mv) < 0) + return; + + if (mi->pred_flag != PF_BI) { + const int lx = mi->pred_flag - PF_L0; + luma_prof_uni(lc, dst0, fc->frame->linesize[0], ref[lx]->frame, + mv, x, y, sbw, sbh, pu->cb_prof_flag[lx], + pu->diff_mv_x[lx], pu->diff_mv_y[lx]); + } else { + luma_prof_bi(lc, dst0, fc->frame->linesize[0], ref[0]->frame, ref[1]->frame, + mv, x, y, sbw, sbh); + } + if (fc->ps.sps->r->sps_chroma_format_idc) { + if (!av_mod_uintp2(sby, vs) && !av_mod_uintp2(sbx, hs)) { + MvField mvc; + derive_affine_mvc(&mvc, fc, mv, x, y, sbw, sbh); + pred_regular_chroma(lc, &mvc, x, y, sbw<fc; + const CodingUnit *cu = lc->cu; + const PredictionUnit *pu = &cu->pu; + + if (pu->merge_gpm_flag) + pred_gpm_blk(lc); + else if (pu->inter_affine_flag) + pred_affine_blk(lc); + else + pred_regular_blk(lc, 1); //intra block is not ready yet, skip ciip + + if (!pu->dmvr_flag) + fill_dmvr_info(fc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + if (lc->sc->sh.r->sh_lmcs_used_flag && !cu->ciip_flag) { + uint8_t* dst0 = POS(0, cu->x0, cu->y0); + fc->vvcdsp.lmcs.filter(dst0, fc->frame->linesize[LUMA], cu->cb_width, cu->cb_height, fc->ps.lmcs.fwd_lut); + } +} + +static int has_inter_luma(const CodingUnit *cu) +{ + return cu->pred_mode != MODE_INTRA && cu->pred_mode != MODE_PLT && cu->tree_type != DUAL_TREE_CHROMA; +} + +int ff_vvc_predict_inter(VVCLocalContext *lc, const int rs) +{ + const VVCFrameContext *fc = lc->fc; + const CTU *ctu = fc->tab.ctus + rs; + CodingUnit *cu = ctu->cus; + + while (cu) { + lc->cu = cu; + if (has_inter_luma(cu)) + predict_inter(lc); + cu = cu->next; + } + + return 0; +} + +void ff_vvc_predict_ciip(VVCLocalContext *lc) +{ + av_assert0(lc->cu->ciip_flag); + + //todo: refact out ciip from pred_regular_blk + pred_regular_blk(lc, 0); +} + +#undef POS diff --git a/libavcodec/vvc/vvc_inter.h b/libavcodec/vvc/vvc_inter.h new file mode 100644 index 00000000000..25bfb35cd9e --- /dev/null +++ b/libavcodec/vvc/vvc_inter.h @@ -0,0 +1,42 @@ +/* + * VVC inter prediction + * + * Copyright (C) 2023 Nuo Mi + * + * 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_VVC_INTER_H +#define AVCODEC_VVC_INTER_H + +#include "vvc_ctu.h" + +/** + * Loop entire CTU to predict all inter coding blocks + * @param lc local context for CTU + * @param rs raster order for the CTU + * @return AVERROR + */ +int ff_vvc_predict_inter(VVCLocalContext *lc, int rs); + +/** + * CIIP(Combined Inter-Intra Prediction) for a coding block + * @param lc local context for CTU + */ +void ff_vvc_predict_ciip(VVCLocalContext *lc); + +#endif // AVCODEC_VVC_INTER_H diff --git a/libavcodec/vvc/vvc_inter_template.c b/libavcodec/vvc/vvc_inter_template.c new file mode 100644 index 00000000000..5eb4496e9d0 --- /dev/null +++ b/libavcodec/vvc/vvc_inter_template.c @@ -0,0 +1,1473 @@ +/* + * VVC inter prediction DSP + * + * Copyright (C) 2022 Nuo Mi + * + * 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 + */ + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// +static void FUNC(put_vvc_pel_pixels)(int16_t *dst, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = src[x] << (14 - BIT_DEPTH); + src += src_stride; + dst += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_pel_uni_pixels)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int height, + const intptr_t mx, const intptr_t my, const int width, const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + + for (int y = 0; y < height; y++) { + memcpy(dst, src, width * sizeof(pixel)); + src += src_stride; + dst += dst_stride; + } +} + +static void FUNC(put_vvc_pel_bi_pixels)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int shift = 14 + 1 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel(((src[x] << (14 - BIT_DEPTH)) + src0[x] + offset) >> shift); + src += src_stride; + dst += dst_stride; + src0 += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_pel_uni_w_pixels)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int height, + const int denom, const int wx, const int _ox, const intptr_t mx, const intptr_t my, + const int width, const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int shift = denom + 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + const int ox = _ox * (1 << (BIT_DEPTH - 8)); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const int v = (src[x] << (14 - BIT_DEPTH)); + dst[x] = av_clip_pixel(((v * wx + offset) >> shift) + ox); + } + src += src_stride; + dst += dst_stride; + } +} + +static void FUNC(put_vvc_pel_bi_w_pixels)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const int denom, const int wx0, const int wx1, + const int _ox0, const int _ox1, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int shift = 14 + 1 - BIT_DEPTH; + const int log2Wd = denom + shift - 1; + const int ox0 = _ox0 * (1 << (BIT_DEPTH - 8)); + const int ox1 = _ox1 * (1 << (BIT_DEPTH - 8)); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel(( (src[x] << (14 - BIT_DEPTH)) * wx1 + src0[x] * wx0 + (ox0 + ox1 + 1) * (1 << log2Wd)) >> (log2Wd + 1)); + src += src_stride; + dst += dst_stride; + src0 += MAX_PB_SIZE; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// +#define LUMA_FILTER(src, stride) \ + (filter[0] * src[x - 3 * stride] + \ + filter[1] * src[x - 2 * stride] + \ + filter[2] * src[x - stride] + \ + filter[3] * src[x ] + \ + filter[4] * src[x + stride] + \ + filter[5] * src[x + 2 * stride] + \ + filter[6] * src[x + 3 * stride] + \ + filter[7] * src[x + 4 * stride]) + +static void FUNC(put_vvc_luma_h)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_luma_filters[hf_idx][mx]; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + dst += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_luma_v)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_luma_filters[vf_idx][my]; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = LUMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8); + src += src_stride; + dst += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_luma_hv)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + int16_t tmp_array[(MAX_PB_SIZE + LUMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const pixel *src = (const pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_luma_filters[hf_idx][mx]; + + src -= LUMA_EXTRA_BEFORE * src_stride; + for (int y = 0; y < height + LUMA_EXTRA; y++) { + for (int x = 0; x < width; x++) + tmp[x] = LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + tmp += MAX_PB_SIZE; + } + + tmp = tmp_array + LUMA_EXTRA_BEFORE * MAX_PB_SIZE; + filter = ff_vvc_luma_filters[vf_idx][my]; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = LUMA_FILTER(tmp, MAX_PB_SIZE) >> 6; + tmp += MAX_PB_SIZE; + dst += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_luma_uni_h)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel*)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_luma_filters[hf_idx][mx]; + const int shift = 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const int val = LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + dst[x] = av_clip_pixel((val + offset) >> shift); + } + src += src_stride; + dst += dst_stride; + } +} + +static void FUNC(put_vvc_luma_bi_h)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (pixel*)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_luma_filters[hf_idx][mx]; + const int shift = 14 + 1 - BIT_DEPTH; +#if BIT_DEPTH < 14 + int offset = 1 << (shift - 1); +#else + int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel(((LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8)) + src0[x] + offset) >> shift); + src += src_stride; + dst += dst_stride; + src0 += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_luma_uni_v)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + + const pixel *src = (const pixel*)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_luma_filters[vf_idx][my]; + const int shift = 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const int val = LUMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8); + dst[x] = av_clip_pixel((val + offset) >> shift); + } + src += src_stride; + dst += dst_stride; + } +} + +static void FUNC(put_vvc_luma_bi_v)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (pixel*)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_luma_filters[vf_idx][my]; + const int shift = 14 + 1 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel(((LUMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8)) + src0[x] + offset) >> shift); + src += src_stride; + dst += dst_stride; + src0 += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_luma_uni_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + int16_t tmp_array[(MAX_PB_SIZE + LUMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const pixel *src = (const pixel*)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_luma_filters[hf_idx][mx]; + const int shift = 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + src -= LUMA_EXTRA_BEFORE * src_stride; + for (int y = 0; y < height + LUMA_EXTRA; y++) { + for (int x = 0; x < width; x++) + tmp[x] = LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + tmp += MAX_PB_SIZE; + } + + tmp = tmp_array + LUMA_EXTRA_BEFORE * MAX_PB_SIZE; + filter = ff_vvc_luma_filters[vf_idx][my]; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const int val = LUMA_FILTER(tmp, MAX_PB_SIZE) >> 6; + dst[x] = av_clip_pixel((val + offset) >> shift); + } + tmp += MAX_PB_SIZE; + dst += dst_stride; + } + +} + +static void FUNC(put_vvc_luma_bi_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + int16_t tmp_array[(MAX_PB_SIZE + LUMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const pixel *src = (const pixel*)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_luma_filters[hf_idx][mx]; + const int shift = 14 + 1 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + src -= LUMA_EXTRA_BEFORE * src_stride; + for (int y = 0; y < height + LUMA_EXTRA; y++) { + for (int x = 0; x < width; x++) + tmp[x] = LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + tmp += MAX_PB_SIZE; + } + + tmp = tmp_array + LUMA_EXTRA_BEFORE * MAX_PB_SIZE; + filter = ff_vvc_luma_filters[vf_idx][my]; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel(((LUMA_FILTER(tmp, MAX_PB_SIZE) >> 6) + src0[x] + offset) >> shift); + tmp += MAX_PB_SIZE; + dst += dst_stride; + src0 += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_luma_uni_w_h)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, int height, + const int denom, const int wx, const int _ox, + const intptr_t mx, const intptr_t my, const int width, const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel*)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_luma_filters[hf_idx][mx]; + const int ox = _ox * (1 << (BIT_DEPTH - 8)); + const int shift = denom + 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel((((LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); + src += src_stride; + dst += dst_stride; + } +} + +static void FUNC(put_vvc_luma_bi_w_h)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, const int height, + const int denom, const int w0, const int w1, const int o0, int o1, + const intptr_t mx, const intptr_t my, const int width, const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel*)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_luma_filters[hf_idx][mx]; + const int shift = denom + FFMAX(2, 14 - BIT_DEPTH) + 1; + const int offset = (((o0 + o1) << (BIT_DEPTH - 8)) + 1) << (shift - 1); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const int src1 = LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + dst[x] = av_clip_pixel((src1 * w1 + src0[x] * w0 + offset) >> shift); + } + src += src_stride; + dst += dst_stride; + src0 += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_luma_uni_w_v)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int height, + const int denom, const int wx, const int _ox, + const intptr_t mx, const intptr_t my, const int width, const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel*)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_luma_filters[vf_idx][my]; + const int ox = _ox * (1 << (BIT_DEPTH - 8)); + const int shift = denom + 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel((((LUMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); + src += src_stride; + dst += dst_stride; + } +} + +static void FUNC(put_vvc_luma_bi_w_v)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const int denom, const int wx0, const int wx1, + const int _ox0, const int _ox1, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel*)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_luma_filters[vf_idx][my]; + const int shift = 14 + 1 - BIT_DEPTH; + const int log2Wd = denom + shift - 1; + const int ox0 = _ox0 * (1 << (BIT_DEPTH - 8)); + const int ox1 = _ox1 * (1 << (BIT_DEPTH - 8)); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel(((LUMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8)) * wx1 + src0[x] * wx0 + + ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1)); + src += src_stride; + dst += dst_stride; + src0 += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_luma_uni_w_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int height, const int denom, + const int wx, const int _ox, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + int16_t tmp_array[(MAX_PB_SIZE + LUMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const pixel *src = (const pixel*)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_luma_filters[hf_idx][mx]; + const int ox = _ox * (1 << (BIT_DEPTH - 8)); + const int shift = denom + 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + src -= LUMA_EXTRA_BEFORE * src_stride; + for (int y = 0; y < height + LUMA_EXTRA; y++) { + for (int x = 0; x < width; x++) + tmp[x] = LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + tmp += MAX_PB_SIZE; + } + + tmp = tmp_array + LUMA_EXTRA_BEFORE * MAX_PB_SIZE; + filter = ff_vvc_luma_filters[vf_idx][my]; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel((((LUMA_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx + offset) >> shift) + ox); + tmp += MAX_PB_SIZE; + dst += dst_stride; + } +} + +static void FUNC(put_vvc_luma_bi_w_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const int denom, const int wx0, const int wx1, + const int _ox0, const int _ox1, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + int16_t tmp_array[(MAX_PB_SIZE + LUMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const pixel *src = (const pixel*)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_luma_filters[hf_idx][mx]; + const int ox0 = _ox0 * (1 << (BIT_DEPTH - 8)); + const int ox1 = _ox1 * (1 << (BIT_DEPTH - 8)); + const int shift = 14 + 1 - BIT_DEPTH; + const int log2Wd = denom + shift - 1; + + src -= LUMA_EXTRA_BEFORE * src_stride; + for (int y = 0; y < height + LUMA_EXTRA; y++) { + for (int x = 0; x < width; x++) + tmp[x] = LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + tmp += MAX_PB_SIZE; + } + + tmp = tmp_array + LUMA_EXTRA_BEFORE * MAX_PB_SIZE; + filter = ff_vvc_luma_filters[vf_idx][my]; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel(((LUMA_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx1 + src0[x] * wx0 + + ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1)); + tmp += MAX_PB_SIZE; + dst += dst_stride; + src0 += MAX_PB_SIZE; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// +#define CHROMA_FILTER(src, stride) \ + (filter[0] * src[x - stride] + \ + filter[1] * src[x] + \ + filter[2] * src[x + stride] + \ + filter[3] * src[x + 2 * stride]) + +static void FUNC(put_vvc_chroma_h)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[hf_idx][mx]; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + dst += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_chroma_v)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[vf_idx][my]; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = CHROMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8); + src += src_stride; + dst += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_chroma_hv)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + int16_t tmp_array[(MAX_PB_SIZE + CHROMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[hf_idx][mx]; + + src -= CHROMA_EXTRA_BEFORE * src_stride; + + for (int y = 0; y < height + CHROMA_EXTRA; y++) { + for (int x = 0; x < width; x++) + tmp[x] = CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + tmp += MAX_PB_SIZE; + } + + tmp = tmp_array + CHROMA_EXTRA_BEFORE * MAX_PB_SIZE; + filter = ff_vvc_chroma_filters[vf_idx][my]; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = CHROMA_FILTER(tmp, MAX_PB_SIZE) >> 6; + tmp += MAX_PB_SIZE; + dst += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_chroma_uni_h)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[hf_idx][mx]; + const int shift = 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel(((CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8)) + offset) >> shift); + src += src_stride; + dst += dst_stride; + } +} + +static void FUNC(put_vvc_chroma_bi_h)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[hf_idx][mx]; + const int shift = 14 + 1 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel(((CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8)) + src0[x] + offset) >> shift); + dst += dst_stride; + src += src_stride; + src0 += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_chroma_uni_v)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[vf_idx][my]; + const int shift = 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel(((CHROMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8)) + offset) >> shift); + src += src_stride; + dst += dst_stride; + } +} + +static void FUNC(put_vvc_chroma_bi_v)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int shift = 14 + 1 - BIT_DEPTH; + const int8_t *filter = ff_vvc_chroma_filters[vf_idx][my]; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel(((CHROMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8)) + src0[x] + offset) >> shift); + dst += dst_stride; + src += src_stride; + src0 += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_chroma_uni_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + int16_t tmp_array[(MAX_PB_SIZE + CHROMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[hf_idx][mx]; + const int shift = 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + src -= CHROMA_EXTRA_BEFORE * src_stride; + + for (int y = 0; y < height + CHROMA_EXTRA; y++) { + for (int x = 0; x < width; x++) + tmp[x] = CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + tmp += MAX_PB_SIZE; + } + + tmp = tmp_array + CHROMA_EXTRA_BEFORE * MAX_PB_SIZE; + filter = ff_vvc_chroma_filters[vf_idx][my]; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel(((CHROMA_FILTER(tmp, MAX_PB_SIZE) >> 6) + offset) >> shift); + tmp += MAX_PB_SIZE; + dst += dst_stride; + } +} + +static void FUNC(put_vvc_chroma_bi_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + int16_t tmp_array[(MAX_PB_SIZE + CHROMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[hf_idx][mx]; + const int shift = 14 + 1 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + src -= CHROMA_EXTRA_BEFORE * src_stride; + + for (int y = 0; y < height + CHROMA_EXTRA; y++) { + for (int x = 0; x < width; x++) + tmp[x] = CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + tmp += MAX_PB_SIZE; + } + + tmp = tmp_array + CHROMA_EXTRA_BEFORE * MAX_PB_SIZE; + filter = ff_vvc_chroma_filters[vf_idx][my]; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel(((CHROMA_FILTER(tmp, MAX_PB_SIZE) >> 6) + src0[x] + offset) >> shift); + tmp += MAX_PB_SIZE; + dst += dst_stride; + src0 += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_chroma_uni_w_h)(uint8_t *_dst, ptrdiff_t _dst_stride, + const uint8_t *_src, ptrdiff_t _src_stride, int height, int denom, int wx, int ox, + intptr_t mx, intptr_t my, int width, const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[hf_idx][mx]; + const int shift = denom + 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + ox = ox * (1 << (BIT_DEPTH - 8)); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + dst[x] = av_clip_pixel((((CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); + } + dst += dst_stride; + src += src_stride; + } +} + +static void FUNC(put_vvc_chroma_bi_w_h)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const int denom, const int w0, const int w1, + int o0, int o1, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[hf_idx][mx]; + const int shift = denom + FFMAX(2, 14 - BIT_DEPTH) + 1; + const int offset = (((o0 + o1) << (BIT_DEPTH - 8)) + 1) << (shift - 1); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const int src1 = CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + dst[x] = av_clip_pixel((src1 * w1 + src0[x] * w0 + offset) >> shift); + } + src += src_stride; + dst += dst_stride; + src0 += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_chroma_uni_w_v)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int height, + const int denom, const int wx, const int _ox, const intptr_t mx, const intptr_t my, + const int width, const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[vf_idx][my]; + const int shift = denom + 14 - BIT_DEPTH; + const int ox = _ox * (1 << (BIT_DEPTH - 8)); +#if BIT_DEPTH < 14 + int offset = 1 << (shift - 1); +#else + int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + dst[x] = av_clip_pixel((((CHROMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); + } + dst += dst_stride; + src += src_stride; + } +} + +static void FUNC(put_vvc_chroma_bi_w_v)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const int denom, const int wx0, const int wx1, + const int _ox0, const int _ox1, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[vf_idx][my]; + const int ox0 = _ox0 * (1 << (BIT_DEPTH - 8)); + const int ox1 = _ox1 * (1 << (BIT_DEPTH - 8)); + const int shift = 14 + 1 - BIT_DEPTH; + const int log2Wd = denom + shift - 1; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel(((CHROMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8)) * wx1 + src0[x] * wx0 + + ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1)); + src += src_stride; + dst += dst_stride; + src0 += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_chroma_uni_w_hv)(uint8_t *_dst, ptrdiff_t _dst_stride, + const uint8_t *_src, ptrdiff_t _src_stride, int height, int denom, int wx, int ox, + intptr_t mx, intptr_t my, int width, const int hf_idx, const int vf_idx) +{ + int16_t tmp_array[(MAX_PB_SIZE + CHROMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[hf_idx][mx]; + const int shift = denom + 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + src -= CHROMA_EXTRA_BEFORE * src_stride; + + for (int y = 0; y < height + CHROMA_EXTRA; y++) { + for (int x = 0; x < width; x++) + tmp[x] = CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + tmp += MAX_PB_SIZE; + } + + tmp = tmp_array + CHROMA_EXTRA_BEFORE * MAX_PB_SIZE; + filter = ff_vvc_chroma_filters[vf_idx][my]; + + ox = ox * (1 << (BIT_DEPTH - 8)); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel((((CHROMA_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx + offset) >> shift) + ox); + tmp += MAX_PB_SIZE; + dst += dst_stride; + } +} + +static void FUNC(put_vvc_chroma_bi_w_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const int denom, const int wx0, const int wx1, + const int _ox0, const int _ox1, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + int16_t tmp_array[(MAX_PB_SIZE + CHROMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[hf_idx][mx]; + const int ox0 = _ox0 * (1 << (BIT_DEPTH - 8)); + const int ox1 = _ox1 * (1 << (BIT_DEPTH - 8)); + const int shift = 14 + 1 - BIT_DEPTH; + const int log2Wd = denom + shift - 1; + + src -= CHROMA_EXTRA_BEFORE * src_stride; + for (int y = 0; y < height + CHROMA_EXTRA; y++) { + for (int x = 0; x < width; x++) + tmp[x] = CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + tmp += MAX_PB_SIZE; + } + + tmp = tmp_array + CHROMA_EXTRA_BEFORE * MAX_PB_SIZE; + filter = ff_vvc_chroma_filters[vf_idx][my]; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel(((CHROMA_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx1 + src0[x] * wx0 + + ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1)); + tmp += MAX_PB_SIZE; + dst += dst_stride; + src0 += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_ciip)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const int width, const int height, + const uint8_t *_inter, const ptrdiff_t _inter_stride, const int intra_weight) +{ + pixel *dst = (pixel *)_dst; + pixel *inter = (pixel *)_inter; + const size_t dst_stride = _dst_stride / sizeof(pixel); + const size_t inter_stride = _inter_stride / sizeof(pixel); + const int inter_weight = 4 - intra_weight; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = (dst[x] * intra_weight + inter[x] * inter_weight + 2) >> 2; + dst += dst_stride; + inter += inter_stride; + } +} + +static void FUNC(put_vvc_gpm)(uint8_t *_dst, ptrdiff_t dst_stride, + const int width, const int height, + const int16_t *tmp, const int16_t *tmp1, const ptrdiff_t tmp_stride, + const uint8_t *weights, const int step_x, const int step_y) +{ + const int shift = FFMAX(5, 17 - BIT_DEPTH); + const int offset = 1 << (shift - 1); + pixel *dst = (pixel *)_dst; + + dst_stride /= sizeof(pixel); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const uint8_t w = weights[x * step_x]; + dst[x] = av_clip_pixel((tmp[x] * w + tmp1[x] * (8 - w) + offset) >> shift); + } + dst += dst_stride; + tmp += tmp_stride; + tmp1 += tmp_stride; + weights += step_y; + } +} + +//8.5.6.3.3 Luma integer sample fetching process, add one extra pad line +static void FUNC(bdof_fetch_samples)(int16_t *_dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int x_frac, const int y_frac, const int width, const int height) +{ + const int x_off = (x_frac >> 3) - 1; + const int y_off = (y_frac >> 3) - 1; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const pixel *src = (pixel*)_src + (x_off) + y_off * src_stride; + int16_t *dst = _dst - 1 - MAX_PB_SIZE; + const int shift = 14 - BIT_DEPTH; + const int bdof_width = width + 2 * BDOF_BORDER_EXT; + + // top + for (int i = 0; i < bdof_width; i++) + dst[i] = src[i] << shift; + + dst += MAX_PB_SIZE; + src += src_stride; + + for (int i = 0; i < height; i++) { + dst[0] = src[0] << shift; + dst[1 + width] = src[1 + width] << shift; + dst += MAX_PB_SIZE; + src += src_stride; + } + for (int i = 0; i < bdof_width; i++) + dst[i] = src[i] << shift; +} + +//8.5.6.3.3 Luma integer sample fetching process +static void FUNC(fetch_samples)(int16_t *_dst, const uint8_t *_src, const ptrdiff_t _src_stride, const int x_frac, const int y_frac) +{ + FUNC(bdof_fetch_samples)(_dst, _src, _src_stride, x_frac, y_frac, AFFINE_MIN_BLOCK_SIZE, AFFINE_MIN_BLOCK_SIZE); +} + +static void FUNC(prof_grad_filter)(int16_t *_gradient_h, int16_t *_gradient_v, const ptrdiff_t gradient_stride, + const int16_t *_src, const ptrdiff_t src_stride, const int width, const int height, const int pad) +{ + const int shift = 6; + const int16_t *src = _src; + int16_t *gradient_h = _gradient_h + pad * (1 + gradient_stride); + int16_t *gradient_v = _gradient_v + pad * (1 + gradient_stride); + + for (int y = 0; y < height; y++) { + const int16_t *p = src; + for (int x = 0; x < width; x++) { + gradient_h[x] = (p[1] >> shift) - (p[-1] >> shift); + gradient_v[x] = (p[src_stride] >> shift) - (p[-src_stride] >> shift); + p++; + } + gradient_h += gradient_stride; + gradient_v += gradient_stride; + src += src_stride; + } + if (pad) { + pad_int16(_gradient_h + 1 + gradient_stride, gradient_stride, width, height); + pad_int16(_gradient_v + 1 + gradient_stride, gradient_stride, width, height); + } +} + +static void FUNC(apply_prof)(int16_t *dst, const int16_t *src, const int16_t *diff_mv_x, const int16_t *diff_mv_y) +{ + const int limit = (1 << FFMAX(13, BIT_DEPTH + 1)); ///< dILimit + + int16_t gradient_h[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + int16_t gradient_v[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + FUNC(prof_grad_filter)(gradient_h, gradient_v, AFFINE_MIN_BLOCK_SIZE, src, MAX_PB_SIZE, AFFINE_MIN_BLOCK_SIZE, AFFINE_MIN_BLOCK_SIZE, 0); + + for (int y = 0; y < AFFINE_MIN_BLOCK_SIZE; y++) { + for (int x = 0; x < AFFINE_MIN_BLOCK_SIZE; x++) { + const int o = y * AFFINE_MIN_BLOCK_SIZE + x; + const int di = gradient_h[o] * diff_mv_x[o] + gradient_v[o] * diff_mv_y[o]; + const int val = src[x] + av_clip(di, -limit, limit - 1); + dst[x] = val; + + } + src += MAX_PB_SIZE; + dst += MAX_PB_SIZE; + } +} + +static void FUNC(apply_prof_uni)(uint8_t *_dst, const ptrdiff_t _dst_stride, const int16_t *src, const int16_t *diff_mv_x, const int16_t *diff_mv_y) +{ + const int limit = (1 << FFMAX(13, BIT_DEPTH + 1)); ///< dILimit + pixel *dst = (pixel*)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int shift = 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + int16_t gradient_h[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + int16_t gradient_v[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + + FUNC(prof_grad_filter)(gradient_h, gradient_v, AFFINE_MIN_BLOCK_SIZE, src, MAX_PB_SIZE, AFFINE_MIN_BLOCK_SIZE, AFFINE_MIN_BLOCK_SIZE, 0); + + for (int y = 0; y < AFFINE_MIN_BLOCK_SIZE; y++) { + for (int x = 0; x < AFFINE_MIN_BLOCK_SIZE; x++) { + const int o = y * AFFINE_MIN_BLOCK_SIZE + x; + const int di = gradient_h[o] * diff_mv_x[o] + gradient_v[o] * diff_mv_y[o]; + const int val = src[x] + av_clip(di, -limit, limit - 1); + dst[x] = av_clip_pixel((val + offset) >> shift); + + } + src += MAX_PB_SIZE; + dst += dst_stride; + } +} + +static void FUNC(apply_prof_uni_w)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const int16_t *src, const int16_t *diff_mv_x, const int16_t *diff_mv_y, + const int denom, const int wx, const int _ox) +{ + const int limit = (1 << FFMAX(13, BIT_DEPTH + 1)); ///< dILimit + pixel *dst = (pixel*)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int shift = denom + FFMAX(2, 14 - BIT_DEPTH); + const int offset = 1 << (shift - 1); + const int ox = _ox * (1 << (BIT_DEPTH - 8)); + int16_t gradient_h[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + int16_t gradient_v[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + + FUNC(prof_grad_filter)(gradient_h, gradient_v, AFFINE_MIN_BLOCK_SIZE, src, MAX_PB_SIZE, AFFINE_MIN_BLOCK_SIZE, AFFINE_MIN_BLOCK_SIZE, 0); + + for (int y = 0; y < AFFINE_MIN_BLOCK_SIZE; y++) { + for (int x = 0; x < AFFINE_MIN_BLOCK_SIZE; x++) { + const int o = y * AFFINE_MIN_BLOCK_SIZE + x; + const int di = gradient_h[o] * diff_mv_x[o] + gradient_v[o] * diff_mv_y[o]; + const int val = src[x] + av_clip(di, -limit, limit - 1); + dst[x] = av_clip_pixel(((val * wx + offset) >> shift) + ox); + } + src += MAX_PB_SIZE; + dst += dst_stride; + } +} + +static void FUNC(apply_prof_bi)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const int16_t *src0, const int16_t *src1, const int16_t *diff_mv_x, const int16_t *diff_mv_y) +{ + const int limit = (1 << FFMAX(13, BIT_DEPTH + 1)); ///< dILimit + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + pixel *dst = (pixel*)_dst; + const int shift = 14 + 1 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + int16_t gradient_h[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + int16_t gradient_v[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + + FUNC(prof_grad_filter)(gradient_h, gradient_v, AFFINE_MIN_BLOCK_SIZE, src1, MAX_PB_SIZE, AFFINE_MIN_BLOCK_SIZE, AFFINE_MIN_BLOCK_SIZE, 0); + + for (int y = 0; y < AFFINE_MIN_BLOCK_SIZE; y++) { + for (int x = 0; x < AFFINE_MIN_BLOCK_SIZE; x++) { + const int o = y * AFFINE_MIN_BLOCK_SIZE + x; + const int di = gradient_h[o] * diff_mv_x[o] + gradient_v[o] * diff_mv_y[o]; + const int val = src1[x] + av_clip(di, -limit, limit - 1); + dst[x] = av_clip_pixel((val + src0[x] + offset) >> shift); + + } + src0 += MAX_PB_SIZE; + src1 += MAX_PB_SIZE; + dst += dst_stride; + } +} + +static void FUNC(apply_prof_bi_w)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const int16_t *src0, const int16_t *src1,const int16_t *diff_mv_x, const int16_t *diff_mv_y, + const int denom, const int w0, const int w1, const int _o0, int _o1) +{ + const int limit = (1 << FFMAX(13, BIT_DEPTH + 1)); ///< dILimit + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + pixel *dst = (pixel*)_dst; + const int o0 = _o0 * (1 << (BIT_DEPTH - 8)); + const int o1 = _o1 * (1 << (BIT_DEPTH - 8)); + const int shift = 14 + 1 - BIT_DEPTH; + const int log2Wd = denom + shift - 1; + const int offset = ((o0 + o1 + 1) * (1 << log2Wd)); + int16_t gradient_h[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + int16_t gradient_v[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + + + FUNC(prof_grad_filter)(gradient_h, gradient_v, AFFINE_MIN_BLOCK_SIZE, src1, MAX_PB_SIZE, AFFINE_MIN_BLOCK_SIZE, AFFINE_MIN_BLOCK_SIZE, 0); + + for (int y = 0; y < AFFINE_MIN_BLOCK_SIZE; y++) { + for (int x = 0; x < AFFINE_MIN_BLOCK_SIZE; x++) { + const int o = y * AFFINE_MIN_BLOCK_SIZE + x; + const int di = gradient_h[o] * diff_mv_x[o] + gradient_v[o] * diff_mv_y[o]; + const int val = src1[x] + av_clip(di, -limit, limit - 1); + dst[x] = av_clip_pixel((val * w1 + src0[x] * w0 + offset) >> (log2Wd + 1)); + } + src0 += MAX_PB_SIZE; + src1 += MAX_PB_SIZE; + dst += dst_stride; + } +} + +static void FUNC(derive_bdof_vx_vy)(const int16_t *_src0, const int16_t *_src1, + const int16_t **gradient_h, const int16_t **gradient_v, ptrdiff_t gradient_stride, + int* vx, int* vy) +{ + const int shift2 = 4; + const int shift3 = 1; + const int thres = 1 << 4; + int sgx2 = 0, sgy2 = 0, sgxgy = 0, sgxdi = 0, sgydi = 0; + const int16_t *src0 = _src0 - 1 - MAX_PB_SIZE; + const int16_t *src1 = _src1 - 1 - MAX_PB_SIZE; + + for (int y = 0; y < BDOF_GRADIENT_SIZE; y++) { + for (int x = 0; x < BDOF_GRADIENT_SIZE; x++) { + const int diff = (src0[x] >> shift2) - (src1[x] >> shift2); + const int idx = gradient_stride * y + x; + const int temph = (gradient_h[0][idx] + gradient_h[1][idx]) >> shift3; + const int tempv = (gradient_v[0][idx] + gradient_v[1][idx]) >> shift3; + sgx2 += FFABS(temph); + sgy2 += FFABS(tempv); + sgxgy += VVC_SIGN(tempv) * temph; + sgxdi += -VVC_SIGN(temph) * diff; + sgydi += -VVC_SIGN(tempv) * diff; + } + src0 += MAX_PB_SIZE; + src1 += MAX_PB_SIZE; + } + *vx = sgx2 > 0 ? av_clip((sgxdi << 2) >> av_log2(sgx2) , -thres + 1, thres - 1) : 0; + *vy = sgy2 > 0 ? av_clip(((sgydi << 2) - ((*vx * sgxgy) >> 1)) >> av_log2(sgy2), -thres + 1, thres - 1) : 0; +} + +static void FUNC(apply_bdof_min_block)(pixel* dst, const ptrdiff_t dst_stride, const int16_t *src0, const int16_t *src1, + const int16_t **gradient_h, const int16_t **gradient_v, const int vx, const int vy) +{ + const int shift4 = 15 - BIT_DEPTH; + const int offset4 = 1 << (shift4 - 1); + + const int16_t* gh[] = { gradient_h[0] + 1 + BDOF_PADDED_SIZE, gradient_h[1] + 1 + BDOF_PADDED_SIZE }; + const int16_t* gv[] = { gradient_v[0] + 1 + BDOF_PADDED_SIZE, gradient_v[1] + 1 + BDOF_PADDED_SIZE }; + + for (int y = 0; y < BDOF_BLOCK_SIZE; y++) { + for (int x = 0; x < BDOF_BLOCK_SIZE; x++) { + const int idx = y * BDOF_PADDED_SIZE + x; + const int bdof_offset = vx * (gh[0][idx] - gh[1][idx]) + vy * (gv[0][idx] - gv[1][idx]); + dst[x] = av_clip_pixel((src0[x] + offset4 + src1[x] + bdof_offset) >> shift4); + } + dst += dst_stride; + src0 += MAX_PB_SIZE; + src1 += MAX_PB_SIZE; + } +} + +static void FUNC(apply_bdof)(uint8_t *_dst, const ptrdiff_t _dst_stride, int16_t *_src0, int16_t *_src1, + const int block_w, const int block_h) +{ + int16_t gradient_h[2][BDOF_PADDED_SIZE * BDOF_PADDED_SIZE]; + int16_t gradient_v[2][BDOF_PADDED_SIZE * BDOF_PADDED_SIZE]; + int vx, vy; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + pixel* dst = (pixel*)_dst; + + FUNC(prof_grad_filter)(gradient_h[0], gradient_v[0], BDOF_PADDED_SIZE, + _src0, MAX_PB_SIZE, block_w, block_h, 1); + pad_int16(_src0, MAX_PB_SIZE, block_w, block_h); + FUNC(prof_grad_filter)(gradient_h[1], gradient_v[1], BDOF_PADDED_SIZE, + _src1, MAX_PB_SIZE, block_w, block_h, 1); + pad_int16(_src1, MAX_PB_SIZE, block_w, block_h); + + for (int y = 0; y < block_h; y += BDOF_BLOCK_SIZE) { + for (int x = 0; x < block_w; x += BDOF_BLOCK_SIZE) { + const int16_t* src0 = _src0 + y * MAX_PB_SIZE + x; + const int16_t* src1 = _src1 + y * MAX_PB_SIZE + x; + pixel *d = dst + x; + const int idx = BDOF_PADDED_SIZE * y + x; + const int16_t* gh[] = { gradient_h[0] + idx, gradient_h[1] + idx }; + const int16_t* gv[] = { gradient_v[0] + idx, gradient_v[1] + idx }; + FUNC(derive_bdof_vx_vy)(src0, src1, gh, gv, BDOF_PADDED_SIZE, &vx, &vy); + FUNC(apply_bdof_min_block)(d, dst_stride, src0, src1, gh, gv, vx, vy); + } + dst += BDOF_BLOCK_SIZE * dst_stride; + } +} + +#define DMVR_FILTER(src, stride) \ + (filter[0] * src[x] + \ + filter[1] * src[x + stride]) + +//8.5.3.2.2 Luma sample bilinear interpolation process +static void FUNC(dmvr_vvc_luma)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width) +{ + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); +#if BIT_DEPTH > 10 + const int shift4 = BIT_DEPTH - 10; + const int offset4 = 1 << (shift4 - 1); +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { +#if BIT_DEPTH > 10 + dst[x] = (src[x] + offset4) >> shift4; +#else + dst[x] = src[x] << (10 - BIT_DEPTH); +#endif + } + src += src_stride; + dst += MAX_PB_SIZE; + } + +} + +//8.5.3.2.2 Luma sample bilinear interpolation process +static void FUNC(dmvr_vvc_luma_h)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width) +{ + const pixel *src = (const pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_dmvr_filters[mx]; + const int shift1 = BIT_DEPTH - 6; + const int offset1 = 1 << (shift1 - 1); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = (DMVR_FILTER(src, 1) + offset1) >> shift1; + src += src_stride; + dst += MAX_PB_SIZE; + } +} + +//8.5.3.2.2 Luma sample bilinear interpolation process +static void FUNC(dmvr_vvc_luma_v)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width) +{ + const pixel *src = (pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_dmvr_filters[my]; + const int shift1 = BIT_DEPTH - 6; + const int offset1 = 1 << (shift1 - 1); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = (DMVR_FILTER(src, src_stride) + offset1) >> shift1; + src += src_stride; + dst += MAX_PB_SIZE; + } + +} + +//8.5.3.2.2 Luma sample bilinear interpolation process +static void FUNC(dmvr_vvc_luma_hv)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width) +{ + int16_t tmp_array[(MAX_PB_SIZE + BILINEAR_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const pixel *src = (const pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_dmvr_filters[mx]; + const int shift1 = BIT_DEPTH - 6; + const int offset1 = 1 << (shift1 - 1); + const int shift2 = 4; + const int offset2 = 1 << (shift2 - 1); + + src -= BILINEAR_EXTRA_BEFORE * src_stride; + for (int y = 0; y < height + BILINEAR_EXTRA; y++) { + for (int x = 0; x < width; x++) + tmp[x] = (DMVR_FILTER(src, 1) + offset1) >> shift1; + src += src_stride; + tmp += MAX_PB_SIZE; + } + + tmp = tmp_array + BILINEAR_EXTRA_BEFORE * MAX_PB_SIZE; + filter = ff_vvc_dmvr_filters[my]; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = (DMVR_FILTER(tmp, MAX_PB_SIZE) + offset2) >> shift2; + tmp += MAX_PB_SIZE; + dst += MAX_PB_SIZE; + } +} + +#define PEL_FUNC(dst, idx1, idx2, idx3, a) \ + inter->dst[idx1][idx2][idx3] = FUNC(a) \ + +#define DIR_FUNCS(d, C, c) \ + PEL_FUNC(put_##d, C, 0, 0, put_vvc_pel_##d##_pixels); \ + PEL_FUNC(put_##d, C, 0, 1, put_vvc_##c##_##d##_h); \ + PEL_FUNC(put_##d, C, 1, 0, put_vvc_##c##_##d##_v); \ + PEL_FUNC(put_##d, C, 1, 1, put_vvc_##c##_##d##_hv); \ + PEL_FUNC(put_##d##_w, C, 0, 0, put_vvc_pel_##d##_w_pixels); \ + PEL_FUNC(put_##d##_w, C, 0, 1, put_vvc_##c##_##d##_w_h); \ + PEL_FUNC(put_##d##_w, C, 1, 0, put_vvc_##c##_##d##_w_v); \ + PEL_FUNC(put_##d##_w, C, 1, 1, put_vvc_##c##_##d##_w_hv); + +#define FUNCS(C, c) \ + PEL_FUNC(put, C, 0, 0, put_vvc_pel_pixels); \ + PEL_FUNC(put, C, 0, 1, put_vvc_##c##_h); \ + PEL_FUNC(put, C, 1, 0, put_vvc_##c##_v); \ + PEL_FUNC(put, C, 1, 1, put_vvc_##c##_hv); \ + DIR_FUNCS(uni, C, c); \ + DIR_FUNCS(bi, C, c); \ + +static void FUNC(ff_vvc_inter_dsp_init)(VVCInterDSPContext *const inter) +{ + FUNCS(LUMA, luma); + FUNCS(CHROMA, chroma); + + inter->dmvr[0][0] = FUNC(dmvr_vvc_luma); + inter->dmvr[0][1] = FUNC(dmvr_vvc_luma_h); + inter->dmvr[1][0] = FUNC(dmvr_vvc_luma_v); + inter->dmvr[1][1] = FUNC(dmvr_vvc_luma_hv); + + inter->put_ciip = FUNC(put_vvc_ciip); + inter->put_gpm = FUNC(put_vvc_gpm); + + inter->fetch_samples = FUNC(fetch_samples); + inter->bdof_fetch_samples = FUNC(bdof_fetch_samples); + inter->apply_prof = FUNC(apply_prof); + inter->apply_prof_uni = FUNC(apply_prof_uni); + inter->apply_prof_uni_w = FUNC(apply_prof_uni_w); + inter->apply_prof_bi = FUNC(apply_prof_bi); + inter->apply_prof_bi_w = FUNC(apply_prof_bi_w); + inter->apply_bdof = FUNC(apply_bdof); + inter->prof_grad_filter = FUNC(prof_grad_filter); + inter->sad = vvc_sad; +} + +#undef FUNCS +#undef PEL_FUNC +#undef DMVR_FUNCS diff --git a/libavcodec/vvc/vvc_intra.c b/libavcodec/vvc/vvc_intra.c new file mode 100644 index 00000000000..7af2af439b3 --- /dev/null +++ b/libavcodec/vvc/vvc_intra.c @@ -0,0 +1,768 @@ +/* + * VVC intra prediction + * + * Copyright (C) 2021 Nuo Mi + * + * 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 "vvc_data.h" +#include "vvc_inter.h" +#include "vvc_intra.h" +#include "vvc_itx_1d.h" + +static int is_cclm(enum IntraPredMode mode) +{ + return mode == INTRA_LT_CCLM || mode == INTRA_L_CCLM || mode == INTRA_T_CCLM; +} + +static int derive_ilfnst_pred_mode_intra(const VVCLocalContext *lc, const TransformBlock *tb) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const CodingUnit *cu = lc->cu; + const int x_tb = tb->x0 >> fc->ps.sps->min_cb_log2_size_y; + const int y_tb = tb->y0 >> fc->ps.sps->min_cb_log2_size_y; + const int x_c = (tb->x0 + (tb->tb_width << sps->hshift[1] >> 1) ) >> fc->ps.sps->min_cb_log2_size_y; + const int y_c = (tb->y0 + (tb->tb_height << sps->vshift[1] >> 1)) >> fc->ps.sps->min_cb_log2_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int intra_mip_flag = SAMPLE_CTB(fc->tab.imf, x_tb, y_tb); + int pred_mode_intra = tb->c_idx == 0 ? cu->intra_pred_mode_y : cu->intra_pred_mode_c; + if (intra_mip_flag && !tb->c_idx) { + pred_mode_intra = INTRA_PLANAR; + } else if (is_cclm(pred_mode_intra)) { + int intra_mip_flag_c = SAMPLE_CTB(fc->tab.imf, x_c, y_c); + int cu_pred_mode = SAMPLE_CTB(fc->tab.cpm[0], x_c, y_c); + if (intra_mip_flag_c) { + pred_mode_intra = INTRA_PLANAR; + } else if (cu_pred_mode == MODE_IBC || cu_pred_mode == MODE_PLT) { + pred_mode_intra = INTRA_DC; + } else { + pred_mode_intra = SAMPLE_CTB(fc->tab.ipm, x_c, y_c); + } + } + pred_mode_intra = ff_vvc_wide_angle_mode_mapping(cu, tb->tb_width, tb->tb_height, tb->c_idx, pred_mode_intra); + + return pred_mode_intra; +} + +//8.7.4 Transformation process for scaled transform coefficients +static void ilfnst_transform(const VVCLocalContext *lc, TransformBlock *tb) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const CodingUnit *cu = lc->cu; + const int w = tb->tb_width; + const int h = tb->tb_height; + const int n_lfnst_out_size = (w >= 8 && h >= 8) ? 48 : 16; ///< nLfnstOutSize + const int log2_lfnst_size = (w >= 8 && h >= 8) ? 3 : 2; ///< log2LfnstSize + const int n_lfnst_size = 1 << log2_lfnst_size; ///< nLfnstSize + const int non_zero_size = ((w == 8 && h == 8) || (w == 4 && h == 4)) ? 8 : 16; ///< nonZeroSize + const int pred_mode_intra = derive_ilfnst_pred_mode_intra(lc, tb); + const int transpose = pred_mode_intra > 34; + int u[16], v[48]; + + for (int x = 0; x < non_zero_size; x++) { + int xc = ff_vvc_diag_scan_x[2][2][x]; + int yc = ff_vvc_diag_scan_y[2][2][x]; + u[x] = tb->coeffs[w * yc + xc]; + } + ff_vvc_inv_lfnst_1d(v, u, non_zero_size, n_lfnst_out_size, pred_mode_intra, + cu->lfnst_idx, sps->log2_transform_range); + if (transpose) { + int *dst = tb->coeffs; + const int *src = v; + if (n_lfnst_size == 4) { + for (int y = 0; y < 4; y++) { + dst[0] = src[0]; + dst[1] = src[4]; + dst[2] = src[8]; + dst[3] = src[12]; + src++; + dst += w; + } + } else { + for (int y = 0; y < 8; y++) { + dst[0] = src[0]; + dst[1] = src[8]; + dst[2] = src[16]; + dst[3] = src[24]; + if (y < 4) { + dst[4] = src[32]; + dst[5] = src[36]; + dst[6] = src[40]; + dst[7] = src[44]; + } + src++; + dst += w; + } + } + + } else { + int *dst = tb->coeffs; + const int *src = v; + for (int y = 0; y < n_lfnst_size; y++) { + int size = (y < 4) ? n_lfnst_size : 4; + memcpy(dst, src, size * sizeof(int)); + src += size; + dst += w; + } + } + tb->max_scan_x = n_lfnst_size - 1; + tb->max_scan_y = n_lfnst_size - 1; +} + +//part of 8.7.4 Transformation process for scaled transform coefficients +static void derive_transform_type(const VVCFrameContext *fc, const VVCLocalContext *lc, const TransformBlock *tb, enum TxType *trh, enum TxType *trv) +{ + const CodingUnit *cu = lc->cu; + static const enum TxType mts_to_trh[] = {DCT2, DST7, DCT8, DST7, DCT8}; + static const enum TxType mts_to_trv[] = {DCT2, DST7, DST7, DCT8, DCT8}; + const VVCSPS *sps = fc->ps.sps; + int implicit_mts_enabled = 0; + if (tb->c_idx || (cu->isp_split_type != ISP_NO_SPLIT && cu->lfnst_idx)) { + *trh = *trv = DCT2; + return; + } + + if (sps->r->sps_mts_enabled_flag) { + if (cu->isp_split_type != ISP_NO_SPLIT || + (cu->sbt_flag && FFMAX(tb->tb_width, tb->tb_height) <= 32) || + (!sps->r->sps_explicit_mts_intra_enabled_flag && cu->pred_mode == MODE_INTRA && + !cu->lfnst_idx && !cu->intra_mip_flag)) { + implicit_mts_enabled = 1; + } + } + if (implicit_mts_enabled) { + const int w = tb->tb_width; + const int h = tb->tb_height; + if (cu->sbt_flag) { + *trh = (cu->sbt_horizontal_flag || cu->sbt_pos_flag) ? DST7 : DCT8; + *trv = (!cu->sbt_horizontal_flag || cu->sbt_pos_flag) ? DST7 : DCT8; + } else { + *trh = (w >= 4 && w <= 16) ? DST7 : DCT2; + *trv = (h >= 4 && h <= 16) ? DST7 : DCT2; + } + return; + } + *trh = mts_to_trh[cu->mts_idx]; + *trv = mts_to_trv[cu->mts_idx]; +} + +static void add_residual_for_joint_coding_chroma(VVCLocalContext *lc, + const TransformUnit *tu, TransformBlock *tb, const int chroma_scale) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const int c_sign = 1 - 2 * fc->ps.ph.r->ph_joint_cbcr_sign_flag; + const int shift = tu->coded_flag[1] ^ tu->coded_flag[2]; + const int c_idx = 1 + tu->coded_flag[1]; + const ptrdiff_t stride = fc->frame->linesize[c_idx]; + const int hs = fc->ps.sps->hshift[c_idx]; + const int vs = fc->ps.sps->vshift[c_idx]; + uint8_t *dst = &fc->frame->data[c_idx][(tb->y0 >> vs) * stride + + ((tb->x0 >> hs) << fc->ps.sps->pixel_shift)]; + if (chroma_scale) { + fc->vvcdsp.itx.pred_residual_joint(tb->coeffs, tb->tb_width, tb->tb_height, c_sign, shift); + fc->vvcdsp.intra.lmcs_scale_chroma(lc, tb->coeffs, tb->coeffs, tb->tb_width, tb->tb_height, cu->x0, cu->y0); + fc->vvcdsp.itx.add_residual(dst, tb->coeffs, tb->tb_width, tb->tb_height, stride); + } else { + fc->vvcdsp.itx.add_residual_joint(dst, tb->coeffs, tb->tb_width, tb->tb_height, stride, c_sign, shift); + } +} + +static int add_reconstructed_area(VVCLocalContext *lc, const int ch_type, const int x0, const int y0, const int w, const int h) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const int hs = sps->hshift[ch_type]; + const int vs = sps->vshift[ch_type]; + ReconstructedArea *a; + + if (lc->num_ras[ch_type] >= FF_ARRAY_ELEMS(lc->ras[ch_type])) + return AVERROR_INVALIDDATA; + + a = &lc->ras[ch_type][lc->num_ras[ch_type]]; + a->x = x0 >> hs; + a->y = y0 >> vs; + a->w = w >> hs; + a->h = h >> vs; + lc->num_ras[ch_type]++; + + return 0; +} + +static void add_tu_area(const TransformUnit *tu, int *x0, int *y0, int *w, int *h) +{ + *x0 = tu->x0; + *y0 = tu->y0; + *w = tu->width; + *h = tu->height; +} + +#define MIN_ISP_PRED_WIDTH 4 +static int get_luma_predict_unit(const CodingUnit *cu, const TransformUnit *tu, const int idx, int *x0, int *y0, int *w, int *h) +{ + int has_luma = 1; + add_tu_area(tu, x0, y0, w, h); + if (cu->isp_split_type == ISP_VER_SPLIT && tu->width < MIN_ISP_PRED_WIDTH) { + *w = MIN_ISP_PRED_WIDTH; + has_luma = !(idx % (MIN_ISP_PRED_WIDTH / tu->width)); + } + return has_luma; +} + +static int get_chroma_predict_unit(const CodingUnit *cu, const TransformUnit *tu, const int idx, int *x0, int *y0, int *w, int *h) +{ + if (cu->isp_split_type == ISP_NO_SPLIT) { + add_tu_area(tu, x0, y0, w, h); + return 1; + } + if (idx == cu->num_intra_subpartitions - 1) { + *x0 = cu->x0; + *y0 = cu->y0; + *w = cu->cb_width; + *h = cu->cb_height; + return 1; + } + return 0; +} + +//8.4.5.1 General decoding process for intra blocks +static void predict_intra(VVCLocalContext *lc, const TransformUnit *tu, const int idx, const int target_ch_type) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const VVCTreeType tree_type = cu->tree_type; + int x0, y0, w, h; + if (cu->pred_mode != MODE_INTRA) { + add_reconstructed_area(lc, target_ch_type, tu->x0, tu->y0, tu->width, tu->height); + return; + } + if (!target_ch_type && tree_type != DUAL_TREE_CHROMA) { + if (get_luma_predict_unit(cu, tu, idx, &x0, &y0, &w, &h)) { + ff_vvc_set_neighbour_available(lc, x0, y0, w, h); + fc->vvcdsp.intra.intra_pred(lc, x0, y0, w, h, 0); + add_reconstructed_area(lc, 0, x0, y0, w, h); + } + } + if (target_ch_type && tree_type != DUAL_TREE_LUMA) { + if (get_chroma_predict_unit(cu, tu, idx, &x0, &y0, &w, &h)){ + ff_vvc_set_neighbour_available(lc, x0, y0, w, h); + if (is_cclm(cu->intra_pred_mode_c)) { + fc->vvcdsp.intra.intra_cclm_pred(lc, x0, y0, w, h); + } else { + fc->vvcdsp.intra.intra_pred(lc, x0, y0, w, h, 1); + fc->vvcdsp.intra.intra_pred(lc, x0, y0, w, h, 2); + } + add_reconstructed_area(lc, 1, x0, y0, w, h); + } + } +} + +static void scale_clip(int *coeff, const int nzw, const int w, const int h, + const int shift, const int log2_transform_range) +{ + const int add = 1 << (shift - 1); + for (int y = 0; y < h; y++) { + int *p = coeff + y * w; + for (int x = 0; x < nzw; x++) { + *p = av_clip_intp2((*p + add) >> shift, log2_transform_range); + p++; + } + memset(p, 0, sizeof(*p) * (w - nzw)); + } +} + +static void scale(int *out, const int *in, const int w, const int h, const int shift) +{ + const int add = 1 << (shift - 1); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + int *o = out + y * w + x; + const int *i = in + y * w + x; + *o = (*i + add) >> shift; + } + } +} + +// part of 8.7.3 Scaling process for transform coefficients +static void derive_qp(const VVCLocalContext *lc, const TransformUnit *tu, TransformBlock *tb) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const CodingUnit *cu = lc->cu; + int qp, qp_act_offset; + + if (tb->c_idx == 0) { + //fix me + qp = cu->qp[LUMA] + sps->qp_bd_offset; + qp_act_offset = cu->act_enabled_flag ? -5 : 0; + } else { + const int is_jcbcr = tu->joint_cbcr_residual_flag && tu->coded_flag[CB] && tu->coded_flag[CR]; + const int idx = is_jcbcr ? JCBCR : tb->c_idx; + qp = cu->qp[idx]; + qp_act_offset = cu->act_enabled_flag ? 1 : 0; + } + if (tb->ts) { + const int qp_prime_ts_min = 4 + 6 * sps->r->sps_min_qp_prime_ts; + + tb->qp = av_clip(qp + qp_act_offset, qp_prime_ts_min, 63 + sps->qp_bd_offset); + tb->rect_non_ts_flag = 0; + tb->bd_shift = 10; + } else { + const int log_sum = tb->log2_tb_width + tb->log2_tb_height; + const int rect_non_ts_flag = log_sum & 1; + + tb->qp = av_clip(qp + qp_act_offset, 0, 63 + sps->qp_bd_offset); + tb->rect_non_ts_flag = rect_non_ts_flag; + tb->bd_shift = sps->bit_depth + rect_non_ts_flag + (log_sum / 2) + + 10 - sps->log2_transform_range + rsh->sh_dep_quant_used_flag; + } + tb->bd_offset = (1 << tb->bd_shift) >> 1; +} + +//8.7.3 Scaling process for transform coefficients +static av_always_inline int derive_scale(const TransformBlock *tb, const int sh_dep_quant_used_flag) +{ + static const uint8_t rem6[63 + 2 * 6 + 1] = { + 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, + 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, + 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, + 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3 + }; + + static const uint8_t div6[63 + 2 * 6 + 1] = { + 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, + 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, + 10, 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12 + }; + + const static int level_scale[2][6] = { + { 40, 45, 51, 57, 64, 72 }, + { 57, 64, 72, 80, 90, 102 } + }; + const int addin = sh_dep_quant_used_flag && !tb->ts; + const int qp = tb->qp + addin; + + return level_scale[tb->rect_non_ts_flag][rem6[qp]] << div6[qp]; +} + +//8.7.3 Scaling process for transform coefficients +static const uint8_t* derive_scale_m(const VVCLocalContext *lc, const TransformBlock *tb, uint8_t *scale_m) +{ + //Table 38 – Specification of the scaling matrix identifier variable id according to predMode, cIdx, nTbW, and nTbH + const int ids[2][3][6] = { + { + { 0, 2, 8, 14, 20, 26 }, + { 0, 3, 9, 15, 21, 21 }, + { 0, 4, 10, 16, 22, 22 } + }, + { + { 0, 5, 11, 17, 23, 27 }, + { 0, 6, 12, 18, 24, 24 }, + { 1, 7, 13, 19, 25, 25 }, + } + }; + const VVCFrameParamSets *ps = &lc->fc->ps; + const VVCSPS *sps = ps->sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const CodingUnit *cu = lc->cu; + const VVCScalingList *sl = ps->sl; + const int id = ids[cu->pred_mode != MODE_INTRA][tb->c_idx][FFMAX(tb->log2_tb_height, tb->log2_tb_width) - 1]; + const int log2_matrix_size = (id < 2) ? 1 : (id < 8) ? 2 : 3; + uint8_t *p = scale_m; + + av_assert0(!sps->r->sps_scaling_matrix_for_alternative_colour_space_disabled_flag); + + if (!rsh->sh_explicit_scaling_list_used_flag || tb->ts || + sps->r->sps_scaling_matrix_for_lfnst_disabled_flag && cu->apply_lfnst_flag[tb->c_idx]) + return ff_vvc_default_scale_m; + + if (!sl) { + av_log(lc->fc->avctx, AV_LOG_WARNING, "bug: no scaling list aps, id = %d", ps->ph.r->ph_scaling_list_aps_id); + return ff_vvc_default_scale_m; + } + + for (int y = tb->min_scan_y; y <= tb->max_scan_y; y++) { + const int off = y << log2_matrix_size >> tb->log2_tb_height << log2_matrix_size; + const uint8_t *m = &sl->scaling_matrix_rec[id][off]; + + for (int x = tb->min_scan_x; x <= tb->max_scan_x; x++) + *p++ = m[x << log2_matrix_size >> tb->log2_tb_width]; + } + if (id >= SL_START_16x16 && !tb->min_scan_x && !tb->min_scan_y) + *scale_m = sl->scaling_matrix_dc_rec[id - SL_START_16x16]; + + return scale_m; +} + +//8.7.3 Scaling process for transform coefficients +static av_always_inline int scale_coeff(const TransformBlock *tb, int coeff, + const int scale, const int scale_m, const int log2_transform_range) +{ + coeff = (coeff * scale * scale_m + tb->bd_offset) >> tb->bd_shift; + coeff = av_clip_intp2(coeff, log2_transform_range); + return coeff; +} + +static void dequant(const VVCLocalContext *lc, const TransformUnit *tu, TransformBlock *tb) +{ + uint8_t tmp[MAX_TB_SIZE * MAX_TB_SIZE]; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const VVCSPS *sps = lc->fc->ps.sps; + const uint8_t *scale_m = derive_scale_m(lc, tb, tmp); + int scale; + + derive_qp(lc, tu, tb); + scale = derive_scale(tb, rsh->sh_dep_quant_used_flag); + + for (int y = tb->min_scan_y; y <= tb->max_scan_y; y++) { + for (int x = tb->min_scan_x; x <= tb->max_scan_x; x++) { + int *coeff = tb->coeffs + y * tb->tb_width + x; + + if (*coeff) + *coeff = scale_coeff(tb, *coeff, scale, *scale_m, sps->log2_transform_range); + scale_m++; + } + } +} + +static void itx_2d(const VVCFrameContext *fc, TransformBlock *tb, const enum TxType trh, const enum TxType trv, int *temp) +{ + const VVCSPS *sps = fc->ps.sps; + const int w = tb->tb_width; + const int h = tb->tb_height; + const int nzw = tb->max_scan_x + 1; + + for (int x = 0; x < nzw; x++) + fc->vvcdsp.itx.itx[trv][tb->log2_tb_height - 1](temp + x, w, tb->coeffs + x, w); + scale_clip(temp, nzw, w, h, 7, sps->log2_transform_range); + + for (int y = 0; y < h; y++) + fc->vvcdsp.itx.itx[trh][tb->log2_tb_width - 1](tb->coeffs + y * w, 1, temp + y * w, 1); + scale(tb->coeffs, tb->coeffs, w, h, 5 + sps->log2_transform_range - sps->bit_depth); +} + +static void itx_1d(const VVCFrameContext *fc, TransformBlock *tb, const enum TxType trh, const enum TxType trv, int *temp) +{ + const VVCSPS *sps = fc->ps.sps; + const int w = tb->tb_width; + const int h = tb->tb_height; + + if (w > 1) + fc->vvcdsp.itx.itx[trh][tb->log2_tb_width - 1](temp, 1, tb->coeffs, 1); + else + fc->vvcdsp.itx.itx[trv][tb->log2_tb_height - 1](temp, 1, tb->coeffs, 1); + scale(tb->coeffs, temp, w, h, 6 + sps->log2_transform_range - sps->bit_depth); +} + +static void transform_bdpcm(TransformBlock *tb, const VVCLocalContext *lc, const CodingUnit *cu) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const IntraPredMode mode = tb->c_idx ? cu->intra_pred_mode_c : cu->intra_pred_mode_y; + const int vertical = mode == INTRA_VERT; + lc->fc->vvcdsp.itx.transform_bdpcm(tb->coeffs, tb->tb_width, tb->tb_height, + vertical, sps->log2_transform_range); + if (vertical) + tb->max_scan_y = tb->tb_height - 1; + else + tb->max_scan_x = tb->tb_width - 1; +} + +static void itransform(VVCLocalContext *lc, TransformUnit *tu, const int tu_idx, const int target_ch_type) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCSH *sh = &lc->sc->sh; + const CodingUnit *cu = lc->cu; + const int ps = fc->ps.sps->pixel_shift; + DECLARE_ALIGNED(32, int, temp)[MAX_TB_SIZE * MAX_TB_SIZE]; + + for (int i = 0; i < tu->nb_tbs; i++) { + TransformBlock *tb = &tu->tbs[i]; + const int c_idx = tb->c_idx; + const int ch_type = c_idx > 0; + + if (ch_type == target_ch_type && tb->has_coeffs) { + const int w = tb->tb_width; + const int h = tb->tb_height; + const int chroma_scale = ch_type && sh->r->sh_lmcs_used_flag && fc->ps.ph.r->ph_chroma_residual_scale_flag && (w * h > 4); + const ptrdiff_t stride = fc->frame->linesize[c_idx]; + const int hs = sps->hshift[c_idx]; + const int vs = sps->vshift[c_idx]; + uint8_t *dst = &fc->frame->data[c_idx][(tb->y0 >> vs) * stride + ((tb->x0 >> hs) << ps)]; + + if (cu->bdpcm_flag[tb->c_idx]) + transform_bdpcm(tb, lc, cu); + dequant(lc, tu, tb); + if (!tb->ts) { + enum TxType trh, trv; + + if (cu->apply_lfnst_flag[c_idx]) + ilfnst_transform(lc, tb); + derive_transform_type(fc, lc, tb, &trh, &trv); + if (w > 1 && h > 1) + itx_2d(fc, tb, trh, trv, temp); + else + itx_1d(fc, tb, trh, trv, temp); + } + + if (chroma_scale) + fc->vvcdsp.intra.lmcs_scale_chroma(lc, temp, tb->coeffs, w, h, cu->x0, cu->y0); + fc->vvcdsp.itx.add_residual(dst, chroma_scale ? temp : tb->coeffs, w, h, stride); + + if (tu->joint_cbcr_residual_flag && tb->c_idx) + add_residual_for_joint_coding_chroma(lc, tu, tb, chroma_scale); + } + } +} + +static int reconstruct(VVCLocalContext *lc) +{ + VVCFrameContext *fc = lc->fc; + CodingUnit *cu = lc->cu; + const int start = cu->tree_type == DUAL_TREE_CHROMA; + const int end = fc->ps.sps->r->sps_chroma_format_idc && (cu->tree_type != DUAL_TREE_LUMA); + + for (int ch_type = start; ch_type <= end; ch_type++) { + TransformUnit *tu = cu->tus.head; + for (int i = 0; tu; i++) { + predict_intra(lc, tu, i, ch_type); + itransform(lc, tu, i, ch_type); + tu = tu->next; + } + } + return 0; +} + +int ff_vvc_reconstruct(VVCLocalContext *lc, const int rs, const int rx, const int ry) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int x_ctb = rx << sps->ctb_log2_size_y; + const int y_ctb = ry << sps->ctb_log2_size_y; + CTU *ctu = fc->tab.ctus + rs; + CodingUnit *cu = ctu->cus; + int ret = 0; + + lc->num_ras[0] = lc->num_ras[1] = 0; + lc->lmcs.x_vpdu = -1; + lc->lmcs.y_vpdu = -1; + ff_vvc_decode_neighbour(lc, x_ctb, y_ctb, rx, ry, rs); + while (cu) { + lc->cu = cu; + + if (cu->ciip_flag) + ff_vvc_predict_ciip(lc); + if (cu->coded_flag) { + ret = reconstruct(lc); + } else { + add_reconstructed_area(lc, LUMA, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + add_reconstructed_area(lc, CHROMA, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + } + cu = cu->next; + } + ff_vvc_ctu_free_cus(ctu); + return ret; +} + +int ff_vvc_get_mip_size_id(const int w, const int h) +{ + if (w == 4 && h == 4) + return 0; + if ((w == 4 || h == 4) || (w == 8 && h == 8)) + return 1; + return 2; +} + +int ff_vvc_nscale_derive(const int w, const int h, const int mode) +{ + int side_size, nscale; + av_assert0(mode < INTRA_LT_CCLM && !(mode > INTRA_HORZ && mode < INTRA_VERT)); + if (mode == INTRA_PLANAR || mode == INTRA_DC || + mode == INTRA_HORZ || mode == INTRA_VERT) { + nscale = (av_log2(w) + av_log2(h) - 2) >> 2; + } else { + const int intra_pred_angle = ff_vvc_intra_pred_angle_derive(mode); + const int inv_angle = ff_vvc_intra_inv_angle_derive(intra_pred_angle); + if (mode >= INTRA_VERT) + side_size = h; + if (mode <= INTRA_HORZ) + side_size = w; + nscale = FFMIN(2, av_log2(side_size) - av_log2(3 * inv_angle - 2) + 8); + } + return nscale; +} + +int ff_vvc_need_pdpc(const int w, const int h, const uint8_t bdpcm_flag, const int mode, const int ref_idx) +{ + av_assert0(mode < INTRA_LT_CCLM); + if ((w >= 4 && h >= 4) && !ref_idx && !bdpcm_flag) { + int nscale; + if (mode == INTRA_PLANAR || mode == INTRA_DC || + mode == INTRA_HORZ || mode == INTRA_VERT) + return 1; + if (mode > INTRA_HORZ && mode < INTRA_VERT) + return 0; + nscale = ff_vvc_nscale_derive(w, h, mode); + return nscale >= 0; + + } + return 0; +} + +static const ReconstructedArea* get_reconstructed_area(const VVCLocalContext *lc, const int x, const int y, const int c_idx) +{ + const int ch_type = c_idx > 0; + for (int i = lc->num_ras[ch_type] - 1; i >= 0; i--) { + const ReconstructedArea* a = &lc->ras[ch_type][i]; + const int r = (a->x + a->w); + const int b = (a->y + a->h); + if (a->x <= x && x < r && a->y <= y && y < b) + return a; + + //it's too far away, no need check it; + if (x >= r && y >= b) + break; + } + return NULL; +} + +int ff_vvc_get_top_available(const VVCLocalContext *lc, const int x, const int y, int target_size, const int c_idx) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int hs = sps->hshift[c_idx]; + const int vs = sps->vshift[c_idx]; + const int log2_ctb_size_v = sps->ctb_log2_size_y - vs; + const int end_of_ctb_x = ((lc->cu->x0 >> sps->ctb_log2_size_y) + 1) << sps->ctb_log2_size_y; + const int y0b = av_mod_uintp2(y, log2_ctb_size_v); + const int max_x = FFMIN(fc->ps.pps->width, end_of_ctb_x) >> hs; + const ReconstructedArea *a; + int px = x; + + if (!y0b) { + if (!lc->ctb_up_flag) + return 0; + target_size = FFMIN(target_size, (lc->end_of_tiles_x >> hs) - x); + if (sps->r->sps_entropy_coding_sync_enabled_flag) + target_size = FFMIN(target_size, (end_of_ctb_x >> hs) - x); + return target_size; + } + + target_size = FFMAX(0, FFMIN(target_size, max_x - x)); + while (target_size > 0 && (a = get_reconstructed_area(lc, px, y - 1, c_idx))) { + const int sz = FFMIN(target_size, a->x + a->w - px); + px += sz; + target_size -= sz; + } + return px - x; +} + +int ff_vvc_get_left_available(const VVCLocalContext *lc, const int x, const int y, int target_size, const int c_idx) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int hs = sps->hshift[c_idx]; + const int vs = sps->vshift[c_idx]; + const int log2_ctb_size_h = sps->ctb_log2_size_y - hs; + const int x0b = av_mod_uintp2(x, log2_ctb_size_h); + const int end_of_ctb_y = ((lc->cu->y0 >> sps->ctb_log2_size_y) + 1) << sps->ctb_log2_size_y; + const int max_y = FFMIN(fc->ps.pps->height, end_of_ctb_y) >> vs; + const ReconstructedArea *a; + int py = y; + + if (!x0b && !lc->ctb_left_flag) + return 0; + + target_size = FFMAX(0, FFMIN(target_size, max_y - y)); + if (!x0b) + return target_size; + + while (target_size > 0 && (a = get_reconstructed_area(lc, x - 1, py, c_idx))) { + const int sz = FFMIN(target_size, a->y + a->h - py); + py += sz; + target_size -= sz; + } + return py - y; +} + +static int less(const void *a, const void *b) +{ + return *(const int*)a - *(const int*)b; +} + +int ff_vvc_ref_filter_flag_derive(const int mode) +{ + static const int modes[] = { -14, -12, -10, -6, INTRA_PLANAR, 2, 34, 66, 72, 76, 78, 80}; + return bsearch(&mode, modes, FF_ARRAY_ELEMS(modes), sizeof(int), less) != NULL; +} + +int ff_vvc_intra_pred_angle_derive(const int pred_mode) +{ + static const int angles[] = { + 0, 1, 2, 3, 4, 6, 8, 10, 12, 14, 16, 18, 20, 23, 26, 29, + 32, 35, 39, 45, 51, 57, 64, 73, 86, 102, 128, 171, 256, 341, 512 + }; + int sign = 1, idx, intra_pred_angle; + if (pred_mode > INTRA_DIAG) { + idx = pred_mode - INTRA_VERT; + } else if (pred_mode > 0) { + idx = INTRA_HORZ - pred_mode; + } else { + idx = INTRA_HORZ - 2 - pred_mode; + } + if (idx < 0) { + idx = -idx; + sign = -1; + } + intra_pred_angle = sign * angles[idx]; + return intra_pred_angle; +} + +#define ROUND(f) (int)(f < 0 ? -(-f + 0.5) : (f + 0.5)) +int ff_vvc_intra_inv_angle_derive(const int intra_pred_angle) +{ + float inv_angle; + av_assert0(intra_pred_angle); + inv_angle = 32 * 512.0 / intra_pred_angle; + return ROUND(inv_angle); +} + +//8.4.5.2.7 Wide angle intra prediction mode mapping proces +int ff_vvc_wide_angle_mode_mapping(const CodingUnit *cu, + const int tb_width, const int tb_height, const int c_idx, int pred_mode_intra) +{ + int nw, nh, wh_ratio, min, max; + + if (cu->isp_split_type == ISP_NO_SPLIT || c_idx) { + nw = tb_width; + nh = tb_height; + } else { + nw = cu->cb_width; + nh = cu->cb_height; + } + wh_ratio = FFABS(ff_log2(nw) - ff_log2(nh)); + max = (wh_ratio > 1) ? (8 + 2 * wh_ratio) : 8; + min = (wh_ratio > 1) ? (60 - 2 * wh_ratio) : 60; + + if (nw > nh && pred_mode_intra >=2 && pred_mode_intra < max) + pred_mode_intra += 65; + else if (nh > nw && pred_mode_intra <= 66 && pred_mode_intra > min) + pred_mode_intra -= 67; + return pred_mode_intra; +} diff --git a/libavcodec/vvc/vvc_intra.h b/libavcodec/vvc/vvc_intra.h new file mode 100644 index 00000000000..12d0dae8013 --- /dev/null +++ b/libavcodec/vvc/vvc_intra.h @@ -0,0 +1,49 @@ +/* + * VVC intra prediction + * + * Copyright (C) 2021 Nuo Mi + * + * 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_VVC_INTRA_H +#define AVCODEC_VVC_INTRA_H + +#include "vvc_ctu.h" + +/** + * reconstruct a CTU + * @param lc local context for CTU + * @param rs raster order for the CTU. + * @param rx raster order x for the CTU. + * @param ry raster order y for the CTU. + * @return AVERROR + */ +int ff_vvc_reconstruct(VVCLocalContext *lc, const int rs, const int rx, const int ry); + +//utils for vvc_intra_template +int ff_vvc_get_top_available(const VVCLocalContext *lc, int x0, int y0, int target_size, int c_idx); +int ff_vvc_get_left_available(const VVCLocalContext *lc, int x0, int y0, int target_size, int c_idx); +int ff_vvc_get_mip_size_id(int w, int h); +int ff_vvc_need_pdpc(int w, int h, uint8_t bdpcm_flag, int mode, int ref_idx); +int ff_vvc_nscale_derive(int w, int h, int mode); +int ff_vvc_ref_filter_flag_derive(int mode); +int ff_vvc_intra_pred_angle_derive(int pred_mode); +int ff_vvc_intra_inv_angle_derive(int pred_mode); +int ff_vvc_wide_angle_mode_mapping(const CodingUnit *cu, + int tb_width, int tb_height, int c_idx, int pred_mode_intra); + +#endif // AVCODEC_VVC_INTRA_H diff --git a/libavcodec/vvc/vvc_intra_template.c b/libavcodec/vvc/vvc_intra_template.c new file mode 100644 index 00000000000..81987a579ec --- /dev/null +++ b/libavcodec/vvc/vvc_intra_template.c @@ -0,0 +1,1018 @@ +/* + * VVC intra prediction DSP + * + * Copyright (C) 2021-2023 Nuomi + * + * 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 "libavcodec/bit_depth_template.c" + +#include "vvc_data.h" +#include "vvc_intra.h" + +#define POS(x, y) src[(x) + stride * (y)] + +static av_always_inline void FUNC(cclm_linear_pred)(VVCFrameContext *fc, const int x0, const int y0, + const int w, const int h, const pixel* pdsy, const int *a, const int *b, const int *k) +{ + const VVCSPS *sps = fc->ps.sps; + for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS - 1; i++) { + const int c_idx = i + 1; + const int x = x0 >> sps->hshift[c_idx]; + const int y = y0 >> sps->vshift[c_idx]; + const ptrdiff_t stride = fc->frame->linesize[c_idx] / sizeof(pixel); + pixel *src = (pixel*)fc->frame->data[c_idx] + x + y * stride; + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + const int dsy = pdsy[y * w + x]; + const int pred = ((dsy * a[i]) >> k[i]) + b[i]; + POS(x, y) = CLIP(pred); + } + } + } +} + +#define MAX_PICK_POS 4 +#define TOP 0 +#define LEFT 1 + +static av_always_inline void FUNC(cclm_get_params_default)(int *a, int *b, int *k) +{ + for (int i = 0; i < 2; i++) { + a[i] = k[i] = 0; + b[i] = 1 << (BIT_DEPTH - 1); + } +} + +static av_always_inline int FUNC(cclm_get_select_pos)(const VVCLocalContext *lc, + const int x, const int y, const int w, const int h, const int avail_t, const int avail_l, + int cnt[2], int pos[2][MAX_PICK_POS]) +{ + const enum IntraPredMode mode = lc->cu->intra_pred_mode_c; + const int num_is4 = !avail_t || !avail_l || mode != INTRA_LT_CCLM; + int num_samp[2]; + + if (mode == INTRA_LT_CCLM) { + num_samp[TOP] = avail_t ? w : 0; + num_samp[LEFT] = avail_l ? h : 0; + } else { + num_samp[TOP] = (avail_t && mode == INTRA_T_CCLM) ? ff_vvc_get_top_available(lc, x, y, w + FFMIN(w, h), 1) : 0; + num_samp[LEFT] = (avail_l && mode == INTRA_L_CCLM) ? ff_vvc_get_left_available(lc, x, y, h + FFMIN(w, h), 1) : 0; + } + if (!num_samp[TOP] && !num_samp[LEFT]) { + return 0; + } + for (int i = TOP; i <= LEFT; i++) { + const int start = num_samp[i] >> (2 + num_is4); + const int step = FFMAX(1, num_samp[i] >> (1 + num_is4)) ; + cnt[i] = FFMIN(num_samp[i], (1 + num_is4) << 1); + for (int c = 0; c < cnt[i]; c++) + pos[i][c] = start + c * step; + } + return 1; +} + +static av_always_inline void FUNC(cclm_select_luma_444)(const pixel *src, const int step, + const int cnt, const int pos[MAX_PICK_POS], pixel *sel_luma) +{ + for (int i = 0; i < cnt; i++) + sel_luma[i] = src[pos[i] * step]; +} + +static av_always_inline void FUNC(cclm_select_luma)(const VVCFrameContext *fc, + const int x0, const int y0, const int avail_t, const int avail_l, const int cnt[2], const int pos[2][MAX_PICK_POS], + pixel *sel_luma) +{ + const VVCSPS *sps = fc->ps.sps; + + const int b_ctu_boundary = !av_mod_uintp2(y0, sps->ctb_log2_size_y); + const int hs = sps->hshift[1]; + const int vs = sps->vshift[1]; + const ptrdiff_t stride = fc->frame->linesize[0] / sizeof(pixel); + + if (!hs && !vs) { + const pixel* src = (pixel*)fc->frame->data[0] + x0 + y0 * stride; + FUNC(cclm_select_luma_444)(src - avail_t * stride, 1, cnt[TOP], pos[TOP], sel_luma); + FUNC(cclm_select_luma_444)(src - avail_l, stride, cnt[LEFT], pos[LEFT], sel_luma + cnt[TOP]); + } else { + // top + if (vs && !b_ctu_boundary) { + const pixel *source = (pixel *)fc->frame->data[0] + x0 + (y0 - 2) * stride; + for (int i = 0; i < cnt[TOP]; i++) { + const int x = pos[TOP][i] << hs; + const pixel *src = source + x; + const int has_left = x || avail_l; + const pixel l = has_left ? POS(-1, 0) : POS(0, 0); + if (sps->r->sps_chroma_vertical_collocated_flag) { + sel_luma[i] = (POS(0, -1) + l + 4 * POS(0, 0) + POS(1, 0) + POS(0, 1) + 4) >> 3; + } else { + const pixel l1 = has_left ? POS(-1, 1) : POS(0, 1); + sel_luma[i] = (l + l1 + 2 * (POS(0, 0) + POS(0, 1)) + POS(1, 0) + POS(1, 1) + 4) >> 3; + } + } + } else { + const pixel *source = (pixel*)fc->frame->data[0] + x0 + (y0 - 1) * stride; + for (int i = 0; i < cnt[TOP]; i++) { + const int x = pos[TOP][i] << hs; + const pixel *src = source + x; + const int has_left = x || avail_l; + const pixel l = has_left ? POS(-1, 0) : POS(0, 0); + sel_luma[i] = (l + 2 * POS(0, 0) + POS(1, 0) + 2) >> 2; + } + } + + // left + { + const pixel *left; + const pixel *source = (pixel *)fc->frame->data[0] + x0 + y0 * stride - (1 + hs) * avail_l; + left = source - avail_l; + + for (int i = 0; i < cnt[LEFT]; i++) { + const int y = pos[LEFT][i] << vs; + const int offset = y * stride; + const pixel *l = left + offset; + const pixel *src = source + offset; + pixel pred; + if (!vs) { + pred = (*l + 2 * POS(0, 0) + POS(1, 0) + 2) >> 2; + } else { + if (sps->r->sps_chroma_vertical_collocated_flag) { + const int has_top = y || avail_t; + const pixel t = has_top ? POS(0, -1) : POS(0, 0); + pred = (*l + t + 4 * POS(0, 0) + POS(1, 0) + POS(0, 1) + 4) >> 3; + } else { + pred = (*l + *(l + stride) + 2 * POS(0, 0) + 2 * POS(0, 1) + POS(1, 0) + POS(1, 1) + 4) >> 3; + } + } + sel_luma[i + cnt[TOP]] = pred; + } + } + } +} + +static av_always_inline void FUNC(cclm_select_chroma)(const VVCFrameContext *fc, + const int x, const int y, const int cnt[2], const int pos[2][MAX_PICK_POS], + pixel sel[][MAX_PICK_POS * 2]) +{ + for (int c_idx = 1; c_idx < VVC_MAX_SAMPLE_ARRAYS; c_idx++) { + const ptrdiff_t stride = fc->frame->linesize[c_idx] / sizeof(pixel); + + //top + const pixel *src = (pixel*)fc->frame->data[c_idx] + x + (y - 1)* stride; + for (int i = 0; i < cnt[TOP]; i++) { + sel[c_idx][i] = src[pos[TOP][i]]; + } + + //left + src = (pixel*)fc->frame->data[c_idx] + x - 1 + y * stride; + for (int i = 0; i < cnt[LEFT]; i++) { + sel[c_idx][i + cnt[TOP]] = src[pos[LEFT][i] * stride]; + } + } +} + +static av_always_inline int FUNC(cclm_select_samples)(const VVCLocalContext *lc, + const int x0, const int y0, const int w, const int h, const int avail_t, const int avail_l, + pixel sel[][MAX_PICK_POS * 2]) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int x = x0 >> sps->hshift[1]; + const int y = y0 >> sps->vshift[1]; + int cnt[2], pos[2][MAX_PICK_POS]; + + if (!FUNC(cclm_get_select_pos)(lc, x, y, w, h, avail_t, avail_l, cnt, pos)) + return 0; + + FUNC(cclm_select_luma)(fc, x0, y0, avail_t, avail_l, cnt, pos, sel[LUMA]); + FUNC(cclm_select_chroma)(fc, x, y, cnt, pos, sel); + + if (cnt[TOP] + cnt[LEFT] == 2) { + for (int c_idx = 0; c_idx < VVC_MAX_SAMPLE_ARRAYS; c_idx++) { + sel[c_idx][3] = sel[c_idx][0]; + sel[c_idx][2] = sel[c_idx][1]; + sel[c_idx][0] = sel[c_idx][1]; + sel[c_idx][1] = sel[c_idx][3]; + } + } + return 1; +} + +static av_always_inline void FUNC(cclm_get_min_max)( + const pixel sel[][MAX_PICK_POS * 2], int *min, int *max) +{ + int min_grp_idx[] = { 0, 2 }; + int max_grp_idx[] = { 1, 3 }; + + if (sel[LUMA][min_grp_idx[0]] > sel[LUMA][min_grp_idx[1]]) + FFSWAP(int, min_grp_idx[0], min_grp_idx[1]); + if (sel[LUMA][max_grp_idx[0]] > sel[LUMA][max_grp_idx[1]]) + FFSWAP(int, max_grp_idx[0], max_grp_idx[1]); + if (sel[LUMA][min_grp_idx[0]] > sel[LUMA][max_grp_idx[1]]) { + FFSWAP(int, min_grp_idx[0], max_grp_idx[0]); + FFSWAP(int, min_grp_idx[1], max_grp_idx[1]); + } + if (sel[LUMA][min_grp_idx[1]] > sel[LUMA][max_grp_idx[0]]) + FFSWAP(int, min_grp_idx[1], max_grp_idx[0]); + for (int c_idx = 0; c_idx < VVC_MAX_SAMPLE_ARRAYS; c_idx++) { + max[c_idx] = (sel[c_idx][max_grp_idx[0]] + sel[c_idx][max_grp_idx[1]] + 1) >> 1; + min[c_idx] = (sel[c_idx][min_grp_idx[0]] + sel[c_idx][min_grp_idx[1]] + 1) >> 1; + } +} + +static av_always_inline void FUNC(cclm_get_params)(const VVCLocalContext *lc, + const int x0, const int y0, const int w, const int h, const int avail_t, const int avail_l, + int *a, int *b, int *k) +{ + pixel sel[VVC_MAX_SAMPLE_ARRAYS][MAX_PICK_POS * 2]; + int max[VVC_MAX_SAMPLE_ARRAYS], min[VVC_MAX_SAMPLE_ARRAYS]; + int diff; + + if (!FUNC(cclm_select_samples)(lc, x0, y0, w, h, avail_t, avail_l, sel)) { + FUNC(cclm_get_params_default)(a, b, k); + return; + } + + FUNC(cclm_get_min_max)(sel, min, max); + + diff = max[LUMA] - min[LUMA]; + if (diff == 0) { + for (int i = 0; i < 2; i++) { + a[i] = k[i] = 0; + b[i] = min[i + 1]; + } + return; + } + for (int i = 0; i < 2; i++) { + const static int div_sig_table[] = {0, 7, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 1, 1, 0}; + const int diffc = max[i + 1] - min[i + 1]; + int x = av_log2(diff); + int y, v, sign, add; + const int norm_diff = ((diff << 4) >> x) & 15; + x += (norm_diff) ? 1 : 0; + y = abs(diffc) > 0 ? av_log2(abs(diffc)) + 1 : 0; + v = div_sig_table[norm_diff] | 8; + add = (1 << y >> 1); + a[i] = (diffc * v + add) >> y; + k[i] = FFMAX(1, 3 + x -y); + sign = a[i] < 0 ? -1 : (a[i] > 0); + a[i] = ((3 + x - y) < 1) ? sign * 15 : a[i]; + b[i] = min[i + 1] - ((a[i] * min[0]) >> k[i]); + } + +} + +#undef TOP +#undef LEFT + +static av_always_inline void FUNC(cclm_get_luma_rec_pixels)(const VVCFrameContext *fc, + const int x0, const int y0, const int w, const int h, const int avail_t, const int avail_l, + pixel *pdsy) +{ + const int hs = fc->ps.sps->hshift[1]; + const int vs = fc->ps.sps->vshift[1]; + const ptrdiff_t stride = fc->frame->linesize[0] / sizeof(pixel); + const pixel *source = (pixel*)fc->frame->data[0] + x0 + y0 * stride; + const pixel *left = source - avail_l; + const pixel *top = source - avail_t * stride; + + const VVCSPS *sps = fc->ps.sps; + if (!hs && !vs) { + for (int i = 0; i < h; i++) + memcpy(pdsy + i * w, source + i * stride, w * sizeof(pixel)); + return; + } + for (int i = 0; i < h; i++) { + const pixel *src = source; + const pixel *l = left; + const pixel *t = top; + if (!vs) { + for (int j = 0; j < w; j++) { + pixel pred = (*l + 2 * POS(0, 0) + POS(1, 0) + 2) >> 2; + pdsy[i * w + j] = pred; + src += 2; + l = src - 1; + } + + } else { + if (sps->r->sps_chroma_vertical_collocated_flag) { + for (int j = 0; j < w; j++) { + pixel pred = (*l + *t + 4 * POS(0, 0) + POS(1, 0) + POS(0, 1) + 4) >> 3; + pdsy[i * w + j] = pred; + src += 2; + t += 2; + l = src - 1; + } + } else { + for (int j = 0; j < w; j++) { + pixel pred = (*l + *(l + stride) + 2 * POS(0, 0) + 2 * POS(0, 1) + POS(1, 0) + POS(1, 1) + 4) >> 3; + + pdsy[i * w + j] = pred; + src += 2; + l = src - 1; + } + } + } + source += (stride << vs); + left += (stride << vs); + top = source - stride; + } +} + +static av_always_inline void FUNC(cclm_pred_default)(VVCFrameContext *fc, + const int x, const int y, const int w, const int h, const int avail_t, const int avail_l) +{ + for (int c_idx = 1; c_idx < VVC_MAX_SAMPLE_ARRAYS; c_idx++) { + const ptrdiff_t stride = fc->frame->linesize[c_idx] / sizeof(pixel); + pixel *dst = (pixel*)fc->frame->data[c_idx] + x + y * stride; + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + dst[j] = 1 << (BIT_DEPTH - 1); + } + dst += stride; + } + } +} + +//8.4.5.2.14 Specification of INTRA_LT_CCLM, INTRA_L_CCLM and INTRA_T_CCLM intra prediction mode +static void FUNC(intra_cclm_pred)(const VVCLocalContext *lc, const int x0, const int y0, + const int width, const int height) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int avail_t = ff_vvc_get_top_available(lc, x0, y0, 1, 0); + const int avail_l = ff_vvc_get_left_available(lc, x0, y0, 1, 0); + const int hs = sps->hshift[1]; + const int vs = sps->vshift[1]; + const int x = x0 >> hs; + const int y = y0 >> vs; + const int w = width >> hs; + const int h = height >> vs; + int a[2], b[2], k[2]; + + pixel dsy[MAX_TB_SIZE * MAX_TB_SIZE]; + if (!avail_t && !avail_l) { + FUNC(cclm_pred_default)(fc, x, y, w, h, avail_t, avail_l); + return; + } + FUNC(cclm_get_luma_rec_pixels)(fc, x0, y0, w, h, avail_t, avail_l, dsy); + FUNC(cclm_get_params) (lc, x0, y0, w, h, avail_t, avail_l, a, b, k); + FUNC(cclm_linear_pred)(fc, x0, y0, w, h, dsy, a, b, k); +} + +static int FUNC(lmcs_sum_samples)(const pixel *start, ptrdiff_t stride, const int avail, const int target_size) +{ + const int size = FFMIN(avail, target_size); + int sum = 0; + for (int i = 0; i < size; i++) { + sum += *start; + start += stride; + } + sum += *(start - stride) * (target_size - size); + return sum; +} + +// 8.7.5.3 Picture reconstruction with luma dependent chroma residual scaling process for chroma samples +static int FUNC(lmcs_derive_chroma_scale)(VVCLocalContext *lc, const int x0, const int y0) +{ + VVCFrameContext *fc = lc->fc; + const VVCLMCS *lmcs = &fc->ps.lmcs; + const int size_y = FFMIN(fc->ps.sps->ctb_size_y, 64); + + const int x = x0 & ~(size_y - 1); + const int y = y0 & ~(size_y - 1); + if (lc->lmcs.x_vpdu != x || lc->lmcs.y_vpdu != y) { + int cnt = 0, luma = 0, i; + const pixel *src = (const pixel *)(fc->frame->data[LUMA] + y * fc->frame->linesize[LUMA] + (x << fc->ps.sps->pixel_shift)); + const ptrdiff_t stride = fc->frame->linesize[LUMA] / sizeof(pixel); + const int avail_t = ff_vvc_get_top_available (lc, x, y, 1, 0); + const int avail_l = ff_vvc_get_left_available(lc, x, y, 1, 0); + if (avail_l) { + luma += FUNC(lmcs_sum_samples)(src - 1, stride, fc->ps.pps->height - y, size_y); + cnt = size_y; + } + if (avail_t) { + luma += FUNC(lmcs_sum_samples)(src - stride, 1, fc->ps.pps->width - x, size_y); + cnt += size_y; + } + if (cnt) + luma = (luma + (cnt >> 1)) >> av_log2(cnt); + else + luma = 1 << (BIT_DEPTH - 1); + + for (i = lmcs->min_bin_idx; i <= lmcs->max_bin_idx; i++) { + if (luma < lmcs->pivot[i + 1]) + break; + } + i = FFMIN(i, LMCS_MAX_BIN_SIZE - 1); + + lc->lmcs.chroma_scale = lmcs->chroma_scale_coeff[i]; + lc->lmcs.x_vpdu = x; + lc->lmcs.y_vpdu = y; + } + return lc->lmcs.chroma_scale; +} + +// 8.7.5.3 Picture reconstruction with luma dependent chroma residual scaling process for chroma samples +static void FUNC(lmcs_scale_chroma)(VVCLocalContext *lc, int *dst, const int *coeff, + const int width, const int height, const int x0_cu, const int y0_cu) +{ + const int chroma_scale = FUNC(lmcs_derive_chroma_scale)(lc, x0_cu, y0_cu); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const int c = av_clip_intp2(*coeff, BIT_DEPTH); + + if (c > 0) + *dst = (c * chroma_scale + (1 << 10)) >> 11; + else + *dst = -((-c * chroma_scale + (1 << 10)) >> 11); + coeff++; + dst++; + } + } +} + +static av_always_inline void FUNC(ref_filter)(const pixel *left, const pixel *top, + pixel *filtered_left, pixel *filtered_top, const int left_size, const int top_size, + const int unfilter_last_one) +{ + filtered_left[-1] = filtered_top[-1] = (left[0] + 2 * left[-1] + top[0] + 2 ) >> 2; + for (int i = 0; i < left_size - unfilter_last_one; i++) { + filtered_left[i] = (left[i- 1] + 2 * left[i] + left[i + 1] + 2) >> 2; + } + for (int i = 0; i < top_size - unfilter_last_one; i++) { + filtered_top[i] = (top[i-1] + 2 * top[i] + top[i + 1] + 2) >> 2; + } + if (unfilter_last_one) { + filtered_top[top_size - 1] = top[top_size - 1]; + filtered_left[left_size - 1] = left[left_size - 1]; + } +} + +static av_always_inline void FUNC(prepare_intra_edge_params)(const VVCLocalContext *lc, + IntraEdgeParams* edge, const pixel *src, const ptrdiff_t stride, + const int x, int y, int w, int h, int c_idx, const int is_intra_mip, + const int mode, const int ref_idx, const int need_pdpc) +{ +#define EXTEND(ptr, val, len) \ +do { \ + for (i = 0; i < (len); i++) \ + *(ptr + i) = val; \ +} while (0) + const CodingUnit *cu = lc->cu; + const int ref_filter_flag = is_intra_mip ? 0 : ff_vvc_ref_filter_flag_derive(mode); + const int filter_flag = !ref_idx && w * h > 32 && !c_idx && + cu->isp_split_type == ISP_NO_SPLIT && ref_filter_flag; + int cand_up_left = lc->na.cand_up_left; + pixel *left = (pixel*)edge->left_array + MAX_TB_SIZE + 3; + pixel *top = (pixel*)edge->top_array + MAX_TB_SIZE + 3; + pixel *filtered_left = (pixel*)edge->filtered_left_array + MAX_TB_SIZE + 3; + pixel *filtered_top = (pixel*)edge->filtered_top_array + MAX_TB_SIZE + 3; + const int ref_line = ref_idx == 3 ? -4 : (-1 - ref_idx); + int left_size, top_size, unfilter_left_size, unfilter_top_size; + int left_available, top_available; + int refw, refh; + int intra_pred_angle, inv_angle; + int i; + + if (is_intra_mip || mode == INTRA_PLANAR) { + left_size = h + 1; + top_size = w + 1; + unfilter_left_size = left_size + filter_flag; + unfilter_top_size = top_size + filter_flag; + } else if (mode == INTRA_DC) { + unfilter_left_size = left_size = h; + unfilter_top_size = top_size = w; + } else if (mode == INTRA_VERT) { + //we may need 1 pixel to predict the top left. + unfilter_left_size = left_size = need_pdpc ? h : 1; + unfilter_top_size = top_size = w; + } else if (mode == INTRA_HORZ) { + unfilter_left_size = left_size = h; + //even need_pdpc == 0, we may need 1 pixel to predict the top left. + unfilter_top_size = top_size = need_pdpc ? w : 1; + } else { + if (cu->isp_split_type == ISP_NO_SPLIT || c_idx) { + refw = w * 2; + refh = h * 2; + } else { + refw = cu->cb_width + w; + refh = cu->cb_height + h; + } + intra_pred_angle = ff_vvc_intra_pred_angle_derive(mode); + inv_angle = ff_vvc_intra_inv_angle_derive(intra_pred_angle); + unfilter_top_size = top_size = refw; + unfilter_left_size = left_size = refh; + } + + left_available = ff_vvc_get_left_available(lc, x, y, unfilter_left_size, c_idx); + for (i = 0; i < left_available; i++) + left[i] = POS(ref_line, i); + + top_available = ff_vvc_get_top_available(lc, x, y, unfilter_top_size, c_idx); + memcpy(top, src + ref_line * stride, top_available * sizeof(pixel)); + + for (int i = -1; i >= ref_line; i--) { + if (cand_up_left) { + left[i] = POS(ref_line, i); + top[i] = POS(i, ref_line); + } else if (left_available) { + left[i] = top[i] = left[0]; + } else if (top_available) { + left[i] = top[i] = top[0]; + } else { + left[i] = top[i] = 1 << (BIT_DEPTH - 1); + } + } + + EXTEND(top + top_available, top[top_available-1], unfilter_top_size - top_available); + EXTEND(left + left_available, left[left_available-1], unfilter_left_size - left_available); + + if (ref_filter_flag) { + if (!ref_idx && w * h > 32 && !c_idx && cu->isp_split_type == ISP_NO_SPLIT ) { + const int unfilter_last_one = left_size == unfilter_left_size; + FUNC(ref_filter)(left, top, filtered_left, filtered_top, unfilter_left_size, unfilter_top_size, unfilter_last_one); + left = filtered_left; + top = filtered_top; + } + } + if (!is_intra_mip && mode != INTRA_PLANAR && mode != INTRA_DC) { + if (ref_filter_flag || ref_idx || cu->isp_split_type != ISP_NO_SPLIT) { + edge->filter_flag = 0; + } else { + const int min_dist_ver_hor = FFMIN(abs(mode - 50), abs(mode - 18)); + const int intra_hor_ver_dist_thres[] = {24, 14, 2, 0, 0}; + const int ntbs = (av_log2(w) + av_log2(h)) >> 1; + edge->filter_flag = min_dist_ver_hor > intra_hor_ver_dist_thres[ntbs - 2]; + } + + if (mode != INTRA_VERT && mode != INTRA_HORZ) { + if (mode >= INTRA_DIAG) { + if (intra_pred_angle < 0) { + pixel *p = top - (ref_idx + 1); + for (int x = -h; x < 0; x++) { + const int idx = -1 - ref_idx + FFMIN((x*inv_angle + 256) >> 9, h); + p[x] = left[idx]; + } + } else { + for (int i = refw; i <= refw + FFMAX(1, w/h) * ref_idx + 1; i++) + top[i] = top[refw - 1]; + } + } else { + if (intra_pred_angle < 0) { + pixel *p = left - (ref_idx + 1); + for (int x = -w; x < 0; x++) { + const int idx = -1 - ref_idx + FFMIN((x*inv_angle + 256) >> 9, w); + p[x] = top[idx]; + } + } else { + for (int i = refh; i <= refh + FFMAX(1, h/w) * ref_idx + 1; i++) + left[i] = left[refh - 1]; + } + } + } + } + edge->left = (uint8_t*)left; + edge->top = (uint8_t*)top; +} + +//8.4.1 General decoding process for coding units coded in intra prediction mode +static void FUNC(intra_pred)(const VVCLocalContext *lc, int x0, int y0, + const int width, const int height, int c_idx) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const CodingUnit *cu = lc->cu; + const int log2_min_cb_size = sps->min_cb_log2_size_y; + const int min_cb_width = pps->min_cb_width; + const int x_cb = x0 >> log2_min_cb_size; + const int y_cb = y0 >> log2_min_cb_size; + + const int hshift = fc->ps.sps->hshift[c_idx]; + const int vshift = fc->ps.sps->vshift[c_idx]; + const int x = x0 >> hshift; + const int y = y0 >> vshift; + const int w = width >> hshift; + const int h = height >> vshift; + const ptrdiff_t stride = fc->frame->linesize[c_idx] / sizeof(pixel); + + const int pred_mode = c_idx ? cu->intra_pred_mode_c : cu->intra_pred_mode_y; + const int mode = ff_vvc_wide_angle_mode_mapping(cu, w, h, c_idx, pred_mode); + + const int intra_mip_flag = SAMPLE_CTB(fc->tab.imf, x_cb, y_cb); + const int is_intra_mip = intra_mip_flag && (!c_idx || cu->mip_chroma_direct_flag); + const int ref_idx = c_idx ? 0 : cu->intra_luma_ref_idx; + const int need_pdpc = ff_vvc_need_pdpc(w, h, cu->bdpcm_flag[c_idx], mode, ref_idx); + + + pixel *src = (pixel*)fc->frame->data[c_idx] + x + y * stride; + IntraEdgeParams edge; + + FUNC(prepare_intra_edge_params)(lc, &edge, src, stride, x, y, w, h, c_idx, is_intra_mip, mode, ref_idx, need_pdpc); + + if (is_intra_mip) { + int intra_mip_transposed_flag = SAMPLE_CTB(fc->tab.imtf, x_cb, y_cb); + int intra_mip_mode = SAMPLE_CTB(fc->tab.imm, x_cb, y_cb); + + fc->vvcdsp.intra.pred_mip((uint8_t *)src, edge.top, edge.left, + w, h, stride, intra_mip_mode, intra_mip_transposed_flag); + } else if (mode == INTRA_PLANAR) { + fc->vvcdsp.intra.pred_planar((uint8_t *)src, edge.top, edge.left, w, h, stride); + } else if (mode == INTRA_DC) { + fc->vvcdsp.intra.pred_dc((uint8_t *)src, edge.top, edge.left, w, h, stride); + } else if (mode == INTRA_VERT) { + fc->vvcdsp.intra.pred_v((uint8_t *)src, edge.top, w, h, stride); + } else if (mode == INTRA_HORZ) { + fc->vvcdsp.intra.pred_h((uint8_t *)src, edge.left, w, h, stride); + } else { + if (mode >= INTRA_DIAG) { + fc->vvcdsp.intra.pred_angular_v((uint8_t *)src, edge.top, edge.left, + w, h, stride, c_idx, mode, ref_idx, + edge.filter_flag, need_pdpc); + } else { + fc->vvcdsp.intra.pred_angular_h((uint8_t *)src, edge.top, edge.left, + w, h, stride, c_idx, mode, ref_idx, + edge.filter_flag, need_pdpc); + } + } + if (need_pdpc) { + //8.4.5.2.15 Position-dependent intra prediction sample filtering process + if (!is_intra_mip && (mode == INTRA_PLANAR || mode == INTRA_DC || + mode == INTRA_VERT || mode == INTRA_HORZ)) { + const int scale = (av_log2(w) + av_log2(h) - 2) >> 2; + const pixel *left = (pixel*)edge.left; + const pixel *top = (pixel*)edge.top; + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + int l, t, wl, wt, pred; + pixel val; + if (mode == INTRA_PLANAR || mode == INTRA_DC) { + l = left[y]; + t = top[x]; + wl = 32 >> FFMIN((x << 1) >> scale, 31); + wt = 32 >> FFMIN((y << 1) >> scale, 31); + } else { + l = left[y] - left[-1] + POS(x,y); + t = top[x] - top[-1] + POS(x,y); + wl = (mode == INTRA_VERT) ? (32 >> FFMIN((x << 1) >> scale, 31)) : 0; + wt = (mode == INTRA_HORZ) ? (32 >> FFMIN((y << 1) >> scale, 31)) : 0; + } + val = POS(x, y); + pred = val + ((wl * (l - val) + wt * (t - val) + 32) >> 6); + POS(x, y) = CLIP(pred); + } + } + } + } +} + +//8.4.5.2.11 Specification of INTRA_PLANAR intra prediction mode +static av_always_inline void FUNC(pred_planar)(uint8_t *_src, const uint8_t *_top, + const uint8_t *_left, const int w, const int h, const ptrdiff_t stride) +{ + int x, y; + pixel *src = (pixel *)_src; + const pixel *top = (const pixel *)_top; + const pixel *left = (const pixel *)_left; + const int logw = av_log2(w); + const int logh = av_log2(h); + const int size = w * h; + const int shift = (logw + logh + 1); + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + const int pred_v = ((h - 1 - y) * top[x] + (y + 1) * left[h]) << logw; + const int pred_h = ((w - 1 - x) * left[y] + (x + 1) * top[w]) << logh; + const int pred = (pred_v + pred_h + size) >> shift; + POS(x, y) = pred; + } + } +} + +//8.4.5.2.3 MIP boundary sample downsampling process +static av_always_inline void FUNC(mip_downsampling)(int *reduced, const int boundary_size, + const pixel *ref, const int n_tb_s) +{ + const int b_dwn = n_tb_s / boundary_size; + const int log2 = av_log2(b_dwn); + + if (boundary_size == n_tb_s) { + for (int i = 0; i < n_tb_s; i++) + reduced[i] = ref[i]; + return; + } + for (int i = 0; i < boundary_size; i++) { + int r; + r = *ref++; + for (int j = 1; j < b_dwn; j++) + r += *ref++; + reduced[i] = (r + (1 << (log2 - 1))) >> log2; + } +} + +static av_always_inline void FUNC(mip_reduced_pred)(pixel *src, const ptrdiff_t stride, + const int up_hor, const int up_ver, const int pred_size, const int *reduced, const int reduced_size, + const int ow, const int temp0, const uint8_t *matrix, int is_transposed) +{ + src = &POS(up_hor - 1, up_ver - 1); + for (int y = 0; y < pred_size; y++) { + for (int x = 0; x < pred_size; x++) { + int pred = 0; + for (int i = 0; i < reduced_size; i++) + pred += reduced[i] * matrix[i]; + matrix += reduced_size; + pred = ((pred + ow) >> 6) + temp0; + pred = av_clip(pred, 0, (1< 1 || up_ver > 1) { + if (up_hor > 1) + FUNC(mip_upsampling_1d)(&POS(0, up_ver - 1), 1, up_ver * stride, pred_size, up_hor, left + up_ver - 1, up_ver, pred_size); + if (up_ver > 1) + FUNC(mip_upsampling_1d)(src, stride, 1, w, up_ver, top, 1, pred_size); + } +} + +static av_always_inline pixel FUNC(pred_dc_val)(const pixel *top, const pixel *left, + const int w, const int h) +{ + pixel dc_val; + int sum = 0; + unsigned int offset = (w == h) ? (w << 1) : FFMAX(w, h); + const int shift = av_log2(offset); + offset >>= 1; + if (w >= h) { + for (int i = 0; i < w; i++) + sum += top[i]; + } + if (w <= h) { + for (int i = 0; i < h; i++) + sum += left[i]; + } + dc_val = (sum + offset) >> shift; + return dc_val; +} + +//8.4.5.2.12 Specification of INTRA_DC intra prediction mode +static av_always_inline void FUNC(pred_dc)(uint8_t *_src, const uint8_t *_top, + const uint8_t *_left, const int w, const int h, const ptrdiff_t stride) +{ + int x, y; + pixel *src = (pixel *)_src; + const pixel *top = (const pixel *)_top; + const pixel *left = (const pixel *)_left; + const pixel dc = FUNC(pred_dc_val)(top, left, w, h); + const pixel4 a = PIXEL_SPLAT_X4(dc); + for (y = 0; y < h; y++) { + pixel *s = src; + for (x = 0; x < w; x += 4) { + AV_WN4P(s, a); + s += 4; + } + src += stride; + } +} + +static av_always_inline void FUNC(pred_v)(uint8_t *_src, const uint8_t *_top, + const int w, const int h, const ptrdiff_t stride) +{ + pixel *src = (pixel *)_src; + const pixel *top = (const pixel *)_top; + for (int y = 0; y < h; y++) { + memcpy(src, top, sizeof(pixel) * w); + src += stride; + } +} + +static void FUNC(pred_h)(uint8_t *_src, const uint8_t *_left, const int w, const int h, + const ptrdiff_t stride) +{ + pixel *src = (pixel *)_src; + const pixel *left = (const pixel *)_left; + for (int y = 0; y < h; y++) { + const pixel4 a = PIXEL_SPLAT_X4(left[y]); + for (int x = 0; x < w; x += 4) { + AV_WN4P(&POS(x, y), a); + } + } +} + +//8.4.5.2.13 Specification of INTRA_ANGULAR2..INTRA_ANGULAR66 intra prediction modes +static void FUNC(pred_angular_v)(uint8_t *_src, const uint8_t *_top, const uint8_t *_left, + const int w, const int h, const ptrdiff_t stride, const int c_idx, const int mode, + const int ref_idx, const int filter_flag, const int need_pdpc) +{ + pixel *src = (pixel *)_src; + const pixel *left = (const pixel *)_left; + const pixel *top = (const pixel *)_top - (1 + ref_idx); + const int intra_pred_angle = ff_vvc_intra_pred_angle_derive(mode); + int pos = (1 + ref_idx) * intra_pred_angle; + const int dp = intra_pred_angle; + const int is_luma = !c_idx; + int nscale, inv_angle; + + if (need_pdpc) { + inv_angle = ff_vvc_intra_inv_angle_derive(intra_pred_angle); + nscale = ff_vvc_nscale_derive(w, h, mode); + } + + for (int y = 0; y < h; y++) { + const int idx = (pos >> 5) + ref_idx; + const int fact = pos & 31; + if (!fact && (!is_luma || !filter_flag)) { + for (int x = 0; x < w; x++) { + const pixel *p = top + x + idx + 1; + const pixel pred = p[0]; + POS(x, y) = pred; + } + } else { + if (!c_idx) { + const int8_t* f = filter_flag ? ff_vvc_filter_g[fact] : ff_vvc_filter_c[fact]; + for (int x = 0; x < w; x++) { + const pixel *p = top + x + idx; + const int pred = (p[0] * f[0] + p[1] * f[1] + + p[2] * f[2] + p[3] * f[3] + 32) >> 6; + POS(x, y) = av_clip_pixel(pred); + } + } else { + for (int x = 0; x < w; x++) { + const pixel *p = top + x + idx + 1; + const pixel pred = ((32 - fact) * p[0] + fact * p[1] + 16) >> 5; + POS(x, y) = pred; + } + } + } + if (need_pdpc) { + int inv_angle_sum = 256 + inv_angle; + for (int x = 0; x < FFMIN(w, 3 << nscale); x++) { + const pixel l = left[y + (inv_angle_sum >> 9)]; + const pixel val = POS(x, y); + const int wl = 32 >> ((x << 1) >> nscale); + const int pred = val + (((l - val) * wl + 32) >> 6); + POS(x, y) = CLIP(pred); + inv_angle_sum += inv_angle; + } + } + pos += dp; + } +} + +//8.4.5.2.13 Specification of INTRA_ANGULAR2..INTRA_ANGULAR66 intra prediction modes +static void FUNC(pred_angular_h)(uint8_t *_src, const uint8_t *_top, const uint8_t *_left, + const int w, const int h, const ptrdiff_t stride, const int c_idx, const int mode, + const int ref_idx, const int filter_flag, const int need_pdpc) +{ + pixel *src = (pixel *)_src; + const pixel *left = (const pixel *)_left - (1 + ref_idx); + const pixel *top = (const pixel *)_top; + const int is_luma = !c_idx; + const int intra_pred_angle = ff_vvc_intra_pred_angle_derive(mode); + const int dp = intra_pred_angle; + int nscale = 0, inv_angle, inv_angle_sum; + + if (need_pdpc) { + inv_angle = ff_vvc_intra_inv_angle_derive(intra_pred_angle); + inv_angle_sum = 256 + inv_angle; + nscale = ff_vvc_nscale_derive(w, h, mode); + } + + for (int y = 0; y < h; y++) { + int pos = (1 + ref_idx) * intra_pred_angle; + int wt; + if (need_pdpc) + wt = (32 >> ((y * 2) >> nscale)); + + for (int x = 0; x < w; x++) { + const int idx = (pos >> 5) + ref_idx; + const int fact = pos & 31; + const pixel *p = left + y + idx; + int pred; + if (!fact && (!is_luma || !filter_flag)) { + pred = p[1]; + } else { + if (!c_idx) { + const int8_t* f = filter_flag ? ff_vvc_filter_g[fact] : ff_vvc_filter_c[fact] ; + pred = (p[0] * f[0] + p[1] * f[1] + p[2] * f[2] + p[3] * f[3] + 32) >> 6; + pred = CLIP(pred); + } else { + pred = ((32 - fact) * p[1] + fact * p[2] + 16) >> 5; + } + } + if (need_pdpc) { + if (y < (3 << nscale)) { + const pixel t = top[x + (inv_angle_sum >> 9)]; + pred = CLIP(pred + (((t - pred) * wt + 32) >> 6)); + } + } + POS(x, y) = pred; + pos += dp; + } + if (need_pdpc) + inv_angle_sum += inv_angle; + } +} + +static void FUNC(ff_vvc_intra_dsp_init)(VVCIntraDSPContext *const intra) +{ + intra->lmcs_scale_chroma = FUNC(lmcs_scale_chroma); + intra->intra_cclm_pred = FUNC(intra_cclm_pred); + intra->intra_pred = FUNC(intra_pred); + intra->pred_planar = FUNC(pred_planar); + intra->pred_mip = FUNC(pred_mip); + intra->pred_dc = FUNC(pred_dc); + intra->pred_v = FUNC(pred_v); + intra->pred_h = FUNC(pred_h); + intra->pred_angular_v = FUNC(pred_angular_v); + intra->pred_angular_h = FUNC(pred_angular_h); +} diff --git a/libavcodec/vvc/vvc_itx_1d.c b/libavcodec/vvc/vvc_itx_1d.c new file mode 100644 index 00000000000..e96919f41df --- /dev/null +++ b/libavcodec/vvc/vvc_itx_1d.c @@ -0,0 +1,713 @@ +/* + * VVC 1D transform + * + * Copyright (C) 2023 Nuo Mi + * + * 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 + */ + +/* The copyright in this software is being made available under the BSD + * License, included below. This software may be subject to other third party + * and contributor rights, including patent rights, and no such rights are + * granted under this license. + * + * Copyright (c) 2010-2021, ITU/ISO/IEC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ITU/ISO/IEC nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* optimizaed with partial butterfly, see Hung C-Y, Landman P (1997) + Compact inverse discrete cosine transform circuit for MPEG video decoding. + */ + +#include "vvc_data.h" +#include "vvc_itx_1d.h" +#include "libavutil/avutil.h" + +/* +transmatrix[2][2] = { + { a, a }, + { a, -a }, +} + */ +void ff_vvc_inv_dct2_2(int *out, const ptrdiff_t out_stride, const int *in, ptrdiff_t in_stride) +{ + const int a = 64; + const int x0 = in[0 * in_stride], x1 = in[1 * in_stride]; + + out[0 * out_stride] = a * (x0 + x1); + out[1 * out_stride] = a * (x0 - x1); +} + +/* +transmatrix[4][4] = { + { a, a, a, a}, + { b, c, -c, -b}, + { a, -a, -a, a}, + { c, -b, b, -c}, +} + */ +void ff_vvc_inv_dct2_4(int *out, const ptrdiff_t out_stride, const int *in, ptrdiff_t in_stride) +{ + const int a = 64, b = 83, c = 36; + const int x0 = in[0 * in_stride], x1 = in[1 * in_stride]; + const int x2 = in[2 * in_stride], x3 = in[3 * in_stride]; + const int E[2] = { + a * (x0 + x2), + a * (x0 - x2), + }; + const int O[2] = { + b * x1 + c * x3, + c * x1 - b * x3, + }; + + out[0 * out_stride] = E[0] + O[0]; + out[1 * out_stride] = E[1] + O[1]; + out[2 * out_stride] = E[1] - O[1]; + out[3 * out_stride] = E[0] - O[0]; +} + +/* +transmatrix[8][8] = { + { a, a, a, a, a, a, a, a}, + { d, e, f, g, -g, -f, -e, -d}, + { b, c, -c, -b, -b, -c, c, b}, + { e, -g, -d, -f, f, d, g, -e}, + { a, -a, -a, a, a, -a, -a, a}, + { f, -d, g, e, -e, -g, d, -f}, + { c, -b, b, -c, -c, b, -b, c}, + { g, -f, e, -d, d, -e, f, -g}, +} + */ +void ff_vvc_inv_dct2_8(int *out, const ptrdiff_t out_stride, const int *in, ptrdiff_t in_stride) +{ + const int a = 64, b = 83, c = 36, d = 89, e = 75, f = 50, g = 18; + const int x0 = in[0 * in_stride], x1 = in[1 * in_stride]; + const int x2 = in[2 * in_stride], x3 = in[3 * in_stride]; + const int x4 = in[4 * in_stride], x5 = in[5 * in_stride]; + const int x6 = in[6 * in_stride], x7 = in[7 * in_stride]; + const int EE[2] = { + a * (x0 + x4), + a * (x0 - x4), + }; + const int EO[2] = { + b * x2 + c * x6, + c * x2 - b * x6, + }; + const int E[4] = { + EE[0] + EO[0], EE[1] + EO[1], + EE[1] - EO[1], EE[0] - EO[0], + }; + const int O[4] = { + d * x1 + e * x3 + f * x5 + g * x7, + e * x1 - g * x3 - d * x5 - f * x7, + f * x1 - d * x3 + g * x5 + e * x7, + g * x1 - f * x3 + e * x5 - d * x7, + }; + + out[0 * out_stride] = E[0] + O[0]; + out[1 * out_stride] = E[1] + O[1]; + out[2 * out_stride] = E[2] + O[2]; + out[3 * out_stride] = E[3] + O[3]; + out[4 * out_stride] = E[3] - O[3]; + out[5 * out_stride] = E[2] - O[2]; + out[6 * out_stride] = E[1] - O[1]; + out[7 * out_stride] = E[0] - O[0]; +} + +/* +transmatrix[16][16] = { + { a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a}, + { h, i, j, k, l, m, n, o, -o, -n, -m, -l, -k, -j, -i, -h}, + { d, e, f, g, -g, -f, -e, -d, -d, -e, -f, -g, g, f, e, d}, + { i, l, o, -m, -j, -h, -k, -n, n, k, h, j, m, -o, -l, -i}, + { b, c, -c, -b, -b, -c, c, b, b, c, -c, -b, -b, -c, c, b}, + { j, o, -k, -i, -n, l, h, m, -m, -h, -l, n, i, k, -o, -j}, + { e, -g, -d, -f, f, d, g, -e, -e, g, d, f, -f, -d, -g, e}, + { k, -m, -i, o, h, n, -j, -l, l, j, -n, -h, -o, i, m, -k}, + { a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a}, + { l, -j, -n, h, -o, -i, m, k, -k, -m, i, o, -h, n, j, -l}, + { f, -d, g, e, -e, -g, d, -f, -f, d, -g, -e, e, g, -d, f}, + { m, -h, l, n, -i, k, o, -j, j, -o, -k, i, -n, -l, h, -m}, + { c, -b, b, -c, -c, b, -b, c, c, -b, b, -c, -c, b, -b, c}, + { n, -k, h, -j, m, o, -l, i, -i, l, -o, -m, j, -h, k, -n}, + { g, -f, e, -d, d, -e, f, -g, -g, f, -e, d, -d, e, -f, g}, + { o, -n, m, -l, k, -j, i, -h, h, -i, j, -k, l, -m, n, -o}, +} + */ +void ff_vvc_inv_dct2_16(int *out, const ptrdiff_t out_stride, const int *in, ptrdiff_t in_stride) +{ + const int a = 64, b = 83, c = 36, d = 89, e = 75, f = 50, g = 18, h = 90; + const int i = 87, j = 80, k = 70, l = 57, m = 43, n = 25, o = 9; + const int x0 = in[0 * in_stride], x1 = in[1 * in_stride]; + const int x2 = in[2 * in_stride], x3 = in[3 * in_stride]; + const int x4 = in[4 * in_stride], x5 = in[5 * in_stride]; + const int x6 = in[6 * in_stride], x7 = in[7 * in_stride]; + const int x8 = in[8 * in_stride], x9 = in[9 * in_stride]; + const int x10 = in[10 * in_stride], x11 = in[11 * in_stride]; + const int x12 = in[12 * in_stride], x13 = in[13 * in_stride]; + const int x14 = in[14 * in_stride], x15 = in[15 * in_stride]; + const int EEE[2] = { + a * (x0 + x8), + a * (x0 - x8), + }; + const int EEO[2] = { + b * x4 + c * x12, + c * x4 - b * x12, + }; + const int EE[4] = { + EEE[0] + EEO[0], EEE[1] + EEO[1], + EEE[1] - EEO[1], EEE[0] - EEO[0], + }; + const int EO[4] = { + d * x2 + e * x6 + f * x10 + g * x14, + e * x2 - g * x6 - d * x10 - f * x14, + f * x2 - d * x6 + g * x10 + e * x14, + g * x2 - f * x6 + e * x10 - d * x14, + }; + const int E[8] = { + EE[0] + EO[0], EE[1] + EO[1], EE[2] + EO[2], EE[3] + EO[3], + EE[3] - EO[3], EE[2] - EO[2], EE[1] - EO[1], EE[0] - EO[0], + }; + const int O[8] = { + h * x1 + i * x3 + j * x5 + k * x7 + l * x9 + m * x11 + n * x13 + o * x15, + i * x1 + l * x3 + o * x5 - m * x7 - j * x9 - h * x11 - k * x13 - n * x15, + j * x1 + o * x3 - k * x5 - i * x7 - n * x9 + l * x11 + h * x13 + m * x15, + k * x1 - m * x3 - i * x5 + o * x7 + h * x9 + n * x11 - j * x13 - l * x15, + l * x1 - j * x3 - n * x5 + h * x7 - o * x9 - i * x11 + m * x13 + k * x15, + m * x1 - h * x3 + l * x5 + n * x7 - i * x9 + k * x11 + o * x13 - j * x15, + n * x1 - k * x3 + h * x5 - j * x7 + m * x9 + o * x11 - l * x13 + i * x15, + o * x1 - n * x3 + m * x5 - l * x7 + k * x9 - j * x11 + i * x13 - h * x15, + }; + + out[0 * out_stride] = E[0] + O[0]; + out[1 * out_stride] = E[1] + O[1]; + out[2 * out_stride] = E[2] + O[2]; + out[3 * out_stride] = E[3] + O[3]; + out[4 * out_stride] = E[4] + O[4]; + out[5 * out_stride] = E[5] + O[5]; + out[6 * out_stride] = E[6] + O[6]; + out[7 * out_stride] = E[7] + O[7]; + out[8 * out_stride] = E[7] - O[7]; + out[9 * out_stride] = E[6] - O[6]; + out[10 * out_stride] = E[5] - O[5]; + out[11 * out_stride] = E[4] - O[4]; + out[12 * out_stride] = E[3] - O[3]; + out[13 * out_stride] = E[2] - O[2]; + out[14 * out_stride] = E[1] - O[1]; + out[15 * out_stride] = E[0] - O[0]; +} + +/* +transMatrix[32][32] = { + { a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a}, + { p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, -E, -D, -C, -B, -A, -z, -y, -x, -w, -v, -u, -t, -s, -r, -q, -p}, + { h, i, j, k, l, m, n, o, -o, -n, -m, -l, -k, -j, -i, -h, -h, -i, -j, -k, -l, -m, -n, -o, o, n, m, l, k, j, i, h}, + { q, t, w, z, C, -E, -B, -y, -v, -s, -p, -r, -u, -x, -A, -D, D, A, x, u, r, p, s, v, y, B, E, -C, -z, -w, -t, -q}, + { d, e, f, g, -g, -f, -e, -d, -d, -e, -f, -g, g, f, e, d, d, e, f, g, -g, -f, -e, -d, -d, -e, -f, -g, g, f, e, d}, + { r, w, B, -D, -y, -t, -p, -u, -z, -E, A, v, q, s, x, C, -C, -x, -s, -q, -v, -A, E, z, u, p, t, y, D, -B, -w, -r}, + { i, l, o, -m, -j, -h, -k, -n, n, k, h, j, m, -o, -l, -i, -i, -l, -o, m, j, h, k, n, -n, -k, -h, -j, -m, o, l, i}, + { s, z, -D, -w, -p, -v, -C, A, t, r, y, -E, -x, -q, -u, -B, B, u, q, x, E, -y, -r, -t, -A, C, v, p, w, D, -z, -s}, + { b, c, -c, -b, -b, -c, c, b, b, c, -c, -b, -b, -c, c, b, b, c, -c, -b, -b, -c, c, b, b, c, -c, -b, -b, -c, c, b}, + { t, C, -y, -p, -x, D, u, s, B, -z, -q, -w, E, v, r, A, -A, -r, -v, -E, w, q, z, -B, -s, -u, -D, x, p, y, -C, -t}, + { j, o, -k, -i, -n, l, h, m, -m, -h, -l, n, i, k, -o, -j, -j, -o, k, i, n, -l, -h, -m, m, h, l, -n, -i, -k, o, j}, + { u, -E, -t, -v, D, s, w, -C, -r, -x, B, q, y, -A, -p, -z, z, p, A, -y, -q, -B, x, r, C, -w, -s, -D, v, t, E, -u}, + { e, -g, -d, -f, f, d, g, -e, -e, g, d, f, -f, -d, -g, e, e, -g, -d, -f, f, d, g, -e, -e, g, d, f, -f, -d, -g, e}, + { v, -B, -p, -C, u, w, -A, -q, -D, t, x, -z, -r, -E, s, y, -y, -s, E, r, z, -x, -t, D, q, A, -w, -u, C, p, B, -v}, + { k, -m, -i, o, h, n, -j, -l, l, j, -n, -h, -o, i, m, -k, -k, m, i, -o, -h, -n, j, l, -l, -j, n, h, o, -i, -m, k}, + { w, -y, -u, A, s, -C, -q, E, p, D, -r, -B, t, z, -v, -x, x, v, -z, -t, B, r, -D, -p, -E, q, C, -s, -A, u, y, -w}, + { a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a}, + { x, -v, -z, t, B, -r, -D, p, -E, -q, C, s, -A, -u, y, w, -w, -y, u, A, -s, -C, q, E, -p, D, r, -B, -t, z, v, -x}, + { l, -j, -n, h, -o, -i, m, k, -k, -m, i, o, -h, n, j, -l, -l, j, n, -h, o, i, -m, -k, k, m, -i, -o, h, -n, -j, l}, + { y, -s, -E, r, -z, -x, t, D, -q, A, w, -u, -C, p, -B, -v, v, B, -p, C, u, -w, -A, q, -D, -t, x, z, -r, E, s, -y}, + { f, -d, g, e, -e, -g, d, -f, -f, d, -g, -e, e, g, -d, f, f, -d, g, e, -e, -g, d, -f, -f, d, -g, -e, e, g, -d, f}, + { z, -p, A, y, -q, B, x, -r, C, w, -s, D, v, -t, E, u, -u, -E, t, -v, -D, s, -w, -C, r, -x, -B, q, -y, -A, p, -z}, + { m, -h, l, n, -i, k, o, -j, j, -o, -k, i, -n, -l, h, -m, -m, h, -l, -n, i, -k, -o, j, -j, o, k, -i, n, l, -h, m}, + { A, -r, v, -E, -w, q, -z, -B, s, -u, D, x, -p, y, C, -t, t, -C, -y, p, -x, -D, u, -s, B, z, -q, w, E, -v, r, -A}, + { c, -b, b, -c, -c, b, -b, c, c, -b, b, -c, -c, b, -b, c, c, -b, b, -c, -c, b, -b, c, c, -b, b, -c, -c, b, -b, c}, + { B, -u, q, -x, E, y, -r, t, -A, -C, v, -p, w, -D, -z, s, -s, z, D, -w, p, -v, C, A, -t, r, -y, -E, x, -q, u, -B}, + { n, -k, h, -j, m, o, -l, i, -i, l, -o, -m, j, -h, k, -n, -n, k, -h, j, -m, -o, l, -i, i, -l, o, m, -j, h, -k, n}, + { C, -x, s, -q, v, -A, -E, z, -u, p, -t, y, -D, -B, w, -r, r, -w, B, D, -y, t, -p, u, -z, E, A, -v, q, -s, x, -C}, + { g, -f, e, -d, d, -e, f, -g, -g, f, -e, d, -d, e, -f, g, g, -f, e, -d, d, -e, f, -g, -g, f, -e, d, -d, e, -f, g}, + { D, -A, x, -u, r, -p, s, -v, y, -B, E, C, -z, w, -t, q, -q, t, -w, z, -C, -E, B, -y, v, -s, p, -r, u, -x, A, -D}, + { o, -n, m, -l, k, -j, i, -h, h, -i, j, -k, l, -m, n, -o, -o, n, -m, l, -k, j, -i, h, -h, i, -j, k, -l, m, -n, o}, + { E, -D, C, -B, A, -z, y, -x, w, -v, u, -t, s, -r, q, -p, p, -q, r, -s, t, -u, v, -w, x, -y, z, -A, B, -C, D, -E}, +} + */ +void ff_vvc_inv_dct2_32(int *out, const ptrdiff_t out_stride, const int *in, ptrdiff_t in_stride) +{ + const int a = 64, b = 83, c = 36, d = 89, e = 75, f = 50, g = 18, h = 90; + const int i = 87, j = 80, k = 70, l = 57, m = 43, n = 25, o = 9, p = 90; + const int q = 90, r = 88, s = 85, t = 82, u = 78, v = 73, w = 67, x = 61; + const int y = 54, z = 46, A = 38, B = 31, C = 22, D = 13, E_= 4; + const int x0 = in[0 * in_stride], x1 = in[1 * in_stride]; + const int x2 = in[2 * in_stride], x3 = in[3 * in_stride]; + const int x4 = in[4 * in_stride], x5 = in[5 * in_stride]; + const int x6 = in[6 * in_stride], x7 = in[7 * in_stride]; + const int x8 = in[8 * in_stride], x9 = in[9 * in_stride]; + const int x10 = in[10 * in_stride], x11 = in[11 * in_stride]; + const int x12 = in[12 * in_stride], x13 = in[13 * in_stride]; + const int x14 = in[14 * in_stride], x15 = in[15 * in_stride]; + const int x16 = in[16 * in_stride], x17 = in[17 * in_stride]; + const int x18 = in[18 * in_stride], x19 = in[19 * in_stride]; + const int x20 = in[20 * in_stride], x21 = in[21 * in_stride]; + const int x22 = in[22 * in_stride], x23 = in[23 * in_stride]; + const int x24 = in[24 * in_stride], x25 = in[25 * in_stride]; + const int x26 = in[26 * in_stride], x27 = in[27 * in_stride]; + const int x28 = in[28 * in_stride], x29 = in[29 * in_stride]; + const int x30 = in[30 * in_stride], x31 = in[31 * in_stride]; + const int EEEE[2] = { + a * (x0 + x16), + a * (x0 - x16), + }; + const int EEEO[2] = { + b * x8 + c * x24, + c * x8 - b * x24, + }; + const int EEE[4] = { + EEEE[0] + EEEO[0], EEEE[1] + EEEO[1], + EEEE[1] - EEEO[1], EEEE[0] - EEEO[0], + }; + const int EEO[4] = { + d * x4 + e * x12 + f * x20 + g * x28, + e * x4 - g * x12 - d * x20 - f * x28, + f * x4 - d * x12 + g * x20 + e * x28, + g * x4 - f * x12 + e * x20 - d * x28, + }; + const int EE[8] = { + EEE[0] + EEO[0], EEE[1] + EEO[1], EEE[2] + EEO[2], EEE[3] + EEO[3], + EEE[3] - EEO[3], EEE[2] - EEO[2], EEE[1] - EEO[1], EEE[0] - EEO[0], + }; + const int EO[8] = { + h * x2 + i * x6 + j * x10 + k * x14 + l * x18 + m * x22 + n * x26 + o * x30, + i * x2 + l * x6 + o * x10 - m * x14 - j * x18 - h * x22 - k * x26 - n * x30, + j * x2 + o * x6 - k * x10 - i * x14 - n * x18 + l * x22 + h * x26 + m * x30, + k * x2 - m * x6 - i * x10 + o * x14 + h * x18 + n * x22 - j * x26 - l * x30, + l * x2 - j * x6 - n * x10 + h * x14 - o * x18 - i * x22 + m * x26 + k * x30, + m * x2 - h * x6 + l * x10 + n * x14 - i * x18 + k * x22 + o * x26 - j * x30, + n * x2 - k * x6 + h * x10 - j * x14 + m * x18 + o * x22 - l * x26 + i * x30, + o * x2 - n * x6 + m * x10 - l * x14 + k * x18 - j * x22 + i * x26 - h * x30, + }; + const int E[16] = { + EE[0] + EO[0], EE[1] + EO[1], EE[2] + EO[2], EE[3] + EO[3], EE[4] + EO[4], EE[5] + EO[5], EE[6] + EO[6], EE[7] + EO[7], + EE[7] - EO[7], EE[6] - EO[6], EE[5] - EO[5], EE[4] - EO[4], EE[3] - EO[3], EE[2] - EO[2], EE[1] - EO[1], EE[0] - EO[0], + }; + const int O[16] = { + p * x1 + q * x3 + r * x5 + s * x7 + t * x9 + u * x11 + v * x13 + w * x15 + x * x17 + y * x19 + z * x21 + A * x23 + B * x25 + C * x27 + D * x29 + E_* x31, + q * x1 + t * x3 + w * x5 + z * x7 + C * x9 - E_* x11 - B * x13 - y * x15 - v * x17 - s * x19 - p * x21 - r * x23 - u * x25 - x * x27 - A * x29 - D * x31, + r * x1 + w * x3 + B * x5 - D * x7 - y * x9 - t * x11 - p * x13 - u * x15 - z * x17 - E_* x19 + A * x21 + v * x23 + q * x25 + s * x27 + x * x29 + C * x31, + s * x1 + z * x3 - D * x5 - w * x7 - p * x9 - v * x11 - C * x13 + A * x15 + t * x17 + r * x19 + y * x21 - E_* x23 - x * x25 - q * x27 - u * x29 - B * x31, + t * x1 + C * x3 - y * x5 - p * x7 - x * x9 + D * x11 + u * x13 + s * x15 + B * x17 - z * x19 - q * x21 - w * x23 + E_* x25 + v * x27 + r * x29 + A * x31, + u * x1 - E_* x3 - t * x5 - v * x7 + D * x9 + s * x11 + w * x13 - C * x15 - r * x17 - x * x19 + B * x21 + q * x23 + y * x25 - A * x27 - p * x29 - z * x31, + v * x1 - B * x3 - p * x5 - C * x7 + u * x9 + w * x11 - A * x13 - q * x15 - D * x17 + t * x19 + x * x21 - z * x23 - r * x25 - E_* x27 + s * x29 + y * x31, + w * x1 - y * x3 - u * x5 + A * x7 + s * x9 - C * x11 - q * x13 + E_* x15 + p * x17 + D * x19 - r * x21 - B * x23 + t * x25 + z * x27 - v * x29 - x * x31, + x * x1 - v * x3 - z * x5 + t * x7 + B * x9 - r * x11 - D * x13 + p * x15 - E_* x17 - q * x19 + C * x21 + s * x23 - A * x25 - u * x27 + y * x29 + w * x31, + y * x1 - s * x3 - E_* x5 + r * x7 - z * x9 - x * x11 + t * x13 + D * x15 - q * x17 + A * x19 + w * x21 - u * x23 - C * x25 + p * x27 - B * x29 - v * x31, + z * x1 - p * x3 + A * x5 + y * x7 - q * x9 + B * x11 + x * x13 - r * x15 + C * x17 + w * x19 - s * x21 + D * x23 + v * x25 - t * x27 + E_* x29 + u * x31, + A * x1 - r * x3 + v * x5 - E_* x7 - w * x9 + q * x11 - z * x13 - B * x15 + s * x17 - u * x19 + D * x21 + x * x23 - p * x25 + y * x27 + C * x29 - t * x31, + B * x1 - u * x3 + q * x5 - x * x7 + E_* x9 + y * x11 - r * x13 + t * x15 - A * x17 - C * x19 + v * x21 - p * x23 + w * x25 - D * x27 - z * x29 + s * x31, + C * x1 - x * x3 + s * x5 - q * x7 + v * x9 - A * x11 - E_* x13 + z * x15 - u * x17 + p * x19 - t * x21 + y * x23 - D * x25 - B * x27 + w * x29 - r * x31, + D * x1 - A * x3 + x * x5 - u * x7 + r * x9 - p * x11 + s * x13 - v * x15 + y * x17 - B * x19 + E_* x21 + C * x23 - z * x25 + w * x27 - t * x29 + q * x31, + E_* x1 - D * x3 + C * x5 - B * x7 + A * x9 - z * x11 + y * x13 - x * x15 + w * x17 - v * x19 + u * x21 - t * x23 + s * x25 - r * x27 + q * x29 - p * x31, + }; + + out[0 * out_stride] = E[0] + O[0]; + out[1 * out_stride] = E[1] + O[1]; + out[2 * out_stride] = E[2] + O[2]; + out[3 * out_stride] = E[3] + O[3]; + out[4 * out_stride] = E[4] + O[4]; + out[5 * out_stride] = E[5] + O[5]; + out[6 * out_stride] = E[6] + O[6]; + out[7 * out_stride] = E[7] + O[7]; + out[8 * out_stride] = E[8] + O[8]; + out[9 * out_stride] = E[9] + O[9]; + out[10 * out_stride] = E[10] + O[10]; + out[11 * out_stride] = E[11] + O[11]; + out[12 * out_stride] = E[12] + O[12]; + out[13 * out_stride] = E[13] + O[13]; + out[14 * out_stride] = E[14] + O[14]; + out[15 * out_stride] = E[15] + O[15]; + out[16 * out_stride] = E[15] - O[15]; + out[17 * out_stride] = E[14] - O[14]; + out[18 * out_stride] = E[13] - O[13]; + out[19 * out_stride] = E[12] - O[12]; + out[20 * out_stride] = E[11] - O[11]; + out[21 * out_stride] = E[10] - O[10]; + out[22 * out_stride] = E[9] - O[9]; + out[23 * out_stride] = E[8] - O[8]; + out[24 * out_stride] = E[7] - O[7]; + out[25 * out_stride] = E[6] - O[6]; + out[26 * out_stride] = E[5] - O[5]; + out[27 * out_stride] = E[4] - O[4]; + out[28 * out_stride] = E[3] - O[3]; + out[29 * out_stride] = E[2] - O[2]; + out[30 * out_stride] = E[1] - O[1]; + out[31 * out_stride] = E[0] - O[0]; +} + +/* +transMatrix[64][64] = { + { aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa }, + { bf, bg, bh, bi, bj, bk, bl, bm, bn, bo, bp, bq, br, bs, bt, bu, bv, bw, bx, by, bz, ca, cb, cc, cd, ce, cf, cg, ch, ci, cj, ck, -ck, -cj, -ci, -ch, -cg, -cf, -ce, -cd, -cc, -cb, -ca, -bz, -by, -bx, -bw, -bv, -bu, -bt, -bs, -br, -bq, -bp, -bo, -bn, -bm, -bl, -bk, -bj, -bi, -bh, -bg, -bf }, + { ap, aq, ar, as, at, au, av, aw, ax, ay, az, ba, bb, bc, bd, be, -be, -bd, -bc, -bb, -ba, -az, -ay, -ax, -aw, -av, -au, -at, -as, -ar, -aq, -ap, -ap, -aq, -ar, -as, -at, -au, -av, -aw, -ax, -ay, -az, -ba, -bb, -bc, -bd, -be, be, bd, bc, bb, ba, az, ay, ax, aw, av, au, at, as, ar, aq, ap }, + { bg, bj, bm, bp, bs, bv, by, cb, ce, ch, ck, -ci, -cf, -cc, -bz, -bw, -bt, -bq, -bn, -bk, -bh, -bf, -bi, -bl, -bo, -br, -bu, -bx, -ca, -cd, -cg, -cj, cj, cg, cd, ca, bx, bu, br, bo, bl, bi, bf, bh, bk, bn, bq, bt, bw, bz, cc, cf, ci, -ck, -ch, -ce, -cb, -by, -bv, -bs, -bp, -bm, -bj, -bg }, + { ah, ai, aj, ak, al, am, an, ao, -ao, -an, -am, -al, -ak, -aj, -ai, -ah, -ah, -ai, -aj, -ak, -al, -am, -an, -ao, ao, an, am, al, ak, aj, ai, ah, ah, ai, aj, ak, al, am, an, ao, -ao, -an, -am, -al, -ak, -aj, -ai, -ah, -ah, -ai, -aj, -ak, -al, -am, -an, -ao, ao, an, am, al, ak, aj, ai, ah }, + { bh, bm, br, bw, cb, cg, -ck, -cf, -ca, -bv, -bq, -bl, -bg, -bi, -bn, -bs, -bx, -cc, -ch, cj, ce, bz, bu, bp, bk, bf, bj, bo, bt, by, cd, ci, -ci, -cd, -by, -bt, -bo, -bj, -bf, -bk, -bp, -bu, -bz, -ce, -cj, ch, cc, bx, bs, bn, bi, bg, bl, bq, bv, ca, cf, ck, -cg, -cb, -bw, -br, -bm, -bh }, + { aq, at, aw, az, bc, -be, -bb, -ay, -av, -as, -ap, -ar, -au, -ax, -ba, -bd, bd, ba, ax, au, ar, ap, as, av, ay, bb, be, -bc, -az, -aw, -at, -aq, -aq, -at, -aw, -az, -bc, be, bb, ay, av, as, ap, ar, au, ax, ba, bd, -bd, -ba, -ax, -au, -ar, -ap, -as, -av, -ay, -bb, -be, bc, az, aw, at, aq }, + { bi, bp, bw, cd, ck, -ce, -bx, -bq, -bj, -bh, -bo, -bv, -cc, -cj, cf, by, br, bk, bg, bn, bu, cb, ci, -cg, -bz, -bs, -bl, -bf, -bm, -bt, -ca, -ch, ch, ca, bt, bm, bf, bl, bs, bz, cg, -ci, -cb, -bu, -bn, -bg, -bk, -br, -by, -cf, cj, cc, bv, bo, bh, bj, bq, bx, ce, -ck, -cd, -bw, -bp, -bi }, + { ad, ae, af, ag, -ag, -af, -ae, -ad, -ad, -ae, -af, -ag, ag, af, ae, ad, ad, ae, af, ag, -ag, -af, -ae, -ad, -ad, -ae, -af, -ag, ag, af, ae, ad, ad, ae, af, ag, -ag, -af, -ae, -ad, -ad, -ae, -af, -ag, ag, af, ae, ad, ad, ae, af, ag, -ag, -af, -ae, -ad, -ad, -ae, -af, -ag, ag, af, ae, ad }, + { bj, bs, cb, ck, -cc, -bt, -bk, -bi, -br, -ca, -cj, cd, bu, bl, bh, bq, bz, ci, -ce, -bv, -bm, -bg, -bp, -by, -ch, cf, bw, bn, bf, bo, bx, cg, -cg, -bx, -bo, -bf, -bn, -bw, -cf, ch, by, bp, bg, bm, bv, ce, -ci, -bz, -bq, -bh, -bl, -bu, -cd, cj, ca, br, bi, bk, bt, cc, -ck, -cb, -bs, -bj }, + { ar, aw, bb, -bd, -ay, -at, -ap, -au, -az, -be, ba, av, aq, as, ax, bc, -bc, -ax, -as, -aq, -av, -ba, be, az, au, ap, at, ay, bd, -bb, -aw, -ar, -ar, -aw, -bb, bd, ay, at, ap, au, az, be, -ba, -av, -aq, -as, -ax, -bc, bc, ax, as, aq, av, ba, -be, -az, -au, -ap, -at, -ay, -bd, bb, aw, ar }, + { bk, bv, cg, -ce, -bt, -bi, -bm, -bx, -ci, cc, br, bg, bo, bz, ck, -ca, -bp, -bf, -bq, -cb, cj, by, bn, bh, bs, cd, -ch, -bw, -bl, -bj, -bu, -cf, cf, bu, bj, bl, bw, ch, -cd, -bs, -bh, -bn, -by, -cj, cb, bq, bf, bp, ca, -ck, -bz, -bo, -bg, -br, -cc, ci, bx, bm, bi, bt, ce, -cg, -bv, -bk }, + { ai, al, ao, -am, -aj, -ah, -ak, -an, an, ak, ah, aj, am, -ao, -al, -ai, -ai, -al, -ao, am, aj, ah, ak, an, -an, -ak, -ah, -aj, -am, ao, al, ai, ai, al, ao, -am, -aj, -ah, -ak, -an, an, ak, ah, aj, am, -ao, -al, -ai, -ai, -al, -ao, am, aj, ah, ak, an, -an, -ak, -ah, -aj, -am, ao, al, ai }, + { bl, by, -ck, -bx, -bk, -bm, -bz, cj, bw, bj, bn, ca, -ci, -bv, -bi, -bo, -cb, ch, bu, bh, bp, cc, -cg, -bt, -bg, -bq, -cd, cf, bs, bf, br, ce, -ce, -br, -bf, -bs, -cf, cd, bq, bg, bt, cg, -cc, -bp, -bh, -bu, -ch, cb, bo, bi, bv, ci, -ca, -bn, -bj, -bw, -cj, bz, bm, bk, bx, ck, -by, -bl }, + { as, az, -bd, -aw, -ap, -av, -bc, ba, at, ar, ay, -be, -ax, -aq, -au, -bb, bb, au, aq, ax, be, -ay, -ar, -at, -ba, bc, av, ap, aw, bd, -az, -as, -as, -az, bd, aw, ap, av, bc, -ba, -at, -ar, -ay, be, ax, aq, au, bb, -bb, -au, -aq, -ax, -be, ay, ar, at, ba, -bc, -av, -ap, -aw, -bd, az, as }, + { bm, cb, -cf, -bq, -bi, -bx, cj, bu, bf, bt, ci, -by, -bj, -bp, -ce, cc, bn, bl, ca, -cg, -br, -bh, -bw, ck, bv, bg, bs, ch, -bz, -bk, -bo, -cd, cd, bo, bk, bz, -ch, -bs, -bg, -bv, -ck, bw, bh, br, cg, -ca, -bl, -bn, -cc, ce, bp, bj, by, -ci, -bt, -bf, -bu, -cj, bx, bi, bq, cf, -cb, -bm }, + { ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab }, + { bn, ce, -ca, -bj, -br, -ci, bw, bf, bv, -cj, -bs, -bi, -bz, cf, bo, bm, cd, -cb, -bk, -bq, -ch, bx, bg, bu, -ck, -bt, -bh, -by, cg, bp, bl, cc, -cc, -bl, -bp, -cg, by, bh, bt, ck, -bu, -bg, -bx, ch, bq, bk, cb, -cd, -bm, -bo, -cf, bz, bi, bs, cj, -bv, -bf, -bw, ci, br, bj, ca, -ce, -bn }, + { at, bc, -ay, -ap, -ax, bd, au, as, bb, -az, -aq, -aw, be, av, ar, ba, -ba, -ar, -av, -be, aw, aq, az, -bb, -as, -au, -bd, ax, ap, ay, -bc, -at, -at, -bc, ay, ap, ax, -bd, -au, -as, -bb, az, aq, aw, -be, -av, -ar, -ba, ba, ar, av, be, -aw, -aq, -az, bb, as, au, bd, -ax, -ap, -ay, bc, at }, + { bo, ch, -bv, -bh, -ca, cc, bj, bt, -cj, -bq, -bm, -cf, bx, bf, by, -ce, -bl, -br, -ck, bs, bk, cd, -bz, -bg, -bw, cg, bn, bp, ci, -bu, -bi, -cb, cb, bi, bu, -ci, -bp, -bn, -cg, bw, bg, bz, -cd, -bk, -bs, ck, br, bl, ce, -by, -bf, -bx, cf, bm, bq, cj, -bt, -bj, -cc, ca, bh, bv, -ch, -bo }, + { aj, ao, -ak, -ai, -an, al, ah, am, -am, -ah, -al, an, ai, ak, -ao, -aj, -aj, -ao, ak, ai, an, -al, -ah, -am, am, ah, al, -an, -ai, -ak, ao, aj, aj, ao, -ak, -ai, -an, al, ah, am, -am, -ah, -al, an, ai, ak, -ao, -aj, -aj, -ao, ak, ai, an, -al, -ah, -am, am, ah, al, -an, -ai, -ak, ao, aj }, + { bp, ck, -bq, -bo, -cj, br, bn, ci, -bs, -bm, -ch, bt, bl, cg, -bu, -bk, -cf, bv, bj, ce, -bw, -bi, -cd, bx, bh, cc, -by, -bg, -cb, bz, bf, ca, -ca, -bf, -bz, cb, bg, by, -cc, -bh, -bx, cd, bi, bw, -ce, -bj, -bv, cf, bk, bu, -cg, -bl, -bt, ch, bm, bs, -ci, -bn, -br, cj, bo, bq, -ck, -bp }, + { au, -be, -at, -av, bd, as, aw, -bc, -ar, -ax, bb, aq, ay, -ba, -ap, -az, az, ap, ba, -ay, -aq, -bb, ax, ar, bc, -aw, -as, -bd, av, at, be, -au, -au, be, at, av, -bd, -as, -aw, bc, ar, ax, -bb, -aq, -ay, ba, ap, az, -az, -ap, -ba, ay, aq, bb, -ax, -ar, -bc, aw, as, bd, -av, -at, -be, au }, + { bq, -ci, -bl, -bv, cd, bg, ca, -by, -bi, -cf, bt, bn, ck, -bo, -bs, cg, bj, bx, -cb, -bf, -cc, bw, bk, ch, -br, -bp, cj, bm, bu, -ce, -bh, -bz, bz, bh, ce, -bu, -bm, -cj, bp, br, -ch, -bk, -bw, cc, bf, cb, -bx, -bj, -cg, bs, bo, -ck, -bn, -bt, cf, bi, by, -ca, -bg, -cd, bv, bl, ci, -bq }, + { ae, -ag, -ad, -af, af, ad, ag, -ae, -ae, ag, ad, af, -af, -ad, -ag, ae, ae, -ag, -ad, -af, af, ad, ag, -ae, -ae, ag, ad, af, -af, -ad, -ag, ae, ae, -ag, -ad, -af, af, ad, ag, -ae, -ae, ag, ad, af, -af, -ad, -ag, ae, ae, -ag, -ad, -af, af, ad, ag, -ae, -ae, ag, ad, af, -af, -ad, -ag, ae }, + { br, -cf, -bg, -cc, bu, bo, -ci, -bj, -bz, bx, bl, ck, -bm, -bw, ca, bi, ch, -bp, -bt, cd, bf, ce, -bs, -bq, cg, bh, cb, -bv, -bn, cj, bk, by, -by, -bk, -cj, bn, bv, -cb, -bh, -cg, bq, bs, -ce, -bf, -cd, bt, bp, -ch, -bi, -ca, bw, bm, -ck, -bl, -bx, bz, bj, ci, -bo, -bu, cc, bg, cf, -br }, + { av, -bb, -ap, -bc, au, aw, -ba, -aq, -bd, at, ax, -az, -ar, -be, as, ay, -ay, -as, be, ar, az, -ax, -at, bd, aq, ba, -aw, -au, bc, ap, bb, -av, -av, bb, ap, bc, -au, -aw, ba, aq, bd, -at, -ax, az, ar, be, -as, -ay, ay, as, -be, -ar, -az, ax, at, -bd, -aq, -ba, aw, au, -bc, -ap, -bb, av }, + { bs, -cc, -bi, -cj, bl, bz, -bv, -bp, cf, bf, cg, -bo, -bw, by, bm, -ci, -bh, -cd, br, bt, -cb, -bj, -ck, bk, ca, -bu, -bq, ce, bg, ch, -bn, -bx, bx, bn, -ch, -bg, -ce, bq, bu, -ca, -bk, ck, bj, cb, -bt, -br, cd, bh, ci, -bm, -by, bw, bo, -cg, -bf, -cf, bp, bv, -bz, -bl, cj, bi, cc, -bs }, + { ak, -am, -ai, ao, ah, an, -aj, -al, al, aj, -an, -ah, -ao, ai, am, -ak, -ak, am, ai, -ao, -ah, -an, aj, al, -al, -aj, an, ah, ao, -ai, -am, ak, ak, -am, -ai, ao, ah, an, -aj, -al, al, aj, -an, -ah, -ao, ai, am, -ak, -ak, am, ai, -ao, -ah, -an, aj, al, -al, -aj, an, ah, ao, -ai, -am, ak }, + { bt, -bz, -bn, cf, bh, ck, -bi, -ce, bo, by, -bu, -bs, ca, bm, -cg, -bg, -cj, bj, cd, -bp, -bx, bv, br, -cb, -bl, ch, bf, ci, -bk, -cc, bq, bw, -bw, -bq, cc, bk, -ci, -bf, -ch, bl, cb, -br, -bv, bx, bp, -cd, -bj, cj, bg, cg, -bm, -ca, bs, bu, -by, -bo, ce, bi, -ck, -bh, -cf, bn, bz, -bt }, + { aw, -ay, -au, ba, as, -bc, -aq, be, ap, bd, -ar, -bb, at, az, -av, -ax, ax, av, -az, -at, bb, ar, -bd, -ap, -be, aq, bc, -as, -ba, au, ay, -aw, -aw, ay, au, -ba, -as, bc, aq, -be, -ap, -bd, ar, bb, -at, -az, av, ax, -ax, -av, az, at, -bb, -ar, bd, ap, be, -aq, -bc, as, ba, -au, -ay, aw }, + { bu, -bw, -bs, by, bq, -ca, -bo, cc, bm, -ce, -bk, cg, bi, -ci, -bg, ck, bf, cj, -bh, -ch, bj, cf, -bl, -cd, bn, cb, -bp, -bz, br, bx, -bt, -bv, bv, bt, -bx, -br, bz, bp, -cb, -bn, cd, bl, -cf, -bj, ch, bh, -cj, -bf, -ck, bg, ci, -bi, -cg, bk, ce, -bm, -cc, bo, ca, -bq, -by, bs, bw, -bu }, + { aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa }, + { bv, -bt, -bx, br, bz, -bp, -cb, bn, cd, -bl, -cf, bj, ch, -bh, -cj, bf, -ck, -bg, ci, bi, -cg, -bk, ce, bm, -cc, -bo, ca, bq, -by, -bs, bw, bu, -bu, -bw, bs, by, -bq, -ca, bo, cc, -bm, -ce, bk, cg, -bi, -ci, bg, ck, -bf, cj, bh, -ch, -bj, cf, bl, -cd, -bn, cb, bp, -bz, -br, bx, bt, -bv }, + { ax, -av, -az, at, bb, -ar, -bd, ap, -be, -aq, bc, as, -ba, -au, ay, aw, -aw, -ay, au, ba, -as, -bc, aq, be, -ap, bd, ar, -bb, -at, az, av, -ax, -ax, av, az, -at, -bb, ar, bd, -ap, be, aq, -bc, -as, ba, au, -ay, -aw, aw, ay, -au, -ba, as, bc, -aq, -be, ap, -bd, -ar, bb, at, -az, -av, ax }, + { bw, -bq, -cc, bk, ci, -bf, ch, bl, -cb, -br, bv, bx, -bp, -cd, bj, cj, -bg, cg, bm, -ca, -bs, bu, by, -bo, -ce, bi, ck, -bh, cf, bn, -bz, -bt, bt, bz, -bn, -cf, bh, -ck, -bi, ce, bo, -by, -bu, bs, ca, -bm, -cg, bg, -cj, -bj, cd, bp, -bx, -bv, br, cb, -bl, -ch, bf, -ci, -bk, cc, bq, -bw }, + { al, -aj, -an, ah, -ao, -ai, am, ak, -ak, -am, ai, ao, -ah, an, aj, -al, -al, aj, an, -ah, ao, ai, -am, -ak, ak, am, -ai, -ao, ah, -an, -aj, al, al, -aj, -an, ah, -ao, -ai, am, ak, -ak, -am, ai, ao, -ah, an, aj, -al, -al, aj, an, -ah, ao, ai, -am, -ak, ak, am, -ai, -ao, ah, -an, -aj, al }, + { bx, -bn, -ch, bg, -ce, -bq, bu, ca, -bk, -ck, bj, -cb, -bt, br, cd, -bh, ci, bm, -by, -bw, bo, cg, -bf, cf, bp, -bv, -bz, bl, cj, -bi, cc, bs, -bs, -cc, bi, -cj, -bl, bz, bv, -bp, -cf, bf, -cg, -bo, bw, by, -bm, -ci, bh, -cd, -br, bt, cb, -bj, ck, bk, -ca, -bu, bq, ce, -bg, ch, bn, -bx }, + { ay, -as, -be, ar, -az, -ax, at, bd, -aq, ba, aw, -au, -bc, ap, -bb, -av, av, bb, -ap, bc, au, -aw, -ba, aq, -bd, -at, ax, az, -ar, be, as, -ay, -ay, as, be, -ar, az, ax, -at, -bd, aq, -ba, -aw, au, bc, -ap, bb, av, -av, -bb, ap, -bc, -au, aw, ba, -aq, bd, at, -ax, -az, ar, -be, -as, ay }, + { by, -bk, cj, bn, -bv, -cb, bh, -cg, -bq, bs, ce, -bf, cd, bt, -bp, -ch, bi, -ca, -bw, bm, ck, -bl, bx, bz, -bj, ci, bo, -bu, -cc, bg, -cf, -br, br, cf, -bg, cc, bu, -bo, -ci, bj, -bz, -bx, bl, -ck, -bm, bw, ca, -bi, ch, bp, -bt, -cd, bf, -ce, -bs, bq, cg, -bh, cb, bv, -bn, -cj, bk, -by }, + { af, -ad, ag, ae, -ae, -ag, ad, -af, -af, ad, -ag, -ae, ae, ag, -ad, af, af, -ad, ag, ae, -ae, -ag, ad, -af, -af, ad, -ag, -ae, ae, ag, -ad, af, af, -ad, ag, ae, -ae, -ag, ad, -af, -af, ad, -ag, -ae, ae, ag, -ad, af, af, -ad, ag, ae, -ae, -ag, ad, -af, -af, ad, -ag, -ae, ae, ag, -ad, af }, + { bz, -bh, ce, bu, -bm, cj, bp, -br, -ch, bk, -bw, -cc, bf, -cb, -bx, bj, -cg, -bs, bo, ck, -bn, bt, cf, -bi, by, ca, -bg, cd, bv, -bl, ci, bq, -bq, -ci, bl, -bv, -cd, bg, -ca, -by, bi, -cf, -bt, bn, -ck, -bo, bs, cg, -bj, bx, cb, -bf, cc, bw, -bk, ch, br, -bp, -cj, bm, -bu, -ce, bh, -bz }, + { az, -ap, ba, ay, -aq, bb, ax, -ar, bc, aw, -as, bd, av, -at, be, au, -au, -be, at, -av, -bd, as, -aw, -bc, ar, -ax, -bb, aq, -ay, -ba, ap, -az, -az, ap, -ba, -ay, aq, -bb, -ax, ar, -bc, -aw, as, -bd, -av, at, -be, -au, au, be, -at, av, bd, -as, aw, bc, -ar, ax, bb, -aq, ay, ba, -ap, az }, + { ca, -bf, bz, cb, -bg, by, cc, -bh, bx, cd, -bi, bw, ce, -bj, bv, cf, -bk, bu, cg, -bl, bt, ch, -bm, bs, ci, -bn, br, cj, -bo, bq, ck, -bp, bp, -ck, -bq, bo, -cj, -br, bn, -ci, -bs, bm, -ch, -bt, bl, -cg, -bu, bk, -cf, -bv, bj, -ce, -bw, bi, -cd, -bx, bh, -cc, -by, bg, -cb, -bz, bf, -ca }, + { am, -ah, al, an, -ai, ak, ao, -aj, aj, -ao, -ak, ai, -an, -al, ah, -am, -am, ah, -al, -an, ai, -ak, -ao, aj, -aj, ao, ak, -ai, an, al, -ah, am, am, -ah, al, an, -ai, ak, ao, -aj, aj, -ao, -ak, ai, -an, -al, ah, -am, -am, ah, -al, -an, ai, -ak, -ao, aj, -aj, ao, ak, -ai, an, al, -ah, am }, + { cb, -bi, bu, ci, -bp, bn, -cg, -bw, bg, -bz, -cd, bk, -bs, -ck, br, -bl, ce, by, -bf, bx, cf, -bm, bq, -cj, -bt, bj, -cc, -ca, bh, -bv, -ch, bo, -bo, ch, bv, -bh, ca, cc, -bj, bt, cj, -bq, bm, -cf, -bx, bf, -by, -ce, bl, -br, ck, bs, -bk, cd, bz, -bg, bw, cg, -bn, bp, -ci, -bu, bi, -cb }, + { ba, -ar, av, -be, -aw, aq, -az, -bb, as, -au, bd, ax, -ap, ay, bc, -at, at, -bc, -ay, ap, -ax, -bd, au, -as, bb, az, -aq, aw, be, -av, ar, -ba, -ba, ar, -av, be, aw, -aq, az, bb, -as, au, -bd, -ax, ap, -ay, -bc, at, -at, bc, ay, -ap, ax, bd, -au, as, -bb, -az, aq, -aw, -be, av, -ar, ba }, + { cc, -bl, bp, -cg, -by, bh, -bt, ck, bu, -bg, bx, ch, -bq, bk, -cb, -cd, bm, -bo, cf, bz, -bi, bs, -cj, -bv, bf, -bw, -ci, br, -bj, ca, ce, -bn, bn, -ce, -ca, bj, -br, ci, bw, -bf, bv, cj, -bs, bi, -bz, -cf, bo, -bm, cd, cb, -bk, bq, -ch, -bx, bg, -bu, -ck, bt, -bh, by, cg, -bp, bl, -cc }, + { ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac }, + { cd, -bo, bk, -bz, -ch, bs, -bg, bv, -ck, -bw, bh, -br, cg, ca, -bl, bn, -cc, -ce, bp, -bj, by, ci, -bt, bf, -bu, cj, bx, -bi, bq, -cf, -cb, bm, -bm, cb, cf, -bq, bi, -bx, -cj, bu, -bf, bt, -ci, -by, bj, -bp, ce, cc, -bn, bl, -ca, -cg, br, -bh, bw, ck, -bv, bg, -bs, ch, bz, -bk, bo, -cd }, + { bb, -au, aq, -ax, be, ay, -ar, at, -ba, -bc, av, -ap, aw, -bd, -az, as, -as, az, bd, -aw, ap, -av, bc, ba, -at, ar, -ay, -be, ax, -aq, au, -bb, -bb, au, -aq, ax, -be, -ay, ar, -at, ba, bc, -av, ap, -aw, bd, az, -as, as, -az, -bd, aw, -ap, av, -bc, -ba, at, -ar, ay, be, -ax, aq, -au, bb }, + { ce, -br, bf, -bs, cf, cd, -bq, bg, -bt, cg, cc, -bp, bh, -bu, ch, cb, -bo, bi, -bv, ci, ca, -bn, bj, -bw, cj, bz, -bm, bk, -bx, ck, by, -bl, bl, -by, -ck, bx, -bk, bm, -bz, -cj, bw, -bj, bn, -ca, -ci, bv, -bi, bo, -cb, -ch, bu, -bh, bp, -cc, -cg, bt, -bg, bq, -cd, -cf, bs, -bf, br, -ce }, + { an, -ak, ah, -aj, am, ao, -al, ai, -ai, al, -ao, -am, aj, -ah, ak, -an, -an, ak, -ah, aj, -am, -ao, al, -ai, ai, -al, ao, am, -aj, ah, -ak, an, an, -ak, ah, -aj, am, ao, -al, ai, -ai, al, -ao, -am, aj, -ah, ak, -an, -an, ak, -ah, aj, -am, -ao, al, -ai, ai, -al, ao, am, -aj, ah, -ak, an }, + { cf, -bu, bj, -bl, bw, -ch, -cd, bs, -bh, bn, -by, cj, cb, -bq, bf, -bp, ca, ck, -bz, bo, -bg, br, -cc, -ci, bx, -bm, bi, -bt, ce, cg, -bv, bk, -bk, bv, -cg, -ce, bt, -bi, bm, -bx, ci, cc, -br, bg, -bo, bz, -ck, -ca, bp, -bf, bq, -cb, -cj, by, -bn, bh, -bs, cd, ch, -bw, bl, -bj, bu, -cf }, + { bc, -ax, as, -aq, av, -ba, -be, az, -au, ap, -at, ay, -bd, -bb, aw, -ar, ar, -aw, bb, bd, -ay, at, -ap, au, -az, be, ba, -av, aq, -as, ax, -bc, -bc, ax, -as, aq, -av, ba, be, -az, au, -ap, at, -ay, bd, bb, -aw, ar, -ar, aw, -bb, -bd, ay, -at, ap, -au, az, -be, -ba, av, -aq, as, -ax, bc }, + { cg, -bx, bo, -bf, bn, -bw, cf, ch, -by, bp, -bg, bm, -bv, ce, ci, -bz, bq, -bh, bl, -bu, cd, cj, -ca, br, -bi, bk, -bt, cc, ck, -cb, bs, -bj, bj, -bs, cb, -ck, -cc, bt, -bk, bi, -br, ca, -cj, -cd, bu, -bl, bh, -bq, bz, -ci, -ce, bv, -bm, bg, -bp, by, -ch, -cf, bw, -bn, bf, -bo, bx, -cg }, + { ag, -af, ae, -ad, ad, -ae, af, -ag, -ag, af, -ae, ad, -ad, ae, -af, ag, ag, -af, ae, -ad, ad, -ae, af, -ag, -ag, af, -ae, ad, -ad, ae, -af, ag, ag, -af, ae, -ad, ad, -ae, af, -ag, -ag, af, -ae, ad, -ad, ae, -af, ag, ag, -af, ae, -ad, ad, -ae, af, -ag, -ag, af, -ae, ad, -ad, ae, -af, ag }, + { ch, -ca, bt, -bm, bf, -bl, bs, -bz, cg, ci, -cb, bu, -bn, bg, -bk, br, -by, cf, cj, -cc, bv, -bo, bh, -bj, bq, -bx, ce, ck, -cd, bw, -bp, bi, -bi, bp, -bw, cd, -ck, -ce, bx, -bq, bj, -bh, bo, -bv, cc, -cj, -cf, by, -br, bk, -bg, bn, -bu, cb, -ci, -cg, bz, -bs, bl, -bf, bm, -bt, ca, -ch }, + { bd, -ba, ax, -au, ar, -ap, as, -av, ay, -bb, be, bc, -az, aw, -at, aq, -aq, at, -aw, az, -bc, -be, bb, -ay, av, -as, ap, -ar, au, -ax, ba, -bd, -bd, ba, -ax, au, -ar, ap, -as, av, -ay, bb, -be, -bc, az, -aw, at, -aq, aq, -at, aw, -az, bc, be, -bb, ay, -av, as, -ap, ar, -au, ax, -ba, bd }, + { ci, -cd, by, -bt, bo, -bj, bf, -bk, bp, -bu, bz, -ce, cj, ch, -cc, bx, -bs, bn, -bi, bg, -bl, bq, -bv, ca, -cf, ck, cg, -cb, bw, -br, bm, -bh, bh, -bm, br, -bw, cb, -cg, -ck, cf, -ca, bv, -bq, bl, -bg, bi, -bn, bs, -bx, cc, -ch, -cj, ce, -bz, bu, -bp, bk, -bf, bj, -bo, bt, -by, cd, -ci }, + { ao, -an, am, -al, ak, -aj, ai, -ah, ah, -ai, aj, -ak, al, -am, an, -ao, -ao, an, -am, al, -ak, aj, -ai, ah, -ah, ai, -aj, ak, -al, am, -an, ao, ao, -an, am, -al, ak, -aj, ai, -ah, ah, -ai, aj, -ak, al, -am, an, -ao, -ao, an, -am, al, -ak, aj, -ai, ah, -ah, ai, -aj, ak, -al, am, -an, ao }, + { cj, -cg, cd, -ca, bx, -bu, br, -bo, bl, -bi, bf, -bh, bk, -bn, bq, -bt, bw, -bz, cc, -cf, ci, ck, -ch, ce, -cb, by, -bv, bs, -bp, bm, -bj, bg, -bg, bj, -bm, bp, -bs, bv, -by, cb, -ce, ch, -ck, -ci, cf, -cc, bz, -bw, bt, -bq, bn, -bk, bh, -bf, bi, -bl, bo, -br, bu, -bx, ca, -cd, cg, -cj }, + { be, -bd, bc, -bb, ba, -az, ay, -ax, aw, -av, au, -at, as, -ar, aq, -ap, ap, -aq, ar, -as, at, -au, av, -aw, ax, -ay, az, -ba, bb, -bc, bd, -be, -be, bd, -bc, bb, -ba, az, -ay, ax, -aw, av, -au, at, -as, ar, -aq, ap, -ap, aq, -ar, as, -at, au, -av, aw, -ax, ay, -az, ba, -bb, bc, -bd, be }, + { ck, -cj, ci, -ch, cg, -cf, ce, -cd, cc, -cb, ca, -bz, by, -bx, bw, -bv, bu, -bt, bs, -br, bq, -bp, bo, -bn, bm, -bl, bk, -bj, bi, -bh, bg, -bf, bf, -bg, bh, -bi, bj, -bk, bl, -bm, bn, -bo, bp, -bq, br, -bs, bt, -bu, bv, -bw, bx, -by, bz, -ca, cb, -cc, cd, -ce, cf, -cg, ch, -ci, cj, -ck }, +} + */ + +void ff_vvc_inv_dct2_64(int *out, const ptrdiff_t out_stride, const int *in, ptrdiff_t in_stride) +{ + const int aa = 64, ab = 83, ac = 36, ad = 89, ae = 75, af = 50, ag = 18, ah = 90; + const int ai = 87, aj = 80, ak = 70, al = 57, am = 43, an = 25, ao = 9, ap = 90; + const int aq = 90, ar = 88, as = 85, at = 82, au = 78, av = 73, aw = 67, ax = 61; + const int ay = 54, az = 46, ba = 38, bb = 31, bc = 22, bd = 13, be = 4, bf = 91; + const int bg = 90, bh = 90, bi = 90, bj = 88, bk = 87, bl = 86, bm = 84, bn = 83; + const int bo = 81, bp = 79, bq = 77, br = 73, bs = 71, bt = 69, bu = 65, bv = 62; + const int bw = 59, bx = 56, by = 52, bz = 48, ca = 44, cb = 41, cc = 37, cd = 33; + const int ce = 28, cf = 24, cg = 20, ch = 15, ci = 11, cj = 7, ck = 2; + const int x0 = in[0 * in_stride], x1 = in[1 * in_stride]; + const int x2 = in[2 * in_stride], x3 = in[3 * in_stride]; + const int x4 = in[4 * in_stride], x5 = in[5 * in_stride]; + const int x6 = in[6 * in_stride], x7 = in[7 * in_stride]; + const int x8 = in[8 * in_stride], x9 = in[9 * in_stride]; + const int x10 = in[10 * in_stride], x11 = in[11 * in_stride]; + const int x12 = in[12 * in_stride], x13 = in[13 * in_stride]; + const int x14 = in[14 * in_stride], x15 = in[15 * in_stride]; + const int x16 = in[16 * in_stride], x17 = in[17 * in_stride]; + const int x18 = in[18 * in_stride], x19 = in[19 * in_stride]; + const int x20 = in[20 * in_stride], x21 = in[21 * in_stride]; + const int x22 = in[22 * in_stride], x23 = in[23 * in_stride]; + const int x24 = in[24 * in_stride], x25 = in[25 * in_stride]; + const int x26 = in[26 * in_stride], x27 = in[27 * in_stride]; + const int x28 = in[28 * in_stride], x29 = in[29 * in_stride]; + const int x30 = in[30 * in_stride], x31 = in[31 * in_stride]; + const int x32 = in[32 * in_stride], x33 = in[33 * in_stride]; + const int x34 = in[34 * in_stride], x35 = in[35 * in_stride]; + const int x36 = in[36 * in_stride], x37 = in[37 * in_stride]; + const int x38 = in[38 * in_stride], x39 = in[39 * in_stride]; + const int x40 = in[40 * in_stride], x41 = in[41 * in_stride]; + const int x42 = in[42 * in_stride], x43 = in[43 * in_stride]; + const int x44 = in[44 * in_stride], x45 = in[45 * in_stride]; + const int x46 = in[46 * in_stride], x47 = in[47 * in_stride]; + const int x48 = in[48 * in_stride], x49 = in[49 * in_stride]; + const int x50 = in[50 * in_stride], x51 = in[51 * in_stride]; + const int x52 = in[52 * in_stride], x53 = in[53 * in_stride]; + const int x54 = in[54 * in_stride], x55 = in[55 * in_stride]; + const int x56 = in[56 * in_stride], x57 = in[57 * in_stride]; + const int x58 = in[58 * in_stride], x59 = in[59 * in_stride]; + const int x60 = in[60 * in_stride], x61 = in[61 * in_stride]; + const int x62 = in[62 * in_stride], x63 = in[63 * in_stride]; + const int EEEEE[2] = { + aa * (x0 + x32), + aa * (x0 - x32), + }; + const int EEEEO[2] = { + ab * x16 + ac * x48, + ac * x16 - ab * x48, + }; + const int EEEE[4] = { + EEEEE[0] + EEEEO[0], EEEEE[1] + EEEEO[1], + EEEEE[1] - EEEEO[1], EEEEE[0] - EEEEO[0], + }; + const int EEEO[4] = { + ad * x8 + ae * x24 + af * x40 + ag * x56, + ae * x8 - ag * x24 - ad * x40 - af * x56, + af * x8 - ad * x24 + ag * x40 + ae * x56, + ag * x8 - af * x24 + ae * x40 - ad * x56, + }; + const int EEE[8] = { + EEEE[0] + EEEO[0], EEEE[1] + EEEO[1], EEEE[2] + EEEO[2], EEEE[3] + EEEO[3], + EEEE[3] - EEEO[3], EEEE[2] - EEEO[2], EEEE[1] - EEEO[1], EEEE[0] - EEEO[0], + }; + const int EEO[8] = { + ah * x4 + ai * x12 + aj * x20 + ak * x28 + al * x36 + am * x44 + an * x52 + ao * x60, + ai * x4 + al * x12 + ao * x20 - am * x28 - aj * x36 - ah * x44 - ak * x52 - an * x60, + aj * x4 + ao * x12 - ak * x20 - ai * x28 - an * x36 + al * x44 + ah * x52 + am * x60, + ak * x4 - am * x12 - ai * x20 + ao * x28 + ah * x36 + an * x44 - aj * x52 - al * x60, + al * x4 - aj * x12 - an * x20 + ah * x28 - ao * x36 - ai * x44 + am * x52 + ak * x60, + am * x4 - ah * x12 + al * x20 + an * x28 - ai * x36 + ak * x44 + ao * x52 - aj * x60, + an * x4 - ak * x12 + ah * x20 - aj * x28 + am * x36 + ao * x44 - al * x52 + ai * x60, + ao * x4 - an * x12 + am * x20 - al * x28 + ak * x36 - aj * x44 + ai * x52 - ah * x60, + }; + const int EE[16] = { + EEE[0] + EEO[0], EEE[1] + EEO[1], EEE[2] + EEO[2], EEE[3] + EEO[3], EEE[4] + EEO[4], EEE[5] + EEO[5], EEE[6] + EEO[6], EEE[7] + EEO[7], + EEE[7] - EEO[7], EEE[6] - EEO[6], EEE[5] - EEO[5], EEE[4] - EEO[4], EEE[3] - EEO[3], EEE[2] - EEO[2], EEE[1] - EEO[1], EEE[0] - EEO[0], + }; + const int EO[16] = { + ap * x2 + aq * x6 + ar * x10 + as * x14 + at * x18 + au * x22 + av * x26 + aw * x30 + ax * x34 + ay * x38 + az * x42 + ba * x46 + bb * x50 + bc * x54 + bd * x58 + be * x62, + aq * x2 + at * x6 + aw * x10 + az * x14 + bc * x18 - be * x22 - bb * x26 - ay * x30 - av * x34 - as * x38 - ap * x42 - ar * x46 - au * x50 - ax * x54 - ba * x58 - bd * x62, + ar * x2 + aw * x6 + bb * x10 - bd * x14 - ay * x18 - at * x22 - ap * x26 - au * x30 - az * x34 - be * x38 + ba * x42 + av * x46 + aq * x50 + as * x54 + ax * x58 + bc * x62, + as * x2 + az * x6 - bd * x10 - aw * x14 - ap * x18 - av * x22 - bc * x26 + ba * x30 + at * x34 + ar * x38 + ay * x42 - be * x46 - ax * x50 - aq * x54 - au * x58 - bb * x62, + at * x2 + bc * x6 - ay * x10 - ap * x14 - ax * x18 + bd * x22 + au * x26 + as * x30 + bb * x34 - az * x38 - aq * x42 - aw * x46 + be * x50 + av * x54 + ar * x58 + ba * x62, + au * x2 - be * x6 - at * x10 - av * x14 + bd * x18 + as * x22 + aw * x26 - bc * x30 - ar * x34 - ax * x38 + bb * x42 + aq * x46 + ay * x50 - ba * x54 - ap * x58 - az * x62, + av * x2 - bb * x6 - ap * x10 - bc * x14 + au * x18 + aw * x22 - ba * x26 - aq * x30 - bd * x34 + at * x38 + ax * x42 - az * x46 - ar * x50 - be * x54 + as * x58 + ay * x62, + aw * x2 - ay * x6 - au * x10 + ba * x14 + as * x18 - bc * x22 - aq * x26 + be * x30 + ap * x34 + bd * x38 - ar * x42 - bb * x46 + at * x50 + az * x54 - av * x58 - ax * x62, + ax * x2 - av * x6 - az * x10 + at * x14 + bb * x18 - ar * x22 - bd * x26 + ap * x30 - be * x34 - aq * x38 + bc * x42 + as * x46 - ba * x50 - au * x54 + ay * x58 + aw * x62, + ay * x2 - as * x6 - be * x10 + ar * x14 - az * x18 - ax * x22 + at * x26 + bd * x30 - aq * x34 + ba * x38 + aw * x42 - au * x46 - bc * x50 + ap * x54 - bb * x58 - av * x62, + az * x2 - ap * x6 + ba * x10 + ay * x14 - aq * x18 + bb * x22 + ax * x26 - ar * x30 + bc * x34 + aw * x38 - as * x42 + bd * x46 + av * x50 - at * x54 + be * x58 + au * x62, + ba * x2 - ar * x6 + av * x10 - be * x14 - aw * x18 + aq * x22 - az * x26 - bb * x30 + as * x34 - au * x38 + bd * x42 + ax * x46 - ap * x50 + ay * x54 + bc * x58 - at * x62, + bb * x2 - au * x6 + aq * x10 - ax * x14 + be * x18 + ay * x22 - ar * x26 + at * x30 - ba * x34 - bc * x38 + av * x42 - ap * x46 + aw * x50 - bd * x54 - az * x58 + as * x62, + bc * x2 - ax * x6 + as * x10 - aq * x14 + av * x18 - ba * x22 - be * x26 + az * x30 - au * x34 + ap * x38 - at * x42 + ay * x46 - bd * x50 - bb * x54 + aw * x58 - ar * x62, + bd * x2 - ba * x6 + ax * x10 - au * x14 + ar * x18 - ap * x22 + as * x26 - av * x30 + ay * x34 - bb * x38 + be * x42 + bc * x46 - az * x50 + aw * x54 - at * x58 + aq * x62, + be * x2 - bd * x6 + bc * x10 - bb * x14 + ba * x18 - az * x22 + ay * x26 - ax * x30 + aw * x34 - av * x38 + au * x42 - at * x46 + as * x50 - ar * x54 + aq * x58 - ap * x62, + }; + const int E[32] = { + EE[0] + EO[0], EE[1] + EO[1], EE[2] + EO[2], EE[3] + EO[3], EE[4] + EO[4], EE[5] + EO[5], EE[6] + EO[6], EE[7] + EO[7], EE[8] + EO[8], EE[9] + EO[9], EE[10] + EO[10], EE[11] + EO[11], EE[12] + EO[12], EE[13] + EO[13], EE[14] + EO[14], EE[15] + EO[15], + EE[15] - EO[15], EE[14] - EO[14], EE[13] - EO[13], EE[12] - EO[12], EE[11] - EO[11], EE[10] - EO[10], EE[9] - EO[9], EE[8] - EO[8], EE[7] - EO[7], EE[6] - EO[6], EE[5] - EO[5], EE[4] - EO[4], EE[3] - EO[3], EE[2] - EO[2], EE[1] - EO[1], EE[0] - EO[0], + }; + const int O[32] = { + bf * x1 + bg * x3 + bh * x5 + bi * x7 + bj * x9 + bk * x11 + bl * x13 + bm * x15 + bn * x17 + bo * x19 + bp * x21 + bq * x23 + br * x25 + bs * x27 + bt * x29 + bu * x31 + bv * x33 + bw * x35 + bx * x37 + by * x39 + bz * x41 + ca * x43 + cb * x45 + cc * x47 + cd * x49 + ce * x51 + cf * x53 + cg * x55 + ch * x57 + ci * x59 + cj * x61 + ck * x63, + bg * x1 + bj * x3 + bm * x5 + bp * x7 + bs * x9 + bv * x11 + by * x13 + cb * x15 + ce * x17 + ch * x19 + ck * x21 - ci * x23 + -cf * x25 - cc * x27 - bz * x29 - bw * x31 - bt * x33 - bq * x35 - bn * x37 - bk * x39 - bh * x41 - bf * x43 - bi * x45 - bl * x47 - bo * x49 - br * x51 - bu * x53 - bx * x55 - ca * x57 - cd * x59 - cg * x61 - cj * x63, + bh * x1 + bm * x3 + br * x5 + bw * x7 + cb * x9 + cg * x11 - ck * x13 - cf * x15 - ca * x17 - bv * x19 - bq * x21 - bl * x23 + -bg * x25 - bi * x27 - bn * x29 - bs * x31 - bx * x33 - cc * x35 - ch * x37 + cj * x39 + ce * x41 + bz * x43 + bu * x45 + bp * x47 + bk * x49 + bf * x51 + bj * x53 + bo * x55 + bt * x57 + by * x59 + cd * x61 + ci * x63, + bi * x1 + bp * x3 + bw * x5 + cd * x7 + ck * x9 - ce * x11 - bx * x13 - bq * x15 - bj * x17 - bh * x19 - bo * x21 - bv * x23 + -cc * x25 - cj * x27 + cf * x29 + by * x31 + br * x33 + bk * x35 + bg * x37 + bn * x39 + bu * x41 + cb * x43 + ci * x45 - cg * x47 - bz * x49 - bs * x51 - bl * x53 - bf * x55 - bm * x57 - bt * x59 - ca * x61 - ch * x63, + bj * x1 + bs * x3 + cb * x5 + ck * x7 - cc * x9 - bt * x11 - bk * x13 - bi * x15 - br * x17 - ca * x19 - cj * x21 + cd * x23 + bu * x25 + bl * x27 + bh * x29 + bq * x31 + bz * x33 + ci * x35 - ce * x37 - bv * x39 - bm * x41 - bg * x43 - bp * x45 - by * x47 - ch * x49 + cf * x51 + bw * x53 + bn * x55 + bf * x57 + bo * x59 + bx * x61 + cg * x63, + bk * x1 + bv * x3 + cg * x5 - ce * x7 - bt * x9 - bi * x11 - bm * x13 - bx * x15 - ci * x17 + cc * x19 + br * x21 + bg * x23 + bo * x25 + bz * x27 + ck * x29 - ca * x31 - bp * x33 - bf * x35 - bq * x37 - cb * x39 + cj * x41 + by * x43 + bn * x45 + bh * x47 + bs * x49 + cd * x51 - ch * x53 - bw * x55 - bl * x57 - bj * x59 - bu * x61 - cf * x63, + bl * x1 + by * x3 - ck * x5 - bx * x7 - bk * x9 - bm * x11 - bz * x13 + cj * x15 + bw * x17 + bj * x19 + bn * x21 + ca * x23 + -ci * x25 - bv * x27 - bi * x29 - bo * x31 - cb * x33 + ch * x35 + bu * x37 + bh * x39 + bp * x41 + cc * x43 - cg * x45 - bt * x47 - bg * x49 - bq * x51 - cd * x53 + cf * x55 + bs * x57 + bf * x59 + br * x61 + ce * x63, + bm * x1 + cb * x3 - cf * x5 - bq * x7 - bi * x9 - bx * x11 + cj * x13 + bu * x15 + bf * x17 + bt * x19 + ci * x21 - by * x23 + -bj * x25 - bp * x27 - ce * x29 + cc * x31 + bn * x33 + bl * x35 + ca * x37 - cg * x39 - br * x41 - bh * x43 - bw * x45 + ck * x47 + bv * x49 + bg * x51 + bs * x53 + ch * x55 - bz * x57 - bk * x59 - bo * x61 - cd * x63, + bn * x1 + ce * x3 - ca * x5 - bj * x7 - br * x9 - ci * x11 + bw * x13 + bf * x15 + bv * x17 - cj * x19 - bs * x21 - bi * x23 + -bz * x25 + cf * x27 + bo * x29 + bm * x31 + cd * x33 - cb * x35 - bk * x37 - bq * x39 - ch * x41 + bx * x43 + bg * x45 + bu * x47 - ck * x49 - bt * x51 - bh * x53 - by * x55 + cg * x57 + bp * x59 + bl * x61 + cc * x63, + bo * x1 + ch * x3 - bv * x5 - bh * x7 - ca * x9 + cc * x11 + bj * x13 + bt * x15 - cj * x17 - bq * x19 - bm * x21 - cf * x23 + bx * x25 + bf * x27 + by * x29 - ce * x31 - bl * x33 - br * x35 - ck * x37 + bs * x39 + bk * x41 + cd * x43 - bz * x45 - bg * x47 - bw * x49 + cg * x51 + bn * x53 + bp * x55 + ci * x57 - bu * x59 - bi * x61 - cb * x63, + bp * x1 + ck * x3 - bq * x5 - bo * x7 - cj * x9 + br * x11 + bn * x13 + ci * x15 - bs * x17 - bm * x19 - ch * x21 + bt * x23 + bl * x25 + cg * x27 - bu * x29 - bk * x31 - cf * x33 + bv * x35 + bj * x37 + ce * x39 - bw * x41 - bi * x43 - cd * x45 + bx * x47 + bh * x49 + cc * x51 - by * x53 - bg * x55 - cb * x57 + bz * x59 + bf * x61 + ca * x63, + bq * x1 - ci * x3 - bl * x5 - bv * x7 + cd * x9 + bg * x11 + ca * x13 - by * x15 - bi * x17 - cf * x19 + bt * x21 + bn * x23 + ck * x25 - bo * x27 - bs * x29 + cg * x31 + bj * x33 + bx * x35 - cb * x37 - bf * x39 - cc * x41 + bw * x43 + bk * x45 + ch * x47 - br * x49 - bp * x51 + cj * x53 + bm * x55 + bu * x57 - ce * x59 - bh * x61 - bz * x63, + br * x1 - cf * x3 - bg * x5 - cc * x7 + bu * x9 + bo * x11 - ci * x13 - bj * x15 - bz * x17 + bx * x19 + bl * x21 + ck * x23 + -bm * x25 - bw * x27 + ca * x29 + bi * x31 + ch * x33 - bp * x35 - bt * x37 + cd * x39 + bf * x41 + ce * x43 - bs * x45 - bq * x47 + cg * x49 + bh * x51 + cb * x53 - bv * x55 - bn * x57 + cj * x59 + bk * x61 + by * x63, + bs * x1 - cc * x3 - bi * x5 - cj * x7 + bl * x9 + bz * x11 - bv * x13 - bp * x15 + cf * x17 + bf * x19 + cg * x21 - bo * x23 + -bw * x25 + by * x27 + bm * x29 - ci * x31 - bh * x33 - cd * x35 + br * x37 + bt * x39 - cb * x41 - bj * x43 - ck * x45 + bk * x47 + ca * x49 - bu * x51 - bq * x53 + ce * x55 + bg * x57 + ch * x59 - bn * x61 - bx * x63, + bt * x1 - bz * x3 - bn * x5 + cf * x7 + bh * x9 + ck * x11 - bi * x13 - ce * x15 + bo * x17 + by * x19 - bu * x21 - bs * x23 + ca * x25 + bm * x27 - cg * x29 - bg * x31 - cj * x33 + bj * x35 + cd * x37 - bp * x39 - bx * x41 + bv * x43 + br * x45 - cb * x47 - bl * x49 + ch * x51 + bf * x53 + ci * x55 - bk * x57 - cc * x59 + bq * x61 + bw * x63, + bu * x1 - bw * x3 - bs * x5 + by * x7 + bq * x9 - ca * x11 - bo * x13 + cc * x15 + bm * x17 - ce * x19 - bk * x21 + cg * x23 + bi * x25 - ci * x27 - bg * x29 + ck * x31 + bf * x33 + cj * x35 - bh * x37 - ch * x39 + bj * x41 + cf * x43 - bl * x45 - cd * x47 + bn * x49 + cb * x51 - bp * x53 - bz * x55 + br * x57 + bx * x59 - bt * x61 - bv * x63, + bv * x1 - bt * x3 - bx * x5 + br * x7 + bz * x9 - bp * x11 - cb * x13 + bn * x15 + cd * x17 - bl * x19 - cf * x21 + bj * x23 + ch * x25 - bh * x27 - cj * x29 + bf * x31 - ck * x33 - bg * x35 + ci * x37 + bi * x39 - cg * x41 - bk * x43 + ce * x45 + bm * x47 - cc * x49 - bo * x51 + ca * x53 + bq * x55 - by * x57 - bs * x59 + bw * x61 + bu * x63, + bw * x1 - bq * x3 - cc * x5 + bk * x7 + ci * x9 - bf * x11 + ch * x13 + bl * x15 - cb * x17 - br * x19 + bv * x21 + bx * x23 + -bp * x25 - cd * x27 + bj * x29 + cj * x31 - bg * x33 + cg * x35 + bm * x37 - ca * x39 - bs * x41 + bu * x43 + by * x45 - bo * x47 - ce * x49 + bi * x51 + ck * x53 - bh * x55 + cf * x57 + bn * x59 - bz * x61 - bt * x63, + bx * x1 - bn * x3 - ch * x5 + bg * x7 - ce * x9 - bq * x11 + bu * x13 + ca * x15 - bk * x17 - ck * x19 + bj * x21 - cb * x23 + -bt * x25 + br * x27 + cd * x29 - bh * x31 + ci * x33 + bm * x35 - by * x37 - bw * x39 + bo * x41 + cg * x43 - bf * x45 + cf * x47 + bp * x49 - bv * x51 - bz * x53 + bl * x55 + cj * x57 - bi * x59 + cc * x61 + bs * x63, + by * x1 - bk * x3 + cj * x5 + bn * x7 - bv * x9 - cb * x11 + bh * x13 - cg * x15 - bq * x17 + bs * x19 + ce * x21 - bf * x23 + cd * x25 + bt * x27 - bp * x29 - ch * x31 + bi * x33 - ca * x35 - bw * x37 + bm * x39 + ck * x41 - bl * x43 + bx * x45 + bz * x47 - bj * x49 + ci * x51 + bo * x53 - bu * x55 - cc * x57 + bg * x59 - cf * x61 - br * x63, + bz * x1 - bh * x3 + ce * x5 + bu * x7 - bm * x9 + cj * x11 + bp * x13 - br * x15 - ch * x17 + bk * x19 - bw * x21 - cc * x23 + bf * x25 - cb * x27 - bx * x29 + bj * x31 - cg * x33 - bs * x35 + bo * x37 + ck * x39 - bn * x41 + bt * x43 + cf * x45 - bi * x47 + by * x49 + ca * x51 - bg * x53 + cd * x55 + bv * x57 - bl * x59 + ci * x61 + bq * x63, + ca * x1 - bf * x3 + bz * x5 + cb * x7 - bg * x9 + by * x11 + cc * x13 - bh * x15 + bx * x17 + cd * x19 - bi * x21 + bw * x23 + ce * x25 - bj * x27 + bv * x29 + cf * x31 - bk * x33 + bu * x35 + cg * x37 - bl * x39 + bt * x41 + ch * x43 - bm * x45 + bs * x47 + ci * x49 - bn * x51 + br * x53 + cj * x55 - bo * x57 + bq * x59 + ck * x61 - bp * x63, + cb * x1 - bi * x3 + bu * x5 + ci * x7 - bp * x9 + bn * x11 - cg * x13 - bw * x15 + bg * x17 - bz * x19 - cd * x21 + bk * x23 + -bs * x25 - ck * x27 + br * x29 - bl * x31 + ce * x33 + by * x35 - bf * x37 + bx * x39 + cf * x41 - bm * x43 + bq * x45 - cj * x47 - bt * x49 + bj * x51 - cc * x53 - ca * x55 + bh * x57 - bv * x59 - ch * x61 + bo * x63, + cc * x1 - bl * x3 + bp * x5 - cg * x7 - by * x9 + bh * x11 - bt * x13 + ck * x15 + bu * x17 - bg * x19 + bx * x21 + ch * x23 + -bq * x25 + bk * x27 - cb * x29 - cd * x31 + bm * x33 - bo * x35 + cf * x37 + bz * x39 - bi * x41 + bs * x43 - cj * x45 - bv * x47 + bf * x49 - bw * x51 - ci * x53 + br * x55 - bj * x57 + ca * x59 + ce * x61 - bn * x63, + cd * x1 - bo * x3 + bk * x5 - bz * x7 - ch * x9 + bs * x11 - bg * x13 + bv * x15 - ck * x17 - bw * x19 + bh * x21 - br * x23 + cg * x25 + ca * x27 - bl * x29 + bn * x31 - cc * x33 - ce * x35 + bp * x37 - bj * x39 + by * x41 + ci * x43 - bt * x45 + bf * x47 - bu * x49 + cj * x51 + bx * x53 - bi * x55 + bq * x57 - cf * x59 - cb * x61 + bm * x63, + ce * x1 - br * x3 + bf * x5 - bs * x7 + cf * x9 + cd * x11 - bq * x13 + bg * x15 - bt * x17 + cg * x19 + cc * x21 - bp * x23 + bh * x25 - bu * x27 + ch * x29 + cb * x31 - bo * x33 + bi * x35 - bv * x37 + ci * x39 + ca * x41 - bn * x43 + bj * x45 - bw * x47 + cj * x49 + bz * x51 - bm * x53 + bk * x55 - bx * x57 + ck * x59 + by * x61 - bl * x63, + cf * x1 - bu * x3 + bj * x5 - bl * x7 + bw * x9 - ch * x11 - cd * x13 + bs * x15 - bh * x17 + bn * x19 - by * x21 + cj * x23 + cb * x25 - bq * x27 + bf * x29 - bp * x31 + ca * x33 + ck * x35 - bz * x37 + bo * x39 - bg * x41 + br * x43 - cc * x45 - ci * x47 + bx * x49 - bm * x51 + bi * x53 - bt * x55 + ce * x57 + cg * x59 - bv * x61 + bk * x63, + cg * x1 - bx * x3 + bo * x5 - bf * x7 + bn * x9 - bw * x11 + cf * x13 + ch * x15 - by * x17 + bp * x19 - bg * x21 + bm * x23 + -bv * x25 + ce * x27 + ci * x29 - bz * x31 + bq * x33 - bh * x35 + bl * x37 - bu * x39 + cd * x41 + cj * x43 - ca * x45 + br * x47 - bi * x49 + bk * x51 - bt * x53 + cc * x55 + ck * x57 - cb * x59 + bs * x61 - bj * x63, + ch * x1 - ca * x3 + bt * x5 - bm * x7 + bf * x9 - bl * x11 + bs * x13 - bz * x15 + cg * x17 + ci * x19 - cb * x21 + bu * x23 + -bn * x25 + bg * x27 - bk * x29 + br * x31 - by * x33 + cf * x35 + cj * x37 - cc * x39 + bv * x41 - bo * x43 + bh * x45 - bj * x47 + bq * x49 - bx * x51 + ce * x53 + ck * x55 - cd * x57 + bw * x59 - bp * x61 + bi * x63, + ci * x1 - cd * x3 + by * x5 - bt * x7 + bo * x9 - bj * x11 + bf * x13 - bk * x15 + bp * x17 - bu * x19 + bz * x21 - ce * x23 + cj * x25 + ch * x27 - cc * x29 + bx * x31 - bs * x33 + bn * x35 - bi * x37 + bg * x39 - bl * x41 + bq * x43 - bv * x45 + ca * x47 - cf * x49 + ck * x51 + cg * x53 - cb * x55 + bw * x57 - br * x59 + bm * x61 - bh * x63, + cj * x1 - cg * x3 + cd * x5 - ca * x7 + bx * x9 - bu * x11 + br * x13 - bo * x15 + bl * x17 - bi * x19 + bf * x21 - bh * x23 + bk * x25 - bn * x27 + bq * x29 - bt * x31 + bw * x33 - bz * x35 + cc * x37 - cf * x39 + ci * x41 + ck * x43 - ch * x45 + ce * x47 - cb * x49 + by * x51 - bv * x53 + bs * x55 - bp * x57 + bm * x59 - bj * x61 + bg * x63, + ck * x1 - cj * x3 + ci * x5 - ch * x7 + cg * x9 - cf * x11 + ce * x13 - cd * x15 + cc * x17 - cb * x19 + ca * x21 - bz * x23 + by * x25 - bx * x27 + bw * x29 - bv * x31 + bu * x33 - bt * x35 + bs * x37 - br * x39 + bq * x41 - bp * x43 + bo * x45 - bn * x47 + bm * x49 - bl * x51 + bk * x53 - bj * x55 + bi * x57 - bh * x59 + bg * x61 - bf * x63, + }; + + out[0 * out_stride] = E[0 ] + O[0 ]; + out[1 * out_stride] = E[1 ] + O[1 ]; + out[2 * out_stride] = E[2 ] + O[2 ]; + out[3 * out_stride] = E[3 ] + O[3 ]; + out[4 * out_stride] = E[4 ] + O[4 ]; + out[5 * out_stride] = E[5 ] + O[5 ]; + out[6 * out_stride] = E[6 ] + O[6 ]; + out[7 * out_stride] = E[7 ] + O[7 ]; + out[8 * out_stride] = E[8 ] + O[8 ]; + out[9 * out_stride] = E[9 ] + O[9 ]; + out[10 * out_stride] = E[10] + O[10]; + out[11 * out_stride] = E[11] + O[11]; + out[12 * out_stride] = E[12] + O[12]; + out[13 * out_stride] = E[13] + O[13]; + out[14 * out_stride] = E[14] + O[14]; + out[15 * out_stride] = E[15] + O[15]; + out[16 * out_stride] = E[16] + O[16]; + out[17 * out_stride] = E[17] + O[17]; + out[18 * out_stride] = E[18] + O[18]; + out[19 * out_stride] = E[19] + O[19]; + out[20 * out_stride] = E[20] + O[20]; + out[21 * out_stride] = E[21] + O[21]; + out[22 * out_stride] = E[22] + O[22]; + out[23 * out_stride] = E[23] + O[23]; + out[24 * out_stride] = E[24] + O[24]; + out[25 * out_stride] = E[25] + O[25]; + out[26 * out_stride] = E[26] + O[26]; + out[27 * out_stride] = E[27] + O[27]; + out[28 * out_stride] = E[28] + O[28]; + out[29 * out_stride] = E[29] + O[29]; + out[30 * out_stride] = E[30] + O[30]; + out[31 * out_stride] = E[31] + O[31]; + out[32 * out_stride] = E[31] - O[31]; + out[33 * out_stride] = E[30] - O[30]; + out[34 * out_stride] = E[29] - O[29]; + out[35 * out_stride] = E[28] - O[28]; + out[36 * out_stride] = E[27] - O[27]; + out[37 * out_stride] = E[26] - O[26]; + out[38 * out_stride] = E[25] - O[25]; + out[39 * out_stride] = E[24] - O[24]; + out[40 * out_stride] = E[23] - O[23]; + out[41 * out_stride] = E[22] - O[22]; + out[42 * out_stride] = E[21] - O[21]; + out[43 * out_stride] = E[20] - O[20]; + out[44 * out_stride] = E[19] - O[19]; + out[45 * out_stride] = E[18] - O[18]; + out[46 * out_stride] = E[17] - O[17]; + out[47 * out_stride] = E[16] - O[16]; + out[48 * out_stride] = E[15] - O[15]; + out[49 * out_stride] = E[14] - O[14]; + out[50 * out_stride] = E[13] - O[13]; + out[51 * out_stride] = E[12] - O[12]; + out[52 * out_stride] = E[11] - O[11]; + out[53 * out_stride] = E[10] - O[10]; + out[54 * out_stride] = E[9] - O[9]; + out[55 * out_stride] = E[8] - O[8]; + out[56 * out_stride] = E[7] - O[7]; + out[57 * out_stride] = E[6] - O[6]; + out[58 * out_stride] = E[5] - O[5]; + out[59 * out_stride] = E[4] - O[4]; + out[60 * out_stride] = E[3] - O[3]; + out[61 * out_stride] = E[2] - O[2]; + out[62 * out_stride] = E[1] - O[1]; + out[63 * out_stride] = E[0] - O[0]; +}; + +static void matrix_mul(int *out, const ptrdiff_t out_stride, const int *in, const ptrdiff_t in_stride, const int8_t* matrix, const int size) +{ + for (int i = 0; i < size; i++) { + int o = 0; + + for (int j = 0; j < size; j++) + o += in[j * in_stride] * matrix[j * size]; + *out = o; + out += out_stride; + matrix++; + } +} + +static void inv_dct8(int *out, const ptrdiff_t out_stride, const int *in, const ptrdiff_t in_stride, const int8_t *matrix, const int size) +{ + matrix_mul(out, out_stride, in, in_stride, matrix, size); +} + +#define DEFINE_INV_DCT8_1D(S) \ +void ff_vvc_inv_dct8_ ## S(int *out, ptrdiff_t out_stride, const int *in, ptrdiff_t in_stride) \ +{ \ + inv_dct8(out, out_stride, in, in_stride, &ff_vvc_dct8_##S##x##S[0][0], S); \ +} + +DEFINE_INV_DCT8_1D( 4) +DEFINE_INV_DCT8_1D( 8) +DEFINE_INV_DCT8_1D(16) +DEFINE_INV_DCT8_1D(32) + +static void inv_dst7(int *out, const ptrdiff_t out_stride, const int *in, const ptrdiff_t in_stride, const int8_t* matrix, const int size) +{ + matrix_mul(out, out_stride, in, in_stride, matrix, size); +} + +#define DEFINE_INV_DST7_1D(S) \ +void ff_vvc_inv_dst7_ ## S(int *out, ptrdiff_t out_stride, const int *in, ptrdiff_t in_stride) \ +{ \ + inv_dst7(out, out_stride, in, in_stride, &ff_vvc_dst7_##S##x##S[0][0], S); \ +} + +DEFINE_INV_DST7_1D( 4) +DEFINE_INV_DST7_1D( 8) +DEFINE_INV_DST7_1D(16) +DEFINE_INV_DST7_1D(32) + +void ff_vvc_inv_lfnst_1d(int *v, const int *u, int no_zero_size, int n_tr_s, + int pred_mode_intra, int lfnst_idx, int log2_transform_range) +{ + int lfnst_tr_set_idx = pred_mode_intra < 0 ? 1 : ff_vvc_lfnst_tr_set_index[pred_mode_intra]; + const int8_t *tr_mat = n_tr_s > 16 ? ff_vvc_lfnst_8x8[lfnst_tr_set_idx][lfnst_idx-1][0] : ff_vvc_lfnst_4x4[lfnst_tr_set_idx][lfnst_idx - 1][0]; + + for (int j = 0; j < n_tr_s; j++, tr_mat++) { + int t = 0; + + for (int i = 0; i < no_zero_size; i++) + t += u[i] * tr_mat[i * n_tr_s]; + v[j] = av_clip_intp2((t + 64) >> 7 , log2_transform_range); + } +} diff --git a/libavcodec/vvc/vvc_itx_1d.h b/libavcodec/vvc/vvc_itx_1d.h new file mode 100644 index 00000000000..313bc7fc5de --- /dev/null +++ b/libavcodec/vvc/vvc_itx_1d.h @@ -0,0 +1,52 @@ +/* + * VVC 1D transform + * + * Copyright (C) 2023 Nuo Mi + * + * 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_VVC_ITX_1D_H +#define AVCODEC_VVC_ITX_1D_H + +#include +#include + +#define vvc_itx_1d_fn(name) \ + void (name)(int *out, ptrdiff_t out_stride, const int *in, ptrdiff_t in_stride) +typedef vvc_itx_1d_fn(*vvc_itx_1d_fn); + +vvc_itx_1d_fn(ff_vvc_inv_dct2_2); +vvc_itx_1d_fn(ff_vvc_inv_dct2_4); +vvc_itx_1d_fn(ff_vvc_inv_dct2_8); +vvc_itx_1d_fn(ff_vvc_inv_dct2_16); +vvc_itx_1d_fn(ff_vvc_inv_dct2_32); +vvc_itx_1d_fn(ff_vvc_inv_dct2_64); +vvc_itx_1d_fn(ff_vvc_inv_dst7_4); +vvc_itx_1d_fn(ff_vvc_inv_dst7_8); +vvc_itx_1d_fn(ff_vvc_inv_dst7_16); +vvc_itx_1d_fn(ff_vvc_inv_dst7_32); +vvc_itx_1d_fn(ff_vvc_inv_dct8_4); +vvc_itx_1d_fn(ff_vvc_inv_dct8_8); +vvc_itx_1d_fn(ff_vvc_inv_dct8_16); +vvc_itx_1d_fn(ff_vvc_inv_dct8_32); + + +void ff_vvc_inv_lfnst_1d(int *v, const int *u, int no_zero_size, int n_tr_s, + int pred_mode_intra, int lfnst_idx, int log2_transform_range); + +#endif // AVCODEC_VVC_ITX_1D_H diff --git a/libavcodec/vvc/vvc_mvs.c b/libavcodec/vvc/vvc_mvs.c new file mode 100644 index 00000000000..e3ab4e45710 --- /dev/null +++ b/libavcodec/vvc/vvc_mvs.c @@ -0,0 +1,1806 @@ +/* + * VVC motion vector decoder + * + * Copyright (C) 2023 Nuo Mi + * Copyright (C) 2022 Xu Mu + * 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 "vvc_ctu.h" +#include "vvc_data.h" +#include "vvc_refs.h" +#include "vvc_mvs.h" + +#define IS_SAME_MV(a, b) (AV_RN64A(a) == AV_RN64A(b)) + +//check if the two luma locations belong to the same motion estimation region +static av_always_inline int is_same_mer(const VVCFrameContext *fc, const int xN, const int yN, const int xP, const int yP) +{ + const uint8_t plevel = fc->ps.sps->log2_parallel_merge_level; + + return xN >> plevel == xP >> plevel && + yN >> plevel == yP >> plevel; +} + +//return true if we have same mvs and ref_idxs +static av_always_inline int compare_mv_ref_idx(const MvField *n, const MvField *o) +{ + if (!o || n->pred_flag != o->pred_flag) + return 0; + for (int i = 0; i < 2; i++) { + PredFlag mask = i + 1; + if (n->pred_flag & mask) { + const int same_ref_idx = n->ref_idx[i] == o->ref_idx[i]; + const int same_mv = IS_SAME_MV(n->mv + i, o->mv + i); + if (!same_ref_idx || !same_mv) + return 0; + } + } + return 1; +} + +// 8.5.2.15 Temporal motion buffer compression process for collocated motion vectors +static av_always_inline void mv_compression(Mv *motion) +{ + int mv[2] = {motion->x, motion->y}; + for (int i = 0; i < 2; i++) { + const int s = mv[i] >> 17; + const int f = av_log2((mv[i] ^ s) | 31) - 4; + const int mask = (-1 << f) >> 1; + const int round = (1 << f) >> 2; + mv[i] = (mv[i] + round) & mask; + } + motion->x = mv[0]; + motion->y = mv[1]; +} + +void ff_vvc_mv_scale(Mv *dst, const Mv *src, int td, int tb) +{ + int tx, scale_factor; + + td = av_clip_int8(td); + tb = av_clip_int8(tb); + tx = (0x4000 + (abs(td) >> 1)) / td; + scale_factor = av_clip_intp2((tb * tx + 32) >> 6, 12); + dst->x = av_clip_intp2((scale_factor * src->x + 127 + + (scale_factor * src->x < 0)) >> 8, 17); + dst->y = av_clip_intp2((scale_factor * src->y + 127 + + (scale_factor * src->y < 0)) >> 8, 17); +} + +//part of 8.5.2.12 Derivation process for collocated motion vectors +static int check_mvset(Mv *mvLXCol, Mv *mvCol, + int colPic, int poc, + const RefPicList *refPicList, int X, int refIdxLx, + const RefPicList *refPicList_col, int listCol, int refidxCol) +{ + int cur_lt = refPicList[X].isLongTerm[refIdxLx]; + int col_lt = refPicList_col[listCol].isLongTerm[refidxCol]; + int col_poc_diff, cur_poc_diff; + + if (cur_lt != col_lt) { + mvLXCol->x = 0; + mvLXCol->y = 0; + return 0; + } + + col_poc_diff = colPic - refPicList_col[listCol].list[refidxCol]; + cur_poc_diff = poc - refPicList[X].list[refIdxLx]; + + mv_compression(mvCol); + if (cur_lt || col_poc_diff == cur_poc_diff) { + mvLXCol->x = av_clip_intp2(mvCol->x, 17); + mvLXCol->y = av_clip_intp2(mvCol->y, 17); + } else { + ff_vvc_mv_scale(mvLXCol, mvCol, col_poc_diff, cur_poc_diff); + } + return 1; +} + +#define CHECK_MVSET(l) \ + check_mvset(mvLXCol, temp_col.mv + l, \ + colPic, fc->ps.ph.poc, \ + refPicList, X, refIdxLx, \ + refPicList_col, L ## l, temp_col.ref_idx[l]) + +//derive NoBackwardPredFlag +int ff_vvc_no_backward_pred_flag(const VVCLocalContext *lc) +{ + int check_diffpicount = 0; + int i, j; + const RefPicList *rpl = lc->sc->rpl; + + for (j = 0; j < 2; j++) { + for (i = 0; i < rpl[j].nb_refs; i++) { + if (rpl[j].list[i] > lc->fc->ps.ph.poc) { + check_diffpicount++; + break; + } + } + } + return !check_diffpicount; +} + +//8.5.2.12 Derivation process for collocated motion vectors +static int derive_temporal_colocated_mvs(const VVCLocalContext *lc, MvField temp_col, + int refIdxLx, Mv *mvLXCol, int X, + int colPic, const RefPicList *refPicList_col, int sb_flag) +{ + const VVCFrameContext *fc = lc->fc; + const SliceContext *sc = lc->sc; + RefPicList* refPicList = sc->rpl; + + if (temp_col.pred_flag == PF_INTRA) + return 0; + + if (sb_flag){ + if (X == 0) { + if (temp_col.pred_flag & PF_L0) + return CHECK_MVSET(0); + else if (ff_vvc_no_backward_pred_flag(lc) && (temp_col.pred_flag & PF_L1)) + return CHECK_MVSET(1); + } else { + if (temp_col.pred_flag & PF_L1) + return CHECK_MVSET(1); + else if (ff_vvc_no_backward_pred_flag(lc) && (temp_col.pred_flag & PF_L0)) + return CHECK_MVSET(0); + } + } else { + if (!(temp_col.pred_flag & PF_L0)) + return CHECK_MVSET(1); + else if (temp_col.pred_flag == PF_L0) + return CHECK_MVSET(0); + else if (temp_col.pred_flag == PF_BI) { + if (ff_vvc_no_backward_pred_flag(lc)) { + if (X == 0) + return CHECK_MVSET(0); + else + return CHECK_MVSET(1); + } else { + if (!lc->sc->sh.r->sh_collocated_from_l0_flag) + return CHECK_MVSET(0); + else + return CHECK_MVSET(1); + } + } + } + + return 0; +} + +#define TAB_MVF(x, y) \ + tab_mvf[((y) >> MIN_PU_LOG2) * min_pu_width + ((x) >> MIN_PU_LOG2)] + +#define TAB_MVF_PU(v) \ + TAB_MVF(x ## v, y ## v) + +#define TAB_CP_MV(lx, x, y) \ + fc->tab.cp_mv[lx][((((y) >> min_cb_log2_size) * min_cb_width + ((x) >> min_cb_log2_size)) ) * MAX_CONTROL_POINTS] + + +#define DERIVE_TEMPORAL_COLOCATED_MVS(sb_flag) \ + derive_temporal_colocated_mvs(lc, temp_col, \ + refIdxLx, mvLXCol, X, colPic, \ + ff_vvc_get_ref_list(fc, ref, x, y), sb_flag) + +//8.5.2.11 Derivation process for temporal luma motion vector prediction +static int temporal_luma_motion_vector(const VVCLocalContext *lc, + const int refIdxLx, Mv *mvLXCol, const int X, int check_center, int sb_flag) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const CodingUnit *cu = lc->cu; + int x, y, colPic, availableFlagLXCol = 0; + int min_pu_width = fc->ps.pps->min_pu_width; + VVCFrame *ref = fc->ref->collocated_ref; + MvField *tab_mvf; + MvField temp_col; + + if (!ref) { + memset(mvLXCol, 0, sizeof(*mvLXCol)); + return 0; + } + + if (!fc->ps.ph.r->ph_temporal_mvp_enabled_flag || (cu->cb_width * cu->cb_height <= 32)) + return 0; + + tab_mvf = ref->tab_dmvr_mvf; + colPic = ref->poc; + + //bottom right collocated motion vector + x = cu->x0 + cu->cb_width; + y = cu->y0 + cu->cb_height; + + if (tab_mvf && + (cu->y0 >> sps->ctb_log2_size_y) == (y >> sps->ctb_log2_size_y) && + y < fc->ps.sps->height && + x < fc->ps.sps->width) { + x &= ~7; + y &= ~7; + temp_col = TAB_MVF(x, y); + availableFlagLXCol = DERIVE_TEMPORAL_COLOCATED_MVS(sb_flag); + } + if (check_center) { + // derive center collocated motion vector + if (tab_mvf && !availableFlagLXCol) { + x = cu->x0 + (cu->cb_width >> 1); + y = cu->y0 + (cu->cb_height >> 1); + x &= ~7; + y &= ~7; + temp_col = TAB_MVF(x, y); + availableFlagLXCol = DERIVE_TEMPORAL_COLOCATED_MVS(sb_flag); + } + } + return availableFlagLXCol; +} + +void ff_vvc_set_mvf(const VVCLocalContext *lc, const int x0, const int y0, const int w, const int h, const MvField *mvf) +{ + const VVCFrameContext *fc = lc->fc; + MvField *tab_mvf = fc->tab.mvf; + const int min_pu_width = fc->ps.pps->min_pu_width; + const int min_pu_size = 1 << MIN_PU_LOG2; + for (int dy = 0; dy < h; dy += min_pu_size) { + for (int dx = 0; dx < w; dx += min_pu_size) { + const int x = x0 + dx; + const int y = y0 + dy; + TAB_MVF(x, y) = *mvf; + } + } +} + +void ff_vvc_set_intra_mvf(const VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + MvField *tab_mvf = fc->tab.mvf; + const int min_pu_width = fc->ps.pps->min_pu_width; + const int min_pu_size = 1 << MIN_PU_LOG2; + for (int dy = 0; dy < cu->cb_height; dy += min_pu_size) { + for (int dx = 0; dx < cu->cb_width; dx += min_pu_size) { + const int x = cu->x0 + dx; + const int y = cu->y0 + dy; + TAB_MVF(x, y).pred_flag = PF_INTRA; + } + } +} + +//cbProfFlagLX from 8.5.5.9 Derivation process for motion vector arrays from affine control point motion vectors +static int derive_cb_prof_flag_lx(const VVCLocalContext *lc, const PredictionUnit* pu, int lx, int is_fallback) +{ + const MotionInfo* mi = &pu->mi; + const Mv* cp_mv = &mi->mv[lx][0]; + if (lc->fc->ps.ph.r->ph_prof_disabled_flag || is_fallback) + return 0; + if (mi->motion_model_idc == MOTION_4_PARAMS_AFFINE) { + if (IS_SAME_MV(cp_mv, cp_mv + 1)) + return 0; + } + if (mi->motion_model_idc == MOTION_6_PARAMS_AFFINE) { + if (IS_SAME_MV(cp_mv, cp_mv + 1) && IS_SAME_MV(cp_mv, cp_mv + 2)) + return 0; + } + //fixme: RprConstraintsActiveFlag + return 1; +} + +typedef struct SubblockParams { + int d_hor_x; + int d_ver_x; + int d_hor_y; + int d_ver_y; + int mv_scale_hor; + int mv_scale_ver; + int is_fallback; + + int cb_width; + int cb_height; +} SubblockParams; + +static int is_fallback_mode(const SubblockParams *sp, const PredFlag pred_flag) +{ + const int a = 4 * (2048 + sp->d_hor_x); + const int b = 4 * sp->d_hor_y; + const int c = 4 * (2048 + sp->d_ver_y); + const int d = 4 * sp->d_ver_x; + if (pred_flag == PF_BI) { + const int max_w4 = FFMAX(0, FFMAX(a, FFMAX(b, a + b))); + const int min_w4 = FFMIN(0, FFMIN(a, FFMIN(b, a + b))); + const int max_h4 = FFMAX(0, FFMAX(c, FFMAX(d, c + d))); + const int min_h4 = FFMIN(0, FFMIN(c, FFMIN(d, c + d))); + const int bx_wx4 = ((max_w4 - min_w4) >> 11) + 9; + const int bx_hx4 = ((max_h4 - min_h4) >> 11) + 9; + return bx_wx4 * bx_hx4 > 225; + } else { + const int bx_wxh = (FFABS(a) >> 11) + 9; + const int bx_hxh = (FFABS(d) >> 11) + 9; + const int bx_wxv = (FFABS(b) >> 11) + 9; + const int bx_hxv = (FFABS(c) >> 11) + 9; + if (bx_wxh * bx_hxh <= 165 && bx_wxv * bx_hxv <= 165) + return 0; + } + return 1; + +} + +static void init_subblock_params(SubblockParams *sp, const MotionInfo* mi, + const int cb_width, const int cb_height, const int lx) +{ + const int log2_cbw = av_log2(cb_width); + const int log2_cbh = av_log2(cb_height); + const Mv* cp_mv = mi->mv[lx]; + const int num_cp_mv = mi->motion_model_idc + 1; + sp->d_hor_x = (cp_mv[1].x - cp_mv[0].x) << (MAX_CU_DEPTH - log2_cbw); + sp->d_ver_x = (cp_mv[1].y - cp_mv[0].y) << (MAX_CU_DEPTH - log2_cbw); + if (num_cp_mv == 3) { + sp->d_hor_y = (cp_mv[2].x - cp_mv[0].x) << (MAX_CU_DEPTH - log2_cbh); + sp->d_ver_y = (cp_mv[2].y - cp_mv[0].y) << (MAX_CU_DEPTH - log2_cbh); + } else { + sp->d_hor_y = -sp->d_ver_x; + sp->d_ver_y = sp->d_hor_x; + } + sp->mv_scale_hor = (cp_mv[0].x) << MAX_CU_DEPTH; + sp->mv_scale_ver = (cp_mv[0].y) << MAX_CU_DEPTH; + sp->cb_width = cb_width; + sp->cb_height = cb_height; + sp->is_fallback = is_fallback_mode(sp, mi->pred_flag); +} + +static void derive_subblock_diff_mvs(const VVCLocalContext *lc, PredictionUnit* pu, const SubblockParams* sp, const int lx) +{ + pu->cb_prof_flag[lx] = derive_cb_prof_flag_lx(lc, pu, lx, sp->is_fallback); + if (pu->cb_prof_flag[lx]) { + const int dmv_limit = 1 << 5; + const int pos_offset_x = 6 * (sp->d_hor_x + sp->d_hor_y); + const int pos_offset_y = 6 * (sp->d_ver_x + sp->d_ver_y); + for (int x = 0; x < AFFINE_MIN_BLOCK_SIZE; x++) { + for (int y = 0; y < AFFINE_MIN_BLOCK_SIZE; y++) { + Mv diff; + diff.x = x * (sp->d_hor_x << 2) + y * (sp->d_hor_y << 2) - pos_offset_x; + diff.y = x * (sp->d_ver_x << 2) + y * (sp->d_ver_y << 2) - pos_offset_y; + ff_vvc_round_mv(&diff, 0, 8); + pu->diff_mv_x[lx][AFFINE_MIN_BLOCK_SIZE * y + x] = av_clip(diff.x, -dmv_limit + 1, dmv_limit - 1); + pu->diff_mv_y[lx][AFFINE_MIN_BLOCK_SIZE * y + x] = av_clip(diff.y, -dmv_limit + 1, dmv_limit - 1); + } + } + } +} + +static void store_cp_mv(const VVCLocalContext *lc, const MotionInfo *mi, const int lx) +{ + VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const int log2_min_cb_size = fc->ps.sps->min_cb_log2_size_y; + const int min_cb_size = fc->ps.sps->min_cb_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int num_cp_mv = mi->motion_model_idc + 1; + + for (int dy = 0; dy < cu->cb_height; dy += min_cb_size) { + for (int dx = 0; dx < cu->cb_width; dx += min_cb_size) { + const int x_cb = (cu->x0 + dx) >> log2_min_cb_size; + const int y_cb = (cu->y0 + dy) >> log2_min_cb_size; + const int offset = (y_cb * min_cb_width + x_cb) * MAX_CONTROL_POINTS; + + memcpy(&fc->tab.cp_mv[lx][offset], mi->mv[lx], sizeof(Mv) * num_cp_mv); + SAMPLE_CTB(fc->tab.mmi, x_cb, y_cb) = mi->motion_model_idc; + } + } +} + +//8.5.5.9 Derivation process for motion vector arrays from affine control point motion vectors +void ff_vvc_store_sb_mvs(const VVCLocalContext *lc, PredictionUnit *pu) +{ + const CodingUnit *cu = lc->cu; + const MotionInfo *mi = &pu->mi; + const int sbw = cu->cb_width / mi->num_sb_x; + const int sbh = cu->cb_height / mi->num_sb_y; + SubblockParams params[2]; + MvField mvf; + + mvf.pred_flag = mi->pred_flag; + mvf.bcw_idx = mi->bcw_idx; + mvf.hpel_if_idx = mi->hpel_if_idx; + mvf.ciip_flag = 0; + for (int i = 0; i < 2; i++) { + const PredFlag mask = i + 1; + if (mi->pred_flag & mask) { + store_cp_mv(lc, mi, i); + init_subblock_params(params + i, mi, cu->cb_width, cu->cb_height, i); + derive_subblock_diff_mvs(lc, pu, params + i, i); + mvf.ref_idx[i] = mi->ref_idx[i]; + } + } + + for (int sby = 0; sby < mi->num_sb_y; sby++) { + for (int sbx = 0; sbx < mi->num_sb_x; sbx++) { + const int x0 = cu->x0 + sbx * sbw; + const int y0 = cu->y0 + sby * sbh; + for (int i = 0; i < 2; i++) { + const PredFlag mask = i + 1; + if (mi->pred_flag & mask) { + const SubblockParams* sp = params + i; + const int x_pos_cb = sp->is_fallback ? (cu->cb_width >> 1) : (2 + (sbx << MIN_CU_LOG2)); + const int y_pos_cb = sp->is_fallback ? (cu->cb_height >> 1) : (2 + (sby << MIN_CU_LOG2)); + Mv *mv = mvf.mv + i; + + mv->x = sp->mv_scale_hor + sp->d_hor_x * x_pos_cb + sp->d_hor_y * y_pos_cb; + mv->y = sp->mv_scale_ver + sp->d_ver_x * x_pos_cb + sp->d_ver_y * y_pos_cb; + ff_vvc_round_mv(mv, 0, MAX_CU_DEPTH); + ff_vvc_clip_mv(mv); + } + } + ff_vvc_set_mvf(lc, x0, y0, sbw, sbh, &mvf); + } + } +} + +void ff_vvc_store_gpm_mvf(const VVCLocalContext *lc, const PredictionUnit *pu) +{ + const CodingUnit *cu = lc->cu; + const int angle_idx = ff_vvc_gpm_angle_idx[pu->gpm_partition_idx]; + const int distance_idx = ff_vvc_gpm_distance_idx[pu->gpm_partition_idx]; + const int displacement_x = ff_vvc_gpm_distance_lut[angle_idx]; + const int displacement_y = ff_vvc_gpm_distance_lut[(angle_idx + 8) % 32]; + const int is_flip = angle_idx >= 13 &&angle_idx <= 27; + const int shift_hor = (angle_idx % 16 == 8 || (angle_idx % 16 && cu->cb_height >= cu->cb_width)) ? 0 : 1; + const int sign = angle_idx < 16 ? 1 : -1; + const int block_size = 4; + int offset_x = (-cu->cb_width) >> 1; + int offset_y = (-cu->cb_height) >> 1; + + if (!shift_hor) + offset_y += sign * ((distance_idx * cu->cb_height) >> 3); + else + offset_x += sign * ((distance_idx * cu->cb_width) >> 3); + + for (int y = 0; y < cu->cb_height; y += block_size) { + for (int x = 0; x < cu->cb_width; x += block_size) { + const int motion_idx = (((x + offset_x) << 1) + 5) * displacement_x + + (((y + offset_y) << 1) + 5) * displacement_y; + const int s_type = FFABS(motion_idx) < 32 ? 2 : (motion_idx <= 0 ? (1 - is_flip) : is_flip); + const int pred_flag = pu->gpm_mv[0].pred_flag | pu->gpm_mv[1].pred_flag; + const int x0 = cu->x0 + x; + const int y0 = cu->y0 + y; + + if (!s_type) + ff_vvc_set_mvf(lc, x0, y0, block_size, block_size, pu->gpm_mv + 0); + else if (s_type == 1 || (s_type == 2 && pred_flag != PF_BI)) + ff_vvc_set_mvf(lc, x0, y0, block_size, block_size, pu->gpm_mv + 1); + else { + MvField mvf = pu->gpm_mv[0]; + const MvField *mv1 = &pu->gpm_mv[1]; + const int lx = mv1->pred_flag - PF_L0; + mvf.pred_flag = PF_BI; + mvf.ref_idx[lx] = mv1->ref_idx[lx]; + mvf.mv[lx] = mv1->mv[lx]; + ff_vvc_set_mvf(lc, x0, y0, block_size, block_size, &mvf); + } + } + } + +} + +void ff_vvc_store_mvf(const VVCLocalContext *lc, const MvField *mvf) +{ + const CodingUnit *cu = lc->cu; + ff_vvc_set_mvf(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height, mvf); +} + +void ff_vvc_store_mv(const VVCLocalContext *lc, const MotionInfo *mi) +{ + const CodingUnit *cu = lc->cu; + MvField mvf; + + mvf.hpel_if_idx = mi->hpel_if_idx; + mvf.bcw_idx = mi->bcw_idx; + mvf.pred_flag = mi->pred_flag; + mvf.ciip_flag = 0; + + for (int i = 0; i < 2; i++) { + const PredFlag mask = i + 1; + if (mvf.pred_flag & mask) { + mvf.mv[i] = mi->mv[i][0]; + mvf.ref_idx[i] = mi->ref_idx[i]; + } + } + ff_vvc_set_mvf(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height, &mvf); +} + +typedef enum NeighbourIdx { + A0, + A1, + A2, + B0, + B1, + B2, + B3, + NUM_NBS, + NB_IDX_NONE = NUM_NBS, +} NeighbourIdx; + +typedef struct Neighbour { + int x; + int y; + + int checked; + int available; +} Neighbour; + +typedef struct NeighbourContext { + Neighbour neighbours[NUM_NBS]; + const VVCLocalContext *lc; +} NeighbourContext; + +static int is_a0_available(const VVCLocalContext *lc, const CodingUnit *cu) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int x0b = av_mod_uintp2(cu->x0, sps->ctb_log2_size_y); + int cand_bottom_left; + + if (!x0b && !lc->ctb_left_flag) { + cand_bottom_left = 0; + } else { + const int log2_min_cb_size = sps->min_cb_log2_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int x = (cu->x0 - 1) >> log2_min_cb_size; + const int y = (cu->y0 + cu->cb_height) >> log2_min_cb_size; + const int max_y = FFMIN(fc->ps.pps->height, ((cu->y0 >> sps->ctb_log2_size_y) + 1) << sps->ctb_log2_size_y); + if (cu->y0 + cu->cb_height >= max_y) + cand_bottom_left = 0; + else + cand_bottom_left = SAMPLE_CTB(fc->tab.cb_width[0], x, y) != 0; + } + return cand_bottom_left; +} + +static void init_neighbour_context(NeighbourContext *ctx, const VVCLocalContext *lc) +{ + const CodingUnit *cu = lc->cu; + const NeighbourAvailable *na = &lc->na; + const int x0 = cu->x0; + const int y0 = cu->y0; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + const int a0_available = is_a0_available(lc, cu); + + Neighbour neighbours[NUM_NBS] = { + { x0 - 1, y0 + cb_height, !a0_available }, //A0 + { x0 - 1, y0 + cb_height - 1, !na->cand_left }, //A1 + { x0 - 1, y0, !na->cand_left }, //A2 + { x0 + cb_width, y0 - 1, !na->cand_up_right }, //B0 + { x0 + cb_width - 1, y0 - 1, !na->cand_up }, //B1 + { x0 - 1, y0 - 1, !na->cand_up_left }, //B2 + { x0, y0 - 1, !na->cand_up }, //B3 + }; + + memcpy(ctx->neighbours, neighbours, sizeof(neighbours)); + ctx->lc = lc; +} + +static int check_available(Neighbour *n, const VVCLocalContext *lc, const int is_mvp) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const CodingUnit *cu = lc->cu; + const MvField *tab_mvf = fc->tab.mvf; + const int min_pu_width = fc->ps.pps->min_pu_width; + + if (!n->checked) { + n->checked = 1; + n->available = !sps->r->sps_entropy_coding_sync_enabled_flag || ((n->x >> sps->ctb_log2_size_y) <= (cu->x0 >> sps->ctb_log2_size_y)); + n->available &= TAB_MVF(n->x, n->y).pred_flag != PF_INTRA; + if (!is_mvp) + n->available &= !is_same_mer(fc, n->x, n->y, cu->x0, cu->y0); + } + return n->available; +} + +static const MvField *mv_merge_candidate(const VVCLocalContext *lc, const int x_cand, const int y_cand) +{ + const VVCFrameContext *fc = lc->fc; + const int min_pu_width = fc->ps.pps->min_pu_width; + const MvField* tab_mvf = fc->tab.mvf; + const MvField *mvf = &TAB_MVF(x_cand, y_cand); + + return mvf; +} + +static const MvField* mv_merge_from_nb(NeighbourContext *ctx, const NeighbourIdx nb) +{ + const VVCLocalContext *lc = ctx->lc; + const int is_mvp = 0; + Neighbour *n = &ctx->neighbours[nb]; + + if (check_available(n, lc, is_mvp)) + return mv_merge_candidate(lc, n->x, n->y); + return 0; +} +#define MV_MERGE_FROM_NB(nb) mv_merge_from_nb(&nctx, nb) + +//8.5.2.3 Derivation process for spatial merging candidates +static int mv_merge_spatial_candidates(const VVCLocalContext *lc, const int merge_idx, + const MvField **nb_list, MvField *cand_list, int *nb_merge_cand) +{ + const MvField *cand; + int num_cands = 0; + NeighbourContext nctx; + + static NeighbourIdx nbs[][2] = { + {B1, NB_IDX_NONE }, + {A1, B1 }, + {B0, B1 }, + {A0, A1 }, + }; + + init_neighbour_context(&nctx, lc); + for (int i = 0; i < FF_ARRAY_ELEMS(nbs); i++) { + NeighbourIdx nb = nbs[i][0]; + NeighbourIdx old = nbs[i][1]; + cand = nb_list[nb] = MV_MERGE_FROM_NB(nb); + if (cand && !compare_mv_ref_idx(cand, nb_list[old])) { + cand_list[num_cands] = *cand; + if (merge_idx == num_cands) + return 1; + num_cands++; + } + } + if (num_cands != 4) { + cand = MV_MERGE_FROM_NB(B2); + if (cand && !compare_mv_ref_idx(cand, nb_list[A1]) + && !compare_mv_ref_idx(cand, nb_list[B1])) { + cand_list[num_cands] = *cand; + if (merge_idx == num_cands) + return 1; + num_cands++; + } + } + *nb_merge_cand = num_cands; + return 0; +} + +static int mv_merge_temporal_candidate(const VVCLocalContext *lc, MvField *cand) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + + memset(cand, 0, sizeof(*cand)); + if (fc->ps.ph.r->ph_temporal_mvp_enabled_flag && (cu->cb_width * cu->cb_height > 32)) { + int available_l0 = temporal_luma_motion_vector(lc, 0, cand->mv + 0, 0, 1, 0); + int available_l1 = IS_B(lc->sc->sh.r) ? + temporal_luma_motion_vector(lc, 0, cand->mv + 1, 1, 1, 0) : 0; + cand->pred_flag = available_l0 + (available_l1 << 1); + } + + return cand->pred_flag; +} + +//8.5.2.6 Derivation process for history-based merging candidates +static int mv_merge_history_candidates(const VVCLocalContext *lc, const int merge_idx, + const MvField **nb_list, MvField *cand_list, int *num_cands) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const EntryPoint* ep = lc->ep; + for (int i = 1; i <= ep->num_hmvp && (*num_cands < sps->max_num_merge_cand - 1); i++) { + const MvField *h = &ep->hmvp[ep->num_hmvp - i]; + const int same_motion = i <= 2 && (compare_mv_ref_idx(h, nb_list[A1]) || compare_mv_ref_idx(h, nb_list[B1])); + if (!same_motion) { + cand_list[*num_cands] = *h; + if (merge_idx == *num_cands) + return 1; + (*num_cands)++; + } + } + return 0; +} + +//8.5.2.4 Derivation process for pairwise average merging candidate +static int mv_merge_pairwise_candidate(MvField *cand_list, const int num_cands, const int is_b) +{ + if (num_cands > 1) { + const int num_ref_rists = is_b ? 2 : 1; + const MvField* p0 = cand_list + 0; + const MvField* p1 = cand_list + 1; + MvField* cand = cand_list + num_cands; + + cand->pred_flag = 0; + for (int i = 0; i < num_ref_rists; i++) { + PredFlag mask = i + 1; + if (p0->pred_flag & mask) { + cand->pred_flag |= mask; + cand->ref_idx[i] = p0->ref_idx[i]; + if (p1->pred_flag & mask) { + Mv *mv = cand->mv + i; + mv->x = p0->mv[i].x + p1->mv[i].x; + mv->y = p0->mv[i].y + p1->mv[i].y; + ff_vvc_round_mv(mv, 0, 1); + } else { + cand->mv[i] = p0->mv[i]; + } + } else if (p1->pred_flag & mask) { + cand->pred_flag |= mask; + cand->mv[i] = p1->mv[i]; + cand->ref_idx[i] = p1->ref_idx[i]; + } + } + if (cand->pred_flag) { + cand->hpel_if_idx = p0->hpel_if_idx == p1->hpel_if_idx ? p0->hpel_if_idx : 0; + cand->bcw_idx = 0; + cand->ciip_flag = 0; + return 1; + } + } + return 0; +} + +//8.5.2.5 Derivation process for zero motion vector merging candidates +static void mv_merge_zero_motion_candidate(const VVCLocalContext *lc, const int merge_idx, + MvField *cand_list, int num_cands) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int num_ref_idx = IS_P(rsh) ? + rsh->num_ref_idx_active[L0] : FFMIN(rsh->num_ref_idx_active[L0], rsh->num_ref_idx_active[L1]); + int zero_idx = 0; + + while (num_cands < sps->max_num_merge_cand) { + MvField *cand = cand_list + num_cands; + + cand->pred_flag = PF_L0 + (IS_B(rsh) << 1); + AV_ZERO64(cand->mv + 0); + AV_ZERO64(cand->mv + 1); + cand->ref_idx[0] = zero_idx < num_ref_idx ? zero_idx : 0; + cand->ref_idx[1] = zero_idx < num_ref_idx ? zero_idx : 0; + cand->bcw_idx = 0; + cand->hpel_if_idx = 0; + if (merge_idx == num_cands) + return; + num_cands++; + zero_idx++; + } +} + +static void mv_merge_mode(const VVCLocalContext *lc, const int merge_idx, MvField *cand_list) +{ + int num_cands = 0; + const MvField *nb_list[NUM_NBS + 1] = { NULL }; + + if (mv_merge_spatial_candidates(lc, merge_idx, nb_list, cand_list, &num_cands)) + return; + + if (mv_merge_temporal_candidate(lc, &cand_list[num_cands])) { + if (merge_idx == num_cands) + return; + num_cands++; + } + + if (mv_merge_history_candidates(lc, merge_idx, nb_list, cand_list, &num_cands)) + return; + + if (mv_merge_pairwise_candidate(cand_list, num_cands, IS_B(lc->sc->sh.r))) { + if (merge_idx == num_cands) + return; + num_cands++; + } + + mv_merge_zero_motion_candidate(lc, merge_idx, cand_list, num_cands); +} + +//8.5.2.2 Derivation process for luma motion vectors for merge mode +void ff_vvc_luma_mv_merge_mode(VVCLocalContext *lc, const int merge_idx, const int ciip_flag, MvField *mv) +{ + const CodingUnit *cu = lc->cu; + MvField cand_list[MRG_MAX_NUM_CANDS]; + + ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + mv_merge_mode(lc, merge_idx, cand_list); + *mv = cand_list[merge_idx]; + //ciip flag in not inhritable + mv->ciip_flag = ciip_flag; +} + +//8.5.4.2 Derivation process for luma motion vectors for geometric partitioning merge mode +void ff_vvc_luma_mv_merge_gpm(VVCLocalContext *lc, const int merge_gpm_idx[2], MvField *mv) +{ + const CodingUnit *cu = lc->cu; + MvField cand_list[MRG_MAX_NUM_CANDS]; + + const int idx[] = { merge_gpm_idx[0], merge_gpm_idx[1] + (merge_gpm_idx[1] >= merge_gpm_idx[0]) }; + + ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + mv_merge_mode(lc, FFMAX(idx[0], idx[1]), cand_list); + memset(mv, 0, 2 * sizeof(*mv)); + for (int i = 0; i < 2; i++) { + int lx = idx[i] & 1; + int mask = lx + PF_L0; + MvField *cand = cand_list + idx[i]; + if (!(cand->pred_flag & mask)) { + lx = !lx; + mask = lx + PF_L0; + } + mv[i].pred_flag = mask; + mv[i].ref_idx[lx] = cand->ref_idx[lx]; + mv[i].mv[lx] = cand->mv[lx]; + } + +} + +//8.5.5.5 Derivation process for luma affine control point motion vectors from a neighbouring block +static void affine_cps_from_nb(const VVCLocalContext *lc, + const int x_nb, int y_nb, const int nbw, const int nbh, const int lx, + Mv *cps, int num_cps) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const int x0 = cu->x0; + const int y0 = cu->y0; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + const MvField* tab_mvf = fc->tab.mvf; + const int min_cb_log2_size = fc->ps.sps->min_cb_log2_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + + const int log2_nbw = ff_log2(nbw); + const int log2_nbh = ff_log2(nbh); + const int is_ctb_boundary = !((y_nb + nbh) % fc->ps.sps->ctb_size_y) && (y_nb + nbh == y0); + const Mv *l, *r; + int mv_scale_hor, mv_scale_ver, d_hor_x, d_ver_x, d_hor_y, d_ver_y, motion_model_idc_nb; + if (is_ctb_boundary) { + const int min_pu_width = fc->ps.pps->min_pu_width; + l = &TAB_MVF(x_nb, y_nb + nbh - 1).mv[lx]; + r = &TAB_MVF(x_nb + nbw - 1, y_nb + nbh - 1).mv[lx]; + } else { + const int x = x_nb >> min_cb_log2_size; + const int y = y_nb >> min_cb_log2_size; + motion_model_idc_nb = SAMPLE_CTB(fc->tab.mmi, x, y); + + l = &TAB_CP_MV(lx, x_nb, y_nb); + r = &TAB_CP_MV(lx, x_nb + nbw - 1, y_nb) + 1; + } + mv_scale_hor = l->x << 7; + mv_scale_ver = l->y << 7; + d_hor_x = (r->x - l->x) << (7 - log2_nbw); + d_ver_x = (r->y - l->y) << (7 - log2_nbw); + if (!is_ctb_boundary && motion_model_idc_nb == MOTION_6_PARAMS_AFFINE) { + const Mv* lb = &TAB_CP_MV(lx, x_nb, y_nb + nbh - 1) + 2; + d_hor_y = (lb->x - l->x) << (7 - log2_nbh); + d_ver_y = (lb->y - l->y) << (7 - log2_nbh); + } else { + d_hor_y = -d_ver_x; + d_ver_y = d_hor_x; + } + + if (is_ctb_boundary) { + y_nb = y0; + } + cps[0].x = mv_scale_hor + d_hor_x * (x0 - x_nb) + d_hor_y * (y0 - y_nb); + cps[0].y = mv_scale_ver + d_ver_x * (x0 - x_nb) + d_ver_y * (y0 - y_nb); + cps[1].x = mv_scale_hor + d_hor_x * (x0 + cb_width - x_nb) + d_hor_y * (y0 - y_nb); + cps[1].y = mv_scale_ver + d_ver_x * (x0 + cb_width - x_nb) + d_ver_y * (y0 - y_nb); + if (num_cps == 3) { + cps[2].x = mv_scale_hor + d_hor_x * (x0 - x_nb) + d_hor_y * (y0 + cb_height - y_nb); + cps[2].y = mv_scale_ver + d_ver_x * (x0 - x_nb) + d_ver_y * (y0 + cb_height - y_nb); + } + for (int i = 0; i < num_cps; i++) { + ff_vvc_round_mv(cps + i, 0, 7); + ff_vvc_clip_mv(cps + i); + } +} + +//derive affine neighbour's postion, width and height, +static int affine_neighbour_cb(const VVCFrameContext *fc, const int x_nb, const int y_nb, int *x_cb, int *y_cb, int *cbw, int *cbh) +{ + const int log2_min_cb_size = fc->ps.sps->min_cb_log2_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int x = x_nb >> log2_min_cb_size; + const int y = y_nb >> log2_min_cb_size; + const int motion_model_idc = SAMPLE_CTB(fc->tab.mmi, x, y); + if (motion_model_idc) { + *x_cb = SAMPLE_CTB(fc->tab.cb_pos_x[0], x, y); + *y_cb = SAMPLE_CTB(fc->tab.cb_pos_y[0], x, y); + *cbw = SAMPLE_CTB(fc->tab.cb_width[0], x, y); + *cbh = SAMPLE_CTB(fc->tab.cb_height[0], x, y); + } + return motion_model_idc; +} + +//part of 8.5.5.2 Derivation process for motion vectors and reference indices in subblock merge mode +static int affine_merge_candidate(const VVCLocalContext *lc, const int x_cand, const int y_cand, MotionInfo* mi) +{ + const VVCFrameContext *fc = lc->fc; + int x, y, w, h, motion_model_idc; + + motion_model_idc = affine_neighbour_cb(fc, x_cand, y_cand, &x, &y, &w, &h); + if (motion_model_idc) { + const int min_pu_width = fc->ps.pps->min_pu_width; + const MvField* tab_mvf = fc->tab.mvf; + const MvField *mvf = &TAB_MVF(x, y); + + mi->bcw_idx = mvf->bcw_idx; + mi->pred_flag = mvf->pred_flag; + for (int i = 0; i < 2; i++) { + PredFlag mask = i + 1; + if (mi->pred_flag & mask) { + affine_cps_from_nb(lc, x, y, w, h, i, &mi->mv[i][0], motion_model_idc + 1); + } + mi->ref_idx[i] = mvf->ref_idx[i]; + } + mi->motion_model_idc = motion_model_idc; + } + return motion_model_idc; +} + +static int affine_merge_from_nbs(NeighbourContext *ctx, const NeighbourIdx *nbs, const int num_nbs, MotionInfo* cand) +{ + const VVCLocalContext *lc = ctx->lc; + const int is_mvp = 0; + for (int i = 0; i < num_nbs; i++) { + Neighbour *n = &ctx->neighbours[nbs[i]]; + if (check_available(n, lc, is_mvp) && affine_merge_candidate(lc, n->x, n->y, cand)) + return 1; + } + return 0; +} +#define AFFINE_MERGE_FROM_NBS(nbs) affine_merge_from_nbs(&nctx, nbs, FF_ARRAY_ELEMS(nbs), mi) + + +static const MvField* derive_corner_mvf(NeighbourContext *ctx, const NeighbourIdx *neighbour, const int num_neighbour) +{ + const VVCFrameContext *fc = ctx->lc->fc; + const MvField *tab_mvf = fc->tab.mvf; + const int min_pu_width = fc->ps.pps->min_pu_width; + for (int i = 0; i < num_neighbour; i++) { + Neighbour *n = &ctx->neighbours[neighbour[i]]; + if (check_available(n, ctx->lc, 0)) { + return &TAB_MVF(n->x, n->y); + } + } + return NULL; +} + +#define DERIVE_CORNER_MV(nbs) derive_corner_mvf(nctx, nbs, FF_ARRAY_ELEMS(nbs)) + +// check if the mv's and refidx are the same between A and B +static av_always_inline int compare_pf_ref_idx(const MvField *A, const struct MvField *B, const struct MvField *C, const int lx) +{ + + const PredFlag mask = (lx + 1) & A->pred_flag; + if (!(B->pred_flag & mask)) + return 0; + if (A->ref_idx[lx] != B->ref_idx[lx]) + return 0; + if (C) { + if (!(C->pred_flag & mask)) + return 0; + if (A->ref_idx[lx] != C->ref_idx[lx]) + return 0; + } + return 1; +} + +static av_always_inline void sb_clip_location(const VVCFrameContext *fc, + const int x_ctb, const int y_ctb, const Mv* temp_mv, int *x, int *y) +{ + const VVCPPS *pps = fc->ps.pps; + const int ctb_log2_size = fc->ps.sps->ctb_log2_size_y; + *y = av_clip(*y + temp_mv->y, y_ctb, FFMIN(pps->height - 1, y_ctb + (1 << ctb_log2_size) - 1)) & ~7; + *x = av_clip(*x + temp_mv->x, x_ctb, FFMIN(pps->width - 1, x_ctb + (1 << ctb_log2_size) + 3)) & ~7; +} + +static void sb_temproal_luma_motion(const VVCLocalContext *lc, + const int x_ctb, const int y_ctb, const Mv *temp_mv, + int x, int y, uint8_t *pred_flag, Mv *mv) +{ + MvField temp_col; + Mv* mvLXCol; + const int refIdxLx = 0; + const VVCFrameContext *fc = lc->fc; + const VVCSH *sh = &lc->sc->sh; + const int min_pu_width = fc->ps.pps->min_pu_width; + VVCFrame *ref = fc->ref->collocated_ref; + MvField *tab_mvf = ref->tab_dmvr_mvf; + int colPic = ref->poc; + int X = 0; + + sb_clip_location(fc, x_ctb, y_ctb, temp_mv, &x, &y); + + temp_col = TAB_MVF(x, y); + mvLXCol = mv + 0; + *pred_flag = DERIVE_TEMPORAL_COLOCATED_MVS(1); + if (IS_B(sh->r)) { + X = 1; + mvLXCol = mv + 1; + *pred_flag |= (DERIVE_TEMPORAL_COLOCATED_MVS(1)) << 1; + } +} + +//8.5.5.4 Derivation process for subblock-based temporal merging base motion data +static int sb_temporal_luma_motion_data(const VVCLocalContext *lc, const MvField *a1, + const int x_ctb, const int y_ctb, MvField *ctr_mvf, Mv *temp_mv) +{ + const VVCFrameContext *fc = lc->fc; + const RefPicList *rpl = lc->sc->rpl; + const CodingUnit *cu = lc->cu; + const int x = cu->x0 + cu->cb_width / 2; + const int y = cu->y0 + cu->cb_height / 2; + const VVCFrame *ref = fc->ref->collocated_ref; + + int colPic; + + memset(temp_mv, 0, sizeof(*temp_mv)); + + if (!ref) { + memset(ctr_mvf, 0, sizeof(*ctr_mvf)); + return 0; + } + + colPic = ref->poc; + + AV_ZERO64(temp_mv); + if (a1) { + if ((a1->pred_flag & PF_L0) && colPic == rpl[0].list[a1->ref_idx[0]]) + *temp_mv = a1->mv[0]; + else if ((a1->pred_flag & PF_L1) && colPic == rpl[1].list[a1->ref_idx[1]]) + *temp_mv = a1->mv[1]; + ff_vvc_round_mv(temp_mv, 0, 4); + } + sb_temproal_luma_motion(lc, x_ctb, y_ctb, temp_mv, x, y, &ctr_mvf->pred_flag , ctr_mvf->mv); + + return ctr_mvf->pred_flag; +} + + +//8.5.5.3 Derivation process for subblock-based temporal merging candidates +static int sb_temporal_merge_candidate(const VVCLocalContext* lc, NeighbourContext *nctx, PredictionUnit *pu) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const VVCSPS *sps = fc->ps.sps; + const VVCPH *ph = &fc->ps.ph; + MotionInfo *mi = &pu->mi; + const int ctb_log2_size = sps->ctb_log2_size_y; + const int x0 = cu->x0; + const int y0 = cu->y0; + const NeighbourIdx n = A1; + const MvField *a1; + MvField ctr_mvf; + Mv temp_mv; + const int x_ctb = (x0 >> ctb_log2_size) << ctb_log2_size; + const int y_ctb = (y0 >> ctb_log2_size) << ctb_log2_size; + + + if (!ph->r->ph_temporal_mvp_enabled_flag || + !sps->r->sps_sbtmvp_enabled_flag || + (cu->cb_width < 8 && cu->cb_height < 8)) + return 0; + + mi->num_sb_x = cu->cb_width >> 3; + mi->num_sb_y = cu->cb_height >> 3; + + a1 = derive_corner_mvf(nctx, &n, 1); + if (sb_temporal_luma_motion_data(lc, a1, x_ctb, y_ctb, &ctr_mvf, &temp_mv)) { + const int sbw = cu->cb_width / mi->num_sb_x; + const int sbh = cu->cb_height / mi->num_sb_y; + MvField mvf = {0}; + for (int sby = 0; sby < mi->num_sb_y; sby++) { + for (int sbx = 0; sbx < mi->num_sb_x; sbx++) { + int x = x0 + sbx * sbw; + int y = y0 + sby * sbh; + sb_temproal_luma_motion(lc, x_ctb, y_ctb, &temp_mv, x + sbw / 2, y + sbh / 2, &mvf.pred_flag, mvf.mv); + if (!mvf.pred_flag) { + mvf.pred_flag = ctr_mvf.pred_flag; + memcpy(mvf.mv, ctr_mvf.mv, sizeof(mvf.mv)); + } + ff_vvc_set_mvf(lc, x, y, sbw, sbh, &mvf); + } + } + return 1; + } + + return 0; +} + +static int affine_merge_const1(const MvField *c0, const MvField *c1, const MvField *c2, MotionInfo *mi) +{ + if (c0 && c1 && c2) { + mi->pred_flag = 0; + for (int i = 0; i < 2; i++) { + PredFlag mask = i + 1; + if (compare_pf_ref_idx(c0, c1, c2, i)) { + mi->pred_flag |= mask; + mi->ref_idx[i] = c0->ref_idx[i]; + mi->mv[i][0] = c0->mv[i]; + mi->mv[i][1] = c1->mv[i]; + mi->mv[i][2] = c2->mv[i]; + } + } + if (mi->pred_flag) { + if (mi->pred_flag == PF_BI) + mi->bcw_idx = c0->bcw_idx; + mi->motion_model_idc = MOTION_6_PARAMS_AFFINE; + return 1; + } + } + return 0; +} + +static int affine_merge_const2(const MvField *c0, const MvField *c1, const MvField *c3, MotionInfo *mi) +{ + if (c0 && c1 && c3) { + mi->pred_flag = 0; + for (int i = 0; i < 2; i++) { + PredFlag mask = i + 1; + if (compare_pf_ref_idx(c0, c1, c3, i)) { + mi->pred_flag |= mask; + mi->ref_idx[i] = c0->ref_idx[i]; + mi->mv[i][0] = c0->mv[i]; + mi->mv[i][1] = c1->mv[i]; + mi->mv[i][2].x = c3->mv[i].x + c0->mv[i].x - c1->mv[i].x; + mi->mv[i][2].y = c3->mv[i].y + c0->mv[i].y - c1->mv[i].y; + ff_vvc_clip_mv(&mi->mv[i][2]); + } + } + if (mi->pred_flag) { + mi->bcw_idx = mi->pred_flag == PF_BI ? c0->bcw_idx : 0; + mi->motion_model_idc = MOTION_6_PARAMS_AFFINE; + return 1; + } + } + return 0; +} + +static int affine_merge_const3(const MvField *c0, const MvField *c2, const MvField *c3, MotionInfo *mi) +{ + if (c0 && c2 && c3) { + mi->pred_flag = 0; + for (int i = 0; i < 2; i++) { + PredFlag mask = i + 1; + if (compare_pf_ref_idx(c0, c2, c3, i)) { + mi->pred_flag |= mask; + mi->ref_idx[i] = c0->ref_idx[i]; + mi->mv[i][0] = c0->mv[i]; + mi->mv[i][1].x = c3->mv[i].x + c0->mv[i].x - c2->mv[i].x; + mi->mv[i][1].y = c3->mv[i].y + c0->mv[i].y - c2->mv[i].y; + ff_vvc_clip_mv(&mi->mv[i][1]); + mi->mv[i][2] = c2->mv[i]; + } + } + if (mi->pred_flag) { + mi->bcw_idx = mi->pred_flag == PF_BI ? c0->bcw_idx : 0; + mi->motion_model_idc = MOTION_6_PARAMS_AFFINE; + return 1; + } + } + return 0; +} + +static int affine_merge_const4(const MvField *c1, const MvField *c2, const MvField *c3, MotionInfo *mi) +{ + if (c1 && c2 && c3) { + mi->pred_flag = 0; + for (int i = 0; i < 2; i++) { + PredFlag mask = i + 1; + if (compare_pf_ref_idx(c1, c2, c3, i)) { + mi->pred_flag |= mask; + mi->ref_idx[i] = c1->ref_idx[i]; + mi->mv[i][0].x = c1->mv[i].x + c2->mv[i].x - c3->mv[i].x; + mi->mv[i][0].y = c1->mv[i].y + c2->mv[i].y - c3->mv[i].y; + ff_vvc_clip_mv(&mi->mv[i][0]); + mi->mv[i][1] = c1->mv[i]; + mi->mv[i][2] = c2->mv[i]; + } + } + if (mi->pred_flag) { + mi->bcw_idx = mi->pred_flag == PF_BI ? c1->bcw_idx : 0; + mi->motion_model_idc = MOTION_6_PARAMS_AFFINE; + return 1; + } + } + return 0; + +} + +static int affine_merge_const5(const MvField *c0, const MvField *c1, MotionInfo *mi) +{ + if (c0 && c1) { + mi->pred_flag = 0; + for (int i = 0; i < 2; i++) { + PredFlag mask = i + 1; + if (compare_pf_ref_idx(c0, c1, NULL, i)) { + mi->pred_flag |= mask; + mi->ref_idx[i] = c0->ref_idx[i]; + mi->mv[i][0] = c0->mv[i]; + mi->mv[i][1] = c1->mv[i]; + } + } + if (mi->pred_flag) { + if (mi->pred_flag == PF_BI) + mi->bcw_idx = c0->bcw_idx; + mi->motion_model_idc = MOTION_4_PARAMS_AFFINE; + return 1; + } + } + return 0; +} + +static int affine_merge_const6(const MvField* c0, const MvField* c2, const int cb_width, const int cb_height, MotionInfo *mi) +{ + if (c0 && c2) { + const int shift = 7 + av_log2(cb_width) - av_log2(cb_height); + mi->pred_flag = 0; + for (int i = 0; i < 2; i++) { + PredFlag mask = i + 1; + if (compare_pf_ref_idx(c0, c2, NULL, i)) { + mi->pred_flag |= mask; + mi->ref_idx[i] = c0->ref_idx[i]; + mi->mv[i][0] = c0->mv[i]; + mi->mv[i][1].x = (c0->mv[i].x << 7) + ((c2->mv[i].y - c0->mv[i].y) << shift); + mi->mv[i][1].y = (c0->mv[i].y << 7) - ((c2->mv[i].x - c0->mv[i].x) << shift); + ff_vvc_round_mv(&mi->mv[i][1], 0, 7); + ff_vvc_clip_mv(&mi->mv[i][1]); + } + } + if (mi->pred_flag) { + if (mi->pred_flag == PF_BI) + mi->bcw_idx = c0->bcw_idx; + mi->motion_model_idc = MOTION_4_PARAMS_AFFINE; + return 1; + } + } + return 0; +} + +static void affine_merge_zero_motion(const VVCLocalContext *lc, MotionInfo *mi) +{ + const CodingUnit *cu = lc->cu; + + memset(mi, 0, sizeof(*mi)); + mi->pred_flag = PF_L0 + (IS_B(lc->sc->sh.r) << 1); + mi->motion_model_idc = MOTION_4_PARAMS_AFFINE; + mi->num_sb_x = cu->cb_width >> MIN_PU_LOG2; + mi->num_sb_y = cu->cb_height >> MIN_PU_LOG2; +} + +//8.5.5.6 Derivation process for constructed affine control point motion vector merging candidates +static int affine_merge_const_candidates(const VVCLocalContext *lc, MotionInfo *mi, + NeighbourContext *nctx, const int merge_subblock_idx, int num_cands) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const NeighbourIdx tl[] = { B2, B3, A2 }; + const NeighbourIdx tr[] = { B1, B0}; + const NeighbourIdx bl[] = { A1, A0}; + const MvField *c0, *c1, *c2; + + c0 = DERIVE_CORNER_MV(tl); + c1 = DERIVE_CORNER_MV(tr); + c2 = DERIVE_CORNER_MV(bl); + + if (fc->ps.sps->r->sps_6param_affine_enabled_flag) { + MvField corner3, *c3 = NULL; + //Const1 + if (affine_merge_const1(c0, c1, c2, mi)) { + if (merge_subblock_idx == num_cands) + return 1; + num_cands++; + } + + memset(&corner3, 0, sizeof(corner3)); + if (fc->ps.ph.r->ph_temporal_mvp_enabled_flag){ + const int available_l0 = temporal_luma_motion_vector(lc, 0, corner3.mv + 0, 0, 0, 0); + const int available_l1 = (lc->sc->sh.r->sh_slice_type == VVC_SLICE_TYPE_B) ? + temporal_luma_motion_vector(lc, 0, corner3.mv + 1, 1, 0, 0) : 0; + + corner3.pred_flag = available_l0 + (available_l1 << 1); + if (corner3.pred_flag) + c3 = &corner3; + } + + //Const2 + if (affine_merge_const2(c0, c1, c3, mi)) { + if (merge_subblock_idx == num_cands) + return 1; + num_cands++; + } + + //Const3 + if (affine_merge_const3(c0, c2, c3, mi)) { + if (merge_subblock_idx == num_cands) + return 1; + num_cands++; + } + + //Const4 + if (affine_merge_const4(c1, c2, c3, mi)) { + if (merge_subblock_idx == num_cands) + return 1; + num_cands++; + } + } + + //Const5 + if (affine_merge_const5(c0, c1, mi)) { + if (merge_subblock_idx == num_cands) + return 1; + num_cands++; + } + + if (affine_merge_const6(c0, c2, cu->cb_width, cu->cb_height, mi)) { + if (merge_subblock_idx == num_cands) + return 1; + } + return 0; + +} + + +//8.5.5.2 Derivation process for motion vectors and reference indices in subblock merge mode +//return 1 if candidate is SbCol +static int sb_mv_merge_mode(const VVCLocalContext *lc, const int merge_subblock_idx, PredictionUnit *pu) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const CodingUnit *cu = lc->cu; + MotionInfo *mi = &pu->mi; + int num_cands = 0; + NeighbourContext nctx; + + init_neighbour_context(&nctx, lc); + + //SbCol + if (sb_temporal_merge_candidate(lc, &nctx, pu)) { + if (merge_subblock_idx == num_cands) + return 1; + num_cands++; + } + + pu->inter_affine_flag = 1; + mi->num_sb_x = cu->cb_width >> MIN_PU_LOG2; + mi->num_sb_y = cu->cb_height >> MIN_PU_LOG2; + + if (sps->r->sps_affine_enabled_flag) { + const NeighbourIdx ak[] = { A0, A1 }; + const NeighbourIdx bk[] = { B0, B1, B2 }; + //A + if (AFFINE_MERGE_FROM_NBS(ak)) { + if (merge_subblock_idx == num_cands) + return 0; + num_cands++; + } + + //B + if (AFFINE_MERGE_FROM_NBS(bk)) { + if (merge_subblock_idx == num_cands) + return 0; + num_cands++; + } + + //Const1 to Const6 + if (affine_merge_const_candidates(lc, mi, &nctx, merge_subblock_idx, num_cands)) + return 0; + } + //Zero + affine_merge_zero_motion(lc, mi); + return 0; +} + +void ff_vvc_sb_mv_merge_mode(VVCLocalContext *lc, const int merge_subblock_idx, PredictionUnit *pu) +{ + const CodingUnit *cu = lc->cu; + ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + if (!sb_mv_merge_mode(lc, merge_subblock_idx, pu)) { + ff_vvc_store_sb_mvs(lc, pu); + } +} + +static int mvp_candidate(const VVCLocalContext *lc, const int x_cand, const int y_cand, + const int lx, const int8_t *ref_idx, Mv *mv) +{ + const VVCFrameContext *fc = lc->fc; + const RefPicList *rpl = lc->sc->rpl; + const int min_pu_width = fc->ps.pps->min_pu_width; + const MvField* tab_mvf = fc->tab.mvf; + const MvField *mvf = &TAB_MVF(x_cand, y_cand); + const PredFlag maskx = lx + 1; + const int poc = rpl[lx].list[ref_idx[lx]]; + int available = 0; + + if ((mvf->pred_flag & maskx) && rpl[lx].list[mvf->ref_idx[lx]] == poc) { + available = 1; + *mv = mvf->mv[lx]; + } else { + const int ly = !lx; + const PredFlag masky = ly + 1; + if ((mvf->pred_flag & masky) && rpl[ly].list[mvf->ref_idx[ly]] == poc) { + available = 1; + *mv = mvf->mv[ly]; + } + } + + return available; +} + +static int affine_mvp_candidate(const VVCLocalContext *lc, + const int x_cand, const int y_cand, const int lx, const int8_t *ref_idx, + Mv *cps, const int num_cp) +{ + const VVCFrameContext *fc = lc->fc; + int x_nb, y_nb, nbw, nbh, motion_model_idc, available = 0; + + motion_model_idc = affine_neighbour_cb(fc, x_cand, y_cand, &x_nb, &y_nb, &nbw, &nbh); + if (motion_model_idc) { + const int min_pu_width = fc->ps.pps->min_pu_width; + const MvField* tab_mvf = fc->tab.mvf; + const MvField *mvf = &TAB_MVF(x_nb, y_nb); + RefPicList* rpl = lc->sc->rpl; + const PredFlag maskx = lx + 1; + const int poc = rpl[lx].list[ref_idx[lx]]; + + if ((mvf->pred_flag & maskx) && rpl[lx].list[mvf->ref_idx[lx]] == poc) { + available = 1; + affine_cps_from_nb(lc, x_nb, y_nb, nbw, nbh, lx, cps, num_cp); + } else { + const int ly = !lx; + const PredFlag masky = ly + 1; + if ((mvf->pred_flag & masky) && rpl[ly].list[mvf->ref_idx[ly]] == poc) { + available = 1; + affine_cps_from_nb(lc, x_nb, y_nb, nbw, nbh, ly, cps, num_cp); + } + } + + } + return available; +} + +static int mvp_from_nbs(NeighbourContext *ctx, + const NeighbourIdx *nbs, const int num_nbs, const int lx, const int8_t *ref_idx, const int amvr_shift, + Mv *cps, const int num_cps) +{ + const VVCLocalContext *lc = ctx->lc; + const int is_mvp = 1; + int available = 0; + + for (int i = 0; i < num_nbs; i++) { + Neighbour *n = &ctx->neighbours[nbs[i]]; + if (check_available(n, lc, is_mvp)) { + if (num_cps > 1) + available = affine_mvp_candidate(lc, n->x, n->y, lx, ref_idx, cps, num_cps); + else + available = mvp_candidate(lc, n->x, n->y, lx, ref_idx, cps); + if (available) { + for (int c = 0; c < num_cps; c++) + ff_vvc_round_mv(cps + c, amvr_shift, amvr_shift); + return 1; + } + } + } + return 0; +} + +//get mvp from neighbours +#define AFFINE_MVP_FROM_NBS(nbs) \ + mvp_from_nbs(&nctx, nbs, FF_ARRAY_ELEMS(nbs), lx, ref_idx, amvr_shift, cps, num_cp) \ + +#define MVP_FROM_NBS(nbs) \ + mvp_from_nbs(&nctx, nbs, FF_ARRAY_ELEMS(nbs), lx, ref_idx, amvr_shift, mv, 1) \ + +static int mvp_spatial_candidates(const VVCLocalContext *lc, + const int mvp_lx_flag, const int lx, const int8_t* ref_idx, const int amvr_shift, + Mv* mv, int *nb_merge_cand) +{ + const NeighbourIdx ak[] = { A0, A1 }; + const NeighbourIdx bk[] = { B0, B1, B2 }; + NeighbourContext nctx; + int available_a, num_cands = 0; + Mv mv_a; + + init_neighbour_context(&nctx, lc); + + available_a = MVP_FROM_NBS(ak); + if (available_a) { + if (mvp_lx_flag == num_cands) + return 1; + num_cands++; + mv_a = *mv; + } + if (MVP_FROM_NBS(bk)) { + if (!available_a || !IS_SAME_MV(&mv_a, mv)) { + if (mvp_lx_flag == num_cands) + return 1; + num_cands++; + } + } + *nb_merge_cand = num_cands; + return 0; +} + +static int mvp_temporal_candidates(const VVCLocalContext* lc, + const int mvp_lx_flag, const int lx, const int8_t *ref_idx, const int amvr_shift, + Mv* mv, int *num_cands) +{ + if (temporal_luma_motion_vector(lc, ref_idx[lx], mv, lx, 1, 0)) { + if (mvp_lx_flag == *num_cands) { + ff_vvc_round_mv(mv, amvr_shift, amvr_shift); + return 1; + } + (*num_cands)++; + } + return 0; + +} + +static int mvp_history_candidates(const VVCLocalContext *lc, + const int mvp_lx_flag, const int lx, const int8_t ref_idx, const int amvr_shift, + Mv *mv, int num_cands) +{ + const EntryPoint* ep = lc->ep; + const RefPicList* rpl = lc->sc->rpl; + const int poc = rpl[lx].list[ref_idx]; + + if (ep->num_hmvp == 0) + return 0; + for (int i = 1; i <= FFMIN(4, ep->num_hmvp); i++) { + const MvField* h = &ep->hmvp[i - 1]; + for (int j = 0; j < 2; j++) { + const int ly = (j ? !lx : lx); + PredFlag mask = PF_L0 + ly; + if ((h->pred_flag & mask) && poc == rpl[ly].list[h->ref_idx[ly]]) { + if (mvp_lx_flag == num_cands) { + *mv = h->mv[ly]; + ff_vvc_round_mv(mv, amvr_shift, amvr_shift); + return 1; + } + num_cands++; + } + } + } + return 0; +} + +//8.5.2.8 Derivation process for luma motion vector prediction +static void mvp(const VVCLocalContext *lc, const int mvp_lx_flag, const int lx, + const int8_t *ref_idx, const int amvr_shift, Mv *mv) +{ + int num_cands; + + if (mvp_spatial_candidates(lc, mvp_lx_flag, lx, ref_idx, amvr_shift, mv, &num_cands)) + return; + + if (mvp_temporal_candidates(lc, mvp_lx_flag, lx, ref_idx, amvr_shift, mv, &num_cands)) + return; + + if (mvp_history_candidates(lc, mvp_lx_flag, lx, ref_idx[lx], amvr_shift, mv, num_cands)) + return; + + memset(mv, 0, sizeof(*mv)); +} + +void ff_vvc_mvp(VVCLocalContext *lc, const int *mvp_lx_flag, const int amvr_shift, MotionInfo *mi) +{ + const CodingUnit *cu = lc->cu; + mi->num_sb_x = 1; + mi->num_sb_y = 1; + + ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + if (mi->pred_flag != PF_L1) + mvp(lc, mvp_lx_flag[L0], L0, mi->ref_idx, amvr_shift, &mi->mv[L0][0]); + if (mi->pred_flag != PF_L0) + mvp(lc, mvp_lx_flag[L1], L1, mi->ref_idx, amvr_shift, &mi->mv[L1][0]); +} + +static int affine_mvp_constructed_cp(NeighbourContext *ctx, + const NeighbourIdx *neighbour, const int num_neighbour, + const int lx, const int8_t ref_idx, const int amvr_shift, Mv *cp) +{ + const VVCLocalContext *lc = ctx->lc; + const VVCFrameContext *fc = lc->fc; + const MvField *tab_mvf = fc->tab.mvf; + const int min_pu_width = fc->ps.pps->min_pu_width; + const RefPicList* rpl = lc->sc->rpl; + const int is_mvp = 1; + int available = 0; + + for (int i = 0; i < num_neighbour; i++) { + Neighbour *n = &ctx->neighbours[neighbour[i]]; + if (check_available(n, ctx->lc, is_mvp)) { + const PredFlag maskx = lx + 1; + const MvField* mvf = &TAB_MVF(n->x, n->y); + const int poc = rpl[lx].list[ref_idx]; + if ((mvf->pred_flag & maskx) && rpl[lx].list[mvf->ref_idx[lx]] == poc) { + available = 1; + *cp = mvf->mv[lx]; + } else { + const int ly = !lx; + const PredFlag masky = ly + 1; + if ((mvf->pred_flag & masky) && rpl[ly].list[mvf->ref_idx[ly]] == poc) { + available = 1; + *cp = mvf->mv[ly]; + } + } + if (available) { + ff_vvc_round_mv(cp, amvr_shift, amvr_shift); + return 1; + } + } + } + return 0; +} + +#define AFFINE_MVP_CONSTRUCTED_CP(cands, cp) \ + affine_mvp_constructed_cp(nctx, cands, FF_ARRAY_ELEMS(cands), lx, ref_idx, \ + amvr_shift, cp) + +//8.5.5.8 Derivation process for constructed affine control point motion vector prediction candidates +static int affine_mvp_const1(NeighbourContext* nctx, + const int lx, const int8_t ref_idx, const int amvr_shift, + Mv *cps, int *available) +{ + const NeighbourIdx tl[] = { B2, B3, A2 }; + const NeighbourIdx tr[] = { B1, B0 }; + const NeighbourIdx bl[] = { A1, A0 }; + + available[0] = AFFINE_MVP_CONSTRUCTED_CP(tl, cps + 0); + available[1] = AFFINE_MVP_CONSTRUCTED_CP(tr, cps + 1); + available[2] = AFFINE_MVP_CONSTRUCTED_CP(bl, cps + 2); + return available[0] && available[1]; +} + +//8.5.5.7 item 7 +static void affine_mvp_const2(const int idx, Mv *cps, const int num_cp) +{ + const Mv mv = cps[idx]; + for (int j = 0; j < num_cp; j++) + cps[j] = mv; +} + +//8.5.5.7 Derivation process for luma affine control point motion vector predictors +static void affine_mvp(const VVCLocalContext *lc, + const int mvp_lx_flag, const int lx, const int8_t *ref_idx, const int amvr_shift, + MotionModelIdc motion_model_idc, Mv *cps) +{ + const NeighbourIdx ak[] = { A0, A1 }; + const NeighbourIdx bk[] = { B0, B1, B2 }; + const int num_cp = motion_model_idc + 1; + NeighbourContext nctx; + int available[MAX_CONTROL_POINTS]; + int num_cands = 0; + + init_neighbour_context(&nctx, lc); + //Ak + if (AFFINE_MVP_FROM_NBS(ak)) { + if (mvp_lx_flag == num_cands) + return; + num_cands++; + } + //Bk + if (AFFINE_MVP_FROM_NBS(bk)) { + if (mvp_lx_flag == num_cands) + return; + num_cands++; + } + + //Const1 + if (affine_mvp_const1(&nctx, lx, ref_idx[lx], amvr_shift, cps, available)) { + if (available[2] || motion_model_idc == MOTION_4_PARAMS_AFFINE) { + if (mvp_lx_flag == num_cands) + return; + num_cands++; + } + } + + //Const2 + for (int i = 2; i >= 0; i--) { + if (available[i]) { + if (mvp_lx_flag == num_cands) { + affine_mvp_const2(i, cps, num_cp); + return; + } + num_cands++; + } + } + if (temporal_luma_motion_vector(lc, ref_idx[lx], cps, lx, 1, 0)) { + if (mvp_lx_flag == num_cands) { + ff_vvc_round_mv(cps, amvr_shift, amvr_shift); + for (int i = 1; i < num_cp; i++) + cps[i] = cps[0]; + return; + } + num_cands++; + } + + //Zero Mv + memset(cps, 0, num_cp * sizeof(Mv)); +} + +void ff_vvc_affine_mvp(VVCLocalContext *lc, const int *mvp_lx_flag, const int amvr_shift, MotionInfo *mi) +{ + const CodingUnit *cu = lc->cu; + + mi->num_sb_x = cu->cb_width >> MIN_PU_LOG2; + mi->num_sb_y = cu->cb_height >> MIN_PU_LOG2; + + ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + if (mi->pred_flag != PF_L1) + affine_mvp(lc, mvp_lx_flag[L0], L0, mi->ref_idx, amvr_shift, mi->motion_model_idc, &mi->mv[L0][0]); + if (mi->pred_flag != PF_L0) + affine_mvp(lc, mvp_lx_flag[L1], L1, mi->ref_idx, amvr_shift, mi->motion_model_idc, &mi->mv[L1][0]); +} + +//8.5.2.14 Rounding process for motion vectors +void ff_vvc_round_mv(Mv *mv, const int lshift, const int rshift) +{ + if (rshift) { + const int offset = 1 << (rshift - 1); + mv->x = ((mv->x + offset - (mv->x >= 0)) >> rshift) << lshift; + mv->y = ((mv->y + offset - (mv->y >= 0)) >> rshift) << lshift; + } else { + mv->x = mv->x << lshift; + mv->y = mv->y << lshift; + } +} + +void ff_vvc_clip_mv(Mv *mv) +{ + mv->x = av_clip(mv->x, -(1 << 17), (1 << 17) - 1); + mv->y = av_clip(mv->y, -(1 << 17), (1 << 17) - 1); +} + +//8.5.2.1 Derivation process for motion vector components and reference indices +static av_always_inline int is_greater_mer(const VVCFrameContext *fc, const int x0, const int y0, const int x0_br, const int y0_br) +{ + const uint8_t plevel = fc->ps.sps->log2_parallel_merge_level; + + return x0_br >> plevel > x0 >> plevel && + y0_br >> plevel > y0 >> plevel; +} + +//8.5.2.16 Updating process for the history-based motion vector predictor candidate list +void ff_vvc_update_hmvp(VVCLocalContext *lc, const MotionInfo *mi) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const int min_pu_width = fc->ps.pps->min_pu_width; + const MvField* tab_mvf = fc->tab.mvf; + EntryPoint* ep = lc->ep; + const MvField *mvf; + int i; + + if (!is_greater_mer(fc, cu->x0, cu->y0, cu->x0 + cu->cb_width, cu->y0 + cu->cb_height)) + return; + mvf = &TAB_MVF(cu->x0, cu->y0); + + for (i = 0; i < ep->num_hmvp; i++) { + if (compare_mv_ref_idx(mvf, ep->hmvp + i)) { + ep->num_hmvp--; + break; + } + } + if (i == MAX_NUM_HMVP_CANDS) { + ep->num_hmvp--; + i = 0; + } + + memmove(ep->hmvp + i, ep->hmvp + i + 1, (ep->num_hmvp - i) * sizeof(MvField)); + ep->hmvp[ep->num_hmvp++] = *mvf; +} + +MvField* ff_vvc_get_mvf(const VVCFrameContext *fc, const int x0, const int y0) +{ + const int min_pu_width = fc->ps.pps->min_pu_width; + MvField* tab_mvf = fc->tab.mvf; + + return &TAB_MVF(x0, y0); +} diff --git a/libavcodec/vvc/vvc_mvs.h b/libavcodec/vvc/vvc_mvs.h new file mode 100644 index 00000000000..f87fcfe0ca7 --- /dev/null +++ b/libavcodec/vvc/vvc_mvs.h @@ -0,0 +1,46 @@ +/* + * VVC motion vector decoder + * + * Copyright (C) 2023 Nuo Mi + * + * 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_VVC_MVS_H +#define AVCODEC_VVC_MVS_H + +#include "vvcdec.h" + +void ff_vvc_round_mv(Mv *mv, int lshift, int rshift); +void ff_vvc_clip_mv(Mv *mv); +void ff_vvc_mv_scale(Mv *dst, const Mv *src, int td, int tb); +void ff_vvc_luma_mv_merge_mode(VVCLocalContext *lc, int merge_idx, int ciip_flag, MvField *mv); +void ff_vvc_luma_mv_merge_gpm(VVCLocalContext *lc, const int merge_gpm_idx[2], MvField *mv); +void ff_vvc_mvp(VVCLocalContext *lc, const int *mvp_lx_flag, const int amvr_shift, MotionInfo *mi); +void ff_vvc_sb_mv_merge_mode(VVCLocalContext *lc, int merge_subblock_idx, PredictionUnit *pu); +void ff_vvc_affine_mvp(VVCLocalContext *lc, const int *mvp_lx_flag, const int amvr_shift, MotionInfo* mi); +void ff_vvc_store_sb_mvs(const VVCLocalContext *lc, PredictionUnit *pu); +void ff_vvc_store_mv(const VVCLocalContext *lc, const MotionInfo *mi); +void ff_vvc_store_mvf(const VVCLocalContext *lc, const MvField *mvf); +void ff_vvc_store_gpm_mvf(const VVCLocalContext *lc, const PredictionUnit* pu); +void ff_vvc_update_hmvp(VVCLocalContext *lc, const MotionInfo *mi); +int ff_vvc_no_backward_pred_flag(const VVCLocalContext *lc); +MvField* ff_vvc_get_mvf(const VVCFrameContext *fc, const int x0, const int y0); +void ff_vvc_set_mvf(const VVCLocalContext *lc, const int x0, const int y0, const int w, const int h, const MvField *mvf); +void ff_vvc_set_intra_mvf(const VVCLocalContext *lc); + +#endif //AVCODEC_VVC_MVS_H diff --git a/libavcodec/vvc/vvc_ps.c b/libavcodec/vvc/vvc_ps.c new file mode 100644 index 00000000000..d9fb721bbd9 --- /dev/null +++ b/libavcodec/vvc/vvc_ps.c @@ -0,0 +1,1282 @@ +/* + * VVC parameter set parser + * + * Copyright (C) 2023 Nuo Mi + * Copyright (C) 2022 Xu Mu + * + * 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 "libavcodec/cbs_h266.h" +#include "libavutil/imgutils.h" +#include "vvc_data.h" +#include "vvc_ps.h" +#include "vvcdec.h" + +typedef void (*free_fn)(uint8_t *data); + +static void ps_free(void *opaque, uint8_t *data) +{ + free_fn free = (free_fn)opaque; + + free(data); + av_freep(&data); +} + +static AVBufferRef* ps_alloc(size_t size, free_fn free) +{ + AVBufferRef *buf; + uint8_t *data = av_mallocz(size); + + if (!data) + return NULL; + + buf = av_buffer_create(data, size, ps_free, free, 0); + if (!buf) + av_freep(&data); + + return buf; +} + +static int sps_map_pixel_format(VVCSPS *sps, void *log_ctx) +{ + const H266RawSPS *r = sps->r; + const AVPixFmtDescriptor *desc; + + switch (sps->bit_depth) { + case 8: + if (r->sps_chroma_format_idc == 0) sps->pix_fmt = AV_PIX_FMT_GRAY8; + if (r->sps_chroma_format_idc == 1) sps->pix_fmt = AV_PIX_FMT_YUV420P; + if (r->sps_chroma_format_idc == 2) sps->pix_fmt = AV_PIX_FMT_YUV422P; + if (r->sps_chroma_format_idc == 3) sps->pix_fmt = AV_PIX_FMT_YUV444P; + break; + case 10: + if (r->sps_chroma_format_idc == 0) sps->pix_fmt = AV_PIX_FMT_GRAY10; + if (r->sps_chroma_format_idc == 1) sps->pix_fmt = AV_PIX_FMT_YUV420P10; + if (r->sps_chroma_format_idc == 2) sps->pix_fmt = AV_PIX_FMT_YUV422P10; + if (r->sps_chroma_format_idc == 3) sps->pix_fmt = AV_PIX_FMT_YUV444P10; + break; + case 12: + if (r->sps_chroma_format_idc == 0) sps->pix_fmt = AV_PIX_FMT_GRAY12; + if (r->sps_chroma_format_idc == 1) sps->pix_fmt = AV_PIX_FMT_YUV420P12; + if (r->sps_chroma_format_idc == 2) sps->pix_fmt = AV_PIX_FMT_YUV422P12; + if (r->sps_chroma_format_idc == 3) sps->pix_fmt = AV_PIX_FMT_YUV444P12; + break; + default: + av_log(log_ctx, AV_LOG_ERROR, + "The following bit-depths are currently specified: 8, 10, 12 bits, " + "chroma_format_idc is %d, depth is %d\n", + r->sps_chroma_format_idc, sps->bit_depth); + return AVERROR_INVALIDDATA; + } + + desc = av_pix_fmt_desc_get(sps->pix_fmt); + if (!desc) + return AVERROR(EINVAL); + + sps->hshift[0] = sps->vshift[0] = 0; + sps->hshift[2] = sps->hshift[1] = desc->log2_chroma_w; + sps->vshift[2] = sps->vshift[1] = desc->log2_chroma_h; + + sps->pixel_shift = sps->bit_depth > 8; + + return 0; +} + +static int sps_bit_depth(VVCSPS *sps, void *log_ctx) +{ + const H266RawSPS *r = sps->r; + + sps->bit_depth = r->sps_bitdepth_minus8 + 8; + sps->qp_bd_offset = 6 * (sps->bit_depth - 8); + sps->log2_transform_range = + r->sps_extended_precision_flag ? FFMAX(15, FFMIN(20, sps->bit_depth + 6)) : 15; + return sps_map_pixel_format(sps, log_ctx); +} + +static int sps_chroma_qp_table(VVCSPS *sps) +{ + const H266RawSPS *r = sps->r; + const int num_qp_tables = r->sps_same_qp_table_for_chroma_flag ? + 1 : (r->sps_joint_cbcr_enabled_flag ? 3 : 2); + + for (int i = 0; i < num_qp_tables; i++) { + int num_points_in_qp_table; + int qp_in[VVC_MAX_POINTS_IN_QP_TABLE], qp_out[VVC_MAX_POINTS_IN_QP_TABLE]; + unsigned int delta_qp_in[VVC_MAX_POINTS_IN_QP_TABLE]; + int off = sps->qp_bd_offset; + + num_points_in_qp_table = r->sps_num_points_in_qp_table_minus1[i] + 1; + + qp_out[0] = qp_in[0] = r->sps_qp_table_start_minus26[i] + 26; + for (int j = 0; j < num_points_in_qp_table; j++ ) { + delta_qp_in[j] = r->sps_delta_qp_in_val_minus1[i][j] + 1; + qp_in[j+1] = qp_in[j] + delta_qp_in[j]; + qp_out[j+1] = qp_out[j] + (r->sps_delta_qp_in_val_minus1[i][j] ^ r->sps_delta_qp_diff_val[i][j]); + } + sps->chroma_qp_table[i][qp_in[0] + off] = qp_out[0]; + for (int k = qp_in[0] - 1 + off; k >= 0; k--) + sps->chroma_qp_table[i][k] = av_clip(sps->chroma_qp_table[i][k+1]-1, -off, 63); + + for (int j = 0; j < num_points_in_qp_table; j++) { + int sh = delta_qp_in[j] >> 1; + for (int k = qp_in[j] + 1 + off, m = 1; k <= qp_in[j+1] + off; k++, m++) { + sps->chroma_qp_table[i][k] = sps->chroma_qp_table[i][qp_in[j] + off] + + ((qp_out[j+1] - qp_out[j]) * m + sh) / delta_qp_in[j]; + } + } + for (int k = qp_in[num_points_in_qp_table] + 1 + off; k <= 63 + off; k++) + sps->chroma_qp_table[i][k] = av_clip(sps->chroma_qp_table[i][k-1] + 1, -sps->qp_bd_offset, 63); + } + if (r->sps_same_qp_table_for_chroma_flag) { + memcpy(&sps->chroma_qp_table[1], &sps->chroma_qp_table[0], sizeof(sps->chroma_qp_table[0])); + memcpy(&sps->chroma_qp_table[2], &sps->chroma_qp_table[0], sizeof(sps->chroma_qp_table[0])); + } + + return 0; +} + +static void sps_poc(VVCSPS *sps) +{ + sps->max_pic_order_cnt_lsb = 1 << (sps->r->sps_log2_max_pic_order_cnt_lsb_minus4 + 4); +} + +static void sps_inter(VVCSPS *sps) +{ + const H266RawSPS *r = sps->r; + + sps->max_num_merge_cand = 6 - r->sps_six_minus_max_num_merge_cand; + sps->max_num_ibc_merge_cand = 6 - r->sps_six_minus_max_num_ibc_merge_cand; + + if (sps->r->sps_gpm_enabled_flag) { + sps->max_num_gpm_merge_cand = 2; + if (sps->max_num_merge_cand >= 3) + sps->max_num_gpm_merge_cand = sps->max_num_merge_cand - r->sps_max_num_merge_cand_minus_max_num_gpm_cand; + } + + sps->log2_parallel_merge_level = r->sps_log2_parallel_merge_level_minus2 + 2; +} + +static void sps_partition_constraints(VVCSPS* sps) +{ + const H266RawSPS *r = sps->r; + + sps->ctb_log2_size_y = r->sps_log2_ctu_size_minus5 + 5; + sps->ctb_size_y = 1 << sps->ctb_log2_size_y; + sps->min_cb_log2_size_y = r->sps_log2_min_luma_coding_block_size_minus2 + 2; + sps->min_cb_size_y = 1 << sps->min_cb_log2_size_y; + sps->max_tb_size_y = 1 << (r->sps_max_luma_transform_size_64_flag ? 6 : 5); + sps->max_ts_size = 1 << (r->sps_log2_transform_skip_max_size_minus2 + 2); +} + +static void sps_ladf(VVCSPS* sps) +{ + const H266RawSPS *r = sps->r; + + if (r->sps_ladf_enabled_flag) { + sps->num_ladf_intervals = r->sps_num_ladf_intervals_minus2 + 2; + sps->ladf_interval_lower_bound[0] = 0; + for (int i = 0; i < sps->num_ladf_intervals - 1; i++) { + sps->ladf_interval_lower_bound[i + 1] = + sps->ladf_interval_lower_bound[i] + r->sps_ladf_delta_threshold_minus1[i] + 1; + } + } +} + +static int sps_derive(VVCSPS *sps, void *log_ctx) +{ + int ret; + const H266RawSPS *r = sps->r; + + sps->width = r->sps_pic_width_max_in_luma_samples; + sps->height = r->sps_pic_height_max_in_luma_samples; + + ret = sps_bit_depth(sps, log_ctx); + if (ret < 0) + return ret; + sps_poc(sps); + sps_inter(sps); + sps_partition_constraints(sps); + sps_ladf(sps); + if (r->sps_chroma_format_idc != 0) + sps_chroma_qp_table(sps); + + return 0; +} + +static void sps_free(uint8_t *data) +{ + VVCSPS *sps = (VVCSPS*)data; + av_buffer_unref(&sps->rref); +} + +static AVBufferRef *sps_alloc(const H266RawSPS *rsps, AVBufferRef *rsps_buf, void *log_ctx) +{ + int ret; + VVCSPS *sps; + AVBufferRef *sps_buf = ps_alloc(sizeof(*sps), sps_free); + + if (!sps_buf) + return NULL; + sps = (VVCSPS *)sps_buf->data; + + ret = av_buffer_replace(&sps->rref, rsps_buf); + if (ret < 0) + goto fail; + sps->r = rsps; + + ret = sps_derive(sps, log_ctx); + if (ret < 0) + goto fail; + + return sps_buf; + +fail: + av_buffer_unref(&sps_buf); + return NULL; +} + +static int decode_sps(VVCParamSets *ps, + const H266RawSPS *rsps, AVBufferRef *rsps_buf, void *log_ctx) +{ + int ret; + const int sps_id = rsps->sps_seq_parameter_set_id; + AVBufferRef *sps_buf = ps->sps_list[sps_id]; + + if (sps_buf && ((VVCSPS *)sps_buf->data)->r == rsps) + return 0; + + sps_buf = sps_alloc(rsps, rsps_buf, log_ctx); + if (!sps_buf) + return AVERROR(ENOMEM); + + ret = av_buffer_replace(&ps->sps_list[sps_id], sps_buf); + + av_buffer_unref(&sps_buf); + return ret; +} + +static void pps_chroma_qp_offset(VVCPPS *pps) +{ + pps->chroma_qp_offset[CB - 1] = pps->r->pps_cb_qp_offset; + pps->chroma_qp_offset[CR - 1] = pps->r->pps_cr_qp_offset; + pps->chroma_qp_offset[JCBCR - 1]= pps->r->pps_joint_cbcr_qp_offset_value; + for (int i = 0; i < 6; i++) { + pps->chroma_qp_offset_list[i][CB - 1] = pps->r->pps_cb_qp_offset_list[i]; + pps->chroma_qp_offset_list[i][CR - 1] = pps->r->pps_cr_qp_offset_list[i]; + pps->chroma_qp_offset_list[i][JCBCR - 1]= pps->r->pps_joint_cbcr_qp_offset_list[i]; + } +} + +static void pps_width_height(VVCPPS *pps, const VVCSPS *sps) +{ + const H266RawPPS *r = pps->r; + + pps->width = r->pps_pic_width_in_luma_samples; + pps->height = r->pps_pic_height_in_luma_samples; + + pps->ctb_width = AV_CEIL_RSHIFT(pps->width, sps->ctb_log2_size_y); + pps->ctb_height = AV_CEIL_RSHIFT(pps->height, sps->ctb_log2_size_y); + pps->ctb_count = pps->ctb_width * pps->ctb_height; + + pps->min_cb_width = pps->width >> sps->min_cb_log2_size_y; + pps->min_cb_height = pps->height >> sps->min_cb_log2_size_y; + + pps->min_pu_width = pps->width >> MIN_PU_LOG2; + pps->min_pu_height = pps->height >> MIN_PU_LOG2; + pps->min_tu_width = pps->width >> MIN_TU_LOG2; + pps->min_tu_height = pps->height >> MIN_TU_LOG2; + + pps->width32 = AV_CEIL_RSHIFT(pps->width, 5); + pps->height32 = AV_CEIL_RSHIFT(pps->height, 5); + pps->width64 = AV_CEIL_RSHIFT(pps->width, 6); + pps->height64 = AV_CEIL_RSHIFT(pps->height, 6); +} + +static void pps_bd(VVCPPS *pps) +{ + const H266RawPPS *r = pps->r; + + for (int i = 0, j = 0; i < r->num_tile_columns; i++) { + pps->col_bd[i] = j; + j += r->col_width_val[i]; + for (int k = pps->col_bd[i]; k < j; k++) + pps->ctb_to_col_bd[k] = pps->col_bd[i]; + } + + for (int i = 0, j = 0; i < r->num_tile_rows; i++) { + pps->row_bd[i] = j; + j += r->row_height_val[i]; + for (int k = pps->row_bd[i]; k < j; k++) + pps->ctb_to_row_bd[k] = pps->row_bd[i]; + } +} + + +static int next_tile_idx(int tile_idx, const int i, const H266RawPPS *r) +{ + if (r->pps_tile_idx_delta_present_flag) { + tile_idx += r->pps_tile_idx_delta_val[i]; + } else { + tile_idx += r->pps_slice_width_in_tiles_minus1[i] + 1; + if (tile_idx % r->num_tile_columns == 0) + tile_idx += (r->pps_slice_height_in_tiles_minus1[i]) * r->num_tile_columns; + } + return tile_idx; +} + +static void tile_xy(int *tile_x, int *tile_y, const int tile_idx, const VVCPPS *pps) +{ + *tile_x = tile_idx % pps->r->num_tile_columns; + *tile_y = tile_idx / pps->r->num_tile_columns; +} + +static void ctu_xy(int *ctu_x, int *ctu_y, const int tile_x, const int tile_y, const VVCPPS *pps) +{ + *ctu_x = pps->col_bd[tile_x]; + *ctu_y = pps->row_bd[tile_y]; +} + +static int ctu_rs(const int ctu_x, const int ctu_y, const VVCPPS *pps) +{ + return pps->ctb_width * ctu_y + ctu_x; +} + +static int pps_add_ctus(VVCPPS *pps, int *off, const int ctu_x, const int ctu_y, + const int w, const int h) +{ + int start = *off; + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + pps->ctb_addr_in_slice[*off] = ctu_rs(ctu_x + x, ctu_y + y, pps); + (*off)++; + } + } + return *off - start; +} + +static int pps_one_tile_slices(VVCPPS *pps, const int tile_idx, int i, int *off) +{ + const H266RawPPS *r = pps->r; + int ctu_x, ctu_y, ctu_y_end, tile_x, tile_y; + + tile_xy(&tile_x, &tile_y, tile_idx, pps); + ctu_xy(&ctu_x, &ctu_y, tile_x, tile_y, pps); + ctu_y_end = ctu_y + r->row_height_val[tile_y]; + while (ctu_y < ctu_y_end) { + pps->slice_start_offset[i] = *off; + pps->num_ctus_in_slice[i] = pps_add_ctus(pps, off, ctu_x, ctu_y, + r->col_width_val[tile_x], r->slice_height_in_ctus[i]); + ctu_y += r->slice_height_in_ctus[i++]; + } + i--; + return i; +} + +static void pps_multi_tiles_slice(VVCPPS *pps, const int tile_idx, const int i, int *off) +{ + const H266RawPPS *r = pps->r; + int ctu_x, ctu_y,tile_x, tile_y; + + tile_xy(&tile_x, &tile_y, tile_idx, pps); + pps->slice_start_offset[i] = *off; + pps->num_ctus_in_slice[i] = 0; + for (int ty = tile_y; ty <= tile_y + r->pps_slice_height_in_tiles_minus1[i]; ty++) { + for (int tx = tile_x; tx <= tile_x + r->pps_slice_width_in_tiles_minus1[i]; tx++) { + ctu_xy(&ctu_x, &ctu_y, tx, ty, pps); + pps->num_ctus_in_slice[i] += pps_add_ctus(pps, off, ctu_x, ctu_y, + r->col_width_val[tx], r->row_height_val[ty]); + } + } +} + +static void pps_rect_slice(VVCPPS* pps) +{ + const H266RawPPS* r = pps->r; + int tile_idx = 0, off = 0; + + for (int i = 0; i < r->pps_num_slices_in_pic_minus1 + 1; i++) { + if (!r->pps_slice_width_in_tiles_minus1[i] && + !r->pps_slice_height_in_tiles_minus1[i]) { + i = pps_one_tile_slices(pps, tile_idx, i, &off); + } else { + pps_multi_tiles_slice(pps, tile_idx, i, &off); + + } + tile_idx = next_tile_idx(tile_idx, i, r); + } +} + +static void pps_no_rect_slice(VVCPPS* pps) +{ + const H266RawPPS* r = pps->r; + int ctu_x, ctu_y, off = 0; + + for (int tile_y = 0; tile_y < r->num_tile_rows; tile_y++) { + for (int tile_x = 0; tile_x < r->num_tile_columns; tile_x++) { + ctu_xy(&ctu_x, &ctu_y, tile_x, tile_y, pps); + pps_add_ctus(pps, &off, ctu_x, ctu_y, r->col_width_val[tile_x], r->row_height_val[tile_y]); + } + } +} + +static void pps_slice_map(VVCPPS *pps) +{ + if (pps->r->pps_rect_slice_flag) + pps_rect_slice(pps); + else + pps_no_rect_slice(pps); +} + +static void pps_ref_wraparound_offset(VVCPPS *pps, const VVCSPS *sps) +{ + const H266RawPPS *r = pps->r; + + if (r->pps_ref_wraparound_enabled_flag) + pps->ref_wraparound_offset = (pps->width / sps->min_cb_size_y) - r->pps_pic_width_minus_wraparound_offset; +} + +static int pps_derive(VVCPPS *pps, const VVCSPS *sps) +{ + pps_chroma_qp_offset(pps); + pps_width_height(pps, sps); + pps_bd(pps); + pps_slice_map(pps); + pps_ref_wraparound_offset(pps, sps); + return 0; +} + +static void pps_free(uint8_t *data) +{ + VVCPPS *pps = (VVCPPS *)data; + av_buffer_unref(&pps->rref); +} + +static AVBufferRef *pps_alloc(const H266RawPPS *rpps, AVBufferRef *rpps_buf, const VVCSPS *sps) +{ + int ret; + AVBufferRef *pps_buf; + VVCPPS *pps; + + pps_buf = ps_alloc(sizeof(*pps), pps_free); + if (!pps_buf) + return NULL; + pps = (VVCPPS *)pps_buf->data; + + ret = av_buffer_replace(&pps->rref, rpps_buf); + if (ret < 0) + goto fail; + pps->r = rpps; + + ret = pps_derive(pps, sps); + if (ret < 0) + goto fail; + + return pps_buf; + +fail: + av_buffer_unref(&pps_buf); + return NULL; +} + +static int decode_pps(VVCParamSets *ps, + const H266RawPPS *rpps, AVBufferRef *rpps_buf) +{ + AVBufferRef *pps_buf; + int ret = 0; + const int pps_id = rpps->pps_pic_parameter_set_id; + const int sps_id = rpps->pps_seq_parameter_set_id; + + pps_buf = ps->pps_list[pps_id]; + if (pps_buf && ((VVCPPS*)pps_buf->data)->r == rpps) + return 0; + + pps_buf = pps_alloc(rpps, rpps_buf, (VVCSPS*)(ps->sps_list[sps_id]->data)); + if (!pps_buf) + return AVERROR(ENOMEM); + + ret = av_buffer_replace(&ps->pps_list[pps_id], pps_buf); + + av_buffer_unref(&pps_buf); + return ret; +} + +#define WEIGHT_TABLE(x) \ + w->nb_weights[L##x] = r->num_weights_l##x; \ + for (int i = 0; i < w->nb_weights[L##x]; i++) { \ + w->weight_flag[L##x][LUMA][i] = r->luma_weight_l##x##_flag[i]; \ + w->weight_flag[L##x][CHROMA][i] = r->chroma_weight_l##x##_flag[i]; \ + w->weight[L##x][LUMA][i] = denom[LUMA] + r->delta_luma_weight_l##x[i]; \ + w->offset[L##x][LUMA][i] = r->luma_offset_l##x[i]; \ + for (int j = CB; j <= CR; j++) { \ + w->weight[L##x][j][i] = denom[CHROMA] + r->delta_chroma_weight_l##x[i][j - 1]; \ + w->offset[L##x][j][i] = 128 + r->delta_chroma_offset_l##x[i][j - 1]; \ + w->offset[L##x][j][i] -= (128 * w->weight[L##x][j][i]) >> w->log2_denom[CHROMA]; \ + w->offset[L##x][j][i] = av_clip_intp2(w->offset[L##x][j][i], 7); \ + } \ + } \ + +static void pred_weight_table(PredWeightTable *w, const H266RawPredWeightTable *r) +{ + int denom[2]; + + w->log2_denom[LUMA] = r->luma_log2_weight_denom; + w->log2_denom[CHROMA] = w->log2_denom[LUMA] + r->delta_chroma_log2_weight_denom; + denom[LUMA] = 1 << w->log2_denom[LUMA]; + denom[CHROMA] = 1 << w->log2_denom[CHROMA]; + WEIGHT_TABLE(0) + WEIGHT_TABLE(1) +} + +// 8.3.1 Decoding process for picture order count +static int ph_compute_poc(const H266RawPictureHeader *ph, const H266RawSPS *sps, const int poc_tid0, const int is_clvss) +{ + const int max_poc_lsb = 1 << (sps->sps_log2_max_pic_order_cnt_lsb_minus4 + 4); + const int prev_poc_lsb = poc_tid0 % max_poc_lsb; + const int prev_poc_msb = poc_tid0 - prev_poc_lsb; + const int poc_lsb = ph->ph_pic_order_cnt_lsb; + int poc_msb; + + if (ph->ph_poc_msb_cycle_present_flag) { + poc_msb = ph->ph_poc_msb_cycle_val * max_poc_lsb; + } else if (is_clvss) { + poc_msb = 0; + } else { + if (poc_lsb < prev_poc_lsb && prev_poc_lsb - poc_lsb >= max_poc_lsb / 2) + poc_msb = prev_poc_msb + max_poc_lsb; + else if (poc_lsb > prev_poc_lsb && poc_lsb - prev_poc_lsb > max_poc_lsb / 2) + poc_msb = prev_poc_msb - max_poc_lsb; + else + poc_msb = prev_poc_msb; + } + + return poc_msb + poc_lsb; +} + +static av_always_inline int lmcs_derive_lut_sample(int sample, + int *pivot1, int *pivot2, int *scale_coeff, const int idx, const int max) +{ + const int lut_sample = + pivot1[idx] + ((scale_coeff[idx] * (sample - pivot2[idx]) + (1<< 10)) >> 11); + return av_clip(lut_sample, 0, max - 1); +} + +//8.8.2.2 Inverse mapping process for a luma sample +static int lmcs_derive_lut(VVCLMCS *lmcs, const AVBufferRef *lmcs_buf, const H266RawSPS *sps) +{ + const H266RawAPS *rlmcs; + const int bit_depth = (sps->sps_bitdepth_minus8 + 8); + const int max = (1 << bit_depth); + const int org_cw = max / LMCS_MAX_BIN_SIZE; + const int shift = av_log2(org_cw); + const int off = 1 << (shift - 1); + int cw[LMCS_MAX_BIN_SIZE]; + int input_pivot[LMCS_MAX_BIN_SIZE]; + int scale_coeff[LMCS_MAX_BIN_SIZE]; + int inv_scale_coeff[LMCS_MAX_BIN_SIZE]; + int i, delta_crs; + if (bit_depth > LMCS_MAX_BIT_DEPTH) + return AVERROR_PATCHWELCOME; + + if (!lmcs_buf) + return AVERROR_INVALIDDATA; + rlmcs = (const H266RawAPS *)lmcs_buf->data; + + + lmcs->min_bin_idx = rlmcs->lmcs_min_bin_idx; + lmcs->max_bin_idx = LMCS_MAX_BIN_SIZE - 1 - rlmcs->lmcs_min_bin_idx; + + memset(cw, 0, sizeof(cw)); + for (int i = lmcs->min_bin_idx; i <= lmcs->max_bin_idx; i++) + cw[i] = org_cw + (1 - 2 * rlmcs->lmcs_delta_sign_cw_flag[i]) * rlmcs->lmcs_delta_abs_cw[i]; + + delta_crs = (1 - 2 * rlmcs->lmcs_delta_sign_crs_flag) * rlmcs->lmcs_delta_abs_crs; + + lmcs->pivot[0] = 0; + for (i = 0; i < LMCS_MAX_BIN_SIZE; i++) { + input_pivot[i] = i * org_cw; + lmcs->pivot[i + 1] = lmcs->pivot[i] + cw[i]; + scale_coeff[i] = (cw[i] * (1 << 11) + off) >> shift; + if (cw[i] == 0) { + inv_scale_coeff[i] = 0; + lmcs->chroma_scale_coeff[i] = (1 << 11); + } else { + inv_scale_coeff[i] = org_cw * (1 << 11) / cw[i]; + lmcs->chroma_scale_coeff[i] = org_cw * (1 << 11) / (cw[i] + delta_crs); + } + } + + //derive lmcs_fwd_lut + for (int sample = 0; sample < max; sample++) { + const int idx_y = sample / org_cw; + const int fwd_sample = lmcs_derive_lut_sample(sample, lmcs->pivot, + input_pivot, scale_coeff, idx_y, max); + if (bit_depth > 8) + ((uint16_t *)lmcs->fwd_lut)[sample] = fwd_sample; + else + lmcs->fwd_lut[sample] = fwd_sample; + + } + + //derive lmcs_inv_lut + i = lmcs->min_bin_idx; + for (int sample = 0; sample < max; sample++) { + int inv_sample; + while (sample >= lmcs->pivot[i + 1] && i <= lmcs->max_bin_idx) + i++; + + inv_sample = lmcs_derive_lut_sample(sample, input_pivot, lmcs->pivot, + inv_scale_coeff, i, max); + + if (bit_depth > 8) + ((uint16_t *)lmcs->inv_lut)[sample] = inv_sample; + else + lmcs->inv_lut[sample] = inv_sample; + } + + return 0; +} + +static int ph_scaling_list(VVCFrameParamSets *fps, AVBufferRef *sl_buf) +{ + int ret; + + if (!sl_buf) + return AVERROR_INVALIDDATA; + + ret = av_buffer_replace(&fps->sl_buf, sl_buf); + if (ret < 0) + return ret; + + fps->sl = (const VVCScalingList*)sl_buf->data; + + return 0; +} + +static int ph_derive(VVCPH *ph, const H266RawSPS *sps, const H266RawPPS *pps, const int poc_tid0, const int is_clvss) +{ + if (sps->sps_affine_enabled_flag) + ph->max_num_subblock_merge_cand = 5 - sps->sps_five_minus_max_num_subblock_merge_cand; + else + ph->max_num_subblock_merge_cand = sps->sps_sbtmvp_enabled_flag && ph->r->ph_temporal_mvp_enabled_flag; + + ph->poc = ph_compute_poc(ph->r, sps, poc_tid0, is_clvss); + + if (pps->pps_wp_info_in_ph_flag) + pred_weight_table(&ph->pwt, &ph->r->ph_pred_weight_table); + + return 0; +} + +static int decode_ph(VVCFrameParamSets *fps, const H266RawPictureHeader *rph, AVBufferRef *rph_ref, + const int poc_tid0, const int is_clvss) +{ + int ret; + VVCPH *ph = &fps->ph; + const H266RawSPS *sps = fps->sps->r; + const H266RawPPS *pps = fps->pps->r; + + ret = av_buffer_replace(&ph->rref, rph_ref); + if (ret < 0) + return ret; + ph->r = rph; + + ret = ph_derive(ph, sps, pps, poc_tid0, is_clvss); + if (ret < 0) + return ret; + + return 0; +} + +static int decode_ps(VVCParamSets *ps, const CodedBitstreamH266Context *h266, void *log_ctx) +{ + const H266RawPictureHeader *ph = h266->ph; + const H266RawPPS *rpps; + const H266RawSPS *rsps; + AVBufferRef *rsps_buf, *rpps_buf; + int ret; + + if (!ph) + return AVERROR_INVALIDDATA; + + rpps = h266->pps[ph->ph_pic_parameter_set_id]; + rpps_buf = h266->pps_ref[ph->ph_pic_parameter_set_id]; + if (!rpps || !rpps_buf) + return AVERROR_INVALIDDATA; + + rsps = h266->sps[rpps->pps_seq_parameter_set_id]; + rsps_buf = h266->sps_ref[rpps->pps_seq_parameter_set_id]; + if (!rsps || !rsps_buf) + return AVERROR_INVALIDDATA; + + ret = decode_sps(ps, rsps, rsps_buf, log_ctx); + if (ret < 0) + return ret; + + ret = decode_pps(ps, rpps, rpps_buf); + if (ret < 0) + return ret; + + return 0; +} + +static int decode_frame_ps(VVCFrameParamSets *fps, const VVCParamSets *ps, + const CodedBitstreamH266Context *h266, const int poc_tid0, const int is_clvss) +{ + const H266RawPictureHeader *ph = h266->ph; + const H266RawPPS *rpps; + int ret; + + if (!ph) + return AVERROR_INVALIDDATA; + + rpps = h266->pps[ph->ph_pic_parameter_set_id]; + if (!rpps) + return AVERROR_INVALIDDATA; + + ret = av_buffer_replace(&fps->sps_buf, ps->sps_list[rpps->pps_seq_parameter_set_id]); + if (ret < 0) + return ret; + fps->sps = (VVCSPS *)fps->sps_buf->data; + + ret = av_buffer_replace(&fps->pps_buf, ps->pps_list[rpps->pps_pic_parameter_set_id]); + if (ret < 0) + return ret; + fps->pps = (VVCPPS *)fps->pps_buf->data; + + ret = decode_ph(fps, ph, h266->ph_ref, poc_tid0, is_clvss); + if (ret < 0) + return ret; + + if (ph->ph_explicit_scaling_list_enabled_flag) { + ret = ph_scaling_list(fps, ps->scaling_list[ph->ph_scaling_list_aps_id]); + if (ret < 0) + return ret; + } + + if (ph->ph_lmcs_enabled_flag) { + ret = lmcs_derive_lut(&fps->lmcs, ps->lmcs_list[ph->ph_lmcs_aps_id], fps->sps->r); + if (ret < 0) + return ret; + } + + for (int i = 0; i < FF_ARRAY_ELEMS(fps->alf_list); i++) { + ret = av_buffer_replace(&fps->alf_list[i], ps->alf_list[i]); + if (ret < 0) + return ret; + } + return 0; +} + +static void decode_recovery_flag(VVCContext *s) +{ + if (IS_IDR(s)) + s->no_output_before_recovery_flag = 0; + else if (IS_CRA(s) || IS_GDR(s)) + s->no_output_before_recovery_flag = s->last_eos; +} + +static void decode_recovery_poc(VVCContext *s, const VVCPH *ph) +{ + if (s->no_output_before_recovery_flag) { + if (IS_GDR(s)) + s->gdr_recovery_point_poc = ph->poc + ph->r->ph_recovery_poc_cnt; + if (!GDR_IS_RECOVERED(s) && s->gdr_recovery_point_poc <= ph->poc) + GDR_SET_RECOVERED(s); + } +} + +int ff_vvc_decode_frame_ps(VVCFrameParamSets *fps, struct VVCContext *s) +{ + int ret = 0; + VVCParamSets *ps = &s->ps; + const CodedBitstreamH266Context *h266 = s->cbc->priv_data; + + ret = decode_ps(ps, h266, s->avctx); + if (ret < 0) + return ret; + + decode_recovery_flag(s); + ret = decode_frame_ps(fps, ps, h266, s->poc_tid0, IS_CLVSS(s)); + decode_recovery_poc(s, &fps->ph); + return ret; +} + +void ff_vvc_frame_ps_free(VVCFrameParamSets *fps) +{ + av_buffer_unref(&fps->sps_buf); + av_buffer_unref(&fps->pps_buf); + av_buffer_unref(&fps->ph.rref); + av_buffer_unref(&fps->sl_buf); + for (int i = 0; i < FF_ARRAY_ELEMS(fps->alf_list); i++) + av_buffer_unref(&fps->alf_list[i]); +} + +void ff_vvc_ps_uninit(VVCParamSets *ps) +{ + int i; + + for (i = 0; i < FF_ARRAY_ELEMS(ps->scaling_list); i++) + av_buffer_unref(&ps->scaling_list[i]); + for (i = 0; i < FF_ARRAY_ELEMS(ps->lmcs_list); i++) + av_buffer_unref(&ps->lmcs_list[i]); + for (i = 0; i < FF_ARRAY_ELEMS(ps->alf_list); i++) + av_buffer_unref(&ps->alf_list[i]); + for (i = 0; i < FF_ARRAY_ELEMS(ps->vps_list); i++) + av_buffer_unref(&ps->vps_list[i]); + for (i = 0; i < FF_ARRAY_ELEMS(ps->sps_list); i++) + av_buffer_unref(&ps->sps_list[i]); + for (i = 0; i < FF_ARRAY_ELEMS(ps->pps_list); i++) + av_buffer_unref(&ps->pps_list[i]); +} + +static void alf_coeff(int16_t *coeff, + const uint8_t *abs, const uint8_t *sign, const int size) +{ + for (int i = 0; i < size; i++) + coeff[i] = (1 - 2 * sign[i]) * abs[i]; +} + +static void alf_coeff_cc(int16_t *coeff, + const uint8_t *mapped_abs, const uint8_t *sign) +{ + for (int i = 0; i < ALF_NUM_COEFF_CC; i++) { + int c = mapped_abs[i];; + if (c) + c = (1 - 2 * sign[i]) * (1 << (c - 1)); + coeff[i] = c; + } +} + +static void alf_luma(VVCALF *alf) +{ + const H266RawAPS *aps = (const H266RawAPS *)alf->rref->data; + + if (!aps->alf_luma_filter_signal_flag) + return; + + for (int i = 0; i < ALF_NUM_FILTERS_LUMA; i++) { + const int ref = aps->alf_luma_coeff_delta_idx[i]; + const uint8_t *abs = aps->alf_luma_coeff_abs[ref]; + const uint8_t *sign = aps->alf_luma_coeff_sign[ref]; + + alf_coeff(alf->luma_coeff[i], abs, sign, ALF_NUM_COEFF_LUMA); + memcpy(alf->luma_clip_idx[i], aps->alf_luma_clip_idx[ref], + sizeof(alf->luma_clip_idx[i])); + } +} + +static void alf_chroma(VVCALF *alf) +{ + const H266RawAPS *aps = (const H266RawAPS *)alf->rref->data; + + + if (!aps->alf_chroma_filter_signal_flag) + return; + + alf->num_chroma_filters = aps->alf_chroma_num_alt_filters_minus1 + 1; + for (int i = 0; i < alf->num_chroma_filters; i++) { + const uint8_t *abs = aps->alf_chroma_coeff_abs[i]; + const uint8_t *sign = aps->alf_chroma_coeff_sign[i]; + + alf_coeff(alf->chroma_coeff[i], abs, sign, ALF_NUM_COEFF_CHROMA); + memcpy(alf->chroma_clip_idx[i], aps->alf_chroma_clip_idx[i], + sizeof(alf->chroma_clip_idx[i])); + } +} + +static void alf_cc(VVCALF *alf) +{ + const H266RawAPS *aps = (const H266RawAPS *)alf->rref->data; + const uint8_t (*abs[])[ALF_NUM_COEFF_CC] = + { aps->alf_cc_cb_mapped_coeff_abs, aps->alf_cc_cr_mapped_coeff_abs }; + const uint8_t (*sign[])[ALF_NUM_COEFF_CC] = + {aps->alf_cc_cb_coeff_sign, aps->alf_cc_cr_coeff_sign }; + const int signaled[] = { aps->alf_cc_cb_filter_signal_flag, aps->alf_cc_cr_filter_signal_flag}; + + alf->num_cc_filters[0] = aps->alf_cc_cb_filters_signalled_minus1 + 1; + alf->num_cc_filters[1] = aps->alf_cc_cr_filters_signalled_minus1 + 1; + + for (int idx = 0; idx < 2; idx++) { + if (signaled[idx]) { + for (int i = 0; i < alf->num_cc_filters[idx]; i++) + alf_coeff_cc(alf->cc_coeff[idx][i], abs[idx][i], sign[idx][i]); + } + } +} + +static void alf_derive(VVCALF *alf) +{ + alf_luma(alf); + alf_chroma(alf); + alf_cc(alf); +} + +static void alf_free(uint8_t *data) +{ + VVCALF *alf = (VVCALF*)data; + + av_buffer_unref(&alf->rref); +} + +static AVBufferRef *alf_alloc(AVBufferRef *aps_buf) +{ + int ret; + VVCALF *alf; + AVBufferRef *buf = ps_alloc(sizeof(*alf), alf_free); + if (!buf) + return NULL; + + alf = (VVCALF *)buf->data; + ret = av_buffer_replace(&alf->rref, aps_buf); + if (ret < 0) + goto fail; + + alf_derive(alf); + + return buf; + +fail: + av_buffer_unref(&buf); + return buf; +} + +static int aps_decode_alf(AVBufferRef **alf_buf, AVBufferRef *aps_buf) +{ + int ret; + AVBufferRef *buf = alf_alloc(aps_buf); + + if (!buf) + return AVERROR(ENOMEM); + + ret = av_buffer_replace(alf_buf, buf); + + av_buffer_unref(&buf); + return ret; + +} + +static int is_luma_list(const int id) +{ + return id % VVC_MAX_SAMPLE_ARRAYS == SL_START_4x4 || id == SL_START_64x64 + 1; +} + +static int derive_matrix_size(const int id) +{ + return id < SL_START_4x4 ? 2 : (id < SL_START_8x8 ? 4 : 8); +} + +// 7.4.3.20 Scaling list data semantics +static int scaling_derive(VVCScalingList *sl) +{ + const H266RawAPS *aps = (const H266RawAPS *)sl->rref->data; + + for (int id = 0; id < SL_MAX_ID; id++) { + const int matrix_size = derive_matrix_size(id); + const int log2_size = log2(matrix_size); + const int list_size = matrix_size * matrix_size; + int coeff[SL_MAX_MATRIX_SIZE * SL_MAX_MATRIX_SIZE]; + const uint8_t *pred; + const int *scaling_list; + int dc = 0; + + if (aps->aps_chroma_present_flag || is_luma_list(id)) { + if (!aps->scaling_list_copy_mode_flag[id]) { + int next_coef = 0; + + if (id >= SL_START_16x16) + dc = next_coef = aps->scaling_list_dc_coef[id - SL_START_16x16]; + + for (int i = 0; i < list_size; i++) { + const int x = ff_vvc_diag_scan_x[3][3][i]; + const int y = ff_vvc_diag_scan_y[3][3][i]; + + if (!(id >= SL_START_64x64 && x >= 4 && y >= 4)) + next_coef += aps->scaling_list_delta_coef[id][i]; + coeff[i] = next_coef; + } + } + } + + //dc + if (id >= SL_START_16x16) { + if (!aps->scaling_list_copy_mode_flag[id] && !aps->scaling_list_pred_mode_flag[id]) { + sl->scaling_matrix_dc_rec[id - SL_START_16x16] = 8; + } else if (!aps->scaling_list_pred_id_delta[id]) { + sl->scaling_matrix_dc_rec[id - SL_START_16x16] = 16; + } else { + const int ref_id = id - aps->scaling_list_pred_id_delta[id]; + if (ref_id >= SL_START_16x16) + dc += sl->scaling_matrix_dc_rec[ref_id - SL_START_16x16]; + else + dc += sl->scaling_matrix_rec[ref_id][0]; + sl->scaling_matrix_dc_rec[id - SL_START_16x16] = dc & 255; + } + } + + //ac + scaling_list = aps->scaling_list_copy_mode_flag[id] ? ff_vvc_scaling_list0 : coeff; + if (!aps->scaling_list_copy_mode_flag[id] && !aps->scaling_list_pred_mode_flag[id]) + pred = ff_vvc_scaling_pred_8; + else if (!aps->scaling_list_pred_id_delta[id]) + pred = ff_vvc_scaling_pred_16; + else + pred = sl->scaling_matrix_rec[id - aps->scaling_list_pred_id_delta[id]]; + for (int i = 0; i < list_size; i++) { + const int x = ff_vvc_diag_scan_x[log2_size][log2_size][i]; + const int y = ff_vvc_diag_scan_y[log2_size][log2_size][i]; + const int off = y * matrix_size + x; + sl->scaling_matrix_rec[id][off] = (pred[off] + scaling_list[i]) & 255; + } + } + + return 0; +} + +static void scaling_free(uint8_t *data) +{ + VVCScalingList *sl = (VVCScalingList*)data; + + av_buffer_unref(&sl->rref); +} + +static AVBufferRef *scaling_alloc(AVBufferRef *aps_buf) +{ + int ret; + VVCScalingList *sl; + AVBufferRef *buf = ps_alloc(sizeof(*sl), scaling_free); + + if (!buf) + return NULL; + sl = (VVCScalingList *)buf->data; + + ret = av_buffer_replace(&sl->rref, aps_buf); + if (ret < 0) + goto fail; + + ret = scaling_derive(sl); + if (ret < 0) + goto fail; + + return buf; + +fail: + av_buffer_unref(&buf); + return buf; +} + +static int aps_decode_scaling(AVBufferRef **sl_buf, AVBufferRef *aps_buf) +{ + int ret; + AVBufferRef *buf = scaling_alloc(aps_buf); + if (!buf) + return AVERROR(ENOMEM); + + ret = av_buffer_replace(sl_buf, buf); + + av_buffer_unref(&buf); + return ret; +} + +int ff_vvc_decode_aps(VVCParamSets *ps, const CodedBitstreamUnit *unit) +{ + AVBufferRef *aps_buf = unit->content_ref; + const H266RawAPS *aps = unit->content; + int ret = 0; + + if (!aps_buf || !aps) + return AVERROR_INVALIDDATA; + + switch (aps->aps_params_type) { + case APS_ALF: + ret = aps_decode_alf(&ps->alf_list[aps->aps_adaptation_parameter_set_id], aps_buf); + break; + case APS_LMCS: + ret = av_buffer_replace(&ps->lmcs_list[aps->aps_adaptation_parameter_set_id], aps_buf); + break; + case APS_SCALING: + ret = aps_decode_scaling(&ps->scaling_list[aps->aps_adaptation_parameter_set_id], aps_buf); + break; + } + + return ret; +} + +static int sh_subpic_idx(const H266RawSliceHeader *sh, const H266RawSPS *sps, const H266RawPPS *pps) +{ + const int sps_num_subpics = sps->sps_num_subpics_minus1 + 1; + + if (!sps->sps_subpic_info_present_flag) + return 0; + + if (!sps->sps_subpic_id_mapping_explicitly_signalled_flag) + return sh->sh_subpic_id; + + for (int i = 0; i <= sps_num_subpics; i++) { + if (pps->pps_subpic_id[i] == sh->sh_subpic_id) + return i; + } + + return sps_num_subpics; +} + +static void sh_slice_address(VVCSH *sh, const H266RawSPS *sps, const VVCPPS *pps) +{ + const int slice_address = sh->r->sh_slice_address; + const int curr_subpic_idx = sh_subpic_idx(sh->r, sps, pps->r); + + if (pps->r->pps_rect_slice_flag) { + int pic_level_slice_idx = slice_address; + for (int j = 0; j < curr_subpic_idx; j++) + pic_level_slice_idx += pps->r->num_slices_in_subpic[j]; + sh->ctb_addr_in_curr_slice = pps->ctb_addr_in_slice + pps->slice_start_offset[pic_level_slice_idx]; + sh->num_ctus_in_curr_slice = pps->num_ctus_in_slice[pic_level_slice_idx]; + } else { + int tile_x = slice_address % pps->r->num_tile_columns; + int tile_y = slice_address / pps->r->num_tile_columns; + const int slice_start_ctb = pps->row_bd[tile_y] * pps->ctb_width + pps->col_bd[tile_x] * pps->r->row_height_val[tile_y]; + + sh->ctb_addr_in_curr_slice = pps->ctb_addr_in_slice + slice_start_ctb; + + sh->num_ctus_in_curr_slice = 0; + for (int tile_idx = slice_address; tile_idx <= slice_address + sh->r->sh_num_tiles_in_slice_minus1; tile_idx++) { + tile_x = tile_idx % pps->r->num_tile_columns; + tile_y = tile_idx / pps->r->num_tile_columns; + sh->num_ctus_in_curr_slice += pps->r->row_height_val[tile_y] * pps->r->col_width_val[tile_x]; + } + } +} + +static void sh_qp_y(VVCSH *sh, const H266RawPPS *pps, const H266RawPictureHeader *ph) +{ + const int init_qp = pps->pps_init_qp_minus26 + 26; + + if (!pps->pps_qp_delta_info_in_ph_flag) + sh->slice_qp_y = init_qp + sh->r->sh_qp_delta; + else + sh->slice_qp_y = init_qp + ph->ph_qp_delta; +} + +static void sh_inter(VVCSH *sh, const H266RawSPS *sps, const H266RawPPS *pps) +{ + const H266RawSliceHeader *rsh = sh->r; + if (!pps->pps_wp_info_in_ph_flag && + ((pps->pps_weighted_pred_flag && IS_P(rsh)) || + (pps->pps_weighted_bipred_flag && IS_B(rsh)))) + pred_weight_table(&sh->pwt, &rsh->sh_pred_weight_table); +} + +static void sh_deblock_offsets(VVCSH *sh) +{ + const H266RawSliceHeader *r = sh->r; + + if (!r->sh_deblocking_filter_disabled_flag) { + sh->deblock.beta_offset[LUMA] = r->sh_luma_beta_offset_div2 << 1; + sh->deblock.tc_offset[LUMA] = r->sh_luma_tc_offset_div2 << 1; + sh->deblock.beta_offset[CB] = r->sh_cb_beta_offset_div2 << 1; + sh->deblock.tc_offset[CB] = r->sh_cb_tc_offset_div2 << 1; + sh->deblock.beta_offset[CR] = r->sh_cr_beta_offset_div2 << 1; + sh->deblock.tc_offset[CR] = r->sh_cr_tc_offset_div2 << 1; + } +} + +static void sh_partition_constraints(VVCSH *sh, const H266RawSPS *sps, const H266RawPictureHeader *ph) +{ + const int min_cb_log2_size_y = sps->sps_log2_min_luma_coding_block_size_minus2 + 2; + int min_qt_log2_size_y[2]; + + if (IS_I(sh->r)) { + min_qt_log2_size_y[LUMA] = (min_cb_log2_size_y + ph->ph_log2_diff_min_qt_min_cb_intra_slice_luma); + min_qt_log2_size_y[CHROMA] = (min_cb_log2_size_y + ph->ph_log2_diff_min_qt_min_cb_intra_slice_chroma); + + sh->max_bt_size[LUMA] = 1 << (min_qt_log2_size_y[LUMA] + ph->ph_log2_diff_max_bt_min_qt_intra_slice_luma); + sh->max_bt_size[CHROMA] = 1 << (min_qt_log2_size_y[CHROMA]+ ph->ph_log2_diff_max_bt_min_qt_intra_slice_chroma); + + sh->max_tt_size[LUMA] = 1 << (min_qt_log2_size_y[LUMA] + ph->ph_log2_diff_max_tt_min_qt_intra_slice_luma); + sh->max_tt_size[CHROMA] = 1 << (min_qt_log2_size_y[CHROMA]+ ph->ph_log2_diff_max_tt_min_qt_intra_slice_chroma); + + sh->max_mtt_depth[LUMA] = ph->ph_max_mtt_hierarchy_depth_intra_slice_luma; + sh->max_mtt_depth[CHROMA] = ph->ph_max_mtt_hierarchy_depth_intra_slice_chroma; + + sh->cu_qp_delta_subdiv = ph->ph_cu_qp_delta_subdiv_intra_slice; + sh->cu_chroma_qp_offset_subdiv = ph->ph_cu_chroma_qp_offset_subdiv_intra_slice; + } else { + for (int i = LUMA; i <= CHROMA; i++) { + min_qt_log2_size_y[i] = (min_cb_log2_size_y + ph->ph_log2_diff_min_qt_min_cb_inter_slice); + sh->max_bt_size[i] = 1 << (min_qt_log2_size_y[i] + ph->ph_log2_diff_max_bt_min_qt_inter_slice); + sh->max_tt_size[i] = 1 << (min_qt_log2_size_y[i] + ph->ph_log2_diff_max_tt_min_qt_inter_slice); + sh->max_mtt_depth[i] = ph->ph_max_mtt_hierarchy_depth_inter_slice; + } + + sh->cu_qp_delta_subdiv = ph->ph_cu_qp_delta_subdiv_inter_slice; + sh->cu_chroma_qp_offset_subdiv = ph->ph_cu_chroma_qp_offset_subdiv_inter_slice; + } + + sh->min_qt_size[LUMA] = 1 << min_qt_log2_size_y[LUMA]; + sh->min_qt_size[CHROMA] = 1 << min_qt_log2_size_y[CHROMA]; +} + +static void sh_entry_points(VVCSH *sh, const H266RawSPS *sps, const VVCPPS *pps) +{ + if (sps->sps_entry_point_offsets_present_flag) { + for (int i = 1, j = 0; i < sh->num_ctus_in_curr_slice; i++) { + const int pre_ctb_addr_x = sh->ctb_addr_in_curr_slice[i - 1] % pps->ctb_width; + const int pre_ctb_addr_y = sh->ctb_addr_in_curr_slice[i - 1] / pps->ctb_width; + const int ctb_addr_x = sh->ctb_addr_in_curr_slice[i] % pps->ctb_width; + const int ctb_addr_y = sh->ctb_addr_in_curr_slice[i] / pps->ctb_width; + if (pps->ctb_to_row_bd[ctb_addr_y] != pps->ctb_to_row_bd[pre_ctb_addr_y] || + pps->ctb_to_col_bd[ctb_addr_x] != pps->ctb_to_col_bd[pre_ctb_addr_x] || + (ctb_addr_y != pre_ctb_addr_y && sps->sps_entropy_coding_sync_enabled_flag)) { + sh->entry_point_start_ctu[j++] = i; + } + } + } +} + +static int sh_derive(VVCSH *sh, const VVCFrameParamSets *fps) +{ + const H266RawSPS *sps = fps->sps->r; + const H266RawPPS *pps = fps->pps->r; + const H266RawPictureHeader *ph = fps->ph.r; + + sh_slice_address(sh, sps, fps->pps); + sh_inter(sh, sps, pps); + sh_qp_y(sh, pps, ph); + sh_deblock_offsets(sh); + sh_partition_constraints(sh, sps, ph); + sh_entry_points(sh, sps, fps->pps); + + return 0; +} + +int ff_vvc_decode_sh(VVCSH *sh, const VVCFrameParamSets *fps, const CodedBitstreamUnit *unit) +{ + int ret; + + if (!fps->sps || !fps->pps) + return AVERROR_INVALIDDATA; + + ret = av_buffer_replace(&sh->rref, unit->content_ref); + if (ret < 0) + return ret; + sh->r = (const H266RawSliceHeader *)unit->content; + + ret = sh_derive(sh, fps); + if (ret < 0) + return ret; + + return 0; +} diff --git a/libavcodec/vvc/vvc_ps.h b/libavcodec/vvc/vvc_ps.h new file mode 100644 index 00000000000..93fbd0b743a --- /dev/null +++ b/libavcodec/vvc/vvc_ps.h @@ -0,0 +1,284 @@ +/* + * VVC parameter set parser + * + * Copyright (C) 2023 Nuo Mi + * + * 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_VVC_PS_H +#define AVCODEC_VVC_PS_H + +#include "libavcodec/vvc.h" + +#define IS_IDR(s) ((s)->vcl_unit_type == VVC_IDR_W_RADL || (s)->vcl_unit_type == VVC_IDR_N_LP) +#define IS_CRA(s) ((s)->vcl_unit_type == VVC_CRA_NUT) +#define IS_IRAP(s) (IS_IDR(s) || IS_CRA(s)) +#define IS_GDR(s) ((s)->vcl_unit_type == VVC_GDR_NUT) +#define IS_CVSS(s) (IS_IRAP(s)|| IS_GDR(s)) +#define IS_CLVSS(s) (IS_CVSS(s) && s->no_output_before_recovery_flag) +#define IS_RASL(s) ((s)->vcl_unit_type == VVC_RASL_NUT) +#define IS_RADL(s) ((s)->vcl_unit_type == VVC_RADL_NUT) + +#define IS_I(rsh) ((rsh)->sh_slice_type == VVC_SLICE_TYPE_I) +#define IS_P(rsh) ((rsh)->sh_slice_type == VVC_SLICE_TYPE_P) +#define IS_B(rsh) ((rsh)->sh_slice_type == VVC_SLICE_TYPE_B) + +#define INV_POC INT_MIN +#define GDR_IS_RECOVERED(s) (s->gdr_recovery_point_poc == INV_POC) +#define GDR_SET_RECOVERED(s) (s->gdr_recovery_point_poc = INV_POC) + +#define LMCS_MAX_BIT_DEPTH 12 +#define LMCS_MAX_LUT_SIZE (1 << LMCS_MAX_BIT_DEPTH) +#define LMCS_MAX_BIN_SIZE 16 +#define LADF_MAX_INTERVAL 5 + +#define MAX_CTU_WIDTH (VVC_MAX_WIDTH / (1 << 5) + 1) +#define MAX_CTU_HEIGHT (VVC_MAX_HEIGHT / (1 << 5) + 1) + +enum { + CHROMA_FORMAT_MONO, + CHROMA_FORMAT_420, + CHROMA_FORMAT_422, + CHROMA_FORMAT_444, +}; + +typedef struct VVCSPS { + AVBufferRef *rref; + const H266RawSPS *r; + + //derived values + uint16_t width; + uint16_t height; + int hshift[VVC_MAX_SAMPLE_ARRAYS]; + int vshift[VVC_MAX_SAMPLE_ARRAYS]; + uint32_t max_pic_order_cnt_lsb; ///< MaxPicOrderCntLsb + + int pixel_shift; + enum AVPixelFormat pix_fmt; + + uint8_t bit_depth; ///< BitDepth + uint8_t qp_bd_offset; ///< QpBdOffset + uint8_t ctb_log2_size_y; ///< CtbLog2SizeY + uint8_t ctb_size_y; ///< CtbSizeY + uint8_t min_cb_log2_size_y; ///< MinCbLog2SizeY + uint8_t min_cb_size_y; ///< MinCbSizeY + uint8_t max_tb_size_y; ///< MaxTbSizeY + uint8_t max_ts_size; ///< MaxTsSize + uint8_t max_num_merge_cand; ///< MaxNumMergeCand + uint8_t max_num_ibc_merge_cand; ///< MaxNumIbcMergeCand + uint8_t max_num_gpm_merge_cand; ///< MaxNumGpmMergeCand + uint8_t num_ladf_intervals; ///< sps_num_ladf_intervals_minus2 + 2; + uint32_t ladf_interval_lower_bound[LADF_MAX_INTERVAL]; ///< SpsLadfIntervalLowerBound[] + uint8_t log2_parallel_merge_level; ///< sps_log2_parallel_merge_level_minus2 + 2; + uint8_t log2_transform_range; ///< Log2TransformRange + int chroma_qp_table[3][VVC_MAX_POINTS_IN_QP_TABLE]; +} VVCSPS; + +typedef struct DBParams { + int beta_offset[VVC_MAX_SAMPLE_ARRAYS]; + int tc_offset[VVC_MAX_SAMPLE_ARRAYS]; +} DBParams; + +typedef struct VVCPPS { + AVBufferRef *rref; + const H266RawPPS *r; + + //derived value; + int8_t chroma_qp_offset[3]; ///< pps_cb_qp_offset, pps_cr_qp_offset, pps_joint_cbcr_qp_offset_value + int8_t chroma_qp_offset_list[6][3]; ///< pps_cb_qp_offset_list, pps_cr_qp_offset_list, pps_joint_cbcr_qp_offset_list + + uint16_t width; + uint16_t height; + + uint16_t slice_start_offset [VVC_MAX_SLICES]; + uint16_t num_ctus_in_slice [VVC_MAX_SLICES]; + + uint16_t min_cb_width; + uint16_t min_cb_height; + + uint16_t ctb_width; + uint16_t ctb_height; + int ctb_count; + + uint16_t min_pu_width; + uint16_t min_pu_height; + uint16_t min_tu_width; + uint16_t min_tu_height; + + uint32_t ctb_addr_in_slice[MAX_CTU_WIDTH * MAX_CTU_HEIGHT]; ///< CtbAddrInCurrSlice for entire picture + uint16_t col_bd[VVC_MAX_TILE_COLUMNS]; + uint16_t row_bd[VVC_MAX_TILE_ROWS]; + uint16_t ctb_to_col_bd[MAX_CTU_WIDTH]; + uint16_t ctb_to_row_bd[MAX_CTU_HEIGHT]; + + uint16_t width32; ///< width in 32 pixels + uint16_t height32; ///< height in 32 pixels + uint16_t width64; ///< width in 64 pixels + uint16_t height64; ///< height in 64 pixels + + uint16_t ref_wraparound_offset; ///< PpsRefWraparoundOffset + +} VVCPPS; + +#define MAX_WEIGHTS 15 +typedef struct PredWeightTable { + int log2_denom[2]; ///< luma_log2_weight_denom, ChromaLog2WeightDenom + + int nb_weights[2]; ///< num_l0_weights, num_l1_weights + int weight_flag[2][2][MAX_WEIGHTS]; ///< luma_weight_l0_flag, chroma_weight_l0_flag, + ///< luma_weight_l1_flag, chroma_weight_l1_flag, + int weight[2][VVC_MAX_SAMPLE_ARRAYS][MAX_WEIGHTS]; ///< LumaWeightL0, LumaWeightL1, ChromaWeightL0, ChromaWeightL1 + int offset[2][VVC_MAX_SAMPLE_ARRAYS][MAX_WEIGHTS]; ///< luma_offset_l0, luma_offset_l1, ChromaOffsetL0, ChromaOffsetL1 +} PredWeightTable; + +typedef struct VVCPH { + AVBufferRef *rref; + const H266RawPictureHeader *r; + + //derived values + unsigned int max_num_subblock_merge_cand; ///< MaxNumSubblockMergeCand + int poc; ///< PicOrderCntVal + + PredWeightTable pwt; +} VVCPH; + +#define VVC_MAX_ALF_COUNT 8 +#define VVC_MAX_LMCS_COUNT 4 +#define VVC_MAX_SL_COUNT 8 + +#define ALF_NUM_FILTERS_LUMA 25 +#define ALF_NUM_FILTERS_CHROMA 8 +#define ALF_NUM_FILTERS_CC 5 + +#define ALF_NUM_COEFF_LUMA 12 +#define ALF_NUM_COEFF_CHROMA 6 +#define ALF_NUM_COEFF_CC 7 + +enum { + APS_ALF, + APS_LMCS, + APS_SCALING, +}; + +typedef struct VVCALF { + AVBufferRef *rref; + + int16_t luma_coeff [ALF_NUM_FILTERS_LUMA][ALF_NUM_COEFF_LUMA]; + uint8_t luma_clip_idx [ALF_NUM_FILTERS_LUMA][ALF_NUM_COEFF_LUMA]; + + uint8_t num_chroma_filters; + int16_t chroma_coeff [ALF_NUM_FILTERS_CHROMA][ALF_NUM_COEFF_CHROMA]; + uint8_t chroma_clip_idx[ALF_NUM_FILTERS_CHROMA][ALF_NUM_COEFF_CHROMA]; + + uint8_t num_cc_filters[2]; ///< alf_cc_cb_filters_signalled_minus1 + 1, alf_cc_cr_filters_signalled_minus1 + 1 + int16_t cc_coeff[2][ALF_NUM_FILTERS_CC][ALF_NUM_COEFF_CC]; +} VVCALF; + +typedef struct VVCLMCS { + int min_bin_idx; + int max_bin_idx; + + //*2 for high depth + uint8_t fwd_lut[LMCS_MAX_LUT_SIZE * 2]; + uint8_t inv_lut[LMCS_MAX_LUT_SIZE * 2]; + + int pivot[LMCS_MAX_BIN_SIZE + 1]; + int chroma_scale_coeff[LMCS_MAX_BIN_SIZE]; +} VVCLMCS; + +#define SL_MAX_ID 28 +#define SL_MAX_MATRIX_SIZE 8 + +enum { + SL_START_2x2 = 0, + SL_START_4x4 = 2, + SL_START_8x8 = 8, + SL_START_16x16 = 14, + SL_START_32x32 = 20, + SL_START_64x64 = 26, +}; + +typedef struct VVCScalingList { + AVBufferRef *rref; + + uint8_t scaling_matrix_rec[SL_MAX_ID][SL_MAX_MATRIX_SIZE * SL_MAX_MATRIX_SIZE]; ///< ScalingMatrixRec + uint8_t scaling_matrix_dc_rec[SL_MAX_ID - SL_START_16x16]; ///< ScalingMatrixDcRec[refId − 14] +} VVCScalingList; + +typedef struct VVCParamSets { + AVBufferRef *vps_list[VVC_MAX_VPS_COUNT]; + AVBufferRef *sps_list[VVC_MAX_SPS_COUNT]; + AVBufferRef *pps_list[VVC_MAX_PPS_COUNT]; + AVBufferRef *alf_list[VVC_MAX_ALF_COUNT]; + AVBufferRef *lmcs_list[VVC_MAX_LMCS_COUNT]; + AVBufferRef *scaling_list[VVC_MAX_SL_COUNT]; +} VVCParamSets; + +typedef struct VVCFrameParamSets { + AVBufferRef *sps_buf; + AVBufferRef *pps_buf; + AVBufferRef *alf_list[VVC_MAX_ALF_COUNT]; + AVBufferRef *sl_buf; + + const VVCSPS *sps; + const VVCPPS *pps; + VVCPH ph; + VVCLMCS lmcs; + const VVCScalingList *sl; +} VVCFrameParamSets; + +typedef struct VVCSH { + AVBufferRef *rref; + const H266RawSliceHeader *r; + + // derived values + // ctu address + int num_ctus_in_curr_slice; ///< NumCtusInCurrSlice + const uint32_t* ctb_addr_in_curr_slice; ///< CtbAddrInCurrSlice + + // inter + PredWeightTable pwt; + int8_t ref_idx_sym[2]; ///< RefIdxSymL0, RefIdxSymL1 + + // qp_y + int8_t slice_qp_y; ///< SliceQpY + + // deblock_offsets + DBParams deblock; + + // partition constrains + uint8_t min_qt_size[2]; ///< MinQtSizeY, MinQtSizeC + uint8_t max_bt_size[2]; ///< MaxBtSizeY, MaxBtSizeC + uint8_t max_tt_size[2]; ///< MaxTtSizeY, MaxTtSizeC + uint8_t max_mtt_depth[2]; ///< MaxMttDepthY, MaxMttDepthC + uint8_t cu_qp_delta_subdiv; ///< CuQpDeltaSubdiv + uint8_t cu_chroma_qp_offset_subdiv; ///< CuChromaQpOffsetSubdiv + + // entries + uint32_t entry_point_start_ctu[VVC_MAX_ENTRY_POINTS]; ///< entry point start in ctu_addr +} VVCSH; + +struct VVCContext; + +int ff_vvc_decode_frame_ps(VVCFrameParamSets *fps, struct VVCContext *s); +int ff_vvc_decode_aps(VVCParamSets *ps, const CodedBitstreamUnit *unit); +int ff_vvc_decode_sh(VVCSH *sh, const VVCFrameParamSets *ps, const CodedBitstreamUnit *unit); +void ff_vvc_frame_ps_free(VVCFrameParamSets *fps); +void ff_vvc_ps_uninit(VVCParamSets *ps); + +#endif /* AVCODEC_VVC_PS_H */ diff --git a/libavcodec/vvc/vvc_refs.c b/libavcodec/vvc/vvc_refs.c new file mode 100644 index 00000000000..10471686620 --- /dev/null +++ b/libavcodec/vvc/vvc_refs.c @@ -0,0 +1,533 @@ +/* + * VVC reference management + * + * Copyright (C) 2023 Nuo Mi + * + * 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 + +#include "libavutil/thread.h" + +#include "vvc_refs.h" + +#if !HAVE_THREADS +#define pthread_mutex_init(m, a) 0 +#define pthread_mutex_lock(l) do {} while(0) +#define pthread_mutex_unlock(l) do {} while(0) +#define pthread_mutex_destroy(l) do {} while(0) +#endif + +#define VVC_FRAME_FLAG_OUTPUT (1 << 0) +#define VVC_FRAME_FLAG_SHORT_REF (1 << 1) +#define VVC_FRAME_FLAG_LONG_REF (1 << 2) +#define VVC_FRAME_FLAG_BUMPING (1 << 3) + +typedef struct FrameProgress { + atomic_int progress[VVC_PROGRESS_LAST]; + pthread_mutex_t lock; +} FrameProgress; + +void ff_vvc_unref_frame(VVCFrameContext *fc, VVCFrame *frame, int flags) +{ + /* frame->frame can be NULL if context init failed */ + if (!frame->frame || !frame->frame->buf[0]) + return; + + frame->flags &= ~flags; + if (!frame->flags) { + ff_thread_release_ext_buffer(fc->avctx, &frame->tf); + + av_buffer_unref(&frame->progress_buf); + + av_buffer_unref(&frame->tab_dmvr_mvf_buf); + frame->tab_dmvr_mvf = NULL; + + av_buffer_unref(&frame->rpl_buf); + av_buffer_unref(&frame->rpl_tab_buf); + frame->rpl_tab = NULL; + + frame->collocated_ref = NULL; + } +} + +const RefPicList *ff_vvc_get_ref_list(const VVCFrameContext *fc, const VVCFrame *ref, int x0, int y0) +{ + int x_cb = x0 >> fc->ps.sps->ctb_log2_size_y; + int y_cb = y0 >> fc->ps.sps->ctb_log2_size_y; + int pic_width_cb = fc->ps.pps->ctb_width; + int ctb_addr_rs = y_cb * pic_width_cb + x_cb; + + return (const RefPicList *)ref->rpl_tab[ctb_addr_rs]; +} + +void ff_vvc_clear_refs(VVCFrameContext *fc) +{ + int i; + for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) + ff_vvc_unref_frame(fc, &fc->DPB[i], + VVC_FRAME_FLAG_SHORT_REF | VVC_FRAME_FLAG_LONG_REF); +} + +static void free_progress(void *opaque, uint8_t *data) +{ + pthread_mutex_destroy(&((FrameProgress*)data)->lock); + av_free(data); +} + +static AVBufferRef *alloc_progress(void) +{ + int ret; + AVBufferRef *buf; + FrameProgress *p = av_mallocz(sizeof(FrameProgress)); + + if (!p) + return NULL; + + ret = pthread_mutex_init(&p->lock, NULL); + if (ret) { + av_free(p); + return NULL; + } + buf = av_buffer_create((void*)p, sizeof(*p), free_progress, NULL, 0); + if (!buf) + free_progress(NULL, (void*)p); + return buf; +} + +static VVCFrame *alloc_frame(VVCContext *s, VVCFrameContext *fc) +{ + const VVCPPS *pps = fc->ps.pps; + int i, j, ret; + for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *frame = &fc->DPB[i]; + if (frame->frame->buf[0]) + continue; + + ret = ff_thread_get_ext_buffer(fc->avctx, &frame->tf, + AV_GET_BUFFER_FLAG_REF); + if (ret < 0) + return NULL; + + frame->rpl_buf = av_buffer_allocz(s->current_frame.nb_units * sizeof(RefPicListTab)); + if (!frame->rpl_buf) + goto fail; + + frame->tab_dmvr_mvf_buf = av_buffer_pool_get(fc->tab_dmvr_mvf_pool); + if (!frame->tab_dmvr_mvf_buf) + goto fail; + frame->tab_dmvr_mvf = (MvField *)frame->tab_dmvr_mvf_buf->data; + //fixme: remove this + memset(frame->tab_dmvr_mvf, 0, frame->tab_dmvr_mvf_buf->size); + + frame->rpl_tab_buf = av_buffer_pool_get(fc->rpl_tab_pool); + if (!frame->rpl_tab_buf) + goto fail; + frame->rpl_tab = (RefPicListTab **)frame->rpl_tab_buf->data; + frame->ctb_count = pps->ctb_width * pps->ctb_height; + for (j = 0; j < frame->ctb_count; j++) + frame->rpl_tab[j] = (RefPicListTab *)frame->rpl_buf->data; + + + frame->progress_buf = alloc_progress(); + if (!frame->progress_buf) + goto fail; + + return frame; +fail: + ff_vvc_unref_frame(fc, frame, ~0); + return NULL; + } + av_log(s->avctx, AV_LOG_ERROR, "Error allocating frame, DPB full.\n"); + return NULL; +} + +int ff_vvc_set_new_ref(VVCContext *s, VVCFrameContext *fc, AVFrame **frame) +{ + const VVCPH *ph= &fc->ps.ph; + const int poc = ph->poc; + VVCFrame *ref; + int i; + + /* check that this POC doesn't already exist */ + for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *frame = &fc->DPB[i]; + + if (frame->frame->buf[0] && frame->sequence == s->seq_decode && + frame->poc == poc) { + av_log(s->avctx, AV_LOG_ERROR, "Duplicate POC in a sequence: %d.\n", + poc); + return AVERROR_INVALIDDATA; + } + } + + ref = alloc_frame(s, fc); + if (!ref) + return AVERROR(ENOMEM); + + *frame = ref->frame; + fc->ref = ref; + + if (s->no_output_before_recovery_flag && (IS_RASL(s) || !GDR_IS_RECOVERED(s))) + ref->flags = 0; + else if (ph->r->ph_pic_output_flag) + ref->flags = VVC_FRAME_FLAG_OUTPUT; + + if (!ph->r->ph_non_ref_pic_flag) + ref->flags |= VVC_FRAME_FLAG_SHORT_REF; + + ref->poc = poc; + ref->sequence = s->seq_decode; + ref->frame->crop_left = fc->ps.pps->r->pps_conf_win_left_offset; + ref->frame->crop_right = fc->ps.pps->r->pps_conf_win_right_offset; + ref->frame->crop_top = fc->ps.pps->r->pps_conf_win_top_offset; + ref->frame->crop_bottom = fc->ps.pps->r->pps_conf_win_bottom_offset; + + return 0; +} + +int ff_vvc_output_frame(VVCContext *s, VVCFrameContext *fc, AVFrame *out, const int no_output_of_prior_pics_flag, int flush) +{ + const VVCSPS *sps = fc->ps.sps; + do { + int nb_output = 0; + int min_poc = INT_MAX; + int i, min_idx, ret; + + if (no_output_of_prior_pics_flag) { + for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *frame = &fc->DPB[i]; + if (!(frame->flags & VVC_FRAME_FLAG_BUMPING) && frame->poc != fc->ps.ph.poc && + frame->sequence == s->seq_output) { + ff_vvc_unref_frame(fc, frame, VVC_FRAME_FLAG_OUTPUT); + } + } + } + + for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *frame = &fc->DPB[i]; + if ((frame->flags & VVC_FRAME_FLAG_OUTPUT) && + frame->sequence == s->seq_output) { + nb_output++; + if (frame->poc < min_poc || nb_output == 1) { + min_poc = frame->poc; + min_idx = i; + } + } + } + + /* wait for more frames before output */ + if (!flush && s->seq_output == s->seq_decode && sps && + nb_output <= sps->r->sps_dpb_params.dpb_max_dec_pic_buffering_minus1[sps->r->sps_max_sublayers_minus1] + 1) + return 0; + + if (nb_output) { + VVCFrame *frame = &fc->DPB[min_idx]; + + ret = av_frame_ref(out, frame->frame); + if (frame->flags & VVC_FRAME_FLAG_BUMPING) + ff_vvc_unref_frame(fc, frame, VVC_FRAME_FLAG_OUTPUT | VVC_FRAME_FLAG_BUMPING); + else + ff_vvc_unref_frame(fc, frame, VVC_FRAME_FLAG_OUTPUT); + if (ret < 0) + return ret; + + av_log(s->avctx, AV_LOG_DEBUG, + "Output frame with POC %d.\n", frame->poc); + return 1; + } + + if (s->seq_output != s->seq_decode) + s->seq_output = (s->seq_output + 1) & 0xff; + else + break; + } while (1); + return 0; +} + +void ff_vvc_bump_frame(VVCContext *s, VVCFrameContext *fc) +{ + const VVCSPS *sps = fc->ps.sps; + const int poc = fc->ps.ph.poc; + int dpb = 0; + int min_poc = INT_MAX; + int i; + + for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *frame = &fc->DPB[i]; + if ((frame->flags) && + frame->sequence == s->seq_output && + frame->poc != poc) { + dpb++; + } + } + + if (sps && dpb >= sps->r->sps_dpb_params.dpb_max_dec_pic_buffering_minus1[sps->r->sps_max_sublayers_minus1] + 1) { + for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *frame = &fc->DPB[i]; + if ((frame->flags) && + frame->sequence == s->seq_output && + frame->poc != poc) { + if (frame->flags == VVC_FRAME_FLAG_OUTPUT && frame->poc < min_poc) { + min_poc = frame->poc; + } + } + } + + for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *frame = &fc->DPB[i]; + if (frame->flags & VVC_FRAME_FLAG_OUTPUT && + frame->sequence == s->seq_output && + frame->poc <= min_poc) { + frame->flags |= VVC_FRAME_FLAG_BUMPING; + } + } + + dpb--; + } +} + +static VVCFrame *find_ref_idx(VVCContext *s, VVCFrameContext *fc, int poc, uint8_t use_msb) +{ + int mask = use_msb ? ~0 : fc->ps.sps->max_pic_order_cnt_lsb - 1; + int i; + + for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *ref = &fc->DPB[i]; + if (ref->frame->buf[0] && ref->sequence == s->seq_decode) { + if ((ref->poc & mask) == poc) + return ref; + } + } + return NULL; +} + +static void mark_ref(VVCFrame *frame, int flag) +{ + frame->flags &= ~(VVC_FRAME_FLAG_LONG_REF | VVC_FRAME_FLAG_SHORT_REF); + frame->flags |= flag; +} + +static VVCFrame *generate_missing_ref(VVCContext *s, VVCFrameContext *fc, int poc) +{ + const VVCSPS *sps = fc->ps.sps; + VVCFrame *frame; + int i, y; + + frame = alloc_frame(s, fc); + if (!frame) + return NULL; + + if (!s->avctx->hwaccel) { + if (!sps->pixel_shift) { + for (i = 0; frame->frame->buf[i]; i++) + memset(frame->frame->buf[i]->data, 1 << (sps->bit_depth - 1), + frame->frame->buf[i]->size); + } else { + for (i = 0; frame->frame->data[i]; i++) + for (y = 0; y < (sps->height >> sps->vshift[i]); y++) { + uint8_t *dst = frame->frame->data[i] + y * frame->frame->linesize[i]; + AV_WN16(dst, 1 << (sps->bit_depth - 1)); + av_memcpy_backptr(dst + 2, 2, 2*(sps->width >> sps->hshift[i]) - 2); + } + } + } + + frame->poc = poc; + frame->sequence = s->seq_decode; + frame->flags = 0; + + ff_vvc_report_frame_finished(frame); + + return frame; +} + +/* add a reference with the given poc to the list and mark it as used in DPB */ +static int add_candidate_ref(VVCContext *s, VVCFrameContext *fc, RefPicList *list, + int poc, int ref_flag, uint8_t use_msb) +{ + VVCFrame *ref = find_ref_idx(s, fc, poc, use_msb); + + if (ref == fc->ref || list->nb_refs >= VVC_MAX_REF_ENTRIES) + return AVERROR_INVALIDDATA; + + if (!ref) { + ref = generate_missing_ref(s, fc, poc); + if (!ref) + return AVERROR(ENOMEM); + } + + list->list[list->nb_refs] = poc; + list->ref[list->nb_refs] = ref; + list->isLongTerm[list->nb_refs] = ref_flag & VVC_FRAME_FLAG_LONG_REF; + list->nb_refs++; + + mark_ref(ref, ref_flag); + return 0; +} + +static int init_slice_rpl(const VVCFrameContext *fc, SliceContext *sc) +{ + VVCFrame *frame = fc->ref; + const VVCSH *sh = &sc->sh; + + if (sc->slice_idx >= frame->rpl_buf->size / sizeof(RefPicListTab)) + return AVERROR_INVALIDDATA; + + for (int i = 0; i < sh->num_ctus_in_curr_slice; i++) { + const int rs = sh->ctb_addr_in_curr_slice[i]; + frame->rpl_tab[rs] = (RefPicListTab *)frame->rpl_buf->data + sc->slice_idx; + } + + sc->rpl = (RefPicList *)frame->rpl_tab[sh->ctb_addr_in_curr_slice[0]]; + + return 0; +} + +static int delta_poc_st(const H266RefPicListStruct *rpls, + const int lx, const int i, const VVCSPS *sps) +{ + int abs_delta_poc_st = rpls->abs_delta_poc_st[i]; + if (!((sps->r->sps_weighted_pred_flag || + sps->r->sps_weighted_bipred_flag) && i != 0)) + abs_delta_poc_st++; + return (1 - 2 * rpls->strp_entry_sign_flag[i]) * abs_delta_poc_st; +} + +static int poc_lt(int *prev_delta_poc_msb, const int poc, const H266RefPicLists *ref_lists, + const int lx, const int j, const int max_poc_lsb) +{ + const H266RefPicListStruct *rpls = ref_lists->rpl_ref_list + lx; + int lt_poc = rpls->ltrp_in_header_flag ? ref_lists->poc_lsb_lt[lx][j] : rpls->rpls_poc_lsb_lt[j]; + + if (ref_lists->delta_poc_msb_cycle_present_flag[lx][j]) { + const uint32_t delta = ref_lists->delta_poc_msb_cycle_lt[lx][j] + *prev_delta_poc_msb; + lt_poc += poc - delta * max_poc_lsb - (poc & (max_poc_lsb - 1)); + *prev_delta_poc_msb = delta; + } + return lt_poc; +} + +int ff_vvc_slice_rpl(VVCContext *s, VVCFrameContext *fc, SliceContext *sc) +{ + const VVCSPS *sps = fc->ps.sps; + const H266RawPPS *pps = fc->ps.pps->r; + const VVCPH *ph = &fc->ps.ph; + const H266RawSliceHeader *rsh = sc->sh.r; + const int max_poc_lsb = sps->max_pic_order_cnt_lsb; + const H266RefPicLists *ref_lists = + pps->pps_rpl_info_in_ph_flag ? &ph->r->ph_ref_pic_lists : &rsh->sh_ref_pic_lists; + int ret = 0; + + ret = init_slice_rpl(fc, sc); + if (ret < 0) + return ret; + + for (int lx = L0; lx <= L1; lx++) { + const H266RefPicListStruct *rpls = ref_lists->rpl_ref_list + lx; + RefPicList *rpl = sc->rpl + lx; + int poc_base = ph->poc; + int prev_delta_poc_msb = 0; + + rpl->nb_refs = 0; + for (int i = 0, j = 0; i < rpls->num_ref_entries; i++) { + int poc; + if (!rpls->inter_layer_ref_pic_flag[i]) { + int use_msb = 1; + int ref_flag; + if (rpls->st_ref_pic_flag[i]) { + poc = poc_base + delta_poc_st(rpls, lx, i, sps); + poc_base = poc; + ref_flag = VVC_FRAME_FLAG_SHORT_REF; + } else { + use_msb = ref_lists->delta_poc_msb_cycle_present_flag[lx][j]; + poc = poc_lt(&prev_delta_poc_msb, ph->poc, ref_lists, lx, j, max_poc_lsb); + ref_flag = VVC_FRAME_FLAG_LONG_REF; + j++; + } + ret = add_candidate_ref(s, fc, rpl, poc, ref_flag, use_msb); + if (ret < 0) + goto fail; + } else { + avpriv_request_sample(fc->avctx, "Inter layer ref"); + ret = AVERROR_PATCHWELCOME; + goto fail; + } + } + if ((!rsh->sh_collocated_from_l0_flag) == lx && + rsh->sh_collocated_ref_idx < rpl->nb_refs) + fc->ref->collocated_ref = rpl->ref[rsh->sh_collocated_ref_idx]; + } +fail: + return ret; +} + +int ff_vvc_frame_rpl(VVCContext *s, VVCFrameContext *fc, SliceContext *sc) +{ + int i, ret = 0; + + /* clear the reference flags on all frames except the current one */ + for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *frame = &fc->DPB[i]; + + if (frame == fc->ref) + continue; + + mark_ref(frame, 0); + } + + if ((ret = ff_vvc_slice_rpl(s, fc, sc)) < 0) + goto fail; + +fail: + /* release any frames that are now unused */ + for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) + ff_vvc_unref_frame(fc, &fc->DPB[i], 0); + return ret; +} + +void ff_vvc_report_frame_finished(VVCFrame *frame) +{ + ff_vvc_report_progress(frame, VVC_PROGRESS_MV, INT_MAX); + ff_vvc_report_progress(frame, VVC_PROGRESS_PIXEL, INT_MAX); +} + +void ff_vvc_report_progress(VVCFrame *frame, const VVCProgress vp, const int y) +{ + FrameProgress *p = (FrameProgress*)frame->progress_buf->data; + + pthread_mutex_lock(&p->lock); + + av_assert0(p->progress[vp] < y || p->progress[vp] == INT_MAX); + p->progress[vp] = y; + + pthread_mutex_unlock(&p->lock); +} + +int ff_vvc_check_progress(VVCFrame *frame, const VVCProgress vp, const int y) +{ + int ready ; + FrameProgress *p = (FrameProgress*)frame->progress_buf->data; + + pthread_mutex_lock(&p->lock); + + ready = p->progress[vp] > y + 1; + + pthread_mutex_unlock(&p->lock); + return ready; +} diff --git a/libavcodec/vvc/vvc_refs.h b/libavcodec/vvc/vvc_refs.h new file mode 100644 index 00000000000..1bba2ca34a3 --- /dev/null +++ b/libavcodec/vvc/vvc_refs.h @@ -0,0 +1,47 @@ +/* + * VVC reference management + * + * Copyright (C) 2023 Nuo Mi + * + * 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_VVC_REFS_H +#define AVCODEC_VVC_REFS_H + +#include "vvcdec.h" + +int ff_vvc_output_frame(VVCContext *s, VVCFrameContext *fc, AVFrame *out, int no_output_of_prior_pics_flag, int flush); +void ff_vvc_bump_frame(VVCContext *s, VVCFrameContext *fc); +int ff_vvc_set_new_ref(VVCContext *s, VVCFrameContext *fc, AVFrame **frame); +const RefPicList *ff_vvc_get_ref_list(const VVCFrameContext *fc, const VVCFrame *ref, int x0, int y0); +int ff_vvc_frame_rpl(VVCContext *s, VVCFrameContext *fc, SliceContext *sc); +int ff_vvc_slice_rpl(VVCContext *s, VVCFrameContext *fc, SliceContext *sc); +void ff_vvc_unref_frame(VVCFrameContext *fc, VVCFrame *frame, int flags); +void ff_vvc_clear_refs(VVCFrameContext *fc); + +typedef enum VVCProgress{ + VVC_PROGRESS_MV, + VVC_PROGRESS_PIXEL, + VVC_PROGRESS_LAST, +} VVCProgress; + +void ff_vvc_report_frame_finished(VVCFrame *frame); +void ff_vvc_report_progress(VVCFrame *frame, VVCProgress vp, int y); +int ff_vvc_check_progress(VVCFrame *frame, VVCProgress vp, int y); + +#endif // AVCODEC_VVC_REFS_H diff --git a/libavcodec/vvc/vvc_thread.c b/libavcodec/vvc/vvc_thread.c new file mode 100644 index 00000000000..54658a8e548 --- /dev/null +++ b/libavcodec/vvc/vvc_thread.c @@ -0,0 +1,817 @@ +/* + * VVC thread logic + * + * Copyright (C) 2023 Nuo Mi + * + * 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 + +#include "libavutil/thread.h" + +#include "vvc_thread.h" +#include "vvc_ctu.h" +#include "vvc_filter.h" +#include "vvc_inter.h" +#include "vvc_intra.h" +#include "vvc_refs.h" + +#if !HAVE_THREADS +#define pthread_cond_init(c, a) 0 +#define pthread_cond_broadcast(c) do {} while(0) +#define pthread_cond_wait(c, m) do {} while(0) +#define pthread_cond_destroy(c) do {} while(0) + +#define pthread_mutex_init(m, a) 0 +#define pthread_mutex_lock(l) do {} while(0) +#define pthread_mutex_unlock(l) do {} while(0) +#define pthread_mutex_destroy(l) do {} while(0) +#endif + +typedef struct VVCRowThread { + VVCTask reconstruct_task; + VVCTask deblock_v_task; + VVCTask sao_task; + atomic_int progress[VVC_PROGRESS_LAST]; +} VVCRowThread; + +typedef struct VVCColThread { + VVCTask deblock_h_task; +} VVCColThread; + +struct VVCFrameThread { + // error return for tasks + atomic_int ret; + + atomic_uchar *avails; + + VVCRowThread *rows; + VVCColThread *cols; + VVCTask *tasks; + + int ctu_size; + int ctu_width; + int ctu_height; + int ctu_count; + + //protected by lock + int nb_scheduled_tasks; + int nb_parse_tasks; + int row_progress[VVC_PROGRESS_LAST]; + + pthread_mutex_t lock; + pthread_cond_t cond; +}; + +static int get_avail(const VVCFrameThread *ft, const int rx, const int ry, const VVCTaskType type) +{ + atomic_uchar *avail; + if (rx < 0 || ry < 0) + return 1; + avail = ft->avails + FFMIN(ry, ft->ctu_height - 1)* ft->ctu_width + FFMIN(rx, ft->ctu_width - 1); + return atomic_load(avail) & (1 << type); +} + +static void set_avail(const VVCFrameThread *ft, const int rx, const int ry, const VVCTaskType type) +{ + atomic_uchar *avail = ft->avails + ry * ft->ctu_width + rx; + if (rx < 0 || rx >= ft->ctu_width || ry < 0 || ry >= ft->ctu_height) + return; + atomic_fetch_or(avail, 1 << type); +} + +void ff_vvc_task_init(VVCTask *task, VVCTaskType type, VVCFrameContext *fc) +{ + memset(task, 0, sizeof(*task)); + task->type = type; + task->fc = fc; +} + +void ff_vvc_parse_task_init(VVCTask *t, VVCTaskType type, VVCFrameContext *fc, + SliceContext *sc, EntryPoint *ep, const int ctu_idx) +{ + const VVCFrameThread *ft = fc->frame_thread; + const int rs = sc->sh.ctb_addr_in_curr_slice[ctu_idx]; + + ff_vvc_task_init(t, type, fc); + t->sc = sc; + t->ep = ep; + t->ctu_idx = ctu_idx; + t->rx = rs % ft->ctu_width; + t->ry = rs / ft->ctu_width; +} + +VVCTask* ff_vvc_task_alloc(void) +{ + return av_malloc(sizeof(VVCTask)); +} + +static int check_colocation_ctu(const VVCFrameContext *fc, const VVCTask *t) +{ + if (fc->ps.ph.r->ph_temporal_mvp_enabled_flag || fc->ps.sps->r->sps_sbtmvp_enabled_flag) { + //-1 to avoid we are waiting for next CTU line. + const int y = (t->ry << fc->ps.sps->ctb_log2_size_y) - 1; + VVCFrame *col = fc->ref->collocated_ref; + if (col && !ff_vvc_check_progress(col, VVC_PROGRESS_MV, y)) + return 0; + } + return 1; +} + +static int is_parse_ready(const VVCFrameContext *fc, const VVCTask *t) +{ + av_assert0(t->type == VVC_TASK_TYPE_PARSE); + if (!check_colocation_ctu(fc, t)) + return 0; + if (fc->ps.sps->r->sps_entropy_coding_sync_enabled_flag && t->ry != fc->ps.pps->ctb_to_row_bd[t->ry]) + return get_avail(fc->frame_thread, t->rx, t->ry - 1, VVC_TASK_TYPE_PARSE); + return 1; +} + +static int is_inter_ready(const VVCFrameContext *fc, const VVCTask *t) +{ + VVCFrameThread *ft = fc->frame_thread; + const int rs = t->ry * ft->ctu_width + t->rx; + const int slice_idx = fc->tab.slice_idx[rs]; + + av_assert0(t->type == VVC_TASK_TYPE_INTER); + + if (slice_idx != -1) { + const SliceContext *sc = fc->slices[slice_idx]; + const VVCSH *sh = &sc->sh; + CTU *ctu = fc->tab.ctus + rs; + if (!IS_I(sh->r)) { + for (int lx = 0; lx < 2; lx++) { + for (int i = ctu->max_y_idx[lx]; i < sh->r->num_ref_idx_active[lx]; i++) { + const int y = ctu->max_y[lx][i]; + VVCFrame *ref = sc->rpl[lx].ref[i]; + if (ref && y >= 0) { + if (!ff_vvc_check_progress(ref, VVC_PROGRESS_PIXEL, y + LUMA_EXTRA_AFTER)) + return 0; + } + ctu->max_y_idx[lx]++; + } + } + } + } + return 1; +} + +static int is_recon_ready(const VVCFrameContext *fc, const VVCTask *t) +{ + const VVCFrameThread *ft = fc->frame_thread; + + av_assert0(t->type == VVC_TASK_TYPE_RECON); + return get_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_INTER) && + get_avail(ft, t->rx + 1, t->ry - 1, VVC_TASK_TYPE_RECON) && + get_avail(ft, t->rx - 1, t->ry, VVC_TASK_TYPE_RECON); +} + +static int is_lmcs_ready(const VVCFrameContext *fc, const VVCTask *t) +{ + const VVCFrameThread *ft = fc->frame_thread; + + av_assert0(t->type == VVC_TASK_TYPE_LMCS); + return get_avail(ft, t->rx + 1, t->ry + 1, VVC_TASK_TYPE_RECON) && + get_avail(ft, t->rx, t->ry + 1, VVC_TASK_TYPE_RECON) && + get_avail(ft, t->rx + 1, t->ry, VVC_TASK_TYPE_RECON); +} + +static int is_deblock_v_ready(const VVCFrameContext *fc, const VVCTask *t) +{ + const VVCFrameThread *ft = fc->frame_thread; + + av_assert0(t->type == VVC_TASK_TYPE_DEBLOCK_V); + return get_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_LMCS) && + get_avail(ft, t->rx + 1, t->ry, VVC_TASK_TYPE_LMCS); +} + +static int is_deblock_h_ready(const VVCFrameContext *fc, const VVCTask *t) +{ + const VVCFrameThread *ft = fc->frame_thread; + + av_assert0(t->type == VVC_TASK_TYPE_DEBLOCK_H); + return get_avail(ft, t->rx - 1, t->ry, VVC_TASK_TYPE_DEBLOCK_H) && + get_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_DEBLOCK_V); +} + +static int is_sao_ready(const VVCFrameContext *fc, const VVCTask *t) +{ + av_assert0(t->type == VVC_TASK_TYPE_SAO); + return get_avail(fc->frame_thread, t->rx + 1, t->ry - 1, VVC_TASK_TYPE_SAO) && + get_avail(fc->frame_thread, t->rx - 1, t->ry + 1, VVC_TASK_TYPE_DEBLOCK_H) && + get_avail(fc->frame_thread, t->rx, t->ry + 1, VVC_TASK_TYPE_DEBLOCK_H) && + get_avail(fc->frame_thread, t->rx + 1, t->ry + 1, VVC_TASK_TYPE_DEBLOCK_H); +} + +static int is_alf_ready(const VVCFrameContext *fc, const VVCTask *t) +{ + av_assert0(t->type == VVC_TASK_TYPE_ALF); + return 1; +} + +typedef int (*is_ready_func)(const VVCFrameContext *fc, const VVCTask *t); + +int ff_vvc_task_ready(const AVTask *_t, void *user_data) +{ + const VVCTask *t = (const VVCTask*)_t; + VVCFrameThread *ft = t->fc->frame_thread; + int ready; + is_ready_func is_ready[] = { + is_parse_ready, + is_inter_ready, + is_recon_ready, + is_lmcs_ready, + is_deblock_v_ready, + is_deblock_h_ready, + is_sao_ready, + is_alf_ready, + }; + + if (atomic_load(&ft->ret)) + return 1; + ready = is_ready[t->type](t->fc, t); + + return ready; +} + +#define CHECK(a, b) \ + do { \ + if ((a) != (b)) \ + return (a) < (b); \ + } while (0) + +int ff_vvc_task_priority_higher(const AVTask *_a, const AVTask *_b) +{ + const VVCTask *a = (const VVCTask*)_a; + const VVCTask *b = (const VVCTask*)_b; + + CHECK(a->fc->decode_order, b->fc->decode_order); //decode order + + if (a->type == VVC_TASK_TYPE_PARSE || b->type == VVC_TASK_TYPE_PARSE) { + CHECK(a->type, b->type); + CHECK(a->ry, b->ry); + return a->rx < b->rx; + } + + CHECK(a->rx + a->ry + a->type, b->rx + b->ry + b->type); //zigzag with type + CHECK(a->rx + a->ry, b->rx + b->ry); //zigzag + return a->ry < b->ry; +} + +static void add_task(VVCContext *s, VVCTask *t, const VVCTaskType type) +{ + t->type = type; + ff_vvc_frame_add_task(s, t); +} + +static int run_parse(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + SliceContext *sc = t->sc; + const VVCSH *sh = &sc->sh; + EntryPoint *ep = t->ep; + VVCFrameThread *ft = fc->frame_thread; + int ret, rs, prev_ry; + + lc->sc = sc; + lc->ep = ep; + + //reconstruct one line a time + rs = sh->ctb_addr_in_curr_slice[t->ctu_idx]; + do { + + prev_ry = t->ry; + + ret = ff_vvc_coding_tree_unit(lc, t->ctu_idx, rs, t->rx, t->ry); + if (ret < 0) + return ret; + + set_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_PARSE); + add_task(s, ft->tasks + rs, VVC_TASK_TYPE_INTER); + + if (fc->ps.sps->r->sps_entropy_coding_sync_enabled_flag && t->rx == pps->ctb_to_col_bd[t->rx]) { + EntryPoint *next = ep + 1; + if (next < sc->eps + sc->nb_eps) { + memcpy(next->cabac_state, ep->cabac_state, sizeof(next->cabac_state)); + av_assert0(!next->parse_task->type); + ff_vvc_ep_init_stat_coeff(lc->ep, sps->bit_depth, sps->r->sps_persistent_rice_adaptation_enabled_flag); + ff_vvc_frame_add_task(s, next->parse_task); + } + } + + t->ctu_idx++; + if (t->ctu_idx >= ep->ctu_end) + break; + + rs = sh->ctb_addr_in_curr_slice[t->ctu_idx]; + t->rx = rs % ft->ctu_width; + t->ry = rs / ft->ctu_width; + } while (t->ry == prev_ry && is_parse_ready(fc, t)); + + if (t->ctu_idx < ep->ctu_end) + ff_vvc_frame_add_task(s, t); + + return 0; +} + +static void report_frame_progress(VVCFrameContext *fc, VVCTask *t) +{ + VVCFrameThread *ft = fc->frame_thread; + const int ctu_size = ft->ctu_size; + const int idx = t->type == VVC_TASK_TYPE_INTER ? VVC_PROGRESS_MV : VVC_PROGRESS_PIXEL; + int old; + + if (atomic_fetch_add(&ft->rows[t->ry].progress[idx], 1) == ft->ctu_width - 1) { + int y; + pthread_mutex_lock(&ft->lock); + y = old = ft->row_progress[idx]; + while (y < ft->ctu_height && atomic_load(&ft->rows[y].progress[idx]) == ft->ctu_width) + y++; + if (old != y) { + const int progress = y == ft->ctu_height ? INT_MAX : y * ctu_size; + ft->row_progress[idx] = y; + ff_vvc_report_progress(fc->ref, idx, progress); + } + pthread_mutex_unlock(&ft->lock); + } +} + +static int run_inter(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->frame_thread; + const int rs = t->ry * ft->ctu_width + t->rx; + const int slice_idx = fc->tab.slice_idx[rs]; + + if (slice_idx != -1) { + lc->sc = fc->slices[slice_idx]; + ff_vvc_predict_inter(lc, rs); + if (!t->rx) + ff_vvc_frame_add_task(s, &ft->rows[t->ry].reconstruct_task); + } + set_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_INTER); + report_frame_progress(fc, t); + + return 0; +} + +static int run_recon(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->frame_thread; + + do { + const int rs = t->ry * ft->ctu_width + t->rx; + const int slice_idx = fc->tab.slice_idx[rs]; + + if (slice_idx != -1) { + lc->sc = fc->slices[slice_idx]; + ff_vvc_reconstruct(lc, rs, t->rx, t->ry); + } + + set_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_RECON); + add_task(s, ft->tasks + rs, VVC_TASK_TYPE_LMCS); + + t->rx++; + } while (t->rx < ft->ctu_width && is_recon_ready(fc, t)); + + if (t->rx < ft->ctu_width) + ff_vvc_frame_add_task(s, t); + return 0; +} + +static int run_lmcs(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->frame_thread; + const int ctu_size = ft->ctu_size; + const int x0 = t->rx * ctu_size; + const int y0 = t->ry * ctu_size; + const int rs = t->ry * ft->ctu_width + t->rx; + const int slice_idx = fc->tab.slice_idx[rs]; + + if (slice_idx != -1) { + lc->sc = fc->slices[slice_idx]; + ff_vvc_lmcs_filter(lc, x0, y0); + } + set_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_LMCS); + if (!t->rx) + add_task(s, &ft->rows[t->ry].deblock_v_task, VVC_TASK_TYPE_DEBLOCK_V); + + return 0; +} + +static int run_deblock_v(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->frame_thread; + int rs = t->ry * ft->ctu_width + t->rx; + const int ctb_size = ft->ctu_size; + + do { + const int x0 = t->rx * ctb_size; + const int y0 = t->ry * ctb_size; + const int slice_idx = fc->tab.slice_idx[rs]; + + if (slice_idx != -1) { + lc->sc = fc->slices[slice_idx]; + if (!lc->sc->sh.r->sh_deblocking_filter_disabled_flag) { + ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, rs); + ff_vvc_deblock_vertical(lc, x0, y0); + } + } + + set_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_DEBLOCK_V); + + if (!t->ry) + add_task(s, &ft->cols[t->rx].deblock_h_task , VVC_TASK_TYPE_DEBLOCK_H); + + t->rx++; + rs++; + } while (t->rx < ft->ctu_width && is_deblock_v_ready(fc, t)); + + if (t->rx < ft->ctu_width) + ff_vvc_frame_add_task(s, t); + + return 0; +} + +static int run_deblock_h(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->frame_thread; + const int ctb_size = ft->ctu_size; + int rs = t->ry * ft->ctu_width + t->rx; + + do { + const int x0 = t->rx * ctb_size; + const int y0 = t->ry * ctb_size; + const int slice_idx = fc->tab.slice_idx[rs]; + + if (slice_idx != -1) { + lc->sc = fc->slices[slice_idx]; + if (!lc->sc->sh.r->sh_deblocking_filter_disabled_flag) { + ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, rs); + ff_vvc_deblock_horizontal(lc, x0, y0); + } + } + + set_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_DEBLOCK_H); + + if (!t->rx) + add_task(s, &ft->rows[t->ry].sao_task, VVC_TASK_TYPE_SAO); + + rs += ft->ctu_width; + t->ry++; + } while (t->ry < ft->ctu_height && is_deblock_h_ready(fc, t)); + + if (t->ry < ft->ctu_height) + ff_vvc_frame_add_task(s, t); + + return 0; +} + +static void add_alf_tasks(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->frame_thread; + VVCTask *at = ft->tasks + ft->ctu_width * t->ry + t->rx; + if (t->ry > 0) { + VVCTask *top = at - ft->ctu_width; + if (t->rx > 0) + add_task(s, top - 1, VVC_TASK_TYPE_ALF); + if (t->rx == ft->ctu_width - 1) + add_task(s, top, VVC_TASK_TYPE_ALF); + } + if (t->ry == ft->ctu_height - 1) { + if (t->rx > 0) + add_task(s, at - 1, VVC_TASK_TYPE_ALF); + if (t->rx == ft->ctu_width - 1) + add_task(s, at, VVC_TASK_TYPE_ALF); + } + +} + +static int run_sao(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->frame_thread; + int rs = t->ry * fc->ps.pps->ctb_width + t->rx; + const int ctb_size = ft->ctu_size; + + do { + const int x0 = t->rx * ctb_size; + const int y0 = t->ry * ctb_size; + + if (fc->ps.sps->r->sps_sao_enabled_flag) { + ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, rs); + ff_vvc_sao_filter(lc, x0, y0); + } + + if (fc->ps.sps->r->sps_alf_enabled_flag) + ff_vvc_alf_copy_ctu_to_hv(lc, x0, y0); + + set_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_SAO); + + add_alf_tasks(s, lc, t); + + rs++; + t->rx++; + } while (t->rx < ft->ctu_width && is_sao_ready(fc, t)); + + if (t->rx < ft->ctu_width) + ff_vvc_frame_add_task(s, t); + + return 0; +} + +static int run_alf(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->frame_thread; + const int ctu_size = ft->ctu_size; + const int x0 = t->rx * ctu_size; + const int y0 = t->ry * ctu_size; + + if (fc->ps.sps->r->sps_alf_enabled_flag) { + const int slice_idx = CTB(fc->tab.slice_idx, t->rx, t->ry); + if (slice_idx != -1) { + const int rs = t->ry * fc->ps.pps->ctb_width + t->rx; + lc->sc = fc->slices[slice_idx]; + ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, rs); + ff_vvc_alf_filter(lc, x0, y0); + } + } + set_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_ALF); + report_frame_progress(fc, t); + + return 0; +} + +static void finished_one_task(VVCFrameThread *ft, const VVCTaskType type) +{ + int parse_done = 0; + pthread_mutex_lock(&ft->lock); + + av_assert0(ft->nb_scheduled_tasks); + ft->nb_scheduled_tasks--; + + if (type == VVC_TASK_TYPE_PARSE) { + av_assert0(ft->nb_parse_tasks); + ft->nb_parse_tasks--; + if (!ft->nb_parse_tasks) + parse_done = 1; + } + if (parse_done || !ft->nb_scheduled_tasks) + pthread_cond_broadcast(&ft->cond); + + pthread_mutex_unlock(&ft->lock); +} + + +#define VVC_THREAD_DEBUG +#ifdef VVC_THREAD_DEBUG +const static char* task_name[] = { + "P", + "I", + "R", + "L", + "V", + "H", + "S", + "A" +}; +#endif + +typedef int (*run_func)(VVCContext *s, VVCLocalContext *lc, VVCTask *t); + +int ff_vvc_task_run(AVTask *_t, void *local_context, void *user_data) +{ + VVCTask *t = (VVCTask*)_t; + VVCContext *s = (VVCContext *)user_data; + VVCLocalContext *lc = local_context; + VVCFrameThread *ft = t->fc->frame_thread; + const VVCTaskType type = t->type; + int ret = 0; + run_func run[] = { + run_parse, + run_inter, + run_recon, + run_lmcs, + run_deblock_v, + run_deblock_h, + run_sao, + run_alf, + }; + + lc->fc = t->fc; + +#ifdef VVC_THREAD_DEBUG + av_log(s->avctx, AV_LOG_DEBUG, "frame %5d, %s(%3d, %3d)\r\n", (int)t->fc->decode_order, task_name[t->type], t->rx, t->ry); +#endif + + if (!atomic_load(&ft->ret)) { + if ((ret = run[t->type](s, lc, t)) < 0) { +#ifdef COMPAT_ATOMICS_WIN32_STDATOMIC_H + intptr_t zero = 0; +#else + int zero = 0; +#endif + atomic_compare_exchange_strong(&ft->ret, &zero, ret); + av_log(s->avctx, AV_LOG_ERROR, + "frame %5d, %s(%3d, %3d) failed with %d\r\n", + (int)t->fc->decode_order, task_name[type], t->rx, t->ry, ret); + } + } + + // t->type may changed by run(), we use a local copy of t->type + finished_one_task(ft, type); + + return ret; +} + +void ff_vvc_frame_thread_free(VVCFrameContext *fc) +{ + VVCFrameThread *ft = fc->frame_thread; + + if (!ft) + return; + + pthread_mutex_destroy(&ft->lock); + pthread_cond_destroy(&ft->cond); + av_freep(&ft->avails); + av_freep(&ft->cols); + av_freep(&ft->rows); + av_freep(&ft->tasks); + av_freep(&ft); +} + +int ff_vvc_frame_thread_init(VVCFrameContext *fc) +{ + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + VVCFrameThread *ft = fc->frame_thread; + int ret; + + if (!ft || ft->ctu_width != pps->ctb_width || + ft->ctu_height != pps->ctb_height || + ft->ctu_size != sps->ctb_size_y) { + + ff_vvc_frame_thread_free(fc); + ft = av_calloc(1, sizeof(*fc->frame_thread)); + if (!ft) + return AVERROR(ENOMEM); + + ft->ctu_width = fc->ps.pps->ctb_width; + ft->ctu_height = fc->ps.pps->ctb_height; + ft->ctu_count = fc->ps.pps->ctb_count; + ft->ctu_size = fc->ps.sps->ctb_size_y; + + ft->rows = av_calloc(ft->ctu_height, sizeof(*ft->rows)); + if (!ft->rows) + goto fail; + + for (int y = 0; y < ft->ctu_height; y++) { + VVCRowThread *row = ft->rows + y; + ff_vvc_task_init(&row->deblock_v_task, VVC_TASK_TYPE_DEBLOCK_V, fc); + row->deblock_v_task.ry = y; + ff_vvc_task_init(&row->sao_task, VVC_TASK_TYPE_SAO, fc); + row->sao_task.ry = y; + ff_vvc_task_init(&row->reconstruct_task, VVC_TASK_TYPE_RECON, fc); + row->reconstruct_task.ry = y; + } + + ft->cols = av_calloc(ft->ctu_width, sizeof(*ft->cols)); + if (!ft->cols) + goto fail; + for (int x = 0; x < ft->ctu_width; x++) { + VVCColThread *col = ft->cols + x; + ff_vvc_task_init(&col->deblock_h_task, VVC_TASK_TYPE_DEBLOCK_H, fc); + col->deblock_h_task.rx = x; + } + + ft->avails = av_calloc(ft->ctu_count, sizeof(*ft->avails)); + if (!ft->avails) + goto fail; + + ft->tasks = av_calloc(ft->ctu_count, sizeof(*ft->tasks)); + if (!ft->tasks) + goto fail; + for (int rs = 0; rs < ft->ctu_count; rs++) { + VVCTask *t = ft->tasks + rs; + t->rx = rs % ft->ctu_width; + t->ry = rs / ft->ctu_width; + t->fc = fc; + } + + if ((ret = pthread_cond_init(&ft->cond, NULL))) + goto fail; + + if ((ret = pthread_mutex_init(&ft->lock, NULL))) { + pthread_cond_destroy(&ft->cond); + goto fail; + } + } + + ft->ret = 0; + for (int y = 0; y < ft->ctu_height; y++) { + VVCRowThread *row = ft->rows + y; + + row->reconstruct_task.rx = 0; + memset(&row->progress[0], 0, sizeof(row->progress)); + row->deblock_v_task.rx = 0; + row->sao_task.rx = 0; + } + + for (int x = 0; x < ft->ctu_width; x++) { + VVCColThread *col = ft->cols + x; + col->deblock_h_task.ry = 0; + } + + for (int rs = 0; rs < ft->ctu_count; rs++) + ft->avails[rs] = 0; + + memset(&ft->row_progress[0], 0, sizeof(ft->row_progress)); + fc->frame_thread = ft; + + return 0; + +fail: + if (ft) { + av_freep(&ft->avails); + av_freep(&ft->cols); + av_freep(&ft->rows); + av_freep(&ft->tasks); + av_freep(&ft); + } + + return AVERROR(ENOMEM); +} + +void ff_vvc_frame_add_task(VVCContext *s, VVCTask *t) +{ + VVCFrameContext *fc = t->fc; + VVCFrameThread *ft = fc->frame_thread; + + pthread_mutex_lock(&ft->lock); + + ft->nb_scheduled_tasks++; + if (t->type == VVC_TASK_TYPE_PARSE) + ft->nb_parse_tasks++; + + pthread_mutex_unlock(&ft->lock); + + avpriv_executor_execute(s->executor, &t->task); +} + +int ff_vvc_frame_wait(VVCContext *s, VVCFrameContext *fc) +{ + VVCFrameThread *ft = fc->frame_thread; + int check_missed_slices = 1; + + pthread_mutex_lock(&ft->lock); + + while (ft->nb_scheduled_tasks) { + if (check_missed_slices && !ft->nb_parse_tasks) { + // abort for missed slices + for (int rs = 0; rs < ft->ctu_count; rs++){ + atomic_uchar mask = 1 << VVC_TASK_TYPE_PARSE; + if (!(atomic_load(ft->avails + rs) & mask)) { + atomic_store(&ft->ret, AVERROR_INVALIDDATA); + break; + } + } + check_missed_slices = 0; + } + pthread_cond_wait(&ft->cond, &ft->lock); + } + + pthread_mutex_unlock(&ft->lock); + ff_vvc_report_frame_finished(fc->ref); + + // maybe all threads are waiting, let us wake up one + avpriv_executor_execute(s->executor, NULL); + +#ifdef VVC_THREAD_DEBUG + av_log(s->avctx, AV_LOG_DEBUG, "frame %5d done\r\n", (int)fc->decode_order); +#endif + return ft->ret; +} diff --git a/libavcodec/vvc/vvc_thread.h b/libavcodec/vvc/vvc_thread.h new file mode 100644 index 00000000000..2f7fe28bbe8 --- /dev/null +++ b/libavcodec/vvc/vvc_thread.h @@ -0,0 +1,72 @@ +/* + * VVC thread logic + * + * Copyright (C) 2023 Nuo Mi + * + * 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_VVC_THREAD_H +#define AVCODEC_VVC_THREAD_H + +#include "vvcdec.h" + +typedef enum VVCTaskType { + VVC_TASK_TYPE_PARSE, + VVC_TASK_TYPE_INTER, + VVC_TASK_TYPE_RECON, + VVC_TASK_TYPE_LMCS, + VVC_TASK_TYPE_DEBLOCK_V, + VVC_TASK_TYPE_DEBLOCK_H, + VVC_TASK_TYPE_SAO, + VVC_TASK_TYPE_ALF, + VVC_TASK_TYPE_LAST +} VVCTaskType; + +struct VVCTask { + union { + VVCTask *next; //for executor debug only + AVTask task; + }; + + VVCTaskType type; + + // ctu x, y in raster order + int rx, ry; + VVCFrameContext *fc; + + // reconstruct task only + SliceContext *sc; + EntryPoint *ep; + int ctu_idx; //ctu idx in the current slice +}; + +void ff_vvc_task_init(VVCTask *task, VVCTaskType type, VVCFrameContext *fc); +void ff_vvc_parse_task_init(VVCTask *task, VVCTaskType type, VVCFrameContext *fc, + SliceContext *sc, EntryPoint *ep, int ctu_addr); +VVCTask* ff_vvc_task_alloc(void); + +int ff_vvc_task_ready(const AVTask* t, void* user_data); +int ff_vvc_task_priority_higher(const AVTask *a, const AVTask *b); +int ff_vvc_task_run(AVTask *t, void *local_context, void *user_data); + +int ff_vvc_frame_thread_init(VVCFrameContext *fc); +void ff_vvc_frame_thread_free(VVCFrameContext *fc); +void ff_vvc_frame_add_task(VVCContext *s, VVCTask *t); +int ff_vvc_frame_wait(VVCContext *s, VVCFrameContext *fc); + +#endif // AVCODEC_VVC_THREAD_H diff --git a/libavcodec/vvc/vvcdec.c b/libavcodec/vvc/vvcdec.c new file mode 100644 index 00000000000..1eb2b7724ad --- /dev/null +++ b/libavcodec/vvc/vvcdec.c @@ -0,0 +1,1107 @@ +/* + * VVC video decoder + * + * Copyright (C) 2021 Nuo Mi + * Copyright (C) 2022 Xu Mu + * + * 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 "libavcodec/codec_internal.h" +#include "libavcodec/decode.h" +#include "libavcodec/profiles.h" +#include "libavcodec/vvc.h" + +#include "libavutil/cpu.h" + +#include "vvcdec.h" +#include "vvc_ctu.h" +#include "vvc_data.h" +#include "vvc_refs.h" +#include "vvc_thread.h" + +static int vvc_frame_start(VVCContext *s, VVCFrameContext *fc, SliceContext *sc) +{ + const VVCPH *ph = &fc->ps.ph; + const H266RawSliceHeader *rsh = sc->sh.r; + int ret; + + // 8.3.1 Decoding process for picture order count + if (!s->temporal_id && !ph->r->ph_non_ref_pic_flag && !(IS_RASL(s) || IS_RADL(s))) + s->poc_tid0 = ph->poc; + + if ((ret = ff_vvc_set_new_ref(s, fc, &fc->frame)) < 0) + goto fail; + + if (!IS_IDR(s)) + ff_vvc_bump_frame(s, fc); + + av_frame_unref(fc->output_frame); + + if ((ret = ff_vvc_output_frame(s, fc, fc->output_frame,rsh->sh_no_output_of_prior_pics_flag, 0)) < 0) + goto fail; + + if ((ret = ff_vvc_frame_rpl(s, fc, sc)) < 0) + goto fail; + + if ((ret = ff_vvc_frame_thread_init(fc)) < 0) + goto fail; + return 0; +fail: + if (fc->ref) + ff_vvc_unref_frame(fc, fc->ref, ~0); + fc->ref = NULL; + return ret; +} + +static void ctb_arrays_free(VVCFrameContext *fc) +{ + av_freep(&fc->tab.deblock); + av_freep(&fc->tab.sao); + av_freep(&fc->tab.alf); + av_freep(&fc->tab.slice_idx); + av_freep(&fc->tab.coeffs); + if (fc->tab.ctus) { + for (int i = 0; i < fc->tab.ctu_count; i++) + ff_vvc_ctu_free_cus(fc->tab.ctus + i); + av_freep(&fc->tab.ctus); + } + av_buffer_pool_uninit(&fc->rpl_tab_pool); +} + +static int ctb_arrays_init(VVCFrameContext *fc, const int ctu_count, const int ctu_size) +{ + if (fc->tab.ctu_count != ctu_count || fc->tab.ctu_size != ctu_size) { + ctb_arrays_free(fc); + fc->tab.deblock = av_calloc(ctu_count, sizeof(*fc->tab.deblock)); + fc->tab.sao = av_calloc(ctu_count, sizeof(*fc->tab.sao)); + fc->tab.alf = av_calloc(ctu_count, sizeof(*fc->tab.alf)); + fc->tab.ctus = av_calloc(ctu_count, sizeof(*fc->tab.ctus)); + fc->tab.slice_idx = av_malloc(ctu_count * sizeof(*fc->tab.slice_idx)); + if (!fc->tab.deblock || !fc->tab.sao || !fc->tab.alf || !fc->tab.ctus || !fc->tab.slice_idx ) + return AVERROR(ENOMEM); + fc->tab.coeffs = av_malloc(ctu_count * sizeof(*fc->tab.coeffs) * ctu_size * VVC_MAX_SAMPLE_ARRAYS); + if (!fc->tab.coeffs) + return AVERROR(ENOMEM); + fc->rpl_tab_pool = av_buffer_pool_init(ctu_count * sizeof(RefPicListTab), av_buffer_allocz); + if (!fc->rpl_tab_pool) + return AVERROR(ENOMEM); + } else { + memset(fc->tab.deblock, 0, ctu_count * sizeof(*fc->tab.deblock)); + memset(fc->tab.sao, 0, ctu_count * sizeof(*fc->tab.sao)); + memset(fc->tab.alf, 0, ctu_count * sizeof(*fc->tab.alf)); + for (int i = 0; i < fc->tab.ctu_count; i++) + ff_vvc_ctu_free_cus(fc->tab.ctus + i); + memset(fc->tab.ctus, 0, ctu_count * sizeof(*fc->tab.ctus)); + } + memset(fc->tab.slice_idx, -1, ctu_count * sizeof(*fc->tab.slice_idx)); + + return 0; +} + +static void min_cb_arrays_free(VVCFrameContext *fc) +{ + for (int i = LUMA; i <= CHROMA; i++) { + av_freep(&fc->tab.cb_pos_x[i]); + av_freep(&fc->tab.cb_pos_y[i]); + av_freep(&fc->tab.cb_width[i]); + av_freep(&fc->tab.cb_height[i]); + av_freep(&fc->tab.cqt_depth[i]); + av_freep(&fc->tab.cpm[i]); + av_freep(&fc->tab.cp_mv[i]); + } + + av_freep(&fc->tab.ipm); + av_freep(&fc->tab.imf); + av_freep(&fc->tab.imtf); + av_freep(&fc->tab.imm); + av_freep(&fc->tab.skip); +} + +static int min_cb_arrays_init(VVCFrameContext *fc, const int pic_size_in_min_cb) +{ + if (fc->tab.pic_size_in_min_cb != pic_size_in_min_cb) { + min_cb_arrays_free(fc); + for (int i = LUMA; i <= CHROMA; i++) { + fc->tab.cb_pos_x[i] = av_mallocz(pic_size_in_min_cb * sizeof(int)); + fc->tab.cb_pos_y[i] = av_mallocz(pic_size_in_min_cb * sizeof(int)); + fc->tab.cb_width[i] = av_mallocz(pic_size_in_min_cb); + fc->tab.cb_height[i] = av_mallocz(pic_size_in_min_cb); + fc->tab.cqt_depth[i] = av_mallocz(pic_size_in_min_cb); + if (!fc->tab.cb_pos_x[i] || !fc->tab.cb_pos_y[i] || !fc->tab.cb_width[i] || !fc->tab.cb_height[i] || !fc->tab.cqt_depth[i]) + return AVERROR(ENOMEM); + + fc->tab.cpm[i] = av_mallocz(pic_size_in_min_cb); + fc->tab.cp_mv[i] = av_mallocz(pic_size_in_min_cb * sizeof(Mv) * MAX_CONTROL_POINTS); + if (!fc->tab.cpm[i] || !fc->tab.cp_mv[i]) + return AVERROR(ENOMEM); + } + + fc->tab.ipm = av_mallocz(pic_size_in_min_cb); + fc->tab.imf = av_mallocz(pic_size_in_min_cb); + fc->tab.imtf = av_mallocz(pic_size_in_min_cb); + fc->tab.imm = av_mallocz(pic_size_in_min_cb); + fc->tab.skip = av_mallocz(pic_size_in_min_cb); + if (!fc->tab.ipm || !fc->tab.imf || !fc->tab.imtf || !fc->tab.imm || !fc->tab.skip) + return AVERROR(ENOMEM); + } else { + for (int i = LUMA; i <= CHROMA; i++) { + memset(fc->tab.cb_pos_x[i], 0, pic_size_in_min_cb * sizeof(int)); + memset(fc->tab.cb_pos_y[i], 0, pic_size_in_min_cb * sizeof(int)); + memset(fc->tab.cb_width[i], 0, pic_size_in_min_cb); + memset(fc->tab.cb_height[i], 0, pic_size_in_min_cb); + memset(fc->tab.cqt_depth[i], 0, pic_size_in_min_cb); + memset(fc->tab.cpm[i], 0, pic_size_in_min_cb); + memset(fc->tab.cp_mv[i], 0, pic_size_in_min_cb * sizeof(Mv) * MAX_CONTROL_POINTS); + } + + memset(fc->tab.ipm, 0, pic_size_in_min_cb); + memset(fc->tab.imf, 0, pic_size_in_min_cb); + memset(fc->tab.imtf, 0, pic_size_in_min_cb); + memset(fc->tab.imm, 0, pic_size_in_min_cb); + memset(fc->tab.skip, 0, pic_size_in_min_cb); + } + return 0; +} + +static void min_tu_arrays_free(VVCFrameContext *fc) +{ + for (int i = LUMA; i <= CHROMA; i++) { + av_freep(&fc->tab.tb_pos_x0[i]); + av_freep(&fc->tab.tb_pos_y0[i]); + av_freep(&fc->tab.tb_width[i]); + av_freep(&fc->tab.tb_height[i]); + av_freep(&fc->tab.pcmf[i]); + } + + for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS; i++) { + av_freep(&fc->tab.qp[i]); + av_freep(&fc->tab.tu_coded_flag[i]); + } + + av_freep(&fc->tab.tu_joint_cbcr_residual_flag); +} + +static int min_tu_arrays_init(VVCFrameContext *fc, const int pic_size_in_min_tu) +{ + if (fc->tab.pic_size_in_min_tu != pic_size_in_min_tu) { + min_tu_arrays_free(fc); + for (int i = LUMA; i <= CHROMA; i++) { + fc->tab.tb_pos_x0[i] = av_mallocz(pic_size_in_min_tu * sizeof(*fc->tab.tb_pos_x0[0])); + fc->tab.tb_pos_y0[i] = av_mallocz(pic_size_in_min_tu * sizeof(*fc->tab.tb_pos_y0[0])) ; + fc->tab.tb_width[i] = av_mallocz(pic_size_in_min_tu); + fc->tab.tb_height[i] = av_mallocz(pic_size_in_min_tu); + fc->tab.pcmf[i] = av_mallocz(pic_size_in_min_tu); + if (!fc->tab.tb_pos_x0[i] || !fc->tab.tb_pos_y0[i] || + !fc->tab.tb_width[i] || !fc->tab.tb_height[i] || !fc->tab.pcmf[i]) + return AVERROR(ENOMEM); + } + + for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS; i++) { + fc->tab.tu_coded_flag[i] = av_mallocz(pic_size_in_min_tu); + if (!fc->tab.tu_coded_flag[i]) + return AVERROR(ENOMEM); + + fc->tab.qp[i] = av_mallocz(pic_size_in_min_tu); + if (!fc->tab.qp[i]) + return AVERROR(ENOMEM); + } + + fc->tab.tu_joint_cbcr_residual_flag = av_mallocz(pic_size_in_min_tu); + if (!fc->tab.tu_joint_cbcr_residual_flag) + return AVERROR(ENOMEM); + } else { + for (int i = LUMA; i <= CHROMA; i++) { + memset(fc->tab.tb_pos_x0[i], 0, pic_size_in_min_tu * sizeof(*fc->tab.tb_pos_x0[0])); + memset(fc->tab.tb_pos_y0[i], 0, pic_size_in_min_tu * sizeof(*fc->tab.tb_pos_y0[0])) ; + memset(fc->tab.tb_width[i], 0, pic_size_in_min_tu); + memset(fc->tab.tb_height[i], 0, pic_size_in_min_tu); + memset(fc->tab.pcmf[i], 0, pic_size_in_min_tu); + } + + for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS; i++) { + memset(fc->tab.tu_coded_flag[i], 0, pic_size_in_min_tu); + memset(fc->tab.qp[i], 0, pic_size_in_min_tu); + } + memset(fc->tab.tu_joint_cbcr_residual_flag, 0, pic_size_in_min_tu); + } + return 0; +} + +static void min_pu_arrays_free(VVCFrameContext *fc) +{ + av_freep(&fc->tab.mvf); + av_freep(&fc->tab.msf); + av_freep(&fc->tab.iaf); + av_freep(&fc->tab.mmi); + av_buffer_pool_uninit(&fc->tab_dmvr_mvf_pool); +} + +static int min_pu_arrays_init(VVCFrameContext *fc, const int pic_size_in_min_pu) +{ + if (fc->tab.pic_size_in_min_pu != pic_size_in_min_pu) { + min_pu_arrays_free(fc); + fc->tab.msf = av_mallocz(pic_size_in_min_pu); + fc->tab.iaf = av_mallocz(pic_size_in_min_pu); + fc->tab.mmi = av_mallocz(pic_size_in_min_pu); + fc->tab.mvf = av_mallocz(pic_size_in_min_pu * sizeof(*fc->tab.mvf)); + if (!fc->tab.msf || !fc->tab.iaf || !fc->tab.mmi || !fc->tab.mvf) + return AVERROR(ENOMEM); + fc->tab_dmvr_mvf_pool = av_buffer_pool_init(pic_size_in_min_pu * sizeof(MvField), av_buffer_allocz); + if (!fc->tab_dmvr_mvf_pool) + return AVERROR(ENOMEM); + } else { + memset(fc->tab.msf, 0, pic_size_in_min_pu); + memset(fc->tab.iaf, 0, pic_size_in_min_pu); + memset(fc->tab.mmi, 0, pic_size_in_min_pu); + memset(fc->tab.mvf, 0, pic_size_in_min_pu * sizeof(*fc->tab.mvf)); + } + + return 0; +} + +static void bs_arrays_free(VVCFrameContext *fc) +{ + for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS; i++) { + av_freep(&fc->tab.horizontal_bs[i]); + av_freep(&fc->tab.vertical_bs[i]); + } + av_freep(&fc->tab.horizontal_q); + av_freep(&fc->tab.horizontal_p); + av_freep(&fc->tab.vertical_p); + av_freep(&fc->tab.vertical_q); +} + +static int bs_arrays_init(VVCFrameContext *fc, const int bs_width, const int bs_height) +{ + if (fc->tab.bs_width != bs_width || fc->tab.bs_height != bs_height) { + bs_arrays_free(fc); + for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS; i++) { + fc->tab.horizontal_bs[i] = av_calloc(bs_width, bs_height); + fc->tab.vertical_bs[i] = av_calloc(bs_width, bs_height); + if (!fc->tab.horizontal_bs[i] || !fc->tab.vertical_bs[i]) + return AVERROR(ENOMEM); + } + fc->tab.horizontal_q = av_calloc(bs_width, bs_height); + fc->tab.horizontal_p = av_calloc(bs_width, bs_height); + fc->tab.vertical_p = av_calloc(bs_width, bs_height); + fc->tab.vertical_q = av_calloc(bs_width, bs_height); + if (!fc->tab.horizontal_q || !fc->tab.horizontal_p || !fc->tab.vertical_p || !fc->tab.vertical_q) + return AVERROR(ENOMEM); + } else { + for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS; i++) { + memset(fc->tab.horizontal_bs[i], 0, bs_width * bs_height); + memset(fc->tab.vertical_bs[i], 0, bs_width * bs_height); + } + memset(fc->tab.horizontal_q, 0, bs_width * bs_height); + memset(fc->tab.horizontal_p, 0, bs_width * bs_height); + memset(fc->tab.vertical_p, 0, bs_width * bs_height); + memset(fc->tab.vertical_q, 0, bs_width * bs_height); + } + return 0; +} + +static void pixel_buffer_free(VVCFrameContext *fc) +{ + for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS; i++) { + av_freep(&fc->tab.sao_pixel_buffer_h[i]); + av_freep(&fc->tab.sao_pixel_buffer_v[i]); + for (int j = 0; j < 2; j++) { + av_freep(&fc->tab.alf_pixel_buffer_h[i][j]); + av_freep(&fc->tab.alf_pixel_buffer_v[i][j]); + } + } +} + +static int pixel_buffer_init(VVCFrameContext *fc, const int width, const int height, + const int ctu_width, const int ctu_height, const int chroma_format_idc, const int ps) +{ + const VVCSPS *sps = fc->ps.sps; + const int c_end = chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : 1; + + if (fc->tab.chroma_format_idc != chroma_format_idc || + fc->tab.width != width || fc->tab.height != height || + fc->tab.ctu_width != ctu_width || fc->tab.ctu_height != ctu_height) { + pixel_buffer_free(fc); + for (int c_idx = 0; c_idx < c_end; c_idx++) { + const int w = width >> sps->hshift[c_idx]; + const int h = height >> sps->vshift[c_idx]; + fc->tab.sao_pixel_buffer_h[c_idx] = av_malloc((w * 2 * ctu_height) << ps); + fc->tab.sao_pixel_buffer_v[c_idx] = av_malloc((h * 2 * ctu_width) << ps); + if (!fc->tab.sao_pixel_buffer_h[c_idx] || !fc->tab.sao_pixel_buffer_v[c_idx]) + return AVERROR(ENOMEM); + } + + for (int c_idx = 0; c_idx < c_end; c_idx++) { + const int w = width >> sps->hshift[c_idx]; + const int h = height >> sps->vshift[c_idx]; + const int border_pixels = c_idx ? ALF_BORDER_CHROMA : ALF_BORDER_LUMA; + for (int i = 0; i < 2; i++) { + fc->tab.alf_pixel_buffer_h[c_idx][i] = av_malloc((w * border_pixels * ctu_height) << ps); + fc->tab.alf_pixel_buffer_v[c_idx][i] = av_malloc(h * ALF_PADDING_SIZE * ctu_width); + if (!fc->tab.alf_pixel_buffer_h[c_idx][i] || !fc->tab.alf_pixel_buffer_v[c_idx][i]) + return AVERROR(ENOMEM); + } + } + } + return 0; +} + +static void pic_arrays_free(VVCFrameContext *fc) +{ + ctb_arrays_free(fc); + min_cb_arrays_free(fc); + min_pu_arrays_free(fc); + min_tu_arrays_free(fc); + bs_arrays_free(fc); + av_buffer_pool_uninit(&fc->cu_pool); + av_buffer_pool_uninit(&fc->tu_pool); + pixel_buffer_free(fc); + + for (int i = 0; i < 2; i++) + av_freep(&fc->tab.msm[i]); + av_freep(&fc->tab.ispmf); + + fc->tab.ctu_count = 0; + fc->tab.ctu_size = 0; + fc->tab.pic_size_in_min_cb = 0; + fc->tab.pic_size_in_min_pu = 0; + fc->tab.pic_size_in_min_tu = 0; + fc->tab.width = 0; + fc->tab.height = 0; + fc->tab.ctu_width = 0; + fc->tab.ctu_height = 0; + fc->tab.bs_width = 0; + fc->tab.bs_height = 0; +} + +static int pic_arrays_init(VVCContext *s, VVCFrameContext *fc) +{ + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int ctu_size = 1 << sps->ctb_log2_size_y << sps->ctb_log2_size_y; + const int pic_size_in_min_cb = pps->min_cb_width * pps->min_cb_height; + const int pic_size_in_min_pu = pps->min_pu_width * pps->min_pu_height; + const int pic_size_in_min_tu = pps->min_tu_width * pps->min_tu_height; + const int w32 = AV_CEIL_RSHIFT(pps->width, 5); + const int h32 = AV_CEIL_RSHIFT(pps->height, 5); + const int w64 = AV_CEIL_RSHIFT(pps->width, 6); + const int h64 = AV_CEIL_RSHIFT(pps->height, 6); + const int bs_width = (fc->ps.pps->width >> 2) + 1; + const int bs_height = (fc->ps.pps->height >> 2) + 1; + int ret; + + if ((ret = ctb_arrays_init(fc, pps->ctb_count, ctu_size)) < 0) + goto fail; + + if ((ret = min_cb_arrays_init(fc, pic_size_in_min_cb)) < 0) + goto fail; + + if ((ret = min_pu_arrays_init(fc, pic_size_in_min_pu)) < 0) + goto fail; + + if ((ret = min_tu_arrays_init(fc, pic_size_in_min_tu)) < 0) + goto fail; + + if ((ret = bs_arrays_init(fc, bs_width, bs_height)) < 0) + goto fail; + + if ((ret = pixel_buffer_init(fc, pps->width, pps->height, pps->ctb_width, pps->ctb_height, + sps->r->sps_chroma_format_idc, sps->pixel_shift)) < 0) + goto fail; + + if (AV_CEIL_RSHIFT(fc->tab.width, 5) != w32 || AV_CEIL_RSHIFT(fc->tab.height, 5) != h32) { + for (int i = LUMA; i <= CHROMA; i++) { + av_freep(&fc->tab.msm[i]); + fc->tab.msm[i] = av_calloc(w32, h32); + if (!fc->tab.msm[i]) + goto fail; + } + } else { + for (int i = LUMA; i <= CHROMA; i++) + memset(fc->tab.msm[i], 0, w32 * h32); + } + if (AV_CEIL_RSHIFT(fc->tab.width, 6) != w64 || AV_CEIL_RSHIFT(fc->tab.height, 6) != h64) { + av_freep(&fc->tab.ispmf); + fc->tab.ispmf = av_calloc(w64, h64); + if (!fc->tab.ispmf) + goto fail; + } else { + memset(fc->tab.ispmf, 0, w64 * h64); + } + + if (!fc->cu_pool) { + fc->cu_pool = av_buffer_pool_init(sizeof(CodingUnit), NULL); + if (!fc->cu_pool) + goto fail; + } + + if (!fc->tu_pool) { + fc->tu_pool = av_buffer_pool_init(sizeof(TransformUnit), NULL); + if (!fc->tu_pool) + goto fail; + } + + fc->tab.ctu_count = pps->ctb_count; + fc->tab.ctu_size = ctu_size; + fc->tab.pic_size_in_min_cb = pic_size_in_min_cb; + fc->tab.pic_size_in_min_pu = pic_size_in_min_pu; + fc->tab.pic_size_in_min_tu = pic_size_in_min_tu; + fc->tab.width = pps->width; + fc->tab.height = pps->height; + fc->tab.ctu_width = pps->ctb_width; + fc->tab.ctu_height = pps->ctb_height; + fc->tab.chroma_format_idc = sps->r->sps_chroma_format_idc; + fc->tab.pixel_shift = sps->pixel_shift; + fc->tab.bs_width = bs_width; + fc->tab.bs_height = bs_height; + + return 0; +fail: + pic_arrays_free(fc); + return ret; +} + +static int min_positive(const int idx, const int diff, const int min_diff) +{ + return diff > 0 && (idx < 0 || diff < min_diff); +} + +static int max_negtive(const int idx, const int diff, const int max_diff) +{ + return diff < 0 && (idx < 0 || diff > max_diff); +} + +typedef int (*smvd_find_fxn)(const int idx, const int diff, const int old_diff); + +static int8_t smvd_find(const VVCFrameContext *fc, const SliceContext *sc, int lx, smvd_find_fxn find) +{ + const H266RawSliceHeader *rsh = sc->sh.r; + const RefPicList *rpl = sc->rpl + lx; + const int poc = fc->ref->poc; + int8_t idx = -1; + int old_diff = -1; + for (int i = 0; i < rsh->num_ref_idx_active[lx]; i++) { + if (!rpl->isLongTerm[i]) { + int diff = poc - rpl->list[i]; + if (find(idx, diff, old_diff)) { + idx = i; + old_diff = diff; + } + } + } + return idx; +} + +static void vvc_smvd_ref_idx(const VVCFrameContext *fc, SliceContext *sc) +{ + VVCSH *sh = &sc->sh; + if (IS_B(sh->r)) { + sh->ref_idx_sym[0] = smvd_find(fc, sc, 0, min_positive); + sh->ref_idx_sym[1] = smvd_find(fc, sc, 1, max_negtive); + if (sh->ref_idx_sym[0] == -1 || sh->ref_idx_sym[1] == -1) { + sh->ref_idx_sym[0] = smvd_find(fc, sc, 0, max_negtive); + sh->ref_idx_sym[1] = smvd_find(fc, sc, 1, min_positive); + } + } +} + +static void eps_free(SliceContext *slice) +{ + if (slice->eps) { + for (int j = 0; j < slice->nb_eps; j++) + av_free(slice->eps[j].parse_task); + av_freep(&slice->eps); + } +} + +static void slices_free(VVCFrameContext *fc) +{ + if (fc->slices) { + for (int i = 0; i < fc->nb_slices_allocated; i++) { + SliceContext *slice = fc->slices[i]; + if (slice) { + av_buffer_unref(&slice->sh.rref); + eps_free(slice); + av_free(slice); + } + } + av_freep(&fc->slices); + } + fc->nb_slices_allocated = 0; + fc->nb_slices = 0; +} + +static int slices_realloc(VVCFrameContext *fc) +{ + void *p; + const int size = (fc->nb_slices_allocated + 1) * 3 / 2; + + if (fc->nb_slices < fc->nb_slices_allocated) + return 0; + + p = av_realloc(fc->slices, size * sizeof(*fc->slices)); + if (!p) + return AVERROR(ENOMEM); + + fc->slices = p; + for (int i = fc->nb_slices_allocated; i < size; i++) { + fc->slices[i] = av_calloc(1, sizeof(*fc->slices[0])); + if (!fc->slices[i]) { + for (int j = fc->nb_slices_allocated; j < i; j++) + av_freep(&fc->slices[j]); + return AVERROR(ENOMEM); + } + fc->slices[i]->slice_idx = i; + } + fc->nb_slices_allocated = size; + return 0; +} + +static void ep_init_cabac_decoder(SliceContext *sc, const int index, const H2645NAL *nal, GetBitContext *gb) +{ + const H266RawSliceHeader *rsh = sc->sh.r; + EntryPoint *ep = sc->eps + index; + int size; + + if (index < rsh->num_entry_points) { + int skipped = 0; + int64_t start = (gb->index >> 3); + int64_t end = start + rsh->sh_entry_point_offset_minus1[index] + 1; + while (skipped < nal->skipped_bytes && nal->skipped_bytes_pos[skipped] <= start) { + skipped++; + } + while (skipped < nal->skipped_bytes && nal->skipped_bytes_pos[skipped] < end) { + end--; + skipped++; + } + size = end - start; + } else { + size = get_bits_left(gb) / 8; + } + ff_init_cabac_decoder (&ep->cc, gb->buffer + get_bits_count(gb) / 8, size); + skip_bits(gb, size * 8); +} + +static int init_slice_context(SliceContext *sc, VVCFrameContext *fc, const H2645NAL *nal, const CodedBitstreamUnit *unit) +{ + const VVCSH *sh = &sc->sh; + const H266RawSlice *slice = (const H266RawSlice *)unit->content; + int nb_eps = sh->r->num_entry_points + 1; + int ctu_addr = 0; + GetBitContext gb; + + if (sc->nb_eps != nb_eps) { + eps_free(sc); + sc->eps = av_calloc(nb_eps, sizeof(*sc->eps)); + if (!sc->eps) + return AVERROR(ENOMEM); + sc->nb_eps = nb_eps; + for (int i = 0; i < sc->nb_eps; i++) { + EntryPoint *ep = sc->eps + i; + ep->parse_task = ff_vvc_task_alloc(); + if (!ep->parse_task) + return AVERROR(ENOMEM); + } + } + + init_get_bits8(&gb, slice->data, slice->data_size); + for (int i = 0; i < sc->nb_eps; i++) + { + EntryPoint *ep = sc->eps + i; + ff_vvc_parse_task_init(ep->parse_task, VVC_TASK_TYPE_PARSE, fc, sc, ep, ctu_addr); + ep->ctu_end = (i + 1 == sc->nb_eps ? sh->num_ctus_in_curr_slice : sh->entry_point_start_ctu[i]); + ep_init_cabac_decoder(sc, i, nal, &gb); + if (i + 1 < sc->nb_eps) + ctu_addr = sh->entry_point_start_ctu[i]; + } + + return 0; +} + +static VVCFrameContext* get_frame_context(const VVCContext *s, const VVCFrameContext *fc, const int delta) +{ + const int size = s->nb_fcs; + const int idx = (fc - s->fcs + delta + size) % size; + return s->fcs + idx; +} + +static int vvc_ref_frame(VVCFrameContext *fc, VVCFrame *dst, VVCFrame *src) +{ + int ret; + + ret = ff_thread_ref_frame(&dst->tf, &src->tf); + if (ret < 0) + return ret; + + dst->progress_buf = av_buffer_ref(src->progress_buf); + + dst->tab_dmvr_mvf_buf = av_buffer_ref(src->tab_dmvr_mvf_buf); + if (!dst->tab_dmvr_mvf_buf) + goto fail; + dst->tab_dmvr_mvf = src->tab_dmvr_mvf; + + dst->rpl_tab_buf = av_buffer_ref(src->rpl_tab_buf); + if (!dst->rpl_tab_buf) + goto fail; + dst->rpl_tab = src->rpl_tab; + + dst->rpl_buf = av_buffer_ref(src->rpl_buf); + if (!dst->rpl_buf) + goto fail; + + dst->poc = src->poc; + dst->ctb_count = src->ctb_count; + dst->flags = src->flags; + dst->sequence = src->sequence; + + return 0; +fail: + ff_vvc_unref_frame(fc, dst, ~0); + return AVERROR(ENOMEM); +} + +static av_cold void frame_context_free(VVCFrameContext *fc) +{ + slices_free(fc); + + for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + ff_vvc_unref_frame(fc, &fc->DPB[i], ~0); + av_frame_free(&fc->DPB[i].frame); + } + + ff_vvc_frame_thread_free(fc); + pic_arrays_free(fc); + av_frame_free(&fc->output_frame); + ff_vvc_frame_ps_free(&fc->ps); + av_freep(&fc->avctx); +} + +static av_cold int frame_context_init(VVCFrameContext *fc, AVCodecContext *avctx) +{ + + fc->avctx = av_memdup(avctx, sizeof(*avctx)); + if (!fc->avctx) + goto fail; + + fc->output_frame = av_frame_alloc(); + if (!fc->output_frame) + goto fail; + + for (int j = 0; j < FF_ARRAY_ELEMS(fc->DPB); j++) { + fc->DPB[j].frame = av_frame_alloc(); + if (!fc->DPB[j].frame) + goto fail; + fc->DPB[j].tf.f = fc->DPB[j].frame; + } + + return 0; +fail: + return AVERROR(ENOMEM); +} + +static int frame_context_setup(VVCFrameContext *fc, VVCContext *s) +{ + int ret = 0; + + // copy refs from the last frame + if (s->nb_frames && s->nb_fcs > 1) { + VVCFrameContext *prev = get_frame_context(s, fc, -1); + for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + ff_vvc_unref_frame(fc, &fc->DPB[i], ~0); + if (prev->DPB[i].frame->buf[0]) { + ret = vvc_ref_frame(fc, &fc->DPB[i], &prev->DPB[i]); + if (ret < 0) + goto fail; + } + } + } + + if (IS_IDR(s)) { + s->seq_decode = (s->seq_decode + 1) & 0xff; + ff_vvc_clear_refs(fc); + } + + ret = pic_arrays_init(s, fc); + if (ret < 0) + goto fail; + ff_vvc_dsp_init(&fc->vvcdsp, fc->ps.sps->bit_depth); + ff_videodsp_init(&fc->vdsp, fc->ps.sps->bit_depth); + +fail: + return ret; +} + +static void export_frame_params(VVCFrameContext *fc) +{ + AVCodecContext *c = fc->avctx; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + + c->pix_fmt = sps->pix_fmt; + c->coded_width = pps->width; + c->coded_height = pps->height; + c->width = pps->width - pps->r->pps_conf_win_left_offset - pps->r->pps_conf_win_right_offset; + c->height = pps->height - pps->r->pps_conf_win_top_offset - pps->r->pps_conf_win_bottom_offset; +} + +static int decode_slice(VVCContext *s, VVCFrameContext *fc, const H2645NAL *nal, const CodedBitstreamUnit *unit) +{ + int ret = 0; + SliceContext *sc; + VVCSH *sh; + const int is_first_slice = !fc->nb_slices; + + ret = slices_realloc(fc); + if (ret < 0) + return ret; + sc = fc->slices[fc->nb_slices]; + + sh = &sc->sh; + + if (ret < 0) + goto fail; + + s->vcl_unit_type = nal->type; + if (is_first_slice) { + //first slice + ret = ff_vvc_decode_frame_ps(&fc->ps, s); + if (ret < 0) + return ret; + + ret = frame_context_setup(fc, s); + if (ret < 0) + goto fail; + + export_frame_params(fc); + } + + ret = ff_vvc_decode_sh(&sc->sh, &fc->ps, unit); + if (ret < 0) + return ret; + + if (is_first_slice) { + ret = vvc_frame_start(s, fc, sc); + if (ret < 0) + return ret; + } else if (fc->ref) { + if (!IS_I(sh->r)) { + ret = ff_vvc_slice_rpl(s, fc, sc); + if (ret < 0) { + av_log(fc->avctx, AV_LOG_WARNING, + "Error constructing the reference lists for the current slice.\n"); + return ret; + } + } + } else { + av_log(fc->avctx, AV_LOG_ERROR, "First slice in a frame missing.\n"); + return ret; + } + + if (!IS_I(sh->r)) + vvc_smvd_ref_idx(fc, sc); + + ret = init_slice_context(sc, fc, nal, unit); + if (ret < 0) + goto fail; + fc->nb_slices++; + + for (int i = 0; i < (fc->ps.sps->r->sps_entropy_coding_sync_enabled_flag ? 1 : sc->nb_eps); i++) + ff_vvc_frame_add_task(s, sc->eps[i].parse_task); + +fail: + return ret; +} + +static int decode_nal_unit(VVCContext *s, VVCFrameContext *fc, const H2645NAL *nal, const CodedBitstreamUnit *unit) +{ + int ret; + + s->temporal_id = nal->temporal_id; + + switch (unit->type) { + case VVC_VPS_NUT: + case VVC_SPS_NUT: + case VVC_PPS_NUT: + /* vps, sps, sps cached by s->cbc */ + break; + case VVC_TRAIL_NUT: + case VVC_STSA_NUT: + case VVC_RADL_NUT: + case VVC_RASL_NUT: + case VVC_IDR_W_RADL: + case VVC_IDR_N_LP: + case VVC_CRA_NUT: + case VVC_GDR_NUT: + ret = decode_slice(s, fc, nal, unit); + if (ret < 0) + goto fail; + break; + case VVC_PREFIX_APS_NUT: + case VVC_SUFFIX_APS_NUT: + ret = ff_vvc_decode_aps(&s->ps, unit); + if (ret < 0) + goto fail; + break; + default: + av_log(s->avctx, AV_LOG_INFO, + "Skipping NAL unit %d\n", unit->type); + } + + return 0; +fail: + return ret; +} + +static int decode_nal_units(VVCContext *s, VVCFrameContext *fc, AVPacket *avpkt) +{ + const CodedBitstreamH266Context *h266 = (const CodedBitstreamH266Context *)s->cbc->priv_data; + CodedBitstreamFragment *frame = &s->current_frame; + int i, ret = 0; + int eos_at_start = 1; + s->last_eos = s->eos; + s->eos = 0; + + ff_cbs_fragment_reset(frame); + ret = ff_cbs_read_packet(s->cbc, frame, avpkt); + if (ret < 0) { + av_log(s->avctx, AV_LOG_ERROR, "Failed to read packet.\n"); + return ret; + } + /* decode the NAL units */ + for (i = 0; i < frame->nb_units; i++) { + const H2645NAL *nal = h266->common.read_packet.nals + i; + const CodedBitstreamUnit *unit = frame->units + i; + + if (unit->type == VVC_EOB_NUT || unit->type == VVC_EOS_NUT) { + if (eos_at_start) + s->last_eos = 1; + else + s->eos = 1; + } else { + ret = decode_nal_unit(s, fc, nal, unit); + if (ret < 0) { + av_log(s->avctx, AV_LOG_WARNING, + "Error parsing NAL unit #%d.\n", i); + goto fail; + } + } + } + return 0; + +fail: + if (fc->ref) + ff_vvc_report_frame_finished(fc->ref); + return ret; +} + +static int set_output_format(const VVCContext *s, const AVFrame *output) +{ + AVCodecContext *c = s->avctx; + int ret; + + if (output->width != c->width || output->height != c->height) { + if ((ret = ff_set_dimensions(c, output->width, output->height)) < 0) + return ret; + } + c->pix_fmt = output->format; + return 0; +} + +static int wait_delayed_frame(VVCContext *s, AVFrame *output, int *got_output) +{ + VVCFrameContext *delayed = get_frame_context(s, s->fcs, s->nb_frames - s->nb_delayed); + int ret = ff_vvc_frame_wait(s, delayed); + + if (!ret && delayed->output_frame->buf[0]) { + av_frame_move_ref(output, delayed->output_frame); + ret = set_output_format(s, output); + if (!ret) + *got_output = 1; + } + s->nb_delayed--; + + return ret; +} + +static int submit_frame(VVCContext *s, VVCFrameContext *fc, AVFrame *output, int *got_output) +{ + int ret; + s->nb_frames++; + s->nb_delayed++; + if (s->nb_delayed >= s->nb_fcs) { + if ((ret = wait_delayed_frame(s, output, got_output)) < 0) + return ret; + } + return 0; +} + +static int vvc_decode_frame(AVCodecContext *avctx, AVFrame *output, + int *got_output, AVPacket *avpkt) +{ + VVCContext *s = avctx->priv_data; + VVCFrameContext *fc; + int ret; + + if (!avpkt->size) { + while (s->nb_delayed) { + if ((ret = wait_delayed_frame(s, output, got_output)) < 0) + return ret; + if (*got_output) + return 0; + } + if (s->nb_frames) { + //we still have frames cached in dpb. + VVCFrameContext *last = get_frame_context(s, s->fcs, s->nb_frames - 1); + + ret = ff_vvc_output_frame(s, last, output, 0, 1); + if (ret < 0) + return ret; + if (ret) { + *got_output = ret; + if ((ret = set_output_format(s, output)) < 0) + return ret; + } + } + return 0; + } + + fc = get_frame_context(s, s->fcs, s->nb_frames); + + fc->nb_slices = 0; + fc->decode_order = s->nb_frames; + + ret = decode_nal_units(s, fc, avpkt); + if (ret < 0) + return ret; + + ret = submit_frame(s, fc, output, got_output); + if (ret < 0) + return ret; + + return avpkt->size; +} + +static void vvc_decode_flush(AVCodecContext *avctx) +{ + VVCContext *s = avctx->priv_data; + int got_output; + AVFrame *output = av_frame_alloc(); + + if (output) { + while (s->nb_delayed) { + wait_delayed_frame(s, output, &got_output); + if (got_output) { + av_frame_unref(output); + } + } + av_frame_free(&output); + } +} + +static av_cold int vvc_decode_free(AVCodecContext *avctx) +{ + VVCContext *s = avctx->priv_data; + int i; + + ff_cbs_fragment_free(&s->current_frame); + vvc_decode_flush(avctx); + avpriv_executor_free(&s->executor); + if (s->fcs) { + for (i = 0; i < s->nb_fcs; i++) + frame_context_free(s->fcs + i); + av_free(s->fcs); + } + ff_vvc_ps_uninit(&s->ps); + ff_cbs_close(&s->cbc); + + return 0; +} + +#define VVC_MAX_FRMAE_DELAY 16 +static av_cold int vvc_decode_init(AVCodecContext *avctx) +{ + VVCContext *s = avctx->priv_data; + int ret; + AVTaskCallbacks callbacks = { + s, + sizeof(VVCLocalContext), + ff_vvc_task_priority_higher, + ff_vvc_task_ready, + ff_vvc_task_run, + }; + + s->avctx = avctx; + + if (ff_cbs_init(&s->cbc, AV_CODEC_ID_VVC, avctx)) + goto fail; + + s->nb_fcs = (avctx->flags & AV_CODEC_FLAG_LOW_DELAY) ? 1 : FFMIN(av_cpu_count(), VVC_MAX_FRMAE_DELAY); + s->fcs = av_calloc(s->nb_fcs, sizeof(*s->fcs)); + if (!s->fcs) + goto fail; + + for (int i = 0; i < s->nb_fcs; i++) { + VVCFrameContext *fc = s->fcs + i; + ret = frame_context_init(fc, avctx); + if (ret < 0) + goto fail; + } + + s->executor = avpriv_executor_alloc(&callbacks, s->nb_fcs); + s->eos = 1; + GDR_SET_RECOVERED(s); + memset(&ff_vvc_default_scale_m, 16, sizeof(ff_vvc_default_scale_m)); + + return 0; + +fail: + vvc_decode_free(avctx); + return AVERROR(ENOMEM); +} + +#define OFFSET(x) offsetof(VVCContext, x) +#define PAR (AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) + +static const AVOption options[] = { + { NULL }, +}; + +static const AVClass vvc_decoder_class = { + .class_name = "vvc decoder", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const FFCodec ff_vvc_decoder = { + .p.name = "vvc", + .p.long_name = NULL_IF_CONFIG_SMALL("VVC (Versatile Video Coding)"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VVC, + .priv_data_size = sizeof(VVCContext), + .p.priv_class = &vvc_decoder_class, + .init = vvc_decode_init, + .close = vvc_decode_free, + FF_CODEC_DECODE_CB(vvc_decode_frame), + .flush = vvc_decode_flush, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_OTHER_THREADS, + .caps_internal = FF_CODEC_CAP_EXPORTS_CROPPING | FF_CODEC_CAP_INIT_CLEANUP | + FF_CODEC_CAP_AUTO_THREADS, + .p.profiles = NULL_IF_CONFIG_SMALL(ff_vvc_profiles), +}; diff --git a/libavcodec/vvc/vvcdec.h b/libavcodec/vvc/vvcdec.h new file mode 100644 index 00000000000..255f374d905 --- /dev/null +++ b/libavcodec/vvc/vvcdec.h @@ -0,0 +1,316 @@ +/* + * VVC video decoder + * + * Copyright (C) 2021 Nuo Mi + * Copyright (C) 2022 Xu Mu + * + * 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_VVCDEC_H +#define AVCODEC_VVCDEC_H + +#include "libavcodec/cbs.h" +#include "libavcodec/cbs_h266.h" +#include "libavcodec/h2645_parse.h" +#include "libavcodec/threadframe.h" +#include "libavcodec/videodsp.h" +#include "libavcodec/vvc.h" +#include "libavutil/executor.h" + +#include "vvc_ps.h" +#include "vvcdsp.h" + +#define LUMA 0 +#define CHROMA 1 +#define CB 1 +#define CR 2 +#define JCBCR 3 + +#define MAX_CTU_SIZE 128 + +#define MAX_CU_SIZE MAX_CTU_SIZE +#define MIN_CU_SIZE 4 +#define MIN_CU_LOG2 2 +#define MAX_CU_DEPTH 7 + +#define MIN_PU_SIZE 4 +#define MIN_PU_LOG2 2 + +#define MAX_TB_SIZE 64 +#define MIN_TU_LOG2 2 ///< MinTbLog2SizeY +#define MIN_TU_SIZE 4 +#define MAX_TUS_IN_CU 64 + +#define MAX_PARTS_IN_CTU ((MAX_CTU_SIZE >> MIN_CU_LOG2) * (MAX_CTU_SIZE >> MIN_CU_LOG2)) + +#define MAX_CONTROL_POINTS 3 + +#define MRG_MAX_NUM_CANDS 6 +#define MAX_NUM_HMVP_CANDS 5 + +#define L0 0 +#define L1 1 + +#define CHROMA_EXTRA_BEFORE 1 +#define CHROMA_EXTRA_AFTER 2 +#define CHROMA_EXTRA 3 +#define LUMA_EXTRA_BEFORE 3 +#define LUMA_EXTRA_AFTER 4 +#define LUMA_EXTRA 7 +#define BILINEAR_EXTRA_BEFORE 0 +#define BILINEAR_EXTRA_AFTER 1 +#define BILINEAR_EXTRA 1 + +#define MAX_QP 63 +#define DEFAULT_INTRA_TC_OFFSET 2 + +#define SAO_PADDING_SIZE 1 + +#define ALF_PADDING_SIZE 8 +#define ALF_BLOCK_SIZE 4 + +#define ALF_BORDER_LUMA 3 +#define ALF_BORDER_CHROMA 2 + +#define ALF_VB_POS_ABOVE_LUMA 4 +#define ALF_VB_POS_ABOVE_CHROMA 2 + +#define ALF_GRADIENT_STEP 2 +#define ALF_GRADIENT_BORDER 2 +#define ALF_GRADIENT_SIZE ((MAX_CU_SIZE + ALF_GRADIENT_BORDER * 2) / ALF_GRADIENT_STEP) +#define ALF_NUM_DIR 4 + +#define MAX_PB_SIZE 128 +#define EDGE_EMU_BUFFER_STRIDE (MAX_PB_SIZE + 32) + +#define AFFINE_MIN_BLOCK_SIZE 4 +#define PROF_BORDER_EXT 1 +#define PROF_BLOCK_SIZE (AFFINE_MIN_BLOCK_SIZE + PROF_BORDER_EXT * 2) +#define BDOF_BORDER_EXT 1 + +#define BDOF_PADDED_SIZE (16 + BDOF_BORDER_EXT * 2) +#define BDOF_BLOCK_SIZE 4 +#define BDOF_GRADIENT_SIZE (BDOF_BLOCK_SIZE + BDOF_BORDER_EXT * 2) + +/** + * Value of the luma sample at position (x, y) in the 2D array tab. + */ +#define SAMPLE(tab, x, y) ((tab)[(y) * s->sps->width + (x)]) +#define SAMPLE_CTB(tab, x, y) ((tab)[(y) * min_cb_width + (x)]) +#define CTB(tab, x, y) ((tab)[(y) * fc->ps.pps->ctb_width + (x)]) + +typedef struct VVCLocalContext VVCLocalContext; +typedef struct SliceContext SliceContext; +typedef struct VVCFrameContext VVCFrameContext; +typedef struct VVCFrameThread VVCFrameThread; +typedef struct EntryPoint EntryPoint; +typedef struct VVCTask VVCTask; +typedef struct Mv Mv; +typedef struct MvField MvField; +typedef struct CTU CTU; +typedef struct SAOParams SAOParams; +typedef struct ALFParams ALFParams; + +typedef struct RefPicList { + struct VVCFrame *ref[VVC_MAX_REF_ENTRIES]; + int list[VVC_MAX_REF_ENTRIES]; + int isLongTerm[VVC_MAX_REF_ENTRIES]; + int nb_refs; +} RefPicList; + +typedef struct RefPicListTab { + RefPicList refPicList[2]; +} RefPicListTab; + +typedef struct VVCFrame { + AVFrame *frame; + ThreadFrame tf; + + MvField *tab_dmvr_mvf; + RefPicListTab **rpl_tab; + + int ctb_count; + + int poc; + + struct VVCFrame *collocated_ref; + + AVBufferRef *tab_dmvr_mvf_buf; + AVBufferRef *rpl_tab_buf; + AVBufferRef *rpl_buf; + AVBufferRef *progress_buf; + + /** + * A sequence counter, so that old frames are output first + * after a POC reset + */ + uint16_t sequence; + /** + * A combination of VVC_FRAME_FLAG_* + */ + uint8_t flags; +} VVCFrame; + +struct SliceContext { + int slice_idx; + VVCSH sh; + EntryPoint *eps; + int nb_eps; + RefPicList *rpl; +}; + +struct VVCFrameContext { + AVCodecContext *avctx; + + // +1 for the current frame + VVCFrame DPB[VVC_MAX_DPB_SIZE + 1]; + + AVFrame *frame; + AVFrame *output_frame; + VVCFrameParamSets ps; + + SliceContext **slices; + int nb_slices; + int nb_slices_allocated; + + VVCFrame *ref; + + VVCDSPContext vvcdsp; + VideoDSPContext vdsp; + + VVCFrameThread *frame_thread; + + uint64_t decode_order; + + AVBufferPool *tab_dmvr_mvf_pool; + AVBufferPool *rpl_tab_pool; + + AVBufferPool *cu_pool; + AVBufferPool *tu_pool; + + struct { + int16_t *slice_idx; + + DBParams *deblock; + SAOParams *sao; + ALFParams *alf; + + int *cb_pos_x[2]; ///< CbPosX[][][] + int *cb_pos_y[2]; ///< CbPosY[][][] + uint8_t *cb_width[2]; ///< CbWidth[][][] + uint8_t *cb_height[2]; ///< CbHeight[][][] + uint8_t *cqt_depth[2]; ///< CqtDepth[][][] + int8_t *qp[VVC_MAX_SAMPLE_ARRAYS]; + + uint8_t *skip; ///< CuSkipFlag[][] + uint8_t *ispmf; ///< intra_sub_partitions_mode_flag + uint8_t *msm[2]; ///< MttSplitMode[][][] in 32 pixels + uint8_t *imf; ///< IntraMipFlag[][] + uint8_t *imtf; ///< intra_mip_transposed_flag[][] + uint8_t *imm; ///< intra_mip_mode[][] + uint8_t *ipm; ///< IntraPredModeY[][] + uint8_t *cpm[2]; ///< CuPredMode[][][] + uint8_t *msf; ///< MergeSubblockFlag[][] + uint8_t *iaf; ///< InterAffineFlag[][] + uint8_t *mmi; ///< MotionModelIdc[][] + Mv *cp_mv[2]; ///< CpMvLX[][][][MAX_CONTROL_POINTS]; + MvField *mvf; ///< MvDmvrL0, MvDmvrL1 + + uint8_t *tu_coded_flag[VVC_MAX_SAMPLE_ARRAYS]; ///< tu_y_coded_flag[][], tu_cb_coded_flag[][], tu_cr_coded_flag[][] + uint8_t *tu_joint_cbcr_residual_flag; ///< tu_joint_cbcr_residual_flag[][] + int *tb_pos_x0[2]; + int *tb_pos_y0[2]; + uint8_t *tb_width[2]; + uint8_t *tb_height[2]; + uint8_t *pcmf[2]; + + uint8_t *horizontal_bs[VVC_MAX_SAMPLE_ARRAYS]; + uint8_t *vertical_bs[VVC_MAX_SAMPLE_ARRAYS]; + uint8_t *horizontal_p; ///< horizontal maxFilterLengthPs for luma + uint8_t *horizontal_q; ///< horizontal maxFilterLengthPs for luma + uint8_t *vertical_p; ///< vertical maxFilterLengthQs for luma + uint8_t *vertical_q; ///< vertical maxFilterLengthQs for luma + + uint8_t *sao_pixel_buffer_h[VVC_MAX_SAMPLE_ARRAYS]; + uint8_t *sao_pixel_buffer_v[VVC_MAX_SAMPLE_ARRAYS]; + uint8_t *alf_pixel_buffer_h[VVC_MAX_SAMPLE_ARRAYS][2]; + uint8_t *alf_pixel_buffer_v[VVC_MAX_SAMPLE_ARRAYS][2]; + + int *coeffs; + CTU *ctus; + + //used in arrays_init only + int ctu_count; + int ctu_size; + int pic_size_in_min_cb; + int pic_size_in_min_pu; + int pic_size_in_min_tu; + int ctu_width; + int ctu_height; + int width; + int height; + int chroma_format_idc; + int pixel_shift; + int bs_width; + int bs_height; + } tab; +} ; + +typedef struct VVCContext { + const AVClass *c; // needed by private avoptions + AVCodecContext *avctx; + + CodedBitstreamContext *cbc; + CodedBitstreamFragment current_frame; + + VVCParamSets ps; + + int temporal_id; ///< temporal_id_plus1 - 1 + int poc_tid0; + + int eos; ///< current packet contains an EOS/EOB NAL + int last_eos; ///< last packet contains an EOS/EOB NAL + + + enum VVCNALUnitType vcl_unit_type; + int no_output_before_recovery_flag; ///< NoOutputBeforeRecoveryFlag + int gdr_recovery_point_poc; ///< recoveryPointPocVal + + /** + * Sequence counters for decoded and output frames, so that old + * frames are output first after a POC reset + */ + uint16_t seq_decode; + uint16_t seq_output; + + int is_nalff; ///< this flag is != 0 if bitstream is encapsulated + ///< as a format defined in 14496-15 + + int apply_defdispwin; + int nal_length_size; ///< Number of bytes used for nal length (1, 2 or 4) + + AVExecutor *executor; + + VVCFrameContext *fcs; + int nb_fcs; + + uint64_t nb_frames; ///< processed frames + int nb_delayed; ///< delayed frames +} VVCContext ; + +#endif /* AVCODEC_VVCDEC_H */ diff --git a/libavcodec/vvc/vvcdsp.c b/libavcodec/vvc/vvcdsp.c new file mode 100644 index 00000000000..c061f65cff6 --- /dev/null +++ b/libavcodec/vvc/vvcdsp.c @@ -0,0 +1,326 @@ +/* + * VVC DSP + * + * Copyright (C) 2021 Nuo Mi + * + * 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 "vvcdsp.h" +#include "vvc_ctu.h" +#include "vvc_itx_1d.h" + +#define VVC_SIGN(v) (v < 0 ? -1 : !!v) + +DECLARE_ALIGNED(16, const int8_t, ff_vvc_chroma_filters)[3][32][4] = { + { + //1x, Table 33 + { 0, 64, 0, 0 }, + { -1, 63, 2, 0 }, + { -2, 62, 4, 0 }, + { -2, 60, 7, -1 }, + { -2, 58, 10, -2 }, + { -3, 57, 12, -2 }, + { -4, 56, 14, -2 }, + { -4, 55, 15, -2 }, + { -4, 54, 16, -2 }, + { -5, 53, 18, -2 }, + { -6, 52, 20, -2 }, + { -6, 49, 24, -3 }, + { -6, 46, 28, -4 }, + { -5, 44, 29, -4 }, + { -4, 42, 30, -4 }, + { -4, 39, 33, -4 }, + { -4, 36, 36, -4 }, + { -4, 33, 39, -4 }, + { -4, 30, 42, -4 }, + { -4, 29, 44, -5 }, + { -4, 28, 46, -6 }, + { -3, 24, 49, -6 }, + { -2, 20, 52, -6 }, + { -2, 18, 53, -5 }, + { -2, 16, 54, -4 }, + { -2, 15, 55, -4 }, + { -2, 14, 56, -4 }, + { -2, 12, 57, -3 }, + { -2, 10, 58, -2 }, + { -1, 7, 60, -2 }, + { 0, 4, 62, -2 }, + { 0, 2, 63, -1 }, + }, + { + //1.5x, Table 34 + { 12, 40, 12, 0 }, + { 11, 40, 13, 0 }, + { 10, 40, 15, -1 }, + { 9, 40, 16, -1 }, + { 8, 40, 17, -1 }, + { 8, 39, 18, -1 }, + { 7, 39, 19, -1 }, + { 6, 38, 21, -1 }, + { 5, 38, 22, -1 }, + { 4, 38, 23, -1 }, + { 4, 37, 24, -1 }, + { 3, 36, 25, 0 }, + { 3, 35, 26, 0 }, + { 2, 34, 28, 0 }, + { 2, 33, 29, 0 }, + { 1, 33, 30, 0 }, + { 1, 31, 31, 1 }, + { 0, 30, 33, 1 }, + { 0, 29, 33, 2 }, + { 0, 28, 34, 2 }, + { 0, 26, 35, 3 }, + { 0, 25, 36, 3 }, + { -1, 24, 37, 4 }, + { -1, 23, 38, 4 }, + { -1, 22, 38, 5 }, + { -1, 21, 38, 6 }, + { -1, 19, 39, 7 }, + { -1, 18, 39, 8 }, + { -1, 17, 40, 8 }, + { -1, 16, 40, 9 }, + { -1, 15, 40, 10 }, + { 0, 13, 40, 11 }, + }, + { + //2x, Table 35 + { 17, 30, 17, 0 }, + { 17, 30, 18, -1 }, + { 16, 30, 18, 0 }, + { 16, 30, 18, 0 }, + { 15, 30, 18, 1 }, + { 14, 30, 18, 2 }, + { 13, 29, 19, 3 }, + { 13, 29, 19, 3 }, + { 12, 29, 20, 3 }, + { 11, 28, 21, 4 }, + { 10, 28, 22, 4 }, + { 10, 27, 22, 5 }, + { 9, 27, 23, 5 }, + { 9, 26, 24, 5 }, + { 8, 26, 24, 6 }, + { 7, 26, 25, 6 }, + { 7, 25, 25, 7 }, + { 6, 25, 26, 7 }, + { 6, 24, 26, 8 }, + { 5, 24, 26, 9 }, + { 5, 23, 27, 9 }, + { 5, 22, 27, 10 }, + { 4, 22, 28, 10 }, + { 4, 21, 28, 11 }, + { 3, 20, 29, 12 }, + { 3, 19, 29, 13 }, + { 3, 19, 29, 13 }, + { 2, 18, 30, 14 }, + { 1, 18, 30, 15 }, + { 0, 18, 30, 16 }, + { 0, 18, 30, 16 }, + { -1, 18, 30, 17 }, + }, +}; + +DECLARE_ALIGNED(16, const int8_t, ff_vvc_luma_filters)[3][16][8] = { + { + //1x, hpelIfIdx == 0, Table 27 + { 0, 0, 0, 64, 0, 0, 0, 0 }, + { 0, 1, -3, 63, 4, -2, 1, 0 }, + { -1, 2, -5, 62, 8, -3, 1, 0 }, + { -1, 3, -8, 60, 13, -4, 1, 0 }, + { -1, 4, -10, 58, 17, -5, 1, 0 }, + { -1, 4, -11, 52, 26, -8, 3, -1 }, + { -1, 3, -9, 47, 31, -10, 4, -1 }, + { -1, 4, -11, 45, 34, -10, 4, -1 }, + { -1, 4, -11, 40, 40, -11, 4, -1 }, + { -1, 4, -10, 34, 45, -11, 4, -1 }, + { -1, 4, -10, 31, 47, -9, 3, -1 }, + { -1, 3, -8, 26, 52, -11, 4, -1 }, + { 0, 1, -5, 17, 58, -10, 4, -1 }, + { 0, 1, -4, 13, 60, -8, 3, -1 }, + { 0, 1, -3, 8, 62, -5, 2, -1 }, + { 0, 1, -2, 4, 63, -3, 1, 0 }, + }, + + { + //1x, hpelIfIdx == 1, Table 27 + { 0, 0, 0, 64, 0, 0, 0, 0 }, + { 0, 1, -3, 63, 4, -2, 1, 0 }, + { -1, 2, -5, 62, 8, -3, 1, 0 }, + { -1, 3, -8, 60, 13, -4, 1, 0 }, + { -1, 4, -10, 58, 17, -5, 1, 0 }, + { -1, 4, -11, 52, 26, -8, 3, -1 }, + { -1, 3, -9, 47, 31, -10, 4, -1 }, + { -1, 4, -11, 45, 34, -10, 4, -1 }, + { 0, 3, 9, 20, 20, 9, 3, 0 }, + { -1, 4, -10, 34, 45, -11, 4, -1 }, + { -1, 4, -10, 31, 47, -9, 3, -1 }, + { -1, 3, -8, 26, 52, -11, 4, -1 }, + { 0, 1, -5, 17, 58, -10, 4, -1 }, + { 0, 1, -4, 13, 60, -8, 3, -1 }, + { 0, 1, -3, 8, 62, -5, 2, -1 }, + { 0, 1, -2, 4, 63, -3, 1, 0 }, + }, + + { + //1x, affine, Table 30 + { 0, 0, 0, 64, 0, 0, 0, 0 }, + { 0, 1, -3, 63, 4, -2, 1, 0 }, + { 0, 1, -5, 62, 8, -3, 1, 0 }, + { 0, 2, -8, 60, 13, -4, 1, 0 }, + { 0, 3, -10, 58, 17, -5, 1, 0 }, + { 0, 3, -11, 52, 26, -8, 2, 0 }, + { 0, 2, -9, 47, 31, -10, 3, 0 }, + { 0, 3, -11, 45, 34, -10, 3, 0 }, + { 0, 3, -11, 40, 40, -11, 3, 0 }, + { 0, 3, -10, 34, 45, -11, 3, 0 }, + { 0, 3, -10, 31, 47, -9, 2, 0 }, + { 0, 2, -8, 26, 52, -11, 3, 0 }, + { 0, 1, -5, 17, 58, -10, 3, 0 }, + { 0, 1, -4, 13, 60, -8, 2, 0 }, + { 0, 1, -3, 8, 62, -5, 1, 0 }, + { 0, 1, -2, 4, 63, -3, 1, 0 }, + }, + +}; + +DECLARE_ALIGNED(16, const int8_t, ff_vvc_dmvr_filters)[16][2] = { + { 16, 0}, + { 15, 1}, + { 14, 2}, + { 13, 3}, + { 12, 4}, + { 11, 5}, + { 10, 6}, + { 9, 7}, + { 8, 8}, + { 7, 9}, + { 6, 10}, + { 5, 11}, + { 4, 12}, + { 3, 13}, + { 2, 14}, + { 1, 15}, +}; + +static void av_always_inline pad_int16(int16_t *_dst, const ptrdiff_t dst_stride, const int width, const int height) +{ + const int padded_width = width + 2; + int16_t *dst; + for (int y = 0; y < height; y++) { + dst = _dst + y * dst_stride; + for (int x = 0; x < width; x++) { + dst[-1] = dst[0]; + dst[width] = dst[width - 1]; + } + } + + _dst--; + //top + memcpy(_dst - dst_stride, _dst, padded_width * sizeof(int16_t)); + //bottom + _dst += dst_stride * height; + memcpy(_dst, _dst - dst_stride, padded_width * sizeof(int16_t)); +} + +static int vvc_sad(const int16_t *src0, const int16_t *src1, int dx, int dy, + const int block_w, const int block_h) +{ + int sad = 0; + dx -= 2; + dy -= 2; + src0 += (2 + dy) * MAX_PB_SIZE + 2 + dx; + src1 += (2 - dy) * MAX_PB_SIZE + 2 - dx; + for (int y = 0; y < block_h; y += 2) { + for (int x = 0; x < block_w; x++) { + sad += FFABS(src0[x] - src1[x]); + } + src0 += 2 * MAX_PB_SIZE; + src1 += 2 * MAX_PB_SIZE; + } + return sad; +} + +#define itx_fn(type, s) \ +static void itx_##type##_##s(int *out, ptrdiff_t out_step, const int *in, ptrdiff_t in_step) \ +{ \ + ff_vvc_inv_##type##_##s(out, out_step, in, in_step); \ +} + +#define itx_fn_common(type) \ + itx_fn(type, 4); \ + itx_fn(type, 8); \ + itx_fn(type, 16); \ + itx_fn(type, 32); \ + +itx_fn_common(dct2); +itx_fn_common(dst7); +itx_fn_common(dct8); +itx_fn(dct2, 2); +itx_fn(dct2, 64); + +typedef struct IntraEdgeParams { + uint8_t* top; + uint8_t* left; + int filter_flag; + + uint16_t left_array[3 * MAX_TB_SIZE + 3]; + uint16_t filtered_left_array[3 * MAX_TB_SIZE + 3]; + uint16_t top_array[3 * MAX_TB_SIZE + 3]; + uint16_t filtered_top_array[3 * MAX_TB_SIZE + 3]; +} IntraEdgeParams; + +#define BIT_DEPTH 8 +#include "vvcdsp_template.c" +#undef BIT_DEPTH + +#define BIT_DEPTH 10 +#include "vvcdsp_template.c" +#undef BIT_DEPTH + +#define BIT_DEPTH 12 +#include "vvcdsp_template.c" +#undef BIT_DEPTH + +void ff_vvc_dsp_init(VVCDSPContext *vvcdsp, int bit_depth) +{ +#undef FUNC +#define FUNC(a, depth) a ## _ ## depth + +#define VVC_DSP(depth) \ + FUNC(ff_vvc_inter_dsp_init, depth)(&vvcdsp->inter); \ + FUNC(ff_vvc_intra_dsp_init, depth)(&vvcdsp->intra); \ + FUNC(ff_vvc_itx_dsp_init, depth)(&vvcdsp->itx); \ + FUNC(ff_vvc_lmcs_dsp_init, depth)(&vvcdsp->lmcs); \ + FUNC(ff_vvc_lf_dsp_init, depth)(&vvcdsp->lf); \ + FUNC(ff_vvc_sao_dsp_init, depth)(&vvcdsp->sao); \ + FUNC(ff_vvc_alf_dsp_init, depth)(&vvcdsp->alf); \ + + switch (bit_depth) { + case 12: + VVC_DSP(12); + break; + case 10: + VVC_DSP(10); + break; + default: + VVC_DSP(8); + break; + } +#if ARCH_X86 + ff_vvc_dsp_init_x86(vvcdsp, bit_depth); +#endif +} diff --git a/libavcodec/vvc/vvcdsp.h b/libavcodec/vvc/vvcdsp.h new file mode 100644 index 00000000000..6034b6b7a3e --- /dev/null +++ b/libavcodec/vvc/vvcdsp.h @@ -0,0 +1,183 @@ +/* + * VVC DSP + * + * Copyright (C) 2021 Nuo Mi + * + * + * 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_VVCDSP_H +#define AVCODEC_VVCDSP_H + +#include +#include + +enum TxType { + DCT2, + DST7, + DCT8, + N_TX_TYPE, +}; + +enum TxSize { + TX_SIZE_2, + TX_SIZE_4, + TX_SIZE_8, + TX_SIZE_16, + TX_SIZE_32, + TX_SIZE_64, + N_TX_SIZE, +}; + +typedef struct VVCInterDSPContext { + void (*put[2 /* luma, chroma */][2 /* int, frac */][2 /* int, frac */])( + int16_t *dst, const uint8_t *src, ptrdiff_t src_stride, + int height, intptr_t mx, intptr_t my, int width, int hf_idx, int vf_idx); + + void (*put_uni[2 /* luma, chroma */][2 /* int, frac */][2 /* int, frac */])( + uint8_t *dst, ptrdiff_t dst_stride, + const uint8_t *src, ptrdiff_t src_stride, int height, + intptr_t mx, intptr_t my, int width, int hf_idx, int vf_idx); + void (*put_uni_w[2 /* luma, chroma */][2 /* int, frac */][2 /* int, frac */])( + uint8_t *dst, ptrdiff_t dst_stride, + const uint8_t *src, ptrdiff_t src_stride, int height, int denom, int wx, int ox, + intptr_t mx, intptr_t my, int width, int hf_idx, int vf_idx); + + void (*put_bi[2 /* luma, chroma */][2 /* int, frac */][2 /* int, frac */])( + uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *src, ptrdiff_t src_stride, const int16_t *src2, + int height, intptr_t mx, intptr_t my, int width, int hf_idx, int vf_idx); + void (*put_bi_w[2 /* luma, chroma */][2 /* int, frac */][2 /* int, frac */])( + uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *src, ptrdiff_t src_stride, const int16_t *src2, + int height, int denom, int wx0, int wx1, int ox0, int ox1, + intptr_t mx, intptr_t my, int width, int hf_idx, int vf_idx); + + void (*put_ciip)(uint8_t *dst, ptrdiff_t dst_stride, int width, int height, + const uint8_t *inter, ptrdiff_t inter_stride, int inter_weight); + + void (*put_gpm)(uint8_t *dst, ptrdiff_t dst_stride, int width, int height, + const int16_t *tmp, const int16_t *tmp1, const ptrdiff_t tmp_stride, + const uint8_t *weights, int step_x, int step_y); + + void (*fetch_samples)(int16_t *dst, const uint8_t *src, ptrdiff_t src_stride, int x_frac, int y_frac); + void (*bdof_fetch_samples)(int16_t *dst, const uint8_t *src, ptrdiff_t src_stride, int x_frac, int y_frac, + int width, int height); + + void (*prof_grad_filter)(int16_t *gradient_h, int16_t *gradient_v, const ptrdiff_t gradient_stride, + const int16_t *src, const ptrdiff_t src_stride, int width, int height, const int pad); + void (*apply_prof)(int16_t *dst, const int16_t *src, const int16_t *diff_mv_x, const int16_t *diff_mv_y); + + void (*apply_prof_uni)(uint8_t *dst, ptrdiff_t dst_stride, const int16_t *src, + const int16_t *diff_mv_x, const int16_t *diff_mv_y); + void (*apply_prof_uni_w)(uint8_t *dst, const ptrdiff_t dst_stride, const int16_t *src, + const int16_t *diff_mv_x, const int16_t *diff_mv_y, int denom, int wx, int ox); + + void (*apply_prof_bi)(uint8_t *dst, ptrdiff_t dst_stride, const int16_t *src0, const int16_t *src1, + const int16_t *diff_mv_x, const int16_t *diff_mv_y); + void (*apply_prof_bi_w)(uint8_t *dst, ptrdiff_t dst_stride, const int16_t *src0, const int16_t *src1, + const int16_t *diff_mv_x, const int16_t *diff_mv_y, int denom, int w0, int w1, int o0, int o1); + + void (*apply_bdof)(uint8_t *dst, ptrdiff_t dst_stride, int16_t *src0, int16_t *src1, int block_w, int block_h); + + int (*sad)(const int16_t *src0, const int16_t *src1, int dx, int dy, int block_w, int block_h); + void (*dmvr[2][2])(int16_t *dst, const uint8_t *src, ptrdiff_t src_stride, int height, + intptr_t mx, intptr_t my, int width); +} VVCInterDSPContext; + +struct VVCLocalContext; + +typedef struct VVCIntraDSPContext { + void (*intra_cclm_pred)(const struct VVCLocalContext *lc, int x0, int y0, int w, int h); + void (*lmcs_scale_chroma)(struct VVCLocalContext *lc, int *dst, const int *coeff, int w, int h, int x0_cu, int y0_cu); + void (*intra_pred)(const struct VVCLocalContext *lc, int x0, int y0, int w, int h, int c_idx); + void (*pred_planar)(uint8_t *src, const uint8_t *top, const uint8_t *left, int w, int h, ptrdiff_t stride); + void (*pred_mip)(uint8_t *src, const uint8_t *top, const uint8_t *left, int w, int h, ptrdiff_t stride, + int mode_id, int is_transpose); + void (*pred_dc)(uint8_t *src, const uint8_t *top, const uint8_t *left, int w, int h, ptrdiff_t stride); + void (*pred_v)(uint8_t *src, const uint8_t *_top, int w, int h, ptrdiff_t stride); + void (*pred_h)(uint8_t *src, const uint8_t *_left, int w, int h, ptrdiff_t stride); + void (*pred_angular_v)(uint8_t *src, const uint8_t *_top, const uint8_t *_left, + int w, int h, ptrdiff_t stride, int c_idx, int mode, int ref_idx, int filter_flag, int need_pdpc); + void (*pred_angular_h)(uint8_t *src, const uint8_t *_top, const uint8_t *_left, int w, int h, ptrdiff_t stride, + int c_idx, int mode, int ref_idx, int filter_flag, int need_pdpc); +} VVCIntraDSPContext; + +typedef struct VVCItxDSPContext { + void (*add_residual)(uint8_t *dst, const int *res, int width, int height, ptrdiff_t stride); + void (*add_residual_joint)(uint8_t *dst, const int *res, int width, int height, ptrdiff_t stride, int c_sign, int shift); + void (*pred_residual_joint)(int *buf, int width, int height, int c_sign, int shift); + + void (*itx[N_TX_TYPE][N_TX_SIZE])(int *out, ptrdiff_t out_step, const int *in, ptrdiff_t in_step); + void (*transform_bdpcm)(int *coeffs, int width, int height, int vertical, int log2_transform_range); +} VVCItxDSPContext; + +typedef struct VVCLMCSDSPContext { + void (*filter)(uint8_t *dst, ptrdiff_t dst_stride, int width, int height, const uint8_t *lut); +} VVCLMCSDSPContext; + +typedef struct VVCLFDSPContext { + int (*ladf_level[2 /* h, v */])(const uint8_t *pix, ptrdiff_t stride); + + void (*filter_luma[2 /* h, v */])(uint8_t *pix, ptrdiff_t stride, int beta, int32_t tc, + uint8_t no_p, uint8_t no_q, uint8_t max_len_p, uint8_t max_len_q, int hor_ctu_edge); + void (*filter_chroma[2 /* h, v */])(uint8_t *pix, ptrdiff_t stride, int beta, int32_t tc, + uint8_t no_p, uint8_t no_q, int shift, int max_len_p, int max_len_q); +} VVCLFDSPContext; + +struct SAOParams; +typedef struct VVCSAODSPContext { + void (*band_filter[9])(uint8_t *dst, const uint8_t *src, ptrdiff_t dst_stride, ptrdiff_t src_stride, + const int16_t *sao_offset_val, int sao_left_class, int width, int height); + /* implicit src_stride parameter has value of 2 * MAX_PB_SIZE + AV_INPUT_BUFFER_PADDING_SIZE */ + void (*edge_filter[9])(uint8_t *dst /* align 16 */, const uint8_t *src /* align 32 */, ptrdiff_t dst_stride, + const int16_t *sao_offset_val, int sao_eo_class, int width, int height); + void (*edge_restore[2])(uint8_t *dst, const uint8_t *src, ptrdiff_t dst_stride, ptrdiff_t src_stride, + const struct SAOParams *sao, const int *borders, int width, int height, int c_idx, + const uint8_t *vert_edge, const uint8_t *horiz_edge, const uint8_t *diag_edge); +} VVCSAODSPContext; + +typedef struct VVCALFDSPContext { + void (*filter[2 /* luma, chroma */])(uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *src, ptrdiff_t src_stride, + int width, int height, const int16_t *filter, const int16_t *clip, int vb_pos); + void (*filter_cc)(uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *luma, ptrdiff_t luma_stride, + int width, int height, int hs, int vs, const int16_t *filter, int vb_pos); + + void (*classify)(int *class_idx, int *transpose_idx, const uint8_t *src, ptrdiff_t src_stride, int width, int height, + int vb_pos, int *gradient_tmp); + void (*recon_coeff_and_clip)(int16_t *coeff, int16_t *clip, const int *class_idx, const int *transpose_idx, int size, + const int16_t *coeff_set, const uint8_t *clip_idx_set, const uint8_t *class_to_filt); +} VVCALFDSPContext; + +typedef struct VVCDSPContext { + VVCInterDSPContext inter; + VVCIntraDSPContext intra; + VVCItxDSPContext itx; + VVCLMCSDSPContext lmcs; + VVCLFDSPContext lf; + VVCSAODSPContext sao; + VVCALFDSPContext alf; +} VVCDSPContext; + +void ff_vvc_dsp_init(VVCDSPContext *hpc, int bit_depth); + +extern const int8_t ff_vvc_chroma_filters[3][32][4]; +extern const int8_t ff_vvc_luma_filters[3][16][8]; +extern const int8_t ff_vvc_dmvr_filters[16][2]; + +void ff_vvc_dsp_init_x86(VVCDSPContext *c, const int bit_depth); + +#endif /* AVCODEC_VVCDSP_H */ diff --git a/libavcodec/vvc/vvcdsp_template.c b/libavcodec/vvc/vvcdsp_template.c new file mode 100644 index 00000000000..d3998e633f6 --- /dev/null +++ b/libavcodec/vvc/vvcdsp_template.c @@ -0,0 +1,119 @@ +/* + * VVC transform and residual DSP + * + * Copyright (C) 2021 Nuo Mi + * + * 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 "libavcodec/bit_depth_template.c" + +#include "vvcdec.h" + +#include "vvc_inter_template.c" +#include "vvc_intra_template.c" +#include "vvc_filter_template.c" + +static void FUNC(add_residual)(uint8_t *_dst, const int *res, + const int w, const int h, const ptrdiff_t _stride) +{ + pixel *dst = (pixel *)_dst; + + const int stride = _stride / sizeof(pixel); + + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + dst[x] = av_clip_pixel(dst[x] + *res); + res++; + } + dst += stride; + } +} + +static void FUNC(add_residual_joint)(uint8_t *_dst, const int *res, + const int w, const int h, const ptrdiff_t _stride, const int c_sign, const int shift) +{ + pixel *dst = (pixel *)_dst; + + const int stride = _stride / sizeof(pixel); + + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + const int r = ((*res) * c_sign) >> shift; + dst[x] = av_clip_pixel(dst[x] + r); + res++; + } + dst += stride; + } +} + +static void FUNC(pred_residual_joint)(int *buf, const int w, const int h, + const int c_sign, const int shift) +{ + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + *buf = ((*buf) * c_sign) >> shift; + buf++; + } + } +} + +static void FUNC(transform_bdpcm)(int *coeffs, const int width, const int height, + const int vertical, const int log2_transform_range) +{ + int x, y; + + if (vertical) { + coeffs += width; + for (y = 0; y < height - 1; y++) { + for (x = 0; x < width; x++) + coeffs[x] = av_clip_intp2(coeffs[x] + coeffs[x - width], log2_transform_range); + coeffs += width; + } + } else { + for (y = 0; y < height; y++) { + for (x = 1; x < width; x++) + coeffs[x] = av_clip_intp2(coeffs[x] + coeffs[x - 1], log2_transform_range); + coeffs += width; + } + } +} + +static void FUNC(ff_vvc_itx_dsp_init)(VVCItxDSPContext *const itx) +{ +#define VVC_ITX(TYPE, type, s) \ + itx->itx[TYPE][TX_SIZE_##s] = itx_##type##_##s; \ + +#define VVC_ITX_COMMON(TYPE, type) \ + VVC_ITX(TYPE, type, 4); \ + VVC_ITX(TYPE, type, 8); \ + VVC_ITX(TYPE, type, 16); \ + VVC_ITX(TYPE, type, 32); + + itx->add_residual = FUNC(add_residual); + itx->add_residual_joint = FUNC(add_residual_joint); + itx->pred_residual_joint = FUNC(pred_residual_joint); + itx->transform_bdpcm = FUNC(transform_bdpcm); + VVC_ITX(DCT2, dct2, 2) + VVC_ITX(DCT2, dct2, 64) + VVC_ITX_COMMON(DCT2, dct2) + VVC_ITX_COMMON(DCT8, dct8) + VVC_ITX_COMMON(DST7, dst7) + +#undef VVC_ITX +#undef VVC_ITX_COMMON +} diff --git a/libavcodec/vvc_mp4toannexb_bsf.c b/libavcodec/vvc_mp4toannexb_bsf.c new file mode 100644 index 00000000000..25c37269181 --- /dev/null +++ b/libavcodec/vvc_mp4toannexb_bsf.c @@ -0,0 +1,327 @@ +/* + * H.266/VVC MP4 to Annex B byte stream format filter + * Copyright (c) 2022, Thomas Siedel + * + * 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 + +#include "libavutil/intreadwrite.h" +#include "libavutil/mem.h" + +#include "bsf.h" +#include "bsf_internal.h" +#include "bytestream.h" +#include "defs.h" +#include "vvc.h" + +#define MIN_VVCC_LENGTH 23 + +typedef struct VVCBSFContext { + uint8_t length_size; + int extradata_parsed; +} VVCBSFContext; + +static int vvc_extradata_to_annexb(AVBSFContext *ctx) +{ + GetByteContext gb; + int length_size, num_arrays, i, j; + int ret = 0; + int temp = 0; + int ptl_present; + + uint8_t *new_extradata = NULL; + size_t new_extradata_size = 0; + + int max_picture_width = 0; + int max_picture_height = 0; + int avg_frame_rate = 0; + + bytestream2_init(&gb, ctx->par_in->extradata, ctx->par_in->extradata_size); + temp = bytestream2_get_byte(&gb); + length_size = ((temp & 6) >> 1) + 1; + ptl_present = temp & 1; + if (ptl_present) { + int num_bytes_constraint_info; + int general_profile_idc; + int general_tier_flag; + int general_level_idc; + int ptl_frame_only_constraint_flag; + int ptl_multi_layer_enabled_flag; + int ptl_num_sub_profiles; + int temp3, temp4, temp5; + int temp2 = bytestream2_get_be16(&gb); + int ols_idx = (temp2 >> 7) & 0x1ff; + int num_sublayers = (temp2 >> 4) & 0x7; + int constant_frame_rate = (temp2 >> 2) & 0x3; + int chroma_format_idc = temp2 & 0x3; + int bit_depth_minus8 = (bytestream2_get_byte(&gb) >> 5) & 0x7; + av_log(ctx, AV_LOG_DEBUG, + "bit_depth_minus8 %d chroma_format_idc %d\n", bit_depth_minus8, + chroma_format_idc); + av_log(ctx, AV_LOG_DEBUG, "constant_frame_rate %d, ols_idx %d\n", + constant_frame_rate, ols_idx); + // VvcPTLRecord(num_sublayers) native_ptl + temp3 = bytestream2_get_byte(&gb); + num_bytes_constraint_info = (temp3) & 0x3f; + temp4 = bytestream2_get_byte(&gb); + general_profile_idc = (temp4 >> 1) & 0x7f; + general_tier_flag = (temp4) & 1; + general_level_idc = bytestream2_get_byte(&gb); + av_log(ctx, AV_LOG_DEBUG, + "general_profile_idc %d, general_tier_flag %d, general_level_idc %d, num_sublayers %d num_bytes_constraint_info %d\n", + general_profile_idc, general_tier_flag, general_level_idc, + num_sublayers, num_bytes_constraint_info); + + temp5 = bytestream2_get_byte(&gb); + ptl_frame_only_constraint_flag = (temp5 >> 7) & 0x1; + ptl_multi_layer_enabled_flag = (temp5 >> 6) & 0x1; + for (i = 0; i < num_bytes_constraint_info - 1; i++) { + // unsigned int(8*num_bytes_constraint_info - 2) general_constraint_info; + bytestream2_get_byte(&gb); + } + + av_log(ctx, AV_LOG_DEBUG, + "ptl_multi_layer_enabled_flag %d, ptl_frame_only_constraint_flag %d\n", + ptl_multi_layer_enabled_flag, ptl_frame_only_constraint_flag); + + if (num_sublayers > 1) { + int temp6 = bytestream2_get_byte(&gb); + uint8_t ptl_sublayer_level_present_flag[8] = { 0 }; + //uint8_t sublayer_level_idc[8] = {0}; + for (i = num_sublayers - 2; i >= 0; i--) { + ptl_sublayer_level_present_flag[i] = + (temp6 >> (7 - (num_sublayers - 2 - i))) & 0x01; + } + // for (j=num_sublayers; j<=8 && num_sublayers > 1; j++) + // bit(1) ptl_reserved_zero_bit = 0; + for (i = num_sublayers - 2; i >= 0; i--) { + if (ptl_sublayer_level_present_flag[i]) { + //sublayer_level_idc[i] = bytestream2_get_byte(&gb); + } + } + } + + ptl_num_sub_profiles = bytestream2_get_byte(&gb); + for (j = 0; j < ptl_num_sub_profiles; j++) { + // unsigned int(32) general_sub_profile_idc[j]; + bytestream2_get_be16(&gb); + bytestream2_get_be16(&gb); + } + + max_picture_width = bytestream2_get_be16(&gb); // unsigned_int(16) max_picture_width; + max_picture_height = bytestream2_get_be16(&gb); // unsigned_int(16) max_picture_height; + avg_frame_rate = bytestream2_get_be16(&gb); // unsigned int(16) avg_frame_rate; } + av_log(ctx, AV_LOG_DEBUG, + "max_picture_width %d, max_picture_height %d, avg_frame_rate %d\n", + max_picture_width, max_picture_height, avg_frame_rate); + } + + num_arrays = bytestream2_get_byte(&gb); + + for (i = 0; i < num_arrays; i++) { + int cnt; + int type = bytestream2_get_byte(&gb) & 0x1f; + + if (type == VVC_OPI_NUT || type == VVC_DCI_NUT) + cnt = 1; + else + cnt = bytestream2_get_be16(&gb); + + av_log(ctx, AV_LOG_DEBUG, "nalu_type %d cnt %d\n", type, cnt); + + if (!(type == VVC_OPI_NUT || type == VVC_DCI_NUT || + type == VVC_VPS_NUT || type == VVC_SPS_NUT || type == VVC_PPS_NUT + || type == VVC_PREFIX_SEI_NUT || type == VVC_SUFFIX_SEI_NUT)) { + av_log(ctx, AV_LOG_ERROR, + "Invalid NAL unit type in extradata: %d\n", type); + ret = AVERROR_INVALIDDATA; + goto fail; + } + + for (j = 0; j < cnt; j++) { + int nalu_len = bytestream2_get_be16(&gb); + + if (4 + AV_INPUT_BUFFER_PADDING_SIZE + nalu_len > + SIZE_MAX - new_extradata_size) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + ret = av_reallocp(&new_extradata, new_extradata_size + nalu_len + 4 + + AV_INPUT_BUFFER_PADDING_SIZE); + if (ret < 0) + goto fail; + + AV_WB32(new_extradata + new_extradata_size, 1); // add the startcode + bytestream2_get_buffer(&gb, new_extradata + new_extradata_size + 4, + nalu_len); + new_extradata_size += 4 + nalu_len; + memset(new_extradata + new_extradata_size, 0, + AV_INPUT_BUFFER_PADDING_SIZE); + } + } + + av_freep(&ctx->par_out->extradata); + ctx->par_out->extradata = new_extradata; + ctx->par_out->extradata_size = new_extradata_size; + + if (!new_extradata_size) + av_log(ctx, AV_LOG_WARNING, "No parameter sets in the extradata\n"); + + return length_size; + fail: + av_freep(&new_extradata); + return ret; +} + +static int vvc_mp4toannexb_init(AVBSFContext *ctx) +{ + VVCBSFContext *s = ctx->priv_data; + int ret; + + if (ctx->par_in->extradata_size < MIN_VVCC_LENGTH || + AV_RB24(ctx->par_in->extradata) == 1 || + AV_RB32(ctx->par_in->extradata) == 1) { + av_log(ctx, AV_LOG_VERBOSE, + "The input looks like it is Annex B already\n"); + } else { + ret = vvc_extradata_to_annexb(ctx); + if (ret < 0) + return ret; + s->length_size = ret; + s->extradata_parsed = 1; + } + + return 0; +} + +static int vvc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out) +{ + VVCBSFContext *s = ctx->priv_data; + AVPacket *in; + GetByteContext gb; + + int is_irap = 0; + int added_extra = 0; + int i, ret = 0; + + ret = ff_bsf_get_packet(ctx, &in); + if (ret < 0) + return ret; + + if (!s->extradata_parsed) { + av_packet_move_ref(out, in); + av_packet_free(&in); + return 0; + } + + bytestream2_init(&gb, in->data, in->size); + + /* check if this packet contains an IRAP. The extradata will need to be added before any potential PH_NUT */ + while (bytestream2_get_bytes_left(&gb)) { + uint32_t nalu_size = 0; + int nalu_type; + + if (bytestream2_get_bytes_left(&gb) < s->length_size) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + for (i = 0; i < s->length_size; i++) + nalu_size = (nalu_size << 8) | bytestream2_get_byte(&gb); + + if (nalu_size < 2 || nalu_size > bytestream2_get_bytes_left(&gb)) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + nalu_type = (bytestream2_peek_be16(&gb) >> 3) & 0x1f; + is_irap = nalu_type >= VVC_IDR_W_RADL && nalu_type <= VVC_RSV_IRAP_11; + if (is_irap) { + break; + } + bytestream2_seek(&gb, nalu_size, SEEK_CUR); + } + + bytestream2_seek(&gb, 0, SEEK_SET); + while (bytestream2_get_bytes_left(&gb)) { + uint32_t nalu_size = 0; + int nalu_type; + int add_extradata, extra_size, prev_size; + + if (bytestream2_get_bytes_left(&gb) < s->length_size) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + for (i = 0; i < s->length_size; i++) + nalu_size = (nalu_size << 8) | bytestream2_get_byte(&gb); + + if (nalu_size < 2 || nalu_size > bytestream2_get_bytes_left(&gb)) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + nalu_type = (bytestream2_peek_be16(&gb) >> 3) & 0x1f; + + /* prepend extradata to IRAP frames */ + add_extradata = is_irap && nalu_type != VVC_AUD_NUT && !added_extra; + extra_size = add_extradata * ctx->par_out->extradata_size; + added_extra |= add_extradata; + + if (FFMIN(INT_MAX, SIZE_MAX) < 4ULL + nalu_size + extra_size) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + prev_size = out->size; + + ret = av_grow_packet(out, 4 + nalu_size + extra_size); + if (ret < 0) + goto fail; + + if (extra_size) + memcpy(out->data + prev_size, ctx->par_out->extradata, extra_size); + AV_WB32(out->data + prev_size + extra_size, 1); + bytestream2_get_buffer(&gb, out->data + prev_size + 4 + extra_size, + nalu_size); + } + + ret = av_packet_copy_props(out, in); + if (ret < 0) + goto fail; + + fail: + if (ret < 0) + av_packet_unref(out); + av_packet_free(&in); + + return ret; +} + +static const enum AVCodecID codec_ids[] = { + AV_CODEC_ID_VVC, AV_CODEC_ID_NONE, +}; + +const FFBitStreamFilter ff_vvc_mp4toannexb_bsf = { + .p.name = "vvc_mp4toannexb", + .p.codec_ids = codec_ids, + .priv_data_size = sizeof(VVCBSFContext), + .init = vvc_mp4toannexb_init, + .filter = vvc_mp4toannexb_filter, +}; diff --git a/libavcodec/vvc_parser.c b/libavcodec/vvc_parser.c new file mode 100644 index 00000000000..3951ebe50af --- /dev/null +++ b/libavcodec/vvc_parser.c @@ -0,0 +1,517 @@ +/* + * H.266 / VVC parser + * + * Copyright (C) 2021 Nuo Mi + * + * 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 "cbs.h" +#include "cbs_h266.h" +#include "parser.h" + +#define START_CODE 0x000001 ///< start_code_prefix_one_3bytes +#define IS_IDR(nut) (nut == VVC_IDR_W_RADL || nut == VVC_IDR_N_LP) +#define IS_H266_SLICE(nut) (nut <= VVC_RASL_NUT || (nut >= VVC_IDR_W_RADL && nut <= VVC_GDR_NUT)) + +typedef struct PuInfo { + const H266RawPPS *pps; + const H266RawSPS *sps; + const H266RawPictureHeader *ph; + const H266RawSlice *slice; + int pic_type; +} PuInfo; + +typedef struct AuDetector { + uint8_t prev_layer_id; + int prev_tid0_poc; + int prev_poc; +} AuDetector; + +typedef struct VVCParserContext { + ParseContext pc; + CodedBitstreamContext *cbc; + + CodedBitstreamFragment picture_unit; + + AVPacket au; + AVPacket last_au; + + AuDetector au_detector; + + int parsed_extradata; +} VVCParserContext; + +static const enum AVPixelFormat pix_fmts_8bit[] = { + AV_PIX_FMT_GRAY8, AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P +}; + +static const enum AVPixelFormat pix_fmts_10bit[] = { + AV_PIX_FMT_GRAY10, AV_PIX_FMT_YUV420P10, + AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10 +}; + +static int get_format(const H266RawSPS *sps) +{ + switch (sps->sps_bitdepth_minus8) { + case 0: + return pix_fmts_8bit[sps->sps_chroma_format_idc]; + case 2: + return pix_fmts_10bit[sps->sps_chroma_format_idc]; + } + return AV_PIX_FMT_NONE; +} + +/** + * Find the end of the current frame in the bitstream. + * @return the position of the first byte of the next frame, or END_NOT_FOUND + */ +static int find_frame_end(AVCodecParserContext *s, const uint8_t *buf, + int buf_size) +{ + VVCParserContext *ctx = s->priv_data; + ParseContext *pc = &ctx->pc; + int i; + + for (i = 0; i < buf_size; i++) { + int nut, code_len; + + pc->state64 = (pc->state64 << 8) | buf[i]; + + if (((pc->state64 >> 3 * 8) & 0xFFFFFF) != START_CODE) + continue; + + code_len = ((pc->state64 >> 3 * 8) & 0xFFFFFFFF) == 0x01 ? 4 : 3; + + nut = (pc->state64 >> (8 + 3)) & 0x1F; + // 7.4.2.4.3 and 7.4.2.4.4 + if ((nut >= VVC_OPI_NUT && nut <= VVC_PREFIX_APS_NUT && + nut != VVC_PH_NUT) || nut == VVC_AUD_NUT + || (nut == VVC_PREFIX_SEI_NUT && !pc->frame_start_found) + || nut == VVC_RSV_NVCL_26 || nut == VVC_UNSPEC_28 + || nut == VVC_UNSPEC_29) { + if (pc->frame_start_found) { + pc->frame_start_found = 0; + return i - (code_len + 2); + } + } else if (nut == VVC_PH_NUT || IS_H266_SLICE(nut)) { + int sh_picture_header_in_slice_header_flag = buf[i] >> 7; + + if (nut == VVC_PH_NUT || sh_picture_header_in_slice_header_flag) { + if (!pc->frame_start_found) { + pc->frame_start_found = 1; + } else { // First slice of next frame found + pc->frame_start_found = 0; + return i - (code_len + 2); + } + } + } + } + return END_NOT_FOUND; +} + +static int get_pict_type(const CodedBitstreamFragment *pu) +{ + int has_p = 0; + for (int i = 0; i < pu->nb_units; i++) { + CodedBitstreamUnit *unit = &pu->units[i]; + if (IS_H266_SLICE(unit->type)) { + const H266RawSlice *slice = unit->content; + uint8_t type = slice->header.sh_slice_type; + if (type == VVC_SLICE_TYPE_B) { + return AV_PICTURE_TYPE_B; + } + if (type == VVC_SLICE_TYPE_P) { + has_p = 1; + } + } + } + return has_p ? AV_PICTURE_TYPE_P : AV_PICTURE_TYPE_I; +} + +static void set_parser_ctx(AVCodecParserContext *s, AVCodecContext *avctx, + const PuInfo *pu) +{ + static const uint8_t h266_sub_width_c[] = { + 1, 2, 2, 1 + }; + static const uint8_t h266_sub_height_c[] = { + 1, 2, 1, 1 + }; + const H266RawSPS *sps = pu->sps; + const H266RawPPS *pps = pu->pps; + const H266RawNALUnitHeader *nal = &pu->slice->header.nal_unit_header; + + s->pict_type = pu->pic_type; + s->format = get_format(sps); + s->picture_structure = AV_PICTURE_STRUCTURE_FRAME; + + s->key_frame = nal->nal_unit_type == VVC_IDR_W_RADL || + nal->nal_unit_type == VVC_IDR_N_LP || + nal->nal_unit_type == VVC_CRA_NUT || + nal->nal_unit_type == VVC_GDR_NUT; + + s->coded_width = pps->pps_pic_width_in_luma_samples; + s->coded_height = pps->pps_pic_height_in_luma_samples; + s->width = pps->pps_pic_width_in_luma_samples - + (pps->pps_conf_win_left_offset + pps->pps_conf_win_right_offset) * + h266_sub_width_c[sps->sps_chroma_format_idc]; + s->height = pps->pps_pic_height_in_luma_samples - + (pps->pps_conf_win_top_offset + pps->pps_conf_win_bottom_offset) * + h266_sub_height_c[sps->sps_chroma_format_idc];; + + avctx->profile = sps->profile_tier_level.general_profile_idc; + avctx->level = sps->profile_tier_level.general_level_idc; + + avctx->colorspace = (enum AVColorSpace) sps->vui.vui_matrix_coeffs; + avctx->color_primaries = (enum AVColorPrimaries) sps->vui.vui_colour_primaries; + avctx->color_trc = (enum AVColorTransferCharacteristic) sps->vui.vui_transfer_characteristics; + avctx->color_range = + sps->vui.vui_full_range_flag ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; + + avctx->has_b_frames = (sps->sps_max_sublayers_minus1 + 1) > 2 ? 2 : + sps->sps_max_sublayers_minus1; + avctx->max_b_frames = sps->sps_max_sublayers_minus1; + + if (sps->sps_ptl_dpb_hrd_params_present_flag && + sps->sps_timing_hrd_params_present_flag) { + int num = sps->sps_general_timing_hrd_parameters.num_units_in_tick; + int den = sps->sps_general_timing_hrd_parameters.time_scale; + + if (num != 0 && den != 0) + av_reduce(&avctx->framerate.den, &avctx->framerate.num, + num, den, 1 << 30); + } +} + +//8.3.1 Decoding process for picture order count. +//VTM did not follow the spec, and it's much simpler than spec. +//We follow the VTM. +static void get_slice_poc(VVCParserContext *s, int *poc, + const H266RawSPS *sps, + const H266RawPictureHeader *ph, + const H266RawSliceHeader *slice, void *log_ctx) +{ + int poc_msb, max_poc_lsb, poc_lsb; + AuDetector *d = &s->au_detector; + max_poc_lsb = 1 << (sps->sps_log2_max_pic_order_cnt_lsb_minus4 + 4); + poc_lsb = ph->ph_pic_order_cnt_lsb; + if (IS_IDR(slice->nal_unit_header.nal_unit_type)) { + if (ph->ph_poc_msb_cycle_present_flag) + poc_msb = ph->ph_poc_msb_cycle_val * max_poc_lsb; + else + poc_msb = 0; + } else { + int prev_poc = d->prev_tid0_poc; + int prev_poc_lsb = prev_poc & (max_poc_lsb - 1); + int prev_poc_msb = prev_poc - prev_poc_lsb; + if (ph->ph_poc_msb_cycle_present_flag) { + poc_msb = ph->ph_poc_msb_cycle_val * max_poc_lsb; + } else { + if ((poc_lsb < prev_poc_lsb) && ((prev_poc_lsb - poc_lsb) >= + (max_poc_lsb / 2))) + poc_msb = prev_poc_msb + max_poc_lsb; + else if ((poc_lsb > prev_poc_lsb) && ((poc_lsb - prev_poc_lsb) > + (max_poc_lsb / 2))) + poc_msb = prev_poc_msb - max_poc_lsb; + else + poc_msb = prev_poc_msb; + } + } + + *poc = poc_msb + poc_lsb; +} + +static void au_detector_init(AuDetector *d) +{ + d->prev_layer_id = UINT8_MAX; + d->prev_poc = INT_MAX; + d->prev_tid0_poc = INT_MAX; +} + +static int is_au_start(VVCParserContext *s, const PuInfo *pu, void *log_ctx) +{ + //7.4.2.4.3 + AuDetector *d = &s->au_detector; + const H266RawSPS *sps = pu->sps; + const H266RawNALUnitHeader *nal = &pu->slice->header.nal_unit_header; + const H266RawPictureHeader *ph = pu->ph; + const H266RawSlice *slice = pu->slice; + int ret, poc, nut; + + get_slice_poc(s, &poc, sps, ph, &slice->header, log_ctx); + + ret = (nal->nuh_layer_id <= d->prev_layer_id) || (poc != d->prev_poc); + + nut = nal->nal_unit_type; + d->prev_layer_id = nal->nuh_layer_id; + d->prev_poc = poc; + if (nal->nuh_temporal_id_plus1 == 1 && + !ph->ph_non_ref_pic_flag && nut != VVC_RADL_NUT + && nut != VVC_RASL_NUT) { + d->prev_tid0_poc = poc; + } + return ret; +} + +static int get_pu_info(PuInfo *info, const CodedBitstreamH266Context *h266, + const CodedBitstreamFragment *pu, void *logctx) +{ + const H266RawNALUnitHeader *nal; + int ret; + + memset(info, 0, sizeof(*info)); + for (int i = 0; i < pu->nb_units; i++) { + nal = pu->units[i].content; + if (!nal) + continue; + if ( nal->nal_unit_type == VVC_PH_NUT ) { + const H266RawPH *ph = pu->units[i].content; + info->ph = &ph->ph_picture_header; + } else if (IS_H266_SLICE(nal->nal_unit_type)) { + info->slice = pu->units[i].content; + if (info->slice->header.sh_picture_header_in_slice_header_flag) + info->ph = &info->slice->header.sh_picture_header; + if (!info->ph) { + av_log(logctx, AV_LOG_ERROR, + "can't find picture header in picture unit.\n"); + ret = AVERROR_INVALIDDATA; + goto error; + } + break; + } + } + if (!info->slice) { + av_log(logctx, AV_LOG_ERROR, "can't find slice in picture unit.\n"); + ret = AVERROR_INVALIDDATA; + goto error; + } + info->pps = h266->pps[info->ph->ph_pic_parameter_set_id]; + if (!info->pps) { + av_log(logctx, AV_LOG_ERROR, "PPS id %d is not avaliable.\n", + info->ph->ph_pic_parameter_set_id); + ret = AVERROR_INVALIDDATA; + goto error; + } + info->sps = h266->sps[info->pps->pps_seq_parameter_set_id]; + if (!info->sps) { + av_log(logctx, AV_LOG_ERROR, "SPS id %d is not avaliable.\n", + info->pps->pps_seq_parameter_set_id); + ret = AVERROR_INVALIDDATA; + goto error; + } + info->pic_type = get_pict_type(pu); + return 0; + error: + memset(info, 0, sizeof(*info)); + return ret; +} + +static int append_au(AVPacket *pkt, const uint8_t *buf, int buf_size) +{ + int offset = pkt->size; + int ret; + if ((ret = av_grow_packet(pkt, buf_size)) < 0) + goto end; + memcpy(pkt->data + offset, buf, buf_size); + end: + return ret; +} + +/** + * Parse NAL units of found picture and decode some basic information. + * + * @param s parser context. + * @param avctx codec context. + * @param buf buffer with field/frame data. + * @param buf_size size of the buffer. + * @return < 0 for error, == 0 for a complete au, > 0 is not a completed au. + */ +static int parse_nal_units(AVCodecParserContext *s, const uint8_t *buf, + int buf_size, AVCodecContext *avctx) +{ + VVCParserContext *ctx = s->priv_data; + const CodedBitstreamH266Context *h266 = ctx->cbc->priv_data; + + CodedBitstreamFragment *pu = &ctx->picture_unit; + int ret; + PuInfo info; + + if (!buf_size) { + if (ctx->au.size) { + av_packet_move_ref(&ctx->last_au, &ctx->au); + return 0; + } + return 1; + } + + if ((ret = ff_cbs_read(ctx->cbc, pu, buf, buf_size)) < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to parse picture unit.\n"); + goto end; + } + if ((ret = get_pu_info(&info, h266, pu, avctx)) < 0) + goto end; + if (append_au(&ctx->au, buf, buf_size) < 0) { + ret = AVERROR(ENOMEM); + goto end; + } + if (is_au_start(ctx, &info, avctx)) { + set_parser_ctx(s, avctx, &info); + av_packet_move_ref(&ctx->last_au, &ctx->au); + } else { + ret = 1; //not a completed au + } + end: + ff_cbs_fragment_reset(pu); + return ret; +} + +/** + * Combine PU to AU + * + * @param s parser context. + * @param avctx codec context. + * @param buf buffer to a PU. + * @param buf_size size of the buffer. + * @return < 0 for error, == 0 a complete au, > 0 not a completed au. + */ +static int combine_au(AVCodecParserContext *s, AVCodecContext *avctx, + const uint8_t **buf, int *buf_size) +{ + VVCParserContext *ctx = s->priv_data; + int ret; + + ctx->cbc->log_ctx = avctx; + + av_packet_unref(&ctx->last_au); + ret = parse_nal_units(s, *buf, *buf_size, avctx); + if (ret == 0) { + if (ctx->last_au.size) { + *buf = ctx->last_au.data; + *buf_size = ctx->last_au.size; + } else { + ret = 1; //no output + } + } + ctx->cbc->log_ctx = NULL; + return ret; +} + +static int vvc_parser_parse(AVCodecParserContext *s, AVCodecContext *avctx, + const uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size) +{ + int next, ret; + VVCParserContext *ctx = s->priv_data; + ParseContext *pc = &ctx->pc; + CodedBitstreamFragment *pu = &ctx->picture_unit; + + int is_dummy_buf = !buf_size; + int flush = !buf_size; + const uint8_t *dummy_buf = buf; + + *poutbuf = NULL; + *poutbuf_size = 0; + + if (avctx->extradata_size && !ctx->parsed_extradata) { + ctx->parsed_extradata = 1; + + ret = ff_cbs_read_extradata_from_codec(ctx->cbc, pu, avctx); + if (ret < 0) + av_log(avctx, AV_LOG_WARNING, "Failed to parse extradata.\n"); + + ff_cbs_fragment_reset(pu); + } + + if (s->flags & PARSER_FLAG_COMPLETE_FRAMES) { + next = buf_size; + } else { + next = find_frame_end(s, buf, buf_size); + if (ff_combine_frame(pc, next, &buf, &buf_size) < 0) + return buf_size; + } + + is_dummy_buf &= (dummy_buf == buf); + + if (!is_dummy_buf) { + ret = combine_au(s, avctx, &buf, &buf_size); + if (ret > 0 && flush) { + buf_size = 0; + ret = combine_au(s, avctx, &buf, &buf_size); + } + if (ret != 0) + return next; + } + + *poutbuf = buf; + *poutbuf_size = buf_size; + + return next; +} + +static const CodedBitstreamUnitType decompose_unit_types[] = { + VVC_TRAIL_NUT, + VVC_STSA_NUT, + VVC_RADL_NUT, + VVC_RASL_NUT, + VVC_IDR_W_RADL, + VVC_IDR_N_LP, + VVC_CRA_NUT, + VVC_GDR_NUT, + VVC_VPS_NUT, + VVC_SPS_NUT, + VVC_PPS_NUT, + VVC_PH_NUT, + VVC_AUD_NUT, +}; + +static av_cold int vvc_parser_init(AVCodecParserContext *s) +{ + VVCParserContext *ctx = s->priv_data; + int ret; + + ret = ff_cbs_init(&ctx->cbc, AV_CODEC_ID_VVC, NULL); + if (ret < 0) + return ret; + au_detector_init(&ctx->au_detector); + + ctx->cbc->decompose_unit_types = decompose_unit_types; + ctx->cbc->nb_decompose_unit_types = FF_ARRAY_ELEMS(decompose_unit_types); + + return ret; +} + +static av_cold void vvc_parser_close(AVCodecParserContext *s) +{ + VVCParserContext *ctx = s->priv_data; + + av_packet_unref(&ctx->au); + av_packet_unref(&ctx->last_au); + ff_cbs_fragment_free(&ctx->picture_unit); + + ff_cbs_close(&ctx->cbc); + av_freep(&ctx->pc.buffer); +} + +const AVCodecParser ff_vvc_parser = { + .codec_ids = { AV_CODEC_ID_VVC }, + .priv_data_size = sizeof(VVCParserContext), + .parser_init = vvc_parser_init, + .parser_close = vvc_parser_close, + .parser_parse = vvc_parser_parse, +}; diff --git a/libavcodec/wavarc.c b/libavcodec/wavarc.c new file mode 100644 index 00000000000..8e4c7d17dcd --- /dev/null +++ b/libavcodec/wavarc.c @@ -0,0 +1,856 @@ +/* + * WavArc audio decoder + * Copyright (c) 2023 Paul B Mahol + * + * 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 "libavutil/internal.h" +#include "libavutil/intreadwrite.h" +#include "avcodec.h" +#include "codec_internal.h" +#include "decode.h" +#include "get_bits.h" +#include "bytestream.h" +#include "mathops.h" +#include "unary.h" + +typedef struct WavArcContext { + AVClass *av_class; + + GetBitContext gb; + + int shift; + int nb_samples; + int offset; + int align; + + int eof; + int skip; + uint8_t *bitstream; + int64_t max_framesize; + int bitstream_size; + int bitstream_index; + + int pred[2][70]; + int filter[2][70]; + int samples[2][640]; + uint8_t model[256]; + uint16_t freqs[257]; + uint16_t ac_value; + uint16_t ac_low; + uint16_t ac_high; + uint16_t range_high; + uint16_t range_low; + uint16_t freq_range; + int ac_pred[70]; + int ac_out[570]; +} WavArcContext; + +static av_cold int wavarc_init(AVCodecContext *avctx) +{ + WavArcContext *s = avctx->priv_data; + + if (avctx->extradata_size < 52) + return AVERROR_INVALIDDATA; + if (AV_RL32(avctx->extradata + 16) != MKTAG('R','I','F','F')) + return AVERROR_INVALIDDATA; + if (AV_RL32(avctx->extradata + 24) != MKTAG('W','A','V','E')) + return AVERROR_INVALIDDATA; + if (AV_RL32(avctx->extradata + 28) != MKTAG('f','m','t',' ')) + return AVERROR_INVALIDDATA; + if (AV_RL16(avctx->extradata + 38) != 1 && + AV_RL16(avctx->extradata + 38) != 2) + return AVERROR_INVALIDDATA; + + av_channel_layout_uninit(&avctx->ch_layout); + av_channel_layout_default(&avctx->ch_layout, AV_RL16(avctx->extradata + 38)); + avctx->sample_rate = AV_RL32(avctx->extradata + 40); + + s->align = avctx->ch_layout.nb_channels; + + switch (AV_RL16(avctx->extradata + 50)) { + case 8: avctx->sample_fmt = AV_SAMPLE_FMT_U8P; break; + case 16: s->align *= 2; + avctx->sample_fmt = AV_SAMPLE_FMT_S16P; break; + } + + s->shift = 0; + switch (avctx->codec_tag) { + case MKTAG('0','C','P','Y'): + s->nb_samples = 640; + s->offset = 0; + break; + case MKTAG('1','D','I','F'): + s->nb_samples = 256; + s->offset = 4; + break; + case MKTAG('2','S','L','P'): + case MKTAG('3','N','L','P'): + case MKTAG('4','A','L','P'): + case MKTAG('5','E','L','P'): + s->nb_samples = 570; + s->offset = 70; + break; + default: + return AVERROR_INVALIDDATA; + } + + s->max_framesize = s->nb_samples * 16; + s->bitstream = av_calloc(s->max_framesize, sizeof(*s->bitstream)); + if (!s->bitstream) + return AVERROR(ENOMEM); + + return 0; +} + +static unsigned get_urice(GetBitContext *gb, int k) +{ + unsigned x = get_unary(gb, 1, get_bits_left(gb)); + unsigned y = get_bits_long(gb, k); + unsigned z = (x << k) | y; + + return z; +} + +static int get_srice(GetBitContext *gb, int k) +{ + unsigned z = get_urice(gb, k); + + return (z & 1) ? ~((int)(z >> 1)) : z >> 1; +} + +static void do_stereo(WavArcContext *s, int ch, int correlated, int len) +{ + const int nb_samples = s->nb_samples; + const int shift = s->shift; + + if (ch == 0) { + if (correlated) { + for (int n = 0; n < len; n++) { + s->samples[0][n] = s->samples[0][nb_samples + n] >> shift; + s->samples[1][n] = s->pred[1][n] >> shift; + } + } else { + for (int n = 0; n < len; n++) { + s->samples[0][n] = s->samples[0][nb_samples + n] >> shift; + s->samples[1][n] = s->pred[0][n] >> shift; + } + } + } else { + if (correlated) { + for (int n = 0; n < nb_samples; n++) + s->samples[1][n + len] += s->samples[0][n + len]; + } + for (int n = 0; n < len; n++) { + s->pred[0][n] = s->samples[1][nb_samples + n]; + s->pred[1][n] = s->pred[0][n] - s->samples[0][nb_samples + n]; + } + } +} + +static int decode_0cpy(AVCodecContext *avctx, + WavArcContext *s, GetBitContext *gb) +{ + const int bits = s->align * 8; + + s->nb_samples = FFMIN(640, get_bits_left(gb) / bits); + + switch (avctx->sample_fmt) { + case AV_SAMPLE_FMT_U8P: + for (int n = 0; n < s->nb_samples; n++) { + for (int ch = 0; ch < avctx->ch_layout.nb_channels; ch++) + s->samples[ch][n] = get_bits(gb, 8) - 0x80; + } + break; + case AV_SAMPLE_FMT_S16P: + for (int n = 0; n < s->nb_samples; n++) { + for (int ch = 0; ch < avctx->ch_layout.nb_channels; ch++) + s->samples[ch][n] = sign_extend(av_bswap16(get_bits(gb, 16)), 16); + } + break; + } + return 0; +} + +static int decode_1dif(AVCodecContext *avctx, + WavArcContext *s, GetBitContext *gb) +{ + int ch, finished, fill, correlated; + + ch = 0; + finished = 0; + while (!finished) { + int *samples = s->samples[ch]; + int k, block_type; + + if (get_bits_left(gb) <= 0) + return AVERROR_INVALIDDATA; + + block_type = get_urice(gb, 1); + if (block_type < 4 && block_type >= 0) { + k = 1 + (avctx->sample_fmt == AV_SAMPLE_FMT_S16P); + k = get_urice(gb, k) + 1; + if (k >= 32) + return AVERROR_INVALIDDATA; + } + + switch (block_type) { + case 8: + s->eof = 1; + return AVERROR_EOF; + case 7: + s->nb_samples = get_bits(gb, 8); + continue; + case 6: + s->shift = get_urice(gb, 2); + continue; + case 5: + if (avctx->sample_fmt == AV_SAMPLE_FMT_U8P) { + fill = (int8_t)get_bits(gb, 8); + fill -= 0x80; + } else { + fill = (int16_t)get_bits(gb, 16); + fill -= 0x8000; + } + + for (int n = 0; n < s->nb_samples; n++) + samples[n + 4] = fill; + finished = 1; + break; + case 4: + for (int n = 0; n < s->nb_samples; n++) + samples[n + 4] = 0; + finished = 1; + break; + case 3: + for (int n = 0; n < s->nb_samples; n++) + samples[n + 4] = get_srice(gb, k) + (samples[n + 3] - samples[n + 2]) * 3 + + samples[n + 1]; + finished = 1; + break; + case 2: + for (int n = 0; n < s->nb_samples; n++) + samples[n + 4] = get_srice(gb, k) + (samples[n + 3] * 2 - samples[n + 2]); + finished = 1; + break; + case 1: + for (int n = 0; n < s->nb_samples; n++) + samples[n + 4] = get_srice(gb, k) + samples[n + 3]; + finished = 1; + break; + case 0: + for (int n = 0; n < s->nb_samples; n++) + samples[n + 4] = get_srice(gb, k); + finished = 1; + break; + default: + return AVERROR_INVALIDDATA; + } + + if (finished == 1 && avctx->ch_layout.nb_channels == 2) { + if (ch == 0) + correlated = get_bits1(gb); + finished = ch != 0; + do_stereo(s, ch, correlated, 4); + ch = 1; + } + } + + if (avctx->ch_layout.nb_channels == 1) { + for (int n = 0; n < 4; n++) + s->samples[0][n] = s->samples[0][s->nb_samples + n]; + } + + return 0; +} + +static int decode_2slp(AVCodecContext *avctx, + WavArcContext *s, GetBitContext *gb) +{ + int ch, finished, fill, correlated, order; + + ch = 0; + finished = 0; + while (!finished) { + int *samples = s->samples[ch]; + int k, block_type; + + if (get_bits_left(gb) <= 0) + return AVERROR_INVALIDDATA; + + block_type = get_urice(gb, 1); + if (block_type < 5 && block_type >= 0) { + k = 1 + (avctx->sample_fmt == AV_SAMPLE_FMT_S16P); + k = get_urice(gb, k) + 1; + if (k >= 32) + return AVERROR_INVALIDDATA; + } + + switch (block_type) { + case 9: + s->eof = 1; + return AVERROR_EOF; + case 8: + s->nb_samples = get_urice(gb, 8); + if (s->nb_samples > 570) { + s->nb_samples = 570; + return AVERROR_INVALIDDATA; + } + continue; + case 7: + s->shift = get_urice(gb, 2); + continue; + case 6: + if (avctx->sample_fmt == AV_SAMPLE_FMT_U8P) { + fill = (int8_t)get_bits(gb, 8); + fill -= 0x80; + } else { + fill = (int16_t)get_bits(gb, 16); + fill -= 0x8000; + } + + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] = fill; + finished = 1; + break; + case 5: + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] = 0; + finished = 1; + break; + case 4: + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] = get_srice(gb, k) + (samples[n + 69] - samples[n + 68]) * 3 + + samples[n + 67]; + finished = 1; + break; + case 3: + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] = get_srice(gb, k) + (samples[n + 69] * 2 - samples[n + 68]); + finished = 1; + break; + case 2: + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] = get_srice(gb, k); + finished = 1; + break; + case 1: + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] = get_srice(gb, k) + samples[n + 69]; + finished = 1; + break; + case 0: + order = get_urice(gb, 2); + if ((unsigned)order > FF_ARRAY_ELEMS(s->filter[ch])) + return AVERROR_INVALIDDATA; + for (int o = 0; o < order; o++) + s->filter[ch][o] = get_srice(gb, 2); + for (int n = 0; n < s->nb_samples; n++) { + int sum = 15; + + for (int o = 0; o < order; o++) + sum += s->filter[ch][o] * (unsigned)samples[n + 70 - o - 1]; + + samples[n + 70] = get_srice(gb, k) + (sum >> 4); + } + finished = 1; + break; + default: + return AVERROR_INVALIDDATA; + } + + if (finished == 1 && avctx->ch_layout.nb_channels == 2) { + if (ch == 0) + correlated = get_bits1(gb); + finished = ch != 0; + do_stereo(s, ch, correlated, 70); + ch = 1; + } + } + + if (avctx->ch_layout.nb_channels == 1) { + for (int n = 0; n < 70; n++) + s->samples[0][n] = s->samples[0][s->nb_samples + n]; + } + + return 0; +} + +static int ac_init(AVCodecContext *avctx, + WavArcContext *s, GetBitContext *gb) +{ + s->ac_low = 0; + s->ac_high = 0xffffu; + s->ac_value = get_bits(gb, 16); + + s->freq_range = s->freqs[256]; + if (!s->freq_range) + return AVERROR_INVALIDDATA; + return 0; +} + +static unsigned ac_get_prob(WavArcContext *s) +{ + return ((s->freq_range - 1) + ((unsigned)s->ac_value - (unsigned)s->ac_low) * + (unsigned)s->freq_range) / (((unsigned)s->ac_high - (unsigned)s->ac_low) + 1) & 0xffffu; +} + +static unsigned ac_map_symbol(WavArcContext *s, unsigned prob) +{ + int idx = 255; + + while (prob < s->freqs[idx]) + idx--; + + s->range_high = s->freqs[idx + 1]; + s->range_low = s->freqs[idx]; + + return idx; +} + +static int ac_normalize(AVCodecContext *avctx, WavArcContext *s, GetBitContext *gb) +{ + int range; + + range = (unsigned)s->ac_high - (unsigned)s->ac_low + 1; + s->ac_high = (int16_t)((range * (unsigned)s->range_high) / (unsigned)s->freq_range) + -1 + s->ac_low; + s->ac_low += (int16_t)((range * (unsigned)s->range_low) / (unsigned)s->freq_range); + + for (;;) { + if ((s->ac_high & 0x8000) != (s->ac_low & 0x8000)) { + if ((s->ac_low & 0x4000) == 0 || (s->ac_high & 0x4000) != 0) + return 0; + s->ac_value ^= 0x4000; + s->ac_low &= 0x3fff; + s->ac_high |= 0x4000; + } + + s->ac_low = s->ac_low << 1; + s->ac_high = s->ac_high * 2 + 1; + if (s->ac_high < s->ac_low) { + av_log(avctx, AV_LOG_ERROR, "invalid state\n"); + return AVERROR_INVALIDDATA; + } + + if (get_bits_left(gb) <= 0) { + av_log(avctx, AV_LOG_ERROR, "overread in arithmetic coder\n"); + return AVERROR_INVALIDDATA; + } + + s->ac_value = s->ac_value * 2 + get_bits1(gb); + } +} + +static void ac_init_model(WavArcContext *s) +{ + memset(s->freqs, 0, sizeof(s->freqs)); + + for (int n = 0; n < 256; n++) + s->freqs[n+1] = s->model[n] + s->freqs[n]; +} + +static int ac_read_model(AVCodecContext *avctx, + WavArcContext *s, + GetBitContext *gb) +{ + unsigned start, end; + + memset(s->model, 0, sizeof(s->model)); + + start = get_bits(gb, 8); + end = get_bits(gb, 8); + + for (;;) { + while (start <= end) + s->model[start++] = get_bits(gb, 8); + + if (get_bits_left(gb) < 8) + return AVERROR_INVALIDDATA; + + if (get_bits_left(gb) < 8) + return AVERROR_INVALIDDATA; + + start = get_bits(gb, 8); + if (!start) + break; + + end = get_bits(gb, 8); + } + + ac_init_model(s); + + return 0; +} + +static int decode_5elp(AVCodecContext *avctx, + WavArcContext *s, GetBitContext *gb) +{ + int ch, finished, fill, correlated, order = 0; + + ch = 0; + finished = 0; + while (!finished) { + int *samples = s->samples[ch]; + int *ac_pred = s->ac_pred; + int *ac_out = s->ac_out; + int k, block_type; + + if (get_bits_left(gb) <= 0) + return AVERROR_INVALIDDATA; + + memset(s->ac_out, 0, sizeof(s->ac_out)); + + block_type = get_urice(gb, 1); + av_log(avctx, AV_LOG_DEBUG, "block_type : %d\n", block_type); + + if (block_type >= 0 && block_type <= 7) { + k = 1 + (avctx->sample_fmt == AV_SAMPLE_FMT_S16P); + k = get_urice(gb, k) + 1; + } + + if (block_type <= 2 || block_type == 6 || block_type == 13 || + block_type == 14 || block_type == 15 || block_type == 19) { + order = get_urice(gb, 2); + if ((unsigned)order > FF_ARRAY_ELEMS(s->filter[ch])) + return AVERROR_INVALIDDATA; + for (int o = 0; o < order; o++) + s->filter[ch][o] = get_srice(gb, 2); + } + + if (block_type >= 0 && block_type <= 7) { + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] = get_srice(gb, k); + } else { + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] = 0; + } + + if (block_type >= 13 && block_type <= 20) { + const int ac_size = get_bits(gb, 12); + const int ac_pos = get_bits_count(gb); + GetBitContext ac_gb = *gb; + int ret; + + skip_bits_long(gb, ac_size); + ret = ac_read_model(avctx, s, &ac_gb); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "bad arithmetic model\n"); + return ret; + } + + ret = ac_init(avctx, s, &ac_gb); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "cannot init arithmetic decoder\n"); + return ret; + } + + for (int n = 0; n < s->nb_samples; n++) { + unsigned prob = ac_get_prob(s); + prob = ac_map_symbol(s, prob); + ac_out[n] = prob - 0x80; + if ((ret = ac_normalize(avctx, s, &ac_gb)) < 0) + return ret; + } + + if (get_bits_count(&ac_gb) != ac_pos + ac_size) { + av_log(avctx, AV_LOG_DEBUG, "over/under-read in arithmetic coder: %d\n", + ac_pos + ac_size - get_bits_count(&ac_gb)); + } + } + + switch (block_type) { + case 12: + s->eof = 1; + return AVERROR_EOF; + case 11: + s->nb_samples = get_urice(gb, 8); + if (s->nb_samples > 570) { + s->nb_samples = 570; + return AVERROR_INVALIDDATA; + } + continue; + case 10: + s->shift = get_urice(gb, 2); + continue; + case 9: + if (avctx->sample_fmt == AV_SAMPLE_FMT_U8P) { + fill = (int8_t)get_bits(gb, 8); + fill -= 0x80; + } else { + fill = (int16_t)get_bits(gb, 16); + fill -= 0x8000; + } + + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] = fill; + finished = 1; + break; + case 8: + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] = 0; + finished = 1; + break; + case 20: + case 7: + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] = ac_out[n] + samples[n + 69] * 3 - samples[n + 68] * 3 + samples[n + 67]; + finished = 1; + break; + case 19: + case 6: + for (int n = 0; n < s->nb_samples; n++) { + int sum = 15; + + for (int o = 0; o < order; o++) + sum += s->filter[ch][o] * (unsigned)samples[n + 70 - o - 1]; + + samples[n + 70] += ac_out[n] + (sum >> 4); + } + finished = 1; + break; + case 18: + case 5: + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] = ac_out[n] + samples[n + 69] * 2 - samples[n + 68]; + finished = 1; + break; + case 17: + case 4: + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] = ac_out[n]; + finished = 1; + break; + case 16: + case 3: + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] = ac_out[n] + samples[n + 69]; + finished = 1; + break; + case 15: + case 2: + for (int n = 0; n < 70; n++) { + ac_pred[n] = samples[n]; + samples[n] = 0; + } + + for (int n = 0; n < s->nb_samples; n++) { + int sum = 15; + + for (int o = 0; o < order; o++) + sum += s->filter[ch][o] * (unsigned)samples[n + 70 - o - 1]; + + samples[n + 70] += ac_out[n] + (sum >> 4); + } + + for (int n = 0; n < 70; n++) + samples[n] = ac_pred[n]; + + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] += samples[n + 69] * 2 - samples[n + 68]; + + finished = 1; + break; + case 14: + case 1: + for (int n = 0; n < 70; n++) { + ac_pred[n] = samples[n]; + samples[n] = 0; + } + + for (int n = 0; n < s->nb_samples; n++) { + int sum = 15; + + for (int o = 0; o < order; o++) + sum += s->filter[ch][o] * (unsigned)samples[n + 70 - o - 1]; + + samples[n + 70] += ac_out[n] + (sum >> 4); + } + + for (int n = 0; n < 70; n++) + samples[n] = ac_pred[n]; + + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] += samples[n + 69]; + + finished = 1; + break; + case 13: + case 0: + for (int n = 0; n < s->nb_samples; n++) { + int sum = 15; + + for (int o = 0; o < order; o++) + sum += s->filter[ch][o] * (unsigned)samples[n + 70 - o - 1]; + + samples[n + 70] += ac_out[n] + (sum >> 4); + } + finished = 1; + break; + default: + return AVERROR_INVALIDDATA; + } + + if (finished == 1 && avctx->ch_layout.nb_channels == 2) { + if (ch == 0) + correlated = get_bits1(gb); + finished = ch != 0; + do_stereo(s, ch, correlated, 70); + ch = 1; + } + } + + if (avctx->ch_layout.nb_channels == 1) { + for (int n = 0; n < 70; n++) + s->samples[0][n] = s->samples[0][s->nb_samples + n]; + } + + return 0; +} + +static int wavarc_decode(AVCodecContext *avctx, AVFrame *frame, + int *got_frame_ptr, AVPacket *pkt) +{ + WavArcContext *s = avctx->priv_data; + GetBitContext *gb = &s->gb; + int buf_size, input_buf_size; + const uint8_t *buf; + int ret, n; + + if ((!pkt->size && !s->bitstream_size) || s->nb_samples == 0 || s->eof) { + *got_frame_ptr = 0; + return pkt->size; + } + + buf_size = FFMIN(pkt->size, s->max_framesize - s->bitstream_size); + input_buf_size = buf_size; + if (s->bitstream_index + s->bitstream_size + buf_size + AV_INPUT_BUFFER_PADDING_SIZE > s->max_framesize) { + memmove(s->bitstream, &s->bitstream[s->bitstream_index], s->bitstream_size); + s->bitstream_index = 0; + } + if (pkt->data) + memcpy(&s->bitstream[s->bitstream_index + s->bitstream_size], pkt->data, buf_size); + buf = &s->bitstream[s->bitstream_index]; + buf_size += s->bitstream_size; + s->bitstream_size = buf_size; + if (buf_size < s->max_framesize && pkt->data) { + *got_frame_ptr = 0; + return input_buf_size; + } + + if ((ret = init_get_bits8(gb, buf, buf_size)) < 0) + goto fail; + skip_bits(gb, s->skip); + + switch (avctx->codec_tag) { + case MKTAG('0','C','P','Y'): + ret = decode_0cpy(avctx, s, gb); + break; + case MKTAG('1','D','I','F'): + ret = decode_1dif(avctx, s, gb); + break; + case MKTAG('2','S','L','P'): + case MKTAG('3','N','L','P'): + case MKTAG('4','A','L','P'): + ret = decode_2slp(avctx, s, gb); + break; + case MKTAG('5','E','L','P'): + ret = decode_5elp(avctx, s, gb); + break; + default: + ret = AVERROR_INVALIDDATA; + } + + if (ret < 0) + goto fail; + + s->skip = get_bits_count(gb) - 8 * (get_bits_count(gb) / 8); + n = get_bits_count(gb) / 8; + + if (n > buf_size) { +fail: + s->bitstream_size = 0; + s->bitstream_index = 0; + if (ret == AVERROR_EOF) + return 0; + return AVERROR_INVALIDDATA; + } + + frame->nb_samples = s->nb_samples; + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) + goto fail; + + switch (avctx->sample_fmt) { + case AV_SAMPLE_FMT_U8P: + for (int ch = 0; ch < avctx->ch_layout.nb_channels; ch++) { + uint8_t *dst = (uint8_t *)frame->extended_data[ch]; + const int *src = s->samples[ch] + s->offset; + + for (int n = 0; n < frame->nb_samples; n++) + dst[n] = src[n] * (1U << s->shift) + 0x80U; + } + break; + case AV_SAMPLE_FMT_S16P: + for (int ch = 0; ch < avctx->ch_layout.nb_channels; ch++) { + int16_t *dst = (int16_t *)frame->extended_data[ch]; + const int *src = s->samples[ch] + s->offset; + + for (int n = 0; n < frame->nb_samples; n++) + dst[n] = src[n] * (1U << s->shift); + } + break; + } + + *got_frame_ptr = 1; + + if (s->bitstream_size) { + s->bitstream_index += n; + s->bitstream_size -= n; + return input_buf_size; + } + + return n; +} + +static av_cold int wavarc_close(AVCodecContext *avctx) +{ + WavArcContext *s = avctx->priv_data; + + av_freep(&s->bitstream); + s->bitstream_size = 0; + + return 0; +} + +const FFCodec ff_wavarc_decoder = { + .p.name = "wavarc", + CODEC_LONG_NAME("Waveform Archiver"), + .p.type = AVMEDIA_TYPE_AUDIO, + .p.id = AV_CODEC_ID_WAVARC, + .priv_data_size = sizeof(WavArcContext), + .init = wavarc_init, + FF_CODEC_DECODE_CB(wavarc_decode), + .close = wavarc_close, + .p.capabilities = AV_CODEC_CAP_DR1 | +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DELAY, + .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_U8P, + AV_SAMPLE_FMT_S16P, + AV_SAMPLE_FMT_NONE }, +}; diff --git a/libavcodec/wavpackenc.c b/libavcodec/wavpackenc.c index bdb536382f2..9271e87990e 100644 --- a/libavcodec/wavpackenc.c +++ b/libavcodec/wavpackenc.c @@ -2824,7 +2824,7 @@ static void fill_buffer(WavPackEncodeContext *s, switch (s->avctx->sample_fmt) { case AV_SAMPLE_FMT_U8P: - COPY_SAMPLES(int8_t, 0x80, 0); + COPY_SAMPLES(uint8_t, 0x80, 0); break; case AV_SAMPLE_FMT_S16P: COPY_SAMPLES(int16_t, 0, 0); @@ -2963,7 +2963,8 @@ const FFCodec ff_wavpack_encoder = { CODEC_LONG_NAME("WavPack"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_WAVPACK, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SMALL_LAST_FRAME, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SMALL_LAST_FRAME | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(WavPackEncodeContext), .p.priv_class = &wavpack_encoder_class, .init = wavpack_encode_init, diff --git a/libavcodec/wbmpdec.c b/libavcodec/wbmpdec.c index 9638b55b943..3b5753abcd0 100644 --- a/libavcodec/wbmpdec.c +++ b/libavcodec/wbmpdec.c @@ -72,9 +72,9 @@ static int wbmp_decode_frame(AVCodecContext *avctx, AVFrame *p, if (p->linesize[0] == (width + 7) / 8) bytestream2_get_buffer(&gb, p->data[0], height * ((width + 7) / 8)); else - readbits(p->data[0], width, height, p->linesize[0], gb.buffer, gb.buffer_end - gb.buffer_start); + readbits(p->data[0], width, height, p->linesize[0], gb.buffer, gb.buffer_end - gb.buffer); - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; p->pict_type = AV_PICTURE_TYPE_I; *got_frame = 1; diff --git a/libavcodec/wbmpenc.c b/libavcodec/wbmpenc.c index 25fac746fc4..abb66b4ca97 100644 --- a/libavcodec/wbmpenc.c +++ b/libavcodec/wbmpenc.c @@ -80,7 +80,8 @@ const FFCodec ff_wbmp_encoder = { CODEC_LONG_NAME("WBMP (Wireless Application Protocol Bitmap) image"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_WBMP, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, FF_CODEC_ENCODE_CB(wbmp_encode_frame), .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_MONOBLACK, diff --git a/libavcodec/wcmv.c b/libavcodec/wcmv.c index 2f1d22bc246..b2413ee9c0e 100644 --- a/libavcodec/wcmv.c +++ b/libavcodec/wcmv.c @@ -156,7 +156,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if (bytestream2_get_bytes_left(&gb) < 8LL * blocks) return AVERROR_INVALIDDATA; - if (!avctx->frame_number) { + if (!avctx->frame_num) { ptrdiff_t linesize[4] = { s->prev_frame->linesize[0], 0, 0, 0 }; av_image_fill_black(s->prev_frame->data, linesize, avctx->pix_fmt, 0, avctx->width, avctx->height); @@ -195,7 +195,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, } } - s->prev_frame->key_frame = intra; + if (intra) + s->prev_frame->flags |= AV_FRAME_FLAG_KEY; + else + s->prev_frame->flags &= ~AV_FRAME_FLAG_KEY; s->prev_frame->pict_type = intra ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; if ((ret = av_frame_ref(frame, s->prev_frame)) < 0) diff --git a/libavcodec/webp.c b/libavcodec/webp.c index b4357f95d57..d35cb66f8d4 100644 --- a/libavcodec/webp.c +++ b/libavcodec/webp.c @@ -1186,7 +1186,7 @@ static int vp8_lossless_decode_frame(AVCodecContext *avctx, AVFrame *p, *got_frame = 1; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; ret = data_size; free_and_return: diff --git a/libavcodec/wmaenc.c b/libavcodec/wmaenc.c index 2c647af13bd..80ff696be66 100644 --- a/libavcodec/wmaenc.c +++ b/libavcodec/wmaenc.c @@ -440,7 +440,7 @@ const FFCodec ff_wmav1_encoder = { CODEC_LONG_NAME("Windows Media Audio 1"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_WMAV1, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(WMACodecContext), .init = encode_init, FF_CODEC_ENCODE_CB(encode_superframe), @@ -456,7 +456,7 @@ const FFCodec ff_wmav2_encoder = { CODEC_LONG_NAME("Windows Media Audio 2"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_WMAV2, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(WMACodecContext), .init = encode_init, FF_CODEC_ENCODE_CB(encode_superframe), diff --git a/libavcodec/wmalosslessdec.c b/libavcodec/wmalosslessdec.c index d545d848e2a..5d1c7ac66bb 100644 --- a/libavcodec/wmalosslessdec.c +++ b/libavcodec/wmalosslessdec.c @@ -1334,7 +1334,11 @@ const FFCodec ff_wmalossless_decoder = { .close = decode_close, FF_CODEC_DECODE_CB(decode_packet), .flush = flush, - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_S32P, diff --git a/libavcodec/wmaprodec.c b/libavcodec/wmaprodec.c index fbfe75ee33b..f523f3637fa 100644 --- a/libavcodec/wmaprodec.c +++ b/libavcodec/wmaprodec.c @@ -1678,7 +1678,7 @@ static int decode_packet(AVCodecContext *avctx, WMAProDecodeCtx *s, skip_bits(gb, 2); } else { int num_frames = get_bits(gb, 6); - ff_dlog(avctx, "packet[%d]: number of frames %d\n", avctx->frame_number, num_frames); + ff_dlog(avctx, "packet[%"PRId64"]: number of frames %d\n", avctx->frame_num, num_frames); packet_sequence_number = 0; } @@ -1687,10 +1687,10 @@ static int decode_packet(AVCodecContext *avctx, WMAProDecodeCtx *s, if (avctx->codec_id != AV_CODEC_ID_WMAPRO) { skip_bits(gb, 3); s->skip_packets = get_bits(gb, 8); - ff_dlog(avctx, "packet[%d]: skip packets %d\n", avctx->frame_number, s->skip_packets); + ff_dlog(avctx, "packet[%"PRId64"]: skip packets %d\n", avctx->frame_num, s->skip_packets); } - ff_dlog(avctx, "packet[%d]: nbpf %x\n", avctx->frame_number, + ff_dlog(avctx, "packet[%"PRId64"]: nbpf %x\n", avctx->frame_num, num_bits_prev_frame); /** check for packet loss */ @@ -2094,7 +2094,11 @@ const FFCodec ff_wmapro_decoder = { .init = wmapro_decode_init, .close = wmapro_decode_end, FF_CODEC_DECODE_CB(wmapro_decode_packet), - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1, .flush = wmapro_flush, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, @@ -2110,7 +2114,12 @@ const FFCodec ff_xma1_decoder = { .init = xma_decode_init, .close = xma_decode_end, FF_CODEC_DECODE_CB(xma_decode_packet), - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, + .flush = xma_flush, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, @@ -2126,7 +2135,11 @@ const FFCodec ff_xma2_decoder = { .close = xma_decode_end, FF_CODEC_DECODE_CB(xma_decode_packet), .flush = xma_flush, - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, diff --git a/libavcodec/wmavoice.c b/libavcodec/wmavoice.c index bb98f841a50..44fda0e2d68 100644 --- a/libavcodec/wmavoice.c +++ b/libavcodec/wmavoice.c @@ -2004,7 +2004,11 @@ const FFCodec ff_wmavoice_decoder = { .init = wmavoice_decode_init, .close = wmavoice_decode_end, FF_CODEC_DECODE_CB(wmavoice_decode_packet), - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .flush = wmavoice_flush, }; diff --git a/libavcodec/wmv2enc.c b/libavcodec/wmv2enc.c index 05f993525db..8eb56444a31 100644 --- a/libavcodec/wmv2enc.c +++ b/libavcodec/wmv2enc.c @@ -93,7 +93,7 @@ static av_cold int wmv2_encode_init(AVCodecContext *avctx) return 0; } -int ff_wmv2_encode_picture_header(MpegEncContext *s, int picture_number) +int ff_wmv2_encode_picture_header(MpegEncContext *s) { WMV2EncContext *const w = (WMV2EncContext *) s; @@ -242,6 +242,7 @@ const FFCodec ff_wmv2_encoder = { .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_WMV2, .p.priv_class = &ff_mpv_enc_class, + .p.capabilities = AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(WMV2EncContext), .init = wmv2_encode_init, FF_CODEC_ENCODE_CB(ff_mpv_encode_picture), diff --git a/libavcodec/wmv2enc.h b/libavcodec/wmv2enc.h index 3f7f5104a6e..263265acf38 100644 --- a/libavcodec/wmv2enc.h +++ b/libavcodec/wmv2enc.h @@ -23,7 +23,7 @@ #include "mpegvideo.h" -int ff_wmv2_encode_picture_header(MpegEncContext * s, int picture_number); +int ff_wmv2_encode_picture_header(MpegEncContext * s); void ff_wmv2_encode_mb(MpegEncContext * s, int16_t block[6][64], int motion_x, int motion_y); diff --git a/libavcodec/wnv1.c b/libavcodec/wnv1.c index 88532ee4266..5c57db00548 100644 --- a/libavcodec/wnv1.c +++ b/libavcodec/wnv1.c @@ -69,7 +69,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; if ((ret = init_get_bits8(&gb, buf + 8, buf_size - 8)) < 0) return ret; diff --git a/libavcodec/wrapped_avframe.c b/libavcodec/wrapped_avframe.c index c9579848e6a..0278ea42ba0 100644 --- a/libavcodec/wrapped_avframe.c +++ b/libavcodec/wrapped_avframe.c @@ -109,6 +109,7 @@ const FFCodec ff_wrapped_avframe_encoder = { CODEC_LONG_NAME("AVFrame to AVPacket passthrough"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_WRAPPED_AVFRAME, + .p.capabilities = AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, FF_CODEC_ENCODE_CB(wrapped_avframe_encode), }; diff --git a/libavcodec/x86/Makefile b/libavcodec/x86/Makefile index 118daca333f..71a1cdf63e7 100644 --- a/libavcodec/x86/Makefile +++ b/libavcodec/x86/Makefile @@ -82,6 +82,7 @@ OBJS-$(CONFIG_VP9_DECODER) += x86/vp9dsp_init.o \ x86/vp9dsp_init_12bpp.o \ x86/vp9dsp_init_16bpp.o OBJS-$(CONFIG_WEBP_DECODER) += x86/vp8dsp_init.o +OBJS-$(CONFIG_VVC_DECODER) += x86/vvcdsp_init.o # GCC inline assembly optimizations @@ -202,4 +203,8 @@ X86ASM-OBJS-$(CONFIG_VP9_DECODER) += x86/vp9intrapred.o \ x86/vp9lpf_16bpp.o \ x86/vp9mc.o \ x86/vp9mc_16bpp.o +X86ASM-OBJS-$(CONFIG_VVC_DECODER) += x86/vvc_alf.o \ + x86/vvc_sao.o \ + x86/vvc_sao_10bit.o \ + x86/vvc_mc.o X86ASM-OBJS-$(CONFIG_WEBP_DECODER) += x86/vp8dsp.o diff --git a/libavcodec/x86/aacpsdsp.asm b/libavcodec/x86/aacpsdsp.asm index 105e1af5c56..cc496d4df86 100644 --- a/libavcodec/x86/aacpsdsp.asm +++ b/libavcodec/x86/aacpsdsp.asm @@ -49,7 +49,7 @@ align 16 add dstq, mmsize add nq, mmsize*2 jl .loop - REP_RET + RET %endmacro INIT_XMM sse @@ -83,7 +83,7 @@ align 16 add src2q, mmsize add nq, mmsize*2 jl .loop - REP_RET + RET ;*********************************************************************** ;void ff_ps_stereo_interpolate_sse3(float (*l)[2], float (*r)[2], @@ -116,7 +116,7 @@ align 16 movhps [rq+nq], m2 add nq, 8 jl .loop - REP_RET + RET ;*************************************************************************** ;void ps_stereo_interpolate_ipdopd_sse3(float (*l)[2], float (*r)[2], @@ -164,7 +164,7 @@ align 16 movhps [rq+nq], m2 add nq, 8 jl .loop - REP_RET + RET ;********************************************************** ;void ps_hybrid_analysis_ileave_sse(float out[2][38][64], @@ -484,7 +484,7 @@ align 16 add outq, strideq add nq, 64 jl .loop - REP_RET + RET %endmacro INIT_XMM sse diff --git a/libavcodec/x86/ac3dsp.asm b/libavcodec/x86/ac3dsp.asm index c11a94ca931..a95d359d952 100644 --- a/libavcodec/x86/ac3dsp.asm +++ b/libavcodec/x86/ac3dsp.asm @@ -60,7 +60,7 @@ cglobal ac3_exponent_min, 3, 4, 2, exp, reuse_blks, expn, offset sub expnq, mmsize jg .nextexp .end: - REP_RET + RET %endmacro %define LOOP_ALIGN ALIGN 16 @@ -126,7 +126,7 @@ cglobal float_to_fixed24, 3, 3, 9, dst, src, len sub lenq, 16 %endif ja .loop - REP_RET + RET ;------------------------------------------------------------------------------ ; int ff_ac3_compute_mantissa_size(uint16_t mant_cnt[6][16]) @@ -220,7 +220,7 @@ cglobal ac3_extract_exponents, 3, 3, 4, exp, coef, len add lenq, 4 jl .loop - REP_RET + RET %endmacro %if HAVE_SSE2_EXTERNAL diff --git a/libavcodec/x86/alacdsp.asm b/libavcodec/x86/alacdsp.asm index bb2069f7858..1cfd302de2a 100644 --- a/libavcodec/x86/alacdsp.asm +++ b/libavcodec/x86/alacdsp.asm @@ -100,7 +100,7 @@ align 16 add lenq, mmsize*2 jl .loop - REP_RET + RET %if ARCH_X86_64 cglobal alac_append_extra_bits_mono, 2, 5, 3, buf, exbuf, exbits, ch, len @@ -130,4 +130,4 @@ align 16 add lenq, mmsize*2 jl .loop - REP_RET + RET diff --git a/libavcodec/x86/audiodsp.asm b/libavcodec/x86/audiodsp.asm index f64077cb13a..cf5baa9415d 100644 --- a/libavcodec/x86/audiodsp.asm +++ b/libavcodec/x86/audiodsp.asm @@ -123,7 +123,7 @@ cglobal vector_clip_int32%5, 5,5,%1, dst, src, min, max, len add dstq, mmsize*4*(%2+%3) sub lend, mmsize*(%2+%3) jg .loop - REP_RET + RET %endmacro INIT_XMM sse2 diff --git a/libavcodec/x86/cabac.h b/libavcodec/x86/cabac.h index b046a56a6b7..ce2aefcbac8 100644 --- a/libavcodec/x86/cabac.h +++ b/libavcodec/x86/cabac.h @@ -178,7 +178,7 @@ #if HAVE_7REGS && !BROKEN_COMPILER #define get_cabac_inline get_cabac_inline_x86 static -#if defined(_WIN32) && !defined(_WIN64) && defined(__clang__) +#if ARCH_X86_32 av_noinline #else av_always_inline diff --git a/libavcodec/x86/dirac_dwt.asm b/libavcodec/x86/dirac_dwt.asm index 6c8b3c0d889..1f3b238aee5 100644 --- a/libavcodec/x86/dirac_dwt.asm +++ b/libavcodec/x86/dirac_dwt.asm @@ -75,7 +75,7 @@ cglobal vertical_compose53iL0_%1, 4,4,1, b0, b1, b2, width COMPOSE_53iL0 m0, m1, [b2q+2*widthq], m2 mova [b1q+2*widthq], m0 jg .loop - REP_RET + RET ; void vertical_compose_dirac53iH0(IDWTELEM *b0, IDWTELEM *b1, IDWTELEM *b2, ; int width) @@ -93,7 +93,7 @@ cglobal vertical_compose_dirac53iH0_%1, 4,4,1, b0, b1, b2, width paddw m0, [b1q+2*widthq] mova [b1q+2*widthq], m0 jg .loop - REP_RET + RET ; void vertical_compose_dd97iH0(IDWTELEM *b0, IDWTELEM *b1, IDWTELEM *b2, ; IDWTELEM *b3, IDWTELEM *b4, int width) @@ -110,7 +110,7 @@ cglobal vertical_compose_dd97iH0_%1, 6,6,5, b0, b1, b2, b3, b4, width COMPOSE_DD97iH0 [b2q+2*widthq], [b3q+2*widthq], [b4q+2*widthq] mova [b2q+2*widthq], m1 jg .loop - REP_RET + RET ; void vertical_compose_dd137iL0(IDWTELEM *b0, IDWTELEM *b1, IDWTELEM *b2, ; IDWTELEM *b3, IDWTELEM *b4, int width) @@ -139,7 +139,7 @@ cglobal vertical_compose_dd137iL0_%1, 6,6,6, b0, b1, b2, b3, b4, width psubw m5, m1 mova [b2q+2*widthq], m5 jg .loop - REP_RET + RET ; void vertical_compose_haar(IDWTELEM *b0, IDWTELEM *b1, int width) cglobal vertical_compose_haar_%1, 3,4,3, b0, b1, width @@ -159,7 +159,7 @@ cglobal vertical_compose_haar_%1, 3,4,3, b0, b1, width paddw m2, m0 mova [b1q+2*widthq], m2 jg .loop - REP_RET + RET %endmacro ; extend the left and right edges of the tmp array by %1 and %2 respectively @@ -225,7 +225,7 @@ cglobal horizontal_compose_haar%2i_%1, 3,6,4, b, tmp, w, x, w2, b_w2 cmp xq, w2q jl .highpass_loop .end: - REP_RET + RET %endmacro @@ -290,7 +290,7 @@ cglobal horizontal_compose_dd97i_ssse3, 3,6,8, b, tmp, w, x, w2, b_w2 cmp xd, w2d jl .highpass_loop .end: - REP_RET + RET INIT_XMM diff --git a/libavcodec/x86/fft.asm b/libavcodec/x86/fft.asm index a44596e5655..34c3fc9a0f5 100644 --- a/libavcodec/x86/fft.asm +++ b/libavcodec/x86/fft.asm @@ -475,7 +475,7 @@ cglobal fft_calc, 2,5,8 mov r0, r1 mov r1, r3 FFT_DISPATCH _interleave %+ SUFFIX, r1 - REP_RET + RET %endif @@ -510,7 +510,7 @@ cglobal fft_calc, 2,5,8 add r2, mmsize*2 jl .loop .end: - REP_RET + RET cglobal fft_permute, 2,7,1 mov r4, [r0 + FFTContext.revtab] @@ -543,7 +543,7 @@ cglobal fft_permute, 2,7,1 movaps [r1 + r2 + 16], xmm1 add r2, 32 jl .loopcopy - REP_RET + RET INIT_XMM sse cglobal imdct_calc, 3,5,3 @@ -583,7 +583,7 @@ cglobal imdct_calc, 3,5,3 sub r3, mmsize add r2, mmsize jl .loop - REP_RET + RET %ifdef PIC %define SECTION_REL - $$ diff --git a/libavcodec/x86/flacdsp.asm b/libavcodec/x86/flacdsp.asm index 6d755f49728..44416e4dfde 100644 --- a/libavcodec/x86/flacdsp.asm +++ b/libavcodec/x86/flacdsp.asm @@ -79,7 +79,7 @@ ALIGN 16 movd [decodedq+4], m1 jg .loop_sample .ret: - REP_RET + RET %endmacro %if HAVE_XOP_EXTERNAL @@ -133,7 +133,7 @@ align 16 mova [outq + lenq], m%2 add lenq, 16 jl .loop - REP_RET + RET %endmacro INIT_XMM sse2 @@ -177,7 +177,7 @@ align 16 add outq, mmsize*2 sub lend, mmsize/4 jg .loop - REP_RET + RET %endmacro INIT_XMM sse2 @@ -302,7 +302,7 @@ align 16 add outq, mmsize*REPCOUNT sub lend, mmsize/4 jg .loop - REP_RET + RET %endmacro INIT_XMM ssse3 diff --git a/libavcodec/x86/h264_chromamc.asm b/libavcodec/x86/h264_chromamc.asm index a5c53034a2b..e70bc492b2d 100644 --- a/libavcodec/x86/h264_chromamc.asm +++ b/libavcodec/x86/h264_chromamc.asm @@ -112,7 +112,7 @@ cglobal %1_%2_chroma_mc8%3, 6, 7 + extra_regs, 0 jne .at_least_one_non_zero ; mx == 0 AND my == 0 - no filter needed mv0_pixels_mc8 - REP_RET + RET .at_least_one_non_zero: %ifidn %2, rv40 @@ -192,7 +192,7 @@ cglobal %1_%2_chroma_mc8%3, 6, 7 + extra_regs, 0 add r1, r2 dec r3d jne .next1drow - REP_RET + RET .both_non_zero: ; general case, bilinear movd m4, r4d ; x @@ -365,7 +365,7 @@ cglobal %1_%2_chroma_mc4, 6, 6 + extra_regs, 0 add r0, r2 sub r3d, 2 jnz .next2rows - REP_RET + RET %endmacro %macro chroma_mc2_mmx_func 2 @@ -407,7 +407,7 @@ cglobal %1_%2_chroma_mc2, 6, 7, 0 add r0, r2 sub r3d, 1 jnz .nextrow - REP_RET + RET %endmacro %define rnd_1d_h264 pw_4 @@ -453,7 +453,7 @@ cglobal %1_%2_chroma_mc8%3, 6, 7, 8 jne .at_least_one_non_zero ; mx == 0 AND my == 0 - no filter needed mv0_pixels_mc8 - REP_RET + RET .at_least_one_non_zero: test r5d, r5d @@ -514,7 +514,7 @@ cglobal %1_%2_chroma_mc8%3, 6, 7, 8 sub r3d, 2 lea r0, [r0+r2*2] jg .next2rows - REP_RET + RET .my_is_zero: mov r5d, r4d @@ -551,7 +551,7 @@ cglobal %1_%2_chroma_mc8%3, 6, 7, 8 lea r0, [r0+r2*2] lea r1, [r1+r2*2] jg .next2xrows - REP_RET + RET .mx_is_zero: mov r4d, r5d @@ -588,7 +588,7 @@ cglobal %1_%2_chroma_mc8%3, 6, 7, 8 sub r3d, 2 lea r0, [r0+r2*2] jg .next2yrows - REP_RET + RET %endmacro %macro chroma_mc4_ssse3_func 2 @@ -638,7 +638,7 @@ cglobal %1_%2_chroma_mc4, 6, 7, 0 sub r3d, 2 lea r0, [r0+r2*2] jg .next2rows - REP_RET + RET %endmacro %define CHROMAMC_AVG NOTHING diff --git a/libavcodec/x86/h264_chromamc_10bit.asm b/libavcodec/x86/h264_chromamc_10bit.asm index fdc4f407c7a..d4f92c90c72 100644 --- a/libavcodec/x86/h264_chromamc_10bit.asm +++ b/libavcodec/x86/h264_chromamc_10bit.asm @@ -67,7 +67,7 @@ cglobal %1_h264_chroma_mc8_10, 6,7,8 jne .at_least_one_non_zero ; mx == 0 AND my == 0 - no filter needed MV0_PIXELS_MC8 - REP_RET + RET .at_least_one_non_zero: mov r6d, 2 @@ -102,7 +102,7 @@ cglobal %1_h264_chroma_mc8_10, 6,7,8 add r1, r2 dec r3d jne .next1drow - REP_RET + RET .xy_interpolation: ; general case, bilinear movd m4, r4m ; x @@ -144,7 +144,7 @@ cglobal %1_h264_chroma_mc8_10, 6,7,8 add r0, r2 dec r3d jne .next2drow - REP_RET + RET %endmacro ;----------------------------------------------------------------------------- @@ -194,7 +194,7 @@ cglobal %1_h264_chroma_mc4_10, 6,6,7 MC4_OP m6, m0 sub r3d, 2 jnz .next2rows - REP_RET + RET %endmacro ;----------------------------------------------------------------------------- @@ -234,7 +234,7 @@ cglobal %1_h264_chroma_mc2_10, 6,7 add r0, r2 dec r3d jnz .nextrow - REP_RET + RET %endmacro %macro NOTHING 2-3 diff --git a/libavcodec/x86/h264_deblock_10bit.asm b/libavcodec/x86/h264_deblock_10bit.asm index 23971b5cb51..033f2f4d550 100644 --- a/libavcodec/x86/h264_deblock_10bit.asm +++ b/libavcodec/x86/h264_deblock_10bit.asm @@ -372,7 +372,7 @@ cglobal deblock_v_luma_10, 5,5,15 add r4, 2 dec r3 jg .loop - REP_RET + RET cglobal deblock_h_luma_10, 5,7,15 shl r2d, 2 @@ -411,7 +411,7 @@ cglobal deblock_h_luma_10, 5,7,15 lea r5, [r5+r1*8] dec r6 jg .loop - REP_RET + RET %endmacro INIT_XMM sse2 @@ -648,7 +648,7 @@ cglobal deblock_v_luma_intra_10, 4,7,16 add r4, mmsize dec r6 jg .loop - REP_RET + RET ;----------------------------------------------------------------------------- ; void ff_deblock_h_luma_intra_10(uint16_t *pix, int stride, int alpha, diff --git a/libavcodec/x86/h264_idct.asm b/libavcodec/x86/h264_idct.asm index 9b5920d3b02..1f86e51d82d 100644 --- a/libavcodec/x86/h264_idct.asm +++ b/libavcodec/x86/h264_idct.asm @@ -354,7 +354,7 @@ INIT_MMX cpuname add r2, 128 cmp r5, 16 jl .nextblock - REP_RET + RET .no_dc: INIT_XMM cpuname mov dst2d, dword [r1+r5*4] @@ -368,7 +368,7 @@ INIT_XMM cpuname add r2, 128 cmp r5, 16 jl .nextblock - REP_RET + RET INIT_MMX mmx h264_idct_add8_mmx_plane: @@ -508,7 +508,7 @@ cglobal h264_idct_add16_8, 5, 5 + ARCH_X86_64, 8 add16_sse2_cycle 5, 0x24 add16_sse2_cycle 6, 0x1e add16_sse2_cycle 7, 0x26 -REP_RET +RET %macro add16intra_sse2_cycle 2 movzx r0, word [r4+%2] @@ -555,7 +555,7 @@ cglobal h264_idct_add16intra_8, 5, 7 + ARCH_X86_64, 8 add16intra_sse2_cycle 5, 0x24 add16intra_sse2_cycle 6, 0x1e add16intra_sse2_cycle 7, 0x26 -REP_RET +RET %macro add8_sse2_cycle 2 movzx r0, word [r4+%2] @@ -610,7 +610,7 @@ cglobal h264_idct_add8_8, 5, 7 + ARCH_X86_64, 8 %endif add8_sse2_cycle 2, 0x5c add8_sse2_cycle 3, 0x64 -REP_RET +RET ;void ff_h264_luma_dc_dequant_idct_mmx(int16_t *output, int16_t *input, int qmul) diff --git a/libavcodec/x86/h264_idct_10bit.asm b/libavcodec/x86/h264_idct_10bit.asm index 9fd05abb2b7..b990db7121e 100644 --- a/libavcodec/x86/h264_idct_10bit.asm +++ b/libavcodec/x86/h264_idct_10bit.asm @@ -155,7 +155,7 @@ cglobal h264_idct_add16_10, 5,6 ADD16_OP 13, 7+3*8 ADD16_OP 14, 6+4*8 ADD16_OP 15, 7+4*8 - REP_RET + RET %endmacro INIT_XMM sse2 @@ -292,7 +292,7 @@ cglobal h264_idct_add16intra_10,5,7,8 ADD16_OP_INTRA 10, 4+4*8 ADD16_OP_INTRA 12, 6+3*8 ADD16_OP_INTRA 14, 6+4*8 - REP_RET + RET AC 8 AC 10 AC 12 @@ -335,7 +335,7 @@ cglobal h264_idct_add8_10,5,8,7 %endif ADD16_OP_INTRA 32, 4+11*8 ADD16_OP_INTRA 34, 4+12*8 - REP_RET + RET AC 16 AC 18 AC 32 @@ -384,7 +384,7 @@ cglobal h264_idct_add8_422_10, 5, 8, 7 ADD16_OP_INTRA 34, 4+12*8 ADD16_OP_INTRA 40, 4+13*8 ; i+4 ADD16_OP_INTRA 42, 4+14*8 ; i+4 -REP_RET +RET AC 16 AC 18 AC 24 ; i+4 diff --git a/libavcodec/x86/h264_intrapred.asm b/libavcodec/x86/h264_intrapred.asm index 31840a14720..8a38ba2bb5d 100644 --- a/libavcodec/x86/h264_intrapred.asm +++ b/libavcodec/x86/h264_intrapred.asm @@ -62,7 +62,7 @@ cglobal pred16x16_vertical_8, 2,3 lea r0, [r0+r1*2] dec r2 jg .loop - REP_RET + RET ;----------------------------------------------------------------------------- ; void ff_pred16x16_horizontal_8(uint8_t *src, ptrdiff_t stride) @@ -95,7 +95,7 @@ cglobal pred16x16_horizontal_8, 2,3 lea r0, [r0+r1*2] dec r2 jg .loop - REP_RET + RET %endmacro INIT_MMX mmxext @@ -146,7 +146,7 @@ cglobal pred16x16_dc_8, 2,7 lea r4, [r4+r1*2] dec r3d jg .loop - REP_RET + RET %endmacro INIT_XMM sse2 @@ -192,7 +192,7 @@ cglobal pred16x16_tm_vp8_8, 2,6,6 lea r0, [r0+r1*2] dec r5d jg .loop - REP_RET + RET %if HAVE_AVX2_EXTERNAL INIT_YMM avx2 @@ -228,7 +228,7 @@ cglobal pred16x16_tm_vp8_8, 2, 4, 5, dst, stride, stride3, iteration lea dstq, [dstq+strideq*4] dec iterationd jg .loop - REP_RET + RET %endif ;----------------------------------------------------------------------------- @@ -427,7 +427,7 @@ cglobal pred16x16_plane_%1_8, 2,9,7 lea r0, [r0+r2*2] dec r4 jg .loop - REP_RET + RET %endmacro INIT_XMM sse2 @@ -556,7 +556,7 @@ ALIGN 16 lea r0, [r0+r2*2] dec r4 jg .loop - REP_RET + RET %endmacro INIT_XMM sse2 @@ -599,7 +599,7 @@ cglobal pred8x8_horizontal_8, 2,3 lea r0, [r0+r1*2] dec r2 jg .loop - REP_RET + RET %endmacro INIT_MMX mmxext @@ -737,7 +737,7 @@ cglobal pred8x8_dc_rv40_8, 2,7 lea r4, [r4+r1*2] dec r3d jg .loop - REP_RET + RET ;----------------------------------------------------------------------------- ; void ff_pred8x8_tm_vp8_8(uint8_t *src, ptrdiff_t stride) @@ -770,7 +770,7 @@ cglobal pred8x8_tm_vp8_8, 2,6,4 lea r0, [r0+r1*2] dec r5d jg .loop - REP_RET + RET INIT_XMM ssse3 cglobal pred8x8_tm_vp8_8, 2,3,6 @@ -797,7 +797,7 @@ cglobal pred8x8_tm_vp8_8, 2,3,6 lea r0, [r0+r1*2] dec r2d jg .loop - REP_RET + RET ; dest, left, right, src, tmp ; output: %1 = (t[n-1] + t[n]*2 + t[n+1] + 2) >> 2 @@ -1802,7 +1802,7 @@ cglobal pred4x4_tm_vp8_8, 3,6 lea r0, [r0+r2*2] dec r5d jg .loop - REP_RET + RET INIT_XMM ssse3 cglobal pred4x4_tm_vp8_8, 3,3 diff --git a/libavcodec/x86/h264_intrapred_10bit.asm b/libavcodec/x86/h264_intrapred_10bit.asm index c4645d434ee..2f30807332a 100644 --- a/libavcodec/x86/h264_intrapred_10bit.asm +++ b/libavcodec/x86/h264_intrapred_10bit.asm @@ -327,7 +327,7 @@ cglobal pred8x8_horizontal_10, 2, 3 lea r0, [r0+r1*2] dec r2d jg .loop - REP_RET + RET ;----------------------------------------------------------------------------- ; void ff_predict_8x8_dc_10(pixel *src, ptrdiff_t stride) @@ -481,7 +481,7 @@ cglobal pred8x8_plane_10, 2, 7, 7 add r0, r1 dec r2d jg .loop - REP_RET + RET ;----------------------------------------------------------------------------- @@ -994,7 +994,7 @@ cglobal pred16x16_vertical_10, 2, 3 lea r0, [r0+r1*2] dec r2d jg .loop - REP_RET + RET ;----------------------------------------------------------------------------- ; void ff_pred16x16_horizontal_10(pixel *src, ptrdiff_t stride) @@ -1012,7 +1012,7 @@ cglobal pred16x16_horizontal_10, 2, 3 lea r0, [r0+r1*2] dec r2d jg .vloop - REP_RET + RET ;----------------------------------------------------------------------------- ; void ff_pred16x16_dc_10(pixel *src, ptrdiff_t stride) @@ -1048,7 +1048,7 @@ cglobal pred16x16_dc_10, 2, 6 lea r5, [r5+r1*2] dec r3d jg .loop - REP_RET + RET ;----------------------------------------------------------------------------- ; void ff_pred16x16_top_dc_10(pixel *src, ptrdiff_t stride) @@ -1070,7 +1070,7 @@ cglobal pred16x16_top_dc_10, 2, 3 lea r0, [r0+r1*2] dec r2d jg .loop - REP_RET + RET ;----------------------------------------------------------------------------- ; void ff_pred16x16_left_dc_10(pixel *src, ptrdiff_t stride) @@ -1101,7 +1101,7 @@ cglobal pred16x16_left_dc_10, 2, 6 lea r5, [r5+r1*2] dec r3d jg .loop - REP_RET + RET ;----------------------------------------------------------------------------- ; void ff_pred16x16_128_dc_10(pixel *src, ptrdiff_t stride) @@ -1116,4 +1116,4 @@ cglobal pred16x16_128_dc_10, 2,3 lea r0, [r0+r1*2] dec r2d jg .loop - REP_RET + RET diff --git a/libavcodec/x86/h264_qpel_10bit.asm b/libavcodec/x86/h264_qpel_10bit.asm index c862cb22269..80483b15ba7 100644 --- a/libavcodec/x86/h264_qpel_10bit.asm +++ b/libavcodec/x86/h264_qpel_10bit.asm @@ -211,7 +211,7 @@ cglobal %1_h264_qpel16_mc00_10, 3,4 lea r1, [r1+r2*2] dec r3d jg .loop - REP_RET + RET %endmacro %define OP_MOV mova diff --git a/libavcodec/x86/h264_qpel_8bit.asm b/libavcodec/x86/h264_qpel_8bit.asm index 6269b3cf4f5..4e64329991e 100644 --- a/libavcodec/x86/h264_qpel_8bit.asm +++ b/libavcodec/x86/h264_qpel_8bit.asm @@ -89,7 +89,7 @@ cglobal %1_h264_qpel4_h_lowpass, 4,5 ; dst, src, dstStride, srcStride add r1, r3 dec r4d jg .loop - REP_RET + RET %endmacro INIT_MMX mmxext @@ -149,7 +149,7 @@ cglobal %1_h264_qpel8_h_lowpass, 4,5 ; dst, src, dstStride, srcStride add r1, r3 dec r4d jg .loop - REP_RET + RET %endmacro INIT_MMX mmxext @@ -192,7 +192,7 @@ cglobal %1_h264_qpel8_h_lowpass, 4,5,8 ; dst, src, dstStride, srcStride add r0, r2 dec r4d jne .loop - REP_RET + RET %endmacro INIT_XMM ssse3 @@ -239,7 +239,7 @@ cglobal %1_h264_qpel4_h_lowpass_l2, 5,6 ; dst, src, src2, dstStride, srcStride add r2, r4 dec r5d jg .loop - REP_RET + RET %endmacro INIT_MMX mmxext @@ -303,7 +303,7 @@ cglobal %1_h264_qpel8_h_lowpass_l2, 5,6 ; dst, src, src2, dstStride, srcStride add r2, r4 dec r5d jg .loop - REP_RET + RET %endmacro INIT_MMX mmxext @@ -350,7 +350,7 @@ cglobal %1_h264_qpel8_h_lowpass_l2, 5,6,8 ; dst, src, src2, dstStride, src2Strid add r2, r4 dec r5d jg .loop - REP_RET + RET %endmacro INIT_XMM ssse3 @@ -458,7 +458,7 @@ cglobal %1_h264_qpel8or16_v_lowpass_op, 5,5,8 ; dst, src, dstStride, srcStride, FILT_V %1 FILT_V %1 .end: - REP_RET + RET %endmacro INIT_XMM sse2 @@ -531,7 +531,7 @@ cglobal %1_h264_qpel4_hv_lowpass_h, 3,4 ; tmp, dst, dstStride add r1, r2 dec r3d jnz .loop - REP_RET + RET %endmacro INIT_MMX mmxext @@ -574,7 +574,7 @@ cglobal %1_h264_qpel8or16_hv1_lowpass_op, 4,4,8 ; src, tmp, srcStride, size FILT_HV 14*48 FILT_HV 15*48 .end: - REP_RET + RET %endmacro INIT_XMM sse2 @@ -619,7 +619,7 @@ cglobal %1_h264_qpel8or16_hv2_lowpass_op, 5,5 ; dst, tmp, dstStride, unused, h add r0, r2 dec r4d jne .loop - REP_RET + RET %endmacro INIT_MMX mmxext @@ -710,7 +710,7 @@ cglobal %1_h264_qpel8or16_hv2_lowpass, 5,5,8 ; dst, tmp, dstStride, tmpStride, s dec r4d jne .op16 .done: - REP_RET + RET %endmacro INIT_XMM ssse3 @@ -776,7 +776,7 @@ cglobal %1_pixels8_l2_shift5, 6, 6 ; dst, src16, src8, dstStride, src8Stride, h lea r0, [r0+2*r3] sub r5d, 2 jne .loop - REP_RET + RET %endmacro INIT_MMX mmxext @@ -845,7 +845,7 @@ cglobal %1_h264_qpel16_h_lowpass_l2, 5, 6, 16 ; dst, src, src2, dstStride, src2S add r2, r4 dec r5d jg .loop - REP_RET + RET %endmacro INIT_XMM ssse3 diff --git a/libavcodec/x86/h264_weight.asm b/libavcodec/x86/h264_weight.asm index 6076e64ae05..66353d1a9c1 100644 --- a/libavcodec/x86/h264_weight.asm +++ b/libavcodec/x86/h264_weight.asm @@ -79,7 +79,7 @@ cglobal h264_weight_%1, 6, 6, %2 add r0, r1 dec r2d jnz .nextrow - REP_RET + RET %endmacro INIT_XMM sse2 @@ -102,7 +102,7 @@ cglobal h264_weight_%1, 6, 6, %2 add r0, r3 dec r2d jnz .nextrow - REP_RET + RET %endmacro INIT_MMX mmxext @@ -196,7 +196,7 @@ cglobal h264_biweight_%1, 7, 8, %2 add r1, r2 dec r3d jnz .nextrow - REP_RET + RET %endmacro INIT_XMM sse2 @@ -223,7 +223,7 @@ cglobal h264_biweight_%1, 7, 8, %2 add r1, r4 dec r3d jnz .nextrow - REP_RET + RET %endmacro INIT_MMX mmxext @@ -258,7 +258,7 @@ cglobal h264_biweight_16, 7, 8, 8 add r1, r2 dec r3d jnz .nextrow - REP_RET + RET INIT_XMM ssse3 cglobal h264_biweight_8, 7, 8, 8 @@ -281,4 +281,4 @@ cglobal h264_biweight_8, 7, 8, 8 add r1, r4 dec r3d jnz .nextrow - REP_RET + RET diff --git a/libavcodec/x86/h264_weight_10bit.asm b/libavcodec/x86/h264_weight_10bit.asm index f924e558544..356871bc62d 100644 --- a/libavcodec/x86/h264_weight_10bit.asm +++ b/libavcodec/x86/h264_weight_10bit.asm @@ -101,7 +101,7 @@ cglobal h264_weight_16_10 add r0, r1 dec r2d jnz .nextrow - REP_RET + RET %endmacro INIT_XMM sse2 @@ -120,7 +120,7 @@ cglobal h264_weight_8_10 add r0, r1 dec r2d jnz .nextrow - REP_RET + RET %endmacro INIT_XMM sse2 @@ -142,7 +142,7 @@ cglobal h264_weight_4_10 add r0, r3 dec r2d jnz .nextrow - REP_RET + RET %endmacro INIT_XMM sse2 @@ -234,7 +234,7 @@ cglobal h264_biweight_16_10 add r1, r2 dec r3d jnz .nextrow - REP_RET + RET %endmacro INIT_XMM sse2 @@ -253,7 +253,7 @@ cglobal h264_biweight_8_10 add r1, r2 dec r3d jnz .nextrow - REP_RET + RET %endmacro INIT_XMM sse2 @@ -275,7 +275,7 @@ cglobal h264_biweight_4_10 add r1, r4 dec r3d jnz .nextrow - REP_RET + RET %endmacro INIT_XMM sse2 diff --git a/libavcodec/x86/hevc_sao.asm b/libavcodec/x86/hevc_sao.asm index 2eb8924da81..8abb16150d0 100644 --- a/libavcodec/x86/hevc_sao.asm +++ b/libavcodec/x86/hevc_sao.asm @@ -166,7 +166,7 @@ INIT_YMM cpuname add srcq, srcstrideq ; src += srcstride dec heightd ; cmp height jnz .loop ; height loop - REP_RET + RET %endmacro diff --git a/libavcodec/x86/hevc_sao_10bit.asm b/libavcodec/x86/hevc_sao_10bit.asm index 38005740e50..0daa9c645cd 100644 --- a/libavcodec/x86/hevc_sao_10bit.asm +++ b/libavcodec/x86/hevc_sao_10bit.asm @@ -145,7 +145,7 @@ align 16 add srcq, srcstrideq dec heightd jg .loop - REP_RET + RET %endmacro %macro HEVC_SAO_BAND_FILTER_FUNCS 0 diff --git a/libavcodec/x86/hpeldsp.asm b/libavcodec/x86/hpeldsp.asm index b3a270a173b..7a2b7135d80 100644 --- a/libavcodec/x86/hpeldsp.asm +++ b/libavcodec/x86/hpeldsp.asm @@ -78,7 +78,7 @@ cglobal put_pixels8_x2, 4,5 add r0, r4 sub r3d, 4 jne .loop - REP_RET + RET %endmacro INIT_MMX mmxext @@ -120,7 +120,7 @@ cglobal put_pixels16_x2, 4,5 add r0, r4 sub r3d, 4 jne .loop - REP_RET + RET %endmacro INIT_MMX mmxext @@ -162,7 +162,7 @@ cglobal put_no_rnd_pixels8_x2, 4,5 add r0, r4 sub r3d, 4 jne .loop - REP_RET + RET ; void ff_put_pixels8_y2(uint8_t *block, const uint8_t *pixels, ptrdiff_t line_size, int h) @@ -194,7 +194,7 @@ cglobal put_pixels8_y2, 4,5 add r0, r4 sub r3d, 4 jne .loop - REP_RET + RET %endmacro INIT_MMX mmxext @@ -232,7 +232,7 @@ cglobal put_no_rnd_pixels8_y2, 4,5 add r0, r4 sub r3d, 4 jne .loop - REP_RET + RET ; void ff_avg_pixels8_x2(uint8_t *block, const uint8_t *pixels, ptrdiff_t line_size, int h) @@ -280,7 +280,7 @@ cglobal avg_pixels8_x2, 4,5 add r0, r4 sub r3d, 4 jne .loop - REP_RET + RET %endmacro INIT_MMX mmxext @@ -323,7 +323,7 @@ cglobal avg_pixels8_y2, 4,5 add r0, r4 sub r3d, 4 jne .loop - REP_RET + RET %endmacro INIT_MMX mmxext @@ -370,7 +370,7 @@ cglobal avg_approx_pixels8_xy2, 4,5 add r0, r4 sub r3d, 4 jne .loop - REP_RET + RET ; void ff_avg_pixels16_xy2(uint8_t *block, const uint8_t *pixels, ptrdiff_t line_size, int h) @@ -448,7 +448,7 @@ cglobal %1_pixels8_xy2, 4,5 add r4, r2 sub r3d, 2 jnz .loop - REP_RET + RET %endmacro INIT_MMX mmxext @@ -514,7 +514,7 @@ cglobal %1_pixels8_xy2, 4,5 add r4, r2 sub r3d, 2 jnz .loop - REP_RET + RET %endmacro INIT_MMX ssse3 diff --git a/libavcodec/x86/hpeldsp_vp3.asm b/libavcodec/x86/hpeldsp_vp3.asm index 88ca8e8e0af..e580133e454 100644 --- a/libavcodec/x86/hpeldsp_vp3.asm +++ b/libavcodec/x86/hpeldsp_vp3.asm @@ -60,7 +60,7 @@ cglobal put_no_rnd_pixels8_x2_exact, 4,5 lea r0, [r0+r2*4] sub r3d, 4 jg .loop - REP_RET + RET ; void ff_put_no_rnd_pixels8_y2_exact(uint8_t *block, const uint8_t *pixels, ptrdiff_t line_size, int h) @@ -96,4 +96,4 @@ cglobal put_no_rnd_pixels8_y2_exact, 4,5 lea r0, [r0+r2*4] sub r3d, 4 jg .loop - REP_RET + RET diff --git a/libavcodec/x86/huffyuvdsp.asm b/libavcodec/x86/huffyuvdsp.asm index c5c40e991b2..c1b375f4791 100644 --- a/libavcodec/x86/huffyuvdsp.asm +++ b/libavcodec/x86/huffyuvdsp.asm @@ -74,7 +74,7 @@ cglobal add_hfyu_left_pred_bgr32, 4,4,3, dst, src, w, left jl .loop movd m0, [dstq-4] movd [leftq], m0 - REP_RET + RET ; void add_hfyu_median_prediction_mmxext(uint8_t *dst, const uint8_t *top, const uint8_t *diff, int mask, int w, int *left, int *left_top) diff --git a/libavcodec/x86/jpeg2000dsp.asm b/libavcodec/x86/jpeg2000dsp.asm index 61dfdd4f71b..c61cc707844 100644 --- a/libavcodec/x86/jpeg2000dsp.asm +++ b/libavcodec/x86/jpeg2000dsp.asm @@ -113,7 +113,7 @@ align 16 movaps [src1q+csizeq], m5 add csizeq, mmsize jl .loop - REP_RET + RET %endmacro INIT_XMM sse @@ -153,7 +153,7 @@ align 16 mova [src0q+csizeq], m2 add csizeq, mmsize jl .loop - REP_RET + RET %endmacro INIT_XMM sse2 diff --git a/libavcodec/x86/lossless_videodsp.asm b/libavcodec/x86/lossless_videodsp.asm index eb1b80506ee..7159aafe678 100644 --- a/libavcodec/x86/lossless_videodsp.asm +++ b/libavcodec/x86/lossless_videodsp.asm @@ -229,7 +229,7 @@ cglobal add_bytes, 3,4,2, dst, src, w, size inc wq jl .3 .end: - REP_RET + RET %endmacro INIT_XMM sse2 diff --git a/libavcodec/x86/lossless_videoencdsp.asm b/libavcodec/x86/lossless_videoencdsp.asm index c579891d6ac..8ccaea91393 100644 --- a/libavcodec/x86/lossless_videoencdsp.asm +++ b/libavcodec/x86/lossless_videoencdsp.asm @@ -110,7 +110,7 @@ cglobal diff_bytes, 4,5,2, dst, src1, src2, w inc wq jl .loop_gpr_%1%2 .end_%1%2: - REP_RET + RET %endmacro INIT_XMM sse2 diff --git a/libavcodec/x86/mathops.h b/libavcodec/x86/mathops.h index 6298f5ed198..ca7e2dffc10 100644 --- a/libavcodec/x86/mathops.h +++ b/libavcodec/x86/mathops.h @@ -35,12 +35,20 @@ static av_always_inline av_const int MULL(int a, int b, unsigned shift) { int rt, dummy; + if (__builtin_constant_p(shift)) __asm__ ( "imull %3 \n\t" "shrdl %4, %%edx, %%eax \n\t" :"=a"(rt), "=d"(dummy) - :"a"(a), "rm"(b), "ci"((uint8_t)shift) + :"a"(a), "rm"(b), "i"(shift & 0x1F) ); + else + __asm__ ( + "imull %3 \n\t" + "shrdl %4, %%edx, %%eax \n\t" + :"=a"(rt), "=d"(dummy) + :"a"(a), "rm"(b), "c"((uint8_t)shift) + ); return rt; } @@ -113,19 +121,31 @@ __asm__ volatile(\ // avoid +32 for shift optimization (gcc should do that ...) #define NEG_SSR32 NEG_SSR32 static inline int32_t NEG_SSR32( int32_t a, int8_t s){ + if (__builtin_constant_p(s)) __asm__ ("sarl %1, %0\n\t" : "+r" (a) - : "ic" ((uint8_t)(-s)) + : "i" (-s & 0x1F) ); + else + __asm__ ("sarl %1, %0\n\t" + : "+r" (a) + : "c" ((uint8_t)(-s)) + ); return a; } #define NEG_USR32 NEG_USR32 static inline uint32_t NEG_USR32(uint32_t a, int8_t s){ + if (__builtin_constant_p(s)) __asm__ ("shrl %1, %0\n\t" : "+r" (a) - : "ic" ((uint8_t)(-s)) + : "i" (-s & 0x1F) ); + else + __asm__ ("shrl %1, %0\n\t" + : "+r" (a) + : "c" ((uint8_t)(-s)) + ); return a; } diff --git a/libavcodec/x86/me_cmp.asm b/libavcodec/x86/me_cmp.asm index eb036ee4bc0..923eb8078b4 100644 --- a/libavcodec/x86/me_cmp.asm +++ b/libavcodec/x86/me_cmp.asm @@ -458,7 +458,7 @@ cglobal hf_noise%1, 3,3,0, pix1, lsize, h psrlq m6, 32 paddd m0, m6 movd eax, m0 ; eax = result of hf_noise8; - REP_RET ; return eax; + RET ; return eax; %endmacro INIT_MMX mmx diff --git a/libavcodec/x86/pngdsp.asm b/libavcodec/x86/pngdsp.asm index 7bc43c79a0d..efaf652cd4a 100644 --- a/libavcodec/x86/pngdsp.asm +++ b/libavcodec/x86/pngdsp.asm @@ -75,7 +75,7 @@ cglobal add_bytes_l2, 4, 6, 2, dst, src1, src2, wa, w, i .end_s: cmp iq, wq jl .loop_s - REP_RET + RET %macro ADD_PAETH_PRED_FN 1 cglobal add_png_paeth_prediction, 5, 7, %1, dst, src, top, w, bpp, end, cntr diff --git a/libavcodec/x86/qpel.asm b/libavcodec/x86/qpel.asm index 4e72d5084ff..481251314a7 100644 --- a/libavcodec/x86/qpel.asm +++ b/libavcodec/x86/qpel.asm @@ -81,7 +81,7 @@ cglobal %1_pixels4_l2, 6,6 add r2, 16 sub r5d, 4 jne .loop - REP_RET + RET %endmacro INIT_MMX mmxext @@ -125,7 +125,7 @@ cglobal %1_pixels8_l2, 6,6 add r2, 32 sub r5d, 4 jne .loop - REP_RET + RET %endmacro INIT_MMX mmxext @@ -171,7 +171,7 @@ cglobal %1_pixels16_l2, 6,6 add r2, 32 sub r5d, 2 jne .loop - REP_RET + RET %endmacro INIT_MMX mmxext diff --git a/libavcodec/x86/qpeldsp.asm b/libavcodec/x86/qpeldsp.asm index 3a6a6506544..30d26a5acc5 100644 --- a/libavcodec/x86/qpeldsp.asm +++ b/libavcodec/x86/qpeldsp.asm @@ -92,7 +92,7 @@ cglobal put_no_rnd_pixels8_l2, 6,6 add r2, 32 sub r5d, 4 jne .loop - REP_RET + RET %endmacro INIT_MMX mmxext @@ -161,7 +161,7 @@ cglobal put_no_rnd_pixels16_l2, 6,6 add r2, 32 sub r5d, 2 jne .loop - REP_RET + RET %endmacro INIT_MMX mmxext @@ -274,7 +274,7 @@ cglobal %1_mpeg4_qpel16_h_lowpass, 5, 5, 0, 16 add r0, r2 dec r4d jne .loop - REP_RET + RET %endmacro %macro PUT_OP 2-3 @@ -357,7 +357,7 @@ cglobal %1_mpeg4_qpel8_h_lowpass, 5, 5, 0, 8 add r0, r2 dec r4d jne .loop - REP_RET + RET %endmacro INIT_MMX mmxext @@ -466,7 +466,7 @@ cglobal %1_mpeg4_qpel16_v_lowpass, 4, 6, 0, 544 add r0, r1 dec r4d jne .loopv - REP_RET + RET %endmacro %macro PUT_OPH 2-3 @@ -543,7 +543,7 @@ cglobal %1_mpeg4_qpel8_v_lowpass, 4, 6, 0, 288 add r0, r1 dec r4d jne .loopv - REP_RET + RET %endmacro INIT_MMX mmxext diff --git a/libavcodec/x86/rv34dsp.asm b/libavcodec/x86/rv34dsp.asm index 0a3d99c53f2..f29bfd715c7 100644 --- a/libavcodec/x86/rv34dsp.asm +++ b/libavcodec/x86/rv34dsp.asm @@ -54,7 +54,7 @@ cglobal rv34_idct_dc_noround, 1, 2, 0 movq [r0+ 8], m0 movq [r0+16], m0 movq [r0+24], m0 - REP_RET + RET ; Load coeffs and perform row transform ; Output: coeffs in mm[0467], rounder in mm5 diff --git a/libavcodec/x86/rv40dsp.asm b/libavcodec/x86/rv40dsp.asm index f2ce236d441..e02ad2c63f1 100644 --- a/libavcodec/x86/rv40dsp.asm +++ b/libavcodec/x86/rv40dsp.asm @@ -170,7 +170,7 @@ cglobal %1_rv40_qpel_v, 6,6+npicregs,12, dst, dststride, src, srcstride, height, add srcq, srcstrideq dec heightd ; next row jg .nextrow - REP_RET + RET %endmacro %macro FILTER_H 1 @@ -227,7 +227,7 @@ cglobal %1_rv40_qpel_h, 6, 6+npicregs, 12, dst, dststride, src, srcstride, heigh add srcq, srcstrideq dec heightd ; next row jg .nextrow - REP_RET + RET %endmacro INIT_XMM sse2 @@ -280,7 +280,7 @@ cglobal %1_rv40_qpel_v, 6,6+npicregs,8, dst, dststride, src, srcstride, height, add srcq, srcstrideq dec heightd ; next row jg .nextrow - REP_RET + RET cglobal %1_rv40_qpel_h, 6,6+npicregs,8, dst, dststride, src, srcstride, height, mx, picreg %ifdef PIC @@ -313,7 +313,7 @@ cglobal %1_rv40_qpel_h, 6,6+npicregs,8, dst, dststride, src, srcstride, height, add srcq, srcstrideq dec heightd ; next row jg .nextrow - REP_RET + RET %endmacro INIT_XMM ssse3 @@ -464,7 +464,7 @@ cglobal rv40_weight_func_%1_%2, 6, 7, 8 .loop: MAIN_LOOP %2, RND jnz .loop - REP_RET + RET %endmacro INIT_XMM sse2 diff --git a/libavcodec/x86/sbrdsp.asm b/libavcodec/x86/sbrdsp.asm index 87dcdc43ce1..d02f70d7043 100644 --- a/libavcodec/x86/sbrdsp.asm +++ b/libavcodec/x86/sbrdsp.asm @@ -208,7 +208,7 @@ cglobal sbr_sum64x5, 1,2,4,z add zq, 32 cmp zq, r1q jne .loop - REP_RET + RET INIT_XMM sse cglobal sbr_qmf_post_shuffle, 2,3,4,W,z @@ -227,7 +227,7 @@ cglobal sbr_qmf_post_shuffle, 2,3,4,W,z add zq, 16 cmp zq, r2q jl .loop - REP_RET + RET INIT_XMM sse cglobal sbr_neg_odd_64, 1,2,4,z @@ -248,7 +248,7 @@ cglobal sbr_neg_odd_64, 1,2,4,z add zq, 64 cmp zq, r1q jne .loop - REP_RET + RET ; void ff_sbr_qmf_deint_bfly_sse2(float *v, const float *src0, const float *src1) INIT_XMM sse2 @@ -276,7 +276,7 @@ cglobal sbr_qmf_deint_bfly, 3,5,8, v,src0,src1,vrev,c add vrevq, 2*mmsize sub cq, 2*mmsize jge .loop - REP_RET + RET INIT_XMM sse2 cglobal sbr_qmf_pre_shuffle, 1,4,6,z @@ -306,7 +306,7 @@ cglobal sbr_qmf_pre_shuffle, 1,4,6,z jge .loop movq m2, [zq] movq [r2q], m2 - REP_RET + RET %ifdef PIC %define NREGS 1 @@ -432,7 +432,7 @@ cglobal sbr_qmf_deint_neg, 2,4,4,v,src,vrev,c sub vq, mmsize add cq, mmsize jl .loop - REP_RET + RET %macro SBR_AUTOCORRELATE 0 cglobal sbr_autocorrelate, 2,3,8,32, x, phi, cnt diff --git a/libavcodec/x86/takdsp.asm b/libavcodec/x86/takdsp.asm index 5f3ded3ea2d..be8e1ab5533 100644 --- a/libavcodec/x86/takdsp.asm +++ b/libavcodec/x86/takdsp.asm @@ -43,7 +43,7 @@ cglobal tak_decorrelate_ls, 3, 3, 2, p1, p2, length mova [p2q+lengthq+mmsize*1], m1 add lengthq, mmsize*2 jl .loop - REP_RET + RET cglobal tak_decorrelate_sr, 3, 3, 2, p1, p2, length shl lengthd, 2 @@ -60,7 +60,7 @@ cglobal tak_decorrelate_sr, 3, 3, 2, p1, p2, length mova [p1q+lengthq+mmsize*1], m1 add lengthq, mmsize*2 jl .loop - REP_RET + RET cglobal tak_decorrelate_sm, 3, 3, 6, p1, p2, length shl lengthd, 2 @@ -87,7 +87,7 @@ cglobal tak_decorrelate_sm, 3, 3, 6, p1, p2, length mova [p2q+lengthq+mmsize], m4 add lengthq, mmsize*2 jl .loop - REP_RET + RET INIT_XMM sse4 cglobal tak_decorrelate_sf, 3, 3, 5, p1, p2, length, dshift, dfactor @@ -113,4 +113,4 @@ cglobal tak_decorrelate_sf, 3, 3, 5, p1, p2, length, dshift, dfactor mova [p1q+lengthq], m1 add lengthq, mmsize jl .loop - REP_RET + RET diff --git a/libavcodec/x86/utvideodsp.asm b/libavcodec/x86/utvideodsp.asm index b799c44b640..9d54deeb32b 100644 --- a/libavcodec/x86/utvideodsp.asm +++ b/libavcodec/x86/utvideodsp.asm @@ -69,7 +69,7 @@ DEFINE_ARGS src_r, src_g, src_b, linesize_r, linesize_g, linesize_b, x add src_bq, linesize_bq sub hd, 1 jg .nextrow - REP_RET + RET %endmacro INIT_XMM sse2 @@ -125,7 +125,7 @@ DEFINE_ARGS src_r, src_g, src_b, linesize_r, linesize_g, linesize_b, x add src_bq, linesize_bq sub hd, 1 jg .nextrow - REP_RET + RET %endmacro INIT_XMM sse2 diff --git a/libavcodec/x86/v210.asm b/libavcodec/x86/v210.asm index f247737ed03..8ae592205f9 100644 --- a/libavcodec/x86/v210.asm +++ b/libavcodec/x86/v210.asm @@ -116,7 +116,7 @@ cglobal v210_planar_unpack_%1, 5, 5, 6 + 2 * cpuflag(avx2), src, y, u, v, w add wq, (mmsize*3)/8 jl .loop - REP_RET + RET %endmacro INIT_XMM ssse3 diff --git a/libavcodec/x86/vc1dsp_mc.asm b/libavcodec/x86/vc1dsp_mc.asm index 0e6d87dd8b5..c1b3ed1bc3b 100644 --- a/libavcodec/x86/vc1dsp_mc.asm +++ b/libavcodec/x86/vc1dsp_mc.asm @@ -139,7 +139,7 @@ cglobal vc1_put_ver_16b_shift2, 4,7,0, dst, src, stride add dstq, 8 dec i jnz .loop - REP_RET + RET %undef rnd %undef shift %undef stride_neg2 diff --git a/libavcodec/x86/videodsp.asm b/libavcodec/x86/videodsp.asm index b19a8300c54..3cc07878d3b 100644 --- a/libavcodec/x86/videodsp.asm +++ b/libavcodec/x86/videodsp.asm @@ -433,4 +433,4 @@ cglobal prefetch, 3, 3, 0, buf, stride, h add bufq, strideq dec hd jg .loop - REP_RET + RET diff --git a/libavcodec/x86/vp8dsp.asm b/libavcodec/x86/vp8dsp.asm index 33d488bf6f2..6ac5a7721bf 100644 --- a/libavcodec/x86/vp8dsp.asm +++ b/libavcodec/x86/vp8dsp.asm @@ -200,7 +200,7 @@ cglobal put_vp8_epel%1_h6, 6, 6 + npicregs, 8, dst, dststride, src, srcstride, h add srcq, srcstrideq dec heightd ; next row jg .nextrow - REP_RET + RET cglobal put_vp8_epel%1_h4, 6, 6 + npicregs, 7, dst, dststride, src, srcstride, height, mx, picreg shl mxd, 4 @@ -230,7 +230,7 @@ cglobal put_vp8_epel%1_h4, 6, 6 + npicregs, 7, dst, dststride, src, srcstride, h add srcq, srcstrideq dec heightd ; next row jg .nextrow - REP_RET + RET cglobal put_vp8_epel%1_v4, 7, 7, 8, dst, dststride, src, srcstride, height, picreg, my shl myd, 4 @@ -268,7 +268,7 @@ cglobal put_vp8_epel%1_v4, 7, 7, 8, dst, dststride, src, srcstride, height, picr add srcq, srcstrideq dec heightd ; next row jg .nextrow - REP_RET + RET cglobal put_vp8_epel%1_v6, 7, 7, 8, dst, dststride, src, srcstride, height, picreg, my lea myd, [myq*3] @@ -314,7 +314,7 @@ cglobal put_vp8_epel%1_v6, 7, 7, 8, dst, dststride, src, srcstride, height, picr add srcq, srcstrideq dec heightd ; next row jg .nextrow - REP_RET + RET %endmacro INIT_MMX ssse3 @@ -368,7 +368,7 @@ cglobal put_vp8_epel4_h4, 6, 6 + npicregs, 0, dst, dststride, src, srcstride, he add srcq, srcstrideq dec heightd ; next row jg .nextrow - REP_RET + RET ; 4x4 block, H-only 6-tap filter INIT_MMX mmxext @@ -426,7 +426,7 @@ cglobal put_vp8_epel4_h6, 6, 6 + npicregs, 0, dst, dststride, src, srcstride, he add srcq, srcstrideq dec heightd ; next row jg .nextrow - REP_RET + RET INIT_XMM sse2 cglobal put_vp8_epel8_h4, 6, 6 + npicregs, 10, dst, dststride, src, srcstride, height, mx, picreg @@ -474,7 +474,7 @@ cglobal put_vp8_epel8_h4, 6, 6 + npicregs, 10, dst, dststride, src, srcstride, h add srcq, srcstrideq dec heightd ; next row jg .nextrow - REP_RET + RET INIT_XMM sse2 cglobal put_vp8_epel8_h6, 6, 6 + npicregs, 14, dst, dststride, src, srcstride, height, mx, picreg @@ -537,7 +537,7 @@ cglobal put_vp8_epel8_h6, 6, 6 + npicregs, 14, dst, dststride, src, srcstride, h add srcq, srcstrideq dec heightd ; next row jg .nextrow - REP_RET + RET %macro FILTER_V 1 ; 4x4 block, V-only 4-tap filter @@ -590,7 +590,7 @@ cglobal put_vp8_epel%1_v4, 7, 7, 8, dst, dststride, src, srcstride, height, picr add srcq, srcstrideq dec heightd ; next row jg .nextrow - REP_RET + RET ; 4x4 block, V-only 6-tap filter @@ -655,7 +655,7 @@ cglobal put_vp8_epel%1_v6, 7, 7, 8, dst, dststride, src, srcstride, height, picr add srcq, srcstrideq dec heightd ; next row jg .nextrow - REP_RET + RET %endmacro INIT_MMX mmxext @@ -738,7 +738,7 @@ cglobal put_vp8_bilinear%1_v, 7, 7, 7, dst, dststride, src, srcstride, height, p lea srcq, [srcq+srcstrideq*2] sub heightd, 2 jg .nextrow - REP_RET + RET %if cpuflag(ssse3) cglobal put_vp8_bilinear%1_h, 6, 6 + npicregs, 5, dst, dststride, src, srcstride, height, mx, picreg @@ -815,7 +815,7 @@ cglobal put_vp8_bilinear%1_h, 6, 6 + npicregs, 7, dst, dststride, src, srcstride lea srcq, [srcq+srcstrideq*2] sub heightd, 2 jg .nextrow - REP_RET + RET %endmacro INIT_MMX mmxext @@ -838,7 +838,7 @@ cglobal put_vp8_pixels8, 5, 5, 0, dst, dststride, src, srcstride, height lea dstq, [dstq+dststrideq*2] sub heightd, 2 jg .nextrow - REP_RET + RET INIT_XMM sse cglobal put_vp8_pixels16, 5, 5, 2, dst, dststride, src, srcstride, height @@ -851,7 +851,7 @@ cglobal put_vp8_pixels16, 5, 5, 2, dst, dststride, src, srcstride, height lea dstq, [dstq+dststrideq*2] sub heightd, 2 jg .nextrow - REP_RET + RET ;----------------------------------------------------------------------------- ; void ff_vp8_idct_dc_add_(uint8_t *dst, int16_t block[16], ptrdiff_t stride); diff --git a/libavcodec/x86/vvc_alf.asm b/libavcodec/x86/vvc_alf.asm new file mode 100644 index 00000000000..af4118a5097 --- /dev/null +++ b/libavcodec/x86/vvc_alf.asm @@ -0,0 +1,797 @@ +;****************************************************************************** +;* VVC Adaptive Loop Filter SIMD optimizations +;* +;* Copyright (c) 2023 Nuo Mi +;* +;* 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 "libavutil/x86/x86util.asm" + +SECTION_RODATA + +%macro PARAM_SHUFFE 1 +%assign i (%1 * 2) +%assign j ((i + 1) << 8) + (i) +param_shuffe_ %+ %1: +%rep 2 + times 4 dw j + times 4 dw (j + 0x0808) +%endrep +%endmacro + +PARAM_SHUFFE 0 +PARAM_SHUFFE 1 +PARAM_SHUFFE 2 +PARAM_SHUFFE 3 + +CLASSIFY_SHUFFE: times 2 db 2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13 +TRANSPOSE_PERMUTE: dd 0, 1, 4, 5, 2, 3, 6, 7 +ARG_VAR_SHUFFE: times 2 db 0, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4 + +dd448: times 8 dd 512 - 64 +dw64: times 8 dd 64 +dd2: times 8 dd 2 +dw3: times 8 dd 3 +dw5: times 8 dd 5 +dd15: times 8 dd 15 + +SECTION .text + + +%define ALF_NUM_COEFF_LUMA 12 +%define ALF_NUM_COEFF_CHROMA 6 +%define ALF_NUM_COEFF_CC 7 + +%if HAVE_AVX2_EXTERNAL + +;%1-%3 out +;%4 clip or filter +%macro LOAD_LUMA_PARAMS_W16 4 + lea offsetq, [3 * xq] ;xq * ALF_NUM_COEFF_LUMA / ALF_BLOCK_SIZE + movu m%1, [%4q + 2 * offsetq + 0 * 32] ; 2 * for sizeof(int16_t) + movu m%2, [%4q + 2 * offsetq + 1 * 32] + movu m%3, [%4q + 2 * offsetq + 2 * 32] +%endmacro + +%macro LOAD_LUMA_PARAMS_W16 6 + LOAD_LUMA_PARAMS_W16 %1, %2, %3, %4 + ;m%1 = 03 02 01 00 + ;m%2 = 07 06 05 04 + ;m%3 = 11 10 09 08 + + vshufpd m%5, m%1, m%2, 0011b ;06 02 05 01 + vshufpd m%6, m%3, m%5, 1001b ;06 10 01 09 + + vshufpd m%1, m%1, m%6, 1100b ;06 03 09 00 + vshufpd m%2, m%2, m%6, 0110b ;10 07 01 04 + vshufpd m%3, m%3, m%5, 0110b ;02 11 05 08 + + vpermpd m%1, m%1, 01111000b ;09 06 03 00 + vshufpd m%2, m%2, m%2, 1001b ;10 07 04 01 + vpermpd m%3, m%3, 10000111b ;11 08 05 02 +%endmacro + +;%1-%3 out +;%4 clip or filter +;%5, %6 tmp +%macro LOAD_LUMA_PARAMS 6 + LOAD_LUMA_PARAMS_W16 %1, %2, %3, %4, %5, %6 +%endmacro + +%macro LOAD_CHROMA_PARAMS 4 + ;LOAD_CHROMA_PARAMS_W %+ WIDTH %1, %2, %3, %4 + movq xm%1, [%3q] + movd xm%2, [%3q + 8] + vpbroadcastq m%1, xm%1 + vpbroadcastq m%2, xm%2 +%endmacro + +%macro LOAD_PARAMS 0 + %if LUMA + LOAD_LUMA_PARAMS 3, 4, 5, filter, 6, 7 + LOAD_LUMA_PARAMS 6, 7, 8, clip, 9, 10 + %else + LOAD_CHROMA_PARAMS 3, 4, filter, 5 + LOAD_CHROMA_PARAMS 6, 7, clip, 8 + %endif +%endmacro + +;FILTER(param_idx) +;input: m2, m9, m10 +;output: m0, m1 +;m11 ~ m13: tmp +%macro FILTER 1 + %assign i (%1 % 4) + %assign j (%1 / 4 + 3) + %assign k (%1 / 4 + 6) + %define filters m %+ j + %define clips m %+ k + + pshufb m12, clips, [param_shuffe_ %+ i] ;clip + pxor m11, m11 + psubw m11, m12 ;-clip + + vpsubw m9, m2 + CLIPW m9, m11, m12 + + vpsubw m10, m2 + CLIPW m10, m11, m12 + + vpunpckhwd m13, m9, m10 + vpunpcklwd m9, m9, m10 + + pshufb m12, filters, [param_shuffe_ %+ i] ;filter + vpunpcklwd m10, m12, m12 + vpunpckhwd m12, m12, m12 + + vpmaddwd m9, m10 + vpmaddwd m12, m13 + + paddd m0, m9 + paddd m1, m12 +%endmacro + +;FILTER(param_idx, bottom, top, byte_offset) +;input: param_idx, bottom, top, byte_offset +;output: m0, m1 +;temp: m9, m10, offsetq +%macro FILTER 4 + mov offsetq, %4 + LOAD_PIXELS m10, [%2 + offsetq] + neg offsetq + LOAD_PIXELS m9, [%3 + offsetq] + + FILTER %1 +%endmacro + +;get source lines +;GET_SRCS(line) +;input: src, src_stride, vb_pos +;output: s1...s6 +%macro GET_SRCS 1 + lea s1q, [srcq + src_strideq] + lea s3q, [s1q + src_strideq] +%if LUMA + lea s5q, [s3q + src_strideq] +%endif + neg src_strideq + lea s2q, [srcq + src_strideq] + lea s4q, [s2q + src_strideq] +%if LUMA + lea s6q, [s4q + src_strideq] +%endif + neg src_strideq + +%if LUMA + cmp vb_posq, 0 + je %%vb_bottom + cmp vb_posq, 4 + jne %%vb_end +%else + cmp vb_posq, 2 + jne %%vb_end + %if %1 >= 2 + jmp %%vb_bottom + %endif +%endif + +%%vb_above: + ;above + ;p1 = (y + i == vb_pos - 1) ? p0 : p1; + ;p2 = (y + i == vb_pos - 1) ? p0 : p2; + ;p3 = (y + i >= vb_pos - 2) ? p1 : p3; + ;p4 = (y + i >= vb_pos - 2) ? p2 : p4; + ;p5 = (y + i >= vb_pos - 3) ? p3 : p5; + ;p6 = (y + i >= vb_pos - 3) ? p4 : p6; + dec vb_posq + cmp vb_posq, %1 + cmove s1q, srcq + cmove s2q, srcq + + dec vb_posq + cmp vb_posq, %1 + cmovbe s3q, s1q + cmovbe s4q, s2q + + dec vb_posq +%if LUMA + cmp vb_posq, %1 + cmovbe s5q, s3q + cmovbe s6q, s4q +%endif + add vb_posq, 3 + jmp %%vb_end + +%%vb_bottom: + ;bottom + ;p1 = (y + i == vb_pos ) ? p0 : p1; + ;p2 = (y + i == vb_pos ) ? p0 : p2; + ;p3 = (y + i <= vb_pos + 1) ? p1 : p3; + ;p4 = (y + i <= vb_pos + 1) ? p2 : p4; + ;p5 = (y + i <= vb_pos + 2) ? p3 : p5; + ;p6 = (y + i <= vb_pos + 2) ? p4 : p6; + cmp vb_posq, %1 + cmove s1q, srcq + cmove s2q, srcq + + inc vb_posq + cmp vb_posq, %1 + cmovae s3q, s1q + cmovae s4q, s2q + + inc vb_posq +%if LUMA + cmp vb_posq, %1 + cmovae s5q, s3q + cmovae s6q, s4q +%endif + sub vb_posq, 2 +%%vb_end: +%endmacro + +;shift filter result +;SHIFT_VB(line) +;input: m0, m1, vb_pos +;output: m0 +;temp: m9 +%macro SHIFT_VB 1 +%define SHIFT 7 + %if LUMA + %if %1 == 3 + cmp vb_posq, 4 + je %%near_vb + %elif %1 == 0 + cmp vb_posq, 0 + je %%near_vb + %endif + %else + %if %1 == 1 || %1 == 2 + cmp vb_posq, 2 + je %%near_vb + %endif + %endif + ;no vb + vpsrad m0, SHIFT + vpsrad m1, SHIFT + jmp %%shift_end +%%near_vb: + ;vb + vpbroadcastd m9, [dd448] + paddd m0, m9 + paddd m1, m9 + vpsrad m0, SHIFT + 3 + vpsrad m1, SHIFT + 3 +%%shift_end: + vpackssdw m0, m0, m1 +%endmacro + +;filter pixels for luma and chroma +;FILTER_VB(line) +;input: line +;output: m0, m1 +;temp: s0q...s1q +%macro FILTER_VB 1 + vpbroadcastd m0, [dw64] + vpbroadcastd m1, [dw64] + + GET_SRCS %1 + %if LUMA + FILTER 0, s5q, s6q, 0 * ps + FILTER 1, s3q, s4q, 1 * ps + FILTER 2, s3q, s4q, 0 * ps + FILTER 3, s3q, s4q, -1 * ps + FILTER 4, s1q, s2q, 2 * ps + FILTER 5, s1q, s2q, 1 * ps + FILTER 6, s1q, s2q, 0 * ps + FILTER 7, s1q, s2q, -1 * ps + FILTER 8, s1q, s2q, -2 * ps + FILTER 9, srcq, srcq, 3 * ps + FILTER 10, srcq, srcq, 2 * ps + FILTER 11, srcq, srcq, 1 * ps + %else + FILTER 0, s3q, s4q, 0 * ps + FILTER 1, s1q, s2q, 1 * ps + FILTER 2, s1q, s2q, 0 * ps + FILTER 3, s1q, s2q, -1 * ps + FILTER 4, srcq, srcq, 2 * ps + FILTER 5, srcq, srcq, 1 * ps + %endif + SHIFT_VB %1 +%endmacro + +%macro FILTER_16x4 0 + push filterq + push clipq + push strideq + push xq + %define s1q filterq + %define s2q clipq + %define s3q strideq + %define s4q pixel_maxq + %define s5q xq + + %define %%i 0 + %rep 4 + LOAD_PIXELS m2, [srcq] ;p0 + + FILTER_VB %%i + + paddw m0, m2 + + ;clip to pixel + CLIPW m0, m14, m15 + + STORE_PIXELS [dstq] , 0 + + lea srcq, [srcq + src_strideq] + lea dstq, [dstq + dst_strideq] + %assign %%i %%i + 1 + %endrep + + %rep 4 + sub srcq, src_strideq + sub dstq, dst_strideq + %endrep + + pop xq + pop strideq + pop clipq + pop filterq +%endmacro + +;STORE_PIXELS(dest, src) +%macro STORE_PIXELS 2 + %if ps == 2 + movu %1, m%2 + %else + vpackuswb m%2, m%2 + vpermq m%2, m%2, 0x8 + movu %1, xm%2 + %endif +%endmacro + +;LOAD_PIXELS(dest, src) +%macro LOAD_PIXELS 2 +%if ps == 2 + movu %1, %2 +%else + vpmovzxbw %1, %2 +%endif +%endmacro + +;FILTER(bpc, luma/chroma) +%macro ALF_FILTER 2 +%xdefine BPC %1 +%ifidn %2, luma + %xdefine LUMA 1 +%else + %xdefine LUMA 0 +%endif +; void vvc_alf_filter_%2_%1bpc_avx2(uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *src, ptrdiff_t src_stride, +; const ptrdiff_t width, cosnt ptr_diff_t height, const int16_t *filter, const int16_t *clip, ptrdiff_t stride, +; ptrdiff_t vb_pos, ptrdiff_t pixel_max); + +; see c code for s1 to s6 + +cglobal vvc_alf_filter_%2_%1bpc, 11, 14, 16, 6*8, dst, dst_stride, src, src_stride, width, height, filter, clip, stride, vb_pos, pixel_max, \ + offset, x, s6 +;pixel size +%define ps (%1 / 8) + movd xm15, pixel_maxd + vpbroadcastw m15, xm15 + pxor m14, m14 + +.loop: + push srcq + push dstq + xor xd, xd + + .loop_w: + LOAD_PARAMS + FILTER_16x4 + + add srcq, 16 * ps + add dstq, 16 * ps + add xd, 16 + cmp xd, widthd + jl .loop_w + + pop dstq + pop srcq + lea srcq, [srcq + 4 * src_strideq] + lea dstq, [dstq + 4 * dst_strideq] + + lea filterq, [filterq + 2 * strideq] + lea clipq, [clipq + 2 * strideq] + + sub vb_posq, 4 + sub heightq, 4 + jg .loop + RET +%endmacro + +;FILTER(bpc) +%macro ALF_FILTER 1 + ALF_FILTER %1, luma + ALF_FILTER %1, chroma +%endmacro + +%define ALF_GRADIENT_BORDER 2 +%define ALF_BORDER_LUMA 3 + +%macro ALF_CLASSIFY_GRAD 1 +;void ff_vvc_alf_classify_grad(int *gradient_sum, +; const uint8_t *src, ptrdiff_t src_stride, intptr_t width, intptr_t height, +; intptr_t vb_pos); +cglobal vvc_alf_classify_grad_%1bpc, 6, 14, 16, gradient_sum, src, src_stride, width, height, vb_pos, \ + x, y, s0, s1, s2, s3, vb_pos_below, src_stride3 + + lea src_stride3q, [src_strideq * 2 + src_strideq] + + lea vb_pos_belowd, [vb_posd + ALF_GRADIENT_BORDER] + + ;src = src - ALF_BORDER_LUMA * src_stride - ALF_BORDER_LUMA + sub srcq, src_stride3q + sub srcq, ALF_BORDER_LUMA * ps + + add widthd, ALF_GRADIENT_BORDER * 2 + add heightd, ALF_GRADIENT_BORDER * 2 + + xor yd, yd + +.loop_h: + xor xd, xd + pxor m15, m15 ;prev + .loop_w: + lea s0q, [srcq + xq * ps] + lea s1q, [s0q + src_strideq] + lea s2q, [s0q + 2 * src_strideq] + lea s3q, [s0q + src_stride3q] + + cmp yd, vb_pos_belowd + cmove s0q, s1q + + cmp yd, vb_posd + cmove s3q, s2q + + LOAD_PIXELS m0, [s0q] + LOAD_PIXELS m1, [s1q] + LOAD_PIXELS m2, [s2q] + LOAD_PIXELS m3, [s3q] + + LOAD_PIXELS m4, [s0q + 2 * ps] + LOAD_PIXELS m5, [s1q + 2 * ps] + LOAD_PIXELS m6, [s2q + 2 * ps] + LOAD_PIXELS m7, [s3q + 2 * ps] + + vpblendw m8, m0, m1, 0xaa ;nw + vpblendw m9, m0, m5, 0x55 ;n + vpblendw m10, m4, m5, 0xaa ;ne + vpblendw m11, m1, m2, 0xaa ;w + vpblendw m12, m5, m6, 0xaa ;e + vpblendw m13, m2, m3, 0xaa ;sw + vpblendw m14, m2, m7, 0x55 ;s + + + vpblendw m0, m1, m6, 0x55 + vpaddw m0, m0 ;c + + movu m1, [CLASSIFY_SHUFFE] + pshufb m1, m0, m1 ;d + + vpaddw m9, m14 ;n + s + vpsubw m9, m0 ;(n + s) - c + vpabsw m9, m9 ;ver + + vpaddw m11, m12 ;w + e + vpsubw m11, m1 ;(w + e) - d + vpabsw m11, m11 ;hor + + vpblendw m14, m6, m7, 0xaa ;se + vpaddw m8, m14 ;nw + se + vpsubw m8, m1 ;(nw + se) - d + vpabsw m8, m8 ;di0 + + vpaddw m10, m13 ;ne + sw + vpsubw m10, m1 ;(nw + se) - d + vpabsw m10, m10 ;di1 + + phaddw m9, m11 ;vh, each word represent 2x2 pixels + phaddw m8, m10 ;di, each word represent 2x2 pixels + phaddw m0, m9, m8 ;all = each word represent 4x2 pixels, order is v_h_d0_d1 x 4 + + vinserti128 m15, m15, xm0, 1 + vpblendw m1, m0, m15, 0xaa ;t + + phaddw m1, m0 ;each word represent 8x2 pixels, adjacent word share 4x2 pixels + + vextracti128 xm15, m0, 1 ;prev + + movu [gradient_sumq], m1 + + add gradient_sumq, 32 + add xd, 16 + cmp xd, widthd + jl .loop_w + + lea srcq, [srcq + 2 * src_strideq] + add yd, 2 + cmp yd, heightd + jl .loop_h + RET +%endmacro + +;SAVE_CLASSIFY_PARAM_W16(dest, src) +%macro SAVE_CLASSIFY_PARAM_W16 2 + lea tempq, [%1q + xq] + movu [tempq], xm%2 + vperm2i128 m%2, m%2, m%2, 1 + movu [tempq + widthq], xm%2 +%endmacro + +;SAVE_CLASSIFY_PARAM_W8 +%macro SAVE_CLASSIFY_PARAM_W8 2 + movq [%1], xm%2 + vperm2i128 m%2, m%2, m%2, 1 + movq [%1 + widthq], xm%2 +%endmacro + +;SAVE_CLASSIFY_PARAM_W4 +%macro SAVE_CLASSIFY_PARAM_W4 2 + movd [%1], xm%2 + vperm2i128 m%2, m%2, m%2, 1 + movd [%1 + widthq], xm%2 +%endmacro + +;SAVE_CLASSIFY_PARAM_W(dest, src) +%macro SAVE_CLASSIFY_PARAM_W 2 + lea tempq, [%1q + xq] + + cmp wd, 8 + jl %%w4 + SAVE_CLASSIFY_PARAM_W8 tempq, %2 + vpermq m%2, m%2, 00010011b + add tempq, 8 + cmp wd, 8 + je %%end + %%w4: + SAVE_CLASSIFY_PARAM_W4 tempq, %2 +%%end: +%endmacro + +%macro ALF_CLASSIFY_H8 0 + ;first line, sum of 16x4 pixels (includes borders) + lea gradq, [gradient_sumq + 2 * xq] + movu m0, [gradq] + movu m1, [gradq + sum_strideq] + movu m2, [gradq + 2 * sum_strideq] + + pcmpeqb m11, m11 + movd xm13, yd + vpbroadcastd m13, xm13 + movd xm12, vb_posd + vpbroadcastd m12, xm12 + vpcmpeqd m13, m12 ; y == vb_pos + pandn m13, m11 ; y != vb_pos + + vpbroadcastd m14, [dw3] + pblendvb m14, m14, [dd2], m13 ;ac + + pblendvb m3, m15, [gradq + sum_stride3q], m13 + + ;extent to dword to avoid overflow + vpunpcklwd m4, m0, m15 + vpunpckhwd m5, m0, m15 + vpunpcklwd m6, m1, m15 + vpunpckhwd m7, m1, m15 + vpunpcklwd m8, m2, m15 + vpunpckhwd m9, m2, m15 + vpunpcklwd m10, m3, m15 + vpunpckhwd m11, m3, m15 + + vpaddd m0, m4, m6 + vpaddd m1, m5, m7 + vpaddd m2, m8, m10 + vpaddd m3, m9, m11 + + ;sum of the first row + vpaddd m0, m2 ;low + vpaddd m1, m3 ;high + + lea gradq, [gradq + 2 * sum_strideq] + + pblendvb m10, m15, [gradq], m13 + + movu m11, [gradq + sum_strideq] + movu m12, [gradq + 2 * sum_strideq] + movu m13, [gradq + sum_stride3q] + + vpunpcklwd m4, m10, m15 + vpunpckhwd m5, m10, m15 + vpunpcklwd m6, m11, m15 + vpunpckhwd m7, m11, m15 + vpunpcklwd m8, m12, m15 + vpunpckhwd m9, m12, m15 + vpunpcklwd m10, m13, m15 + vpunpckhwd m11, m13, m15 + + vpaddd m2, m4, m6 + vpaddd m3, m5, m7 + vpaddd m4, m8, m10 + vpaddd m5, m9, m11 + + ;sum of the second row + vpaddd m2, m4 ;low + vpaddd m3, m5 ;high + + vpunpckldq m4, m0, m2 + vpunpckhdq m5, m0, m2 + vpunpckldq m6, m1, m3 + vpunpckhdq m7, m1, m3 + + ;each dword represent 4x2 alf blocks + ;the order is 01452367 + vpunpckldq m0, m4, m6 ;sum_v + vpunpckhdq m1, m4, m6 ;sum_h + vpunpckldq m2, m5, m7 ;sum_d0 + vpunpckhdq m3, m5, m7 ;sum_d1 + + vpcmpgtd m4, m0, m1 ;dir_hv - 1 + vpmaxsd m5, m0, m1 ;hv1 + vpminsd m6, m0, m1 ;hv0 + + vpaddd m0, m1; ;sum_hv + + vpcmpgtd m7, m2, m3 ;dir_d - 1 + vpmaxsd m8, m2, m3 ;d1 + vpminsd m9, m2, m3 ;d0 + + ;*transpose_idx = dir_d * 2 + dir_hv; + vpbroadcastd m10, [dw3] + vpaddd m11, m7, m7 + vpaddd m11, m4 + vpaddd m10, m11 + vpermq m10, m10, 11011000b + SAVE_CLASSIFY_PARAM transpose_idx, 10 + + vpsrlq m10, m8, 32 + vpsrlq m11, m6, 32 + pmuldq m12, m10, m11 ;d1 * hv0 high + vpsrlq m1, m9, 32 + vpsrlq m2, m5, 32 + pmuldq m3, m1, m2 ;d0 * hv1 high + vpcmpgtq m10, m12, m3 ;dir1 - 1 high + + pmuldq m1, m8, m6 ;d1 * hv0 low + pmuldq m2, m9, m5 ;d0 * hv1 low + vpcmpgtq m1, m2 ;dir1 - 1 low + + vpblendd m1, m1, m10, 0xaa ;dir1 - 1 + + pblendvb m2, m5, m8, m1 ;hvd1 + pblendvb m3, m6, m9, m1 ;hvd0 + + movd xm5, bit_depthd + vpbroadcastd m5, xm5 + + ;*class_idx = arg_var[av_clip_uintp2(sum_hv * ac >> (BIT_DEPTH - 1), 4)]; + vpmulld m0, m14 ;sum_hv * ac + vpsrlvd m0, m0, m5 + vpminsd m0, [dd15] + movu m6, [ARG_VAR_SHUFFE] + pshufb m6, m0 ;class_idx + + vpbroadcastd m10, [dw5] + + ;if (hvd1 * 2 > 9 * hvd0) + ; *class_idx += ((dir1 << 1) + 2) * 5; + ;else if (hvd1 > 2 * hvd0) + ; *class_idx += ((dir1 << 1) + 1) * 5; + paddd m7, m3, m3 + pcmpgtd m7, m2, m7 ;hvd1 > 2 * hvd0 + pand m7, m10 + paddd m6, m7 ;class_idx + + paddd m8, m2, m2 + vpslld m9, m3, 3 + paddd m9, m3 + pcmpgtd m8, m9 ;hvd1 * 2 > 9 * hvd0 + pand m8, m10 + paddd m6, m8 ;class_idx + + pandn m1, m7 + paddd m1, m1 ;dir1 << 1 + paddd m6, m1 ;class_idx + vpermq m6, m6, 11011000b + + SAVE_CLASSIFY_PARAM class_idx, 6 +%endmacro + +%macro ALF_CLASSIFY_16x8 0 +%define SAVE_CLASSIFY_PARAM SAVE_CLASSIFY_PARAM_W16 + ALF_CLASSIFY_H8 +%undef SAVE_CLASSIFY_PARAM +%endmacro + +%macro ALF_CLASSIFY_Wx8 0 +%define SAVE_CLASSIFY_PARAM SAVE_CLASSIFY_PARAM_W + ALF_CLASSIFY_H8 +%undef SAVE_CLASSIFY_PARAM +%endmacro + +%macro ALF_CLASSIFY 1 +;void ff_vvc_alf_classify(int *class_idx, int *transpose_idx, +; const int *gradient_sum, intptr_t width, intptr_t height, +; intptr_t vb_pos, int *gradient_tmp, intptr_t bit_depth); + +;pixel size +%define ps (%1 / 8) +ALF_CLASSIFY_GRAD %1 +cglobal vvc_alf_classify_%1bpc, 7, 15, 16, class_idx, transpose_idx, gradient_sum, width, height, vb_pos, bit_depth, \ + x, y, grad, sum_stride, sum_stride3, temp, w + + sub bit_depthq, 1 + + ;now we can use gradient to get class idx and transpose idx + lea sum_strideq, [widthd + ALF_GRADIENT_BORDER * 2] + add sum_strideq, 15 + and sum_strideq, ~15 ;align to 16 + add sum_strideq, sum_strideq ;two rows a time + + add gradient_sumq, 8 ;first 4 words are garbage + + lea sum_stride3q, [3 * sum_strideq] + + xor yd, yd + and vb_posd, ~7 ;floor align to 8 + pxor m15, m15 + +.loop_sum_h: + xor xd, xd + .loop_sum_w16: + lea wd, [widthd] + sub wd, xd + cmp wd, 16 + jl .loop_sum_w16_end + ALF_CLASSIFY_16x8 + add xd, 16 + jmp .loop_sum_w16 + .loop_sum_w16_end: + + cmp wd, 0 + je .loop_sum_w_end + ALF_CLASSIFY_Wx8 + .loop_sum_w_end: + + lea gradient_sumq, [gradient_sumq + 4 * sum_strideq] + lea transpose_idxq, [transpose_idxq + 2 * widthq] + lea class_idxq, [class_idxq + 2 * widthq] + + add yd, 8 + cmp yd, heightd + jl .loop_sum_h + + RET +%endmacro + +INIT_YMM avx2 +ALF_FILTER 16 +ALF_FILTER 8 +ALF_CLASSIFY 16 +ALF_CLASSIFY 8 +%endif diff --git a/libavcodec/x86/vvc_mc.asm b/libavcodec/x86/vvc_mc.asm new file mode 100644 index 00000000000..5e82ce6cde9 --- /dev/null +++ b/libavcodec/x86/vvc_mc.asm @@ -0,0 +1,1963 @@ +; /* +; * Provide SIMD MC functions for VVC decoding +; * +; * Copyright (c) 2023 Wu Jianhua +; * +; * 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 "libavutil/x86/x86util.asm" + +SECTION_RODATA 64 + +%macro VVC_LUMA_FILTERS 1 +p%1_vvc_luma_filters: +%assign %%i 0 +%rep 2 + d%1 0, 0, 0, 64, 0, 0, 0, 0, + d%1 0, 1, -3, 63, 4, -2, 1, 0, + d%1 -1, 2, -5, 62, 8, -3, 1, 0, + d%1 -1, 3, -8, 60, 13, -4, 1, 0, + d%1 -1, 4, -10, 58, 17, -5, 1, 0, + d%1 -1, 4, -11, 52, 26, -8, 3, -1, + d%1 -1, 3, -9, 47, 31, -10, 4, -1, + d%1 -1, 4, -11, 45, 34, -10, 4, -1, +%if %%i == 0 + d%1 -1, 4, -11, 40, 40, -11, 4, -1, +%else + d%1 0, 3, 9, 20, 20, 9, 3, 0, +%endif + d%1 -1, 4, -10, 34, 45, -11, 4, -1, + d%1 -1, 4, -10, 31, 47, -9, 3, -1, + d%1 -1, 3, -8, 26, 52, -11, 4, -1, + d%1 0, 1, -5, 17, 58, -10, 4, -1, + d%1 0, 1, -4, 13, 60, -8, 3, -1, + d%1 0, 1, -3, 8, 62, -5, 2, -1, + d%1 0, 1, -2, 4, 63, -3, 1, 0, +%assign %%i %%i+1 +%endrep + + ; affine, Table 30 + d%1 0, 0, 0, 64, 0, 0, 0, 0, + d%1 0, 1, -3, 63, 4, -2, 1, 0, + d%1 0, 1, -5, 62, 8, -3, 1, 0, + d%1 0, 2, -8, 60, 13, -4, 1, 0, + d%1 0, 3, -10, 58, 17, -5, 1, 0, + d%1 0, 3, -11, 52, 26, -8, 2, 0, + d%1 0, 2, -9, 47, 31, -10, 3, 0, + d%1 0, 3, -11, 45, 34, -10, 3, 0, + d%1 0, 3, -11, 40, 40, -11, 3, 0, + d%1 0, 3, -10, 34, 45, -11, 3, 0, + d%1 0, 3, -10, 31, 47, -9, 2, 0, + d%1 0, 2, -8, 26, 52, -11, 3, 0, + d%1 0, 1, -5, 17, 58, -10, 3, 0, + d%1 0, 1, -4, 13, 60, -8, 2, 0, + d%1 0, 1, -3, 8, 62, -5, 1, 0, + d%1 0, 1, -2, 4, 63, -3, 1, 0 +%endmacro + +VVC_LUMA_FILTERS w +VVC_LUMA_FILTERS b + +pw_vvc_iter_shuffle_index dw 0, 1, + dw 1, 2, + dw 2, 3, + dw 3, 4, + dw 4, 5, + dw 5, 6, + dw 6, 7, + dw 7, 8, + dw 8, 9, + dw 9, 10, + dw 10, 11, + dw 11, 12, + dw 12, 13, + dw 13, 14, + dw 14, 15, + dw 15, 16 + + dw 2, 3, + dw 3, 4, + dw 4, 5, + dw 5, 6, + dw 6, 7, + dw 7, 8, + dw 8, 9, + dw 9, 10, + dw 10, 11, + dw 11, 12, + dw 12, 13, + dw 13, 14, + dw 14, 15, + dw 15, 16, + dw 16, 17, + dw 17, 18 + + dw 4, 5, + dw 5, 6, + dw 6, 7, + dw 7, 8, + dw 8, 9, + dw 9, 10, + dw 10, 11, + dw 11, 12, + dw 12, 13, + dw 13, 14, + dw 14, 15, + dw 15, 16, + dw 16, 17, + dw 17, 18, + dw 18, 19, + dw 19, 20 + + dw 6, 7, + dw 7, 8, + dw 8, 9, + dw 9, 10, + dw 10, 11, + dw 11, 12, + dw 12, 13, + dw 13, 14, + dw 14, 15, + dw 15, 16, + dw 16, 17, + dw 17, 18, + dw 18, 19, + dw 19, 20, + dw 20, 21, + dw 21, 22 + +pw_vvc_iter_shuffle_index_half dw 0, 1, + dw 1, 2, + dw 2, 3, + dw 3, 4, + dw 4, 5, + dw 5, 6, + dw 6, 7, + dw 7, 8, + dw 16, 17, + dw 17, 18, + dw 18, 19, + dw 19, 20, + dw 20, 21, + dw 21, 22, + dw 22, 23, + dw 23, 24, + + dw 2, 3, + dw 3, 4, + dw 4, 5, + dw 5, 6, + dw 6, 7, + dw 7, 8, + dw 8, 9, + dw 9, 10, + dw 18, 19, + dw 19, 20, + dw 20, 21, + dw 21, 22, + dw 22, 23, + dw 23, 24, + dw 24, 25, + dw 25, 26, + + dw 4, 5, + dw 5, 6, + dw 6, 7, + dw 7, 8, + dw 8, 9, + dw 9, 10, + dw 10, 11, + dw 11, 12, + dw 20, 21, + dw 21, 22, + dw 22, 23, + dw 23, 24, + dw 24, 25, + dw 25, 26, + dw 26, 27, + dw 27, 28, + + dw 6, 7, + dw 7, 8, + dw 8, 9, + dw 9, 10, + dw 10, 11, + dw 11, 12, + dw 12, 13, + dw 13, 14, + dw 22, 23, + dw 23, 24, + dw 24, 25, + dw 25, 26, + dw 26, 27, + dw 27, 28, + dw 28, 29, + dw 29, 30 + + +pw_vvc_iter_shuffle_index_quarter times 2 dw 0, 1, 1, 2, 2, 3, 3, 4, 16, 17, 17, 18, 18, 19, 19, 20, + times 2 dw 2, 3, 3, 4, 4, 5, 5, 6, 18, 19, 19, 20, 20, 21, 21, 22, + times 2 dw 4, 5, 5, 6, 6, 7, 7, 8, 20, 21, 21, 22, 22, 23, 23, 24, + times 2 dw 6, 7, 7, 8, 8, 9, 9, 10, 22, 23, 23, 24, 24, 25, 25, 26, + +pq_vvc_iter_shuffle_index dq 0, 1, 4, 5, 2, 3, 6, 7 + +pb_vvc_iter_shuffle_index_w db 0, 1, 2, 3, 2, 3, 4, 5, 4, 5, 6, 7, 6, 7, 8, 9 + db 4, 5, 6, 7, 6, 7, 8, 9, 8, 9, 10, 11, 10, 11, 12, 13 + +pb_vvc_iter_shuffle_index_b db 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6 + db 4, 5, 6, 7, 5, 6, 7, 8, 6, 7, 8, 9, 7, 8, 9, 10 + db 8, 9, 10, 11, 9, 10, 11, 12, 10, 11, 12, 13, 11, 12, 13, 14 + +pb_vvc_transpose_4x4b_index db 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15 + +SECTION .text + +%define MAX_PB_SIZE 128*2 + +%macro MOVSXDIFNIDN 1 + movsxdifnidn heightq, heightd + movsxdifnidn widthq, widthd + movsxdifnidn hf_idxq, hf_idxd + movsxdifnidn vf_idxq, vf_idxd +%if %1 > 8 + movsxdifnidn bitdepthq, bitdepthd +%endif +%endmacro + +%macro PRE_CAL_INDEX 3 + sal %1q, 4 + lea %1q, [%1q + %2q] + sal %1q, %3 +%endmacro + +%macro LOAD_FILTER 4-6 + lea r3srcq, [p%1_vvc_luma_filters] + vpbroadcastd m%3, [r3srcq + %2q + 0 * 4] + vpbroadcastd m%4, [r3srcq + %2q + 1 * 4] +%if %0 == 6 + vpbroadcastd m%5, [r3srcq + %2q + 2 * 4] + vpbroadcastd m%6, [r3srcq + %2q + 3 * 4] +%endif +%endmacro + +%macro LOAD_FILTER_16 6 + PRE_CAL_INDEX %1, %2, 4 + LOAD_FILTER w, %1, %3, %4, %5, %6 +%endmacro + +%macro LOAD_FILTER_8 4 + PRE_CAL_INDEX %1, %2, 3 + LOAD_FILTER b, %1, %3, %4 +%endmacro + +%macro LOAD_SHUFFLE_16 5 + mova m%2, [%1 + 0 * 64] + mova m%3, [%1 + 1 * 64] + mova m%4, [%1 + 2 * 64] + mova m%5, [%1 + 3 * 64] +%endmacro + +%macro H_COMPUTE_8 6 + vpermw m16, m0, m%1 + vpermw m17, m1, m%1 + vpermw m18, m2, m%1 + vpermw m19, m3, m%1 + vpxor m%1, m%1, m%1 + vpxor m20, m20, m20 + vpdpwssd m%1, m16, m%2 + vpdpwssd m20, m17, m%3 + vpdpwssd m%1, m18, m%4 + vpdpwssd m20, m19, m%5 + vpaddd m%1, m%1, m20 + vpsrad m%1, %6 +%endmacro + +%macro H_COMPUTE_H8_16 1 +H_COMPUTE_8 %1, 4, 5, 6, 7, xm28 +%endmacro + +%macro H_COMPUTE_V8_10 1 +H_COMPUTE_8 %1, 24, 25, 26, 27, 6 +%endmacro + +%macro H_COMPUTE_H4 7 + vpermw m17, m0, m%2 + vpermw m16, m0, m%1 + vinserti64x4 m16, m16, ym17, 1 + + vpermw m18, m1, m%2 + vpermw m17, m1, m%1 + vinserti64x4 m17, m17, ym18, 1 + + vpermw m19, m2, m%2 + vpermw m18, m2, m%1 + vinserti64x4 m18, m18, ym19, 1 + + vpermw m20, m3, m%2 + vpermw m19, m3, m%1 + vinserti64x4 m19, m19, ym20, 1 + + vpxor m%1, m%1, m%1 + vpxor m%2, m%2, m%2 + vpdpwssd m%1, m16, m%3 + vpdpwssd m%2, m17, m%4 + vpdpwssd m%1, m18, m%5 + vpdpwssd m%2, m19, m%6 + + vpaddd m%1, m%1, m%2 + vpsrad m%1, %7 +%endmacro + +%macro H_COMPUTE_H4_16 2 +H_COMPUTE_H4 %1, %2, 4, 5, 6, 7, xm28 +%endmacro + +%macro H_COMPUTE_V4_16 2 +H_COMPUTE_H4 %1, %2, 24, 25, 26, 27, 6 +%endmacro + +%macro LOOP_END 2 +%assign %%s %1/8 + lea _srcq, [_srcq + %%s * %2] + lea dstq, [ dstq + 2 * %2] + sub xq, %2 + jnz .loop_v%2 + + lea r3srcq, [widthq * 2] + lea _srcq, [_srcq + srcstrideq * %2] +%if %1 == 16 + sub _srcq, r3srcq +%else + sub _srcq, widthq +%endif + lea dstq, [dstq + MAX_PB_SIZE * %2] + sub dstq, r3srcq + sub heightq, %2 + jnz .loop_h%2 +%endmacro + +; +; void ff_vvc_put_vvc_luma_hv(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, +; const int height, const intptr_t mx, const intptr_t my, const int width, +; const int hf_idx, const int vf_idx); +; +%macro VVC_PUT_VVC_LUMA_HV_AVX512ICL 1 +cglobal vvc_put_vvc_luma_hv_%1, 10, 13, 32, dst, src, srcstride, height, mx, my, width, hf_idx, vf_idx, bitdepth, r3src, _src, x + MOVSXDIFNIDN %1 + LOAD_FILTER_16 hf_idx, mx, 4, 5, 6, 7 + LOAD_FILTER_16 vf_idx, my, 24, 25, 26, 27 + + sub bitdepthq, 8 + movq xm28, bitdepthq + mov _srcq, srcq + + cmp heightq, 4 + je .hv4 + cmp widthq, 4 + je .hv4 + +.hv8: + mova m23, [pq_vvc_iter_shuffle_index] + LOAD_SHUFFLE_16 pw_vvc_iter_shuffle_index_half, 0, 1, 2, 3 + +.loop_h8: + mov xq, widthq +.loop_v8: + mov srcq, _srcq + lea r3srcq, [srcstrideq * 3] + sub srcq, 6 + sub srcq, r3srcq + movu ym8, [srcq ] + movu ym9, [srcq + srcstrideq * 1] + movu ym10, [srcq + srcstrideq * 2] + movu ym11, [srcq + r3srcq ] + + movu ym12, [srcq + srcstrideq * 4] + + lea srcq, [srcq + r3srcq ] + movu ym13, [srcq + srcstrideq * 2] + + lea srcq, [srcq + r3srcq ] + movu ym14, [srcq ] + movu ym15, [srcq + srcstrideq * 1] + + ; only vinserti64x4: 20318 + ; mov and vinserti64x4: 17377 + vpxor m22, m22, m22 + movu ym16, [srcq + srcstrideq * 2] + movu ym17, [srcq + r3srcq ] + movu ym18, [srcq + srcstrideq * 4] + lea srcq, [srcq + r3srcq ] + movu ym19, [srcq + srcstrideq * 2] + lea srcq, [srcq + r3srcq ] + movu ym20, [srcq ] + movu ym21, [srcq + srcstrideq * 1] + movu ym22, [srcq + srcstrideq * 2] + vinserti64x4 m8, m8, ym16, 1 + vinserti64x4 m9, m9, ym17, 1 + vinserti64x4 m10, m10, ym18, 1 + vinserti64x4 m11, m11, ym19, 1 + vinserti64x4 m12, m12, ym20, 1 + vinserti64x4 m13, m13, ym21, 1 + vinserti64x4 m14, m14, ym22, 1 + +%assign %%i 8 +%rep 8 + H_COMPUTE_H8_16 %%i +%assign %%i %%i+1 +%endrep + + vpackssdw m8, m8, m12 + vpackssdw m9, m9, m13 + vpackssdw m10, m10, m14 + vpackssdw m11, m11, m15 + + vpunpcklwd m12, m8, m9 + vpunpckhwd m13, m8, m9 + vpunpcklwd m14, m10, m11 + vpunpckhwd m15, m10, m11 + + vpunpckldq m8, m12, m14 + vpunpckhdq m9, m12, m14 + vpunpckldq m10, m13, m15 + vpunpckhdq m11, m13, m15 + + vpunpcklqdq m12, m8, m10 + vpunpckhqdq m13, m8, m10 + vpunpcklqdq m14, m9, m11 + vpunpckhqdq m15, m9, m11 + + vpermq m8, m23, m12 + vpermq m9, m23, m13 + vpermq m10, m23, m14 + vpermq m11, m23, m15 + +%assign %%i 8 +%rep 4 + H_COMPUTE_V8_10 %%i +%assign %%i %%i+1 +%endrep + + vpackssdw m8, m8, m10 + vpackssdw m9, m9, m11 + + vpunpcklwd m10, m8, m9 + vpunpckhwd m11, m8, m9 + vpunpckldq m8, m10, m11 + vpunpckhdq m9, m10, m11 + + vextracti64x4 ym10, m8, 1 + vextracti64x4 ym11, m9, 1 + + vpunpcklqdq ym12, ym8, ym10 + vpunpckhqdq ym13, ym8, ym10 + vpunpcklqdq ym14, ym9, ym11 + vpunpckhqdq ym15, ym9, ym11 + + movu [dstq + MAX_PB_SIZE * 0], xm12 + movu [dstq + MAX_PB_SIZE * 1], xm13 + movu [dstq + MAX_PB_SIZE * 2], xm14 + movu [dstq + MAX_PB_SIZE * 3], xm15 + vextracti32x4 [dstq + MAX_PB_SIZE * 4], ym12, 1 + vextracti32x4 [dstq + MAX_PB_SIZE * 5], ym13, 1 + vextracti32x4 [dstq + MAX_PB_SIZE * 6], ym14, 1 + vextracti32x4 [dstq + MAX_PB_SIZE * 7], ym15, 1 + + LOOP_END %1, 8 + + RET + +.hv4: + LOAD_SHUFFLE_16 pw_vvc_iter_shuffle_index_quarter, 0, 1, 2, 3 + +.loop_h4: + mov xq, widthq +.loop_v4: + mov srcq, _srcq + lea r3srcq, [srcstrideq * 3] + sub srcq, 6 + sub srcq, r3srcq + movu ym8, [srcq ] + movu ym9, [srcq + srcstrideq * 1] + movu ym10, [srcq + srcstrideq * 2] + movu ym11, [srcq + r3srcq ] + + movu ym12, [srcq + srcstrideq * 4] + + lea srcq, [srcq + r3srcq] + movu ym13, [srcq + srcstrideq * 2] + + lea srcq, [srcq + r3srcq] + movu ym14, [srcq ] + movu ym15, [srcq + srcstrideq * 1] + vinserti64x4 m8, m8, ym12, 1 + vinserti64x4 m9, m9, ym13, 1 + vinserti64x4 m10, m10, ym14, 1 + vinserti64x4 m11, m11, ym15, 1 + vpxor m13, m13, m13 + movu ym12, [srcq + srcstrideq * 2] + movu ym13, [srcq + r3srcq ] + movu ym14, [srcq + srcstrideq * 4] + vinserti64x4 m12, m12, ym14, 1 + + H_COMPUTE_H4_16 8, 9 + H_COMPUTE_H4_16 10, 11 + H_COMPUTE_H4_16 12, 13 + + vpackssdw m8, m8, m10 + vpmovdw ym12, m12 + + vextracti64x4 ym9, m8, 1 + vpunpcklwd ym10, ym8, ym9 + vpunpckhwd ym11, ym8, ym9 + vpunpckldq ym8, ym10, ym11 + vpunpckhdq ym9, ym10, ym11 + + vextracti32x4 xm13, ym12, 1 + vpunpcklwd xm10, xm12, xm13 + vpunpckhwd xm11, xm12, xm13 + vpunpckldq xm12, xm10, xm11 + vpunpckhdq xm13, xm10, xm11 + + vextracti64x2 xm10, ym8, 1 + vextracti64x2 xm11, ym9, 1 + vinserti32x4 ym8, xm12, 1 + vinserti32x4 ym9, xm13, 1 + vpunpcklqdq ym14, ym8, ym10 + vpunpckhqdq ym8, ym8, ym10 + vpunpcklqdq ym10, ym9, ym11 + vpunpckhqdq ym9, ym9, ym11 + + vinserti64x4 m9, m8, ym9, 1 + vinserti64x4 m8, m14, ym10, 1 + + H_COMPUTE_V4_16 8, 9 + vpmovdw ym8, m8 + vextracti64x2 xm9, ym8, 1 + vpunpcklwd xm10, xm8, xm9 + vpunpckhwd xm11, xm8, xm9 + vpunpckldq xm8, xm10, xm11 + vpunpckhdq xm9, xm10, xm11 + + psrldq xm10, xm8, 8 + psrldq xm11, xm9, 8 + + movq [dstq + MAX_PB_SIZE * 0], xm8 + movq [dstq + MAX_PB_SIZE * 1], xm10 + movq [dstq + MAX_PB_SIZE * 2], xm9 + movq [dstq + MAX_PB_SIZE * 3], xm11 + + LOOP_END %1, 4 + + RET +%endmacro + +%macro H_COMPUTE_H8_16_LOOP 2 +%assign %%i %1 +%rep %2 + H_COMPUTE_H8_16 %%i + %assign %%i %%i+1 +%endrep +%endmacro + +%macro STORE_LOOP 6 +%assign %%i 0 +%rep %2 + %assign %%j 0 + %rep %3 + %assign %%k (%1 + %%i * %3 + %%j) + %4 [dstq + %%i * MAX_PB_SIZE + %%j * %6], %5 %+ %%k + %assign %%j %%j+1 + %endrep + %assign %%i %%i+1 +%endrep +%endmacro + +%macro STORE4_LOOP 3 +STORE_LOOP %1, %2, %3, movq, xm, mmsize +%endmacro + +%macro STORE8_LOOP 3 +STORE_LOOP %1, %2, %3, movu, xm, mmsize +%endmacro + +%macro STORE16_LOOP 3 +STORE_LOOP %1, %2, %3, vpmovdw, m, mmsize/2 +%endmacro + +%macro H_LOOP_END 2 + lea srcq, [srcq + srcstrideq * %2] + lea dstq, [dstq + MAX_PB_SIZE * %2] + sub heightq, %2 + jnz .h%1_loop +%endmacro + +%macro VVC_PUT_VVC_LUMA_H_AVX512ICL 1 +cglobal vvc_put_vvc_luma_h_%1, 10, 13, 32, dst, src, srcstride, height, mx, my, width, hf_idx, vf_idx, bitdepth, r3src, _src, x + MOVSXDIFNIDN %1 + LOAD_FILTER_16 hf_idx, mx, 4, 5, 6, 7 + + sub bitdepthq, 8 + movq xm28, bitdepthq + mov _srcq, srcq + lea r3srcq, [srcstrideq * 3] + sub srcq, 6 + + cmp widthq, 4 + jne .h8 + +.h4: + LOAD_SHUFFLE_16 pw_vvc_iter_shuffle_index_quarter, 0, 1, 2, 3 + +.h4_loop: + movu ym8, [srcq ] + movu ym9, [srcq + srcstrideq * 1] + vinserti64x4 m8, [srcq + srcstrideq * 2], 1 + vinserti64x4 m9, [srcq + r3srcq ], 1 + + H_COMPUTE_H4_16 8, 9 + vpmovdw ym8, m8 + vextracti64x2 xm9, ym8, 1 + + movq [dstq + MAX_PB_SIZE * 0], xm8 + movq [dstq + MAX_PB_SIZE * 1], xm9 + psrldq xm8, xm8, 8 + psrldq xm9, xm9, 8 + movq [dstq + MAX_PB_SIZE * 2], xm8 + movq [dstq + MAX_PB_SIZE * 3], xm9 + + H_LOOP_END 4, 4 + + RET + +.h8: + cmp widthq, 8 + jne .h16 + LOAD_SHUFFLE_16 pw_vvc_iter_shuffle_index_half, 0, 1, 2, 3 + +.h8_loop: + movu ym8, [srcq ] + movu ym9, [srcq + srcstrideq * 1] + movu ym10, [srcq + srcstrideq * 2] + movu ym11, [srcq + r3srcq ] + vinserti64x4 m8, ym10, 1 + vinserti64x4 m9, ym11, 1 + + H_COMPUTE_H8_16_LOOP 8, 2 + + vpmovdw ym8, m8 + vpmovdw ym9, m9 + + movu [dstq + MAX_PB_SIZE * 0], xm8 + movu [dstq + MAX_PB_SIZE * 1], xm9 + vextracti64x2 [dstq + MAX_PB_SIZE * 2], ym8, 1 + vextracti64x2 [dstq + MAX_PB_SIZE * 3], ym9, 1 + + H_LOOP_END 8, 4 + + RET + +.h16: + LOAD_SHUFFLE_16 pw_vvc_iter_shuffle_index, 0, 1, 2, 3 + + cmp widthq, 32 + je .h32_loop + + cmp widthq, 64 + je .h64_loop + + cmp widthq, 128 + je .h128_loop + +.h16_loop: + movu m8, [srcq ] + movu m9, [srcq + srcstrideq * 1] + movu m10, [srcq + srcstrideq * 2] + movu m11, [srcq + r3srcq ] + + H_COMPUTE_H8_16_LOOP 8, 4 + STORE16_LOOP 8, 4, 1 + H_LOOP_END 16, 4 + + RET + +.h32_loop: + movu m8, [srcq ] + movu m9, [srcq + 32 ] + movu m10, [srcq + srcstrideq * 1 ] + movu m11, [srcq + srcstrideq * 1 + 32] + + movu m12, [srcq + srcstrideq * 2 ] + movu m13, [srcq + srcstrideq * 2 + 32] + movu m14, [srcq + r3srcq ] + movu m15, [srcq + r3srcq + 32] + + H_COMPUTE_H8_16_LOOP 8, 8 + STORE16_LOOP 8, 4, 2 + + H_LOOP_END 32, 4 + + RET + +.h64_loop: + movu m8, [srcq ] + movu m9, [srcq + 32 ] + movu m10, [srcq + 64 ] + movu m11, [srcq + 96 ] + movu m12, [srcq + srcstrideq * 1 ] + movu m13, [srcq + srcstrideq * 1 + 32 ] + movu m14, [srcq + srcstrideq * 1 + 64 ] + movu m15, [srcq + srcstrideq * 1 + 96 ] + + H_COMPUTE_H8_16_LOOP 8, 8 + STORE16_LOOP 8, 2, 4 + + H_LOOP_END 64, 2 + + RET + +.h128_loop: + movu m8, [srcq ] + movu m9, [srcq + 32] + movu m10, [srcq + 64] + movu m11, [srcq + 96] + movu m12, [srcq + 128] + movu m13, [srcq + 160] + movu m14, [srcq + 192] + movu m15, [srcq + 224] + + H_COMPUTE_H8_16_LOOP 8, 8 + STORE16_LOOP 8, 1, 8 + + H_LOOP_END 128, 1 + + RET + +%endmacro + +%macro VVC_PUT_VVC_LUMA_V_AVX512ICL 1 +cglobal vvc_put_vvc_luma_v_%1, 10, 13, 32, dst, src, srcstride, height, mx, my, width, hf_idx, vf_idx, bitdepth, r3src, _src, x + MOVSXDIFNIDN %1 + LOAD_FILTER_16 vf_idx, my, 4, 5, 6, 7 + + sub bitdepthq, 8 + movq xm28, bitdepthq + mov _srcq, srcq + mov _srcq, srcq + + cmp heightq, 4 + je .hv4 + cmp widthq, 4 + je .hv4 + +.hv8: + mova m23, [pq_vvc_iter_shuffle_index] + LOAD_SHUFFLE_16 pw_vvc_iter_shuffle_index_half, 0, 1, 2, 3 + +.loop_h8: + mov xq, widthq +.loop_v8: + mov srcq, _srcq + lea r3srcq, [srcstrideq * 3] + sub srcq, r3srcq + movu xm8, [srcq ] + movu xm9, [srcq + srcstrideq * 1] + movu xm10, [srcq + srcstrideq * 2] + movu xm11, [srcq + r3srcq ] + + movu xm12, [srcq + srcstrideq * 4] + + lea srcq, [srcq + r3srcq ] + movu xm13, [srcq + srcstrideq * 2] + + lea srcq, [srcq + r3srcq ] + movu xm14, [srcq ] + movu xm15, [srcq + srcstrideq * 1] + + vpxor m22, m22, m22 + movu xm16, [srcq + srcstrideq * 2] + movu xm17, [srcq + r3srcq ] + movu xm18, [srcq + srcstrideq * 4] + lea srcq, [srcq + r3srcq ] + movu xm19, [srcq + srcstrideq * 2] + lea srcq, [srcq + r3srcq ] + movu xm20, [srcq ] + movu xm21, [srcq + srcstrideq * 1] + movu xm22, [srcq + srcstrideq * 2] + + vinserti64x2 ym8, ym8, xm16, 1 + vinserti64x2 ym9, ym9, xm17, 1 + vinserti64x2 ym10, ym10, xm18, 1 + vinserti64x2 ym11, ym11, xm19, 1 + vinserti64x2 ym12, ym12, xm20, 1 + vinserti64x2 ym13, ym13, xm21, 1 + vinserti64x2 ym14, ym14, xm22, 1 + + vpunpcklwd ym16, ym8, ym9 + vpunpckhwd ym17, ym8, ym9 + vpunpcklwd ym18, ym10, ym11 + vpunpckhwd ym19, ym10, ym11 + vpunpcklwd ym8, ym12, ym13 + vpunpckhwd ym9, ym12, ym13 + vpunpcklwd ym10, ym14, ym15 + vpunpckhwd ym11, ym14, ym15 + + vpunpckldq ym12, ym16, ym18 + vpunpckhdq ym13, ym16, ym18 + vpunpckldq ym14, ym17, ym19 + vpunpckhdq ym15, ym17, ym19 + vpunpckldq ym16, ym8, ym10 + vpunpckhdq ym17, ym8, ym10 + vpunpckldq ym18, ym9, ym11 + vpunpckhdq ym19, ym9, ym11 + + vpunpcklqdq ym8, ym12, ym16 + vpunpckhqdq ym9, ym12, ym16 + vpunpcklqdq ym10, ym13, ym17 + vpunpckhqdq ym11, ym13, ym17 + vpunpcklqdq ym12, ym14, ym18 + vpunpckhqdq ym13, ym14, ym18 + vpunpcklqdq ym14, ym15, ym19 + vpunpckhqdq ym15, ym15, ym19 + + vinserti64x4 m8, m8, ym12, 1 + vinserti64x4 m9, m9, ym13, 1 + vinserti64x4 m10, m10, ym14, 1 + vinserti64x4 m11, m11, ym15, 1 + +%assign %%i 8 +%rep 4 + H_COMPUTE_H8_16 %%i +%assign %%i %%i+1 +%endrep + + vpackssdw m8, m8, m10 + vpackssdw m9, m9, m11 + + vpunpcklwd m10, m8, m9 + vpunpckhwd m11, m8, m9 + vpunpckldq m8, m10, m11 + vpunpckhdq m9, m10, m11 + + vextracti64x4 ym10, m8, 1 + vextracti64x4 ym11, m9, 1 + + vpunpcklqdq ym12, ym8, ym10 + vpunpckhqdq ym13, ym8, ym10 + vpunpcklqdq ym14, ym9, ym11 + vpunpckhqdq ym15, ym9, ym11 + + movu [dstq + MAX_PB_SIZE * 0], xm12 + movu [dstq + MAX_PB_SIZE * 1], xm13 + movu [dstq + MAX_PB_SIZE * 2], xm14 + movu [dstq + MAX_PB_SIZE * 3], xm15 + vextracti32x4 [dstq + MAX_PB_SIZE * 4], ym12, 1 + vextracti32x4 [dstq + MAX_PB_SIZE * 5], ym13, 1 + vextracti32x4 [dstq + MAX_PB_SIZE * 6], ym14, 1 + vextracti32x4 [dstq + MAX_PB_SIZE * 7], ym15, 1 + + LOOP_END %1, 8 + + RET + +.hv4: + LOAD_SHUFFLE_16 pw_vvc_iter_shuffle_index_quarter, 0, 1, 2, 3 + +.loop_h4: + mov xq, widthq +.loop_v4: + mov srcq, _srcq + lea r3srcq, [srcstrideq * 3] + sub srcq, r3srcq + vpxor ym11, ym11, ym11 + movu xm8, [srcq ] + movu xm9, [srcq + srcstrideq * 1] + movu xm10, [srcq + srcstrideq * 2] + movu xm11, [srcq + r3srcq ] + + movu xm12, [srcq + srcstrideq * 4] + + lea srcq, [srcq + r3srcq ] + movu xm13, [srcq + srcstrideq * 2] + + lea srcq, [srcq + r3srcq ] + movu xm14, [srcq ] + movu xm15, [srcq + srcstrideq * 1] + + movu xm16, [srcq + srcstrideq * 2] + movu xm17, [srcq + r3srcq ] + movu xm18, [srcq + srcstrideq * 4] + vinserti64x2 ym8, ym8, xm16, 1 + vinserti64x2 ym9, ym9, xm17, 1 + vinserti64x2 ym10, ym10, xm18, 1 + + vpunpcklwd ym8, ym8, ym9 + vpunpcklwd ym10, ym10, ym11 + vpunpcklwd ym12, ym12, ym13 + vpunpcklwd ym14, ym14, ym15 + + vpunpckldq ym9, ym8, ym10 + vpunpckhdq ym11, ym8, ym10 + vpunpckldq ym10, ym12, ym14 + vpunpckhdq ym12, ym12, ym14 + + vpunpcklqdq ym8, ym9, ym10 + vpunpckhqdq ym9, ym9, ym10 + vpunpcklqdq ym10, ym11, ym12 + vpunpckhqdq ym11, ym11, ym12 + + vinserti64x4 m8, m8, ym10, 1 + vinserti64x4 m9, m9, ym11, 1 + + H_COMPUTE_H4_16 8, 9 + vpmovdw ym8, m8 + vextracti64x2 xm9, ym8, 1 + vpunpcklwd xm10, xm8, xm9 + vpunpckhwd xm11, xm8, xm9 + vpunpckldq xm8, xm10, xm11 + vpunpckhdq xm9, xm10, xm11 + + psrldq xm10, xm8, 8 + psrldq xm11, xm9, 8 + + movq [dstq + MAX_PB_SIZE * 0], xm8 + movq [dstq + MAX_PB_SIZE * 1], xm10 + movq [dstq + MAX_PB_SIZE * 2], xm9 + movq [dstq + MAX_PB_SIZE * 3], xm11 + + LOOP_END %1, 4 + + RET +%endmacro + +%macro VINSERTI128 3 + vinserti128 %1, %1, %2, %3 +%endmacro + +%macro H_COMPUTE_16_AVX2 6 + vpshufb m%4, m%1, m5 + vpshufb m%1, m4 + vpmaddwd m%5, m1, m%4 + vpmaddwd m%1, m0 + vpshufb m%2, m5 + vshufpd m%4, m%4, m%2, 0x05 + vpaddd m%1, m%5 + vpmaddwd m%5, m3, m%2 + vpaddd m%1, m%5 + vpmaddwd m%5, m2, m%4 + vpshufb m%3, m5 + vpmaddwd m%4, m0 + vpaddd m%1, m%5 + vpmaddwd m%5, m1, m%2 + vshufpd m%2, m%2, m%3, 0x05 + vpmaddwd m%3, m3 + vpmaddwd m%2, m2 + vpaddd m%4, m%5 + vpaddd m%3, m%4 + vpaddd m%2, m%3 + vpsrad m%1, %6 + vpsrad m%2, %6 + vpackssdw m%1, m%2 +%endmacro + +%macro H_COMPUTE_H8_16_AVX2 5 + H_COMPUTE_16_AVX2 %1, %2, %3, %4, %5, xm6 +%endmacro + +%macro H_LOAD_COMPUTE_H8_16 6 + movu m%1, [srcq + srcstrideq * 0 + 0] + VINSERTI128 m%1, [srcq + srcstrideq * %6 + 0], 1 + movu xm%3, [srcq + srcstrideq * 0 + 16] + VINSERTI128 m%3, [srcq + srcstrideq * %6 + 16], 1 + shufpd m%2, m%1, m%3, 0x05 + H_COMPUTE_H8_16_AVX2 %1, %2, %3, %4, %5 + lea srcq, [srcq + srcstrideq] +%endmacro + +%macro H_LOAD_COMPUTE_V8_16 5-6 + vperm2i128 m%3, m%1, m%2, 0x31 + VINSERTI128 m%1, xm%2, 1 + shufpd m%2, m%1, m%3, 0x05 +%if %0 == 6 + H_COMPUTE_16_AVX2 %1, %2, %3, %4, %5, %6 +%else + H_COMPUTE_16_AVX2 %1, %2, %3, %4, %5, 6 +%endif +%endmacro + +%macro PUSH_MM 1 + mova [rsp + mm_stack_offset], m%1 + %assign mm_stack_offset mm_stack_offset+mmsize +%endmacro + +%macro POP_MM 1 + %assign mm_stack_offset mm_stack_offset-mmsize + mova m%1, [rsp + mm_stack_offset] +%endmacro + +%macro VVC_PUT_VVC_LUMA_HV_AVX2 1 +cglobal vvc_put_vvc_luma_hv_%1, 10, 13, 16, 0-mmsize*3, dst, src, srcstride, height, mx, my, width, hf_idx, vf_idx, bitdepth, r3src, _src, x +%assign mm_stack_offset 0 + MOVSXDIFNIDN %1 + + PRE_CAL_INDEX hf_idx, mx, 4 + PRE_CAL_INDEX vf_idx, my, 4 + + vbroadcasti128 m4, [pb_vvc_iter_shuffle_index_w + 0 * 16] + vbroadcasti128 m5, [pb_vvc_iter_shuffle_index_w + 1 * 16] + + sub bitdepthq, 8 + movq xm6, bitdepthq + + mov _srcq, srcq + lea r3srcq, [srcstrideq * 3 ] + neg r3srcq + lea _srcq, [srcq + r3srcq - 6] + + cmp heightd, 4 + je .hv4 + cmp widthd, 4 + je .hv4 + +.hv8: +.loop_h8: + mov xq, widthq +.loop_v8: + mov srcq, _srcq + LOAD_FILTER w, hf_idx, 0, 1, 2, 3 + H_LOAD_COMPUTE_H8_16 7, 8, 9, 10, 11, 8 + H_LOAD_COMPUTE_H8_16 8, 9, 10, 11, 12, 8 + H_LOAD_COMPUTE_H8_16 9, 10, 11, 12, 13, 8 + H_LOAD_COMPUTE_H8_16 10, 11, 12, 13, 14, 8 + H_LOAD_COMPUTE_H8_16 11, 12, 13, 14, 15, 8 + PUSH_MM 9 + PUSH_MM 10 + PUSH_MM 11 + H_LOAD_COMPUTE_H8_16 12, 9, 10, 11, 15, 8 + H_LOAD_COMPUTE_H8_16 13, 9, 10, 11, 15, 8 + + movu xm14, [srcq + srcstrideq * 0 + 0] + movu xm10, [srcq + srcstrideq * 0 + 16] + shufpd m9, m14, m10, 0x05 + H_COMPUTE_H8_16_AVX2 14, 9, 10, 11, 15 + POP_MM 11 + POP_MM 10 + POP_MM 9 + + vpunpcklwd m0, m7, m8 + vpunpckhwd m1, m7, m8 + vpunpcklwd m2, m9, m10 + vpunpckhwd m3, m9, m10 + vpunpcklwd m7, m11, m12 + vpunpckhwd m8, m11, m12 + vpunpcklwd m9, m13, m14 + vpunpckhwd m10, m13, m14 + + vpunpckldq m11, m0, m2 + vpunpckhdq m12, m0, m2 + vpunpckldq m13, m1, m3 + vpunpckhdq m14, m1, m3 + vpunpckldq m0, m7, m9 + vpunpckhdq m1, m7, m9 + vpunpckldq m2, m8, m10 + vpunpckhdq m3, m8, m10 + + vpunpcklqdq m7, m11, m0 + vpunpckhqdq m8, m11, m0 + vpunpcklqdq m9, m12, m1 + vpunpckhqdq m10, m12, m1 + vpunpcklqdq m11, m13, m2 + vpunpckhqdq m12, m13, m2 + vpunpcklqdq m13, m14, m3 + vpunpckhqdq m14, m14, m3 + + PUSH_MM 10 + PUSH_MM 14 + + LOAD_FILTER w, vf_idx, 0, 1, 2, 3 + H_LOAD_COMPUTE_V8_16 7, 11, 10, 14, 15 + H_LOAD_COMPUTE_V8_16 8, 12, 10, 14, 15 + H_LOAD_COMPUTE_V8_16 9, 13, 10, 14, 15 + POP_MM 14 + POP_MM 10 + H_LOAD_COMPUTE_V8_16 10, 14, 11, 12, 15 + + vpunpcklwd m0, m7, m8 + vpunpckhwd m1, m7, m8 + vpunpcklwd m2, m9, m10 + vpunpckhwd m3, m9, m10 + + vpunpckldq m11, m0, m2 + vpunpckhdq m12, m0, m2 + vpunpckldq m13, m1, m3 + vpunpckhdq m14, m1, m3 + + vextracti128 xm0, m11, 1 + vextracti128 xm1, m12, 1 + vextracti128 xm2, m13, 1 + vextracti128 xm3, m14, 1 + + vpunpcklqdq m7, m11, m0 + vpunpckhqdq m8, m11, m0 + vpunpcklqdq m9, m12, m1 + vpunpckhqdq m10, m12, m1 + vpunpcklqdq m11, m13, m2 + vpunpckhqdq m12, m13, m2 + vpunpcklqdq m13, m14, m3 + vpunpckhqdq m14, m14, m3 + + movu [dstq + MAX_PB_SIZE * 0], xm7 + movu [dstq + MAX_PB_SIZE * 1], xm8 + movu [dstq + MAX_PB_SIZE * 2], xm9 + movu [dstq + MAX_PB_SIZE * 3], xm10 + movu [dstq + MAX_PB_SIZE * 4], xm11 + movu [dstq + MAX_PB_SIZE * 5], xm12 + movu [dstq + MAX_PB_SIZE * 6], xm13 + movu [dstq + MAX_PB_SIZE * 7], xm14 + + LOOP_END %1, 8 + + RET + +.hv4: +.loop_h4: + mov xq, widthq +.loop_v4: + mov srcq, _srcq + LOAD_FILTER w, hf_idx, 0, 1, 2, 3 + H_LOAD_COMPUTE_H8_16 7, 8, 9, 10, 11, 8 + H_LOAD_COMPUTE_H8_16 8, 9, 10, 11, 12, 8 + H_LOAD_COMPUTE_H8_16 9, 10, 11, 12, 13, 8 + + vpunpcklwd m7, m7, m8 + + movu xm10, [srcq + srcstrideq * 0 + 0] + movu xm12, [srcq + srcstrideq * 0 + 16] + shufpd m11, m10, m12, 0x05 + H_COMPUTE_H8_16_AVX2 10, 11, 12, 13, 14 + + lea srcq, [srcq + srcstrideq ] + H_LOAD_COMPUTE_H8_16 8, 11, 12, 13, 14, 2 + H_LOAD_COMPUTE_H8_16 11, 12, 13, 14, 15, 2 + + vpunpcklwd m9, m9, m10 + vpunpcklwd m8, m8, m11 + + vpunpckldq m10, m7, m9 + vpunpckhdq m11, m7, m9 + + vextracti128 xm7, m8, 1 + vpunpckldq xm12, xm8, xm7 + vpunpckhdq xm13, xm8, xm7 + + vpunpcklqdq m7, m10, m12 + vpunpckhqdq m8, m10, m12 + vpunpcklqdq m9, m11, m13 + vpunpckhqdq m10, m11, m13 + + LOAD_FILTER w, vf_idx, 0, 1, 2, 3 + H_LOAD_COMPUTE_V8_16 7, 9, 11, 12, 13 + H_LOAD_COMPUTE_V8_16 8, 10, 11, 12, 13 + + vpunpcklwd m7, m7, m8 + vextracti128 xm8, m7, 1 + vpunpckldq xm9, xm7, xm8 + vpunpckhdq xm10, xm7, xm8 + + psrldq xm7, xm9, 8 + psrldq xm8, xm10, 8 + + movq [dstq + MAX_PB_SIZE * 0], xm9 + movq [dstq + MAX_PB_SIZE * 1], xm7 + movq [dstq + MAX_PB_SIZE * 2], xm10 + movq [dstq + MAX_PB_SIZE * 3], xm8 + + LOOP_END %1, 4 + + RET +%endmacro + +%macro VVC_PUT_VVC_LUMA_H_AVX2 1 +cglobal vvc_put_vvc_luma_h_%1, 10, 11, 12, dst, src, srcstride, height, mx, my, width, hf_idx, vf_idx, bitdepth, r3src + MOVSXDIFNIDN %1 + LOAD_FILTER_16 hf_idx, mx, 0, 1, 2, 3 + + vbroadcasti128 m4, [pb_vvc_iter_shuffle_index_w + 0 * 16] + vbroadcasti128 m5, [pb_vvc_iter_shuffle_index_w + 1 * 16] + + sub bitdepthq, 8 + movq xm6, bitdepthq + sub srcq, 6 + + cmp widthq, 4 + jne .h8 + +.h4: +.h4_loop: + movu xm7, [srcq + srcstrideq * 0 + 0] + VINSERTI128 m7, [srcq + srcstrideq * 1 + 0], 1 + movu xm9, [srcq + srcstrideq * 0 + 16] + VINSERTI128 m9, [srcq + srcstrideq * 1 + 16], 1 + shufpd m8, m7, m9, 0x05 + + H_COMPUTE_H8_16_AVX2 7, 8, 9, 10, 11 + + movq [dstq + MAX_PB_SIZE * 0], xm7 + vextracti128 xm7, ym7, 1 + movq [dstq + MAX_PB_SIZE * 1], xm7 + + H_LOOP_END 4, 2 + + RET + +.h8: + cmp widthq, 8 + jne .h16 + +.h8_loop: + movu xm7, [srcq + srcstrideq * 0 + 0] + VINSERTI128 m7, [srcq + srcstrideq * 1 + 0], 1 + movu xm9, [srcq + srcstrideq * 0 + 16] + VINSERTI128 m9, [srcq + srcstrideq * 1 + 16], 1 + shufpd m8, m7, m9, 0x05 + + H_COMPUTE_H8_16_AVX2 7, 8, 9, 10, 11 + + movu [dstq + MAX_PB_SIZE * 0], xm7 + vextracti128 [dstq + MAX_PB_SIZE * 1], ym7, 1 + + H_LOOP_END 8, 2 + + RET + +.h16: +.h16_loop: + mov r3srcq, widthq +.h16_loop_w: + movu m7, [srcq + r3srcq * 2 - 32] + movu m8, [srcq + r3srcq * 2 - 24] + movu m9, [srcq + r3srcq * 2 - 16] + + H_COMPUTE_H8_16_AVX2 7, 8, 9, 10, 11 + movu [dstq + r3srcq * 2 - 32], m7 + sub r3srcq, 16 + jg .h16_loop_w + H_LOOP_END 16, 1 + + RET +%endmacro + +%macro VVC_PUT_VVC_LUMA_V_AVX2 1 +cglobal vvc_put_vvc_luma_v_%1, 10, 13, 16, 0-mmsize*2, dst, src, srcstride, height, mx, my, width, hf_idx, vf_idx, bitdepth, r3src, _src, x +%assign mm_stack_offset 0 + MOVSXDIFNIDN %1 + PRE_CAL_INDEX hf_idx, mx, 4 + PRE_CAL_INDEX vf_idx, my, 4 + + vbroadcasti128 m4, [pb_vvc_iter_shuffle_index_w + 0 * 16] + vbroadcasti128 m5, [pb_vvc_iter_shuffle_index_w + 1 * 16] + + sub bitdepthq, 8 + movq xm6, bitdepthq + + lea r3srcq, [srcstrideq * 3] + neg r3srcq + lea _srcq, [srcq + r3srcq ] + + cmp heightq, 4 + je .hv4 + cmp widthq, 4 + je .hv4 + +.hv8: +.loop_h8: + mov xq, widthq +.loop_v8: + mov srcq, _srcq + lea r3srcq, [srcstrideq * 3 ] + movu xm7, [srcq ] + movu xm8, [srcq + srcstrideq * 1] + movu xm9, [srcq + srcstrideq * 2] + movu xm10, [srcq + r3srcq ] + + movu xm11, [srcq + srcstrideq * 4] + + lea srcq, [srcq + r3srcq ] + movu xm12, [srcq + srcstrideq * 2] + + lea srcq, [srcq + r3srcq ] + movu xm13, [srcq ] + movu xm14, [srcq + srcstrideq * 1] + + VINSERTI128 m7, [srcq + srcstrideq * 2], 1 + VINSERTI128 m8, [srcq + r3srcq ], 1 + VINSERTI128 m9, [srcq + srcstrideq * 4], 1 + lea srcq, [srcq + r3srcq ] + VINSERTI128 m10, [srcq + srcstrideq * 2], 1 + lea srcq, [srcq + r3srcq ] + VINSERTI128 m11, [srcq ], 1 + VINSERTI128 m12, [srcq + srcstrideq * 1], 1 + VINSERTI128 m13, [srcq + srcstrideq * 2], 1 + + vpunpcklwd m0, m7, m8 + vpunpckhwd m1, m7, m8 + vpunpcklwd m2, m9, m10 + vpunpckhwd m3, m9, m10 + vpunpcklwd m7, m11, m12 + vpunpckhwd m8, m11, m12 + vpunpcklwd m9, m13, m14 + vpunpckhwd m10, m13, m14 + + vpunpckldq m11, m0, m2 + vpunpckhdq m12, m0, m2 + vpunpckldq m13, m1, m3 + vpunpckhdq m14, m1, m3 + vpunpckldq m0, m7, m9 + vpunpckhdq m1, m7, m9 + vpunpckldq m2, m8, m10 + vpunpckhdq m3, m8, m10 + + vpunpcklqdq m7, m11, m0 + vpunpckhqdq m8, m11, m0 + vpunpcklqdq m9, m12, m1 + vpunpckhqdq m10, m12, m1 + vpunpcklqdq m11, m13, m2 + vpunpckhqdq m12, m13, m2 + vpunpcklqdq m13, m14, m3 + vpunpckhqdq m14, m14, m3 + + PUSH_MM 10 + PUSH_MM 14 + + LOAD_FILTER w, vf_idx, 0, 1, 2, 3 + H_LOAD_COMPUTE_V8_16 7, 11, 10, 14, 15, xm6 + H_LOAD_COMPUTE_V8_16 8, 12, 10, 14, 15, xm6 + H_LOAD_COMPUTE_V8_16 9, 13, 10, 14, 15, xm6 + POP_MM 14 + POP_MM 10 + H_LOAD_COMPUTE_V8_16 10, 14, 11, 12, 15, xm6 + + vpunpcklwd m0, m7, m8 + vpunpckhwd m1, m7, m8 + vpunpcklwd m2, m9, m10 + vpunpckhwd m3, m9, m10 + + vpunpckldq m11, m0, m2 + vpunpckhdq m12, m0, m2 + vpunpckldq m13, m1, m3 + vpunpckhdq m14, m1, m3 + + vextracti128 xm0, m11, 1 + vextracti128 xm1, m12, 1 + vextracti128 xm2, m13, 1 + vextracti128 xm3, m14, 1 + + vpunpcklqdq m7, m11, m0 + vpunpckhqdq m8, m11, m0 + vpunpcklqdq m9, m12, m1 + vpunpckhqdq m10, m12, m1 + vpunpcklqdq m11, m13, m2 + vpunpckhqdq m12, m13, m2 + vpunpcklqdq m13, m14, m3 + vpunpckhqdq m14, m14, m3 + + movu [dstq + MAX_PB_SIZE * 0], xm7 + movu [dstq + MAX_PB_SIZE * 1], xm8 + movu [dstq + MAX_PB_SIZE * 2], xm9 + movu [dstq + MAX_PB_SIZE * 3], xm10 + movu [dstq + MAX_PB_SIZE * 4], xm11 + movu [dstq + MAX_PB_SIZE * 5], xm12 + movu [dstq + MAX_PB_SIZE * 6], xm13 + movu [dstq + MAX_PB_SIZE * 7], xm14 + + LOOP_END %1, 8 + + RET + +.hv4: +.loop_h4: + mov xq, widthq +.loop_v4: + mov srcq, _srcq + lea r3srcq, [srcstrideq * 3 ] + movu xm7, [srcq ] + movu xm8, [srcq + srcstrideq * 1] + movu xm9, [srcq + srcstrideq * 2] + movu xm10, [srcq + r3srcq ] + + movu xm11, [srcq + srcstrideq * 4] + + lea srcq, [srcq + r3srcq ] + movu xm12, [srcq + srcstrideq * 2] + + lea srcq, [srcq + r3srcq ] + VINSERTI128 m11, [srcq ], 1 + VINSERTI128 m12, [srcq + srcstrideq * 1], 1 + + VINSERTI128 m7, [srcq + srcstrideq * 2], 1 + VINSERTI128 m8, [srcq + r3srcq ], 1 + VINSERTI128 m9, [srcq + srcstrideq * 4], 1 + + vpunpcklwd m7, m7, m8 + vpunpcklwd m9, m9, m10 + vpunpcklwd m8, m11, m12 + + vpunpckldq m10, m7, m9 + vpunpckhdq m11, m7, m9 + + vextracti128 xm7, m8, 1 + vpunpckldq xm12, xm8, xm7 + vpunpckhdq xm13, xm8, xm7 + + vpunpcklqdq m7, m10, m12 + vpunpckhqdq m8, m10, m12 + vpunpcklqdq m9, m11, m13 + vpunpckhqdq m10, m11, m13 + + LOAD_FILTER w, vf_idx, 0, 1, 2, 3 + H_LOAD_COMPUTE_V8_16 7, 9, 11, 12, 13, xm6 + H_LOAD_COMPUTE_V8_16 8, 10, 11, 12, 13, xm6 + + vpunpcklwd m7, m7, m8 + vextracti128 xm8, m7, 1 + vpunpckldq xm9, xm7, xm8 + vpunpckhdq xm10, xm7, xm8 + + psrldq xm7, xm9, 8 + psrldq xm8, xm10, 8 + + movq [dstq + MAX_PB_SIZE * 0], xm9 + movq [dstq + MAX_PB_SIZE * 1], xm7 + movq [dstq + MAX_PB_SIZE * 2], xm10 + movq [dstq + MAX_PB_SIZE * 3], xm8 + + LOOP_END %1, 4 + + RET +%endmacro + +%macro H_COMPUTE_H4_8_AVX2 2 + pshufb m%2, m%1, m3 + pshufb m%1, m%1, m2 + pmaddubsw m%1, m0 + pmaddubsw m%2, m1 + paddw m%1, m%2 + phaddw m%1, m%1 +%endmacro + +%macro H_COMPUTE_H8_8_AVX2 4 + pshufb m%2, m%1, m3 + pshufb m%3, m%1, m4 + pshufb m%1, m%1, m2 + pmaddubsw m%1, m0 + pmaddubsw m%4, m%2, m1 + pmaddubsw m%2, m0 + pmaddubsw m%3, m1 + paddw m%1, m%4 + paddw m%2, m%3 + phaddw m%1, m%2 +%endmacro + +%macro H_LOAD_COMPUTE_H8_8_AVX2 5-6 + movu xm%1, [srcq ] + VINSERTI128 m%1, [srcq + srcstrideq * 8], 1 + lea srcq, [srcq + srcstrideq ] + H_COMPUTE_H8_8_AVX2 %1, %2, %3, %4 + movu xm%2, [srcq ] + VINSERTI128 m%2, [srcq + srcstrideq * 8], 1 + lea srcq, [srcq + srcstrideq ] + H_COMPUTE_H8_8_AVX2 %2, %3, %4, %5 + vpunpcklwd m%3, m%1, m%2 + vpunpckhwd m%4, m%1, m%2 +%endmacro + +%macro VVC_PUT_VVC_LUMA_HV_8_AVX2 1 +cglobal vvc_put_vvc_luma_hv_%1, 9, 12, 16, 0-mmsize*2, dst, src, srcstride, height, mx, my, width, hf_idx, vf_idx, r3src, _src, x +%assign mm_stack_offset 0 + MOVSXDIFNIDN %1 + PRE_CAL_INDEX hf_idx, mx, 3 + PRE_CAL_INDEX vf_idx, my, 4 + + mov r3srcq, 0x0000ffff + movq xm6, r3srcq + vpbroadcastd m6, xm6 + + lea r3srcq, [srcstrideq * 3 ] + neg r3srcq + lea _srcq, [srcq + r3srcq - 3] + + cmp heightq, 4 + je .hv4 + cmp widthq, 4 + je .hv4 + +.hv8: +.loop_h8: + mov xq, widthq +.loop_v8: + LOAD_FILTER b, hf_idx, 0, 1 + vbroadcasti128 m2, [pb_vvc_iter_shuffle_index_b + 0 * 16] + vbroadcasti128 m3, [pb_vvc_iter_shuffle_index_b + 1 * 16] + vbroadcasti128 m4, [pb_vvc_iter_shuffle_index_b + 2 * 16] + mov srcq, _srcq + + H_LOAD_COMPUTE_H8_8_AVX2 7, 8, 9, 10, 11 + H_LOAD_COMPUTE_H8_8_AVX2 7, 8, 11, 12, 13 + H_LOAD_COMPUTE_H8_8_AVX2 7, 8, 13, 14, 15 + PUSH_MM 13 + PUSH_MM 14 + movu xm7, [srcq ] + movu xm8, [srcq + srcstrideq * 1] + VINSERTI128 m7, [srcq + srcstrideq * 8], 1 + H_COMPUTE_H8_8_AVX2 7, 13, 14, 15 + H_COMPUTE_H8_8_AVX2 8, 13, 14, 15 + vpunpcklwd m13, m7, m8 + vpunpckhwd m14, m7, m8 + POP_MM 1 + POP_MM 0 + + vpunpckldq m2, m9, m11 + vpunpckhdq m3, m9, m11 + vpunpckldq m4, m10, m12 + vpunpckhdq m5, m10, m12 + vpunpckldq m9, m0, m13 + vpunpckhdq m10, m0, m13 + vpunpckldq m7, m1, m14 + vpunpckhdq m8, m1, m14 + + vpunpcklqdq m11, m4, m7 + vpunpckhqdq m12, m4, m7 + vpunpcklqdq m13, m5, m8 + vpunpckhqdq m14, m5, m8 + vpunpcklqdq m7, m2, m9 + vpunpckhqdq m8, m2, m9 + vpunpcklqdq m9, m3, m10 + vpunpckhqdq m10, m3, m10 + + PUSH_MM 10 + PUSH_MM 14 + LOAD_FILTER w, vf_idx, 0, 1, 2, 3 + vbroadcasti128 m4, [pb_vvc_iter_shuffle_index_w + 0 * 16] + vbroadcasti128 m5, [pb_vvc_iter_shuffle_index_w + 1 * 16] + H_LOAD_COMPUTE_V8_16 7, 11, 10, 14, 15 + H_LOAD_COMPUTE_V8_16 8, 12, 10, 14, 15 + H_LOAD_COMPUTE_V8_16 9, 13, 10, 14, 15 + POP_MM 14 + POP_MM 10 + H_LOAD_COMPUTE_V8_16 10, 14, 11, 12, 15 + + vpunpcklwd m0, m7, m8 + vpunpckhwd m1, m7, m8 + vpunpcklwd m2, m9, m10 + vpunpckhwd m3, m9, m10 + + vpunpckldq m11, m0, m2 + vpunpckhdq m12, m0, m2 + vpunpckldq m13, m1, m3 + vpunpckhdq m14, m1, m3 + + vextracti128 xm0, m11, 1 + vextracti128 xm1, m12, 1 + vextracti128 xm2, m13, 1 + vextracti128 xm3, m14, 1 + + vpunpcklqdq m7, m11, m0 + vpunpckhqdq m8, m11, m0 + vpunpcklqdq m9, m12, m1 + vpunpckhqdq m10, m12, m1 + vpunpcklqdq m11, m13, m2 + vpunpckhqdq m12, m13, m2 + vpunpcklqdq m13, m14, m3 + vpunpckhqdq m14, m14, m3 + + movu [dstq + MAX_PB_SIZE * 0], xm7 + movu [dstq + MAX_PB_SIZE * 1], xm8 + movu [dstq + MAX_PB_SIZE * 2], xm9 + movu [dstq + MAX_PB_SIZE * 3], xm10 + movu [dstq + MAX_PB_SIZE * 4], xm11 + movu [dstq + MAX_PB_SIZE * 5], xm12 + movu [dstq + MAX_PB_SIZE * 6], xm13 + movu [dstq + MAX_PB_SIZE * 7], xm14 + + LOOP_END %1, 8 + + RET + +.hv4: + vbroadcasti128 m14, [pb_vvc_iter_shuffle_index_b + 0 * 16] + vbroadcasti128 m15, [pb_vvc_iter_shuffle_index_b + 1 * 16] + vbroadcasti128 m4, [pb_vvc_iter_shuffle_index_w + 0 * 16] + vbroadcasti128 m5, [pb_vvc_iter_shuffle_index_w + 1 * 16] +.loop_h4: + mov xq, widthq +.loop_v4: + LOAD_FILTER b, hf_idx, 0, 1 + SWAP 2, 14, 3, 15 + mov srcq, _srcq + lea r3srcq, [srcstrideq * 3 ] + movu xm7, [srcq ] + movu xm8, [srcq + srcstrideq * 1] + movu xm9, [srcq + srcstrideq * 2] + movu xm10, [srcq + r3srcq ] + + movu xm11, [srcq + srcstrideq * 4] + + lea srcq, [srcq + r3srcq ] + movu xm12, [srcq + srcstrideq * 2] + + lea srcq, [srcq + r3srcq ] + VINSERTI128 m11, [srcq ], 1 + VINSERTI128 m12, [srcq + srcstrideq * 1], 1 + VINSERTI128 m7, [srcq + srcstrideq * 2], 1 + VINSERTI128 m8, [srcq + r3srcq ], 1 + VINSERTI128 m9, [srcq + srcstrideq * 4], 1 + + H_COMPUTE_H4_8_AVX2 7, 13 + H_COMPUTE_H4_8_AVX2 8, 13 + H_COMPUTE_H4_8_AVX2 9, 13 + H_COMPUTE_H4_8_AVX2 10, 13 + H_COMPUTE_H4_8_AVX2 11, 13 + H_COMPUTE_H4_8_AVX2 12, 13 + + vpunpcklwd m7, m7, m8 + vpunpcklwd m9, m9, m10 + vpunpcklwd m8, m11, m12 + + vpunpckldq m10, m7, m9 + vpunpckhdq m11, m7, m9 + + vextracti128 xm7, m8, 1 + vpunpckldq xm12, xm8, xm7 + vpunpckhdq xm13, xm8, xm7 + + vpunpcklqdq m7, m10, m12 + vpunpckhqdq m8, m10, m12 + vpunpcklqdq m9, m11, m13 + vpunpckhqdq m10, m11, m13 + + SWAP 14, 2, 15, 3 + LOAD_FILTER w, vf_idx, 0, 1, 2, 3 + H_LOAD_COMPUTE_V8_16 7, 9, 11, 12, 13 + H_LOAD_COMPUTE_V8_16 8, 10, 11, 12, 13 + + vpunpcklwd m7, m7, m8 + vextracti128 xm8, m7, 1 + vpunpckldq xm9, xm7, xm8 + vpunpckhdq xm10, xm7, xm8 + + movq [dstq + MAX_PB_SIZE * 0], xm9 + pextrq [dstq + MAX_PB_SIZE * 1], xm9, 1 + movq [dstq + MAX_PB_SIZE * 2], xm10 + pextrq [dstq + MAX_PB_SIZE * 3], xm10, 1 + + LOOP_END %1, 4 + + RET +%endmacro + +%macro VVC_PUT_VVC_LUMA_H_8_AVX2 1 +cglobal vvc_put_vvc_luma_h_%1, 9, 10, 10, dst, src, srcstride, height, mx, my, width, hf_idx, vf_idx, r3src + MOVSXDIFNIDN %1 + LOAD_FILTER_%1 hf_idx, mx, 0, 1 + + vbroadcasti128 m2, [pb_vvc_iter_shuffle_index_b + 0 * 16] + vbroadcasti128 m3, [pb_vvc_iter_shuffle_index_b + 1 * 16] + + lea srcq, [srcq - 3 ] + lea r3srcq, [srcstrideq * 3] + + cmp widthq, 4 + jne .h8 + +.h4: +.h4_loop: + movu xm5, [srcq + srcstrideq * 0] + VINSERTI128 m5, [srcq + srcstrideq * 1], 1 + movu xm7, [srcq + srcstrideq * 2] + VINSERTI128 m7, [srcq + r3srcq ], 1 + + H_COMPUTE_H4_8_AVX2 5, 6 + H_COMPUTE_H4_8_AVX2 7, 8 + + vextracti128 xm6, m5, 1 + vextracti128 xm8, m7, 1 + + STORE4_LOOP 5, 4, 1 + H_LOOP_END 4, 4 + + RET + +.h8: + vbroadcasti128 m4, [pb_vvc_iter_shuffle_index_b + 2 * 16] + cmp widthq, 8 + jne .h16 + +.h8_loop: + movu xm5, [srcq + srcstrideq * 0] + VINSERTI128 m5, [srcq + srcstrideq * 1], 1 + movu xm7, [srcq + srcstrideq * 2] + VINSERTI128 m7, [srcq + r3srcq ], 1 + + H_COMPUTE_H8_8_AVX2 5, 6, 8, 9 + H_COMPUTE_H8_8_AVX2 7, 6, 8, 9 + + vextracti128 xm6, m5, 1 + vextracti128 xm8, m7, 1 + + STORE8_LOOP 5, 4, 1 + H_LOOP_END 8, 4 + + RET + +.h16: +.h16_loop: + mov mxq, widthq +.h16_loop_w: + lea r3srcq, [srcq + mxq - 16 ] + movu xm5, [r3srcq + srcstrideq * 0 + 0] + VINSERTI128 m5, [r3srcq + srcstrideq * 0 + 8], 1 + movu xm6, [r3srcq + srcstrideq * 1 + 0] + VINSERTI128 m6, [r3srcq + srcstrideq * 1 + 8], 1 + + H_COMPUTE_H8_8_AVX2 5, 7, 8, 9 + H_COMPUTE_H8_8_AVX2 6, 7, 8, 9 + + movu [dstq + mxq * 2 - 32 + MAX_PB_SIZE * 0], m5 + movu [dstq + mxq * 2 - 32 + MAX_PB_SIZE * 1], m6 + + sub mxq, 16 + jg .h16_loop_w + + H_LOOP_END 16, 2 + + RET +%endmacro + +%macro TRANSPOSE8X4B 2 + vpunpcklbw xm14, xm%1, xm%2 + vpunpckhbw xm15, xm%1, xm%2 + vpunpcklwd xm%1, xm14, xm15 + vpunpckhwd xm%2, xm14, xm15 +%endmacro + +%macro VVC_PUT_VVC_LUMA_V_8_AVX2 1 +cglobal vvc_put_vvc_luma_v_%1, 9, 12, 16, dst, src, srcstride, height, mx, my, width, hf_idx, vf_idx, r3src, _src, x + MOVSXDIFNIDN %1 + PRE_CAL_INDEX vf_idx, my, 3 + LOAD_FILTER b, vf_idx, 0, 1 + vbroadcasti128 m2, [pb_vvc_iter_shuffle_index_b + 0 * 16] + vbroadcasti128 m3, [pb_vvc_iter_shuffle_index_b + 1 * 16] + vbroadcasti128 m4, [pb_vvc_iter_shuffle_index_b + 2 * 16] + + lea r3srcq, [srcstrideq * 3] + neg r3srcq + lea _srcq, [srcq + r3srcq ] + + cmp heightq, 4 + je .hv4 + cmp widthq, 4 + je .hv4 + +.hv8: +.loop_h8: + mov xq, widthq +.loop_v8: + mov srcq, _srcq + lea r3srcq, [srcstrideq * 3 ] + movq xm6, [srcq ] + movq xm7, [srcq + srcstrideq * 1] + pinsrq xm6, [srcq + srcstrideq * 2], 1 + pinsrq xm7, [srcq + r3srcq ], 1 + movq xm8, [srcq + srcstrideq * 4] + lea srcq, [srcq + r3srcq ] + movq xm9, [srcq + srcstrideq * 2] + lea srcq, [srcq + r3srcq ] + pinsrq xm8, [srcq ], 1 + pinsrq xm9, [srcq + srcstrideq * 1], 1 + movq xm10, [srcq + srcstrideq * 2] + movq xm11, [srcq + r3srcq ] + pinsrq xm10, [srcq + srcstrideq * 4], 1 + lea srcq, [srcq + r3srcq ] + pinsrq xm11, [srcq + srcstrideq * 2], 1 + lea srcq, [srcq + r3srcq ] + movq xm12, [srcq ] + movq xm13, [srcq + srcstrideq * 1] + pinsrq xm12, [srcq + srcstrideq * 2], 1 + + TRANSPOSE8X4B 6, 7 + TRANSPOSE8X4B 8, 9 + TRANSPOSE8X4B 10, 11 + TRANSPOSE8X4B 12, 13 + + vpunpckldq xm14, xm6, xm8 + vpunpckhdq xm15, xm6, xm8 + vpunpckldq xm6, xm7, xm9 + vpunpckhdq xm8, xm7, xm9 + vpunpckldq xm7, xm10, xm12 + vpunpckhdq xm9, xm10, xm12 + vpunpckldq xm10, xm11, xm13 + vpunpckhdq xm12, xm11, xm13 + + vpunpcklqdq xm11, xm14, xm7 + vpunpckhqdq xm13, xm14, xm7 + vpunpcklqdq xm14, xm15, xm9 + vpunpckhqdq xm7, xm15, xm9 + vpunpcklqdq xm15, xm6, xm10 + vpunpckhqdq xm9, xm6, xm10 + vpunpcklqdq xm6, xm8, xm12 + vpunpckhqdq xm10, xm8, xm12 + + vinserti128 m10, m7, xm10, 1 + vinserti128 m8, m13, xm9, 1 + vinserti128 m9, m14, xm6, 1 + vinserti128 m7, m11, xm15, 1 + + H_COMPUTE_H8_8_AVX2 7, 11, 12, 13 + H_COMPUTE_H8_8_AVX2 8, 11, 12, 13 + H_COMPUTE_H8_8_AVX2 9, 11, 12, 13 + H_COMPUTE_H8_8_AVX2 10, 11, 12, 13 + + vpunpcklwd m12, m7, m8 + vpunpckhwd m13, m7, m8 + vpunpcklwd m14, m9, m10 + vpunpckhwd m15, m9, m10 + + vpunpckldq m8, m12, m14 + vpunpckhdq m10, m12, m14 + vpunpckldq m12, m13, m15 + vpunpckhdq m14, m13, m15 + + vextracti128 xm9, m8, 1 + vextracti128 xm11, m10, 1 + vextracti128 xm13, m12, 1 + vextracti128 xm15, m14, 1 + + vpunpcklqdq xm6, xm8, xm9 + vpunpckhqdq xm7, xm8, xm9 + vpunpcklqdq xm8, xm10, xm11 + vpunpckhqdq xm9, xm10, xm11 + vpunpcklqdq xm10, xm12, xm13 + vpunpckhqdq xm11, xm12, xm13 + vpunpcklqdq xm12, xm14, xm15 + vpunpckhqdq xm13, xm14, xm15 + + movu [dstq + MAX_PB_SIZE * 0], xm6 + movu [dstq + MAX_PB_SIZE * 1], xm7 + movu [dstq + MAX_PB_SIZE * 2], xm8 + movu [dstq + MAX_PB_SIZE * 3], xm9 + movu [dstq + MAX_PB_SIZE * 4], xm10 + movu [dstq + MAX_PB_SIZE * 5], xm11 + movu [dstq + MAX_PB_SIZE * 6], xm12 + movu [dstq + MAX_PB_SIZE * 7], xm13 + + LOOP_END %1, 8 + + RET + +.hv4: + mova xm5, [pb_vvc_transpose_4x4b_index] +.loop_h4: + mov xq, widthq +.loop_v4: + mov srcq, _srcq + lea r3srcq, [srcstrideq * 3 ] + movd xm7, [srcq ] + movd xm9, [srcq + srcstrideq * 1] + pinsrd xm7, [srcq + srcstrideq * 2], 1 + pinsrd xm9, [srcq + r3srcq ], 1 + vpunpckldq xm7, xm9 + + movd xm8, [srcq + srcstrideq * 4] + lea srcq, [srcq + r3srcq ] + movd xm10, [srcq + srcstrideq * 2] + + lea srcq, [srcq + r3srcq ] + pinsrd xm8, [srcq ], 1 + pinsrd xm10, [srcq + srcstrideq * 1], 1 + vpunpckldq xm8, xm10 + + movd xm9, [srcq + srcstrideq * 2] + movd xm10, [srcq + r3srcq ] + pinsrd xm9, [srcq + srcstrideq * 4], 1 + vpunpckldq xm9, xm10 + + pshufb xm7, xm7, xm5 + pshufb xm8, xm8, xm5 + pshufb xm9, xm9, xm5 + + vpunpckldq xm10, xm7, xm8 + vpunpckhdq xm11, xm7, xm8 + + pshufd xm12, xm9, 0x50 + pshufd xm9, xm9, 0xFA + vpunpcklqdq xm7, xm10, xm12 + vpunpckhqdq xm8, xm10, xm12 + vpunpckhqdq xm10, xm11, xm9 + vpunpcklqdq xm9, xm11, xm9 + + VINSERTI128 m7, xm9, 1 + VINSERTI128 m8, xm10, 1 + + H_COMPUTE_H4_8_AVX2 7, 9 + H_COMPUTE_H4_8_AVX2 8, 9 + + vpunpcklwd m7, m7, m8 + vextracti128 xm8, m7, 1 + vpunpckldq xm9, xm7, xm8 + vpunpckhdq xm10, xm7, xm8 + + movq [dstq + MAX_PB_SIZE * 0], xm9 + pextrq [dstq + MAX_PB_SIZE * 1], xm9, 1 + movq [dstq + MAX_PB_SIZE * 2], xm10 + pextrq [dstq + MAX_PB_SIZE * 3], xm10, 1 + + LOOP_END %1, 4 + + RET +%endmacro + +%if ARCH_X86_64 +%if HAVE_AVX512ICL_EXTERNAL + +INIT_ZMM avx512icl +VVC_PUT_VVC_LUMA_HV_AVX512ICL 16 + +VVC_PUT_VVC_LUMA_H_AVX512ICL 16 + +VVC_PUT_VVC_LUMA_V_AVX512ICL 16 + +%endif + +%if HAVE_AVX2_EXTERNAL +INIT_YMM avx2 +VVC_PUT_VVC_LUMA_HV_AVX2 16 + +VVC_PUT_VVC_LUMA_H_AVX2 16 + +VVC_PUT_VVC_LUMA_V_AVX2 16 + +VVC_PUT_VVC_LUMA_HV_8_AVX2 8 + +VVC_PUT_VVC_LUMA_H_8_AVX2 8 + +VVC_PUT_VVC_LUMA_V_8_AVX2 8 + +%endif + +%endif diff --git a/libavcodec/x86/vvc_sao.asm b/libavcodec/x86/vvc_sao.asm new file mode 100644 index 00000000000..53e851b2c22 --- /dev/null +++ b/libavcodec/x86/vvc_sao.asm @@ -0,0 +1,331 @@ +;****************************************************************************** +;* SIMD optimized SAO functions for VVC 8bit decoding +;* +;* Copyright (c) 2013 Pierre-Edouard LEPERE +;* Copyright (c) 2014 James Almer +;* Copyright (c) 2023 Shaun Loo +;* +;* 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 "libavutil/x86/x86util.asm" + +SECTION_RODATA 32 + +pb_edge_shuffle: times 2 db 1, 2, 0, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +pb_eo: db -1, 0, 1, 0, 0, -1, 0, 1, -1, -1, 1, 1, 1, -1, -1, 1 +cextern pb_1 +cextern pb_2 + +SECTION .text + +;****************************************************************************** +;SAO Band Filter +;****************************************************************************** + +%macro VVC_SAO_BAND_FILTER_INIT 0 + and leftq, 31 + movd xm0, leftd + add leftq, 1 + and leftq, 31 + movd xm1, leftd + add leftq, 1 + and leftq, 31 + movd xm2, leftd + add leftq, 1 + and leftq, 31 + movd xm3, leftd + + SPLATW m0, xm0 + SPLATW m1, xm1 + SPLATW m2, xm2 + SPLATW m3, xm3 +%if mmsize > 16 + SPLATW m4, [offsetq + 2] + SPLATW m5, [offsetq + 4] + SPLATW m6, [offsetq + 6] + SPLATW m7, [offsetq + 8] +%else + movq m7, [offsetq + 2] + SPLATW m4, m7, 0 + SPLATW m5, m7, 1 + SPLATW m6, m7, 2 + SPLATW m7, m7, 3 +%endif + +%if ARCH_X86_64 + pxor m14, m14 + +%else ; ARCH_X86_32 + mova [rsp+mmsize*0], m0 + mova [rsp+mmsize*1], m1 + mova [rsp+mmsize*2], m2 + mova [rsp+mmsize*3], m3 + mova [rsp+mmsize*4], m4 + mova [rsp+mmsize*5], m5 + mova [rsp+mmsize*6], m6 + pxor m0, m0 + %assign MMSIZE mmsize + %define m14 m0 + %define m13 m1 + %define m9 m2 + %define m8 m3 +%endif ; ARCH +DEFINE_ARGS dst, src, dststride, srcstride, offset, height + mov heightd, r7m +%endmacro + +%macro VVC_SAO_BAND_FILTER_COMPUTE 2 + psraw %1, %2, 3 +%if ARCH_X86_64 + pcmpeqw m10, %1, m0 + pcmpeqw m11, %1, m1 + pcmpeqw m12, %1, m2 + pcmpeqw %1, m3 + pand m10, m4 + pand m11, m5 + pand m12, m6 + pand %1, m7 + por m10, m11 + por m12, %1 + por m10, m12 + paddw %2, m10 +%else ; ARCH_X86_32 + pcmpeqw m4, %1, [rsp+MMSIZE*0] + pcmpeqw m5, %1, [rsp+MMSIZE*1] + pcmpeqw m6, %1, [rsp+MMSIZE*2] + pcmpeqw %1, [rsp+MMSIZE*3] + pand m4, [rsp+MMSIZE*4] + pand m5, [rsp+MMSIZE*5] + pand m6, [rsp+MMSIZE*6] + pand %1, m7 + por m4, m5 + por m6, %1 + por m4, m6 + paddw %2, m4 +%endif ; ARCH +%endmacro + +;void ff_vvc_sao_band_filter__8_(uint8_t *_dst, const uint8_t *_src, ptrdiff_t _stride_dst, ptrdiff_t _stride_src, +; int16_t *sao_offset_val, int sao_left_class, int width, int height); +%macro VVC_SAO_BAND_FILTER 2 +cglobal vvc_sao_band_filter_%1_8, 6, 6, 15, 7*mmsize*ARCH_X86_32, dst, src, dststride, srcstride, offset, left + VVC_SAO_BAND_FILTER_INIT + +align 16 +.loop: +%if %1 == 8 + movq m8, [srcq] + punpcklbw m8, m14 + VVC_SAO_BAND_FILTER_COMPUTE m9, m8 + packuswb m8, m14 + movq [dstq], m8 +%endif ; %1 == 8 + +%assign i 0 +%rep %2 + mova m13, [srcq + i] + punpcklbw m8, m13, m14 + VVC_SAO_BAND_FILTER_COMPUTE m9, m8 + punpckhbw m13, m14 + VVC_SAO_BAND_FILTER_COMPUTE m9, m13 + packuswb m8, m13 + mova [dstq + i], m8 +%assign i i+mmsize +%endrep + +%if %1 == 48 +INIT_XMM cpuname + + mova m13, [srcq + i] + punpcklbw m8, m13, m14 + VVC_SAO_BAND_FILTER_COMPUTE m9, m8 + punpckhbw m13, m14 + VVC_SAO_BAND_FILTER_COMPUTE m9, m13 + packuswb m8, m13 + mova [dstq + i], m8 +%if cpuflag(avx2) +INIT_YMM cpuname +%endif +%endif ; %1 == 48 + + add dstq, dststrideq ; dst += dststride + add srcq, srcstrideq ; src += srcstride + dec heightd ; cmp height + jnz .loop ; height loop + REP_RET +%endmacro + +%if HAVE_AVX2_EXTERNAL +INIT_XMM avx2 +VVC_SAO_BAND_FILTER 8, 0 +VVC_SAO_BAND_FILTER 16, 1 +INIT_YMM avx2 +VVC_SAO_BAND_FILTER 32, 1 +VVC_SAO_BAND_FILTER 48, 1 +VVC_SAO_BAND_FILTER 64, 2 +VVC_SAO_BAND_FILTER 80, 3 +VVC_SAO_BAND_FILTER 96, 3 +VVC_SAO_BAND_FILTER 112, 4 +VVC_SAO_BAND_FILTER 128, 4 +%endif + +;****************************************************************************** +;SAO Edge Filter +;****************************************************************************** + +%define MAX_PB_SIZE 64 +%define PADDING_SIZE 64 ; AV_INPUT_BUFFER_PADDING_SIZE +%define EDGE_SRCSTRIDE 4 * MAX_PB_SIZE + PADDING_SIZE + +%macro VVC_SAO_EDGE_FILTER_INIT 0 +%if WIN64 + movsxd eoq, dword eom +%elif ARCH_X86_64 + movsxd eoq, eod +%else + mov eoq, r4m +%endif + lea tmp2q, [pb_eo] + movsx a_strideq, byte [tmp2q+eoq*4+1] + movsx b_strideq, byte [tmp2q+eoq*4+3] + imul a_strideq, EDGE_SRCSTRIDE + imul b_strideq, EDGE_SRCSTRIDE + movsx tmpq, byte [tmp2q+eoq*4] + add a_strideq, tmpq + movsx tmpq, byte [tmp2q+eoq*4+2] + add b_strideq, tmpq +%endmacro + +%macro VVC_SAO_EDGE_FILTER_COMPUTE 1 + pminub m4, m1, m2 + pminub m5, m1, m3 + pcmpeqb m2, m4 + pcmpeqb m3, m5 + pcmpeqb m4, m1 + pcmpeqb m5, m1 + psubb m4, m2 + psubb m5, m3 + paddb m4, m6 + paddb m4, m5 + + pshufb m2, m0, m4 +%if %1 > 8 + punpckhbw m5, m7, m1 + punpckhbw m4, m2, m7 + punpcklbw m3, m7, m1 + punpcklbw m2, m7 + pmaddubsw m5, m4 + pmaddubsw m3, m2 + packuswb m3, m5 +%else + punpcklbw m3, m7, m1 + punpcklbw m2, m7 + pmaddubsw m3, m2 + packuswb m3, m3 +%endif +%endmacro + +;void ff_vvc_sao_edge_filter__8_(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, int16_t *sao_offset_val, +; int eo, int width, int height); +%macro VVC_SAO_EDGE_FILTER 2-3 +%if ARCH_X86_64 +cglobal vvc_sao_edge_filter_%1_8, 4, 9, 8, dst, src, dststride, offset, eo, a_stride, b_stride, height, tmp +%define tmp2q heightq + VVC_SAO_EDGE_FILTER_INIT + mov heightd, r6m + +%else ; ARCH_X86_32 +cglobal vvc_sao_edge_filter_%1_8, 1, 6, 8, dst, src, dststride, a_stride, b_stride, height +%define eoq srcq +%define tmpq heightq +%define tmp2q dststrideq +%define offsetq heightq + VVC_SAO_EDGE_FILTER_INIT + mov srcq, srcm + mov offsetq, r3m + mov dststrideq, dststridem +%endif ; ARCH + +%if mmsize > 16 + vbroadcasti128 m0, [offsetq] +%else + movu m0, [offsetq] +%endif + mova m1, [pb_edge_shuffle] + packsswb m0, m0 + mova m7, [pb_1] + pshufb m0, m1 + mova m6, [pb_2] +%if ARCH_X86_32 + mov heightd, r6m +%endif + +align 16 +.loop: + +%if %1 == 8 + movq m1, [srcq] + movq m2, [srcq + a_strideq] + movq m3, [srcq + b_strideq] + VVC_SAO_EDGE_FILTER_COMPUTE %1 + movq [dstq], m3 +%endif + +%assign i 0 +%rep %2 + mova m1, [srcq + i] + movu m2, [srcq + a_strideq + i] + movu m3, [srcq + b_strideq + i] + VVC_SAO_EDGE_FILTER_COMPUTE %1 + mov%3 [dstq + i], m3 +%assign i i+mmsize +%endrep + +%if %1 == 48 +INIT_XMM cpuname + + mova m1, [srcq + i] + movu m2, [srcq + a_strideq + i] + movu m3, [srcq + b_strideq + i] + VVC_SAO_EDGE_FILTER_COMPUTE %1 + mova [dstq + i], m3 +%if cpuflag(avx2) +INIT_YMM cpuname +%endif +%endif + + add dstq, dststrideq + add srcq, EDGE_SRCSTRIDE + dec heightd + jg .loop + RET +%endmacro + +%if HAVE_AVX2_EXTERNAL +INIT_XMM avx2 +VVC_SAO_EDGE_FILTER 8, 0 +INIT_YMM avx2 +VVC_SAO_EDGE_FILTER 16, 1, u +VVC_SAO_EDGE_FILTER 32, 1, a +VVC_SAO_EDGE_FILTER 48, 1, u +VVC_SAO_EDGE_FILTER 64, 2, a +VVC_SAO_EDGE_FILTER 80, 3, u +VVC_SAO_EDGE_FILTER 96, 3, u +VVC_SAO_EDGE_FILTER 112, 4, u +VVC_SAO_EDGE_FILTER 128, 4, a +%endif diff --git a/libavcodec/x86/vvc_sao_10bit.asm b/libavcodec/x86/vvc_sao_10bit.asm new file mode 100644 index 00000000000..ee2bd0f3cfb --- /dev/null +++ b/libavcodec/x86/vvc_sao_10bit.asm @@ -0,0 +1,355 @@ +;****************************************************************************** +;* SIMD optimized SAO functions for VVC 10/12bit decoding +;* +;* Copyright (c) 2013 Pierre-Edouard LEPERE +;* Copyright (c) 2014 James Almer +;* Copyright (c) 2023 Shaun Loo +;* +;* 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 "libavutil/x86/x86util.asm" + +SECTION_RODATA 32 + +pw_m2: times 16 dw -2 +pw_mask10: times 16 dw 0x03FF +pw_mask12: times 16 dw 0x0FFF +pb_eo: db -1, 0, 1, 0, 0, -1, 0, 1, -1, -1, 1, 1, 1, -1, -1, 1 +cextern pw_m1 +cextern pw_1 +cextern pw_2 + +SECTION .text + +;****************************************************************************** +;SAO Band Filter +;****************************************************************************** + +%macro VVC_SAO_BAND_FILTER_INIT 1 + and leftq, 31 + movd xm0, leftd + add leftq, 1 + and leftq, 31 + movd xm1, leftd + add leftq, 1 + and leftq, 31 + movd xm2, leftd + add leftq, 1 + and leftq, 31 + movd xm3, leftd + + SPLATW m0, xm0 + SPLATW m1, xm1 + SPLATW m2, xm2 + SPLATW m3, xm3 +%if mmsize > 16 + SPLATW m4, [offsetq + 2] + SPLATW m5, [offsetq + 4] + SPLATW m6, [offsetq + 6] + SPLATW m7, [offsetq + 8] +%else + movq m7, [offsetq + 2] + SPLATW m4, m7, 0 + SPLATW m5, m7, 1 + SPLATW m6, m7, 2 + SPLATW m7, m7, 3 +%endif + +%if ARCH_X86_64 + mova m13, [pw_mask %+ %1] + pxor m14, m14 + +%else ; ARCH_X86_32 + mova [rsp+mmsize*0], m0 + mova [rsp+mmsize*1], m1 + mova [rsp+mmsize*2], m2 + mova [rsp+mmsize*3], m3 + mova [rsp+mmsize*4], m4 + mova [rsp+mmsize*5], m5 + mova [rsp+mmsize*6], m6 + mova m1, [pw_mask %+ %1] + pxor m0, m0 + %define m14 m0 + %define m13 m1 + %define m9 m2 + %define m8 m3 +%endif ; ARCH +DEFINE_ARGS dst, src, dststride, srcstride, offset, height + mov heightd, r7m +%endmacro + +;void ff_vvc_sao_band_filter___(uint8_t *_dst, const uint8_t *_src, ptrdiff_t _stride_dst, ptrdiff_t _stride_src, +; int16_t *sao_offset_val, int sao_left_class, int width, int height); +%macro VVC_SAO_BAND_FILTER 3 +cglobal vvc_sao_band_filter_%2_%1, 6, 6, 15, 7*mmsize*ARCH_X86_32, dst, src, dststride, srcstride, offset, left + VVC_SAO_BAND_FILTER_INIT %1 + +align 16 +.loop: + +%assign i 0 +%assign j 0 +%rep %3 +%assign k 8+(j&1) +%assign l 9-(j&1) + mova m %+ k, [srcq + i] + psraw m %+ l, m %+ k, %1-5 +%if ARCH_X86_64 + pcmpeqw m10, m %+ l, m0 + pcmpeqw m11, m %+ l, m1 + pcmpeqw m12, m %+ l, m2 + pcmpeqw m %+ l, m3 + pand m10, m4 + pand m11, m5 + pand m12, m6 + pand m %+ l, m7 + por m10, m11 + por m12, m %+ l + por m10, m12 + paddw m %+ k, m10 +%else ; ARCH_X86_32 + pcmpeqw m4, m %+ l, [rsp+mmsize*0] + pcmpeqw m5, m %+ l, [rsp+mmsize*1] + pcmpeqw m6, m %+ l, [rsp+mmsize*2] + pcmpeqw m %+ l, [rsp+mmsize*3] + pand m4, [rsp+mmsize*4] + pand m5, [rsp+mmsize*5] + pand m6, [rsp+mmsize*6] + pand m %+ l, m7 + por m4, m5 + por m6, m %+ l + por m4, m6 + paddw m %+ k, m4 +%endif ; ARCH + CLIPW m %+ k, m14, m13 + mova [dstq + i], m %+ k +%assign i i+mmsize +%assign j j+1 +%endrep + + add dstq, dststrideq + add srcq, srcstrideq + dec heightd + jg .loop + REP_RET +%endmacro + +%if HAVE_AVX2_EXTERNAL +INIT_XMM avx2 +VVC_SAO_BAND_FILTER 10, 8, 1 +INIT_YMM avx2 +VVC_SAO_BAND_FILTER 10, 16, 1 +VVC_SAO_BAND_FILTER 10, 32, 2 +VVC_SAO_BAND_FILTER 10, 48, 3 +VVC_SAO_BAND_FILTER 10, 64, 4 +VVC_SAO_BAND_FILTER 10, 80, 5 +VVC_SAO_BAND_FILTER 10, 96, 6 +VVC_SAO_BAND_FILTER 10, 112, 7 +VVC_SAO_BAND_FILTER 10, 128, 8 + +INIT_XMM avx2 +VVC_SAO_BAND_FILTER 12, 8, 1 +INIT_YMM avx2 +VVC_SAO_BAND_FILTER 12, 16, 1 +VVC_SAO_BAND_FILTER 12, 32, 2 +VVC_SAO_BAND_FILTER 12, 48, 3 +VVC_SAO_BAND_FILTER 12, 64, 4 +VVC_SAO_BAND_FILTER 12, 80, 5 +VVC_SAO_BAND_FILTER 12, 96, 6 +VVC_SAO_BAND_FILTER 12, 112, 7 +VVC_SAO_BAND_FILTER 12, 128, 8 +%endif + +;****************************************************************************** +;SAO Edge Filter +;****************************************************************************** + +%define MAX_PB_SIZE 64 +%define PADDING_SIZE 64 ; AV_INPUT_BUFFER_PADDING_SIZE +%define EDGE_SRCSTRIDE 4 * MAX_PB_SIZE + PADDING_SIZE + +%macro PMINUW 4 +%if cpuflag(sse4) + pminuw %1, %2, %3 +%else + psubusw %4, %2, %3 + psubw %1, %2, %4 +%endif +%endmacro + +%macro VVC_SAO_EDGE_FILTER_INIT 0 +%if WIN64 + movsxd eoq, dword eom +%elif ARCH_X86_64 + movsxd eoq, eod +%else + mov eoq, r4m +%endif + lea tmp2q, [pb_eo] + movsx a_strideq, byte [tmp2q+eoq*4+1] + movsx b_strideq, byte [tmp2q+eoq*4+3] + imul a_strideq, EDGE_SRCSTRIDE >> 1 + imul b_strideq, EDGE_SRCSTRIDE >> 1 + movsx tmpq, byte [tmp2q+eoq*4] + add a_strideq, tmpq + movsx tmpq, byte [tmp2q+eoq*4+2] + add b_strideq, tmpq +%endmacro + +;void ff_vvc_sao_edge_filter___(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, int16_t *sao_offset_val, +; int eo, int width, int height); +%macro VVC_SAO_EDGE_FILTER 3 +%if ARCH_X86_64 +cglobal vvc_sao_edge_filter_%2_%1, 4, 9, 16, dst, src, dststride, offset, eo, a_stride, b_stride, height, tmp +%define tmp2q heightq + VVC_SAO_EDGE_FILTER_INIT + mov heightd, r6m + add a_strideq, a_strideq + add b_strideq, b_strideq + +%else ; ARCH_X86_32 +cglobal vvc_sao_edge_filter_%2_%1, 1, 6, 8, 5*mmsize, dst, src, dststride, a_stride, b_stride, height +%define eoq srcq +%define tmpq heightq +%define tmp2q dststrideq +%define offsetq heightq +%define m8 m1 +%define m9 m2 +%define m10 m3 +%define m11 m4 +%define m12 m5 + VVC_SAO_EDGE_FILTER_INIT + mov srcq, srcm + mov offsetq, r3m + mov dststrideq, dststridem + add a_strideq, a_strideq + add b_strideq, b_strideq + +%endif ; ARCH + +%if mmsize > 16 + SPLATW m8, [offsetq+2] + SPLATW m9, [offsetq+4] + SPLATW m10, [offsetq+0] + SPLATW m11, [offsetq+6] + SPLATW m12, [offsetq+8] +%else + movq m10, [offsetq+0] + movd m12, [offsetq+6] + SPLATW m8, xm10, 1 + SPLATW m9, xm10, 2 + SPLATW m10, xm10, 0 + SPLATW m11, xm12, 0 + SPLATW m12, xm12, 1 +%endif + pxor m0, m0 +%if ARCH_X86_64 + mova m13, [pw_m1] + mova m14, [pw_1] + mova m15, [pw_2] +%else + mov heightd, r6m + mova [rsp+mmsize*0], m8 + mova [rsp+mmsize*1], m9 + mova [rsp+mmsize*2], m10 + mova [rsp+mmsize*3], m11 + mova [rsp+mmsize*4], m12 +%endif + +align 16 +.loop: + +%assign i 0 +%rep %3 + mova m1, [srcq + i] + movu m2, [srcq+a_strideq + i] + movu m3, [srcq+b_strideq + i] + PMINUW m4, m1, m2, m6 + PMINUW m5, m1, m3, m7 + pcmpeqw m2, m4 + pcmpeqw m3, m5 + pcmpeqw m4, m1 + pcmpeqw m5, m1 + psubw m4, m2 + psubw m5, m3 + + paddw m4, m5 + pcmpeqw m2, m4, [pw_m2] +%if ARCH_X86_64 + pcmpeqw m3, m4, m13 + pcmpeqw m5, m4, m0 + pcmpeqw m6, m4, m14 + pcmpeqw m7, m4, m15 + pand m2, m8 + pand m3, m9 + pand m5, m10 + pand m6, m11 + pand m7, m12 +%else + pcmpeqw m3, m4, [pw_m1] + pcmpeqw m5, m4, m0 + pcmpeqw m6, m4, [pw_1] + pcmpeqw m7, m4, [pw_2] + pand m2, [rsp+mmsize*0] + pand m3, [rsp+mmsize*1] + pand m5, [rsp+mmsize*2] + pand m6, [rsp+mmsize*3] + pand m7, [rsp+mmsize*4] +%endif + paddw m2, m3 + paddw m5, m6 + paddw m2, m7 + paddw m2, m1 + paddw m2, m5 + CLIPW m2, m0, [pw_mask %+ %1] + mova [dstq + i], m2 +%assign i i+mmsize +%endrep + + add dstq, dststrideq + add srcq, EDGE_SRCSTRIDE + dec heightd + jg .loop + RET +%endmacro + +%if HAVE_AVX2_EXTERNAL +INIT_XMM avx2 +VVC_SAO_EDGE_FILTER 10, 8, 1 +INIT_YMM avx2 +VVC_SAO_EDGE_FILTER 10, 16, 1 +VVC_SAO_EDGE_FILTER 10, 32, 2 +VVC_SAO_EDGE_FILTER 10, 48, 3 +VVC_SAO_EDGE_FILTER 10, 64, 4 +VVC_SAO_EDGE_FILTER 10, 80, 5 +VVC_SAO_EDGE_FILTER 10, 96, 6 +VVC_SAO_EDGE_FILTER 10, 112, 7 +VVC_SAO_EDGE_FILTER 10, 128, 8 + +INIT_XMM avx2 +VVC_SAO_EDGE_FILTER 12, 8, 1 +INIT_YMM avx2 +VVC_SAO_EDGE_FILTER 12, 16, 1 +VVC_SAO_EDGE_FILTER 12, 32, 2 +VVC_SAO_EDGE_FILTER 12, 48, 3 +VVC_SAO_EDGE_FILTER 12, 64, 4 +VVC_SAO_EDGE_FILTER 12, 80, 5 +VVC_SAO_EDGE_FILTER 12, 96, 6 +VVC_SAO_EDGE_FILTER 12, 112, 7 +VVC_SAO_EDGE_FILTER 12, 128, 8 +%endif diff --git a/libavcodec/x86/vvcdsp.h b/libavcodec/x86/vvcdsp.h new file mode 100644 index 00000000000..bb963cf42df --- /dev/null +++ b/libavcodec/x86/vvcdsp.h @@ -0,0 +1,55 @@ +/* + * VVC DSP for x86 + * + * Copyright (C) 2022 Nuo Mi + * + * + * 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_X86_VVCDSP_H +#define AVCODEC_X86_VVCDSP_H + +void ff_vvc_alf_filter_luma_16bpc_avx2(uint8_t *dst, ptrdiff_t dst_stride, + const uint8_t *src, ptrdiff_t src_stride, ptrdiff_t width, ptrdiff_t height, + const int16_t *filter, const int16_t *clip, ptrdiff_t stride, ptrdiff_t vb_pos, ptrdiff_t pixel_max); + +void ff_vvc_alf_filter_chroma_16bpc_avx2(uint8_t *dst, ptrdiff_t dst_stride, + const uint8_t *src, ptrdiff_t src_stride, ptrdiff_t width, ptrdiff_t height, + const int16_t *filter, const int16_t *clip, ptrdiff_t stride, ptrdiff_t vb_pos, ptrdiff_t pixel_max); + +void ff_vvc_alf_filter_luma_8bpc_avx2(uint8_t *dst, ptrdiff_t dst_stride, + const uint8_t *src, ptrdiff_t src_stride, ptrdiff_t width, ptrdiff_t height, + const int16_t *filter, const int16_t *clip, ptrdiff_t stride, ptrdiff_t vb_pos, ptrdiff_t pixel_max); + +void ff_vvc_alf_filter_chroma_8bpc_avx2(uint8_t *dst, ptrdiff_t dst_stride, + const uint8_t *src, ptrdiff_t src_stride, ptrdiff_t width, ptrdiff_t height, + const int16_t *filter, const int16_t *clip, ptrdiff_t stride, ptrdiff_t vb_pos, ptrdiff_t pixel_max); + +/* each word in gradient_sum is sum gradient of 8x2 pixels, adjacent word share 4x2 pixels */ +/* gradient_sum's stride aligned to 16 words */ +void ff_vvc_alf_classify_grad_8bpc_avx2(int *gradient_sum, + const uint8_t *src, ptrdiff_t src_stride, intptr_t width, intptr_t height, intptr_t vb_pos); +void ff_vvc_alf_classify_grad_16bpc_avx2(int *gradient_sum, + const uint8_t *src, ptrdiff_t src_stride, intptr_t width, intptr_t height, intptr_t vb_pos); + +void ff_vvc_alf_classify_8bpc_avx2(int *class_idx, int *transpose_idx, const int *gradient_sum, + intptr_t width, intptr_t height, intptr_t vb_pos, intptr_t bit_depth); +void ff_vvc_alf_classify_16bpc_avx2(int *class_idx, int *transpose_idx, const int *gradient_sum, + intptr_t width, intptr_t height, intptr_t vb_pos, intptr_t bit_depth); + +#endif //AVCODEC_X86_VVCDSP_H diff --git a/libavcodec/x86/vvcdsp_init.c b/libavcodec/x86/vvcdsp_init.c new file mode 100644 index 00000000000..a5849e3a2ed --- /dev/null +++ b/libavcodec/x86/vvcdsp_init.c @@ -0,0 +1,300 @@ +/* + * VVC DSP init for x86 + * + * Copyright (C) 2022-2023 Nuo Mi + * Copyright (c) 2023 Wu Jianhua + * + * 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/cpu.h" +#include "libavcodec/vvc/vvcdec.h" +#include "libavcodec/vvc/vvcdsp.h" +#include "libavutil/x86/asm.h" +#include "libavutil/x86/cpu.h" +#include "libavcodec/x86/vvcdsp.h" +#include +#include + +#define PIXEL_MAX_8 ((1 << 8) - 1) +#define PIXEL_MAX_10 ((1 << 10) - 1) +#define PIXEL_MAX_12 ((1 << 12) - 1) + +static void alf_filter_luma_8_avx2(uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *src, ptrdiff_t src_stride, + int width, int height, const int16_t *filter, const int16_t *clip, const int vb_pos) +{ + const int param_stride = (width >> 2) * ALF_NUM_COEFF_LUMA; + ff_vvc_alf_filter_luma_8bpc_avx2(dst, dst_stride, src, src_stride, width, height, + filter, clip, param_stride, vb_pos, PIXEL_MAX_8); +} + +static void alf_filter_luma_16bpc_avx2(uint8_t *dst, const ptrdiff_t dst_stride, + const uint8_t *src, const ptrdiff_t src_stride, const int width, const int height, + const int16_t *filter, const int16_t *clip, const int vb_pos, const int pixel_max) +{ + const int param_stride = (width >> 2) * ALF_NUM_COEFF_LUMA; + ff_vvc_alf_filter_luma_16bpc_avx2(dst, dst_stride, src, src_stride, width, height, + filter, clip, param_stride, vb_pos, pixel_max); +} + +static void alf_filter_luma_10_avx2(uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *src, ptrdiff_t src_stride, + int width, int height, const int16_t *filter, const int16_t *clip, const int vb_pos) +{ + alf_filter_luma_16bpc_avx2(dst, dst_stride, src, src_stride, width, height, + filter, clip, vb_pos, PIXEL_MAX_10); +} + +static void alf_filter_luma_12_avx2(uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *src, ptrdiff_t src_stride, + int width, int height, const int16_t *filter, const int16_t *clip, const int vb_pos) +{ + alf_filter_luma_16bpc_avx2(dst, dst_stride, src, src_stride, width, height, + filter, clip, vb_pos, PIXEL_MAX_12); +} + +static void alf_filter_chroma_8_avx2(uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *src, ptrdiff_t src_stride, + int width, int height, const int16_t *filter, const int16_t *clip, const int vb_pos) +{ + ff_vvc_alf_filter_chroma_8bpc_avx2(dst, dst_stride, src, src_stride, width, height, + filter, clip, 0, vb_pos, PIXEL_MAX_8); +} + +static void alf_filter_chroma_16bpc_avx2(uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *src, ptrdiff_t src_stride, + int width, int height, const int16_t *filter, const int16_t *clip, const int vb_pos, const int pixel_max) +{ + ff_vvc_alf_filter_chroma_16bpc_avx2(dst, dst_stride, src, src_stride, width, height, + filter, clip, 0, vb_pos, pixel_max); +} + +static void alf_filter_chroma_10_avx2(uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *src, ptrdiff_t src_stride, + int width, int height, const int16_t *filter, const int16_t *clip, const int vb_pos) +{ + alf_filter_chroma_16bpc_avx2(dst, dst_stride, src, src_stride, width, height, + filter, clip, vb_pos, PIXEL_MAX_10); +} + +static void alf_filter_chroma_12_avx2(uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *src, ptrdiff_t src_stride, + int width, int height, const int16_t *filter, const int16_t *clip, const int vb_pos) +{ + alf_filter_chroma_16bpc_avx2(dst, dst_stride, src, src_stride, width, height, + filter, clip, vb_pos, PIXEL_MAX_12); +} + +static void alf_classify_8_avx2(int *class_idx, int *transpose_idx, + const uint8_t *src, ptrdiff_t src_stride, int width, int height, int vb_pos, int *gradient_tmp) +{ + ff_vvc_alf_classify_grad_8bpc_avx2(gradient_tmp, src, src_stride, width, height, vb_pos); + ff_vvc_alf_classify_8bpc_avx2(class_idx, transpose_idx, gradient_tmp, width, height, vb_pos, 8); +} + +static void alf_classify_10_avx2(int *class_idx, int *transpose_idx, + const uint8_t *src, ptrdiff_t src_stride, int width, int height, int vb_pos, int *gradient_tmp) +{ + ff_vvc_alf_classify_grad_16bpc_avx2(gradient_tmp, src, src_stride, width, height, vb_pos); + ff_vvc_alf_classify_16bpc_avx2(class_idx, transpose_idx, gradient_tmp, width, height, vb_pos, 10); +} + +static void alf_classify_12_avx2(int *class_idx, int *transpose_idx, + const uint8_t *src, ptrdiff_t src_stride, int width, int height, int vb_pos, int *gradient_tmp) +{ + ff_vvc_alf_classify_grad_16bpc_avx2(gradient_tmp, src, src_stride, width, height, vb_pos); + ff_vvc_alf_classify_16bpc_avx2(class_idx, transpose_idx, gradient_tmp, width, height, vb_pos, 12); +} + +#define ALF_DSP(depth) do { \ + c->alf.filter[LUMA] = alf_filter_luma_##depth##_avx2; \ + c->alf.filter[CHROMA] = alf_filter_chroma_##depth##_avx2; \ + c->alf.classify = alf_classify_##depth##_avx2; \ + } while (0) + + +#define SAO_BAND_FILTER_FUNCS(bitd, opt) \ +void ff_vvc_sao_band_filter_8_##bitd##_##opt(uint8_t *_dst, const uint8_t *_src, ptrdiff_t _stride_dst, ptrdiff_t _stride_src, \ + const int16_t *sao_offset_val, int sao_left_class, int width, int height); \ +void ff_vvc_sao_band_filter_16_##bitd##_##opt(uint8_t *_dst, const uint8_t *_src, ptrdiff_t _stride_dst, ptrdiff_t _stride_src, \ + const int16_t *sao_offset_val, int sao_left_class, int width, int height); \ +void ff_vvc_sao_band_filter_32_##bitd##_##opt(uint8_t *_dst, const uint8_t *_src, ptrdiff_t _stride_dst, ptrdiff_t _stride_src, \ + const int16_t *sao_offset_val, int sao_left_class, int width, int height); \ +void ff_vvc_sao_band_filter_48_##bitd##_##opt(uint8_t *_dst, const uint8_t *_src, ptrdiff_t _stride_dst, ptrdiff_t _stride_src, \ + const int16_t *sao_offset_val, int sao_left_class, int width, int height); \ +void ff_vvc_sao_band_filter_64_##bitd##_##opt(uint8_t *_dst, const uint8_t *_src, ptrdiff_t _stride_dst, ptrdiff_t _stride_src, \ + const int16_t *sao_offset_val, int sao_left_class, int width, int height); \ +void ff_vvc_sao_band_filter_80_##bitd##_##opt(uint8_t *_dst, const uint8_t *_src, ptrdiff_t _stride_dst, ptrdiff_t _stride_src, \ + const int16_t *sao_offset_val, int sao_left_class, int width, int height); \ +void ff_vvc_sao_band_filter_96_##bitd##_##opt(uint8_t *_dst, const uint8_t *_src, ptrdiff_t _stride_dst, ptrdiff_t _stride_src, \ + const int16_t *sao_offset_val, int sao_left_class, int width, int height); \ +void ff_vvc_sao_band_filter_112_##bitd##_##opt(uint8_t *_dst, const uint8_t *_src, ptrdiff_t _stride_dst, ptrdiff_t _stride_src, \ + const int16_t *sao_offset_val, int sao_left_class, int width, int height); \ +void ff_vvc_sao_band_filter_128_##bitd##_##opt(uint8_t *_dst, const uint8_t *_src, ptrdiff_t _stride_dst, ptrdiff_t _stride_src, \ + const int16_t *sao_offset_val, int sao_left_class, int width, int height); \ + +SAO_BAND_FILTER_FUNCS(8, avx2) +SAO_BAND_FILTER_FUNCS(10, avx2) +SAO_BAND_FILTER_FUNCS(12, avx2) + +#define SAO_BAND_INIT(bitd, opt) do { \ + c->sao.band_filter[0] = ff_vvc_sao_band_filter_8_##bitd##_##opt; \ + c->sao.band_filter[1] = ff_vvc_sao_band_filter_16_##bitd##_##opt; \ + c->sao.band_filter[2] = ff_vvc_sao_band_filter_32_##bitd##_##opt; \ + c->sao.band_filter[3] = ff_vvc_sao_band_filter_48_##bitd##_##opt; \ + c->sao.band_filter[4] = ff_vvc_sao_band_filter_64_##bitd##_##opt; \ + c->sao.band_filter[5] = ff_vvc_sao_band_filter_80_##bitd##_##opt; \ + c->sao.band_filter[6] = ff_vvc_sao_band_filter_96_##bitd##_##opt; \ + c->sao.band_filter[7] = ff_vvc_sao_band_filter_112_##bitd##_##opt; \ + c->sao.band_filter[8] = ff_vvc_sao_band_filter_128_##bitd##_##opt; \ +} while (0) + +#define SAO_EDGE_FILTER_FUNCS(bitd, opt) \ +void ff_vvc_sao_edge_filter_8_##bitd##_##opt(uint8_t *_dst, const uint8_t *_src, ptrdiff_t stride_dst, \ + const int16_t *sao_offset_val, int eo, int width, int height); \ +void ff_vvc_sao_edge_filter_16_##bitd##_##opt(uint8_t *_dst, const uint8_t *_src, ptrdiff_t stride_dst, \ + const int16_t *sao_offset_val, int eo, int width, int height); \ +void ff_vvc_sao_edge_filter_32_##bitd##_##opt(uint8_t *_dst, const uint8_t *_src, ptrdiff_t stride_dst, \ + const int16_t *sao_offset_val, int eo, int width, int height); \ +void ff_vvc_sao_edge_filter_48_##bitd##_##opt(uint8_t *_dst, const uint8_t *_src, ptrdiff_t stride_dst, \ + const int16_t *sao_offset_val, int eo, int width, int height); \ +void ff_vvc_sao_edge_filter_64_##bitd##_##opt(uint8_t *_dst, const uint8_t *_src, ptrdiff_t stride_dst, \ + const int16_t *sao_offset_val, int eo, int width, int height); \ +void ff_vvc_sao_edge_filter_80_##bitd##_##opt(uint8_t *_dst, const uint8_t *_src, ptrdiff_t stride_dst, \ + const int16_t *sao_offset_val, int eo, int width, int height); \ +void ff_vvc_sao_edge_filter_96_##bitd##_##opt(uint8_t *_dst, const uint8_t *_src, ptrdiff_t stride_dst, \ + const int16_t *sao_offset_val, int eo, int width, int height); \ +void ff_vvc_sao_edge_filter_112_##bitd##_##opt(uint8_t *_dst, const uint8_t *_src, ptrdiff_t stride_dst, \ + const int16_t *sao_offset_val, int eo, int width, int height); \ +void ff_vvc_sao_edge_filter_128_##bitd##_##opt(uint8_t *_dst, const uint8_t *_src, ptrdiff_t stride_dst, \ + const int16_t *sao_offset_val, int eo, int width, int height); \ + + +SAO_EDGE_FILTER_FUNCS(8, avx2) +SAO_EDGE_FILTER_FUNCS(10, avx2) +SAO_EDGE_FILTER_FUNCS(12, avx2) + +#define SAO_EDGE_INIT(bitd, opt) do { \ + c->sao.edge_filter[0] = ff_vvc_sao_edge_filter_8_##bitd##_##opt; \ + c->sao.edge_filter[1] = ff_vvc_sao_edge_filter_16_##bitd##_##opt; \ + c->sao.edge_filter[2] = ff_vvc_sao_edge_filter_32_##bitd##_##opt; \ + c->sao.edge_filter[3] = ff_vvc_sao_edge_filter_48_##bitd##_##opt; \ + c->sao.edge_filter[4] = ff_vvc_sao_edge_filter_64_##bitd##_##opt; \ + c->sao.edge_filter[5] = ff_vvc_sao_edge_filter_80_##bitd##_##opt; \ + c->sao.edge_filter[6] = ff_vvc_sao_edge_filter_96_##bitd##_##opt; \ + c->sao.edge_filter[7] = ff_vvc_sao_edge_filter_112_##bitd##_##opt; \ + c->sao.edge_filter[8] = ff_vvc_sao_edge_filter_128_##bitd##_##opt; \ +} while (0) + +#define PUT_VVC_LUMA_8_FUNC(dir, opt) \ + void ff_vvc_put_vvc_luma_##dir##_8_##opt(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, \ + const int height, const intptr_t mx, const intptr_t my, const int width, \ + const int hf_idx, const int vf_idx); \ + +#define PUT_VVC_LUMA_16_FUNC(dir, opt) \ + void ff_vvc_put_vvc_luma_##dir##_16_##opt(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, \ + const int height, const intptr_t mx, const intptr_t my, const int width, \ + const int hf_idx, const int vf_idx, const int bitdepth); + +#define PUT_VVC_LUMA_FUNCS(bitd, opt) \ + PUT_VVC_LUMA_##bitd##_FUNC(h, opt) \ + PUT_VVC_LUMA_##bitd##_FUNC(v, opt) \ + PUT_VVC_LUMA_##bitd##_FUNC(hv, opt) + +#define PUT_VVC_LUMA_FORWARD_FUNC(dir, bitd, opt) \ +static void ff_vvc_put_vvc_luma_##dir##_##bitd##_##opt(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, \ + const int height, const intptr_t mx, const intptr_t my, const int width, \ + const int hf_idx, const int vf_idx) \ +{ \ + ff_vvc_put_vvc_luma_##dir##_16_##opt(dst, _src, _src_stride, height, mx, my, width, hf_idx, vf_idx, bitd); \ +} + +#define PUT_VVC_LUMA_FORWARD_FUNCS(bitd, opt) \ + PUT_VVC_LUMA_FORWARD_FUNC(h, bitd, opt) \ + PUT_VVC_LUMA_FORWARD_FUNC(v, bitd, opt) \ + PUT_VVC_LUMA_FORWARD_FUNC(hv, bitd, opt) + +PUT_VVC_LUMA_FUNCS(8, avx2) +PUT_VVC_LUMA_FUNCS(16, avx2) +PUT_VVC_LUMA_FORWARD_FUNCS(10, avx2) +PUT_VVC_LUMA_FORWARD_FUNCS(12, avx2) + +#if HAVE_AVX512ICL_EXTERNAL +PUT_VVC_LUMA_FUNCS(16, avx512icl) +PUT_VVC_LUMA_FORWARD_FUNCS(10, avx512icl) +PUT_VVC_LUMA_FORWARD_FUNCS(12, avx512icl) +#endif + +#define PUT_VVC_LUMA_INIT(bitd, opt) do { \ + c->inter.put[LUMA][0][1] = ff_vvc_put_vvc_luma_h_##bitd##_##opt; \ + c->inter.put[LUMA][1][0] = ff_vvc_put_vvc_luma_v_##bitd##_##opt; \ + c->inter.put[LUMA][1][1] = ff_vvc_put_vvc_luma_hv_##bitd##_##opt; \ +} while (0) + +void ff_vvc_dsp_init_x86(VVCDSPContext *const c, const int bit_depth) +{ + const int cpu_flags = av_get_cpu_flags(); + + if (EXTERNAL_AVX2(cpu_flags)) { + switch (bit_depth) { + case 8: + ALF_DSP(8); + PUT_VVC_LUMA_INIT(8, avx2); + c->sao.band_filter[0] = ff_vvc_sao_band_filter_8_8_avx2; + c->sao.band_filter[1] = ff_vvc_sao_band_filter_16_8_avx2; + break; + case 10: + ALF_DSP(10); + PUT_VVC_LUMA_INIT(10, avx2); + c->sao.band_filter[0] = ff_vvc_sao_band_filter_8_10_avx2; + break; + case 12: + ALF_DSP(12); + PUT_VVC_LUMA_INIT(12, avx2); + break; + default: + break; + } + } + if (EXTERNAL_AVX2_FAST(cpu_flags)) { + switch (bit_depth) { + case 8: + SAO_BAND_INIT(8, avx2); + SAO_EDGE_INIT(8, avx2); + break; + case 10: + SAO_BAND_INIT(10, avx2); + SAO_EDGE_INIT(10, avx2); + break; + case 12: + SAO_BAND_INIT(12, avx2); + SAO_EDGE_INIT(12, avx2); + default: + break; + } + } +#if HAVE_AVX512ICL_EXTERNAL + if (EXTERNAL_AVX512ICL(cpu_flags)) { + switch (bit_depth) { + case 10: + PUT_VVC_LUMA_INIT(10, avx512icl); + break; + case 12: + PUT_VVC_LUMA_INIT(12, avx512icl); + break; + default: + break; + } + } +#endif +} diff --git a/libavcodec/xbmdec.c b/libavcodec/xbmdec.c index a0cc1cb8c61..ecfc957a3f6 100644 --- a/libavcodec/xbmdec.c +++ b/libavcodec/xbmdec.c @@ -130,7 +130,7 @@ static int xbm_decode_frame(AVCodecContext *avctx, AVFrame *p, } } - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; p->pict_type = AV_PICTURE_TYPE_I; *got_frame = 1; diff --git a/libavcodec/xbmenc.c b/libavcodec/xbmenc.c index 664c6599bff..cd8b73afa3e 100644 --- a/libavcodec/xbmenc.c +++ b/libavcodec/xbmenc.c @@ -82,7 +82,7 @@ const FFCodec ff_xbm_encoder = { CODEC_LONG_NAME("XBM (X BitMap) image"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_XBM, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, FF_CODEC_ENCODE_CB(xbm_encode_frame), .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_MONOWHITE, AV_PIX_FMT_NONE }, diff --git a/libavcodec/xfaceenc.c b/libavcodec/xfaceenc.c index 7125f1f085c..4998d42ea50 100644 --- a/libavcodec/xfaceenc.c +++ b/libavcodec/xfaceenc.c @@ -216,7 +216,7 @@ const FFCodec ff_xface_encoder = { CODEC_LONG_NAME("X-face image"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_XFACE, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_MONOWHITE, AV_PIX_FMT_NONE }, .priv_data_size = sizeof(XFaceContext), FF_CODEC_ENCODE_CB(xface_encode_frame), diff --git a/libavcodec/xl.c b/libavcodec/xl.c index 283cd39aa74..f008d56e896 100644 --- a/libavcodec/xl.c +++ b/libavcodec/xl.c @@ -60,7 +60,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; Y = p->data[0]; U = p->data[1]; diff --git a/libavcodec/xpmdec.c b/libavcodec/xpmdec.c index ff1f51dd32b..5bc02378c80 100644 --- a/libavcodec/xpmdec.c +++ b/libavcodec/xpmdec.c @@ -354,6 +354,9 @@ static int xpm_decode_frame(AVCodecContext *avctx, AVFrame *p, return AVERROR_INVALIDDATA; } + if (size > SIZE_MAX / 4) + return AVERROR(ENOMEM); + size *= 4; ptr += mod_strcspn(ptr, ",") + 1; @@ -419,7 +422,7 @@ static int xpm_decode_frame(AVCodecContext *avctx, AVFrame *p, ptr += mod_strcspn(ptr, ",") + 1; } - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; p->pict_type = AV_PICTURE_TYPE_I; *got_frame = 1; diff --git a/libavcodec/xwddec.c b/libavcodec/xwddec.c index 6c5bc44a029..f691587be90 100644 --- a/libavcodec/xwddec.c +++ b/libavcodec/xwddec.c @@ -216,7 +216,7 @@ static int xwd_decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; p->pict_type = AV_PICTURE_TYPE_I; if (avctx->pix_fmt == AV_PIX_FMT_PAL8) { diff --git a/libavcodec/xwdenc.c b/libavcodec/xwdenc.c index 6c588f3acca..08554d86323 100644 --- a/libavcodec/xwdenc.c +++ b/libavcodec/xwdenc.c @@ -216,7 +216,7 @@ const FFCodec ff_xwd_encoder = { CODEC_LONG_NAME("XWD (X Window Dump) image"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_XWD, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, FF_CODEC_ENCODE_CB(xwd_encode_frame), .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_BGRA, AV_PIX_FMT_RGBA, diff --git a/libavcodec/y41pdec.c b/libavcodec/y41pdec.c index b461f349ad2..14e36dc9985 100644 --- a/libavcodec/y41pdec.c +++ b/libavcodec/y41pdec.c @@ -51,7 +51,7 @@ static int y41p_decode_frame(AVCodecContext *avctx, AVFrame *pic, if ((ret = ff_get_buffer(avctx, pic, 0)) < 0) return ret; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; pic->pict_type = AV_PICTURE_TYPE_I; for (i = avctx->height - 1; i >= 0 ; i--) { diff --git a/libavcodec/y41penc.c b/libavcodec/y41penc.c index d3ef88c2ce2..e86769da66b 100644 --- a/libavcodec/y41penc.c +++ b/libavcodec/y41penc.c @@ -82,7 +82,7 @@ const FFCodec ff_y41p_encoder = { CODEC_LONG_NAME("Uncompressed YUV 4:1:1 12-bit"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_Y41P, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .init = y41p_encode_init, FF_CODEC_ENCODE_CB(y41p_encode_frame), .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV411P, diff --git a/libavcodec/ylc.c b/libavcodec/ylc.c index 29c10f05da0..c0c4d27c09f 100644 --- a/libavcodec/ylc.c +++ b/libavcodec/ylc.c @@ -427,7 +427,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, } p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; diff --git a/libavcodec/yop.c b/libavcodec/yop.c index 816fe8bdc8f..1294c5cc004 100644 --- a/libavcodec/yop.c +++ b/libavcodec/yop.c @@ -207,7 +207,7 @@ static int yop_decode_frame(AVCodecContext *avctx, AVFrame *rframe, if ((ret = ff_reget_buffer(avctx, frame, 0)) < 0) return ret; - if (!avctx->frame_number) + if (!avctx->frame_num) memset(frame->data[1], 0, AVPALETTE_SIZE); s->dstbuf = frame->data[0]; @@ -232,7 +232,11 @@ static int yop_decode_frame(AVCodecContext *avctx, AVFrame *rframe, (palette[i + firstcolor] >> 6) & 0x30303; } +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif for (y = 0; y < avctx->height; y += 2) { for (x = 0; x < avctx->width; x += 2) { diff --git a/libavcodec/yuv4dec.c b/libavcodec/yuv4dec.c index 15424b1940b..ad83a2125c0 100644 --- a/libavcodec/yuv4dec.c +++ b/libavcodec/yuv4dec.c @@ -46,7 +46,7 @@ static int yuv4_decode_frame(AVCodecContext *avctx, AVFrame *pic, if ((ret = ff_get_buffer(avctx, pic, 0)) < 0) return ret; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; pic->pict_type = AV_PICTURE_TYPE_I; y = pic->data[0]; diff --git a/libavcodec/yuv4enc.c b/libavcodec/yuv4enc.c index 27e786dd545..8123260d5db 100644 --- a/libavcodec/yuv4enc.c +++ b/libavcodec/yuv4enc.c @@ -64,7 +64,7 @@ const FFCodec ff_yuv4_encoder = { CODEC_LONG_NAME("Uncompressed packed 4:2:0"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_YUV4, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }, FF_CODEC_ENCODE_CB(yuv4_encode_frame), }; diff --git a/libavcodec/zerocodec.c b/libavcodec/zerocodec.c index 42fb24ff6c5..6c3bcebce04 100644 --- a/libavcodec/zerocodec.c +++ b/libavcodec/zerocodec.c @@ -40,7 +40,7 @@ static int zerocodec_decode_frame(AVCodecContext *avctx, AVFrame *pic, int i, j, zret, ret; if (avpkt->flags & AV_PKT_FLAG_KEY) { - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; pic->pict_type = AV_PICTURE_TYPE_I; } else { if (!prev) { @@ -50,7 +50,7 @@ static int zerocodec_decode_frame(AVCodecContext *avctx, AVFrame *pic, prev += (avctx->height - 1) * prev_pic->linesize[0]; - pic->key_frame = 0; + pic->flags &= ~AV_FRAME_FLAG_KEY; pic->pict_type = AV_PICTURE_TYPE_P; } diff --git a/libavcodec/zmbv.c b/libavcodec/zmbv.c index 0b44851811f..d309a8612b4 100644 --- a/libavcodec/zmbv.c +++ b/libavcodec/zmbv.c @@ -559,11 +559,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, return ret; if (c->flags & ZMBV_KEYFRAME) { - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; zmbv_decode_intra(c); } else { - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_P; if (c->decomp_len < 2LL * ((c->width + c->bw - 1) / c->bw) * ((c->height + c->bh - 1) / c->bh)) return AVERROR_INVALIDDATA; diff --git a/libavcodec/zmbvenc.c b/libavcodec/zmbvenc.c index c12f783d5ad..d20330568d9 100644 --- a/libavcodec/zmbvenc.c +++ b/libavcodec/zmbvenc.c @@ -416,7 +416,7 @@ const FFCodec ff_zmbv_encoder = { CODEC_LONG_NAME("Zip Motion Blocks Video"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_ZMBV, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(ZmbvEncContext), .init = encode_init, FF_CODEC_ENCODE_CB(encode_frame), diff --git a/libavdevice/Makefile b/libavdevice/Makefile index 8a62822b69e..c30449201d4 100644 --- a/libavdevice/Makefile +++ b/libavdevice/Makefile @@ -57,6 +57,7 @@ OBJS-$(CONFIG_LIBDC1394_INDEV) += libdc1394.o # Objects duplicated from other libraries for shared builds SHLIBOBJS-$(CONFIG_DECKLINK_INDEV) += reverse.o +SHLIBOBJS-$(CONFIG_DECKLINK_OUTDEV) += ccfifo.o # Windows resource file SHLIBOBJS-$(HAVE_GNU_WINDRES) += avdeviceres.o diff --git a/libavdevice/alldevices.c b/libavdevice/alldevices.c index 22323a0a44a..8a90fcb5d78 100644 --- a/libavdevice/alldevices.c +++ b/libavdevice/alldevices.c @@ -19,40 +19,41 @@ */ #include "libavformat/internal.h" +#include "libavformat/mux.h" #include "avdevice.h" /* devices */ extern const AVInputFormat ff_alsa_demuxer; -extern const AVOutputFormat ff_alsa_muxer; +extern const FFOutputFormat ff_alsa_muxer; extern const AVInputFormat ff_android_camera_demuxer; -extern const AVOutputFormat ff_audiotoolbox_muxer; +extern const FFOutputFormat ff_audiotoolbox_muxer; extern const AVInputFormat ff_avfoundation_demuxer; extern const AVInputFormat ff_bktr_demuxer; -extern const AVOutputFormat ff_caca_muxer; +extern const FFOutputFormat ff_caca_muxer; extern const AVInputFormat ff_decklink_demuxer; -extern const AVOutputFormat ff_decklink_muxer; +extern const FFOutputFormat ff_decklink_muxer; extern const AVInputFormat ff_dshow_demuxer; extern const AVInputFormat ff_fbdev_demuxer; -extern const AVOutputFormat ff_fbdev_muxer; +extern const FFOutputFormat ff_fbdev_muxer; extern const AVInputFormat ff_gdigrab_demuxer; extern const AVInputFormat ff_iec61883_demuxer; extern const AVInputFormat ff_jack_demuxer; extern const AVInputFormat ff_kmsgrab_demuxer; extern const AVInputFormat ff_lavfi_demuxer; extern const AVInputFormat ff_openal_demuxer; -extern const AVOutputFormat ff_opengl_muxer; +extern const FFOutputFormat ff_opengl_muxer; extern const AVInputFormat ff_oss_demuxer; -extern const AVOutputFormat ff_oss_muxer; +extern const FFOutputFormat ff_oss_muxer; extern const AVInputFormat ff_pulse_demuxer; -extern const AVOutputFormat ff_pulse_muxer; -extern const AVOutputFormat ff_sdl2_muxer; +extern const FFOutputFormat ff_pulse_muxer; +extern const FFOutputFormat ff_sdl2_muxer; extern const AVInputFormat ff_sndio_demuxer; -extern const AVOutputFormat ff_sndio_muxer; +extern const FFOutputFormat ff_sndio_muxer; extern const AVInputFormat ff_v4l2_demuxer; -extern const AVOutputFormat ff_v4l2_muxer; +extern const FFOutputFormat ff_v4l2_muxer; extern const AVInputFormat ff_vfwcap_demuxer; extern const AVInputFormat ff_xcbgrab_demuxer; -extern const AVOutputFormat ff_xv_muxer; +extern const FFOutputFormat ff_xv_muxer; /* external libraries */ extern const AVInputFormat ff_libcdio_demuxer; @@ -97,12 +98,12 @@ static const void *next_output(const AVOutputFormat *prev, AVClassCategory c2) const AVClass *pc; const AVClassCategory c1 = AV_CLASS_CATEGORY_DEVICE_OUTPUT; AVClassCategory category = AV_CLASS_CATEGORY_NA; - const AVOutputFormat *fmt = NULL; + const FFOutputFormat *fmt = NULL; int i = 0; while (prev && (fmt = outdev_list[i])) { i++; - if (prev == fmt) + if (prev == &fmt->p) break; } @@ -110,7 +111,7 @@ static const void *next_output(const AVOutputFormat *prev, AVClassCategory c2) fmt = outdev_list[i++]; if (!fmt) break; - pc = fmt->priv_class; + pc = fmt->p.priv_class; if (!pc) continue; category = pc->category; diff --git a/libavdevice/alsa_enc.c b/libavdevice/alsa_enc.c index e461829d03b..62a20c7ba48 100644 --- a/libavdevice/alsa_enc.c +++ b/libavdevice/alsa_enc.c @@ -165,18 +165,18 @@ static const AVClass alsa_muxer_class = { .category = AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT, }; -const AVOutputFormat ff_alsa_muxer = { - .name = "alsa", - .long_name = NULL_IF_CONFIG_SMALL("ALSA audio output"), +const FFOutputFormat ff_alsa_muxer = { + .p.name = "alsa", + .p.long_name = NULL_IF_CONFIG_SMALL("ALSA audio output"), .priv_data_size = sizeof(AlsaData), - .audio_codec = DEFAULT_CODEC_ID, - .video_codec = AV_CODEC_ID_NONE, + .p.audio_codec = DEFAULT_CODEC_ID, + .p.video_codec = AV_CODEC_ID_NONE, .write_header = audio_write_header, .write_packet = audio_write_packet, .write_trailer = ff_alsa_close, .write_uncoded_frame = audio_write_frame, .get_device_list = audio_get_device_list, .get_output_timestamp = audio_get_output_timestamp, - .flags = AVFMT_NOFILE, - .priv_class = &alsa_muxer_class, + .p.flags = AVFMT_NOFILE, + .p.priv_class = &alsa_muxer_class, }; diff --git a/libavdevice/audiotoolbox.m b/libavdevice/audiotoolbox.m index 27a1fd4a78c..aa49e2c992a 100644 --- a/libavdevice/audiotoolbox.m +++ b/libavdevice/audiotoolbox.m @@ -30,6 +30,7 @@ #include "libavutil/opt.h" #include "libavformat/internal.h" +#include "libavformat/mux.h" #include "libavutil/internal.h" #include "avdevice.h" @@ -294,15 +295,15 @@ static av_cold int at_write_trailer(AVFormatContext *avctx) .category = AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT, }; -const AVOutputFormat ff_audiotoolbox_muxer = { - .name = "audiotoolbox", - .long_name = NULL_IF_CONFIG_SMALL("AudioToolbox output device"), +const FFOutputFormat ff_audiotoolbox_muxer = { + .p.name = "audiotoolbox", + .p.long_name = NULL_IF_CONFIG_SMALL("AudioToolbox output device"), .priv_data_size = sizeof(ATContext), - .audio_codec = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE), - .video_codec = AV_CODEC_ID_NONE, + .p.audio_codec = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE), + .p.video_codec = AV_CODEC_ID_NONE, .write_header = at_write_header, .write_packet = at_write_packet, .write_trailer = at_write_trailer, - .flags = AVFMT_NOFILE, - .priv_class = &at_class, + .p.flags = AVFMT_NOFILE, + .p.priv_class = &at_class, }; diff --git a/libavdevice/avdevice.c b/libavdevice/avdevice.c index 58996404b3b..38110ddfdb2 100644 --- a/libavdevice/avdevice.c +++ b/libavdevice/avdevice.c @@ -19,19 +19,14 @@ #include "libavutil/avassert.h" #include "avdevice.h" #include "internal.h" - -#if FF_API_DEVICE_CAPABILITIES -const AVOption av_device_capabilities[] = { - { NULL } -}; -#endif +#include "libavformat/mux.h" int avdevice_app_to_dev_control_message(struct AVFormatContext *s, enum AVAppToDevMessageType type, void *data, size_t data_size) { - if (!s->oformat || !s->oformat->control_message) + if (!s->oformat || !ffofmt(s->oformat)->control_message) return AVERROR(ENOSYS); - return s->oformat->control_message(s, type, data, data_size); + return ffofmt(s->oformat)->control_message(s, type, data, data_size); } int avdevice_dev_to_app_control_message(struct AVFormatContext *s, enum AVDevToAppMessageType type, @@ -42,26 +37,13 @@ int avdevice_dev_to_app_control_message(struct AVFormatContext *s, enum AVDevToA return s->control_message_cb(s, type, data, data_size); } -#if FF_API_DEVICE_CAPABILITIES -int avdevice_capabilities_create(AVDeviceCapabilitiesQuery **caps, AVFormatContext *s, - AVDictionary **device_options) -{ - return AVERROR(ENOSYS); -} - -void avdevice_capabilities_free(AVDeviceCapabilitiesQuery **caps, AVFormatContext *s) -{ - return; -} -#endif - int avdevice_list_devices(AVFormatContext *s, AVDeviceInfoList **device_list) { int ret; av_assert0(s); av_assert0(device_list); av_assert0(s->oformat || s->iformat); - if ((s->oformat && !s->oformat->get_device_list) || + if ((s->oformat && !ffofmt(s->oformat)->get_device_list) || (s->iformat && !s->iformat->get_device_list)) { *device_list = NULL; return AVERROR(ENOSYS); @@ -72,7 +54,7 @@ int avdevice_list_devices(AVFormatContext *s, AVDeviceInfoList **device_list) /* no default device by default */ (*device_list)->default_device = -1; if (s->oformat) - ret = s->oformat->get_device_list(s, *device_list); + ret = ffofmt(s->oformat)->get_device_list(s, *device_list); else ret = s->iformat->get_device_list(s, *device_list); if (ret < 0) { diff --git a/libavdevice/avdevice.h b/libavdevice/avdevice.h index 185593053f7..887fd5e3c80 100644 --- a/libavdevice/avdevice.h +++ b/libavdevice/avdevice.h @@ -327,136 +327,6 @@ int avdevice_dev_to_app_control_message(struct AVFormatContext *s, enum AVDevToAppMessageType type, void *data, size_t data_size); -#if FF_API_DEVICE_CAPABILITIES -/** - * Following API allows user to probe device capabilities (supported codecs, - * pixel formats, sample formats, resolutions, channel counts, etc). - * It is build on top op AVOption API. - * Queried capabilities make it possible to set up converters of video or audio - * parameters that fit to the device. - * - * List of capabilities that can be queried: - * - Capabilities valid for both audio and video devices: - * - codec: supported audio/video codecs. - * type: AV_OPT_TYPE_INT (AVCodecID value) - * - Capabilities valid for audio devices: - * - sample_format: supported sample formats. - * type: AV_OPT_TYPE_INT (AVSampleFormat value) - * - sample_rate: supported sample rates. - * type: AV_OPT_TYPE_INT - * - channels: supported number of channels. - * type: AV_OPT_TYPE_INT - * - channel_layout: supported channel layouts. - * type: AV_OPT_TYPE_INT64 - * - Capabilities valid for video devices: - * - pixel_format: supported pixel formats. - * type: AV_OPT_TYPE_INT (AVPixelFormat value) - * - window_size: supported window sizes (describes size of the window size presented to the user). - * type: AV_OPT_TYPE_IMAGE_SIZE - * - frame_size: supported frame sizes (describes size of provided video frames). - * type: AV_OPT_TYPE_IMAGE_SIZE - * - fps: supported fps values - * type: AV_OPT_TYPE_RATIONAL - * - * Value of the capability may be set by user using av_opt_set() function - * and AVDeviceCapabilitiesQuery object. Following queries will - * limit results to the values matching already set capabilities. - * For example, setting a codec may impact number of formats or fps values - * returned during next query. Setting invalid value may limit results to zero. - * - * Example of the usage basing on opengl output device: - * - * @code - * AVFormatContext *oc = NULL; - * AVDeviceCapabilitiesQuery *caps = NULL; - * AVOptionRanges *ranges; - * int ret; - * - * if ((ret = avformat_alloc_output_context2(&oc, NULL, "opengl", NULL)) < 0) - * goto fail; - * if (avdevice_capabilities_create(&caps, oc, NULL) < 0) - * goto fail; - * - * //query codecs - * if (av_opt_query_ranges(&ranges, caps, "codec", AV_OPT_MULTI_COMPONENT_RANGE)) < 0) - * goto fail; - * //pick codec here and set it - * av_opt_set(caps, "codec", AV_CODEC_ID_RAWVIDEO, 0); - * - * //query format - * if (av_opt_query_ranges(&ranges, caps, "pixel_format", AV_OPT_MULTI_COMPONENT_RANGE)) < 0) - * goto fail; - * //pick format here and set it - * av_opt_set(caps, "pixel_format", AV_PIX_FMT_YUV420P, 0); - * - * //query and set more capabilities - * - * fail: - * //clean up code - * avdevice_capabilities_free(&query, oc); - * avformat_free_context(oc); - * @endcode - */ - -/** - * Structure describes device capabilities. - * - * It is used by devices in conjunction with av_device_capabilities AVOption table - * to implement capabilities probing API based on AVOption API. Should not be used directly. - */ -typedef struct AVDeviceCapabilitiesQuery { - const AVClass *av_class; - AVFormatContext *device_context; - enum AVCodecID codec; - enum AVSampleFormat sample_format; - enum AVPixelFormat pixel_format; - int sample_rate; - int channels; - int64_t channel_layout; - int window_width; - int window_height; - int frame_width; - int frame_height; - AVRational fps; -} AVDeviceCapabilitiesQuery; - -/** - * AVOption table used by devices to implement device capabilities API. Should not be used by a user. - */ -attribute_deprecated -extern const AVOption av_device_capabilities[]; - -/** - * Initialize capabilities probing API based on AVOption API. - * - * avdevice_capabilities_free() must be called when query capabilities API is - * not used anymore. - * - * @param[out] caps Device capabilities data. Pointer to a NULL pointer must be passed. - * @param s Context of the device. - * @param device_options An AVDictionary filled with device-private options. - * On return this parameter will be destroyed and replaced with a dict - * containing options that were not found. May be NULL. - * The same options must be passed later to avformat_write_header() for output - * devices or avformat_open_input() for input devices, or at any other place - * that affects device-private options. - * - * @return >= 0 on success, negative otherwise. - */ -attribute_deprecated -int avdevice_capabilities_create(AVDeviceCapabilitiesQuery **caps, AVFormatContext *s, - AVDictionary **device_options); - -/** - * Free resources created by avdevice_capabilities_create() - * - * @param caps Device capabilities data to be freed. - * @param s Context of the device. - */ -attribute_deprecated -void avdevice_capabilities_free(AVDeviceCapabilitiesQuery **caps, AVFormatContext *s); -#endif - /** * Structure describes basic parameters of the device. */ diff --git a/libavdevice/caca.c b/libavdevice/caca.c index 5536fd036ed..6af1649137e 100644 --- a/libavdevice/caca.c +++ b/libavdevice/caca.c @@ -21,6 +21,7 @@ #include #include "libavutil/opt.h" #include "libavutil/pixdesc.h" +#include "libavformat/mux.h" #include "avdevice.h" typedef struct CACAContext { @@ -220,15 +221,15 @@ static const AVClass caca_class = { .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT, }; -const AVOutputFormat ff_caca_muxer = { - .name = "caca", - .long_name = NULL_IF_CONFIG_SMALL("caca (color ASCII art) output device"), +const FFOutputFormat ff_caca_muxer = { + .p.name = "caca", + .p.long_name = NULL_IF_CONFIG_SMALL("caca (color ASCII art) output device"), .priv_data_size = sizeof(CACAContext), - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_RAWVIDEO, + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_RAWVIDEO, .write_header = caca_write_header, .write_packet = caca_write_packet, .deinit = caca_deinit, - .flags = AVFMT_NOFILE, - .priv_class = &caca_class, + .p.flags = AVFMT_NOFILE, + .p.priv_class = &caca_class, }; diff --git a/libavcodec/h263_parser.h b/libavdevice/ccfifo.c similarity index 73% rename from libavcodec/h263_parser.h rename to libavdevice/ccfifo.c index 565a222bc1e..9007094f0b3 100644 --- a/libavcodec/h263_parser.h +++ b/libavdevice/ccfifo.c @@ -1,6 +1,8 @@ /* - * H.263 parser - * Copyright (c) 2002-2004 Michael Niedermayer + * CEA-708 Closed Captioning FIFO + * Copyright (c) 2023 LTN Global Communications + * + * Author: Devin Heitmueller * * This file is part of FFmpeg. * @@ -19,11 +21,4 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef AVCODEC_H263_PARSER_H -#define AVCODEC_H263_PARSER_H - -#include "parser.h" - -int ff_h263_find_frame_end(ParseContext *pc, const uint8_t *buf, int buf_size); - -#endif /* AVCODEC_H263_PARSER_H */ +#include "libavfilter/ccfifo.c" diff --git a/libavdevice/decklink_common.cpp b/libavdevice/decklink_common.cpp index acd1f77e6c9..47de7ef6b0f 100644 --- a/libavdevice/decklink_common.cpp +++ b/libavdevice/decklink_common.cpp @@ -390,6 +390,116 @@ int ff_decklink_set_format(AVFormatContext *avctx, decklink_direction_t directio return ff_decklink_set_format(avctx, 0, 0, 0, 0, AV_FIELD_UNKNOWN, direction); } +void ff_decklink_packet_queue_init(AVFormatContext *avctx, DecklinkPacketQueue *q, int64_t queue_size) +{ + memset(q, 0, sizeof(DecklinkPacketQueue)); + pthread_mutex_init(&q->mutex, NULL); + pthread_cond_init(&q->cond, NULL); + q->avctx = avctx; + q->max_q_size = queue_size; +} + +void ff_decklink_packet_queue_flush(DecklinkPacketQueue *q) +{ + AVPacket pkt; + + pthread_mutex_lock(&q->mutex); + while (avpriv_packet_list_get(&q->pkt_list, &pkt) == 0) { + av_packet_unref(&pkt); + } + q->nb_packets = 0; + q->size = 0; + pthread_mutex_unlock(&q->mutex); +} + +void ff_decklink_packet_queue_end(DecklinkPacketQueue *q) +{ + ff_decklink_packet_queue_flush(q); + pthread_mutex_destroy(&q->mutex); + pthread_cond_destroy(&q->cond); +} + +unsigned long long ff_decklink_packet_queue_size(DecklinkPacketQueue *q) +{ + unsigned long long size; + pthread_mutex_lock(&q->mutex); + size = q->size; + pthread_mutex_unlock(&q->mutex); + return size; +} + +int ff_decklink_packet_queue_put(DecklinkPacketQueue *q, AVPacket *pkt) +{ + int pkt_size = pkt->size; + int ret; + + // Drop Packet if queue size is > maximum queue size + if (ff_decklink_packet_queue_size(q) > (uint64_t)q->max_q_size) { + av_packet_unref(pkt); + av_log(q->avctx, AV_LOG_WARNING, "Decklink input buffer overrun!\n"); + return -1; + } + /* ensure the packet is reference counted */ + if (av_packet_make_refcounted(pkt) < 0) { + av_packet_unref(pkt); + return -1; + } + + pthread_mutex_lock(&q->mutex); + + ret = avpriv_packet_list_put(&q->pkt_list, pkt, NULL, 0); + if (ret == 0) { + q->nb_packets++; + q->size += pkt_size + sizeof(AVPacket); + pthread_cond_signal(&q->cond); + } else { + av_packet_unref(pkt); + } + + pthread_mutex_unlock(&q->mutex); + return ret; +} + +int ff_decklink_packet_queue_get(DecklinkPacketQueue *q, AVPacket *pkt, int block) +{ + int ret; + + pthread_mutex_lock(&q->mutex); + + for (;; ) { + ret = avpriv_packet_list_get(&q->pkt_list, pkt); + if (ret == 0) { + q->nb_packets--; + q->size -= pkt->size + sizeof(AVPacket); + ret = 1; + break; + } else if (!block) { + ret = 0; + break; + } else { + pthread_cond_wait(&q->cond, &q->mutex); + } + } + pthread_mutex_unlock(&q->mutex); + return ret; +} + +int64_t ff_decklink_packet_queue_peekpts(DecklinkPacketQueue *q) +{ + PacketListEntry *pkt1; + int64_t pts = -1; + + pthread_mutex_lock(&q->mutex); + pkt1 = q->pkt_list.head; + if (pkt1) { + pts = pkt1->pkt.pts; + } + pthread_mutex_unlock(&q->mutex); + + return pts; +} + + int ff_decklink_list_devices(AVFormatContext *avctx, struct AVDeviceInfoList *device_list, int show_inputs, int show_outputs) diff --git a/libavdevice/decklink_common.h b/libavdevice/decklink_common.h index 79d6ac5b384..34ab1b96700 100644 --- a/libavdevice/decklink_common.h +++ b/libavdevice/decklink_common.h @@ -31,6 +31,7 @@ extern "C" { #include "libavcodec/packet_internal.h" +#include "libavfilter/ccfifo.h" } #include "libavutil/thread.h" #include "decklink_common_c.h" @@ -77,7 +78,7 @@ static char *dup_cfstring_to_utf8(CFStringRef w) class decklink_output_callback; class decklink_input_callback; -typedef struct AVPacketQueue { +typedef struct DecklinkPacketQueue { PacketList pkt_list; int nb_packets; unsigned long long size; @@ -86,7 +87,7 @@ typedef struct AVPacketQueue { pthread_cond_t cond; AVFormatContext *avctx; int64_t max_q_size; -} AVPacketQueue; +} DecklinkPacketQueue; struct decklink_ctx { /* DeckLink SDK interfaces */ @@ -110,7 +111,12 @@ struct decklink_ctx { int supports_vanc; /* Capture buffer queue */ - AVPacketQueue queue; + DecklinkPacketQueue queue; + + CCFifo cc_fifo; ///< closed captions + + /* Output VANC queue */ + DecklinkPacketQueue vanc_queue; /* Streams present */ int audio; @@ -118,6 +124,7 @@ struct decklink_ctx { /* Status */ int playback_started; + int64_t first_pts; int64_t last_pts; unsigned long frameCount; unsigned int dropped; @@ -231,4 +238,12 @@ int ff_decklink_list_formats(AVFormatContext *avctx, decklink_direction_t direct void ff_decklink_cleanup(AVFormatContext *avctx); int ff_decklink_init_device(AVFormatContext *avctx, const char* name); +void ff_decklink_packet_queue_init(AVFormatContext *avctx, DecklinkPacketQueue *q, int64_t queue_size); +void ff_decklink_packet_queue_flush(DecklinkPacketQueue *q); +void ff_decklink_packet_queue_end(DecklinkPacketQueue *q); +unsigned long long ff_decklink_packet_queue_size(DecklinkPacketQueue *q); +int ff_decklink_packet_queue_put(DecklinkPacketQueue *q, AVPacket *pkt); +int ff_decklink_packet_queue_get(DecklinkPacketQueue *q, AVPacket *pkt, int block); +int64_t ff_decklink_packet_queue_peekpts(DecklinkPacketQueue *q); + #endif /* AVDEVICE_DECKLINK_COMMON_H */ diff --git a/libavdevice/decklink_common_c.h b/libavdevice/decklink_common_c.h index 75896ad32b2..9c55d891494 100644 --- a/libavdevice/decklink_common_c.h +++ b/libavdevice/decklink_common_c.h @@ -63,6 +63,7 @@ struct decklink_cctx { char *format_code; int raw_format; int64_t queue_size; + int64_t vanc_queue_size; int copyts; int64_t timestamp_align; int timing_offset; diff --git a/libavdevice/decklink_dec.cpp b/libavdevice/decklink_dec.cpp index 7bf5e3724ca..11640f72caa 100644 --- a/libavdevice/decklink_dec.cpp +++ b/libavdevice/decklink_dec.cpp @@ -471,120 +471,6 @@ static uint8_t *get_metadata(AVFormatContext *avctx, uint16_t *buf, size_t width return tgt; } -static void avpacket_queue_init(AVFormatContext *avctx, AVPacketQueue *q) -{ - struct decklink_cctx *ctx = (struct decklink_cctx *)avctx->priv_data; - memset(q, 0, sizeof(AVPacketQueue)); - pthread_mutex_init(&q->mutex, NULL); - pthread_cond_init(&q->cond, NULL); - q->avctx = avctx; - q->max_q_size = ctx->queue_size; -} - -static void avpacket_queue_flush(AVPacketQueue *q) -{ - PacketListEntry *pkt, *pkt1; - - pthread_mutex_lock(&q->mutex); - for (pkt = q->pkt_list.head; pkt != NULL; pkt = pkt1) { - pkt1 = pkt->next; - av_packet_unref(&pkt->pkt); - av_freep(&pkt); - } - q->pkt_list.head = NULL; - q->pkt_list.tail = NULL; - q->nb_packets = 0; - q->size = 0; - pthread_mutex_unlock(&q->mutex); -} - -static void avpacket_queue_end(AVPacketQueue *q) -{ - avpacket_queue_flush(q); - pthread_mutex_destroy(&q->mutex); - pthread_cond_destroy(&q->cond); -} - -static unsigned long long avpacket_queue_size(AVPacketQueue *q) -{ - unsigned long long size; - pthread_mutex_lock(&q->mutex); - size = q->size; - pthread_mutex_unlock(&q->mutex); - return size; -} - -static int avpacket_queue_put(AVPacketQueue *q, AVPacket *pkt) -{ - PacketListEntry *pkt1; - - // Drop Packet if queue size is > maximum queue size - if (avpacket_queue_size(q) > (uint64_t)q->max_q_size) { - av_packet_unref(pkt); - av_log(q->avctx, AV_LOG_WARNING, "Decklink input buffer overrun!\n"); - return -1; - } - /* ensure the packet is reference counted */ - if (av_packet_make_refcounted(pkt) < 0) { - av_packet_unref(pkt); - return -1; - } - - pkt1 = (PacketListEntry *)av_malloc(sizeof(*pkt1)); - if (!pkt1) { - av_packet_unref(pkt); - return -1; - } - av_packet_move_ref(&pkt1->pkt, pkt); - pkt1->next = NULL; - - pthread_mutex_lock(&q->mutex); - - if (!q->pkt_list.tail) { - q->pkt_list.head = pkt1; - } else { - q->pkt_list.tail->next = pkt1; - } - - q->pkt_list.tail = pkt1; - q->nb_packets++; - q->size += pkt1->pkt.size + sizeof(*pkt1); - - pthread_cond_signal(&q->cond); - - pthread_mutex_unlock(&q->mutex); - return 0; -} - -static int avpacket_queue_get(AVPacketQueue *q, AVPacket *pkt, int block) -{ - int ret; - - pthread_mutex_lock(&q->mutex); - - for (;; ) { - PacketListEntry *pkt1 = q->pkt_list.head; - if (pkt1) { - q->pkt_list.head = pkt1->next; - if (!q->pkt_list.head) { - q->pkt_list.tail = NULL; - } - q->nb_packets--; - q->size -= pkt1->pkt.size + sizeof(*pkt1); - *pkt = pkt1->pkt; - av_free(pkt1); - ret = 1; - break; - } else if (!block) { - ret = 0; - break; - } else { - pthread_cond_wait(&q->cond, &q->mutex); - } - } - pthread_mutex_unlock(&q->mutex); - return ret; -} static void handle_klv(AVFormatContext *avctx, decklink_ctx *ctx, IDeckLinkVideoInputFrame *videoFrame, int64_t pts) { @@ -682,7 +568,7 @@ static void handle_klv(AVFormatContext *avctx, decklink_ctx *ctx, IDeckLinkVideo klv_packet.data = klv.data(); klv_packet.size = klv.size(); - if (avpacket_queue_put(&ctx->queue, &klv_packet) < 0) { + if (ff_decklink_packet_queue_put(&ctx->queue, &klv_packet) < 0) { ++ctx->dropped; } } @@ -874,7 +760,7 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( if (videoFrame) { AVPacket pkt = { 0 }; if (ctx->frameCount % 25 == 0) { - unsigned long long qsize = avpacket_queue_size(&ctx->queue); + unsigned long long qsize = ff_decklink_packet_queue_size(&ctx->queue); av_log(avctx, AV_LOG_DEBUG, "Frame received (#%lu) - Valid (%liB) - QSize %fMB\n", ctx->frameCount, @@ -1038,7 +924,7 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( txt_pkt.stream_index = ctx->teletext_st->index; txt_pkt.data = txt_buf0; txt_pkt.size = txt_buf - txt_buf0; - if (avpacket_queue_put(&ctx->queue, &txt_pkt) < 0) { + if (ff_decklink_packet_queue_put(&ctx->queue, &txt_pkt) < 0) { ++ctx->dropped; } } @@ -1049,7 +935,7 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( if (pkt.buf) videoFrame->AddRef(); - if (avpacket_queue_put(&ctx->queue, &pkt) < 0) { + if (ff_decklink_packet_queue_put(&ctx->queue, &pkt) < 0) { ++ctx->dropped; } } @@ -1071,7 +957,7 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( pkt.stream_index = ctx->audio_st->index; pkt.data = (uint8_t *)audioFrameBytes; - if (avpacket_queue_put(&ctx->queue, &pkt) < 0) { + if (ff_decklink_packet_queue_put(&ctx->queue, &pkt) < 0) { ++ctx->dropped; } } @@ -1153,7 +1039,7 @@ av_cold int ff_decklink_read_close(AVFormatContext *avctx) } ff_decklink_cleanup(avctx); - avpacket_queue_end(&ctx->queue); + ff_decklink_packet_queue_end(&ctx->queue); av_freep(&cctx->ctx); @@ -1411,7 +1297,7 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx) goto error; } - avpacket_queue_init (avctx, &ctx->queue); + ff_decklink_packet_queue_init(avctx, &ctx->queue, cctx->queue_size); if (ctx->dli->StartStreams() != S_OK) { av_log(avctx, AV_LOG_ERROR, "Cannot start input stream\n"); @@ -1431,7 +1317,7 @@ int ff_decklink_read_packet(AVFormatContext *avctx, AVPacket *pkt) struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data; struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx; - avpacket_queue_get(&ctx->queue, pkt, 1); + ff_decklink_packet_queue_get(&ctx->queue, pkt, 1); if (ctx->tc_format && !(av_dict_get(ctx->video_st->metadata, "timecode", NULL, 0))) { size_t size; diff --git a/libavdevice/decklink_enc.cpp b/libavdevice/decklink_enc.cpp index fb686b9032d..ffd0ad92508 100644 --- a/libavdevice/decklink_enc.cpp +++ b/libavdevice/decklink_enc.cpp @@ -32,6 +32,7 @@ extern "C" { extern "C" { #include "libavformat/avformat.h" +#include "libavcodec/bytestream.h" #include "libavutil/internal.h" #include "libavutil/imgutils.h" #include "avdevice.h" @@ -243,19 +244,32 @@ static int decklink_setup_audio(AVFormatContext *avctx, AVStream *st) av_log(avctx, AV_LOG_ERROR, "Only one audio stream is supported!\n"); return -1; } - if (c->sample_rate != 48000) { - av_log(avctx, AV_LOG_ERROR, "Unsupported sample rate!" - " Only 48kHz is supported.\n"); - return -1; - } - if (c->ch_layout.nb_channels != 2 && c->ch_layout.nb_channels != 8 && c->ch_layout.nb_channels != 16) { - av_log(avctx, AV_LOG_ERROR, "Unsupported number of channels!" - " Only 2, 8 or 16 channels are supported.\n"); + + if (c->codec_id == AV_CODEC_ID_AC3) { + /* Regardless of the number of channels in the codec, we're only + using 2 SDI audio channels at 48000Hz */ + ctx->channels = 2; + } else if (c->codec_id == AV_CODEC_ID_PCM_S16LE) { + if (c->sample_rate != 48000) { + av_log(avctx, AV_LOG_ERROR, "Unsupported sample rate!" + " Only 48kHz is supported.\n"); + return -1; + } + if (c->ch_layout.nb_channels != 2 && c->ch_layout.nb_channels != 8 && c->ch_layout.nb_channels != 16) { + av_log(avctx, AV_LOG_ERROR, "Unsupported number of channels!" + " Only 2, 8 or 16 channels are supported.\n"); + return -1; + } + ctx->channels = c->ch_layout.nb_channels; + } else { + av_log(avctx, AV_LOG_ERROR, "Unsupported codec specified!" + " Only PCM_S16LE and AC-3 are supported.\n"); return -1; } + if (ctx->dlo->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger, - c->ch_layout.nb_channels, + ctx->channels, bmdAudioOutputStreamTimestamped) != S_OK) { av_log(avctx, AV_LOG_ERROR, "Could not enable audio output!\n"); return -1; @@ -266,14 +280,90 @@ static int decklink_setup_audio(AVFormatContext *avctx, AVStream *st) } /* The device expects the sample rate to be fixed. */ - avpriv_set_pts_info(st, 64, 1, c->sample_rate); - ctx->channels = c->ch_layout.nb_channels; + avpriv_set_pts_info(st, 64, 1, 48000); ctx->audio = 1; return 0; } +/* Wrap the AC-3 packet into an S337 payload that is in S16LE format which can be easily + injected into the PCM stream. Note: despite the function name, only AC-3 is implemented */ +static int create_s337_payload(AVPacket *pkt, uint8_t **outbuf, int *outsize) +{ + /* Note: if the packet size is not divisible by four, we need to make the actual + payload larger to ensure it ends on an two channel S16LE boundary */ + int payload_size = FFALIGN(pkt->size, 4) + 8; + uint16_t bitcount = pkt->size * 8; + uint8_t *s337_payload; + PutByteContext pb; + + /* Sanity check: According to SMPTE ST 340:2015 Sec 4.1, the AC-3 sync frame will + exactly match the 1536 samples of baseband (PCM) audio that it represents. */ + if (pkt->size > 1536) + return AVERROR(EINVAL); + + /* Encapsulate AC3 syncframe into SMPTE 337 packet */ + s337_payload = (uint8_t *) av_malloc(payload_size); + if (s337_payload == NULL) + return AVERROR(ENOMEM); + bytestream2_init_writer(&pb, s337_payload, payload_size); + bytestream2_put_le16u(&pb, 0xf872); /* Sync word 1 */ + bytestream2_put_le16u(&pb, 0x4e1f); /* Sync word 1 */ + bytestream2_put_le16u(&pb, 0x0001); /* Burst Info, including data type (1=ac3) */ + bytestream2_put_le16u(&pb, bitcount); /* Length code */ + for (int i = 0; i < (pkt->size - 1); i += 2) + bytestream2_put_le16u(&pb, (pkt->data[i] << 8) | pkt->data[i+1]); + + /* Ensure final payload is aligned on 4-byte boundary */ + if (pkt->size & 1) + bytestream2_put_le16u(&pb, pkt->data[pkt->size - 1] << 8); + if ((pkt->size & 3) == 1 || (pkt->size & 3) == 2) + bytestream2_put_le16u(&pb, 0); + + *outsize = payload_size; + *outbuf = s337_payload; + return 0; +} + +static int decklink_setup_subtitle(AVFormatContext *avctx, AVStream *st) +{ + int ret = -1; + + switch(st->codecpar->codec_id) { +#if CONFIG_LIBKLVANC + case AV_CODEC_ID_EIA_608: + /* No special setup required */ + ret = 0; + break; +#endif + default: + av_log(avctx, AV_LOG_ERROR, "Unsupported subtitle codec specified\n"); + break; + } + + return ret; +} + +static int decklink_setup_data(AVFormatContext *avctx, AVStream *st) +{ + int ret = -1; + + switch(st->codecpar->codec_id) { +#if CONFIG_LIBKLVANC + case AV_CODEC_ID_SMPTE_2038: + /* No specific setup required */ + ret = 0; + break; +#endif + default: + av_log(avctx, AV_LOG_ERROR, "Unsupported data codec specified\n"); + break; + } + + return ret; +} + av_cold int ff_decklink_write_trailer(AVFormatContext *avctx) { struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data; @@ -299,7 +389,9 @@ av_cold int ff_decklink_write_trailer(AVFormatContext *avctx) #if CONFIG_LIBKLVANC klvanc_context_destroy(ctx->vanc_ctx); #endif + ff_decklink_packet_queue_end(&ctx->vanc_queue); + ff_ccfifo_uninit(&ctx->cc_fifo); av_freep(&cctx->ctx); return 0; @@ -328,7 +420,7 @@ static void construct_cc(AVFormatContext *avctx, struct decklink_ctx *ctx, ret = klvanc_set_framerate_EIA_708B(cdp, ctx->bmd_tb_num, ctx->bmd_tb_den); if (ret) { - av_log(avctx, AV_LOG_ERROR, "Invalid framerate specified: %lld/%lld\n", + av_log(avctx, AV_LOG_ERROR, "Invalid framerate specified: %" PRId64 "/%" PRId64 "\n", ctx->bmd_tb_num, ctx->bmd_tb_den); klvanc_destroy_eia708_cdp(cdp); return; @@ -367,8 +459,108 @@ static void construct_cc(AVFormatContext *avctx, struct decklink_ctx *ctx, } } +/* See SMPTE ST 2016-3:2009 */ +static void construct_afd(AVFormatContext *avctx, struct decklink_ctx *ctx, + AVPacket *pkt, struct klvanc_line_set_s *vanc_lines, + AVStream *st) +{ + struct klvanc_packet_afd_s *afd = NULL; + uint16_t *afd_words = NULL; + uint16_t len; + size_t size; + int f1_line = 12, f2_line = 0, ret; + + const uint8_t *data = av_packet_get_side_data(pkt, AV_PKT_DATA_AFD, &size); + if (!data || size == 0) + return; + + ret = klvanc_create_AFD(&afd); + if (ret) + return; + + ret = klvanc_set_AFD_val(afd, data[0]); + if (ret) { + av_log(avctx, AV_LOG_ERROR, "Invalid AFD value specified: %d\n", + data[0]); + klvanc_destroy_AFD(afd); + return; + } + + /* Compute the AR flag based on the DAR (see ST 2016-1:2009 Sec 9.1). Note, we treat + anything below 1.4 as 4:3 (as opposed to the standard 1.33), because there are lots + of streams in the field that aren't *exactly* 4:3 but a tiny bit larger after doing + the math... */ + if (av_cmp_q((AVRational) {st->codecpar->width * st->codecpar->sample_aspect_ratio.num, + st->codecpar->height * st->codecpar->sample_aspect_ratio.den}, (AVRational) {14, 10}) == 1) + afd->aspectRatio = ASPECT_16x9; + else + afd->aspectRatio = ASPECT_4x3; + + ret = klvanc_convert_AFD_to_words(afd, &afd_words, &len); + if (ret) { + av_log(avctx, AV_LOG_ERROR, "Failed converting AFD packet to words\n"); + goto out; + } + + ret = klvanc_line_insert(ctx->vanc_ctx, vanc_lines, afd_words, len, f1_line, 0); + if (ret) { + av_log(avctx, AV_LOG_ERROR, "VANC line insertion failed\n"); + goto out; + } + + /* For interlaced video, insert into both fields. Switching lines for field 2 + derived from SMPTE RP 168:2009, Sec 6, Table 2. */ + switch (ctx->bmd_mode) { + case bmdModeNTSC: + case bmdModeNTSC2398: + f2_line = 273 - 10 + f1_line; + break; + case bmdModePAL: + f2_line = 319 - 6 + f1_line; + break; + case bmdModeHD1080i50: + case bmdModeHD1080i5994: + case bmdModeHD1080i6000: + f2_line = 569 - 7 + f1_line; + break; + default: + f2_line = 0; + break; + } + + if (f2_line > 0) { + ret = klvanc_line_insert(ctx->vanc_ctx, vanc_lines, afd_words, len, f2_line, 0); + if (ret) { + av_log(avctx, AV_LOG_ERROR, "VANC line insertion failed\n"); + goto out; + } + } + +out: + if (afd) + klvanc_destroy_AFD(afd); + if (afd_words) + free(afd_words); +} + +/* Parse any EIA-608 subtitles sitting on the queue, and write packet side data + that will later be handled by construct_cc... */ +static void parse_608subs(AVFormatContext *avctx, struct decklink_ctx *ctx, AVPacket *pkt) +{ + size_t cc_size = ff_ccfifo_getoutputsize(&ctx->cc_fifo); + uint8_t *cc_data; + + if (!ff_ccfifo_ccdetected(&ctx->cc_fifo)) + return; + + cc_data = av_packet_new_side_data(pkt, AV_PKT_DATA_A53_CC, cc_size); + if (cc_data) + ff_ccfifo_injectbytes(&ctx->cc_fifo, cc_data, cc_size); +} + static int decklink_construct_vanc(AVFormatContext *avctx, struct decklink_ctx *ctx, - AVPacket *pkt, decklink_frame *frame) + AVPacket *pkt, decklink_frame *frame, + AVStream *st) { struct klvanc_line_set_s vanc_lines = { 0 }; int ret = 0, i; @@ -376,7 +568,61 @@ static int decklink_construct_vanc(AVFormatContext *avctx, struct decklink_ctx * if (!ctx->supports_vanc) return 0; + parse_608subs(avctx, ctx, pkt); construct_cc(avctx, ctx, pkt, &vanc_lines); + construct_afd(avctx, ctx, pkt, &vanc_lines, st); + + /* See if there any pending data packets to process */ + while (ff_decklink_packet_queue_size(&ctx->vanc_queue) > 0) { + AVStream *vanc_st; + AVPacket vanc_pkt; + int64_t pts; + + pts = ff_decklink_packet_queue_peekpts(&ctx->vanc_queue); + if (pts > ctx->last_pts) { + /* We haven't gotten to the video frame we are supposed to inject + the oldest VANC packet into yet, so leave it on the queue... */ + break; + } + + ret = ff_decklink_packet_queue_get(&ctx->vanc_queue, &vanc_pkt, 1); + if (vanc_pkt.pts + 1 < ctx->last_pts) { + av_log(avctx, AV_LOG_WARNING, "VANC packet too old, throwing away\n"); + av_packet_unref(&vanc_pkt); + continue; + } + + vanc_st = avctx->streams[vanc_pkt.stream_index]; + if (vanc_st->codecpar->codec_id == AV_CODEC_ID_SMPTE_2038) { + struct klvanc_smpte2038_anc_data_packet_s *pkt_2038 = NULL; + + klvanc_smpte2038_parse_pes_payload(vanc_pkt.data, vanc_pkt.size, &pkt_2038); + if (pkt_2038 == NULL) { + av_log(avctx, AV_LOG_ERROR, "failed to decode SMPTE 2038 PES packet"); + av_packet_unref(&vanc_pkt); + continue; + } + for (int i = 0; i < pkt_2038->lineCount; i++) { + struct klvanc_smpte2038_anc_data_line_s *l = &pkt_2038->lines[i]; + uint16_t *vancWords = NULL; + uint16_t vancWordCount; + + if (klvanc_smpte2038_convert_line_to_words(l, &vancWords, + &vancWordCount) < 0) + break; + + ret = klvanc_line_insert(ctx->vanc_ctx, &vanc_lines, vancWords, + vancWordCount, l->line_number, 0); + free(vancWords); + if (ret != 0) { + av_log(avctx, AV_LOG_ERROR, "VANC line insertion failed\n"); + break; + } + } + klvanc_smpte2038_anc_data_packet_free(pkt_2038); + } + av_packet_unref(&vanc_pkt); + } IDeckLinkVideoFrameAncillary *vanc; int result = ctx->dlo->CreateAncillaryData(bmdFormat10BitYUV, &vanc); @@ -441,6 +687,8 @@ static int decklink_write_video_packet(AVFormatContext *avctx, AVPacket *pkt) uint32_t buffered; HRESULT hr; + ctx->last_pts = FFMAX(ctx->last_pts, pkt->pts); + if (st->codecpar->codec_id == AV_CODEC_ID_WRAPPED_AVFRAME) { if (tmp->format != AV_PIX_FMT_UYVY422 || tmp->width != ctx->bmd_width || @@ -466,7 +714,7 @@ static int decklink_write_video_packet(AVFormatContext *avctx, AVPacket *pkt) frame = new decklink_frame(ctx, avpacket, st->codecpar->codec_id, ctx->bmd_height, ctx->bmd_width); #if CONFIG_LIBKLVANC - if (decklink_construct_vanc(avctx, ctx, pkt, frame)) + if (decklink_construct_vanc(avctx, ctx, pkt, frame, st)) av_log(avctx, AV_LOG_ERROR, "Failed to construct VANC\n"); #endif } @@ -486,6 +734,9 @@ static int decklink_write_video_packet(AVFormatContext *avctx, AVPacket *pkt) ctx->frames_buffer_available_spots--; pthread_mutex_unlock(&ctx->mutex); + if (ctx->first_pts == AV_NOPTS_VALUE) + ctx->first_pts = pkt->pts; + /* Schedule frame for playback. */ hr = ctx->dlo->ScheduleVideoFrame((class IDeckLinkVideoFrame *) frame, pkt->pts * ctx->bmd_tb_num, @@ -505,14 +756,14 @@ static int decklink_write_video_packet(AVFormatContext *avctx, AVPacket *pkt) " Video may misbehave!\n"); /* Preroll video frames. */ - if (!ctx->playback_started && pkt->pts > ctx->frames_preroll) { + if (!ctx->playback_started && pkt->pts > (ctx->first_pts + ctx->frames_preroll)) { av_log(avctx, AV_LOG_DEBUG, "Ending audio preroll.\n"); if (ctx->audio && ctx->dlo->EndAudioPreroll() != S_OK) { av_log(avctx, AV_LOG_ERROR, "Could not end audio preroll!\n"); return AVERROR(EIO); } av_log(avctx, AV_LOG_DEBUG, "Starting scheduled playback.\n"); - if (ctx->dlo->StartScheduledPlayback(0, ctx->bmd_tb_den, 1.0) != S_OK) { + if (ctx->dlo->StartScheduledPlayback(ctx->first_pts * ctx->bmd_tb_num, ctx->bmd_tb_den, 1.0) != S_OK) { av_log(avctx, AV_LOG_ERROR, "Could not start scheduled playback!\n"); return AVERROR(EIO); } @@ -526,18 +777,58 @@ static int decklink_write_audio_packet(AVFormatContext *avctx, AVPacket *pkt) { struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data; struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx; - int sample_count = pkt->size / (ctx->channels << 1); + AVStream *st = avctx->streams[pkt->stream_index]; + int sample_count; uint32_t buffered; + uint8_t *outbuf = NULL; + int ret = 0; ctx->dlo->GetBufferedAudioSampleFrameCount(&buffered); if (pkt->pts > 1 && !buffered) av_log(avctx, AV_LOG_WARNING, "There's no buffered audio." " Audio will misbehave!\n"); - if (ctx->dlo->ScheduleAudioSamples(pkt->data, sample_count, pkt->pts, + if (st->codecpar->codec_id == AV_CODEC_ID_AC3) { + /* Encapsulate AC3 syncframe into SMPTE 337 packet */ + int outbuf_size; + ret = create_s337_payload(pkt, &outbuf, &outbuf_size); + if (ret < 0) + return ret; + sample_count = outbuf_size / 4; + } else { + sample_count = pkt->size / (ctx->channels << 1); + outbuf = pkt->data; + } + + if (ctx->dlo->ScheduleAudioSamples(outbuf, sample_count, pkt->pts, bmdAudioSampleRate48kHz, NULL) != S_OK) { av_log(avctx, AV_LOG_ERROR, "Could not schedule audio samples.\n"); - return AVERROR(EIO); + ret = AVERROR(EIO); + } + + if (st->codecpar->codec_id == AV_CODEC_ID_AC3) + av_freep(&outbuf); + + return ret; +} + +static int decklink_write_subtitle_packet(AVFormatContext *avctx, AVPacket *pkt) +{ + struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data; + struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx; + + ff_ccfifo_extractbytes(&ctx->cc_fifo, pkt->data, pkt->size); + + return 0; +} + +static int decklink_write_data_packet(AVFormatContext *avctx, AVPacket *pkt) +{ + struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data; + struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx; + + if (ff_decklink_packet_queue_put(&ctx->vanc_queue, pkt) < 0) { + av_log(avctx, AV_LOG_WARNING, "Failed to queue DATA packet\n"); } return 0; @@ -559,6 +850,7 @@ av_cold int ff_decklink_write_header(AVFormatContext *avctx) ctx->list_formats = cctx->list_formats; ctx->preroll = cctx->preroll; ctx->duplex_mode = cctx->duplex_mode; + ctx->first_pts = AV_NOPTS_VALUE; if (cctx->link > 0 && (unsigned int)cctx->link < FF_ARRAY_ELEMS(decklink_link_conf_map)) ctx->link = decklink_link_conf_map[cctx->link]; cctx->ctx = ctx; @@ -606,12 +898,35 @@ av_cold int ff_decklink_write_header(AVFormatContext *avctx) } else if (c->codec_type == AVMEDIA_TYPE_VIDEO) { if (decklink_setup_video(avctx, st)) goto error; + } else if (c->codec_type == AVMEDIA_TYPE_DATA) { + if (decklink_setup_data(avctx, st)) + goto error; + } else if (c->codec_type == AVMEDIA_TYPE_SUBTITLE) { + if (decklink_setup_subtitle(avctx, st)) + goto error; } else { av_log(avctx, AV_LOG_ERROR, "Unsupported stream type.\n"); goto error; } } + /* Reconfigure the data/subtitle stream clocks to match the video */ + for (n = 0; n < avctx->nb_streams; n++) { + AVStream *st = avctx->streams[n]; + AVCodecParameters *c = st->codecpar; + + if(c->codec_type == AVMEDIA_TYPE_DATA || + c->codec_type == AVMEDIA_TYPE_SUBTITLE) + avpriv_set_pts_info(st, 64, ctx->bmd_tb_num, ctx->bmd_tb_den); + } + ff_decklink_packet_queue_init(avctx, &ctx->vanc_queue, cctx->vanc_queue_size); + + ret = ff_ccfifo_init(&ctx->cc_fifo, av_make_q(ctx->bmd_tb_den, ctx->bmd_tb_num), avctx); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Failure to setup CC FIFO queue\n"); + goto error; + } + return 0; error: @@ -621,16 +936,16 @@ av_cold int ff_decklink_write_header(AVFormatContext *avctx) int ff_decklink_write_packet(AVFormatContext *avctx, AVPacket *pkt) { - struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data; - struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx; AVStream *st = avctx->streams[pkt->stream_index]; - ctx->last_pts = FFMAX(ctx->last_pts, pkt->pts); - if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) return decklink_write_video_packet(avctx, pkt); else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) return decklink_write_audio_packet(avctx, pkt); + else if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA) + return decklink_write_data_packet(avctx, pkt); + else if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) + return decklink_write_subtitle_packet(avctx, pkt); return AVERROR(EIO); } diff --git a/libavdevice/decklink_enc_c.c b/libavdevice/decklink_enc_c.c index 990fbb81684..25ffe777840 100644 --- a/libavdevice/decklink_enc_c.c +++ b/libavdevice/decklink_enc_c.c @@ -20,6 +20,7 @@ */ #include "libavformat/avformat.h" +#include "libavformat/mux.h" #include "libavutil/opt.h" #include "decklink_common_c.h" @@ -31,6 +32,7 @@ static const AVOption options[] = { { "list_devices", "use ffmpeg -sinks decklink instead", OFFSET(list_devices), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, ENC | AV_OPT_FLAG_DEPRECATED}, { "list_formats", "list supported formats" , OFFSET(list_formats), AV_OPT_TYPE_INT , { .i64 = 0 }, 0, 1, ENC }, { "preroll" , "video preroll in seconds", OFFSET(preroll ), AV_OPT_TYPE_DOUBLE, { .dbl = 0.5 }, 0, 5, ENC }, + { "vanc_queue_size", "VANC queue buffer size", OFFSET(vanc_queue_size), AV_OPT_TYPE_INT64, { .i64 = (1024 * 1024)}, 0, INT64_MAX, ENC }, #if BLACKMAGIC_DECKLINK_API_VERSION >= 0x0b000000 { "duplex_mode" , "duplex mode" , OFFSET(duplex_mode ), AV_OPT_TYPE_INT , { .i64 = 0 }, 0, 5, ENC, "duplex_mode"}, #else @@ -71,15 +73,15 @@ static const AVClass decklink_muxer_class = { .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT, }; -const AVOutputFormat ff_decklink_muxer = { - .name = "decklink", - .long_name = NULL_IF_CONFIG_SMALL("Blackmagic DeckLink output"), - .audio_codec = AV_CODEC_ID_PCM_S16LE, - .video_codec = AV_CODEC_ID_WRAPPED_AVFRAME, - .subtitle_codec = AV_CODEC_ID_NONE, - .flags = AVFMT_NOFILE, +const FFOutputFormat ff_decklink_muxer = { + .p.name = "decklink", + .p.long_name = NULL_IF_CONFIG_SMALL("Blackmagic DeckLink output"), + .p.audio_codec = AV_CODEC_ID_PCM_S16LE, + .p.video_codec = AV_CODEC_ID_WRAPPED_AVFRAME, + .p.subtitle_codec = AV_CODEC_ID_EIA_608, + .p.flags = AVFMT_NOFILE, + .p.priv_class = &decklink_muxer_class, .get_device_list = ff_decklink_list_output_devices, - .priv_class = &decklink_muxer_class, .priv_data_size = sizeof(struct decklink_cctx), .write_header = ff_decklink_write_header, .write_packet = ff_decklink_write_packet, diff --git a/libavdevice/fbdev_enc.c b/libavdevice/fbdev_enc.c index 77233880e7d..7f3e8e5935f 100644 --- a/libavdevice/fbdev_enc.c +++ b/libavdevice/fbdev_enc.c @@ -28,6 +28,7 @@ #include "libavutil/log.h" #include "libavutil/opt.h" #include "libavformat/avformat.h" +#include "libavformat/mux.h" #include "fbdev_common.h" #include "avdevice.h" @@ -206,16 +207,16 @@ static const AVClass fbdev_class = { .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT, }; -const AVOutputFormat ff_fbdev_muxer = { - .name = "fbdev", - .long_name = NULL_IF_CONFIG_SMALL("Linux framebuffer"), +const FFOutputFormat ff_fbdev_muxer = { + .p.name = "fbdev", + .p.long_name = NULL_IF_CONFIG_SMALL("Linux framebuffer"), .priv_data_size = sizeof(FBDevContext), - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_RAWVIDEO, + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_RAWVIDEO, .write_header = fbdev_write_header, .write_packet = fbdev_write_packet, .write_trailer = fbdev_write_trailer, .get_device_list = fbdev_get_device_list, - .flags = AVFMT_NOFILE | AVFMT_VARIABLE_FPS | AVFMT_NOTIMESTAMPS, - .priv_class = &fbdev_class, + .p.flags = AVFMT_NOFILE | AVFMT_VARIABLE_FPS | AVFMT_NOTIMESTAMPS, + .p.priv_class = &fbdev_class, }; diff --git a/libavdevice/lavfi.c b/libavdevice/lavfi.c index 246f7dff3b3..b2cec0400bd 100644 --- a/libavdevice/lavfi.c +++ b/libavdevice/lavfi.c @@ -174,6 +174,10 @@ av_cold static int lavfi_read_header(AVFormatContext *avctx) * create a mapping between them and the streams */ for (i = 0, inout = output_links; inout; i++, inout = inout->next) { int stream_idx = 0, suffix = 0, use_subcc = 0; + if (!inout->name) { + av_log(avctx, AV_LOG_ERROR, "Missing %d outpad name\n", i); + FAIL(AVERROR(EINVAL)); + } sscanf(inout->name, "out%n%d%n", &suffix, &stream_idx, &suffix); if (!suffix) { av_log(avctx, AV_LOG_ERROR, @@ -343,7 +347,11 @@ static int create_subcc_packet(AVFormatContext *avctx, AVFrame *frame, memcpy(lavfi->subcc_packet.data, sd->data, sd->size); lavfi->subcc_packet.stream_index = stream_idx; lavfi->subcc_packet.pts = frame->pts; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS lavfi->subcc_packet.pos = frame->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif return 0; } @@ -450,7 +458,11 @@ static int lavfi_read_packet(AVFormatContext *avctx, AVPacket *pkt) pkt->stream_index = stream_idx; pkt->pts = frame->pts; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS pkt->pos = frame->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif if (st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) av_frame_free(&frame); diff --git a/libavdevice/opengl_enc.c b/libavdevice/opengl_enc.c index b2140c80be5..f9dc846bf11 100644 --- a/libavdevice/opengl_enc.c +++ b/libavdevice/opengl_enc.c @@ -594,7 +594,8 @@ static av_cold int opengl_read_limits(AVFormatContext *h) } av_log(h, AV_LOG_DEBUG, "OpenGL version: %s\n", version); - sscanf(version, "%d.%d", &major, &minor); + if (sscanf(version, "%d.%d", &major, &minor) != 2) + return AVERROR(ENOSYS); for (i = 0; required_extensions[i].extension; i++) { if (major < required_extensions[i].major && @@ -1200,6 +1201,10 @@ static int opengl_draw(AVFormatContext *h, void *input, int repaint, int is_pkt) int ret; #if CONFIG_SDL2 + /* At this point, opengl->glcontext implies opengl->glcontext */ + if (opengl->glcontext) + SDL_GL_MakeCurrent(opengl->window, opengl->glcontext); + if (!opengl->no_window && (ret = opengl_sdl_process_events(h)) < 0) goto fail; #endif @@ -1292,17 +1297,17 @@ static const AVClass opengl_class = { .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT, }; -const AVOutputFormat ff_opengl_muxer = { - .name = "opengl", - .long_name = NULL_IF_CONFIG_SMALL("OpenGL output"), +const FFOutputFormat ff_opengl_muxer = { + .p.name = "opengl", + .p.long_name = NULL_IF_CONFIG_SMALL("OpenGL output"), + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_WRAPPED_AVFRAME, + .p.flags = AVFMT_NOFILE | AVFMT_VARIABLE_FPS | AVFMT_NOTIMESTAMPS, + .p.priv_class = &opengl_class, .priv_data_size = sizeof(OpenGLContext), - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_WRAPPED_AVFRAME, .write_header = opengl_write_header, .write_packet = opengl_write_packet, .write_uncoded_frame = opengl_write_frame, .write_trailer = opengl_write_trailer, .control_message = opengl_control_message, - .flags = AVFMT_NOFILE | AVFMT_VARIABLE_FPS | AVFMT_NOTIMESTAMPS, - .priv_class = &opengl_class, }; diff --git a/libavdevice/oss_enc.c b/libavdevice/oss_enc.c index 704f434c53a..c125e6c0e9f 100644 --- a/libavdevice/oss_enc.c +++ b/libavdevice/oss_enc.c @@ -32,6 +32,7 @@ #include "avdevice.h" #include "libavformat/internal.h" +#include "libavformat/mux.h" #include "oss.h" @@ -94,18 +95,18 @@ static const AVClass oss_muxer_class = { .category = AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT, }; -const AVOutputFormat ff_oss_muxer = { - .name = "oss", - .long_name = NULL_IF_CONFIG_SMALL("OSS (Open Sound System) playback"), +const FFOutputFormat ff_oss_muxer = { + .p.name = "oss", + .p.long_name = NULL_IF_CONFIG_SMALL("OSS (Open Sound System) playback"), .priv_data_size = sizeof(OSSAudioData), /* XXX: we make the assumption that the soundcard accepts this format */ /* XXX: find better solution with "preinit" method, needed also in other formats */ - .audio_codec = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE), - .video_codec = AV_CODEC_ID_NONE, + .p.audio_codec = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE), + .p.video_codec = AV_CODEC_ID_NONE, .write_header = audio_write_header, .write_packet = audio_write_packet, .write_trailer = audio_write_trailer, - .flags = AVFMT_NOFILE, - .priv_class = &oss_muxer_class, + .p.flags = AVFMT_NOFILE, + .p.priv_class = &oss_muxer_class, }; diff --git a/libavdevice/pulse_audio_common.h b/libavdevice/pulse_audio_common.h index 902795e4f76..9def3cb7968 100644 --- a/libavdevice/pulse_audio_common.h +++ b/libavdevice/pulse_audio_common.h @@ -23,7 +23,7 @@ #define AVDEVICE_PULSE_AUDIO_COMMON_H #include -#include "libavcodec/avcodec.h" +#include "libavcodec/codec_id.h" #include "avdevice.h" pa_sample_format_t ff_codec_id_to_pulse_format(enum AVCodecID codec_id); diff --git a/libavdevice/pulse_audio_enc.c b/libavdevice/pulse_audio_enc.c index 038401c680c..3d8323233f7 100644 --- a/libavdevice/pulse_audio_enc.c +++ b/libavdevice/pulse_audio_enc.c @@ -788,12 +788,12 @@ static const AVClass pulse_muxer_class = { .category = AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT, }; -const AVOutputFormat ff_pulse_muxer = { - .name = "pulse", - .long_name = NULL_IF_CONFIG_SMALL("Pulse audio output"), +const FFOutputFormat ff_pulse_muxer = { + .p.name = "pulse", + .p.long_name = NULL_IF_CONFIG_SMALL("Pulse audio output"), .priv_data_size = sizeof(PulseData), - .audio_codec = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE), - .video_codec = AV_CODEC_ID_NONE, + .p.audio_codec = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE), + .p.video_codec = AV_CODEC_ID_NONE, .write_header = pulse_write_header, .write_packet = pulse_write_packet, .write_uncoded_frame = pulse_write_frame, @@ -801,6 +801,6 @@ const AVOutputFormat ff_pulse_muxer = { .get_output_timestamp = pulse_get_output_timestamp, .get_device_list = pulse_get_device_list, .control_message = pulse_control_message, - .flags = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH, - .priv_class = &pulse_muxer_class, + .p.flags = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH, + .p.priv_class = &pulse_muxer_class, }; diff --git a/libavdevice/sdl2.c b/libavdevice/sdl2.c index a9023153f13..342a253dc09 100644 --- a/libavdevice/sdl2.c +++ b/libavdevice/sdl2.c @@ -33,6 +33,7 @@ #include "libavutil/pixdesc.h" #include "libavutil/time.h" #include "avdevice.h" +#include "libavformat/mux.h" typedef struct { AVClass *class; @@ -355,15 +356,15 @@ static const AVClass sdl2_class = { .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT, }; -const AVOutputFormat ff_sdl2_muxer = { - .name = "sdl,sdl2", - .long_name = NULL_IF_CONFIG_SMALL("SDL2 output device"), +const FFOutputFormat ff_sdl2_muxer = { + .p.name = "sdl,sdl2", + .p.long_name = NULL_IF_CONFIG_SMALL("SDL2 output device"), .priv_data_size = sizeof(SDLContext), - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_RAWVIDEO, + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_RAWVIDEO, .write_header = sdl2_write_header, .write_packet = sdl2_write_packet, .write_trailer = sdl2_write_trailer, - .flags = AVFMT_NOFILE | AVFMT_VARIABLE_FPS | AVFMT_NOTIMESTAMPS, - .priv_class = &sdl2_class, + .p.flags = AVFMT_NOFILE | AVFMT_VARIABLE_FPS | AVFMT_NOTIMESTAMPS, + .p.priv_class = &sdl2_class, }; diff --git a/libavdevice/sndio_enc.c b/libavdevice/sndio_enc.c index 0cf58fdc6ab..578e287a41f 100644 --- a/libavdevice/sndio_enc.c +++ b/libavdevice/sndio_enc.c @@ -24,6 +24,7 @@ #include "libavutil/internal.h" +#include "libavformat/mux.h" #include "libavdevice/avdevice.h" #include "libavdevice/sndio.h" @@ -86,18 +87,18 @@ static const AVClass sndio_muxer_class = { .category = AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT, }; -const AVOutputFormat ff_sndio_muxer = { - .name = "sndio", - .long_name = NULL_IF_CONFIG_SMALL("sndio audio playback"), +const FFOutputFormat ff_sndio_muxer = { + .p.name = "sndio", + .p.long_name = NULL_IF_CONFIG_SMALL("sndio audio playback"), .priv_data_size = sizeof(SndioData), /* XXX: we make the assumption that the soundcard accepts this format */ /* XXX: find better solution with "preinit" method, needed also in other formats */ - .audio_codec = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE), - .video_codec = AV_CODEC_ID_NONE, + .p.audio_codec = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE), + .p.video_codec = AV_CODEC_ID_NONE, .write_header = audio_write_header, .write_packet = audio_write_packet, .write_trailer = audio_write_trailer, - .flags = AVFMT_NOFILE, - .priv_class = &sndio_muxer_class, + .p.flags = AVFMT_NOFILE, + .p.priv_class = &sndio_muxer_class, }; diff --git a/libavdevice/v4l2enc.c b/libavdevice/v4l2enc.c index b1006461b7a..8e8f911996c 100644 --- a/libavdevice/v4l2enc.c +++ b/libavdevice/v4l2enc.c @@ -21,6 +21,7 @@ #include "libavutil/imgutils.h" #include "libavutil/pixdesc.h" #include "libavformat/avformat.h" +#include "libavformat/mux.h" #include "v4l2-common.h" typedef struct { @@ -111,15 +112,15 @@ static const AVClass v4l2_class = { .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT, }; -const AVOutputFormat ff_v4l2_muxer = { - .name = "video4linux2,v4l2", - .long_name = NULL_IF_CONFIG_SMALL("Video4Linux2 output device"), +const FFOutputFormat ff_v4l2_muxer = { + .p.name = "video4linux2,v4l2", + .p.long_name = NULL_IF_CONFIG_SMALL("Video4Linux2 output device"), .priv_data_size = sizeof(V4L2Context), - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_RAWVIDEO, + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_RAWVIDEO, .write_header = write_header, .write_packet = write_packet, .write_trailer = write_trailer, - .flags = AVFMT_NOFILE, - .priv_class = &v4l2_class, + .p.flags = AVFMT_NOFILE, + .p.priv_class = &v4l2_class, }; diff --git a/libavdevice/version.h b/libavdevice/version.h index 3e654fff892..0796e41221e 100644 --- a/libavdevice/version.h +++ b/libavdevice/version.h @@ -29,7 +29,7 @@ #include "version_major.h" -#define LIBAVDEVICE_VERSION_MINOR 8 +#define LIBAVDEVICE_VERSION_MINOR 2 #define LIBAVDEVICE_VERSION_MICRO 101 #define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \ diff --git a/libavdevice/version_major.h b/libavdevice/version_major.h index d255ff6992d..b884fd42246 100644 --- a/libavdevice/version_major.h +++ b/libavdevice/version_major.h @@ -25,13 +25,12 @@ * Libavdevice version macros */ -#define LIBAVDEVICE_VERSION_MAJOR 59 +#define LIBAVDEVICE_VERSION_MAJOR 60 /** * FF_API_* defines may be placed below to indicate public API that will be * dropped at a future version bump. The defines themselves are not part of * the public API and may change, break or disappear at any time. */ -#define FF_API_DEVICE_CAPABILITIES (LIBAVDEVICE_VERSION_MAJOR < 60) #endif /* AVDEVICE_VERSION_MAJOR_H */ diff --git a/libavdevice/xv.c b/libavdevice/xv.c index 348c289beae..441f8541218 100644 --- a/libavdevice/xv.c +++ b/libavdevice/xv.c @@ -376,17 +376,17 @@ static const AVClass xv_class = { .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT, }; -const AVOutputFormat ff_xv_muxer = { - .name = "xv", - .long_name = NULL_IF_CONFIG_SMALL("XV (XVideo) output device"), +const FFOutputFormat ff_xv_muxer = { + .p.name = "xv", + .p.long_name = NULL_IF_CONFIG_SMALL("XV (XVideo) output device"), + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_WRAPPED_AVFRAME, + .p.flags = AVFMT_NOFILE | AVFMT_VARIABLE_FPS | AVFMT_NOTIMESTAMPS, + .p.priv_class = &xv_class, .priv_data_size = sizeof(XVContext), - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_WRAPPED_AVFRAME, .write_header = xv_write_header, .write_packet = xv_write_packet, .write_uncoded_frame = xv_write_frame, .write_trailer = xv_write_trailer, .control_message = xv_control_message, - .flags = AVFMT_NOFILE | AVFMT_VARIABLE_FPS | AVFMT_NOTIMESTAMPS, - .priv_class = &xv_class, }; diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 5783be281d1..30a0e22ef81 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -14,6 +14,7 @@ OBJS = allfilters.o \ buffersink.o \ buffersrc.o \ colorspace.o \ + ccfifo.o \ drawutils.o \ fifo.o \ formats.o \ @@ -88,6 +89,7 @@ OBJS-$(CONFIG_APULSATOR_FILTER) += af_apulsator.o OBJS-$(CONFIG_AREALTIME_FILTER) += f_realtime.o OBJS-$(CONFIG_ARESAMPLE_FILTER) += af_aresample.o OBJS-$(CONFIG_AREVERSE_FILTER) += f_reverse.o +OBJS-$(CONFIG_ARLS_FILTER) += af_arls.o OBJS-$(CONFIG_ARNNDN_FILTER) += af_arnndn.o OBJS-$(CONFIG_ASDR_FILTER) += af_asdr.o OBJS-$(CONFIG_ASEGMENT_FILTER) += f_segment.o @@ -171,6 +173,8 @@ OBJS-$(CONFIG_VOLUME_FILTER) += af_volume.o OBJS-$(CONFIG_VOLUMEDETECT_FILTER) += af_volumedetect.o OBJS-$(CONFIG_AEVALSRC_FILTER) += aeval.o +OBJS-$(CONFIG_AFDELAYSRC_FILTER) += asrc_afdelaysrc.o +OBJS-$(CONFIG_AFIREQSRC_FILTER) += asrc_afirsrc.o OBJS-$(CONFIG_AFIRSRC_FILTER) += asrc_afirsrc.o OBJS-$(CONFIG_ANOISESRC_FILTER) += asrc_anoisesrc.o OBJS-$(CONFIG_ANULLSRC_FILTER) += asrc_anullsrc.o @@ -209,7 +213,11 @@ OBJS-$(CONFIG_BOXBLUR_FILTER) += vf_boxblur.o boxblur.o OBJS-$(CONFIG_BOXBLUR_OPENCL_FILTER) += vf_avgblur_opencl.o opencl.o \ opencl/avgblur.o boxblur.o OBJS-$(CONFIG_BWDIF_FILTER) += vf_bwdif.o yadif_common.o +OBJS-$(CONFIG_BWDIF_CUDA_FILTER) += vf_bwdif_cuda.o vf_bwdif_cuda.ptx.o \ + yadif_common.o +OBJS-$(CONFIG_BWDIF_VULKAN_FILTER) += vf_bwdif_vulkan.o yadif_common.o vulkan.o vulkan_filter.o OBJS-$(CONFIG_CAS_FILTER) += vf_cas.o +OBJS-$(CONFIG_CCREPACK_FILTER) += vf_ccrepack.o OBJS-$(CONFIG_CHROMABER_VULKAN_FILTER) += vf_chromaber_vulkan.o vulkan.o vulkan_filter.o OBJS-$(CONFIG_CHROMAHOLD_FILTER) += vf_chromakey.o OBJS-$(CONFIG_CHROMAKEY_FILTER) += vf_chromakey.o @@ -259,7 +267,7 @@ OBJS-$(CONFIG_DECONVOLVE_FILTER) += vf_convolve.o framesync.o OBJS-$(CONFIG_DEDOT_FILTER) += vf_dedot.o OBJS-$(CONFIG_DEFLATE_FILTER) += vf_neighbor.o OBJS-$(CONFIG_DEFLICKER_FILTER) += vf_deflicker.o -OBJS-$(CONFIG_DEINTERLACE_QSV_FILTER) += vf_deinterlace_qsv.o +OBJS-$(CONFIG_DEINTERLACE_QSV_FILTER) += vf_vpp_qsv.o OBJS-$(CONFIG_DEINTERLACE_VAAPI_FILTER) += vf_deinterlace_vaapi.o vaapi_vpp.o OBJS-$(CONFIG_DEJUDDER_FILTER) += vf_dejudder.o OBJS-$(CONFIG_DELOGO_FILTER) += vf_delogo.o @@ -384,6 +392,8 @@ OBJS-$(CONFIG_MULTIPLY_FILTER) += vf_multiply.o OBJS-$(CONFIG_NEGATE_FILTER) += vf_negate.o OBJS-$(CONFIG_NLMEANS_FILTER) += vf_nlmeans.o OBJS-$(CONFIG_NLMEANS_OPENCL_FILTER) += vf_nlmeans_opencl.o opencl.o opencl/nlmeans.o +OBJS-$(CONFIG_NLMEANS_VULKAN_FILTER) += vf_nlmeans_vulkan.o vulkan.o vulkan_filter.o \ + vulkan/prefix_sum.o OBJS-$(CONFIG_NNEDI_FILTER) += vf_nnedi.o OBJS-$(CONFIG_NOFORMAT_FILTER) += vf_format.o OBJS-$(CONFIG_NOISE_FILTER) += vf_noise.o @@ -445,8 +455,9 @@ OBJS-$(CONFIG_SCALE_FILTER) += vf_scale.o scale_eval.o OBJS-$(CONFIG_SCALE_CUDA_FILTER) += vf_scale_cuda.o scale_eval.o \ vf_scale_cuda.ptx.o cuda/load_helper.o OBJS-$(CONFIG_SCALE_NPP_FILTER) += vf_scale_npp.o scale_eval.o -OBJS-$(CONFIG_SCALE_QSV_FILTER) += vf_scale_qsv.o +OBJS-$(CONFIG_SCALE_QSV_FILTER) += vf_vpp_qsv.o OBJS-$(CONFIG_SCALE_VAAPI_FILTER) += vf_scale_vaapi.o scale_eval.o vaapi_vpp.o +OBJS-$(CONFIG_SCALE_VT_FILTER) += vf_scale_vt.o scale_eval.o OBJS-$(CONFIG_SCALE_VULKAN_FILTER) += vf_scale_vulkan.o vulkan.o vulkan_filter.o OBJS-$(CONFIG_SCALE2REF_FILTER) += vf_scale.o scale_eval.o OBJS-$(CONFIG_SCALE2REF_NPP_FILTER) += vf_scale_npp.o scale_eval.o @@ -485,6 +496,7 @@ OBJS-$(CONFIG_SPLIT_FILTER) += split.o OBJS-$(CONFIG_SPP_FILTER) += vf_spp.o qp_table.o OBJS-$(CONFIG_SR_FILTER) += vf_sr.o OBJS-$(CONFIG_SSIM_FILTER) += vf_ssim.o framesync.o +OBJS-$(CONFIG_SSIM360_FILTER) += vf_ssim360.o framesync.o OBJS-$(CONFIG_STEREO3D_FILTER) += vf_stereo3d.o OBJS-$(CONFIG_STREAMSELECT_FILTER) += f_streamselect.o framesync.o OBJS-$(CONFIG_SUBTITLES_FILTER) += vf_subtitles.o @@ -513,6 +525,7 @@ OBJS-$(CONFIG_TRANSPOSE_FILTER) += vf_transpose.o OBJS-$(CONFIG_TRANSPOSE_NPP_FILTER) += vf_transpose_npp.o OBJS-$(CONFIG_TRANSPOSE_OPENCL_FILTER) += vf_transpose_opencl.o opencl.o opencl/transpose.o OBJS-$(CONFIG_TRANSPOSE_VAAPI_FILTER) += vf_transpose_vaapi.o vaapi_vpp.o +OBJS-$(CONFIG_TRANSPOSE_VT_FILTER) += vf_transpose_vt.o OBJS-$(CONFIG_TRANSPOSE_VULKAN_FILTER) += vf_transpose_vulkan.o vulkan.o vulkan_filter.o OBJS-$(CONFIG_TRIM_FILTER) += trim.o OBJS-$(CONFIG_UNPREMULTIPLY_FILTER) += vf_premultiply.o framesync.o @@ -543,6 +556,7 @@ OBJS-$(CONFIG_XBR_FILTER) += vf_xbr.o OBJS-$(CONFIG_XCORRELATE_FILTER) += vf_convolve.o framesync.o OBJS-$(CONFIG_XFADE_FILTER) += vf_xfade.o OBJS-$(CONFIG_XFADE_OPENCL_FILTER) += vf_xfade_opencl.o opencl.o opencl/xfade.o +OBJS-$(CONFIG_XFADE_VULKAN_FILTER) += vf_xfade_vulkan.o vulkan.o vulkan_filter.o OBJS-$(CONFIG_XMEDIAN_FILTER) += vf_xmedian.o framesync.o OBJS-$(CONFIG_XSTACK_FILTER) += vf_stack.o framesync.o OBJS-$(CONFIG_YADIF_FILTER) += vf_yadif.o yadif_common.o @@ -556,6 +570,12 @@ OBJS-$(CONFIG_YAEPBLUR_FILTER) += vf_yaepblur.o OBJS-$(CONFIG_ZMQ_FILTER) += f_zmq.o OBJS-$(CONFIG_ZOOMPAN_FILTER) += vf_zoompan.o OBJS-$(CONFIG_ZSCALE_FILTER) += vf_zscale.o +OBJS-$(CONFIG_HSTACK_VAAPI_FILTER) += vf_stack_vaapi.o framesync.o vaapi_vpp.o +OBJS-$(CONFIG_VSTACK_VAAPI_FILTER) += vf_stack_vaapi.o framesync.o vaapi_vpp.o +OBJS-$(CONFIG_XSTACK_VAAPI_FILTER) += vf_stack_vaapi.o framesync.o vaapi_vpp.o +OBJS-$(CONFIG_HSTACK_QSV_FILTER) += vf_stack_qsv.o framesync.o +OBJS-$(CONFIG_VSTACK_QSV_FILTER) += vf_stack_qsv.o framesync.o +OBJS-$(CONFIG_XSTACK_QSV_FILTER) += vf_stack_qsv.o framesync.o OBJS-$(CONFIG_ALLRGB_FILTER) += vsrc_testsrc.o OBJS-$(CONFIG_ALLYUV_FILTER) += vsrc_testsrc.o @@ -579,9 +599,11 @@ OBJS-$(CONFIG_RGBTESTSRC_FILTER) += vsrc_testsrc.o OBJS-$(CONFIG_SIERPINSKI_FILTER) += vsrc_sierpinski.o OBJS-$(CONFIG_SMPTEBARS_FILTER) += vsrc_testsrc.o OBJS-$(CONFIG_SMPTEHDBARS_FILTER) += vsrc_testsrc.o +OBJS-$(CONFIG_COLOR_VULKAN_FILTER) += vsrc_testsrc_vulkan.o vulkan.o vulkan_filter.o OBJS-$(CONFIG_TESTSRC_FILTER) += vsrc_testsrc.o OBJS-$(CONFIG_TESTSRC2_FILTER) += vsrc_testsrc.o OBJS-$(CONFIG_YUVTESTSRC_FILTER) += vsrc_testsrc.o +OBJS-$(CONFIG_ZONEPLATE_FILTER) += vsrc_testsrc.o OBJS-$(CONFIG_NULLSINK_FILTER) += vsink_nullsink.o @@ -610,6 +632,10 @@ OBJS-$(CONFIG_AVSYNCTEST_FILTER) += src_avsynctest.o OBJS-$(CONFIG_AMOVIE_FILTER) += src_movie.o OBJS-$(CONFIG_MOVIE_FILTER) += src_movie.o +# vulkan libs +OBJS-$(CONFIG_LIBGLSLANG) += vulkan_glslang.o +OBJS-$(CONFIG_LIBSHADERC) += vulkan_shaderc.o + # Objects duplicated from other libraries for shared builds SHLIBOBJS += log2_tab.o @@ -619,24 +645,30 @@ SHLIBOBJS-$(HAVE_GNU_WINDRES) += avfilterres.o SKIPHEADERS-$(CONFIG_LCMS2) += fflcms2.h SKIPHEADERS-$(CONFIG_LIBVIDSTAB) += vidstabutils.h -SKIPHEADERS-$(CONFIG_QSVVPP) += qsvvpp.h +SKIPHEADERS-$(CONFIG_QSVVPP) += qsvvpp.h stack_internal.h SKIPHEADERS-$(CONFIG_OPENCL) += opencl.h -SKIPHEADERS-$(CONFIG_VAAPI) += vaapi_vpp.h +SKIPHEADERS-$(CONFIG_VAAPI) += vaapi_vpp.h stack_internal.h SKIPHEADERS-$(CONFIG_VULKAN) += vulkan.h vulkan_filter.h +SKIPHEADERS-$(CONFIG_LIBSHADERC) += vulkan_spirv.h +SKIPHEADERS-$(CONFIG_LIBGLSLANG) += vulkan_spirv.h TOOLS = graph2dot TESTPROGS = drawutils filtfmts formats integral -TESTPROGS-$(CONFIG_DNN) += dnn-layer-avgpool dnn-layer-conv2d dnn-layer-dense \ - dnn-layer-depth2space dnn-layer-mathbinary \ - dnn-layer-mathunary dnn-layer-maximum dnn-layer-pad \ TOOLS-$(CONFIG_LIBZMQ) += zmqsend clean:: - $(RM) $(CLEANSUFFIXES:%=libavfilter/dnn/%) $(CLEANSUFFIXES:%=libavfilter/opencl/%) + $(RM) $(CLEANSUFFIXES:%=libavfilter/dnn/%) $(CLEANSUFFIXES:%=libavfilter/opencl/%) \ + $(CLEANSUFFIXES:%=libavfilter/vulkan/%) OPENCL = $(subst $(SRC_PATH)/,,$(wildcard $(SRC_PATH)/libavfilter/opencl/*.cl)) .SECONDARY: $(OPENCL:.cl=.c) libavfilter/opencl/%.c: TAG = OPENCL libavfilter/opencl/%.c: $(SRC_PATH)/libavfilter/opencl/%.cl - $(M)$(SRC_PATH)/tools/cl2c $< $@ + $(M)$(SRC_PATH)/tools/source2c $< $@ + +VULKAN = $(subst $(SRC_PATH)/,,$(wildcard $(SRC_PATH)/libavfilter/vulkan/*.comp)) +.SECONDARY: $(VULKAN:.comp=.c) +libavfilter/vulkan/%.c: TAG = OPENCL +libavfilter/vulkan/%.c: $(SRC_PATH)/libavfilter/vulkan/%.comp + $(M)$(SRC_PATH)/tools/source2c $< $@ diff --git a/libavfilter/aarch64/Makefile b/libavfilter/aarch64/Makefile index b58daa3a3fe..b68209bc94a 100644 --- a/libavfilter/aarch64/Makefile +++ b/libavfilter/aarch64/Makefile @@ -1,3 +1,5 @@ +OBJS-$(CONFIG_BWDIF_FILTER) += aarch64/vf_bwdif_init_aarch64.o OBJS-$(CONFIG_NLMEANS_FILTER) += aarch64/vf_nlmeans_init.o +NEON-OBJS-$(CONFIG_BWDIF_FILTER) += aarch64/vf_bwdif_neon.o NEON-OBJS-$(CONFIG_NLMEANS_FILTER) += aarch64/vf_nlmeans_neon.o diff --git a/libavfilter/aarch64/vf_bwdif_init_aarch64.c b/libavfilter/aarch64/vf_bwdif_init_aarch64.c new file mode 100644 index 00000000000..f52bc4b9b43 --- /dev/null +++ b/libavfilter/aarch64/vf_bwdif_init_aarch64.c @@ -0,0 +1,125 @@ +/* + * bwdif aarch64 NEON optimisations + * + * Copyright (c) 2023 John Cox + * + * 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 "libavutil/common.h" +#include "libavfilter/bwdif.h" +#include "libavutil/aarch64/cpu.h" + +void ff_bwdif_filter_edge_neon(void *dst1, void *prev1, void *cur1, void *next1, + int w, int prefs, int mrefs, int prefs2, int mrefs2, + int parity, int clip_max, int spat); + +void ff_bwdif_filter_intra_neon(void *dst1, void *cur1, int w, int prefs, int mrefs, + int prefs3, int mrefs3, int parity, int clip_max); + +void ff_bwdif_filter_line_neon(void *dst1, void *prev1, void *cur1, void *next1, + int w, int prefs, int mrefs, int prefs2, int mrefs2, + int prefs3, int mrefs3, int prefs4, int mrefs4, + int parity, int clip_max); + +void ff_bwdif_filter_line3_neon(void * dst1, int d_stride, + const void * prev1, const void * cur1, const void * next1, int s_stride, + int w, int parity, int clip_max); + + +static void filter_line3_helper(void * dst1, int d_stride, + const void * prev1, const void * cur1, const void * next1, int s_stride, + int w, int parity, int clip_max) +{ + // Asm works on 16 byte chunks + // If w is a multiple of 16 then all is good - if not then if width rounded + // up to nearest 16 will fit in both src & dst strides then allow the asm + // to write over the padding bytes as that is almost certainly faster than + // having to invoke the C version to clean up the tail. + const int w1 = FFALIGN(w, 16); + const int w0 = clip_max != 255 ? 0 : + d_stride <= w1 && s_stride <= w1 ? w : w & ~15; + + ff_bwdif_filter_line3_neon(dst1, d_stride, + prev1, cur1, next1, s_stride, + w0, parity, clip_max); + + if (w0 < w) + ff_bwdif_filter_line3_c((char *)dst1 + w0, d_stride, + (const char *)prev1 + w0, (const char *)cur1 + w0, (const char *)next1 + w0, s_stride, + w - w0, parity, clip_max); +} + +static void filter_line_helper(void *dst1, void *prev1, void *cur1, void *next1, + int w, int prefs, int mrefs, int prefs2, int mrefs2, + int prefs3, int mrefs3, int prefs4, int mrefs4, + int parity, int clip_max) +{ + const int w0 = clip_max != 255 ? 0 : w & ~15; + + ff_bwdif_filter_line_neon(dst1, prev1, cur1, next1, + w0, prefs, mrefs, prefs2, mrefs2, prefs3, mrefs3, prefs4, mrefs4, parity, clip_max); + + if (w0 < w) + ff_bwdif_filter_line_c((char *)dst1 + w0, (char *)prev1 + w0, (char *)cur1 + w0, (char *)next1 + w0, + w - w0, prefs, mrefs, prefs2, mrefs2, prefs3, mrefs3, prefs4, mrefs4, parity, clip_max); +} + +static void filter_edge_helper(void *dst1, void *prev1, void *cur1, void *next1, + int w, int prefs, int mrefs, int prefs2, int mrefs2, + int parity, int clip_max, int spat) +{ + const int w0 = clip_max != 255 ? 0 : w & ~15; + + ff_bwdif_filter_edge_neon(dst1, prev1, cur1, next1, w0, prefs, mrefs, prefs2, mrefs2, + parity, clip_max, spat); + + if (w0 < w) + ff_bwdif_filter_edge_c((char *)dst1 + w0, (char *)prev1 + w0, (char *)cur1 + w0, (char *)next1 + w0, + w - w0, prefs, mrefs, prefs2, mrefs2, + parity, clip_max, spat); +} + +static void filter_intra_helper(void *dst1, void *cur1, int w, int prefs, int mrefs, + int prefs3, int mrefs3, int parity, int clip_max) +{ + const int w0 = clip_max != 255 ? 0 : w & ~15; + + ff_bwdif_filter_intra_neon(dst1, cur1, w0, prefs, mrefs, prefs3, mrefs3, parity, clip_max); + + if (w0 < w) + ff_bwdif_filter_intra_c((char *)dst1 + w0, (char *)cur1 + w0, + w - w0, prefs, mrefs, prefs3, mrefs3, parity, clip_max); +} + +void +ff_bwdif_init_aarch64(BWDIFContext *s, int bit_depth) +{ + const int cpu_flags = av_get_cpu_flags(); + + if (bit_depth != 8) + return; + + if (!have_neon(cpu_flags)) + return; + + s->filter_intra = filter_intra_helper; + s->filter_line = filter_line_helper; + s->filter_edge = filter_edge_helper; + s->filter_line3 = filter_line3_helper; +} + diff --git a/libavfilter/aarch64/vf_bwdif_neon.S b/libavfilter/aarch64/vf_bwdif_neon.S new file mode 100644 index 00000000000..ae9aab20cdd --- /dev/null +++ b/libavfilter/aarch64/vf_bwdif_neon.S @@ -0,0 +1,788 @@ +/* + * bwdif aarch64 NEON optimisations + * + * Copyright (c) 2023 John Cox + * + * 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 "libavutil/aarch64/asm.S" + +// Space taken on the stack by an int (32-bit) +#ifdef __APPLE__ +.set SP_INT, 4 +#else +.set SP_INT, 8 +#endif + +.macro SQSHRUNN b, s0, s1, s2, s3, n + sqshrun \s0\().4h, \s0\().4s, #\n - 8 + sqshrun2 \s0\().8h, \s1\().4s, #\n - 8 + sqshrun \s1\().4h, \s2\().4s, #\n - 8 + sqshrun2 \s1\().8h, \s3\().4s, #\n - 8 + uzp2 \b\().16b, \s0\().16b, \s1\().16b +.endm + +.macro SMULL4K a0, a1, a2, a3, s0, s1, k + smull \a0\().4s, \s0\().4h, \k + smull2 \a1\().4s, \s0\().8h, \k + smull \a2\().4s, \s1\().4h, \k + smull2 \a3\().4s, \s1\().8h, \k +.endm + +.macro UMULL4K a0, a1, a2, a3, s0, s1, k + umull \a0\().4s, \s0\().4h, \k + umull2 \a1\().4s, \s0\().8h, \k + umull \a2\().4s, \s1\().4h, \k + umull2 \a3\().4s, \s1\().8h, \k +.endm + +.macro UMLAL4K a0, a1, a2, a3, s0, s1, k + umlal \a0\().4s, \s0\().4h, \k + umlal2 \a1\().4s, \s0\().8h, \k + umlal \a2\().4s, \s1\().4h, \k + umlal2 \a3\().4s, \s1\().8h, \k +.endm + +.macro UMLSL4K a0, a1, a2, a3, s0, s1, k + umlsl \a0\().4s, \s0\().4h, \k + umlsl2 \a1\().4s, \s0\().8h, \k + umlsl \a2\().4s, \s1\().4h, \k + umlsl2 \a3\().4s, \s1\().8h, \k +.endm + +// int b = m2s1 - m1; +// int f = p2s1 - p1; +// int dc = c0s1 - m1; +// int de = c0s1 - p1; +// int sp_max = FFMIN(p1 - c0s1, m1 - c0s1); +// sp_max = FFMIN(sp_max, FFMAX(-b,-f)); +// int sp_min = FFMIN(c0s1 - p1, c0s1 - m1); +// sp_min = FFMIN(sp_min, FFMAX(b,f)); +// diff = diff == 0 ? 0 : FFMAX3(diff, sp_min, sp_max); +.macro SPAT_CHECK diff, m2s1, m1, c0s1, p1, p2s1, t0, t1, t2, t3 + uqsub \t0\().16b, \p1\().16b, \c0s1\().16b + uqsub \t2\().16b, \m1\().16b, \c0s1\().16b + umin \t2\().16b, \t0\().16b, \t2\().16b + + uqsub \t1\().16b, \m1\().16b, \m2s1\().16b + uqsub \t3\().16b, \p1\().16b, \p2s1\().16b + umax \t3\().16b, \t3\().16b, \t1\().16b + umin \t3\().16b, \t3\().16b, \t2\().16b + + uqsub \t0\().16b, \c0s1\().16b, \p1\().16b + uqsub \t2\().16b, \c0s1\().16b, \m1\().16b + umin \t2\().16b, \t0\().16b, \t2\().16b + + uqsub \t1\().16b, \m2s1\().16b, \m1\().16b + uqsub \t0\().16b, \p2s1\().16b, \p1\().16b + umax \t0\().16b, \t0\().16b, \t1\().16b + umin \t2\().16b, \t2\().16b, \t0\().16b + + cmeq \t1\().16b, \diff\().16b, #0 + umax \diff\().16b, \diff\().16b, \t3\().16b + umax \diff\().16b, \diff\().16b, \t2\().16b + bic \diff\().16b, \diff\().16b, \t1\().16b +.endm + +// i0 = s0; +// if (i0 > d0 + diff0) +// i0 = d0 + diff0; +// else if (i0 < d0 - diff0) +// i0 = d0 - diff0; +// +// i0 = s0 is safe +.macro DIFF_CLIP i0, s0, d0, diff, t0, t1 + uqadd \t0\().16b, \d0\().16b, \diff\().16b + uqsub \t1\().16b, \d0\().16b, \diff\().16b + umin \i0\().16b, \s0\().16b, \t0\().16b + umax \i0\().16b, \i0\().16b, \t1\().16b +.endm + +// i0 = FFABS(m1 - p1) > td0 ? i1 : i2; +// DIFF_CLIP +// +// i0 = i1 is safe +.macro INTERPOL i0, i1, i2, m1, d0, p1, td0, diff, t0, t1, t2 + uabd \t0\().16b, \m1\().16b, \p1\().16b + cmhi \t0\().16b, \t0\().16b, \td0\().16b + bsl \t0\().16b, \i1\().16b, \i2\().16b + DIFF_CLIP \i0, \t0, \d0, \diff, \t1, \t2 +.endm + +.macro PUSH_VREGS + stp d8, d9, [sp, #-64]! + stp d10, d11, [sp, #16] + stp d12, d13, [sp, #32] + stp d14, d15, [sp, #48] +.endm + +.macro POP_VREGS + ldp d14, d15, [sp, #48] + ldp d12, d13, [sp, #32] + ldp d10, d11, [sp, #16] + ldp d8, d9, [sp], #64 +.endm + +.macro LDR_COEFFS d, t0 + movrel \t0, coeffs, 0 + ld1 {\d\().8h}, [\t0] +.endm + +// static const uint16_t coef_lf[2] = { 4309, 213 }; +// static const uint16_t coef_hf[3] = { 5570, 3801, 1016 }; +// static const uint16_t coef_sp[2] = { 5077, 981 }; + +const coeffs, align=4 // align 4 means align on 2^4 boundry + .hword 4309 * 4, 213 * 4 // lf[0]*4 = v0.h[0] + .hword 5570, 3801, 1016, -3801 // hf[0] = v0.h[2], -hf[1] = v0.h[5] + .hword 5077, 981 // sp[0] = v0.h[6] +endconst + +// =========================================================================== +// +// void ff_bwdif_filter_line3_neon( +// void * dst1, // x0 +// int d_stride, // w1 +// const void * prev1, // x2 +// const void * cur1, // x3 +// const void * next1, // x4 +// int s_stride, // w5 +// int w, // w6 +// int parity, // w7 +// int clip_max); // [sp, #0] (Ignored) + +function ff_bwdif_filter_line3_neon, export=1 + // Sanity check w + cmp w6, #0 + ble 99f + + LDR_COEFFS v0, x17 + +// #define prev2 cur +// const uint8_t * restrict next2 = parity ? prev : next; + cmp w7, #0 + csel x17, x2, x4, ne + + // We want all the V registers - save all the ones we must + PUSH_VREGS + + // Some rearrangement of initial values for nice layout of refs in regs + mov w10, w6 // w10 = loop count + neg w9, w5 // w9 = mref + lsl w8, w9, #1 // w8 = mref2 + add w7, w9, w9, LSL #1 // w7 = mref3 + lsl w6, w9, #2 // w6 = mref4 + mov w11, w5 // w11 = pref + lsl w12, w5, #1 // w12 = pref2 + add w13, w5, w5, LSL #1 // w13 = pref3 + lsl w14, w5, #2 // w14 = pref4 + add w15, w5, w5, LSL #2 // w15 = pref5 + add w16, w14, w12 // w16 = pref6 + + lsl w5, w1, #1 // w5 = d_stride * 2 + +// for (x = 0; x < w; x++) { +// int diff0, diff2; +// int d0, d2; +// int temporal_diff0, temporal_diff2; +// +// int i1, i2; +// int j1, j2; +// int p6, p5, p4, p3, p2, p1, c0, m1, m2, m3, m4; + +10: +// c0 = prev2[0] + next2[0]; // c0 = v20, v21 +// d0 = c0 >> 1; // d0 = v10 +// temporal_diff0 = FFABS(prev2[0] - next2[0]); // td0 = v11 + ldr q31, [x3] + ldr q21, [x17] + uhadd v10.16b, v31.16b, v21.16b + uabd v11.16b, v31.16b, v21.16b + uaddl v20.8h, v21.8b, v31.8b + uaddl2 v21.8h, v21.16b, v31.16b + + ldr q31, [x3, w6, sxtw] + ldr q23, [x17, w6, sxtw] + +// i1 = coef_hf[0] * c0; // i1 = v2-v5 + UMULL4K v2, v3, v4, v5, v20, v21, v0.h[2] + + ldr q30, [x3, w14, sxtw] + ldr q25, [x17, w14, sxtw] + +// m4 = prev2[mrefs4] + next2[mrefs4]; // m4 = v22,v23 + uaddl v22.8h, v23.8b, v31.8b + uaddl2 v23.8h, v23.16b, v31.16b + +// p4 = prev2[prefs4] + next2[prefs4]; // p4 = v24,v25, (p4 >> 1) = v12 + uhadd v12.16b, v25.16b, v30.16b + uaddl v24.8h, v25.8b, v30.8b + uaddl2 v25.8h, v25.16b, v30.16b + +// j1 = -coef_hf[1] * (c0 + p4); // j1 = v6-v9 (-c0:v20,v21) + add v20.8h, v20.8h, v24.8h + add v21.8h, v21.8h, v25.8h + SMULL4K v6, v7, v8, v9, v20, v21, v0.h[5] + +// m3 = cur[mrefs3]; // m3 = v20 + ldr q20, [x3, w7, sxtw] + +// p3 = cur[prefs3]; // p3 = v21 + ldr q21, [x3, w13, sxtw] + +// i1 += coef_hf[2] * (m4 + p4); // (-m4:v22,v23) (-p4:v24,v25) + add v22.8h, v22.8h, v24.8h + add v23.8h, v23.8h, v25.8h + UMLAL4K v2, v3, v4, v5, v22, v23, v0.h[4] + + ldr q29, [x3, w8, sxtw] + ldr q23, [x17, w8, sxtw] + +// i1 -= coef_lf[1] * 4 * (m3 + p3); // - + uaddl v30.8h, v20.8b, v21.8b + uaddl2 v31.8h, v20.16b, v21.16b + + ldr q28, [x3, w16, sxtw] + ldr q25, [x17, w16, sxtw] + + UMLSL4K v2, v3, v4, v5, v30, v31, v0.h[1] + +// m2 = prev2[mrefs2] + next2[mrefs2]; // m2 = v22,v23, (m2 >> 1) = v13 + uhadd v13.16b, v23.16b, v29.16b + uaddl v22.8h, v23.8b, v29.8b + uaddl2 v23.8h, v23.16b, v29.16b + + ldr q31, [x3, w12, sxtw] + ldr q27, [x17, w12, sxtw] + +// p6 = prev2[prefs6] + next2[prefs6]; // p6 = v24,v25 + uaddl v24.8h, v25.8b, v28.8b + uaddl2 v25.8h, v25.16b, v28.16b + +// j1 += coef_hf[2] * (m2 + p6); // (-p6:v24,v25) + add v24.8h, v24.8h, v22.8h + add v25.8h, v25.8h, v23.8h + UMLAL4K v6, v7, v8, v9, v24, v25, v0.h[4] + +// m1 = cur[mrefs]; // m1 = v24 + ldr q24, [x3, w9, sxtw] + +// p5 = cur[prefs5]; // p5 = v25 + ldr q25, [x3, w15, sxtw] + +// p2 = prev2[prefs2] + next2[prefs2]; // p2 = v26, v27 +// temporal_diff2 = FFABS(prev2[prefs2] - next2[prefs2]); // td2 = v14 +// d2 = p2 >> 1; // d2 = v15 + uabd v14.16b, v31.16b, v27.16b + uhadd v15.16b, v31.16b, v27.16b + uaddl v26.8h, v27.8b, v31.8b + uaddl2 v27.8h, v27.16b, v31.16b + +// j1 += coef_hf[0] * p2; // - + UMLAL4K v6, v7, v8, v9, v26, v27, v0.h[2] + +// i1 -= coef_hf[1] * (m2 + p2); // (-m2:v22,v23*) (-p2:v26*,v27*) + add v22.8h, v22.8h, v26.8h + add v23.8h, v23.8h, v27.8h + UMLSL4K v2, v3, v4, v5, v22, v23, v0.h[3] + +// p1 = cur[prefs]; // p1 = v22 + ldr q22, [x3, w11, sxtw] + +// j1 -= coef_lf[1] * 4 * (m1 + p5); // - + uaddl v26.8h, v24.8b, v25.8b + uaddl2 v27.8h, v24.16b, v25.16b + UMLSL4K v6, v7, v8, v9, v26, v27, v0.h[1] + +// j2 = (coef_sp[0] * (p1 + p3) - coef_sp[1] * (m1 + p5)) >> 13; // (-p5:v25*) j2=v16 + uaddl v18.8h, v22.8b, v21.8b + uaddl2 v19.8h, v22.16b, v21.16b + UMULL4K v28, v29, v30, v31, v18, v19, v0.h[6] + + uaddl v18.8h, v24.8b, v25.8b + uaddl2 v19.8h, v24.16b, v25.16b + UMLSL4K v28, v29, v30, v31, v18, v19, v0.h[7] + + SQSHRUNN v16, v28, v29, v30, v31, 13 + +// i2 = (coef_sp[0] * (m1 + p1) - coef_sp[1] * (m3 + p3)) >> 13; // (-m3:v20*) i2=v17 + uaddl v18.8h, v22.8b, v24.8b + uaddl2 v19.8h, v22.16b, v24.16b + UMULL4K v28, v29, v30, v31, v18, v19, v0.h[6] + + uaddl v18.8h, v20.8b, v21.8b + uaddl2 v19.8h, v20.16b, v21.16b + UMLSL4K v28, v29, v30, v31, v18, v19, v0.h[7] + + SQSHRUNN v17, v28, v29, v30, v31, 13 + +// i1 += coef_lf[0] * 4 * (m1 + p1); // p1 = v22, m1 = v24 + uaddl v26.8h, v24.8b, v22.8b + uaddl2 v27.8h, v24.16b, v22.16b + UMLAL4K v2, v3, v4, v5, v26, v27, v0.h[0] + + ldr q31, [x2, w9, sxtw] + ldr q29, [x4, w9, sxtw] + +// j1 += coef_lf[0] * 4 * (p1 + p3); // p1 = v22, p3 = v21 + uaddl v26.8h, v21.8b, v22.8b + uaddl2 v27.8h, v21.16b, v22.16b + UMLAL4K v6, v7, v8, v9, v26, v27, v0.h[0] + + ldr q30, [x2, w11, sxtw] + ldr q28, [x4, w11, sxtw] + +// i1 >>= 15; // i1 = v2, -v3, -v4*, -v5* + SQSHRUNN v2, v2, v3, v4, v5, 15 + +// j1 >>= 15; // j1 = v3, -v6*, -v7*, -v8*, -v9* + SQSHRUNN v3, v6, v7, v8, v9, 15 + +// { +// int t1 =(FFABS(prev[mrefs] - m1) + FFABS(prev[prefs] - p1)) >> 1; +// int t2 =(FFABS(next[mrefs] - m1) + FFABS(next[prefs] - p1)) >> 1; + uabd v30.16b, v22.16b, v30.16b + uabd v31.16b, v24.16b, v31.16b + uabd v28.16b, v22.16b, v28.16b + uabd v29.16b, v24.16b, v29.16b + uhadd v31.16b, v31.16b, v30.16b + uhadd v29.16b, v29.16b, v28.16b + + ldr q27, [x2, w13, sxtw] + ldr q26, [x4, w13, sxtw] + +// diff0 = FFMAX3(temporal_diff0 >> 1, t1, t2); // diff0=v18 + ushr v18.16b, v11.16b, #1 + umax v18.16b, v18.16b, v31.16b + umax v18.16b, v18.16b, v29.16b +// } // v28, v30 preserved for next block +// { // tdiff2 = v14 +// int t1 =(FFABS(prev[prefs] - p1) + FFABS(prev[prefs3] - p3)) >> 1; +// int t2 =(FFABS(next[prefs] - p1) + FFABS(next[prefs3] - p3)) >> 1; + uabd v31.16b, v21.16b, v27.16b + uabd v29.16b, v21.16b, v26.16b + uhadd v31.16b, v31.16b, v30.16b + uhadd v29.16b, v29.16b, v28.16b + +// diff2 = FFMAX3(temporal_diff2 >> 1, t1, t2); // diff2=v19 + ushr v19.16b, v14.16b, #1 + umax v19.16b, v19.16b, v31.16b + umax v19.16b, v19.16b, v29.16b +// } + + // diff0 = v18, (m2 >> 1) = v13, m1 = v24, d0 = v10, p1 = v22, d2 = v15 + SPAT_CHECK v18, v13, v24, v10, v22, v15, v31, v30, v29, v28 + + // diff2 = v19, d0 = v10, p1 = v22, d2 = v15, p3 = v21, (p4 >> 1) = v12 + SPAT_CHECK v19, v10, v22, v15, v21, v12, v31, v30, v29, v28 + + // j1 = v3, j2 = v16, p1 = v22, d2 = v15, p3 = v21, td2 = v14, diff2 = v19 + INTERPOL v3, v3, v16, v22, v15, v21, v14, v19, v31, v30, v29 + +// dst[d_stride * 2] = av_clip_uint8(interpol); + str q3, [x0, w5, sxtw] + +// dst[d_stride] = p1; + str q22, [x0, w1, sxtw] + + // i1 = v2, i2 = v17, m1 = v24, d0 = v10, p1 = v22, td2 = v11, diff2 = v18 + INTERPOL v2, v2, v17, v24, v10, v22, v11, v18, v31, v30, v29 + +// dst[0] = av_clip_uint8(interpol); + str q2, [x0], #16 +// } +// +// dst++; +// cur++; +// prev++; +// prev2++; +// next++; +// } + subs w10, w10, #16 + add x2, x2, #16 + add x3, x3, #16 + add x4, x4, #16 + add x17, x17, #16 + bgt 10b + + POP_VREGS +99: + ret +endfunc + +// =========================================================================== +// +// void filter_line( +// void *dst1, // x0 +// void *prev1, // x1 +// void *cur1, // x2 +// void *next1, // x3 +// int w, // w4 +// int prefs, // w5 +// int mrefs, // w6 +// int prefs2, // w7 +// int mrefs2, // [sp, #0] +// int prefs3, // [sp, #SP_INT] +// int mrefs3, // [sp, #SP_INT*2] +// int prefs4, // [sp, #SP_INT*3] +// int mrefs4, // [sp, #SP_INT*4] +// int parity, // [sp, #SP_INT*5] +// int clip_max) // [sp, #SP_INT*6] + +function ff_bwdif_filter_line_neon, export=1 + // Sanity check w + cmp w4, #0 + ble 99f + + // Rearrange regs to be the same as line3 for ease of debug! + mov w10, w4 // w10 = loop count + mov w9, w6 // w9 = mref + mov w12, w7 // w12 = pref2 + mov w11, w5 // w11 = pref + ldr w8, [sp, #0] // w8 = mref2 + ldr w7, [sp, #SP_INT*2] // w7 = mref3 + ldr w6, [sp, #SP_INT*4] // w6 = mref4 + ldr w13, [sp, #SP_INT] // w13 = pref3 + ldr w14, [sp, #SP_INT*3] // w14 = pref4 + + mov x4, x3 + mov x3, x2 + mov x2, x1 + + LDR_COEFFS v0, x17 + +// #define prev2 cur +// const uint8_t * restrict next2 = parity ? prev : next; + ldr w17, [sp, #SP_INT*5] // parity + cmp w17, #0 + csel x17, x2, x4, ne + + PUSH_VREGS + +// for (x = 0; x < w; x++) { +// int diff0, diff2; +// int d0, d2; +// int temporal_diff0, temporal_diff2; +// +// int i1, i2; +// int j1, j2; +// int p6, p5, p4, p3, p2, p1, c0, m1, m2, m3, m4; + +10: +// c0 = prev2[0] + next2[0]; // c0 = v20, v21 +// d0 = c0 >> 1; // d0 = v10 +// temporal_diff0 = FFABS(prev2[0] - next2[0]); // td0 = v11 + ldr q31, [x3] + ldr q21, [x17] + uhadd v10.16b, v31.16b, v21.16b + uabd v11.16b, v31.16b, v21.16b + uaddl v20.8h, v21.8b, v31.8b + uaddl2 v21.8h, v21.16b, v31.16b + + ldr q31, [x3, w6, sxtw] + ldr q23, [x17, w6, sxtw] + +// i1 = coef_hf[0] * c0; // i1 = v2-v5 + UMULL4K v2, v3, v4, v5, v20, v21, v0.h[2] + + ldr q30, [x3, w14, sxtw] + ldr q25, [x17, w14, sxtw] + +// m4 = prev2[mrefs4] + next2[mrefs4]; // m4 = v22,v23 + uaddl v22.8h, v23.8b, v31.8b + uaddl2 v23.8h, v23.16b, v31.16b + +// p4 = prev2[prefs4] + next2[prefs4]; // p4 = v24,v25, (p4 >> 1) = v12 + uhadd v12.16b, v25.16b, v30.16b + uaddl v24.8h, v25.8b, v30.8b + uaddl2 v25.8h, v25.16b, v30.16b + +// m3 = cur[mrefs3]; // m3 = v20 + ldr q20, [x3, w7, sxtw] + +// p3 = cur[prefs3]; // p3 = v21 + ldr q21, [x3, w13, sxtw] + +// i1 += coef_hf[2] * (m4 + p4); // (-m4:v22,v23) (-p4:v24,v25) + add v22.8h, v22.8h, v24.8h + add v23.8h, v23.8h, v25.8h + UMLAL4K v2, v3, v4, v5, v22, v23, v0.h[4] + + ldr q29, [x3, w8, sxtw] + ldr q23, [x17, w8, sxtw] + +// i1 -= coef_lf[1] * 4 * (m3 + p3); // - + uaddl v30.8h, v20.8b, v21.8b + uaddl2 v31.8h, v20.16b, v21.16b + + UMLSL4K v2, v3, v4, v5, v30, v31, v0.h[1] + + ldr q31, [x3, w12, sxtw] + ldr q27, [x17, w12, sxtw] + +// m2 = prev2[mrefs2] + next2[mrefs2]; // m2 = v22,v23, (m2 >> 1) = v13 + uhadd v13.16b, v23.16b, v29.16b + uaddl v22.8h, v23.8b, v29.8b + uaddl2 v23.8h, v23.16b, v29.16b + +// m1 = cur[mrefs]; // m1 = v24 + ldr q24, [x3, w9, sxtw] + +// p2 = prev2[prefs2] + next2[prefs2]; // p2 = v26, v27 +// temporal_diff2 = FFABS(prev2[prefs2] - next2[prefs2]); // td2 = v14 +// d2 = p2 >> 1; // d2 = v15 + uabd v14.16b, v31.16b, v27.16b + uhadd v15.16b, v31.16b, v27.16b + uaddl v26.8h, v27.8b, v31.8b + uaddl2 v27.8h, v27.16b, v31.16b + +// i1 -= coef_hf[1] * (m2 + p2); // (-m2:v22,v23*) (-p2:v26*,v27*) + add v22.8h, v22.8h, v26.8h + add v23.8h, v23.8h, v27.8h + UMLSL4K v2, v3, v4, v5, v22, v23, v0.h[3] + +// p1 = cur[prefs]; // p1 = v22 + ldr q22, [x3, w11, sxtw] + +// i2 = (coef_sp[0] * (m1 + p1) - coef_sp[1] * (m3 + p3)) >> 13; // (-m3:v20*) i2=v17 + uaddl v18.8h, v22.8b, v24.8b + uaddl2 v19.8h, v22.16b, v24.16b + UMULL4K v28, v29, v30, v31, v18, v19, v0.h[6] + + uaddl v18.8h, v20.8b, v21.8b + uaddl2 v19.8h, v20.16b, v21.16b + UMLSL4K v28, v29, v30, v31, v18, v19, v0.h[7] + + SQSHRUNN v17, v28, v29, v30, v31, 13 + +// i1 += coef_lf[0] * 4 * (m1 + p1); // p1 = v22, m1 = v24 + uaddl v26.8h, v24.8b, v22.8b + uaddl2 v27.8h, v24.16b, v22.16b + UMLAL4K v2, v3, v4, v5, v26, v27, v0.h[0] + + ldr q31, [x2, w9, sxtw] + ldr q29, [x4, w9, sxtw] + + ldr q30, [x2, w11, sxtw] + ldr q28, [x4, w11, sxtw] + +// i1 >>= 15; // i1 = v2, -v3, -v4*, -v5* + SQSHRUNN v2, v2, v3, v4, v5, 15 + +// { +// int t1 =(FFABS(prev[mrefs] - m1) + FFABS(prev[prefs] - p1)) >> 1; +// int t2 =(FFABS(next[mrefs] - m1) + FFABS(next[prefs] - p1)) >> 1; + uabd v30.16b, v22.16b, v30.16b + uabd v31.16b, v24.16b, v31.16b + uabd v28.16b, v22.16b, v28.16b + uabd v29.16b, v24.16b, v29.16b + uhadd v31.16b, v31.16b, v30.16b + uhadd v29.16b, v29.16b, v28.16b + +// diff0 = FFMAX3(temporal_diff0 >> 1, t1, t2); // diff0=v18 + ushr v18.16b, v11.16b, #1 + umax v18.16b, v18.16b, v31.16b + umax v18.16b, v18.16b, v29.16b + + // diff0 = v18, (m2 >> 1) = v13, m1 = v24, d0 = v10, p1 = v22, d2 = v15 + SPAT_CHECK v18, v13, v24, v10, v22, v15, v31, v30, v29, v28 + + // i1 = v2, i2 = v17, m1 = v24, d0 = v10, p1 = v22, td2 = v11, diff2 = v18 + INTERPOL v2, v2, v17, v24, v10, v22, v11, v18, v31, v30, v29 + +// dst[0] = av_clip_uint8(interpol); + str q2, [x0], #16 +// } +// +// dst++; +// cur++; +// prev++; +// prev2++; +// next++; +// } + + subs w10, w10, #16 + add x2, x2, #16 + add x3, x3, #16 + add x4, x4, #16 + add x17, x17, #16 + bgt 10b + + POP_VREGS +99: + ret +endfunc + +// ============================================================================ +// +// void ff_bwdif_filter_edge_neon( +// void *dst1, // x0 +// void *prev1, // x1 +// void *cur1, // x2 +// void *next1, // x3 +// int w, // w4 +// int prefs, // w5 +// int mrefs, // w6 +// int prefs2, // w7 +// int mrefs2, // [sp, #0] +// int parity, // [sp, #SP_INT] +// int clip_max, // [sp, #SP_INT*2] unused +// int spat); // [sp, #SP_INT*3] + +function ff_bwdif_filter_edge_neon, export=1 + // Sanity check w + cmp w4, #0 + ble 99f + +// #define prev2 cur +// const uint8_t * restrict next2 = parity ? prev : next; + + ldr w8, [sp, #0] // mrefs2 + + ldr w17, [sp, #SP_INT] // parity + ldr w16, [sp, #SP_INT*3] // spat + cmp w17, #0 + csel x17, x1, x3, ne + +// for (x = 0; x < w; x++) { + +10: +// int m1 = cur[mrefs]; +// int d = (prev2[0] + next2[0]) >> 1; +// int p1 = cur[prefs]; +// int temporal_diff0 = FFABS(prev2[0] - next2[0]); +// int temporal_diff1 =(FFABS(prev[mrefs] - m1) + FFABS(prev[prefs] - p1)) >> 1; +// int temporal_diff2 =(FFABS(next[mrefs] - m1) + FFABS(next[prefs] - p1)) >> 1; +// int diff = FFMAX3(temporal_diff0 >> 1, temporal_diff1, temporal_diff2); + ldr q31, [x2] + ldr q21, [x17] + uhadd v16.16b, v31.16b, v21.16b // d0 = v16 + uabd v17.16b, v31.16b, v21.16b // td0 = v17 + ldr q24, [x2, w6, sxtw] // m1 = v24 + ldr q22, [x2, w5, sxtw] // p1 = v22 + + ldr q0, [x1, w6, sxtw] // prev[mrefs] + ldr q2, [x1, w5, sxtw] // prev[prefs] + ldr q1, [x3, w6, sxtw] // next[mrefs] + ldr q3, [x3, w5, sxtw] // next[prefs] + + ushr v29.16b, v17.16b, #1 + + uabd v31.16b, v0.16b, v24.16b + uabd v30.16b, v2.16b, v22.16b + uhadd v0.16b, v31.16b, v30.16b // td1 = q0 + + uabd v31.16b, v1.16b, v24.16b + uabd v30.16b, v3.16b, v22.16b + uhadd v1.16b, v31.16b, v30.16b // td2 = q1 + + umax v0.16b, v0.16b, v29.16b + umax v0.16b, v0.16b, v1.16b // diff = v0 + +// if (spat) { +// SPAT_CHECK() +// } +// i0 = (m1 + p1) >> 1; + cbz w16, 1f + + ldr q31, [x2, w8, sxtw] + ldr q18, [x17, w8, sxtw] + ldr q30, [x2, w7, sxtw] + ldr q19, [x17, w7, sxtw] + uhadd v18.16b, v18.16b, v31.16b + uhadd v19.16b, v19.16b, v30.16b + + SPAT_CHECK v0, v18, v24, v16, v22, v19, v31, v30, v29, v28 + +1: + uhadd v2.16b, v22.16b, v24.16b + + // i0 = v2, s0 = v2, d0 = v16, diff = v0, t0 = v31, t1 = v30 + DIFF_CLIP v2, v2, v16, v0, v31, v30 + +// dst[0] = av_clip(interpol, 0, clip_max); + str q2, [x0], #16 + +// dst++; +// cur++; +// } + subs w4, w4, #16 + add x1, x1, #16 + add x2, x2, #16 + add x3, x3, #16 + add x17, x17, #16 + bgt 10b + +99: + ret +endfunc + +// ============================================================================ +// +// void ff_bwdif_filter_intra_neon( +// void *dst1, // x0 +// void *cur1, // x1 +// int w, // w2 +// int prefs, // w3 +// int mrefs, // w4 +// int prefs3, // w5 +// int mrefs3, // w6 +// int parity, // w7 unused +// int clip_max) // [sp, #0] unused + +function ff_bwdif_filter_intra_neon, export=1 + cmp w2, #0 + ble 99f + + LDR_COEFFS v0, x17 + +// for (x = 0; x < w; x++) { +10: + +// interpol = (coef_sp[0] * (cur[mrefs] + cur[prefs]) - coef_sp[1] * (cur[mrefs3] + cur[prefs3])) >> 13; + ldr q31, [x1, w4, sxtw] + ldr q30, [x1, w3, sxtw] + ldr q29, [x1, w6, sxtw] + ldr q28, [x1, w5, sxtw] + + uaddl v20.8h, v31.8b, v30.8b + uaddl2 v21.8h, v31.16b, v30.16b + + UMULL4K v2, v3, v4, v5, v20, v21, v0.h[6] + + uaddl v20.8h, v29.8b, v28.8b + uaddl2 v21.8h, v29.16b, v28.16b + + UMLSL4K v2, v3, v4, v5, v20, v21, v0.h[7] + +// dst[0] = av_clip(interpol, 0, clip_max); + SQSHRUNN v2, v2, v3, v4, v5, 13 + str q2, [x0], #16 + +// dst++; +// cur++; +// } + + subs w2, w2, #16 + add x1, x1, #16 + bgt 10b + +99: + ret +endfunc diff --git a/libavfilter/adynamicequalizer_template.c b/libavfilter/adynamicequalizer_template.c new file mode 100644 index 00000000000..26fcebf8bcc --- /dev/null +++ b/libavfilter/adynamicequalizer_template.c @@ -0,0 +1,264 @@ +/* + * 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 + */ + +#undef ftype +#undef SQRT +#undef TAN +#undef ONE +#undef TWO +#undef ZERO +#undef FMAX +#undef FMIN +#undef CLIP +#undef SAMPLE_FORMAT +#undef EPSILON +#undef FABS +#if DEPTH == 32 +#define SAMPLE_FORMAT float +#define SQRT sqrtf +#define TAN tanf +#define ONE 1.f +#define TWO 2.f +#define ZERO 0.f +#define FMIN fminf +#define FMAX fmaxf +#define CLIP av_clipf +#define FABS fabsf +#define ftype float +#define EPSILON (1.f / (1 << 22)) +#else +#define SAMPLE_FORMAT double +#define SQRT sqrt +#define TAN tan +#define ONE 1.0 +#define TWO 2.0 +#define ZERO 0.0 +#define FMIN fmin +#define FMAX fmax +#define CLIP av_clipd +#define FABS fabs +#define ftype double +#define EPSILON (1.0 / (1LL << 51)) +#endif + +#define fn3(a,b) a##_##b +#define fn2(a,b) fn3(a,b) +#define fn(a) fn2(a, SAMPLE_FORMAT) + +static ftype fn(get_svf)(ftype in, const ftype *m, const ftype *a, ftype *b) +{ + const ftype v0 = in; + const ftype v3 = v0 - b[1]; + const ftype v1 = a[0] * b[0] + a[1] * v3; + const ftype v2 = b[1] + a[1] * b[0] + a[2] * v3; + + b[0] = TWO * v1 - b[0]; + b[1] = TWO * v2 - b[1]; + + return m[0] * v0 + m[1] * v1 + m[2] * v2; +} + +static int fn(filter_prepare)(AVFilterContext *ctx) +{ + AudioDynamicEqualizerContext *s = ctx->priv; + const ftype sample_rate = ctx->inputs[0]->sample_rate; + const ftype dfrequency = FMIN(s->dfrequency, sample_rate * 0.5); + const ftype dg = TAN(M_PI * dfrequency / sample_rate); + const ftype dqfactor = s->dqfactor; + const int dftype = s->dftype; + ftype *da = fn(s->da); + ftype *dm = fn(s->dm); + ftype k; + + s->attack_coef = get_coef(s->attack, sample_rate); + s->release_coef = get_coef(s->release, sample_rate); + + switch (dftype) { + case 0: + k = ONE / dqfactor; + + da[0] = ONE / (ONE + dg * (dg + k)); + da[1] = dg * da[0]; + da[2] = dg * da[1]; + + dm[0] = ZERO; + dm[1] = k; + dm[2] = ZERO; + break; + case 1: + k = ONE / dqfactor; + + da[0] = ONE / (ONE + dg * (dg + k)); + da[1] = dg * da[0]; + da[2] = dg * da[1]; + + dm[0] = ZERO; + dm[1] = ZERO; + dm[2] = ONE; + break; + case 2: + k = ONE / dqfactor; + + da[0] = ONE / (ONE + dg * (dg + k)); + da[1] = dg * da[0]; + da[2] = dg * da[1]; + + dm[0] = ZERO; + dm[1] = -k; + dm[2] = -ONE; + break; + case 3: + k = ONE / dqfactor; + + da[0] = ONE / (ONE + dg * (dg + k)); + da[1] = dg * da[0]; + da[2] = dg * da[1]; + + dm[0] = ONE; + dm[1] = -k; + dm[2] = -TWO; + break; + } + + return 0; +} + +static int fn(filter_channels)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + AudioDynamicEqualizerContext *s = ctx->priv; + ThreadData *td = arg; + AVFrame *in = td->in; + AVFrame *out = td->out; + const ftype sample_rate = in->sample_rate; + const ftype makeup = s->makeup; + const ftype ratio = s->ratio; + const ftype range = s->range; + const ftype tfrequency = FMIN(s->tfrequency, sample_rate * 0.5); + const ftype release = s->release_coef; + const ftype attack = s->attack_coef; + const ftype tqfactor = s->tqfactor; + const ftype itqfactor = ONE / tqfactor; + const ftype fg = TAN(M_PI * tfrequency / sample_rate); + const int start = (in->ch_layout.nb_channels * jobnr) / nb_jobs; + const int end = (in->ch_layout.nb_channels * (jobnr+1)) / nb_jobs; + const int detection = s->detection; + const int direction = s->direction; + const int tftype = s->tftype; + const int mode = s->mode; + const ftype *da = fn(s->da); + const ftype *dm = fn(s->dm); + + for (int ch = start; ch < end; ch++) { + const ftype *src = (const ftype *)in->extended_data[ch]; + ftype *dst = (ftype *)out->extended_data[ch]; + ftype *state = (ftype *)s->state->extended_data[ch]; + const ftype threshold = detection == 0 ? state[5] : s->threshold; + ftype fa[3], fm[3]; + + if (detection < 0) + state[5] = threshold; + + memcpy(fa, state + 8, sizeof(fa)); + memcpy(fm, state + 11, sizeof(fm)); + + for (int n = 0; n < out->nb_samples; n++) { + ftype detect, gain, v, listen; + ftype k, g; + + detect = listen = fn(get_svf)(src[n], dm, da, state); + detect = FABS(detect); + + if (detection > 0) + state[5] = FMAX(state[5], detect); + + if (mode >= 0) { + if (direction == 0 && detect < threshold) { + detect = CLIP(ONE + makeup + (threshold - detect) * ratio, ONE, range); + if (!mode) + detect = ONE / detect; + } else if (direction == 1 && detect > threshold) { + detect = CLIP(ONE + makeup + (detect - threshold) * ratio, ONE, range); + if (!mode) + detect = ONE / detect; + } else { + detect = ONE; + } + + { + ftype delta = detect - state[4]; + + if (delta > EPSILON) + detect = state[4] + attack * delta; + else if (delta < -EPSILON) + detect = state[4] + release * delta; + } + } + + if (state[4] != detect) { + state[4] = gain = detect; + + switch (tftype) { + case 0: + k = itqfactor / gain; + + fa[0] = ONE / (ONE + fg * (fg + k)); + fa[1] = fg * fa[0]; + fa[2] = fg * fa[1]; + + fm[0] = ONE; + fm[1] = k * (gain * gain - ONE); + fm[2] = ZERO; + break; + case 1: + k = itqfactor; + g = fg / SQRT(gain); + + fa[0] = ONE / (ONE + g * (g + k)); + fa[1] = g * fa[0]; + fa[2] = g * fa[1]; + + fm[0] = ONE; + fm[1] = k * (gain - ONE); + fm[2] = gain * gain - ONE; + break; + case 2: + k = itqfactor; + g = fg * SQRT(gain); + + fa[0] = ONE / (ONE + g * (g + k)); + fa[1] = g * fa[0]; + fa[2] = g * fa[1]; + + fm[0] = gain * gain; + fm[1] = k * (ONE - gain) * gain; + fm[2] = ONE - gain * gain; + break; + } + } + + v = fn(get_svf)(src[n], fm, fa, &state[2]); + v = mode == -1 ? listen : v; + dst[n] = ctx->is_disabled ? src[n] : v; + } + + memcpy(state + 8, fa, sizeof(fa)); + memcpy(state + 11, fm, sizeof(fm)); + } + + return 0; +} diff --git a/libavfilter/aeval.c b/libavfilter/aeval.c index 94d7690ef4d..b26d75f129a 100644 --- a/libavfilter/aeval.c +++ b/libavfilter/aeval.c @@ -33,6 +33,7 @@ #include "avfilter.h" #include "audio.h" #include "filters.h" +#include "formats.h" #include "internal.h" static const char * const var_names[] = { diff --git a/libavfilter/af_acontrast.c b/libavfilter/af_acontrast.c index a4ed29e30aa..1fcbad8782f 100644 --- a/libavfilter/af_acontrast.c +++ b/libavfilter/af_acontrast.c @@ -23,7 +23,6 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" typedef struct AudioContrastContext { const AVClass *class; @@ -169,20 +168,13 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_acontrast = { .name = "acontrast", .description = NULL_IF_CONFIG_SMALL("Simple audio dynamic range compression/expansion filter."), .priv_size = sizeof(AudioContrastContext), .priv_class = &acontrast_class, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBLP), }; diff --git a/libavfilter/af_acopy.c b/libavfilter/af_acopy.c index 1591ec9639f..f65f04d4618 100644 --- a/libavfilter/af_acopy.c +++ b/libavfilter/af_acopy.c @@ -53,17 +53,10 @@ static const AVFilterPad acopy_inputs[] = { }, }; -static const AVFilterPad acopy_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_acopy = { .name = "acopy", .description = NULL_IF_CONFIG_SMALL("Copy the input audio unchanged to the output."), .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(acopy_inputs), - FILTER_OUTPUTS(acopy_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), }; diff --git a/libavfilter/af_acrusher.c b/libavfilter/af_acrusher.c index 5c9f36529c7..f29524c9604 100644 --- a/libavfilter/af_acrusher.c +++ b/libavfilter/af_acrusher.c @@ -325,13 +325,6 @@ static const AVFilterPad avfilter_af_acrusher_inputs[] = { }, }; -static const AVFilterPad avfilter_af_acrusher_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_acrusher = { .name = "acrusher", .description = NULL_IF_CONFIG_SMALL("Reduce audio bit resolution."), @@ -339,7 +332,7 @@ const AVFilter ff_af_acrusher = { .priv_class = &acrusher_class, .uninit = uninit, FILTER_INPUTS(avfilter_af_acrusher_inputs), - FILTER_OUTPUTS(avfilter_af_acrusher_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBL), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, diff --git a/libavfilter/af_adeclick.c b/libavfilter/af_adeclick.c index 2db9a29fd35..822f3065b85 100644 --- a/libavfilter/af_adeclick.c +++ b/libavfilter/af_adeclick.c @@ -23,7 +23,6 @@ #include "avfilter.h" #include "audio.h" #include "filters.h" -#include "formats.h" #include "internal.h" typedef struct DeclickChannel { @@ -724,13 +723,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_adeclick = { .name = "adeclick", .description = NULL_IF_CONFIG_SMALL("Remove impulsive noise from input audio."), @@ -740,7 +732,7 @@ const AVFilter ff_af_adeclick = { .activate = activate, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), .flags = AVFILTER_FLAG_SLICE_THREADS | AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, }; @@ -776,7 +768,7 @@ const AVFilter ff_af_adeclip = { .activate = activate, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), .flags = AVFILTER_FLAG_SLICE_THREADS | AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, }; diff --git a/libavfilter/af_adecorrelate.c b/libavfilter/af_adecorrelate.c index cadc62c1f7f..87355e72e0e 100644 --- a/libavfilter/af_adecorrelate.c +++ b/libavfilter/af_adecorrelate.c @@ -26,7 +26,6 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" #define MAX_STAGES 16 #define FILTER_FC 1100.0 @@ -230,13 +229,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_adecorrelate = { .name = "adecorrelate", .description = NULL_IF_CONFIG_SMALL("Apply decorrelation to input audio."), @@ -244,7 +236,7 @@ const AVFilter ff_af_adecorrelate = { .priv_class = &adecorrelate_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/af_adelay.c b/libavfilter/af_adelay.c index 9e63e2d6184..87a86170a87 100644 --- a/libavfilter/af_adelay.c +++ b/libavfilter/af_adelay.c @@ -44,9 +44,12 @@ typedef struct AudioDelayContext { int block_align; int64_t padding; int64_t max_delay; + int64_t offset; int64_t next_pts; int eof; + AVFrame *input; + void (*delay_channel)(ChanDelay *d, int nb_samples, const uint8_t *src, uint8_t *dst); int (*resize_channel_samples)(ChanDelay *d, int64_t new_delay); @@ -187,6 +190,7 @@ static int config_input(AVFilterLink *inlink) char *p, *saveptr = NULL; int i; + s->next_pts = AV_NOPTS_VALUE; s->chandelay = av_calloc(inlink->ch_layout.nb_channels, sizeof(*s->chandelay)); if (!s->chandelay) return AVERROR(ENOMEM); @@ -224,6 +228,10 @@ static int config_input(AVFilterLink *inlink) d->delay -= s->padding; } + + s->offset = av_rescale_q(s->padding, + av_make_q(1, inlink->sample_rate), + inlink->time_base); } for (i = 0; i < s->nb_delays; i++) { @@ -323,11 +331,16 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) AVFrame *out_frame; int i; - if (ctx->is_disabled || !s->delays) + if (ctx->is_disabled || !s->delays) { + s->input = NULL; return ff_filter_frame(outlink, frame); + } + + s->next_pts = av_rescale_q(frame->pts, inlink->time_base, outlink->time_base); out_frame = ff_get_audio_buffer(outlink, frame->nb_samples); if (!out_frame) { + s->input = NULL; av_frame_free(&frame); return AVERROR(ENOMEM); } @@ -344,9 +357,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) s->delay_channel(d, frame->nb_samples, src, dst); } - out_frame->pts = s->next_pts; - s->next_pts += av_rescale_q(frame->nb_samples, (AVRational){1, outlink->sample_rate}, outlink->time_base); + out_frame->pts = s->next_pts + s->offset; + out_frame->duration = av_rescale_q(out_frame->nb_samples, (AVRational){1, outlink->sample_rate}, outlink->time_base); + s->next_pts += out_frame->duration; av_frame_free(&frame); + s->input = NULL; return ff_filter_frame(outlink, out_frame); } @@ -361,6 +376,20 @@ static int activate(AVFilterContext *ctx) FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + if (!s->input) { + ret = ff_inlink_consume_frame(inlink, &s->input); + if (ret < 0) + return ret; + } + + if (ff_inlink_acknowledge_status(inlink, &status, &pts)) { + if (status == AVERROR_EOF) + s->eof = 1; + } + + if (s->next_pts == AV_NOPTS_VALUE && pts != AV_NOPTS_VALUE) + s->next_pts = av_rescale_q(pts, inlink->time_base, outlink->time_base); + if (s->padding) { int nb_samples = FFMIN(s->padding, 2048); @@ -374,24 +403,17 @@ static int activate(AVFilterContext *ctx) outlink->ch_layout.nb_channels, frame->format); + frame->duration = av_rescale_q(frame->nb_samples, + (AVRational){1, outlink->sample_rate}, + outlink->time_base); frame->pts = s->next_pts; - if (s->next_pts != AV_NOPTS_VALUE) - s->next_pts += av_rescale_q(nb_samples, (AVRational){1, outlink->sample_rate}, outlink->time_base); + s->next_pts += frame->duration; return ff_filter_frame(outlink, frame); } - ret = ff_inlink_consume_frame(inlink, &frame); - if (ret < 0) - return ret; - - if (ret > 0) - return filter_frame(inlink, frame); - - if (ff_inlink_acknowledge_status(inlink, &status, &pts)) { - if (status == AVERROR_EOF) - s->eof = 1; - } + if (s->input) + return filter_frame(inlink, s->input); if (s->eof && s->max_delay) { int nb_samples = FFMIN(s->max_delay, 2048); @@ -406,7 +428,11 @@ static int activate(AVFilterContext *ctx) outlink->ch_layout.nb_channels, frame->format); + frame->duration = av_rescale_q(frame->nb_samples, + (AVRational){1, outlink->sample_rate}, + outlink->time_base); frame->pts = s->next_pts; + s->next_pts += frame->duration; return filter_frame(inlink, frame); } @@ -440,13 +466,6 @@ static const AVFilterPad adelay_inputs[] = { }, }; -static const AVFilterPad adelay_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_adelay = { .name = "adelay", .description = NULL_IF_CONFIG_SMALL("Delay one or more audio channels."), @@ -455,7 +474,7 @@ const AVFilter ff_af_adelay = { .activate = activate, .uninit = uninit, FILTER_INPUTS(adelay_inputs), - FILTER_OUTPUTS(adelay_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_U8P, AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_S32P, AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, diff --git a/libavfilter/af_aderivative.c b/libavfilter/af_aderivative.c index 853c0f34122..eeaa23ff88d 100644 --- a/libavfilter/af_aderivative.c +++ b/libavfilter/af_aderivative.c @@ -153,13 +153,6 @@ static const AVFilterPad aderivative_inputs[] = { }, }; -static const AVFilterPad aderivative_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - static const AVOption aderivative_options[] = { { NULL } }; @@ -173,7 +166,7 @@ const AVFilter ff_af_aderivative = { .priv_class = &aderivative_class, .uninit = uninit, FILTER_INPUTS(aderivative_inputs), - FILTER_OUTPUTS(aderivative_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_S32P, AV_SAMPLE_FMT_DBLP), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, @@ -186,7 +179,7 @@ const AVFilter ff_af_aintegral = { .priv_class = &aderivative_class, .uninit = uninit, FILTER_INPUTS(aderivative_inputs), - FILTER_OUTPUTS(aderivative_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, }; diff --git a/libavfilter/af_adrc.c b/libavfilter/af_adrc.c index 54997c383ec..34e5433a6b6 100644 --- a/libavfilter/af_adrc.c +++ b/libavfilter/af_adrc.c @@ -485,13 +485,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_adrc = { .name = "adrc", .description = NULL_IF_CONFIG_SMALL("Audio Spectral Dynamic Range Controller."), @@ -499,7 +492,7 @@ const AVFilter ff_af_adrc = { .priv_class = &adrc_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_FLTP), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/af_adynamicequalizer.c b/libavfilter/af_adynamicequalizer.c index 144a5fcfe8b..2c674cbd7c3 100644 --- a/libavfilter/af_adynamicequalizer.c +++ b/libavfilter/af_adynamicequalizer.c @@ -40,172 +40,80 @@ typedef struct AudioDynamicEqualizerContext { double release_coef; int mode; int direction; - int type; + int detection; + int tftype; + int dftype; + int precision; + int format; + + int (*filter_prepare)(AVFilterContext *ctx); + int (*filter_channels)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); + + double da_double[3], dm_double[3]; + float da_float[3], dm_float[3]; AVFrame *state; } AudioDynamicEqualizerContext; -static int config_input(AVFilterLink *inlink) +static int query_formats(AVFilterContext *ctx) { - AVFilterContext *ctx = inlink->dst; AudioDynamicEqualizerContext *s = ctx->priv; + static const enum AVSampleFormat sample_fmts[3][3] = { + { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP, AV_SAMPLE_FMT_NONE }, + { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, + { AV_SAMPLE_FMT_DBLP, AV_SAMPLE_FMT_NONE }, + }; + int ret; - s->state = ff_get_audio_buffer(inlink, 8); - if (!s->state) - return AVERROR(ENOMEM); - - for (int ch = 0; ch < inlink->ch_layout.nb_channels; ch++) { - double *state = (double *)s->state->extended_data[ch]; + if ((ret = ff_set_common_all_channel_counts(ctx)) < 0) + return ret; - state[4] = 1.; - } + if ((ret = ff_set_common_formats_from_list(ctx, sample_fmts[s->precision])) < 0) + return ret; - return 0; + return ff_set_common_all_samplerates(ctx); } -static double get_svf(double in, double *m, double *a, double *b) +static double get_coef(double x, double sr) { - const double v0 = in; - const double v3 = v0 - b[1]; - const double v1 = a[0] * b[0] + a[1] * v3; - const double v2 = b[1] + a[1] * b[0] + a[2] * v3; - - b[0] = 2. * v1 - b[0]; - b[1] = 2. * v2 - b[1]; - - return m[0] * v0 + m[1] * v1 + m[2] * v2; + return 1.0 - exp(-1000. / (x * sr)); } typedef struct ThreadData { AVFrame *in, *out; } ThreadData; -static int filter_channels(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - AudioDynamicEqualizerContext *s = ctx->priv; - ThreadData *td = arg; - AVFrame *in = td->in; - AVFrame *out = td->out; - const double sample_rate = in->sample_rate; - const double makeup = s->makeup; - const double ratio = s->ratio; - const double range = s->range; - const double dfrequency = fmin(s->dfrequency, sample_rate * 0.5); - const double tfrequency = fmin(s->tfrequency, sample_rate * 0.5); - const double threshold = s->threshold; - const double release = s->release_coef; - const double irelease = 1. - release; - const double attack = s->attack_coef; - const double iattack = 1. - attack; - const double dqfactor = s->dqfactor; - const double tqfactor = s->tqfactor; - const double fg = tan(M_PI * tfrequency / sample_rate); - const double dg = tan(M_PI * dfrequency / sample_rate); - const int start = (in->ch_layout.nb_channels * jobnr) / nb_jobs; - const int end = (in->ch_layout.nb_channels * (jobnr+1)) / nb_jobs; - const int direction = s->direction; - const int mode = s->mode; - const int type = s->type; - double da[3], dm[3]; - - { - double k = 1. / dqfactor; - - da[0] = 1. / (1. + dg * (dg + k)); - da[1] = dg * da[0]; - da[2] = dg * da[1]; - - dm[0] = 0.; - dm[1] = k; - dm[2] = 0.; - } - - for (int ch = start; ch < end; ch++) { - const double *src = (const double *)in->extended_data[ch]; - double *dst = (double *)out->extended_data[ch]; - double *state = (double *)s->state->extended_data[ch]; - - for (int n = 0; n < out->nb_samples; n++) { - double detect, gain, v, listen; - double fa[3], fm[3]; - double k, g; - - detect = listen = get_svf(src[n], dm, da, state); - detect = fabs(detect); - - if (direction == 0 && mode == 0 && detect < threshold) - detect = 1. / av_clipd(1. + makeup + (threshold - detect) * ratio, 1., range); - else if (direction == 0 && mode == 1 && detect < threshold) - detect = av_clipd(1. + makeup + (threshold - detect) * ratio, 1., range); - else if (direction == 1 && mode == 0 && detect > threshold) - detect = 1. / av_clipd(1. + makeup + (detect - threshold) * ratio, 1., range); - else if (direction == 1 && mode == 1 && detect > threshold) - detect = av_clipd(1. + makeup + (detect - threshold) * ratio, 1., range); - else - detect = 1.; +#define DEPTH 32 +#include "adynamicequalizer_template.c" - if (detect < state[4]) { - detect = iattack * detect + attack * state[4]; - } else { - detect = irelease * detect + release * state[4]; - } +#undef DEPTH +#define DEPTH 64 +#include "adynamicequalizer_template.c" - if (state[4] != detect || n == 0) { - state[4] = gain = detect; - - switch (type) { - case 0: - k = 1. / (tqfactor * gain); - - fa[0] = 1. / (1. + fg * (fg + k)); - fa[1] = fg * fa[0]; - fa[2] = fg * fa[1]; - - fm[0] = 1.; - fm[1] = k * (gain * gain - 1.); - fm[2] = 0.; - break; - case 1: - k = 1. / tqfactor; - g = fg / sqrt(gain); - - fa[0] = 1. / (1. + g * (g + k)); - fa[1] = g * fa[0]; - fa[2] = g * fa[1]; - - fm[0] = 1.; - fm[1] = k * (gain - 1.); - fm[2] = gain * gain - 1.; - break; - case 2: - k = 1. / tqfactor; - g = fg / sqrt(gain); - - fa[0] = 1. / (1. + g * (g + k)); - fa[1] = g * fa[0]; - fa[2] = g * fa[1]; +static int config_input(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + AudioDynamicEqualizerContext *s = ctx->priv; - fm[0] = gain * gain; - fm[1] = k * (1. - gain) * gain; - fm[2] = 1. - gain * gain; - break; - } - } + s->format = inlink->format; + s->state = ff_get_audio_buffer(inlink, 16); + if (!s->state) + return AVERROR(ENOMEM); - v = get_svf(src[n], fm, fa, &state[2]); - v = mode == -1 ? listen : v; - dst[n] = ctx->is_disabled ? src[n] : v; - } + switch (s->format) { + case AV_SAMPLE_FMT_DBLP: + s->filter_prepare = filter_prepare_double; + s->filter_channels = filter_channels_double; + break; + case AV_SAMPLE_FMT_FLTP: + s->filter_prepare = filter_prepare_float; + s->filter_channels = filter_channels_float; + break; } return 0; } -static double get_coef(double x, double sr) -{ - return exp(-1000. / (x * sr)); -} - static int filter_frame(AVFilterLink *inlink, AVFrame *in) { AVFilterContext *ctx = inlink->dst; @@ -225,12 +133,10 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) av_frame_copy_props(out, in); } - s->attack_coef = get_coef(s->attack, in->sample_rate); - s->release_coef = get_coef(s->release, in->sample_rate); - td.in = in; td.out = out; - ff_filter_execute(ctx, filter_channels, &td, NULL, + s->filter_prepare(ctx); + ff_filter_execute(ctx, s->filter_channels, &td, NULL, FFMIN(outlink->ch_layout.nb_channels, ff_filter_get_nb_threads(ctx))); if (out != in) @@ -246,6 +152,7 @@ static av_cold void uninit(AVFilterContext *ctx) } #define OFFSET(x) offsetof(AudioDynamicEqualizerContext, x) +#define AF AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption adynamicequalizer_options[] = { @@ -263,13 +170,26 @@ static const AVOption adynamicequalizer_options[] = { { "listen", 0, 0, AV_OPT_TYPE_CONST, {.i64=-1}, 0, 0, FLAGS, "mode" }, { "cut", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "mode" }, { "boost", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "mode" }, - { "tftype", "set target filter type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, "type" }, - { "bell", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "type" }, - { "lowshelf", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "type" }, - { "highshelf",0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "type" }, + { "dftype", "set detection filter type",OFFSET(dftype), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, FLAGS, "dftype" }, + { "bandpass", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "dftype" }, + { "lowpass", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "dftype" }, + { "highpass", 0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "dftype" }, + { "peak", 0, 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, FLAGS, "dftype" }, + { "tftype", "set target filter type", OFFSET(tftype), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, "tftype" }, + { "bell", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "tftype" }, + { "lowshelf", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "tftype" }, + { "highshelf",0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "tftype" }, { "direction", "set direction", OFFSET(direction), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "direction" }, { "downward", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "direction" }, { "upward", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "direction" }, + { "auto", "set auto threshold", OFFSET(detection), AV_OPT_TYPE_INT, {.i64=-1}, -1, 1, FLAGS, "auto" }, + { "disabled", 0, 0, AV_OPT_TYPE_CONST, {.i64=-1}, 0, 0, FLAGS, "auto" }, + { "off", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "auto" }, + { "on", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "auto" }, + { "precision", "set processing precision", OFFSET(precision), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, AF, "precision" }, + { "auto", "set auto processing precision", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, "precision" }, + { "float", "set single-floating point processing precision", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, "precision" }, + { "double","set double-floating point processing precision", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, AF, "precision" }, { NULL } }; @@ -284,13 +204,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_adynamicequalizer = { .name = "adynamicequalizer", .description = NULL_IF_CONFIG_SMALL("Apply Dynamic Equalization of input audio."), @@ -298,8 +211,8 @@ const AVFilter ff_af_adynamicequalizer = { .priv_class = &adynamicequalizer_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), - FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), + FILTER_OUTPUTS(ff_audio_default_filterpad), + FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/af_adynamicsmooth.c b/libavfilter/af_adynamicsmooth.c index 3f98d09f5d7..8afe5922261 100644 --- a/libavfilter/af_adynamicsmooth.c +++ b/libavfilter/af_adynamicsmooth.c @@ -20,7 +20,6 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" typedef struct AudioDynamicSmoothContext { const AVClass *class; @@ -121,13 +120,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_adynamicsmooth = { .name = "adynamicsmooth", .description = NULL_IF_CONFIG_SMALL("Apply Dynamic Smoothing of input audio."), @@ -135,7 +127,7 @@ const AVFilter ff_af_adynamicsmooth = { .priv_class = &adynamicsmooth_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, .process_command = ff_filter_process_command, diff --git a/libavfilter/af_aecho.c b/libavfilter/af_aecho.c index e610c30d592..0b57c0e5870 100644 --- a/libavfilter/af_aecho.c +++ b/libavfilter/af_aecho.c @@ -328,13 +328,6 @@ static int activate(AVFilterContext *ctx) return request_frame(outlink); } -static const AVFilterPad aecho_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - static const AVFilterPad aecho_outputs[] = { { .name = "default", @@ -351,7 +344,7 @@ const AVFilter ff_af_aecho = { .init = init, .activate = activate, .uninit = uninit, - FILTER_INPUTS(aecho_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), FILTER_OUTPUTS(aecho_outputs), FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_S32P, AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP), diff --git a/libavfilter/af_aemphasis.c b/libavfilter/af_aemphasis.c index 91878987d38..4f05b269933 100644 --- a/libavfilter/af_aemphasis.c +++ b/libavfilter/af_aemphasis.c @@ -361,13 +361,6 @@ static const AVFilterPad avfilter_af_aemphasis_inputs[] = { }, }; -static const AVFilterPad avfilter_af_aemphasis_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_aemphasis = { .name = "aemphasis", .description = NULL_IF_CONFIG_SMALL("Audio emphasis."), @@ -375,7 +368,7 @@ const AVFilter ff_af_aemphasis = { .priv_class = &aemphasis_class, .uninit = uninit, FILTER_INPUTS(avfilter_af_aemphasis_inputs), - FILTER_OUTPUTS(avfilter_af_aemphasis_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | diff --git a/libavfilter/af_aexciter.c b/libavfilter/af_aexciter.c index 8e829f992ba..400d6707b54 100644 --- a/libavfilter/af_aexciter.c +++ b/libavfilter/af_aexciter.c @@ -264,13 +264,6 @@ static const AVFilterPad avfilter_af_aexciter_inputs[] = { }, }; -static const AVFilterPad avfilter_af_aexciter_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_aexciter = { .name = "aexciter", .description = NULL_IF_CONFIG_SMALL("Enhance high frequency part of audio."), @@ -278,7 +271,7 @@ const AVFilter ff_af_aexciter = { .priv_class = &aexciter_class, .uninit = uninit, FILTER_INPUTS(avfilter_af_aexciter_inputs), - FILTER_OUTPUTS(avfilter_af_aexciter_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBL), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, diff --git a/libavfilter/af_afade.c b/libavfilter/af_afade.c index 48938384ffc..226b63c2756 100644 --- a/libavfilter/af_afade.c +++ b/libavfilter/af_afade.c @@ -42,8 +42,8 @@ typedef struct AudioFadeContext { double silence; double unity; int overlap; - int cf0_eof; - int crossfade_is_over; + int status[2]; + int passthrough; int64_t pts; void (*fade_samples)(uint8_t **dst, uint8_t * const *src, @@ -521,6 +521,13 @@ CROSSFADE(flt, float) CROSSFADE(s16, int16_t) CROSSFADE(s32, int32_t) +static int check_input(AVFilterLink *inlink) +{ + const int queued_samples = ff_inlink_queued_samples(inlink); + + return ff_inlink_check_available_samples(inlink, queued_samples + 1) == 1; +} + static int activate(AVFilterContext *ctx) { AudioFadeContext *s = ctx->priv; @@ -531,7 +538,7 @@ static int activate(AVFilterContext *ctx) FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx); - if (s->crossfade_is_over) { + if (s->passthrough && s->status[0]) { ret = ff_inlink_consume_frame(ctx->inputs[1], &in); if (ret > 0) { in->pts = s->pts; @@ -541,10 +548,10 @@ static int activate(AVFilterContext *ctx) } else if (ret < 0) { return ret; } else if (ff_inlink_acknowledge_status(ctx->inputs[1], &status, &pts)) { - ff_outlink_set_status(ctx->outputs[0], status, pts); + ff_outlink_set_status(outlink, status, pts); return 0; } else if (!ret) { - if (ff_outlink_frame_wanted(ctx->outputs[0])) { + if (ff_outlink_frame_wanted(outlink)) { ff_inlink_request_frame(ctx->inputs[1]); return 0; } @@ -554,6 +561,7 @@ static int activate(AVFilterContext *ctx) nb_samples = ff_inlink_queued_samples(ctx->inputs[0]); if (nb_samples > s->nb_samples) { nb_samples -= s->nb_samples; + s->passthrough = 1; ret = ff_inlink_consume_samples(ctx->inputs[0], nb_samples, nb_samples, &in); if (ret < 0) return ret; @@ -561,7 +569,7 @@ static int activate(AVFilterContext *ctx) s->pts += av_rescale_q(in->nb_samples, (AVRational){ 1, outlink->sample_rate }, outlink->time_base); return ff_filter_frame(outlink, in); - } else if (s->cf0_eof && nb_samples >= s->nb_samples && + } else if (s->status[0] && nb_samples >= s->nb_samples && ff_inlink_queued_samples(ctx->inputs[1]) >= s->nb_samples) { if (s->overlap) { out = ff_get_audio_buffer(outlink, s->nb_samples); @@ -587,7 +595,7 @@ static int activate(AVFilterContext *ctx) out->pts = s->pts; s->pts += av_rescale_q(s->nb_samples, (AVRational){ 1, outlink->sample_rate }, outlink->time_base); - s->crossfade_is_over = 1; + s->passthrough = 1; av_frame_free(&cf[0]); av_frame_free(&cf[1]); return ff_filter_frame(outlink, out); @@ -627,19 +635,20 @@ static int activate(AVFilterContext *ctx) out->pts = s->pts; s->pts += av_rescale_q(s->nb_samples, (AVRational){ 1, outlink->sample_rate }, outlink->time_base); - s->crossfade_is_over = 1; + s->passthrough = 1; av_frame_free(&cf[1]); return ff_filter_frame(outlink, out); } - } else if (ff_outlink_frame_wanted(ctx->outputs[0])) { - if (!s->cf0_eof && ff_outlink_get_status(ctx->inputs[0])) { - s->cf0_eof = 1; - } - if (ff_outlink_get_status(ctx->inputs[1])) { - ff_outlink_set_status(ctx->outputs[0], AVERROR_EOF, AV_NOPTS_VALUE); + } else if (ff_outlink_frame_wanted(outlink)) { + if (!s->status[0] && check_input(ctx->inputs[0])) + s->status[0] = AVERROR_EOF; + s->passthrough = !s->status[0]; + if (check_input(ctx->inputs[1])) { + s->status[1] = AVERROR_EOF; + ff_outlink_set_status(outlink, AVERROR_EOF, AV_NOPTS_VALUE); return 0; } - if (!s->cf0_eof) + if (!s->status[0]) ff_inlink_request_frame(ctx->inputs[0]); else ff_inlink_request_frame(ctx->inputs[1]); @@ -672,14 +681,26 @@ static int acrossfade_config_output(AVFilterLink *outlink) return 0; } +static AVFrame *get_audio_buffer(AVFilterLink *inlink, int nb_samples) +{ + AVFilterContext *ctx = inlink->dst; + AudioFadeContext *s = ctx->priv; + + return s->passthrough ? + ff_null_get_audio_buffer (inlink, nb_samples) : + ff_default_get_audio_buffer(inlink, nb_samples); +} + static const AVFilterPad avfilter_af_acrossfade_inputs[] = { { .name = "crossfade0", .type = AVMEDIA_TYPE_AUDIO, + .get_buffer.audio = get_audio_buffer, }, { .name = "crossfade1", .type = AVMEDIA_TYPE_AUDIO, + .get_buffer.audio = get_audio_buffer, }, }; diff --git a/libavfilter/af_afftdn.c b/libavfilter/af_afftdn.c index aa30e35d576..b509d40eb5a 100644 --- a/libavfilter/af_afftdn.c +++ b/libavfilter/af_afftdn.c @@ -26,7 +26,6 @@ #include "libavutil/tx.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" #include "filters.h" #define C (M_LN10 * 0.1) @@ -1196,7 +1195,7 @@ static int output_frame(AVFilterLink *inlink, AVFrame *in) return AVERROR(ENOMEM); } - out->pts = in->pts; + av_frame_copy_props(out, in); } for (int ch = 0; ch < inlink->ch_layout.nb_channels; ch++) { @@ -1357,13 +1356,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_afftdn = { .name = "afftdn", .description = NULL_IF_CONFIG_SMALL("Denoise audio samples using FFT."), @@ -1372,7 +1364,7 @@ const AVFilter ff_af_afftdn = { .activate = activate, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | diff --git a/libavfilter/af_afftfilt.c b/libavfilter/af_afftfilt.c index a65ccc85944..2b2a70b0b67 100644 --- a/libavfilter/af_afftfilt.c +++ b/libavfilter/af_afftfilt.c @@ -345,7 +345,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) goto fail; } - out->pts = in->pts; + av_frame_copy_props(out, in); out->nb_samples = in->nb_samples; for (ch = 0; ch < inlink->ch_layout.nb_channels; ch++) { @@ -439,20 +439,13 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_afftfilt = { .name = "afftfilt", .description = NULL_IF_CONFIG_SMALL("Apply arbitrary expressions to samples in frequency domain."), .priv_size = sizeof(AFFTFiltContext), .priv_class = &afftfilt_class, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_FLTP), .activate = activate, .uninit = uninit, diff --git a/libavfilter/af_afir.c b/libavfilter/af_afir.c index 11fa5074d01..5d3f4070a7d 100644 --- a/libavfilter/af_afir.c +++ b/libavfilter/af_afir.c @@ -45,6 +45,7 @@ #include "internal.h" #include "af_afir.h" #include "af_afirdsp.h" +#include "video.h" static void drawtext(AVFrame *pic, int x, int y, const char *txt, uint32_t color) { @@ -106,16 +107,65 @@ static int fir_channel(AVFilterContext *ctx, AVFrame *out, int ch) { AudioFIRContext *s = ctx->priv; const int min_part_size = s->min_part_size; + const int prev_selir = s->prev_selir; + const int selir = s->selir; for (int offset = 0; offset < out->nb_samples; offset += min_part_size) { switch (s->format) { case AV_SAMPLE_FMT_FLTP: - fir_quantum_float(ctx, out, ch, offset); + if (prev_selir != selir && s->loading[ch] != 0) { + const float *xfade0 = (const float *)s->xfade[0]->extended_data[ch]; + const float *xfade1 = (const float *)s->xfade[1]->extended_data[ch]; + float *src0 = (float *)s->fadein[0]->extended_data[ch]; + float *src1 = (float *)s->fadein[1]->extended_data[ch]; + float *dst = ((float *)out->extended_data[ch]) + offset; + + memset(src0, 0, min_part_size * sizeof(float)); + memset(src1, 0, min_part_size * sizeof(float)); + + fir_quantum_float(ctx, s->fadein[0], ch, offset, 0, prev_selir); + fir_quantum_float(ctx, s->fadein[1], ch, offset, 0, selir); + + if (s->loading[ch] > s->max_offset[selir]) { + for (int n = 0; n < min_part_size; n++) + dst[n] = xfade1[n] * src0[n] + xfade0[n] * src1[n]; + s->loading[ch] = 0; + } else { + memcpy(dst, src0, min_part_size * sizeof(float)); + } + } else { + fir_quantum_float(ctx, out, ch, offset, offset, selir); + } break; case AV_SAMPLE_FMT_DBLP: - fir_quantum_double(ctx, out, ch, offset); + if (prev_selir != selir && s->loading[ch] != 0) { + const double *xfade0 = (const double *)s->xfade[0]->extended_data[ch]; + const double *xfade1 = (const double *)s->xfade[1]->extended_data[ch]; + double *src0 = (double *)s->fadein[0]->extended_data[ch]; + double *src1 = (double *)s->fadein[1]->extended_data[ch]; + double *dst = ((double *)out->extended_data[ch]) + offset; + + memset(src0, 0, min_part_size * sizeof(double)); + memset(src1, 0, min_part_size * sizeof(double)); + + fir_quantum_double(ctx, s->fadein[0], ch, offset, 0, prev_selir); + fir_quantum_double(ctx, s->fadein[1], ch, offset, 0, selir); + + if (s->loading[ch] > s->max_offset[selir]) { + for (int n = 0; n < min_part_size; n++) + dst[n] = xfade1[n] * src0[n] + xfade0[n] * src1[n]; + s->loading[ch] = 0; + } else { + memcpy(dst, src0, min_part_size * sizeof(double)); + } + } else { + fir_quantum_double(ctx, out, ch, offset, offset, selir); + } break; } + + if (selir != prev_selir && s->loading[ch] != 0) + s->loading[ch] += min_part_size; } return 0; @@ -143,6 +193,7 @@ static int fir_frame(AudioFIRContext *s, AVFrame *in, AVFilterLink *outlink) av_frame_free(&in); return AVERROR(ENOMEM); } + av_frame_copy_props(out, in); out->pts = s->pts = in->pts; s->in = in; @@ -172,16 +223,15 @@ static int init_segment(AVFilterContext *ctx, AudioFIRSegment *seg, int selir, seg->fft_length = (part_size + 1) * 2; seg->part_size = part_size; - seg->block_size = FFALIGN(seg->fft_length, cpu_align); seg->coeff_size = FFALIGN(seg->part_size + 1, cpu_align); + seg->block_size = FFMAX(seg->coeff_size * 2, FFALIGN(seg->fft_length, cpu_align)); seg->nb_partitions = nb_partitions; seg->input_size = offset + s->min_part_size; seg->input_offset = offset; - seg->loading = av_calloc(ctx->inputs[0]->ch_layout.nb_channels, sizeof(*seg->loading)); seg->part_index = av_calloc(ctx->inputs[0]->ch_layout.nb_channels, sizeof(*seg->part_index)); seg->output_offset = av_calloc(ctx->inputs[0]->ch_layout.nb_channels, sizeof(*seg->output_offset)); - if (!seg->part_index || !seg->output_offset || !seg->loading) + if (!seg->part_index || !seg->output_offset) return AVERROR(ENOMEM); switch (s->format) { @@ -252,7 +302,6 @@ static void uninit_segment(AVFilterContext *ctx, AudioFIRSegment *seg) } av_freep(&seg->itx); - av_freep(&seg->loading); av_freep(&seg->output_offset); av_freep(&seg->part_index); @@ -267,7 +316,7 @@ static void uninit_segment(AVFilterContext *ctx, AudioFIRSegment *seg) seg->input_size = 0; for (int i = 0; i < MAX_IR_STREAMS; i++) - av_frame_free(&seg->coeff[i]); + av_frame_free(&seg->coeff); } static int convert_coeffs(AVFilterContext *ctx, int selir) @@ -286,24 +335,23 @@ static int convert_coeffs(AVFilterContext *ctx, int selir) if (s->minp > s->maxp) s->maxp = s->minp; - if (s->nb_segments) + if (s->nb_segments[selir]) goto skip; left = s->nb_taps[selir]; part_size = 1 << av_log2(s->minp); max_part_size = 1 << av_log2(s->maxp); - s->min_part_size = part_size; - for (int i = 0; left > 0; i++) { - int step = part_size == max_part_size ? INT_MAX : 1 + (i == 0); + int step = (part_size == max_part_size) ? INT_MAX : 1 + (i == 0); int nb_partitions = FFMIN(step, (left + part_size - 1) / part_size); - s->nb_segments = i + 1; - ret = init_segment(ctx, &s->seg[i], selir, offset, nb_partitions, part_size, i); + s->nb_segments[selir] = i + 1; + ret = init_segment(ctx, &s->seg[selir][i], selir, offset, nb_partitions, part_size, i); if (ret < 0) return ret; offset += nb_partitions * part_size; + s->max_offset[selir] = offset; left -= nb_partitions * part_size; part_size *= 2; part_size = FFMIN(part_size, max_part_size); @@ -341,7 +389,7 @@ static int convert_coeffs(AVFilterContext *ctx, int selir) } av_log(ctx, AV_LOG_DEBUG, "nb_taps: %d\n", cur_nb_taps); - av_log(ctx, AV_LOG_DEBUG, "nb_segments: %d\n", s->nb_segments); + av_log(ctx, AV_LOG_DEBUG, "nb_segments: %d\n", s->nb_segments[selir]); switch (s->format) { case AV_SAMPLE_FMT_FLTP: @@ -355,12 +403,12 @@ static int convert_coeffs(AVFilterContext *ctx, int selir) get_power_float(ctx, s, nb_taps, ch, time); - for (int n = 0; n < s->nb_segments; n++) { - AudioFIRSegment *seg = &s->seg[n]; + for (int n = 0; n < s->nb_segments[selir]; n++) { + AudioFIRSegment *seg = &s->seg[selir][n]; - if (!seg->coeff[selir]) - seg->coeff[selir] = ff_get_audio_buffer(ctx->inputs[0], seg->nb_partitions * seg->coeff_size * 2); - if (!seg->coeff[selir]) + if (!seg->coeff) + seg->coeff = ff_get_audio_buffer(ctx->inputs[0], seg->nb_partitions * seg->coeff_size * 2); + if (!seg->coeff) return AVERROR(ENOMEM); for (int i = 0; i < seg->nb_partitions; i++) @@ -378,12 +426,12 @@ static int convert_coeffs(AVFilterContext *ctx, int selir) time[i] = 0; get_power_double(ctx, s, nb_taps, ch, time); - for (int n = 0; n < s->nb_segments; n++) { - AudioFIRSegment *seg = &s->seg[n]; + for (int n = 0; n < s->nb_segments[selir]; n++) { + AudioFIRSegment *seg = &s->seg[selir][n]; - if (!seg->coeff[selir]) - seg->coeff[selir] = ff_get_audio_buffer(ctx->inputs[0], seg->nb_partitions * seg->coeff_size * 2); - if (!seg->coeff[selir]) + if (!seg->coeff) + seg->coeff = ff_get_audio_buffer(ctx->inputs[0], seg->nb_partitions * seg->coeff_size * 2); + if (!seg->coeff) return AVERROR(ENOMEM); for (int i = 0; i < seg->nb_partitions; i++) @@ -398,7 +446,7 @@ static int convert_coeffs(AVFilterContext *ctx, int selir) return 0; } -static int check_ir(AVFilterLink *link) +static int check_ir(AVFilterLink *link, int selir) { AVFilterContext *ctx = link->dst; AudioFIRContext *s = ctx->priv; @@ -411,6 +459,9 @@ static int check_ir(AVFilterLink *link) return AVERROR(EINVAL); } + if (ff_inlink_check_available_samples(link, nb_taps + 1) == 1) + s->eof_coeffs[selir] = 1; + return 0; } @@ -425,27 +476,32 @@ static int activate(AVFilterContext *ctx) FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[0], ctx); if (s->response) FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[1], ctx); - if (!s->eof_coeffs[s->selir]) { - ret = check_ir(ctx->inputs[1 + s->selir]); - if (ret < 0) - return ret; - if (ff_outlink_get_status(ctx->inputs[1 + s->selir]) == AVERROR_EOF) - s->eof_coeffs[s->selir] = 1; + for (int i = 0; i < s->nb_irs; i++) { + const int selir = i; - if (!s->eof_coeffs[s->selir]) { - if (ff_outlink_frame_wanted(ctx->outputs[0])) - ff_inlink_request_frame(ctx->inputs[1 + s->selir]); - else if (s->response && ff_outlink_frame_wanted(ctx->outputs[1])) - ff_inlink_request_frame(ctx->inputs[1 + s->selir]); - return 0; + if (s->ir_load && selir != s->selir) + continue; + + if (!s->eof_coeffs[selir]) { + ret = check_ir(ctx->inputs[1 + selir], selir); + if (ret < 0) + return ret; + + if (!s->eof_coeffs[selir]) { + if (ff_outlink_frame_wanted(ctx->outputs[0])) + ff_inlink_request_frame(ctx->inputs[1 + selir]); + else if (s->response && ff_outlink_frame_wanted(ctx->outputs[1])) + ff_inlink_request_frame(ctx->inputs[1 + selir]); + return 0; + } } - } - if (!s->have_coeffs[s->selir] && s->eof_coeffs[s->selir]) { - ret = convert_coeffs(ctx, s->selir); - if (ret < 0) - return ret; + if (!s->have_coeffs[selir] && s->eof_coeffs[selir]) { + ret = convert_coeffs(ctx, selir); + if (ret < 0) + return ret; + } } available = ff_inlink_queued_samples(ctx->inputs[0]); @@ -454,6 +510,9 @@ static int activate(AVFilterContext *ctx) if (ret > 0) ret = fir_frame(s, in, outlink); + if (s->selir != s->prev_selir && s->loading[0] == 0) + s->prev_selir = s->selir; + if (ret < 0) return ret; @@ -485,15 +544,13 @@ static int activate(AVFilterContext *ctx) } } - if (ff_outlink_frame_wanted(ctx->outputs[0]) && - !ff_outlink_get_status(ctx->inputs[0])) { + if (ff_outlink_frame_wanted(ctx->outputs[0])) { ff_inlink_request_frame(ctx->inputs[0]); return 0; } if (s->response && - ff_outlink_frame_wanted(ctx->outputs[1]) && - !ff_outlink_get_status(ctx->inputs[0])) { + ff_outlink_frame_wanted(ctx->outputs[1])) { ff_inlink_request_frame(ctx->inputs[0]); return 0; } @@ -570,6 +627,44 @@ FF_ENABLE_DEPRECATION_WARNINGS s->format = outlink->format; s->nb_channels = outlink->ch_layout.nb_channels; + s->loading = av_calloc(ctx->inputs[0]->ch_layout.nb_channels, sizeof(*s->loading)); + if (!s->loading) + return AVERROR(ENOMEM); + + s->fadein[0] = ff_get_audio_buffer(outlink, s->min_part_size); + s->fadein[1] = ff_get_audio_buffer(outlink, s->min_part_size); + if (!s->fadein[0] || !s->fadein[1]) + return AVERROR(ENOMEM); + + s->xfade[0] = ff_get_audio_buffer(outlink, s->min_part_size); + s->xfade[1] = ff_get_audio_buffer(outlink, s->min_part_size); + if (!s->xfade[0] || !s->xfade[1]) + return AVERROR(ENOMEM); + + switch (s->format) { + case AV_SAMPLE_FMT_FLTP: + for (int ch = 0; ch < s->nb_channels; ch++) { + float *dst0 = (float *)s->xfade[0]->extended_data[ch]; + float *dst1 = (float *)s->xfade[1]->extended_data[ch]; + + for (int n = 0; n < s->min_part_size; n++) { + dst0[n] = (n + 1.f) / s->min_part_size; + dst1[n] = 1.f - dst0[n]; + } + } + break; + case AV_SAMPLE_FMT_DBLP: + for (int ch = 0; ch < s->nb_channels; ch++) { + double *dst0 = (double *)s->xfade[0]->extended_data[ch]; + double *dst1 = (double *)s->xfade[1]->extended_data[ch]; + + for (int n = 0; n < s->min_part_size; n++) { + dst0[n] = (n + 1.0) / s->min_part_size; + dst1[n] = 1.0 - dst0[n]; + } + } + break; + } return 0; } @@ -578,16 +673,23 @@ static av_cold void uninit(AVFilterContext *ctx) { AudioFIRContext *s = ctx->priv; - for (int i = 0; i < s->nb_segments; i++) - uninit_segment(ctx, &s->seg[i]); - av_freep(&s->fdsp); + av_freep(&s->loading); for (int i = 0; i < s->nb_irs; i++) { + for (int j = 0; j < s->nb_segments[i]; j++) + uninit_segment(ctx, &s->seg[i][j]); + av_frame_free(&s->ir[i]); av_frame_free(&s->norm_ir[i]); } + av_frame_free(&s->fadein[0]); + av_frame_free(&s->fadein[1]); + + av_frame_free(&s->xfade[0]); + av_frame_free(&s->xfade[1]); + av_frame_free(&s->video); } @@ -669,6 +771,9 @@ static av_cold int init(AVFilterContext *ctx) ff_afir_init(&s->afirdsp); + s->min_part_size = 1 << av_log2(s->minp); + s->max_part_size = 1 << av_log2(s->maxp); + return 0; } @@ -690,12 +795,9 @@ static int process_command(AVFilterContext *ctx, s->selir = FFMIN(s->nb_irs - 1, s->selir); if (s->selir != prev_selir) { s->prev_selir = prev_selir; - for (int n = 0; n < s->nb_segments; n++) { - AudioFIRSegment *seg = &s->seg[n]; - for (int ch = 0; ch < s->nb_channels; ch++) - seg->loading[ch] = 0; - } + for (int ch = 0; ch < s->nb_channels; ch++) + s->loading[ch] = 1; } return 0; @@ -734,6 +836,9 @@ static const AVOption afir_options[] = { { "auto", "set auto processing precision", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, "precision" }, { "float", "set single-floating point processing precision", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, "precision" }, { "double","set double-floating point processing precision", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, AF, "precision" }, + { "irload", "set IR loading type", OFFSET(ir_load), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, AF, "irload" }, + { "init", "load all IRs on init", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, "irload" }, + { "access", "load IR on access", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, "irload" }, { NULL } }; diff --git a/libavfilter/af_afir.h b/libavfilter/af_afir.h index a9f6d217f41..7c9a3b3e31f 100644 --- a/libavfilter/af_afir.h +++ b/libavfilter/af_afir.h @@ -39,7 +39,6 @@ typedef struct AudioFIRSegment { int input_size; int input_offset; - int *loading; int *output_offset; int *part_index; @@ -49,7 +48,7 @@ typedef struct AudioFIRSegment { AVFrame *tempin; AVFrame *tempout; AVFrame *buffer; - AVFrame *coeff[MAX_IR_STREAMS]; + AVFrame *coeff; AVFrame *input; AVFrame *output; @@ -66,6 +65,7 @@ typedef struct AudioFIRContext { int gtype; float ir_gain; int ir_format; + int ir_load; float max_ir_len; int response; int w, h; @@ -82,17 +82,22 @@ typedef struct AudioFIRContext { int eof_coeffs[MAX_IR_STREAMS]; int have_coeffs[MAX_IR_STREAMS]; int nb_taps[MAX_IR_STREAMS]; + int nb_segments[MAX_IR_STREAMS]; + int max_offset[MAX_IR_STREAMS]; int nb_channels; int one2many; + int *loading; - AudioFIRSegment seg[1024]; - int nb_segments; + AudioFIRSegment seg[MAX_IR_STREAMS][1024]; AVFrame *in; + AVFrame *xfade[2]; + AVFrame *fadein[2]; AVFrame *ir[MAX_IR_STREAMS]; AVFrame *norm_ir[MAX_IR_STREAMS]; AVFrame *video; int min_part_size; + int max_part_size; int64_t pts; AudioFIRDSPContext afirdsp; diff --git a/libavfilter/af_aformat.c b/libavfilter/af_aformat.c index a14e4c1240e..367a02df33c 100644 --- a/libavfilter/af_aformat.c +++ b/libavfilter/af_aformat.c @@ -176,20 +176,6 @@ static int query_formats(AVFilterContext *ctx) return ret; } -static const AVFilterPad avfilter_af_aformat_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - -static const AVFilterPad avfilter_af_aformat_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO - }, -}; - const AVFilter ff_af_aformat = { .name = "aformat", .description = NULL_IF_CONFIG_SMALL("Convert the input audio to one of the specified formats."), @@ -198,7 +184,7 @@ const AVFilter ff_af_aformat = { .priv_size = sizeof(AFormatContext), .priv_class = &aformat_class, .flags = AVFILTER_FLAG_METADATA_ONLY, - FILTER_INPUTS(avfilter_af_aformat_inputs), - FILTER_OUTPUTS(avfilter_af_aformat_outputs), + FILTER_INPUTS(ff_audio_default_filterpad), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/af_afreqshift.c b/libavfilter/af_afreqshift.c index 70bf8e419f3..9a6cfd2fc18 100644 --- a/libavfilter/af_afreqshift.c +++ b/libavfilter/af_afreqshift.c @@ -24,7 +24,6 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" #define MAX_NB_COEFFS 16 @@ -66,6 +65,7 @@ static void pfilter_channel_## name(AVFilterContext *ctx, \ type *o1 = (type *)s->o1->extended_data[ch]; \ type *i2 = (type *)s->i2->extended_data[ch]; \ type *o2 = (type *)s->o2->extended_data[ch]; \ + const int nb_coeffs = s->nb_coeffs; \ const type *c = s->cc; \ const type level = s->level; \ type shift = s->shift * M_PI; \ @@ -76,7 +76,7 @@ static void pfilter_channel_## name(AVFilterContext *ctx, \ type xn1 = src[n], xn2 = src[n]; \ type I, Q; \ \ - for (int j = 0; j < s->nb_coeffs; j++) { \ + for (int j = 0; j < nb_coeffs; j++) { \ I = c[j] * (xn1 + o2[j]) - i2[j]; \ i2[j] = i1[j]; \ i1[j] = xn1; \ @@ -85,7 +85,7 @@ static void pfilter_channel_## name(AVFilterContext *ctx, \ xn1 = I; \ } \ \ - for (int j = s->nb_coeffs; j < s->nb_coeffs*2; j++) { \ + for (int j = nb_coeffs; j < nb_coeffs*2; j++) { \ Q = c[j] * (xn2 + o2[j]) - i2[j]; \ i2[j] = i1[j]; \ i1[j] = xn2; \ @@ -93,7 +93,7 @@ static void pfilter_channel_## name(AVFilterContext *ctx, \ o1[j] = Q; \ xn2 = Q; \ } \ - Q = o2[s->nb_coeffs * 2 - 1]; \ + Q = o2[nb_coeffs * 2 - 1]; \ \ dst[n] = (I * cos_theta - Q * sin_theta) * level; \ } \ @@ -115,6 +115,7 @@ static void ffilter_channel_## name(AVFilterContext *ctx, \ type *o1 = (type *)s->o1->extended_data[ch]; \ type *i2 = (type *)s->i2->extended_data[ch]; \ type *o2 = (type *)s->o2->extended_data[ch]; \ + const int nb_coeffs = s->nb_coeffs; \ const type *c = s->cc; \ const type level = s->level; \ type ts = 1. / in->sample_rate; \ @@ -125,7 +126,7 @@ static void ffilter_channel_## name(AVFilterContext *ctx, \ type xn1 = src[n], xn2 = src[n]; \ type I, Q, theta; \ \ - for (int j = 0; j < s->nb_coeffs; j++) { \ + for (int j = 0; j < nb_coeffs; j++) { \ I = c[j] * (xn1 + o2[j]) - i2[j]; \ i2[j] = i1[j]; \ i1[j] = xn1; \ @@ -134,7 +135,7 @@ static void ffilter_channel_## name(AVFilterContext *ctx, \ xn1 = I; \ } \ \ - for (int j = s->nb_coeffs; j < s->nb_coeffs*2; j++) { \ + for (int j = nb_coeffs; j < nb_coeffs*2; j++) { \ Q = c[j] * (xn2 + o2[j]) - i2[j]; \ i2[j] = i1[j]; \ i1[j] = xn2; \ @@ -142,7 +143,7 @@ static void ffilter_channel_## name(AVFilterContext *ctx, \ o1[j] = Q; \ xn2 = Q; \ } \ - Q = o2[s->nb_coeffs * 2 - 1]; \ + Q = o2[nb_coeffs * 2 - 1]; \ \ theta = 2. * M_PI * fmod(shift * (N + n) * ts, 1.); \ dst[n] = (I * cos(theta) - Q * sin(theta)) * level; \ @@ -364,13 +365,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_afreqshift = { .name = "afreqshift", .description = NULL_IF_CONFIG_SMALL("Apply frequency shifting to input audio."), @@ -378,7 +372,7 @@ const AVFilter ff_af_afreqshift = { .priv_class = &afreqshift_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS_ARRAY(sample_fmts), .process_command = ff_filter_process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | @@ -401,7 +395,7 @@ const AVFilter ff_af_aphaseshift = { .priv_class = &aphaseshift_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS_ARRAY(sample_fmts), .process_command = ff_filter_process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | diff --git a/libavfilter/af_afwtdn.c b/libavfilter/af_afwtdn.c index cf41b6f4bc8..0fcfa779f91 100644 --- a/libavfilter/af_afwtdn.c +++ b/libavfilter/af_afwtdn.c @@ -21,12 +21,10 @@ #include #include "libavutil/avassert.h" -#include "libavutil/avstring.h" #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" #include "filters.h" -#include "formats.h" enum WaveletTypes { SYM2, @@ -1290,13 +1288,6 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar return 0; } -static const AVFilterPad inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - static const AVFilterPad outputs[] = { { .name = "default", @@ -1312,7 +1303,7 @@ const AVFilter ff_af_afwtdn = { .priv_class = &afwtdn_class, .activate = activate, .uninit = uninit, - FILTER_INPUTS(inputs), + FILTER_INPUTS(ff_audio_default_filterpad), FILTER_OUTPUTS(outputs), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), .process_command = process_command, diff --git a/libavfilter/af_agate.c b/libavfilter/af_agate.c index 51bc8c04d4b..8205045ba50 100644 --- a/libavfilter/af_agate.c +++ b/libavfilter/af_agate.c @@ -228,20 +228,13 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_agate = { .name = "agate", .description = NULL_IF_CONFIG_SMALL("Audio gate."), .priv_class = &agate_sidechaingate_class, .priv_size = sizeof(AudioGateContext), FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBL), .process_command = ff_filter_process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/af_aiir.c b/libavfilter/af_aiir.c index 3e7ec78b635..41709aa360f 100644 --- a/libavfilter/af_aiir.c +++ b/libavfilter/af_aiir.c @@ -26,7 +26,9 @@ #include "libavutil/xga_font_data.h" #include "audio.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" +#include "video.h" typedef struct ThreadData { AVFrame *in, *out; diff --git a/libavfilter/af_alimiter.c b/libavfilter/af_alimiter.c index c683c4bcf41..f08893229de 100644 --- a/libavfilter/af_alimiter.c +++ b/libavfilter/af_alimiter.c @@ -31,7 +31,6 @@ #include "audio.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" typedef struct MetaItem { diff --git a/libavfilter/af_amerge.c b/libavfilter/af_amerge.c index 8bcc0ac5be9..6ef05e213f4 100644 --- a/libavfilter/af_amerge.c +++ b/libavfilter/af_amerge.c @@ -30,6 +30,7 @@ #include "avfilter.h" #include "filters.h" #include "audio.h" +#include "formats.h" #include "internal.h" #define SWR_CH_MAX 64 @@ -243,6 +244,10 @@ static int try_push_frame(AVFilterContext *ctx, int nb_samples) outbuf->pts = inbuf[0]->pts; outbuf->nb_samples = nb_samples; + outbuf->duration = av_rescale_q(outbuf->nb_samples, + av_make_q(1, outlink->sample_rate), + outlink->time_base); + if ((ret = av_channel_layout_copy(&outbuf->ch_layout, &outlink->ch_layout)) < 0) return ret; #if FF_API_OLD_CHANNEL_LAYOUT diff --git a/libavfilter/af_amix.c b/libavfilter/af_amix.c index d7e00ab1f11..aa42514106f 100644 --- a/libavfilter/af_amix.c +++ b/libavfilter/af_amix.c @@ -43,7 +43,6 @@ #include "audio.h" #include "avfilter.h" #include "filters.h" -#include "formats.h" #include "internal.h" #define INPUT_ON 1 /**< input is active */ diff --git a/libavfilter/af_amultiply.c b/libavfilter/af_amultiply.c index 97728954a5c..3d8782002b8 100644 --- a/libavfilter/af_amultiply.c +++ b/libavfilter/af_amultiply.c @@ -21,11 +21,9 @@ #include "libavutil/channel_layout.h" #include "libavutil/common.h" #include "libavutil/float_dsp.h" -#include "libavutil/opt.h" #include "audio.h" #include "avfilter.h" -#include "formats.h" #include "filters.h" #include "internal.h" diff --git a/libavfilter/af_anequalizer.c b/libavfilter/af_anequalizer.c index c7b9a83d99b..69a9c629cdc 100644 --- a/libavfilter/af_anequalizer.c +++ b/libavfilter/af_anequalizer.c @@ -25,8 +25,10 @@ #include "libavutil/opt.h" #include "libavutil/parseutils.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" #include "audio.h" +#include "video.h" #define FILTER_ORDER 4 diff --git a/libavfilter/af_anlmdn.c b/libavfilter/af_anlmdn.c index 8f01c5f8a27..d1a4df18118 100644 --- a/libavfilter/af_anlmdn.c +++ b/libavfilter/af_anlmdn.c @@ -21,11 +21,9 @@ #include #include "libavutil/avassert.h" -#include "libavutil/avstring.h" #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" #include "filters.h" #include "af_anlmdndsp.h" @@ -343,13 +341,6 @@ static av_cold void uninit(AVFilterContext *ctx) av_frame_free(&s->window); } -static const AVFilterPad inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - static const AVFilterPad outputs[] = { { .name = "default", @@ -365,7 +356,7 @@ const AVFilter ff_af_anlmdn = { .priv_class = &anlmdn_class, .activate = activate, .uninit = uninit, - FILTER_INPUTS(inputs), + FILTER_INPUTS(ff_audio_default_filterpad), FILTER_OUTPUTS(outputs), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_FLTP), .process_command = process_command, diff --git a/libavfilter/af_anlms.c b/libavfilter/af_anlms.c index 6658cd29400..3191ed1b31c 100644 --- a/libavfilter/af_anlms.c +++ b/libavfilter/af_anlms.c @@ -25,7 +25,6 @@ #include "audio.h" #include "avfilter.h" -#include "formats.h" #include "filters.h" #include "internal.h" @@ -34,6 +33,7 @@ enum OutModes { DESIRED_MODE, OUT_MODE, NOISE_MODE, + ERROR_MODE, NB_OMODES }; @@ -73,28 +73,12 @@ static const AVOption anlms_options[] = { { "d", "desired", 0, AV_OPT_TYPE_CONST, {.i64=DESIRED_MODE}, 0, 0, AT, "mode" }, { "o", "output", 0, AV_OPT_TYPE_CONST, {.i64=OUT_MODE}, 0, 0, AT, "mode" }, { "n", "noise", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_MODE}, 0, 0, AT, "mode" }, + { "e", "error", 0, AV_OPT_TYPE_CONST, {.i64=ERROR_MODE}, 0, 0, AT, "mode" }, { NULL } }; AVFILTER_DEFINE_CLASS_EXT(anlms, "anlm(f|s)", anlms_options); -static int query_formats(AVFilterContext *ctx) -{ - static const enum AVSampleFormat sample_fmts[] = { - AV_SAMPLE_FMT_FLTP, - AV_SAMPLE_FMT_NONE - }; - int ret = ff_set_common_all_channel_counts(ctx); - if (ret < 0) - return ret; - - ret = ff_set_common_formats_from_list(ctx, sample_fmts); - if (ret < 0) - return ret; - - return ff_set_common_all_samplerates(ctx); -} - static float fir_sample(AudioNLMSContext *s, float sample, float *delay, float *coeffs, float *tmp, int *offset) { @@ -119,7 +103,7 @@ static float process_sample(AudioNLMSContext *s, float input, float desired, const int order = s->order; const float leakage = s->leakage; const float mu = s->mu; - const float a = 1.f - leakage * mu; + const float a = 1.f - leakage; float sum, output, e, norm, b; int offset = *offsetp; @@ -133,7 +117,7 @@ static float process_sample(AudioNLMSContext *s, float input, float desired, norm = s->eps + sum; b = mu * e / norm; if (s->anlmf) - b *= 4.f * e * e; + b *= e * e; memcpy(tmp, delay + offset, order * sizeof(float)); @@ -146,8 +130,9 @@ static float process_sample(AudioNLMSContext *s, float input, float desired, switch (s->output_mode) { case IN_MODE: output = input; break; case DESIRED_MODE: output = desired; break; - case OUT_MODE: /*output = output;*/ break; - case NOISE_MODE: output = desired - output; break; + case OUT_MODE: output = desired - output; break; + case NOISE_MODE: output = input - output; break; + case ERROR_MODE: break; } return output; } @@ -316,7 +301,7 @@ const AVFilter ff_af_anlms = { .activate = activate, FILTER_INPUTS(inputs), FILTER_OUTPUTS(outputs), - FILTER_QUERY_FUNC(query_formats), + FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_FLTP), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, @@ -332,7 +317,7 @@ const AVFilter ff_af_anlmf = { .activate = activate, FILTER_INPUTS(inputs), FILTER_OUTPUTS(outputs), - FILTER_QUERY_FUNC(query_formats), + FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_FLTP), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/af_anull.c b/libavfilter/af_anull.c index 78c5faeb230..257dba1a41c 100644 --- a/libavfilter/af_anull.c +++ b/libavfilter/af_anull.c @@ -27,24 +27,10 @@ #include "internal.h" #include "libavutil/internal.h" -static const AVFilterPad avfilter_af_anull_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - -static const AVFilterPad avfilter_af_anull_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_anull = { .name = "anull", .description = NULL_IF_CONFIG_SMALL("Pass the source unchanged to the output."), .flags = AVFILTER_FLAG_METADATA_ONLY, - FILTER_INPUTS(avfilter_af_anull_inputs), - FILTER_OUTPUTS(avfilter_af_anull_outputs), + FILTER_INPUTS(ff_audio_default_filterpad), + FILTER_OUTPUTS(ff_audio_default_filterpad), }; diff --git a/libavfilter/af_apad.c b/libavfilter/af_apad.c index df17c9a5315..1e0ecd201e1 100644 --- a/libavfilter/af_apad.c +++ b/libavfilter/af_apad.c @@ -32,12 +32,14 @@ #include "libavutil/avassert.h" #include "avfilter.h" #include "audio.h" +#include "filters.h" #include "internal.h" typedef struct APadContext { const AVClass *class; int64_t next_pts; + int eof; int packet_size; int64_t pad_len, pad_len_left; int64_t whole_len, whole_len_left; @@ -87,50 +89,86 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) return ff_filter_frame(ctx->outputs[0], frame); } -static int request_frame(AVFilterLink *outlink) +static int push_frame(AVFilterLink *outlink) { AVFilterContext *ctx = outlink->src; APadContext *s = ctx->priv; - int ret; + AVFrame *outsamplesref; + int n_out; - ret = ff_request_frame(ctx->inputs[0]); + if (ctx->is_disabled) + return 0; + n_out = s->packet_size; - if (ret == AVERROR_EOF && !ctx->is_disabled) { - int n_out = s->packet_size; - AVFrame *outsamplesref; + if (s->whole_len >= 0 && s->pad_len < 0) { + s->pad_len = s->pad_len_left = s->whole_len_left; + } + if (s->pad_len >=0 || s->whole_len >= 0) { + n_out = FFMIN(n_out, s->pad_len_left); + s->pad_len_left -= n_out; + av_log(ctx, AV_LOG_DEBUG, + "padding n_out:%d pad_len_left:%"PRId64"\n", n_out, s->pad_len_left); + } - if (s->whole_len >= 0 && s->pad_len < 0) { - s->pad_len = s->pad_len_left = s->whole_len_left; - } - if (s->pad_len >=0 || s->whole_len >= 0) { - n_out = FFMIN(n_out, s->pad_len_left); - s->pad_len_left -= n_out; - av_log(ctx, AV_LOG_DEBUG, - "padding n_out:%d pad_len_left:%"PRId64"\n", n_out, s->pad_len_left); - } + if (!n_out) + return AVERROR_EOF; + + outsamplesref = ff_get_audio_buffer(outlink, n_out); + if (!outsamplesref) + return AVERROR(ENOMEM); + + av_assert0(outsamplesref->sample_rate == outlink->sample_rate); + av_assert0(outsamplesref->nb_samples == n_out); + + av_samples_set_silence(outsamplesref->extended_data, 0, + n_out, + outsamplesref->ch_layout.nb_channels, + outsamplesref->format); + + outsamplesref->pts = s->next_pts; + if (s->next_pts != AV_NOPTS_VALUE) + s->next_pts += av_rescale_q(n_out, (AVRational){1, outlink->sample_rate}, outlink->time_base); + + return ff_filter_frame(outlink, outsamplesref); +} + +static int activate(AVFilterContext *ctx) +{ + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + APadContext *s = ctx->priv; + int64_t pts; + int status; - if (!n_out) - return AVERROR_EOF; + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); - outsamplesref = ff_get_audio_buffer(outlink, n_out); - if (!outsamplesref) - return AVERROR(ENOMEM); + if (!s->eof && ff_inlink_queued_frames(inlink)) { + AVFrame *frame = NULL; + int ret; - av_assert0(outsamplesref->sample_rate == outlink->sample_rate); - av_assert0(outsamplesref->nb_samples == n_out); + ret = ff_inlink_consume_frame(inlink, &frame); + if (ret < 0) + return ret; + if (ret > 0) + return filter_frame(inlink, frame); + } - av_samples_set_silence(outsamplesref->extended_data, 0, - n_out, - outsamplesref->ch_layout.nb_channels, - outsamplesref->format); + if (!s->eof && ff_inlink_acknowledge_status(inlink, &status, &pts)) + s->eof = status == AVERROR_EOF; - outsamplesref->pts = s->next_pts; - if (s->next_pts != AV_NOPTS_VALUE) - s->next_pts += av_rescale_q(n_out, (AVRational){1, outlink->sample_rate}, outlink->time_base); + if (s->eof) { + int ret = push_frame(outlink); - return ff_filter_frame(outlink, outsamplesref); + if (ret == AVERROR_EOF) { + ff_outlink_set_status(outlink, AVERROR_EOF, s->next_pts); + return 0; + } + return ret; } - return ret; + + FF_FILTER_FORWARD_WANTED(outlink, inlink); + + return FFERROR_NOT_READY; } static int config_output(AVFilterLink *outlink) @@ -149,20 +187,11 @@ static int config_output(AVFilterLink *outlink) return 0; } -static const AVFilterPad apad_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - .filter_frame = filter_frame, - }, -}; - static const AVFilterPad apad_outputs[] = { { .name = "default", - .request_frame = request_frame, - .config_props = config_output, .type = AVMEDIA_TYPE_AUDIO, + .config_props = config_output, }, }; @@ -170,8 +199,9 @@ const AVFilter ff_af_apad = { .name = "apad", .description = NULL_IF_CONFIG_SMALL("Pad audio with silence."), .init = init, + .activate = activate, .priv_size = sizeof(APadContext), - FILTER_INPUTS(apad_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), FILTER_OUTPUTS(apad_outputs), .priv_class = &apad_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, diff --git a/libavfilter/af_apsyclip.c b/libavfilter/af_apsyclip.c index 0b5859068bb..6a0c23d6eb0 100644 --- a/libavfilter/af_apsyclip.c +++ b/libavfilter/af_apsyclip.c @@ -637,13 +637,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_apsyclip = { .name = "apsyclip", .description = NULL_IF_CONFIG_SMALL("Audio Psychoacoustic Clipper."), @@ -651,7 +644,7 @@ const AVFilter ff_af_apsyclip = { .priv_class = &apsyclip_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_FLTP), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/af_apulsator.c b/libavfilter/af_apulsator.c index d4d3c59c38b..909a50215e5 100644 --- a/libavfilter/af_apulsator.c +++ b/libavfilter/af_apulsator.c @@ -22,6 +22,7 @@ #include "libavutil/channel_layout.h" #include "libavutil/opt.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" #include "audio.h" @@ -237,19 +238,12 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_apulsator = { .name = "apulsator", .description = NULL_IF_CONFIG_SMALL("Audio pulsator."), .priv_size = sizeof(AudioPulsatorContext), .priv_class = &apulsator_class, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/af_aresample.c b/libavfilter/af_aresample.c index 971c861d0ef..f4bcc45616f 100644 --- a/libavfilter/af_aresample.c +++ b/libavfilter/af_aresample.c @@ -32,6 +32,8 @@ #include "libswresample/swresample.h" #include "avfilter.h" #include "audio.h" +#include "filters.h" +#include "formats.h" #include "internal.h" typedef struct AResampleContext { @@ -41,6 +43,7 @@ typedef struct AResampleContext { struct SwrContext *swr; int64_t next_pts; int more_data; + int eof; } AResampleContext; static av_cold int preinit(AVFilterContext *ctx) @@ -169,7 +172,8 @@ static int config_output(AVFilterLink *outlink) static int filter_frame(AVFilterLink *inlink, AVFrame *insamplesref) { - AResampleContext *aresample = inlink->dst->priv; + AVFilterContext *ctx = inlink->dst; + AResampleContext *aresample = ctx->priv; const int n_in = insamplesref->nb_samples; int64_t delay; int n_out = n_in * aresample->ratio + 32; @@ -214,6 +218,7 @@ FF_ENABLE_DEPRECATION_WARNINGS if (n_out <= 0) { av_frame_free(&outsamplesref); av_frame_free(&insamplesref); + ff_inlink_request_frame(inlink); return 0; } @@ -260,8 +265,10 @@ static int flush_frame(AVFilterLink *outlink, int final, AVFrame **outsamplesref static int request_frame(AVFilterLink *outlink) { AVFilterContext *ctx = outlink->src; + AVFilterLink *inlink = ctx->inputs[0]; AResampleContext *aresample = ctx->priv; - int ret; + int ret = 0, status; + int64_t pts; // First try to get data from the internal buffers if (aresample->more_data) { @@ -273,19 +280,52 @@ static int request_frame(AVFilterLink *outlink) } aresample->more_data = 0; + if (!aresample->eof && ff_inlink_acknowledge_status(inlink, &status, &pts)) + aresample->eof = 1; + // Second request more data from the input - ret = ff_request_frame(ctx->inputs[0]); + if (!aresample->eof) + FF_FILTER_FORWARD_WANTED(outlink, inlink); // Third if we hit the end flush - if (ret == AVERROR_EOF) { + if (aresample->eof) { AVFrame *outsamplesref; - if ((ret = flush_frame(outlink, 1, &outsamplesref)) < 0) + if ((ret = flush_frame(outlink, 1, &outsamplesref)) < 0) { + if (ret == AVERROR_EOF) { + ff_outlink_set_status(outlink, AVERROR_EOF, aresample->next_pts); + return 0; + } return ret; + } return ff_filter_frame(outlink, outsamplesref); } - return ret; + + ff_filter_set_ready(ctx, 100); + return 0; +} + +static int activate(AVFilterContext *ctx) +{ + AResampleContext *aresample = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + + if (!aresample->eof && ff_inlink_queued_frames(inlink)) { + AVFrame *frame = NULL; + int ret; + + ret = ff_inlink_consume_frame(inlink, &frame); + if (ret < 0) + return ret; + if (ret > 0) + return filter_frame(inlink, frame); + } + + return request_frame(outlink); } static const AVClass *resample_child_class_iterate(void **iter) @@ -318,19 +358,10 @@ static const AVClass aresample_class = { .child_next = resample_child_next, }; -static const AVFilterPad aresample_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - .filter_frame = filter_frame, - }, -}; - static const AVFilterPad aresample_outputs[] = { { .name = "default", .config_props = config_output, - .request_frame = request_frame, .type = AVMEDIA_TYPE_AUDIO, }, }; @@ -339,10 +370,11 @@ const AVFilter ff_af_aresample = { .name = "aresample", .description = NULL_IF_CONFIG_SMALL("Resample audio data."), .preinit = preinit, + .activate = activate, .uninit = uninit, .priv_size = sizeof(AResampleContext), .priv_class = &aresample_class, - FILTER_INPUTS(aresample_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), FILTER_OUTPUTS(aresample_outputs), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/af_arls.c b/libavfilter/af_arls.c new file mode 100644 index 00000000000..4f2eddffc47 --- /dev/null +++ b/libavfilter/af_arls.c @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2023 Paul B Mahol + * + * 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 "libavutil/common.h" +#include "libavutil/float_dsp.h" +#include "libavutil/opt.h" + +#include "audio.h" +#include "avfilter.h" +#include "filters.h" +#include "internal.h" + +enum OutModes { + IN_MODE, + DESIRED_MODE, + OUT_MODE, + NOISE_MODE, + ERROR_MODE, + NB_OMODES +}; + +typedef struct AudioRLSContext { + const AVClass *class; + + int order; + float lambda; + float delta; + int output_mode; + + int kernel_size; + AVFrame *offset; + AVFrame *delay; + AVFrame *coeffs; + AVFrame *p, *dp; + AVFrame *gains; + AVFrame *u, *tmp; + + AVFrame *frame[2]; + + AVFloatDSPContext *fdsp; +} AudioRLSContext; + +#define OFFSET(x) offsetof(AudioRLSContext, x) +#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define AT AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM + +static const AVOption arls_options[] = { + { "order", "set the filter order", OFFSET(order), AV_OPT_TYPE_INT, {.i64=16}, 1, INT16_MAX, A }, + { "lambda", "set the filter lambda", OFFSET(lambda), AV_OPT_TYPE_FLOAT, {.dbl=1.f}, 0, 1, AT }, + { "delta", "set the filter delta", OFFSET(delta), AV_OPT_TYPE_FLOAT, {.dbl=2.f}, 0, INT16_MAX, A }, + { "out_mode", "set output mode", OFFSET(output_mode), AV_OPT_TYPE_INT, {.i64=OUT_MODE}, 0, NB_OMODES-1, AT, "mode" }, + { "i", "input", 0, AV_OPT_TYPE_CONST, {.i64=IN_MODE}, 0, 0, AT, "mode" }, + { "d", "desired", 0, AV_OPT_TYPE_CONST, {.i64=DESIRED_MODE}, 0, 0, AT, "mode" }, + { "o", "output", 0, AV_OPT_TYPE_CONST, {.i64=OUT_MODE}, 0, 0, AT, "mode" }, + { "n", "noise", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_MODE}, 0, 0, AT, "mode" }, + { "e", "error", 0, AV_OPT_TYPE_CONST, {.i64=ERROR_MODE}, 0, 0, AT, "mode" }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(arls); + +static float fir_sample(AudioRLSContext *s, float sample, float *delay, + float *coeffs, float *tmp, int *offset) +{ + const int order = s->order; + float output; + + delay[*offset] = sample; + + memcpy(tmp, coeffs + order - *offset, order * sizeof(float)); + + output = s->fdsp->scalarproduct_float(delay, tmp, s->kernel_size); + + if (--(*offset) < 0) + *offset = order - 1; + + return output; +} + +static float process_sample(AudioRLSContext *s, float input, float desired, int ch) +{ + float *coeffs = (float *)s->coeffs->extended_data[ch]; + float *delay = (float *)s->delay->extended_data[ch]; + float *gains = (float *)s->gains->extended_data[ch]; + float *tmp = (float *)s->tmp->extended_data[ch]; + float *u = (float *)s->u->extended_data[ch]; + float *p = (float *)s->p->extended_data[ch]; + float *dp = (float *)s->dp->extended_data[ch]; + int *offsetp = (int *)s->offset->extended_data[ch]; + const int kernel_size = s->kernel_size; + const int order = s->order; + const float lambda = s->lambda; + int offset = *offsetp; + float g = lambda; + float output, e; + + delay[offset + order] = input; + + output = fir_sample(s, input, delay, coeffs, tmp, offsetp); + e = desired - output; + + for (int i = 0, pos = offset; i < order; i++, pos++) { + const int ikernel_size = i * kernel_size; + + u[i] = 0.f; + for (int k = 0, pos = offset; k < order; k++, pos++) + u[i] += p[ikernel_size + k] * delay[pos]; + + g += u[i] * delay[pos]; + } + + g = 1.f / g; + + for (int i = 0; i < order; i++) { + const int ikernel_size = i * kernel_size; + + gains[i] = u[i] * g; + coeffs[i] = coeffs[order + i] = coeffs[i] + gains[i] * e; + tmp[i] = 0.f; + for (int k = 0, pos = offset; k < order; k++, pos++) + tmp[i] += p[ikernel_size + k] * delay[pos]; + } + + for (int i = 0; i < order; i++) { + const int ikernel_size = i * kernel_size; + + for (int k = 0; k < order; k++) + dp[ikernel_size + k] = gains[i] * tmp[k]; + } + + for (int i = 0; i < order; i++) { + const int ikernel_size = i * kernel_size; + + for (int k = 0; k < order; k++) + p[ikernel_size + k] = (p[ikernel_size + k] - (dp[ikernel_size + k] + dp[kernel_size * k + i]) * 0.5f) * lambda; + } + + switch (s->output_mode) { + case IN_MODE: output = input; break; + case DESIRED_MODE: output = desired; break; + case OUT_MODE: output = desired - output; break; + case NOISE_MODE: output = input - output; break; + case ERROR_MODE: break; + } + return output; +} + +static int process_channels(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + AudioRLSContext *s = ctx->priv; + AVFrame *out = arg; + const int start = (out->ch_layout.nb_channels * jobnr) / nb_jobs; + const int end = (out->ch_layout.nb_channels * (jobnr+1)) / nb_jobs; + + for (int c = start; c < end; c++) { + const float *input = (const float *)s->frame[0]->extended_data[c]; + const float *desired = (const float *)s->frame[1]->extended_data[c]; + float *output = (float *)out->extended_data[c]; + + for (int n = 0; n < out->nb_samples; n++) { + output[n] = process_sample(s, input[n], desired[n], c); + if (ctx->is_disabled) + output[n] = input[n]; + } + } + + return 0; +} + +static int activate(AVFilterContext *ctx) +{ + AudioRLSContext *s = ctx->priv; + int i, ret, status; + int nb_samples; + int64_t pts; + + FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[0], ctx); + + nb_samples = FFMIN(ff_inlink_queued_samples(ctx->inputs[0]), + ff_inlink_queued_samples(ctx->inputs[1])); + for (i = 0; i < ctx->nb_inputs && nb_samples > 0; i++) { + if (s->frame[i]) + continue; + + if (ff_inlink_check_available_samples(ctx->inputs[i], nb_samples) > 0) { + ret = ff_inlink_consume_samples(ctx->inputs[i], nb_samples, nb_samples, &s->frame[i]); + if (ret < 0) + return ret; + } + } + + if (s->frame[0] && s->frame[1]) { + AVFrame *out; + + out = ff_get_audio_buffer(ctx->outputs[0], s->frame[0]->nb_samples); + if (!out) { + av_frame_free(&s->frame[0]); + av_frame_free(&s->frame[1]); + return AVERROR(ENOMEM); + } + + ff_filter_execute(ctx, process_channels, out, NULL, + FFMIN(ctx->outputs[0]->ch_layout.nb_channels, ff_filter_get_nb_threads(ctx))); + + out->pts = s->frame[0]->pts; + + av_frame_free(&s->frame[0]); + av_frame_free(&s->frame[1]); + + ret = ff_filter_frame(ctx->outputs[0], out); + if (ret < 0) + return ret; + } + + if (!nb_samples) { + for (i = 0; i < 2; i++) { + if (ff_inlink_acknowledge_status(ctx->inputs[i], &status, &pts)) { + ff_outlink_set_status(ctx->outputs[0], status, pts); + return 0; + } + } + } + + if (ff_outlink_frame_wanted(ctx->outputs[0])) { + for (i = 0; i < 2; i++) { + if (ff_inlink_queued_samples(ctx->inputs[i]) > 0) + continue; + ff_inlink_request_frame(ctx->inputs[i]); + return 0; + } + } + return 0; +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + AudioRLSContext *s = ctx->priv; + + s->kernel_size = FFALIGN(s->order, 16); + + if (!s->offset) + s->offset = ff_get_audio_buffer(outlink, 1); + if (!s->delay) + s->delay = ff_get_audio_buffer(outlink, 2 * s->kernel_size); + if (!s->coeffs) + s->coeffs = ff_get_audio_buffer(outlink, 2 * s->kernel_size); + if (!s->gains) + s->gains = ff_get_audio_buffer(outlink, s->kernel_size); + if (!s->p) + s->p = ff_get_audio_buffer(outlink, s->kernel_size * s->kernel_size); + if (!s->dp) + s->dp = ff_get_audio_buffer(outlink, s->kernel_size * s->kernel_size); + if (!s->u) + s->u = ff_get_audio_buffer(outlink, s->kernel_size); + if (!s->tmp) + s->tmp = ff_get_audio_buffer(outlink, s->kernel_size); + + if (!s->delay || !s->coeffs || !s->p || !s->dp || !s->gains || !s->offset || !s->u || !s->tmp) + return AVERROR(ENOMEM); + + for (int ch = 0; ch < s->offset->ch_layout.nb_channels; ch++) { + int *dst = (int *)s->offset->extended_data[ch]; + + for (int i = 0; i < s->kernel_size; i++) + dst[0] = s->kernel_size - 1; + } + + for (int ch = 0; ch < s->p->ch_layout.nb_channels; ch++) { + float *dst = (float *)s->p->extended_data[ch]; + + for (int i = 0; i < s->kernel_size; i++) + dst[i * s->kernel_size + i] = s->delta; + } + + return 0; +} + +static av_cold int init(AVFilterContext *ctx) +{ + AudioRLSContext *s = ctx->priv; + + s->fdsp = avpriv_float_dsp_alloc(0); + if (!s->fdsp) + return AVERROR(ENOMEM); + + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + AudioRLSContext *s = ctx->priv; + + av_freep(&s->fdsp); + av_frame_free(&s->delay); + av_frame_free(&s->coeffs); + av_frame_free(&s->gains); + av_frame_free(&s->offset); + av_frame_free(&s->p); + av_frame_free(&s->dp); + av_frame_free(&s->u); + av_frame_free(&s->tmp); +} + +static const AVFilterPad inputs[] = { + { + .name = "input", + .type = AVMEDIA_TYPE_AUDIO, + }, + { + .name = "desired", + .type = AVMEDIA_TYPE_AUDIO, + }, +}; + +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .config_props = config_output, + }, +}; + +const AVFilter ff_af_arls = { + .name = "arls", + .description = NULL_IF_CONFIG_SMALL("Apply Recursive Least Squares algorithm to first audio stream."), + .priv_size = sizeof(AudioRLSContext), + .priv_class = &arls_class, + .init = init, + .uninit = uninit, + .activate = activate, + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), + FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_FLTP), + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | + AVFILTER_FLAG_SLICE_THREADS, + .process_command = ff_filter_process_command, +}; diff --git a/libavfilter/af_arnndn.c b/libavfilter/af_arnndn.c index 3ef222bc8e4..ee005eb34c6 100644 --- a/libavfilter/af_arnndn.c +++ b/libavfilter/af_arnndn.c @@ -373,14 +373,15 @@ static int config_input(AVFilterLink *inlink) for (int i = 0; i < s->channels; i++) { DenoiseState *st = &s->st[i]; + float scale = 1.f; if (!st->tx) - ret = av_tx_init(&st->tx, &st->tx_fn, AV_TX_FLOAT_FFT, 0, WINDOW_SIZE, NULL, 0); + ret = av_tx_init(&st->tx, &st->tx_fn, AV_TX_FLOAT_FFT, 0, WINDOW_SIZE, &scale, 0); if (ret < 0) return ret; if (!st->txi) - ret = av_tx_init(&st->txi, &st->txi_fn, AV_TX_FLOAT_FFT, 1, WINDOW_SIZE, NULL, 0); + ret = av_tx_init(&st->txi, &st->txi_fn, AV_TX_FLOAT_FFT, 1, WINDOW_SIZE, &scale, 0); if (ret < 0) return ret; } @@ -416,7 +417,7 @@ static void forward_transform(DenoiseState *st, AVComplexFloat *out, const float x[i].im = 0; } - st->tx_fn(st->tx, y, x, sizeof(float)); + st->tx_fn(st->tx, y, x, sizeof(AVComplexFloat)); RNN_COPY(out, y, FREQ_SIZE); } @@ -433,7 +434,7 @@ static void inverse_transform(DenoiseState *st, float *out, const AVComplexFloat x[i].im = -x[WINDOW_SIZE - i].im; } - st->txi_fn(st->txi, y, x, sizeof(float)); + st->txi_fn(st->txi, y, x, sizeof(AVComplexFloat)); for (int i = 0; i < WINDOW_SIZE; i++) out[i] = y[i].re / WINDOW_SIZE; @@ -1436,7 +1437,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) av_frame_free(&in); return AVERROR(ENOMEM); } - out->pts = in->pts; + av_frame_copy_props(out, in); td.in = in; td.out = out; ff_filter_execute(ctx, rnnoise_channels, &td, NULL, @@ -1584,13 +1585,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - #define OFFSET(x) offsetof(AudioRNNContext, x) #define AF AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -1612,7 +1606,7 @@ const AVFilter ff_af_arnndn = { .init = init, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/af_asdr.c b/libavfilter/af_asdr.c index a40246f2802..0307e21fbf8 100644 --- a/libavfilter/af_asdr.c +++ b/libavfilter/af_asdr.c @@ -20,11 +20,8 @@ #include "libavutil/channel_layout.h" #include "libavutil/common.h" -#include "libavutil/opt.h" -#include "audio.h" #include "avfilter.h" -#include "formats.h" #include "filters.h" #include "internal.h" @@ -37,34 +34,43 @@ typedef struct AudioSDRContext { AVFrame *cache[2]; } AudioSDRContext; -static void sdr(AVFilterContext *ctx, const AVFrame *u, const AVFrame *v) +static int sdr(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { AudioSDRContext *s = ctx->priv; - - for (int ch = 0; ch < u->ch_layout.nb_channels; ch++) { + AVFrame *u = s->cache[0]; + AVFrame *v = s->cache[1]; + const int channels = u->ch_layout.nb_channels; + const int start = (channels * jobnr) / nb_jobs; + const int end = (channels * (jobnr+1)) / nb_jobs; + const int nb_samples = u->nb_samples; + + for (int ch = start; ch < end; ch++) { const double *const us = (double *)u->extended_data[ch]; const double *const vs = (double *)v->extended_data[ch]; - double sum_uv = s->sum_uv[ch]; - double sum_u = s->sum_u[ch]; + double sum_uv = 0.; + double sum_u = 0.; - for (int n = 0; n < u->nb_samples; n++) { + for (int n = 0; n < nb_samples; n++) { sum_u += us[n] * us[n]; sum_uv += (us[n] - vs[n]) * (us[n] - vs[n]); } - s->sum_uv[ch] = sum_uv; - s->sum_u[ch] = sum_u; + s->sum_uv[ch] += sum_uv; + s->sum_u[ch] += sum_u; } + + return 0; } static int activate(AVFilterContext *ctx) { AudioSDRContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; int ret, status; int available; int64_t pts; - FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[0], ctx); + FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx); available = FFMIN(ff_inlink_queued_samples(ctx->inputs[0]), ff_inlink_queued_samples(ctx->inputs[1])); if (available > 0) { @@ -78,26 +84,29 @@ static int activate(AVFilterContext *ctx) } } - sdr(ctx, s->cache[0], s->cache[1]); + if (!ctx->is_disabled) + ff_filter_execute(ctx, sdr, NULL, NULL, + FFMIN(outlink->ch_layout.nb_channels, ff_filter_get_nb_threads(ctx))); av_frame_free(&s->cache[1]); out = s->cache[0]; out->nb_samples = available; - out->pts = s->pts; + out->pts = av_rescale_q(s->pts, av_make_q(1, outlink->sample_rate), outlink->time_base); + out->duration = av_rescale_q(out->nb_samples, av_make_q(1, outlink->sample_rate), outlink->time_base); s->pts += available; s->cache[0] = NULL; - return ff_filter_frame(ctx->outputs[0], out); + return ff_filter_frame(outlink, out); } for (int i = 0; i < 2; i++) { if (ff_inlink_acknowledge_status(ctx->inputs[i], &status, &pts)) { - ff_outlink_set_status(ctx->outputs[0], status, s->pts); + ff_outlink_set_status(outlink, status, s->pts); return 0; } } - if (ff_outlink_frame_wanted(ctx->outputs[0])) { + if (ff_outlink_frame_wanted(outlink)) { for (int i = 0; i < 2; i++) { if (ff_inlink_queued_samples(ctx->inputs[i]) > 0) continue; @@ -166,7 +175,9 @@ const AVFilter ff_af_asdr = { .priv_size = sizeof(AudioSDRContext), .activate = activate, .uninit = uninit, - .flags = AVFILTER_FLAG_METADATA_ONLY, + .flags = AVFILTER_FLAG_METADATA_ONLY | + AVFILTER_FLAG_SLICE_THREADS | + AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, FILTER_INPUTS(inputs), FILTER_OUTPUTS(outputs), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), diff --git a/libavfilter/af_asetnsamples.c b/libavfilter/af_asetnsamples.c index 74d3fde35e3..a12e6cadf99 100644 --- a/libavfilter/af_asetnsamples.c +++ b/libavfilter/af_asetnsamples.c @@ -30,7 +30,6 @@ #include "audio.h" #include "filters.h" #include "internal.h" -#include "formats.h" typedef struct ASNSContext { const AVClass *class; @@ -39,7 +38,7 @@ typedef struct ASNSContext { } ASNSContext; #define OFFSET(x) offsetof(ASNSContext, x) -#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption asetnsamples_options[] = { { "nb_out_samples", "set the number of per-frame output samples", OFFSET(nb_out_samples), AV_OPT_TYPE_INT, {.i64=1024}, 1, INT_MAX, FLAGS }, @@ -61,12 +60,15 @@ static int activate(AVFilterContext *ctx) FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); - ret = ff_inlink_consume_samples(inlink, s->nb_out_samples, s->nb_out_samples, &frame); + if (ctx->is_disabled) + ret = ff_inlink_consume_frame(inlink, &frame); + else + ret = ff_inlink_consume_samples(inlink, s->nb_out_samples, s->nb_out_samples, &frame); if (ret < 0) return ret; if (ret > 0) { - if (!s->pad || frame->nb_samples == s->nb_out_samples) + if (!s->pad || ctx->is_disabled || frame->nb_samples == s->nb_out_samples) return ff_filter_frame(outlink, frame); pad_frame = ff_get_audio_buffer(outlink, s->nb_out_samples); @@ -101,26 +103,14 @@ static int activate(AVFilterContext *ctx) return FFERROR_NOT_READY; } -static const AVFilterPad asetnsamples_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - -static const AVFilterPad asetnsamples_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_asetnsamples = { .name = "asetnsamples", .description = NULL_IF_CONFIG_SMALL("Set the number of samples for each output audio frames."), .priv_size = sizeof(ASNSContext), .priv_class = &asetnsamples_class, - FILTER_INPUTS(asetnsamples_inputs), - FILTER_OUTPUTS(asetnsamples_outputs), + FILTER_INPUTS(ff_audio_default_filterpad), + FILTER_OUTPUTS(ff_audio_default_filterpad), .activate = activate, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, + .process_command = ff_filter_process_command, }; diff --git a/libavfilter/af_asetrate.c b/libavfilter/af_asetrate.c index 76f29144e5e..50ccfc91b9e 100644 --- a/libavfilter/af_asetrate.c +++ b/libavfilter/af_asetrate.c @@ -20,6 +20,7 @@ #include "libavutil/opt.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" typedef struct ASetRateContext { @@ -49,7 +50,17 @@ AVFILTER_DEFINE_CLASS(asetrate); static av_cold int query_formats(AVFilterContext *ctx) { ASetRateContext *sr = ctx->priv; - int sample_rates[] = { sr->sample_rate, -1 }; + int ret, sample_rates[] = { sr->sample_rate, -1 }; + + if ((ret = ff_set_common_formats(ctx, ff_all_formats(AVMEDIA_TYPE_AUDIO))) < 0) + return ret; + + if ((ret = ff_set_common_all_channel_counts(ctx)) < 0) + return ret; + + if ((ret = ff_formats_ref(ff_all_samplerates(), + &ctx->inputs[0]->outcfg.samplerates)) < 0) + return ret; return ff_formats_ref(ff_make_format_list(sample_rates), &ctx->outputs[0]->incfg.samplerates); diff --git a/libavfilter/af_ashowinfo.c b/libavfilter/af_ashowinfo.c index 36ba38b478b..17a0a90542b 100644 --- a/libavfilter/af_ashowinfo.c +++ b/libavfilter/af_ashowinfo.c @@ -213,12 +213,11 @@ FF_ENABLE_DEPRECATION_WARNINGS av_channel_layout_describe(&buf->ch_layout, chlayout_str, sizeof(chlayout_str)); av_log(ctx, AV_LOG_INFO, - "n:%"PRId64" pts:%s pts_time:%s pos:%"PRId64" " + "n:%"PRId64" pts:%s pts_time:%s " "fmt:%s channels:%d chlayout:%s rate:%d nb_samples:%d " "checksum:%08"PRIX32" ", inlink->frame_count_out, av_ts2str(buf->pts), av_ts2timestr(buf->pts, &inlink->time_base), - buf->pkt_pos, av_get_sample_fmt_name(buf->format), buf->ch_layout.nb_channels, chlayout_str, buf->sample_rate, buf->nb_samples, checksum); @@ -254,13 +253,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_ashowinfo = { .name = "ashowinfo", .description = NULL_IF_CONFIG_SMALL("Show textual information for each audio frame."), @@ -268,5 +260,5 @@ const AVFilter ff_af_ashowinfo = { .uninit = uninit, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), }; diff --git a/libavfilter/af_asoftclip.c b/libavfilter/af_asoftclip.c index 6212e1d6fee..c06d1c8ea88 100644 --- a/libavfilter/af_asoftclip.c +++ b/libavfilter/af_asoftclip.c @@ -23,7 +23,6 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" #define MAX_OVERSAMPLE 64 @@ -473,20 +472,13 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_asoftclip = { .name = "asoftclip", .description = NULL_IF_CONFIG_SMALL("Audio Soft Clipper."), .priv_size = sizeof(ASoftClipContext), .priv_class = &asoftclip_class, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP), .uninit = uninit, .process_command = ff_filter_process_command, diff --git a/libavfilter/af_aspectralstats.c b/libavfilter/af_aspectralstats.c index b9db6bcfa59..a0fd4aabc91 100644 --- a/libavfilter/af_aspectralstats.c +++ b/libavfilter/af_aspectralstats.c @@ -600,13 +600,6 @@ static av_cold void uninit(AVFilterContext *ctx) av_frame_free(&s->window); } -static const AVFilterPad aspectralstats_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - static const AVFilterPad aspectralstats_outputs[] = { { .name = "default", @@ -622,7 +615,7 @@ const AVFilter ff_af_aspectralstats = { .priv_class = &aspectralstats_class, .uninit = uninit, .activate = activate, - FILTER_INPUTS(aspectralstats_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), FILTER_OUTPUTS(aspectralstats_outputs), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_FLTP), .flags = AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/af_asr.c b/libavfilter/af_asr.c index b402f5ff261..884e17afb7e 100644 --- a/libavfilter/af_asr.c +++ b/libavfilter/af_asr.c @@ -25,6 +25,7 @@ #include "libavutil/opt.h" #include "audio.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" typedef struct ASRContext { @@ -158,13 +159,6 @@ static const AVFilterPad asr_inputs[] = { }, }; -static const AVFilterPad asr_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_asr = { .name = "asr", .description = NULL_IF_CONFIG_SMALL("Automatic Speech Recognition."), @@ -174,6 +168,6 @@ const AVFilter ff_af_asr = { .uninit = asr_uninit, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(asr_inputs), - FILTER_OUTPUTS(asr_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/af_astats.c b/libavfilter/af_astats.c index 8755ab87520..0c94e0e963a 100644 --- a/libavfilter/af_astats.c +++ b/libavfilter/af_astats.c @@ -58,6 +58,7 @@ #define MEASURE_NOISE_FLOOR (1 << 22) #define MEASURE_NOISE_FLOOR_COUNT (1 << 23) #define MEASURE_ENTROPY (1 << 24) +#define MEASURE_ABS_PEAK_COUNT (1 << 25) #define MEASURE_MINMAXPEAK (MEASURE_MIN_LEVEL | MEASURE_MAX_LEVEL | MEASURE_PEAK_LEVEL) @@ -74,8 +75,10 @@ typedef struct ChannelStats { double min_diff, max_diff; double diff1_sum; double diff1_sum_x2; + double abs_peak; uint64_t mask, imask; uint64_t min_count, max_count; + uint64_t abs_peak_count; uint64_t noise_floor_count; uint64_t zero_runs; uint64_t nb_samples; @@ -119,31 +122,32 @@ static const AVOption astats_options[] = { { "measure_perchannel", "Select the parameters which are measured per channel", OFFSET(measure_perchannel), AV_OPT_TYPE_FLAGS, {.i64=MEASURE_ALL}, 0, UINT_MAX, FLAGS, "measure" }, { "none" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NONE }, 0, 0, FLAGS, "measure" }, { "all" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_ALL }, 0, 0, FLAGS, "measure" }, + { "Bit_depth" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_BIT_DEPTH }, 0, 0, FLAGS, "measure" }, + { "Crest_factor" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_CREST_FACTOR }, 0, 0, FLAGS, "measure" }, { "DC_offset" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_DC_OFFSET }, 0, 0, FLAGS, "measure" }, - { "Min_level" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_MIN_LEVEL }, 0, 0, FLAGS, "measure" }, - { "Max_level" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_MAX_LEVEL }, 0, 0, FLAGS, "measure" }, - { "Min_difference" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_MIN_DIFFERENCE }, 0, 0, FLAGS, "measure" }, + { "Dynamic_range" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_DYNAMIC_RANGE }, 0, 0, FLAGS, "measure" }, + { "Entropy" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_ENTROPY }, 0, 0, FLAGS, "measure" }, + { "Flat_factor" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_FLAT_FACTOR }, 0, 0, FLAGS, "measure" }, { "Max_difference" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_MAX_DIFFERENCE }, 0, 0, FLAGS, "measure" }, + { "Max_level" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_MAX_LEVEL }, 0, 0, FLAGS, "measure" }, { "Mean_difference" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_MEAN_DIFFERENCE }, 0, 0, FLAGS, "measure" }, - { "RMS_difference" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_RMS_DIFFERENCE }, 0, 0, FLAGS, "measure" }, + { "Min_difference" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_MIN_DIFFERENCE }, 0, 0, FLAGS, "measure" }, + { "Min_level" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_MIN_LEVEL }, 0, 0, FLAGS, "measure" }, + { "Noise_floor" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NOISE_FLOOR }, 0, 0, FLAGS, "measure" }, + { "Noise_floor_count" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NOISE_FLOOR_COUNT }, 0, 0, FLAGS, "measure" }, + { "Number_of_Infs" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NUMBER_OF_INFS }, 0, 0, FLAGS, "measure" }, + { "Number_of_NaNs" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NUMBER_OF_NANS }, 0, 0, FLAGS, "measure" }, + { "Number_of_denormals" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NUMBER_OF_DENORMALS }, 0, 0, FLAGS, "measure" }, + { "Number_of_samples" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NUMBER_OF_SAMPLES }, 0, 0, FLAGS, "measure" }, + { "Peak_count" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_PEAK_COUNT }, 0, 0, FLAGS, "measure" }, { "Peak_level" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_PEAK_LEVEL }, 0, 0, FLAGS, "measure" }, + { "RMS_difference" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_RMS_DIFFERENCE }, 0, 0, FLAGS, "measure" }, { "RMS_level" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_RMS_LEVEL }, 0, 0, FLAGS, "measure" }, { "RMS_peak" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_RMS_PEAK }, 0, 0, FLAGS, "measure" }, { "RMS_trough" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_RMS_TROUGH }, 0, 0, FLAGS, "measure" }, - { "Crest_factor" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_CREST_FACTOR }, 0, 0, FLAGS, "measure" }, - { "Flat_factor" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_FLAT_FACTOR }, 0, 0, FLAGS, "measure" }, - { "Peak_count" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_PEAK_COUNT }, 0, 0, FLAGS, "measure" }, - { "Bit_depth" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_BIT_DEPTH }, 0, 0, FLAGS, "measure" }, - { "Dynamic_range" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_DYNAMIC_RANGE }, 0, 0, FLAGS, "measure" }, { "Zero_crossings" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_ZERO_CROSSINGS }, 0, 0, FLAGS, "measure" }, { "Zero_crossings_rate" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_ZERO_CROSSINGS_RATE }, 0, 0, FLAGS, "measure" }, - { "Noise_floor" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NOISE_FLOOR }, 0, 0, FLAGS, "measure" }, - { "Noise_floor_count" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NOISE_FLOOR_COUNT }, 0, 0, FLAGS, "measure" }, - { "Entropy" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_ENTROPY }, 0, 0, FLAGS, "measure" }, - { "Number_of_samples" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NUMBER_OF_SAMPLES }, 0, 0, FLAGS, "measure" }, - { "Number_of_NaNs" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NUMBER_OF_NANS }, 0, 0, FLAGS, "measure" }, - { "Number_of_Infs" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NUMBER_OF_INFS }, 0, 0, FLAGS, "measure" }, - { "Number_of_denormals" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NUMBER_OF_DENORMALS }, 0, 0, FLAGS, "measure" }, + { "Abs_Peak_count" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_ABS_PEAK_COUNT }, 0, 0, FLAGS, "measure" }, { "measure_overall", "Select the parameters which are measured overall", OFFSET(measure_overall), AV_OPT_TYPE_FLAGS, {.i64=MEASURE_ALL}, 0, UINT_MAX, FLAGS, "measure" }, { NULL } }; @@ -159,6 +163,7 @@ static void reset_stats(AudioStatsContext *s) p->min = p->nmin = p->min_sigma_x2 = DBL_MAX; p->max = p->nmax = p->max_sigma_x2 =-DBL_MAX; + p->abs_peak = 0; p->min_non_zero = DBL_MAX; p->min_diff = DBL_MAX; p->max_diff = 0; @@ -175,6 +180,7 @@ static void reset_stats(AudioStatsContext *s) p->imask = 0xFFFFFFFFFFFFFFFF; p->min_count = 0; p->max_count = 0; + p->abs_peak_count = 0; p->zero_runs = 0; p->nb_samples = 0; p->nb_nans = 0; @@ -264,9 +270,16 @@ static inline void update_minmax(AudioStatsContext *s, ChannelStats *p, double d static inline void update_stat(AudioStatsContext *s, ChannelStats *p, double d, double nd, int64_t i) { + double abs_d = FFABS(d); double drop; int index; + if (p->abs_peak < abs_d) { + p->abs_peak = abs_d; + p->abs_peak_count = 1; + } else if (p->abs_peak == abs_d) { + p->abs_peak_count++; + } if (d < p->min) { p->min = d; p->nmin = nd; @@ -398,6 +411,7 @@ static void set_metadata(AudioStatsContext *s, AVDictionary **metadata) { uint64_t mask = 0, imask = 0xFFFFFFFFFFFFFFFF, min_count = 0, max_count = 0, nb_samples = 0, noise_floor_count = 0; uint64_t nb_nans = 0, nb_infs = 0, nb_denormals = 0; + uint64_t abs_peak_count = 0; double min_runs = 0, max_runs = 0, min = DBL_MAX, max =-DBL_MAX, min_diff = DBL_MAX, max_diff = 0, nmin = DBL_MAX, nmax =-DBL_MAX, @@ -435,6 +449,7 @@ static void set_metadata(AudioStatsContext *s, AVDictionary **metadata) entropy += p->entropy; min_count += p->min_count; max_count += p->max_count; + abs_peak_count += p->abs_peak_count; min_runs += p->min_runs; max_runs += p->max_runs; mask |= p->mask; @@ -474,6 +489,8 @@ static void set_metadata(AudioStatsContext *s, AVDictionary **metadata) set_meta(metadata, c + 1, "Flat_factor", "%f", LINEAR_TO_DB((p->min_runs + p->max_runs) / (p->min_count + p->max_count))); if (s->measure_perchannel & MEASURE_PEAK_COUNT) set_meta(metadata, c + 1, "Peak_count", "%f", (float)(p->min_count + p->max_count)); + if (s->measure_perchannel & MEASURE_ABS_PEAK_COUNT) + set_meta(metadata, c + 1, "Peak_count", "%f", p->abs_peak_count); if (s->measure_perchannel & MEASURE_NOISE_FLOOR) set_meta(metadata, c + 1, "Noise_floor", "%f", LINEAR_TO_DB(p->noise_floor)); if (s->measure_perchannel & MEASURE_NOISE_FLOOR_COUNT) @@ -525,6 +542,8 @@ static void set_metadata(AudioStatsContext *s, AVDictionary **metadata) set_meta(metadata, 0, "Overall.Flat_factor", "%f", LINEAR_TO_DB((min_runs + max_runs) / (min_count + max_count))); if (s->measure_overall & MEASURE_PEAK_COUNT) set_meta(metadata, 0, "Overall.Peak_count", "%f", (float)(min_count + max_count) / (double)s->nb_channels); + if (s->measure_overall & MEASURE_ABS_PEAK_COUNT) + set_meta(metadata, 0, "Overall.Abs_Peak_count", "%f", (float)(abs_peak_count) / (double)s->nb_channels); if (s->measure_overall & MEASURE_NOISE_FLOOR) set_meta(metadata, 0, "Overall.Noise_floor", "%f", LINEAR_TO_DB(noise_floor)); if (s->measure_overall & MEASURE_NOISE_FLOOR_COUNT) @@ -653,7 +672,7 @@ static void print_stats(AVFilterContext *ctx) { AudioStatsContext *s = ctx->priv; uint64_t mask = 0, imask = 0xFFFFFFFFFFFFFFFF, min_count = 0, max_count = 0, nb_samples = 0, noise_floor_count = 0; - uint64_t nb_nans = 0, nb_infs = 0, nb_denormals = 0; + uint64_t nb_nans = 0, nb_infs = 0, nb_denormals = 0, abs_peak_count = 0; double min_runs = 0, max_runs = 0, min = DBL_MAX, max =-DBL_MAX, min_diff = DBL_MAX, max_diff = 0, nmin = DBL_MAX, nmax =-DBL_MAX, @@ -693,6 +712,7 @@ static void print_stats(AVFilterContext *ctx) entropy += p->entropy; min_count += p->min_count; max_count += p->max_count; + abs_peak_count += p->abs_peak_count; noise_floor_count += p->noise_floor_count; min_runs += p->min_runs; max_runs += p->max_runs; @@ -736,6 +756,8 @@ static void print_stats(AVFilterContext *ctx) av_log(ctx, AV_LOG_INFO, "Flat factor: %f\n", LINEAR_TO_DB((p->min_runs + p->max_runs) / (p->min_count + p->max_count))); if (s->measure_perchannel & MEASURE_PEAK_COUNT) av_log(ctx, AV_LOG_INFO, "Peak count: %"PRId64"\n", p->min_count + p->max_count); + if (s->measure_perchannel & MEASURE_ABS_PEAK_COUNT) + av_log(ctx, AV_LOG_INFO, "Abs Peak count: %"PRId64"\n", p->abs_peak_count); if (s->measure_perchannel & MEASURE_NOISE_FLOOR) av_log(ctx, AV_LOG_INFO, "Noise floor dB: %f\n", LINEAR_TO_DB(p->noise_floor)); if (s->measure_perchannel & MEASURE_NOISE_FLOOR_COUNT) @@ -792,6 +814,8 @@ static void print_stats(AVFilterContext *ctx) av_log(ctx, AV_LOG_INFO, "Flat factor: %f\n", LINEAR_TO_DB((min_runs + max_runs) / (min_count + max_count))); if (s->measure_overall & MEASURE_PEAK_COUNT) av_log(ctx, AV_LOG_INFO, "Peak count: %f\n", (min_count + max_count) / (double)s->nb_channels); + if (s->measure_overall & MEASURE_ABS_PEAK_COUNT) + av_log(ctx, AV_LOG_INFO, "Abs Peak count: %f\n", abs_peak_count / (double)s->nb_channels); if (s->measure_overall & MEASURE_NOISE_FLOOR) av_log(ctx, AV_LOG_INFO, "Noise floor dB: %f\n", LINEAR_TO_DB(noise_floor)); if (s->measure_overall & MEASURE_NOISE_FLOOR_COUNT) diff --git a/libavfilter/af_asubboost.c b/libavfilter/af_asubboost.c index 29a1f66ce65..31db4b81a0c 100644 --- a/libavfilter/af_asubboost.c +++ b/libavfilter/af_asubboost.c @@ -21,7 +21,6 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" typedef struct ASubBoostContext { const AVClass *class; @@ -237,13 +236,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_asubboost = { .name = "asubboost", .description = NULL_IF_CONFIG_SMALL("Boost subwoofer frequencies."), @@ -251,7 +243,7 @@ const AVFilter ff_af_asubboost = { .priv_class = &asubboost_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | diff --git a/libavfilter/af_asupercut.c b/libavfilter/af_asupercut.c index 5c06d559ed3..848388c5208 100644 --- a/libavfilter/af_asupercut.c +++ b/libavfilter/af_asupercut.c @@ -24,7 +24,6 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" typedef struct BiquadCoeffs { double a1, a2; @@ -333,13 +332,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_asupercut = { .name = "asupercut", .description = NULL_IF_CONFIG_SMALL("Cut super frequencies."), @@ -347,7 +339,7 @@ const AVFilter ff_af_asupercut = { .priv_class = &asupercut_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS_ARRAY(sample_fmts), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | @@ -370,7 +362,7 @@ const AVFilter ff_af_asubcut = { .priv_class = &asubcut_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS_ARRAY(sample_fmts), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | @@ -395,7 +387,7 @@ const AVFilter ff_af_asuperpass = { .priv_size = sizeof(ASuperCutContext), .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS_ARRAY(sample_fmts), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | @@ -409,7 +401,7 @@ const AVFilter ff_af_asuperstop = { .priv_size = sizeof(ASuperCutContext), .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS_ARRAY(sample_fmts), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | diff --git a/libavfilter/af_atempo.c b/libavfilter/af_atempo.c index 27f13638b02..4621b67b036 100644 --- a/libavfilter/af_atempo.c +++ b/libavfilter/af_atempo.c @@ -247,10 +247,10 @@ static void yae_release_buffers(ATempoContext *atempo) /* av_realloc is not aligned enough; fortunately, the data does not need to * be preserved */ -#define RE_MALLOC_OR_FAIL(field, field_size) \ +#define RE_MALLOC_OR_FAIL(field, field_size, element_size) \ do { \ av_freep(&field); \ - field = av_calloc(field_size, 1); \ + field = av_calloc(field_size, element_size); \ if (!field) { \ yae_release_buffers(atempo); \ return AVERROR(ENOMEM); \ @@ -290,12 +290,12 @@ static int yae_reset(ATempoContext *atempo, } // initialize audio fragment buffers: - RE_MALLOC_OR_FAIL(atempo->frag[0].data, atempo->window * atempo->stride); - RE_MALLOC_OR_FAIL(atempo->frag[1].data, atempo->window * atempo->stride); - RE_MALLOC_OR_FAIL(atempo->frag[0].xdat_in, (atempo->window + 1) * sizeof(AVComplexFloat)); - RE_MALLOC_OR_FAIL(atempo->frag[1].xdat_in, (atempo->window + 1) * sizeof(AVComplexFloat)); - RE_MALLOC_OR_FAIL(atempo->frag[0].xdat, (atempo->window + 1) * sizeof(AVComplexFloat)); - RE_MALLOC_OR_FAIL(atempo->frag[1].xdat, (atempo->window + 1) * sizeof(AVComplexFloat)); + RE_MALLOC_OR_FAIL(atempo->frag[0].data, atempo->window, atempo->stride); + RE_MALLOC_OR_FAIL(atempo->frag[1].data, atempo->window, atempo->stride); + RE_MALLOC_OR_FAIL(atempo->frag[0].xdat_in, (atempo->window + 1), sizeof(AVComplexFloat)); + RE_MALLOC_OR_FAIL(atempo->frag[1].xdat_in, (atempo->window + 1), sizeof(AVComplexFloat)); + RE_MALLOC_OR_FAIL(atempo->frag[0].xdat, (atempo->window + 1), sizeof(AVComplexFloat)); + RE_MALLOC_OR_FAIL(atempo->frag[1].xdat, (atempo->window + 1), sizeof(AVComplexFloat)); // initialize rDFT contexts: av_tx_uninit(&atempo->real_to_complex); @@ -313,14 +313,14 @@ static int yae_reset(ATempoContext *atempo, return AVERROR(ENOMEM); } - RE_MALLOC_OR_FAIL(atempo->correlation_in, (atempo->window + 1) * sizeof(AVComplexFloat)); - RE_MALLOC_OR_FAIL(atempo->correlation, atempo->window * sizeof(AVComplexFloat)); + RE_MALLOC_OR_FAIL(atempo->correlation_in, (atempo->window + 1), sizeof(AVComplexFloat)); + RE_MALLOC_OR_FAIL(atempo->correlation, atempo->window, sizeof(AVComplexFloat)); atempo->ring = atempo->window * 3; - RE_MALLOC_OR_FAIL(atempo->buffer, atempo->ring * atempo->stride); + RE_MALLOC_OR_FAIL(atempo->buffer, atempo->ring, atempo->stride); // initialize the Hann window function: - RE_MALLOC_OR_FAIL(atempo->hann, atempo->window * sizeof(float)); + RE_MALLOC_OR_FAIL(atempo->hann, atempo->window, sizeof(float)); for (i = 0; i < atempo->window; i++) { double t = (double)i / (double)(atempo->window - 1); diff --git a/libavfilter/af_atilt.c b/libavfilter/af_atilt.c index 9ece531ea4d..172e3259dbc 100644 --- a/libavfilter/af_atilt.c +++ b/libavfilter/af_atilt.c @@ -21,7 +21,6 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" #define MAX_ORDER 30 @@ -246,13 +245,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_atilt = { .name = "atilt", .description = NULL_IF_CONFIG_SMALL("Apply spectral tilt to audio."), @@ -260,7 +252,7 @@ const AVFilter ff_af_atilt = { .priv_class = &atilt_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | diff --git a/libavfilter/af_axcorrelate.c b/libavfilter/af_axcorrelate.c index 9be3f1c9215..2e83711273e 100644 --- a/libavfilter/af_axcorrelate.c +++ b/libavfilter/af_axcorrelate.c @@ -25,7 +25,6 @@ #include "audio.h" #include "avfilter.h" -#include "formats.h" #include "filters.h" #include "internal.h" @@ -110,7 +109,7 @@ static int xcorrelate_slow_##suffix(AVFilterContext *ctx, \ AVFrame *out, int available) \ { \ AudioXCorrelateContext *s = ctx->priv; \ - const int size = FFMIN(available, s->size); \ + const int size = s->size; \ int used; \ \ for (int ch = 0; ch < out->ch_layout.nb_channels; ch++) { \ @@ -127,13 +126,13 @@ static int xcorrelate_slow_##suffix(AVFilterContext *ctx, \ used = 1; \ } \ \ - for (int n = 0; n < out->nb_samples; n++) { \ - const int idx = available <= s->size ? out->nb_samples - n - 1 : n + size; \ - \ - dst[n] = xcorrelate_##suffix(x + n, y + n, \ - sumx[0], sumy[0], \ - size); \ - \ + for (int n = 0; n < out->nb_samples; n++) { \ + const int idx = n + size; \ + \ + dst[n] = xcorrelate_##suffix(x + n, y + n, \ + sumx[0], sumy[0],\ + size); \ + \ sumx[0] -= x[n]; \ sumx[0] += x[idx]; \ sumy[0] -= y[n]; \ @@ -147,12 +146,15 @@ static int xcorrelate_slow_##suffix(AVFilterContext *ctx, \ XCORRELATE_SLOW(f, float) XCORRELATE_SLOW(d, double) -#define XCORRELATE_FAST(suffix, type, zero, small, sqrtfun) \ +#define clipf(x) (av_clipf(x, -1.f, 1.f)) +#define clipd(x) (av_clipd(x, -1.0, 1.0)) + +#define XCORRELATE_FAST(suffix, type, zero, small, sqrtfun, CLIP) \ static int xcorrelate_fast_##suffix(AVFilterContext *ctx, AVFrame *out, \ int available) \ { \ AudioXCorrelateContext *s = ctx->priv; \ - const int size = FFMIN(available, s->size); \ + const int size = s->size; \ int used; \ \ for (int ch = 0; ch < out->ch_layout.nb_channels; ch++) { \ @@ -171,14 +173,14 @@ static int xcorrelate_fast_##suffix(AVFilterContext *ctx, AVFrame *out, \ used = 1; \ } \ \ - for (int n = 0; n < out->nb_samples; n++) { \ - const int idx = available <= s->size ? out->nb_samples - n - 1 : n + size; \ - type num, den; \ + for (int n = 0; n < out->nb_samples; n++) { \ + const int idx = n + size; \ + type num, den; \ \ num = num_sum[0] / size; \ den = sqrtfun((den_sumx[0] * den_sumy[0]) / size / size); \ \ - dst[n] = den <= small ? zero : num / den; \ + dst[n] = den <= small ? zero : CLIP(num / den); \ \ num_sum[0] -= x[n] * y[n]; \ num_sum[0] += x[idx] * y[idx]; \ @@ -194,20 +196,82 @@ static int xcorrelate_fast_##suffix(AVFilterContext *ctx, AVFrame *out, \ return used; \ } -XCORRELATE_FAST(f, float, 0.f, 1e-6f, sqrtf) -XCORRELATE_FAST(d, double, 0.0, 1e-9, sqrt) +XCORRELATE_FAST(f, float, 0.f, 1e-6f, sqrtf, clipf) +XCORRELATE_FAST(d, double, 0.0, 1e-9, sqrt, clipd) + +#define XCORRELATE_BEST(suffix, type, zero, small, sqrtfun, FMAX, CLIP) \ +static int xcorrelate_best_##suffix(AVFilterContext *ctx, AVFrame *out, \ + int available) \ +{ \ + AudioXCorrelateContext *s = ctx->priv; \ + const int size = s->size; \ + int used; \ + \ + for (int ch = 0; ch < out->ch_layout.nb_channels; ch++) { \ + const type *x = (const type *)s->cache[0]->extended_data[ch]; \ + const type *y = (const type *)s->cache[1]->extended_data[ch]; \ + type *mean_sumx = (type *)s->mean_sum[0]->extended_data[ch]; \ + type *mean_sumy = (type *)s->mean_sum[1]->extended_data[ch]; \ + type *num_sum = (type *)s->num_sum->extended_data[ch]; \ + type *den_sumx = (type *)s->den_sum[0]->extended_data[ch]; \ + type *den_sumy = (type *)s->den_sum[1]->extended_data[ch]; \ + type *dst = (type *)out->extended_data[ch]; \ + \ + used = s->used; \ + if (!used) { \ + num_sum[0] = square_sum_##suffix(x, y, size); \ + den_sumx[0] = square_sum_##suffix(x, x, size); \ + den_sumy[0] = square_sum_##suffix(y, y, size); \ + mean_sumx[0] = mean_sum_##suffix(x, size); \ + mean_sumy[0] = mean_sum_##suffix(y, size); \ + used = 1; \ + } \ + \ + for (int n = 0; n < out->nb_samples; n++) { \ + const int idx = n + size; \ + type num, den, xm, ym; \ + \ + xm = mean_sumx[0] / size; \ + ym = mean_sumy[0] / size; \ + num = num_sum[0] - size * xm * ym; \ + den = sqrtfun(FMAX(den_sumx[0] - size * xm * xm, zero)) * \ + sqrtfun(FMAX(den_sumy[0] - size * ym * ym, zero)); \ + \ + dst[n] = den <= small ? zero : CLIP(num / den); \ + \ + mean_sumx[0]-= x[n]; \ + mean_sumx[0]+= x[idx]; \ + mean_sumy[0]-= y[n]; \ + mean_sumy[0]+= y[idx]; \ + num_sum[0] -= x[n] * y[n]; \ + num_sum[0] += x[idx] * y[idx]; \ + den_sumx[0] -= x[n] * x[n]; \ + den_sumx[0] += x[idx] * x[idx]; \ + den_sumx[0] = FMAX(den_sumx[0], zero); \ + den_sumy[0] -= y[n] * y[n]; \ + den_sumy[0] += y[idx] * y[idx]; \ + den_sumy[0] = FMAX(den_sumy[0], zero); \ + } \ + } \ + \ + return used; \ +} + +XCORRELATE_BEST(f, float, 0.f, 1e-6f, sqrtf, fmaxf, clipf) +XCORRELATE_BEST(d, double, 0.0, 1e-9, sqrt, fmax, clipd) static int activate(AVFilterContext *ctx) { AudioXCorrelateContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; AVFrame *frame = NULL; int ret, status; int available; int64_t pts; - FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[0], ctx); + FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx); - for (int i = 0; i < 2; i++) { + for (int i = 0; i < 2 && !s->eof; i++) { ret = ff_inlink_consume_frame(ctx->inputs[i], &frame); if (ret > 0) { if (s->pts == AV_NOPTS_VALUE) @@ -221,20 +285,20 @@ static int activate(AVFilterContext *ctx) } available = FFMIN(av_audio_fifo_size(s->fifo[0]), av_audio_fifo_size(s->fifo[1])); - if (available > s->size || (s->eof && available > 0)) { - const int out_samples = s->eof ? available : available - s->size; + if (available > s->size) { + const int out_samples = available - s->size; AVFrame *out; if (!s->cache[0] || s->cache[0]->nb_samples < available) { av_frame_free(&s->cache[0]); - s->cache[0] = ff_get_audio_buffer(ctx->outputs[0], available); + s->cache[0] = ff_get_audio_buffer(outlink, available); if (!s->cache[0]) return AVERROR(ENOMEM); } if (!s->cache[1] || s->cache[1]->nb_samples < available) { av_frame_free(&s->cache[1]); - s->cache[1] = ff_get_audio_buffer(ctx->outputs[0], available); + s->cache[1] = ff_get_audio_buffer(outlink, available); if (!s->cache[1]) return AVERROR(ENOMEM); } @@ -247,7 +311,7 @@ static int activate(AVFilterContext *ctx) if (ret < 0) return ret; - out = ff_get_audio_buffer(ctx->outputs[0], out_samples); + out = ff_get_audio_buffer(outlink, out_samples); if (!out) return AVERROR(ENOMEM); @@ -259,18 +323,31 @@ static int activate(AVFilterContext *ctx) av_audio_fifo_drain(s->fifo[0], out_samples); av_audio_fifo_drain(s->fifo[1], out_samples); - return ff_filter_frame(ctx->outputs[0], out); + return ff_filter_frame(outlink, out); } for (int i = 0; i < 2 && !s->eof; i++) { - if (ff_inlink_acknowledge_status(ctx->inputs[i], &status, &pts)) + if (ff_inlink_acknowledge_status(ctx->inputs[i], &status, &pts)) { + AVFrame *silence = ff_get_audio_buffer(outlink, s->size); + s->eof = 1; + if (!silence) + return AVERROR(ENOMEM); + + av_audio_fifo_write(s->fifo[0], (void **)silence->extended_data, + silence->nb_samples); + + av_audio_fifo_write(s->fifo[1], (void **)silence->extended_data, + silence->nb_samples); + + av_frame_free(&silence); + } } if (s->eof && - (av_audio_fifo_size(s->fifo[0]) <= 0 || - av_audio_fifo_size(s->fifo[1]) <= 0)) { - ff_outlink_set_status(ctx->outputs[0], AVERROR_EOF, s->pts); + (av_audio_fifo_size(s->fifo[0]) <= s->size || + av_audio_fifo_size(s->fifo[1]) <= s->size)) { + ff_outlink_set_status(outlink, AVERROR_EOF, s->pts); return 0; } @@ -280,7 +357,7 @@ static int activate(AVFilterContext *ctx) return 0; } - if (ff_outlink_frame_wanted(ctx->outputs[0]) && !s->eof) { + if (ff_outlink_frame_wanted(outlink) && !s->eof) { for (int i = 0; i < 2; i++) { if (av_audio_fifo_size(s->fifo[i]) > s->size) continue; @@ -316,12 +393,14 @@ static int config_output(AVFilterLink *outlink) switch (s->algo) { case 0: s->xcorrelate = xcorrelate_slow_f; break; case 1: s->xcorrelate = xcorrelate_fast_f; break; + case 2: s->xcorrelate = xcorrelate_best_f; break; } if (outlink->format == AV_SAMPLE_FMT_DBLP) { switch (s->algo) { case 0: s->xcorrelate = xcorrelate_slow_d; break; case 1: s->xcorrelate = xcorrelate_fast_d; break; + case 2: s->xcorrelate = xcorrelate_best_d; break; } } @@ -366,10 +445,11 @@ static const AVFilterPad outputs[] = { #define OFFSET(x) offsetof(AudioXCorrelateContext, x) static const AVOption axcorrelate_options[] = { - { "size", "set segment size", OFFSET(size), AV_OPT_TYPE_INT, {.i64=256}, 2, 131072, AF }, - { "algo", "set algorithm", OFFSET(algo), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, AF, "algo" }, + { "size", "set the segment size", OFFSET(size), AV_OPT_TYPE_INT, {.i64=256}, 2, 131072, AF }, + { "algo", "set the algorithm", OFFSET(algo), AV_OPT_TYPE_INT, {.i64=2}, 0, 2, AF, "algo" }, { "slow", "slow algorithm", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, "algo" }, { "fast", "fast algorithm", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, "algo" }, + { "best", "best algorithm", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, AF, "algo" }, { NULL } }; diff --git a/libavfilter/af_biquads.c b/libavfilter/af_biquads.c index 49ad0a471f0..e4655394111 100644 --- a/libavfilter/af_biquads.c +++ b/libavfilter/af_biquads.c @@ -71,6 +71,7 @@ #include "audio.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" enum FilterType { @@ -109,14 +110,6 @@ enum TransformType { NB_TTYPE, }; -typedef struct ChanCache { - double i1, i2; - double o1, o2; - double ri1, ri2; - double ro1, ro2; - int clippings; -} ChanCache; - typedef struct BiquadsContext { const AVClass *class; @@ -139,24 +132,26 @@ typedef struct BiquadsContext { int normalize; int order; - double a0, a1, a2; - double b0, b1, b2; + double a_double[3]; + double b_double[3]; + + float a_float[3]; + float b_float[3]; - double oa0, oa1, oa2; - double ob0, ob1, ob2; + double oa[3]; + double ob[3]; AVFrame *block[3]; - ChanCache *cache; + int *clip; + AVFrame *cache[2]; int block_align; int64_t pts; int nb_samples; void (*filter)(struct BiquadsContext *s, const void *ibuf, void *obuf, int len, - double *i1, double *i2, double *o1, double *o2, - double b0, double b1, double b2, double a0, double a1, double a2, int *clippings, - int disabled); + void *cache, int *clip, int disabled); } BiquadsContext; static int query_formats(AVFilterContext *ctx) @@ -202,27 +197,26 @@ static int query_formats(AVFilterContext *ctx) return ff_set_common_all_samplerates(ctx); } -#define BIQUAD_FILTER(name, type, min, max, need_clipping) \ +#define BIQUAD_FILTER(name, type, ftype, min, max, need_clipping) \ static void biquad_## name (BiquadsContext *s, \ const void *input, void *output, int len, \ - double *in1, double *in2, \ - double *out1, double *out2, \ - double b0, double b1, double b2, \ - double a0, double a1, double a2, int *clippings, \ - int disabled) \ + void *cache, int *clippings, int disabled) \ { \ const type *ibuf = input; \ type *obuf = output; \ - double i1 = *in1; \ - double i2 = *in2; \ - double o1 = *out1; \ - double o2 = *out2; \ - double wet = s->mix; \ - double dry = 1. - wet; \ - double out; \ + ftype *fcache = cache; \ + ftype i1 = fcache[0], i2 = fcache[1], o1 = fcache[2], o2 = fcache[3]; \ + ftype *a = s->a_##ftype; \ + ftype *b = s->b_##ftype; \ + ftype a1 = -a[1]; \ + ftype a2 = -a[2]; \ + ftype b0 = b[0]; \ + ftype b1 = b[1]; \ + ftype b2 = b[2]; \ + ftype wet = s->mix; \ + ftype dry = 1. - wet; \ + ftype out; \ int i; \ - a1 = -a1; \ - a2 = -a2; \ \ for (i = 0; i+1 < len; i++) { \ o2 = i2 * b2 + i1 * b1 + ibuf[i] * b0 + o2 * a2 + o1 * a1; \ @@ -256,7 +250,7 @@ static void biquad_## name (BiquadsContext *s, \ } \ } \ if (i < len) { \ - double o0 = ibuf[i] * b0 + i1 * b1 + i2 * b2 + o1 * a1 + o2 * a2; \ + ftype o0 = ibuf[i] * b0 + i1 * b1 + i2 * b2 + o1 * a1 + o2 * a2; \ i2 = i1; \ i1 = ibuf[i]; \ o2 = o1; \ @@ -274,36 +268,37 @@ static void biquad_## name (BiquadsContext *s, \ obuf[i] = out; \ } \ } \ - *in1 = i1; \ - *in2 = i2; \ - *out1 = o1; \ - *out2 = o2; \ + fcache[0] = i1; \ + fcache[1] = i2; \ + fcache[2] = o1; \ + fcache[3] = o2; \ } -BIQUAD_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1) -BIQUAD_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1) -BIQUAD_FILTER(flt, float, -1., 1., 0) -BIQUAD_FILTER(dbl, double, -1., 1., 0) +BIQUAD_FILTER(s16, int16_t, float, INT16_MIN, INT16_MAX, 1) +BIQUAD_FILTER(s32, int32_t, double, INT32_MIN, INT32_MAX, 1) +BIQUAD_FILTER(flt, float, float, -1.f, 1.f, 0) +BIQUAD_FILTER(dbl, double, double, -1., 1., 0) -#define BIQUAD_DII_FILTER(name, type, min, max, need_clipping) \ +#define BIQUAD_DII_FILTER(name, type, ftype, min, max, need_clipping) \ static void biquad_dii_## name (BiquadsContext *s, \ const void *input, void *output, int len, \ - double *z1, double *z2, \ - double *unused1, double *unused2, \ - double b0, double b1, double b2, \ - double a0, double a1, double a2, int *clippings, \ - int disabled) \ + void *cache, int *clippings, int disabled) \ { \ const type *ibuf = input; \ type *obuf = output; \ - double w1 = *z1; \ - double w2 = *z2; \ - double wet = s->mix; \ - double dry = 1. - wet; \ - double in, out, w0; \ - \ - a1 = -a1; \ - a2 = -a2; \ + ftype *fcache = cache; \ + ftype *a = s->a_##ftype; \ + ftype *b = s->b_##ftype; \ + ftype a1 = -a[1]; \ + ftype a2 = -a[2]; \ + ftype b0 = b[0]; \ + ftype b1 = b[1]; \ + ftype b2 = b[2]; \ + ftype w1 = fcache[0]; \ + ftype w2 = fcache[1]; \ + ftype wet = s->mix; \ + ftype dry = 1. - wet; \ + ftype in, out, w0; \ \ for (int i = 0; i < len; i++) { \ in = ibuf[i]; \ @@ -324,39 +319,40 @@ static void biquad_dii_## name (BiquadsContext *s, \ obuf[i] = out; \ } \ } \ - *z1 = w1; \ - *z2 = w2; \ + fcache[0] = w1; \ + fcache[1] = w2; \ } -BIQUAD_DII_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1) -BIQUAD_DII_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1) -BIQUAD_DII_FILTER(flt, float, -1., 1., 0) -BIQUAD_DII_FILTER(dbl, double, -1., 1., 0) +BIQUAD_DII_FILTER(s16, int16_t, float, INT16_MIN, INT16_MAX, 1) +BIQUAD_DII_FILTER(s32, int32_t, double, INT32_MIN, INT32_MAX, 1) +BIQUAD_DII_FILTER(flt, float, float, -1.f, 1.f, 0) +BIQUAD_DII_FILTER(dbl, double, double, -1., 1., 0) -#define BIQUAD_TDI_FILTER(name, type, min, max, need_clipping) \ +#define BIQUAD_TDI_FILTER(name, type, ftype, min, max, need_clipping) \ static void biquad_tdi_## name (BiquadsContext *s, \ const void *input, void *output, int len, \ - double *z1, double *z2, \ - double *z3, double *z4, \ - double b0, double b1, double b2, \ - double a0, double a1, double a2, int *clippings, \ - int disabled) \ + void *cache, int *clippings, int disabled) \ { \ const type *ibuf = input; \ type *obuf = output; \ - double s1 = *z1; \ - double s2 = *z2; \ - double s3 = *z3; \ - double s4 = *z4; \ - double wet = s->mix; \ - double dry = 1. - wet; \ - double in, out; \ - \ - a1 = -a1; \ - a2 = -a2; \ + ftype *fcache = cache; \ + ftype *a = s->a_##ftype; \ + ftype *b = s->b_##ftype; \ + ftype a1 = -a[1]; \ + ftype a2 = -a[2]; \ + ftype b0 = b[0]; \ + ftype b1 = b[1]; \ + ftype b2 = b[2]; \ + ftype s1 = fcache[0]; \ + ftype s2 = fcache[1]; \ + ftype s3 = fcache[2]; \ + ftype s4 = fcache[3]; \ + ftype wet = s->mix; \ + ftype dry = 1. - wet; \ + ftype in, out; \ \ for (int i = 0; i < len; i++) { \ - double t1, t2, t3, t4; \ + ftype t1, t2, t3, t4; \ in = ibuf[i] + s1; \ t1 = in * a1 + s2; \ t2 = in * a2; \ @@ -378,36 +374,37 @@ static void biquad_tdi_## name (BiquadsContext *s, \ } \ } \ \ - *z1 = s1; \ - *z2 = s2; \ - *z3 = s3; \ - *z4 = s4; \ + fcache[0] = s1; \ + fcache[1] = s2; \ + fcache[2] = s3; \ + fcache[3] = s4; \ } -BIQUAD_TDI_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1) -BIQUAD_TDI_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1) -BIQUAD_TDI_FILTER(flt, float, -1., 1., 0) -BIQUAD_TDI_FILTER(dbl, double, -1., 1., 0) +BIQUAD_TDI_FILTER(s16, int16_t, float, INT16_MIN, INT16_MAX, 1) +BIQUAD_TDI_FILTER(s32, int32_t, double, INT32_MIN, INT32_MAX, 1) +BIQUAD_TDI_FILTER(flt, float, float, -1.f, 1.f, 0) +BIQUAD_TDI_FILTER(dbl, double, double, -1., 1., 0) -#define BIQUAD_TDII_FILTER(name, type, min, max, need_clipping) \ +#define BIQUAD_TDII_FILTER(name, type, ftype, min, max, need_clipping) \ static void biquad_tdii_## name (BiquadsContext *s, \ const void *input, void *output, int len, \ - double *z1, double *z2, \ - double *unused1, double *unused2, \ - double b0, double b1, double b2, \ - double a0, double a1, double a2, int *clippings, \ - int disabled) \ + void *cache, int *clippings, int disabled) \ { \ const type *ibuf = input; \ type *obuf = output; \ - double w1 = *z1; \ - double w2 = *z2; \ - double wet = s->mix; \ - double dry = 1. - wet; \ - double in, out; \ - \ - a1 = -a1; \ - a2 = -a2; \ + ftype *fcache = cache; \ + ftype *a = s->a_##ftype; \ + ftype *b = s->b_##ftype; \ + ftype a1 = -a[1]; \ + ftype a2 = -a[2]; \ + ftype b0 = b[0]; \ + ftype b1 = b[1]; \ + ftype b2 = b[2]; \ + ftype w1 = fcache[0]; \ + ftype w2 = fcache[1]; \ + ftype wet = s->mix; \ + ftype dry = 1. - wet; \ + ftype in, out; \ \ for (int i = 0; i < len; i++) { \ in = ibuf[i]; \ @@ -427,33 +424,36 @@ static void biquad_tdii_## name (BiquadsContext *s, \ obuf[i] = out; \ } \ } \ - *z1 = w1; \ - *z2 = w2; \ + fcache[0] = w1; \ + fcache[1] = w2; \ } -BIQUAD_TDII_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1) -BIQUAD_TDII_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1) -BIQUAD_TDII_FILTER(flt, float, -1., 1., 0) -BIQUAD_TDII_FILTER(dbl, double, -1., 1., 0) +BIQUAD_TDII_FILTER(s16, int16_t, float, INT16_MIN, INT16_MAX, 1) +BIQUAD_TDII_FILTER(s32, int32_t, double, INT32_MIN, INT32_MAX, 1) +BIQUAD_TDII_FILTER(flt, float, float, -1.f, 1.f, 0) +BIQUAD_TDII_FILTER(dbl, double, double, -1., 1., 0) -#define BIQUAD_LATT_FILTER(name, type, min, max, need_clipping) \ +#define BIQUAD_LATT_FILTER(name, type, ftype, min, max, need_clipping) \ static void biquad_latt_## name (BiquadsContext *s, \ const void *input, void *output, int len, \ - double *z1, double *z2, \ - double *unused1, double *unused2, \ - double v0, double v1, double v2, \ - double unused, double k0, double k1, \ - int *clippings, \ - int disabled) \ + void *cache, int *clippings, int disabled) \ { \ const type *ibuf = input; \ type *obuf = output; \ - double s0 = *z1; \ - double s1 = *z2; \ - double wet = s->mix; \ - double dry = 1. - wet; \ - double in, out; \ - double t0, t1; \ + ftype *fcache = cache; \ + ftype *a = s->a_##ftype; \ + ftype *b = s->b_##ftype; \ + ftype k0 = a[1]; \ + ftype k1 = a[2]; \ + ftype v0 = b[0]; \ + ftype v1 = b[1]; \ + ftype v2 = b[2]; \ + ftype s0 = fcache[0]; \ + ftype s1 = fcache[1]; \ + ftype wet = s->mix; \ + ftype dry = 1. - wet; \ + ftype in, out; \ + ftype t0, t1; \ \ for (int i = 0; i < len; i++) { \ out = 0.; \ @@ -483,32 +483,36 @@ static void biquad_latt_## name (BiquadsContext *s, \ obuf[i] = out; \ } \ } \ - *z1 = s0; \ - *z2 = s1; \ + fcache[0] = s0; \ + fcache[1] = s1; \ } -BIQUAD_LATT_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1) -BIQUAD_LATT_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1) -BIQUAD_LATT_FILTER(flt, float, -1., 1., 0) -BIQUAD_LATT_FILTER(dbl, double, -1., 1., 0) +BIQUAD_LATT_FILTER(s16, int16_t, float, INT16_MIN, INT16_MAX, 1) +BIQUAD_LATT_FILTER(s32, int32_t, double, INT32_MIN, INT32_MAX, 1) +BIQUAD_LATT_FILTER(flt, float, float, -1.f, 1.f, 0) +BIQUAD_LATT_FILTER(dbl, double, double, -1., 1., 0) -#define BIQUAD_SVF_FILTER(name, type, min, max, need_clipping) \ +#define BIQUAD_SVF_FILTER(name, type, ftype, min, max, need_clipping) \ static void biquad_svf_## name (BiquadsContext *s, \ const void *input, void *output, int len, \ - double *y0, double *y1, \ - double *unused1, double *unused2, \ - double b0, double b1, double b2, \ - double a0, double a1, double a2, int *clippings, \ - int disabled) \ + void *cache, int *clippings, int disabled) \ { \ const type *ibuf = input; \ type *obuf = output; \ - double s0 = *y0; \ - double s1 = *y1; \ - double wet = s->mix; \ - double dry = 1. - wet; \ - double in, out; \ - double t0, t1; \ + ftype *fcache = cache; \ + ftype *a = s->a_##ftype; \ + ftype *b = s->b_##ftype; \ + ftype a1 = a[1]; \ + ftype a2 = a[2]; \ + ftype b0 = b[0]; \ + ftype b1 = b[1]; \ + ftype b2 = b[2]; \ + ftype s0 = fcache[0]; \ + ftype s1 = fcache[1]; \ + ftype wet = s->mix; \ + ftype dry = 1. - wet; \ + ftype in, out; \ + ftype t0, t1; \ \ for (int i = 0; i < len; i++) { \ in = ibuf[i]; \ @@ -531,41 +535,46 @@ static void biquad_svf_## name (BiquadsContext *s, \ obuf[i] = out; \ } \ } \ - *y0 = s0; \ - *y1 = s1; \ + fcache[0] = s0; \ + fcache[1] = s1; \ } -BIQUAD_SVF_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1) -BIQUAD_SVF_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1) -BIQUAD_SVF_FILTER(flt, float, -1., 1., 0) -BIQUAD_SVF_FILTER(dbl, double, -1., 1., 0) +BIQUAD_SVF_FILTER(s16, int16_t, float, INT16_MIN, INT16_MAX, 1) +BIQUAD_SVF_FILTER(s32, int32_t, double, INT32_MIN, INT32_MAX, 1) +BIQUAD_SVF_FILTER(flt, float, float, -1.f, 1.f, 0) +BIQUAD_SVF_FILTER(dbl, double, double, -1., 1., 0) -#define BIQUAD_ZDF_FILTER(name, type, min, max, need_clipping) \ +#define BIQUAD_ZDF_FILTER(name, type, ftype, min, max, need_clipping, two) \ static void biquad_zdf_## name (BiquadsContext *s, \ const void *input, void *output, int len, \ - double *y0, double *y1, \ - double *unused1, double *unused2, \ - double m0, double m1, double m2, \ - double a0, double a1, double a2, int *clippings, \ - int disabled) \ + void *cache, int *clippings, int disabled) \ { \ const type *ibuf = input; \ type *obuf = output; \ - double b0 = *y0; \ - double b1 = *y1; \ - double wet = s->mix; \ - double dry = 1. - wet; \ - double out; \ + ftype *fcache = cache; \ + ftype *a = s->a_##ftype; \ + ftype *b = s->b_##ftype; \ + ftype m0 = b[0]; \ + ftype m1 = b[1]; \ + ftype m2 = b[2]; \ + ftype a0 = a[0]; \ + ftype a1 = a[1]; \ + ftype a2 = a[2]; \ + ftype b0 = fcache[0]; \ + ftype b1 = fcache[1]; \ + ftype wet = s->mix; \ + ftype dry = 1. - wet; \ + ftype out; \ \ for (int i = 0; i < len; i++) { \ - const double in = ibuf[i]; \ - const double v0 = in; \ - const double v3 = v0 - b1; \ - const double v1 = a0 * b0 + a1 * v3; \ - const double v2 = b1 + a1 * b0 + a2 * v3; \ + const ftype in = ibuf[i]; \ + const ftype v0 = in; \ + const ftype v3 = v0 - b1; \ + const ftype v1 = a0 * b0 + a1 * v3; \ + const ftype v2 = b1 + a1 * b0 + a2 * v3; \ \ - b0 = 2. * v1 - b0; \ - b1 = 2. * v2 - b1; \ + b0 = two * v1 - b0; \ + b1 = two * v2 - b1; \ \ out = m0 * v0 + m1 * v1 + m2 * v2; \ out = out * wet + in * dry; \ @@ -581,30 +590,30 @@ static void biquad_zdf_## name (BiquadsContext *s, \ obuf[i] = out; \ } \ } \ - *y0 = b0; \ - *y1 = b1; \ + fcache[0] = b0; \ + fcache[1] = b1; \ } -BIQUAD_ZDF_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1) -BIQUAD_ZDF_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1) -BIQUAD_ZDF_FILTER(flt, float, -1., 1., 0) -BIQUAD_ZDF_FILTER(dbl, double, -1., 1., 0) +BIQUAD_ZDF_FILTER(s16, int16_t, float, INT16_MIN, INT16_MAX, 1, 2.f) +BIQUAD_ZDF_FILTER(s32, int32_t, double, INT32_MIN, INT32_MAX, 1, 2.0) +BIQUAD_ZDF_FILTER(flt, float, float, -1.f, 1.f, 0, 2.f) +BIQUAD_ZDF_FILTER(dbl, double, double, -1., 1., 0, 2.0) static void convert_dir2latt(BiquadsContext *s) { double k0, k1, v0, v1, v2; - k1 = s->a2; - k0 = s->a1 / (1. + k1); - v2 = s->b2; - v1 = s->b1 - v2 * s->a1; - v0 = s->b0 - v1 * k0 - v2 * k1; - - s->a1 = k0; - s->a2 = k1; - s->b0 = v0; - s->b1 = v1; - s->b2 = v2; + k1 = s->a_double[2]; + k0 = s->a_double[1] / (1. + k1); + v2 = s->b_double[2]; + v1 = s->b_double[1] - v2 * s->a_double[1]; + v0 = s->b_double[0] - v1 * k0 - v2 * k1; + + s->a_double[1] = k0; + s->a_double[2] = k1; + s->b_double[0] = v0; + s->b_double[1] = v1; + s->b_double[2] = v2; } static void convert_dir2svf(BiquadsContext *s) @@ -612,17 +621,17 @@ static void convert_dir2svf(BiquadsContext *s) double a[2]; double b[3]; - a[0] = -s->a1; - a[1] = -s->a2; - b[0] = s->b1 - s->a1 * s->b0; - b[1] = s->b2 - s->a2 * s->b0; - b[2] = s->b0; - - s->a1 = a[0]; - s->a2 = a[1]; - s->b0 = b[0]; - s->b1 = b[1]; - s->b2 = b[2]; + a[0] = -s->a_double[1]; + a[1] = -s->a_double[2]; + b[0] = s->b_double[1] - s->a_double[1] * s->b_double[0]; + b[1] = s->b_double[2] - s->a_double[2] * s->b_double[0]; + b[2] = s->b_double[0]; + + s->a_double[1] = a[0]; + s->a_double[2] = a[1]; + s->b_double[0] = b[0]; + s->b_double[1] = b[1]; + s->b_double[2] = b[2]; } static double convert_width2qfactor(double width, @@ -669,12 +678,12 @@ static void convert_dir2zdf(BiquadsContext *s, int sample_rate) switch (s->filter_type) { case biquad: - a[0] = s->oa0; - a[1] = s->oa1; - a[2] = s->oa2; - m[0] = s->ob0; - m[1] = s->ob1; - m[2] = s->ob2; + a[0] = s->oa[0]; + a[1] = s->oa[1]; + a[2] = s->oa[2]; + m[0] = s->ob[0]; + m[1] = s->ob[1]; + m[2] = s->ob[2]; break; case equalizer: A = ff_exp10(s->gain / 40.); @@ -713,7 +722,7 @@ static void convert_dir2zdf(BiquadsContext *s, int sample_rate) case treble: case highshelf: A = ff_exp10(s->gain / 40.); - g = tan(M_PI * s->frequency / sample_rate) / sqrt(A); + g = tan(M_PI * s->frequency / sample_rate) * sqrt(A); k = 1. / Q; a[0] = 1. / (1. + g * (g + k)); a[1] = g * a[0]; @@ -776,12 +785,12 @@ static void convert_dir2zdf(BiquadsContext *s, int sample_rate) av_assert0(0); } - s->a0 = a[0]; - s->a1 = a[1]; - s->a2 = a[2]; - s->b0 = m[0]; - s->b1 = m[1]; - s->b2 = m[2]; + s->a_double[0] = a[0]; + s->a_double[1] = a[1]; + s->a_double[2] = a[2]; + s->b_double[0] = m[0]; + s->b_double[1] = m[1]; + s->b_double[2] = m[2]; } static int config_filter(AVFilterLink *outlink, int reset) @@ -831,20 +840,20 @@ static int config_filter(AVFilterLink *outlink, int reset) switch (s->filter_type) { case biquad: - s->a0 = s->oa0; - s->a1 = s->oa1; - s->a2 = s->oa2; - s->b0 = s->ob0; - s->b1 = s->ob1; - s->b2 = s->ob2; + s->a_double[0] = s->oa[0]; + s->a_double[1] = s->oa[1]; + s->a_double[2] = s->oa[2]; + s->b_double[0] = s->ob[0]; + s->b_double[1] = s->ob[1]; + s->b_double[2] = s->ob[2]; break; case equalizer: - s->a0 = 1 + alpha / A; - s->a1 = -2 * cos(w0); - s->a2 = 1 - alpha / A; - s->b0 = 1 + alpha * A; - s->b1 = -2 * cos(w0); - s->b2 = 1 - alpha * A; + s->a_double[0] = 1 + alpha / A; + s->a_double[1] = -2 * cos(w0); + s->a_double[2] = 1 - alpha / A; + s->b_double[0] = 1 + alpha * A; + s->b_double[1] = -2 * cos(w0); + s->b_double[2] = 1 - alpha * A; break; case bass: beta = sqrt((A * A + 1) - (A - 1) * (A - 1)); @@ -858,19 +867,19 @@ static int config_filter(AVFilterLink *outlink, int reset) double beta0 = ((1 + A) + (1 - A) * alpha1) * 0.5; double beta1 = ((1 - A) + (1 + A) * alpha1) * 0.5; - s->a0 = 1 + ro * alpha1; - s->a1 = -ro - alpha1; - s->a2 = 0; - s->b0 = beta0 + ro * beta1; - s->b1 = -beta1 - ro * beta0; - s->b2 = 0; + s->a_double[0] = 1 + ro * alpha1; + s->a_double[1] = -ro - alpha1; + s->a_double[2] = 0; + s->b_double[0] = beta0 + ro * beta1; + s->b_double[1] = -beta1 - ro * beta0; + s->b_double[2] = 0; } else { - s->a0 = (A + 1) + (A - 1) * cos(w0) + beta * alpha; - s->a1 = -2 * ((A - 1) + (A + 1) * cos(w0)); - s->a2 = (A + 1) + (A - 1) * cos(w0) - beta * alpha; - s->b0 = A * ((A + 1) - (A - 1) * cos(w0) + beta * alpha); - s->b1 = 2 * A * ((A - 1) - (A + 1) * cos(w0)); - s->b2 = A * ((A + 1) - (A - 1) * cos(w0) - beta * alpha); + s->a_double[0] = (A + 1) + (A - 1) * cos(w0) + beta * alpha; + s->a_double[1] = -2 * ((A - 1) + (A + 1) * cos(w0)); + s->a_double[2] = (A + 1) + (A - 1) * cos(w0) - beta * alpha; + s->b_double[0] = A * ((A + 1) - (A - 1) * cos(w0) + beta * alpha); + s->b_double[1] = 2 * A * ((A - 1) - (A + 1) * cos(w0)); + s->b_double[2] = A * ((A + 1) - (A - 1) * cos(w0) - beta * alpha); } break; case treble: @@ -884,97 +893,97 @@ static int config_filter(AVFilterLink *outlink, int reset) double beta0 = ((1 + A) + (1 - A) * alpha1) * 0.5; double beta1 = ((1 - A) + (1 + A) * alpha1) * 0.5; - s->a0 = 1 + ro * alpha1; - s->a1 = ro + alpha1; - s->a2 = 0; - s->b0 = beta0 + ro * beta1; - s->b1 = beta1 + ro * beta0; - s->b2 = 0; + s->a_double[0] = 1 + ro * alpha1; + s->a_double[1] = ro + alpha1; + s->a_double[2] = 0; + s->b_double[0] = beta0 + ro * beta1; + s->b_double[1] = beta1 + ro * beta0; + s->b_double[2] = 0; } else { - s->a0 = (A + 1) - (A - 1) * cos(w0) + beta * alpha; - s->a1 = 2 * ((A - 1) - (A + 1) * cos(w0)); - s->a2 = (A + 1) - (A - 1) * cos(w0) - beta * alpha; - s->b0 = A * ((A + 1) + (A - 1) * cos(w0) + beta * alpha); - s->b1 =-2 * A * ((A - 1) + (A + 1) * cos(w0)); - s->b2 = A * ((A + 1) + (A - 1) * cos(w0) - beta * alpha); + s->a_double[0] = (A + 1) - (A - 1) * cos(w0) + beta * alpha; + s->a_double[1] = 2 * ((A - 1) - (A + 1) * cos(w0)); + s->a_double[2] = (A + 1) - (A - 1) * cos(w0) - beta * alpha; + s->b_double[0] = A * ((A + 1) + (A - 1) * cos(w0) + beta * alpha); + s->b_double[1] =-2 * A * ((A - 1) + (A + 1) * cos(w0)); + s->b_double[2] = A * ((A + 1) + (A - 1) * cos(w0) - beta * alpha); } break; case bandpass: if (s->csg) { - s->a0 = 1 + alpha; - s->a1 = -2 * cos(w0); - s->a2 = 1 - alpha; - s->b0 = sin(w0) / 2; - s->b1 = 0; - s->b2 = -sin(w0) / 2; + s->a_double[0] = 1 + alpha; + s->a_double[1] = -2 * cos(w0); + s->a_double[2] = 1 - alpha; + s->b_double[0] = sin(w0) / 2; + s->b_double[1] = 0; + s->b_double[2] = -sin(w0) / 2; } else { - s->a0 = 1 + alpha; - s->a1 = -2 * cos(w0); - s->a2 = 1 - alpha; - s->b0 = alpha; - s->b1 = 0; - s->b2 = -alpha; + s->a_double[0] = 1 + alpha; + s->a_double[1] = -2 * cos(w0); + s->a_double[2] = 1 - alpha; + s->b_double[0] = alpha; + s->b_double[1] = 0; + s->b_double[2] = -alpha; } break; case bandreject: - s->a0 = 1 + alpha; - s->a1 = -2 * cos(w0); - s->a2 = 1 - alpha; - s->b0 = 1; - s->b1 = -2 * cos(w0); - s->b2 = 1; + s->a_double[0] = 1 + alpha; + s->a_double[1] = -2 * cos(w0); + s->a_double[2] = 1 - alpha; + s->b_double[0] = 1; + s->b_double[1] = -2 * cos(w0); + s->b_double[2] = 1; break; case lowpass: if (s->poles == 1) { - s->a0 = 1; - s->a1 = -exp(-w0); - s->a2 = 0; - s->b0 = 1 + s->a1; - s->b1 = 0; - s->b2 = 0; + s->a_double[0] = 1; + s->a_double[1] = -exp(-w0); + s->a_double[2] = 0; + s->b_double[0] = 1 + s->a_double[1]; + s->b_double[1] = 0; + s->b_double[2] = 0; } else { - s->a0 = 1 + alpha; - s->a1 = -2 * cos(w0); - s->a2 = 1 - alpha; - s->b0 = (1 - cos(w0)) / 2; - s->b1 = 1 - cos(w0); - s->b2 = (1 - cos(w0)) / 2; + s->a_double[0] = 1 + alpha; + s->a_double[1] = -2 * cos(w0); + s->a_double[2] = 1 - alpha; + s->b_double[0] = (1 - cos(w0)) / 2; + s->b_double[1] = 1 - cos(w0); + s->b_double[2] = (1 - cos(w0)) / 2; } break; case highpass: if (s->poles == 1) { - s->a0 = 1; - s->a1 = -exp(-w0); - s->a2 = 0; - s->b0 = (1 - s->a1) / 2; - s->b1 = -s->b0; - s->b2 = 0; + s->a_double[0] = 1; + s->a_double[1] = -exp(-w0); + s->a_double[2] = 0; + s->b_double[0] = (1 - s->a_double[1]) / 2; + s->b_double[1] = -s->b_double[0]; + s->b_double[2] = 0; } else { - s->a0 = 1 + alpha; - s->a1 = -2 * cos(w0); - s->a2 = 1 - alpha; - s->b0 = (1 + cos(w0)) / 2; - s->b1 = -(1 + cos(w0)); - s->b2 = (1 + cos(w0)) / 2; + s->a_double[0] = 1 + alpha; + s->a_double[1] = -2 * cos(w0); + s->a_double[2] = 1 - alpha; + s->b_double[0] = (1 + cos(w0)) / 2; + s->b_double[1] = -(1 + cos(w0)); + s->b_double[2] = (1 + cos(w0)) / 2; } break; case allpass: switch (s->order) { case 1: - s->a0 = 1.; - s->a1 = -(1. - K) / (1. + K); - s->a2 = 0.; - s->b0 = s->a1; - s->b1 = s->a0; - s->b2 = 0.; + s->a_double[0] = 1.; + s->a_double[1] = -(1. - K) / (1. + K); + s->a_double[2] = 0.; + s->b_double[0] = s->a_double[1]; + s->b_double[1] = s->a_double[0]; + s->b_double[2] = 0.; break; case 2: - s->a0 = 1 + alpha; - s->a1 = -2 * cos(w0); - s->a2 = 1 - alpha; - s->b0 = 1 - alpha; - s->b1 = -2 * cos(w0); - s->b2 = 1 + alpha; + s->a_double[0] = 1 + alpha; + s->a_double[1] = -2 * cos(w0); + s->a_double[2] = 1 - alpha; + s->b_double[0] = 1 - alpha; + s->b_double[1] = -2 * cos(w0); + s->b_double[2] = 1 + alpha; break; } break; @@ -982,40 +991,55 @@ static int config_filter(AVFilterLink *outlink, int reset) av_assert0(0); } - av_log(ctx, AV_LOG_VERBOSE, "a=%f %f %f:b=%f %f %f\n", s->a0, s->a1, s->a2, s->b0, s->b1, s->b2); + av_log(ctx, AV_LOG_VERBOSE, "a=%f %f %f:b=%f %f %f\n", + s->a_double[0], s->a_double[1], s->a_double[2], + s->b_double[0], s->b_double[1], s->b_double[2]); - s->a1 /= s->a0; - s->a2 /= s->a0; - s->b0 /= s->a0; - s->b1 /= s->a0; - s->b2 /= s->a0; - s->a0 /= s->a0; + s->a_double[1] /= s->a_double[0]; + s->a_double[2] /= s->a_double[0]; + s->b_double[0] /= s->a_double[0]; + s->b_double[1] /= s->a_double[0]; + s->b_double[2] /= s->a_double[0]; + s->a_double[0] /= s->a_double[0]; - if (s->normalize && fabs(s->b0 + s->b1 + s->b2) > 1e-6) { - double factor = (s->a0 + s->a1 + s->a2) / (s->b0 + s->b1 + s->b2); + if (s->normalize && fabs(s->b_double[0] + s->b_double[1] + s->b_double[2]) > 1e-6) { + double factor = (s->a_double[0] + s->a_double[1] + s->a_double[2]) / + (s->b_double[0] + s->b_double[1] + s->b_double[2]); - s->b0 *= factor; - s->b1 *= factor; - s->b2 *= factor; + s->b_double[0] *= factor; + s->b_double[1] *= factor; + s->b_double[2] *= factor; } switch (s->filter_type) { case tiltshelf: - s->b0 /= A; - s->b1 /= A; - s->b2 /= A; + s->b_double[0] /= A; + s->b_double[1] /= A; + s->b_double[2] /= A; break; } - s->cache = av_realloc_f(s->cache, sizeof(ChanCache), inlink->ch_layout.nb_channels); - if (!s->cache) + if (!s->cache[0]) + s->cache[0] = ff_get_audio_buffer(outlink, 4 * sizeof(double)); + if (!s->clip) + s->clip = av_calloc(outlink->ch_layout.nb_channels, sizeof(*s->clip)); + if (!s->cache[0] || !s->clip) return AVERROR(ENOMEM); - if (reset) - memset(s->cache, 0, sizeof(ChanCache) * inlink->ch_layout.nb_channels); + if (reset) { + av_samples_set_silence(s->cache[0]->extended_data, 0, s->cache[0]->nb_samples, + s->cache[0]->ch_layout.nb_channels, s->cache[0]->format); + } if (reset && s->block_samples > 0) { + if (!s->cache[1]) + s->cache[1] = ff_get_audio_buffer(outlink, 4 * sizeof(double)); + if (!s->cache[1]) + return AVERROR(ENOMEM); + av_samples_set_silence(s->cache[1]->extended_data, 0, s->cache[1]->nb_samples, + s->cache[1]->ch_layout.nb_channels, s->cache[1]->format); for (int i = 0; i < 3; i++) { - s->block[i] = ff_get_audio_buffer(outlink, s->block_samples * 2); + if (!s->block[i]) + s->block[i] = ff_get_audio_buffer(outlink, s->block_samples * 2); if (!s->block[i]) return AVERROR(ENOMEM); av_samples_set_silence(s->block[i]->extended_data, 0, s->block_samples * 2, @@ -1145,16 +1169,23 @@ static int config_filter(AVFilterLink *outlink, int reset) break; default: av_assert0(0); - } + } + + s->block_align = av_get_bytes_per_sample(inlink->format); - s->block_align = av_get_bytes_per_sample(inlink->format); + if (s->transform_type == LATT) + convert_dir2latt(s); + else if (s->transform_type == SVF) + convert_dir2svf(s); + else if (s->transform_type == ZDF) + convert_dir2zdf(s, inlink->sample_rate); - if (s->transform_type == LATT) - convert_dir2latt(s); - else if (s->transform_type == SVF) - convert_dir2svf(s); - else if (s->transform_type == ZDF) - convert_dir2zdf(s, inlink->sample_rate); + s->a_float[0] = s->a_double[0]; + s->a_float[1] = s->a_double[1]; + s->a_float[2] = s->a_double[2]; + s->b_float[0] = s->b_double[0]; + s->b_float[1] = s->b_double[1]; + s->b_float[2] = s->b_double[2]; return 0; } @@ -1227,8 +1258,7 @@ static int filter_channel(AVFilterContext *ctx, void *arg, int jobnr, int nb_job if (!s->block_samples) { s->filter(s, buf->extended_data[ch], out_buf->extended_data[ch], buf->nb_samples, - &s->cache[ch].i1, &s->cache[ch].i2, &s->cache[ch].o1, &s->cache[ch].o2, - s->b0, s->b1, s->b2, s->a0, s->a1, s->a2, &s->cache[ch].clippings, ctx->is_disabled); + s->cache[0]->extended_data[ch], s->clip+ch, ctx->is_disabled); } else if (td->eof) { memcpy(out_buf->extended_data[ch], s->block[1]->extended_data[ch] + s->block_align * s->block_samples, s->nb_samples * s->block_align); @@ -1238,25 +1268,19 @@ static int filter_channel(AVFilterContext *ctx, void *arg, int jobnr, int nb_job memset(s->block[0]->extended_data[ch] + s->block_align * (s->block_samples + buf->nb_samples), 0, (s->block_samples - buf->nb_samples) * s->block_align); s->filter(s, s->block[0]->extended_data[ch], s->block[1]->extended_data[ch], s->block_samples, - &s->cache[ch].i1, &s->cache[ch].i2, &s->cache[ch].o1, &s->cache[ch].o2, - s->b0, s->b1, s->b2, s->a0, s->a1, s->a2, &s->cache[ch].clippings, ctx->is_disabled); - s->cache[ch].ri1 = s->cache[ch].i1; - s->cache[ch].ri2 = s->cache[ch].i2; - s->cache[ch].ro1 = s->cache[ch].o1; - s->cache[ch].ro2 = s->cache[ch].o2; + s->cache[0]->extended_data[ch], s->clip+ch, ctx->is_disabled); + av_samples_copy(s->cache[1]->extended_data, s->cache[0]->extended_data, 0, 0, + s->cache[0]->nb_samples, s->cache[0]->ch_layout.nb_channels, + s->cache[0]->format); s->filter(s, s->block[0]->extended_data[ch] + s->block_samples * s->block_align, s->block[1]->extended_data[ch] + s->block_samples * s->block_align, - s->block_samples, - &s->cache[ch].ri1, &s->cache[ch].ri2, &s->cache[ch].ro1, &s->cache[ch].ro2, - s->b0, s->b1, s->b2, s->a0, s->a1, s->a2, &s->cache[ch].clippings, ctx->is_disabled); + s->block_samples, s->cache[1]->extended_data[ch], s->clip+ch, + ctx->is_disabled); reverse_samples(s->block[2], s->block[1], ch, 0, 0, 2 * s->block_samples); - s->cache[ch].ri1 = 0.; - s->cache[ch].ri2 = 0.; - s->cache[ch].ro1 = 0.; - s->cache[ch].ro2 = 0.; + av_samples_set_silence(s->cache[1]->extended_data, 0, s->cache[1]->nb_samples, + s->cache[1]->ch_layout.nb_channels, s->cache[1]->format); s->filter(s, s->block[2]->extended_data[ch], s->block[2]->extended_data[ch], 2 * s->block_samples, - &s->cache[ch].ri1, &s->cache[ch].ri2, &s->cache[ch].ro1, &s->cache[ch].ro2, - s->b0, s->b1, s->b2, s->a0, s->a1, s->a2, &s->cache[ch].clippings, ctx->is_disabled); + s->cache[1]->extended_data[ch], s->clip+ch, ctx->is_disabled); reverse_samples(s->block[1], s->block[2], ch, 0, 0, 2 * s->block_samples); memcpy(out_buf->extended_data[ch], s->block[1]->extended_data[ch], s->block_samples * s->block_align); @@ -1309,10 +1333,10 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf, int eof) FFMIN(outlink->ch_layout.nb_channels, ff_filter_get_nb_threads(ctx))); for (ch = 0; ch < outlink->ch_layout.nb_channels; ch++) { - if (s->cache[ch].clippings > 0) + if (s->clip[ch] > 0) av_log(ctx, AV_LOG_WARNING, "Channel %d clipping %d times. Please reduce gain.\n", - ch, s->cache[ch].clippings); - s->cache[ch].clippings = 0; + ch, s->clip[ch]); + s->clip[ch] = 0; } if (s->block_samples > 0) { @@ -1402,17 +1426,12 @@ static av_cold void uninit(AVFilterContext *ctx) for (int i = 0; i < 3; i++) av_frame_free(&s->block[i]); - av_freep(&s->cache); + av_frame_free(&s->cache[0]); + av_frame_free(&s->cache[1]); + av_freep(&s->clip); av_channel_layout_uninit(&s->ch_layout); } -static const AVFilterPad inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - static const AVFilterPad outputs[] = { { .name = "default", @@ -1442,7 +1461,7 @@ const AVFilter ff_af_##name_ = { \ .init = name_##_init, \ .activate = activate, \ .uninit = uninit, \ - FILTER_INPUTS(inputs), \ + FILTER_INPUTS(ff_audio_default_filterpad), \ FILTER_OUTPUTS(outputs), \ FILTER_QUERY_FUNC(query_formats), \ .process_command = process_command, \ @@ -1657,12 +1676,12 @@ DEFINE_BIQUAD_FILTER(allpass, "Apply a two-pole all-pass filter."); #endif /* CONFIG_ALLPASS_FILTER */ #if CONFIG_BIQUAD_FILTER static const AVOption biquad_options[] = { - {"a0", NULL, OFFSET(oa0), AV_OPT_TYPE_DOUBLE, {.dbl=1}, INT32_MIN, INT32_MAX, FLAGS}, - {"a1", NULL, OFFSET(oa1), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS}, - {"a2", NULL, OFFSET(oa2), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS}, - {"b0", NULL, OFFSET(ob0), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS}, - {"b1", NULL, OFFSET(ob1), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS}, - {"b2", NULL, OFFSET(ob2), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS}, + {"a0", NULL, OFFSET(oa[0]), AV_OPT_TYPE_DOUBLE, {.dbl=1}, INT32_MIN, INT32_MAX, FLAGS}, + {"a1", NULL, OFFSET(oa[1]), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS}, + {"a2", NULL, OFFSET(oa[2]), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS}, + {"b0", NULL, OFFSET(ob[0]), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS}, + {"b1", NULL, OFFSET(ob[1]), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS}, + {"b2", NULL, OFFSET(ob[2]), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS}, MIX_CHANNELS_NORMALIZE_OPTION(1, "all", 0), TRANSFORM_OPTION(DI), PRECISION_OPTION(-1), diff --git a/libavfilter/af_channelmap.c b/libavfilter/af_channelmap.c index eb173d20c4c..09bc4cfbe19 100644 --- a/libavfilter/af_channelmap.c +++ b/libavfilter/af_channelmap.c @@ -409,13 +409,6 @@ static const AVFilterPad avfilter_af_channelmap_inputs[] = { }, }; -static const AVFilterPad avfilter_af_channelmap_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO - }, -}; - const AVFilter ff_af_channelmap = { .name = "channelmap", .description = NULL_IF_CONFIG_SMALL("Remap audio channels."), @@ -423,6 +416,6 @@ const AVFilter ff_af_channelmap = { .priv_size = sizeof(ChannelMapContext), .priv_class = &channelmap_class, FILTER_INPUTS(avfilter_af_channelmap_inputs), - FILTER_OUTPUTS(avfilter_af_channelmap_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(channelmap_query_formats), }; diff --git a/libavfilter/af_channelsplit.c b/libavfilter/af_channelsplit.c index b537e1380f1..bd4afff1226 100644 --- a/libavfilter/af_channelsplit.c +++ b/libavfilter/af_channelsplit.c @@ -232,13 +232,6 @@ static int activate(AVFilterContext *ctx) return FFERROR_NOT_READY; } -static const AVFilterPad avfilter_af_channelsplit_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_channelsplit = { .name = "channelsplit", .description = NULL_IF_CONFIG_SMALL("Split audio into per-channel streams."), @@ -247,7 +240,7 @@ const AVFilter ff_af_channelsplit = { .init = init, .activate = activate, .uninit = uninit, - FILTER_INPUTS(avfilter_af_channelsplit_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), .outputs = NULL, FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS, diff --git a/libavfilter/af_compand.c b/libavfilter/af_compand.c index ba90d21cede..3e799c3b15e 100644 --- a/libavfilter/af_compand.c +++ b/libavfilter/af_compand.c @@ -306,8 +306,6 @@ static int config_output(AVFilterLink *outlink) int nb_attacks, nb_decays, nb_points; int new_nb_items, num; int i; - int err; - count_items(s->attacks, &nb_attacks); count_items(s->decays, &nb_decays); @@ -495,25 +493,9 @@ static int config_output(AVFilterLink *outlink) return 0; } - s->delay_frame = av_frame_alloc(); - if (!s->delay_frame) { - uninit(ctx); + s->delay_frame = ff_get_audio_buffer(outlink, s->delay_samples); + if (!s->delay_frame) return AVERROR(ENOMEM); - } - - s->delay_frame->format = outlink->format; - s->delay_frame->nb_samples = s->delay_samples; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - s->delay_frame->channel_layout = outlink->channel_layout; -FF_ENABLE_DEPRECATION_WARNINGS -#endif - if ((err = av_channel_layout_copy(&s->delay_frame->ch_layout, &outlink->ch_layout)) < 0) - return err; - - err = av_frame_get_buffer(s->delay_frame, 0); - if (err) - return err; s->compand = compand_delay; return 0; diff --git a/libavfilter/af_compensationdelay.c b/libavfilter/af_compensationdelay.c index c59ebdca91c..924ccefe94b 100644 --- a/libavfilter/af_compensationdelay.c +++ b/libavfilter/af_compensationdelay.c @@ -68,7 +68,6 @@ static int config_input(AVFilterLink *inlink) AVFilterContext *ctx = inlink->dst; CompensationDelayContext *s = ctx->priv; unsigned min_size, new_size = 1; - int ret; s->delay = (s->distance_m * 100. + s->distance_cm * 1. + s->distance_mm * .1) * COMP_DELAY_SOUND_FRONT_DELAY(s->temp) * inlink->sample_rate; @@ -77,23 +76,12 @@ static int config_input(AVFilterLink *inlink) while (new_size < min_size) new_size <<= 1; - s->delay_frame = av_frame_alloc(); + s->buf_size = new_size; + s->delay_frame = ff_get_audio_buffer(inlink, s->buf_size); if (!s->delay_frame) return AVERROR(ENOMEM); - s->buf_size = new_size; - s->delay_frame->format = inlink->format; - s->delay_frame->nb_samples = new_size; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - s->delay_frame->channel_layout = inlink->channel_layout; - s->delay_frame->channels = inlink->ch_layout.nb_channels; -FF_ENABLE_DEPRECATION_WARNINGS -#endif - if ((ret = av_channel_layout_copy(&s->delay_frame->ch_layout, &inlink->ch_layout)) < 0) - return ret; - - return av_frame_get_buffer(s->delay_frame, 0); + return 0; } static int filter_frame(AVFilterLink *inlink, AVFrame *in) @@ -178,13 +166,6 @@ static const AVFilterPad compensationdelay_inputs[] = { }, }; -static const AVFilterPad compensationdelay_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_compensationdelay = { .name = "compensationdelay", .description = NULL_IF_CONFIG_SMALL("Audio Compensation Delay Line."), @@ -192,7 +173,7 @@ const AVFilter ff_af_compensationdelay = { .priv_class = &compensationdelay_class, .uninit = uninit, FILTER_INPUTS(compensationdelay_inputs), - FILTER_OUTPUTS(compensationdelay_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, diff --git a/libavfilter/af_crossfeed.c b/libavfilter/af_crossfeed.c index 9a518e52586..ee6540a9fce 100644 --- a/libavfilter/af_crossfeed.c +++ b/libavfilter/af_crossfeed.c @@ -362,13 +362,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_crossfeed = { .name = "crossfeed", .description = NULL_IF_CONFIG_SMALL("Apply headphone crossfeed filter."), @@ -377,7 +370,7 @@ const AVFilter ff_af_crossfeed = { .activate = activate, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, .process_command = process_command, diff --git a/libavfilter/af_crystalizer.c b/libavfilter/af_crystalizer.c index 449f24a082b..01cdf8bd630 100644 --- a/libavfilter/af_crystalizer.c +++ b/libavfilter/af_crystalizer.c @@ -22,7 +22,6 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" typedef struct CrystalizerContext { const AVClass *class; @@ -233,13 +232,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_crystalizer = { .name = "crystalizer", .description = NULL_IF_CONFIG_SMALL("Simple audio noise sharpening filter."), @@ -247,7 +239,7 @@ const AVFilter ff_af_crystalizer = { .priv_class = &crystalizer_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBLP), .process_command = ff_filter_process_command, diff --git a/libavfilter/af_dcshift.c b/libavfilter/af_dcshift.c index 480f01f4a3d..3e9ba7e5284 100644 --- a/libavfilter/af_dcshift.c +++ b/libavfilter/af_dcshift.c @@ -122,13 +122,6 @@ static const AVFilterPad dcshift_inputs[] = { }, }; -static const AVFilterPad dcshift_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_dcshift = { .name = "dcshift", .description = NULL_IF_CONFIG_SMALL("Apply a DC shift to the audio."), @@ -136,7 +129,7 @@ const AVFilter ff_af_dcshift = { .priv_class = &dcshift_class, .init = init, FILTER_INPUTS(dcshift_inputs), - FILTER_OUTPUTS(dcshift_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_S32P), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/af_deesser.c b/libavfilter/af_deesser.c index cdef8371554..3c5ae8a8cd7 100644 --- a/libavfilter/af_deesser.c +++ b/libavfilter/af_deesser.c @@ -24,7 +24,6 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" typedef struct DeesserChannel { double s1, s2, s3; @@ -193,13 +192,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_deesser = { .name = "deesser", .description = NULL_IF_CONFIG_SMALL("Apply de-essing to the audio."), @@ -207,7 +199,7 @@ const AVFilter ff_af_deesser = { .priv_class = &deesser_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, }; diff --git a/libavfilter/af_dialoguenhance.c b/libavfilter/af_dialoguenhance.c index ae95f95d681..1762ea7cde6 100644 --- a/libavfilter/af_dialoguenhance.c +++ b/libavfilter/af_dialoguenhance.c @@ -24,6 +24,7 @@ #include "audio.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" #include "window_func.h" @@ -320,7 +321,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) s->in = in; de_stereo(ctx, out); - out->pts = in->pts; + av_frame_copy_props(out, in); out->nb_samples = in->nb_samples; ret = ff_filter_frame(outlink, out); fail: @@ -385,13 +386,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_dialoguenhance = { .name = "dialoguenhance", .description = NULL_IF_CONFIG_SMALL("Audio Dialogue Enhancement."), @@ -399,7 +393,7 @@ const AVFilter ff_af_dialoguenhance = { .priv_class = &dialoguenhance_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, .activate = activate, diff --git a/libavfilter/af_drmeter.c b/libavfilter/af_drmeter.c index 3e80691339c..5eea865575e 100644 --- a/libavfilter/af_drmeter.c +++ b/libavfilter/af_drmeter.c @@ -41,7 +41,7 @@ typedef struct DRMeterContext { const AVClass *class; ChannelStats *chstats; int nb_channels; - uint64_t tc_samples; + int64_t tc_samples; double time_constant; } DRMeterContext; @@ -59,11 +59,11 @@ static int config_output(AVFilterLink *outlink) { DRMeterContext *s = outlink->src->priv; - s->chstats = av_calloc(sizeof(*s->chstats), outlink->ch_layout.nb_channels); + s->chstats = av_calloc(outlink->ch_layout.nb_channels, sizeof(*s->chstats)); if (!s->chstats) return AVERROR(ENOMEM); s->nb_channels = outlink->ch_layout.nb_channels; - s->tc_samples = s->time_constant * outlink->sample_rate + .5; + s->tc_samples = lrint(s->time_constant * outlink->sample_rate); return 0; } @@ -73,7 +73,7 @@ static void finish_block(ChannelStats *p) int peak_bin, rms_bin; float peak, rms; - rms = sqrt(2 * p->sum / p->nb_samples); + rms = sqrtf(2.f * p->sum / p->nb_samples); peak = p->peak; rms_bin = av_clip(lrintf(rms * BINS), 0, BINS); peak_bin = av_clip(lrintf(peak * BINS), 0, BINS); @@ -88,36 +88,33 @@ static void finish_block(ChannelStats *p) static void update_stat(DRMeterContext *s, ChannelStats *p, float sample) { - if (p->nb_samples >= s->tc_samples) { - finish_block(p); - } - - p->peak = FFMAX(FFABS(sample), p->peak); + p->peak = fmaxf(fabsf(sample), p->peak); p->sum += sample * sample; p->nb_samples++; + if (p->nb_samples >= s->tc_samples) + finish_block(p); } static int filter_frame(AVFilterLink *inlink, AVFrame *buf) { DRMeterContext *s = inlink->dst->priv; const int channels = s->nb_channels; - int i, c; switch (inlink->format) { case AV_SAMPLE_FMT_FLTP: - for (c = 0; c < channels; c++) { + for (int c = 0; c < channels; c++) { ChannelStats *p = &s->chstats[c]; const float *src = (const float *)buf->extended_data[c]; - for (i = 0; i < buf->nb_samples; i++, src++) + for (int i = 0; i < buf->nb_samples; i++, src++) update_stat(s, p, *src); } break; case AV_SAMPLE_FMT_FLT: { const float *src = (const float *)buf->extended_data[0]; - for (i = 0; i < buf->nb_samples; i++) { - for (c = 0; c < channels; c++, src++) + for (int i = 0; i < buf->nb_samples; i++) { + for (int c = 0; c < channels; c++, src++) update_stat(s, &s->chstats[c], *src); }} break; @@ -131,39 +128,42 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf) static void print_stats(AVFilterContext *ctx) { DRMeterContext *s = ctx->priv; - float dr = 0; - int ch; + float dr = 0.f; - for (ch = 0; ch < s->nb_channels; ch++) { + for (int ch = 0; ch < s->nb_channels; ch++) { ChannelStats *p = &s->chstats[ch]; - float chdr, secondpeak, rmssum = 0; - int i, j, first = 0; + float chdr, secondpeak, rmssum = 0.f; + int first = 0, last = lrintf(0.2f * p->blknum); + int peak_bin = BINS; if (!p->nb_samples) { av_log(ctx, AV_LOG_INFO, "No data, dynamic range not meassurable\n"); return; } - finish_block(p); + if (p->nb_samples) + finish_block(p); - for (i = 0; i <= BINS; i++) { - if (p->peaks[BINS - i]) { - if (first) + for (int i = BINS; i >= 0; i--) { + if (p->peaks[i]) { + if (first || p->peaks[i] > 1) { + peak_bin = i; break; + } first = 1; } } - secondpeak = (BINS - i) / (double)BINS; + secondpeak = peak_bin / (float)BINS; - for (i = BINS, j = 0; i >= 0 && j < 0.2 * p->blknum; i--) { + for (int64_t i = BINS, j = 0; i >= 0 && j < last; i--) { if (p->rms[i]) { - rmssum += SQR(i / (double)BINS); + rmssum += SQR(i / (float)BINS) * p->rms[i]; j += p->rms[i]; } } - chdr = 20 * log10(secondpeak / sqrt(rmssum / (0.2 * p->blknum))); + chdr = 20.f * log10f(secondpeak / sqrtf(rmssum / (float)last)); dr += chdr; av_log(ctx, AV_LOG_INFO, "Channel %d: DR: %g\n", ch + 1, chdr); } diff --git a/libavfilter/af_dynaudnorm.c b/libavfilter/af_dynaudnorm.c index e9d8ad8ec86..fb0581b6e4b 100644 --- a/libavfilter/af_dynaudnorm.c +++ b/libavfilter/af_dynaudnorm.c @@ -1019,13 +1019,6 @@ static const AVFilterPad avfilter_af_dynaudnorm_inputs[] = { }, }; -static const AVFilterPad avfilter_af_dynaudnorm_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_dynaudnorm = { .name = "dynaudnorm", .description = NULL_IF_CONFIG_SMALL("Dynamic Audio Normalizer."), @@ -1034,7 +1027,7 @@ const AVFilter ff_af_dynaudnorm = { .uninit = uninit, .activate = activate, FILTER_INPUTS(avfilter_af_dynaudnorm_inputs), - FILTER_OUTPUTS(avfilter_af_dynaudnorm_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), .priv_class = &dynaudnorm_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | diff --git a/libavfilter/af_earwax.c b/libavfilter/af_earwax.c index f420a5ac551..38ff9c1f3ff 100644 --- a/libavfilter/af_earwax.c +++ b/libavfilter/af_earwax.c @@ -222,19 +222,12 @@ static const AVFilterPad earwax_inputs[] = { }, }; -static const AVFilterPad earwax_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_earwax = { .name = "earwax", .description = NULL_IF_CONFIG_SMALL("Widen the stereo image."), .priv_size = sizeof(EarwaxContext), .uninit = uninit, FILTER_INPUTS(earwax_inputs), - FILTER_OUTPUTS(earwax_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/af_extrastereo.c b/libavfilter/af_extrastereo.c index 6f1d691d8e0..2b1b09f9c29 100644 --- a/libavfilter/af_extrastereo.c +++ b/libavfilter/af_extrastereo.c @@ -110,20 +110,13 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_extrastereo = { .name = "extrastereo", .description = NULL_IF_CONFIG_SMALL("Increase difference between stereo audio channels."), .priv_size = sizeof(ExtraStereoContext), .priv_class = &extrastereo_class, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, .process_command = ff_filter_process_command, diff --git a/libavfilter/af_flanger.c b/libavfilter/af_flanger.c index 452436a4b66..2b9da2db894 100644 --- a/libavfilter/af_flanger.c +++ b/libavfilter/af_flanger.c @@ -195,13 +195,6 @@ static const AVFilterPad flanger_inputs[] = { }, }; -static const AVFilterPad flanger_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_flanger = { .name = "flanger", .description = NULL_IF_CONFIG_SMALL("Apply a flanging effect to the audio."), @@ -210,6 +203,6 @@ const AVFilter ff_af_flanger = { .init = init, .uninit = uninit, FILTER_INPUTS(flanger_inputs), - FILTER_OUTPUTS(flanger_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), }; diff --git a/libavfilter/af_haas.c b/libavfilter/af_haas.c index d283da72618..c6d8aa79d8c 100644 --- a/libavfilter/af_haas.c +++ b/libavfilter/af_haas.c @@ -206,13 +206,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_haas = { .name = "haas", .description = NULL_IF_CONFIG_SMALL("Apply Haas Stereo Enhancer."), @@ -220,6 +213,6 @@ const AVFilter ff_af_haas = { .priv_class = &haas_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/af_hdcd.c b/libavfilter/af_hdcd.c index 49bfa1bfa8e..a03c9aadf72 100644 --- a/libavfilter/af_hdcd.c +++ b/libavfilter/af_hdcd.c @@ -47,6 +47,7 @@ #include "libavutil/opt.h" #include "libavutil/avassert.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" #include "audio.h" @@ -1763,13 +1764,6 @@ static const AVFilterPad avfilter_af_hdcd_inputs[] = { }, }; -static const AVFilterPad avfilter_af_hdcd_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_hdcd = { .name = "hdcd", .description = NULL_IF_CONFIG_SMALL("Apply High Definition Compatible Digital (HDCD) decoding."), @@ -1778,6 +1772,6 @@ const AVFilter ff_af_hdcd = { .init = init, .uninit = uninit, FILTER_INPUTS(avfilter_af_hdcd_inputs), - FILTER_OUTPUTS(avfilter_af_hdcd_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/af_headphone.c b/libavfilter/af_headphone.c index 005e151e2c2..c8f753387f9 100644 --- a/libavfilter/af_headphone.c +++ b/libavfilter/af_headphone.c @@ -29,6 +29,7 @@ #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" #include "audio.h" @@ -319,6 +320,16 @@ static int check_ir(AVFilterLink *inlink, int input_number) s->hrir_in[input_number].ir_len = ir_len; s->ir_len = FFMAX(ir_len, s->ir_len); + if (ff_inlink_check_available_samples(inlink, ir_len + 1) == 1) { + s->hrir_in[input_number].eof = 1; + return 1; + } + + if (!s->hrir_in[input_number].eof) { + ff_inlink_request_frame(inlink); + return 0; + } + return 0; } @@ -534,7 +545,7 @@ static int activate(AVFilterContext *ctx) AVFrame *in = NULL; int i, ret; - FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[0], ctx); + FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx); if (!s->eof_hrirs) { int eof = 1; for (i = 0; i < s->nb_hrir_inputs; i++) { @@ -543,24 +554,23 @@ static int activate(AVFilterContext *ctx) if (s->hrir_in[i].eof) continue; - if ((ret = check_ir(input, i)) < 0) + if ((ret = check_ir(input, i)) <= 0) return ret; - if (ff_outlink_get_status(input) == AVERROR_EOF) { + if (s->hrir_in[i].eof) { if (!ff_inlink_queued_samples(input)) { av_log(ctx, AV_LOG_ERROR, "No samples provided for " "HRIR stream %d.\n", i); return AVERROR_INVALIDDATA; } - s->hrir_in[i].eof = 1; } else { - if (ff_outlink_frame_wanted(ctx->outputs[0])) - ff_inlink_request_frame(input); eof = 0; } } - if (!eof) + if (!eof) { + ff_filter_set_ready(ctx, 100); return 0; + } s->eof_hrirs = 1; ret = convert_coeffs(ctx, inlink); @@ -569,7 +579,7 @@ static int activate(AVFilterContext *ctx) } else if (!s->have_hrirs) return AVERROR_EOF; - if ((ret = ff_inlink_consume_samples(ctx->inputs[0], s->size, s->size, &in)) > 0) { + if ((ret = ff_inlink_consume_samples(inlink, s->size, s->size, &in)) > 0) { ret = headphone_frame(s, in, outlink); if (ret < 0) return ret; @@ -578,9 +588,9 @@ static int activate(AVFilterContext *ctx) if (ret < 0) return ret; - FF_FILTER_FORWARD_STATUS(ctx->inputs[0], ctx->outputs[0]); - if (ff_outlink_frame_wanted(ctx->outputs[0])) - ff_inlink_request_frame(ctx->inputs[0]); + FF_FILTER_FORWARD_STATUS(inlink, outlink); + if (ff_outlink_frame_wanted(outlink)) + ff_inlink_request_frame(inlink); return 0; } diff --git a/libavfilter/af_join.c b/libavfilter/af_join.c index dc075a8b27d..5dbf9d8d22f 100644 --- a/libavfilter/af_join.c +++ b/libavfilter/af_join.c @@ -52,6 +52,7 @@ typedef struct JoinContext { AVChannelLayout ch_layout; int64_t eof_pts; + int eof; ChannelMap *channels; @@ -520,6 +521,10 @@ static int try_push_frame(AVFilterContext *ctx) } frame->nb_samples = nb_samples; + frame->duration = av_rescale_q(frame->nb_samples, + av_make_q(1, outlink->sample_rate), + outlink->time_base); + #if FF_API_OLD_CHANNEL_LAYOUT FF_DISABLE_DEPRECATION_WARNINGS frame->channel_layout = outlink->channel_layout; @@ -527,7 +532,7 @@ FF_DISABLE_DEPRECATION_WARNINGS FF_ENABLE_DEPRECATION_WARNINGS #endif if ((ret = av_channel_layout_copy(&frame->ch_layout, &outlink->ch_layout)) < 0) - return ret; + goto fail; frame->sample_rate = outlink->sample_rate; frame->format = outlink->format; frame->pts = s->input_frames[0]->pts; @@ -552,10 +557,11 @@ FF_ENABLE_DEPRECATION_WARNINGS return ret; eof: for (i = 0; i < ctx->nb_inputs; i++) { - if (ff_outlink_get_status(ctx->inputs[i]) && + if (s->eof && ff_inlink_queued_samples(ctx->inputs[i]) <= 0 && !s->input_frames[i]) { ff_outlink_set_status(outlink, AVERROR_EOF, s->eof_pts); + break; } } @@ -576,11 +582,10 @@ static int activate(AVFilterContext *ctx) if (ret < 0) { return ret; } else if (ret == 0 && ff_inlink_acknowledge_status(ctx->inputs[0], &status, &pts)) { - ff_outlink_set_status(ctx->outputs[0], status, s->eof_pts); - return 0; + s->eof |= status == AVERROR_EOF; } - if (!s->input_frames[0] && ff_outlink_frame_wanted(ctx->outputs[0])) { + if (!s->eof && !s->input_frames[0] && ff_outlink_frame_wanted(ctx->outputs[0])) { ff_inlink_request_frame(ctx->inputs[0]); return 0; } @@ -596,11 +601,10 @@ static int activate(AVFilterContext *ctx) if (ret < 0) { return ret; } else if (ff_inlink_acknowledge_status(ctx->inputs[i], &status, &pts)) { - ff_outlink_set_status(ctx->outputs[0], status, pts); - return 0; + s->eof |= status == AVERROR_EOF; } - if (!s->input_frames[i]) { + if (!s->eof && !s->input_frames[i]) { ff_inlink_request_frame(ctx->inputs[i]); return 0; } diff --git a/libavfilter/af_ladspa.c b/libavfilter/af_ladspa.c index fc93d56af7f..85e215850d2 100644 --- a/libavfilter/af_ladspa.c +++ b/libavfilter/af_ladspa.c @@ -33,6 +33,7 @@ #include "libavutil/opt.h" #include "audio.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" typedef struct MetaItem { diff --git a/libavfilter/af_loudnorm.c b/libavfilter/af_loudnorm.c index 609eae797c8..d83398ae2ad 100644 --- a/libavfilter/af_loudnorm.c +++ b/libavfilter/af_loudnorm.c @@ -23,6 +23,7 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" #include "audio.h" #include "ebur128.h" @@ -730,26 +731,24 @@ static int activate(AVFilterContext *ctx) static int query_formats(AVFilterContext *ctx) { LoudNormContext *s = ctx->priv; - AVFilterFormats *formats = NULL; static const int input_srate[] = {192000, -1}; + static const enum AVSampleFormat sample_fmts[] = { + AV_SAMPLE_FMT_DBL, + AV_SAMPLE_FMT_NONE + }; int ret = ff_set_common_all_channel_counts(ctx); if (ret < 0) return ret; - ret = ff_add_format(&formats, AV_SAMPLE_FMT_DBL); - if (ret) - return ret; - ret = ff_set_common_formats(ctx, formats); - if (ret) + ret = ff_set_common_formats_from_list(ctx, sample_fmts); + if (ret < 0) return ret; - if (s->frame_type != LINEAR_MODE) { - formats = ff_make_format_list(input_srate); + if (s->frame_type == LINEAR_MODE) { + return ff_set_common_all_samplerates(ctx); } else { - formats = ff_all_samplerates(); + return ff_set_common_samplerates_from_list(ctx, input_srate); } - - return ff_set_common_samplerates(ctx, formats); } static int config_input(AVFilterLink *inlink) @@ -928,13 +927,6 @@ static const AVFilterPad avfilter_af_loudnorm_inputs[] = { }, }; -static const AVFilterPad avfilter_af_loudnorm_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_loudnorm = { .name = "loudnorm", .description = NULL_IF_CONFIG_SMALL("EBU R128 loudness normalization"), @@ -944,6 +936,6 @@ const AVFilter ff_af_loudnorm = { .activate = activate, .uninit = uninit, FILTER_INPUTS(avfilter_af_loudnorm_inputs), - FILTER_OUTPUTS(avfilter_af_loudnorm_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/af_lv2.c b/libavfilter/af_lv2.c index 3cdf16457cd..e577b1624d0 100644 --- a/libavfilter/af_lv2.c +++ b/libavfilter/af_lv2.c @@ -33,6 +33,7 @@ #include "libavutil/opt.h" #include "audio.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" typedef struct URITable { diff --git a/libavfilter/af_pan.c b/libavfilter/af_pan.c index 067f6468053..cfed9f146ad 100644 --- a/libavfilter/af_pan.c +++ b/libavfilter/af_pan.c @@ -313,7 +313,7 @@ static int config_props(AVFilterLink *link) pan->channel_map[i] = ch_id; } - av_opt_set_int(pan->swr, "uch", pan->nb_output_channels, 0); + av_opt_set_chlayout(pan->swr, "uchl", &pan->out_channel_layout, 0); swr_set_channel_mapping(pan->swr, pan->channel_map); } else { // renormalize @@ -385,12 +385,14 @@ FF_DISABLE_DEPRECATION_WARNINGS outsamples->channels = outlink->ch_layout.nb_channels; FF_ENABLE_DEPRECATION_WARNINGS #endif - if ((ret = av_channel_layout_copy(&outsamples->ch_layout, &outlink->ch_layout)) < 0) + if ((ret = av_channel_layout_copy(&outsamples->ch_layout, &outlink->ch_layout)) < 0) { + av_frame_free(&outsamples); + av_frame_free(&insamples); return ret; + } - ret = ff_filter_frame(outlink, outsamples); av_frame_free(&insamples); - return ret; + return ff_filter_frame(outlink, outsamples); } static av_cold void uninit(AVFilterContext *ctx) @@ -417,13 +419,6 @@ static const AVFilterPad pan_inputs[] = { }, }; -static const AVFilterPad pan_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_pan = { .name = "pan", .description = NULL_IF_CONFIG_SMALL("Remix channels with coefficients (panning)."), @@ -432,6 +427,6 @@ const AVFilter ff_af_pan = { .init = init, .uninit = uninit, FILTER_INPUTS(pan_inputs), - FILTER_OUTPUTS(pan_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/af_replaygain.c b/libavfilter/af_replaygain.c index 53852ac8bc1..266121e2c00 100644 --- a/libavfilter/af_replaygain.c +++ b/libavfilter/af_replaygain.c @@ -23,10 +23,14 @@ * ReplayGain scanner */ +#include + #include "libavutil/avassert.h" #include "libavutil/channel_layout.h" +#include "libavutil/opt.h" #include "audio.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" #define HISTOGRAM_SLOTS 12000 @@ -306,8 +310,11 @@ static const ReplayGainFreqInfo freqinfos[] = }; typedef struct ReplayGainContext { + const AVClass *class; + uint32_t histogram[HISTOGRAM_SLOTS]; float peak; + float gain; int yule_hist_i, butter_hist_i; const double *yule_coeff_a; const double *yule_coeff_b; @@ -576,13 +583,22 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) return ff_filter_frame(outlink, in); } -static av_cold void uninit(AVFilterContext *ctx) +static int request_frame(AVFilterLink *outlink) { + AVFilterContext *ctx = outlink->src; ReplayGainContext *s = ctx->priv; - float gain = calc_replaygain(s->histogram); + int ret = 0; - av_log(ctx, AV_LOG_INFO, "track_gain = %+.2f dB\n", gain); - av_log(ctx, AV_LOG_INFO, "track_peak = %.6f\n", s->peak); + ret = ff_request_frame(ctx->inputs[0]); + + if (ret == AVERROR_EOF) { + s->gain = calc_replaygain(s->histogram); + + av_log(ctx, AV_LOG_INFO, "track_gain = %+.2f dB\n", s->gain); + av_log(ctx, AV_LOG_INFO, "track_peak = %.6f\n", s->peak); + } + + return ret; } static const AVFilterPad replaygain_inputs[] = { @@ -598,14 +614,26 @@ static const AVFilterPad replaygain_outputs[] = { { .name = "default", .type = AVMEDIA_TYPE_AUDIO, + .request_frame = request_frame, }, }; +#define OFFSET(x) offsetof(ReplayGainContext, x) +#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_EXPORT|AV_OPT_FLAG_READONLY + +static const AVOption replaygain_options[] = { + { "track_gain", "track gain (dB)", OFFSET(gain), AV_OPT_TYPE_FLOAT,{.dbl=0}, -FLT_MAX, FLT_MAX, FLAGS }, + { "track_peak", "track peak", OFFSET(peak), AV_OPT_TYPE_FLOAT,{.dbl=0}, -FLT_MAX, FLT_MAX, FLAGS }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(replaygain); + const AVFilter ff_af_replaygain = { .name = "replaygain", .description = NULL_IF_CONFIG_SMALL("ReplayGain scanner."), - .uninit = uninit, .priv_size = sizeof(ReplayGainContext), + .priv_class = &replaygain_class, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(replaygain_inputs), FILTER_OUTPUTS(replaygain_outputs), diff --git a/libavfilter/af_rubberband.c b/libavfilter/af_rubberband.c index 34a16056673..63a0c5acce3 100644 --- a/libavfilter/af_rubberband.c +++ b/libavfilter/af_rubberband.c @@ -25,7 +25,6 @@ #include "audio.h" #include "avfilter.h" #include "filters.h" -#include "formats.h" #include "internal.h" typedef struct RubberBandContext { @@ -38,7 +37,9 @@ typedef struct RubberBandContext { int64_t nb_samples_out; int64_t nb_samples_in; int64_t first_pts; + int64_t last_pts; int nb_samples; + int eof; } RubberBandContext; #define OFFSET(x) offsetof(RubberBandContext, x) @@ -100,7 +101,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) if (s->first_pts == AV_NOPTS_VALUE) s->first_pts = in->pts; - rubberband_process(s->rbs, (const float *const *)in->data, in->nb_samples, ff_outlink_get_status(inlink)); + rubberband_process(s->rbs, (const float *const *)in->extended_data, in->nb_samples, s->eof); s->nb_samples_in += in->nb_samples; nb_samples = rubberband_available(s->rbs); @@ -113,7 +114,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) out->pts = s->first_pts + av_rescale_q(s->nb_samples_out, (AVRational){ 1, outlink->sample_rate }, outlink->time_base); - nb_samples = rubberband_retrieve(s->rbs, (float *const *)out->data, nb_samples); + s->last_pts = out->pts; + nb_samples = rubberband_retrieve(s->rbs, (float *const *)out->extended_data, nb_samples); out->nb_samples = nb_samples; ret = ff_filter_frame(outlink, out); s->nb_samples_out += nb_samples; @@ -151,11 +153,16 @@ static int activate(AVFilterContext *ctx) AVFilterLink *outlink = ctx->outputs[0]; RubberBandContext *s = ctx->priv; AVFrame *in = NULL; - int ret; + int64_t pts; + int status, ret; FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); ret = ff_inlink_consume_samples(inlink, s->nb_samples, s->nb_samples, &in); + + if (ff_inlink_acknowledge_status(inlink, &status, &pts)) + s->eof |= status == AVERROR_EOF; + if (ret < 0) return ret; if (ret > 0) { @@ -164,7 +171,11 @@ static int activate(AVFilterContext *ctx) return ret; } - FF_FILTER_FORWARD_STATUS(inlink, outlink); + if (s->eof) { + ff_outlink_set_status(outlink, AVERROR_EOF, s->last_pts); + return 0; + } + FF_FILTER_FORWARD_WANTED(outlink, inlink); return FFERROR_NOT_READY; @@ -195,13 +206,6 @@ static const AVFilterPad rubberband_inputs[] = { }, }; -static const AVFilterPad rubberband_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_rubberband = { .name = "rubberband", .description = NULL_IF_CONFIG_SMALL("Apply time-stretching and pitch-shifting."), @@ -210,7 +214,7 @@ const AVFilter ff_af_rubberband = { .uninit = uninit, .activate = activate, FILTER_INPUTS(rubberband_inputs), - FILTER_OUTPUTS(rubberband_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_FLTP), .process_command = process_command, }; diff --git a/libavfilter/af_silencedetect.c b/libavfilter/af_silencedetect.c index 6518283d9f4..845c65bfed6 100644 --- a/libavfilter/af_silencedetect.c +++ b/libavfilter/af_silencedetect.c @@ -28,7 +28,6 @@ #include "libavutil/opt.h" #include "libavutil/timestamp.h" #include "audio.h" -#include "formats.h" #include "avfilter.h" #include "internal.h" @@ -253,20 +252,13 @@ static const AVFilterPad silencedetect_inputs[] = { }, }; -static const AVFilterPad silencedetect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_silencedetect = { .name = "silencedetect", .description = NULL_IF_CONFIG_SMALL("Detect silence."), .priv_size = sizeof(SilenceDetectContext), .uninit = uninit, FILTER_INPUTS(silencedetect_inputs), - FILTER_OUTPUTS(silencedetect_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBLP, AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_S32P, diff --git a/libavfilter/af_silenceremove.c b/libavfilter/af_silenceremove.c index d5a2ac6a410..d565c75f07e 100644 --- a/libavfilter/af_silenceremove.c +++ b/libavfilter/af_silenceremove.c @@ -23,18 +23,27 @@ #include /* DBL_MAX */ -#include "libavutil/audio_fifo.h" #include "libavutil/avassert.h" #include "libavutil/opt.h" -#include "libavutil/timestamp.h" #include "audio.h" -#include "formats.h" +#include "filters.h" #include "avfilter.h" #include "internal.h" enum SilenceDetect { - D_PEAK, + D_AVG, D_RMS, + D_PEAK, + D_MEDIAN, + D_PTP, + D_DEV, + D_NB +}; + +enum TimestampMode { + TS_WRITE, + TS_COPY, + TS_NB }; enum ThresholdMode { @@ -42,371 +51,116 @@ enum ThresholdMode { T_ALL, }; -enum SilenceMode { - SILENCE_TRIM, - SILENCE_TRIM_FLUSH, - SILENCE_COPY, - SILENCE_COPY_FLUSH, - SILENCE_STOP -}; - typedef struct SilenceRemoveContext { const AVClass *class; - enum SilenceMode mode; - + int start_mode; int start_periods; int64_t start_duration; int64_t start_duration_opt; double start_threshold; int64_t start_silence; int64_t start_silence_opt; - int start_mode; + int stop_mode; int stop_periods; int64_t stop_duration; int64_t stop_duration_opt; double stop_threshold; int64_t stop_silence; int64_t stop_silence_opt; - int stop_mode; int64_t window_duration_opt; - AVFrame *start_holdoff; - AVFrame *start_silence_hold; - size_t start_holdoff_offset; - size_t start_holdoff_end; - size_t start_silence_offset; - size_t start_silence_end; - int start_found_periods; - - AVFrame *stop_holdoff; - AVFrame *stop_silence_hold; - size_t stop_holdoff_offset; - size_t stop_holdoff_end; - size_t stop_silence_offset; - size_t stop_silence_end; - int stop_found_periods; - - AVFrame *window; - int window_offset; + int timestamp_mode; + + int start_found_periods; + int stop_found_periods; + + int start_sample_count; + int start_silence_count; + + int stop_sample_count; + int stop_silence_count; + + AVFrame *start_window; + AVFrame *stop_window; + + int *start_front; + int *start_back; + + int *stop_front; + int *stop_back; + int64_t window_duration; - double sum; + int cache_size; + + int start_window_pos; + int start_window_size; + + int stop_window_pos; + int stop_window_size; + + double *start_cache; + double *stop_cache; + + AVFrame *start_queuef; + int start_queue_pos; + int start_queue_size; + + AVFrame *stop_queuef; + int stop_queue_pos; + int stop_queue_size; - int one_period; int restart; + int found_nonsilence; int64_t next_pts; int detection; - void (*update)(struct SilenceRemoveContext *s, AVFrame *frame, int ch, int offset); - double (*compute)(struct SilenceRemoveContext *s, AVFrame *frame, int ch, int offset); - void (*copy)(struct SilenceRemoveContext *s, AVFrame *out, AVFrame *in, - int ch, int out_offset, int in_offset); - AVAudioFifo *fifo; + float (*compute_flt)(float *c, float s, float ws, int size, int *front, int *back); + double (*compute_dbl)(double *c, double s, double ws, int size, int *front, int *back); } SilenceRemoveContext; #define OFFSET(x) offsetof(SilenceRemoveContext, x) #define AF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_AUDIO_PARAM +#define AFR AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption silenceremove_options[] = { { "start_periods", "set periods of silence parts to skip from start", OFFSET(start_periods), AV_OPT_TYPE_INT, {.i64=0}, 0, 9000, AF }, { "start_duration", "set start duration of non-silence part", OFFSET(start_duration_opt), AV_OPT_TYPE_DURATION, {.i64=0}, 0, INT32_MAX, AF }, - { "start_threshold", "set threshold for start silence detection", OFFSET(start_threshold), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, DBL_MAX, AF }, + { "start_threshold", "set threshold for start silence detection", OFFSET(start_threshold), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, DBL_MAX, AFR }, { "start_silence", "set start duration of silence part to keep", OFFSET(start_silence_opt), AV_OPT_TYPE_DURATION, {.i64=0}, 0, INT32_MAX, AF }, - { "start_mode", "set which channel will trigger trimming from start", OFFSET(start_mode), AV_OPT_TYPE_INT, {.i64=T_ANY}, T_ANY, T_ALL, AF, "mode" }, - { "any", 0, 0, AV_OPT_TYPE_CONST, {.i64=T_ANY}, 0, 0, AF, "mode" }, - { "all", 0, 0, AV_OPT_TYPE_CONST, {.i64=T_ALL}, 0, 0, AF, "mode" }, + { "start_mode", "set which channel will trigger trimming from start", OFFSET(start_mode), AV_OPT_TYPE_INT, {.i64=T_ANY}, T_ANY, T_ALL, AFR, "mode" }, + { "any", 0, 0, AV_OPT_TYPE_CONST, {.i64=T_ANY}, 0, 0, AFR, "mode" }, + { "all", 0, 0, AV_OPT_TYPE_CONST, {.i64=T_ALL}, 0, 0, AFR, "mode" }, { "stop_periods", "set periods of silence parts to skip from end", OFFSET(stop_periods), AV_OPT_TYPE_INT, {.i64=0}, -9000, 9000, AF }, - { "stop_duration", "set stop duration of non-silence part", OFFSET(stop_duration_opt), AV_OPT_TYPE_DURATION, {.i64=0}, 0, INT32_MAX, AF }, - { "stop_threshold", "set threshold for stop silence detection", OFFSET(stop_threshold), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, DBL_MAX, AF }, + { "stop_duration", "set stop duration of silence part", OFFSET(stop_duration_opt), AV_OPT_TYPE_DURATION, {.i64=0}, 0, INT32_MAX, AF }, + { "stop_threshold", "set threshold for stop silence detection", OFFSET(stop_threshold), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, DBL_MAX, AFR }, { "stop_silence", "set stop duration of silence part to keep", OFFSET(stop_silence_opt), AV_OPT_TYPE_DURATION, {.i64=0}, 0, INT32_MAX, AF }, - { "stop_mode", "set which channel will trigger trimming from end", OFFSET(stop_mode), AV_OPT_TYPE_INT, {.i64=T_ANY}, T_ANY, T_ALL, AF, "mode" }, - { "detection", "set how silence is detected", OFFSET(detection), AV_OPT_TYPE_INT, {.i64=D_RMS}, D_PEAK,D_RMS, AF, "detection" }, - { "peak", "use absolute values of samples", 0, AV_OPT_TYPE_CONST, {.i64=D_PEAK},0, 0, AF, "detection" }, - { "rms", "use squared values of samples", 0, AV_OPT_TYPE_CONST, {.i64=D_RMS}, 0, 0, AF, "detection" }, + { "stop_mode", "set which channel will trigger trimming from end", OFFSET(stop_mode), AV_OPT_TYPE_INT, {.i64=T_ALL}, T_ANY, T_ALL, AFR, "mode" }, + { "detection", "set how silence is detected", OFFSET(detection), AV_OPT_TYPE_INT, {.i64=D_RMS}, 0, D_NB-1, AF, "detection" }, + { "avg", "use mean absolute values of samples", 0, AV_OPT_TYPE_CONST, {.i64=D_AVG}, 0, 0, AF, "detection" }, + { "rms", "use root mean squared values of samples", 0, AV_OPT_TYPE_CONST, {.i64=D_RMS}, 0, 0, AF, "detection" }, + { "peak", "use max absolute values of samples", 0, AV_OPT_TYPE_CONST, {.i64=D_PEAK},0, 0, AF, "detection" }, + { "median", "use median of absolute values of samples", 0, AV_OPT_TYPE_CONST, {.i64=D_MEDIAN},0, 0, AF, "detection" }, + { "ptp", "use absolute of max peak to min peak difference", 0, AV_OPT_TYPE_CONST, {.i64=D_PTP}, 0, 0, AF, "detection" }, + { "dev", "use standard deviation from values of samples", 0, AV_OPT_TYPE_CONST, {.i64=D_DEV}, 0, 0, AF, "detection" }, { "window", "set duration of window for silence detection", OFFSET(window_duration_opt), AV_OPT_TYPE_DURATION, {.i64=20000}, 0, 100000000, AF }, + { "timestamp", "set how every output frame timestamp is processed", OFFSET(timestamp_mode), AV_OPT_TYPE_INT, {.i64=TS_WRITE}, 0, TS_NB-1, AF, "timestamp" }, + { "write", "full timestamps rewrite, keep only the start time", 0, AV_OPT_TYPE_CONST, {.i64=TS_WRITE}, 0, 0, AF, "timestamp" }, + { "copy", "non-dropped frames are left with same timestamp", 0, AV_OPT_TYPE_CONST, {.i64=TS_COPY}, 0, 0, AF, "timestamp" }, { NULL } }; AVFILTER_DEFINE_CLASS(silenceremove); -static void copy_double(SilenceRemoveContext *s, AVFrame *out, AVFrame *in, - int ch, int out_offset, int in_offset) -{ - const double *srcp = (const double *)in->data[0]; - const double src = srcp[in->ch_layout.nb_channels * in_offset + ch]; - double *dstp = (double *)out->data[0]; - - dstp[out->ch_layout.nb_channels * out_offset + ch] = src; -} - -static void copy_doublep(SilenceRemoveContext *s, AVFrame *out, AVFrame *in, - int ch, int out_offset, int in_offset) -{ - const double *srcp = (const double *)in->extended_data[ch]; - const double src = srcp[in_offset]; - double *dstp = (double *)out->extended_data[ch]; +#define DEPTH 32 +#include "silenceremove_template.c" - dstp[out_offset] = src; -} - -static void copy_float(SilenceRemoveContext *s, AVFrame *out, AVFrame *in, - int ch, int out_offset, int in_offset) -{ - const float *srcp = (const float *)in->data[0]; - const float src = srcp[in->ch_layout.nb_channels * in_offset + ch]; - float *dstp = (float *)out->data[0]; - - dstp[out->ch_layout.nb_channels * out_offset + ch] = src; -} - -static void copy_floatp(SilenceRemoveContext *s, AVFrame *out, AVFrame *in, - int ch, int out_offset, int in_offset) -{ - const float *srcp = (const float *)in->extended_data[ch]; - const float src = srcp[in_offset]; - float *dstp = (float *)out->extended_data[ch]; - - dstp[out_offset] = src; -} - -static double compute_peak_double(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const double *samples = (const double *)frame->data[0]; - const double *wsamples = (const double *)s->window->data[0]; - double sample = samples[frame->ch_layout.nb_channels * offset + ch]; - double wsample = wsamples[frame->ch_layout.nb_channels * s->window_offset + ch]; - double new_sum; - - new_sum = s->sum; - new_sum -= wsample; - new_sum = fmax(new_sum, 0.); - new_sum += fabs(sample); - - return new_sum / s->window_duration; -} - -static void update_peak_double(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const double *samples = (const double *)frame->data[0]; - double *wsamples = (double *)s->window->data[0]; - double sample = samples[frame->ch_layout.nb_channels * offset + ch]; - double *wsample = &wsamples[frame->ch_layout.nb_channels * s->window_offset + ch]; - - s->sum -= *wsample; - s->sum = fmax(s->sum, 0.); - *wsample = fabs(sample); - s->sum += *wsample; -} - -static double compute_peak_float(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const float *samples = (const float *)frame->data[0]; - const float *wsamples = (const float *)s->window->data[0]; - float sample = samples[frame->ch_layout.nb_channels * offset + ch]; - float wsample = wsamples[frame->ch_layout.nb_channels * s->window_offset + ch]; - float new_sum; - - new_sum = s->sum; - new_sum -= wsample; - new_sum = fmaxf(new_sum, 0.f); - new_sum += fabsf(sample); - - return new_sum / s->window_duration; -} - -static void update_peak_float(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const float *samples = (const float *)frame->data[0]; - float *wsamples = (float *)s->window->data[0]; - float sample = samples[frame->ch_layout.nb_channels * offset + ch]; - float *wsample = &wsamples[frame->ch_layout.nb_channels * s->window_offset + ch]; - - s->sum -= *wsample; - s->sum = fmaxf(s->sum, 0.f); - *wsample = fabsf(sample); - s->sum += *wsample; -} - -static double compute_rms_double(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const double *samples = (const double *)frame->data[0]; - const double *wsamples = (const double *)s->window->data[0]; - double sample = samples[frame->ch_layout.nb_channels * offset + ch]; - double wsample = wsamples[frame->ch_layout.nb_channels * s->window_offset + ch]; - double new_sum; - - new_sum = s->sum; - new_sum -= wsample; - new_sum = fmax(new_sum, 0.); - new_sum += sample * sample; - - av_assert2(new_sum >= 0.); - return sqrt(new_sum / s->window_duration); -} - -static void update_rms_double(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const double *samples = (const double *)frame->data[0]; - double *wsamples = (double *)s->window->data[0]; - double sample = samples[frame->ch_layout.nb_channels * offset + ch]; - double *wsample = &wsamples[frame->ch_layout.nb_channels * s->window_offset + ch]; - - s->sum -= *wsample; - s->sum = fmax(s->sum, 0.); - *wsample = sample * sample; - s->sum += *wsample; -} - -static double compute_rms_float(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const float *samples = (const float *)frame->data[0]; - const float *wsamples = (const float *)s->window->data[0]; - float sample = samples[frame->ch_layout.nb_channels * offset + ch]; - float wsample = wsamples[frame->ch_layout.nb_channels * s->window_offset + ch]; - float new_sum; - - new_sum = s->sum; - new_sum -= wsample; - new_sum = fmaxf(new_sum, 0.f); - new_sum += sample * sample; - - av_assert2(new_sum >= 0.f); - return sqrtf(new_sum / s->window_duration); -} - -static void update_rms_float(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const float *samples = (const float *)frame->data[0]; - float sample = samples[frame->ch_layout.nb_channels * offset + ch]; - float *wsamples = (float *)s->window->data[0]; - float *wsample = &wsamples[frame->ch_layout.nb_channels * s->window_offset + ch]; - - s->sum -= *wsample; - s->sum = fmaxf(s->sum, 0.f); - *wsample = sample * sample; - s->sum += *wsample; -} - -static double compute_peak_doublep(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const double *samples = (const double *)frame->extended_data[ch]; - const double *wsamples = (const double *)s->window->extended_data[ch]; - double sample = samples[offset]; - double wsample = wsamples[s->window_offset]; - double new_sum; - - new_sum = s->sum; - new_sum -= wsample; - new_sum = fmax(new_sum, 0.); - new_sum += fabs(sample); - - return new_sum / s->window_duration; -} - -static void update_peak_doublep(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const double *samples = (const double *)frame->extended_data[ch]; - double *wsamples = (double *)s->window->extended_data[ch]; - double sample = samples[offset]; - double *wsample = &wsamples[s->window_offset]; - - s->sum -= *wsample; - s->sum = fmax(s->sum, 0.); - *wsample = fabs(sample); - s->sum += *wsample; -} - -static double compute_peak_floatp(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const float *samples = (const float *)frame->extended_data[ch]; - const float *wsamples = (const float *)s->window->extended_data[ch]; - float sample = samples[offset]; - float wsample = wsamples[s->window_offset]; - float new_sum; - - new_sum = s->sum; - new_sum -= wsample; - new_sum = fmaxf(new_sum, 0.f); - new_sum += fabsf(sample); - - return new_sum / s->window_duration; -} - -static void update_peak_floatp(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const float *samples = (const float *)frame->extended_data[ch]; - float *wsamples = (float *)s->window->extended_data[ch]; - float sample = samples[offset]; - float *wsample = &wsamples[s->window_offset]; - - s->sum -= *wsample; - s->sum = fmaxf(s->sum, 0.f); - *wsample = fabsf(sample); - s->sum += *wsample; -} - -static double compute_rms_doublep(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const double *samples = (const double *)frame->extended_data[ch]; - const double *wsamples = (const double *)s->window->extended_data[ch]; - double sample = samples[offset]; - double wsample = wsamples[s->window_offset]; - double new_sum; - - new_sum = s->sum; - new_sum -= wsample; - new_sum = fmax(new_sum, 0.); - new_sum += sample * sample; - - av_assert2(new_sum >= 0.); - return sqrt(new_sum / s->window_duration); -} - -static void update_rms_doublep(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const double *samples = (const double *)frame->extended_data[ch]; - double *wsamples = (double *)s->window->extended_data[ch]; - double sample = samples[offset]; - double *wsample = &wsamples[s->window_offset]; - - s->sum -= *wsample; - s->sum = fmax(s->sum, 0.); - *wsample = sample * sample; - s->sum += *wsample; -} - -static double compute_rms_floatp(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const float *samples = (const float *)frame->extended_data[ch]; - const float *wsamples = (const float *)s->window->extended_data[ch]; - float sample = samples[offset]; - float wsample = wsamples[s->window_offset]; - float new_sum; - - new_sum = s->sum; - new_sum -= wsample; - new_sum = fmaxf(new_sum, 0.f); - new_sum += sample * sample; - - av_assert2(new_sum >= 0.f); - return sqrtf(new_sum / s->window_duration); -} - -static void update_rms_floatp(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const float *samples = (const float *)frame->extended_data[ch]; - float *wsamples = (float *)s->window->extended_data[ch]; - float sample = samples[offset]; - float *wsample = &wsamples[s->window_offset]; - - s->sum -= *wsample; - s->sum = fmaxf(s->sum, 0.f); - *wsample = sample * sample; - s->sum += *wsample; -} +#undef DEPTH +#define DEPTH 64 +#include "silenceremove_template.c" static av_cold int init(AVFilterContext *ctx) { @@ -420,13 +174,25 @@ static av_cold int init(AVFilterContext *ctx) return 0; } -static void clear_window(SilenceRemoveContext *s) +static void clear_windows(SilenceRemoveContext *s) { - av_samples_set_silence(s->window->extended_data, 0, s->window_duration, - s->window->ch_layout.nb_channels, s->window->format); - - s->window_offset = 0; - s->sum = 0; + av_samples_set_silence(s->start_window->extended_data, 0, + s->start_window->nb_samples, + s->start_window->ch_layout.nb_channels, + s->start_window->format); + av_samples_set_silence(s->stop_window->extended_data, 0, + s->stop_window->nb_samples, + s->stop_window->ch_layout.nb_channels, + s->stop_window->format); + + s->start_window_pos = 0; + s->start_window_size = 0; + s->stop_window_pos = 0; + s->stop_window_size = 0; + s->start_queue_pos = 0; + s->start_queue_size = 0; + s->stop_queue_pos = 0; + s->stop_queue_size = 0; } static int config_input(AVFilterLink *inlink) @@ -438,505 +204,262 @@ static int config_input(AVFilterLink *inlink) s->window_duration = av_rescale(s->window_duration_opt, inlink->sample_rate, AV_TIME_BASE); s->window_duration = FFMAX(1, s->window_duration); - s->window = ff_get_audio_buffer(ctx->outputs[0], s->window_duration); - if (!s->window) - return AVERROR(ENOMEM); - - clear_window(s); s->start_duration = av_rescale(s->start_duration_opt, inlink->sample_rate, AV_TIME_BASE); s->start_silence = av_rescale(s->start_silence_opt, inlink->sample_rate, AV_TIME_BASE); - s->stop_duration = av_rescale(s->stop_duration_opt, inlink->sample_rate, + s->stop_duration = av_rescale(s->stop_duration_opt, inlink->sample_rate, AV_TIME_BASE); - s->stop_silence = av_rescale(s->stop_silence_opt, inlink->sample_rate, + s->stop_silence = av_rescale(s->stop_silence_opt, inlink->sample_rate, AV_TIME_BASE); - s->start_holdoff = ff_get_audio_buffer(ctx->outputs[0], - FFMAX(s->start_duration, 1)); - if (!s->start_holdoff) - return AVERROR(ENOMEM); + s->start_found_periods = 0; + s->stop_found_periods = 0; - s->start_silence_hold = ff_get_audio_buffer(ctx->outputs[0], - FFMAX(s->start_silence, 1)); - if (!s->start_silence_hold) - return AVERROR(ENOMEM); + return 0; +} - s->start_holdoff_offset = 0; - s->start_holdoff_end = 0; - s->start_found_periods = 0; +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + SilenceRemoveContext *s = ctx->priv; - s->stop_holdoff = ff_get_audio_buffer(ctx->outputs[0], - FFMAX(s->stop_duration, 1)); - if (!s->stop_holdoff) + switch (s->detection) { + case D_AVG: + case D_RMS: + s->cache_size = 1; + break; + case D_DEV: + s->cache_size = 2; + break; + case D_MEDIAN: + case D_PEAK: + case D_PTP: + s->cache_size = s->window_duration; + break; + } + + s->start_window = ff_get_audio_buffer(outlink, s->window_duration); + s->stop_window = ff_get_audio_buffer(outlink, s->window_duration); + s->start_cache = av_calloc(outlink->ch_layout.nb_channels, s->cache_size * sizeof(*s->start_cache)); + s->stop_cache = av_calloc(outlink->ch_layout.nb_channels, s->cache_size * sizeof(*s->stop_cache)); + if (!s->start_window || !s->stop_window || !s->start_cache || !s->stop_cache) return AVERROR(ENOMEM); - s->stop_silence_hold = ff_get_audio_buffer(ctx->outputs[0], - FFMAX(s->stop_silence, 1)); - if (!s->stop_silence_hold) + s->start_queuef = ff_get_audio_buffer(outlink, s->start_silence + 1); + s->stop_queuef = ff_get_audio_buffer(outlink, s->stop_silence + 1); + if (!s->start_queuef || !s->stop_queuef) return AVERROR(ENOMEM); - s->stop_holdoff_offset = 0; - s->stop_holdoff_end = 0; - s->stop_found_periods = 0; + s->start_front = av_calloc(outlink->ch_layout.nb_channels, sizeof(*s->start_front)); + s->start_back = av_calloc(outlink->ch_layout.nb_channels, sizeof(*s->start_back)); + s->stop_front = av_calloc(outlink->ch_layout.nb_channels, sizeof(*s->stop_front)); + s->stop_back = av_calloc(outlink->ch_layout.nb_channels, sizeof(*s->stop_back)); + if (!s->start_front || !s->start_back || !s->stop_front || !s->stop_back) + return AVERROR(ENOMEM); - if (s->start_periods) { - s->mode = SILENCE_TRIM; - s->one_period = 1; - } else { - s->mode = SILENCE_COPY; - } + clear_windows(s); - switch (inlink->format) { - case AV_SAMPLE_FMT_DBL: - s->copy = copy_double; - switch (s->detection) { - case D_PEAK: - s->update = update_peak_double; - s->compute = compute_peak_double; - break; - case D_RMS: - s->update = update_rms_double; - s->compute = compute_rms_double; - break; - } + switch (s->detection) { + case D_AVG: + s->compute_flt = compute_avg_flt; + s->compute_dbl = compute_avg_dbl; break; - case AV_SAMPLE_FMT_FLT: - s->copy = copy_float; - switch (s->detection) { - case D_PEAK: - s->update = update_peak_float; - s->compute = compute_peak_float; - break; - case D_RMS: - s->update = update_rms_float; - s->compute = compute_rms_float; - break; - } + case D_DEV: + s->compute_flt = compute_dev_flt; + s->compute_dbl = compute_dev_dbl; break; - case AV_SAMPLE_FMT_DBLP: - s->copy = copy_doublep; - switch (s->detection) { - case D_PEAK: - s->update = update_peak_doublep; - s->compute = compute_peak_doublep; - break; - case D_RMS: - s->update = update_rms_doublep; - s->compute = compute_rms_doublep; - break; - } + case D_PTP: + s->compute_flt = compute_ptp_flt; + s->compute_dbl = compute_ptp_dbl; break; - case AV_SAMPLE_FMT_FLTP: - s->copy = copy_floatp; - switch (s->detection) { - case D_PEAK: - s->update = update_peak_floatp; - s->compute = compute_peak_floatp; - break; - case D_RMS: - s->update = update_rms_floatp; - s->compute = compute_rms_floatp; - break; - } + case D_MEDIAN: + s->compute_flt = compute_median_flt; + s->compute_dbl = compute_median_dbl; + break; + case D_PEAK: + s->compute_flt = compute_peak_flt; + s->compute_dbl = compute_peak_dbl; + break; + case D_RMS: + s->compute_flt = compute_rms_flt; + s->compute_dbl = compute_rms_dbl; break; - default: - return AVERROR_BUG; } - s->fifo = av_audio_fifo_alloc(inlink->format, inlink->ch_layout.nb_channels, 1024); - if (!s->fifo) - return AVERROR(ENOMEM); - return 0; } -static void flush(SilenceRemoveContext *s, - AVFrame *out, AVFilterLink *outlink, - int *nb_samples_written, int flush_silence) +static int filter_frame(AVFilterLink *outlink, AVFrame *in) { - AVFrame *silence; - - if (*nb_samples_written) { - out->nb_samples = *nb_samples_written; - - av_audio_fifo_write(s->fifo, (void **)out->extended_data, out->nb_samples); - *nb_samples_written = 0; - } - - av_frame_free(&out); - - if (s->stop_silence_end <= 0 || !flush_silence) - return; - - silence = ff_get_audio_buffer(outlink, s->stop_silence_end); - if (!silence) - return; - - if (s->stop_silence_offset < s->stop_silence_end) { - av_samples_copy(silence->extended_data, s->stop_silence_hold->extended_data, 0, - s->stop_silence_offset, - s->stop_silence_end - s->stop_silence_offset, - outlink->ch_layout.nb_channels, outlink->format); - } - - if (s->stop_silence_offset > 0) { - av_samples_copy(silence->extended_data, s->stop_silence_hold->extended_data, - s->stop_silence_end - s->stop_silence_offset, - 0, s->stop_silence_offset, - outlink->ch_layout.nb_channels, outlink->format); - } - - s->stop_silence_offset = 0; - s->stop_silence_end = 0; - - av_audio_fifo_write(s->fifo, (void **)silence->extended_data, silence->nb_samples); - av_frame_free(&silence); -} - -static int filter_frame(AVFilterLink *inlink, AVFrame *in) -{ - AVFilterContext *ctx = inlink->dst; - AVFilterLink *outlink = ctx->outputs[0]; + const int nb_channels = outlink->ch_layout.nb_channels; + AVFilterContext *ctx = outlink->src; SilenceRemoveContext *s = ctx->priv; - int nbs, nb_samples_read, nb_samples_written; - int i, j, threshold, ret = 0; + int max_out_nb_samples; + int out_nb_samples = 0; + int in_nb_samples; + const double *srcd; + const float *srcf; AVFrame *out; - - nb_samples_read = nb_samples_written = 0; + double *dstd; + float *dstf; if (s->next_pts == AV_NOPTS_VALUE) s->next_pts = in->pts; - switch (s->mode) { - case SILENCE_TRIM: -silence_trim: - nbs = in->nb_samples - nb_samples_read; - if (!nbs) - break; - - for (i = 0; i < nbs; i++) { - if (s->start_mode == T_ANY) { - threshold = 0; - for (j = 0; j < outlink->ch_layout.nb_channels; j++) { - threshold |= s->compute(s, in, j, nb_samples_read) > s->start_threshold; - } - } else { - threshold = 1; - for (j = 0; j < outlink->ch_layout.nb_channels; j++) { - threshold &= s->compute(s, in, j, nb_samples_read) > s->start_threshold; - } - } - - if (threshold) { - for (j = 0; j < outlink->ch_layout.nb_channels; j++) { - s->update(s, in, j, nb_samples_read); - s->copy(s, s->start_holdoff, in, j, s->start_holdoff_end, nb_samples_read); - } + in_nb_samples = in->nb_samples; + max_out_nb_samples = in->nb_samples + + s->start_silence + + s->stop_silence; + if (max_out_nb_samples <= 0) { + av_frame_free(&in); + ff_filter_set_ready(ctx, 100); + return 0; + } - s->window_offset++; - if (s->window_offset >= s->window_duration) - s->window_offset = 0; - s->start_holdoff_end++; - nb_samples_read++; - - if (s->start_holdoff_end >= s->start_duration) { - s->start_found_periods += s->one_period >= 1; - s->one_period = 0; - if (s->start_found_periods >= s->start_periods) { - s->mode = SILENCE_TRIM_FLUSH; - goto silence_trim_flush; - } - - s->start_holdoff_offset = 0; - s->start_holdoff_end = 0; - s->start_silence_offset = 0; - s->start_silence_end = 0; - } - } else { - s->start_holdoff_end = 0; - s->one_period++; - - for (j = 0; j < outlink->ch_layout.nb_channels; j++) { - s->update(s, in, j, nb_samples_read); - if (s->start_silence) - s->copy(s, s->start_silence_hold, in, j, s->start_silence_offset, nb_samples_read); - } + out = ff_get_audio_buffer(outlink, max_out_nb_samples); + if (!out) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } - s->window_offset++; - if (s->window_offset >= s->window_duration) - s->window_offset = 0; - nb_samples_read++; - s->start_silence_offset++; + if (s->timestamp_mode == TS_WRITE) + out->pts = s->next_pts; + else + out->pts = in->pts; - if (s->start_silence) { - s->start_silence_end = FFMIN(s->start_silence_end + 1, s->start_silence); - if (s->start_silence_offset >= s->start_silence) - s->start_silence_offset = 0; + switch (outlink->format) { + case AV_SAMPLE_FMT_FLT: + srcf = (const float *)in->data[0]; + dstf = (float *)out->data[0]; + if (s->start_periods > 0 && s->stop_periods > 0) { + const float *src = srcf; + if (s->start_found_periods >= 0) { + for (int n = 0; n < in_nb_samples; n++) { + filter_start_flt(ctx, src + n * nb_channels, + dstf, &out_nb_samples, + nb_channels); } + in_nb_samples = out_nb_samples; + out_nb_samples = 0; + src = dstf; } - } - break; - - case SILENCE_TRIM_FLUSH: -silence_trim_flush: - nbs = s->start_holdoff_end - s->start_holdoff_offset; - if (!nbs) - break; - - out = ff_get_audio_buffer(outlink, nbs + s->start_silence_end); - if (!out) { - av_frame_free(&in); - return AVERROR(ENOMEM); - } - - if (s->start_silence_end > 0) { - if (s->start_silence_offset < s->start_silence_end) { - av_samples_copy(out->extended_data, s->start_silence_hold->extended_data, 0, - s->start_silence_offset, - s->start_silence_end - s->start_silence_offset, - outlink->ch_layout.nb_channels, outlink->format); + for (int n = 0; n < in_nb_samples; n++) { + filter_stop_flt(ctx, src + n * nb_channels, + dstf, &out_nb_samples, + nb_channels); } - - if (s->start_silence_offset > 0) { - av_samples_copy(out->extended_data, s->start_silence_hold->extended_data, - s->start_silence_end - s->start_silence_offset, - 0, s->start_silence_offset, - outlink->ch_layout.nb_channels, outlink->format); + } else if (s->start_periods > 0) { + for (int n = 0; n < in_nb_samples; n++) { + filter_start_flt(ctx, srcf + n * nb_channels, + dstf, &out_nb_samples, + nb_channels); + } + } else if (s->stop_periods > 0) { + for (int n = 0; n < in_nb_samples; n++) { + filter_stop_flt(ctx, srcf + n * nb_channels, + dstf, &out_nb_samples, + nb_channels); } - } - - av_samples_copy(out->extended_data, s->start_holdoff->extended_data, - s->start_silence_end, - s->start_holdoff_offset, nbs, - outlink->ch_layout.nb_channels, outlink->format); - - s->start_holdoff_offset += nbs; - - av_audio_fifo_write(s->fifo, (void **)out->extended_data, out->nb_samples); - av_frame_free(&out); - - if (s->start_holdoff_offset == s->start_holdoff_end) { - s->start_holdoff_offset = 0; - s->start_holdoff_end = 0; - s->start_silence_offset = 0; - s->start_silence_end = 0; - s->mode = SILENCE_COPY; - goto silence_copy; } break; - - case SILENCE_COPY: -silence_copy: - nbs = in->nb_samples - nb_samples_read; - if (!nbs) - break; - - out = ff_get_audio_buffer(outlink, nbs); - if (!out) { - av_frame_free(&in); - return AVERROR(ENOMEM); - } - - if (s->stop_periods) { - for (i = 0; i < nbs; i++) { - if (s->stop_mode == T_ANY) { - threshold = 0; - for (j = 0; j < outlink->ch_layout.nb_channels; j++) { - threshold |= s->compute(s, in, j, nb_samples_read) > s->stop_threshold; - } - } else { - threshold = 1; - for (j = 0; j < outlink->ch_layout.nb_channels; j++) { - threshold &= s->compute(s, in, j, nb_samples_read) > s->stop_threshold; - } - } - - if (threshold && s->stop_holdoff_end && !s->stop_silence) { - s->mode = SILENCE_COPY_FLUSH; - flush(s, out, outlink, &nb_samples_written, 0); - s->one_period++; - goto silence_copy_flush; - } else if (threshold) { - for (j = 0; j < outlink->ch_layout.nb_channels; j++) { - s->update(s, in, j, nb_samples_read); - s->copy(s, out, in, j, nb_samples_written, nb_samples_read); - } - - s->window_offset++; - if (s->window_offset >= s->window_duration) - s->window_offset = 0; - nb_samples_read++; - nb_samples_written++; - s->one_period++; - } else if (!threshold) { - for (j = 0; j < outlink->ch_layout.nb_channels; j++) { - s->update(s, in, j, nb_samples_read); - if (s->stop_silence) - s->copy(s, s->stop_silence_hold, in, j, s->stop_silence_offset, nb_samples_read); - - s->copy(s, s->stop_holdoff, in, j, s->stop_holdoff_end, nb_samples_read); - } - - if (s->stop_silence) { - s->stop_silence_offset++; - s->stop_silence_end = FFMIN(s->stop_silence_end + 1, s->stop_silence); - if (s->stop_silence_offset >= s->stop_silence) { - s->stop_silence_offset = 0; - } - } - - s->window_offset++; - if (s->window_offset >= s->window_duration) - s->window_offset = 0; - nb_samples_read++; - s->stop_holdoff_end++; - - if (s->stop_holdoff_end >= s->stop_duration) { - s->stop_found_periods += s->one_period >= 1; - s->one_period = 0; - if (s->stop_found_periods >= s->stop_periods) { - s->stop_holdoff_offset = 0; - s->stop_holdoff_end = 0; - - if (!s->restart) { - s->mode = SILENCE_STOP; - flush(s, out, outlink, &nb_samples_written, 1); - goto silence_stop; - } else { - s->stop_found_periods = 0; - s->start_found_periods = 0; - s->start_holdoff_offset = 0; - s->start_holdoff_end = 0; - s->start_silence_offset = 0; - s->start_silence_end = 0; - clear_window(s); - s->mode = SILENCE_TRIM; - flush(s, out, outlink, &nb_samples_written, 1); - goto silence_trim; - } - } - s->mode = SILENCE_COPY_FLUSH; - flush(s, out, outlink, &nb_samples_written, 0); - goto silence_copy_flush; - } + case AV_SAMPLE_FMT_DBL: + srcd = (const double *)in->data[0]; + dstd = (double *)out->data[0]; + if (s->start_periods > 0 && s->stop_periods > 0) { + const double *src = srcd; + if (s->start_found_periods >= 0) { + for (int n = 0; n < in_nb_samples; n++) { + filter_start_dbl(ctx, src + n * nb_channels, + dstd, &out_nb_samples, + nb_channels); } + in_nb_samples = out_nb_samples; + out_nb_samples = 0; + src = dstd; + } + for (int n = 0; n < in_nb_samples; n++) { + filter_stop_dbl(ctx, src + n * nb_channels, + dstd, &out_nb_samples, + nb_channels); + } + } else if (s->start_periods > 0) { + for (int n = 0; n < in_nb_samples; n++) { + filter_start_dbl(ctx, srcd + n * nb_channels, + dstd, &out_nb_samples, + nb_channels); + } + } else if (s->stop_periods > 0) { + for (int n = 0; n < in_nb_samples; n++) { + filter_stop_dbl(ctx, srcd + n * nb_channels, + dstd, &out_nb_samples, + nb_channels); } - s->one_period++; - flush(s, out, outlink, &nb_samples_written, 0); - } else { - av_samples_copy(out->extended_data, in->extended_data, - nb_samples_written, - nb_samples_read, nbs, - outlink->ch_layout.nb_channels, outlink->format); - - av_audio_fifo_write(s->fifo, (void **)out->extended_data, out->nb_samples); - av_frame_free(&out); - } - break; - - case SILENCE_COPY_FLUSH: -silence_copy_flush: - nbs = s->stop_holdoff_end - s->stop_holdoff_offset; - if (!nbs) - break; - - out = ff_get_audio_buffer(outlink, nbs); - if (!out) { - av_frame_free(&in); - return AVERROR(ENOMEM); - } - - av_samples_copy(out->extended_data, s->stop_holdoff->extended_data, 0, - s->stop_holdoff_offset, nbs, - outlink->ch_layout.nb_channels, outlink->format); - - s->stop_holdoff_offset += nbs; - - av_audio_fifo_write(s->fifo, (void **)out->extended_data, out->nb_samples); - av_frame_free(&out); - - if (s->stop_holdoff_offset == s->stop_holdoff_end) { - s->stop_holdoff_offset = 0; - s->stop_holdoff_end = 0; - s->stop_silence_offset = 0; - s->stop_silence_end = 0; - s->mode = SILENCE_COPY; - goto silence_copy; } break; - case SILENCE_STOP: -silence_stop: - break; - default: - ret = AVERROR_BUG; } av_frame_free(&in); - - if (av_audio_fifo_size(s->fifo) > 0) { - out = ff_get_audio_buffer(outlink, av_audio_fifo_size(s->fifo)); - if (!out) - return AVERROR(ENOMEM); - - av_audio_fifo_read(s->fifo, (void **)out->extended_data, out->nb_samples); - out->pts = s->next_pts; - s->next_pts += av_rescale_q(out->nb_samples, - (AVRational){1, outlink->sample_rate}, - outlink->time_base); - - ret = ff_filter_frame(outlink, out); + if (out_nb_samples > 0) { + s->next_pts += out_nb_samples; + out->nb_samples = out_nb_samples; + return ff_filter_frame(outlink, out); } - return ret; + av_frame_free(&out); + ff_filter_set_ready(ctx, 100); + + return 0; } -static int request_frame(AVFilterLink *outlink) +static int activate(AVFilterContext *ctx) { - AVFilterContext *ctx = outlink->src; + AVFilterLink *outlink = ctx->outputs[0]; + AVFilterLink *inlink = ctx->inputs[0]; SilenceRemoveContext *s = ctx->priv; + AVFrame *in; int ret; - ret = ff_request_frame(ctx->inputs[0]); - if (ret == AVERROR_EOF && (s->mode == SILENCE_COPY_FLUSH || - s->mode == SILENCE_COPY)) { - int nbs = s->stop_holdoff_end - s->stop_holdoff_offset; - if (nbs) { - AVFrame *frame; - - frame = ff_get_audio_buffer(outlink, nbs); - if (!frame) - return AVERROR(ENOMEM); - - av_samples_copy(frame->extended_data, s->stop_holdoff->extended_data, 0, - s->stop_holdoff_offset, nbs, - outlink->ch_layout.nb_channels, outlink->format); - - frame->pts = s->next_pts; - s->next_pts += av_rescale_q(frame->nb_samples, - (AVRational){1, outlink->sample_rate}, - outlink->time_base); - - ret = ff_filter_frame(outlink, frame); + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + + ret = ff_inlink_consume_frame(inlink, &in); + if (ret < 0) + return ret; + if (ret > 0) { + if (s->start_periods == 1 && s->stop_periods == 0 && + s->start_found_periods < 0) { + if (s->timestamp_mode == TS_WRITE) + in->pts = s->next_pts; + s->next_pts += in->nb_samples; + return ff_filter_frame(outlink, in); } - s->mode = SILENCE_STOP; + if (s->start_periods == 0 && s->stop_periods == 0) + return ff_filter_frame(outlink, in); + return filter_frame(outlink, in); } - return ret; + + FF_FILTER_FORWARD_STATUS(inlink, outlink); + FF_FILTER_FORWARD_WANTED(outlink, inlink); + + return FFERROR_NOT_READY; } static av_cold void uninit(AVFilterContext *ctx) { SilenceRemoveContext *s = ctx->priv; - av_frame_free(&s->start_holdoff); - av_frame_free(&s->start_silence_hold); - av_frame_free(&s->stop_holdoff); - av_frame_free(&s->stop_silence_hold); - av_frame_free(&s->window); - - av_audio_fifo_free(s->fifo); - s->fifo = NULL; + av_frame_free(&s->start_window); + av_frame_free(&s->stop_window); + av_frame_free(&s->start_queuef); + av_frame_free(&s->stop_queuef); + + av_freep(&s->start_cache); + av_freep(&s->stop_cache); + av_freep(&s->start_front); + av_freep(&s->start_back); + av_freep(&s->stop_front); + av_freep(&s->stop_back); } static const AVFilterPad silenceremove_inputs[] = { @@ -944,15 +467,14 @@ static const AVFilterPad silenceremove_inputs[] = { .name = "default", .type = AVMEDIA_TYPE_AUDIO, .config_props = config_input, - .filter_frame = filter_frame, }, }; static const AVFilterPad silenceremove_outputs[] = { { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - .request_frame = request_frame, + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .config_props = config_output, }, }; @@ -962,9 +484,12 @@ const AVFilter ff_af_silenceremove = { .priv_size = sizeof(SilenceRemoveContext), .priv_class = &silenceremove_class, .init = init, + .activate = activate, .uninit = uninit, FILTER_INPUTS(silenceremove_inputs), FILTER_OUTPUTS(silenceremove_outputs), - FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP, - AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBLP), + FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLT, + AV_SAMPLE_FMT_DBL), + .process_command = ff_filter_process_command, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, }; diff --git a/libavfilter/af_sofalizer.c b/libavfilter/af_sofalizer.c index be947314f3a..60e0da30e99 100644 --- a/libavfilter/af_sofalizer.c +++ b/libavfilter/af_sofalizer.c @@ -36,6 +36,7 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" #include "audio.h" @@ -1086,13 +1087,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_sofalizer = { .name = "sofalizer", .description = NULL_IF_CONFIG_SMALL("SOFAlizer (Spatially Oriented Format for Acoustics)."), @@ -1102,7 +1096,7 @@ const AVFilter ff_af_sofalizer = { .activate = activate, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SLICE_THREADS, }; diff --git a/libavfilter/af_speechnorm.c b/libavfilter/af_speechnorm.c index 4f56ad9a8f0..a8bed2cb8f4 100644 --- a/libavfilter/af_speechnorm.c +++ b/libavfilter/af_speechnorm.c @@ -590,13 +590,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_speechnorm = { .name = "speechnorm", .description = NULL_IF_CONFIG_SMALL("Speech Normalizer."), @@ -605,7 +598,7 @@ const AVFilter ff_af_speechnorm = { .activate = activate, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, .process_command = process_command, diff --git a/libavfilter/af_stereotools.c b/libavfilter/af_stereotools.c index eedc7c68bd0..4bcd696cf90 100644 --- a/libavfilter/af_stereotools.c +++ b/libavfilter/af_stereotools.c @@ -365,13 +365,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_stereotools = { .name = "stereotools", .description = NULL_IF_CONFIG_SMALL("Apply various stereo tools."), @@ -379,7 +372,7 @@ const AVFilter ff_af_stereotools = { .priv_class = &stereotools_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, diff --git a/libavfilter/af_stereowiden.c b/libavfilter/af_stereowiden.c index a91ea039577..af4b23e8a57 100644 --- a/libavfilter/af_stereowiden.c +++ b/libavfilter/af_stereowiden.c @@ -146,13 +146,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_stereowiden = { .name = "stereowiden", .description = NULL_IF_CONFIG_SMALL("Apply stereo widening effect."), @@ -160,7 +153,7 @@ const AVFilter ff_af_stereowiden = { .priv_class = &stereowiden_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, .process_command = ff_filter_process_command, diff --git a/libavfilter/af_surround.c b/libavfilter/af_surround.c index 702fdc624a8..64abf1fdedf 100644 --- a/libavfilter/af_surround.c +++ b/libavfilter/af_surround.c @@ -74,6 +74,7 @@ typedef struct AudioSurroundContext { float focus; int win_size; int win_func; + float win_gain; float overlap; float all_x; @@ -330,16 +331,16 @@ static void angle_transform(float *x, float *y, float angle) if (angle == 90.f) return; - reference = angle * M_PI / 180.f; + reference = angle * M_PIf / 180.f; r = hypotf(*x, *y); a = atan2f(*x, *y); r /= r_distance(a); - if (fabsf(a) <= M_PI_4) - a *= reference / M_PI_2; + if (fabsf(a) <= M_PI_4f) + a *= reference / M_PI_2f; else - a = M_PI + (-2.f * M_PI + reference) * (M_PI - fabsf(a)) * FFDIFFSIGN(a, 0.f) / (3.f * M_PI_2); + a = M_PIf + (-2.f * M_PIf + reference) * (M_PIf - fabsf(a)) * FFDIFFSIGN(a, 0.f) / (3.f * M_PI_2f); r *= r_distance(a); @@ -366,16 +367,16 @@ static void focus_transform(float *x, float *y, float focus) static void stereo_position(float a, float p, float *x, float *y) { av_assert2(a >= -1.f && a <= 1.f); - av_assert2(p >= 0.f && p <= M_PI); - *x = av_clipf(a+a*fmaxf(0.f, p*p-M_PI_2), -1.f, 1.f); - *y = av_clipf(cosf(a*M_PI_2+M_PI)*cosf(M_PI_2-p/M_PI)*M_LN10+1.f, -1.f, 1.f); + av_assert2(p >= 0.f && p <= M_PIf); + *x = av_clipf(a+a*fmaxf(0.f, p*p-M_PI_2f), -1.f, 1.f); + *y = av_clipf(cosf(a*M_PI_2f+M_PIf)*cosf(M_PI_2f-p/M_PIf)*M_LN10f+1.f, -1.f, 1.f); } static inline void get_lfe(int output_lfe, int n, float lowcut, float highcut, float *lfe_mag, float c_mag, float *mag_total, int lfe_mode) { if (output_lfe && n < highcut) { - *lfe_mag = n < lowcut ? 1.f : .5f*(1.f+cosf(M_PI*(lowcut-n)/(lowcut-highcut))); + *lfe_mag = n < lowcut ? 1.f : .5f*(1.f+cosf(M_PIf*(lowcut-n)/(lowcut-highcut))); *lfe_mag *= c_mag; if (lfe_mode) *mag_total -= *lfe_mag; @@ -413,7 +414,7 @@ static void calculate_factors(AVFilterContext *ctx, int ch, int chan) break; case AV_CHAN_LOW_FREQUENCY: for (int n = 0; n < rdft_size; n++) - factor[n] = powf(1.f - fabsf(x[n]), f_x) * powf((1.f - fabs(y[n])), f_y); + factor[n] = powf(1.f - fabsf(x[n]), f_x) * powf((1.f - fabsf(y[n])), f_y); break; case AV_CHAN_BACK_CENTER: for (int n = 0; n < rdft_size; n++) @@ -637,7 +638,7 @@ static void upmix_7_1_5_0_side(AVFilterContext *ctx, { float fl_mag, fr_mag, ls_mag, rs_mag, lb_mag, rb_mag; float *dstc, *dstl, *dstr, *dstls, *dstrs, *dstlb, *dstrb, *dstlfe; - float lfe_mag, c_phase, mag_total = (mag_totall + mag_totalr) * 0.5; + float lfe_mag, c_phase, mag_total = (mag_totall + mag_totalr) * 0.5f; AudioSurroundContext *s = ctx->priv; dstl = (float *)s->output->extended_data[0]; @@ -747,6 +748,7 @@ static void filter_stereo(AVFilterContext *ctx) const float *srcl = (const float *)s->input->extended_data[0]; const float *srcr = (const float *)s->input->extended_data[1]; const int output_lfe = s->output_lfe && s->create_lfe; + const int rdft_size = s->rdft_size; const int lfe_mode = s->lfe_mode; const float highcut = s->highcut; const float lowcut = s->lowcut; @@ -761,7 +763,7 @@ static void filter_stereo(AVFilterContext *ctx) float *xpos = s->x_pos; float *ypos = s->y_pos; - for (int n = 0; n < s->rdft_size; n++) { + for (int n = 0; n < rdft_size; n++) { float l_re = srcl[2 * n], r_re = srcr[2 * n]; float l_im = srcl[2 * n + 1], r_im = srcr[2 * n + 1]; float c_phase = atan2f(l_im + r_im, l_re + r_re); @@ -777,8 +779,8 @@ static void filter_stereo(AVFilterContext *ctx) mag_sum = mag_sum < MIN_MAG_SUM ? 1.f : mag_sum; mag_dif = (l_mag - r_mag) / mag_sum; - if (phase_dif > M_PI) - phase_dif = 2.f * M_PI - phase_dif; + if (phase_dif > M_PIf) + phase_dif = 2.f * M_PIf - phase_dif; stereo_position(mag_dif, phase_dif, &x, &y); angle_transform(&x, &y, angle); @@ -801,6 +803,7 @@ static void filter_2_1(AVFilterContext *ctx) const float *srcl = (const float *)s->input->extended_data[0]; const float *srcr = (const float *)s->input->extended_data[1]; const float *srclfe = (const float *)s->input->extended_data[2]; + const int rdft_size = s->rdft_size; const float angle = s->angle; const float focus = s->focus; float *magtotal = s->mag_total; @@ -813,7 +816,7 @@ static void filter_2_1(AVFilterContext *ctx) float *xpos = s->x_pos; float *ypos = s->y_pos; - for (int n = 0; n < s->rdft_size; n++) { + for (int n = 0; n < rdft_size; n++) { float l_re = srcl[2 * n], r_re = srcr[2 * n]; float l_im = srcl[2 * n + 1], r_im = srcr[2 * n + 1]; float lfe_re = srclfe[2 * n], lfe_im = srclfe[2 * n + 1]; @@ -832,8 +835,8 @@ static void filter_2_1(AVFilterContext *ctx) mag_sum = mag_sum < MIN_MAG_SUM ? 1.f : mag_sum; mag_dif = (l_mag - r_mag) / mag_sum; - if (phase_dif > M_PI) - phase_dif = 2.f * M_PI - phase_dif; + if (phase_dif > M_PIf) + phase_dif = 2.f * M_PIf - phase_dif; stereo_position(mag_dif, phase_dif, &x, &y); angle_transform(&x, &y, angle); @@ -858,6 +861,7 @@ static void filter_surround(AVFilterContext *ctx) const float *srcr = (const float *)s->input->extended_data[1]; const float *srcc = (const float *)s->input->extended_data[2]; const int output_lfe = s->output_lfe && s->create_lfe; + const int rdft_size = s->rdft_size; const int lfe_mode = s->lfe_mode; const float highcut = s->highcut; const float lowcut = s->lowcut; @@ -872,7 +876,7 @@ static void filter_surround(AVFilterContext *ctx) float *xpos = s->x_pos; float *ypos = s->y_pos; - for (int n = 0; n < s->rdft_size; n++) { + for (int n = 0; n < rdft_size; n++) { float l_re = srcl[2 * n], r_re = srcr[2 * n]; float l_im = srcl[2 * n + 1], r_im = srcr[2 * n + 1]; float c_re = srcc[2 * n], c_im = srcc[2 * n + 1]; @@ -889,8 +893,8 @@ static void filter_surround(AVFilterContext *ctx) mag_sum = mag_sum < MIN_MAG_SUM ? 1.f : mag_sum; mag_dif = (l_mag - r_mag) / mag_sum; - if (phase_dif > M_PI) - phase_dif = 2.f * M_PI - phase_dif; + if (phase_dif > M_PIf) + phase_dif = 2.f * M_PIf - phase_dif; stereo_position(mag_dif, phase_dif, &x, &y); angle_transform(&x, &y, angle); @@ -910,6 +914,7 @@ static void filter_surround(AVFilterContext *ctx) static void filter_5_0_side(AVFilterContext *ctx) { AudioSurroundContext *s = ctx->priv; + const int rdft_size = s->rdft_size; float *srcl, *srcr, *srcc, *srcsl, *srcsr; int n; @@ -919,7 +924,7 @@ static void filter_5_0_side(AVFilterContext *ctx) srcsl = (float *)s->input->extended_data[3]; srcsr = (float *)s->input->extended_data[4]; - for (n = 0; n < s->rdft_size; n++) { + for (n = 0; n < rdft_size; n++) { float fl_re = srcl[2 * n], fr_re = srcr[2 * n]; float fl_im = srcl[2 * n + 1], fr_im = srcr[2 * n + 1]; float c_re = srcc[2 * n], c_im = srcc[2 * n + 1]; @@ -946,11 +951,11 @@ static void filter_5_0_side(AVFilterContext *ctx) float xl, yl; float xr, yr; - if (phase_difl > M_PI) - phase_difl = 2.f * M_PI - phase_difl; + if (phase_difl > M_PIf) + phase_difl = 2.f * M_PIf - phase_difl; - if (phase_difr > M_PI) - phase_difr = 2.f * M_PI - phase_difr; + if (phase_difr > M_PIf) + phase_difr = 2.f * M_PIf - phase_difr; stereo_position(mag_difl, phase_difl, &xl, &yl); stereo_position(mag_difr, phase_difr, &xr, &yr); @@ -967,6 +972,7 @@ static void filter_5_0_side(AVFilterContext *ctx) static void filter_5_1_side(AVFilterContext *ctx) { AudioSurroundContext *s = ctx->priv; + const int rdft_size = s->rdft_size; float *srcl, *srcr, *srcc, *srclfe, *srcsl, *srcsr; int n; @@ -977,7 +983,7 @@ static void filter_5_1_side(AVFilterContext *ctx) srcsl = (float *)s->input->extended_data[4]; srcsr = (float *)s->input->extended_data[5]; - for (n = 0; n < s->rdft_size; n++) { + for (n = 0; n < rdft_size; n++) { float fl_re = srcl[2 * n], fr_re = srcr[2 * n]; float fl_im = srcl[2 * n + 1], fr_im = srcr[2 * n + 1]; float c_re = srcc[2 * n], c_im = srcc[2 * n + 1]; @@ -1005,11 +1011,11 @@ static void filter_5_1_side(AVFilterContext *ctx) float xl, yl; float xr, yr; - if (phase_difl > M_PI) - phase_difl = 2.f * M_PI - phase_difl; + if (phase_difl > M_PIf) + phase_difl = 2.f * M_PIf - phase_difl; - if (phase_difr > M_PI) - phase_difr = 2.f * M_PI - phase_difr; + if (phase_difr > M_PIf) + phase_difr = 2.f * M_PIf - phase_difr; stereo_position(mag_difl, phase_difl, &xl, &yl); stereo_position(mag_difr, phase_difr, &xr, &yr); @@ -1026,6 +1032,7 @@ static void filter_5_1_side(AVFilterContext *ctx) static void filter_5_1_back(AVFilterContext *ctx) { AudioSurroundContext *s = ctx->priv; + const int rdft_size = s->rdft_size; float *srcl, *srcr, *srcc, *srclfe, *srcbl, *srcbr; int n; @@ -1036,7 +1043,7 @@ static void filter_5_1_back(AVFilterContext *ctx) srcbl = (float *)s->input->extended_data[4]; srcbr = (float *)s->input->extended_data[5]; - for (n = 0; n < s->rdft_size; n++) { + for (n = 0; n < rdft_size; n++) { float fl_re = srcl[2 * n], fr_re = srcr[2 * n]; float fl_im = srcl[2 * n + 1], fr_im = srcr[2 * n + 1]; float c_re = srcc[2 * n], c_im = srcc[2 * n + 1]; @@ -1064,11 +1071,11 @@ static void filter_5_1_back(AVFilterContext *ctx) float xl, yl; float xr, yr; - if (phase_difl > M_PI) - phase_difl = 2.f * M_PI - phase_difl; + if (phase_difl > M_PIf) + phase_difl = 2.f * M_PIf - phase_difl; - if (phase_difr > M_PI) - phase_difr = 2.f * M_PI - phase_difr; + if (phase_difr > M_PIf) + phase_difr = 2.f * M_PIf - phase_difr; stereo_position(mag_difl, phase_difl, &xl, &yl); stereo_position(mag_difr, phase_difr, &xr, &yr); @@ -1191,6 +1198,23 @@ static av_cold int init(AVFilterContext *ctx) s->window_func_lut[i] = sqrtf(s->window_func_lut[i] / s->win_size); s->hop_size = FFMAX(1, s->win_size * (1. - s->overlap)); + { + float max = 0.f, *temp_lut = av_calloc(s->win_size, sizeof(*temp_lut)); + if (!temp_lut) + return AVERROR(ENOMEM); + + for (int j = 0; j < s->win_size; j += s->hop_size) { + for (int i = 0; i < s->win_size; i++) + temp_lut[(i + j) % s->win_size] += s->window_func_lut[i]; + } + + for (int i = 0; i < s->win_size; i++) + max = fmaxf(temp_lut[i], max); + av_freep(&temp_lut); + + s->win_gain = 1.f / (max * sqrtf(s->win_size)); + } + allchannels_spread(ctx); return 0; @@ -1231,7 +1255,7 @@ static int fft_channels(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) static int ifft_channel(AVFilterContext *ctx, AVFrame *out, int ch) { AudioSurroundContext *s = ctx->priv; - const float level_out = s->output_levels[ch]; + const float level_out = s->output_levels[ch] * s->win_gain; float *dst, *ptr; dst = (float *)s->output_out->extended_data[ch]; @@ -1291,7 +1315,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) FFMIN(outlink->ch_layout.nb_channels, ff_filter_get_nb_threads(ctx))); - out->pts = in->pts; + av_frame_copy_props(out, in); out->nb_samples = in->nb_samples; av_frame_free(&in); diff --git a/libavfilter/af_tremolo.c b/libavfilter/af_tremolo.c index 3e3e2be6f8a..024c402b794 100644 --- a/libavfilter/af_tremolo.c +++ b/libavfilter/af_tremolo.c @@ -121,13 +121,6 @@ static const AVFilterPad avfilter_af_tremolo_inputs[] = { }, }; -static const AVFilterPad avfilter_af_tremolo_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_tremolo = { .name = "tremolo", .description = NULL_IF_CONFIG_SMALL("Apply tremolo effect."), @@ -135,7 +128,7 @@ const AVFilter ff_af_tremolo = { .priv_class = &tremolo_class, .uninit = uninit, FILTER_INPUTS(avfilter_af_tremolo_inputs), - FILTER_OUTPUTS(avfilter_af_tremolo_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBL), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/af_vibrato.c b/libavfilter/af_vibrato.c index 807d2b59965..e54ae2ad4b4 100644 --- a/libavfilter/af_vibrato.c +++ b/libavfilter/af_vibrato.c @@ -54,9 +54,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) { AVFilterContext *ctx = inlink->dst; VibratoContext *s = ctx->priv; + const int wave_table_size = s->wave_table_size; + const double *wave_table = s->wave_table; AVFilterLink *outlink = ctx->outputs[0]; + const int channels = s->channels; + const int buf_size = s->buf_size; + const double depth = s->depth; + int wave_table_index = s->wave_table_index; + int buf_index = s->buf_index; AVFrame *out; - int n, c; const double *src; double *dst; @@ -71,39 +77,40 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) av_frame_copy_props(out, in); } - for (n = 0; n < in->nb_samples; n++) { + for (int n = 0; n < in->nb_samples; n++) { + int samp1_index, samp2_index; double integer, decimal; - decimal = modf(s->depth * s->wave_table[s->wave_table_index], &integer); + decimal = modf(depth * wave_table[wave_table_index], &integer); - s->wave_table_index++; - if (s->wave_table_index >= s->wave_table_size) - s->wave_table_index -= s->wave_table_size; + wave_table_index++; + if (wave_table_index >= wave_table_size) + wave_table_index -= wave_table_size; - for (c = 0; c < inlink->ch_layout.nb_channels; c++) { - int samp1_index, samp2_index; - double *buf; - double this_samp; + samp1_index = buf_index + integer; + if (samp1_index >= buf_size) + samp1_index -= buf_size; + samp2_index = samp1_index + 1; + if (samp2_index >= buf_size) + samp2_index -= buf_size; + + for (int c = 0; c < channels; c++) { + double *buf, this_samp; src = (const double *)in->extended_data[c]; dst = (double *)out->extended_data[c]; buf = s->buf[c]; - samp1_index = s->buf_index + integer; - if (samp1_index >= s->buf_size) - samp1_index -= s->buf_size; - samp2_index = samp1_index + 1; - if (samp2_index >= s->buf_size) - samp2_index -= s->buf_size; - this_samp = src[n]; dst[n] = buf[samp1_index] + (decimal * (buf[samp2_index] - buf[samp1_index])); - buf[s->buf_index] = this_samp; + buf[buf_index] = this_samp; } - s->buf_index++; - if (s->buf_index >= s->buf_size) - s->buf_index -= s->buf_size; + buf_index++; + if (buf_index >= buf_size) + buf_index -= buf_size; } + s->wave_table_index = wave_table_index; + s->buf_index = buf_index; if (in != out) av_frame_free(&in); @@ -158,13 +165,6 @@ static const AVFilterPad avfilter_af_vibrato_inputs[] = { }, }; -static const AVFilterPad avfilter_af_vibrato_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_vibrato = { .name = "vibrato", .description = NULL_IF_CONFIG_SMALL("Apply vibrato effect."), @@ -172,7 +172,7 @@ const AVFilter ff_af_vibrato = { .priv_class = &vibrato_class, .uninit = uninit, FILTER_INPUTS(avfilter_af_vibrato_inputs), - FILTER_OUTPUTS(avfilter_af_vibrato_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/af_virtualbass.c b/libavfilter/af_virtualbass.c index 950159096be..9b9967c4191 100644 --- a/libavfilter/af_virtualbass.c +++ b/libavfilter/af_virtualbass.c @@ -23,6 +23,7 @@ #include "audio.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" #include @@ -146,10 +147,10 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) av_frame_free(&in); return AVERROR(ENOMEM); } + av_frame_copy_props(out, in); vb_stereo(ctx, out, in); - out->pts = in->pts; av_frame_free(&in); return ff_filter_frame(outlink, out); } @@ -163,20 +164,13 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_virtualbass = { .name = "virtualbass", .description = NULL_IF_CONFIG_SMALL("Audio Virtual Bass."), .priv_size = sizeof(AudioVirtualBassContext), .priv_class = &virtualbass_class, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, .process_command = ff_filter_process_command, diff --git a/libavfilter/af_volume.c b/libavfilter/af_volume.c index f6e183df8d3..926529947cd 100644 --- a/libavfilter/af_volume.c +++ b/libavfilter/af_volume.c @@ -48,7 +48,9 @@ static const char *const var_names[] = { "nb_channels", ///< number of channels "nb_consumed_samples", ///< number of samples consumed by the filter "nb_samples", ///< number of samples in the current frame +#if FF_API_FRAME_PKT "pos", ///< position in the file of the frame +#endif "pts", ///< frame presentation timestamp "sample_rate", ///< sample rate "startpts", ///< PTS at start of stream @@ -288,7 +290,9 @@ static int config_output(AVFilterLink *outlink) vol->var_values[VAR_N] = vol->var_values[VAR_NB_CONSUMED_SAMPLES] = vol->var_values[VAR_NB_SAMPLES] = +#if FF_API_FRAME_PKT vol->var_values[VAR_POS] = +#endif vol->var_values[VAR_PTS] = vol->var_values[VAR_STARTPTS] = vol->var_values[VAR_STARTT] = @@ -330,7 +334,6 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf) AVFilterLink *outlink = inlink->dst->outputs[0]; int nb_samples = buf->nb_samples; AVFrame *out_buf; - int64_t pos; AVFrameSideData *sd = av_frame_get_side_data(buf, AV_FRAME_DATA_REPLAYGAIN); int ret; @@ -380,8 +383,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf) vol->var_values[VAR_T ] = TS2T(buf->pts, inlink->time_base); vol->var_values[VAR_N ] = inlink->frame_count_out; - pos = buf->pkt_pos; - vol->var_values[VAR_POS] = pos == -1 ? NAN : pos; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS + { + int64_t pos; + pos = buf->pkt_pos; + vol->var_values[VAR_POS] = pos == -1 ? NAN : pos; + } +FF_ENABLE_DEPRECATION_WARNINGS +#endif if (vol->eval_mode == EVAL_MODE_FRAME) set_volume(ctx); diff --git a/libavfilter/af_volumedetect.c b/libavfilter/af_volumedetect.c index ebfad6914f6..8b001d1cf22 100644 --- a/libavfilter/af_volumedetect.c +++ b/libavfilter/af_volumedetect.c @@ -122,13 +122,6 @@ static const AVFilterPad volumedetect_inputs[] = { }, }; -static const AVFilterPad volumedetect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_volumedetect = { .name = "volumedetect", .description = NULL_IF_CONFIG_SMALL("Detect audio volume."), @@ -136,6 +129,6 @@ const AVFilter ff_af_volumedetect = { .uninit = uninit, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(volumedetect_inputs), - FILTER_OUTPUTS(volumedetect_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S16P), }; diff --git a/libavfilter/afir_template.c b/libavfilter/afir_template.c index 3f3778c6756..099fda25209 100644 --- a/libavfilter/afir_template.c +++ b/libavfilter/afir_template.c @@ -20,7 +20,6 @@ #include "libavutil/tx.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "audio.h" @@ -251,7 +250,7 @@ static void fn(convert_channel)(AVFilterContext *ctx, AudioFIRContext *s, int ch ftype *time = (ftype *)s->norm_ir[selir]->extended_data[ch]; ftype *tempin = (ftype *)seg->tempin->extended_data[ch]; ftype *tempout = (ftype *)seg->tempout->extended_data[ch]; - ctype *coeff = (ctype *)seg->coeff[selir]->extended_data[ch]; + ctype *coeff = (ctype *)seg->coeff->extended_data[ch]; const int remaining = nb_taps - (seg->input_offset + coeff_partition * seg->part_size); const int size = remaining >= seg->part_size ? seg->part_size : remaining; @@ -285,19 +284,19 @@ static void fn(fir_fadd)(AudioFIRContext *s, ftype *dst, const ftype *src, int n } } -static int fn(fir_quantum)(AVFilterContext *ctx, AVFrame *out, int ch, int offset) +static int fn(fir_quantum)(AVFilterContext *ctx, AVFrame *out, int ch, int ioffset, int offset, int selir) { AudioFIRContext *s = ctx->priv; - const ftype *in = (const ftype *)s->in->extended_data[ch] + offset; + const ftype *in = (const ftype *)s->in->extended_data[ch] + ioffset; ftype *blockout, *ptr = (ftype *)out->extended_data[ch] + offset; const int min_part_size = s->min_part_size; const int nb_samples = FFMIN(min_part_size, out->nb_samples - offset); - const int nb_segments = s->nb_segments; + const int nb_segments = s->nb_segments[selir]; const float dry_gain = s->dry_gain; - const int selir = s->selir; + const float wet_gain = s->wet_gain; for (int segment = 0; segment < nb_segments; segment++) { - AudioFIRSegment *seg = &s->seg[segment]; + AudioFIRSegment *seg = &s->seg[selir][segment]; ftype *src = (ftype *)seg->input->extended_data[ch]; ftype *dst = (ftype *)seg->output->extended_data[ch]; ftype *sumin = (ftype *)seg->sumin->extended_data[ch]; @@ -311,7 +310,9 @@ static int fn(fir_quantum)(AVFilterContext *ctx, AVFrame *out, int ch, int offse int j; seg->part_index[ch] = seg->part_index[ch] % nb_partitions; - if (min_part_size >= 8) { + if (dry_gain == 1.f) { + memcpy(src + input_offset, in, nb_samples * sizeof(*src)); + } else if (min_part_size >= 8) { #if DEPTH == 32 s->fdsp->vector_fmul_scalar(src + input_offset, in, dry_gain, FFALIGN(nb_samples, 4)); #else @@ -337,70 +338,18 @@ static int fn(fir_quantum)(AVFilterContext *ctx, AVFrame *out, int ch, int offse memset(sumin, 0, sizeof(*sumin) * seg->fft_length); - if (seg->loading[ch] < nb_partitions) { - j = seg->part_index[ch] <= 0 ? nb_partitions - 1 : seg->part_index[ch] - 1; - for (int i = 0; i < nb_partitions; i++) { - const int input_partition = j; - const int coeff_partition = i; - const int coffset = coeff_partition * seg->coeff_size; - const ftype *blockout = (const ftype *)seg->blockout->extended_data[ch] + input_partition * seg->block_size; - const ctype *coeff = ((const ctype *)seg->coeff[selir]->extended_data[ch]) + coffset; - - if (j == 0) - j = nb_partitions; - j--; - -#if DEPTH == 32 - s->afirdsp.fcmul_add(sumin, blockout, (const ftype *)coeff, part_size); -#else - s->afirdsp.dcmul_add(sumin, blockout, (const ftype *)coeff, part_size); -#endif - } - - seg->itx_fn(seg->itx[ch], sumout, sumin, sizeof(ctype)); - memcpy(dst + part_size, sumout + part_size, part_size * sizeof(*buf)); - memset(sumin, 0, sizeof(*sumin) * seg->fft_length); - } - blockout = (ftype *)seg->blockout->extended_data[ch] + seg->part_index[ch] * seg->block_size; memset(tempin + part_size, 0, sizeof(*tempin) * (seg->block_size - part_size)); memcpy(tempin, src, sizeof(*src) * part_size); seg->tx_fn(seg->tx[ch], blockout, tempin, sizeof(ftype)); - if (seg->loading[ch] < nb_partitions) { - const int selir = s->prev_selir; - - j = seg->part_index[ch]; - for (int i = 0; i < nb_partitions; i++) { - const int input_partition = j; - const int coeff_partition = i; - const int coffset = coeff_partition * seg->coeff_size; - const ftype *blockout = (const ftype *)seg->blockout->extended_data[ch] + input_partition * seg->block_size; - const ctype *coeff = ((const ctype *)seg->coeff[selir]->extended_data[ch]) + coffset; - - if (j == 0) - j = nb_partitions; - j--; - -#if DEPTH == 32 - s->afirdsp.fcmul_add(sumin, blockout, (const ftype *)coeff, part_size); -#else - s->afirdsp.dcmul_add(sumin, blockout, (const ftype *)coeff, part_size); -#endif - } - - seg->itx_fn(seg->itx[ch], sumout, sumin, sizeof(ctype)); - memcpy(dst + 2 * part_size, sumout, 2 * part_size * sizeof(*dst)); - memset(sumin, 0, sizeof(*sumin) * seg->fft_length); - } - j = seg->part_index[ch]; for (int i = 0; i < nb_partitions; i++) { const int input_partition = j; const int coeff_partition = i; const int coffset = coeff_partition * seg->coeff_size; const ftype *blockout = (const ftype *)seg->blockout->extended_data[ch] + input_partition * seg->block_size; - const ctype *coeff = ((const ctype *)seg->coeff[selir]->extended_data[ch]) + coffset; + const ctype *coeff = ((const ctype *)seg->coeff->extended_data[ch]) + coffset; if (j == 0) j = nb_partitions; @@ -415,34 +364,9 @@ static int fn(fir_quantum)(AVFilterContext *ctx, AVFrame *out, int ch, int offse seg->itx_fn(seg->itx[ch], sumout, sumin, sizeof(ctype)); - if (seg->loading[ch] < nb_partitions) { - ftype *ptr1 = dst + part_size; - ftype *ptr2 = dst + part_size * 2; - ftype *ptr3 = dst + part_size * 3; - ftype *ptr4 = dst + part_size * 4; - if (seg->loading[ch] == 0) - memcpy(ptr4, buf, sizeof(*ptr4) * part_size); - for (int n = 0; n < part_size; n++) - ptr2[n] += ptr4[n]; - - if (seg->loading[ch] < nb_partitions - 1) - memcpy(ptr4, ptr3, part_size * sizeof(*dst)); - for (int n = 0; n < part_size; n++) - ptr1[n] += sumout[n]; - - if (seg->loading[ch] == nb_partitions - 1) - memcpy(buf, sumout + part_size, part_size * sizeof(*buf)); - - for (int i = 0; i < part_size; i++) { - const ftype factor = (part_size * seg->loading[ch] + i) / (ftype)(part_size * nb_partitions); - const ftype ifactor = 1 - factor; - dst[i] = ptr1[i] * factor + ptr2[i] * ifactor; - } - } else { - fn(fir_fadd)(s, buf, sumout, part_size); - memcpy(dst, buf, part_size * sizeof(*dst)); - memcpy(buf, sumout + part_size, part_size * sizeof(*buf)); - } + fn(fir_fadd)(s, buf, sumout, part_size); + memcpy(dst, buf, part_size * sizeof(*dst)); + memcpy(buf, sumout + part_size, part_size * sizeof(*buf)); fn(fir_fadd)(s, ptr, dst, nb_samples); @@ -450,23 +374,21 @@ static int fn(fir_quantum)(AVFilterContext *ctx, AVFrame *out, int ch, int offse memmove(src, src + min_part_size, (seg->input_size - min_part_size) * sizeof(*src)); seg->part_index[ch] = (seg->part_index[ch] + 1) % nb_partitions; - if (seg->loading[ch] < nb_partitions) - seg->loading[ch]++; } - if (s->wet_gain == 1.f) + if (wet_gain == 1.f) return 0; if (min_part_size >= 8) { #if DEPTH == 32 - s->fdsp->vector_fmul_scalar(ptr, ptr, s->wet_gain, FFALIGN(nb_samples, 4)); + s->fdsp->vector_fmul_scalar(ptr, ptr, wet_gain, FFALIGN(nb_samples, 4)); #else - s->fdsp->vector_dmul_scalar(ptr, ptr, s->wet_gain, FFALIGN(nb_samples, 8)); + s->fdsp->vector_dmul_scalar(ptr, ptr, wet_gain, FFALIGN(nb_samples, 8)); #endif emms_c(); } else { for (int n = 0; n < nb_samples; n++) - ptr[n] *= s->wet_gain; + ptr[n] *= wet_gain; } return 0; diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 52741b60e4d..089ad3a0ede 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -75,6 +75,7 @@ extern const AVFilter ff_af_apulsator; extern const AVFilter ff_af_arealtime; extern const AVFilter ff_af_aresample; extern const AVFilter ff_af_areverse; +extern const AVFilter ff_af_arls; extern const AVFilter ff_af_arnndn; extern const AVFilter ff_af_asdr; extern const AVFilter ff_af_asegment; @@ -159,6 +160,8 @@ extern const AVFilter ff_af_volume; extern const AVFilter ff_af_volumedetect; extern const AVFilter ff_asrc_aevalsrc; +extern const AVFilter ff_asrc_afdelaysrc; +extern const AVFilter ff_asrc_afireqsrc; extern const AVFilter ff_asrc_afirsrc; extern const AVFilter ff_asrc_anoisesrc; extern const AVFilter ff_asrc_anullsrc; @@ -194,7 +197,10 @@ extern const AVFilter ff_vf_bm3d; extern const AVFilter ff_vf_boxblur; extern const AVFilter ff_vf_boxblur_opencl; extern const AVFilter ff_vf_bwdif; +extern const AVFilter ff_vf_bwdif_cuda; +extern const AVFilter ff_vf_bwdif_vulkan; extern const AVFilter ff_vf_cas; +extern const AVFilter ff_vf_ccrepack; extern const AVFilter ff_vf_chromaber_vulkan; extern const AVFilter ff_vf_chromahold; extern const AVFilter ff_vf_chromakey; @@ -363,6 +369,7 @@ extern const AVFilter ff_vf_multiply; extern const AVFilter ff_vf_negate; extern const AVFilter ff_vf_nlmeans; extern const AVFilter ff_vf_nlmeans_opencl; +extern const AVFilter ff_vf_nlmeans_vulkan; extern const AVFilter ff_vf_nnedi; extern const AVFilter ff_vf_noformat; extern const AVFilter ff_vf_noise; @@ -420,6 +427,7 @@ extern const AVFilter ff_vf_scale_cuda; extern const AVFilter ff_vf_scale_npp; extern const AVFilter ff_vf_scale_qsv; extern const AVFilter ff_vf_scale_vaapi; +extern const AVFilter ff_vf_scale_vt; extern const AVFilter ff_vf_scale_vulkan; extern const AVFilter ff_vf_scale2ref; extern const AVFilter ff_vf_scale2ref_npp; @@ -457,6 +465,7 @@ extern const AVFilter ff_vf_split; extern const AVFilter ff_vf_spp; extern const AVFilter ff_vf_sr; extern const AVFilter ff_vf_ssim; +extern const AVFilter ff_vf_ssim360; extern const AVFilter ff_vf_stereo3d; extern const AVFilter ff_vf_streamselect; extern const AVFilter ff_vf_subtitles; @@ -483,6 +492,7 @@ extern const AVFilter ff_vf_transpose; extern const AVFilter ff_vf_transpose_npp; extern const AVFilter ff_vf_transpose_opencl; extern const AVFilter ff_vf_transpose_vaapi; +extern const AVFilter ff_vf_transpose_vt; extern const AVFilter ff_vf_transpose_vulkan; extern const AVFilter ff_vf_trim; extern const AVFilter ff_vf_unpremultiply; @@ -512,6 +522,7 @@ extern const AVFilter ff_vf_xbr; extern const AVFilter ff_vf_xcorrelate; extern const AVFilter ff_vf_xfade; extern const AVFilter ff_vf_xfade_opencl; +extern const AVFilter ff_vf_xfade_vulkan; extern const AVFilter ff_vf_xmedian; extern const AVFilter ff_vf_xstack; extern const AVFilter ff_vf_yadif; @@ -521,11 +532,18 @@ extern const AVFilter ff_vf_yaepblur; extern const AVFilter ff_vf_zmq; extern const AVFilter ff_vf_zoompan; extern const AVFilter ff_vf_zscale; +extern const AVFilter ff_vf_hstack_vaapi; +extern const AVFilter ff_vf_vstack_vaapi; +extern const AVFilter ff_vf_xstack_vaapi; +extern const AVFilter ff_vf_hstack_qsv; +extern const AVFilter ff_vf_vstack_qsv; +extern const AVFilter ff_vf_xstack_qsv; extern const AVFilter ff_vsrc_allrgb; extern const AVFilter ff_vsrc_allyuv; extern const AVFilter ff_vsrc_cellauto; extern const AVFilter ff_vsrc_color; +extern const AVFilter ff_vsrc_color_vulkan; extern const AVFilter ff_vsrc_colorchart; extern const AVFilter ff_vsrc_colorspectrum; extern const AVFilter ff_vsrc_coreimagesrc; @@ -547,6 +565,7 @@ extern const AVFilter ff_vsrc_smptehdbars; extern const AVFilter ff_vsrc_testsrc; extern const AVFilter ff_vsrc_testsrc2; extern const AVFilter ff_vsrc_yuvtestsrc; +extern const AVFilter ff_vsrc_zoneplate; extern const AVFilter ff_vsink_nullsink; diff --git a/libavfilter/asrc_afdelaysrc.c b/libavfilter/asrc_afdelaysrc.c new file mode 100644 index 00000000000..a7d25c53095 --- /dev/null +++ b/libavfilter/asrc_afdelaysrc.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2023 Paul B Mahol + * + * 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 "libavutil/avassert.h" +#include "libavutil/channel_layout.h" +#include "libavutil/opt.h" + +#include "audio.h" +#include "avfilter.h" +#include "filters.h" +#include "formats.h" +#include "internal.h" + +typedef struct AFDelaySrcContext { + const AVClass *class; + + double delay; + int sample_rate; + int nb_samples; + int nb_taps; + AVChannelLayout chlayout; + char *chlayout_str; + + int64_t pts; +} AFDelaySrcContext; + +static av_cold int init(AVFilterContext *ctx) +{ + AFDelaySrcContext *s = ctx->priv; + int ret; + + ret = ff_parse_channel_layout(&s->chlayout, NULL, s->chlayout_str, ctx); + if (ret < 0) + return ret; + + return 0; +} + +static float sincf(float x) +{ + if (x == 0.f) + return 1.f; + return sinf(M_PI * x) / (M_PI * x); +} + +static int activate(AVFilterContext *ctx) +{ + AVFilterLink *outlink = ctx->outputs[0]; + AFDelaySrcContext *s = ctx->priv; + AVFrame *frame = NULL; + int nb_samples; + float *dst; + + if (!ff_outlink_frame_wanted(outlink)) + return FFERROR_NOT_READY; + + nb_samples = FFMIN(s->nb_samples, s->nb_taps - s->pts); + if (nb_samples <= 0) { + ff_outlink_set_status(outlink, AVERROR_EOF, s->pts); + return 0; + } + + if (!(frame = ff_get_audio_buffer(outlink, nb_samples))) + return AVERROR(ENOMEM); + + dst = (float *)frame->extended_data[0]; + for (int n = 0; n < nb_samples; n++) { + float x = s->pts + n; + dst[n] = sincf(x - s->delay) * cosf(M_PI * (x - s->delay) / s->nb_taps) / sincf((x - s->delay) / s->nb_taps); + } + + for (int ch = 1; ch < frame->ch_layout.nb_channels; ch++) + memcpy(frame->extended_data[ch], dst, sizeof(*dst) * nb_samples); + + frame->pts = s->pts; + s->pts += nb_samples; + + return ff_filter_frame(outlink, frame); +} + +static int query_formats(AVFilterContext *ctx) +{ + AFDelaySrcContext *s = ctx->priv; + AVChannelLayout chlayouts[] = { s->chlayout, { 0 } }; + int sample_rates[] = { s->sample_rate, -1 }; + static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_FLTP, + AV_SAMPLE_FMT_NONE }; + int ret = ff_set_common_formats_from_list(ctx, sample_fmts); + if (ret < 0) + return ret; + + ret = ff_set_common_channel_layouts_from_list(ctx, chlayouts); + if (ret < 0) + return ret; + + return ff_set_common_samplerates_from_list(ctx, sample_rates); +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + AFDelaySrcContext *s = ctx->priv; + + outlink->sample_rate = s->sample_rate; + s->pts = 0; + if (s->nb_taps <= 0) + s->nb_taps = s->delay * 8 + 1; + + return 0; +} + +static const AVFilterPad afdelaysrc_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .config_props = config_output, + }, +}; + +static av_cold void uninit(AVFilterContext *ctx) +{ + AFDelaySrcContext *s = ctx->priv; + + av_channel_layout_uninit(&s->chlayout); +} + +#define AF AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define OFFSET(x) offsetof(AFDelaySrcContext, x) + +static const AVOption afdelaysrc_options[] = { + { "delay", "set fractional delay", OFFSET(delay), AV_OPT_TYPE_DOUBLE,{.dbl=0}, 0, INT16_MAX, AF }, + { "d", "set fractional delay", OFFSET(delay), AV_OPT_TYPE_DOUBLE,{.dbl=0}, 0, INT16_MAX, AF }, + { "sample_rate", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64=44100}, 1, INT_MAX, AF }, + { "r", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64=44100}, 1, INT_MAX, AF }, + { "nb_samples", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64=1024}, 1, INT_MAX, AF }, + { "n", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64=1024}, 1, INT_MAX, AF }, + { "taps", "set number of taps for delay filter", OFFSET(nb_taps), AV_OPT_TYPE_INT, {.i64=0}, 0, 32768, AF }, + { "t", "set number of taps for delay filter", OFFSET(nb_taps), AV_OPT_TYPE_INT, {.i64=0}, 0, 32768, AF }, + { "channel_layout", "set channel layout", OFFSET(chlayout_str),AV_OPT_TYPE_STRING,{.str="stereo"},0, 0, AF }, + { "c", "set channel layout", OFFSET(chlayout_str),AV_OPT_TYPE_STRING,{.str="stereo"},0, 0, AF }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(afdelaysrc); + +const AVFilter ff_asrc_afdelaysrc = { + .name = "afdelaysrc", + .description = NULL_IF_CONFIG_SMALL("Generate a Fractional delay FIR coefficients."), + .priv_size = sizeof(AFDelaySrcContext), + .priv_class = &afdelaysrc_class, + .init = init, + .activate = activate, + .uninit = uninit, + .inputs = NULL, + FILTER_OUTPUTS(afdelaysrc_outputs), + FILTER_QUERY_FUNC(query_formats), +}; diff --git a/libavfilter/asrc_afirsrc.c b/libavfilter/asrc_afirsrc.c index d2ea92c41c7..e2359c159f4 100644 --- a/libavfilter/asrc_afirsrc.c +++ b/libavfilter/asrc_afirsrc.c @@ -18,13 +18,16 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/cpu.h" #include "libavutil/channel_layout.h" +#include "libavutil/ffmath.h" #include "libavutil/eval.h" #include "libavutil/opt.h" #include "libavutil/tx.h" #include "audio.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" #include "window_func.h" @@ -38,6 +41,9 @@ typedef struct AudioFIRSourceContext { int sample_rate; int nb_samples; int win_func; + int preset; + int interp; + int phaset; AVComplexFloat *complexf; float *freq; @@ -54,8 +60,8 @@ typedef struct AudioFIRSourceContext { float *win; int64_t pts; - AVTXContext *tx_ctx; - av_tx_fn tx_fn; + AVTXContext *tx_ctx, *itx_ctx; + av_tx_fn tx_fn, itx_fn; } AudioFIRSourceContext; #define OFFSET(x) offsetof(AudioFIRSourceContext, x) @@ -104,6 +110,7 @@ static av_cold void uninit(AVFilterContext *ctx) av_freep(&s->phase); av_freep(&s->complexf); av_tx_uninit(&s->tx_ctx); + av_tx_uninit(&s->itx_ctx); } static av_cold int query_formats(AVFilterContext *ctx) @@ -131,7 +138,7 @@ static int parse_string(char *str, float **items, int *nb_items, int *items_size float *new_items; char *tail; - new_items = av_fast_realloc(NULL, items_size, 1 * sizeof(float)); + new_items = av_fast_realloc(NULL, items_size, sizeof(float)); if (!new_items) return AVERROR(ENOMEM); *items = new_items; @@ -142,7 +149,7 @@ static int parse_string(char *str, float **items, int *nb_items, int *items_size do { (*items)[(*nb_items)++] = av_strtod(tail, &tail); - new_items = av_fast_realloc(*items, items_size, (*nb_items + 1) * sizeof(float)); + new_items = av_fast_realloc(*items, items_size, (*nb_items + 2) * sizeof(float)); if (!new_items) return AVERROR(ENOMEM); *items = new_items; @@ -300,3 +307,284 @@ const AVFilter ff_asrc_afirsrc = { FILTER_QUERY_FUNC(query_formats), .priv_class = &afirsrc_class, }; + +#define DEFAULT_BANDS "25 40 63 100 160 250 400 630 1000 1600 2500 4000 6300 10000 16000 24000" + +typedef struct EqPreset { + char name[16]; + float gains[16]; +} EqPreset; + +static const EqPreset eq_presets[] = { + { "flat", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, + { "acoustic", { 5.0, 4.5, 4.0, 3.5, 1.5, 1.0, 1.5, 1.5, 2.0, 3.0, 3.5, 4.0, 3.7, 3.0, 3.0 } }, + { "bass", { 10.0, 8.8, 8.5, 6.5, 2.5, 1.5, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, + { "beats", { -5.5, -5.0, -4.5, -4.2, -3.5, -3.0, -1.9, 0, 0, 0, 0, 0, 0, 0, 0 } }, + { "classic", { -0.3, 0.3, -3.5, -9.0, -1.0, 0.0, 1.8, 2.1, 0.0, 0.0, 0.0, 4.4, 9.0, 9.0, 9.0 } }, + { "clear", { 3.5, 5.5, 6.5, 9.5, 8.0, 6.5, 3.5, 2.5, 1.3, 5.0, 7.0, 9.0, 10.0, 11.0, 9.0 } }, + { "deep bass", { 12.0, 8.0, 0.0, -6.7, -12.0, -9.0, -3.5, -3.5, -6.1, 0.0, -3.0, -5.0, 0.0, 1.2, 3.0 } }, + { "dubstep", { 12.0, 10.0, 0.5, -1.0, -3.0, -5.0, -5.0, -4.8, -4.5, -2.5, -1.0, 0.0, -2.5, -2.5, 0.0 } }, + { "electronic", { 4.0, 4.0, 3.5, 1.0, 0.0, -0.5, -2.0, 0.0, 2.0, 0.0, 0.0, 1.0, 3.0, 4.0, 4.5 } }, + { "hardstyle", { 6.1, 7.0, 12.0, 6.1, -5.0, -12.0, -2.5, 3.0, 6.5, 0.0, -2.2, -4.5, -6.1, -9.2, -10.0 } }, + { "hip-hop", { 4.5, 4.3, 4.0, 2.5, 1.5, 3.0, -1.0, -1.5, -1.5, 1.5, 0.0, -1.0, 0.0, 1.5, 3.0 } }, + { "jazz", { 0.0, 0.0, 0.0, 2.0, 4.0, 5.9, -5.9, -4.5, -2.5, 2.5, 1.0, -0.8, -0.8, -0.8, -0.8 } }, + { "metal", { 10.5, 10.5, 7.5, 0.0, 2.0, 5.5, 0.0, 0.0, 0.0, 6.1, 0.0, 0.0, 6.1, 10.0, 12.0 } }, + { "movie", { 3.0, 3.0, 6.1, 8.5, 9.0, 7.0, 6.1, 6.1, 5.0, 8.0, 3.5, 3.5, 8.0, 10.0, 8.0 } }, + { "pop", { 0.0, 0.0, 0.0, 0.0, 0.0, 1.3, 2.0, 2.5, 5.0, -1.5, -2.0, -3.0, -3.0, -3.0, -3.0 } }, + { "r&b", { 3.0, 3.0, 7.0, 6.1, 4.5, 1.5, -1.5, -2.0, -1.5, 2.0, 2.5, 3.0, 3.5, 3.8, 4.0 } }, + { "rock", { 0.0, 0.0, 0.0, 3.0, 3.0, -10.0, -4.0, -1.0, 0.8, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0 } }, + { "vocal booster", { -1.5, -2.0, -3.0, -3.0, -0.5, 1.5, 3.5, 3.5, 3.5, 3.0, 2.0, 1.5, 0.0, 0.0, -1.5 } }, +}; + +static const AVOption afireqsrc_options[] = { + { "preset","set equalizer preset", OFFSET(preset), AV_OPT_TYPE_INT, {.i64=0}, -1, FF_ARRAY_ELEMS(eq_presets)-1, FLAGS, "preset" }, + { "p", "set equalizer preset", OFFSET(preset), AV_OPT_TYPE_INT, {.i64=0}, -1, FF_ARRAY_ELEMS(eq_presets)-1, FLAGS, "preset" }, + { "custom", NULL, 0, AV_OPT_TYPE_CONST, {.i64=-1}, 0, 0, FLAGS, "preset" }, + { eq_presets[ 0].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 0}, 0, 0, FLAGS, "preset" }, + { eq_presets[ 1].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 1}, 0, 0, FLAGS, "preset" }, + { eq_presets[ 2].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 2}, 0, 0, FLAGS, "preset" }, + { eq_presets[ 3].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 3}, 0, 0, FLAGS, "preset" }, + { eq_presets[ 4].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 4}, 0, 0, FLAGS, "preset" }, + { eq_presets[ 5].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 5}, 0, 0, FLAGS, "preset" }, + { eq_presets[ 6].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 6}, 0, 0, FLAGS, "preset" }, + { eq_presets[ 7].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 7}, 0, 0, FLAGS, "preset" }, + { eq_presets[ 8].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 8}, 0, 0, FLAGS, "preset" }, + { eq_presets[ 9].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 9}, 0, 0, FLAGS, "preset" }, + { eq_presets[10].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64=10}, 0, 0, FLAGS, "preset" }, + { eq_presets[11].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64=11}, 0, 0, FLAGS, "preset" }, + { eq_presets[12].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64=12}, 0, 0, FLAGS, "preset" }, + { eq_presets[13].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64=13}, 0, 0, FLAGS, "preset" }, + { eq_presets[14].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64=14}, 0, 0, FLAGS, "preset" }, + { eq_presets[15].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64=15}, 0, 0, FLAGS, "preset" }, + { eq_presets[16].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64=16}, 0, 0, FLAGS, "preset" }, + { eq_presets[17].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64=17}, 0, 0, FLAGS, "preset" }, + { "gains", "set gain values per band", OFFSET(magnitude_str), AV_OPT_TYPE_STRING, {.str="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"}, 0, 0, FLAGS }, + { "g", "set gain values per band", OFFSET(magnitude_str), AV_OPT_TYPE_STRING, {.str="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"}, 0, 0, FLAGS }, + { "bands", "set central frequency values per band", OFFSET(freq_points_str), AV_OPT_TYPE_STRING, {.str=DEFAULT_BANDS}, 0, 0, FLAGS }, + { "b", "set central frequency values per band", OFFSET(freq_points_str), AV_OPT_TYPE_STRING, {.str=DEFAULT_BANDS}, 0, 0, FLAGS }, + { "taps", "set number of taps", OFFSET(nb_taps), AV_OPT_TYPE_INT, {.i64=4096}, 16, UINT16_MAX, FLAGS }, + { "t", "set number of taps", OFFSET(nb_taps), AV_OPT_TYPE_INT, {.i64=4096}, 16, UINT16_MAX, FLAGS }, + { "sample_rate", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64=44100}, 1, INT_MAX, FLAGS }, + { "r", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64=44100}, 1, INT_MAX, FLAGS }, + { "nb_samples", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 1024}, 1, INT_MAX, FLAGS }, + { "n", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 1024}, 1, INT_MAX, FLAGS }, + { "interp","set the interpolation", OFFSET(interp), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "interp" }, + { "i", "set the interpolation", OFFSET(interp), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "interp" }, + { "linear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "interp" }, + { "cubic", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "interp" }, + { "phase","set the phase", OFFSET(phaset), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, "phase" }, + { "h", "set the phase", OFFSET(phaset), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, "phase" }, + { "linear", "linear phase", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "phase" }, + { "min", "minimum phase", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "phase" }, + {NULL} +}; + +AVFILTER_DEFINE_CLASS(afireqsrc); + +static void eq_interp(AVComplexFloat *complexf, + const float *freq, + const float *magnitude, + int m, int interp, int minterp, + const float factor) +{ + for (int i = 0; i < minterp; i++) { + for (int j = 0; j < m; j++) { + const float x = factor * i; + + if (x <= freq[j+1]) { + float g; + + if (interp == 0) { + const float d = freq[j+1] - freq[j]; + const float d0 = x - freq[j]; + const float d1 = freq[j+1] - x; + const float g0 = magnitude[j]; + const float g1 = magnitude[j+1]; + + if (d0 && d1) { + g = (d0 * g1 + d1 * g0) / d; + } else if (d0) { + g = g1; + } else { + g = g0; + } + } else { + if (x <= freq[j]) { + g = magnitude[j]; + } else { + float x1, x2, x3; + float a, b, c, d; + float m0, m1, m2, msum; + const float unit = freq[j+1] - freq[j]; + + m0 = j != 0 ? unit * (magnitude[j] - magnitude[j-1]) / (freq[j] - freq[j-1]) : 0; + m1 = magnitude[j+1] - magnitude[j]; + m2 = j != minterp - 1 ? unit * (magnitude[j+2] - magnitude[j+1]) / (freq[j+2] - freq[j+1]) : 0; + + msum = fabsf(m0) + fabsf(m1); + m0 = msum > 0.f ? (fabsf(m0) * m1 + fabsf(m1) * m0) / msum : 0.f; + msum = fabsf(m1) + fabsf(m2); + m1 = msum > 0.f ? (fabsf(m1) * m2 + fabsf(m2) * m1) / msum : 0.f; + + d = magnitude[j]; + c = m0; + b = 3.f * magnitude[j+1] - m1 - 2.f * c - 3.f * d; + a = magnitude[j+1] - b - c - d; + + x1 = (x - freq[j]) / unit; + x2 = x1 * x1; + x3 = x2 * x1; + + g = a * x3 + b * x2 + c * x1 + d; + } + } + + complexf[i].re = g; + complexf[i].im = 0; + complexf[minterp * 2 - i - 1].re = g; + complexf[minterp * 2 - i - 1].im = 0; + + break; + } + } + } +} + +static av_cold int config_eq_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + AudioFIRSourceContext *s = ctx->priv; + int fft_size, middle, asize, ret; + float scale, factor; + + s->nb_freq = s->nb_magnitude = 0; + if (s->preset < 0) { + ret = parse_string(s->freq_points_str, &s->freq, &s->nb_freq, &s->freq_size); + if (ret < 0) + return ret; + + ret = parse_string(s->magnitude_str, &s->magnitude, &s->nb_magnitude, &s->magnitude_size); + if (ret < 0) + return ret; + } else { + char *freq_str; + + s->nb_magnitude = FF_ARRAY_ELEMS(eq_presets[s->preset].gains); + + freq_str = av_strdup(DEFAULT_BANDS); + if (!freq_str) + return AVERROR(ENOMEM); + + ret = parse_string(freq_str, &s->freq, &s->nb_freq, &s->freq_size); + av_free(freq_str); + if (ret < 0) + return ret; + + s->magnitude = av_calloc(s->nb_magnitude, sizeof(*s->magnitude)); + if (!s->magnitude) + return AVERROR(ENOMEM); + memcpy(s->magnitude, eq_presets[s->preset].gains, sizeof(*s->magnitude) * s->nb_magnitude); + } + + if (s->nb_freq != s->nb_magnitude || s->nb_freq < 2) { + av_log(ctx, AV_LOG_ERROR, "Number of bands and gains must be same and >= 2.\n"); + return AVERROR(EINVAL); + } + + s->freq[s->nb_freq] = outlink->sample_rate * 0.5f; + s->magnitude[s->nb_freq] = s->magnitude[s->nb_freq-1]; + + fft_size = s->nb_taps * 2; + factor = FFMIN(outlink->sample_rate * 0.5f, s->freq[s->nb_freq - 1]) / (float)fft_size; + asize = FFALIGN(fft_size, av_cpu_max_align()); + s->complexf = av_calloc(asize * 2, sizeof(*s->complexf)); + if (!s->complexf) + return AVERROR(ENOMEM); + + scale = 1.f; + ret = av_tx_init(&s->itx_ctx, &s->itx_fn, AV_TX_FLOAT_FFT, 1, fft_size, &scale, 0); + if (ret < 0) + return ret; + + s->taps = av_calloc(s->nb_taps, sizeof(*s->taps)); + if (!s->taps) + return AVERROR(ENOMEM); + + eq_interp(s->complexf, s->freq, s->magnitude, s->nb_freq, s->interp, s->nb_taps, factor); + + for (int i = 0; i < fft_size; i++) + s->complexf[i].re = ff_exp10f(s->complexf[i].re / 20.f); + + if (s->phaset) { + const float threshold = powf(10.f, -100.f / 20.f); + const float logt = logf(threshold); + + scale = 1.f; + ret = av_tx_init(&s->tx_ctx, &s->tx_fn, AV_TX_FLOAT_FFT, 0, fft_size, &scale, 0); + if (ret < 0) + return ret; + + for (int i = 0; i < fft_size; i++) + s->complexf[i].re = s->complexf[i].re < threshold ? logt : logf(s->complexf[i].re); + + s->itx_fn(s->itx_ctx, s->complexf + asize, s->complexf, sizeof(float)); + for (int i = 0; i < fft_size; i++) { + s->complexf[i + asize].re /= fft_size; + s->complexf[i + asize].im /= fft_size; + } + + for (int i = 1; i < s->nb_taps; i++) { + s->complexf[asize + i].re += s->complexf[asize + fft_size - i].re; + s->complexf[asize + i].im -= s->complexf[asize + fft_size - i].im; + s->complexf[asize + fft_size - i].re = 0.f; + s->complexf[asize + fft_size - i].im = 0.f; + } + s->complexf[asize + s->nb_taps - 1].im *= -1.f; + + s->tx_fn(s->tx_ctx, s->complexf, s->complexf + asize, sizeof(float)); + + for (int i = 0; i < fft_size; i++) { + float eR = expf(s->complexf[i].re); + + s->complexf[i].re = eR * cosf(s->complexf[i].im); + s->complexf[i].im = eR * sinf(s->complexf[i].im); + } + + s->itx_fn(s->itx_ctx, s->complexf + asize, s->complexf, sizeof(float)); + + for (int i = 0; i < s->nb_taps; i++) + s->taps[i] = s->complexf[i + asize].re / fft_size; + } else { + s->itx_fn(s->itx_ctx, s->complexf + asize, s->complexf, sizeof(float)); + + middle = s->nb_taps / 2; + for (int i = 0; i < middle; i++) { + s->taps[middle - i] = s->complexf[i + asize].re / fft_size; + s->taps[middle + i] = s->complexf[i + asize].re / fft_size; + } + } + + s->pts = 0; + + return 0; +} + +static const AVFilterPad afireqsrc_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .config_props = config_eq_output, + }, +}; + +const AVFilter ff_asrc_afireqsrc = { + .name = "afireqsrc", + .description = NULL_IF_CONFIG_SMALL("Generate a FIR equalizer coefficients audio stream."), + .uninit = uninit, + .activate = activate, + .priv_size = sizeof(AudioFIRSourceContext), + .inputs = NULL, + FILTER_OUTPUTS(afireqsrc_outputs), + FILTER_QUERY_FUNC(query_formats), + .priv_class = &afireqsrc_class, +}; diff --git a/libavfilter/asrc_anoisesrc.c b/libavfilter/asrc_anoisesrc.c index 96a8fd63e9b..9a445534fc4 100644 --- a/libavfilter/asrc_anoisesrc.c +++ b/libavfilter/asrc_anoisesrc.c @@ -23,6 +23,7 @@ #include "audio.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" #include "libavutil/lfg.h" #include "libavutil/random_seed.h" @@ -31,6 +32,7 @@ typedef struct ANoiseSrcContext { const AVClass *class; int sample_rate; double amplitude; + double density; int64_t duration; int color; int64_t seed; @@ -38,7 +40,7 @@ typedef struct ANoiseSrcContext { int64_t pts; int infinite; - double (*filter)(double white, double *buf, double half_amplitude); + double (*filter)(double white, double *buf); double buf[7]; AVLFG c; } ANoiseSrcContext; @@ -76,6 +78,7 @@ static const AVOption anoisesrc_options[] = { { "s", "set random seed", OFFSET(seed), AV_OPT_TYPE_INT64, {.i64 = -1}, -1, UINT_MAX, FLAGS }, { "nb_samples", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 1024}, 1, INT_MAX, FLAGS }, { "n", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 1024}, 1, INT_MAX, FLAGS }, + { "density", "set density", OFFSET(density), AV_OPT_TYPE_DOUBLE, {.dbl = 0.05}, 0., 1., FLAGS }, {NULL} }; @@ -101,12 +104,12 @@ static av_cold int query_formats(AVFilterContext *ctx) return ff_set_common_samplerates_from_list(ctx, sample_rates); } -static double white_filter(double white, double *buf, double ha) +static double white_filter(double white, double *buf) { return white; } -static double pink_filter(double white, double *buf, double ha) +static double pink_filter(double white, double *buf) { double pink; @@ -122,7 +125,7 @@ static double pink_filter(double white, double *buf, double ha) return pink * 0.11; } -static double blue_filter(double white, double *buf, double ha) +static double blue_filter(double white, double *buf) { double blue; @@ -138,7 +141,7 @@ static double blue_filter(double white, double *buf, double ha) return blue * 0.11; } -static double brown_filter(double white, double *buf, double ha) +static double brown_filter(double white, double *buf) { double brown; @@ -147,7 +150,7 @@ static double brown_filter(double white, double *buf, double ha) return brown * 3.5; } -static double violet_filter(double white, double *buf, double ha) +static double violet_filter(double white, double *buf) { double violet; @@ -156,9 +159,10 @@ static double violet_filter(double white, double *buf, double ha) return violet * 3.5; } -static double velvet_filter(double white, double *buf, double ha) +static double velvet_filter(double white, double *buf) { - return 2. * ha * ((white > ha) - (white < -ha)); + double awhite = fabs(white); + return FFDIFFSIGN(white, 0.0) * buf[1] * (awhite < buf[0]); } static av_cold int config_props(AVFilterLink *outlink) @@ -180,7 +184,9 @@ static av_cold int config_props(AVFilterLink *outlink) case NM_BROWN: s->filter = brown_filter; break; case NM_BLUE: s->filter = blue_filter; break; case NM_VIOLET: s->filter = violet_filter; break; - case NM_VELVET: s->filter = velvet_filter; break; + case NM_VELVET: s->buf[0] = s->amplitude * s->density; + s->buf[1] = s->amplitude; + s->filter = velvet_filter; break; } return 0; @@ -213,7 +219,7 @@ static int activate(AVFilterContext *ctx) for (i = 0; i < nb_samples; i++) { double white; white = s->amplitude * ((2 * ((double) av_lfg_get(&s->c) / 0xffffffff)) - 1); - dst[i] = s->filter(white, s->buf, s->amplitude * 0.5); + dst[i] = s->filter(white, s->buf); } if (!s->infinite) diff --git a/libavfilter/asrc_anullsrc.c b/libavfilter/asrc_anullsrc.c index c071d8aa116..c89100c0dc6 100644 --- a/libavfilter/asrc_anullsrc.c +++ b/libavfilter/asrc_anullsrc.c @@ -33,6 +33,7 @@ #include "audio.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" typedef struct ANullContext { diff --git a/libavfilter/asrc_flite.c b/libavfilter/asrc_flite.c index 5fab8086bac..1c3053aa39d 100644 --- a/libavfilter/asrc_flite.c +++ b/libavfilter/asrc_flite.c @@ -272,7 +272,11 @@ static int request_frame(AVFilterLink *outlink) memcpy(samplesref->data[0], flite->wave_samples, nb_samples * flite->wave->num_channels * 2); samplesref->pts = flite->pts; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS samplesref->pkt_pos = -1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif samplesref->sample_rate = flite->wave->sample_rate; flite->pts += nb_samples; flite->wave_samples += nb_samples * flite->wave->num_channels; diff --git a/libavfilter/asrc_hilbert.c b/libavfilter/asrc_hilbert.c index c8e84354674..98248e7e4e7 100644 --- a/libavfilter/asrc_hilbert.c +++ b/libavfilter/asrc_hilbert.c @@ -22,6 +22,7 @@ #include "libavutil/opt.h" #include "audio.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" #include "filters.h" #include "window_func.h" diff --git a/libavfilter/asrc_sinc.c b/libavfilter/asrc_sinc.c index 258f7a139e8..16e587f1b96 100644 --- a/libavfilter/asrc_sinc.c +++ b/libavfilter/asrc_sinc.c @@ -27,6 +27,7 @@ #include "audio.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" typedef struct SincContext { @@ -91,27 +92,12 @@ static int query_formats(AVFilterContext *ctx) return ff_set_common_samplerates_from_list(ctx, sample_rates); } -static float bessel_I_0(float x) -{ - float term = 1, sum = 1, last_sum, x2 = x / 2; - int i = 1; - - do { - float y = x2 / i++; - - last_sum = sum; - sum += term *= y * y; - } while (sum != last_sum); - - return sum; -} - static float *make_lpf(int num_taps, float Fc, float beta, float rho, float scale, int dc_norm) { int i, m = num_taps - 1; float *h = av_calloc(num_taps, sizeof(*h)), sum = 0; - float mult = scale / bessel_I_0(beta), mult1 = 1.f / (.5f * m + rho); + float mult = scale / av_bessel_i0(beta), mult1 = 1.f / (.5f * m + rho); if (!h) return NULL; @@ -121,7 +107,7 @@ static float *make_lpf(int num_taps, float Fc, float beta, float rho, for (i = 0; i <= m / 2; i++) { float z = i - .5f * m, x = z * M_PI, y = z * mult1; h[i] = x ? sinf(Fc * x) / x : Fc; - sum += h[i] *= bessel_I_0(beta * sqrtf(1.f - y * y)) * mult; + sum += h[i] *= av_bessel_i0(beta * sqrtf(1.f - y * y)) * mult; if (m - i != i) { h[m - i] = h[i]; sum += h[i]; diff --git a/libavfilter/asrc_sine.c b/libavfilter/asrc_sine.c index c0d8d2265b3..c576b57fec4 100644 --- a/libavfilter/asrc_sine.c +++ b/libavfilter/asrc_sine.c @@ -27,6 +27,7 @@ #include "audio.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" typedef struct SineContext { diff --git a/libavfilter/audio.c b/libavfilter/audio.c index 4995047249b..35270c14d26 100644 --- a/libavfilter/audio.c +++ b/libavfilter/audio.c @@ -29,6 +29,13 @@ #include "framepool.h" #include "internal.h" +const AVFilterPad ff_audio_default_filterpad[1] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + } +}; + AVFrame *ff_null_get_audio_buffer(AVFilterLink *link, int nb_samples) { return ff_get_audio_buffer(link->dst->outputs[0], nb_samples); @@ -38,10 +45,10 @@ AVFrame *ff_default_get_audio_buffer(AVFilterLink *link, int nb_samples) { AVFrame *frame = NULL; int channels = link->ch_layout.nb_channels; + int align = av_cpu_max_align(); #if FF_API_OLD_CHANNEL_LAYOUT FF_DISABLE_DEPRECATION_WARNINGS int channel_layout_nb_channels = av_get_channel_layout_nb_channels(link->channel_layout); - int align = av_cpu_max_align(); av_assert0(channels == channel_layout_nb_channels || !channel_layout_nb_channels); FF_ENABLE_DEPRECATION_WARNINGS diff --git a/libavfilter/audio.h b/libavfilter/audio.h index 90709a0ad0f..be90fa347bd 100644 --- a/libavfilter/audio.h +++ b/libavfilter/audio.h @@ -25,6 +25,12 @@ #include "avfilter.h" #include "internal.h" +/** + * An AVFilterPad array whose only entry has name "default" + * and is of type AVMEDIA_TYPE_AUDIO. + */ +extern const AVFilterPad ff_audio_default_filterpad[1]; + /** default handler for get_audio_buffer() for audio inputs */ AVFrame *ff_default_get_audio_buffer(AVFilterLink *link, int nb_samples); diff --git a/libavfilter/avf_a3dscope.c b/libavfilter/avf_a3dscope.c index 089c8d8c010..d7fe2dcb751 100644 --- a/libavfilter/avf_a3dscope.c +++ b/libavfilter/avf_a3dscope.c @@ -243,6 +243,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) for (int y = 0; y < outlink->h; y++) memset(out->data[0] + y * out->linesize[0], 0, outlink->w * 4); out->pts = av_rescale_q(in->pts, inlink->time_base, outlink->time_base); + out->duration = 1; projection_matrix(s->fov, half_width / half_height, 0.1f, 1000000.f, s->projection_matrix); view_matrix(s->eye, s->zoom, s->roll, s->pitch, s->yaw, s->view_matrix); diff --git a/libavfilter/avf_abitscope.c b/libavfilter/avf_abitscope.c index 58e2bdaf616..c74970981e4 100644 --- a/libavfilter/avf_abitscope.c +++ b/libavfilter/avf_abitscope.c @@ -143,29 +143,34 @@ static int config_output(AVFilterLink *outlink) outlink->h = s->h; outlink->sample_aspect_ratio = (AVRational){1,1}; outlink->frame_rate = s->frame_rate; + outlink->time_base = av_inv_q(outlink->frame_rate); return 0; } +#define BITCOUNTER(type, depth, one) \ + memset(counter, 0, sizeof(s->counter)); \ + for (int i = 0; i < nb_samples; i++) { \ + const type x = in[i]; \ + for (int j = 0; j < depth && x; j++) \ + counter[j] += !!(x & (one << j)); \ + } + #define BARS(type, depth, one) \ for (int ch = 0; ch < inlink->ch_layout.nb_channels; ch++) { \ + const int nb_samples = insamples->nb_samples; \ const type *in = (const type *)insamples->extended_data[ch]; \ const int w = outpicref->width / inlink->ch_layout.nb_channels; \ const int h = outpicref->height / depth; \ const uint32_t color = AV_RN32(&s->fg[4 * ch]); \ + uint64_t *counter = s->counter; \ \ - memset(s->counter, 0, sizeof(s->counter)); \ - for (int i = 0; i < insamples->nb_samples; i++) { \ - for (int j = 0; j < depth; j++) { \ - if (in[i] & (one << j)) \ - s->counter[j]++; \ - } \ - } \ + BITCOUNTER(type, depth, one) \ \ for (int b = 0; b < depth; b++) { \ for (int j = 1; j < h - 1; j++) { \ uint8_t *dst = outpicref->data[0] + (b * h + j) * outpicref->linesize[0] + w * ch * 4; \ - const int ww = (s->counter[depth - b - 1] / (float)insamples->nb_samples) * (w - 1); \ + const int ww = (counter[depth - b - 1] / (float)nb_samples) * (w - 1); \ \ for (int i = 0; i < ww; i++) { \ AV_WN32(&dst[i * 4], color); \ @@ -176,25 +181,21 @@ static int config_output(AVFilterLink *outlink) #define DO_TRACE(type, depth, one) \ for (int ch = 0; ch < inlink->ch_layout.nb_channels; ch++) { \ + const int nb_samples = insamples->nb_samples; \ const int w = outpicref->width / inlink->ch_layout.nb_channels; \ const type *in = (const type *)insamples->extended_data[ch]; \ + uint64_t *counter = s->counter; \ const int wb = w / depth; \ int wv; \ \ - memset(s->counter, 0, sizeof(s->counter)); \ - for (int i = 0; i < insamples->nb_samples; i++) { \ - for (int j = 0; j < depth; j++) { \ - if (in[i] & (one << j)) \ - s->counter[j]++; \ - } \ - } \ + BITCOUNTER(type, depth, one) \ \ for (int b = 0; b < depth; b++) { \ uint8_t colors[4]; \ uint32_t color; \ uint8_t *dst = outpicref->data[0] + w * ch * 4 + wb * b * 4 + \ s->current_vpos * outpicref->linesize[0]; \ - wv = (s->counter[depth - b - 1] * 255) / insamples->nb_samples; \ + wv = (counter[depth - b - 1] * 255) / nb_samples; \ colors[0] = (wv * s->fg[ch * 4 + 0] + 127) / 255; \ colors[1] = (wv * s->fg[ch * 4 + 1] + 127) / 255; \ colors[2] = (wv * s->fg[ch * 4 + 2] + 127) / 255; \ @@ -212,6 +213,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) AVFilterLink *outlink = ctx->outputs[0]; AudioBitScopeContext *s = ctx->priv; AVFrame *outpicref; + int ret; if (s->mode == 0 || !s->outpicref) { outpicref = ff_get_video_buffer(outlink, outlink->w, outlink->h); @@ -227,13 +229,20 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) } if (s->mode == 1) { - av_frame_make_writable(s->outpicref); + ret = ff_inlink_make_frame_writable(outlink, &s->outpicref); + if (ret < 0) { + av_frame_free(&insamples); + return ret; + } outpicref = av_frame_clone(s->outpicref); - if (!outpicref) + if (!outpicref) { + av_frame_free(&insamples); return AVERROR(ENOMEM); + } } - outpicref->pts = insamples->pts; + outpicref->pts = av_rescale_q(insamples->pts, inlink->time_base, outlink->time_base); + outpicref->duration = 1; outpicref->sample_aspect_ratio = (AVRational){1,1}; switch (insamples->format) { diff --git a/libavfilter/avf_ahistogram.c b/libavfilter/avf_ahistogram.c index c45493730dc..6df6e18e3d2 100644 --- a/libavfilter/avf_ahistogram.c +++ b/libavfilter/avf_ahistogram.c @@ -207,9 +207,10 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) AVFilterContext *ctx = inlink->dst; AVFilterLink *outlink = ctx->outputs[0]; AudioHistogramContext *s = ctx->priv; + const int nb_samples = in->nb_samples; const int H = s->histogram_h; const int w = s->w; - int c, y, n, p, bin; + int c, y, n, p, bin, ret; uint64_t acmax = 1; AVFrame *clone; @@ -229,7 +230,12 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) } } - av_frame_make_writable(s->out); + ret = ff_inlink_make_frame_writable(outlink, &s->out); + if (ret < 0) { + av_frame_free(&in); + return ret; + } + if (s->dmode == SEPARATE) { for (y = 0; y < w; y++) { s->combine_buffer[3 * y ] = 0; @@ -255,7 +261,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) const float *src = (const float *)in->extended_data[c]; uint64_t *achistogram = &s->achistogram[(s->dmode == SINGLE ? 0: c) * w]; - for (n = 0; n < in->nb_samples; n++) { + for (n = 0; n < nb_samples; n++) { bin = s->get_bin(src[n], w); achistogram[bin]++; @@ -265,7 +271,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) uint64_t *shistogram = &s->shistogram[(s->dmode == SINGLE ? 0: c) * w]; const float *src2 = (const float *)s->in[s->first]->extended_data[c]; - for (n = 0; n < in->nb_samples; n++) { + for (n = 0; n < nb_samples; n++) { bin = s->get_bin(src2[n], w); shistogram[bin]++; @@ -278,7 +284,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) const float *src = (const float *)in->extended_data[c]; uint64_t *achistogram = &s->achistogram[(s->dmode == SINGLE ? 0: c) * w]; - for (n = 0; n < in->nb_samples; n++) { + for (n = 0; n < nb_samples; n++) { bin = s->get_bin(src[n], w); achistogram[bin]++; @@ -288,7 +294,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) uint64_t *shistogram = &s->shistogram[(s->dmode == SINGLE ? 0: c) * w]; const float *src2 = (const float *)s->in[s->first]->extended_data[c]; - for (n = 0; n < in->nb_samples; n++) { + for (n = 0; n < nb_samples; n++) { bin = s->get_bin(src2[n], w); shistogram[bin]++; @@ -352,10 +358,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) h = aa * (H - 1); if (s->dmode == SINGLE) { - - for (y = H - h; y < H; y++) { - s->out->data[0][y * s->out->linesize[0] + n] = 255; - s->out->data[3][y * s->out->linesize[0] + n] = 255; + int start = H - h, end = H; + const int linesizey = s->out->linesize[0]; + const int linesizea = s->out->linesize[3]; + uint8_t *dsty = s->out->data[0] + start * linesizey; + uint8_t *dsta = s->out->data[3] + start * linesizea; + + for (y = start; y < end; y++, dsty += linesizey, dsta += linesizea) { + dsty[n] = 255; + dsta[n] = 255; } if (s->h - H > 0) { @@ -367,18 +378,32 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) s->out->data[3][s->ypos * s->out->linesize[3] + n] = 255; } } else if (s->dmode == SEPARATE) { + int start = H - h, end = H; float *out = &s->combine_buffer[3 * n]; + const int linesizey = s->out->linesize[0]; + const int linesizeu = s->out->linesize[1]; + const int linesizev = s->out->linesize[2]; + const int linesizea = s->out->linesize[3]; + uint8_t *dsty = s->out->data[0] + start * linesizey; + uint8_t *dstu = s->out->data[1] + start * linesizeu; + uint8_t *dstv = s->out->data[2] + start * linesizev; + uint8_t *dsta = s->out->data[3] + start * linesizea; int old; - old = s->out->data[0][(H - h) * s->out->linesize[0] + n]; - for (y = H - h; y < H; y++) { - if (s->out->data[0][y * s->out->linesize[0] + n] != old) + old = dsty[n]; + for (y = start; y < end; y++) { + if (dsty[n] != old) break; - old = s->out->data[0][y * s->out->linesize[0] + n]; - s->out->data[0][y * s->out->linesize[0] + n] = av_clip_uint8(yf); - s->out->data[1][y * s->out->linesize[1] + n] = av_clip_uint8(128.f+uf); - s->out->data[2][y * s->out->linesize[2] + n] = av_clip_uint8(128.f+vf); - s->out->data[3][y * s->out->linesize[3] + n] = 255; + old = dsty[n]; + dsty[n] = av_clip_uint8(yf); + dstu[n] = av_clip_uint8(128.f+uf); + dstv[n] = av_clip_uint8(128.f+vf); + dsta[n] = 255; + + dsty += linesizey; + dstu += linesizeu; + dstv += linesizev; + dsta += linesizea; } out[0] += aa * yf; diff --git a/libavfilter/avf_aphasemeter.c b/libavfilter/avf_aphasemeter.c index 0f7692982ca..fac8d7c048f 100644 --- a/libavfilter/avf_aphasemeter.c +++ b/libavfilter/avf_aphasemeter.c @@ -29,6 +29,7 @@ #include "libavutil/parseutils.h" #include "libavutil/timestamp.h" #include "avfilter.h" +#include "filters.h" #include "formats.h" #include "audio.h" #include "video.h" @@ -37,7 +38,8 @@ typedef struct AudioPhaseMeterContext { const AVClass *class; - AVFrame *out; + AVFrame *out, *in; + int64_t last_pts; int do_video; int do_phasing_detection; int w, h; @@ -50,6 +52,7 @@ typedef struct AudioPhaseMeterContext { int is_out_phase; int start_mono_presence; int start_out_phase_presence; + int nb_samples; float tolerance; float angle; float phase; @@ -126,14 +129,10 @@ static int config_input(AVFilterLink *inlink) { AVFilterContext *ctx = inlink->dst; AudioPhaseMeterContext *s = ctx->priv; - int nb_samples; s->duration = av_rescale(s->duration, inlink->sample_rate, AV_TIME_BASE); - if (s->do_video) { - nb_samples = FFMAX(1, av_rescale(inlink->sample_rate, s->frame_rate.den, s->frame_rate.num)); - inlink->min_samples = - inlink->max_samples = nb_samples; - } + if (s->do_video) + s->nb_samples = FFMAX(1, av_rescale(inlink->sample_rate, s->frame_rate.den, s->frame_rate.num)); return 0; } @@ -143,10 +142,13 @@ static int config_video_output(AVFilterLink *outlink) AVFilterContext *ctx = outlink->src; AudioPhaseMeterContext *s = ctx->priv; + s->last_pts = AV_NOPTS_VALUE; + outlink->w = s->w; outlink->h = s->h; outlink->sample_aspect_ratio = (AVRational){1,1}; outlink->frame_rate = s->frame_rate; + outlink->time_base = av_inv_q(outlink->frame_rate); if (!strcmp(s->mpc_str, "none")) s->draw_median_phase = 0; @@ -160,7 +162,7 @@ static int config_video_output(AVFilterLink *outlink) static inline int get_x(float phase, int w) { - return (phase + 1.) / 2. * (w - 1); + return (phase + 1.f) / 2.f * (w - 1.f); } static inline void add_metadata(AVFrame *insamples, const char *key, char *value) @@ -246,27 +248,30 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) float fphase = 0; AVFrame *out; uint8_t *dst; - int i; + int i, ret; int mono_measurement; int out_phase_measurement; float tolerance = 1.0f - s->tolerance; - float angle = cosf(s->angle/180.0f*M_PI); + float angle = cosf(s->angle/180.0f*M_PIf); + int64_t new_pts; if (s->do_video && (!s->out || s->out->width != outlink->w || s->out->height != outlink->h)) { av_frame_free(&s->out); s->out = ff_get_video_buffer(outlink, outlink->w, outlink->h); if (!s->out) { - av_frame_free(&in); - return AVERROR(ENOMEM); + ret = AVERROR(ENOMEM); + goto fail; } out = s->out; for (i = 0; i < outlink->h; i++) memset(out->data[0] + i * out->linesize[0], 0, outlink->w * 4); } else if (s->do_video) { + ret = ff_inlink_make_frame_writable(outlink, &s->out); + if (ret < 0) + goto fail; out = s->out; - av_frame_make_writable(s->out); for (i = outlink->h - 1; i >= 10; i--) memmove(out->data[0] + (i ) * out->linesize[0], out->data[0] + (i-1) * out->linesize[0], @@ -323,18 +328,59 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) update_out_phase_detection(s, in, out_phase_measurement); } - if (s->do_video) { + if (s->do_video) + new_pts = av_rescale_q(in->pts, inlink->time_base, outlink->time_base); + if (s->do_video && new_pts != s->last_pts) { AVFrame *clone; - s->out->pts = in->pts; - s->out->duration = av_rescale_q(1, av_inv_q(outlink->frame_rate), outlink->time_base); + s->out->pts = s->last_pts = new_pts; + s->out->duration = 1; clone = av_frame_clone(s->out); - if (!clone) - return AVERROR(ENOMEM); - ff_filter_frame(outlink, clone); + if (!clone) { + ret = AVERROR(ENOMEM); + goto fail; + } + ret = ff_filter_frame(outlink, clone); + if (ret < 0) + goto fail; } + s->in = NULL; return ff_filter_frame(aoutlink, in); +fail: + av_frame_free(&in); + s->in = NULL; + return ret; +} + +static int activate(AVFilterContext *ctx) +{ + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + AudioPhaseMeterContext *s = ctx->priv; + int ret; + + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + if (s->do_video) + FF_FILTER_FORWARD_STATUS_BACK(ctx->outputs[1], inlink); + + if (!s->in) { + if (s->nb_samples > 0) + ret = ff_inlink_consume_samples(inlink, s->nb_samples, s->nb_samples, &s->in); + else + ret = ff_inlink_consume_frame(inlink, &s->in); + if (ret < 0) + return ret; + if (ret > 0) + return filter_frame(inlink, s->in); + } + + FF_FILTER_FORWARD_STATUS_ALL(inlink, ctx); + FF_FILTER_FORWARD_WANTED(outlink, inlink); + if (s->do_video) + FF_FILTER_FORWARD_WANTED(ctx->outputs[1], inlink); + + return FFERROR_NOT_READY; } static av_cold void uninit(AVFilterContext *ctx) @@ -381,7 +427,6 @@ static const AVFilterPad inputs[] = { .name = "default", .type = AVMEDIA_TYPE_AUDIO, .config_props = config_input, - .filter_frame = filter_frame, }, }; @@ -392,6 +437,7 @@ const AVFilter ff_avf_aphasemeter = { .uninit = uninit, .priv_size = sizeof(AudioPhaseMeterContext), FILTER_INPUTS(inputs), + .activate = activate, .outputs = NULL, FILTER_QUERY_FUNC(query_formats), .priv_class = &aphasemeter_class, diff --git a/libavfilter/avf_avectorscope.c b/libavfilter/avf_avectorscope.c index 3927d80b428..6e45fd95755 100644 --- a/libavfilter/avf_avectorscope.c +++ b/libavfilter/avf_avectorscope.c @@ -297,6 +297,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) unsigned x, y; unsigned prev_x = s->prev_x, prev_y = s->prev_y; double zoom = s->zoom; + int ret; if (!s->outpicref || s->outpicref->width != outlink->w || s->outpicref->height != outlink->h) { @@ -314,7 +315,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) s->outpicref->pts = av_rescale_q(insamples->pts, inlink->time_base, outlink->time_base); s->outpicref->duration = 1; - av_frame_make_writable(s->outpicref); + ret = ff_inlink_make_frame_writable(outlink, &s->outpicref); + if (ret < 0) { + av_frame_free(&insamples); + return ret; + } ff_filter_execute(ctx, fade, NULL, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx))); if (zoom < 1) { diff --git a/libavfilter/avf_concat.c b/libavfilter/avf_concat.c index c85c17b51f0..33edd7a3943 100644 --- a/libavfilter/avf_concat.c +++ b/libavfilter/avf_concat.c @@ -28,6 +28,7 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" #include "video.h" #include "audio.h" @@ -179,6 +180,7 @@ static int push_frame(AVFilterContext *ctx, unsigned in_no, AVFrame *buf) struct concat_in *in = &cat->in[in_no]; buf->pts = av_rescale_q(buf->pts, inlink->time_base, outlink->time_base); + buf->duration = av_rescale_q(buf->duration, inlink->time_base, outlink->time_base); in->pts = buf->pts; in->nb_frames++; /* add duration to input PTS */ diff --git a/libavfilter/avf_showcqt.c b/libavfilter/avf_showcqt.c index 76086477e93..a7b6e5bcf50 100644 --- a/libavfilter/avf_showcqt.c +++ b/libavfilter/avf_showcqt.c @@ -26,10 +26,14 @@ #include "libavutil/eval.h" #include "libavutil/pixdesc.h" #include "libavutil/time.h" +#include "audio.h" #include "avfilter.h" +#include "filters.h" +#include "formats.h" #include "internal.h" #include "lavfutils.h" #include "lswsutils.h" +#include "video.h" #if CONFIG_LIBFREETYPE #include @@ -51,8 +55,6 @@ "st(1, if(between(ld(0),0,1), 0.5-0.5*cos(2*PI*ld(0)), 0));" \ "r(1-ld(1)) + b(ld(1))" #define CSCHEME "1|0.5|0|0|0.5|1" -#define PTS_STEP 10 -#define PTS_TOLERANCE 1 #define OFFSET(x) offsetof(ShowCQTContext, x) #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM) @@ -344,7 +346,7 @@ static int init_cqt(ShowCQTContext *s) } av_expr_free(expr); - av_log(s->ctx, AV_LOG_INFO, "nb_cqt_coeffs = %d.\n", nb_cqt_coeffs); + av_log(s->ctx, AV_LOG_VERBOSE, "nb_cqt_coeffs = %d.\n", nb_cqt_coeffs); return 0; error: @@ -1186,9 +1188,6 @@ static int plot_cqt(AVFilterContext *ctx, AVFrame **frameout) s->draw_sono(out, s->sono_frame, s->bar_h + s->axis_h, s->sono_idx); UPDATE_TIME(s->sono_time); } - out->pts = s->next_pts; - out->duration = PTS_STEP; - s->next_pts += PTS_STEP; } s->sono_count = (s->sono_count + 1) % s->count; if (s->sono_h) @@ -1366,8 +1365,8 @@ static int config_output(AVFilterLink *outlink) s->format = outlink->format; outlink->sample_aspect_ratio = av_make_q(1, 1); outlink->frame_rate = s->rate; - outlink->time_base = av_mul_q(av_inv_q(s->rate), av_make_q(1, PTS_STEP)); - av_log(ctx, AV_LOG_INFO, "video: %dx%d %s %d/%d fps, bar_h = %d, axis_h = %d, sono_h = %d.\n", + outlink->time_base = av_inv_q(s->rate); + av_log(ctx, AV_LOG_VERBOSE, "video: %dx%d %s %d/%d fps, bar_h = %d, axis_h = %d, sono_h = %d.\n", s->width, s->height, av_get_pix_fmt_name(s->format), s->rate.num, s->rate.den, s->bar_h, s->axis_h, s->sono_h); @@ -1380,7 +1379,7 @@ static int config_output(AVFilterLink *outlink) s->fft_bits = FFMAX(ceil(log2(inlink->sample_rate * s->timeclamp)), 4); s->fft_len = 1 << s->fft_bits; - av_log(ctx, AV_LOG_INFO, "fft_len = %d, cqt_len = %d.\n", s->fft_len, s->cqt_len); + av_log(ctx, AV_LOG_VERBOSE, "fft_len = %d, cqt_len = %d.\n", s->fft_len, s->cqt_len); ret = av_tx_init(&s->fft_ctx, &s->tx_fn, AV_TX_FLOAT_FFT, 0, s->fft_len, &scale, 0); s->fft_data = av_calloc(s->fft_len, sizeof(*s->fft_data)); @@ -1470,11 +1469,10 @@ static int config_output(AVFilterLink *outlink) s->step = (int)(s->step_frac.num / s->step_frac.den); s->step_frac.num %= s->step_frac.den; if (s->step_frac.num) { - av_log(ctx, AV_LOG_INFO, "audio: %d Hz, step = %d + %d/%d.\n", + av_log(ctx, AV_LOG_VERBOSE, "audio: %d Hz, step = %d + %d/%d.\n", inlink->sample_rate, s->step, s->step_frac.num, s->step_frac.den); - av_log(ctx, AV_LOG_WARNING, "fractional step.\n"); } else { - av_log(ctx, AV_LOG_INFO, "audio: %d Hz, step = %d.\n", + av_log(ctx, AV_LOG_VERBOSE, "audio: %d Hz, step = %d.\n", inlink->sample_rate, s->step); } @@ -1487,7 +1485,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) AVFilterContext *ctx = inlink->dst; AVFilterLink *outlink = ctx->outputs[0]; ShowCQTContext *s = ctx->priv; - int remaining, step, ret, x, i, j, m; + int remaining, step, ret, x, i, j, m, got_frame = 0; float *audio_data; AVFrame *out = NULL; @@ -1503,11 +1501,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) for (x = 0; x < (s->fft_len/2 + s->remaining_fill_max - step); x++) s->fft_data[x] = s->fft_data[x+step]; s->remaining_fill += step; + s->next_pts++; - if (out) + if (out) { + out->pts = s->next_pts; + out->duration = 1; return ff_filter_frame(outlink, out); + } } - return AVERROR_EOF; + return 0; } remaining = insamples->nb_samples; @@ -1528,16 +1530,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) } remaining -= s->remaining_fill; if (out) { - int64_t pts = av_rescale_q(insamples->pts, inlink->time_base, av_make_q(1, inlink->sample_rate)); - pts += insamples->nb_samples - remaining - s->remaining_fill_max; - pts = av_rescale_q(pts, av_make_q(1, inlink->sample_rate), outlink->time_base); - if (FFABS(pts - out->pts) > PTS_TOLERANCE) { - av_log(ctx, AV_LOG_DEBUG, "changing pts from %"PRId64" (%.3f) to %"PRId64" (%.3f).\n", - out->pts, out->pts * av_q2d(outlink->time_base), - pts, pts * av_q2d(outlink->time_base)); - out->pts = pts; - s->next_pts = pts + PTS_STEP; - } + int64_t pts = av_rescale_q(insamples->nb_samples - remaining - s->remaining_fill_max, + av_make_q(1, inlink->sample_rate), inlink->time_base); + out->pts = av_rescale_q(insamples->pts + pts, inlink->time_base, outlink->time_base); + out->duration = 1; + got_frame = 1; ret = ff_filter_frame(outlink, out); if (ret < 0) { av_frame_free(&insamples); @@ -1559,35 +1556,49 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) remaining = 0; } } + if (!got_frame) + ff_filter_set_ready(ctx, 100); av_frame_free(&insamples); return 0; } -static int request_frame(AVFilterLink *outlink) +static int activate(AVFilterContext *ctx) { - AVFilterLink *inlink = outlink->src->inputs[0]; - int ret; + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + ShowCQTContext *s = ctx->priv; + int nb_samples, ret, status; + int64_t pts; + AVFrame *in; - ret = ff_request_frame(inlink); - if (ret == AVERROR_EOF) - ret = filter_frame(inlink, NULL); - return ret; -} + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); -static const AVFilterPad showcqt_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - .filter_frame = filter_frame, - }, -}; + nb_samples = s->step + (s->step_frac.num + s->remaining_frac) / s->step_frac.den; + ret = ff_inlink_consume_samples(inlink, nb_samples, nb_samples, &in); + if (ret < 0) + return ret; + if (ret > 0) + return filter_frame(inlink, in); + + if (ff_inlink_acknowledge_status(inlink, &status, &pts)) { + if (status == AVERROR_EOF) { + s->next_pts = av_rescale_q(pts, inlink->time_base, outlink->time_base); + ret = filter_frame(inlink, NULL); + ff_outlink_set_status(outlink, AVERROR_EOF, s->next_pts); + return ret; + } + } + + FF_FILTER_FORWARD_WANTED(outlink, inlink); + + return FFERROR_NOT_READY; +} static const AVFilterPad showcqt_outputs[] = { { .name = "default", .type = AVMEDIA_TYPE_VIDEO, .config_props = config_output, - .request_frame = request_frame, }, }; @@ -1595,9 +1606,10 @@ const AVFilter ff_avf_showcqt = { .name = "showcqt", .description = NULL_IF_CONFIG_SMALL("Convert input audio to a CQT (Constant/Clamped Q Transform) spectrum video output."), .init = init, + .activate = activate, .uninit = uninit, .priv_size = sizeof(ShowCQTContext), - FILTER_INPUTS(showcqt_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), FILTER_OUTPUTS(showcqt_outputs), FILTER_QUERY_FUNC(query_formats), .priv_class = &showcqt_class, diff --git a/libavfilter/avf_showcwt.c b/libavfilter/avf_showcwt.c index 2c7c4473057..d5bc920a4b4 100644 --- a/libavfilter/avf_showcwt.c +++ b/libavfilter/avf_showcwt.c @@ -25,10 +25,12 @@ #include "libavutil/avassert.h" #include "libavutil/avstring.h" #include "libavutil/channel_layout.h" +#include "libavutil/float_dsp.h" #include "libavutil/cpu.h" #include "libavutil/opt.h" #include "libavutil/parseutils.h" #include "audio.h" +#include "formats.h" #include "video.h" #include "avfilter.h" #include "filters.h" @@ -36,13 +38,25 @@ enum FrequencyScale { FSCALE_LINEAR, - FSCALE_LOG2, + FSCALE_LOG, FSCALE_BARK, FSCALE_MEL, FSCALE_ERBS, + FSCALE_SQRT, + FSCALE_CBRT, + FSCALE_QDRT, NB_FSCALE }; +enum IntensityScale { + ISCALE_LOG, + ISCALE_LINEAR, + ISCALE_SQRT, + ISCALE_CBRT, + ISCALE_QDRT, + NB_ISCALE +}; + enum DirectionMode { DIRECTION_LR, DIRECTION_RL, @@ -65,30 +79,28 @@ typedef struct ShowCWTContext { char *rate_str; AVRational auto_frame_rate; AVRational frame_rate; - AVTXContext **fft; - AVTXContext **ifft; - av_tx_fn tx_fn; - av_tx_fn itx_fn; - int fft_in_size; - int fft_out_size; - int ifft_in_size; - int ifft_out_size; + AVTXContext **fft, **ifft; + av_tx_fn tx_fn, itx_fn; + int fft_size, ifft_size; int pos; int64_t in_pts; int64_t old_pts; int64_t eof_pts; float *frequency_band; - AVFrame *kernel; + AVComplexFloat **kernel; unsigned *index; - int *kernel_start; - int *kernel_stop; - AVFrame *cache[2]; + int *kernel_start, *kernel_stop; + AVFrame *cache; AVFrame *outpicref; AVFrame *fft_in; AVFrame *fft_out; + AVFrame *dst_x; + AVFrame *src_x; AVFrame *ifft_in; AVFrame *ifft_out; AVFrame *ch_out; + AVFrame *over; + AVFrame *bh_out; int nb_threads; int nb_channels; int nb_consumed_samples; @@ -97,20 +109,22 @@ typedef struct ShowCWTContext { int slide; int new_frame; int direction; - int hop_size; - int hop_index; - int ihop_size; - int ihop_index; - int input_padding_size; - int input_sample_count; - int output_padding_size; - int output_sample_count; + int hop_size, ihop_size; + int hop_index, ihop_index; + int input_padding_size, output_padding_size; + int input_sample_count, output_sample_count; int frequency_band_count; float logarithmic_basis; + int intensity_scale; int frequency_scale; - float minimum_frequency; - float maximum_frequency; + float minimum_frequency, maximum_frequency; + float minimum_intensity, maximum_intensity; float deviation; + float bar_ratio; + int bar_size; + float rotation; + + AVFloatDSPContext *fdsp; } ShowCWTContext; #define OFFSET(x) offsetof(ShowCWTContext, x) @@ -123,14 +137,25 @@ static const AVOption showcwt_options[] = { { "r", "set video rate", OFFSET(rate_str), AV_OPT_TYPE_STRING, {.str = "25"}, 0, 0, FLAGS }, { "scale", "set frequency scale", OFFSET(frequency_scale), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_FSCALE-1, FLAGS, "scale" }, { "linear", "linear", 0, AV_OPT_TYPE_CONST,{.i64=FSCALE_LINEAR}, 0, 0, FLAGS, "scale" }, - { "log2", "logarithmic", 0, AV_OPT_TYPE_CONST,{.i64=FSCALE_LOG2}, 0, 0, FLAGS, "scale" }, + { "log", "logarithmic", 0, AV_OPT_TYPE_CONST,{.i64=FSCALE_LOG}, 0, 0, FLAGS, "scale" }, { "bark", "bark", 0, AV_OPT_TYPE_CONST,{.i64=FSCALE_BARK}, 0, 0, FLAGS, "scale" }, { "mel", "mel", 0, AV_OPT_TYPE_CONST,{.i64=FSCALE_MEL}, 0, 0, FLAGS, "scale" }, { "erbs", "erbs", 0, AV_OPT_TYPE_CONST,{.i64=FSCALE_ERBS}, 0, 0, FLAGS, "scale" }, - { "min", "set minimum frequency", OFFSET(minimum_frequency), AV_OPT_TYPE_FLOAT, {.dbl = 20.}, 1, 2000, FLAGS }, - { "max", "set maximum frequency", OFFSET(maximum_frequency), AV_OPT_TYPE_FLOAT, {.dbl = 20000.}, 0, 192000, FLAGS }, + { "sqrt", "sqrt", 0, AV_OPT_TYPE_CONST,{.i64=FSCALE_SQRT}, 0, 0, FLAGS, "scale" }, + { "cbrt", "cbrt", 0, AV_OPT_TYPE_CONST,{.i64=FSCALE_CBRT}, 0, 0, FLAGS, "scale" }, + { "qdrt", "qdrt", 0, AV_OPT_TYPE_CONST,{.i64=FSCALE_QDRT}, 0, 0, FLAGS, "scale" }, + { "iscale", "set intensity scale", OFFSET(intensity_scale),AV_OPT_TYPE_INT, {.i64=0}, 0, NB_ISCALE-1, FLAGS, "iscale" }, + { "linear", "linear", 0, AV_OPT_TYPE_CONST,{.i64=ISCALE_LINEAR}, 0, 0, FLAGS, "iscale" }, + { "log", "logarithmic", 0, AV_OPT_TYPE_CONST,{.i64=ISCALE_LOG}, 0, 0, FLAGS, "iscale" }, + { "sqrt", "sqrt", 0, AV_OPT_TYPE_CONST,{.i64=ISCALE_SQRT}, 0, 0, FLAGS, "iscale" }, + { "cbrt", "cbrt", 0, AV_OPT_TYPE_CONST,{.i64=ISCALE_CBRT}, 0, 0, FLAGS, "iscale" }, + { "qdrt", "qdrt", 0, AV_OPT_TYPE_CONST,{.i64=ISCALE_QDRT}, 0, 0, FLAGS, "iscale" }, + { "min", "set minimum frequency", OFFSET(minimum_frequency), AV_OPT_TYPE_FLOAT, {.dbl = 20.}, 1, 192000, FLAGS }, + { "max", "set maximum frequency", OFFSET(maximum_frequency), AV_OPT_TYPE_FLOAT, {.dbl = 20000.}, 1, 192000, FLAGS }, + { "imin", "set minimum intensity", OFFSET(minimum_intensity), AV_OPT_TYPE_FLOAT, {.dbl = 0.}, 0, 1, FLAGS }, + { "imax", "set maximum intensity", OFFSET(maximum_intensity), AV_OPT_TYPE_FLOAT, {.dbl = 1.}, 0, 1, FLAGS }, { "logb", "set logarithmic basis", OFFSET(logarithmic_basis), AV_OPT_TYPE_FLOAT, {.dbl = 0.0001}, 0, 1, FLAGS }, - { "deviation", "set frequency deviation", OFFSET(deviation), AV_OPT_TYPE_FLOAT, {.dbl = 1.}, 0, 10, FLAGS }, + { "deviation", "set frequency deviation", OFFSET(deviation), AV_OPT_TYPE_FLOAT, {.dbl = 1.}, 0, 100, FLAGS }, { "pps", "set pixels per second", OFFSET(pps), AV_OPT_TYPE_INT, {.i64 = 64}, 1, 1024, FLAGS }, { "mode", "set output mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 4, FLAGS, "mode" }, { "magnitude", "magnitude", 0, AV_OPT_TYPE_CONST,{.i64=0}, 0, 0, FLAGS, "mode" }, @@ -147,6 +172,8 @@ static const AVOption showcwt_options[] = { { "rl", "right to left", 0, AV_OPT_TYPE_CONST,{.i64=DIRECTION_RL}, 0, 0, FLAGS, "direction" }, { "ud", "up to down", 0, AV_OPT_TYPE_CONST,{.i64=DIRECTION_UD}, 0, 0, FLAGS, "direction" }, { "du", "down to up", 0, AV_OPT_TYPE_CONST,{.i64=DIRECTION_DU}, 0, 0, FLAGS, "direction" }, + { "bar", "set bar ratio", OFFSET(bar_ratio), AV_OPT_TYPE_FLOAT, {.dbl = 0.}, 0, 1, FLAGS }, + { "rotation", "set color rotation", OFFSET(rotation), AV_OPT_TYPE_FLOAT, {.dbl = 0}, -1, 1, FLAGS }, { NULL } }; @@ -161,15 +188,17 @@ static av_cold void uninit(AVFilterContext *ctx) av_freep(&s->kernel_stop); av_freep(&s->index); - av_frame_free(&s->kernel); - av_frame_free(&s->cache[0]); - av_frame_free(&s->cache[1]); + av_frame_free(&s->cache); av_frame_free(&s->outpicref); av_frame_free(&s->fft_in); av_frame_free(&s->fft_out); + av_frame_free(&s->dst_x); + av_frame_free(&s->src_x); av_frame_free(&s->ifft_in); av_frame_free(&s->ifft_out); av_frame_free(&s->ch_out); + av_frame_free(&s->over); + av_frame_free(&s->bh_out); if (s->fft) { for (int n = 0; n < s->nb_threads; n++) @@ -182,6 +211,14 @@ static av_cold void uninit(AVFilterContext *ctx) av_tx_uninit(&s->ifft[n]); av_freep(&s->ifft); } + + if (s->kernel) { + for (int n = 0; n < s->frequency_band_count; n++) + av_freep(&s->kernel[n]); + } + av_freep(&s->kernel); + + av_freep(&s->fdsp); } static int query_formats(AVFilterContext *ctx) @@ -213,19 +250,21 @@ static int query_formats(AVFilterContext *ctx) return 0; } -static void frequency_band(float *frequency_band, - int frequency_band_count, - float frequency_range, - float frequency_offset, - int frequency_scale, float deviation) +static float frequency_band(float *frequency_band, + int frequency_band_count, + float frequency_range, + float frequency_offset, + int frequency_scale, float deviation) { - deviation *= sqrtf(1.f / (4.f * M_PI)); // Heisenberg Gabor Limit + float ret = 0.f; + + deviation = sqrtf(deviation / (4.f * M_PI)); // Heisenberg Gabor Limit for (int y = 0; y < frequency_band_count; y++) { float frequency = frequency_range * (1.f - (float)y / frequency_band_count) + frequency_offset; float frequency_derivative = frequency_range / frequency_band_count; switch (frequency_scale) { - case FSCALE_LOG2: + case FSCALE_LOG: frequency = powf(2.f, frequency); frequency_derivative *= logf(2.f) * frequency; break; @@ -239,22 +278,62 @@ static void frequency_band(float *frequency_band, break; case FSCALE_ERBS: frequency = 676170.4f / (47.06538f - expf(frequency * 0.08950404f)) - 14678.49f; - frequency_derivative *= (frequency * frequency + 14990.4 * frequency + 4577850.f) / 160514.f; + frequency_derivative *= (frequency * frequency + 14990.4f * frequency + 4577850.f) / 160514.f; + break; + case FSCALE_SQRT: + frequency = frequency * frequency; + frequency_derivative *= 2.f * sqrtf(frequency); + break; + case FSCALE_CBRT: + frequency = frequency * frequency * frequency; + frequency_derivative *= 3.f * powf(frequency, 2.f / 3.f); + break; + case FSCALE_QDRT: + frequency = frequency * frequency * frequency * frequency; + frequency_derivative *= 4.f * powf(frequency, 3.f / 4.f); break; } frequency_band[y*2 ] = frequency; frequency_band[y*2+1] = frequency_derivative * deviation; + + ret = 1.f / (frequency_derivative * deviation); } + + return ret; } -static float remap_log(float value, float log_factor) +static float remap_log(ShowCWTContext *s, float value, int iscale, float log_factor) { - float sign = (0 < value) - (value < 0); + const float max = s->maximum_intensity; + const float min = s->minimum_intensity; + float ret; - value = logf(value * sign) * log_factor; + value += min; + + switch (iscale) { + case ISCALE_LINEAR: + ret = max - expf(value / log_factor); + break; + case ISCALE_LOG: + value = logf(value) * log_factor; + ret = max - av_clipf(value, 0.f, 1.f); + break; + case ISCALE_SQRT: + value = max - expf(value / log_factor); + ret = sqrtf(value); + break; + case ISCALE_CBRT: + value = max - expf(value / log_factor); + ret = cbrtf(value); + break; + case ISCALE_QDRT: + value = max - expf(value / log_factor); + ret = powf(value, 0.25f); + break; + } - return 1.f - av_clipf(value, 0.f, 1.f); + return av_clipf(ret, 0.f, 1.f); } static int run_channel_cwt_prepare(AVFilterContext *ctx, void *arg, int jobnr, int ch) @@ -262,38 +341,105 @@ static int run_channel_cwt_prepare(AVFilterContext *ctx, void *arg, int jobnr, i ShowCWTContext *s = ctx->priv; const int hop_size = s->hop_size; AVFrame *fin = arg; - float *cache0 = (float *)s->cache[0]->extended_data[ch]; - float *cache = (float *)s->cache[1]->extended_data[ch]; + float *cache = (float *)s->cache->extended_data[ch]; AVComplexFloat *src = (AVComplexFloat *)s->fft_in->extended_data[ch]; AVComplexFloat *dst = (AVComplexFloat *)s->fft_out->extended_data[ch]; + const int offset = (s->input_padding_size - hop_size) >> 1; if (fin) { - const int offset = s->hop_index; const float *input = (const float *)fin->extended_data[ch]; + const int offset = s->hop_size - fin->nb_samples; - memcpy(&cache[offset], input, - fin->nb_samples * sizeof(float)); + memmove(cache, &cache[fin->nb_samples], offset * sizeof(float)); + memcpy(&cache[offset], input, fin->nb_samples * sizeof(float)); } - if (fin == NULL) { - memset(&cache[s->hop_index], 0, - (hop_size - s->hop_index) * sizeof(float)); - } else if (s->hop_index + fin->nb_samples < hop_size) { + if (fin && s->hop_index + fin->nb_samples < hop_size) return 0; - } - for (int n = 0; n < hop_size; n++) { - src[n].re = cache0[n]; - src[n].im = 0.f; - src[n + hop_size].re = cache[n]; - src[n + hop_size].im = 0.f; - } + memset(src, 0, sizeof(float) * s->fft_size); + for (int n = 0; n < hop_size; n++) + src[n+offset].re = cache[n]; s->tx_fn(s->fft[jobnr], dst, src, sizeof(*src)); return 0; } +#define DRAW_BAR_COLOR(x) \ +do { \ + if (Y <= ht) { \ + dstY[x] = 0; \ + dstU[x] = 128; \ + dstV[x] = 128; \ + } else { \ + float mul = (Y - ht) * bh[0]; \ + dstY[x] = av_clip_uint8(lrintf(Y * mul * 255.f)); \ + dstU[x] = av_clip_uint8(lrintf((U-0.5f) * 128.f + 128)); \ + dstV[x] = av_clip_uint8(lrintf((V-0.5f) * 128.f + 128)); \ + } \ +} while (0) + +static void draw_bar(ShowCWTContext *s, int y, + float Y, float U, float V) +{ + float *bh = ((float *)s->bh_out->extended_data[0]) + y; + const ptrdiff_t ylinesize = s->outpicref->linesize[0]; + const ptrdiff_t ulinesize = s->outpicref->linesize[1]; + const ptrdiff_t vlinesize = s->outpicref->linesize[2]; + const int direction = s->direction; + const int bar_size = s->bar_size; + const float rcp_bar_h = 1.f / bar_size; + uint8_t *dstY, *dstU, *dstV; + const int w_1 = s->w - 1; + + bh[0] = 1.f / (Y + 0.0001f); + switch (direction) { + case DIRECTION_LR: + dstY = s->outpicref->data[0] + y * ylinesize; + dstU = s->outpicref->data[1] + y * ulinesize; + dstV = s->outpicref->data[2] + y * vlinesize; + for (int x = 0; x < bar_size; x++) { + float ht = (bar_size - x) * rcp_bar_h; + DRAW_BAR_COLOR(x); + } + break; + case DIRECTION_RL: + dstY = s->outpicref->data[0] + y * ylinesize; + dstU = s->outpicref->data[1] + y * ulinesize; + dstV = s->outpicref->data[2] + y * vlinesize; + for (int x = 0; x < bar_size; x++) { + float ht = x * rcp_bar_h; + DRAW_BAR_COLOR(w_1 - bar_size + x); + } + break; + case DIRECTION_UD: + dstY = s->outpicref->data[0] + w_1 - y; + dstU = s->outpicref->data[1] + w_1 - y; + dstV = s->outpicref->data[2] + w_1 - y; + for (int x = 0; x < bar_size; x++) { + float ht = (bar_size - x) * rcp_bar_h; + DRAW_BAR_COLOR(0); + dstY += ylinesize; + dstU += ulinesize; + dstV += vlinesize; + } + break; + case DIRECTION_DU: + dstY = s->outpicref->data[0] + w_1 - y + ylinesize * (s->h - 1 - bar_size); + dstU = s->outpicref->data[1] + w_1 - y + ulinesize * (s->h - 1 - bar_size); + dstV = s->outpicref->data[2] + w_1 - y + vlinesize * (s->h - 1 - bar_size); + for (int x = 0; x < bar_size; x++) { + float ht = x * rcp_bar_h; + DRAW_BAR_COLOR(0); + dstY += ylinesize; + dstU += ulinesize; + dstV += vlinesize; + } + break; + } +} + static int draw(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { ShowCWTContext *s = ctx->priv; @@ -305,18 +451,22 @@ static int draw(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) const int count = s->frequency_band_count; const int start = (count * jobnr) / nb_jobs; const int end = (count * (jobnr+1)) / nb_jobs; + const int nb_channels = s->nb_channels; + const int iscale = s->intensity_scale; const int ihop_index = s->ihop_index; const int ihop_size = s->ihop_size; + const float rotation = s->rotation; const int direction = s->direction; uint8_t *dstY, *dstU, *dstV, *dstA; + const int bar_size = s->bar_size; const int mode = s->mode; const int w_1 = s->w - 1; const int x = s->pos; float Y, U, V; for (int y = start; y < end; y++) { - const AVComplexFloat *src = ((const AVComplexFloat *)s->ch_out->extended_data[0]) + - y * ihop_size + ihop_index; + const AVComplexFloat *src = ((const AVComplexFloat *)s->ch_out->extended_data[y]) + + 0 * ihop_size + ihop_index; switch (direction) { case DIRECTION_LR: @@ -372,27 +522,35 @@ static int draw(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) switch (mode) { case 4: { - const AVComplexFloat *src2 = ((const AVComplexFloat *)s->ch_out->extended_data[FFMIN(1, s->nb_channels - 1)]) + - y * ihop_size + ihop_index; + const AVComplexFloat *src2 = (nb_channels > 1) ? src + ihop_size: src; float z, u, v; z = hypotf(src[0].re + src2[0].re, src[0].im + src2[0].im); u = hypotf(src[0].re, src[0].im); v = hypotf(src2[0].re, src2[0].im); - z = remap_log(z, log_factor); - u = remap_log(u, log_factor); - v = remap_log(v, log_factor); + z = remap_log(s, z, iscale, log_factor); + u = remap_log(s, u, iscale, log_factor); + v = remap_log(s, v, iscale, log_factor); Y = z; - U = 0.5f + z * sinf((v - u) * M_PI_2); - V = 0.5f + z * sinf((u - v) * M_PI_2); + U = sinf((v - u) * M_PI_2); + V = sinf((u - v) * M_PI_2); + + u = U * cosf(rotation * M_PI) - V * sinf(rotation * M_PI); + v = U * sinf(rotation * M_PI) + V * cosf(rotation * M_PI); + + U = 0.5f + 0.5f * z * u; + V = 0.5f + 0.5f * z * v; dstY[0] = av_clip_uint8(lrintf(Y * 255.f)); dstU[0] = av_clip_uint8(lrintf(U * 255.f)); dstV[0] = av_clip_uint8(lrintf(V * 255.f)); if (dstA) dstA[0] = dstY[0]; + + if (bar_size > 0) + draw_bar(s, y, Y, U, V); } break; case 3: @@ -403,16 +561,15 @@ static int draw(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) Y = 0.f; U = V = 0.5f; for (int ch = 0; ch < nb_channels; ch++) { - const AVComplexFloat *src = ((const AVComplexFloat *)s->ch_out->extended_data[ch]) + - y * ihop_size + ihop_index; + const AVComplexFloat *srcn = src + ihop_size * ch; float z; - z = hypotf(src[0].re, src[0].im); - z = remap_log(z, log_factor); + z = hypotf(srcn[0].re, srcn[0].im); + z = remap_log(s, z, iscale, log_factor); Y += z * yf; - U += z * yf * sinf(2.f * M_PI * ch * yf); - V += z * yf * cosf(2.f * M_PI * ch * yf); + U += z * yf * sinf(2.f * M_PI * (ch * yf + rotation)); + V += z * yf * cosf(2.f * M_PI * (ch * yf + rotation)); } dstY[0] = av_clip_uint8(lrintf(Y * 255.f)); @@ -420,11 +577,14 @@ static int draw(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) dstV[0] = av_clip_uint8(lrintf(V * 255.f)); if (dstA) dstA[0] = dstY[0]; + + if (bar_size > 0) + draw_bar(s, y, Y, U, V); } break; case 2: Y = hypotf(src[0].re, src[0].im); - Y = remap_log(Y, log_factor); + Y = remap_log(s, Y, iscale, log_factor); U = atan2f(src[0].im, src[0].re); U = 0.5f + 0.5f * U * Y / M_PI; V = 1.f - U; @@ -434,6 +594,8 @@ static int draw(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) dstV[0] = av_clip_uint8(lrintf(V * 255.f)); if (dstA) dstA[0] = dstY[0]; + if (bar_size > 0) + draw_bar(s, y, Y, U, V); break; case 1: Y = atan2f(src[0].im, src[0].re); @@ -442,14 +604,19 @@ static int draw(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) dstY[0] = av_clip_uint8(lrintf(Y * 255.f)); if (dstA) dstA[0] = dstY[0]; + if (bar_size > 0) + draw_bar(s, y, Y, 0.5f, 0.5f); break; case 0: Y = hypotf(src[0].re, src[0].im); - Y = remap_log(Y, log_factor); + Y = remap_log(s, Y, iscale, log_factor); dstY[0] = av_clip_uint8(lrintf(Y * 255.f)); if (dstA) dstA[0] = dstY[0]; + + if (bar_size > 0) + draw_bar(s, y, Y, 0.5f, 0.5f); break; } } @@ -461,82 +628,159 @@ static int run_channel_cwt(AVFilterContext *ctx, void *arg, int jobnr, int nb_jo { ShowCWTContext *s = ctx->priv; const int ch = *(int *)arg; - AVComplexFloat *dst = (AVComplexFloat *)s->fft_out->extended_data[ch]; - const int output_sample_count = s->output_sample_count; + const AVComplexFloat *fft_out = (const AVComplexFloat *)s->fft_out->extended_data[ch]; + AVComplexFloat *isrc = (AVComplexFloat *)s->ifft_in->extended_data[jobnr]; + AVComplexFloat *idst = (AVComplexFloat *)s->ifft_out->extended_data[jobnr]; + const int output_padding_size = s->output_padding_size; + const int input_padding_size = s->input_padding_size; + const float scale = 1.f / input_padding_size; const int ihop_size = s->ihop_size; - const int ioffset = (s->output_padding_size - ihop_size) >> 1; const int count = s->frequency_band_count; const int start = (count * jobnr) / nb_jobs; const int end = (count * (jobnr+1)) / nb_jobs; for (int y = start; y < end; y++) { - AVComplexFloat *isrc = (AVComplexFloat *)s->ifft_in->extended_data[y]; - AVComplexFloat *idst = (AVComplexFloat *)s->ifft_out->extended_data[y]; - AVComplexFloat *chout = ((AVComplexFloat *)s->ch_out->extended_data[ch]) + y * ihop_size; - const float *kernel = (const float *)s->kernel->extended_data[y]; + AVComplexFloat *chout = ((AVComplexFloat *)s->ch_out->extended_data[y]) + ch * ihop_size; + AVComplexFloat *over = ((AVComplexFloat *)s->over->extended_data[ch]) + y * ihop_size; + AVComplexFloat *dstx = (AVComplexFloat *)s->dst_x->extended_data[jobnr]; + AVComplexFloat *srcx = (AVComplexFloat *)s->src_x->extended_data[jobnr]; + const AVComplexFloat *kernel = s->kernel[y]; const unsigned *index = (const unsigned *)s->index; const int kernel_start = s->kernel_start[y]; const int kernel_stop = s->kernel_stop[y]; + const int kernel_range = kernel_stop - kernel_start + 1; + int offset; + + if (kernel_start >= 0) { + offset = 0; + memcpy(srcx, fft_out + kernel_start, sizeof(*fft_out) * kernel_range); + } else { + offset = -kernel_start; + memcpy(srcx+offset, fft_out, sizeof(*fft_out) * (kernel_range-offset)); + memcpy(srcx, fft_out+input_padding_size-offset, sizeof(*fft_out)*offset); + } - memset(isrc, 0, sizeof(*isrc) * output_sample_count); - for (int i = kernel_start; i < kernel_stop; i++) { - const unsigned n = index[i]; - const float ff = kernel[i]; + s->fdsp->vector_fmul_scalar((float *)srcx, (const float *)srcx, scale, FFALIGN(kernel_range * 2, 4)); + s->fdsp->vector_fmul((float *)dstx, (const float *)srcx, + (const float *)kernel, FFALIGN(kernel_range * 2, 16)); - isrc[n].re += ff * dst[i].re; - isrc[n].im += ff * dst[i].im; + memset(isrc, 0, sizeof(*isrc) * output_padding_size); + if (offset == 0) { + for (int i = 0; i < kernel_range; i++) { + const unsigned n = index[i + kernel_start]; + + isrc[n].re += dstx[i].re; + isrc[n].im += dstx[i].im; + } + } else { + for (int i = 0; i < kernel_range; i++) { + const unsigned n = (i-kernel_start) & (output_padding_size-1); + + isrc[n].re += dstx[i].re; + isrc[n].im += dstx[i].im; + } } s->itx_fn(s->ifft[jobnr], idst, isrc, sizeof(*isrc)); - memcpy(chout, idst + ioffset, sizeof(*chout) * ihop_size); + memcpy(chout, idst, sizeof(*chout) * ihop_size); + for (int n = 0; n < ihop_size; n++) { + chout[n].re += over[n].re; + chout[n].im += over[n].im; + } + memcpy(over, idst + ihop_size, sizeof(*over) * ihop_size); } return 0; } -static void compute_kernel(AVFilterContext *ctx) +static int compute_kernel(AVFilterContext *ctx) { ShowCWTContext *s = ctx->priv; - const int size = s->input_sample_count; - const float scale_factor = 1.f/(float)size; + const int size = s->input_padding_size; const int output_sample_count = s->output_sample_count; const int fsize = s->frequency_band_count; + int *kernel_start = s->kernel_start; + int *kernel_stop = s->kernel_stop; unsigned *index = s->index; + int range_min = INT_MAX; + int range_max = 0, ret = 0; + float *tkernel; + + tkernel = av_malloc_array(size, sizeof(*tkernel)); + if (!tkernel) + return AVERROR(ENOMEM); for (int y = 0; y < fsize; y++) { - float *kernel = (float *)s->kernel->extended_data[y]; - int *kernel_start = s->kernel_start; - int *kernel_stop = s->kernel_stop; - float frequency = s->frequency_band[y*2]; - float deviation = 1.f / (s->frequency_band[y*2+1] * - output_sample_count); - - for (int n = 0; n < size; n++) { - float ff, f = fabsf(n-frequency); - - f = size - fabsf(f - size); - ff = expf(-f*f*deviation) * scale_factor; - kernel[n] = ff; + AVComplexFloat *kernel = s->kernel[y]; + int start = INT_MIN, stop = INT_MAX; + const float frequency = s->frequency_band[y*2]; + const float deviation = 1.f / (s->frequency_band[y*2+1] * + output_sample_count); + const int a = FFMAX(frequency-12.f*sqrtf(1.f/deviation)-0.5f, -size); + const int b = FFMIN(frequency+12.f*sqrtf(1.f/deviation)-0.5f, size+a); + const int range = -a; + + memset(tkernel, 0, size * sizeof(*tkernel)); + for (int n = a; n < b; n++) { + float ff, f = n+0.5f-frequency; + + ff = expf(-f*f*deviation); + tkernel[n+range] = ff; } - for (int n = 0; n < size; n++) { - if (kernel[n] != 0.f) { - kernel_start[y] = n; + for (int n = a; n < b; n++) { + if (tkernel[n+range] != 0.f) { + if (tkernel[n+range] > FLT_MIN) + av_log(ctx, AV_LOG_DEBUG, "out of range kernel %g\n", tkernel[n+range]); + start = n; break; } } - for (int n = 0; n < size; n++) { - if (kernel[size - n - 1] != 0.f) { - kernel_stop[y] = size - n; + for (int n = b; n >= a; n--) { + if (tkernel[n+range] != 0.f) { + if (tkernel[n+range] > FLT_MIN) + av_log(ctx, AV_LOG_DEBUG, "out of range kernel %g\n", tkernel[n+range]); + stop = n; break; } } + + if (start == INT_MIN || stop == INT_MAX) { + ret = AVERROR(EINVAL); + break; + } + + kernel_start[y] = start; + kernel_stop[y] = stop; + + kernel = av_calloc(FFALIGN(stop-start+1, 16), sizeof(*kernel)); + if (!kernel) { + ret = AVERROR(ENOMEM); + break; + } + + for (int n = 0; n <= stop - start; n++) { + kernel[n].re = tkernel[n+range+start]; + kernel[n].im = tkernel[n+range+start]; + } + + range_min = FFMIN(range_min, stop+1-start); + range_max = FFMAX(range_max, stop+1-start); + + s->kernel[y] = kernel; } for (int n = 0; n < size; n++) - index[n] = n % output_sample_count; + index[n] = n & (s->output_padding_size - 1); + + av_log(ctx, AV_LOG_DEBUG, "range_min: %d\n", range_min); + av_log(ctx, AV_LOG_DEBUG, "range_max: %d\n", range_max); + + av_freep(&tkernel); + + return ret; } static int config_output(AVFilterLink *outlink) @@ -549,43 +793,92 @@ static int config_output(AVFilterLink *outlink) float scale = 1.f, factor; int ret; + if (minimum_frequency >= maximum_frequency) { + av_log(ctx, AV_LOG_ERROR, "min frequency (%f) >= (%f) max frequency\n", + minimum_frequency, maximum_frequency); + return AVERROR(EINVAL); + } + uninit(ctx); + s->fdsp = avpriv_float_dsp_alloc(0); + if (!s->fdsp) + return AVERROR(ENOMEM); + switch (s->direction) { case DIRECTION_LR: case DIRECTION_RL: + s->bar_size = s->w * s->bar_ratio; s->frequency_band_count = s->h; break; case DIRECTION_UD: case DIRECTION_DU: + s->bar_size = s->h * s->bar_ratio; s->frequency_band_count = s->w; break; } - s->new_frame = 1; + switch (s->frequency_scale) { + case FSCALE_LOG: + minimum_frequency = logf(minimum_frequency) / logf(2.f); + maximum_frequency = logf(maximum_frequency) / logf(2.f); + break; + case FSCALE_BARK: + minimum_frequency = 6.f * asinhf(minimum_frequency / 600.f); + maximum_frequency = 6.f * asinhf(maximum_frequency / 600.f); + break; + case FSCALE_MEL: + minimum_frequency = 2595.f * log10f(1.f + minimum_frequency / 700.f); + maximum_frequency = 2595.f * log10f(1.f + maximum_frequency / 700.f); + break; + case FSCALE_ERBS: + minimum_frequency = 11.17268f * logf(1.f + (46.06538f * minimum_frequency) / (minimum_frequency + 14678.49f)); + maximum_frequency = 11.17268f * logf(1.f + (46.06538f * maximum_frequency) / (maximum_frequency + 14678.49f)); + break; + case FSCALE_SQRT: + minimum_frequency = sqrtf(minimum_frequency); + maximum_frequency = sqrtf(maximum_frequency); + break; + case FSCALE_CBRT: + minimum_frequency = cbrtf(minimum_frequency); + maximum_frequency = cbrtf(maximum_frequency); + break; + case FSCALE_QDRT: + minimum_frequency = powf(minimum_frequency, 0.25f); + maximum_frequency = powf(maximum_frequency, 0.25f); + break; + } + + s->frequency_band = av_calloc(s->frequency_band_count, + sizeof(*s->frequency_band) * 2); + if (!s->frequency_band) + return AVERROR(ENOMEM); + + s->nb_consumed_samples = inlink->sample_rate * + frequency_band(s->frequency_band, + s->frequency_band_count, maximum_frequency - minimum_frequency, + minimum_frequency, s->frequency_scale, s->deviation); + s->nb_consumed_samples = FFMIN(s->nb_consumed_samples, 65536); + s->nb_threads = FFMIN(s->frequency_band_count, ff_filter_get_nb_threads(ctx)); s->nb_channels = inlink->ch_layout.nb_channels; s->old_pts = AV_NOPTS_VALUE; s->eof_pts = AV_NOPTS_VALUE; - s->nb_consumed_samples = 65536; - s->input_sample_count = s->nb_consumed_samples; - s->hop_size = s->nb_consumed_samples >> 1; - s->input_padding_size = 65536; - s->output_padding_size = FFMAX(16, s->input_padding_size * s->pps / inlink->sample_rate); + s->input_sample_count = 1 << (32 - ff_clz(s->nb_consumed_samples)); + s->input_padding_size = 1 << (32 - ff_clz(s->input_sample_count)); + s->output_sample_count = FFMAX(1, av_rescale(s->input_sample_count, s->pps, inlink->sample_rate)); + s->output_padding_size = 1 << (32 - ff_clz(s->output_sample_count)); + + s->hop_size = s->input_sample_count; + s->ihop_size = s->output_padding_size >> 1; outlink->w = s->w; outlink->h = s->h; outlink->sample_aspect_ratio = (AVRational){1,1}; - s->fft_in_size = FFALIGN(s->input_padding_size, av_cpu_max_align()); - s->fft_out_size = FFALIGN(s->input_padding_size, av_cpu_max_align()); - - s->output_sample_count = s->output_padding_size; - - s->ifft_in_size = FFALIGN(s->output_padding_size, av_cpu_max_align()); - s->ifft_out_size = FFALIGN(s->output_padding_size, av_cpu_max_align()); - s->ihop_size = s->output_padding_size >> 1; + s->fft_size = FFALIGN(s->input_padding_size, av_cpu_max_align()); + s->ifft_size = FFALIGN(s->output_padding_size, av_cpu_max_align()); s->fft = av_calloc(s->nb_threads, sizeof(*s->fft)); if (!s->fft) @@ -607,43 +900,58 @@ static int config_output(AVFilterLink *outlink) return ret; } - s->frequency_band = av_calloc(s->frequency_band_count, - sizeof(*s->frequency_band) * 2); s->outpicref = ff_get_video_buffer(outlink, outlink->w, outlink->h); - s->fft_in = ff_get_audio_buffer(inlink, s->fft_in_size * 2); - s->fft_out = ff_get_audio_buffer(inlink, s->fft_out_size * 2); - s->cache[0] = ff_get_audio_buffer(inlink, s->hop_size); - s->cache[1] = ff_get_audio_buffer(inlink, s->hop_size); - s->ch_out = ff_get_audio_buffer(inlink, s->frequency_band_count * 2 * s->ihop_size); + s->fft_in = ff_get_audio_buffer(inlink, s->fft_size * 2); + s->fft_out = ff_get_audio_buffer(inlink, s->fft_size * 2); + s->dst_x = av_frame_alloc(); + s->src_x = av_frame_alloc(); + s->kernel = av_calloc(s->frequency_band_count, sizeof(*s->kernel)); + s->cache = ff_get_audio_buffer(inlink, s->hop_size); + s->over = ff_get_audio_buffer(inlink, s->frequency_band_count * 2 * s->ihop_size); + s->bh_out = ff_get_audio_buffer(inlink, s->frequency_band_count); s->ifft_in = av_frame_alloc(); s->ifft_out = av_frame_alloc(); - s->kernel = av_frame_alloc(); + s->ch_out = av_frame_alloc(); s->index = av_calloc(s->input_padding_size, sizeof(*s->index)); s->kernel_start = av_calloc(s->frequency_band_count, sizeof(*s->kernel_start)); s->kernel_stop = av_calloc(s->frequency_band_count, sizeof(*s->kernel_stop)); - if (!s->outpicref || !s->fft_in || !s->fft_out || - !s->ifft_in || !s->ifft_out || !s->kernel_start || !s->kernel_stop || - !s->frequency_band || !s->kernel || !s->cache[0] || !s->cache[1] || !s->index) + if (!s->outpicref || !s->fft_in || !s->fft_out || !s->src_x || !s->dst_x || !s->over || + !s->ifft_in || !s->ifft_out || !s->kernel_start || !s->kernel_stop || !s->ch_out || + !s->cache || !s->index || !s->bh_out || !s->kernel) return AVERROR(ENOMEM); + s->ch_out->format = inlink->format; + s->ch_out->nb_samples = 2 * s->ihop_size * inlink->ch_layout.nb_channels; + s->ch_out->ch_layout.nb_channels = s->frequency_band_count; + ret = av_frame_get_buffer(s->ch_out, 0); + if (ret < 0) + return ret; + s->ifft_in->format = inlink->format; - s->ifft_in->nb_samples = s->ifft_in_size * 2; - s->ifft_in->ch_layout.nb_channels = s->frequency_band_count; + s->ifft_in->nb_samples = s->ifft_size * 2; + s->ifft_in->ch_layout.nb_channels = s->nb_threads; ret = av_frame_get_buffer(s->ifft_in, 0); if (ret < 0) return ret; s->ifft_out->format = inlink->format; - s->ifft_out->nb_samples = s->ifft_out_size * 2; - s->ifft_out->ch_layout.nb_channels = s->frequency_band_count; + s->ifft_out->nb_samples = s->ifft_size * 2; + s->ifft_out->ch_layout.nb_channels = s->nb_threads; ret = av_frame_get_buffer(s->ifft_out, 0); if (ret < 0) return ret; - s->kernel->format = inlink->format; - s->kernel->nb_samples = s->input_padding_size; - s->kernel->ch_layout.nb_channels = s->frequency_band_count; - ret = av_frame_get_buffer(s->kernel, 0); + s->src_x->format = inlink->format; + s->src_x->nb_samples = s->fft_size * 2; + s->src_x->ch_layout.nb_channels = s->nb_threads; + ret = av_frame_get_buffer(s->src_x, 0); + if (ret < 0) + return ret; + + s->dst_x->format = inlink->format; + s->dst_x->nb_samples = s->fft_size * 2; + s->dst_x->ch_layout.nb_channels = s->nb_threads; + ret = av_frame_get_buffer(s->dst_x, 0); if (ret < 0) return ret; @@ -659,48 +967,33 @@ static int config_output(AVFilterLink *outlink) s->outpicref->color_range = AVCOL_RANGE_JPEG; - factor = s->nb_consumed_samples / (float)inlink->sample_rate; - minimum_frequency *= factor; - maximum_frequency *= factor; - - switch (s->frequency_scale) { - case FSCALE_LOG2: - minimum_frequency = logf(minimum_frequency) / logf(2.f); - maximum_frequency = logf(maximum_frequency) / logf(2.f); - break; - case FSCALE_BARK: - minimum_frequency = 6.f * asinhf(minimum_frequency / 600.f); - maximum_frequency = 6.f * asinhf(maximum_frequency / 600.f); - break; - case FSCALE_MEL: - minimum_frequency = 2595.f * log10f(1.f + minimum_frequency / 700.f); - maximum_frequency = 2595.f * log10f(1.f + maximum_frequency / 700.f); - break; - case FSCALE_ERBS: - minimum_frequency = 11.17268f * log(1.f + (46.06538f * minimum_frequency) / (minimum_frequency + 14678.49f)); - maximum_frequency = 11.17268f * log(1.f + (46.06538f * maximum_frequency) / (maximum_frequency + 14678.49f)); - break; + factor = s->input_padding_size / (float)inlink->sample_rate; + for (int n = 0; n < s->frequency_band_count; n++) { + s->frequency_band[2*n ] *= factor; + s->frequency_band[2*n+1] *= factor; } - frequency_band(s->frequency_band, - s->frequency_band_count, maximum_frequency - minimum_frequency, - minimum_frequency, s->frequency_scale, s->deviation); - + av_log(ctx, AV_LOG_DEBUG, "factor: %f\n", factor); + av_log(ctx, AV_LOG_DEBUG, "nb_consumed_samples: %d\n", s->nb_consumed_samples); + av_log(ctx, AV_LOG_DEBUG, "hop_size: %d\n", s->hop_size); + av_log(ctx, AV_LOG_DEBUG, "ihop_size: %d\n", s->ihop_size); av_log(ctx, AV_LOG_DEBUG, "input_sample_count: %d\n", s->input_sample_count); + av_log(ctx, AV_LOG_DEBUG, "input_padding_size: %d\n", s->input_padding_size); av_log(ctx, AV_LOG_DEBUG, "output_sample_count: %d\n", s->output_sample_count); + av_log(ctx, AV_LOG_DEBUG, "output_padding_size: %d\n", s->output_padding_size); switch (s->direction) { case DIRECTION_LR: - s->pos = 0; + s->pos = s->bar_size; break; case DIRECTION_RL: - s->pos = s->w - 1; + s->pos = FFMAX(0, s->w - 2 - s->bar_size); break; case DIRECTION_UD: - s->pos = 0; + s->pos = s->bar_size; break; case DIRECTION_DU: - s->pos = s->h - 1; + s->pos = FFMAX(0, s->h - 2 - s->bar_size); break; } @@ -713,7 +1006,9 @@ static int config_output(AVFilterLink *outlink) outlink->frame_rate = s->frame_rate; outlink->time_base = av_inv_q(outlink->frame_rate); - compute_kernel(ctx); + ret = compute_kernel(ctx); + if (ret < 0) + return ret; return 0; } @@ -733,7 +1028,7 @@ static int output_frame(AVFilterContext *ctx) for (int p = 0; p < nb_planes; p++) { ptrdiff_t linesize = s->outpicref->linesize[p]; - for (int y = s->h - 1; y > 0; y--) { + for (int y = s->h - 1; y > s->bar_size; y--) { uint8_t *dst = s->outpicref->data[p] + y * linesize; memmove(dst, dst - linesize, s->w); @@ -744,7 +1039,7 @@ static int output_frame(AVFilterContext *ctx) for (int p = 0; p < nb_planes; p++) { ptrdiff_t linesize = s->outpicref->linesize[p]; - for (int y = 0; y < s->h - 1; y++) { + for (int y = 0; y < s->h - 2 - s->bar_size; y++) { uint8_t *dst = s->outpicref->data[p] + y * linesize; memmove(dst, dst + linesize, s->w); @@ -764,28 +1059,28 @@ static int output_frame(AVFilterContext *ctx) case DIRECTION_LR: s->pos++; if (s->pos >= s->w) { - s->pos = 0; + s->pos = s->bar_size; s->new_frame = 1; } break; case DIRECTION_RL: s->pos--; if (s->pos < 0) { - s->pos = s->w - 1; + s->pos = FFMAX(0, s->w - 2 - s->bar_size); s->new_frame = 1; } break; case DIRECTION_UD: s->pos++; if (s->pos >= s->h) { - s->pos = 0; + s->pos = s->bar_size; s->new_frame = 1; } break; case DIRECTION_DU: s->pos--; if (s->pos < 0) { - s->pos = s->h - 1; + s->pos = FFMAX(0, s->h - 2 - s->bar_size); s->new_frame = 1; } break; @@ -795,13 +1090,13 @@ static int output_frame(AVFilterContext *ctx) switch (s->direction) { case DIRECTION_UD: case DIRECTION_LR: - s->pos = 0; + s->pos = s->bar_size; break; case DIRECTION_RL: - s->pos = s->w - 1; + s->pos = FFMAX(0, s->w - 2 - s->bar_size); break; case DIRECTION_DU: - s->pos = s->h - 1; + s->pos = FFMAX(0, s->h - 2 - s->bar_size); break; } break; @@ -867,13 +1162,16 @@ static int output_frame(AVFilterContext *ctx) if (s->slide != SLIDE_FRAME || s->new_frame == 1) { int64_t pts_offset = s->new_frame ? 0LL : av_rescale(s->ihop_index, s->hop_size, s->ihop_size); + const int offset = (s->input_padding_size - s->hop_size) >> 1; + pts_offset = av_rescale_q(pts_offset - offset, av_make_q(1, inlink->sample_rate), inlink->time_base); s->outpicref->pts = av_rescale_q(s->in_pts + pts_offset, inlink->time_base, outlink->time_base); + s->outpicref->duration = 1; } s->ihop_index++; if (s->ihop_index >= s->ihop_size) - s->ihop_index = 0; + s->ihop_index = s->hop_index = 0; if (s->slide == SLIDE_FRAME && s->new_frame == 0) return 1; @@ -927,7 +1225,7 @@ static int activate(AVFilterContext *ctx) if (s->outpicref) { AVFrame *fin = NULL; - if (s->ihop_index == 0) { + if (s->hop_index < s->hop_size) { if (!s->eof) { ret = ff_inlink_consume_samples(inlink, 1, s->hop_size - s->hop_index, &fin); if (ret < 0) @@ -938,10 +1236,8 @@ static int activate(AVFilterContext *ctx) ff_filter_execute(ctx, run_channels_cwt_prepare, fin, NULL, FFMIN(s->nb_threads, s->nb_channels)); if (fin) { - if ((s->hop_index == 0 && s->slide != SLIDE_FRAME) || s->new_frame) { + if (s->hop_index == 0) s->in_pts = fin->pts; - s->new_frame = 0; - } s->hop_index += fin->nb_samples; av_frame_free(&fin); } else { @@ -951,11 +1247,6 @@ static int activate(AVFilterContext *ctx) } if (s->hop_index >= s->hop_size || s->ihop_index > 0) { - if (s->hop_index) { - FFSWAP(AVFrame *, s->cache[0], s->cache[1]); - s->hop_index = 0; - } - for (int ch = 0; ch < s->nb_channels && s->ihop_index == 0; ch++) { ff_filter_execute(ctx, run_channel_cwt, (void *)&ch, NULL, s->nb_threads); @@ -967,8 +1258,7 @@ static int activate(AVFilterContext *ctx) } } - if (s->eof && s->eof_pts != AV_NOPTS_VALUE && - (s->old_pts + 1 >= s->eof_pts || (s->slide == SLIDE_FRAME))) { + if (s->eof) { if (s->slide == SLIDE_FRAME) ret = output_frame(ctx); ff_outlink_set_status(outlink, AVERROR_EOF, s->eof_pts); @@ -998,13 +1288,6 @@ static int activate(AVFilterContext *ctx) return FFERROR_NOT_READY; } -static const AVFilterPad showcwt_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - static const AVFilterPad showcwt_outputs[] = { { .name = "default", @@ -1018,7 +1301,7 @@ const AVFilter ff_avf_showcwt = { .description = NULL_IF_CONFIG_SMALL("Convert input audio to a CWT (Continuous Wavelet Transform) spectrum video output."), .uninit = uninit, .priv_size = sizeof(ShowCWTContext), - FILTER_INPUTS(showcwt_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), FILTER_OUTPUTS(showcwt_outputs), FILTER_QUERY_FUNC(query_formats), .activate = activate, diff --git a/libavfilter/avf_showfreqs.c b/libavfilter/avf_showfreqs.c index cf99b4331e7..e90c9e8fcb0 100644 --- a/libavfilter/avf_showfreqs.c +++ b/libavfilter/avf_showfreqs.c @@ -30,6 +30,7 @@ #include "libavutil/parseutils.h" #include "audio.h" #include "filters.h" +#include "formats.h" #include "video.h" #include "avfilter.h" #include "internal.h" @@ -547,13 +548,6 @@ static av_cold void uninit(AVFilterContext *ctx) av_frame_free(&s->window); } -static const AVFilterPad showfreqs_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - static const AVFilterPad showfreqs_outputs[] = { { .name = "default", @@ -568,7 +562,7 @@ const AVFilter ff_avf_showfreqs = { .uninit = uninit, .priv_size = sizeof(ShowFreqsContext), .activate = activate, - FILTER_INPUTS(showfreqs_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), FILTER_OUTPUTS(showfreqs_outputs), FILTER_QUERY_FUNC(query_formats), .priv_class = &showfreqs_class, diff --git a/libavfilter/avf_showspatial.c b/libavfilter/avf_showspatial.c index 54d7962ec4f..29b41bee850 100644 --- a/libavfilter/avf_showspatial.c +++ b/libavfilter/avf_showspatial.c @@ -28,6 +28,7 @@ #include "libavutil/opt.h" #include "libavutil/parseutils.h" #include "audio.h" +#include "formats.h" #include "video.h" #include "avfilter.h" #include "filters.h" @@ -315,13 +316,6 @@ static int spatial_activate(AVFilterContext *ctx) return FFERROR_NOT_READY; } -static const AVFilterPad showspatial_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - static const AVFilterPad showspatial_outputs[] = { { .name = "default", @@ -335,7 +329,7 @@ const AVFilter ff_avf_showspatial = { .description = NULL_IF_CONFIG_SMALL("Convert input audio to a spatial video output."), .uninit = uninit, .priv_size = sizeof(ShowSpatialContext), - FILTER_INPUTS(showspatial_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), FILTER_OUTPUTS(showspatial_outputs), FILTER_QUERY_FUNC(query_formats), .activate = spatial_activate, diff --git a/libavfilter/avf_showspectrum.c b/libavfilter/avf_showspectrum.c index 24a424a34a0..8cf73fce705 100644 --- a/libavfilter/avf_showspectrum.c +++ b/libavfilter/avf_showspectrum.c @@ -40,6 +40,7 @@ #include "libavutil/parseutils.h" #include "libavutil/xga_font_data.h" #include "audio.h" +#include "formats.h" #include "video.h" #include "avfilter.h" #include "filters.h" @@ -109,6 +110,7 @@ typedef struct ShowSpectrumContext { float dmin, dmax; uint64_t samples; int (*plot_channel)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); + int eof; float opacity_factor; @@ -1292,6 +1294,8 @@ static int config_output(AVFilterLink *outlink) av_realloc_f(s->combine_buffer, s->w * 4, sizeof(*s->combine_buffer)); } + if (!s->combine_buffer) + return AVERROR(ENOMEM); av_log(ctx, AV_LOG_VERBOSE, "s:%dx%d FFT window size:%d\n", s->w, s->h, s->win_size); @@ -1441,7 +1445,10 @@ static int plot_spectrum_column(AVFilterLink *inlink, AVFrame *insamples) } } - av_frame_make_writable(s->outpicref); + ret = ff_inlink_make_frame_writable(outlink, &s->outpicref); + if (ret < 0) + return ret; + outpicref = s->outpicref; /* copy to output */ if (s->orientation == VERTICAL) { if (s->sliding == SCROLL) { @@ -1538,8 +1545,7 @@ static int plot_spectrum_column(AVFilterLink *inlink, AVFrame *insamples) if (!s->single_pic && (s->sliding != FULLFRAME || s->xpos == 0)) { if (s->old_pts < outpicref->pts || s->sliding == FULLFRAME || - (ff_outlink_get_status(inlink) == AVERROR_EOF && - ff_inlink_queued_samples(inlink) <= s->hop_size)) { + (s->eof && ff_inlink_queued_samples(inlink) <= s->hop_size)) { AVFrame *clone; if (s->legend) { @@ -1595,7 +1601,7 @@ static int activate(AVFilterContext *ctx) FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); - if (s->outpicref) { + if (s->outpicref && ff_inlink_queued_samples(inlink) > 0) { AVFrame *fin; ret = ff_inlink_consume_samples(inlink, s->hop_size, s->hop_size, &fin); @@ -1622,8 +1628,7 @@ static int activate(AVFilterContext *ctx) } } - if (ff_outlink_get_status(inlink) == AVERROR_EOF && - s->sliding == FULLFRAME && + if (s->eof && s->sliding == FULLFRAME && s->xpos > 0 && s->outpicref) { if (s->orientation == VERTICAL) { @@ -1651,11 +1656,15 @@ static int activate(AVFilterContext *ctx) return 0; } - if (ff_inlink_acknowledge_status(inlink, &status, &pts)) { - if (status == AVERROR_EOF) { - ff_outlink_set_status(outlink, status, s->pts); - return 0; - } + if (!s->eof && ff_inlink_acknowledge_status(inlink, &status, &pts)) { + s->eof = status == AVERROR_EOF; + ff_filter_set_ready(ctx, 100); + return 0; + } + + if (s->eof) { + ff_outlink_set_status(outlink, AVERROR_EOF, s->pts); + return 0; } if (ff_inlink_queued_samples(inlink) >= s->hop_size) { @@ -1671,13 +1680,6 @@ static int activate(AVFilterContext *ctx) return FFERROR_NOT_READY; } -static const AVFilterPad showspectrum_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - static const AVFilterPad showspectrum_outputs[] = { { .name = "default", @@ -1691,7 +1693,7 @@ const AVFilter ff_avf_showspectrum = { .description = NULL_IF_CONFIG_SMALL("Convert input audio to a spectrum video output."), .uninit = uninit, .priv_size = sizeof(ShowSpectrumContext), - FILTER_INPUTS(showspectrum_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), FILTER_OUTPUTS(showspectrum_outputs), FILTER_QUERY_FUNC(query_formats), .activate = activate, diff --git a/libavfilter/avf_showvolume.c b/libavfilter/avf_showvolume.c index 952f079a678..fa64d5237ae 100644 --- a/libavfilter/avf_showvolume.c +++ b/libavfilter/avf_showvolume.c @@ -324,7 +324,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) AVFilterLink *outlink = ctx->outputs[0]; ShowVolumeContext *s = ctx->priv; const int step = s->step; - int c, j, k, max_draw; + int c, j, k, max_draw, ret; char channel_name[64]; AVFrame *out; @@ -339,6 +339,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) clear_picture(s, outlink); } s->out->pts = av_rescale_q(insamples->pts, inlink->time_base, outlink->time_base); + s->out->duration = 1; if ((s->f < 1.) && (s->f > 0.)) { for (j = 0; j < outlink->h; j++) { @@ -433,7 +434,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) out = av_frame_clone(s->out); if (!out) return AVERROR(ENOMEM); - av_frame_make_writable(out); + ret = ff_inlink_make_frame_writable(outlink, &out); + if (ret < 0) { + av_frame_free(&out); + return ret; + } /* draw volume level */ for (c = 0; c < inlink->ch_layout.nb_channels && s->h >= 8 && s->draw_volume; c++) { diff --git a/libavfilter/avf_showwaves.c b/libavfilter/avf_showwaves.c index 76399ab13dd..329753c8c86 100644 --- a/libavfilter/avf_showwaves.c +++ b/libavfilter/avf_showwaves.c @@ -28,6 +28,7 @@ #include "libavutil/avassert.h" #include "libavutil/avstring.h" #include "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "libavutil/parseutils.h" #include "avfilter.h" @@ -77,10 +78,12 @@ typedef struct ShowWavesContext { char *colors; int buf_idx; int16_t *buf_idy; /* y coordinate of previous sample for each channel */ + int16_t *history; + int history_nb_samples; + int history_index; AVFrame *outpicref; - int n; + AVRational n, q, c; int pixstep; - int sample_count_mod; int mode; ///< ShowWavesMode int scale; ///< ShowWavesScale int draw_mode; ///< ShowWavesDrawMode @@ -111,7 +114,7 @@ static const AVOption showwaves_options[] = { { "line", "draw a line for each sample", 0, AV_OPT_TYPE_CONST, {.i64=MODE_LINE}, .flags=FLAGS, .unit="mode"}, { "p2p", "draw a line between samples", 0, AV_OPT_TYPE_CONST, {.i64=MODE_P2P}, .flags=FLAGS, .unit="mode"}, { "cline", "draw a centered line for each sample", 0, AV_OPT_TYPE_CONST, {.i64=MODE_CENTERED_LINE}, .flags=FLAGS, .unit="mode"}, - { "n", "set how many samples to show in the same point", OFFSET(n), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, FLAGS }, + { "n", "set how many samples to show in the same point", OFFSET(n), AV_OPT_TYPE_RATIONAL, {.i64 = 0}, 0, INT_MAX, FLAGS }, { "rate", "set video rate", OFFSET(rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, FLAGS }, { "r", "set video rate", OFFSET(rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, FLAGS }, { "split_channels", "draw channels separately", OFFSET(split_channels), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS }, @@ -135,6 +138,7 @@ static av_cold void uninit(AVFilterContext *ctx) av_frame_free(&showwaves->outpicref); av_freep(&showwaves->buf_idy); + av_freep(&showwaves->history); av_freep(&showwaves->fg); if (showwaves->single_pic) { @@ -238,28 +242,26 @@ static void draw_sample_point_rgba_full(uint8_t *buf, int height, int linesize, int16_t *prev_y, const uint8_t color[4], int h) { - if (h >= 0 && h < height) { - buf[h * linesize + 0] = color[0]; - buf[h * linesize + 1] = color[1]; - buf[h * linesize + 2] = color[2]; - buf[h * linesize + 3] = color[3]; - } + uint32_t clr = AV_RN32(color); + if (h >= 0 && h < height) + AV_WN32(buf + h * linesize, clr); } static void draw_sample_line_rgba_scale(uint8_t *buf, int height, int linesize, int16_t *prev_y, const uint8_t color[4], int h) { - int k; int start = height/2; int end = av_clip(h, 0, height-1); + uint8_t *bufk; if (start > end) FFSWAP(int16_t, start, end); - for (k = start; k < end; k++) { - buf[k * linesize + 0] += color[0]; - buf[k * linesize + 1] += color[1]; - buf[k * linesize + 2] += color[2]; - buf[k * linesize + 3] += color[3]; + bufk = buf + start * linesize; + for (int k = start; k < end; k++, bufk += linesize) { + bufk[0] += color[0]; + bufk[1] += color[1]; + bufk[2] += color[2]; + bufk[3] += color[3]; } } @@ -267,24 +269,21 @@ static void draw_sample_line_rgba_full(uint8_t *buf, int height, int linesize, int16_t *prev_y, const uint8_t color[4], int h) { - int k; int start = height/2; int end = av_clip(h, 0, height-1); + uint32_t clr = AV_RN32(color); + uint8_t *bufk; if (start > end) FFSWAP(int16_t, start, end); - for (k = start; k < end; k++) { - buf[k * linesize + 0] = color[0]; - buf[k * linesize + 1] = color[1]; - buf[k * linesize + 2] = color[2]; - buf[k * linesize + 3] = color[3]; - } + bufk = buf + start * linesize; + for (int k = start; k < end; k++, bufk += linesize) + AV_WN32(bufk, clr); } static void draw_sample_p2p_rgba_scale(uint8_t *buf, int height, int linesize, int16_t *prev_y, const uint8_t color[4], int h) { - int k; if (h >= 0 && h < height) { buf[h * linesize + 0] += color[0]; buf[h * linesize + 1] += color[1]; @@ -292,14 +291,16 @@ static void draw_sample_p2p_rgba_scale(uint8_t *buf, int height, int linesize, buf[h * linesize + 3] += color[3]; if (*prev_y && h != *prev_y) { int start = *prev_y; + uint8_t *bufk; int end = av_clip(h, 0, height-1); if (start > end) FFSWAP(int16_t, start, end); - for (k = start + 1; k < end; k++) { - buf[k * linesize + 0] += color[0]; - buf[k * linesize + 1] += color[1]; - buf[k * linesize + 2] += color[2]; - buf[k * linesize + 3] += color[3]; + bufk = buf + (start + 1) * linesize; + for (int k = start + 1; k < end; k++, bufk += linesize) { + bufk[0] += color[0]; + bufk[1] += color[1]; + bufk[2] += color[2]; + bufk[3] += color[3]; } } } @@ -310,23 +311,18 @@ static void draw_sample_p2p_rgba_full(uint8_t *buf, int height, int linesize, int16_t *prev_y, const uint8_t color[4], int h) { - int k; + uint32_t clr = AV_RN32(color); if (h >= 0 && h < height) { - buf[h * linesize + 0] = color[0]; - buf[h * linesize + 1] = color[1]; - buf[h * linesize + 2] = color[2]; - buf[h * linesize + 3] = color[3]; + AV_WN32(buf + h * linesize, clr); if (*prev_y && h != *prev_y) { int start = *prev_y; + uint8_t *bufk; int end = av_clip(h, 0, height-1); if (start > end) FFSWAP(int16_t, start, end); - for (k = start + 1; k < end; k++) { - buf[k * linesize + 0] = color[0]; - buf[k * linesize + 1] = color[1]; - buf[k * linesize + 2] = color[2]; - buf[k * linesize + 3] = color[3]; - } + bufk = buf + (start + 1) * linesize; + for (int k = start + 1; k < end; k++, bufk += linesize) + AV_WN32(bufk, clr); } } *prev_y = h; @@ -336,29 +332,27 @@ static void draw_sample_cline_rgba_scale(uint8_t *buf, int height, int linesize, int16_t *prev_y, const uint8_t color[4], int h) { - int k; const int start = (height - h) / 2; const int end = start + h; - for (k = start; k < end; k++) { - buf[k * linesize + 0] += color[0]; - buf[k * linesize + 1] += color[1]; - buf[k * linesize + 2] += color[2]; - buf[k * linesize + 3] += color[3]; + uint8_t *bufk = buf + start * linesize; + for (int k = start; k < end; k++, bufk += linesize) { + bufk[0] += color[0]; + bufk[1] += color[1]; + bufk[2] += color[2]; + bufk[3] += color[3]; } } - static void draw_sample_cline_rgba_full(uint8_t *buf, int height, int linesize, + +static void draw_sample_cline_rgba_full(uint8_t *buf, int height, int linesize, int16_t *prev_y, const uint8_t color[4], int h) { - int k; + uint32_t clr = AV_RN32(color); const int start = (height - h) / 2; const int end = start + h; - for (k = start; k < end; k++) { - buf[k * linesize + 0] = color[0]; - buf[k * linesize + 1] = color[1]; - buf[k * linesize + 2] = color[2]; - buf[k * linesize + 3] = color[3]; - } + uint8_t *bufk = buf + start * linesize; + for (int k = start; k < end; k++, bufk += linesize) + AV_WN32(bufk, clr); } static void draw_sample_point_gray(uint8_t *buf, int height, int linesize, @@ -422,29 +416,42 @@ static int config_output(AVFilterLink *outlink) uint8_t x; int ch; - if (showwaves->single_pic) - showwaves->n = 1; + showwaves->q = av_make_q(0, 1); + showwaves->c = av_make_q(0, 1); - if (!showwaves->n) - showwaves->n = FFMAX(1, av_rescale_q(inlink->sample_rate, av_make_q(1, showwaves->w), showwaves->rate)); + if (showwaves->single_pic) { + showwaves->n = av_make_q(1, 1); + outlink->frame_rate = av_make_q(1, 1); + } else { + if (!showwaves->n.num || !showwaves->n.den) { + showwaves->n = av_mul_q(av_make_q(inlink->sample_rate, + showwaves->w), av_inv_q(showwaves->rate)); + outlink->frame_rate = showwaves->rate; + } else { + outlink->frame_rate = av_div_q(av_make_q(inlink->sample_rate, showwaves->w), showwaves->n); + } + } showwaves->buf_idx = 0; if (!FF_ALLOCZ_TYPED_ARRAY(showwaves->buf_idy, nb_channels)) { av_log(ctx, AV_LOG_ERROR, "Could not allocate showwaves buffer\n"); return AVERROR(ENOMEM); } + + showwaves->history_nb_samples = av_rescale(showwaves->w * nb_channels * 2, + showwaves->n.num, showwaves->n.den); + showwaves->history = av_calloc(showwaves->history_nb_samples, + sizeof(*showwaves->history)); + if (!showwaves->history) + return AVERROR(ENOMEM); + + outlink->time_base = av_inv_q(outlink->frame_rate); outlink->w = showwaves->w; outlink->h = showwaves->h; outlink->sample_aspect_ratio = (AVRational){1,1}; - if (showwaves->single_pic) - outlink->frame_rate = av_make_q(1, 1); - else - outlink->frame_rate = av_div_q((AVRational){inlink->sample_rate,showwaves->n}, - (AVRational){showwaves->w,1}); - - av_log(ctx, AV_LOG_VERBOSE, "s:%dx%d r:%f n:%d\n", - showwaves->w, showwaves->h, av_q2d(outlink->frame_rate), showwaves->n); + av_log(ctx, AV_LOG_VERBOSE, "s:%dx%d r:%f n:%f\n", + showwaves->w, showwaves->h, av_q2d(outlink->frame_rate), av_q2d(showwaves->n)); switch (outlink->format) { case AV_PIX_FMT_GRAY8: @@ -524,7 +531,7 @@ static int config_output(AVFilterLink *outlink) if (showwaves->draw_mode == DRAW_SCALE) { /* multiplication factor, pre-computed to avoid in-loop divisions */ - x = 255 / ((showwaves->split_channels ? 1 : nb_channels) * showwaves->n); + x = (showwaves->n.den * 255) / ((showwaves->split_channels ? 1 : nb_channels) * showwaves->n.num); } else { x = 255; } @@ -551,18 +558,23 @@ static int config_output(AVFilterLink *outlink) return 0; } -inline static int push_frame(AVFilterLink *outlink) +inline static int push_frame(AVFilterLink *outlink, int i, int64_t pts) { AVFilterContext *ctx = outlink->src; AVFilterLink *inlink = ctx->inputs[0]; ShowWavesContext *showwaves = outlink->src->priv; int nb_channels = inlink->ch_layout.nb_channels; - int ret, i; + int ret; + + showwaves->outpicref->duration = 1; + showwaves->outpicref->pts = av_rescale_q(pts + i, + inlink->time_base, + outlink->time_base); ret = ff_filter_frame(outlink, showwaves->outpicref); showwaves->outpicref = NULL; showwaves->buf_idx = 0; - for (i = 0; i < nb_channels; i++) + for (int i = 0; i < nb_channels; i++) showwaves->buf_idy[i] = 0; return ret; } @@ -591,7 +603,7 @@ static int push_single_pic(AVFilterLink *outlink) av_log(ctx, AV_LOG_DEBUG, "Create frame averaging %"PRId64" samples per column\n", column_max_samples); - memset(sum, 0, nb_channels); + memset(sum, 0, nb_channels * sizeof(*sum)); for (node = showwaves->audio_frames; node; node = node->next) { int i; @@ -633,7 +645,7 @@ static int push_single_pic(AVFilterLink *outlink) } } - return push_frame(outlink); + return push_frame(outlink, 0, 0); } @@ -645,31 +657,23 @@ static int request_frame(AVFilterLink *outlink) ret = ff_request_frame(inlink); if (ret == AVERROR_EOF && showwaves->outpicref) { - if (showwaves->single_pic) - push_single_pic(outlink); - else - push_frame(outlink); + push_single_pic(outlink); } return ret; } -static int alloc_out_frame(ShowWavesContext *showwaves, const int16_t *p, - const AVFilterLink *inlink, AVFilterLink *outlink, - const AVFrame *in) +static int alloc_out_frame(ShowWavesContext *showwaves, + AVFilterLink *outlink) { if (!showwaves->outpicref) { - int j; AVFrame *out = showwaves->outpicref = ff_get_video_buffer(outlink, outlink->w, outlink->h); if (!out) return AVERROR(ENOMEM); out->width = outlink->w; out->height = outlink->h; - out->pts = in->pts + av_rescale_q((p - (int16_t *)in->data[0]) / inlink->ch_layout.nb_channels, - av_make_q(1, inlink->sample_rate), - outlink->time_base); - for (j = 0; j < outlink->h; j++) + for (int j = 0; j < outlink->h; j++) memset(out->data[0] + j*out->linesize[0], 0, outlink->w * showwaves->pixstep); } return 0; @@ -696,45 +700,67 @@ static int showwaves_filter_frame(AVFilterLink *inlink, AVFrame *insamples) ShowWavesContext *showwaves = ctx->priv; const int nb_samples = insamples->nb_samples; AVFrame *outpicref = showwaves->outpicref; - int16_t *p = (int16_t *)insamples->data[0]; - int nb_channels = inlink->ch_layout.nb_channels; - int i, j, ret = 0; + const int16_t *p = (const int16_t *)insamples->data[0]; + int16_t *history = showwaves->history; + const int nb_channels = inlink->ch_layout.nb_channels; + int i, j, ret = 0, linesize; const int pixstep = showwaves->pixstep; - const int n = showwaves->n; const int ch_height = showwaves->split_channels ? outlink->h / nb_channels : outlink->h; + const int history_nb_samples = showwaves->history_nb_samples; + const int split_channels = showwaves->split_channels; + const AVRational i_n = av_inv_q(showwaves->n); + const AVRational u_q = av_make_q(1, 1); + const AVRational z_q = av_make_q(0, 1); + int16_t *buf_idy = showwaves->buf_idy; + int idx = showwaves->history_index; + int buf_idx = showwaves->buf_idx; + const uint8_t *fg = showwaves->fg; + const int w = showwaves->w; + uint8_t *dst; + + for (int n = 0; n < nb_samples * nb_channels; n++) { + history[idx++] = p[n]; + if (idx >= history_nb_samples) + idx = 0; + } + showwaves->history_index = idx; - /* draw data in the buffer */ - for (i = 0; i < nb_samples; i++) { - - ret = alloc_out_frame(showwaves, p, inlink, outlink, insamples); - if (ret < 0) - goto end; - outpicref = showwaves->outpicref; + ret = alloc_out_frame(showwaves, outlink); + if (ret < 0) + goto end; + outpicref = showwaves->outpicref; + linesize = outpicref->linesize[0]; + /* draw data in the buffer */ + dst = outpicref->data[0]; + for (i = 0; i < history_nb_samples; i++) { for (j = 0; j < nb_channels; j++) { - uint8_t *buf = outpicref->data[0] + showwaves->buf_idx * pixstep; - const int linesize = outpicref->linesize[0]; + uint8_t *buf = dst + buf_idx * pixstep; int h; - if (showwaves->split_channels) + if (split_channels) buf += j*ch_height*linesize; - h = showwaves->get_h(*p++, ch_height); + h = showwaves->get_h(history[idx++], ch_height); + if (idx >= history_nb_samples) + idx = 0; showwaves->draw_sample(buf, ch_height, linesize, - &showwaves->buf_idy[j], &showwaves->fg[j * 4], h); + &buf_idy[j], &fg[j * 4], h); } - showwaves->sample_count_mod++; - if (showwaves->sample_count_mod == n) { - showwaves->sample_count_mod = 0; - showwaves->buf_idx++; + showwaves->c = av_add_q(showwaves->c, i_n); + if (av_cmp_q(showwaves->c, u_q) >= 0) { + showwaves->c = z_q; + buf_idx++; } - if (showwaves->buf_idx == showwaves->w || - (ff_outlink_get_status(inlink) && i == nb_samples - 1)) - if ((ret = push_frame(outlink)) < 0) - break; - outpicref = showwaves->outpicref; + if (buf_idx == w) + break; } + showwaves->buf_idx = buf_idx; + + if ((ret = push_frame(outlink, history_nb_samples - i - 1, insamples->pts)) < 0) + goto end; + outpicref = showwaves->outpicref; end: av_frame_free(&insamples); return ret; @@ -745,17 +771,22 @@ static int activate(AVFilterContext *ctx) AVFilterLink *inlink = ctx->inputs[0]; AVFilterLink *outlink = ctx->outputs[0]; ShowWavesContext *showwaves = ctx->priv; + AVRational q; AVFrame *in; - const int nb_samples = showwaves->n * outlink->w; + int nb_samples; int ret; FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + q = av_add_q(showwaves->q, av_mul_q(av_make_q(outlink->w, 1), showwaves->n)); + nb_samples = (q.num + (q.den / 2)) / q.den; ret = ff_inlink_consume_samples(inlink, nb_samples, nb_samples, &in); if (ret < 0) return ret; - if (ret > 0) + if (ret > 0) { + showwaves->q = av_sub_q(q, av_make_q(nb_samples, 1)); return showwaves_filter_frame(inlink, in); + } FF_FILTER_FORWARD_STATUS(inlink, outlink); FF_FILTER_FORWARD_WANTED(outlink, inlink); @@ -763,13 +794,6 @@ static int activate(AVFilterContext *ctx) return FFERROR_NOT_READY; } -static const AVFilterPad showwaves_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - static const AVFilterPad showwaves_outputs[] = { { .name = "default", @@ -784,7 +808,7 @@ const AVFilter ff_avf_showwaves = { .init = init, .uninit = uninit, .priv_size = sizeof(ShowWavesContext), - FILTER_INPUTS(showwaves_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), .activate = activate, FILTER_OUTPUTS(showwaves_outputs), FILTER_QUERY_FUNC(query_formats), @@ -838,13 +862,12 @@ static int showwavespic_filter_frame(AVFilterLink *inlink, AVFrame *insamples) AVFilterContext *ctx = inlink->dst; AVFilterLink *outlink = ctx->outputs[0]; ShowWavesContext *showwaves = ctx->priv; - int16_t *p = (int16_t *)insamples->data[0]; int ret = 0; if (showwaves->single_pic) { struct frame_node *f; - ret = alloc_out_frame(showwaves, p, inlink, outlink, insamples); + ret = alloc_out_frame(showwaves, outlink); if (ret < 0) goto end; diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c index c2ecdffa6f5..23bf8685e95 100644 --- a/libavfilter/avfilter.c +++ b/libavfilter/avfilter.c @@ -43,6 +43,7 @@ #include "formats.h" #include "framepool.h" #include "internal.h" +#include "video.h" static void tlog_ref(void *ctx, AVFrame *ref, int end) { @@ -57,9 +58,9 @@ static void tlog_ref(void *ctx, AVFrame *ref, int end) ff_tlog(ctx, " a:%d/%d s:%dx%d i:%c iskey:%d type:%c", ref->sample_aspect_ratio.num, ref->sample_aspect_ratio.den, ref->width, ref->height, - !ref->interlaced_frame ? 'P' : /* Progressive */ - ref->top_field_first ? 'T' : 'B', /* Top / Bottom */ - ref->key_frame, + !(ref->flags & AV_FRAME_FLAG_INTERLACED) ? 'P' : /* Progressive */ + (ref->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? 'T' : 'B', /* Top / Bottom */ + !!(ref->flags & AV_FRAME_FLAG_KEY), av_get_picture_type_char(ref->pict_type)); } if (ref->nb_samples) { @@ -158,6 +159,11 @@ int avfilter_link(AVFilterContext *src, unsigned srcpad, src->outputs[srcpad] || dst->inputs[dstpad]) return AVERROR(EINVAL); + if (!src->internal->initialized || !dst->internal->initialized) { + av_log(src, AV_LOG_ERROR, "Filters must be initialized before linking.\n"); + return AVERROR(EINVAL); + } + if (src->output_pads[srcpad].type != dst->input_pads[dstpad].type) { av_log(src, AV_LOG_ERROR, "Media type mismatch between the '%s' filter output pad %d (%s) and the '%s' filter input pad %d (%s)\n", @@ -196,6 +202,17 @@ void avfilter_link_free(AVFilterLink **link) av_freep(link); } +static void update_link_current_pts(AVFilterLink *link, int64_t pts) +{ + if (pts == AV_NOPTS_VALUE) + return; + link->current_pts = pts; + link->current_pts_us = av_rescale_q(pts, link->time_base, AV_TIME_BASE_Q); + /* TODO use duration */ + if (link->graph && link->age_index >= 0) + ff_avfilter_graph_update_heap(link->graph, link); +} + void ff_filter_set_ready(AVFilterContext *filter, unsigned priority) { filter->ready = FFMAX(filter->ready, priority); @@ -227,13 +244,17 @@ void ff_avfilter_link_set_in_status(AVFilterLink *link, int status, int64_t pts) ff_filter_set_ready(link->dst, 200); } -void ff_avfilter_link_set_out_status(AVFilterLink *link, int status, int64_t pts) +/** + * Set the status field of a link from the destination filter. + * The pts should probably be left unset (AV_NOPTS_VALUE). + */ +static void link_set_out_status(AVFilterLink *link, int status, int64_t pts) { av_assert0(!link->frame_wanted_out); av_assert0(!link->status_out); link->status_out = status; if (pts != AV_NOPTS_VALUE) - ff_update_link_current_pts(link, pts); + update_link_current_pts(link, pts); filter_unblock(link->dst); ff_filter_set_ready(link->src, 200); } @@ -423,7 +444,7 @@ int ff_request_frame(AVFilterLink *link) /* Acknowledge status change. Filters using ff_request_frame() will handle the change automatically. Filters can also check the status directly but none do yet. */ - ff_avfilter_link_set_out_status(link, link->status_in, link->status_in_pts); + link_set_out_status(link, link->status_in, link->status_in_pts); return link->status_out; } } @@ -473,7 +494,9 @@ static int ff_request_frame_to_filter(AVFilterLink *link) static const char *const var_names[] = { "t", "n", +#if FF_API_FRAME_PKT "pos", +#endif "w", "h", NULL @@ -482,7 +505,9 @@ static const char *const var_names[] = { enum { VAR_T, VAR_N, +#if FF_API_FRAME_PKT VAR_POS, +#endif VAR_W, VAR_H, VAR_VARS_NB @@ -528,17 +553,6 @@ static int set_enable_expr(AVFilterContext *ctx, const char *expr) return 0; } -void ff_update_link_current_pts(AVFilterLink *link, int64_t pts) -{ - if (pts == AV_NOPTS_VALUE) - return; - link->current_pts = pts; - link->current_pts_us = av_rescale_q(pts, link->time_base, AV_TIME_BASE_Q); - /* TODO use duration */ - if (link->graph && link->age_index >= 0) - ff_avfilter_graph_update_heap(link->graph, link); -} - int avfilter_process_command(AVFilterContext *filter, const char *cmd, const char *arg, char *res, int res_len, int flags) { if(!strcmp(cmd, "ping")){ @@ -560,27 +574,6 @@ int avfilter_process_command(AVFilterContext *filter, const char *cmd, const cha return AVERROR(ENOSYS); } -#if FF_API_PAD_COUNT -int avfilter_pad_count(const AVFilterPad *pads) -{ - const AVFilter *filter; - void *opaque = NULL; - - if (!pads) - return 0; - - while (filter = av_filter_iterate(&opaque)) { - if (pads == filter->inputs) - return filter->nb_inputs; - if (pads == filter->outputs) - return filter->nb_outputs; - } - - av_assert0(!"AVFilterPad list not from a filter"); - return AVERROR_BUG; -} -#endif - unsigned avfilter_filter_pad_count(const AVFilter *filter, int is_output) { return is_output ? filter->nb_outputs : filter->nb_inputs; @@ -797,8 +790,8 @@ int ff_filter_get_nb_threads(AVFilterContext *ctx) return ctx->graph->nb_threads; } -static int process_options(AVFilterContext *ctx, AVDictionary **options, - const char *args) +int ff_filter_opt_parse(void *logctx, const AVClass *priv_class, + AVDictionary **options, const char *args) { const AVOption *o = NULL; int ret; @@ -812,8 +805,8 @@ static int process_options(AVFilterContext *ctx, AVDictionary **options, while (*args) { const char *shorthand = NULL; - if (ctx->filter->priv_class) - o = av_opt_next(ctx->priv, o); + if (priv_class) + o = av_opt_next(&priv_class, o); if (o) { if (o->type == AV_OPT_TYPE_CONST || o->offset == offset) continue; @@ -826,9 +819,9 @@ static int process_options(AVFilterContext *ctx, AVDictionary **options, &parsed_key, &value); if (ret < 0) { if (ret == AVERROR(EINVAL)) - av_log(ctx, AV_LOG_ERROR, "No option name near '%s'\n", args); + av_log(logctx, AV_LOG_ERROR, "No option name near '%s'\n", args); else - av_log(ctx, AV_LOG_ERROR, "Unable to parse '%s': %s\n", args, + av_log(logctx, AV_LOG_ERROR, "Unable to parse '%s': %s\n", args, av_err2str(ret)); return ret; } @@ -838,13 +831,13 @@ static int process_options(AVFilterContext *ctx, AVDictionary **options, key = parsed_key; /* discard all remaining shorthand */ - if (ctx->filter->priv_class) - while ((o = av_opt_next(ctx->priv, o))); + if (priv_class) + while ((o = av_opt_next(&priv_class, o))); } else { key = shorthand; } - av_log(ctx, AV_LOG_DEBUG, "Setting '%s' to value '%s'\n", key, value); + av_log(logctx, AV_LOG_DEBUG, "Setting '%s' to value '%s'\n", key, value); av_dict_set(options, key, value, AV_DICT_MULTIKEY); @@ -872,6 +865,11 @@ int avfilter_init_dict(AVFilterContext *ctx, AVDictionary **options) { int ret = 0; + if (ctx->internal->initialized) { + av_log(ctx, AV_LOG_ERROR, "Filter already initialized\n"); + return AVERROR(EINVAL); + } + ret = av_opt_set_dict2(ctx, options, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { av_log(ctx, AV_LOG_ERROR, "Error applying generic filter options.\n"); @@ -898,6 +896,8 @@ int avfilter_init_dict(AVFilterContext *ctx, AVDictionary **options) return ret; } + ctx->internal->initialized = 1; + return 0; } @@ -908,7 +908,7 @@ int avfilter_init_str(AVFilterContext *filter, const char *args) int ret = 0; if (args && *args) { - ret = process_options(filter, &options, args); + ret = ff_filter_opt_parse(filter, filter->filter->priv_class, &options, args); if (ret < 0) goto fail; } @@ -991,6 +991,8 @@ int ff_filter_frame(AVFilterLink *link, AVFrame *frame) av_assert1(frame->width == link->w); av_assert1(frame->height == link->h); } + + frame->sample_aspect_ratio = link->sample_aspect_ratio; } else { if (frame->format != link->format) { av_log(link->dst, AV_LOG_ERROR, "Format change is not supported\n"); @@ -1004,6 +1006,14 @@ int ff_filter_frame(AVFilterLink *link, AVFrame *frame) av_log(link->dst, AV_LOG_ERROR, "Sample rate change is not supported\n"); goto error; } + + frame->duration = av_rescale_q(frame->nb_samples, (AVRational){ 1, frame->sample_rate }, + link->time_base); +#if FF_API_PKT_DURATION +FF_DISABLE_DEPRECATION_WARNINGS + frame->pkt_duration = frame->duration; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } link->frame_blocked_in = link->frame_wanted_out = 0; @@ -1112,7 +1122,7 @@ static int ff_filter_frame_to_filter(AVFilterLink *link) link->frame_count_out--; ret = ff_filter_frame_framed(link, frame); if (ret < 0 && ret != link->status_out) { - ff_avfilter_link_set_out_status(link, ret, AV_NOPTS_VALUE); + link_set_out_status(link, ret, AV_NOPTS_VALUE); } else { /* Run once again, to see if several frames were available, or if the input status has also changed, or any other reason. */ @@ -1142,7 +1152,7 @@ static int forward_status_change(AVFilterContext *filter, AVFilterLink *in) if (!progress) { /* Every output already closed: input no longer interesting (example: overlay in shortest mode, other input closed). */ - ff_avfilter_link_set_out_status(in, in->status_in, in->status_in_pts); + link_set_out_status(in, in->status_in, in->status_in_pts); return 0; } progress = 0; @@ -1244,7 +1254,7 @@ static int ff_filter_activate_default(AVFilterContext *filter) change is considered having already happened. It is set by the destination filter using - ff_avfilter_link_set_out_status(). + link_set_out_status(). Filters are activated according to the ready field, set using the ff_filter_set_ready(). Eventually, a priority queue will be used. @@ -1334,7 +1344,7 @@ int ff_inlink_acknowledge_status(AVFilterLink *link, int *rstatus, int64_t *rpts if (!link->status_in) return *rstatus = 0; *rstatus = link->status_out = link->status_in; - ff_update_link_current_pts(link, link->status_in_pts); + update_link_current_pts(link, link->status_in_pts); *rpts = link->current_pts; return 1; } @@ -1363,7 +1373,7 @@ int ff_inlink_check_available_samples(AVFilterLink *link, unsigned min) static void consume_update(AVFilterLink *link, const AVFrame *frame) { - ff_update_link_current_pts(link, frame->pts); + update_link_current_pts(link, frame->pts); ff_inlink_process_commands(link, frame); link->dst->is_disabled = !ff_inlink_evaluate_timeline_at_frame(link, frame); link->frame_count_out++; @@ -1473,7 +1483,11 @@ int ff_inlink_evaluate_timeline_at_frame(AVFilterLink *link, const AVFrame *fram { AVFilterContext *dstctx = link->dst; int64_t pts = frame->pts; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS int64_t pos = frame->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif if (!dstctx->enable_str) return 1; @@ -1482,7 +1496,9 @@ int ff_inlink_evaluate_timeline_at_frame(AVFilterLink *link, const AVFrame *fram dstctx->var_values[VAR_T] = pts == AV_NOPTS_VALUE ? NAN : pts * av_q2d(link->time_base); dstctx->var_values[VAR_W] = link->w; dstctx->var_values[VAR_H] = link->h; +#if FF_API_FRAME_PKT dstctx->var_values[VAR_POS] = pos == -1 ? NAN : pos; +#endif return fabs(av_expr_eval(dstctx->enable, dstctx->var_values, NULL)) >= 0.5; } @@ -1501,7 +1517,7 @@ void ff_inlink_set_status(AVFilterLink *link, int status) return; link->frame_wanted_out = 0; link->frame_blocked_in = 0; - ff_avfilter_link_set_out_status(link, status, AV_NOPTS_VALUE); + link_set_out_status(link, status, AV_NOPTS_VALUE); while (ff_framequeue_queued_frames(&link->fifo)) { AVFrame *frame = ff_framequeue_take(&link->fifo); av_frame_free(&frame); diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index c2ec7a4b5fc..d69381aed4f 100644 --- a/libavfilter/avfilter.h +++ b/libavfilter/avfilter.h @@ -76,16 +76,6 @@ typedef struct AVFilterPad AVFilterPad; typedef struct AVFilterFormats AVFilterFormats; typedef struct AVFilterChannelLayouts AVFilterChannelLayouts; -#if FF_API_PAD_COUNT -/** - * Get the number of elements in an AVFilter's inputs or outputs array. - * - * @deprecated Use avfilter_filter_pad_count() instead. - */ -attribute_deprecated -int avfilter_pad_count(const AVFilterPad *pads); -#endif - /** * Get the name of an AVFilterPad. * @@ -141,6 +131,11 @@ enum AVMediaType avfilter_pad_get_type(const AVFilterPad *pads, int pad_idx); * received by the filter on one of its inputs. */ #define AVFILTER_FLAG_METADATA_ONLY (1 << 3) + +/** + * The filter can create hardware frames using AVFilterContext.hw_device_ctx. + */ +#define AVFILTER_FLAG_HWDEVICE (1 << 4) /** * Some filters support a generic "enable" expression option that can be used * to enable or disable a filter in the timeline. Filters supporting this @@ -454,6 +449,10 @@ struct AVFilterContext { * in particular, a filter which consumes or processes hardware frames will * instead use the hw_frames_ctx field in AVFilterLink to carry the * hardware context information. + * + * May be set by the caller on filters flagged with AVFILTER_FLAG_HWDEVICE + * before initializing the filter with avfilter_init_str() or + * avfilter_init_dict(). */ AVBufferRef *hw_device_ctx; @@ -1118,6 +1117,317 @@ int avfilter_graph_parse2(AVFilterGraph *graph, const char *filters, AVFilterInOut **inputs, AVFilterInOut **outputs); +/** + * Parameters of a filter's input or output pad. + * + * Created as a child of AVFilterParams by avfilter_graph_segment_parse(). + * Freed in avfilter_graph_segment_free(). + */ +typedef struct AVFilterPadParams { + /** + * An av_malloc()'ed string containing the pad label. + * + * May be av_free()'d and set to NULL by the caller, in which case this pad + * will be treated as unlabeled for linking. + * May also be replaced by another av_malloc()'ed string. + */ + char *label; +} AVFilterPadParams; + +/** + * Parameters describing a filter to be created in a filtergraph. + * + * Created as a child of AVFilterGraphSegment by avfilter_graph_segment_parse(). + * Freed in avfilter_graph_segment_free(). + */ +typedef struct AVFilterParams { + /** + * The filter context. + * + * Created by avfilter_graph_segment_create_filters() based on + * AVFilterParams.filter_name and instance_name. + * + * Callers may also create the filter context manually, then they should + * av_free() filter_name and set it to NULL. Such AVFilterParams instances + * are then skipped by avfilter_graph_segment_create_filters(). + */ + AVFilterContext *filter; + + /** + * Name of the AVFilter to be used. + * + * An av_malloc()'ed string, set by avfilter_graph_segment_parse(). Will be + * passed to avfilter_get_by_name() by + * avfilter_graph_segment_create_filters(). + * + * Callers may av_free() this string and replace it with another one or + * NULL. If the caller creates the filter instance manually, this string + * MUST be set to NULL. + * + * When both AVFilterParams.filter an AVFilterParams.filter_name are NULL, + * this AVFilterParams instance is skipped by avfilter_graph_segment_*() + * functions. + */ + char *filter_name; + /** + * Name to be used for this filter instance. + * + * An av_malloc()'ed string, may be set by avfilter_graph_segment_parse() or + * left NULL. The caller may av_free() this string and replace with another + * one or NULL. + * + * Will be used by avfilter_graph_segment_create_filters() - passed as the + * third argument to avfilter_graph_alloc_filter(), then freed and set to + * NULL. + */ + char *instance_name; + + /** + * Options to be apllied to the filter. + * + * Filled by avfilter_graph_segment_parse(). Afterwards may be freely + * modified by the caller. + * + * Will be applied to the filter by avfilter_graph_segment_apply_opts() + * with an equivalent of av_opt_set_dict2(filter, &opts, AV_OPT_SEARCH_CHILDREN), + * i.e. any unapplied options will be left in this dictionary. + */ + AVDictionary *opts; + + AVFilterPadParams **inputs; + unsigned nb_inputs; + + AVFilterPadParams **outputs; + unsigned nb_outputs; +} AVFilterParams; + +/** + * A filterchain is a list of filter specifications. + * + * Created as a child of AVFilterGraphSegment by avfilter_graph_segment_parse(). + * Freed in avfilter_graph_segment_free(). + */ +typedef struct AVFilterChain { + AVFilterParams **filters; + size_t nb_filters; +} AVFilterChain; + +/** + * A parsed representation of a filtergraph segment. + * + * A filtergraph segment is conceptually a list of filterchains, with some + * supplementary information (e.g. format conversion flags). + * + * Created by avfilter_graph_segment_parse(). Must be freed with + * avfilter_graph_segment_free(). + */ +typedef struct AVFilterGraphSegment { + /** + * The filtergraph this segment is associated with. + * Set by avfilter_graph_segment_parse(). + */ + AVFilterGraph *graph; + + /** + * A list of filter chain contained in this segment. + * Set in avfilter_graph_segment_parse(). + */ + AVFilterChain **chains; + size_t nb_chains; + + /** + * A string containing a colon-separated list of key=value options applied + * to all scale filters in this segment. + * + * May be set by avfilter_graph_segment_parse(). + * The caller may free this string with av_free() and replace it with a + * different av_malloc()'ed string. + */ + char *scale_sws_opts; +} AVFilterGraphSegment; + +/** + * Parse a textual filtergraph description into an intermediate form. + * + * This intermediate representation is intended to be modified by the caller as + * described in the documentation of AVFilterGraphSegment and its children, and + * then applied to the graph either manually or with other + * avfilter_graph_segment_*() functions. See the documentation for + * avfilter_graph_segment_apply() for the canonical way to apply + * AVFilterGraphSegment. + * + * @param graph Filter graph the parsed segment is associated with. Will only be + * used for logging and similar auxiliary purposes. The graph will + * not be actually modified by this function - the parsing results + * are instead stored in seg for further processing. + * @param graph_str a string describing the filtergraph segment + * @param flags reserved for future use, caller must set to 0 for now + * @param seg A pointer to the newly-created AVFilterGraphSegment is written + * here on success. The graph segment is owned by the caller and must + * be freed with avfilter_graph_segment_free() before graph itself is + * freed. + * + * @retval "non-negative number" success + * @retval "negative error code" failure + */ +int avfilter_graph_segment_parse(AVFilterGraph *graph, const char *graph_str, + int flags, AVFilterGraphSegment **seg); + +/** + * Create filters specified in a graph segment. + * + * Walk through the creation-pending AVFilterParams in the segment and create + * new filter instances for them. + * Creation-pending params are those where AVFilterParams.filter_name is + * non-NULL (and hence AVFilterParams.filter is NULL). All other AVFilterParams + * instances are ignored. + * + * For any filter created by this function, the corresponding + * AVFilterParams.filter is set to the newly-created filter context, + * AVFilterParams.filter_name and AVFilterParams.instance_name are freed and set + * to NULL. + * + * @param seg the filtergraph segment to process + * @param flags reserved for future use, caller must set to 0 for now + * + * @retval "non-negative number" Success, all creation-pending filters were + * successfully created + * @retval AVERROR_FILTER_NOT_FOUND some filter's name did not correspond to a + * known filter + * @retval "another negative error code" other failures + * + * @note Calling this function multiple times is safe, as it is idempotent. + */ +int avfilter_graph_segment_create_filters(AVFilterGraphSegment *seg, int flags); + +/** + * Apply parsed options to filter instances in a graph segment. + * + * Walk through all filter instances in the graph segment that have option + * dictionaries associated with them and apply those options with + * av_opt_set_dict2(..., AV_OPT_SEARCH_CHILDREN). AVFilterParams.opts is + * replaced by the dictionary output by av_opt_set_dict2(), which should be + * empty (NULL) if all options were successfully applied. + * + * If any options could not be found, this function will continue processing all + * other filters and finally return AVERROR_OPTION_NOT_FOUND (unless another + * error happens). The calling program may then deal with unapplied options as + * it wishes. + * + * Any creation-pending filters (see avfilter_graph_segment_create_filters()) + * present in the segment will cause this function to fail. AVFilterParams with + * no associated filter context are simply skipped. + * + * @param seg the filtergraph segment to process + * @param flags reserved for future use, caller must set to 0 for now + * + * @retval "non-negative number" Success, all options were successfully applied. + * @retval AVERROR_OPTION_NOT_FOUND some options were not found in a filter + * @retval "another negative error code" other failures + * + * @note Calling this function multiple times is safe, as it is idempotent. + */ +int avfilter_graph_segment_apply_opts(AVFilterGraphSegment *seg, int flags); + +/** + * Initialize all filter instances in a graph segment. + * + * Walk through all filter instances in the graph segment and call + * avfilter_init_dict(..., NULL) on those that have not been initialized yet. + * + * Any creation-pending filters (see avfilter_graph_segment_create_filters()) + * present in the segment will cause this function to fail. AVFilterParams with + * no associated filter context or whose filter context is already initialized, + * are simply skipped. + * + * @param seg the filtergraph segment to process + * @param flags reserved for future use, caller must set to 0 for now + * + * @retval "non-negative number" Success, all filter instances were successfully + * initialized + * @retval "negative error code" failure + * + * @note Calling this function multiple times is safe, as it is idempotent. + */ +int avfilter_graph_segment_init(AVFilterGraphSegment *seg, int flags); + +/** + * Link filters in a graph segment. + * + * Walk through all filter instances in the graph segment and try to link all + * unlinked input and output pads. Any creation-pending filters (see + * avfilter_graph_segment_create_filters()) present in the segment will cause + * this function to fail. Disabled filters and already linked pads are skipped. + * + * Every filter output pad that has a corresponding AVFilterPadParams with a + * non-NULL label is + * - linked to the input with the matching label, if one exists; + * - exported in the outputs linked list otherwise, with the label preserved. + * Unlabeled outputs are + * - linked to the first unlinked unlabeled input in the next non-disabled + * filter in the chain, if one exists + * - exported in the ouputs linked list otherwise, with NULL label + * + * Similarly, unlinked input pads are exported in the inputs linked list. + * + * @param seg the filtergraph segment to process + * @param flags reserved for future use, caller must set to 0 for now + * @param[out] inputs a linked list of all free (unlinked) inputs of the + * filters in this graph segment will be returned here. It + * is to be freed by the caller using avfilter_inout_free(). + * @param[out] outputs a linked list of all free (unlinked) outputs of the + * filters in this graph segment will be returned here. It + * is to be freed by the caller using avfilter_inout_free(). + * + * @retval "non-negative number" success + * @retval "negative error code" failure + * + * @note Calling this function multiple times is safe, as it is idempotent. + */ +int avfilter_graph_segment_link(AVFilterGraphSegment *seg, int flags, + AVFilterInOut **inputs, + AVFilterInOut **outputs); + +/** + * Apply all filter/link descriptions from a graph segment to the associated filtergraph. + * + * This functions is currently equivalent to calling the following in sequence: + * - avfilter_graph_segment_create_filters(); + * - avfilter_graph_segment_apply_opts(); + * - avfilter_graph_segment_init(); + * - avfilter_graph_segment_link(); + * failing if any of them fails. This list may be extended in the future. + * + * Since the above functions are idempotent, the caller may call some of them + * manually, then do some custom processing on the filtergraph, then call this + * function to do the rest. + * + * @param seg the filtergraph segment to process + * @param flags reserved for future use, caller must set to 0 for now + * @param[out] inputs passed to avfilter_graph_segment_link() + * @param[out] outputs passed to avfilter_graph_segment_link() + * + * @retval "non-negative number" success + * @retval "negative error code" failure + * + * @note Calling this function multiple times is safe, as it is idempotent. + */ +int avfilter_graph_segment_apply(AVFilterGraphSegment *seg, int flags, + AVFilterInOut **inputs, + AVFilterInOut **outputs); + +/** + * Free the provided AVFilterGraphSegment and everything associated with it. + * + * @param seg double pointer to the AVFilterGraphSegment to be freed. NULL will + * be written to this pointer on exit from this function. + * + * @note + * The filter contexts (AVFilterParams.filter) are owned by AVFilterGraph rather + * than AVFilterGraphSegment, so they are not freed. + */ +void avfilter_graph_segment_free(AVFilterGraphSegment **seg); + /** * Send a command to one or more filter instances. * diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c index 53f468494d3..68daa93e614 100644 --- a/libavfilter/avfiltergraph.c +++ b/libavfilter/avfiltergraph.c @@ -441,7 +441,7 @@ static int query_formats(AVFilterGraph *graph, void *log_ctx) neg = ff_filter_get_negotiation(link); av_assert0(neg); - for (neg_step = 1; neg_step < neg->nb_mergers; neg_step++) { + for (neg_step = 0; neg_step < neg->nb_mergers; neg_step++) { const AVFilterFormatsMerger *m = &neg->mergers[neg_step]; void *a = FF_FIELD_AT(void *, m->offset, link->incfg); void *b = FF_FIELD_AT(void *, m->offset, link->outcfg); @@ -748,8 +748,10 @@ static int reduce_formats_on_filter(AVFilterContext *filter) (KNOWN(fmt) || fmts->all_counts)) { /* Turn the infinite list into a singleton */ fmts->all_layouts = fmts->all_counts = 0; - if (ff_add_channel_layout(&outlink->incfg.channel_layouts, fmt) < 0) - ret = 1; + ret = ff_add_channel_layout(&outlink->incfg.channel_layouts, fmt); + if (ret < 0) + return ret; + ret = 1; break; } @@ -1301,7 +1303,6 @@ int avfilter_graph_request_oldest(AVFilterGraph *graph) while (graph->sink_links_count) { oldest = graph->sink_links[0]; if (oldest->dst->filter->activate) { - /* For now, buffersink is the only filter implementing activate. */ r = av_buffersink_get_frame_flags(oldest->dst, NULL, AV_BUFFERSINK_FLAG_PEEK); if (r != AVERROR_EOF) diff --git a/libavfilter/blend_modes.c b/libavfilter/blend_modes.c index 0ff96a5b03f..e2be676243a 100644 --- a/libavfilter/blend_modes.c +++ b/libavfilter/blend_modes.c @@ -21,7 +21,6 @@ #include "libavutil/common.h" #include "libavutil/intfloat.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "blend.h" diff --git a/libavfilter/buffersink.c b/libavfilter/buffersink.c index e269cf72d1b..9426ded7ee3 100644 --- a/libavfilter/buffersink.c +++ b/libavfilter/buffersink.c @@ -37,7 +37,9 @@ #include "avfilter.h" #include "buffersink.h" #include "filters.h" +#include "formats.h" #include "internal.h" +#include "video.h" typedef struct BufferSinkContext { const AVClass *class; @@ -154,28 +156,6 @@ int attribute_align_arg av_buffersink_get_samples(AVFilterContext *ctx, return get_frame_internal(ctx, frame, 0, nb_samples); } -#if FF_API_BUFFERSINK_ALLOC -AVBufferSinkParams *av_buffersink_params_alloc(void) -{ - static const int pixel_fmts[] = { AV_PIX_FMT_NONE }; - AVBufferSinkParams *params = av_malloc(sizeof(AVBufferSinkParams)); - if (!params) - return NULL; - - params->pixel_fmts = pixel_fmts; - return params; -} - -AVABufferSinkParams *av_abuffersink_params_alloc(void) -{ - AVABufferSinkParams *params = av_mallocz(sizeof(AVABufferSinkParams)); - - if (!params) - return NULL; - return params; -} -#endif - static av_cold int common_init(AVFilterContext *ctx) { BufferSinkContext *buf = ctx->priv; @@ -399,13 +379,6 @@ static const AVOption abuffersink_options[] = { AVFILTER_DEFINE_CLASS(buffersink); AVFILTER_DEFINE_CLASS(abuffersink); -static const AVFilterPad avfilter_vsink_buffer_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vsink_buffer = { .name = "buffersink", .description = NULL_IF_CONFIG_SMALL("Buffer video frames, and make them available to the end of the filter graph."), @@ -413,18 +386,11 @@ const AVFilter ff_vsink_buffer = { .priv_class = &buffersink_class, .init = common_init, .activate = activate, - FILTER_INPUTS(avfilter_vsink_buffer_inputs), + FILTER_INPUTS(ff_video_default_filterpad), .outputs = NULL, FILTER_QUERY_FUNC(vsink_query_formats), }; -static const AVFilterPad avfilter_asink_abuffer_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_asink_abuffer = { .name = "abuffersink", .description = NULL_IF_CONFIG_SMALL("Buffer audio frames, and make them available to the end of the filter graph."), @@ -432,7 +398,7 @@ const AVFilter ff_asink_abuffer = { .priv_size = sizeof(BufferSinkContext), .init = common_init, .activate = activate, - FILTER_INPUTS(avfilter_asink_abuffer_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), .outputs = NULL, FILTER_QUERY_FUNC(asink_query_formats), }; diff --git a/libavfilter/buffersink.h b/libavfilter/buffersink.h index 01e7c747d8e..64e08de53ee 100644 --- a/libavfilter/buffersink.h +++ b/libavfilter/buffersink.h @@ -94,42 +94,6 @@ int av_buffersink_get_frame_flags(AVFilterContext *ctx, AVFrame *frame, int flag */ #define AV_BUFFERSINK_FLAG_NO_REQUEST 2 -#if FF_API_BUFFERSINK_ALLOC -/** - * Deprecated and unused struct to use for initializing a buffersink context. - */ -typedef struct AVBufferSinkParams { - const enum AVPixelFormat *pixel_fmts; ///< list of allowed pixel formats, terminated by AV_PIX_FMT_NONE -} AVBufferSinkParams; - -/** - * Create an AVBufferSinkParams structure. - * - * Must be freed with av_free(). - */ -attribute_deprecated -AVBufferSinkParams *av_buffersink_params_alloc(void); - -/** - * Deprecated and unused struct to use for initializing an abuffersink context. - */ -typedef struct AVABufferSinkParams { - const enum AVSampleFormat *sample_fmts; ///< list of allowed sample formats, terminated by AV_SAMPLE_FMT_NONE - const int64_t *channel_layouts; ///< list of allowed channel layouts, terminated by -1 - const int *channel_counts; ///< list of allowed channel counts, terminated by -1 - int all_channel_counts; ///< if not 0, accept any channel count or layout - int *sample_rates; ///< list of allowed sample rates, terminated by -1 -} AVABufferSinkParams; - -/** - * Create an AVABufferSinkParams structure. - * - * Must be freed with av_free(). - */ -attribute_deprecated -AVABufferSinkParams *av_abuffersink_params_alloc(void); -#endif - /** * Set the frame size for an audio buffer sink. * diff --git a/libavfilter/buffersrc.c b/libavfilter/buffersrc.c index ae8bba19b07..ea507137011 100644 --- a/libavfilter/buffersrc.c +++ b/libavfilter/buffersrc.c @@ -50,9 +50,6 @@ typedef struct BufferSourceContext { int w, h; enum AVPixelFormat pix_fmt; AVRational pixel_aspect; -#if FF_API_SWS_PARAM_OPTION - char *sws_param; -#endif AVBufferRef *hw_frames_ctx; @@ -64,6 +61,7 @@ typedef struct BufferSourceContext { AVChannelLayout ch_layout; int eof; + int64_t last_pts; } BufferSourceContext; #define CHECK_VIDEO_PARAM_CHANGE(s, c, width, height, format, pts)\ @@ -194,10 +192,12 @@ FF_ENABLE_DEPRECATION_WARNINGS s->nb_failed_requests = 0; if (!frame) - return av_buffersrc_close(ctx, AV_NOPTS_VALUE, flags); + return av_buffersrc_close(ctx, s->last_pts, flags); if (s->eof) return AVERROR(EINVAL); + s->last_pts = frame->pts + frame->duration; + refcounted = !!frame->buf[0]; if (!(flags & AV_BUFFERSRC_FLAG_NO_CHECK_FORMAT)) { @@ -248,6 +248,22 @@ FF_DISABLE_DEPRECATION_WARNINGS if (copy->pkt_duration && copy->pkt_duration != copy->duration) copy->duration = copy->pkt_duration; FF_ENABLE_DEPRECATION_WARNINGS +#endif + +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS + if (copy->interlaced_frame) + copy->flags |= AV_FRAME_FLAG_INTERLACED; + if (copy->top_field_first) + copy->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + +#if FF_API_FRAME_KEY +FF_DISABLE_DEPRECATION_WARNINGS + if (copy->key_frame) + copy->flags |= AV_FRAME_FLAG_KEY; +FF_ENABLE_DEPRECATION_WARNINGS #endif ret = ff_filter_frame(ctx->outputs[0], copy); @@ -276,9 +292,16 @@ static av_cold int init_video(AVFilterContext *ctx) { BufferSourceContext *c = ctx->priv; - if (c->pix_fmt == AV_PIX_FMT_NONE || !c->w || !c->h || - av_q2d(c->time_base) <= 0) { - av_log(ctx, AV_LOG_ERROR, "Invalid parameters provided.\n"); + if (c->pix_fmt == AV_PIX_FMT_NONE) { + av_log(ctx, AV_LOG_ERROR, "Unspecified pixel format\n"); + return AVERROR(EINVAL); + } + if (c->w <= 0 || c->h <= 0) { + av_log(ctx, AV_LOG_ERROR, "Invalid size %dx%d\n", c->w, c->h); + return AVERROR(EINVAL); + } + if (av_q2d(c->time_base) <= 0) { + av_log(ctx, AV_LOG_ERROR, "Invalid time base %d/%d\n", c->time_base.num, c->time_base.den); return AVERROR(EINVAL); } @@ -287,11 +310,6 @@ static av_cold int init_video(AVFilterContext *ctx) c->time_base.num, c->time_base.den, c->frame_rate.num, c->frame_rate.den, c->pixel_aspect.num, c->pixel_aspect.den); -#if FF_API_SWS_PARAM_OPTION - if (c->sws_param) - av_log(ctx, AV_LOG_WARNING, "sws_param option is deprecated and ignored\n"); -#endif - return 0; } @@ -313,9 +331,6 @@ static const AVOption buffer_options[] = { { "pixel_aspect", "sample aspect ratio", OFFSET(pixel_aspect), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, DBL_MAX, V }, { "time_base", NULL, OFFSET(time_base), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, DBL_MAX, V }, { "frame_rate", NULL, OFFSET(frame_rate), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, DBL_MAX, V }, -#if FF_API_SWS_PARAM_OPTION - { "sws_param", NULL, OFFSET(sws_param), AV_OPT_TYPE_STRING, .flags = V }, -#endif { NULL }, }; diff --git a/libavfilter/bwdif.h b/libavfilter/bwdif.h index 889ff772edd..496cec72ef0 100644 --- a/libavfilter/bwdif.h +++ b/libavfilter/bwdif.h @@ -35,8 +35,29 @@ typedef struct BWDIFContext { void (*filter_edge)(void *dst, void *prev, void *cur, void *next, int w, int prefs, int mrefs, int prefs2, int mrefs2, int parity, int clip_max, int spat); + void (*filter_line3)(void *dst, int dstride, + const void *prev, const void *cur, const void *next, int prefs, + int w, int parity, int clip_max); } BWDIFContext; -void ff_bwdif_init_x86(BWDIFContext *bwdif); +void ff_bwdif_init_filter_line(BWDIFContext *bwdif, int bit_depth); +void ff_bwdif_init_x86(BWDIFContext *bwdif, int bit_depth); +void ff_bwdif_init_aarch64(BWDIFContext *bwdif, int bit_depth); + +void ff_bwdif_filter_edge_c(void *dst1, void *prev1, void *cur1, void *next1, + int w, int prefs, int mrefs, int prefs2, int mrefs2, + int parity, int clip_max, int spat); + +void ff_bwdif_filter_intra_c(void *dst1, void *cur1, int w, int prefs, int mrefs, + int prefs3, int mrefs3, int parity, int clip_max); + +void ff_bwdif_filter_line_c(void *dst1, void *prev1, void *cur1, void *next1, + int w, int prefs, int mrefs, int prefs2, int mrefs2, + int prefs3, int mrefs3, int prefs4, int mrefs4, + int parity, int clip_max); + +void ff_bwdif_filter_line3_c(void * dst1, int d_stride, + const void * prev1, const void * cur1, const void * next1, int s_stride, + int w, int parity, int clip_max); #endif /* AVFILTER_BWDIF_H */ diff --git a/libavfilter/ccfifo.c b/libavfilter/ccfifo.c new file mode 100644 index 00000000000..6ae61a4b15b --- /dev/null +++ b/libavfilter/ccfifo.c @@ -0,0 +1,205 @@ +/* + * CEA-708 Closed Captioning FIFO + * Copyright (c) 2023 LTN Global Communications + * + * Author: Devin Heitmueller + * + * 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 "ccfifo.h" + +#define MAX_CC_ELEMENTS 128 +#define CC_BYTES_PER_ENTRY 3 + +struct cc_lookup { + int num; + int den; + int cc_count; + int num_608; +}; + +const static struct cc_lookup cc_lookup_vals[] = { + { 15, 1, 40, 4 }, + { 24, 1, 25, 3 }, + { 24000, 1001, 25, 3 }, + { 30, 1, 20, 2 }, + { 30000, 1001, 20, 2}, + { 60, 1, 10, 1 }, + { 60000, 1001, 10, 1}, +}; + +void ff_ccfifo_uninit(CCFifo *ccf) +{ + av_fifo_freep2(&ccf->cc_608_fifo); + av_fifo_freep2(&ccf->cc_708_fifo); + memset(ccf, 0, sizeof(*ccf)); +} + +int ff_ccfifo_init(CCFifo *ccf, AVRational framerate, void *log_ctx) +{ + int i; + + memset(ccf, 0, sizeof(*ccf)); + ccf->log_ctx = log_ctx; + ccf->framerate = framerate; + + if (!(ccf->cc_708_fifo = av_fifo_alloc2(MAX_CC_ELEMENTS, CC_BYTES_PER_ENTRY, 0))) + goto error; + + if (!(ccf->cc_608_fifo = av_fifo_alloc2(MAX_CC_ELEMENTS, CC_BYTES_PER_ENTRY, 0))) + goto error; + + /* Based on the target FPS, figure out the expected cc_count and number of + 608 tuples per packet. See ANSI/CTA-708-E Sec 4.3.6.1. */ + for (i = 0; i < FF_ARRAY_ELEMS(cc_lookup_vals); i++) { + if (framerate.num == cc_lookup_vals[i].num && + framerate.den == cc_lookup_vals[i].den) { + ccf->expected_cc_count = cc_lookup_vals[i].cc_count; + ccf->expected_608 = cc_lookup_vals[i].num_608; + break; + } + } + + if (ccf->expected_608 == 0) { + /* We didn't find an output frame we support. We'll let the call succeed + and the FIFO to be allocated, but the extract/inject functions will simply + leave everything the way it is */ + ccf->passthrough = 1; + } + + return 0; + +error: + ff_ccfifo_uninit(ccf); + return AVERROR(ENOMEM); +} + +int ff_ccfifo_getoutputsize(const CCFifo *ccf) +{ + return ccf->expected_cc_count * CC_BYTES_PER_ENTRY; +} + +int ff_ccfifo_ccdetected(const CCFifo *ccf) +{ + return ccf->cc_detected; +} + +int ff_ccfifo_injectbytes(CCFifo *ccf, uint8_t *cc_data, size_t len) +{ + int cc_608_tuples = 0; + int cc_708_tuples = 0; + int cc_filled = 0; + + if (ccf->passthrough) { + return 0; + } + + if (len < ff_ccfifo_getoutputsize(ccf)) { + return AVERROR(EINVAL); + } + + /* Insert any available data from the 608 FIFO */ + if (ccf->expected_608 <= av_fifo_can_read(ccf->cc_608_fifo)) + cc_608_tuples = ccf->expected_608; + else + cc_608_tuples = av_fifo_can_read(ccf->cc_608_fifo); + av_fifo_read(ccf->cc_608_fifo, cc_data, cc_608_tuples); + cc_filled += cc_608_tuples; + + /* Insert any available data from the 708 FIFO */ + if ((ccf->expected_cc_count - cc_filled) <= av_fifo_can_read(ccf->cc_708_fifo)) + cc_708_tuples = ccf->expected_cc_count - cc_filled; + else + cc_708_tuples = av_fifo_can_read(ccf->cc_708_fifo); + av_fifo_read(ccf->cc_708_fifo, &cc_data[cc_filled * CC_BYTES_PER_ENTRY], cc_708_tuples); + cc_filled += cc_708_tuples; + + /* Insert 708 padding into any remaining fields */ + while (cc_filled < ccf->expected_cc_count) { + cc_data[cc_filled * CC_BYTES_PER_ENTRY] = 0xfa; + cc_data[cc_filled * CC_BYTES_PER_ENTRY + 1] = 0x00; + cc_data[cc_filled * CC_BYTES_PER_ENTRY + 2] = 0x00; + cc_filled++; + } + + return 0; +} + +int ff_ccfifo_inject(CCFifo *ccf, AVFrame *frame) +{ + AVFrameSideData *sd; + int ret; + + if (ccf->passthrough == 1 || ccf->cc_detected == 0) + return 0; + + sd = av_frame_new_side_data(frame, AV_FRAME_DATA_A53_CC, + ff_ccfifo_getoutputsize(ccf)); + if (sd) { + ret = ff_ccfifo_injectbytes(ccf, sd->data, sd->size); + if (ret < 0) { + av_frame_remove_side_data(frame, AV_FRAME_DATA_A53_CC); + return ret; + } + } + + return 0; +} + +int ff_ccfifo_extractbytes(CCFifo *ccf, uint8_t *cc_bytes, size_t len) +{ + int cc_count = len / CC_BYTES_PER_ENTRY; + + if (ccf->passthrough == 1) { + av_log_once(ccf->log_ctx, AV_LOG_WARNING, AV_LOG_DEBUG, &ccf->passthrough_warning, + "cc_fifo cannot transcode captions fps=%d/%d\n", + ccf->framerate.num, ccf->framerate.den); + return 0; + } + + ccf->cc_detected = 1; + + for (int i = 0; i < cc_count; i++) { + /* See ANSI/CTA-708-E Sec 4.3, Table 3 */ + uint8_t cc_valid = (cc_bytes[CC_BYTES_PER_ENTRY*i] & 0x04) >> 2; + uint8_t cc_type = cc_bytes[CC_BYTES_PER_ENTRY*i] & 0x03; + if (cc_type == 0x00 || cc_type == 0x01) { + av_fifo_write(ccf->cc_608_fifo, &cc_bytes[CC_BYTES_PER_ENTRY*i], 1); + } else if (cc_valid && (cc_type == 0x02 || cc_type == 0x03)) { + av_fifo_write(ccf->cc_708_fifo, &cc_bytes[CC_BYTES_PER_ENTRY*i], 1); + } + } + return 0; +} + +/* Read the A53 side data, discard padding, and put 608/708 into + queues so we can ensure they get into the output frames at + the correct rate... */ +int ff_ccfifo_extract(CCFifo *ccf, AVFrame *frame) +{ + AVFrameSideData *side_data = av_frame_get_side_data(frame, AV_FRAME_DATA_A53_CC); + if (side_data) { + ff_ccfifo_extractbytes(ccf, side_data->data, side_data->size); + + /* Remove the side data, as we will re-create it on the + output as needed */ + if (!ccf->passthrough) + av_frame_remove_side_data(frame, AV_FRAME_DATA_A53_CC); + } + return 0; +} diff --git a/libavfilter/ccfifo.h b/libavfilter/ccfifo.h new file mode 100644 index 00000000000..a3c302b6b29 --- /dev/null +++ b/libavfilter/ccfifo.h @@ -0,0 +1,118 @@ +/* + * CEA-708 Closed Captioning FIFO + * Copyright (c) 2023 LTN Global Communications + * + * Author: Devin Heitmueller + * + * 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 + * CC FIFO Buffer + */ + +#ifndef AVFILTER_CCFIFO_H +#define AVFILTER_CCFIFO_H + +#include "libavutil/avutil.h" +#include "libavutil/frame.h" +#include "libavutil/fifo.h" + +typedef struct CCFifo { + AVFifo *cc_608_fifo; + AVFifo *cc_708_fifo; + AVRational framerate; + int expected_cc_count; + int expected_608; + int cc_detected; + int passthrough; + int passthrough_warning; + void *log_ctx; +} CCFifo; + +/** + * Initialize a CCFifo. + * + * @param framerate output framerate + * @param log_ctx used for any av_log() calls + * @return Zero on success, or negative AVERROR code on failure. + */ +int ff_ccfifo_init(CCFifo *ccf, AVRational framerate, void *log_ctx); + +/** + * Free all memory allocated in a CCFifo and clear the context. + * + * @param ccf Pointer to the CCFifo which should be uninitialized + */ +void ff_ccfifo_uninit(CCFifo *ccf); + +/** + * Extract CC data from an AVFrame + * + * Extract CC bytes from the AVFrame, insert them into our queue, and + * remove the side data from the AVFrame. The side data is removed + * as it will be re-inserted at the appropriate rate later in the + * filter. + * + * @param af CCFifo to write to + * @param frame AVFrame with the video frame to operate on + * @return Zero on success, or negative AVERROR + * code on failure. + */ +int ff_ccfifo_extract(CCFifo *ccf, AVFrame *frame); + +/** + *Just like ff_ccfifo_extract(), but takes the raw bytes instead of an AVFrame + */ +int ff_ccfifo_extractbytes(CCFifo *ccf, uint8_t *data, size_t len); + +/** + * Provide the size in bytes of an output buffer to allocate + * + * Ask for how many bytes the output will contain, so the caller can allocate + * an appropriately sized buffer and pass it to ff_ccfifo_injectbytes() + * + */ +int ff_ccfifo_getoutputsize(const CCFifo *ccf); + +/** + * Insert CC data from the FIFO into an AVFrame (as side data) + * + * Dequeue the appropriate number of CC tuples based on the + * frame rate, and insert them into the AVFrame + * + * @param af CCFifo to read from + * @param frame AVFrame with the video frame to operate on + * @return Zero on success, or negative AVERROR + * code on failure. + */ +int ff_ccfifo_inject(CCFifo *ccf, AVFrame *frame); + +/** + * Just like ff_ccfifo_inject(), but takes the raw bytes to insert the CC data + * int rather than an AVFrame + */ +int ff_ccfifo_injectbytes(CCFifo *ccf, uint8_t *data, size_t len); + +/** + * Returns 1 if captions have been found as a prior call + * to ff_ccfifo_extract() or ff_ccfifo_extractbytes() + */ +int ff_ccfifo_ccdetected(const CCFifo *ccf); + +#endif /* AVFILTER_CCFIFO_H */ diff --git a/libavfilter/dnn/Makefile b/libavfilter/dnn/Makefile index 4cfbce0efc7..5d5697ea427 100644 --- a/libavfilter/dnn/Makefile +++ b/libavfilter/dnn/Makefile @@ -3,16 +3,6 @@ OBJS-$(CONFIG_DNN) += dnn/dnn_io_proc.o OBJS-$(CONFIG_DNN) += dnn/queue.o OBJS-$(CONFIG_DNN) += dnn/safe_queue.o OBJS-$(CONFIG_DNN) += dnn/dnn_backend_common.o -OBJS-$(CONFIG_DNN) += dnn/dnn_backend_native.o -OBJS-$(CONFIG_DNN) += dnn/dnn_backend_native_layers.o -OBJS-$(CONFIG_DNN) += dnn/dnn_backend_native_layer_avgpool.o -OBJS-$(CONFIG_DNN) += dnn/dnn_backend_native_layer_dense.o -OBJS-$(CONFIG_DNN) += dnn/dnn_backend_native_layer_pad.o -OBJS-$(CONFIG_DNN) += dnn/dnn_backend_native_layer_conv2d.o -OBJS-$(CONFIG_DNN) += dnn/dnn_backend_native_layer_depth2space.o -OBJS-$(CONFIG_DNN) += dnn/dnn_backend_native_layer_maximum.o -OBJS-$(CONFIG_DNN) += dnn/dnn_backend_native_layer_mathbinary.o -OBJS-$(CONFIG_DNN) += dnn/dnn_backend_native_layer_mathunary.o DNN-OBJS-$(CONFIG_LIBTENSORFLOW) += dnn/dnn_backend_tf.o DNN-OBJS-$(CONFIG_LIBOPENVINO) += dnn/dnn_backend_openvino.o diff --git a/libavfilter/dnn/dnn_backend_native.c b/libavfilter/dnn/dnn_backend_native.c deleted file mode 100644 index b53799f04d2..00000000000 --- a/libavfilter/dnn/dnn_backend_native.c +++ /dev/null @@ -1,561 +0,0 @@ -/* - * Copyright (c) 2018 Sergey Lavrushkin - * - * 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 - * DNN native backend implementation. - */ - -#include "dnn_backend_native.h" -#include "libavutil/avassert.h" -#include "dnn_backend_native_layer_conv2d.h" -#include "dnn_backend_native_layers.h" -#include "dnn_io_proc.h" -#include "dnn_backend_common.h" - -#define OFFSET(x) offsetof(NativeContext, x) -#define FLAGS AV_OPT_FLAG_FILTERING_PARAM -static const AVOption dnn_native_options[] = { - { "conv2d_threads", "threads num for conv2d layer", OFFSET(options.conv2d_threads), AV_OPT_TYPE_INT, { .i64 = 0 }, INT_MIN, INT_MAX, FLAGS }, - { "async", "use DNN async inference", OFFSET(options.async), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, - { NULL }, -}; - -static const AVClass dnn_native_class = { - .class_name = "dnn_native", - .item_name = av_default_item_name, - .option = dnn_native_options, - .version = LIBAVUTIL_VERSION_INT, - .category = AV_CLASS_CATEGORY_FILTER, -}; - -static int execute_model_native(Queue *lltask_queue); - -static int extract_lltask_from_task(TaskItem *task, Queue *lltask_queue) -{ - NativeModel *native_model = task->model; - NativeContext *ctx = &native_model->ctx; - LastLevelTaskItem *lltask = av_malloc(sizeof(*lltask)); - - if (!lltask) { - av_log(ctx, AV_LOG_ERROR, "Unable to allocate space for LastLevelTaskItem\n"); - return AVERROR(ENOMEM); - } - task->inference_todo = 1; - task->inference_done = 0; - lltask->task = task; - - if (ff_queue_push_back(lltask_queue, lltask) < 0) { - av_log(ctx, AV_LOG_ERROR, "Failed to push back lltask_queue.\n"); - av_freep(&lltask); - return AVERROR(ENOMEM); - } - return 0; -} - -static int get_input_native(void *model, DNNData *input, const char *input_name) -{ - NativeModel *native_model = model; - NativeContext *ctx = &native_model->ctx; - - for (int i = 0; i < native_model->operands_num; ++i) { - DnnOperand *oprd = &native_model->operands[i]; - if (strcmp(oprd->name, input_name) == 0) { - if (oprd->type != DOT_INPUT) { - av_log(ctx, AV_LOG_ERROR, "Found \"%s\" in model, but it is not input node\n", input_name); - return AVERROR(EINVAL); - } - input->dt = oprd->data_type; - av_assert0(oprd->dims[0] == 1); - input->height = oprd->dims[1]; - input->width = oprd->dims[2]; - input->channels = oprd->dims[3]; - return 0; - } - } - - // do not find the input operand - av_log(ctx, AV_LOG_ERROR, "Could not find \"%s\" in model\n", input_name); - return AVERROR(EINVAL); -} - -static int get_output_native(void *model, const char *input_name, int input_width, int input_height, - const char *output_name, int *output_width, int *output_height) -{ - int ret = 0; - NativeModel *native_model = model; - NativeContext *ctx = &native_model->ctx; - TaskItem task; - DNNExecBaseParams exec_params = { - .input_name = input_name, - .output_names = &output_name, - .nb_output = 1, - .in_frame = NULL, - .out_frame = NULL, - }; - - ret = ff_dnn_fill_gettingoutput_task(&task, &exec_params, native_model, input_height, input_width, ctx); - if (ret != 0) { - goto err; - } - - ret = extract_lltask_from_task(&task, native_model->lltask_queue); - if (ret != 0) { - av_log(ctx, AV_LOG_ERROR, "unable to extract last level task from task.\n"); - goto err; - } - - ret = execute_model_native(native_model->lltask_queue); - *output_width = task.out_frame->width; - *output_height = task.out_frame->height; - -err: - av_frame_free(&task.out_frame); - av_frame_free(&task.in_frame); - return ret; -} - -// Loads model and its parameters that are stored in a binary file with following structure: -// layers_num,layer_type,layer_parameterss,layer_type,layer_parameters... -// For CONV layer: activation_function, input_num, output_num, kernel_size, kernel, biases -// For DEPTH_TO_SPACE layer: block_size -DNNModel *ff_dnn_load_model_native(const char *model_filename, DNNFunctionType func_type, const char *options, AVFilterContext *filter_ctx) -{ -#define DNN_NATIVE_MAGIC "FFMPEGDNNNATIVE" - DNNModel *model = NULL; - // sizeof - 1 to skip the terminating '\0' which is not written in the file - char buf[sizeof(DNN_NATIVE_MAGIC) - 1]; - int version, header_size, major_version_expected = 1; - NativeModel *native_model = NULL; - AVIOContext *model_file_context; - int file_size, dnn_size, parsed_size; - int32_t layer; - DNNLayerType layer_type; - - if (avio_open(&model_file_context, model_filename, AVIO_FLAG_READ) < 0){ - return NULL; - } - file_size = avio_size(model_file_context); - - model = av_mallocz(sizeof(DNNModel)); - if (!model){ - goto fail; - } - - /** - * check file header with string and version - */ - if (avio_read(model_file_context, buf, sizeof(buf)) != sizeof(buf) || - memcmp(buf, DNN_NATIVE_MAGIC, sizeof(buf))) - goto fail; - dnn_size = sizeof(buf); - - version = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - if (version != major_version_expected) { - goto fail; - } - - // currently no need to check minor version - version = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - header_size = dnn_size; - - native_model = av_mallocz(sizeof(NativeModel)); - if (!native_model){ - goto fail; - } - model->model = native_model; - - native_model->ctx.class = &dnn_native_class; - model->options = options; - if (av_opt_set_from_string(&native_model->ctx, model->options, NULL, "=", "&") < 0) - goto fail; - native_model->model = model; - - if (native_model->ctx.options.async) { - av_log(&native_model->ctx, AV_LOG_WARNING, "Async not supported. Rolling back to sync\n"); - native_model->ctx.options.async = 0; - } - -#if !HAVE_PTHREAD_CANCEL - if (native_model->ctx.options.conv2d_threads > 1){ - av_log(&native_model->ctx, AV_LOG_WARNING, "'conv2d_threads' option was set but it is not supported " - "on this build (pthread support is required)\n"); - } -#endif - - avio_seek(model_file_context, file_size - 8, SEEK_SET); - native_model->layers_num = (int32_t)avio_rl32(model_file_context); - native_model->operands_num = (int32_t)avio_rl32(model_file_context); - dnn_size += 8; - avio_seek(model_file_context, header_size, SEEK_SET); - - native_model->layers = av_mallocz(native_model->layers_num * sizeof(Layer)); - if (!native_model->layers){ - goto fail; - } - - native_model->operands = av_mallocz(native_model->operands_num * sizeof(DnnOperand)); - if (!native_model->operands){ - goto fail; - } - - native_model->task_queue = ff_queue_create(); - if (!native_model->task_queue) { - goto fail; - } - - native_model->lltask_queue = ff_queue_create(); - if (!native_model->lltask_queue) { - goto fail; - } - - for (layer = 0; layer < native_model->layers_num; ++layer){ - layer_type = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - - if (layer_type >= DLT_COUNT) { - goto fail; - } - - native_model->layers[layer].type = layer_type; - parsed_size = ff_layer_funcs[layer_type].pf_load(&native_model->layers[layer], model_file_context, file_size, native_model->operands_num); - if (!parsed_size) { - goto fail; - } - dnn_size += parsed_size; - } - - for (int32_t i = 0; i < native_model->operands_num; ++i){ - DnnOperand *oprd; - int32_t name_len; - int32_t operand_index = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - - if (operand_index >= native_model->operands_num) { - goto fail; - } - - oprd = &native_model->operands[operand_index]; - name_len = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - - avio_get_str(model_file_context, name_len, oprd->name, sizeof(oprd->name)); - dnn_size += name_len; - - oprd->type = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - - oprd->data_type = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - - for (int32_t dim = 0; dim < 4; ++dim) { - oprd->dims[dim] = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - } - if (oprd->type == DOT_INPUT && oprd->dims[0] != 1) - goto fail; - - oprd->isNHWC = 1; - } - - avio_closep(&model_file_context); - - if (dnn_size != file_size){ - ff_dnn_free_model_native(&model); - return NULL; - } - - model->get_input = &get_input_native; - model->get_output = &get_output_native; - model->filter_ctx = filter_ctx; - model->func_type = func_type; - - return model; - -fail: - ff_dnn_free_model_native(&model); - avio_closep(&model_file_context); - return NULL; -} - -static int execute_model_native(Queue *lltask_queue) -{ - NativeModel *native_model = NULL; - NativeContext *ctx = NULL; - int32_t layer; - DNNData input, output; - DnnOperand *oprd = NULL; - LastLevelTaskItem *lltask = NULL; - TaskItem *task = NULL; - int ret = 0; - - lltask = ff_queue_pop_front(lltask_queue); - if (!lltask) { - av_log(NULL, AV_LOG_ERROR, "Failed to get LastLevelTaskItem\n"); - ret = AVERROR(EINVAL); - goto err; - } - task = lltask->task; - native_model = task->model; - ctx = &native_model->ctx; - - if (native_model->layers_num <= 0 || native_model->operands_num <= 0) { - av_log(ctx, AV_LOG_ERROR, "No operands or layers in model\n"); - ret = AVERROR(EINVAL); - goto err; - } - - for (int i = 0; i < native_model->operands_num; ++i) { - oprd = &native_model->operands[i]; - if (strcmp(oprd->name, task->input_name) == 0) { - if (oprd->type != DOT_INPUT) { - av_log(ctx, AV_LOG_ERROR, "Found \"%s\" in model, but it is not input node\n", task->input_name); - ret = AVERROR(EINVAL); - goto err; - } - break; - } - oprd = NULL; - } - if (!oprd) { - av_log(ctx, AV_LOG_ERROR, "Could not find \"%s\" in model\n", task->input_name); - ret = AVERROR(EINVAL); - goto err; - } - - oprd->dims[1] = task->in_frame->height; - oprd->dims[2] = task->in_frame->width; - - av_freep(&oprd->data); - oprd->length = ff_calculate_operand_data_length(oprd); - if (oprd->length <= 0) { - av_log(ctx, AV_LOG_ERROR, "The input data length overflow\n"); - ret = AVERROR(EINVAL); - goto err; - } - oprd->data = av_malloc(oprd->length); - if (!oprd->data) { - av_log(ctx, AV_LOG_ERROR, "Failed to malloc memory for input data\n"); - ret = AVERROR(ENOMEM); - goto err; - } - - input.height = oprd->dims[1]; - input.width = oprd->dims[2]; - input.channels = oprd->dims[3]; - input.data = oprd->data; - input.dt = oprd->data_type; - if (task->do_ioproc) { - if (native_model->model->frame_pre_proc != NULL) { - native_model->model->frame_pre_proc(task->in_frame, &input, native_model->model->filter_ctx); - } else { - ff_proc_from_frame_to_dnn(task->in_frame, &input, ctx); - } - } - - if (task->nb_output != 1) { - // currently, the filter does not need multiple outputs, - // so we just pending the support until we really need it. - avpriv_report_missing_feature(ctx, "multiple outputs"); - ret = AVERROR(ENOSYS); - goto err; - } - - for (layer = 0; layer < native_model->layers_num; ++layer){ - DNNLayerType layer_type = native_model->layers[layer].type; - ret = ff_layer_funcs[layer_type].pf_exec(native_model->operands, - native_model->layers[layer].input_operand_indexes, - native_model->layers[layer].output_operand_index, - native_model->layers[layer].params, - &native_model->ctx); - if (ret != 0) { - av_log(ctx, AV_LOG_ERROR, "Failed to execute model\n"); - goto err; - } - } - - for (uint32_t i = 0; i < task->nb_output; ++i) { - DnnOperand *oprd = NULL; - const char *output_name = task->output_names[i]; - for (int j = 0; j < native_model->operands_num; ++j) { - if (strcmp(native_model->operands[j].name, output_name) == 0) { - oprd = &native_model->operands[j]; - break; - } - } - - if (oprd == NULL) { - av_log(ctx, AV_LOG_ERROR, "Could not find output in model\n"); - ret = AVERROR(EINVAL); - goto err; - } - - output.data = oprd->data; - output.height = oprd->dims[1]; - output.width = oprd->dims[2]; - output.channels = oprd->dims[3]; - output.dt = oprd->data_type; - - if (task->do_ioproc) { - if (native_model->model->frame_post_proc != NULL) { - native_model->model->frame_post_proc(task->out_frame, &output, native_model->model->filter_ctx); - } else { - ff_proc_from_dnn_to_frame(task->out_frame, &output, ctx); - } - } else { - task->out_frame->width = output.width; - task->out_frame->height = output.height; - } - } - task->inference_done++; -err: - av_freep(&lltask); - return ret; -} - -int ff_dnn_execute_model_native(const DNNModel *model, DNNExecBaseParams *exec_params) -{ - NativeModel *native_model = model->model; - NativeContext *ctx = &native_model->ctx; - TaskItem *task; - int ret = 0; - - ret = ff_check_exec_params(ctx, DNN_NATIVE, model->func_type, exec_params); - if (ret != 0) { - return ret; - } - - task = av_malloc(sizeof(*task)); - if (!task) { - av_log(ctx, AV_LOG_ERROR, "unable to alloc memory for task item.\n"); - return AVERROR(ENOMEM); - } - - ret = ff_dnn_fill_task(task, exec_params, native_model, ctx->options.async, 1); - if (ret != 0) { - av_freep(&task); - return ret; - } - - if (ff_queue_push_back(native_model->task_queue, task) < 0) { - av_freep(&task); - av_log(ctx, AV_LOG_ERROR, "unable to push back task_queue.\n"); - return AVERROR(ENOMEM); - } - - ret = extract_lltask_from_task(task, native_model->lltask_queue); - if (ret != 0) { - av_log(ctx, AV_LOG_ERROR, "unable to extract last level task from task.\n"); - return ret; - } - - return execute_model_native(native_model->lltask_queue); -} - -int ff_dnn_flush_native(const DNNModel *model) -{ - NativeModel *native_model = model->model; - - if (ff_queue_size(native_model->lltask_queue) == 0) { - // no pending task need to flush - return 0; - } - - // for now, use sync node with flush operation - // Switch to async when it is supported - return execute_model_native(native_model->lltask_queue); -} - -DNNAsyncStatusType ff_dnn_get_result_native(const DNNModel *model, AVFrame **in, AVFrame **out) -{ - NativeModel *native_model = model->model; - return ff_dnn_get_result_common(native_model->task_queue, in, out); -} - -int32_t ff_calculate_operand_dims_count(const DnnOperand *oprd) -{ - int32_t result = 1; - for (int i = 0; i < 4; ++i) - result *= oprd->dims[i]; - - return result; -} - -int32_t ff_calculate_operand_data_length(const DnnOperand* oprd) -{ - // currently, we just support DNN_FLOAT - uint64_t len = sizeof(float); - for (int i = 0; i < 4; i++) { - len *= oprd->dims[i]; - if (len > INT32_MAX) - return 0; - } - return len; -} - -void ff_dnn_free_model_native(DNNModel **model) -{ - NativeModel *native_model; - ConvolutionalParams *conv_params; - int32_t layer; - - if (*model) - { - if ((*model)->model) { - native_model = (*model)->model; - if (native_model->layers) { - for (layer = 0; layer < native_model->layers_num; ++layer){ - if (native_model->layers[layer].type == DLT_CONV2D){ - conv_params = (ConvolutionalParams *)native_model->layers[layer].params; - av_freep(&conv_params->kernel); - av_freep(&conv_params->biases); - } - av_freep(&native_model->layers[layer].params); - } - av_freep(&native_model->layers); - } - - if (native_model->operands) { - for (uint32_t operand = 0; operand < native_model->operands_num; ++operand) - av_freep(&native_model->operands[operand].data); - av_freep(&native_model->operands); - } - - while (ff_queue_size(native_model->lltask_queue) != 0) { - LastLevelTaskItem *item = ff_queue_pop_front(native_model->lltask_queue); - av_freep(&item); - } - ff_queue_destroy(native_model->lltask_queue); - - while (ff_queue_size(native_model->task_queue) != 0) { - TaskItem *item = ff_queue_pop_front(native_model->task_queue); - av_frame_free(&item->in_frame); - av_frame_free(&item->out_frame); - av_freep(&item); - } - ff_queue_destroy(native_model->task_queue); - - av_freep(&native_model); - } - av_freep(model); - } -} diff --git a/libavfilter/dnn/dnn_backend_native.h b/libavfilter/dnn/dnn_backend_native.h deleted file mode 100644 index 75bd9a44f77..00000000000 --- a/libavfilter/dnn/dnn_backend_native.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (c) 2018 Sergey Lavrushkin - * - * 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 - * DNN inference functions interface for native backend. - */ - - -#ifndef AVFILTER_DNN_DNN_BACKEND_NATIVE_H -#define AVFILTER_DNN_DNN_BACKEND_NATIVE_H - -#include "../dnn_interface.h" -#include "libavformat/avio.h" -#include "libavutil/opt.h" -#include "queue.h" - -/** - * the enum value of DNNLayerType should not be changed, - * the same values are used in convert_from_tensorflow.py - * and, it is used to index the layer execution/load function pointer. - */ -typedef enum { - DLT_INPUT = 0, - DLT_CONV2D = 1, - DLT_DEPTH_TO_SPACE = 2, - DLT_MIRROR_PAD = 3, - DLT_MAXIMUM = 4, - DLT_MATH_BINARY = 5, - DLT_MATH_UNARY = 6, - DLT_AVG_POOL = 7, - DLT_DENSE = 8, - DLT_COUNT -} DNNLayerType; - -typedef enum {DOT_INPUT = 1, DOT_OUTPUT = 2, DOT_INTERMEDIATE = DOT_INPUT | DOT_OUTPUT} DNNOperandType; -typedef enum {VALID, SAME, SAME_CLAMP_TO_EDGE} DNNPaddingParam; -typedef enum {RELU, TANH, SIGMOID, NONE, LEAKY_RELU} DNNActivationFunc; - -typedef struct Layer{ - DNNLayerType type; - /** - * a layer can have multiple inputs and one output. - * 4 is just a big enough number for input operands (increase it if necessary), - * do not use 'int32_t *input_operand_indexes', so we don't worry about mem leaks. - */ - int32_t input_operand_indexes[4]; - int32_t output_operand_index; - void *params; -} Layer; - -typedef struct DnnOperand{ - /** - * there are two memory layouts, NHWC or NCHW, so we use dims, - * dims[0] is Number. - */ - int32_t dims[4]; - - /** - * input/output/intermediate operand of the network - */ - DNNOperandType type; - - /** - * support different kinds of data type such as float, half float, int8 etc, - * first support float now. - */ - DNNDataType data_type; - - /** - * NHWC if 1, otherwise NCHW. - * let's first support NHWC only, this flag is for extensive usage. - */ - int8_t isNHWC; - - /** - * to avoid possible memory leak, do not use char *name - */ - char name[128]; - - /** - * data pointer with data length in bytes. - * usedNumbersLeft is only valid for intermediate operand, - * it means how many layers still depend on this operand, - * todo: the memory can be reused when usedNumbersLeft is zero. - */ - void *data; - int32_t length; - int32_t usedNumbersLeft; -}DnnOperand; - -typedef struct InputParams{ - int height, width, channels; -} InputParams; - -typedef struct NativeOptions{ - uint8_t async; - uint32_t conv2d_threads; -} NativeOptions; - -typedef struct NativeContext { - const AVClass *class; - NativeOptions options; -} NativeContext; - -// Represents simple feed-forward convolutional network. -typedef struct NativeModel{ - NativeContext ctx; - DNNModel *model; - Layer *layers; - int32_t layers_num; - DnnOperand *operands; - int32_t operands_num; - Queue *task_queue; - Queue *lltask_queue; -} NativeModel; - -DNNModel *ff_dnn_load_model_native(const char *model_filename, DNNFunctionType func_type, const char *options, AVFilterContext *filter_ctx); - -int ff_dnn_execute_model_native(const DNNModel *model, DNNExecBaseParams *exec_params); - -DNNAsyncStatusType ff_dnn_get_result_native(const DNNModel *model, AVFrame **in, AVFrame **out); - -int ff_dnn_flush_native(const DNNModel *model); - -void ff_dnn_free_model_native(DNNModel **model); - -// NOTE: User must check for error (return value <= 0) to handle -// case like integer overflow. -int32_t ff_calculate_operand_data_length(const DnnOperand *oprd); -int32_t ff_calculate_operand_dims_count(const DnnOperand *oprd); -#endif diff --git a/libavfilter/dnn/dnn_backend_native_layer_avgpool.c b/libavfilter/dnn/dnn_backend_native_layer_avgpool.c deleted file mode 100644 index d6fcac8a355..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_avgpool.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (c) 2020 - * - * 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 - * DNN native backend implementation. - */ - -#include "libavutil/avassert.h" -#include "dnn_backend_native_layer_avgpool.h" - -int ff_dnn_load_layer_avg_pool(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num) -{ - AvgPoolParams *avgpool_params; - int dnn_size = 0; - avgpool_params = av_malloc(sizeof(*avgpool_params)); - if(!avgpool_params) - return 0; - - avgpool_params->strides = (int32_t)avio_rl32(model_file_context); - avgpool_params->padding_method = (int32_t)avio_rl32(model_file_context); - avgpool_params->kernel_size = (int32_t)avio_rl32(model_file_context); - dnn_size += 12; - - if (dnn_size > file_size || avgpool_params->kernel_size <= 0 || avgpool_params->strides <=0){ - av_freep(&avgpool_params); - return 0; - } - - layer->params = avgpool_params; - layer->input_operand_indexes[0] = (int32_t)avio_rl32(model_file_context); - layer->output_operand_index = (int32_t)avio_rl32(model_file_context); - dnn_size += 8; - - if (layer->input_operand_indexes[0] >= operands_num || layer->output_operand_index >= operands_num) { - return 0; - } - return dnn_size; -} - -int ff_dnn_execute_layer_avg_pool(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx) -{ - float *output; - int height_end, width_end, height_radius, width_radius, output_height, output_width, kernel_area; - int32_t input_operand_index = input_operand_indexes[0]; - int number = operands[input_operand_index].dims[0]; - int height = operands[input_operand_index].dims[1]; - int width = operands[input_operand_index].dims[2]; - int channel = operands[input_operand_index].dims[3]; - const float *input = operands[input_operand_index].data; - const AvgPoolParams *avgpool_params = parameters; - - int kernel_strides = avgpool_params->strides; - int src_linesize = width * channel; - DnnOperand *output_operand = &operands[output_operand_index]; - - /** - * When padding_method = SAME, the tensorflow will only padding the hald number of 0 pixels - * except the remainders. - * Eg: assuming the input height = 1080, the strides = 11, so the remainders = 1080 % 11 = 2 - * and if ksize = 5: it will fill (5 - 2) >> 1 = 1 line before the first line of input image, - * and 5 - 2 - 1 = 2 lines after the last line of input image. - * and if ksize = 7: it will fill (7 - 2) >> 1 = 2 lines before the first line of input image, - * and 7 - 2 - 2 = 3 lines after the last line of input image. - */ - if (avgpool_params->padding_method == SAME) { - height_end = height; - width_end = width; - height_radius = avgpool_params->kernel_size - ((height - 1) % kernel_strides + 1); - width_radius = avgpool_params->kernel_size - ((width - 1) % kernel_strides + 1); - height_radius = height_radius < 0 ? 0 : height_radius >> 1; - width_radius = width_radius < 0 ? 0 : width_radius >> 1; - output_height = ceil(height / (kernel_strides * 1.0)); - output_width = ceil(width / (kernel_strides * 1.0)); - } else { - av_assert0(avgpool_params->padding_method == VALID); - height_end = height - avgpool_params->kernel_size + 1; - width_end = width - avgpool_params->kernel_size + 1; - height_radius = 0; - width_radius = 0; - output_height = ceil((height - avgpool_params->kernel_size + 1) / (kernel_strides * 1.0)); - output_width = ceil((width - avgpool_params->kernel_size + 1) / (kernel_strides * 1.0)); - } - - output_operand->dims[0] = number; - output_operand->dims[1] = output_height; - output_operand->dims[2] = output_width; - // not support pooling in channel dimension now - output_operand->dims[3] = channel; - output_operand->data_type = operands[input_operand_index].data_type; - output_operand->length = ff_calculate_operand_data_length(output_operand); - if (output_operand->length <= 0) { - av_log(ctx, AV_LOG_ERROR, "The output data length overflow\n"); - return AVERROR(EINVAL); - } - output_operand->data = av_realloc(output_operand->data, output_operand->length); - if (!output_operand->data) { - av_log(ctx, AV_LOG_ERROR, "Failed to reallocate memory for output\n"); - return AVERROR(ENOMEM); - } - output = output_operand->data; - - for (int y = 0; y < height_end; y += kernel_strides) { - for (int x = 0; x < width_end; x += kernel_strides) { - for (int n_channel = 0; n_channel < channel; ++n_channel) { - output[n_channel] = 0.0; - kernel_area = 0; - for (int kernel_y = 0; kernel_y < avgpool_params->kernel_size; ++kernel_y) { - for (int kernel_x = 0; kernel_x < avgpool_params->kernel_size; ++kernel_x) { - float input_pel; - int y_pos = y + (kernel_y - height_radius); - int x_pos = x + (kernel_x - width_radius); - if (x_pos < 0 || x_pos >= width || y_pos < 0 || y_pos >= height) { - input_pel = 0.0; - } else { - kernel_area++; - input_pel = input[y_pos * src_linesize + x_pos * channel + n_channel]; - } - output[n_channel] += input_pel; - } - } - output[n_channel] /= kernel_area; - } - output += channel; - } - } - - return 0; -} diff --git a/libavfilter/dnn/dnn_backend_native_layer_avgpool.h b/libavfilter/dnn/dnn_backend_native_layer_avgpool.h deleted file mode 100644 index 118a160090f..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_avgpool.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2020 - * - * 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 - * DNN inference functions interface for native backend. - */ - -#ifndef AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_AVGPOOL_H -#define AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_AVGPOOL_H - -#include "dnn_backend_native.h" - -typedef struct AvgPoolParams{ - int32_t strides, kernel_size; - DNNPaddingParam padding_method; -} AvgPoolParams; - -/** - * @brief Load Average Pooling Layer. - * - * It assigns the Average Pooling layer with AvgPoolParams - * after parsing from the model file context. - * - * @param layer pointer to the DNN layer instance - * @param model_file_context pointer to model file context - * @param file_size model file size to check if data is read - * correctly from the model file - * @param operands_num operand count of the whole model to - * check if data is read correctly from the model file - * @return number of bytes read from the model file - * @retval 0 if out of memory or an error occurs - */ -int ff_dnn_load_layer_avg_pool(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num); - -/** - * @brief Execute the Average Pooling Layer. - * Padding in channel dimensions is currently not supported. - * - * @param operands all operands for the model - * @param input_operand_indexes input operand indexes for this layer - * @param output_operand_index output operand index for this layer - * @param parameters average pooling parameters - * @param ctx pointer to Native model context for logging - * @retval 0 if the execution succeeds - * @retval AVERROR(ENOMEM) if memory allocation fails - * @retval AVERROR(EINVAL) for invalid arguments - */ -int ff_dnn_execute_layer_avg_pool(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx); - -#endif diff --git a/libavfilter/dnn/dnn_backend_native_layer_conv2d.c b/libavfilter/dnn/dnn_backend_native_layer_conv2d.c deleted file mode 100644 index 2ac37d8855d..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_conv2d.c +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright (c) 2018 Sergey Lavrushkin - * - * 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 "libavutil/avassert.h" -#include "libavutil/thread.h" -#include "libavutil/cpu.h" -#include "dnn_backend_native_layer_conv2d.h" - -#define CLAMP_TO_EDGE(x, w) ((x) < 0 ? 0 : ((x) >= (w) ? (w - 1) : (x))) - -//struct to pass parameters -typedef struct ThreadCommonParam{ - DnnOperand *operands; - const int32_t *input_operand_indexes; - int32_t output_operand_index; - const void *parameters; - NativeContext *ctx; - float *output_data; -} ThreadCommonParam; - -typedef struct ThreadParam{ - ThreadCommonParam *thread_common_param; - int thread_start, thread_end; -#if HAVE_PTHREAD_CANCEL - pthread_t thread; -#endif -} ThreadParam; - -int ff_dnn_load_layer_conv2d(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num) -{ - ConvolutionalParams *conv_params; - int kernel_size; - int dnn_size = 0; - conv_params = av_malloc(sizeof(*conv_params)); - if (!conv_params) - return 0; - - conv_params->dilation = (int32_t)avio_rl32(model_file_context); - conv_params->padding_method = (int32_t)avio_rl32(model_file_context); - conv_params->activation = (int32_t)avio_rl32(model_file_context); - conv_params->input_num = (int32_t)avio_rl32(model_file_context); - conv_params->output_num = (int32_t)avio_rl32(model_file_context); - conv_params->kernel_size = (int32_t)avio_rl32(model_file_context); - conv_params->has_bias = (int32_t)avio_rl32(model_file_context); - dnn_size += 28; - - kernel_size = conv_params->input_num * conv_params->output_num * - conv_params->kernel_size * conv_params->kernel_size; - dnn_size += kernel_size * 4; - if (conv_params->has_bias) - dnn_size += conv_params->output_num * 4; - - if (dnn_size > file_size || conv_params->input_num <= 0 || - conv_params->output_num <= 0 || conv_params->kernel_size <= 0){ - av_freep(&conv_params); - return 0; - } - - conv_params->kernel = av_malloc_array(kernel_size, sizeof(*conv_params->kernel)); - if (!conv_params->kernel) { - av_freep(&conv_params); - return 0; - } - for (int i = 0; i < kernel_size; ++i) { - conv_params->kernel[i] = av_int2float(avio_rl32(model_file_context)); - } - - conv_params->biases = NULL; - if (conv_params->has_bias) { - conv_params->biases = av_malloc_array(conv_params->output_num, sizeof(*conv_params->biases)); - if (!conv_params->biases){ - av_freep(&conv_params->kernel); - av_freep(&conv_params); - return 0; - } - for (int i = 0; i < conv_params->output_num; ++i){ - conv_params->biases[i] = av_int2float(avio_rl32(model_file_context)); - } - } - - layer->params = conv_params; - - layer->input_operand_indexes[0] = (int32_t)avio_rl32(model_file_context); - layer->output_operand_index = (int32_t)avio_rl32(model_file_context); - dnn_size += 8; - - if (layer->input_operand_indexes[0] >= operands_num || layer->output_operand_index >= operands_num) { - return 0; - } - - return dnn_size; -} - -static void * dnn_execute_layer_conv2d_thread(void *threadarg) -{ - //pass parameters - ThreadParam *thread_param = threadarg; - ThreadCommonParam *thread_common_param = thread_param->thread_common_param; - DnnOperand *operands = thread_common_param->operands; - int32_t input_operand_index = thread_common_param->input_operand_indexes[0]; - int height = operands[input_operand_index].dims[1]; - int width = operands[input_operand_index].dims[2]; - int channel = operands[input_operand_index].dims[3]; - const float *input = operands[input_operand_index].data; - const ConvolutionalParams *conv_params = thread_common_param->parameters; - - int radius = conv_params->kernel_size >> 1; - int src_linesize = width * conv_params->input_num; - int filter_linesize = conv_params->kernel_size * conv_params->input_num; - int filter_size = conv_params->kernel_size * filter_linesize; - int pad_size = (conv_params->padding_method == VALID) ? (conv_params->kernel_size - 1) / 2 * conv_params->dilation : 0; - - float *output = thread_common_param->output_data; - output += (conv_params->output_num) * (width - 2 * pad_size) * (thread_param->thread_start - pad_size); - - av_assert0(channel == conv_params->input_num); - - for (int y = thread_param->thread_start; y < thread_param->thread_end; ++y) { - for (int x = pad_size; x < width - pad_size; ++x) { - for (int n_filter = 0; n_filter < conv_params->output_num; ++n_filter) { - if (conv_params->has_bias) - output[n_filter] = conv_params->biases[n_filter]; - else - output[n_filter] = 0.f; - - for (int ch = 0; ch < conv_params->input_num; ++ch) { - for (int kernel_y = 0; kernel_y < conv_params->kernel_size; ++kernel_y) { - for (int kernel_x = 0; kernel_x < conv_params->kernel_size; ++kernel_x) { - float input_pel; - if (conv_params->padding_method == SAME_CLAMP_TO_EDGE) { - int y_pos = CLAMP_TO_EDGE(y + (kernel_y - radius) * conv_params->dilation, height); - int x_pos = CLAMP_TO_EDGE(x + (kernel_x - radius) * conv_params->dilation, width); - input_pel = input[y_pos * src_linesize + x_pos * conv_params->input_num + ch]; - } else { - int y_pos = y + (kernel_y - radius) * conv_params->dilation; - int x_pos = x + (kernel_x - radius) * conv_params->dilation; - input_pel = (x_pos < 0 || x_pos >= width || y_pos < 0 || y_pos >= height) ? 0.0 : - input[y_pos * src_linesize + x_pos * conv_params->input_num + ch]; - } - - - output[n_filter] += input_pel * conv_params->kernel[n_filter * filter_size + kernel_y * filter_linesize + - kernel_x * conv_params->input_num + ch]; - } - } - } - switch (conv_params->activation){ - case RELU: - output[n_filter] = FFMAX(output[n_filter], 0.0); - break; - case TANH: - output[n_filter] = 2.0f / (1.0f + exp(-2.0f * output[n_filter])) - 1.0f; - break; - case SIGMOID: - output[n_filter] = 1.0f / (1.0f + exp(-output[n_filter])); - break; - case NONE: - break; - case LEAKY_RELU: - output[n_filter] = FFMAX(output[n_filter], 0.0) + 0.2 * FFMIN(output[n_filter], 0.0); - } - } - output += conv_params->output_num; - } - } - return NULL; -} - - -int ff_dnn_execute_layer_conv2d(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx) -{ -#if HAVE_PTHREAD_CANCEL - int thread_num = (ctx->options.conv2d_threads <= 0 || ctx->options.conv2d_threads > av_cpu_count()) - ? (av_cpu_count() + 1) : (ctx->options.conv2d_threads); - int ret = 0, thread_stride; - ThreadParam *thread_param; -#else - ThreadParam thread_param = { 0 }; -#endif - ThreadCommonParam thread_common_param; - const ConvolutionalParams *conv_params = parameters; - int height = operands[input_operand_indexes[0]].dims[1]; - int width = operands[input_operand_indexes[0]].dims[2]; - int pad_size = (conv_params->padding_method == VALID) ? (conv_params->kernel_size - 1) / 2 * conv_params->dilation : 0; - DnnOperand *output_operand = &operands[output_operand_index]; - void *tmp; - - output_operand->dims[0] = operands[input_operand_indexes[0]].dims[0]; - output_operand->dims[1] = height - pad_size * 2; - output_operand->dims[2] = width - pad_size * 2; - output_operand->dims[3] = conv_params->output_num; - output_operand->data_type = operands[input_operand_indexes[0]].data_type; - output_operand->length = ff_calculate_operand_data_length(output_operand); - if (output_operand->length <= 0) { - av_log(ctx, AV_LOG_ERROR, "The output data length overflow\n"); - return AVERROR(EINVAL); - } - tmp = av_realloc(output_operand->data, output_operand->length); - if (!tmp) { - av_log(ctx, AV_LOG_ERROR, "Failed to reallocate memory for output\n"); - return AVERROR(ENOMEM); - } - output_operand->data = tmp; - thread_common_param.output_data = output_operand->data; - thread_common_param.operands = operands; - thread_common_param.input_operand_indexes = input_operand_indexes; - thread_common_param.output_operand_index = output_operand_index; - thread_common_param.parameters = parameters; - thread_common_param.ctx = ctx; - -#if HAVE_PTHREAD_CANCEL - thread_param = av_malloc_array(thread_num, sizeof(*thread_param)); - if (!thread_param) - return AVERROR(ENOMEM); - thread_stride = (height - pad_size * 2) / thread_num; - //create threads - for (int i = 0; i < thread_num; i++){ - int thread_ret = 0; - thread_param[i].thread_common_param = &thread_common_param; - thread_param[i].thread_start = thread_stride * i + pad_size; - thread_param[i].thread_end = (i == thread_num - 1) ? (height - pad_size) : (thread_param[i].thread_start + thread_stride); - thread_ret = pthread_create(&thread_param[i].thread, NULL, - dnn_execute_layer_conv2d_thread, &thread_param[i]); - if (thread_ret) { - thread_num = i; - ret = AVERROR(thread_ret); - break; - } - } - - for (int i = 0; i < thread_num; i++){ - pthread_join(thread_param[i].thread, NULL); - } - - //release memory - av_freep(&thread_param); - - return ret; -#else - thread_param.thread_common_param = &thread_common_param; - thread_param.thread_start = pad_size; - thread_param.thread_end = height - pad_size; - dnn_execute_layer_conv2d_thread(&thread_param); - - return 0; -#endif -} diff --git a/libavfilter/dnn/dnn_backend_native_layer_conv2d.h b/libavfilter/dnn/dnn_backend_native_layer_conv2d.h deleted file mode 100644 index f754a9ba185..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_conv2d.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2018 Sergey Lavrushkin - * - * 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 AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_CONV2D_H -#define AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_CONV2D_H - -#include "dnn_backend_native.h" - - -typedef struct ConvolutionalParams{ - int32_t input_num, output_num, kernel_size; - DNNActivationFunc activation; - DNNPaddingParam padding_method; - int32_t dilation; - int32_t has_bias; - float *kernel; - float *biases; -} ConvolutionalParams; - -/** - * @brief Load the 2D Convolution Layer. - * - * It assigns the 2D convolution layer with ConvolutionalParams - * after parsing from the model file context. - * - * @param layer pointer to the DNN layer instance - * @param model_file_context pointer to model file context - * @param file_size model file size to check if data is read - * correctly from the model file - * @param operands_num operand count of the whole model to - * check if data is read correctly from the model file - * @return number of bytes read from the model file - * @retval 0 if out of memory or an error occurs - */ -int ff_dnn_load_layer_conv2d(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num); - -/** - * @brief Execute the 2D Convolution Layer. - * - * @param operands all operands for the model - * @param input_operand_indexes input operand indexes for this layer - * @param output_operand_index output operand index for this layer - * @param parameters convolution parameters - * @param ctx pointer to Native model context for logging - * @retval 0 if the execution succeeds - * @retval AVERROR(ENOMEM) if memory allocation fails - * @retval AVERROR(EINVAL) for invalid arguments - */ -int ff_dnn_execute_layer_conv2d(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx); -#endif diff --git a/libavfilter/dnn/dnn_backend_native_layer_dense.c b/libavfilter/dnn/dnn_backend_native_layer_dense.c deleted file mode 100644 index dff342c1f35..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_dense.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (c) 2020 - * - * 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 "libavutil/avassert.h" -#include "dnn_backend_native_layer_dense.h" - -int ff_dnn_load_layer_dense(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num) -{ - DenseParams *dense_params; - int kernel_size; - int dnn_size = 0; - dense_params = av_malloc(sizeof(*dense_params)); - if (!dense_params) - return 0; - - dense_params->activation = (int32_t)avio_rl32(model_file_context); - dense_params->input_num = (int32_t)avio_rl32(model_file_context); - dense_params->output_num = (int32_t)avio_rl32(model_file_context); - dense_params->has_bias = (int32_t)avio_rl32(model_file_context); - dnn_size += 16; - - kernel_size = dense_params->input_num * dense_params->output_num; - dnn_size += kernel_size * 4; - if (dense_params->has_bias) - dnn_size += dense_params->output_num * 4; - - if (dnn_size > file_size || dense_params->input_num <= 0 || - dense_params->output_num <= 0){ - av_freep(&dense_params); - return 0; - } - - dense_params->kernel = av_malloc(kernel_size * sizeof(float)); - if (!dense_params->kernel) { - av_freep(&dense_params); - return 0; - } - for (int i = 0; i < kernel_size; ++i) { - dense_params->kernel[i] = av_int2float(avio_rl32(model_file_context)); - } - - dense_params->biases = NULL; - if (dense_params->has_bias) { - dense_params->biases = av_malloc(dense_params->output_num * sizeof(float)); - if (!dense_params->biases){ - av_freep(&dense_params->kernel); - av_freep(&dense_params); - return 0; - } - for (int i = 0; i < dense_params->output_num; ++i){ - dense_params->biases[i] = av_int2float(avio_rl32(model_file_context)); - } - } - - layer->params = dense_params; - - layer->input_operand_indexes[0] = (int32_t)avio_rl32(model_file_context); - layer->output_operand_index = (int32_t)avio_rl32(model_file_context); - dnn_size += 8; - - if (layer->input_operand_indexes[0] >= operands_num || layer->output_operand_index >= operands_num) { - return 0; - } - - return dnn_size; -} - -int ff_dnn_execute_layer_dense(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx) -{ - float *output; - int32_t input_operand_index = input_operand_indexes[0]; - int number = operands[input_operand_index].dims[0]; - int height = operands[input_operand_index].dims[1]; - int width = operands[input_operand_index].dims[2]; - int channel = operands[input_operand_index].dims[3]; - const float *input = operands[input_operand_index].data; - const DenseParams *dense_params = parameters; - - int src_linesize = width * channel; - DnnOperand *output_operand = &operands[output_operand_index]; - output_operand->dims[0] = number; - output_operand->dims[1] = height; - output_operand->dims[2] = width; - output_operand->dims[3] = dense_params->output_num; - output_operand->data_type = operands[input_operand_index].data_type; - output_operand->length = ff_calculate_operand_data_length(output_operand); - if (output_operand->length <= 0) { - av_log(ctx, AV_LOG_ERROR, "The output data length overflow\n"); - return AVERROR(EINVAL); - } - output_operand->data = av_realloc(output_operand->data, output_operand->length); - if (!output_operand->data) { - av_log(ctx, AV_LOG_ERROR, "Failed to reallocate memory for output\n"); - return AVERROR(ENOMEM); - } - output = output_operand->data; - - av_assert0(channel == dense_params->input_num); - - for (int y = 0; y < height; ++y) { - for (int x = 0; x < width; ++x) { - for (int n_filter = 0; n_filter < dense_params->output_num; ++n_filter) { - if (dense_params->has_bias) - output[n_filter] = dense_params->biases[n_filter]; - else - output[n_filter] = 0.f; - - for (int ch = 0; ch < dense_params->input_num; ++ch) { - float input_pel; - input_pel = input[y * src_linesize + x * dense_params->input_num + ch]; - output[n_filter] += input_pel * dense_params->kernel[n_filter*dense_params->input_num + ch]; - } - switch (dense_params->activation){ - case RELU: - output[n_filter] = FFMAX(output[n_filter], 0.0); - break; - case TANH: - output[n_filter] = 2.0f / (1.0f + exp(-2.0f * output[n_filter])) - 1.0f; - break; - case SIGMOID: - output[n_filter] = 1.0f / (1.0f + exp(-output[n_filter])); - break; - case NONE: - break; - case LEAKY_RELU: - output[n_filter] = FFMAX(output[n_filter], 0.0) + 0.2 * FFMIN(output[n_filter], 0.0); - } - } - output += dense_params->output_num; - } - } - return 0; -} diff --git a/libavfilter/dnn/dnn_backend_native_layer_dense.h b/libavfilter/dnn/dnn_backend_native_layer_dense.h deleted file mode 100644 index 607fc3e684b..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_dense.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2020 - * - * 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 AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_DENSE_H -#define AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_DENSE_H - -#include "dnn_backend_native.h" - -typedef struct DenseParams{ - int32_t input_num, output_num; - DNNActivationFunc activation; - int32_t has_bias; - float *kernel; - float *biases; -} DenseParams; - -/** - * @brief Load the Densely-Connected Layer. - * - * It assigns the densely connected layer with DenseParams - * after parsing from the model file context. - * - * @param layer pointer to the DNN layer instance - * @param model_file_context pointer to model file context - * @param file_size model file size to check if data is read - * correctly from the model file - * @param operands_num operand count of the whole model to - * check if data is read correctly from the model file - * @return number of bytes read from the model file - * @retval 0 if out of memory or an error occurs - */ -int ff_dnn_load_layer_dense(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num); - -/** - * @brief Execute the Densely-Connected Layer. - * - * @param operands all operands for the model - * @param input_operand_indexes input operand indexes for this layer - * @param output_operand_index output operand index for this layer - * @param parameters dense layer parameters - * @param ctx pointer to Native model context for logging - * @retval 0 if the execution succeeds - * @retval AVERROR(ENOMEM) if memory allocation fails - * @retval AVERROR(EINVAL) for invalid arguments - */ -int ff_dnn_execute_layer_dense(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx); -#endif diff --git a/libavfilter/dnn/dnn_backend_native_layer_depth2space.c b/libavfilter/dnn/dnn_backend_native_layer_depth2space.c deleted file mode 100644 index 358ac3bcaa2..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_depth2space.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2018 Sergey Lavrushkin - * - * 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 - * DNN native backend implementation. - */ - -#include "dnn_backend_native.h" -#include "dnn_backend_native_layer_depth2space.h" - -int ff_dnn_load_layer_depth2space(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num) -{ - DepthToSpaceParams *params; - int dnn_size = 0; - params = av_malloc(sizeof(*params)); - if (!params) - return 0; - - params->block_size = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - layer->input_operand_indexes[0] = (int32_t)avio_rl32(model_file_context); - layer->output_operand_index = (int32_t)avio_rl32(model_file_context); - dnn_size += 8; - layer->params = params; - - if (layer->input_operand_indexes[0] >= operands_num || layer->output_operand_index >= operands_num) { - return 0; - } - - return dnn_size; -} - -int ff_dnn_execute_layer_depth2space(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx) -{ - float *output; - const DepthToSpaceParams *params = parameters; - int block_size = params->block_size; - int32_t input_operand_index = input_operand_indexes[0]; - int number = operands[input_operand_index].dims[0]; - int height = operands[input_operand_index].dims[1]; - int width = operands[input_operand_index].dims[2]; - int channels = operands[input_operand_index].dims[3]; - const float *input = operands[input_operand_index].data; - - int y, x, by, bx, ch; - int new_channels = channels / (block_size * block_size); - int output_linesize = width * channels; - int by_linesize = output_linesize / block_size; - int x_linesize = new_channels * block_size; - - DnnOperand *output_operand = &operands[output_operand_index]; - output_operand->dims[0] = number; - output_operand->dims[1] = height * block_size; - output_operand->dims[2] = width * block_size; - output_operand->dims[3] = new_channels; - output_operand->data_type = operands[input_operand_index].data_type; - output_operand->length = ff_calculate_operand_data_length(output_operand); - if (output_operand->length <= 0) { - av_log(ctx, AV_LOG_ERROR, "The output data length overflow\n"); - return AVERROR(EINVAL); - } - output_operand->data = av_realloc(output_operand->data, output_operand->length); - if (!output_operand->data) { - av_log(ctx, AV_LOG_ERROR, "Failed to reallocate memory for output\n"); - return AVERROR(ENOMEM); - } - output = output_operand->data; - - for (y = 0; y < height; ++y){ - for (x = 0; x < width; ++x){ - for (by = 0; by < block_size; ++by){ - for (bx = 0; bx < block_size; ++bx){ - for (ch = 0; ch < new_channels; ++ch){ - output[by * by_linesize + x * x_linesize + bx * new_channels + ch] = input[ch]; - } - input += new_channels; - } - } - } - output += output_linesize; - } - return 0; -} diff --git a/libavfilter/dnn/dnn_backend_native_layer_depth2space.h b/libavfilter/dnn/dnn_backend_native_layer_depth2space.h deleted file mode 100644 index aaf2df4c13e..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_depth2space.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2018 Sergey Lavrushkin - * - * 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 - * DNN inference functions interface for native backend. - */ - - -#ifndef AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_DEPTH2SPACE_H -#define AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_DEPTH2SPACE_H - -#include "../dnn_interface.h" -#include "libavformat/avio.h" - -typedef struct DepthToSpaceParams{ - int block_size; -} DepthToSpaceParams; - -/** - * @brief Load the Depth to Space Layer. - * - * It assigns the depth to space layer with DepthToSpaceParams - * after parsing from the model file context. - * - * @param layer pointer to the DNN layer instance - * @param model_file_context pointer to model file context - * @param file_size model file size to check if data is read - * correctly from the model file - * @param operands_num operand count of the whole model to - * check if data is read correctly from the model file - * @return number of bytes read from the model file - * @retval 0 if an error occurs or out of memory - */ -int ff_dnn_load_layer_depth2space(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num); - -/** - * @brief Execute the Depth to Space Layer. - * - * It rearranges the input data from depth into spatial - * form by applying Depth to Space transformation. - * - * @param operands all operands for the model - * @param input_operand_indexes input operand indexes for this layer - * @param output_operand_index output operand index for this layer - * @param parameters depth to space layer parameters - * @param ctx pointer to Native model context for logging - * @retval 0 if the execution succeeds - * @retval AVERROR(ENOMEM) if memory allocation fails - * @retval AVERROR(EINVAL) for invalid arguments - */ -int ff_dnn_execute_layer_depth2space(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx); - -#endif diff --git a/libavfilter/dnn/dnn_backend_native_layer_mathbinary.c b/libavfilter/dnn/dnn_backend_native_layer_mathbinary.c deleted file mode 100644 index 1a3fa3f132f..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_mathbinary.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (c) 2020 - * - * 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 - * DNN native backend implementation. - */ - -#include "dnn_backend_native.h" -#include "dnn_backend_native_layer_mathbinary.h" - -typedef float (*FunType)(float src0, float src1); - -static float sub(float src0, float src1) -{ - return src0 - src1; -} -static float add(float src0, float src1) -{ - return src0 + src1; -} -static float mul(float src0, float src1) -{ - return src0 * src1; -} -static float realdiv(float src0, float src1) -{ - return src0 / src1; -} -static float minimum(float src0, float src1) -{ - return FFMIN(src0, src1); -} -static float floormod(float src0, float src1) -{ - return (float)((int)(src0) % (int)(src1)); -} - -static void math_binary_commutative(FunType pfun, const DnnLayerMathBinaryParams *params, const DnnOperand *input, DnnOperand *output, DnnOperand *operands, const int32_t *input_operand_indexes) -{ - int dims_count; - const float *src; - float *dst; - dims_count = ff_calculate_operand_dims_count(output); - src = input->data; - dst = output->data; - if (params->input0_broadcast || params->input1_broadcast) { - for (int i = 0; i < dims_count; ++i) { - dst[i] = pfun(params->v, src[i]); - } - } else { - const DnnOperand *input1 = &operands[input_operand_indexes[1]]; - const float *src1 = input1->data; - for (int i = 0; i < dims_count; ++i) { - dst[i] = pfun(src[i], src1[i]); - } - } -} -static void math_binary_not_commutative(FunType pfun, const DnnLayerMathBinaryParams *params, const DnnOperand *input, DnnOperand *output, DnnOperand *operands, const int32_t *input_operand_indexes) -{ - int dims_count; - const float *src; - float *dst; - dims_count = ff_calculate_operand_dims_count(output); - src = input->data; - dst = output->data; - if (params->input0_broadcast) { - for (int i = 0; i < dims_count; ++i) { - dst[i] = pfun(params->v, src[i]); - } - } else if (params->input1_broadcast) { - for (int i = 0; i < dims_count; ++i) { - dst[i] = pfun(src[i], params->v); - } - } else { - const DnnOperand *input1 = &operands[input_operand_indexes[1]]; - const float *src1 = input1->data; - for (int i = 0; i < dims_count; ++i) { - dst[i] = pfun(src[i], src1[i]); - } - } -} -int ff_dnn_load_layer_math_binary(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num) -{ - DnnLayerMathBinaryParams params = { 0 }; - int dnn_size = 0; - int input_index = 0; - - params.bin_op = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - - params.input0_broadcast = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - if (params.input0_broadcast) { - params.v = av_int2float(avio_rl32(model_file_context)); - } else { - layer->input_operand_indexes[input_index] = (int32_t)avio_rl32(model_file_context); - if (layer->input_operand_indexes[input_index] >= operands_num) { - return 0; - } - input_index++; - } - dnn_size += 4; - - params.input1_broadcast = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - if (params.input1_broadcast) { - params.v = av_int2float(avio_rl32(model_file_context)); - } else { - layer->input_operand_indexes[input_index] = (int32_t)avio_rl32(model_file_context); - if (layer->input_operand_indexes[input_index] >= operands_num) { - return 0; - } - input_index++; - } - dnn_size += 4; - - layer->output_operand_index = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - - if (layer->output_operand_index >= operands_num) { - return 0; - } - layer->params = av_memdup(¶ms, sizeof(params)); - if (!layer->params) - return 0; - - return dnn_size; -} - -int ff_dnn_execute_layer_math_binary(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx) -{ - const DnnOperand *input = &operands[input_operand_indexes[0]]; - DnnOperand *output = &operands[output_operand_index]; - const DnnLayerMathBinaryParams *params = parameters; - - for (int i = 0; i < 4; ++i) - output->dims[i] = input->dims[i]; - - output->data_type = input->data_type; - output->length = ff_calculate_operand_data_length(output); - if (output->length <= 0) { - av_log(ctx, AV_LOG_ERROR, "The output data length overflow\n"); - return AVERROR(EINVAL); - } - output->data = av_realloc(output->data, output->length); - if (!output->data) { - av_log(ctx, AV_LOG_ERROR, "Failed to reallocate memory for output\n"); - return AVERROR(ENOMEM); - } - - switch (params->bin_op) { - case DMBO_SUB: - math_binary_not_commutative(sub, params, input, output, operands, input_operand_indexes); - return 0; - case DMBO_ADD: - math_binary_commutative(add, params, input, output, operands, input_operand_indexes); - return 0; - case DMBO_MUL: - math_binary_commutative(mul, params, input, output, operands, input_operand_indexes); - return 0; - case DMBO_REALDIV: - math_binary_not_commutative(realdiv, params, input, output, operands, input_operand_indexes); - return 0; - case DMBO_MINIMUM: - math_binary_commutative(minimum, params, input, output, operands, input_operand_indexes); - return 0; - case DMBO_FLOORMOD: - math_binary_not_commutative(floormod, params, input, output, operands, input_operand_indexes); - return 0; - default: - av_log(ctx, AV_LOG_ERROR, "Unmatch math binary operator\n"); - return AVERROR(EINVAL); - } -} diff --git a/libavfilter/dnn/dnn_backend_native_layer_mathbinary.h b/libavfilter/dnn/dnn_backend_native_layer_mathbinary.h deleted file mode 100644 index eee294b00f9..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_mathbinary.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2020 - * - * 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 - * DNN inference functions interface for native backend. - */ - - -#ifndef AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_MATHBINARY_H -#define AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_MATHBINARY_H - -#include "libavformat/avio.h" -#include "dnn_backend_native.h" - -typedef enum { - DMBO_SUB = 0, - DMBO_ADD = 1, - DMBO_MUL = 2, - DMBO_REALDIV = 3, - DMBO_MINIMUM = 4, - DMBO_FLOORMOD = 5, - DMBO_COUNT -} DNNMathBinaryOperation; - -typedef struct DnnLayerMathBinaryParams{ - DNNMathBinaryOperation bin_op; - int input0_broadcast; - int input1_broadcast; - float v; -} DnnLayerMathBinaryParams; - -int ff_dnn_load_layer_math_binary(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num); -int ff_dnn_execute_layer_math_binary(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx); - -#endif diff --git a/libavfilter/dnn/dnn_backend_native_layer_mathunary.c b/libavfilter/dnn/dnn_backend_native_layer_mathunary.c deleted file mode 100644 index e3c5106e5e4..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_mathunary.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (c) 2020 - * - * 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 - * DNN native backend implementation. - */ - -#include - -#include "dnn_backend_native.h" -#include "dnn_backend_native_layer_mathunary.h" - -int ff_dnn_load_layer_math_unary(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num) -{ - DnnLayerMathUnaryParams *params; - int dnn_size = 0; - params = av_malloc(sizeof(*params)); - if(!params) - return 0; - - params->un_op = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - layer->params = params; - layer->input_operand_indexes[0] = (int32_t)avio_rl32(model_file_context); - layer->output_operand_index = (int32_t)avio_rl32(model_file_context); - dnn_size += 8; - - if (layer->input_operand_indexes[0] >= operands_num || layer->output_operand_index >= operands_num) { - return 0; - } - - return dnn_size; - -} - -int ff_dnn_execute_layer_math_unary(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx) -{ - const DnnOperand *input = &operands[input_operand_indexes[0]]; - DnnOperand *output = &operands[output_operand_index]; - const DnnLayerMathUnaryParams *params = parameters; - int dims_count; - const float *src; - float *dst; - - for (int i = 0; i < 4; ++i) - output->dims[i] = input->dims[i]; - - output->data_type = input->data_type; - output->length = ff_calculate_operand_data_length(output); - if (output->length <= 0) { - av_log(ctx, AV_LOG_ERROR, "The output data length overflow\n"); - return AVERROR(EINVAL); - } - output->data = av_realloc(output->data, output->length); - if (!output->data) { - av_log(ctx, AV_LOG_ERROR, "Failed to reallocate memory for output\n"); - return AVERROR(ENOMEM); - } - - dims_count = ff_calculate_operand_dims_count(output); - src = input->data; - dst = output->data; - - switch (params->un_op) { - case DMUO_ABS: - for (int i = 0; i < dims_count; ++i) - dst[i] = FFABS(src[i]); - return 0; - case DMUO_SIN: - for (int i = 0; i < dims_count; ++i) - dst[i] = sin(src[i]); - return 0; - case DMUO_COS: - for (int i = 0; i < dims_count; ++i) - dst[i] = cos(src[i]); - return 0; - case DMUO_TAN: - for (int i = 0; i < dims_count; ++i) - dst[i] = tan(src[i]); - return 0; - case DMUO_ASIN: - for (int i = 0; i < dims_count; ++i) - dst[i] = asin(src[i]); - return 0; - case DMUO_ACOS: - for (int i = 0; i < dims_count; ++i) - dst[i] = acos(src[i]); - return 0; - case DMUO_ATAN: - for (int i = 0; i < dims_count; ++i) - dst[i] = atan(src[i]); - return 0; - case DMUO_SINH: - for (int i = 0; i < dims_count; ++i) - dst[i] = sinh(src[i]); - return 0; - case DMUO_COSH: - for (int i = 0; i < dims_count; ++i) - dst[i] = cosh(src[i]); - return 0; - case DMUO_TANH: - for (int i = 0; i < dims_count; ++i) - dst[i] = tanh(src[i]); - return 0; - case DMUO_ASINH: - for (int i = 0; i < dims_count; ++i) - dst[i] = asinh(src[i]); - return 0; - case DMUO_ACOSH: - for (int i = 0; i < dims_count; ++i) - dst[i] = acosh(src[i]); - return 0; - case DMUO_ATANH: - for (int i = 0; i < dims_count; ++i) - dst[i] = atanh(src[i]); - return 0; - case DMUO_CEIL: - for (int i = 0; i < dims_count; ++i) - dst[i] = ceil(src[i]); - return 0; - case DMUO_FLOOR: - for (int i = 0; i < dims_count; ++i) - dst[i] = floor(src[i]); - return 0; - case DMUO_ROUND: - for (int i = 0; i < dims_count; ++i) - dst[i] = round(src[i]); - return 0; - case DMUO_EXP: - for (int i = 0; i < dims_count; ++i) - dst[i] = exp(src[i]); - return 0; - default: - av_log(ctx, AV_LOG_ERROR, "Unmatch math unary operator\n"); - return AVERROR(EINVAL); - } -} diff --git a/libavfilter/dnn/dnn_backend_native_layer_mathunary.h b/libavfilter/dnn/dnn_backend_native_layer_mathunary.h deleted file mode 100644 index 806e73b29fa..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_mathunary.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2020 - * - * 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 - * DNN inference functions interface for native backend. - */ - -#ifndef AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_MATHUNARY_H -#define AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_MATHUNARY_H - -#include "libavformat/avio.h" -#include "dnn_backend_native.h" - -typedef enum { - DMUO_ABS = 0, - DMUO_SIN = 1, - DMUO_COS = 2, - DMUO_TAN = 3, - DMUO_ASIN = 4, - DMUO_ACOS = 5, - DMUO_ATAN = 6, - DMUO_SINH = 7, - DMUO_COSH = 8, - DMUO_TANH = 9, - DMUO_ASINH = 10, - DMUO_ACOSH = 11, - DMUO_ATANH = 12, - DMUO_CEIL = 13, - DMUO_FLOOR = 14, - DMUO_ROUND = 15, - DMUO_EXP = 16, - DMUO_COUNT -} DNNMathUnaryOperation; - -typedef struct DnnLayerMathUnaryParams{ - DNNMathUnaryOperation un_op; -} DnnLayerMathUnaryParams; - -/** - * @brief Load the Unary Math Layer. - * - * It assigns the unary math layer with DnnLayerMathUnaryParams - * after parsing from the model file context. - * - * @param layer pointer to the DNN layer instance - * @param model_file_context pointer to model file context - * @param file_size model file size to check if data is read - * correctly from the model file - * @param operands_num operand count of the whole model to - * check if data is read correctly from the model file - * @return number of bytes read from the model file - * @retval 0 if out of memory or an error occurs - */ -int ff_dnn_load_layer_math_unary(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num); - -/** - * @brief Execute the Unary Math Layer. - * - * It applies the unary operator parsed while - * loading to the given input operands. - * - * @param operands all operands for the model - * @param input_operand_indexes input operand indexes for this layer - * @param output_operand_index output operand index for this layer - * @param parameters unary math layer parameters - * @param ctx pointer to Native model context for logging - * @retval 0 if the execution succeeds - * @retval AVERROR(ENOMEM) if memory allocation fails - * @retval AVERROR(EINVAL) for invalid arguments - */ -int ff_dnn_execute_layer_math_unary(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx); - -#endif diff --git a/libavfilter/dnn/dnn_backend_native_layer_maximum.c b/libavfilter/dnn/dnn_backend_native_layer_maximum.c deleted file mode 100644 index 667efaa3b80..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_maximum.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2019 Guo Yejun - * - * 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 - * DNN native backend implementation. - */ - -#include "dnn_backend_native.h" -#include "dnn_backend_native_layer_maximum.h" - -int ff_dnn_load_layer_maximum(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num) -{ - DnnLayerMaximumParams *params; - int dnn_size = 0; - params = av_malloc(sizeof(*params)); - if (!params) - return 0; - - params->val.u32 = avio_rl32(model_file_context); - dnn_size += 4; - layer->params = params; - layer->input_operand_indexes[0] = (int32_t)avio_rl32(model_file_context); - layer->output_operand_index = (int32_t)avio_rl32(model_file_context); - dnn_size += 8; - - if (layer->input_operand_indexes[0] >= operands_num || layer->output_operand_index >= operands_num) { - return 0; - } - - return dnn_size; -} - -int ff_dnn_execute_layer_maximum(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx) -{ - const DnnOperand *input = &operands[input_operand_indexes[0]]; - DnnOperand *output = &operands[output_operand_index]; - const DnnLayerMaximumParams *params = parameters; - int dims_count; - const float *src; - float *dst; - - for (int i = 0; i < 4; ++i) - output->dims[i] = input->dims[i]; - - output->data_type = input->data_type; - output->length = ff_calculate_operand_data_length(output); - if (output->length <= 0) { - av_log(ctx, AV_LOG_ERROR, "The output data length overflow\n"); - return AVERROR(EINVAL); - } - output->data = av_realloc(output->data, output->length); - if (!output->data) { - av_log(ctx, AV_LOG_ERROR, "Failed to reallocate memory for output\n"); - return AVERROR(ENOMEM); - } - - dims_count = ff_calculate_operand_dims_count(output); - src = input->data; - dst = output->data; - for (int i = 0; i < dims_count; ++i) - dst[i] = FFMAX(src[i], params->val.y); - - return 0; -} diff --git a/libavfilter/dnn/dnn_backend_native_layer_maximum.h b/libavfilter/dnn/dnn_backend_native_layer_maximum.h deleted file mode 100644 index 523acbe05fc..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_maximum.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2019 Guo Yejun - * - * 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 - * DNN inference functions interface for native backend. - */ - - -#ifndef AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_MAXIMUM_H -#define AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_MAXIMUM_H - -#include "libavformat/avio.h" -#include "dnn_backend_native.h" - -typedef struct DnnLayerMaximumParams{ - union { - uint32_t u32; - float y; - }val; -} DnnLayerMaximumParams; - -int ff_dnn_load_layer_maximum(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num); -int ff_dnn_execute_layer_maximum(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx); - -#endif diff --git a/libavfilter/dnn/dnn_backend_native_layer_pad.c b/libavfilter/dnn/dnn_backend_native_layer_pad.c deleted file mode 100644 index e274fe12c60..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_pad.c +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (c) 2019 Guo Yejun - * - * 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 -#include "libavutil/avassert.h" -#include "dnn_backend_native_layer_pad.h" - -int ff_dnn_load_layer_pad(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num) -{ - LayerPadParams *params; - int dnn_size = 0; - params = av_malloc(sizeof(*params)); - if (!params) - return 0; - - params->mode = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - for (int i = 0; i < 4; ++i) { - params->paddings[i][0] = avio_rl32(model_file_context); - params->paddings[i][1] = avio_rl32(model_file_context); - dnn_size += 8; - } - layer->input_operand_indexes[0] = (int32_t)avio_rl32(model_file_context); - layer->output_operand_index = (int32_t)avio_rl32(model_file_context); - dnn_size += 8; - layer->params = params; - - if (layer->input_operand_indexes[0] >= operands_num || layer->output_operand_index >= operands_num) { - return 0; - } - - return dnn_size; -} - -static int before_get_buddy(int given, int paddings, LayerPadModeParam mode) -{ - if (mode == LPMP_SYMMETRIC) { - return (2 * paddings - 1 - given); - } else if (mode == LPMP_REFLECT) { - return (2 * paddings - given); - } else { - av_assert0(!"should not reach here"); - return 0; - } -} - -static int after_get_buddy(int given, int border, LayerPadModeParam mode) -{ - if (mode == LPMP_SYMMETRIC) { - int offset = given - border; - return (border - 1 - offset); - } else if (mode == LPMP_REFLECT) { - int offset = given - border; - return (border - 2 - offset); - } else { - av_assert0(!"should not reach here"); - return 0; - } -} - -int ff_dnn_execute_layer_pad(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx) -{ - int32_t before_paddings; - int32_t after_paddings; - float* output; - const LayerPadParams *params = parameters; - - // suppose format is - int32_t input_operand_index = input_operand_indexes[0]; - int number = operands[input_operand_index].dims[0]; - int height = operands[input_operand_index].dims[1]; - int width = operands[input_operand_index].dims[2]; - int channel = operands[input_operand_index].dims[3]; - const float *input = operands[input_operand_index].data; - - int new_number = number + params->paddings[0][0] + params->paddings[0][1]; - int new_height = height + params->paddings[1][0] + params->paddings[1][1]; - int new_width = width + params->paddings[2][0] + params->paddings[2][1]; - int new_channel = channel + params->paddings[3][0] + params->paddings[3][1]; - - int c_stride = channel; - int wc_stride = c_stride * width; - int hwc_stride = wc_stride * height; - - int new_c_stride = new_channel; - int new_wc_stride = new_c_stride * new_width; - int new_hwc_stride = new_wc_stride * new_height; - - DnnOperand *output_operand = &operands[output_operand_index]; - output_operand->dims[0] = new_number; - output_operand->dims[1] = new_height; - output_operand->dims[2] = new_width; - output_operand->dims[3] = new_channel; - output_operand->data_type = operands[input_operand_index].data_type; - output_operand->length = ff_calculate_operand_data_length(output_operand); - if (output_operand->length <= 0) { - av_log(ctx, AV_LOG_ERROR, "The output data length overflow\n"); - return AVERROR(EINVAL); - } - output_operand->data = av_realloc(output_operand->data, output_operand->length); - if (!output_operand->data) { - av_log(ctx, AV_LOG_ERROR, "Failed to reallocate memory for output\n"); - return AVERROR(ENOMEM); - } - output = output_operand->data; - - // copy the original data - for (int n = 0; n < number; n++) { - for (int h = 0; h < height; h++) { - for (int w = 0; w < width; w++) { - const float *src = input + n * hwc_stride + h * wc_stride + w * c_stride; - float *dst = output + (n + params->paddings[0][0]) * new_hwc_stride - + (h + params->paddings[1][0]) * new_wc_stride - + (w + params->paddings[2][0]) * new_c_stride - + params->paddings[3][0]; - memcpy(dst, src, channel * sizeof(float)); - } - } - } - - // handle the first dimension - before_paddings = params->paddings[0][0]; - after_paddings = params->paddings[0][1]; - for (int n = 0; n < before_paddings; n++) { - float *dst = output + n * new_hwc_stride; - if (params->mode == LPMP_CONSTANT) { - for (int i = 0; i < new_hwc_stride; i++) { - dst[i] = params->constant_values; - } - } - else { - int buddy = before_get_buddy(n, before_paddings, params->mode); - float *src = output + buddy * new_hwc_stride; - memcpy(dst, src, new_hwc_stride * sizeof(float)); - } - } - for (int n = 0; n < after_paddings; n++) { - int given = number + before_paddings + n; - float *dst = output + given * new_hwc_stride; - if (params->mode == LPMP_CONSTANT) { - for (int i = 0; i < new_hwc_stride; i++) { - dst[i] = params->constant_values; - } - } else { - int buddy = after_get_buddy(given, number + before_paddings, params->mode); - float *src = output + buddy * new_hwc_stride; - memcpy(dst, src, new_hwc_stride * sizeof(float)); - } - } - - // handle the second dimension - before_paddings = params->paddings[1][0]; - after_paddings = params->paddings[1][1]; - for (int n = 0; n < new_number; n++) { - float *start = output + n * new_hwc_stride; - for (int h = 0; h < before_paddings; h++) { - float *dst = start + h * new_wc_stride; - if (params->mode == LPMP_CONSTANT) { - for (int i = 0; i < new_wc_stride; i++) { - dst[i] = params->constant_values; - } - } else { - int buddy = before_get_buddy(h, before_paddings, params->mode); - float *src = start + buddy * new_wc_stride; - memcpy(dst, src, new_wc_stride * sizeof(float)); - } - } - for (int h = 0; h < after_paddings; h++) { - int given = height + before_paddings + h; - float *dst = start + given * new_wc_stride; - if (params->mode == LPMP_CONSTANT) { - for (int i = 0; i < new_wc_stride; i++) { - dst[i] = params->constant_values; - } - } else { - int buddy = after_get_buddy(given, height + before_paddings, params->mode); - float *src = start + buddy * new_wc_stride; - memcpy(dst, src, new_wc_stride * sizeof(float)); - } - } - } - - // handle the third dimension - before_paddings = params->paddings[2][0]; - after_paddings = params->paddings[2][1]; - for (int n = 0; n < new_number; n++) { - for (int h = 0; h < new_height; h++) { - float *start = output + n * new_hwc_stride + h * new_wc_stride; - for (int w = 0; w < before_paddings; w++) { - float *dst = start + w * new_c_stride; - if (params->mode == LPMP_CONSTANT) { - for (int i = 0; i < new_c_stride; i++) { - dst[i] = params->constant_values; - } - } else { - int buddy = before_get_buddy(w, before_paddings, params->mode); - float *src = start + buddy * new_c_stride; - memcpy(dst, src, new_c_stride * sizeof(float)); - } - } - for (int w = 0; w < after_paddings; w++) { - int given = width + before_paddings + w; - float *dst = start + given * new_c_stride; - if (params->mode == LPMP_CONSTANT) { - for (int i = 0; i < new_c_stride; i++) { - dst[i] = params->constant_values; - } - } else { - int buddy = after_get_buddy(given, width + before_paddings, params->mode); - float *src = start + buddy * new_c_stride; - memcpy(dst, src, new_c_stride * sizeof(float)); - } - } - } - } - - // handle the fourth dimension - before_paddings = params->paddings[3][0]; - after_paddings = params->paddings[3][1]; - for (int n = 0; n < new_number; n++) { - for (int h = 0; h < new_height; h++) { - for (int w = 0; w < new_width; w++) { - float *start = output + n * new_hwc_stride + h * new_wc_stride + w * new_c_stride; - for (int c = 0; c < before_paddings; c++) { - float *dst = start + c; - if (params->mode == LPMP_CONSTANT) { - *dst = params->constant_values; - } else { - int buddy = before_get_buddy(c, before_paddings, params->mode); - float *src = start + buddy; - *dst = *src; - } - } - for (int c = 0; c < after_paddings; c++) { - int given = channel + before_paddings + c; - float *dst = start + given; - if (params->mode == LPMP_CONSTANT) { - *dst = params->constant_values; - } else { - int buddy = after_get_buddy(given, channel + before_paddings, params->mode); - float *src = start + buddy; - *dst = *src; - } - } - } - } - } - - return 0; -} diff --git a/libavfilter/dnn/dnn_backend_native_layer_pad.h b/libavfilter/dnn/dnn_backend_native_layer_pad.h deleted file mode 100644 index 4f76c67c3f6..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_pad.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2019 Guo Yejun - * - * 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 - * layer pad (equivalent to tf.pad) for native backend. - */ -#ifndef AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_PAD_H -#define AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_PAD_H - -#include -#include "dnn_backend_native.h" - -typedef enum {LPMP_CONSTANT, LPMP_REFLECT, LPMP_SYMMETRIC} LayerPadModeParam; - -typedef struct LayerPadParams{ - int32_t paddings[4][2]; - LayerPadModeParam mode; - float constant_values; -} LayerPadParams; - -int ff_dnn_load_layer_pad(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num); -int ff_dnn_execute_layer_pad(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx); - -#endif diff --git a/libavfilter/dnn/dnn_backend_native_layers.c b/libavfilter/dnn/dnn_backend_native_layers.c deleted file mode 100644 index 492939fd366..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layers.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2019 Guo Yejun - * - * 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 -#include "dnn_backend_native_layers.h" -#include "dnn_backend_native_layer_pad.h" -#include "dnn_backend_native_layer_conv2d.h" -#include "dnn_backend_native_layer_depth2space.h" -#include "dnn_backend_native_layer_maximum.h" -#include "dnn_backend_native_layer_mathbinary.h" -#include "dnn_backend_native_layer_mathunary.h" -#include "dnn_backend_native_layer_avgpool.h" -#include "dnn_backend_native_layer_dense.h" - -const LayerFunc ff_layer_funcs[DLT_COUNT] = { - {NULL, NULL}, - {ff_dnn_execute_layer_conv2d, ff_dnn_load_layer_conv2d}, - {ff_dnn_execute_layer_depth2space, ff_dnn_load_layer_depth2space}, - {ff_dnn_execute_layer_pad, ff_dnn_load_layer_pad}, - {ff_dnn_execute_layer_maximum, ff_dnn_load_layer_maximum}, - {ff_dnn_execute_layer_math_binary, ff_dnn_load_layer_math_binary}, - {ff_dnn_execute_layer_math_unary, ff_dnn_load_layer_math_unary}, - {ff_dnn_execute_layer_avg_pool, ff_dnn_load_layer_avg_pool}, - {ff_dnn_execute_layer_dense, ff_dnn_load_layer_dense}, -}; diff --git a/libavfilter/dnn/dnn_backend_native_layers.h b/libavfilter/dnn/dnn_backend_native_layers.h deleted file mode 100644 index bbd02927c2a..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layers.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2019 Guo Yejun - * - * 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 AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYERS_H -#define AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYERS_H - -#include -#include "dnn_backend_native.h" - -typedef int (*LAYER_EXEC_FUNC)(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx); -typedef int (*LAYER_LOAD_FUNC)(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num); - -typedef struct LayerFunc { - LAYER_EXEC_FUNC pf_exec; - LAYER_LOAD_FUNC pf_load; -}LayerFunc; - -extern const LayerFunc ff_layer_funcs[DLT_COUNT]; - -#endif diff --git a/libavfilter/dnn/dnn_backend_openvino.c b/libavfilter/dnn/dnn_backend_openvino.c index b67f2883364..46cbe8270ed 100644 --- a/libavfilter/dnn/dnn_backend_openvino.c +++ b/libavfilter/dnn/dnn_backend_openvino.c @@ -23,7 +23,6 @@ * DNN OpenVINO backend implementation. */ -#include "dnn_backend_openvino.h" #include "dnn_io_proc.h" #include "libavformat/avio.h" #include "libavutil/avassert.h" @@ -293,6 +292,46 @@ static void infer_completion_callback(void *args) } } +static void dnn_free_model_ov(DNNModel **model) +{ + if (*model){ + OVModel *ov_model = (*model)->model; + while (ff_safe_queue_size(ov_model->request_queue) != 0) { + OVRequestItem *item = ff_safe_queue_pop_front(ov_model->request_queue); + if (item && item->infer_request) { + ie_infer_request_free(&item->infer_request); + } + av_freep(&item->lltasks); + av_freep(&item); + } + ff_safe_queue_destroy(ov_model->request_queue); + + while (ff_queue_size(ov_model->lltask_queue) != 0) { + LastLevelTaskItem *item = ff_queue_pop_front(ov_model->lltask_queue); + av_freep(&item); + } + ff_queue_destroy(ov_model->lltask_queue); + + while (ff_queue_size(ov_model->task_queue) != 0) { + TaskItem *item = ff_queue_pop_front(ov_model->task_queue); + av_frame_free(&item->in_frame); + av_frame_free(&item->out_frame); + av_freep(&item); + } + ff_queue_destroy(ov_model->task_queue); + + if (ov_model->exe_network) + ie_exec_network_free(&ov_model->exe_network); + if (ov_model->network) + ie_network_free(&ov_model->network); + if (ov_model->core) + ie_core_free(&ov_model->core); + av_freep(&ov_model); + av_freep(model); + } +} + + static int init_model_ov(OVModel *ov_model, const char *input_name, const char *output_name) { int ret = 0; @@ -341,7 +380,7 @@ static int init_model_ov(OVModel *ov_model, const char *input_name, const char * if (status != OK) { if (status == NOT_FOUND) { av_log(ctx, AV_LOG_ERROR, "Could not find \"%s\" in model, failed to set output layout as NHWC, "\ - "all output(s) are: \"%s\"\n", input_name, ov_model->all_output_names); + "all output(s) are: \"%s\"\n", output_name, ov_model->all_output_names); } else{ av_log(ctx, AV_LOG_ERROR, "Failed to set layout as NHWC for output %s\n", output_name); } @@ -438,7 +477,7 @@ static int init_model_ov(OVModel *ov_model, const char *input_name, const char * return 0; err: - ff_dnn_free_model_ov(&ov_model->model); + dnn_free_model_ov(&ov_model->model); return ret; } @@ -721,7 +760,7 @@ static int get_output_ov(void *model, const char *input_name, int input_width, i return ret; } -DNNModel *ff_dnn_load_model_ov(const char *model_filename, DNNFunctionType func_type, const char *options, AVFilterContext *filter_ctx) +static DNNModel *dnn_load_model_ov(const char *model_filename, DNNFunctionType func_type, const char *options, AVFilterContext *filter_ctx) { DNNModel *model = NULL; OVModel *ov_model = NULL; @@ -806,11 +845,11 @@ DNNModel *ff_dnn_load_model_ov(const char *model_filename, DNNFunctionType func_ return model; err: - ff_dnn_free_model_ov(&model); + dnn_free_model_ov(&model); return NULL; } -int ff_dnn_execute_model_ov(const DNNModel *model, DNNExecBaseParams *exec_params) +static int dnn_execute_model_ov(const DNNModel *model, DNNExecBaseParams *exec_params) { OVModel *ov_model = model->model; OVContext *ctx = &ov_model->ctx; @@ -893,13 +932,13 @@ int ff_dnn_execute_model_ov(const DNNModel *model, DNNExecBaseParams *exec_param } } -DNNAsyncStatusType ff_dnn_get_result_ov(const DNNModel *model, AVFrame **in, AVFrame **out) +static DNNAsyncStatusType dnn_get_result_ov(const DNNModel *model, AVFrame **in, AVFrame **out) { OVModel *ov_model = model->model; return ff_dnn_get_result_common(ov_model->task_queue, in, out); } -int ff_dnn_flush_ov(const DNNModel *model) +static int dnn_flush_ov(const DNNModel *model) { OVModel *ov_model = model->model; OVContext *ctx = &ov_model->ctx; @@ -937,41 +976,10 @@ int ff_dnn_flush_ov(const DNNModel *model) return 0; } -void ff_dnn_free_model_ov(DNNModel **model) -{ - if (*model){ - OVModel *ov_model = (*model)->model; - while (ff_safe_queue_size(ov_model->request_queue) != 0) { - OVRequestItem *item = ff_safe_queue_pop_front(ov_model->request_queue); - if (item && item->infer_request) { - ie_infer_request_free(&item->infer_request); - } - av_freep(&item->lltasks); - av_freep(&item); - } - ff_safe_queue_destroy(ov_model->request_queue); - - while (ff_queue_size(ov_model->lltask_queue) != 0) { - LastLevelTaskItem *item = ff_queue_pop_front(ov_model->lltask_queue); - av_freep(&item); - } - ff_queue_destroy(ov_model->lltask_queue); - - while (ff_queue_size(ov_model->task_queue) != 0) { - TaskItem *item = ff_queue_pop_front(ov_model->task_queue); - av_frame_free(&item->in_frame); - av_frame_free(&item->out_frame); - av_freep(&item); - } - ff_queue_destroy(ov_model->task_queue); - - if (ov_model->exe_network) - ie_exec_network_free(&ov_model->exe_network); - if (ov_model->network) - ie_network_free(&ov_model->network); - if (ov_model->core) - ie_core_free(&ov_model->core); - av_freep(&ov_model); - av_freep(model); - } -} +const DNNModule ff_dnn_backend_openvino = { + .load_model = dnn_load_model_ov, + .execute_model = dnn_execute_model_ov, + .get_result = dnn_get_result_ov, + .flush = dnn_flush_ov, + .free_model = dnn_free_model_ov, +}; diff --git a/libavfilter/dnn/dnn_backend_openvino.h b/libavfilter/dnn/dnn_backend_openvino.h deleted file mode 100644 index 304bc96b998..00000000000 --- a/libavfilter/dnn/dnn_backend_openvino.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2020 - * - * 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 - * DNN inference functions interface for OpenVINO backend. - */ - - -#ifndef AVFILTER_DNN_DNN_BACKEND_OPENVINO_H -#define AVFILTER_DNN_DNN_BACKEND_OPENVINO_H - -#include "../dnn_interface.h" - -DNNModel *ff_dnn_load_model_ov(const char *model_filename, DNNFunctionType func_type, const char *options, AVFilterContext *filter_ctx); - -int ff_dnn_execute_model_ov(const DNNModel *model, DNNExecBaseParams *exec_params); -DNNAsyncStatusType ff_dnn_get_result_ov(const DNNModel *model, AVFrame **in, AVFrame **out); -int ff_dnn_flush_ov(const DNNModel *model); - -void ff_dnn_free_model_ov(DNNModel **model); - -#endif diff --git a/libavfilter/dnn/dnn_backend_tf.c b/libavfilter/dnn/dnn_backend_tf.c index 3b5084b67b0..b521de7fbe8 100644 --- a/libavfilter/dnn/dnn_backend_tf.c +++ b/libavfilter/dnn/dnn_backend_tf.c @@ -23,18 +23,13 @@ * DNN tensorflow backend implementation. */ -#include "dnn_backend_tf.h" -#include "dnn_backend_native.h" -#include "dnn_backend_native_layer_conv2d.h" -#include "dnn_backend_native_layer_depth2space.h" #include "libavformat/avio.h" #include "libavutil/avassert.h" #include "libavutil/avstring.h" #include "libavutil/cpu.h" +#include "libavutil/opt.h" #include "libavcodec/defs.h" #include "../internal.h" -#include "dnn_backend_native_layer_pad.h" -#include "dnn_backend_native_layer_maximum.h" #include "dnn_io_proc.h" #include "dnn_backend_common.h" #include "safe_queue.h" @@ -175,10 +170,6 @@ static int tf_start_inference(void *args) request->status); if (TF_GetCode(request->status) != TF_OK) { av_log(&tf_model->ctx, AV_LOG_ERROR, "%s", TF_Message(request->status)); - tf_free_request(infer_request); - if (ff_safe_queue_push_back(tf_model->request_queue, request) < 0) { - destroy_request_item(&request); - } return DNN_GENERIC_ERROR; } return 0; @@ -283,6 +274,7 @@ static int get_input_tf(void *model, DNNData *input, const char *input_name) TFModel *tf_model = model; TFContext *ctx = &tf_model->ctx; TF_Status *status; + TF_DataType dt; int64_t dims[4]; TF_Output tf_output; @@ -293,7 +285,18 @@ static int get_input_tf(void *model, DNNData *input, const char *input_name) } tf_output.index = 0; - input->dt = TF_OperationOutputType(tf_output); + dt = TF_OperationOutputType(tf_output); + switch (dt) { + case TF_FLOAT: + input->dt = DNN_FLOAT; + break; + case TF_UINT8: + input->dt = DNN_UINT8; + break; + default: + av_log(ctx, AV_LOG_ERROR, "Unsupported output type %d in model\n", dt); + return AVERROR(EINVAL); + } input->order = DCO_RGB; status = TF_NewStatus(); @@ -438,8 +441,6 @@ static int load_tf_model(TFModel *tf_model, const char *model_filename) TF_DeleteImportGraphDefOptions(graph_opts); TF_DeleteBuffer(graph_def); if (TF_GetCode(tf_model->status) != TF_OK){ - TF_DeleteGraph(tf_model->graph); - TF_DeleteStatus(tf_model->status); av_log(ctx, AV_LOG_ERROR, "Failed to import serialized graph to model graph\n"); av_freep(&sess_config); return DNN_GENERIC_ERROR; @@ -452,8 +453,6 @@ static int load_tf_model(TFModel *tf_model, const char *model_filename) TF_SetConfig(sess_opts, sess_config, sess_config_length,tf_model->status); av_freep(&sess_config); if (TF_GetCode(tf_model->status) != TF_OK) { - TF_DeleteGraph(tf_model->graph); - TF_DeleteStatus(tf_model->status); TF_DeleteSessionOptions(sess_opts); av_log(ctx, AV_LOG_ERROR, "Failed to set config for sess options with %s\n", tf_model->ctx.options.sess_config); @@ -465,8 +464,7 @@ static int load_tf_model(TFModel *tf_model, const char *model_filename) TF_DeleteSessionOptions(sess_opts); if (TF_GetCode(tf_model->status) != TF_OK) { - TF_DeleteGraph(tf_model->graph); - TF_DeleteStatus(tf_model->status); + av_freep(&sess_config); av_log(ctx, AV_LOG_ERROR, "Failed to create new session with model graph\n"); return DNN_GENERIC_ERROR; } @@ -479,9 +477,7 @@ static int load_tf_model(TFModel *tf_model, const char *model_filename) &init_op, 1, NULL, tf_model->status); if (TF_GetCode(tf_model->status) != TF_OK) { - TF_DeleteSession(tf_model->session, tf_model->status); - TF_DeleteGraph(tf_model->graph); - TF_DeleteStatus(tf_model->status); + av_freep(&sess_config); av_log(ctx, AV_LOG_ERROR, "Failed to run session when initializing\n"); return DNN_GENERIC_ERROR; } @@ -490,366 +486,48 @@ static int load_tf_model(TFModel *tf_model, const char *model_filename) return 0; } -#define NAME_BUFFER_SIZE 256 - -static int add_conv_layer(TFModel *tf_model, TF_Operation *transpose_op, TF_Operation **cur_op, - ConvolutionalParams* params, const int layer) +static void dnn_free_model_tf(DNNModel **model) { - TFContext *ctx = &tf_model->ctx; - TF_Operation *op; - TF_OperationDescription *op_desc; - TF_Output input; - int64_t strides[] = {1, 1, 1, 1}; - TF_Tensor *kernel_tensor = NULL, *biases_tensor = NULL; - int64_t dims[4]; - int dims_len; - char name_buffer[NAME_BUFFER_SIZE]; - int32_t size; - - size = params->input_num * params->output_num * params->kernel_size * params->kernel_size; - input.index = 0; - - snprintf(name_buffer, NAME_BUFFER_SIZE, "conv_kernel%d", layer); - op_desc = TF_NewOperation(tf_model->graph, "Const", name_buffer); - TF_SetAttrType(op_desc, "dtype", TF_FLOAT); - dims[0] = params->output_num; - dims[1] = params->kernel_size; - dims[2] = params->kernel_size; - dims[3] = params->input_num; - dims_len = 4; - kernel_tensor = TF_AllocateTensor(TF_FLOAT, dims, dims_len, size * sizeof(float)); - memcpy(TF_TensorData(kernel_tensor), params->kernel, size * sizeof(float)); - TF_SetAttrTensor(op_desc, "value", kernel_tensor, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - goto err; - } - op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - goto err; - } - - snprintf(name_buffer, NAME_BUFFER_SIZE, "transpose%d", layer); - op_desc = TF_NewOperation(tf_model->graph, "Transpose", name_buffer); - input.oper = op; - TF_AddInput(op_desc, input); - input.oper = transpose_op; - TF_AddInput(op_desc, input); - TF_SetAttrType(op_desc, "T", TF_FLOAT); - TF_SetAttrType(op_desc, "Tperm", TF_INT32); - op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - goto err; - } - - snprintf(name_buffer, NAME_BUFFER_SIZE, "conv2d%d", layer); - op_desc = TF_NewOperation(tf_model->graph, "Conv2D", name_buffer); - input.oper = *cur_op; - TF_AddInput(op_desc, input); - input.oper = op; - TF_AddInput(op_desc, input); - TF_SetAttrType(op_desc, "T", TF_FLOAT); - TF_SetAttrIntList(op_desc, "strides", strides, 4); - TF_SetAttrString(op_desc, "padding", "VALID", 5); - *cur_op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - goto err; - } - - snprintf(name_buffer, NAME_BUFFER_SIZE, "conv_biases%d", layer); - op_desc = TF_NewOperation(tf_model->graph, "Const", name_buffer); - TF_SetAttrType(op_desc, "dtype", TF_FLOAT); - dims[0] = params->output_num; - dims_len = 1; - biases_tensor = TF_AllocateTensor(TF_FLOAT, dims, dims_len, params->output_num * sizeof(float)); - memcpy(TF_TensorData(biases_tensor), params->biases, params->output_num * sizeof(float)); - TF_SetAttrTensor(op_desc, "value", biases_tensor, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - goto err; - } - op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - goto err; - } - - snprintf(name_buffer, NAME_BUFFER_SIZE, "bias_add%d", layer); - op_desc = TF_NewOperation(tf_model->graph, "BiasAdd", name_buffer); - input.oper = *cur_op; - TF_AddInput(op_desc, input); - input.oper = op; - TF_AddInput(op_desc, input); - TF_SetAttrType(op_desc, "T", TF_FLOAT); - *cur_op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - goto err; - } - - snprintf(name_buffer, NAME_BUFFER_SIZE, "activation%d", layer); - switch (params->activation){ - case RELU: - op_desc = TF_NewOperation(tf_model->graph, "Relu", name_buffer); - break; - case TANH: - op_desc = TF_NewOperation(tf_model->graph, "Tanh", name_buffer); - break; - case SIGMOID: - op_desc = TF_NewOperation(tf_model->graph, "Sigmoid", name_buffer); - break; - default: - avpriv_report_missing_feature(ctx, "convolutional activation function %d", params->activation); - return AVERROR(ENOSYS); - } - input.oper = *cur_op; - TF_AddInput(op_desc, input); - TF_SetAttrType(op_desc, "T", TF_FLOAT); - *cur_op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - goto err; - } - - return 0; -err: - TF_DeleteTensor(kernel_tensor); - TF_DeleteTensor(biases_tensor); - av_log(ctx, AV_LOG_ERROR, "Failed to add conv layer %d\n", layer); - return DNN_GENERIC_ERROR; -} - -static int add_depth_to_space_layer(TFModel *tf_model, TF_Operation **cur_op, - DepthToSpaceParams *params, const int layer) -{ - TFContext *ctx = &tf_model->ctx; - TF_OperationDescription *op_desc; - TF_Output input; - char name_buffer[NAME_BUFFER_SIZE]; - - snprintf(name_buffer, NAME_BUFFER_SIZE, "depth_to_space%d", layer); - op_desc = TF_NewOperation(tf_model->graph, "DepthToSpace", name_buffer); - input.oper = *cur_op; - input.index = 0; - TF_AddInput(op_desc, input); - TF_SetAttrType(op_desc, "T", TF_FLOAT); - TF_SetAttrInt(op_desc, "block_size", params->block_size); - *cur_op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - av_log(ctx, AV_LOG_ERROR, "Failed to add depth_to_space to layer %d\n", layer); - return DNN_GENERIC_ERROR; - } - - return 0; -} - -static int add_pad_layer(TFModel *tf_model, TF_Operation **cur_op, - LayerPadParams *params, const int layer) -{ - TFContext *ctx = &tf_model->ctx; - TF_Operation *op; - TF_Tensor *tensor; - TF_OperationDescription *op_desc; - TF_Output input; - int32_t *pads; - int64_t pads_shape[] = {4, 2}; - - char name_buffer[NAME_BUFFER_SIZE]; - snprintf(name_buffer, NAME_BUFFER_SIZE, "pad%d", layer); - - op_desc = TF_NewOperation(tf_model->graph, "Const", name_buffer); - TF_SetAttrType(op_desc, "dtype", TF_INT32); - tensor = TF_AllocateTensor(TF_INT32, pads_shape, 2, 4 * 2 * sizeof(int32_t)); - pads = (int32_t *)TF_TensorData(tensor); - pads[0] = params->paddings[0][0]; - pads[1] = params->paddings[0][1]; - pads[2] = params->paddings[1][0]; - pads[3] = params->paddings[1][1]; - pads[4] = params->paddings[2][0]; - pads[5] = params->paddings[2][1]; - pads[6] = params->paddings[3][0]; - pads[7] = params->paddings[3][1]; - TF_SetAttrTensor(op_desc, "value", tensor, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - TF_DeleteTensor(tensor); - av_log(ctx, AV_LOG_ERROR, "Failed to set value for pad of layer %d\n", layer); - return DNN_GENERIC_ERROR; - } - op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - TF_DeleteTensor(tensor); - av_log(ctx, AV_LOG_ERROR, "Failed to add pad to layer %d\n", layer); - return DNN_GENERIC_ERROR; - } - - op_desc = TF_NewOperation(tf_model->graph, "MirrorPad", "mirror_pad"); - input.oper = *cur_op; - input.index = 0; - TF_AddInput(op_desc, input); - input.oper = op; - TF_AddInput(op_desc, input); - TF_SetAttrType(op_desc, "T", TF_FLOAT); - TF_SetAttrType(op_desc, "Tpaddings", TF_INT32); - TF_SetAttrString(op_desc, "mode", "SYMMETRIC", 9); - *cur_op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - TF_DeleteTensor(tensor); - av_log(ctx, AV_LOG_ERROR, "Failed to add mirror_pad to layer %d\n", layer); - return DNN_GENERIC_ERROR; - } - - return 0; -} - -static int add_maximum_layer(TFModel *tf_model, TF_Operation **cur_op, - DnnLayerMaximumParams *params, const int layer) -{ - TFContext *ctx = &tf_model->ctx; - TF_Operation *op; - TF_Tensor *tensor; - TF_OperationDescription *op_desc; - TF_Output input; - float *y; - - char name_buffer[NAME_BUFFER_SIZE]; - snprintf(name_buffer, NAME_BUFFER_SIZE, "maximum/y%d", layer); - - op_desc = TF_NewOperation(tf_model->graph, "Const", name_buffer); - TF_SetAttrType(op_desc, "dtype", TF_FLOAT); - tensor = TF_AllocateTensor(TF_FLOAT, NULL, 0, TF_DataTypeSize(TF_FLOAT)); - y = (float *)TF_TensorData(tensor); - *y = params->val.y; - TF_SetAttrTensor(op_desc, "value", tensor, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - TF_DeleteTensor(tensor); - av_log(ctx, AV_LOG_ERROR, "Failed to set value for maximum/y of layer %d", layer); - return DNN_GENERIC_ERROR; - } - op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - TF_DeleteTensor(tensor); - av_log(ctx, AV_LOG_ERROR, "Failed to add maximum/y to layer %d\n", layer); - return DNN_GENERIC_ERROR; - } - - snprintf(name_buffer, NAME_BUFFER_SIZE, "maximum%d", layer); - op_desc = TF_NewOperation(tf_model->graph, "Maximum", name_buffer); - input.oper = *cur_op; - input.index = 0; - TF_AddInput(op_desc, input); - input.oper = op; - TF_AddInput(op_desc, input); - TF_SetAttrType(op_desc, "T", TF_FLOAT); - *cur_op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - TF_DeleteTensor(tensor); - av_log(ctx, AV_LOG_ERROR, "Failed to add maximum to layer %d\n", layer); - return DNN_GENERIC_ERROR; - } - - return 0; -} - -static int load_native_model(TFModel *tf_model, const char *model_filename) -{ - TFContext *ctx = &tf_model->ctx; - int32_t layer; - TF_OperationDescription *op_desc; - TF_Operation *op; - TF_Operation *transpose_op; - TF_Tensor *tensor = NULL; - TF_Output input; - int32_t *transpose_perm; - int64_t transpose_perm_shape[] = {4}; - int64_t input_shape[] = {1, -1, -1, -1}; - int layer_add_res; - DNNModel *model = NULL; - NativeModel *native_model; - - model = ff_dnn_load_model_native(model_filename, DFT_PROCESS_FRAME, NULL, NULL); - if (!model){ - av_log(ctx, AV_LOG_ERROR, "Failed to load native model\n"); - return AVERROR(EINVAL); - } - - native_model = model->model; - tf_model->graph = TF_NewGraph(); - tf_model->status = TF_NewStatus(); - -#define CLEANUP_ON_ERROR(tf_model) \ - { \ - TF_DeleteTensor(tensor); \ - TF_DeleteGraph(tf_model->graph); \ - TF_DeleteStatus(tf_model->status); \ - av_log(ctx, AV_LOG_ERROR, "Failed to set value or add operator to layer\n"); \ - return DNN_GENERIC_ERROR; \ - } - - op_desc = TF_NewOperation(tf_model->graph, "Placeholder", "x"); - TF_SetAttrType(op_desc, "dtype", TF_FLOAT); - TF_SetAttrShape(op_desc, "shape", input_shape, 4); - op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - CLEANUP_ON_ERROR(tf_model); - } + TFModel *tf_model; - op_desc = TF_NewOperation(tf_model->graph, "Const", "transpose_perm"); - TF_SetAttrType(op_desc, "dtype", TF_INT32); - tensor = TF_AllocateTensor(TF_INT32, transpose_perm_shape, 1, 4 * sizeof(int32_t)); - transpose_perm = (int32_t *)TF_TensorData(tensor); - transpose_perm[0] = 1; - transpose_perm[1] = 2; - transpose_perm[2] = 3; - transpose_perm[3] = 0; - TF_SetAttrTensor(op_desc, "value", tensor, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - CLEANUP_ON_ERROR(tf_model); - } - transpose_op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - CLEANUP_ON_ERROR(tf_model); - } + if (*model){ + tf_model = (*model)->model; + while (ff_safe_queue_size(tf_model->request_queue) != 0) { + TFRequestItem *item = ff_safe_queue_pop_front(tf_model->request_queue); + destroy_request_item(&item); + } + ff_safe_queue_destroy(tf_model->request_queue); - for (layer = 0; layer < native_model->layers_num; ++layer){ - switch (native_model->layers[layer].type){ - case DLT_INPUT: - layer_add_res = 0; - break; - case DLT_CONV2D: - layer_add_res = add_conv_layer(tf_model, transpose_op, &op, - (ConvolutionalParams *)native_model->layers[layer].params, layer); - break; - case DLT_DEPTH_TO_SPACE: - layer_add_res = add_depth_to_space_layer(tf_model, &op, - (DepthToSpaceParams *)native_model->layers[layer].params, layer); - break; - case DLT_MIRROR_PAD: - layer_add_res = add_pad_layer(tf_model, &op, - (LayerPadParams *)native_model->layers[layer].params, layer); - break; - case DLT_MAXIMUM: - layer_add_res = add_maximum_layer(tf_model, &op, - (DnnLayerMaximumParams *)native_model->layers[layer].params, layer); - break; - default: - CLEANUP_ON_ERROR(tf_model); + while (ff_queue_size(tf_model->lltask_queue) != 0) { + LastLevelTaskItem *item = ff_queue_pop_front(tf_model->lltask_queue); + av_freep(&item); } + ff_queue_destroy(tf_model->lltask_queue); - if (layer_add_res != 0){ - CLEANUP_ON_ERROR(tf_model); + while (ff_queue_size(tf_model->task_queue) != 0) { + TaskItem *item = ff_queue_pop_front(tf_model->task_queue); + av_frame_free(&item->in_frame); + av_frame_free(&item->out_frame); + av_freep(&item); } - } + ff_queue_destroy(tf_model->task_queue); - op_desc = TF_NewOperation(tf_model->graph, "Identity", "y"); - input.oper = op; - input.index = 0; - TF_AddInput(op_desc, input); - TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - CLEANUP_ON_ERROR(tf_model); + if (tf_model->graph){ + TF_DeleteGraph(tf_model->graph); + } + if (tf_model->session){ + TF_CloseSession(tf_model->session, tf_model->status); + TF_DeleteSession(tf_model->session, tf_model->status); + } + if (tf_model->status){ + TF_DeleteStatus(tf_model->status); + } + av_freep(&tf_model); + av_freep(model); } - - ff_dnn_free_model_native(&model); - - return 0; } -DNNModel *ff_dnn_load_model_tf(const char *model_filename, DNNFunctionType func_type, const char *options, AVFilterContext *filter_ctx) +static DNNModel *dnn_load_model_tf(const char *model_filename, DNNFunctionType func_type, const char *options, AVFilterContext *filter_ctx) { DNNModel *model = NULL; TFModel *tf_model = NULL; @@ -865,6 +543,7 @@ DNNModel *ff_dnn_load_model_tf(const char *model_filename, DNNFunctionType func_ av_freep(&model); return NULL; } + model->model = tf_model; tf_model->model = model; ctx = &tf_model->ctx; ctx->class = &dnn_tensorflow_class; @@ -877,9 +556,8 @@ DNNModel *ff_dnn_load_model_tf(const char *model_filename, DNNFunctionType func_ } if (load_tf_model(tf_model, model_filename) != 0){ - if (load_native_model(tf_model, model_filename) != 0){ - goto err; - } + av_log(ctx, AV_LOG_ERROR, "Failed to load TensorFlow model: \"%s\"\n", model_filename); + goto err; } if (ctx->options.nireq <= 0) { @@ -931,7 +609,6 @@ DNNModel *ff_dnn_load_model_tf(const char *model_filename, DNNFunctionType func_ goto err; } - model->model = tf_model; model->get_input = &get_input_tf; model->get_output = &get_output_tf; model->options = options; @@ -940,7 +617,7 @@ DNNModel *ff_dnn_load_model_tf(const char *model_filename, DNNFunctionType func_ return model; err: - ff_dnn_free_model_tf(&model); + dnn_free_model_tf(&model); return NULL; } @@ -948,7 +625,7 @@ static int fill_model_input_tf(TFModel *tf_model, TFRequestItem *request) { DNNData input; LastLevelTaskItem *lltask; TaskItem *task; - TFInferRequest *infer_request; + TFInferRequest *infer_request = NULL; TFContext *ctx = &tf_model->ctx; int ret = 0; @@ -1058,7 +735,7 @@ static void infer_completion_callback(void *args) { outputs[i].width = TF_Dim(infer_request->output_tensors[i], 2); outputs[i].channels = TF_Dim(infer_request->output_tensors[i], 3); outputs[i].data = TF_TensorData(infer_request->output_tensors[i]); - outputs[i].dt = TF_TensorType(infer_request->output_tensors[i]); + outputs[i].dt = (DNNDataType)TF_TensorType(infer_request->output_tensors[i]); } switch (tf_model->model->func_type) { case DFT_PROCESS_FRAME: @@ -1138,10 +815,11 @@ static int execute_model_tf(TFRequestItem *request, Queue *lltask_queue) if (ff_safe_queue_push_back(tf_model->request_queue, request) < 0) { destroy_request_item(&request); } + dnn_free_model_tf(&tf_model->model); return ret; } -int ff_dnn_execute_model_tf(const DNNModel *model, DNNExecBaseParams *exec_params) +static int dnn_execute_model_tf(const DNNModel *model, DNNExecBaseParams *exec_params) { TFModel *tf_model = model->model; TFContext *ctx = &tf_model->ctx; @@ -1162,6 +840,7 @@ int ff_dnn_execute_model_tf(const DNNModel *model, DNNExecBaseParams *exec_param ret = ff_dnn_fill_task(task, exec_params, tf_model, ctx->options.async, 1); if (ret != 0) { + av_log(ctx, AV_LOG_ERROR, "Fill task with invalid parameter(s).\n"); av_freep(&task); return ret; } @@ -1174,25 +853,27 @@ int ff_dnn_execute_model_tf(const DNNModel *model, DNNExecBaseParams *exec_param ret = extract_lltask_from_task(task, tf_model->lltask_queue); if (ret != 0) { + av_freep(&task); av_log(ctx, AV_LOG_ERROR, "unable to extract last level task from task.\n"); return ret; } request = ff_safe_queue_pop_front(tf_model->request_queue); if (!request) { + av_freep(&task); av_log(ctx, AV_LOG_ERROR, "unable to get infer request.\n"); return AVERROR(EINVAL); } return execute_model_tf(request, tf_model->lltask_queue); } -DNNAsyncStatusType ff_dnn_get_result_tf(const DNNModel *model, AVFrame **in, AVFrame **out) +static DNNAsyncStatusType dnn_get_result_tf(const DNNModel *model, AVFrame **in, AVFrame **out) { TFModel *tf_model = model->model; return ff_dnn_get_result_common(tf_model->task_queue, in, out); } -int ff_dnn_flush_tf(const DNNModel *model) +static int dnn_flush_tf(const DNNModel *model) { TFModel *tf_model = model->model; TFContext *ctx = &tf_model->ctx; @@ -1222,43 +903,10 @@ int ff_dnn_flush_tf(const DNNModel *model) return ff_dnn_start_inference_async(ctx, &request->exec_module); } -void ff_dnn_free_model_tf(DNNModel **model) -{ - TFModel *tf_model; - - if (*model){ - tf_model = (*model)->model; - while (ff_safe_queue_size(tf_model->request_queue) != 0) { - TFRequestItem *item = ff_safe_queue_pop_front(tf_model->request_queue); - destroy_request_item(&item); - } - ff_safe_queue_destroy(tf_model->request_queue); - - while (ff_queue_size(tf_model->lltask_queue) != 0) { - LastLevelTaskItem *item = ff_queue_pop_front(tf_model->lltask_queue); - av_freep(&item); - } - ff_queue_destroy(tf_model->lltask_queue); - - while (ff_queue_size(tf_model->task_queue) != 0) { - TaskItem *item = ff_queue_pop_front(tf_model->task_queue); - av_frame_free(&item->in_frame); - av_frame_free(&item->out_frame); - av_freep(&item); - } - ff_queue_destroy(tf_model->task_queue); - - if (tf_model->graph){ - TF_DeleteGraph(tf_model->graph); - } - if (tf_model->session){ - TF_CloseSession(tf_model->session, tf_model->status); - TF_DeleteSession(tf_model->session, tf_model->status); - } - if (tf_model->status){ - TF_DeleteStatus(tf_model->status); - } - av_freep(&tf_model); - av_freep(model); - } -} +const DNNModule ff_dnn_backend_tf = { + .load_model = dnn_load_model_tf, + .execute_model = dnn_execute_model_tf, + .get_result = dnn_get_result_tf, + .flush = dnn_flush_tf, + .free_model = dnn_free_model_tf, +}; diff --git a/libavfilter/dnn/dnn_backend_tf.h b/libavfilter/dnn/dnn_backend_tf.h deleted file mode 100644 index 0b63a4b6d22..00000000000 --- a/libavfilter/dnn/dnn_backend_tf.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2018 Sergey Lavrushkin - * - * 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 - * DNN inference functions interface for TensorFlow backend. - */ - - -#ifndef AVFILTER_DNN_DNN_BACKEND_TF_H -#define AVFILTER_DNN_DNN_BACKEND_TF_H - -#include "../dnn_interface.h" - -DNNModel *ff_dnn_load_model_tf(const char *model_filename, DNNFunctionType func_type, const char *options, AVFilterContext *filter_ctx); - -int ff_dnn_execute_model_tf(const DNNModel *model, DNNExecBaseParams *exec_params); -DNNAsyncStatusType ff_dnn_get_result_tf(const DNNModel *model, AVFrame **in, AVFrame **out); -int ff_dnn_flush_tf(const DNNModel *model); - -void ff_dnn_free_model_tf(DNNModel **model); - -#endif diff --git a/libavfilter/dnn/dnn_interface.c b/libavfilter/dnn/dnn_interface.c index 554a36b0dc7..e843826aa6d 100644 --- a/libavfilter/dnn/dnn_interface.c +++ b/libavfilter/dnn/dnn_interface.c @@ -24,57 +24,26 @@ */ #include "../dnn_interface.h" -#include "dnn_backend_native.h" -#include "dnn_backend_tf.h" -#include "dnn_backend_openvino.h" #include "libavutil/mem.h" -DNNModule *ff_get_dnn_module(DNNBackendType backend_type) -{ - DNNModule *dnn_module; - - dnn_module = av_mallocz(sizeof(DNNModule)); - if(!dnn_module){ - return NULL; - } +extern const DNNModule ff_dnn_backend_openvino; +extern const DNNModule ff_dnn_backend_tf; +const DNNModule *ff_get_dnn_module(DNNBackendType backend_type, void *log_ctx) +{ switch(backend_type){ - case DNN_NATIVE: - dnn_module->load_model = &ff_dnn_load_model_native; - dnn_module->execute_model = &ff_dnn_execute_model_native; - dnn_module->get_result = &ff_dnn_get_result_native; - dnn_module->flush = &ff_dnn_flush_native; - dnn_module->free_model = &ff_dnn_free_model_native; - break; - case DNN_TF: #if (CONFIG_LIBTENSORFLOW == 1) - dnn_module->load_model = &ff_dnn_load_model_tf; - dnn_module->execute_model = &ff_dnn_execute_model_tf; - dnn_module->get_result = &ff_dnn_get_result_tf; - dnn_module->flush = &ff_dnn_flush_tf; - dnn_module->free_model = &ff_dnn_free_model_tf; - #else - av_freep(&dnn_module); - return NULL; + case DNN_TF: + return &ff_dnn_backend_tf; #endif - break; - case DNN_OV: #if (CONFIG_LIBOPENVINO == 1) - dnn_module->load_model = &ff_dnn_load_model_ov; - dnn_module->execute_model = &ff_dnn_execute_model_ov; - dnn_module->get_result = &ff_dnn_get_result_ov; - dnn_module->flush = &ff_dnn_flush_ov; - dnn_module->free_model = &ff_dnn_free_model_ov; - #else - av_freep(&dnn_module); - return NULL; + case DNN_OV: + return &ff_dnn_backend_openvino; #endif - break; default: - av_log(NULL, AV_LOG_ERROR, "Module backend_type is not native or tensorflow\n"); - av_freep(&dnn_module); + av_log(log_ctx, AV_LOG_ERROR, + "Module backend_type %d is not supported or enabled.\n", + backend_type); return NULL; } - - return dnn_module; } diff --git a/libavfilter/dnn_filter_common.c b/libavfilter/dnn_filter_common.c index 5083e3de198..d175c919141 100644 --- a/libavfilter/dnn_filter_common.c +++ b/libavfilter/dnn_filter_common.c @@ -68,7 +68,7 @@ int ff_dnn_init(DnnContext *ctx, DNNFunctionType func_type, AVFilterContext *fil return AVERROR(EINVAL); } - ctx->dnn_module = ff_get_dnn_module(ctx->backend_type); + ctx->dnn_module = ff_get_dnn_module(ctx->backend_type, filter_ctx); if (!ctx->dnn_module) { av_log(filter_ctx, AV_LOG_ERROR, "could not create DNN module for requested backend\n"); return AVERROR(ENOMEM); @@ -158,6 +158,5 @@ void ff_dnn_uninit(DnnContext *ctx) { if (ctx->dnn_module) { (ctx->dnn_module->free_model)(&ctx->model); - av_freep(&ctx->dnn_module); } } diff --git a/libavfilter/dnn_filter_common.h b/libavfilter/dnn_filter_common.h index bcdf37c8155..30871ee3819 100644 --- a/libavfilter/dnn_filter_common.h +++ b/libavfilter/dnn_filter_common.h @@ -36,7 +36,7 @@ typedef struct DnnContext { char **model_outputnames; uint32_t nb_outputs; - DNNModule *dnn_module; + const DNNModule *dnn_module; DNNModel *model; } DnnContext; diff --git a/libavfilter/dnn_interface.h b/libavfilter/dnn_interface.h index ef8d7ae66f7..20c6a0a8963 100644 --- a/libavfilter/dnn_interface.h +++ b/libavfilter/dnn_interface.h @@ -32,7 +32,7 @@ #define DNN_GENERIC_ERROR FFERRTAG('D','N','N','!') -typedef enum {DNN_NATIVE, DNN_TF, DNN_OV} DNNBackendType; +typedef enum {DNN_TF = 1, DNN_OV} DNNBackendType; typedef enum {DNN_FLOAT = 1, DNN_UINT8 = 4} DNNDataType; @@ -123,6 +123,6 @@ typedef struct DNNModule{ } DNNModule; // Initializes DNNModule depending on chosen backend. -DNNModule *ff_get_dnn_module(DNNBackendType backend_type); +const DNNModule *ff_get_dnn_module(DNNBackendType backend_type, void *log_ctx); #endif diff --git a/libavfilter/edge_template.c b/libavfilter/edge_template.c index af33c178af4..14635c25af0 100644 --- a/libavfilter/edge_template.c +++ b/libavfilter/edge_template.c @@ -22,7 +22,6 @@ #include "libavutil/avassert.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" diff --git a/libavfilter/f_bench.c b/libavfilter/f_bench.c index 9b55194dbc5..f9099df7efa 100644 --- a/libavfilter/f_bench.c +++ b/libavfilter/f_bench.c @@ -20,9 +20,10 @@ #include "libavutil/opt.h" #include "libavutil/time.h" +#include "audio.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" +#include "video.h" enum BenchAction { ACTION_START, @@ -100,20 +101,13 @@ static const AVFilterPad bench_inputs[] = { }, }; -static const AVFilterPad bench_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_bench = { .name = "bench", .description = NULL_IF_CONFIG_SMALL("Benchmark part of a filtergraph."), .priv_size = sizeof(BenchContext), .init = init, FILTER_INPUTS(bench_inputs), - FILTER_OUTPUTS(bench_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), .priv_class = &bench_class, .flags = AVFILTER_FLAG_METADATA_ONLY, }; @@ -131,20 +125,13 @@ static const AVFilterPad abench_inputs[] = { }, }; -static const AVFilterPad abench_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_abench = { .name = "abench", .description = NULL_IF_CONFIG_SMALL("Benchmark part of a filtergraph."), .priv_size = sizeof(BenchContext), .init = init, FILTER_INPUTS(abench_inputs), - FILTER_OUTPUTS(abench_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), .priv_class = &abench_class, .flags = AVFILTER_FLAG_METADATA_ONLY, }; diff --git a/libavfilter/f_cue.c b/libavfilter/f_cue.c index 9a1b7c03b86..7748e8f1fd4 100644 --- a/libavfilter/f_cue.c +++ b/libavfilter/f_cue.c @@ -22,9 +22,11 @@ #include "libavutil/opt.h" #include "libavutil/time.h" +#include "audio.h" #include "avfilter.h" #include "filters.h" #include "internal.h" +#include "video.h" typedef struct CueContext { const AVClass *class; @@ -99,54 +101,26 @@ static const AVOption options[] = { AVFILTER_DEFINE_CLASS_EXT(cue_acue, "(a)cue", options); #if CONFIG_CUE_FILTER -static const AVFilterPad cue_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - -static const AVFilterPad cue_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_cue = { .name = "cue", .description = NULL_IF_CONFIG_SMALL("Delay filtering to match a cue."), .priv_class = &cue_acue_class, .priv_size = sizeof(CueContext), - FILTER_INPUTS(cue_inputs), - FILTER_OUTPUTS(cue_outputs), + FILTER_INPUTS(ff_video_default_filterpad), + FILTER_OUTPUTS(ff_video_default_filterpad), .activate = activate, }; #endif /* CONFIG_CUE_FILTER */ #if CONFIG_ACUE_FILTER -static const AVFilterPad acue_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - -static const AVFilterPad acue_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_acue = { .name = "acue", .description = NULL_IF_CONFIG_SMALL("Delay filtering to match a cue."), .priv_class = &cue_acue_class, .priv_size = sizeof(CueContext), .flags = AVFILTER_FLAG_METADATA_ONLY, - FILTER_INPUTS(acue_inputs), - FILTER_OUTPUTS(acue_outputs), + FILTER_INPUTS(ff_audio_default_filterpad), + FILTER_OUTPUTS(ff_audio_default_filterpad), .activate = activate, }; #endif /* CONFIG_ACUE_FILTER */ diff --git a/libavfilter/f_ebur128.c b/libavfilter/f_ebur128.c index 8afab37fdb4..ca47a583345 100644 --- a/libavfilter/f_ebur128.c +++ b/libavfilter/f_ebur128.c @@ -26,6 +26,7 @@ * @todo implement start/stop/reset through filter command injection */ +#include #include #include "libavutil/avassert.h" @@ -42,6 +43,7 @@ #include "filters.h" #include "formats.h" #include "internal.h" +#include "video.h" #define ABS_THRES -70 ///< silence gate: we discard anything below this absolute (LUFS) threshold #define ABS_UP_THRES 10 ///< upper loud limit to consider (ABS_THRES being the minimum) @@ -80,7 +82,9 @@ typedef struct EBUR128Context { /* peak metering */ int peak_mode; ///< enabled peak modes + double true_peak; ///< global true peak double *true_peaks; ///< true peaks per channel + double sample_peak; ///< global sample peak double *sample_peaks; ///< sample peaks per channel double *true_peaks_per_frame; ///< true peaks in a frame per channel #if CONFIG_SWRESAMPLE @@ -159,6 +163,8 @@ enum { #define A AV_OPT_FLAG_AUDIO_PARAM #define V AV_OPT_FLAG_VIDEO_PARAM #define F AV_OPT_FLAG_FILTERING_PARAM +#define X AV_OPT_FLAG_EXPORT +#define R AV_OPT_FLAG_READONLY static const AVOption ebur128_options[] = { { "video", "set video output", OFFSET(do_video), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, V|F }, { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "640x480"}, 0, 0, V|F }, @@ -185,6 +191,12 @@ static const AVOption ebur128_options[] = { { "LUFS", "display absolute values (LUFS)", 0, AV_OPT_TYPE_CONST, {.i64 = SCALE_TYPE_ABSOLUTE}, INT_MIN, INT_MAX, V|F, "scaletype" }, { "relative", "display values relative to target (LU)", 0, AV_OPT_TYPE_CONST, {.i64 = SCALE_TYPE_RELATIVE}, INT_MIN, INT_MAX, V|F, "scaletype" }, { "LU", "display values relative to target (LU)", 0, AV_OPT_TYPE_CONST, {.i64 = SCALE_TYPE_RELATIVE}, INT_MIN, INT_MAX, V|F, "scaletype" }, + { "integrated", "integrated loudness (LUFS)", OFFSET(integrated_loudness), AV_OPT_TYPE_DOUBLE, {.dbl = 0}, -DBL_MAX, DBL_MAX, A|F|X|R }, + { "range", "loudness range (LU)", OFFSET(loudness_range), AV_OPT_TYPE_DOUBLE, {.dbl = 0}, -DBL_MAX, DBL_MAX, A|F|X|R }, + { "lra_low", "LRA low (LUFS)", OFFSET(lra_low), AV_OPT_TYPE_DOUBLE, {.dbl = 0}, -DBL_MAX, DBL_MAX, A|F|X|R }, + { "lra_high", "LRA high (LUFS)", OFFSET(lra_high), AV_OPT_TYPE_DOUBLE, {.dbl = 0}, -DBL_MAX, DBL_MAX, A|F|X|R }, + { "sample_peak", "sample peak (dBFS)", OFFSET(sample_peak), AV_OPT_TYPE_DOUBLE, {.dbl = 0}, -DBL_MAX, DBL_MAX, A|F|X|R }, + { "true_peak", "true peak (dBFS)", OFFSET(true_peak), AV_OPT_TYPE_DOUBLE, {.dbl = 0}, -DBL_MAX, DBL_MAX, A|F|X|R }, { NULL }, }; @@ -284,7 +296,6 @@ static int config_video_output(AVFilterLink *outlink) int i, x, y; uint8_t *p; AVFilterContext *ctx = outlink->src; - AVFilterLink *inlink = ctx->inputs[0]; EBUR128Context *ebur128 = ctx->priv; AVFrame *outpicref; @@ -297,8 +308,8 @@ static int config_video_output(AVFilterLink *outlink) outlink->w = ebur128->w; outlink->h = ebur128->h; outlink->sample_aspect_ratio = (AVRational){1,1}; - outlink->time_base = inlink->time_base; outlink->frame_rate = av_make_q(10, 1); + outlink->time_base = av_inv_q(outlink->frame_rate); #define PAD 8 @@ -419,7 +430,7 @@ static int config_audio_input(AVFilterLink *inlink) * can be more complex to integrate in the one-sample loop of * filter_frame()). */ if (ebur128->metadata || (ebur128->peak_mode & PEAK_MODE_TRUE_PEAKS)) - ebur128->nb_samples = inlink->sample_rate / 10; + ebur128->nb_samples = FFMAX(inlink->sample_rate / 10, 1); return 0; } @@ -618,13 +629,13 @@ static int gate_update(struct integrator *integ, double power, static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) { - int i, ch, idx_insample; + int i, ch, idx_insample, ret; AVFilterContext *ctx = inlink->dst; EBUR128Context *ebur128 = ctx->priv; const int nb_channels = ebur128->nb_channels; const int nb_samples = insamples->nb_samples; const double *samples = (double *)insamples->data[0]; - AVFrame *pic = ebur128->outpicref; + AVFrame *pic; #if CONFIG_SWRESAMPLE if (ebur128->peak_mode & PEAK_MODE_TRUE_PEAKS && ebur128->idx_insample == 0) { @@ -701,6 +712,20 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) ebur128->i3000.cache[ch][bin_id_3000] = bin; } +#define FIND_PEAK(global, sp, ptype) do { \ + int ch; \ + double maxpeak; \ + maxpeak = 0.0; \ + if (ebur128->peak_mode & PEAK_MODE_ ## ptype ## _PEAKS) { \ + for (ch = 0; ch < ebur128->nb_channels; ch++) \ + maxpeak = FFMAX(maxpeak, sp[ch]); \ + global = DBFS(maxpeak); \ + } \ +} while (0) + + FIND_PEAK(ebur128->sample_peak, ebur128->sample_peaks, SAMPLES); + FIND_PEAK(ebur128->true_peak, ebur128->true_peaks, TRUE); + /* For integrated loudness, gating blocks are 400ms long with 75% * overlap (see BS.1770-2 p5), so a re-computation is needed each 100ms * (4800 samples at 48kHz). */ @@ -710,7 +735,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) AVFilterLink *outlink = ctx->outputs[0]; const int64_t pts = insamples->pts + av_rescale_q(idx_insample, (AVRational){ 1, inlink->sample_rate }, - outlink->time_base); + ctx->outputs[ebur128->do_video]->time_base); ebur128->sample_count = 0; @@ -821,7 +846,13 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) y_loudness_lu_graph = lu_to_y(ebur128, loudness_3000 - ebur128->target); y_loudness_lu_gauge = lu_to_y(ebur128, gauge_value); - av_frame_make_writable(pic); + ret = ff_inlink_make_frame_writable(outlink, &ebur128->outpicref); + if (ret < 0) { + av_frame_free(&insamples); + ebur128->insamples = NULL; + return ret; + } + pic = ebur128->outpicref; /* draw the graph using the short-term loudness */ p = pic->data[0] + ebur128->graph.y*pic->linesize[0] + ebur128->graph.x*3; for (y = 0; y < ebur128->graph.h; y++) { @@ -856,7 +887,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) } /* set pts and push frame */ - pic->pts = pts; + pic->pts = av_rescale_q(pts, inlink->time_base, outlink->time_base); + pic->duration = 1; clone = av_frame_clone(pic); if (!clone) return AVERROR(ENOMEM); @@ -1017,7 +1049,6 @@ static int query_formats(AVFilterContext *ctx) static av_cold void uninit(AVFilterContext *ctx) { - int i; EBUR128Context *ebur128 = ctx->priv; /* dual-mono correction */ @@ -1041,21 +1072,15 @@ static av_cold void uninit(AVFilterContext *ctx) ebur128->loudness_range, ebur128->i3000.rel_threshold, ebur128->lra_low, ebur128->lra_high); -#define PRINT_PEAK_SUMMARY(str, sp, ptype) do { \ - int ch; \ - double maxpeak; \ - maxpeak = 0.0; \ +#define PRINT_PEAK_SUMMARY(str, value, ptype) do { \ if (ebur128->peak_mode & PEAK_MODE_ ## ptype ## _PEAKS) { \ - for (ch = 0; ch < ebur128->nb_channels; ch++) \ - maxpeak = FFMAX(maxpeak, sp[ch]); \ av_log(ctx, AV_LOG_INFO, "\n\n " str " peak:\n" \ - " Peak: %5.1f dBFS", \ - DBFS(maxpeak)); \ + " Peak: %5.1f dBFS", value); \ } \ } while (0) - PRINT_PEAK_SUMMARY("Sample", ebur128->sample_peaks, SAMPLES); - PRINT_PEAK_SUMMARY("True", ebur128->true_peaks, TRUE); + PRINT_PEAK_SUMMARY("Sample", ebur128->sample_peak, SAMPLES); + PRINT_PEAK_SUMMARY("True", ebur128->true_peak, TRUE); av_log(ctx, AV_LOG_INFO, "\n"); av_freep(&ebur128->y_line_ref); @@ -1070,7 +1095,7 @@ static av_cold void uninit(AVFilterContext *ctx) av_freep(&ebur128->i3000.sum); av_freep(&ebur128->i400.histogram); av_freep(&ebur128->i3000.histogram); - for (i = 0; i < ebur128->nb_channels; i++) { + for (int i = 0; i < ebur128->nb_channels; i++) { if (ebur128->i400.cache) av_freep(&ebur128->i400.cache[i]); if (ebur128->i3000.cache) diff --git a/libavfilter/f_graphmonitor.c b/libavfilter/f_graphmonitor.c index 016a707a27a..ce4153fc793 100644 --- a/libavfilter/f_graphmonitor.c +++ b/libavfilter/f_graphmonitor.c @@ -28,6 +28,7 @@ #include "libavutil/opt.h" #include "libavutil/timestamp.h" #include "libavutil/xga_font_data.h" +#include "audio.h" #include "avfilter.h" #include "filters.h" #include "formats.h" @@ -47,6 +48,8 @@ typedef struct GraphMonitorContext { int flags; AVRational frame_rate; + int eof; + int eof_frames; int64_t pts; int64_t next_pts; uint8_t white[4]; @@ -54,6 +57,7 @@ typedef struct GraphMonitorContext { uint8_t red[4]; uint8_t green[4]; uint8_t blue[4]; + uint8_t gray[4]; uint8_t bg[4]; CacheItem *cache; @@ -62,54 +66,72 @@ typedef struct GraphMonitorContext { } GraphMonitorContext; enum { - MODE_QUEUE = 1 << 0, - MODE_FCIN = 1 << 1, - MODE_FCOUT = 1 << 2, - MODE_PTS = 1 << 3, - MODE_TIME = 1 << 4, - MODE_TB = 1 << 5, - MODE_FMT = 1 << 6, - MODE_SIZE = 1 << 7, - MODE_RATE = 1 << 8, - MODE_EOF = 1 << 9, - MODE_SCIN = 1 << 10, - MODE_SCOUT = 1 << 11, - MODE_PTS_DELTA = 1 << 12, - MODE_TIME_DELTA = 1 << 13, - MODE_FC_DELTA = 1 << 14, - MODE_SC_DELTA = 1 << 15, + MODE_FULL = 0, + MODE_COMPACT = 1, + MODE_NOZERO = 2, + MODE_NOEOF = 4, + MODE_NODISABLED = 8, + MODE_MAX = 15 +}; + +enum { + FLAG_NONE = 0 << 0, + FLAG_QUEUE = 1 << 0, + FLAG_FCIN = 1 << 1, + FLAG_FCOUT = 1 << 2, + FLAG_PTS = 1 << 3, + FLAG_TIME = 1 << 4, + FLAG_TB = 1 << 5, + FLAG_FMT = 1 << 6, + FLAG_SIZE = 1 << 7, + FLAG_RATE = 1 << 8, + FLAG_EOF = 1 << 9, + FLAG_SCIN = 1 << 10, + FLAG_SCOUT = 1 << 11, + FLAG_PTS_DELTA = 1 << 12, + FLAG_TIME_DELTA = 1 << 13, + FLAG_FC_DELTA = 1 << 14, + FLAG_SC_DELTA = 1 << 15, + FLAG_DISABLED = 1 << 16, }; #define OFFSET(x) offsetof(GraphMonitorContext, x) #define VF AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define VFR AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption graphmonitor_options[] = { { "size", "set monitor size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="hd720"}, 0, 0, VF }, { "s", "set monitor size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="hd720"}, 0, 0, VF }, - { "opacity", "set video opacity", OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=.9}, 0, 1, VF }, - { "o", "set video opacity", OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=.9}, 0, 1, VF }, - { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, VF, "mode" }, - { "m", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, VF, "mode" }, - { "full", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, VF, "mode" }, - { "compact", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, VF, "mode" }, - { "flags", "set flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64=MODE_QUEUE}, 0, INT_MAX, VF, "flags" }, - { "f", "set flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64=MODE_QUEUE}, 0, INT_MAX, VF, "flags" }, - { "queue", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_QUEUE}, 0, 0, VF, "flags" }, - { "frame_count_in", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_FCOUT}, 0, 0, VF, "flags" }, - { "frame_count_out", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_FCIN}, 0, 0, VF, "flags" }, - { "frame_count_delta",NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_FC_DELTA},0, 0, VF, "flags" }, - { "pts", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_PTS}, 0, 0, VF, "flags" }, - { "pts_delta", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_PTS_DELTA},0,0, VF, "flags" }, - { "time", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_TIME}, 0, 0, VF, "flags" }, - { "time_delta", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_TIME_DELTA},0,0,VF, "flags" }, - { "timebase", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_TB}, 0, 0, VF, "flags" }, - { "format", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_FMT}, 0, 0, VF, "flags" }, - { "size", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_SIZE}, 0, 0, VF, "flags" }, - { "rate", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_RATE}, 0, 0, VF, "flags" }, - { "eof", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_EOF}, 0, 0, VF, "flags" }, - { "sample_count_in", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_SCOUT}, 0, 0, VF, "flags" }, - { "sample_count_out", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_SCIN}, 0, 0, VF, "flags" }, - { "sample_count_delta",NULL,0, AV_OPT_TYPE_CONST, {.i64=MODE_SC_DELTA},0, 0, VF, "flags" }, + { "opacity", "set video opacity", OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=.9}, 0, 1, VFR }, + { "o", "set video opacity", OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=.9}, 0, 1, VFR }, + { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, MODE_MAX, VFR, "mode" }, + { "m", "set mode", OFFSET(mode), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, MODE_MAX, VFR, "mode" }, + { "full", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_FULL}, 0, 0, VFR, "mode" }, + { "compact", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_COMPACT},0, 0, VFR, "mode" }, + { "nozero", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_NOZERO}, 0, 0, VFR, "mode" }, + { "noeof", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_NOEOF}, 0, 0, VFR, "mode" }, + { "nodisabled",NULL,0,AV_OPT_TYPE_CONST, {.i64=MODE_NODISABLED},0,0,VFR,"mode" }, + { "flags", "set flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64=FLAG_QUEUE}, 0, INT_MAX, VFR, "flags" }, + { "f", "set flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64=FLAG_QUEUE}, 0, INT_MAX, VFR, "flags" }, + { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_NONE}, 0, 0, VFR, "flags" }, + { "all", NULL, 0, AV_OPT_TYPE_CONST, {.i64=INT_MAX}, 0, 0, VFR, "flags" }, + { "queue", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_QUEUE}, 0, 0, VFR, "flags" }, + { "frame_count_in", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_FCOUT}, 0, 0, VFR, "flags" }, + { "frame_count_out", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_FCIN}, 0, 0, VFR, "flags" }, + { "frame_count_delta",NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_FC_DELTA},0, 0, VFR, "flags" }, + { "pts", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_PTS}, 0, 0, VFR, "flags" }, + { "pts_delta", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_PTS_DELTA},0,0, VFR, "flags" }, + { "time", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_TIME}, 0, 0, VFR, "flags" }, + { "time_delta", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_TIME_DELTA},0,0,VFR, "flags" }, + { "timebase", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_TB}, 0, 0, VFR, "flags" }, + { "format", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_FMT}, 0, 0, VFR, "flags" }, + { "size", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_SIZE}, 0, 0, VFR, "flags" }, + { "rate", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_RATE}, 0, 0, VFR, "flags" }, + { "eof", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_EOF}, 0, 0, VFR, "flags" }, + { "sample_count_in", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_SCOUT}, 0, 0, VFR, "flags" }, + { "sample_count_out", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_SCIN}, 0, 0, VFR, "flags" }, + { "sample_count_delta",NULL,0, AV_OPT_TYPE_CONST, {.i64=FLAG_SC_DELTA},0, 0, VFR, "flags" }, + { "disabled", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_DISABLED},0, 0, VFR, "flags" }, { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, VF }, { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, VF }, { NULL } @@ -145,14 +167,22 @@ static int query_formats(AVFilterContext *ctx) static void clear_image(GraphMonitorContext *s, AVFrame *out, AVFilterLink *outlink) { + const int h = out->height; + const int w = out->width; + uint8_t *dst = out->data[0]; int bg = AV_RN32(s->bg); - for (int i = 0; i < out->height; i++) - for (int j = 0; j < out->width; j++) - AV_WN32(out->data[0] + i * out->linesize[0] + j * 4, bg); + for (int j = 0; j < w; j++) + AV_WN32(dst + j * 4, bg); + dst += out->linesize[0]; + for (int i = 1; i < h; i++) { + memcpy(dst, out->data[0], w * 4); + dst += out->linesize[0]; + } } -static void drawtext(AVFrame *pic, int x, int y, const char *txt, uint8_t *color) +static void drawtext(AVFrame *pic, int x, int y, const char *txt, + const int len, uint8_t *color) { const uint8_t *font; int font_height; @@ -161,7 +191,7 @@ static void drawtext(AVFrame *pic, int x, int y, const char *txt, uint8_t *color font = avpriv_cga_font, font_height = 8; if (y + 8 >= pic->height || - x + strlen(txt) * 8 >= pic->width) + x + len * 8 >= pic->width) return; for (i = 0; txt[i]; i++) { @@ -182,6 +212,25 @@ static void drawtext(AVFrame *pic, int x, int y, const char *txt, uint8_t *color } } +static int filter_have_eof(AVFilterContext *filter) +{ + for (int j = 0; j < filter->nb_inputs; j++) { + AVFilterLink *l = filter->inputs[j]; + + if (!ff_outlink_get_status(l)) + return 0; + } + + for (int j = 0; j < filter->nb_outputs; j++) { + AVFilterLink *l = filter->outputs[j]; + + if (!ff_outlink_get_status(l)) + return 0; + } + + return 1; +} + static int filter_have_queued(AVFilterContext *filter) { for (int j = 0; j < filter->nb_inputs; j++) { @@ -203,7 +252,9 @@ static int filter_have_queued(AVFilterContext *filter) return 0; } -static int draw_items(AVFilterContext *ctx, AVFrame *out, +static int draw_items(AVFilterContext *ctx, + AVFilterContext *filter, + AVFrame *out, int xpos, int ypos, AVFilterLink *l, size_t frames) @@ -211,104 +262,112 @@ static int draw_items(AVFilterContext *ctx, AVFrame *out, GraphMonitorContext *s = ctx->priv; int64_t previous_pts_us = s->cache[s->cache_index].previous_pts_us; int64_t current_pts_us = l->current_pts_us; + const int flags = s->flags; + const int mode = s->mode; char buffer[1024] = { 0 }; + int len = 0; - if (s->flags & MODE_FMT) { + if (flags & FLAG_FMT) { if (l->type == AVMEDIA_TYPE_VIDEO) { - snprintf(buffer, sizeof(buffer)-1, " | format: %s", + len = snprintf(buffer, sizeof(buffer)-1, " | format: %s", av_get_pix_fmt_name(l->format)); } else if (l->type == AVMEDIA_TYPE_AUDIO) { - snprintf(buffer, sizeof(buffer)-1, " | format: %s", + len = snprintf(buffer, sizeof(buffer)-1, " | format: %s", av_get_sample_fmt_name(l->format)); } - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_SIZE) { + if (flags & FLAG_SIZE) { if (l->type == AVMEDIA_TYPE_VIDEO) { - snprintf(buffer, sizeof(buffer)-1, " | size: %dx%d", l->w, l->h); + len = snprintf(buffer, sizeof(buffer)-1, " | size: %dx%d", l->w, l->h); } else if (l->type == AVMEDIA_TYPE_AUDIO) { - snprintf(buffer, sizeof(buffer)-1, " | channels: %d", l->ch_layout.nb_channels); + len = snprintf(buffer, sizeof(buffer)-1, " | channels: %d", l->ch_layout.nb_channels); } - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_RATE) { + if (flags & FLAG_RATE) { if (l->type == AVMEDIA_TYPE_VIDEO) { - snprintf(buffer, sizeof(buffer)-1, " | fps: %d/%d", l->frame_rate.num, l->frame_rate.den); + len = snprintf(buffer, sizeof(buffer)-1, " | fps: %d/%d", l->frame_rate.num, l->frame_rate.den); } else if (l->type == AVMEDIA_TYPE_AUDIO) { - snprintf(buffer, sizeof(buffer)-1, " | samplerate: %d", l->sample_rate); + len = snprintf(buffer, sizeof(buffer)-1, " | samplerate: %d", l->sample_rate); } - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; + } + if (flags & FLAG_TB) { + len = snprintf(buffer, sizeof(buffer)-1, " | tb: %d/%d", l->time_base.num, l->time_base.den); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_TB) { - snprintf(buffer, sizeof(buffer)-1, " | tb: %d/%d", l->time_base.num, l->time_base.den); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_QUEUE) && (!(mode & MODE_NOZERO) || frames)) { + len = snprintf(buffer, sizeof(buffer)-1, " | queue: "); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; + len = snprintf(buffer, sizeof(buffer)-1, "%"SIZE_SPECIFIER, frames); + drawtext(out, xpos, ypos, buffer, len, frames > 0 ? frames >= 10 ? frames >= 50 ? s->red : s->yellow : s->green : s->white); + xpos += len * 8; } - if (s->flags & MODE_QUEUE) { - snprintf(buffer, sizeof(buffer)-1, " | queue: "); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; - snprintf(buffer, sizeof(buffer)-1, "%"SIZE_SPECIFIER, frames); - drawtext(out, xpos, ypos, buffer, frames > 0 ? frames >= 10 ? frames >= 50 ? s->red : s->yellow : s->green : s->white); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_FCIN) && (!(mode & MODE_NOZERO) || l->frame_count_in)) { + len = snprintf(buffer, sizeof(buffer)-1, " | in: %"PRId64, l->frame_count_in); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_FCIN) { - snprintf(buffer, sizeof(buffer)-1, " | in: %"PRId64, l->frame_count_in); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_FCOUT) && (!(mode & MODE_NOZERO) || l->frame_count_out)) { + len = snprintf(buffer, sizeof(buffer)-1, " | out: %"PRId64, l->frame_count_out); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_FCOUT) { - snprintf(buffer, sizeof(buffer)-1, " | out: %"PRId64, l->frame_count_out); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_FC_DELTA) && (!(mode & MODE_NOZERO) || (l->frame_count_in - l->frame_count_out))) { + len = snprintf(buffer, sizeof(buffer)-1, " | delta: %"PRId64, l->frame_count_in - l->frame_count_out); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_FC_DELTA) { - snprintf(buffer, sizeof(buffer)-1, " | delta: %"PRId64, l->frame_count_in - l->frame_count_out); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_SCIN) && (!(mode & MODE_NOZERO) || l->sample_count_in)) { + len = snprintf(buffer, sizeof(buffer)-1, " | sin: %"PRId64, l->sample_count_in); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_SCIN) { - snprintf(buffer, sizeof(buffer)-1, " | sin: %"PRId64, l->sample_count_in); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_SCOUT) && (!(mode & MODE_NOZERO) || l->sample_count_out)) { + len = snprintf(buffer, sizeof(buffer)-1, " | sout: %"PRId64, l->sample_count_out); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_SCOUT) { - snprintf(buffer, sizeof(buffer)-1, " | sout: %"PRId64, l->sample_count_out); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_SC_DELTA) && (!(mode & MODE_NOZERO) || (l->sample_count_in - l->sample_count_out))) { + len = snprintf(buffer, sizeof(buffer)-1, " | sdelta: %"PRId64, l->sample_count_in - l->sample_count_out); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_SC_DELTA) { - snprintf(buffer, sizeof(buffer)-1, " | sdelta: %"PRId64, l->sample_count_in - l->sample_count_out); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_PTS) && (!(mode & MODE_NOZERO) || current_pts_us)) { + len = snprintf(buffer, sizeof(buffer)-1, " | pts: %s", av_ts2str(current_pts_us)); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_PTS) { - snprintf(buffer, sizeof(buffer)-1, " | pts: %s", av_ts2str(current_pts_us)); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_PTS_DELTA) && (!(mode & MODE_NOZERO) || (current_pts_us - previous_pts_us))) { + len = snprintf(buffer, sizeof(buffer)-1, " | pts_delta: %s", av_ts2str(current_pts_us - previous_pts_us)); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_PTS_DELTA) { - snprintf(buffer, sizeof(buffer)-1, " | pts_delta: %s", av_ts2str(current_pts_us - previous_pts_us)); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_TIME) && (!(mode & MODE_NOZERO) || current_pts_us)) { + len = snprintf(buffer, sizeof(buffer)-1, " | time: %s", av_ts2timestr(current_pts_us, &AV_TIME_BASE_Q)); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_TIME) { - snprintf(buffer, sizeof(buffer)-1, " | time: %s", av_ts2timestr(current_pts_us, &AV_TIME_BASE_Q)); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_TIME_DELTA) && (!(mode & MODE_NOZERO) || (current_pts_us - previous_pts_us))) { + len = snprintf(buffer, sizeof(buffer)-1, " | time_delta: %s", av_ts2timestr(current_pts_us - previous_pts_us, &AV_TIME_BASE_Q)); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_TIME_DELTA) { - snprintf(buffer, sizeof(buffer)-1, " | time_delta: %s", av_ts2timestr(current_pts_us - previous_pts_us, &AV_TIME_BASE_Q)); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_EOF) && ff_outlink_get_status(l)) { + len = snprintf(buffer, sizeof(buffer)-1, " | eof"); + drawtext(out, xpos, ypos, buffer, len, s->blue); + xpos += len * 8; } - if (s->flags & MODE_EOF && ff_outlink_get_status(l)) { - snprintf(buffer, sizeof(buffer)-1, " | eof"); - drawtext(out, xpos, ypos, buffer, s->blue); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_DISABLED) && filter->is_disabled) { + len = snprintf(buffer, sizeof(buffer)-1, " | off"); + drawtext(out, xpos, ypos, buffer, len, s->gray); + xpos += len * 8; } s->cache[s->cache_index].previous_pts_us = l->current_pts_us; @@ -329,43 +388,56 @@ static int create_frame(AVFilterContext *ctx, int64_t pts) { GraphMonitorContext *s = ctx->priv; AVFilterLink *outlink = ctx->outputs[0]; + int ret, len, xpos, ypos = 0; + char buffer[1024]; AVFrame *out; - int ret, xpos, ypos = 0; out = ff_get_video_buffer(outlink, outlink->w, outlink->h); if (!out) return AVERROR(ENOMEM); + s->bg[3] = 255 * s->opacity; clear_image(s, out, outlink); s->cache_index = 0; for (int i = 0; i < ctx->graph->nb_filters; i++) { AVFilterContext *filter = ctx->graph->filters[i]; - char buffer[1024] = { 0 }; - if (s->mode && !filter_have_queued(filter)) + if ((s->mode & MODE_COMPACT) && !filter_have_queued(filter)) + continue; + + if ((s->mode & MODE_NOEOF) && filter_have_eof(filter)) + continue; + + if ((s->mode & MODE_NODISABLED) && filter->is_disabled) continue; xpos = 0; - drawtext(out, xpos, ypos, filter->name, s->white); - xpos += strlen(filter->name) * 8 + 10; - drawtext(out, xpos, ypos, filter->filter->name, s->white); + len = strlen(filter->name); + drawtext(out, xpos, ypos, filter->name, len, s->white); + xpos += len * 8 + 10; + len = strlen(filter->filter->name); + drawtext(out, xpos, ypos, filter->filter->name, len, s->white); ypos += 10; for (int j = 0; j < filter->nb_inputs; j++) { AVFilterLink *l = filter->inputs[j]; size_t frames = ff_inlink_queued_frames(l); - if (s->mode && !frames) + if ((s->mode & MODE_COMPACT) && !frames) + continue; + + if ((s->mode & MODE_NOEOF) && ff_outlink_get_status(l)) continue; xpos = 10; - snprintf(buffer, sizeof(buffer)-1, "in%d: ", j); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; - drawtext(out, xpos, ypos, l->src->name, s->white); - xpos += strlen(l->src->name) * 8 + 10; - ret = draw_items(ctx, out, xpos, ypos, l, frames); + len = snprintf(buffer, sizeof(buffer)-1, "in%d: ", j); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; + len = strlen(l->src->name); + drawtext(out, xpos, ypos, l->src->name, len, s->white); + xpos += len * 8 + 10; + ret = draw_items(ctx, filter, out, xpos, ypos, l, frames); if (ret < 0) goto error; ypos += 10; @@ -376,16 +448,20 @@ static int create_frame(AVFilterContext *ctx, int64_t pts) AVFilterLink *l = filter->outputs[j]; size_t frames = ff_inlink_queued_frames(l); - if (s->mode && !frames) + if ((s->mode & MODE_COMPACT) && !frames) + continue; + + if ((s->mode & MODE_NOEOF) && ff_outlink_get_status(l)) continue; xpos = 10; - snprintf(buffer, sizeof(buffer)-1, "out%d: ", j); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; - drawtext(out, xpos, ypos, l->dst->name, s->white); - xpos += strlen(l->dst->name) * 8 + 10; - ret = draw_items(ctx, out, xpos, ypos, l, frames); + len = snprintf(buffer, sizeof(buffer)-1, "out%d: ", j); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; + len = strlen(l->dst->name); + drawtext(out, xpos, ypos, l->dst->name, len, s->white); + xpos += len * 8 + 10; + ret = draw_items(ctx, filter, out, xpos, ypos, l, frames); if (ret < 0) goto error; ypos += 10; @@ -394,7 +470,10 @@ static int create_frame(AVFilterContext *ctx, int64_t pts) } out->pts = pts; + out->duration = 1; s->pts = pts + 1; + if (s->eof_frames) + s->eof_frames = 0; return ff_filter_frame(outlink, out); error: av_frame_free(&out); @@ -407,10 +486,11 @@ static int activate(AVFilterContext *ctx) AVFilterLink *inlink = ctx->inputs[0]; AVFilterLink *outlink = ctx->outputs[0]; int64_t pts = AV_NOPTS_VALUE; + int status; FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); - if (ff_inlink_queued_frames(inlink)) { + if (!s->eof && ff_inlink_queued_frames(inlink)) { AVFrame *frame = NULL; int ret; @@ -428,13 +508,31 @@ static int activate(AVFilterContext *ctx) if (s->pts == AV_NOPTS_VALUE) s->pts = pts; s->next_pts = pts; + } else if (s->eof) { + s->next_pts = s->pts + 1; + } + + if (s->eof && s->eof_frames == 0) { + ff_outlink_set_status(outlink, AVERROR_EOF, s->next_pts); + return 0; } - if (s->pts < s->next_pts && ff_outlink_frame_wanted(outlink)) + if (s->eof || (s->pts < s->next_pts && ff_outlink_frame_wanted(outlink))) return create_frame(ctx, s->pts); - FF_FILTER_FORWARD_STATUS(inlink, outlink); - FF_FILTER_FORWARD_WANTED(outlink, inlink); + if (!s->eof && ff_inlink_acknowledge_status(inlink, &status, &pts)) { + s->eof = 1; + s->eof_frames = 1; + ff_filter_set_ready(ctx, 100); + return 0; + } + + if (!s->eof) { + FF_FILTER_FORWARD_WANTED(outlink, inlink); + } else { + ff_filter_set_ready(ctx, 100); + return 0; + } return FFERROR_NOT_READY; } @@ -443,12 +541,12 @@ static int config_output(AVFilterLink *outlink) { GraphMonitorContext *s = outlink->src->priv; - s->bg[3] = 255 * s->opacity; s->white[0] = s->white[1] = s->white[2] = 255; s->yellow[0] = s->yellow[1] = 255; s->red[0] = 255; s->green[1] = 255; s->blue[2] = 255; + s->gray[0] = s->gray[1] = s->gray[2] = 128; s->pts = AV_NOPTS_VALUE; s->next_pts = AV_NOPTS_VALUE; outlink->w = s->w; @@ -472,13 +570,6 @@ AVFILTER_DEFINE_CLASS_EXT(graphmonitor, "(a)graphmonitor", graphmonitor_options) #if CONFIG_GRAPHMONITOR_FILTER -static const AVFilterPad graphmonitor_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - static const AVFilterPad graphmonitor_outputs[] = { { .name = "default", @@ -495,22 +586,16 @@ const AVFilter ff_vf_graphmonitor = { .init = init, .uninit = uninit, .activate = activate, - FILTER_INPUTS(graphmonitor_inputs), + FILTER_INPUTS(ff_video_default_filterpad), FILTER_OUTPUTS(graphmonitor_outputs), FILTER_QUERY_FUNC(query_formats), + .process_command = ff_filter_process_command, }; #endif // CONFIG_GRAPHMONITOR_FILTER #if CONFIG_AGRAPHMONITOR_FILTER -static const AVFilterPad agraphmonitor_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - static const AVFilterPad agraphmonitor_outputs[] = { { .name = "default", @@ -527,8 +612,9 @@ const AVFilter ff_avf_agraphmonitor = { .init = init, .uninit = uninit, .activate = activate, - FILTER_INPUTS(agraphmonitor_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), FILTER_OUTPUTS(agraphmonitor_outputs), FILTER_QUERY_FUNC(query_formats), + .process_command = ff_filter_process_command, }; #endif // CONFIG_AGRAPHMONITOR_FILTER diff --git a/libavfilter/f_interleave.c b/libavfilter/f_interleave.c index 74597a0ec61..e561ca22374 100644 --- a/libavfilter/f_interleave.c +++ b/libavfilter/f_interleave.c @@ -30,7 +30,6 @@ #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "filters.h" #include "internal.h" #include "audio.h" diff --git a/libavfilter/f_latency.c b/libavfilter/f_latency.c index f04fd726f23..a39c3c7d242 100644 --- a/libavfilter/f_latency.c +++ b/libavfilter/f_latency.c @@ -20,11 +20,11 @@ #include "config_components.h" -#include "libavutil/opt.h" +#include "audio.h" #include "avfilter.h" #include "filters.h" -#include "formats.h" #include "internal.h" +#include "video.h" typedef struct LatencyContext { int64_t min_latency; @@ -99,20 +99,6 @@ static av_cold void uninit(AVFilterContext *ctx) #if CONFIG_LATENCY_FILTER -static const AVFilterPad latency_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - -static const AVFilterPad latency_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_latency = { .name = "latency", .description = NULL_IF_CONFIG_SMALL("Report video filtering latency."), @@ -122,28 +108,14 @@ const AVFilter ff_vf_latency = { .activate = activate, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_METADATA_ONLY, - FILTER_INPUTS(latency_inputs), - FILTER_OUTPUTS(latency_outputs), + FILTER_INPUTS(ff_video_default_filterpad), + FILTER_OUTPUTS(ff_video_default_filterpad), }; #endif // CONFIG_LATENCY_FILTER #if CONFIG_ALATENCY_FILTER -static const AVFilterPad alatency_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - -static const AVFilterPad alatency_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_alatency = { .name = "alatency", .description = NULL_IF_CONFIG_SMALL("Report audio filtering latency."), @@ -152,7 +124,7 @@ const AVFilter ff_af_alatency = { .uninit = uninit, .activate = activate, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, - FILTER_INPUTS(alatency_inputs), - FILTER_OUTPUTS(alatency_outputs), + FILTER_INPUTS(ff_audio_default_filterpad), + FILTER_OUTPUTS(ff_audio_default_filterpad), }; #endif // CONFIG_ALATENCY_FILTER diff --git a/libavfilter/f_loop.c b/libavfilter/f_loop.c index d217efe2fd0..0b08a2ead39 100644 --- a/libavfilter/f_loop.c +++ b/libavfilter/f_loop.c @@ -21,13 +21,11 @@ #include "config_components.h" #include "libavutil/audio_fifo.h" -#include "libavutil/fifo.h" #include "libavutil/internal.h" #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" #include "filters.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -39,7 +37,7 @@ typedef struct LoopContext { AVFrame **frames; int nb_frames; int current_frame; - int64_t start_pts; + int64_t time_pts; int64_t duration; int64_t current_sample; int64_t nb_samples; @@ -49,7 +47,10 @@ typedef struct LoopContext { int eof; int64_t size; int64_t start; + int64_t time; int64_t pts; + int64_t pts_offset; + int64_t eof_pts; } LoopContext; #define AFLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM @@ -65,6 +66,17 @@ static void check_size(AVFilterContext *ctx) ctx->input_pads[0].type == AVMEDIA_TYPE_VIDEO ? "frames" : "samples"); } +static void update_time(AVFilterContext *ctx, AVRational tb) +{ + LoopContext *s = ctx->priv; + + if (s->time != INT64_MAX) { + int64_t time_pts = av_rescale_q(s->time, AV_TIME_BASE_Q, tb); + if (s->time_pts == AV_NOPTS_VALUE || time_pts < s->time_pts) + s->time_pts = time_pts; + } +} + #if CONFIG_ALOOP_FILTER static int aconfig_input(AVFilterLink *inlink) @@ -72,6 +84,8 @@ static int aconfig_input(AVFilterLink *inlink) AVFilterContext *ctx = inlink->dst; LoopContext *s = ctx->priv; + s->time_pts = AV_NOPTS_VALUE; + s->fifo = av_audio_fifo_alloc(inlink->format, inlink->ch_layout.nb_channels, 8192); s->left = av_audio_fifo_alloc(inlink->format, inlink->ch_layout.nb_channels, 8192); if (!s->fifo || !s->left) @@ -117,7 +131,6 @@ static int push_samples(AVFilterContext *ctx, int nb_samples) return ret; if (s->current_sample >= s->nb_samples) { - s->duration = s->pts; s->current_sample = 0; if (s->loop > 0) @@ -135,11 +148,17 @@ static int afilter_frame(AVFilterLink *inlink, AVFrame *frame) LoopContext *s = ctx->priv; int ret = 0; - if (s->ignored_samples + frame->nb_samples > s->start && s->size > 0 && s->loop != 0) { + if (((s->start >= 0 && s->ignored_samples + frame->nb_samples > s->start) || + (s->time_pts != AV_NOPTS_VALUE && + frame->pts >= s->time_pts)) && + s->size > 0 && s->loop != 0) { if (s->nb_samples < s->size) { int written = FFMIN(frame->nb_samples, s->size - s->nb_samples); int drain = 0; + if (s->start < 0) + s->start = inlink->sample_count_out - written; + ret = av_audio_fifo_write(s->fifo, (void **)frame->extended_data, written); if (ret < 0) return ret; @@ -221,19 +240,24 @@ static int aactivate(AVFilterContext *ctx) LoopContext *s = ctx->priv; AVFrame *frame = NULL; int ret, status; - int64_t pts; FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + update_time(ctx, inlink->time_base); + if (!s->eof && (s->nb_samples < s->size || !s->loop || !s->size)) { - ret = ff_inlink_consume_frame(inlink, &frame); + const int in_nb_samples = FFMIN(1024, s->size - s->nb_samples); + if (in_nb_samples == 0) + ret = ff_inlink_consume_frame(inlink, &frame); + else + ret = ff_inlink_consume_samples(inlink, in_nb_samples, in_nb_samples, &frame); if (ret < 0) return ret; if (ret > 0) return afilter_frame(inlink, frame); } - if (!s->eof && ff_inlink_acknowledge_status(inlink, &status, &pts)) { + if (!s->eof && ff_inlink_acknowledge_status(inlink, &status, &s->eof_pts)) { if (status == AVERROR_EOF) { s->size = s->nb_samples; s->eof = 1; @@ -241,7 +265,7 @@ static int aactivate(AVFilterContext *ctx) } if (s->eof && (!s->loop || !s->size)) { - ff_outlink_set_status(outlink, AVERROR_EOF, s->duration); + ff_outlink_set_status(outlink, AVERROR_EOF, s->eof_pts + s->pts_offset); return 0; } @@ -259,7 +283,8 @@ static int aactivate(AVFilterContext *ctx) static const AVOption aloop_options[] = { { "loop", "number of loops", OFFSET(loop), AV_OPT_TYPE_INT, {.i64 = 0 }, -1, INT_MAX, AFLAGS }, { "size", "max number of samples to loop", OFFSET(size), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT32_MAX, AFLAGS }, - { "start", "set the loop start sample", OFFSET(start), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT64_MAX, AFLAGS }, + { "start", "set the loop start sample", OFFSET(start), AV_OPT_TYPE_INT64, {.i64 = 0 }, -1, INT64_MAX, AFLAGS }, + { "time", "set the loop start time", OFFSET(time), AV_OPT_TYPE_DURATION, {.i64=INT64_MAX}, INT64_MIN, INT64_MAX, AFLAGS }, { NULL } }; @@ -273,13 +298,6 @@ static const AVFilterPad ainputs[] = { }, }; -static const AVFilterPad aoutputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_aloop = { .name = "aloop", .description = NULL_IF_CONFIG_SMALL("Loop audio samples."), @@ -288,7 +306,7 @@ const AVFilter ff_af_aloop = { .activate = aactivate, .uninit = auninit, FILTER_INPUTS(ainputs), - FILTER_OUTPUTS(aoutputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), }; #endif /* CONFIG_ALOOP_FILTER */ @@ -298,6 +316,8 @@ static av_cold int init(AVFilterContext *ctx) { LoopContext *s = ctx->priv; + s->time_pts = AV_NOPTS_VALUE; + s->frames = av_calloc(s->size, sizeof(*s->frames)); if (!s->frames) return AVERROR(ENOMEM); @@ -307,14 +327,19 @@ static av_cold int init(AVFilterContext *ctx) return 0; } -static av_cold void uninit(AVFilterContext *ctx) +static void free_frames(AVFilterContext *ctx) { LoopContext *s = ctx->priv; - int i; - for (i = 0; i < s->nb_frames; i++) + for (int i = 0; i < s->nb_frames; i++) av_frame_free(&s->frames[i]); +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + LoopContext *s = ctx->priv; + free_frames(ctx); av_freep(&s->frames); s->nb_frames = 0; } @@ -323,35 +348,24 @@ static int push_frame(AVFilterContext *ctx) { AVFilterLink *outlink = ctx->outputs[0]; LoopContext *s = ctx->priv; - int64_t pts, duration; + AVFrame *out; int ret; - AVFrame *out = av_frame_clone(s->frames[s->current_frame]); - + out = av_frame_clone(s->frames[s->current_frame]); if (!out) return AVERROR(ENOMEM); - out->pts += s->duration - s->start_pts; -#if FF_API_PKT_DURATION -FF_DISABLE_DEPRECATION_WARNINGS - if (out->pkt_duration) - duration = out->pkt_duration; - else -FF_ENABLE_DEPRECATION_WARNINGS -#endif - if (out->duration) - duration = out->duration; - else - duration = av_rescale_q(1, av_inv_q(outlink->frame_rate), outlink->time_base); - pts = out->pts + duration; + out->pts += s->pts_offset; ret = ff_filter_frame(outlink, out); s->current_frame++; if (s->current_frame >= s->nb_frames) { - s->duration = pts; s->current_frame = 0; + s->pts_offset += s->duration; if (s->loop > 0) s->loop--; + if (s->loop == 0) + free_frames(ctx); } return ret; @@ -365,35 +379,30 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) int64_t duration; int ret = 0; - if (inlink->frame_count_out >= s->start && s->size > 0 && s->loop != 0) { + if (((s->start >= 0 && inlink->frame_count_out >= s->start) || + (s->time_pts != AV_NOPTS_VALUE && + frame->pts >= s->time_pts)) && + s->size > 0 && s->loop != 0) { if (s->nb_frames < s->size) { - if (!s->nb_frames) - s->start_pts = frame->pts; s->frames[s->nb_frames] = av_frame_clone(frame); if (!s->frames[s->nb_frames]) { av_frame_free(&frame); return AVERROR(ENOMEM); } s->nb_frames++; -#if FF_API_PKT_DURATION -FF_DISABLE_DEPRECATION_WARNINGS - if (frame->pkt_duration) - duration = frame->pkt_duration; - else -FF_ENABLE_DEPRECATION_WARNINGS -#endif if (frame->duration) duration = frame->duration; else duration = av_rescale_q(1, av_inv_q(outlink->frame_rate), outlink->time_base); - s->duration = frame->pts + duration; + s->duration += duration; + s->pts_offset = s->duration; ret = ff_filter_frame(outlink, frame); } else { av_frame_free(&frame); ret = push_frame(ctx); } } else { - frame->pts += s->duration; + frame->pts += s->pts_offset - s->duration; ret = ff_filter_frame(outlink, frame); } @@ -407,9 +416,15 @@ static int activate(AVFilterContext *ctx) LoopContext *s = ctx->priv; AVFrame *frame = NULL; int ret, status; - int64_t pts; - FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + ret = ff_outlink_get_status(outlink); + if (ret) { + ff_inlink_set_status(inlink, ret); + free_frames(ctx); + return 0; + } + + update_time(ctx, inlink->time_base); if (!s->eof && (s->nb_frames < s->size || !s->loop || !s->size)) { ret = ff_inlink_consume_frame(inlink, &frame); @@ -419,7 +434,7 @@ static int activate(AVFilterContext *ctx) return filter_frame(inlink, frame); } - if (!s->eof && ff_inlink_acknowledge_status(inlink, &status, &pts)) { + if (!s->eof && ff_inlink_acknowledge_status(inlink, &status, &s->eof_pts)) { if (status == AVERROR_EOF) { s->size = s->nb_frames; s->eof = 1; @@ -427,7 +442,8 @@ static int activate(AVFilterContext *ctx) } if (s->eof && (!s->loop || !s->size)) { - ff_outlink_set_status(outlink, AVERROR_EOF, s->duration); + ff_outlink_set_status(outlink, AVERROR_EOF, s->eof_pts + s->pts_offset); + free_frames(ctx); return 0; } @@ -445,26 +461,13 @@ static int activate(AVFilterContext *ctx) static const AVOption loop_options[] = { { "loop", "number of loops", OFFSET(loop), AV_OPT_TYPE_INT, {.i64 = 0 }, -1, INT_MAX, VFLAGS }, { "size", "max number of frames to loop", OFFSET(size), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT16_MAX, VFLAGS }, - { "start", "set the loop start frame", OFFSET(start), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT64_MAX, VFLAGS }, + { "start", "set the loop start frame", OFFSET(start), AV_OPT_TYPE_INT64, {.i64 = 0 }, -1, INT64_MAX, VFLAGS }, + { "time", "set the loop start time", OFFSET(time), AV_OPT_TYPE_DURATION, {.i64=INT64_MAX}, INT64_MIN, INT64_MAX, VFLAGS }, { NULL } }; AVFILTER_DEFINE_CLASS(loop); -static const AVFilterPad inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_loop = { .name = "loop", .description = NULL_IF_CONFIG_SMALL("Loop video frames."), @@ -473,7 +476,7 @@ const AVFilter ff_vf_loop = { .init = init, .uninit = uninit, .activate = activate, - FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_INPUTS(ff_video_default_filterpad), + FILTER_OUTPUTS(ff_video_default_filterpad), }; #endif /* CONFIG_LOOP_FILTER */ diff --git a/libavfilter/f_metadata.c b/libavfilter/f_metadata.c index 4b7cfb0cb74..a5a97cdcb1d 100644 --- a/libavfilter/f_metadata.c +++ b/libavfilter/f_metadata.c @@ -36,7 +36,6 @@ #include "libavformat/avio.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -377,13 +376,6 @@ static const AVFilterPad ainputs[] = { }, }; -static const AVFilterPad aoutputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_ametadata = { .name = "ametadata", .description = NULL_IF_CONFIG_SMALL("Manipulate audio frame metadata."), @@ -392,7 +384,7 @@ const AVFilter ff_af_ametadata = { .init = init, .uninit = uninit, FILTER_INPUTS(ainputs), - FILTER_OUTPUTS(aoutputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_METADATA_ONLY, }; @@ -411,13 +403,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_metadata = { .name = "metadata", .description = NULL_IF_CONFIG_SMALL("Manipulate video frame metadata."), @@ -426,7 +411,7 @@ const AVFilter ff_vf_metadata = { .init = init, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_METADATA_ONLY, }; diff --git a/libavfilter/f_perms.c b/libavfilter/f_perms.c index e26a15fd066..16b8e5c12f0 100644 --- a/libavfilter/f_perms.c +++ b/libavfilter/f_perms.c @@ -24,6 +24,7 @@ #include "libavutil/opt.h" #include "libavutil/random_seed.h" #include "audio.h" +#include "filters.h" #include "video.h" enum mode { @@ -96,8 +97,9 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) in_perm == out_perm ? " (no-op)" : ""); if (in_perm == RO && out_perm == RW) { - if ((ret = av_frame_make_writable(frame)) < 0) + if ((ret = ff_inlink_make_frame_writable(inlink, &frame)) < 0) return ret; + out = frame; } else if (in_perm == RW && out_perm == RO) { out = av_frame_clone(frame); if (!out) @@ -123,13 +125,6 @@ static const AVFilterPad aperms_inputs[] = { }, }; -static const AVFilterPad aperms_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_aperms = { .name = "aperms", .description = NULL_IF_CONFIG_SMALL("Set permissions for the output audio frame."), @@ -137,7 +132,7 @@ const AVFilter ff_af_aperms = { .init = init, .priv_size = sizeof(PermsContext), FILTER_INPUTS(aperms_inputs), - FILTER_OUTPUTS(aperms_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_METADATA_ONLY, .process_command = ff_filter_process_command, @@ -154,20 +149,13 @@ static const AVFilterPad perms_inputs[] = { }, }; -static const AVFilterPad perms_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_perms = { .name = "perms", .description = NULL_IF_CONFIG_SMALL("Set permissions for the output video frame."), .init = init, .priv_size = sizeof(PermsContext), FILTER_INPUTS(perms_inputs), - FILTER_OUTPUTS(perms_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), .priv_class = &perms_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_METADATA_ONLY, diff --git a/libavfilter/f_realtime.c b/libavfilter/f_realtime.c index ef713474eaf..83793bbe150 100644 --- a/libavfilter/f_realtime.c +++ b/libavfilter/f_realtime.c @@ -22,8 +22,10 @@ #include "libavutil/opt.h" #include "libavutil/time.h" +#include "audio.h" #include "avfilter.h" #include "internal.h" +#include "video.h" #include typedef struct RealtimeContext { @@ -85,13 +87,6 @@ static const AVFilterPad avfilter_vf_realtime_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_realtime_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_realtime = { .name = "realtime", .description = NULL_IF_CONFIG_SMALL("Slow down filtering to match realtime."), @@ -99,7 +94,7 @@ const AVFilter ff_vf_realtime = { .priv_class = &realtime_class, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(avfilter_vf_realtime_inputs), - FILTER_OUTPUTS(avfilter_vf_realtime_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), .process_command = ff_filter_process_command, }; #endif /* CONFIG_REALTIME_FILTER */ @@ -114,13 +109,6 @@ static const AVFilterPad arealtime_inputs[] = { }, }; -static const AVFilterPad arealtime_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_arealtime = { .name = "arealtime", .description = NULL_IF_CONFIG_SMALL("Slow down filtering to match realtime."), @@ -128,7 +116,7 @@ const AVFilter ff_af_arealtime = { .priv_size = sizeof(RealtimeContext), .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(arealtime_inputs), - FILTER_OUTPUTS(arealtime_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), .process_command = ff_filter_process_command, }; #endif /* CONFIG_AREALTIME_FILTER */ diff --git a/libavfilter/f_reverse.c b/libavfilter/f_reverse.c index 2c99557c75e..086819a207e 100644 --- a/libavfilter/f_reverse.c +++ b/libavfilter/f_reverse.c @@ -20,11 +20,8 @@ #include "config_components.h" -#include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" -#include "video.h" #define DEFAULT_LENGTH 300 @@ -33,7 +30,9 @@ typedef struct ReverseContext { AVFrame **frames; unsigned int frames_size; unsigned int pts_size; + unsigned int duration_size; int64_t *pts; + int64_t *duration; int flush_idx; int64_t nb_samples; } ReverseContext; @@ -47,12 +46,15 @@ static av_cold int init(AVFilterContext *ctx) if (!s->pts) return AVERROR(ENOMEM); + s->duration = av_fast_realloc(NULL, &s->duration_size, + DEFAULT_LENGTH * sizeof(*(s->duration))); + if (!s->duration) + return AVERROR(ENOMEM); + s->frames = av_fast_realloc(NULL, &s->frames_size, DEFAULT_LENGTH * sizeof(*(s->frames))); - if (!s->frames) { - av_freep(&s->pts); + if (!s->frames) return AVERROR(ENOMEM); - } return 0; } @@ -67,6 +69,7 @@ static av_cold void uninit(AVFilterContext *ctx) } av_freep(&s->pts); + av_freep(&s->duration); av_freep(&s->frames); } @@ -83,6 +86,13 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) s->pts = ptr; } + if (s->nb_frames + 1 > s->duration_size / sizeof(*(s->duration))) { + ptr = av_fast_realloc(s->duration, &s->duration_size, s->duration_size * 2); + if (!ptr) + return AVERROR(ENOMEM); + s->duration = ptr; + } + if (s->nb_frames + 1 > s->frames_size / sizeof(*(s->frames))) { ptr = av_fast_realloc(s->frames, &s->frames_size, s->frames_size * 2); if (!ptr) @@ -92,6 +102,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) s->frames[s->nb_frames] = in; s->pts[s->nb_frames] = in->pts; + s->duration[s->nb_frames] = in->duration; s->nb_frames++; return 0; @@ -109,6 +120,7 @@ static int request_frame(AVFilterLink *outlink) if (ret == AVERROR_EOF && s->nb_frames > 0) { AVFrame *out = s->frames[s->nb_frames - 1]; + out->duration= s->duration[s->flush_idx]; out->pts = s->pts[s->flush_idx++]; ret = ff_filter_frame(outlink, out); s->frames[s->nb_frames - 1] = NULL; @@ -252,6 +264,7 @@ static int areverse_request_frame(AVFilterLink *outlink) if (ret == AVERROR_EOF && s->nb_frames > 0) { AVFrame *out = s->frames[s->nb_frames - 1]; + out->duration = s->duration[s->flush_idx]; out->pts = s->pts[s->flush_idx++] - s->nb_samples; s->nb_samples += s->pts[s->flush_idx] - s->pts[s->flush_idx - 1] - out->nb_samples; diff --git a/libavfilter/f_select.c b/libavfilter/f_select.c index 1cfe2d59e5e..49fcdc31ffe 100644 --- a/libavfilter/f_select.c +++ b/libavfilter/f_select.c @@ -134,7 +134,9 @@ enum var_name { VAR_PREV_SELECTED_N, VAR_KEY, +#if FF_API_FRAME_PKT VAR_POS, +#endif VAR_SCENE, @@ -339,8 +341,12 @@ static void select_frame(AVFilterContext *ctx, AVFrame *frame) select->var_values[VAR_N ] = inlink->frame_count_out; select->var_values[VAR_PTS] = TS2D(frame->pts); select->var_values[VAR_T ] = TS2D(frame->pts) * av_q2d(inlink->time_base); +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS select->var_values[VAR_POS] = frame->pkt_pos == -1 ? NAN : frame->pkt_pos; - select->var_values[VAR_KEY] = frame->key_frame; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + select->var_values[VAR_KEY] = !!(frame->flags & AV_FRAME_FLAG_KEY); select->var_values[VAR_CONCATDEC_SELECT] = get_concatdec_select(frame, av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q)); switch (inlink->type) { @@ -350,8 +356,8 @@ static void select_frame(AVFilterContext *ctx, AVFrame *frame) case AVMEDIA_TYPE_VIDEO: select->var_values[VAR_INTERLACE_TYPE] = - !frame->interlaced_frame ? INTERLACE_TYPE_P : - frame->top_field_first ? INTERLACE_TYPE_T : INTERLACE_TYPE_B; + !(frame->flags & AV_FRAME_FLAG_INTERLACED) ? INTERLACE_TYPE_P : + (frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? INTERLACE_TYPE_T : INTERLACE_TYPE_B; select->var_values[VAR_PICT_TYPE] = frame->pict_type; if (select->do_scene_detect) { char buf[32]; @@ -369,13 +375,13 @@ static void select_frame(AVFilterContext *ctx, AVFrame *frame) select->var_values[VAR_N], select->var_values[VAR_PTS], select->var_values[VAR_T], - frame->key_frame); + !!(frame->flags & AV_FRAME_FLAG_KEY)); switch (inlink->type) { case AVMEDIA_TYPE_VIDEO: av_log(inlink->dst, AV_LOG_DEBUG, " interlace_type:%c pict_type:%c scene:%f", - (!frame->interlaced_frame) ? 'P' : - frame->top_field_first ? 'T' : 'B', + !(frame->flags & AV_FRAME_FLAG_INTERLACED) ? 'P' : + (frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? 'T' : 'B', av_get_picture_type_char(frame->pict_type), select->var_values[VAR_SCENE]); break; diff --git a/libavfilter/f_sendcmd.c b/libavfilter/f_sendcmd.c index 857b8413131..922ce78287d 100644 --- a/libavfilter/f_sendcmd.c +++ b/libavfilter/f_sendcmd.c @@ -43,7 +43,9 @@ static const char *const var_names[] = { "N", /* frame number */ "T", /* frame time in seconds */ +#if FF_API_FRAME_PKT "POS", /* original position in the file of the frame */ +#endif "PTS", /* frame pts */ "TS", /* interval start time in seconds */ "TE", /* interval end time in seconds */ @@ -56,7 +58,9 @@ static const char *const var_names[] = { enum var_name { VAR_N, VAR_T, +#if FF_API_FRAME_PKT VAR_POS, +#endif VAR_PTS, VAR_TS, VAR_TE, @@ -531,7 +535,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *ref) double current = TS2T(ref->pts, inlink->time_base); var_values[VAR_N] = inlink->frame_count_in; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS var_values[VAR_POS] = ref->pkt_pos == -1 ? NAN : ref->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif var_values[VAR_PTS] = TS2D(ref->pts); var_values[VAR_T] = current; var_values[VAR_TS] = start; @@ -592,13 +600,6 @@ static const AVFilterPad sendcmd_inputs[] = { }, }; -static const AVFilterPad sendcmd_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_sendcmd = { .name = "sendcmd", .description = NULL_IF_CONFIG_SMALL("Send commands to filters."), @@ -607,7 +608,7 @@ const AVFilter ff_vf_sendcmd = { .priv_size = sizeof(SendCmdContext), .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(sendcmd_inputs), - FILTER_OUTPUTS(sendcmd_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), .priv_class = &sendcmd_class, }; @@ -623,13 +624,6 @@ static const AVFilterPad asendcmd_inputs[] = { }, }; -static const AVFilterPad asendcmd_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_asendcmd = { .name = "asendcmd", .description = NULL_IF_CONFIG_SMALL("Send commands to filters."), @@ -639,7 +633,7 @@ const AVFilter ff_af_asendcmd = { .priv_size = sizeof(SendCmdContext), .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(asendcmd_inputs), - FILTER_OUTPUTS(asendcmd_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), }; #endif diff --git a/libavfilter/f_sidedata.c b/libavfilter/f_sidedata.c index 82bbaa0e7e0..c86190b062f 100644 --- a/libavfilter/f_sidedata.c +++ b/libavfilter/f_sidedata.c @@ -27,9 +27,10 @@ #include "libavutil/internal.h" #include "libavutil/frame.h" #include "libavutil/opt.h" +#include "audio.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" +#include "video.h" enum SideDataMode { SIDEDATA_SELECT, @@ -134,13 +135,6 @@ static const AVFilterPad ainputs[] = { }, }; -static const AVFilterPad aoutputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_asidedata = { .name = "asidedata", .description = NULL_IF_CONFIG_SMALL("Manipulate audio frame side data."), @@ -148,7 +142,7 @@ const AVFilter ff_af_asidedata = { .priv_class = &asidedata_class, .init = init, FILTER_INPUTS(ainputs), - FILTER_OUTPUTS(aoutputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_METADATA_ONLY, }; @@ -167,13 +161,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_sidedata = { .name = "sidedata", .description = NULL_IF_CONFIG_SMALL("Manipulate video frame side data."), @@ -181,7 +168,7 @@ const AVFilter ff_vf_sidedata = { .priv_class = &sidedata_class, .init = init, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_METADATA_ONLY, }; diff --git a/libavfilter/f_zmq.c b/libavfilter/f_zmq.c index 156670dad43..3829b55224f 100644 --- a/libavfilter/f_zmq.c +++ b/libavfilter/f_zmq.c @@ -217,13 +217,6 @@ static const AVFilterPad zmq_inputs[] = { }, }; -static const AVFilterPad zmq_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_zmq = { .name = "zmq", .description = NULL_IF_CONFIG_SMALL("Receive commands through ZMQ and broker them to filters."), @@ -231,7 +224,7 @@ const AVFilter ff_vf_zmq = { .uninit = uninit, .priv_size = sizeof(ZMQContext), FILTER_INPUTS(zmq_inputs), - FILTER_OUTPUTS(zmq_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), .priv_class = &zmq_class, }; @@ -247,13 +240,6 @@ static const AVFilterPad azmq_inputs[] = { }, }; -static const AVFilterPad azmq_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_azmq = { .name = "azmq", .description = NULL_IF_CONFIG_SMALL("Receive commands through ZMQ and broker them to filters."), @@ -262,7 +248,7 @@ const AVFilter ff_af_azmq = { .uninit = uninit, .priv_size = sizeof(ZMQContext), FILTER_INPUTS(azmq_inputs), - FILTER_OUTPUTS(azmq_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), }; #endif diff --git a/libavfilter/formats.c b/libavfilter/formats.c index e8c2888c0c0..d1c97daf642 100644 --- a/libavfilter/formats.c +++ b/libavfilter/formats.c @@ -209,11 +209,10 @@ static int merge_samplerates(void *a, void *b) /** * See merge_pix_fmts(). */ -static int merge_channel_layouts(void *va, void *vb) +static int merge_channel_layouts_internal(AVFilterChannelLayouts *a, + AVFilterChannelLayouts *b, int check) { - AVFilterChannelLayouts *a = va; - AVFilterChannelLayouts *b = vb; - AVChannelLayout *channel_layouts; + AVChannelLayout *channel_layouts = NULL; unsigned a_all = a->all_layouts + a->all_counts; unsigned b_all = b->all_layouts + b->all_counts; int ret_max, ret_nb = 0, i, j, round; @@ -231,8 +230,11 @@ static int merge_channel_layouts(void *va, void *vb) if (a_all == 1 && !b_all) { /* keep only known layouts in b; works also for b_all = 1 */ for (i = j = 0; i < b->nb_channel_layouts; i++) - if (KNOWN(&b->channel_layouts[i]) && i != j++) + if (KNOWN(&b->channel_layouts[i]) && i != j++) { + if (check) + return 1; av_channel_layout_copy(&b->channel_layouts[j], &b->channel_layouts[i]); + } /* Not optimal: the unknown layouts of b may become known after another merge. */ if (!j) @@ -244,7 +246,7 @@ static int merge_channel_layouts(void *va, void *vb) } ret_max = a->nb_channel_layouts + b->nb_channel_layouts; - if (!(channel_layouts = av_calloc(ret_max, sizeof(*channel_layouts)))) + if (!check && !(channel_layouts = av_calloc(ret_max, sizeof(*channel_layouts)))) return AVERROR(ENOMEM); /* a[known] intersect b[known] */ @@ -253,6 +255,8 @@ static int merge_channel_layouts(void *va, void *vb) continue; for (j = 0; j < b->nb_channel_layouts; j++) { if (!av_channel_layout_compare(&a->channel_layouts[i], &b->channel_layouts[j])) { + if (check) + return 1; av_channel_layout_copy(&channel_layouts[ret_nb++], &a->channel_layouts[i]); av_channel_layout_uninit(&a->channel_layouts[i]); av_channel_layout_uninit(&b->channel_layouts[j]); @@ -269,8 +273,11 @@ static int merge_channel_layouts(void *va, void *vb) continue; bfmt = FF_COUNT2LAYOUT(fmt->nb_channels); for (j = 0; j < b->nb_channel_layouts; j++) - if (!av_channel_layout_compare(&b->channel_layouts[j], &bfmt)) + if (!av_channel_layout_compare(&b->channel_layouts[j], &bfmt)) { + if (check) + return 1; av_channel_layout_copy(&channel_layouts[ret_nb++], fmt); + } } /* 1st round: swap to prepare 2nd round; 2nd round: put it back */ FFSWAP(AVFilterChannelLayouts *, a, b); @@ -280,8 +287,11 @@ static int merge_channel_layouts(void *va, void *vb) if (KNOWN(&a->channel_layouts[i])) continue; for (j = 0; j < b->nb_channel_layouts; j++) - if (!av_channel_layout_compare(&a->channel_layouts[i], &b->channel_layouts[j])) + if (!av_channel_layout_compare(&a->channel_layouts[i], &b->channel_layouts[j])) { + if (check) + return 1; av_channel_layout_copy(&channel_layouts[ret_nb++], &a->channel_layouts[i]); + } } if (!ret_nb) { @@ -300,6 +310,17 @@ static int merge_channel_layouts(void *va, void *vb) return 1; } +static int can_merge_channel_layouts(const void *a, const void *b) +{ + return merge_channel_layouts_internal((AVFilterChannelLayouts *)a, + (AVFilterChannelLayouts *)b, 1); +} + +static int merge_channel_layouts(void *a, void *b) +{ + return merge_channel_layouts_internal(a, b, 0); +} + static const AVFilterFormatsMerger mergers_video[] = { { .offset = offsetof(AVFilterFormatsConfig, formats), @@ -312,7 +333,7 @@ static const AVFilterFormatsMerger mergers_audio[] = { { .offset = offsetof(AVFilterFormatsConfig, channel_layouts), .merge = merge_channel_layouts, - .can_merge = NULL, + .can_merge = can_merge_channel_layouts, }, { .offset = offsetof(AVFilterFormatsConfig, samplerates), diff --git a/libavfilter/framesync.c b/libavfilter/framesync.c index ee91e4cf681..c748262ba6a 100644 --- a/libavfilter/framesync.c +++ b/libavfilter/framesync.c @@ -288,7 +288,7 @@ int ff_framesync_get_frame(FFFrameSync *fs, unsigned in, AVFrame **rframe, if (need_copy) { if (!(frame = av_frame_clone(frame))) return AVERROR(ENOMEM); - if ((ret = av_frame_make_writable(frame)) < 0) { + if ((ret = ff_inlink_make_frame_writable(fs->parent->inputs[in], &frame) < 0)) { av_frame_free(&frame); return ret; } diff --git a/libavfilter/graphparser.c b/libavfilter/graphparser.c index 0759c39014e..96ef6b15bf1 100644 --- a/libavfilter/graphparser.c +++ b/libavfilter/graphparser.c @@ -24,33 +24,15 @@ #include #include "libavutil/avstring.h" +#include "libavutil/dict.h" #include "libavutil/mem.h" #include "libavutil/opt.h" #include "avfilter.h" +#include "internal.h" #define WHITESPACES " \n\t\r" -/** - * Link two filters together. - * - * @see avfilter_link() - */ -static int link_filter(AVFilterContext *src, int srcpad, - AVFilterContext *dst, int dstpad, - void *log_ctx) -{ - int ret; - if ((ret = avfilter_link(src, srcpad, dst, dstpad))) { - av_log(log_ctx, AV_LOG_ERROR, - "Cannot create the link %s:%d -> %s:%d\n", - src->filter->name, srcpad, dst->filter->name, dstpad); - return ret; - } - - return 0; -} - /** * Parse the name of a link, which has the format "[linkname]". * @@ -85,119 +67,6 @@ static char *parse_link_name(const char **buf, void *log_ctx) return name; } -/** - * Create an instance of a filter, initialize and insert it in the - * filtergraph in *ctx. - * - * @param filt_ctx put here a filter context in case of successful creation and configuration, NULL otherwise. - * @param ctx the filtergraph context - * @param index an index which is supposed to be unique for each filter instance added to the filtergraph - * @param name the name of the filter to create, can be filter name or filter_name\@id as instance name - * @param args the arguments provided to the filter during its initialization - * @param log_ctx the log context to use - * @return >= 0 in case of success, a negative AVERROR code otherwise - */ -static int create_filter(AVFilterContext **filt_ctx, AVFilterGraph *ctx, int index, - const char *name, const char *args, void *log_ctx) -{ - const AVFilter *filt; - char name2[30]; - const char *inst_name = NULL, *filt_name = NULL; - int ret, k; - - av_strlcpy(name2, name, sizeof(name2)); - - for (k = 0; name2[k]; k++) { - if (name2[k] == '@' && name[k+1]) { - name2[k] = 0; - inst_name = name; - filt_name = name2; - break; - } - } - - if (!inst_name) { - snprintf(name2, sizeof(name2), "Parsed_%s_%d", name, index); - inst_name = name2; - filt_name = name; - } - - filt = avfilter_get_by_name(filt_name); - - if (!filt) { - av_log(log_ctx, AV_LOG_ERROR, - "No such filter: '%s'\n", filt_name); - return AVERROR(EINVAL); - } - - *filt_ctx = avfilter_graph_alloc_filter(ctx, filt, inst_name); - if (!*filt_ctx) { - av_log(log_ctx, AV_LOG_ERROR, - "Error creating filter '%s'\n", filt_name); - return AVERROR(ENOMEM); - } - - if (!strcmp(filt_name, "scale") && ctx->scale_sws_opts) { - ret = av_set_options_string(*filt_ctx, ctx->scale_sws_opts, "=", ":"); - if (ret < 0) - return ret; - } - - ret = avfilter_init_str(*filt_ctx, args); - if (ret < 0) { - av_log(log_ctx, AV_LOG_ERROR, - "Error initializing filter '%s'", filt_name); - if (args) - av_log(log_ctx, AV_LOG_ERROR, " with args '%s'", args); - av_log(log_ctx, AV_LOG_ERROR, "\n"); - avfilter_free(*filt_ctx); - *filt_ctx = NULL; - } - - return ret; -} - -/** - * Parse a string of the form FILTER_NAME[=PARAMS], and create a - * corresponding filter instance which is added to graph with - * create_filter(). - * - * @param filt_ctx Pointer that is set to the created and configured filter - * context on success, set to NULL on failure. - * @param filt_ctx put here a pointer to the created filter context on - * success, NULL otherwise - * @param buf pointer to the buffer to parse, *buf will be updated to - * point to the char next after the parsed string - * @param index an index which is assigned to the created filter - * instance, and which is supposed to be unique for each filter - * instance added to the filtergraph - * @return >= 0 in case of success, a negative AVERROR code otherwise - */ -static int parse_filter(AVFilterContext **filt_ctx, const char **buf, AVFilterGraph *graph, - int index, void *log_ctx) -{ - char *opts = NULL; - char *name = av_get_token(buf, "=,;["); - int ret; - - if (!name) - return AVERROR(ENOMEM); - - if (**buf == '=') { - (*buf)++; - opts = av_get_token(buf, "[],;"); - if (!opts) { - av_free(name); - return AVERROR(ENOMEM); - } - } - - ret = create_filter(filt_ctx, graph, index, name, opts, log_ctx); - av_free(name); - av_free(opts); - return ret; -} - AVFilterInOut *avfilter_inout_alloc(void) { return av_mallocz(sizeof(AVFilterInOut)); @@ -230,12 +99,6 @@ static AVFilterInOut *extract_inout(const char *label, AVFilterInOut **links) return ret; } -static void insert_inout(AVFilterInOut **inouts, AVFilterInOut *element) -{ - element->next = *inouts; - *inouts = element; -} - static void append_inout(AVFilterInOut **inouts, AVFilterInOut **element) { while (*inouts && (*inouts)->next) @@ -248,145 +111,7 @@ static void append_inout(AVFilterInOut **inouts, AVFilterInOut **element) *element = NULL; } -static int link_filter_inouts(AVFilterContext *filt_ctx, - AVFilterInOut **curr_inputs, - AVFilterInOut **open_inputs, void *log_ctx) -{ - int pad, ret; - - for (pad = 0; pad < filt_ctx->nb_inputs; pad++) { - AVFilterInOut *p = *curr_inputs; - - if (p) { - *curr_inputs = (*curr_inputs)->next; - p->next = NULL; - } else if (!(p = av_mallocz(sizeof(*p)))) - return AVERROR(ENOMEM); - - if (p->filter_ctx) { - ret = link_filter(p->filter_ctx, p->pad_idx, filt_ctx, pad, log_ctx); - av_freep(&p->name); - av_freep(&p); - if (ret < 0) - return ret; - } else { - p->filter_ctx = filt_ctx; - p->pad_idx = pad; - append_inout(open_inputs, &p); - } - } - - if (*curr_inputs) { - av_log(log_ctx, AV_LOG_ERROR, - "Too many inputs specified for the \"%s\" filter.\n", - filt_ctx->filter->name); - return AVERROR(EINVAL); - } - - pad = filt_ctx->nb_outputs; - while (pad--) { - AVFilterInOut *currlinkn = av_mallocz(sizeof(AVFilterInOut)); - if (!currlinkn) - return AVERROR(ENOMEM); - currlinkn->filter_ctx = filt_ctx; - currlinkn->pad_idx = pad; - insert_inout(curr_inputs, currlinkn); - } - - return 0; -} - -static int parse_inputs(const char **buf, AVFilterInOut **curr_inputs, - AVFilterInOut **open_outputs, void *log_ctx) -{ - AVFilterInOut *parsed_inputs = NULL; - int pad = 0; - - while (**buf == '[') { - char *name = parse_link_name(buf, log_ctx); - AVFilterInOut *match; - - if (!name) { - avfilter_inout_free(&parsed_inputs); - return AVERROR(EINVAL); - } - - /* First check if the label is not in the open_outputs list */ - match = extract_inout(name, open_outputs); - - if (match) { - av_free(name); - } else { - /* Not in the list, so add it as an input */ - if (!(match = av_mallocz(sizeof(AVFilterInOut)))) { - avfilter_inout_free(&parsed_inputs); - av_free(name); - return AVERROR(ENOMEM); - } - match->name = name; - match->pad_idx = pad; - } - - append_inout(&parsed_inputs, &match); - - *buf += strspn(*buf, WHITESPACES); - pad++; - } - - append_inout(&parsed_inputs, curr_inputs); - *curr_inputs = parsed_inputs; - - return pad; -} - -static int parse_outputs(const char **buf, AVFilterInOut **curr_inputs, - AVFilterInOut **open_inputs, - AVFilterInOut **open_outputs, void *log_ctx) -{ - int ret, pad = 0; - - while (**buf == '[') { - char *name = parse_link_name(buf, log_ctx); - AVFilterInOut *match; - - AVFilterInOut *input = *curr_inputs; - - if (!name) - return AVERROR(EINVAL); - - if (!input) { - av_log(log_ctx, AV_LOG_ERROR, - "No output pad can be associated to link label '%s'.\n", name); - av_free(name); - return AVERROR(EINVAL); - } - *curr_inputs = (*curr_inputs)->next; - - /* First check if the label is not in the open_inputs list */ - match = extract_inout(name, open_inputs); - - if (match) { - ret = link_filter(input->filter_ctx, input->pad_idx, - match->filter_ctx, match->pad_idx, log_ctx); - av_freep(&match->name); - av_freep(&name); - av_freep(&match); - av_freep(&input); - if (ret < 0) - return ret; - } else { - /* Not in the list, so add the first input as an open_output */ - input->name = name; - insert_inout(open_outputs, input); - } - *buf += strspn(*buf, WHITESPACES); - pad++; - } - - return pad; -} - -static int parse_sws_flags(const char **buf, AVFilterGraph *graph) +static int parse_sws_flags(const char **buf, char **dst, void *log_ctx) { char *p = strchr(*buf, ';'); @@ -394,16 +119,16 @@ static int parse_sws_flags(const char **buf, AVFilterGraph *graph) return 0; if (!p) { - av_log(graph, AV_LOG_ERROR, "sws_flags not terminated with ';'.\n"); + av_log(log_ctx, AV_LOG_ERROR, "sws_flags not terminated with ';'.\n"); return AVERROR(EINVAL); } *buf += 4; // keep the 'flags=' part - av_freep(&graph->scale_sws_opts); - if (!(graph->scale_sws_opts = av_mallocz(p - *buf + 1))) + av_freep(dst); + if (!(*dst = av_mallocz(p - *buf + 1))) return AVERROR(ENOMEM); - av_strlcpy(graph->scale_sws_opts, *buf, p - *buf + 1); + av_strlcpy(*dst, *buf, p - *buf + 1); *buf = p + 1; return 0; @@ -413,66 +138,24 @@ int avfilter_graph_parse2(AVFilterGraph *graph, const char *filters, AVFilterInOut **inputs, AVFilterInOut **outputs) { - int index = 0, ret = 0; - char chr = 0; - - AVFilterInOut *curr_inputs = NULL, *open_inputs = NULL, *open_outputs = NULL; - - filters += strspn(filters, WHITESPACES); - - if ((ret = parse_sws_flags(&filters, graph)) < 0) - goto end; - - do { - AVFilterContext *filter; - filters += strspn(filters, WHITESPACES); - - if ((ret = parse_inputs(&filters, &curr_inputs, &open_outputs, graph)) < 0) - goto end; - if ((ret = parse_filter(&filter, &filters, graph, index, graph)) < 0) - goto end; - - - if ((ret = link_filter_inouts(filter, &curr_inputs, &open_inputs, graph)) < 0) - goto end; - - if ((ret = parse_outputs(&filters, &curr_inputs, &open_inputs, &open_outputs, - graph)) < 0) - goto end; - - filters += strspn(filters, WHITESPACES); - chr = *filters++; + AVFilterGraphSegment *seg; + int ret; - if (chr == ';' && curr_inputs) - append_inout(&open_outputs, &curr_inputs); - index++; - } while (chr == ',' || chr == ';'); + ret = avfilter_graph_segment_parse(graph, filters, 0, &seg); + if (ret < 0) + return ret; - if (chr) { - av_log(graph, AV_LOG_ERROR, - "Unable to parse graph description substring: \"%s\"\n", - filters - 1); - ret = AVERROR(EINVAL); + ret = avfilter_graph_segment_apply(seg, 0, inputs, outputs); + avfilter_graph_segment_free(&seg); + if (ret < 0) goto end; - } - - append_inout(&open_outputs, &curr_inputs); - - *inputs = open_inputs; - *outputs = open_outputs; return 0; end: while (graph->nb_filters) avfilter_free(graph->filters[0]); av_freep(&graph->filters); - avfilter_inout_free(&open_inputs); - avfilter_inout_free(&open_outputs); - avfilter_inout_free(&curr_inputs); - - *inputs = NULL; - *outputs = NULL; return ret; } @@ -540,86 +223,818 @@ int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, return ret; } -int avfilter_graph_parse_ptr(AVFilterGraph *graph, const char *filters, - AVFilterInOut **open_inputs_ptr, AVFilterInOut **open_outputs_ptr, - void *log_ctx) +static void pad_params_free(AVFilterPadParams **pfpp) { - int index = 0, ret = 0; - char chr = 0; + AVFilterPadParams *fpp = *pfpp; - AVFilterInOut *curr_inputs = NULL; - AVFilterInOut *open_inputs = open_inputs_ptr ? *open_inputs_ptr : NULL; - AVFilterInOut *open_outputs = open_outputs_ptr ? *open_outputs_ptr : NULL; + if (!fpp) + return; - if ((ret = parse_sws_flags(&filters, graph)) < 0) - goto end; + av_freep(&fpp->label); - do { - AVFilterContext *filter; - const char *filterchain = filters; - filters += strspn(filters, WHITESPACES); + av_freep(pfpp); +} - if ((ret = parse_inputs(&filters, &curr_inputs, &open_outputs, log_ctx)) < 0) - goto end; +static void filter_params_free(AVFilterParams **pp) +{ + AVFilterParams *p = *pp; - if ((ret = parse_filter(&filter, &filters, graph, index, log_ctx)) < 0) - goto end; + if (!p) + return; - if (filter->nb_inputs == 1 && !curr_inputs && !index) { - /* First input pad, assume it is "[in]" if not specified */ - const char *tmp = "[in]"; - if ((ret = parse_inputs(&tmp, &curr_inputs, &open_outputs, log_ctx)) < 0) - goto end; + for (unsigned i = 0; i < p->nb_inputs; i++) + pad_params_free(&p->inputs[i]); + av_freep(&p->inputs); + + for (unsigned i = 0; i < p->nb_outputs; i++) + pad_params_free(&p->outputs[i]); + av_freep(&p->outputs); + + av_dict_free(&p->opts); + + av_freep(&p->filter_name); + av_freep(&p->instance_name); + + av_freep(pp); +} + +static void chain_free(AVFilterChain **pch) +{ + AVFilterChain *ch = *pch; + + if (!ch) + return; + + for (size_t i = 0; i < ch->nb_filters; i++) + filter_params_free(&ch->filters[i]); + av_freep(&ch->filters); + + av_freep(pch); +} + +void avfilter_graph_segment_free(AVFilterGraphSegment **pseg) +{ + AVFilterGraphSegment *seg = *pseg; + + if (!seg) + return; + + for (size_t i = 0; i < seg->nb_chains; i++) + chain_free(&seg->chains[i]); + av_freep(&seg->chains); + + av_freep(&seg->scale_sws_opts); + + av_freep(pseg); +} + +static int linklabels_parse(void *logctx, const char **linklabels, + AVFilterPadParams ***res, unsigned *nb_res) +{ + AVFilterPadParams **pp = NULL; + int nb = 0; + int ret; + + while (**linklabels == '[') { + char *label; + AVFilterPadParams *par; + + label = parse_link_name(linklabels, logctx); + if (!label) { + ret = AVERROR(EINVAL); + goto fail; } - if ((ret = link_filter_inouts(filter, &curr_inputs, &open_inputs, log_ctx)) < 0) - goto end; + par = av_mallocz(sizeof(*par)); + if (!par) { + av_freep(&label); + ret = AVERROR(ENOMEM); + goto fail; + } - if ((ret = parse_outputs(&filters, &curr_inputs, &open_inputs, &open_outputs, - log_ctx)) < 0) - goto end; + par->label = label; - filters += strspn(filters, WHITESPACES); - chr = *filters++; + ret = av_dynarray_add_nofree(&pp, &nb, par); + if (ret < 0) { + pad_params_free(&par); + goto fail; + } - if (chr == ';' && curr_inputs) { - av_log(log_ctx, AV_LOG_ERROR, - "Invalid filterchain containing an unlabelled output pad: \"%s\"\n", - filterchain); + *linklabels += strspn(*linklabels, WHITESPACES); + } + + *res = pp; + *nb_res = nb; + + return 0; +fail: + for (unsigned i = 0; i < nb; i++) + pad_params_free(&pp[i]); + av_freep(&pp); + return ret; +} + +static int filter_parse(void *logctx, const char **filter, + AVFilterParams **pp) +{ + AVFilterParams *p; + char *inst_name; + int ret; + + p = av_mallocz(sizeof(*p)); + if (!p) + return AVERROR(ENOMEM); + + ret = linklabels_parse(logctx, filter, &p->inputs, &p->nb_inputs); + if (ret < 0) + goto fail; + + p->filter_name = av_get_token(filter, "=,;["); + if (!p->filter_name) { + ret = AVERROR(ENOMEM); + goto fail; + } + + inst_name = strchr(p->filter_name, '@'); + if (inst_name) { + *inst_name++ = 0; + p->instance_name = av_strdup(inst_name); + if (!p->instance_name) { + ret = AVERROR(ENOMEM); + goto fail; + } + } + + if (**filter == '=') { + const AVFilter *f = avfilter_get_by_name(p->filter_name); + char *opts; + + (*filter)++; + + opts = av_get_token(filter, "[],;"); + if (!opts) { + ret = AVERROR(ENOMEM); + goto fail; + } + + ret = ff_filter_opt_parse(logctx, f ? f->priv_class : NULL, + &p->opts, opts); + av_freep(&opts); + if (ret < 0) + goto fail; + } + + ret = linklabels_parse(logctx, filter, &p->outputs, &p->nb_outputs); + if (ret < 0) + goto fail; + + *filter += strspn(*filter, WHITESPACES); + + *pp = p; + return 0; +fail: + av_log(logctx, AV_LOG_ERROR, + "Error parsing a filter description around: %s\n", *filter); + filter_params_free(&p); + return ret; +} + +static int chain_parse(void *logctx, const char **pchain, + AVFilterChain **pch) +{ + const char *chain = *pchain; + AVFilterChain *ch; + int ret, nb_filters = 0; + + *pch = NULL; + + ch = av_mallocz(sizeof(*ch)); + if (!ch) + return AVERROR(ENOMEM); + + while (*chain) { + AVFilterParams *p; + char chr; + + ret = filter_parse(logctx, &chain, &p); + if (ret < 0) + goto fail; + + ret = av_dynarray_add_nofree(&ch->filters, &nb_filters, p); + if (ret < 0) { + filter_params_free(&p); + goto fail; + } + ch->nb_filters = nb_filters; + + // a filter ends with one of: , ; end-of-string + chr = *chain; + if (chr && chr != ',' && chr != ';') { + av_log(logctx, AV_LOG_ERROR, + "Trailing garbage after a filter: %s\n", chain); ret = AVERROR(EINVAL); - goto end; + goto fail; } - index++; - } while (chr == ',' || chr == ';'); - if (chr) { - av_log(log_ctx, AV_LOG_ERROR, - "Unable to parse graph description substring: \"%s\"\n", - filters - 1); + if (chr) { + chain++; + chain += strspn(chain, WHITESPACES); + + if (chr == ';') + break; + } + } + + *pchain = chain; + *pch = ch; + + return 0; +fail: + av_log(logctx, AV_LOG_ERROR, + "Error parsing filterchain '%s' around: %s\n", *pchain, chain); + chain_free(&ch); + return ret; +} + +int avfilter_graph_segment_parse(AVFilterGraph *graph, const char *graph_str, + int flags, AVFilterGraphSegment **pseg) +{ + AVFilterGraphSegment *seg; + int ret, nb_chains = 0; + + *pseg = NULL; + + if (flags) + return AVERROR(ENOSYS); + + seg = av_mallocz(sizeof(*seg)); + if (!seg) + return AVERROR(ENOMEM); + + seg->graph = graph; + + graph_str += strspn(graph_str, WHITESPACES); + + ret = parse_sws_flags(&graph_str, &seg->scale_sws_opts, graph); + if (ret < 0) + goto fail; + + graph_str += strspn(graph_str, WHITESPACES); + + while (*graph_str) { + AVFilterChain *ch; + + ret = chain_parse(graph, &graph_str, &ch); + if (ret < 0) + goto fail; + + ret = av_dynarray_add_nofree(&seg->chains, &nb_chains, ch); + if (ret < 0) { + chain_free(&ch); + goto fail; + } + seg->nb_chains = nb_chains; + + graph_str += strspn(graph_str, WHITESPACES); + } + + if (!seg->nb_chains) { + av_log(graph, AV_LOG_ERROR, "No filters specified in the graph description\n"); ret = AVERROR(EINVAL); + goto fail; + } + + *pseg = seg; + + return 0; +fail: + avfilter_graph_segment_free(&seg); + return ret; +} + +int avfilter_graph_segment_create_filters(AVFilterGraphSegment *seg, int flags) +{ + size_t idx = 0; + + if (flags) + return AVERROR(ENOSYS); + + if (seg->scale_sws_opts) { + av_freep(&seg->graph->scale_sws_opts); + seg->graph->scale_sws_opts = av_strdup(seg->scale_sws_opts); + if (!seg->graph->scale_sws_opts) + return AVERROR(ENOMEM); + } + + for (size_t i = 0; i < seg->nb_chains; i++) { + AVFilterChain *ch = seg->chains[i]; + + for (size_t j = 0; j < ch->nb_filters; j++) { + AVFilterParams *p = ch->filters[j]; + const AVFilter *f = avfilter_get_by_name(p->filter_name); + char name[64]; + + // skip already processed filters + if (p->filter || !p->filter_name) + continue; + + if (!f) { + av_log(seg->graph, AV_LOG_ERROR, + "No such filter: '%s'\n", p->filter_name); + return AVERROR_FILTER_NOT_FOUND; + } + + if (!p->instance_name) + snprintf(name, sizeof(name), "Parsed_%s_%zu", f->name, idx); + else + snprintf(name, sizeof(name), "%s@%s", f->name, p->instance_name); + + p->filter = avfilter_graph_alloc_filter(seg->graph, f, name); + if (!p->filter) + return AVERROR(ENOMEM); + + if (!strcmp(f->name, "scale") && seg->graph->scale_sws_opts) { + int ret = av_set_options_string(p->filter, seg->graph->scale_sws_opts, + "=", ":"); + if (ret < 0) { + avfilter_free(p->filter); + p->filter = NULL; + return ret; + } + } + + av_freep(&p->filter_name); + av_freep(&p->instance_name); + + idx++; + } + } + + return 0; +} + +static int fail_creation_pending(AVFilterGraphSegment *seg, const char *fn, + const char *func) +{ + av_log(seg->graph, AV_LOG_ERROR, + "A creation-pending filter '%s' present in the segment. All filters " + "must be created or disabled before calling %s().\n", fn, func); + return AVERROR(EINVAL); +} + +int avfilter_graph_segment_apply_opts(AVFilterGraphSegment *seg, int flags) +{ + int ret, leftover_opts = 0; + + if (flags) + return AVERROR(ENOSYS); + + for (size_t i = 0; i < seg->nb_chains; i++) { + AVFilterChain *ch = seg->chains[i]; + + for (size_t j = 0; j < ch->nb_filters; j++) { + AVFilterParams *p = ch->filters[j]; + + if (p->filter_name) + return fail_creation_pending(seg, p->filter_name, __func__); + if (!p->filter || !p->opts) + continue; + + ret = av_opt_set_dict2(p->filter, &p->opts, AV_OPT_SEARCH_CHILDREN); + if (ret < 0) + return ret; + + if (av_dict_count(p->opts)) + leftover_opts = 1; + } + } + + return leftover_opts ? AVERROR_OPTION_NOT_FOUND : 0; +} + +int avfilter_graph_segment_init(AVFilterGraphSegment *seg, int flags) +{ + if (flags) + return AVERROR(ENOSYS); + + for (size_t i = 0; i < seg->nb_chains; i++) { + AVFilterChain *ch = seg->chains[i]; + + for (size_t j = 0; j < ch->nb_filters; j++) { + AVFilterParams *p = ch->filters[j]; + int ret; + + if (p->filter_name) + return fail_creation_pending(seg, p->filter_name, __func__); + if (!p->filter || p->filter->internal->initialized) + continue; + + ret = avfilter_init_dict(p->filter, NULL); + if (ret < 0) + return ret; + } + } + + return 0; +} + +static unsigned +find_linklabel(AVFilterGraphSegment *seg, const char *label, + int output, size_t idx_chain, size_t idx_filter, + AVFilterParams **pp) +{ + for (; idx_chain < seg->nb_chains; idx_chain++) { + AVFilterChain *ch = seg->chains[idx_chain]; + + for (; idx_filter < ch->nb_filters; idx_filter++) { + AVFilterParams *p = ch->filters[idx_filter]; + AVFilterPadParams **io = output ? p->outputs : p->inputs; + unsigned nb_io = output ? p->nb_outputs : p->nb_inputs; + AVFilterLink **l; + unsigned nb_l; + + if (!p->filter) + continue; + + l = output ? p->filter->outputs : p->filter->inputs; + nb_l = output ? p->filter->nb_outputs : p->filter->nb_inputs; + + for (unsigned i = 0; i < FFMIN(nb_io, nb_l); i++) + if (!l[i] && io[i]->label && !strcmp(io[i]->label, label)) { + *pp = p; + return i; + } + } + + idx_filter = 0; + } + + *pp = NULL; + return 0; +} + +static int inout_add(AVFilterInOut **inouts, AVFilterContext *f, unsigned pad_idx, + const char *label) +{ + AVFilterInOut *io = av_mallocz(sizeof(*io)); + + if (!io) + return AVERROR(ENOMEM); + + io->filter_ctx = f; + io->pad_idx = pad_idx; + + if (label) { + io->name = av_strdup(label); + if (!io->name) { + avfilter_inout_free(&io); + return AVERROR(ENOMEM); + } + } + + append_inout(inouts, &io); + + return 0; +} + +static int link_inputs(AVFilterGraphSegment *seg, size_t idx_chain, + size_t idx_filter, AVFilterInOut **inputs) +{ + AVFilterChain *ch = seg->chains[idx_chain]; + AVFilterParams *p = ch->filters[idx_filter]; + AVFilterContext *f = p->filter; + + int ret; + + if (f->nb_inputs < p->nb_inputs) { + av_log(seg->graph, AV_LOG_ERROR, + "More input link labels specified for filter '%s' than " + "it has inputs: %u > %d\n", f->filter->name, + p->nb_inputs, f->nb_inputs); + return AVERROR(EINVAL); + } + + for (unsigned in = 0; in < f->nb_inputs; in++) { + const char *label = (in < p->nb_inputs) ? p->inputs[in]->label : NULL; + + // skip already linked inputs + if (f->inputs[in]) + continue; + + if (label) { + AVFilterParams *po = NULL; + unsigned idx = find_linklabel(seg, label, 1, idx_chain, idx_filter, &po); + + if (po) { + ret = avfilter_link(po->filter, idx, f, in); + if (ret < 0) + return ret; + + continue; + } + } + + ret = inout_add(inputs, f, in, label); + if (ret < 0) + return ret; + } + + return 0; +} + +static int link_outputs(AVFilterGraphSegment *seg, size_t idx_chain, + size_t idx_filter, AVFilterInOut **outputs) +{ + AVFilterChain *ch = seg->chains[idx_chain]; + AVFilterParams *p = ch->filters[idx_filter]; + AVFilterContext *f = p->filter; + + int ret; + + if (f->nb_outputs < p->nb_outputs) { + av_log(seg->graph, AV_LOG_ERROR, + "More output link labels specified for filter '%s' than " + "it has outputs: %u > %d\n", f->filter->name, + p->nb_outputs, f->nb_outputs); + return AVERROR(EINVAL); + } + for (unsigned out = 0; out < f->nb_outputs; out++) { + char *label = (out < p->nb_outputs) ? p->outputs[out]->label : NULL; + + // skip already linked outputs + if (f->outputs[out]) + continue; + + if (label) { + AVFilterParams *po = NULL; + unsigned idx = find_linklabel(seg, label, 0, idx_chain, idx_filter, &po); + + if (po) { + ret = avfilter_link(f, out, po->filter, idx); + if (ret < 0) + return ret; + + continue; + } + } + + // if this output is unlabeled, try linking it to an unlabeled + // input in the next non-disabled filter in the chain + for (size_t i = idx_filter + 1; i < ch->nb_filters && !label; i++) { + AVFilterParams *p_next = ch->filters[i]; + + if (!p_next->filter) + continue; + + for (unsigned in = 0; in < p_next->filter->nb_inputs; in++) { + if (!p_next->filter->inputs[in] && + (in >= p_next->nb_inputs || !p_next->inputs[in]->label)) { + ret = avfilter_link(f, out, p_next->filter, in); + if (ret < 0) + return ret; + + goto cont; + } + } + break; + } + + ret = inout_add(outputs, f, out, label); + if (ret < 0) + return ret; + +cont:; + } + + return 0; +} + +int avfilter_graph_segment_link(AVFilterGraphSegment *seg, int flags, + AVFilterInOut **inputs, + AVFilterInOut **outputs) +{ + int ret; + + *inputs = NULL; + *outputs = NULL; + + if (flags) + return AVERROR(ENOSYS); + + for (size_t idx_chain = 0; idx_chain < seg->nb_chains; idx_chain++) { + AVFilterChain *ch = seg->chains[idx_chain]; + + for (size_t idx_filter = 0; idx_filter < ch->nb_filters; idx_filter++) { + AVFilterParams *p = ch->filters[idx_filter]; + + if (p->filter_name) { + ret = fail_creation_pending(seg, p->filter_name, __func__); + goto fail; + } + + if (!p->filter) + continue; + + ret = link_inputs(seg, idx_chain, idx_filter, inputs); + if (ret < 0) + goto fail; + + ret = link_outputs(seg, idx_chain, idx_filter, outputs); + if (ret < 0) + goto fail; + } + } + return 0; +fail: + avfilter_inout_free(inputs); + avfilter_inout_free(outputs); + return ret; +} + +// print an error message if some options were not found +static void log_unknown_opt(const AVFilterGraphSegment *seg) +{ + for (size_t i = 0; i < seg->nb_chains; i++) { + const AVFilterChain *ch = seg->chains[i]; + + for (size_t j = 0; j < ch->nb_filters; j++) { + const AVFilterParams *p = ch->filters[j]; + const AVDictionaryEntry *e; + + if (!p->filter) + continue; + + e = av_dict_iterate(p->opts, NULL); + + if (e) { + av_log(p->filter, AV_LOG_ERROR, + "Could not set non-existent option '%s' to value '%s'\n", + e->key, e->value); + return; + } + } + } + +} + +int avfilter_graph_segment_apply(AVFilterGraphSegment *seg, int flags, + AVFilterInOut **inputs, + AVFilterInOut **outputs) +{ + int ret; + + if (flags) + return AVERROR(ENOSYS); + + ret = avfilter_graph_segment_create_filters(seg, 0); + if (ret < 0) { + av_log(seg->graph, AV_LOG_ERROR, "Error creating filters\n"); + return ret; + } + + ret = avfilter_graph_segment_apply_opts(seg, 0); + if (ret < 0) { + if (ret == AVERROR_OPTION_NOT_FOUND) + log_unknown_opt(seg); + av_log(seg->graph, AV_LOG_ERROR, "Error applying filter options\n"); + return ret; + } + + ret = avfilter_graph_segment_init(seg, 0); + if (ret < 0) { + av_log(seg->graph, AV_LOG_ERROR, "Error initializing filters\n"); + return ret; + } + + ret = avfilter_graph_segment_link(seg, 0, inputs, outputs); + if (ret < 0) { + av_log(seg->graph, AV_LOG_ERROR, "Error linking filters\n"); + return ret; + } + + return 0; +} + +int avfilter_graph_parse_ptr(AVFilterGraph *graph, const char *filters, + AVFilterInOut **open_inputs_ptr, AVFilterInOut **open_outputs_ptr, + void *log_ctx) +{ + AVFilterInOut *user_inputs = open_inputs_ptr ? *open_inputs_ptr : NULL; + AVFilterInOut *user_outputs = open_outputs_ptr ? *open_outputs_ptr : NULL; + + AVFilterInOut *inputs = NULL, *outputs = NULL; + AVFilterGraphSegment *seg = NULL; + AVFilterChain *ch; + AVFilterParams *p; + int ret; + + ret = avfilter_graph_segment_parse(graph, filters, 0, &seg); + if (ret < 0) + goto end; + + ret = avfilter_graph_segment_create_filters(seg, 0); + if (ret < 0) + goto end; + + ret = avfilter_graph_segment_apply_opts(seg, 0); + if (ret < 0) { + if (ret == AVERROR_OPTION_NOT_FOUND) + log_unknown_opt(seg); + goto end; + } + + ret = avfilter_graph_segment_init(seg, 0); + if (ret < 0) goto end; + + /* First input pad, assume it is "[in]" if not specified */ + p = seg->chains[0]->filters[0]; + if (p->filter->nb_inputs == 1 && !p->inputs) { + const char *tmp = "[in]"; + + ret = linklabels_parse(graph, &tmp, &p->inputs, &p->nb_inputs); + if (ret < 0) + goto end; } - if (curr_inputs) { - /* Last output pad, assume it is "[out]" if not specified */ + /* Last output pad, assume it is "[out]" if not specified */ + ch = seg->chains[seg->nb_chains - 1]; + p = ch->filters[ch->nb_filters - 1]; + if (p->filter->nb_outputs == 1 && !p->outputs) { const char *tmp = "[out]"; - if ((ret = parse_outputs(&tmp, &curr_inputs, &open_inputs, &open_outputs, - log_ctx)) < 0) + + ret = linklabels_parse(graph, &tmp, &p->outputs, &p->nb_outputs); + if (ret < 0) goto end; } + ret = avfilter_graph_segment_apply(seg, 0, &inputs, &outputs); + avfilter_graph_segment_free(&seg); + if (ret < 0) + goto end; + + // process user-supplied inputs/outputs + while (inputs) { + AVFilterInOut *cur, *match = NULL; + + cur = inputs; + inputs = cur->next; + cur->next = NULL; + + if (cur->name) + match = extract_inout(cur->name, &user_outputs); + + if (match) { + ret = avfilter_link(match->filter_ctx, match->pad_idx, + cur->filter_ctx, cur->pad_idx); + avfilter_inout_free(&match); + avfilter_inout_free(&cur); + if (ret < 0) + goto end; + } else + append_inout(&user_inputs, &cur); + } + while (outputs) { + AVFilterInOut *cur, *match = NULL; + + cur = outputs; + outputs = cur->next; + cur->next = NULL; + + if (cur->name) + match = extract_inout(cur->name, &user_inputs); + + if (match) { + ret = avfilter_link(cur->filter_ctx, cur->pad_idx, + match->filter_ctx, match->pad_idx); + avfilter_inout_free(&match); + avfilter_inout_free(&cur); + if (ret < 0) + goto end; + } else + append_inout(&user_outputs, &cur); + } + end: - /* clear open_in/outputs only if not passed as parameters */ - if (open_inputs_ptr) *open_inputs_ptr = open_inputs; - else avfilter_inout_free(&open_inputs); - if (open_outputs_ptr) *open_outputs_ptr = open_outputs; - else avfilter_inout_free(&open_outputs); - avfilter_inout_free(&curr_inputs); + avfilter_graph_segment_free(&seg); if (ret < 0) { + av_log(graph, AV_LOG_ERROR, "Error processing filtergraph: %s\n", + av_err2str(ret)); + while (graph->nb_filters) avfilter_free(graph->filters[0]); av_freep(&graph->filters); } + + /* clear open_in/outputs only if not passed as parameters */ + if (open_inputs_ptr) *open_inputs_ptr = user_inputs; + else avfilter_inout_free(&user_inputs); + if (open_outputs_ptr) *open_outputs_ptr = user_outputs; + else avfilter_inout_free(&user_outputs); + + avfilter_inout_free(&inputs); + avfilter_inout_free(&outputs); + return ret; } diff --git a/libavfilter/internal.h b/libavfilter/internal.h index aaf2c6c584c..2dbc5def0a9 100644 --- a/libavfilter/internal.h +++ b/libavfilter/internal.h @@ -26,9 +26,7 @@ #include "libavutil/internal.h" #include "avfilter.h" -#include "formats.h" #include "framequeue.h" -#include "video.h" typedef struct AVFilterCommand { double time; ///< time expressed in seconds @@ -137,6 +135,10 @@ struct AVFilterGraphInternal { struct AVFilterInternal { avfilter_execute_func *execute; + + // 1 when avfilter_init_*() was successfully called on this filter + // 0 otherwise + int initialized; }; static av_always_inline int ff_filter_execute(AVFilterContext *ctx, avfilter_action_func *func, @@ -239,8 +241,6 @@ av_warn_unused_result int ff_parse_channel_layout(AVChannelLayout *ret, int *nret, const char *arg, void *log_ctx); -void ff_update_link_current_pts(AVFilterLink *link, int64_t pts); - /** * Set the status field of a link from the source filter. * The pts should reflect the timestamp of the status change, @@ -250,12 +250,6 @@ void ff_update_link_current_pts(AVFilterLink *link, int64_t pts); */ void ff_avfilter_link_set_in_status(AVFilterLink *link, int status, int64_t pts); -/** - * Set the status field of a link from the destination filter. - * The pts should probably be left unset (AV_NOPTS_VALUE). - */ -void ff_avfilter_link_set_out_status(AVFilterLink *link, int status, int64_t pts); - #define D2TS(d) (isnan(d) ? AV_NOPTS_VALUE : (int64_t)(d)) #define TS2D(ts) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts)) #define TS2T(ts, tb) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts) * av_q2d(tb)) @@ -405,4 +399,17 @@ int ff_filter_process_command(AVFilterContext *ctx, const char *cmd, int ff_filter_init_hw_frames(AVFilterContext *avctx, AVFilterLink *link, int default_pool_size); +/** + * Parse filter options into a dictionary. + * + * @param logctx context for logging + * @param priv_class a filter's private class for shorthand options or NULL + * @param options dictionary to store parsed options in + * @param args options string to parse + * + * @return a non-negative number on success, a negative error code on failure + */ +int ff_filter_opt_parse(void *logctx, const AVClass *priv_class, + AVDictionary **options, const char *args); + #endif /* AVFILTER_INTERNAL_H */ diff --git a/libavfilter/median_template.c b/libavfilter/median_template.c index bb7646e0eb1..760a0278c94 100644 --- a/libavfilter/median_template.c +++ b/libavfilter/median_template.c @@ -22,7 +22,6 @@ #include "libavutil/avassert.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" diff --git a/libavfilter/opencl.c b/libavfilter/opencl.c index 5d1e297af8a..48752e95305 100644 --- a/libavfilter/opencl.c +++ b/libavfilter/opencl.c @@ -23,7 +23,6 @@ #include "libavutil/mem.h" #include "libavutil/pixdesc.h" -#include "formats.h" #include "opencl.h" static int opencl_filter_set_device(AVFilterContext *avctx, diff --git a/libavfilter/opencl_source.h b/libavfilter/opencl_source.h index 9eac2dc516a..b6930fb686d 100644 --- a/libavfilter/opencl_source.h +++ b/libavfilter/opencl_source.h @@ -19,19 +19,19 @@ #ifndef AVFILTER_OPENCL_SOURCE_H #define AVFILTER_OPENCL_SOURCE_H -extern const char *ff_opencl_source_avgblur; -extern const char *ff_opencl_source_colorkey; -extern const char *ff_opencl_source_colorspace_common; -extern const char *ff_opencl_source_convolution; -extern const char *ff_opencl_source_deshake; -extern const char *ff_opencl_source_neighbor; -extern const char *ff_opencl_source_nlmeans; -extern const char *ff_opencl_source_overlay; -extern const char *ff_opencl_source_pad; -extern const char *ff_opencl_source_remap; -extern const char *ff_opencl_source_tonemap; -extern const char *ff_opencl_source_transpose; -extern const char *ff_opencl_source_unsharp; -extern const char *ff_opencl_source_xfade; +extern const char *ff_source_avgblur_cl; +extern const char *ff_source_colorkey_cl; +extern const char *ff_source_colorspace_common_cl; +extern const char *ff_source_convolution_cl; +extern const char *ff_source_deshake_cl; +extern const char *ff_source_neighbor_cl; +extern const char *ff_source_nlmeans_cl; +extern const char *ff_source_overlay_cl; +extern const char *ff_source_pad_cl; +extern const char *ff_source_remap_cl; +extern const char *ff_source_tonemap_cl; +extern const char *ff_source_transpose_cl; +extern const char *ff_source_unsharp_cl; +extern const char *ff_source_xfade_cl; #endif /* AVFILTER_OPENCL_SOURCE_H */ diff --git a/libavfilter/phase_template.c b/libavfilter/phase_template.c index 84506702342..c25bc884153 100644 --- a/libavfilter/phase_template.c +++ b/libavfilter/phase_template.c @@ -20,7 +20,6 @@ #include "libavutil/avassert.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -57,10 +56,10 @@ static enum PhaseMode fn(analyze_plane)(void *ctx, enum PhaseMode mode, AVFrame double bdiff, tdiff, pdiff; if (mode == AUTO) { - mode = new->interlaced_frame ? new->top_field_first ? + mode = (new->flags & AV_FRAME_FLAG_INTERLACED) ? (new->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? TOP_FIRST : BOTTOM_FIRST : PROGRESSIVE; } else if (mode == AUTO_ANALYZE) { - mode = new->interlaced_frame ? new->top_field_first ? + mode = (new->flags & AV_FRAME_FLAG_INTERLACED) ? (new->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? TOP_FIRST_ANALYZE : BOTTOM_FIRST_ANALYZE : FULL_ANALYZE; } diff --git a/libavfilter/qsvvpp.c b/libavfilter/qsvvpp.c index 064b105a177..5cdba7d54ab 100644 --- a/libavfilter/qsvvpp.c +++ b/libavfilter/qsvvpp.c @@ -247,6 +247,10 @@ static int pix_fmt_to_mfx_fourcc(int format) return MFX_FOURCC_RGB4; case AV_PIX_FMT_P010: return MFX_FOURCC_P010; +#if CONFIG_VAAPI + case AV_PIX_FMT_UYVY422: + return MFX_FOURCC_UYVY; +#endif } return MFX_FOURCC_NV12; @@ -276,6 +280,11 @@ static int map_frame_to_surface(AVFrame *frame, mfxFrameSurface1 *surface) surface->Data.R = frame->data[0] + 2; surface->Data.A = frame->data[0] + 3; break; + case AV_PIX_FMT_UYVY422: + surface->Data.Y = frame->data[0] + 1; + surface->Data.U = frame->data[0]; + surface->Data.V = frame->data[0] + 2; + break; default: return MFX_ERR_UNSUPPORTED; } @@ -432,7 +441,10 @@ static QSVFrame *submit_frame(QSVVPPContext *s, AVFilterLink *inlink, AVFrame *p return NULL; } - av_frame_copy_props(qsv_frame->frame, picref); + if (av_frame_copy_props(qsv_frame->frame, picref) < 0) { + av_frame_free(&qsv_frame->frame); + return NULL; + } } else qsv_frame->frame = av_frame_clone(picref); @@ -448,8 +460,8 @@ static QSVFrame *submit_frame(QSVVPPContext *s, AVFilterLink *inlink, AVFrame *p inlink->time_base, default_tb); qsv_frame->surface.Info.PicStruct = - !qsv_frame->frame->interlaced_frame ? MFX_PICSTRUCT_PROGRESSIVE : - (qsv_frame->frame->top_field_first ? MFX_PICSTRUCT_FIELD_TFF : + !(qsv_frame->frame->flags & AV_FRAME_FLAG_INTERLACED) ? MFX_PICSTRUCT_PROGRESSIVE : + ((qsv_frame->frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? MFX_PICSTRUCT_FIELD_TFF : MFX_PICSTRUCT_FIELD_BFF); if (qsv_frame->frame->repeat_pict == 1) qsv_frame->surface.Info.PicStruct |= MFX_PICSTRUCT_FIELD_REPEATED; @@ -462,7 +474,7 @@ static QSVFrame *submit_frame(QSVVPPContext *s, AVFilterLink *inlink, AVFrame *p } /* get the output surface */ -static QSVFrame *query_frame(QSVVPPContext *s, AVFilterLink *outlink) +static QSVFrame *query_frame(QSVVPPContext *s, AVFilterLink *outlink, const AVFrame *in) { AVFilterContext *ctx = outlink->src; QSVFrame *out_frame; @@ -481,6 +493,12 @@ static QSVFrame *query_frame(QSVVPPContext *s, AVFilterLink *outlink) if (!out_frame->frame) return NULL; + ret = av_frame_copy_props(out_frame->frame, in); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Failed to copy metadata fields from src to dst.\n"); + return NULL; + } + ret = av_hwframe_get_buffer(outlink->hw_frames_ctx, out_frame->frame, 0); if (ret < 0) { av_log(ctx, AV_LOG_ERROR, "Can't allocate a surface.\n"); @@ -497,16 +515,46 @@ static QSVFrame *query_frame(QSVVPPContext *s, AVFilterLink *outlink) if (!out_frame->frame) return NULL; + ret = av_frame_copy_props(out_frame->frame, in); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Failed to copy metadata fields from src to dst.\n"); + return NULL; + } + ret = map_frame_to_surface(out_frame->frame, &out_frame->surface); if (ret < 0) return NULL; } + if (outlink->frame_rate.num && outlink->frame_rate.den) + out_frame->frame->duration = av_rescale_q(1, av_inv_q(outlink->frame_rate), outlink->time_base); + else + out_frame->frame->duration = 0; + out_frame->frame->width = outlink->w; out_frame->frame->height = outlink->h; out_frame->surface.Info = s->vpp_param.vpp.Out; + for (int i = 0; i < s->vpp_param.NumExtParam; i++) { + mfxExtBuffer *extbuf = s->vpp_param.ExtParam[i]; + + if (extbuf->BufferId == MFX_EXTBUFF_VPP_DEINTERLACING) { +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS + out_frame->frame->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + out_frame->frame->flags &= ~AV_FRAME_FLAG_INTERLACED; + break; + } + } + + out_frame->surface.Info.PicStruct = + !(out_frame->frame->flags & AV_FRAME_FLAG_INTERLACED) ? MFX_PICSTRUCT_PROGRESSIVE : + ((out_frame->frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? MFX_PICSTRUCT_FIELD_TFF : + MFX_PICSTRUCT_FIELD_BFF); + return out_frame; } @@ -638,6 +686,12 @@ static int init_vpp_session(AVFilterContext *avctx, QSVVPPContext *s) if (ret) return ret; + ret = MFXQueryVersion(s->session, &s->ver); + if (ret != MFX_ERR_NONE) { + av_log(avctx, AV_LOG_ERROR, "Error querying the runtime version\n"); + return AVERROR_UNKNOWN; + } + if (handle) { ret = MFXVideoCORE_SetHandle(s->session, handle_type, handle); if (ret != MFX_ERR_NONE) @@ -682,21 +736,26 @@ static int init_vpp_session(AVFilterContext *avctx, QSVVPPContext *s) return 0; } -int ff_qsvvpp_create(AVFilterContext *avctx, QSVVPPContext **vpp, QSVVPPParam *param) +static int set_frame_ext_params_null(AVFilterContext *ctx, const AVFrame *in, AVFrame *out, QSVVPPFrameParam *fp) +{ + return 0; +} + +int ff_qsvvpp_init(AVFilterContext *avctx, QSVVPPParam *param) { int i; int ret; - QSVVPPContext *s; - - s = av_mallocz(sizeof(*s)); - if (!s) - return AVERROR(ENOMEM); + QSVVPPContext *s = avctx->priv; s->filter_frame = param->filter_frame; if (!s->filter_frame) s->filter_frame = ff_filter_frame; s->out_sw_format = param->out_sw_format; + s->set_frame_ext_params = param->set_frame_ext_params; + if (!s->set_frame_ext_params) + s->set_frame_ext_params = set_frame_ext_params_null; + /* create the vpp session */ ret = init_vpp_session(avctx, s); if (ret < 0) @@ -736,40 +795,50 @@ int ff_qsvvpp_create(AVFilterContext *avctx, QSVVPPContext **vpp, QSVVPPParam *p goto failed; } + s->nb_seq_buffers = param->num_ext_buf; #if QSV_HAVE_OPAQUE - if (IS_OPAQUE_MEMORY(s->in_mem_mode) || IS_OPAQUE_MEMORY(s->out_mem_mode)) { - s->nb_ext_buffers = param->num_ext_buf + 1; + if (IS_OPAQUE_MEMORY(s->in_mem_mode) || IS_OPAQUE_MEMORY(s->out_mem_mode)) + s->nb_seq_buffers++; +#endif + + if (s->nb_seq_buffers) { + s->seq_buffers = av_calloc(s->nb_seq_buffers, sizeof(*s->seq_buffers)); + if (!s->seq_buffers) { + ret = AVERROR(ENOMEM); + goto failed; + } + + for (i = 0; i < param->num_ext_buf; i++) + s->seq_buffers[i] = param->ext_buf[i]; + +#if QSV_HAVE_OPAQUE + if (IS_OPAQUE_MEMORY(s->in_mem_mode) || IS_OPAQUE_MEMORY(s->out_mem_mode)) + s->seq_buffers[i] = (mfxExtBuffer *)&s->opaque_alloc; +#endif + + s->nb_ext_buffers = s->nb_seq_buffers; s->ext_buffers = av_calloc(s->nb_ext_buffers, sizeof(*s->ext_buffers)); if (!s->ext_buffers) { ret = AVERROR(ENOMEM); goto failed; } - s->ext_buffers[0] = (mfxExtBuffer *)&s->opaque_alloc; - for (i = 1; i < param->num_ext_buf; i++) - s->ext_buffers[i] = param->ext_buf[i - 1]; - s->vpp_param.ExtParam = s->ext_buffers; - s->vpp_param.NumExtParam = s->nb_ext_buffers; - } else { - s->vpp_param.NumExtParam = param->num_ext_buf; - s->vpp_param.ExtParam = param->ext_buf; + memcpy(s->ext_buffers, s->seq_buffers, s->nb_seq_buffers * sizeof(*s->seq_buffers)); } -#else - s->vpp_param.NumExtParam = param->num_ext_buf; - s->vpp_param.ExtParam = param->ext_buf; -#endif + + s->vpp_param.ExtParam = s->ext_buffers; + s->vpp_param.NumExtParam = s->nb_ext_buffers; s->got_frame = 0; /** keep fifo size at least 1. Even when async_depth is 0, fifo is used. */ - s->async_fifo = av_fifo_alloc2(param->async_depth + 1, sizeof(QSVAsyncFrame), 0); - s->async_depth = param->async_depth; + s->async_fifo = av_fifo_alloc2(s->async_depth + 1, sizeof(QSVAsyncFrame), 0); if (!s->async_fifo) { ret = AVERROR(ENOMEM); goto failed; } - s->vpp_param.AsyncDepth = param->async_depth; + s->vpp_param.AsyncDepth = s->async_depth; if (IS_SYSTEM_MEMORY(s->in_mem_mode)) s->vpp_param.IOPattern |= MFX_IOPATTERN_IN_SYSTEM_MEMORY; @@ -793,32 +862,86 @@ int ff_qsvvpp_create(AVFilterContext *avctx, QSVVPPContext **vpp, QSVVPPParam *p ff_qsvvpp_print_iopattern(avctx, s->vpp_param.IOPattern & 0x0F, "VPP"); /* Print output memory mode */ ff_qsvvpp_print_iopattern(avctx, s->vpp_param.IOPattern & 0xF0, "VPP"); - ret = MFXVideoVPP_Init(s->session, &s->vpp_param); + + /* Validate VPP params, but don't initial VPP session here */ + ret = MFXVideoVPP_Query(s->session, &s->vpp_param, &s->vpp_param); if (ret < 0) { - ret = ff_qsvvpp_print_error(avctx, ret, "Failed to create a qsvvpp"); + ret = ff_qsvvpp_print_error(avctx, ret, "Error querying VPP params"); goto failed; } else if (ret > 0) - ff_qsvvpp_print_warning(avctx, ret, "Warning When creating qsvvpp"); + ff_qsvvpp_print_warning(avctx, ret, "Warning When querying VPP params"); - *vpp = s; return 0; failed: - ff_qsvvpp_free(&s); + ff_qsvvpp_close(avctx); return ret; } -int ff_qsvvpp_free(QSVVPPContext **vpp) +static int qsvvpp_init_vpp_session(AVFilterContext *avctx, QSVVPPContext *s, const QSVFrame *in, QSVFrame *out) { - QSVVPPContext *s = *vpp; + int ret; + mfxExtBuffer *ext_param[QSVVPP_MAX_FRAME_EXTBUFS]; + QSVVPPFrameParam fp = { 0, ext_param }; - if (!s) - return 0; + ret = s->set_frame_ext_params(avctx, in->frame, out->frame, &fp); + if (ret) + return ret; + + if (fp.num_ext_buf) { + av_freep(&s->ext_buffers); + s->nb_ext_buffers = s->nb_seq_buffers + fp.num_ext_buf; + + s->ext_buffers = av_calloc(s->nb_ext_buffers, sizeof(*s->ext_buffers)); + if (!s->ext_buffers) + return AVERROR(ENOMEM); + + memcpy(&s->ext_buffers[0], s->seq_buffers, s->nb_seq_buffers * sizeof(*s->seq_buffers)); + memcpy(&s->ext_buffers[s->nb_seq_buffers], fp.ext_buf, fp.num_ext_buf * sizeof(*fp.ext_buf)); + s->vpp_param.ExtParam = s->ext_buffers; + s->vpp_param.NumExtParam = s->nb_ext_buffers; + } + + if (!s->vpp_initted) { + s->vpp_param.vpp.In.PicStruct = in->surface.Info.PicStruct; + s->vpp_param.vpp.Out.PicStruct = out->surface.Info.PicStruct; + + /* Query VPP params again, including params for frame */ + ret = MFXVideoVPP_Query(s->session, &s->vpp_param, &s->vpp_param); + if (ret < 0) + return ff_qsvvpp_print_error(avctx, ret, "Error querying VPP params"); + else if (ret > 0) + ff_qsvvpp_print_warning(avctx, ret, "Warning When querying VPP params"); + + ret = MFXVideoVPP_Init(s->session, &s->vpp_param); + if (ret < 0) + return ff_qsvvpp_print_error(avctx, ret, "Failed to create a qsvvpp"); + else if (ret > 0) + ff_qsvvpp_print_warning(avctx, ret, "Warning When creating qsvvpp"); + + s->vpp_initted = 1; + } else if (fp.num_ext_buf) { + ret = MFXVideoVPP_Reset(s->session, &s->vpp_param); + if (ret < 0) { + ret = ff_qsvvpp_print_error(avctx, ret, "Failed to reset session for qsvvpp"); + return ret; + } else if (ret > 0) + ff_qsvvpp_print_warning(avctx, ret, "Warning When resetting session for qsvvpp"); + } + + return 0; +} + +int ff_qsvvpp_close(AVFilterContext *avctx) +{ + QSVVPPContext *s = avctx->priv; if (s->session) { MFXVideoVPP_Close(s->session); MFXClose(s->session); + s->session = NULL; + s->vpp_initted = 0; } /* release all the resources */ @@ -826,12 +949,10 @@ int ff_qsvvpp_free(QSVVPPContext **vpp) clear_frame_list(&s->out_frame_list); av_freep(&s->surface_ptrs_in); av_freep(&s->surface_ptrs_out); -#if QSV_HAVE_OPAQUE + av_freep(&s->seq_buffers); av_freep(&s->ext_buffers); -#endif av_freep(&s->frame_infos); av_fifo_freep2(&s->async_fifo); - av_freep(vpp); return 0; } @@ -870,12 +991,16 @@ int ff_qsvvpp_filter_frame(QSVVPPContext *s, AVFilterLink *inlink, AVFrame *picr } do { - out_frame = query_frame(s, outlink); + out_frame = query_frame(s, outlink, in_frame->frame); if (!out_frame) { av_log(ctx, AV_LOG_ERROR, "Failed to query an output frame.\n"); return AVERROR(ENOMEM); } + ret = qsvvpp_init_vpp_session(ctx, s, in_frame, out_frame); + if (ret) + return ret; + do { ret = MFXVideoVPP_RunFrameVPPAsync(s->session, &in_frame->surface, &out_frame->surface, NULL, &sync); diff --git a/libavfilter/qsvvpp.h b/libavfilter/qsvvpp.h index 6f7c9bfc150..4eea7a46c75 100644 --- a/libavfilter/qsvvpp.h +++ b/libavfilter/qsvvpp.h @@ -52,9 +52,20 @@ typedef struct QSVFrame { int queued; } QSVFrame; +#define QSVVPP_MAX_FRAME_EXTBUFS 8 + +typedef struct QSVVPPFrameParam { + /* To fill with MFX enhanced filter configurations */ + int num_ext_buf; + mfxExtBuffer **ext_buf; +} QSVVPPFrameParam; + typedef struct QSVVPPContext { + const AVClass *class; + mfxSession session; int (*filter_frame) (AVFilterLink *outlink, AVFrame *frame); /**< callback */ + int (*set_frame_ext_params)(AVFilterContext *ctx, const AVFrame *in, AVFrame *out, QSVVPPFrameParam *fp); /**< callbak */ enum AVPixelFormat out_sw_format; /**< Real output format */ mfxVideoParam vpp_param; mfxFrameInfo *frame_infos; /**< frame info for each input */ @@ -72,15 +83,23 @@ typedef struct QSVVPPContext { #if QSV_HAVE_OPAQUE /** MFXVPP extern parameters */ mfxExtOpaqueSurfaceAlloc opaque_alloc; +#endif + /** store sequence parameters */ + mfxExtBuffer **seq_buffers; + int nb_seq_buffers; + + /** store all parameters for vpp execution, including parameters per frame */ mfxExtBuffer **ext_buffers; int nb_ext_buffers; -#endif int got_frame; int async_depth; int eof; /** order with frame_out, sync */ AVFifo *async_fifo; + + mfxVersion ver; + int vpp_initted; } QSVVPPContext; typedef struct QSVVPPCrop { @@ -91,6 +110,7 @@ typedef struct QSVVPPCrop { typedef struct QSVVPPParam { /* default is ff_filter_frame */ int (*filter_frame)(AVFilterLink *outlink, AVFrame *frame); + int (*set_frame_ext_params)(AVFilterContext *ctx, const AVFrame *in, AVFrame *out, QSVVPPFrameParam *fp); /**< callbak */ /* To fill with MFX enhanced filter configurations */ int num_ext_buf; @@ -102,15 +122,13 @@ typedef struct QSVVPPParam { /* Crop information for each input, if needed */ int num_crop; QSVVPPCrop *crop; - - int async_depth; } QSVVPPParam; /* create and initialize the QSV session */ -int ff_qsvvpp_create(AVFilterContext *avctx, QSVVPPContext **vpp, QSVVPPParam *param); +int ff_qsvvpp_init(AVFilterContext *avctx, QSVVPPParam *param); /* release the resources (eg.surfaces) */ -int ff_qsvvpp_free(QSVVPPContext **vpp); +int ff_qsvvpp_close(AVFilterContext *avctx); /* vpp filter frame and call the cb if needed */ int ff_qsvvpp_filter_frame(QSVVPPContext *vpp, AVFilterLink *inlink, AVFrame *frame); diff --git a/libavfilter/setpts.c b/libavfilter/setpts.c index 171fae88c0c..d057fc91c46 100644 --- a/libavfilter/setpts.c +++ b/libavfilter/setpts.c @@ -45,7 +45,9 @@ static const char *const var_names[] = { "N", ///< frame / sample number (starting at zero) "NB_CONSUMED_SAMPLES", ///< number of samples consumed by the filter (only audio) "NB_SAMPLES", ///< number of samples in the current frame (only audio) +#if FF_API_FRAME_PKT "POS", ///< original position in the file of the frame +#endif "PREV_INPTS", ///< previous input PTS "PREV_INT", ///< previous input time in seconds "PREV_OUTPTS", ///< previous output PTS @@ -61,6 +63,7 @@ static const char *const var_names[] = { "S", // Number of samples in the current frame "SR", // Audio sample rate "FR", ///< defined only for constant frame-rate video + "T_CHANGE", ///< time of first frame after latest command was applied NULL }; @@ -70,7 +73,9 @@ enum var_name { VAR_N, VAR_NB_CONSUMED_SAMPLES, VAR_NB_SAMPLES, +#if FF_API_FRAME_PKT VAR_POS, +#endif VAR_PREV_INPTS, VAR_PREV_INT, VAR_PREV_OUTPTS, @@ -86,7 +91,8 @@ enum var_name { VAR_S, VAR_SR, VAR_FR, - VAR_VARS_NB + VAR_T_CHANGE, + VAR_VARS_NB, }; typedef struct SetPTSContext { @@ -116,6 +122,7 @@ static av_cold int init(AVFilterContext *ctx) setpts->var_values[VAR_PREV_OUTT] = NAN; setpts->var_values[VAR_STARTPTS] = NAN; setpts->var_values[VAR_STARTT] = NAN; + setpts->var_values[VAR_T_CHANGE] = NAN; return 0; } @@ -159,14 +166,21 @@ static double eval_pts(SetPTSContext *setpts, AVFilterLink *inlink, AVFrame *fra setpts->var_values[VAR_STARTPTS] = TS2D(pts); setpts->var_values[VAR_STARTT ] = TS2T(pts, inlink->time_base); } + if (isnan(setpts->var_values[VAR_T_CHANGE])) { + setpts->var_values[VAR_T_CHANGE] = TS2T(pts, inlink->time_base); + } setpts->var_values[VAR_PTS ] = TS2D(pts); setpts->var_values[VAR_T ] = TS2T(pts, inlink->time_base); +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS setpts->var_values[VAR_POS ] = !frame || frame->pkt_pos == -1 ? NAN : frame->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif setpts->var_values[VAR_RTCTIME ] = av_gettime(); if (frame) { if (inlink->type == AVMEDIA_TYPE_VIDEO) { - setpts->var_values[VAR_INTERLACED] = frame->interlaced_frame; + setpts->var_values[VAR_INTERLACED] = !!(frame->flags & AV_FRAME_FLAG_INTERLACED); } else if (inlink->type == AVMEDIA_TYPE_AUDIO) { setpts->var_values[VAR_S] = frame->nb_samples; setpts->var_values[VAR_NB_SAMPLES] = frame->nb_samples; @@ -187,11 +201,10 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) frame->pts = D2TS(d); av_log(inlink->dst, AV_LOG_TRACE, - "N:%"PRId64" PTS:%s T:%f POS:%s", + "N:%"PRId64" PTS:%s T:%f", (int64_t)setpts->var_values[VAR_N], d2istr(setpts->var_values[VAR_PTS]), - setpts->var_values[VAR_T], - d2istr(setpts->var_values[VAR_POS])); + setpts->var_values[VAR_T]); switch (inlink->type) { case AVMEDIA_TYPE_VIDEO: av_log(inlink->dst, AV_LOG_TRACE, " INTERLACED:%"PRId64, @@ -242,10 +255,9 @@ static int activate(AVFilterContext *ctx) if (ff_inlink_acknowledge_status(inlink, &status, &pts)) { double d = eval_pts(setpts, inlink, NULL, pts); - av_log(ctx, AV_LOG_TRACE, "N:EOF PTS:%s T:%f POS:%s -> PTS:%s T:%f\n", + av_log(ctx, AV_LOG_TRACE, "N:EOF PTS:%s T:%f -> PTS:%s T:%f\n", d2istr(setpts->var_values[VAR_PTS]), setpts->var_values[VAR_T], - d2istr(setpts->var_values[VAR_POS]), d2istr(d), TS2T(d, inlink->time_base)); ff_outlink_set_status(outlink, status, D2TS(d)); return 0; @@ -263,14 +275,45 @@ static av_cold void uninit(AVFilterContext *ctx) setpts->expr = NULL; } +static int process_command(AVFilterContext *ctx, const char *cmd, const char *arg, + char *res, int res_len, int flags) +{ + SetPTSContext *setpts = ctx->priv; + AVExpr *new_expr; + int ret; + + ret = ff_filter_process_command(ctx, cmd, arg, res, res_len, flags); + + if (ret < 0) + return ret; + + if (!strcmp(cmd, "expr")) { + ret = av_expr_parse(&new_expr, arg, var_names, NULL, NULL, NULL, NULL, 0, ctx); + // Only free and replace previous expression if new one succeeds, + // otherwise defensively keep everything intact even if reporting an error. + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Error while parsing expression '%s'\n", arg); + } else { + av_expr_free(setpts->expr); + setpts->expr = new_expr; + setpts->var_values[VAR_T_CHANGE] = NAN; + } + } else { + ret = AVERROR(EINVAL); + } + + return ret; +} + #define OFFSET(x) offsetof(SetPTSContext, x) #define V AV_OPT_FLAG_VIDEO_PARAM #define A AV_OPT_FLAG_AUDIO_PARAM +#define R AV_OPT_FLAG_RUNTIME_PARAM #define F AV_OPT_FLAG_FILTERING_PARAM #if CONFIG_SETPTS_FILTER static const AVOption setpts_options[] = { - { "expr", "Expression determining the frame timestamp", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "PTS" }, .flags = V|F }, + { "expr", "Expression determining the frame timestamp", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "PTS" }, .flags = V|F|R }, { NULL } }; AVFILTER_DEFINE_CLASS(setpts); @@ -283,33 +326,27 @@ static const AVFilterPad avfilter_vf_setpts_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_setpts_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_setpts = { - .name = "setpts", - .description = NULL_IF_CONFIG_SMALL("Set PTS for the output video frame."), - .init = init, - .activate = activate, - .uninit = uninit, - .flags = AVFILTER_FLAG_METADATA_ONLY, + .name = "setpts", + .description = NULL_IF_CONFIG_SMALL("Set PTS for the output video frame."), + .init = init, + .activate = activate, + .uninit = uninit, + .process_command = process_command, + .flags = AVFILTER_FLAG_METADATA_ONLY, .priv_size = sizeof(SetPTSContext), .priv_class = &setpts_class, FILTER_INPUTS(avfilter_vf_setpts_inputs), - FILTER_OUTPUTS(avfilter_vf_setpts_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), }; #endif /* CONFIG_SETPTS_FILTER */ #if CONFIG_ASETPTS_FILTER static const AVOption asetpts_options[] = { - { "expr", "Expression determining the frame timestamp", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "PTS" }, .flags = A|F }, + { "expr", "Expression determining the frame timestamp", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "PTS" }, .flags = A|F|R }, { NULL } }; AVFILTER_DEFINE_CLASS(asetpts); @@ -322,23 +359,17 @@ static const AVFilterPad asetpts_inputs[] = { }, }; -static const AVFilterPad asetpts_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_asetpts = { - .name = "asetpts", - .description = NULL_IF_CONFIG_SMALL("Set PTS for the output audio frame."), - .init = init, - .activate = activate, - .uninit = uninit, - .priv_size = sizeof(SetPTSContext), - .priv_class = &asetpts_class, - .flags = AVFILTER_FLAG_METADATA_ONLY, + .name = "asetpts", + .description = NULL_IF_CONFIG_SMALL("Set PTS for the output audio frame."), + .init = init, + .activate = activate, + .uninit = uninit, + .process_command = process_command, + .priv_size = sizeof(SetPTSContext), + .priv_class = &asetpts_class, + .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(asetpts_inputs), - FILTER_OUTPUTS(asetpts_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), }; #endif /* CONFIG_ASETPTS_FILTER */ diff --git a/libavfilter/settb.c b/libavfilter/settb.c index 23cb02689ba..ba58abd9e9d 100644 --- a/libavfilter/settb.c +++ b/libavfilter/settb.c @@ -165,13 +165,6 @@ static int activate(AVFilterContext *ctx) DEFINE_OPTIONS(settb, VIDEO); AVFILTER_DEFINE_CLASS(settb); -static const AVFilterPad avfilter_vf_settb_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - static const AVFilterPad avfilter_vf_settb_outputs[] = { { .name = "default", @@ -185,7 +178,7 @@ const AVFilter ff_vf_settb = { .description = NULL_IF_CONFIG_SMALL("Set timebase for the video output link."), .priv_size = sizeof(SetTBContext), .priv_class = &settb_class, - FILTER_INPUTS(avfilter_vf_settb_inputs), + FILTER_INPUTS(ff_video_default_filterpad), FILTER_OUTPUTS(avfilter_vf_settb_outputs), .activate = activate, .flags = AVFILTER_FLAG_METADATA_ONLY, @@ -197,13 +190,6 @@ const AVFilter ff_vf_settb = { DEFINE_OPTIONS(asettb, AUDIO); AVFILTER_DEFINE_CLASS(asettb); -static const AVFilterPad avfilter_af_asettb_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - static const AVFilterPad avfilter_af_asettb_outputs[] = { { .name = "default", @@ -216,7 +202,7 @@ const AVFilter ff_af_asettb = { .name = "asettb", .description = NULL_IF_CONFIG_SMALL("Set timebase for the audio output link."), .priv_size = sizeof(SetTBContext), - FILTER_INPUTS(avfilter_af_asettb_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), FILTER_OUTPUTS(avfilter_af_asettb_outputs), .priv_class = &asettb_class, .activate = activate, diff --git a/libavfilter/silenceremove_template.c b/libavfilter/silenceremove_template.c new file mode 100644 index 00000000000..2f34fb5958b --- /dev/null +++ b/libavfilter/silenceremove_template.c @@ -0,0 +1,446 @@ +/* + * 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 + */ + +#undef ftype +#undef FABS +#undef FMAX +#undef SAMPLE_FORMAT +#undef SQRT +#undef ZERO +#undef ONE +#undef TMIN +#if DEPTH == 32 +#define SAMPLE_FORMAT flt +#define SQRT sqrtf +#define FMAX fmaxf +#define FABS fabsf +#define ftype float +#define ZERO 0.f +#define ONE 1.f +#define TMIN -FLT_MAX +#else +#define SAMPLE_FORMAT dbl +#define SQRT sqrt +#define FMAX fmax +#define FABS fabs +#define ftype double +#define ZERO 0.0 +#define ONE 1.0 +#define TMIN -DBL_MAX +#endif + +#define fn3(a,b) a##_##b +#define fn2(a,b) fn3(a,b) +#define fn(a) fn2(a, SAMPLE_FORMAT) + +static void fn(flush)(ftype *dst, const ftype *src, int src_pos, + int nb_channels, int count, int src_nb_samples, + int *out_nb_samples) +{ + int oidx, out_count = count; + int sidx = src_pos; + + if (count <= 0) + return; + + oidx = *out_nb_samples + out_count - 1; + *out_nb_samples += out_count; + while (out_count-- > 0) { + const int spos = sidx * nb_channels; + const int opos = oidx * nb_channels; + + for (int ch = 0; ch < nb_channels; ch++) + dst[opos + ch] = src[spos + ch]; + + oidx--; + sidx--; + if (sidx < 0) + sidx = src_nb_samples - 1; + } +} + +static void fn(queue_sample)(AVFilterContext *ctx, + const ftype *src, + ftype *queue, + int *queue_pos, + int *queue_size, + int *window_pos, + int *window_size, + const int nb_channels, + const int nb_samples, + const int window_nb_samples) +{ + const int pos = *queue_pos * nb_channels; + + for (int ch = 0; ch < nb_channels; ch++) + queue[pos + ch] = src[ch]; + + (*queue_pos)++; + if (*queue_pos >= nb_samples) + *queue_pos = 0; + + if (*queue_size < nb_samples) + (*queue_size)++; + + if (*window_size < window_nb_samples) + (*window_size)++; + + (*window_pos)++; + if (*window_pos >= window_nb_samples) + *window_pos = 0; +} + +static ftype fn(compute_avg)(ftype *cache, ftype x, ftype px, + int window_size, int *unused, int *unused2) +{ + ftype r; + + cache[0] += FABS(x); + cache[0] -= FABS(px); + cache[0] = r = FMAX(cache[0], ZERO); + + return r / window_size; +} + +#define PEAKS(empty_value,op,sample, psample)\ + if (!empty && psample == ss[front]) { \ + ss[front] = empty_value; \ + if (back != front) { \ + front--; \ + if (front < 0) \ + front = n - 1; \ + } \ + empty = front == back; \ + } \ + \ + if (!empty && sample op ss[front]) { \ + while (1) { \ + ss[front] = empty_value; \ + if (back == front) { \ + empty = 1; \ + break; \ + } \ + front--; \ + if (front < 0) \ + front = n - 1; \ + } \ + } \ + \ + while (!empty && sample op ss[back]) { \ + ss[back] = empty_value; \ + if (back == front) { \ + empty = 1; \ + break; \ + } \ + back++; \ + if (back >= n) \ + back = 0; \ + } \ + \ + if (!empty) { \ + back--; \ + if (back < 0) \ + back = n - 1; \ + } + +static ftype fn(compute_median)(ftype *ss, ftype x, ftype px, + int n, int *ffront, int *bback) +{ + ftype r, ax = FABS(x); + int front = *ffront; + int back = *bback; + int empty = front == back && ss[front] == -ONE; + int idx; + + PEAKS(-ONE, >, ax, FABS(px)) + + ss[back] = ax; + idx = (back <= front) ? back + (front - back + 1) / 2 : back + (n + front - back + 1) / 2; + if (idx >= n) + idx -= n; + av_assert2(idx >= 0 && idx < n); + r = ss[idx]; + + *ffront = front; + *bback = back; + + return r; +} + +static ftype fn(compute_peak)(ftype *ss, ftype x, ftype px, + int n, int *ffront, int *bback) +{ + ftype r, ax = FABS(x); + int front = *ffront; + int back = *bback; + int empty = front == back && ss[front] == ZERO; + + PEAKS(ZERO, >=, ax, FABS(px)) + + ss[back] = ax; + r = ss[front]; + + *ffront = front; + *bback = back; + + return r; +} + +static ftype fn(compute_ptp)(ftype *ss, ftype x, ftype px, + int n, int *ffront, int *bback) +{ + int front = *ffront; + int back = *bback; + int empty = front == back && ss[front] == TMIN; + ftype r, max, min; + + PEAKS(TMIN, >=, x, px) + + ss[back] = x; + max = ss[front]; + min = x; + r = FABS(min) + FABS(max - min); + + *ffront = front; + *bback = back; + + return r; +} + +static ftype fn(compute_rms)(ftype *cache, ftype x, ftype px, + int window_size, int *unused, int *unused2) +{ + ftype r; + + cache[0] += x * x; + cache[0] -= px * px; + cache[0] = r = FMAX(cache[0], ZERO); + + return SQRT(r / window_size); +} + +static ftype fn(compute_dev)(ftype *ss, ftype x, ftype px, + int n, int *unused, int *unused2) +{ + ftype r; + + ss[0] += x; + ss[0] -= px; + + ss[1] += x * x; + ss[1] -= px * px; + ss[1] = FMAX(ss[1], ZERO); + + r = FMAX(ss[1] - ss[0] * ss[0] / n, ZERO) / n; + + return SQRT(r); +} + +static void fn(filter_start)(AVFilterContext *ctx, + const ftype *src, ftype *dst, + int *nb_out_samples, + const int nb_channels) +{ + SilenceRemoveContext *s = ctx->priv; + const int start_periods = s->start_periods; + int out_nb_samples = *nb_out_samples; + const int start_window_nb_samples = s->start_window->nb_samples; + const int start_nb_samples = s->start_queuef->nb_samples; + const int start_wpos = s->start_window_pos * nb_channels; + const int start_pos = s->start_queue_pos * nb_channels; + ftype *startw = (ftype *)s->start_window->data[0]; + ftype *start = (ftype *)s->start_queuef->data[0]; + const ftype start_threshold = s->start_threshold; + const int start_mode = s->start_mode; + int start_thres = (start_mode == T_ANY) ? 0 : 1; + const int start_duration = s->start_duration; + ftype *start_cache = (ftype *)s->start_cache; + const int start_silence = s->start_silence; + int window_size = start_window_nb_samples; + const int cache_size = s->cache_size; + int *front = s->start_front; + int *back = s->start_back; + + fn(queue_sample)(ctx, src, start, + &s->start_queue_pos, + &s->start_queue_size, + &s->start_window_pos, + &s->start_window_size, + nb_channels, + start_nb_samples, + start_window_nb_samples); + + if (s->start_found_periods < 0) + goto skip; + + if (s->detection != D_PEAK && s->detection != D_MEDIAN && + s->detection != D_PTP) + window_size = s->start_window_size; + + for (int ch = 0; ch < nb_channels; ch++) { + ftype start_sample = start[start_pos + ch]; + ftype start_ow = startw[start_wpos + ch]; + ftype tstart; + + tstart = fn(s->compute)(start_cache + ch * cache_size, + start_sample, + start_ow, + window_size, + front + ch, + back + ch); + + startw[start_wpos + ch] = start_sample; + + if (start_mode == T_ANY) { + start_thres |= tstart > start_threshold; + } else { + start_thres &= tstart > start_threshold; + } + } + + if (s->start_found_periods >= 0) { + if (start_silence > 0) { + s->start_silence_count++; + if (s->start_silence_count > start_silence) + s->start_silence_count = start_silence; + } + + s->start_sample_count += start_thres; + } + + if (s->start_sample_count > start_duration) { + s->start_found_periods++; + if (s->start_found_periods >= start_periods) { + if (!ctx->is_disabled) + fn(flush)(dst, start, s->start_queue_pos, nb_channels, + s->start_silence_count, start_nb_samples, + &out_nb_samples); + s->start_silence_count = 0; + s->start_found_periods = -1; + } + + s->start_sample_count = 0; + } + +skip: + if (s->start_found_periods < 0 || ctx->is_disabled) { + const int dst_pos = out_nb_samples * nb_channels; + for (int ch = 0; ch < nb_channels; ch++) + dst[dst_pos + ch] = start[start_pos + ch]; + out_nb_samples++; + } + + *nb_out_samples = out_nb_samples; +} + +static void fn(filter_stop)(AVFilterContext *ctx, + const ftype *src, ftype *dst, + int *nb_out_samples, + const int nb_channels) +{ + SilenceRemoveContext *s = ctx->priv; + const int stop_periods = s->stop_periods; + int out_nb_samples = *nb_out_samples; + const int stop_window_nb_samples = s->stop_window->nb_samples; + const int stop_nb_samples = s->stop_queuef->nb_samples; + const int stop_wpos = s->stop_window_pos * nb_channels; + const int stop_pos = s->stop_queue_pos * nb_channels; + ftype *stopw = (ftype *)s->stop_window->data[0]; + const ftype stop_threshold = s->stop_threshold; + ftype *stop = (ftype *)s->stop_queuef->data[0]; + const int stop_mode = s->stop_mode; + int stop_thres = (stop_mode == T_ANY) ? 0 : 1; + const int stop_duration = s->stop_duration; + ftype *stop_cache = (ftype *)s->stop_cache; + const int stop_silence = s->stop_silence; + int window_size = stop_window_nb_samples; + const int cache_size = s->cache_size; + const int restart = s->restart; + int *front = s->stop_front; + int *back = s->stop_back; + + fn(queue_sample)(ctx, src, stop, + &s->stop_queue_pos, + &s->stop_queue_size, + &s->stop_window_pos, + &s->stop_window_size, + nb_channels, + stop_nb_samples, + stop_window_nb_samples); + + if (s->detection != D_PEAK && s->detection != D_MEDIAN && + s->detection != D_PTP) + window_size = s->stop_window_size; + + for (int ch = 0; ch < nb_channels; ch++) { + ftype stop_sample = stop[stop_pos + ch]; + ftype stop_ow = stopw[stop_wpos + ch]; + ftype tstop; + + tstop = fn(s->compute)(stop_cache + ch * cache_size, + stop_sample, + stop_ow, + window_size, + front + ch, + back + ch); + + stopw[stop_wpos + ch] = stop_sample; + + if (stop_mode == T_ANY) { + stop_thres |= tstop <= stop_threshold; + } else { + stop_thres &= tstop <= stop_threshold; + } + } + + s->found_nonsilence = FFMAX(s->found_nonsilence, !stop_thres); + if (restart && !stop_thres) + s->stop_found_periods = 0; + + if (s->stop_found_periods >= 0 || ctx->is_disabled) { + if (s->found_nonsilence) { + s->stop_sample_count += stop_thres; + s->stop_sample_count *= stop_thres; + } + } else if (s->stop_silence_count > 0) { + const int dst_pos = out_nb_samples * nb_channels; + for (int ch = 0; ch < nb_channels; ch++) + dst[dst_pos + ch] = stop[stop_pos + ch]; + s->stop_silence_count--; + out_nb_samples++; + } + + if (s->stop_sample_count > stop_duration) { + s->stop_found_periods++; + if (s->stop_found_periods >= stop_periods) { + s->stop_found_periods = -1; + s->stop_silence_count = stop_silence; + } + + s->stop_sample_count = 0; + } + + if (s->stop_found_periods >= 0 || ctx->is_disabled) { + const int dst_pos = out_nb_samples * nb_channels; + for (int ch = 0; ch < nb_channels; ch++) + dst[dst_pos + ch] = stop[stop_pos + ch]; + out_nb_samples++; + } + + *nb_out_samples = out_nb_samples; +} diff --git a/libavfilter/split.c b/libavfilter/split.c index 98b51f976ec..7935f2d518b 100644 --- a/libavfilter/split.c +++ b/libavfilter/split.c @@ -28,13 +28,11 @@ #include "libavutil/attributes.h" #include "libavutil/avstring.h" #include "libavutil/internal.h" -#include "libavutil/mem.h" #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" #include "filters.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -67,11 +65,15 @@ static int activate(AVFilterContext *ctx) { AVFilterLink *inlink = ctx->inputs[0]; AVFrame *in; - int status, ret; + int status, ret, nb_eofs = 0; int64_t pts; - for (int i = 0; i < ctx->nb_outputs; i++) { - FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[i], ctx); + for (int i = 0; i < ctx->nb_outputs; i++) + nb_eofs += ff_outlink_get_status(ctx->outputs[i]) == AVERROR_EOF; + + if (nb_eofs == ctx->nb_outputs) { + ff_inlink_set_status(inlink, AVERROR_EOF); + return 0; } ret = ff_inlink_consume_frame(inlink, &in); @@ -130,13 +132,6 @@ static const AVOption options[] = { AVFILTER_DEFINE_CLASS_EXT(split, "(a)split", options); -static const AVFilterPad avfilter_vf_split_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_split = { .name = "split", .description = NULL_IF_CONFIG_SMALL("Pass on the input to N video outputs."), @@ -144,18 +139,11 @@ const AVFilter ff_vf_split = { .priv_class = &split_class, .init = split_init, .activate = activate, - FILTER_INPUTS(avfilter_vf_split_inputs), + FILTER_INPUTS(ff_video_default_filterpad), .outputs = NULL, .flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS | AVFILTER_FLAG_METADATA_ONLY, }; -static const AVFilterPad avfilter_af_asplit_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_asplit = { .name = "asplit", .description = NULL_IF_CONFIG_SMALL("Pass on the audio input to N audio outputs."), @@ -163,7 +151,7 @@ const AVFilter ff_af_asplit = { .priv_size = sizeof(SplitContext), .init = split_init, .activate = activate, - FILTER_INPUTS(avfilter_af_asplit_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), .outputs = NULL, .flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS | AVFILTER_FLAG_METADATA_ONLY, }; diff --git a/libavfilter/src_avsynctest.c b/libavfilter/src_avsynctest.c index 78e4a2ba506..a0fef7a1cbd 100644 --- a/libavfilter/src_avsynctest.c +++ b/libavfilter/src_avsynctest.c @@ -67,6 +67,7 @@ typedef struct AVSyncTestContext { #define OFFSET(x) offsetof(AVSyncTestContext, x) #define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM #define V AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define R AV_OPT_FLAG_RUNTIME_PARAM static const AVOption avsynctest_options[] = { {"size", "set frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="hd720"}, 0, 0, V }, @@ -75,14 +76,14 @@ static const AVOption avsynctest_options[] = { {"fr", "set frame rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="30"}, 0,INT_MAX, V }, {"samplerate", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64=44100},8000,384000, A }, {"sr", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64=44100},8000,384000, A }, - {"amplitude", "set beep amplitude", OFFSET(amplitude), AV_OPT_TYPE_FLOAT, {.dbl=.7}, 0., 1., A }, - {"a", "set beep amplitude", OFFSET(amplitude), AV_OPT_TYPE_FLOAT, {.dbl=.7}, 0., 1., A }, + {"amplitude", "set beep amplitude", OFFSET(amplitude), AV_OPT_TYPE_FLOAT, {.dbl=.7}, 0., 1., A|R }, + {"a", "set beep amplitude", OFFSET(amplitude), AV_OPT_TYPE_FLOAT, {.dbl=.7}, 0., 1., A|R }, {"period", "set beep period", OFFSET(period), AV_OPT_TYPE_INT, {.i64=3}, 1, 99., A }, {"p", "set beep period", OFFSET(period), AV_OPT_TYPE_INT, {.i64=3}, 1, 99., A }, - {"delay", "set flash delay", OFFSET(delay), AV_OPT_TYPE_INT, {.i64=0}, -30, 30, V }, - {"dl", "set flash delay", OFFSET(delay), AV_OPT_TYPE_INT, {.i64=0}, -30, 30, V }, - {"cycle", "set delay cycle", OFFSET(cycle), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, V }, - {"c", "set delay cycle", OFFSET(cycle), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, V }, + {"delay", "set flash delay", OFFSET(delay), AV_OPT_TYPE_INT, {.i64=0}, -30, 30, V|R }, + {"dl", "set flash delay", OFFSET(delay), AV_OPT_TYPE_INT, {.i64=0}, -30, 30, V|R }, + {"cycle", "set delay cycle", OFFSET(cycle), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, V|R }, + {"c", "set delay cycle", OFFSET(cycle), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, V|R }, {"duration", "set duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64=0}, 0, INT64_MAX, V|A }, {"d", "set duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64=0}, 0, INT64_MAX, V|A }, {"fg", "set foreground color", OFFSET(rgba[0]), AV_OPT_TYPE_COLOR, {.str="white"}, 0, 0, V }, @@ -170,7 +171,7 @@ static av_cold int config_props(AVFilterLink *outlink) #define FPI 0x8000 -static int32_t sin32(int32_t x, int shift) +static int32_t sin32(int32_t x, AVRational scale) { const double pi = M_PI; const int32_t a = ((2.0 * pi) * (1 << 24)); @@ -194,7 +195,8 @@ static int32_t sin32(int32_t x, int shift) result = a + t2; result *= x; result += (1U << 31); - result >>= (32 - shift); + result >>= 17; + result = av_rescale(result, scale.num, scale.den); return result; } @@ -203,7 +205,7 @@ static int audio_frame(AVFilterLink *outlink) { AVFilterContext *ctx = outlink->src; AVSyncTestContext *s = ctx->priv; - const int a = lrintf(s->amplitude * 15); + const AVRational a = av_d2q(s->amplitude, 32768); int64_t duration[2]; int64_t delta; AVFrame *out; @@ -277,6 +279,9 @@ static int video_frame(AVFilterLink *outlink) int64_t delta, temp, intpart; AVFrame *out; + if (!s->cycle) + s->vdelay = av_make_q(s->delay, 1); + delta = av_rescale_q(s->apts, s->frame_rate, av_make_q(s->sample_rate, 1)) - s->vpts; if (delta < 0) return 1; @@ -400,4 +405,5 @@ const AVFilter ff_avsrc_avsynctest = { .activate = activate, FILTER_OUTPUTS(avsynctest_outputs), FILTER_QUERY_FUNC(query_formats), + .process_command = ff_filter_process_command, }; diff --git a/libavfilter/src_movie.c b/libavfilter/src_movie.c index 5937613d131..e50ebc99dce 100644 --- a/libavfilter/src_movie.c +++ b/libavfilter/src_movie.c @@ -23,7 +23,6 @@ * @file * movie video source * - * @todo use direct rendering (no allocation of a new frame) * @todo support a PTS correction mechanism */ @@ -46,15 +45,19 @@ #include "audio.h" #include "avfilter.h" +#include "filters.h" #include "formats.h" #include "internal.h" #include "video.h" typedef struct MovieStream { + AVFilterLink *link; AVStream *st; AVCodecContext *codec_ctx; int64_t discontinuity_threshold; int64_t last_pts; + AVFrame *frame; + int eof; } MovieStream; typedef struct MovieContext { @@ -71,8 +74,10 @@ typedef struct MovieContext { int64_t ts_offset; int dec_threads; + AVPacket *pkt; AVFormatContext *format_ctx; + int eof; int max_stream_index; /**< max stream # actually used for output */ MovieStream *st; /**< array of all streams, one per output */ int *out_index; /**< stream number -> output number map, or -1 */ @@ -100,7 +105,6 @@ static const AVOption movie_options[]= { }; static int movie_config_output_props(AVFilterLink *outlink); -static int movie_request_frame(AVFilterLink *outlink); static AVStream *find_stream(void *log, AVFormatContext *avf, const char *spec) { @@ -156,6 +160,56 @@ static AVStream *find_stream(void *log, AVFormatContext *avf, const char *spec) return found; } +static int get_buffer(AVCodecContext *avctx, AVFrame *frame, int flags) +{ + int linesize_align[AV_NUM_DATA_POINTERS]; + MovieStream *st = avctx->opaque; + AVFilterLink *outlink = st->link; + int w, h, ow, oh, copy = 0; + AVFrame *new; + + h = oh = frame->height; + w = ow = frame->width; + + copy = frame->format != outlink->format; + switch (avctx->codec_type) { + case AVMEDIA_TYPE_VIDEO: + if (w != outlink->w || h != outlink->h) + copy |= 1; + break; + case AVMEDIA_TYPE_AUDIO: + if (outlink->sample_rate != frame->sample_rate || + av_channel_layout_compare(&outlink->ch_layout, &frame->ch_layout)) + copy |= 1; + break; + } + + if (copy || !(avctx->codec->capabilities & AV_CODEC_CAP_DR1)) + return avcodec_default_get_buffer2(avctx, frame, flags); + + switch (avctx->codec_type) { + case AVMEDIA_TYPE_VIDEO: + avcodec_align_dimensions2(avctx, &w, &h, linesize_align); + new = ff_default_get_video_buffer(outlink, w, h); + break; + case AVMEDIA_TYPE_AUDIO: + new = ff_default_get_audio_buffer(outlink, frame->nb_samples); + break; + default: + return -1; + } + + av_frame_copy_props(new, frame); + av_frame_unref(frame); + av_frame_move_ref(frame, new); + av_frame_free(&new); + + frame->width = ow; + frame->height = oh; + + return 0; +} + static int open_stream(AVFilterContext *ctx, MovieStream *st, int dec_threads) { const AVCodec *codec; @@ -171,6 +225,8 @@ static int open_stream(AVFilterContext *ctx, MovieStream *st, int dec_threads) if (!st->codec_ctx) return AVERROR(ENOMEM); + st->codec_ctx->opaque = st; + st->codec_ctx->get_buffer2 = get_buffer; ret = avcodec_parameters_to_context(st->codec_ctx, st->st->codecpar); if (ret < 0) return ret; @@ -279,6 +335,9 @@ static av_cold int movie_common_init(AVFilterContext *ctx) for (i = 0; i < movie->format_ctx->nb_streams; i++) movie->format_ctx->streams[i]->discard = AVDISCARD_ALL; + movie->pkt = av_packet_alloc(); + if (!movie->pkt) + return AVERROR(ENOMEM); movie->st = av_calloc(nb_streams, sizeof(*movie->st)); if (!movie->st) return AVERROR(ENOMEM); @@ -296,6 +355,10 @@ static av_cold int movie_common_init(AVFilterContext *ctx) movie->max_stream_index = FFMAX(movie->max_stream_index, st->index); movie->st[i].discontinuity_threshold = av_rescale_q(movie->discontinuity_threshold, AV_TIME_BASE_Q, st->time_base); + + movie->st[i].frame = av_frame_alloc(); + if (!movie->st[i].frame) + return AVERROR(ENOMEM); } if (av_strtok(NULL, "+", &cursor)) return AVERROR_BUG; @@ -314,7 +377,6 @@ static av_cold int movie_common_init(AVFilterContext *ctx) if (!pad.name) return AVERROR(ENOMEM); pad.config_props = movie_config_output_props; - pad.request_frame = movie_request_frame; if ((ret = ff_append_outpad_free_name(ctx, &pad)) < 0) return ret; if ( movie->st[i].st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && @@ -343,7 +405,9 @@ static av_cold void movie_uninit(AVFilterContext *ctx) for (i = 0; i < ctx->nb_outputs; i++) { if (movie->st[i].st) avcodec_free_context(&movie->st[i].codec_ctx); + av_frame_free(&movie->st[i].frame); } + av_packet_free(&movie->pkt); av_freep(&movie->st); av_freep(&movie->out_index); if (movie->format_ctx) @@ -406,33 +470,9 @@ static int movie_config_output_props(AVFilterLink *outlink) break; } - return 0; -} + st->link = outlink; -static char *describe_frame_to_str(char *dst, size_t dst_size, - AVFrame *frame, enum AVMediaType frame_type, - AVFilterLink *link) -{ - switch (frame_type) { - case AVMEDIA_TYPE_VIDEO: - snprintf(dst, dst_size, - "video pts:%s time:%s size:%dx%d aspect:%d/%d", - av_ts2str(frame->pts), av_ts2timestr(frame->pts, &link->time_base), - frame->width, frame->height, - frame->sample_aspect_ratio.num, - frame->sample_aspect_ratio.den); - break; - case AVMEDIA_TYPE_AUDIO: - snprintf(dst, dst_size, - "audio pts:%s time:%s samples:%d", - av_ts2str(frame->pts), av_ts2timestr(frame->pts, &link->time_base), - frame->nb_samples); - break; - default: - snprintf(dst, dst_size, "%s BUG", av_get_media_type_string(frame_type)); - break; - } - return dst; + return 0; } static int rewind_file(AVFilterContext *ctx) @@ -456,145 +496,137 @@ static int rewind_file(AVFilterContext *ctx) return 0; } -static int movie_decode_packet(AVFilterContext *ctx) +static int flush_decoder(AVFilterContext *ctx, int i) { MovieContext *movie = ctx->priv; - AVPacket pkt = { 0 }; - int pkt_out_id, ret; - - /* read a new packet from input stream */ - ret = av_read_frame(movie->format_ctx, &pkt); - if (ret == AVERROR_EOF) { - /* EOF -> set all decoders for flushing */ - for (int i = 0; i < ctx->nb_outputs; i++) { - ret = avcodec_send_packet(movie->st[i].codec_ctx, NULL); - if (ret < 0 && ret != AVERROR_EOF) - return ret; - } + AVCodecContext *dec = movie->st[i].codec_ctx; - return 0; - } else if (ret < 0) - return ret; - - /* send the packet to its decoder, if any */ - pkt_out_id = pkt.stream_index > movie->max_stream_index ? -1 : - movie->out_index[pkt.stream_index]; - if (pkt_out_id >= 0) - ret = avcodec_send_packet(movie->st[pkt_out_id].codec_ctx, &pkt); - av_packet_unref(&pkt); - - return ret; + return avcodec_send_packet(dec, NULL); } -/** - * Try to push a frame to the requested output. - * - * @param ctx filter context - * @param out_id number of output where a frame is wanted; - * @return 0 if a frame was pushed on the requested output, - * AVERROR(EAGAIN) if the decoder requires more input - * AVERROR(EOF) if the decoder has been completely flushed - * <0 AVERROR code - */ -static int movie_push_frame(AVFilterContext *ctx, unsigned out_id) +static int decode_packet(AVFilterContext *ctx, int i) { - MovieContext *movie = ctx->priv; - MovieStream *st = &movie->st[out_id]; - AVFilterLink *outlink = ctx->outputs[out_id]; - AVFrame *frame; - int ret; - - frame = av_frame_alloc(); - if (!frame) - return AVERROR(ENOMEM); - - ret = avcodec_receive_frame(st->codec_ctx, frame); - if (ret < 0) { - if (ret != AVERROR_EOF && ret != AVERROR(EAGAIN)) - av_log(ctx, AV_LOG_WARNING, "Decode error: %s\n", av_err2str(ret)); - - av_frame_free(&frame); - return ret; + AVFilterLink *outlink = ctx->outputs[i]; + MovieContext *movie = ctx->priv; + MovieStream *st = &movie->st[i]; + AVCodecContext *dec = movie->st[i].codec_ctx; + AVFrame *frame = movie->st[i].frame; + AVPacket *pkt = movie->pkt; + int ret = 0; + + // submit the packet to the decoder + if (!movie->eof) { + ret = avcodec_send_packet(dec, pkt); + if (ret < 0) + return ret; } - frame->pts = frame->best_effort_timestamp; - if (frame->pts != AV_NOPTS_VALUE) { - if (movie->ts_offset) - frame->pts += av_rescale_q_rnd(movie->ts_offset, AV_TIME_BASE_Q, outlink->time_base, AV_ROUND_UP); - if (st->discontinuity_threshold) { - if (st->last_pts != AV_NOPTS_VALUE) { - int64_t diff = frame->pts - st->last_pts; - if (diff < 0 || diff > st->discontinuity_threshold) { - av_log(ctx, AV_LOG_VERBOSE, "Discontinuity in stream:%d diff:%"PRId64"\n", out_id, diff); - movie->ts_offset += av_rescale_q_rnd(-diff, outlink->time_base, AV_TIME_BASE_Q, AV_ROUND_UP); - frame->pts -= diff; + // get all the available frames from the decoder + if (ret >= 0) { + ret = avcodec_receive_frame(dec, frame); + if (ret < 0) { + // those two return values are special and mean there is no output + // frame available, but there were no errors during decoding + if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) + return 0; + return ret; + } + + frame->pts = frame->best_effort_timestamp; + if (frame->pts != AV_NOPTS_VALUE) { + if (movie->ts_offset) + frame->pts += av_rescale_q_rnd(movie->ts_offset, AV_TIME_BASE_Q, outlink->time_base, AV_ROUND_UP); + if (st->discontinuity_threshold) { + if (st->last_pts != AV_NOPTS_VALUE) { + int64_t diff = frame->pts - st->last_pts; + if (diff < 0 || diff > st->discontinuity_threshold) { + av_log(ctx, AV_LOG_VERBOSE, "Discontinuity in stream:%d diff:%"PRId64"\n", i, diff); + movie->ts_offset += av_rescale_q_rnd(-diff, outlink->time_base, AV_TIME_BASE_Q, AV_ROUND_UP); + frame->pts -= diff; + } } } + st->last_pts = frame->pts; } - st->last_pts = frame->pts; - } - ff_dlog(ctx, "movie_push_frame(): file:'%s' %s\n", movie->file_name, - describe_frame_to_str((char[1024]){0}, 1024, frame, - st->st->codecpar->codec_type, outlink)); - - if (st->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { - if (frame->format != outlink->format) { - av_log(ctx, AV_LOG_ERROR, "Format changed %s -> %s, discarding frame\n", - av_get_pix_fmt_name(outlink->format), - av_get_pix_fmt_name(frame->format) - ); - av_frame_free(&frame); - return 0; - } + ret = ff_filter_frame(outlink, av_frame_clone(frame)); + if (ret < 0) + return ret; + if (ret == 0) + return 1; } - ret = ff_filter_frame(outlink, frame); - if (ret < 0) - return ret; return 0; } -static int movie_request_frame(AVFilterLink *outlink) +static int activate(AVFilterContext *ctx) { - AVFilterContext *ctx = outlink->src; - MovieContext *movie = ctx->priv; - unsigned out_id = FF_OUTLINK_IDX(outlink); + MovieContext *movie = ctx->priv; + int wanted = 0, ret; - while (1) { - int got_eagain = 0, got_eof = 0; - int ret = 0; + for (int i = 0; i < ctx->nb_outputs; i++) { + if (ff_outlink_frame_wanted(ctx->outputs[i])) + wanted++; + } - /* check all decoders for available output */ - for (int i = 0; i < ctx->nb_outputs; i++) { - ret = movie_push_frame(ctx, i); - if (ret == AVERROR(EAGAIN)) - got_eagain++; - else if (ret == AVERROR_EOF) - got_eof++; - else if (ret < 0) - return ret; - else if (i == out_id) - return 0; + if (wanted == 0) + return FFERROR_NOT_READY; + + if (!movie->eof) { + ret = av_read_frame(movie->format_ctx, movie->pkt); + if (ret < 0) { + movie->eof = 1; + for (int i = 0; i < ctx->nb_outputs; i++) + flush_decoder(ctx, i); + ff_filter_set_ready(ctx, 100); + return 0; + } else { + int pkt_out_id = movie->pkt->stream_index > movie->max_stream_index ? -1 : + movie->out_index[movie->pkt->stream_index]; + + if (pkt_out_id >= 0) { + ret = decode_packet(ctx, pkt_out_id); + } + av_packet_unref(movie->pkt); + ff_filter_set_ready(ctx, 100); + return (ret <= 0) ? ret : 0; } + } else { + int nb_eofs = 0; - if (got_eagain) { - /* all decoders require more input -> read a new packet */ - ret = movie_decode_packet(ctx); + for (int i = 0; i < ctx->nb_outputs; i++) { + if (!movie->st[i].eof) { + ret = decode_packet(ctx, i); + if (ret <= 0) + movie->st[i].eof = 1; + } + nb_eofs += movie->st[i].eof == 1; + } + if (nb_eofs == ctx->nb_outputs && movie->loop_count != 1) { + ret = rewind_file(ctx); if (ret < 0) return ret; - } else if (got_eof) { - /* all decoders flushed */ - if (movie->loop_count != 1) { - ret = rewind_file(ctx); - if (ret < 0) - return ret; - movie->loop_count -= movie->loop_count > 1; - av_log(ctx, AV_LOG_VERBOSE, "Stream finished, looping.\n"); - continue; + movie->loop_count -= movie->loop_count > 1; + av_log(ctx, AV_LOG_VERBOSE, "Stream finished, looping.\n"); + ff_filter_set_ready(ctx, 100); + for (int i = 0; i < ctx->nb_outputs; i++) + movie->st[i].eof = 0; + movie->eof = 0; + return 0; + } else { + for (int i = 0; i < ctx->nb_outputs; i++) { + if (movie->st[i].eof) { + ff_outlink_set_status(ctx->outputs[i], AVERROR_EOF, movie->st[i].last_pts); + nb_eofs++; + } } - return AVERROR_EOF; } + + if (nb_eofs < ctx->nb_outputs) + ff_filter_set_ready(ctx, 100); + return 0; } + + return FFERROR_NOT_READY; } static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, @@ -649,6 +681,7 @@ const AVFilter ff_avsrc_movie = { .priv_size = sizeof(MovieContext), .priv_class = &movie_class, .init = movie_common_init, + .activate = activate, .uninit = movie_uninit, FILTER_QUERY_FUNC(movie_query_formats), @@ -668,6 +701,7 @@ const AVFilter ff_avsrc_amovie = { .priv_class = &movie_class, .priv_size = sizeof(MovieContext), .init = movie_common_init, + .activate = activate, .uninit = movie_uninit, FILTER_QUERY_FUNC(movie_query_formats), diff --git a/libavfilter/stack_internal.c b/libavfilter/stack_internal.c new file mode 100644 index 00000000000..1ee20d66cf2 --- /dev/null +++ b/libavfilter/stack_internal.c @@ -0,0 +1,355 @@ +/* + * 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 + */ + +#define OFFSET(x) offsetof(StackHWContext, x) +#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM) + +#define SET_OUTPUT_REGION(region, rx, ry, rw, rh) do { \ + region->x = rx; \ + region->y = ry; \ + region->width = rw; \ + region->height = rh; \ + } while (0) + +static int init_framesync(AVFilterContext *avctx) +{ + StackBaseContext *sctx = avctx->priv; + int ret; + + ret = ff_framesync_init(&sctx->fs, avctx, avctx->nb_inputs); + if (ret < 0) + return ret; + + sctx->fs.on_event = process_frame; + sctx->fs.opaque = sctx; + + for (int i = 0; i < sctx->nb_inputs; i++) { + FFFrameSyncIn *in = &sctx->fs.in[i]; + + in->before = EXT_STOP; + in->after = sctx->shortest ? EXT_STOP : EXT_INFINITY; + in->sync = 1; + in->time_base = avctx->inputs[i]->time_base; + } + + return ff_framesync_configure(&sctx->fs); +} + +static int config_comm_output(AVFilterLink *outlink) +{ + AVFilterContext *avctx = outlink->src; + StackBaseContext *sctx = avctx->priv; + AVFilterLink *inlink0 = avctx->inputs[0]; + int width, height, ret; + + if (sctx->mode == STACK_H) { + height = sctx->tile_height; + width = 0; + + if (!height) + height = inlink0->h; + + for (int i = 0; i < sctx->nb_inputs; i++) { + AVFilterLink *inlink = avctx->inputs[i]; + StackItemRegion *region = &sctx->regions[i]; + + SET_OUTPUT_REGION(region, width, 0, av_rescale(height, inlink->w, inlink->h), height); + width += av_rescale(height, inlink->w, inlink->h); + } + } else if (sctx->mode == STACK_V) { + height = 0; + width = sctx->tile_width; + + if (!width) + width = inlink0->w; + + for (int i = 0; i < sctx->nb_inputs; i++) { + AVFilterLink *inlink = avctx->inputs[i]; + StackItemRegion *region = &sctx->regions[i]; + + SET_OUTPUT_REGION(region, 0, height, width, av_rescale(width, inlink->h, inlink->w)); + height += av_rescale(width, inlink->h, inlink->w); + } + } else if (sctx->nb_grid_rows && sctx->nb_grid_columns) { + int xpos = 0, ypos = 0; + int ow, oh, k = 0; + + ow = sctx->tile_width; + oh = sctx->tile_height; + + if (!ow || !oh) { + ow = avctx->inputs[0]->w; + oh = avctx->inputs[0]->h; + } + + for (int i = 0; i < sctx->nb_grid_columns; i++) { + ypos = 0; + + for (int j = 0; j < sctx->nb_grid_rows; j++) { + StackItemRegion *region = &sctx->regions[k++]; + + SET_OUTPUT_REGION(region, xpos, ypos, ow, oh); + ypos += oh; + } + + xpos += ow; + } + + width = ow * sctx->nb_grid_columns; + height = oh * sctx->nb_grid_rows; + } else { + char *arg, *p = sctx->layout, *saveptr = NULL; + char *arg2, *p2, *saveptr2 = NULL; + char *arg3, *p3, *saveptr3 = NULL; + int xpos, ypos, size; + int ow, oh; + + width = 0; + height = 0; + + for (int i = 0; i < sctx->nb_inputs; i++) { + AVFilterLink *inlink = avctx->inputs[i]; + StackItemRegion *region = &sctx->regions[i]; + + ow = inlink->w; + oh = inlink->h; + + if (!(arg = av_strtok(p, "|", &saveptr))) + return AVERROR(EINVAL); + + p = NULL; + p2 = arg; + xpos = ypos = 0; + + for (int j = 0; j < 3; j++) { + if (!(arg2 = av_strtok(p2, "_", &saveptr2))) { + if (j == 2) + break; + else + return AVERROR(EINVAL); + } + + p2 = NULL; + p3 = arg2; + + if (j == 2) { + if ((ret = av_parse_video_size(&ow, &oh, p3)) < 0) { + av_log(avctx, AV_LOG_ERROR, "Invalid size '%s'\n", p3); + return ret; + } + + break; + } + + while ((arg3 = av_strtok(p3, "+", &saveptr3))) { + p3 = NULL; + if (sscanf(arg3, "w%d", &size) == 1) { + if (size == i || size < 0 || size >= sctx->nb_inputs) + return AVERROR(EINVAL); + + if (!j) + xpos += sctx->regions[size].width; + else + ypos += sctx->regions[size].width; + } else if (sscanf(arg3, "h%d", &size) == 1) { + if (size == i || size < 0 || size >= sctx->nb_inputs) + return AVERROR(EINVAL); + + if (!j) + xpos += sctx->regions[size].height; + else + ypos += sctx->regions[size].height; + } else if (sscanf(arg3, "%d", &size) == 1) { + if (size < 0) + return AVERROR(EINVAL); + + if (!j) + xpos += size; + else + ypos += size; + } else { + return AVERROR(EINVAL); + } + } + } + + SET_OUTPUT_REGION(region, xpos, ypos, ow, oh); + width = FFMAX(width, xpos + ow); + height = FFMAX(height, ypos + oh); + } + + } + + outlink->w = width; + outlink->h = height; + outlink->frame_rate = inlink0->frame_rate; + outlink->sample_aspect_ratio = inlink0->sample_aspect_ratio; + + for (int i = 1; i < sctx->nb_inputs; i++) { + AVFilterLink *inlink = avctx->inputs[i]; + if (outlink->frame_rate.num != inlink->frame_rate.num || + outlink->frame_rate.den != inlink->frame_rate.den) { + av_log(avctx, AV_LOG_VERBOSE, + "Video inputs have different frame rates, output will be VFR\n"); + outlink->frame_rate = av_make_q(1, 0); + break; + } + } + + ret = init_framesync(avctx); + if (ret < 0) + return ret; + + outlink->time_base = sctx->fs.time_base; + + return 0; +} + +static int stack_init(AVFilterContext *avctx) +{ + StackBaseContext *sctx = avctx->priv; + int ret; + + if (!strcmp(avctx->filter->name, HSTACK_NAME)) + sctx->mode = STACK_H; + else if (!strcmp(avctx->filter->name, VSTACK_NAME)) + sctx->mode = STACK_V; + else { + int is_grid; + + av_assert0(strcmp(avctx->filter->name, XSTACK_NAME) == 0); + sctx->mode = STACK_X; + is_grid = sctx->nb_grid_rows && sctx->nb_grid_columns; + + if (sctx->layout && is_grid) { + av_log(avctx, AV_LOG_ERROR, "Both layout and grid were specified. Only one is allowed.\n"); + return AVERROR(EINVAL); + } + + if (!sctx->layout && !is_grid) { + if (sctx->nb_inputs == 2) { + sctx->nb_grid_rows = 1; + sctx->nb_grid_columns = 2; + is_grid = 1; + } else { + av_log(avctx, AV_LOG_ERROR, "No layout or grid specified.\n"); + return AVERROR(EINVAL); + } + } + + if (is_grid) + sctx->nb_inputs = sctx->nb_grid_rows * sctx->nb_grid_columns; + + if (strcmp(sctx->fillcolor_str, "none") && + av_parse_color(sctx->fillcolor, sctx->fillcolor_str, -1, avctx) >= 0) { + sctx->fillcolor_enable = 1; + } else { + sctx->fillcolor_enable = 0; + } + } + + for (int i = 0; i < sctx->nb_inputs; i++) { + AVFilterPad pad = { 0 }; + + pad.type = AVMEDIA_TYPE_VIDEO; + pad.name = av_asprintf("input%d", i); + + if (!pad.name) + return AVERROR(ENOMEM); + + if ((ret = ff_append_inpad_free_name(avctx, &pad)) < 0) + return ret; + } + + sctx->regions = av_calloc(sctx->nb_inputs, sizeof(*sctx->regions)); + if (!sctx->regions) + return AVERROR(ENOMEM); + + return 0; +} + +static av_cold void stack_uninit(AVFilterContext *avctx) +{ + StackBaseContext *sctx = avctx->priv; + + av_freep(&sctx->regions); + ff_framesync_uninit(&sctx->fs); +} + +static int stack_activate(AVFilterContext *avctx) +{ + StackBaseContext *sctx = avctx->priv; + return ff_framesync_activate(&sctx->fs); +} + +static const AVFilterPad stack_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_output, + }, +}; + +#define STACK_COMMON_OPTS \ + { "inputs", "Set number of inputs", OFFSET(base.nb_inputs), AV_OPT_TYPE_INT, { .i64 = 2 }, 2, UINT16_MAX, .flags = FLAGS }, \ + { "shortest", "Force termination when the shortest input terminates", OFFSET(base.shortest), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, + +#define DEFINE_HSTACK_OPTIONS(api) \ + static const AVOption hstack_##api##_options[] = { \ + STACK_COMMON_OPTS \ + { "height", "Set output height (0 to use the height of input 0)", OFFSET(base.tile_height), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, UINT16_MAX, FLAGS }, \ + { NULL } \ + } + +#define DEFINE_VSTACK_OPTIONS(api) \ + static const AVOption vstack_##api##_options[] = { \ + STACK_COMMON_OPTS \ + { "width", "Set output width (0 to use the width of input 0)", OFFSET(base.tile_width), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, UINT16_MAX, FLAGS }, \ + { NULL } \ + } + +#define DEFINE_XSTACK_OPTIONS(api) \ + static const AVOption xstack_##api##_options[] = { \ + STACK_COMMON_OPTS \ + { "layout", "Set custom layout", OFFSET(base.layout), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, .flags = FLAGS }, \ + { "grid", "set fixed size grid layout", OFFSET(base.nb_grid_columns), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS }, \ + { "grid_tile_size", "set tile size in grid layout", OFFSET(base.tile_width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS }, \ + { "fill", "Set the color for unused pixels", OFFSET(base.fillcolor_str), AV_OPT_TYPE_STRING, {.str = "none"}, .flags = FLAGS }, \ + { NULL } \ + } + +#define DEFINE_STACK_FILTER(category, api, capi, filter_flags) \ + static const AVClass category##_##api##_class = { \ + .class_name = #category "_" #api, \ + .item_name = av_default_item_name, \ + .option = category##_##api##_options, \ + .version = LIBAVUTIL_VERSION_INT, \ + }; \ + const AVFilter ff_vf_##category##_##api = { \ + .name = #category "_" #api, \ + .description = NULL_IF_CONFIG_SMALL(#capi " " #category), \ + .priv_size = sizeof(StackHWContext), \ + .priv_class = &category##_##api##_class, \ + .init = api##_stack_init, \ + .uninit = api##_stack_uninit, \ + .activate = stack_activate, \ + FILTER_QUERY_FUNC(api##_stack_query_formats), \ + FILTER_OUTPUTS(stack_outputs), \ + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, \ + .flags = AVFILTER_FLAG_DYNAMIC_INPUTS | filter_flags, \ + } diff --git a/libavfilter/stack_internal.h b/libavfilter/stack_internal.h new file mode 100644 index 00000000000..ace88861529 --- /dev/null +++ b/libavfilter/stack_internal.h @@ -0,0 +1,60 @@ +/* + * 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 AVFILTER_STACK_INTERNAL_H +#define AVFILTER_STACK_INTERNAL_H + +enum { + STACK_H = 0, + STACK_V = 1, + STACK_X = 2 +}; + +typedef struct StackItemRegion { + int x; + int y; + int width; + int height; +} StackItemRegion; + +typedef struct StackBaseContext { + HWContext hwctx; /**< must be the first field */ + + FFFrameSync fs; + int mode; + uint8_t fillcolor[4]; + int fillcolor_enable; + StackItemRegion *regions; + + /* Options */ + int nb_inputs; + int shortest; + int tile_width; + int tile_height; + int nb_grid_columns; + int nb_grid_rows; + char *layout; + char *fillcolor_str; +} StackBaseContext; + +static int config_comm_output(AVFilterLink *outlink); +static int stack_init(AVFilterContext *avctx); +static av_cold void stack_uninit(AVFilterContext *avctx); +static int stack_activate(AVFilterContext *avctx); + +#endif /* AVFILTER_STACK_INTERNAL_H */ diff --git a/libavfilter/tests/dnn-layer-avgpool.c b/libavfilter/tests/dnn-layer-avgpool.c deleted file mode 100644 index 4a925ea22af..00000000000 --- a/libavfilter/tests/dnn-layer-avgpool.c +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (c) 2020 - * - * 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 -#include "libavfilter/dnn/dnn_backend_native_layer_avgpool.h" - -#define EPSON 0.00001 - -static int test_with_same(void) -{ - // the input data and expected data are generated with below python code. - /* - import tensorflow as tf - import numpy as np - - x = tf.placeholder(tf.float32, shape=[1, None, None, 3]) - y = tf.layers.average_pooling2d(x, pool_size=[2,2], strides=[1,1], padding='VALID') - data = np.random.rand(1, 5, 6, 3); - - sess=tf.Session() - sess.run(tf.global_variables_initializer()) - - output = sess.run(y, feed_dict={x: data}) - - print("input:") - print(data.shape) - print(list(data.flatten())) - - print("output:") - print(output.shape) - print(list(output.flatten())) - */ - - AvgPoolParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[1*5*6*3] = { - 0.7461309859908424, 0.7567538372797069, 0.07662743569678687, 0.8882112610336333, 0.9720443314026668, 0.3337200343220823, 0.4421032129780248, - 0.14940809044964876, 0.6773177061961277, 0.9778844630669781, 0.6522650522626998, 0.0317651530878591, 0.31259897552911364, 0.6235936821891896, - 0.40016094349542775, 0.4599222930032276, 0.7893807222960093, 0.8475986363538283, 0.5058802717647394, 0.7827005363222633, 0.3032188123727916, - 0.8983728631302361, 0.20622408444965523, 0.22966072303869878, 0.09535751273161308, 0.8760709100995375, 0.9982324154558745, 0.7904595468621013, - 0.13883671508879347, 0.9332751439533138, 0.0010861680752152214, 0.3607210449251048, 0.6600652759586171, 0.7629572058138805, 0.29441975810476106, - 0.2683471432889405, 0.22574580829831536, 0.8893251976212904, 0.3907737043801005, 0.6421829842863968, 0.6670373870457297, 0.9383850793160277, - 0.4120458907436003, 0.3589847212711481, 0.48047736550128983, 0.6428192648418949, 0.0313661686292348, 0.429357100401472, 0.5123413386514056, - 0.8492446404097114, 0.9045286128486804, 0.8123708563814285, 0.3943245008451698, 0.9576713003177785, 0.5985610965938726, 0.9350833279543561, - 0.8010079897491659, 0.45882114217642866, 0.35275037908941487, 0.4555844661432271, 0.12352455940255314, 0.37801756635035544, 0.2824056214573083, - 0.6229462823245029, 0.7235305681391472, 0.5408259266122064, 0.12142224381781208, 0.34431198802873686, 0.7112823816321276, 0.6307144385115417, - 0.8136734589018082, 0.842095618140585, 0.8602767724004784, 0.6649236853766185, 0.5184782829419623, 0.9119607270982825, 0.3084111974561645, - 0.39460705638161364, 0.17710447526170836, 0.1715485945814199, 0.17277563576521882, 0.40188232428735704, 0.22847985411491878, 0.4135361701550696, - 0.24621846601980057, 0.6576588108454774, 0.6063336087333997, 0.6452342242996931, 0.7071689702737508, 0.1973416063225648 - }; - float expected_output[] = { - 0.75964886, 0.6794307, 0.23580676, 0.5810112, 0.5509369, 0.55973274, 0.5764512, 0.45414522, 0.6601476, 0.52050734, 0.44385415, - 0.50631666, 0.38414115, 0.5170288, 0.544043, 0.61143976, 0.5419003, 0.5579729, 0.5680455, 0.6363218, 0.4655096, 0.51198983, - 0.5270792, 0.66168886, 0.48517057, 0.3513146, 0.7103355, 0.48667657, 0.34504217, 0.7318065, 0.5221889, 0.4746775, 0.69765306, - 0.78766406, 0.34437215, 0.6130092, 0.48132777, 0.7110491, 0.6464378, 0.40914366, 0.4391975, 0.5392131, 0.45033398, 0.37297475, - 0.43326652, 0.4748823, 0.48711336, 0.64649844, 0.51921225, 0.60038865, 0.8538945, 0.7215426, 0.60399896, 0.89988345, 0.707405, - 0.5652921, 0.54241943, 0.41785273, 0.30268195, 0.3263432, 0.3313644, 0.37539417, 0.35238582, 0.34811732, 0.48849532, 0.56799453, - 0.41089734, 0.63070333, 0.5892633, 0.6379743, 0.7604212, 0.5197186, 0.88611877, 0.48666745, 0.45654267, 0.5445326, 0.2399799, - 0.28369135, 0.28949338, 0.20001422, 0.2931559, 0.3240504, 0.44306934, 0.5099349, 0.44572634, 0.68241394, 0.40183762, 0.6452342, - 0.707169, 0.1973416 - }; - float *output; - - params.strides = 1; - params.kernel_size = 2; - params.padding_method = SAME; - - operands[0].data = input; - operands[0].dims[0] = 1; - operands[0].dims[1] = 5; - operands[0].dims[2] = 6; - operands[0].dims[3] = 3; - operands[1].data = NULL; - - input_indexes[0] = 0; - ff_dnn_execute_layer_avg_pool(operands, input_indexes, 1, ¶ms, NULL); - - output = operands[1].data; - for (int i = 0; i < sizeof(expected_output) / sizeof(float); ++i) { - if (fabs(output[i] - expected_output[i]) > EPSON) { - printf("at index %d, output: %f, expected_output: %f\n", i, output[i], expected_output[i]); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; -} - -static int test_with_valid(void) -{ - // the input data and expected data are generated with below python code. - /* - import tensorflow as tf - import numpy as np - - x = tf.placeholder(tf.float32, shape=[1, None, None, 3]) - y = tf.layers.average_pooling2d(x, pool_size=[2,2], strides=[1,1], padding='VALID') - data = np.random.rand(1, 5, 6, 3); - - sess=tf.Session() - sess.run(tf.global_variables_initializer()) - - output = sess.run(y, feed_dict={x: data}) - - print("input:") - print(data.shape) - print(list(data.flatten())) - - print("output:") - print(output.shape) - print(list(output.flatten())) - */ - - AvgPoolParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[1*5*6*3] = { - 0.5046741692941682, 0.9273653202485155, 0.8193878359859937, 0.1904059431360905, 0.8664919633253656, 0.7484625128286059, 0.984534184632278, - 0.31900804890072254, 0.3259426099940872, 0.05388974903570376, 0.7356610151331133, 0.46710858713311965, 0.718553768817036, 0.062478421853278676, - 0.7813224786584609, 0.4826837517658389, 0.9748095400220147, 0.8078547703898341, 0.11976750668368585, 0.8713586777195065, 0.41447321551284355, - 0.9818788239089807, 0.4335715767584073, 0.4059793452147419, 0.3677205907204525, 0.47919995923571, 0.8341395256258882, 0.7059726374074609, - 0.5478504551919791, 0.8622900484790175, 0.8343709722511167, 0.05089827275068537, 0.6465283980840416, 0.544539116066677, 0.39812057257884337, - 0.9578115576866337, 0.25012888117580145, 0.579333516024662, 0.5556732133051457, 0.6119862111181243, 0.0018736758772316398, 0.9795490254040474, - 0.4488085008883018, 0.28947489777011737, 0.4834108668633247, 0.9280490084385024, 0.9895821458049648, 0.31777618554697606, 0.42679693258977847, - 0.74447844466923, 0.9752225305081498, 0.17564130841849335, 0.22382692067314292, 0.009602884447469373, 0.5144884415025782, 0.031622570708844555, - 0.8277532752502512, 0.4111593210409763, 0.5272084646575664, 0.28856508082905297, 0.11317726946036655, 0.7203328275540273, 0.8310055019972384, - 0.8535951508685228, 0.40230347305233227, 0.2819703265132867, 0.6243143957791139, 0.7512463693822311, 0.7523056340495644, 0.8838077258040928, - 0.5472240664033092, 0.2550538284454935, 0.5560317774456567, 0.8966847087518931, 0.6728358284165321, 0.30361297147530875, 0.464343925441822, - 0.34507695659461224, 0.6333175615390685, 0.26661369038523497, 0.9926748632253231, 0.9994267301382666, 0.8684917986974414, 0.3598754806113009, - 0.49550268625464666, 0.03652458679973214, 0.13469081713137177, 0.4579424049273835, 0.48641107969110353, 0.9670250266945365 - }; - float expected_output[1*4*5*3] = { - 0.44918162, 0.7746969, 0.5970757, 0.63113487, 0.5245679, 0.578631, 0.52802926, 0.52042985, 0.6223702, 0.57819676, 0.34922206, - 0.6893124, 0.64503694, 0.37157673, 0.7983793, 0.49094033, 0.47153437, 0.5889187, 0.6025985, 0.30103004, 0.6757697, 0.6126377, - 0.5765268, 0.62440413, 0.7237974, 0.5832023, 0.7004543, 0.49533707, 0.35433105, 0.6472913, 0.44694072, 0.28500956, 0.6628852, - 0.39628282, 0.38472247, 0.6456326, 0.58590746, 0.60042334, 0.47854072, 0.7081889, 0.7219026, 0.5818187, 0.5276401, 0.56669396, - 0.49804622, 0.4463231, 0.4799649, 0.5335578, 0.36531678, 0.4946247, 0.6143306, 0.6498792, 0.5644355, 0.6163815, 0.7432098, - 0.5146416, 0.38221055, 0.6153918, 0.45535153, 0.5272688 - }; - float *output; - - params.strides = 1; - params.kernel_size = 2; - params.padding_method = VALID; - - operands[0].data = input; - operands[0].dims[0] = 1; - operands[0].dims[1] = 5; - operands[0].dims[2] = 6; - operands[0].dims[3] = 3; - operands[1].data = NULL; - - input_indexes[0] = 0; - ff_dnn_execute_layer_avg_pool(operands, input_indexes, 1, ¶ms, NULL); - - output = operands[1].data; - for (int i = 0; i < sizeof(expected_output) / sizeof(float); ++i) { - if (fabs(output[i] - expected_output[i]) > EPSON) { - printf("at index %d, output: %f, expected_output: %f\n", i, output[i], expected_output[i]); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; -} - -int main(int argc, char **argv) -{ - if (test_with_same()) - return 1; - if (test_with_valid()) - return 1; - - return 0; -} diff --git a/libavfilter/tests/dnn-layer-conv2d.c b/libavfilter/tests/dnn-layer-conv2d.c deleted file mode 100644 index 5ee60eeaf0d..00000000000 --- a/libavfilter/tests/dnn-layer-conv2d.c +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright (c) 2019 Guo Yejun - * - * 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 -#include -#include -#include "libavfilter/dnn/dnn_backend_native_layer_conv2d.h" - -#define EPSON 0.00001 - -static int test_with_same_dilate(void) -{ - // the input data and expected data are generated with below python code. - /* - x = tf.placeholder(tf.float32, shape=[1, None, None, 3]) - y = tf.layers.conv2d(x, 2, 3, activation=tf.nn.tanh, padding='same', dilation_rate=(2, 2), bias_initializer=tf.keras.initializers.he_normal()) - data = np.random.rand(1, 5, 6, 3); - - sess=tf.Session() - sess.run(tf.global_variables_initializer()) - - weights = dict([(var.name, sess.run(var)) for var in tf.trainable_variables()]) - kernel = weights['conv2d/kernel:0'] - kernel = np.transpose(kernel, [3, 0, 1, 2]) - print("kernel:") - print(kernel.shape) - print(list(kernel.flatten())) - - bias = weights['conv2d/bias:0'] - print("bias:") - print(bias.shape) - print(list(bias.flatten())) - - output = sess.run(y, feed_dict={x: data}) - - print("input:") - print(data.shape) - print(list(data.flatten())) - - print("output:") - print(output.shape) - print(list(output.flatten())) - */ - - ConvolutionalParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[1*5*6*3] = { - 0.7012556460308194, 0.4233847954643357, 0.19515900664313612, 0.16343083004926495, 0.5758261611052848, 0.9510767434014871, 0.11014085055947687, - 0.906327053637727, 0.8136794715542507, 0.45371764543639526, 0.5768443343523952, 0.19543668786046986, 0.15648326047898609, 0.2099500241141279, - 0.17658777090552413, 0.059335724777169196, 0.1729991838469117, 0.8150514704819208, 0.4435535466703049, 0.3752188477566878, 0.749936650421431, - 0.6823494635284907, 0.10776389679424747, 0.34247481674596836, 0.5147867256244629, 0.9063709728129032, 0.12423605800856818, 0.6064872945412728, - 0.5891681538551459, 0.9865836236466314, 0.9002163879294677, 0.003968273184274618, 0.8628374809643967, 0.1327176268279583, 0.8449799925703798, - 0.1937671869354366, 0.41524410152707425, 0.02038786604756837, 0.49792466069597496, 0.8881874553848784, 0.9683921035597336, 0.4122972568010813, - 0.843553550993252, 0.9588482762501964, 0.5190350762645546, 0.4283584264145317, 0.09781496073714646, 0.9501058833776156, 0.8665541760152776, - 0.31669272550095806, 0.07133074675453632, 0.606438007334886, 0.7007157020538224, 0.4827996264130444, 0.5167615606392761, 0.6385043039312651, - 0.23069664707810555, 0.058233497329354456, 0.06323892961591071, 0.24816458893245974, 0.8646369065257812, 0.24742185893094837, 0.09991225948167437, - 0.625700606979606, 0.7678541502111257, 0.6215834594679912, 0.5623003956582483, 0.07389123942681242, 0.7659100715711249, 0.486061471642225, - 0.9947455699829012, 0.9094911797643259, 0.7644355876253265, 0.05384315321492239, 0.13565394382783613, 0.9810628204953316, 0.007386389078887889, - 0.226182754156241, 0.2609021390764772, 0.24182802076928933, 0.13264782451941648, 0.2035816485767682, 0.005504188177612557, 0.7014619934040155, - 0.956215988391991, 0.5670398541013633, 0.9809764721750784, 0.6886338100487461, 0.5758152317218274, 0.7137823176776179 - }; - float expected_output[1*5*6*2] = { - -0.9480655, -0.7169147, -0.9404794, -0.5567385, -0.8991124, -0.8306558, -0.94487447, -0.8932543, -0.88238764, -0.7301602, - -0.8974813, -0.7026703, -0.8858988, -0.53203243, -0.92881465, -0.5648504, -0.8871471, -0.7000097, -0.91754407, -0.79684794, - -0.760465, -0.117928326, -0.88302773, -0.8975289, -0.70615053, 0.19231977, -0.8318776, -0.386184, -0.80698484, -0.8556624, - -0.7336671, -0.6168619, -0.7658234, -0.63449603, -0.73314047, -0.87502456, -0.58158904, -0.4184259, -0.52618927, -0.13613208, - -0.5093187, -0.21027721, -0.39455596, -0.44507834, -0.22269244, -0.73400885, -0.77655095, -0.74408925, -0.57313335, -0.15333457, - -0.74620694, -0.34858236, -0.42586932, -0.5240488, 0.1634339, -0.2447881, -0.57927346, -0.62732303, -0.82287043, -0.8474058 - }; - float *output; - float kernel[2*3*3*3] = { - 0.26025516, 0.16536498, -0.24351254, 0.33892477, -0.34005195, 0.35202783, 0.34056443, 0.01422739, 0.13799345, 0.29489166, - 0.2781723, 0.178585, 0.22122234, 0.044115514, 0.13134438, 0.31705368, 0.22527462, -0.021323413, 0.115134746, -0.18216397, - -0.21197563, -0.027848959, -0.01704529, -0.12401503, -0.23415318, -0.12661739, -0.35338148, 0.20049328, -0.076153606, - -0.23642601, -0.3125769, -0.025851756, -0.30006272, 0.050762743, 0.32003498, 0.3052225, -0.0017385483, 0.25337684, -0.25664508, - 0.27846587, -0.3112659, 0.2066065, 0.31499845, 0.113178134, 0.09449363, -0.11828774, -0.12671001, -0.36259216, 0.2710235, - -0.19676702, 0.023612618, -0.2596915, -0.34949252, -0.108270735 - }; - float bias[2] = { -1.6574852, -0.72915393 }; - - NativeContext ctx; - ctx.class = NULL; - ctx.options.conv2d_threads = 1; - - params.activation = TANH; - params.has_bias = 1; - params.biases = bias; - params.dilation = 2; - params.input_num = 3; - params.kernel = kernel; - params.kernel_size = 3; - params.output_num = 2; - params.padding_method = SAME; - - operands[0].data = input; - operands[0].dims[0] = 1; - operands[0].dims[1] = 5; - operands[0].dims[2] = 6; - operands[0].dims[3] = 3; - operands[1].data = NULL; - - input_indexes[0] = 0; - ff_dnn_execute_layer_conv2d(operands, input_indexes, 1, ¶ms, &ctx); - - output = operands[1].data; - for (int i = 0; i < sizeof(expected_output) / sizeof(float); i++) { - if (fabs(output[i] - expected_output[i]) > EPSON) { - printf("at index %d, output: %f, expected_output: %f\n", i, output[i], expected_output[i]); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; -} - -static int test_with_valid(void) -{ - // the input data and expected data are generated with below python code. - /* - x = tf.placeholder(tf.float32, shape=[1, None, None, 3]) - y = tf.layers.conv2d(x, 2, 3, activation=tf.nn.tanh, padding='valid', bias_initializer=tf.keras.initializers.he_normal()) - data = np.random.rand(1, 5, 6, 3); - - sess=tf.Session() - sess.run(tf.global_variables_initializer()) - - weights = dict([(var.name, sess.run(var)) for var in tf.trainable_variables()]) - kernel = weights['conv2d/kernel:0'] - kernel = np.transpose(kernel, [3, 0, 1, 2]) - print("kernel:") - print(kernel.shape) - print(list(kernel.flatten())) - - bias = weights['conv2d/bias:0'] - print("bias:") - print(bias.shape) - print(list(bias.flatten())) - - output = sess.run(y, feed_dict={x: data}) - - print("input:") - print(data.shape) - print(list(data.flatten())) - - print("output:") - print(output.shape) - print(list(output.flatten())) - */ - - ConvolutionalParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[1*5*6*3] = { - 0.26126657468269665, 0.42762216215337556, 0.7466274030131497, 0.802550266787863, 0.3709323443076644, 0.5919817068197668, 0.49274512279324967, - 0.7170132295090351, 0.0911793215410649, 0.5134213878288361, 0.670132600785118, 0.49417034512633484, 0.03887389460089885, 0.436785102836845, - 0.1490231658611978, 0.6413606121498127, 0.8595987991375995, 0.9132593077586231, 0.7075959004873255, 0.17754995944845464, 0.5212507214937141, - 0.35379732738215475, 0.25205107358505296, 0.3928792840544273, 0.09485294189485782, 0.8685115437448666, 0.6489046799288605, 0.509253797582924, - 0.8993255536791972, 0.18740056466602373, 0.34237617336313986, 0.3871438962989183, 0.1488532571774911, 0.5187002331293636, 0.8137098818752955, - 0.521761863717401, 0.4622312310118274, 0.29038411334638825, 0.16194915718170566, 0.5175999923925211, 0.8852230040101133, 0.0218263385047206, - 0.08482355352852367, 0.3463638568376264, 0.28627127120619733, 0.9553293378948409, 0.4803391055970835, 0.841635695030805, 0.3556828280031952, - 0.06778527221541808, 0.28193560357091596, 0.8399957619031576, 0.03305536359456385, 0.6625039162109645, 0.9300552020023897, 0.8551529138204146, - 0.6133216915522418, 0.222427800857393, 0.1315422686800336, 0.6189144989185527, 0.5346184916866876, 0.8348888624532548, 0.6544834567840291, - 0.2844062293389934, 0.28780026600883324, 0.5372272015684924, 0.6250226011503823, 0.28119106062279453, 0.49655812908420094, 0.6451488959145951, - 0.7362580606834843, 0.44815578616664087, 0.6454760235835586, 0.6794062414265861, 0.045378883014935756, 0.9008388543865096, 0.7949752851269782, - 0.4179928876222264, 0.28733419007048644, 0.996902319501908, 0.5690851338677467, 0.9511814013279738, 0.025323788678181636, 0.5594359732604794, - 0.1213732595086251, 0.7172624313368294, 0.6759328959074691, 0.07252138454885071, 0.17557735158403442, 0.5988895455048769 - }; - float expected_output[1*3*4*2] = { - -0.556947, -0.42143887, -0.092070885, 0.27404794, -0.41886684, 0.0862887, -0.25001016, -0.342721, 0.020730592, 0.04016919, -0.69839877, - -0.06136704, 0.14186388, -0.11655602, -0.23489095, -0.3845829, -0.19017771, 0.1595885, -0.18308741, -0.3071209, -0.5848686, -0.22509028, - -0.6023201, -0.14448485 - }; - float *output; - float kernel[2*3*3*3] = { - -0.25291282, 0.22402048, 0.028642118, -0.14615723, -0.27362752, -0.34801802, -0.2759148, 0.19594926, -0.25029412, 0.34606284, 0.10376671, - -0.1015394, 0.23616093, 0.2134214, 0.35285157, 0.05893758, 0.0024731457, -0.17143056, 0.35758412, 0.2186206, -0.28384736, -0.21206513, - -0.20871592, 0.27070445, 0.25878823, 0.11136332, -0.33737376, 0.08353335, -0.34290665, 0.041805506, -0.09738535, 0.3284936, -0.16838405, - -0.032494456, -0.29193437, 0.033259362, -0.09272635, -0.2802651, -0.28648436, 0.3542878, 0.2432127, -0.24551713, 0.27813476, 0.21024024, - -0.013690501, -0.1350077, -0.07826337, -0.34563828, 0.3220685, -0.07571727, 0.19420576, 0.20783454, 0.18738335, 0.16672492 - }; - float bias[2] = { -0.4773722, -0.19620377 }; - - NativeContext ctx; - ctx.class = NULL; - ctx.options.conv2d_threads = 1; - - params.activation = TANH; - params.has_bias = 1; - params.biases = bias; - params.dilation = 1; - params.input_num = 3; - params.kernel = kernel; - params.kernel_size = 3; - params.output_num = 2; - params.padding_method = VALID; - - operands[0].data = input; - operands[0].dims[0] = 1; - operands[0].dims[1] = 5; - operands[0].dims[2] = 6; - operands[0].dims[3] = 3; - operands[1].data = NULL; - - input_indexes[0] = 0; - ff_dnn_execute_layer_conv2d(operands, input_indexes, 1, ¶ms, &ctx); - - output = operands[1].data; - for (int i = 0; i < sizeof(expected_output) / sizeof(float); i++) { - if (fabs(output[i] - expected_output[i]) > EPSON) { - printf("at index %d, output: %f, expected_output: %f\n", i, output[i], expected_output[i]); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; -} - -int main(int argc, char **argv) -{ - if (test_with_valid()) - return 1; - if (test_with_same_dilate()) - return 1; - - return 0; -} diff --git a/libavfilter/tests/dnn-layer-dense.c b/libavfilter/tests/dnn-layer-dense.c deleted file mode 100644 index 696f7505e51..00000000000 --- a/libavfilter/tests/dnn-layer-dense.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2020 - * - * 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 -#include -#include -#include "libavfilter/dnn/dnn_backend_native_layer_dense.h" - -#define EPSON 0.00001 - -static int test(void) -{ - // the input data and expected data are generated with below python code. - /* - x = tf.placeholder(tf.float32, shape=[1, None, None, 3]) - y = tf.layers.dense(input_x, 3, activation=tf.nn.sigmoid, bias_initializer=tf.keras.initializers.he_normal()) - data = np.random.rand(1, 5, 6, 3); - - sess=tf.Session() - sess.run(tf.global_variables_initializer()) - - weights = dict([(var.name, sess.run(var)) for var in tf.trainable_variables()]) - kernel = weights['dense/kernel:0'] - kernel = np.transpose(kernel, [1, 0]) - print("kernel:") - print(kernel.shape) - print(list(kernel.flatten())) - - bias = weights['dense/bias:0'] - print("bias:") - print(bias.shape) - print(list(bias.flatten())) - - output = sess.run(y, feed_dict={x: data}) - - print("input:") - print(data.shape) - print(list(data.flatten())) - - print("output:") - print(output.shape) - print(list(output.flatten())) - */ - - DenseParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[1*5*6*3] = { - 0.5552418686576308, 0.20653189262022464, 0.31115120939398877, 0.5897014433221428, 0.37340078861060655, 0.6470921693941893, 0.8039950367872679, 0.8762700891949274, - 0.6556655583829558, 0.5911096107039339, 0.18640250865290997, 0.2803248779238966, 0.31586613136402053, 0.9447300740056483, 0.9443980824873418, 0.8158851991115941, - 0.5631010340387631, 0.9407402251929046, 0.6485434876551682, 0.5631376966470001, 0.17581924875609634, 0.7033802439103178, 0.04802402495561675, 0.9183681450194972, - 0.46059317944364, 0.07964160481596883, 0.871787076270302, 0.973743142324361, 0.15923146943258415, 0.8212946080584571, 0.5415954459227064, 0.9552813822803975, - 0.4908552668172057, 0.33723691635292274, 0.46588057864910026, 0.8994239961321776, 0.09845220457674186, 0.1713400292123486, 0.39570294912818826, 0.08018956486392803, - 0.5290478278169032, 0.7141906125920976, 0.0320878067840098, 0.6412406575332606, 0.0075712007102423096, 0.7150828462386156, 0.1311989216968138, 0.4706847944253756, - 0.5447610794883336, 0.3430923933318001, 0.536082357943209, 0.4371629342483694, 0.40227962985019927, 0.3553806249465469, 0.031806622424259245, 0.7053916426174, - 0.3261570237309813, 0.419500213292063, 0.3155691223480851, 0.05664028113178088, 0.3636491555914486, 0.8502419746667123, 0.9836596530684955, 0.1628681802975801, - 0.09410832912479894, 0.28407218939480294, 0.7983417928813697, 0.24132158596506748, 0.8154729498062224, 0.29173768373895637, 0.13407102008052096, 0.18705786678800385, - 0.7167943621295573, 0.09222004247174376, 0.2319220738766018, 0.17708964382285064, 0.1391440370249517, 0.3254088083499256, 0.4013916894718289, 0.4819742663322323, - 0.15080103744648077, 0.9302407847555013, 0.9397597961319524, 0.5719200825550793, 0.9538938024682824, 0.9583882089203861, 0.5168861091262276, 0.1926396841842669, - 0.6781176744337578, 0.719366447288566 - }; - float expected_output[1*5*6*3] = { - -0.3921688, -0.9243112, -0.29659146, -0.64000785, -0.9466343, -0.62125254, -0.71759033, -0.9171336, -0.735589, -0.34365994, - -0.92100817, -0.23903961, -0.8962277, -0.9521279, -0.90962386, -0.7488303, -0.9563761, -0.7701762, -0.40800542, -0.87684774, - -0.3339763, -0.6354543, -0.97068924, -0.6246325, -0.6992075, -0.9706726, -0.6818918, -0.51864433, -0.9592881, -0.51187396, - -0.7423632, -0.89911884, -0.7457824, -0.82009757, -0.96402895, -0.8235518, -0.61980766, -0.94494647, -0.5410502, -0.8281218, - -0.95508635, -0.8201453, -0.5937325, -0.8679507, -0.500767, -0.39430764, -0.93967676, -0.32183182, -0.58913624, -0.939717, - -0.55179894, -0.55004454, -0.9214453, -0.4889004, -0.75294703, -0.9118363, -0.7200309, -0.3248641, -0.8878874, -0.18977344, - -0.8873837, -0.9571257, -0.90145934, -0.50521654, -0.93739635, -0.39051685, -0.61143184, -0.9591179, -0.605999, -0.40008977, - -0.92219675, -0.26732883, -0.19607787, -0.9172511, -0.07068595, -0.5409857, -0.9387041, -0.44181606, -0.4705004, -0.8899935, - -0.37997037, -0.66105115, -0.89754754, -0.68141997, -0.6324047, -0.886776, -0.65066385, -0.8334821, -0.94801456, -0.83297 - }; - float *output; - float kernel[3*3] = { - 0.56611896, -0.5144603, -0.82600045, 0.19219112, 0.3835776, -0.7475352, 0.5209291, -0.6301091, -0.99442935}; - float bias[3] = {-0.3654299, -1.5711838, -0.15546428}; - - params.activation = TANH; - params.has_bias = 1; - params.biases = bias; - params.input_num = 3; - params.kernel = kernel; - params.output_num = 3; - - operands[0].data = input; - operands[0].dims[0] = 1; - operands[0].dims[1] = 5; - operands[0].dims[2] = 6; - operands[0].dims[3] = 3; - operands[1].data = NULL; - - input_indexes[0] = 0; - ff_dnn_execute_layer_dense(operands, input_indexes, 1, ¶ms, NULL); - - output = operands[1].data; - for (int i = 0; i < sizeof(expected_output) / sizeof(float); i++) { - if (fabs(output[i] - expected_output[i]) > EPSON) { - printf("at index %d, output: %f, expected_output: %f\n", i, output[i], expected_output[i]); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; -} - -int main(int argc, char **argv) -{ - if (test()) - return 1; - - return 0; -} diff --git a/libavfilter/tests/dnn-layer-depth2space.c b/libavfilter/tests/dnn-layer-depth2space.c deleted file mode 100644 index 958247e6753..00000000000 --- a/libavfilter/tests/dnn-layer-depth2space.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2019 Guo Yejun - * - * 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 -#include -#include -#include "libavfilter/dnn/dnn_backend_native.h" -#include "libavfilter/dnn/dnn_backend_native_layer_depth2space.h" - -#define EPSON 0.00001 - -static int test(void) -{ - // the input data and expected data are generated with below python code. - /* - x = tf.placeholder(tf.float32, shape=[1, None, None, 4]) - y = tf.depth_to_space(x, 2) - data = np.random.rand(1, 5, 3, 4); - - sess=tf.Session() - sess.run(tf.global_variables_initializer()) - - output = sess.run(y, feed_dict={x: data}) - - print("input:") - print(data.shape) - print(list(data.flatten())) - - print("output:") - print(output.shape) - print(list(output.flatten())) - */ - - DepthToSpaceParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[1*5*3*4] = { - 0.09771065121566602, 0.6336807372403175, 0.5142416549709786, 0.8027206567330333, 0.2154276025069397, 0.12112878462616772, 0.913936596765778, - 0.38881443647542646, 0.5850447615898835, 0.9311499327398275, 0.3613660929428246, 0.5420722002125493, 0.6002131190230359, 0.44800665702299525, - 0.7271322557896777, 0.3869293511885826, 0.5144404769364138, 0.6910844856987723, 0.6142102742269762, 0.6249991371621018, 0.45663376215836626, - 0.19523477129943423, 0.2483895888532045, 0.64326768256278, 0.5485877602998981, 0.45442067849873546, 0.529374943304256, 0.30439850391811885, - 0.11961343361340993, 0.2909643484561082, 0.9810970344127848, 0.8886928489786549, 0.6112237084436409, 0.8852482695156674, 0.9110868043114374, - 0.21242780027585217, 0.7101536973207572, 0.9709717457443375, 0.2702666770969332, 0.7718295953780221, 0.3957005164588574, 0.24383544252475453, - 0.040143453532367035, 0.26358051835323115, 0.013130251443791319, 0.3016550481482074, 0.03582340459943956, 0.718025513612361, 0.09844204177633753, - 0.04433767496953056, 0.6221895044119757, 0.6190414032940228, 0.8963550834625371, 0.5642449700064629, 0.2482982014723497, 0.17824909294583013, - 0.024401882408643272, 0.21742800875253465, 0.6794724473181843, 0.4814830479242237 - }; - float expected_output[1*10*6*1] = { - 0.097710654, 0.63368076, 0.2154276, 0.12112878, 0.58504474, 0.93114996, 0.51424164, 0.80272067, 0.9139366, 0.38881445, - 0.3613661, 0.5420722, 0.6002131, 0.44800666, 0.5144405, 0.6910845, 0.45663378, 0.19523478, 0.72713226, 0.38692936, - 0.61421025, 0.62499917, 0.24838959, 0.6432677, 0.54858774, 0.4544207, 0.11961343, 0.29096434, 0.6112237, 0.88524824, - 0.52937496, 0.3043985, 0.98109704, 0.88869286, 0.9110868, 0.2124278, 0.7101537, 0.97097176, 0.3957005, 0.24383545, - 0.013130251, 0.30165505, 0.27026668, 0.7718296, 0.040143453, 0.26358053, 0.035823405, 0.7180255, 0.09844204, - 0.044337675, 0.8963551, 0.564245, 0.024401883, 0.21742801, 0.6221895, 0.6190414, 0.2482982, 0.17824909, 0.67947245, 0.48148304 - }; - float *output; - - operands[0].data = input; - operands[0].dims[0] = 1; - operands[0].dims[1] = 5; - operands[0].dims[2] = 3; - operands[0].dims[3] = 4; - operands[1].data = NULL; - - input_indexes[0] = 0; - params.block_size = 2; - ff_dnn_execute_layer_depth2space(operands, input_indexes, 1, ¶ms, NULL); - - output = operands[1].data; - for (int i = 0; i < sizeof(expected_output) / sizeof(float); i++) { - if (fabs(output[i] - expected_output[i]) > EPSON) { - printf("at index %d, output: %f, expected_output: %f\n", i, output[i], expected_output[i]); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; -} - -int main(int argc, char **argv) -{ - return test(); -} diff --git a/libavfilter/tests/dnn-layer-mathbinary.c b/libavfilter/tests/dnn-layer-mathbinary.c deleted file mode 100644 index 2e41dc1ae75..00000000000 --- a/libavfilter/tests/dnn-layer-mathbinary.c +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (c) 2020 - * - * 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 -#include -#include -#include "libavfilter/dnn/dnn_backend_native_layer_mathbinary.h" -#include "libavutil/avassert.h" - -#define EPSON 0.00005 - -static float get_expected(float f1, float f2, DNNMathBinaryOperation op) -{ - switch (op) - { - case DMBO_SUB: - return f1 - f2; - case DMBO_ADD: - return f1 + f2; - case DMBO_MUL: - return f1 * f2; - case DMBO_REALDIV: - return f1 / f2; - case DMBO_MINIMUM: - return (f1 < f2) ? f1 : f2; - case DMBO_FLOORMOD: - return (float)((int)(f1) % (int)(f2)); - default: - av_assert0(!"not supported yet"); - return 0.f; - } -} - -static int test_broadcast_input0(DNNMathBinaryOperation op) -{ - DnnLayerMathBinaryParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[1*1*2*3] = { - -3, 2.5, 2, -2.1, 7.8, 100 - }; - float *output; - - params.bin_op = op; - params.input0_broadcast = 1; - params.input1_broadcast = 0; - params.v = 7.28; - - operands[0].data = input; - operands[0].dims[0] = 1; - operands[0].dims[1] = 1; - operands[0].dims[2] = 2; - operands[0].dims[3] = 3; - operands[1].data = NULL; - - input_indexes[0] = 0; - ff_dnn_execute_layer_math_binary(operands, input_indexes, 1, ¶ms, NULL); - - output = operands[1].data; - for (int i = 0; i < sizeof(input) / sizeof(float); i++) { - float expected_output = get_expected(params.v, input[i], op); - if (fabs(output[i] - expected_output) > EPSON) { - printf("op %d, at index %d, output: %f, expected_output: %f (%s:%d)\n", - op, i, output[i], expected_output, __FILE__, __LINE__); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; -} - -static int test_broadcast_input1(DNNMathBinaryOperation op) -{ - DnnLayerMathBinaryParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[1*1*2*3] = { - -3, 2.5, 2, -2.1, 7.8, 100 - }; - float *output; - - params.bin_op = op; - params.input0_broadcast = 0; - params.input1_broadcast = 1; - params.v = 7.28; - - operands[0].data = input; - operands[0].dims[0] = 1; - operands[0].dims[1] = 1; - operands[0].dims[2] = 2; - operands[0].dims[3] = 3; - operands[1].data = NULL; - - input_indexes[0] = 0; - ff_dnn_execute_layer_math_binary(operands, input_indexes, 1, ¶ms, NULL); - - output = operands[1].data; - for (int i = 0; i < sizeof(input) / sizeof(float); i++) { - float expected_output = get_expected(input[i], params.v, op); - if (fabs(output[i] - expected_output) > EPSON) { - printf("op %d, at index %d, output: %f, expected_output: %f (%s:%d)\n", - op, i, output[i], expected_output, __FILE__, __LINE__); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; -} - -static int test_no_broadcast(DNNMathBinaryOperation op) -{ - DnnLayerMathBinaryParams params; - DnnOperand operands[3]; - int32_t input_indexes[2]; - float input0[1*1*2*3] = { - -3, 2.5, 2, -2.1, 7.8, 100 - }; - float input1[1*1*2*3] = { - -1, 2, 3, -21, 8, 10.0 - }; - float *output; - - params.bin_op = op; - params.input0_broadcast = 0; - params.input1_broadcast = 0; - - operands[0].data = input0; - operands[0].dims[0] = 1; - operands[0].dims[1] = 1; - operands[0].dims[2] = 2; - operands[0].dims[3] = 3; - operands[1].data = input1; - operands[1].dims[0] = 1; - operands[1].dims[1] = 1; - operands[1].dims[2] = 2; - operands[1].dims[3] = 3; - operands[2].data = NULL; - - input_indexes[0] = 0; - input_indexes[1] = 1; - ff_dnn_execute_layer_math_binary(operands, input_indexes, 2, ¶ms, NULL); - - output = operands[2].data; - for (int i = 0; i < sizeof(input0) / sizeof(float); i++) { - float expected_output = get_expected(input0[i], input1[i], op); - if (fabs(output[i] - expected_output) > EPSON) { - printf("op %d, at index %d, output: %f, expected_output: %f (%s:%d)\n", - op, i, output[i], expected_output, __FILE__, __LINE__); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; -} - -static int test(DNNMathBinaryOperation op) -{ - if (test_broadcast_input0(op)) - return 1; - - if (test_broadcast_input1(op)) - return 1; - - if (test_no_broadcast(op)) - return 1; - - return 0; -} - -int main(int argc, char **argv) -{ - if (test(DMBO_SUB)) - return 1; - - if (test(DMBO_ADD)) - return 1; - - if (test(DMBO_MUL)) - return 1; - - if (test(DMBO_REALDIV)) - return 1; - - if (test(DMBO_MINIMUM)) - return 1; - - if (test(DMBO_FLOORMOD)) - return 1; - - return 0; -} diff --git a/libavfilter/tests/dnn-layer-mathunary.c b/libavfilter/tests/dnn-layer-mathunary.c deleted file mode 100644 index 0f84c12960b..00000000000 --- a/libavfilter/tests/dnn-layer-mathunary.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (c) 2020 - * - * 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 -#include -#include -#include "libavfilter/dnn/dnn_backend_native_layer_mathunary.h" -#include "libavutil/avassert.h" - -#define EPS 0.00001 - -static float get_expected(float f, DNNMathUnaryOperation op) -{ - switch (op) - { - case DMUO_ABS: - return (f >= 0) ? f : -f; - case DMUO_SIN: - return sin(f); - case DMUO_COS: - return cos(f); - case DMUO_TAN: - return tan(f); - case DMUO_ASIN: - return asin(f); - case DMUO_ACOS: - return acos(f); - case DMUO_ATAN: - return atan(f); - case DMUO_SINH: - return sinh(f); - case DMUO_COSH: - return cosh(f); - case DMUO_TANH: - return tanh(f); - case DMUO_ASINH: - return asinh(f); - case DMUO_ACOSH: - return acosh(f); - case DMUO_ATANH: - return atanh(f); - case DMUO_CEIL: - return ceil(f); - case DMUO_FLOOR: - return floor(f); - case DMUO_ROUND: - return round(f); - case DMUO_EXP: - return exp(f); - default: - av_assert0(!"not supported yet"); - return 0.f; - } -} - -static int test(DNNMathUnaryOperation op) -{ - DnnLayerMathUnaryParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[1*1*3*3] = { - 0.1, 0.5, 0.75, -3, 2.5, 2, -2.1, 7.8, 100}; - float *output; - - params.un_op = op; - - operands[0].data = input; - operands[0].dims[0] = 1; - operands[0].dims[1] = 1; - operands[0].dims[2] = 3; - operands[0].dims[3] = 3; - operands[1].data = NULL; - - input_indexes[0] = 0; - ff_dnn_execute_layer_math_unary(operands, input_indexes, 1, ¶ms, NULL); - - output = operands[1].data; - for (int i = 0; i < sizeof(input) / sizeof(float); ++i) { - float expected_output = get_expected(input[i], op); - int output_nan = isnan(output[i]); - int expected_nan = isnan(expected_output); - if ((!output_nan && !expected_nan && fabs(output[i] - expected_output) > EPS) || - (output_nan && !expected_nan) || (!output_nan && expected_nan)) { - printf("at index %d, output: %f, expected_output: %f\n", i, output[i], expected_output); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; -} - -int main(int agrc, char **argv) -{ - if (test(DMUO_ABS)) - return 1; - if (test(DMUO_SIN)) - return 1; - if (test(DMUO_COS)) - return 1; - if (test(DMUO_TAN)) - return 1; - if (test(DMUO_ASIN)) - return 1; - if (test(DMUO_ACOS)) - return 1; - if (test(DMUO_ATAN)) - return 1; - if (test(DMUO_SINH)) - return 1; - if (test(DMUO_COSH)) - return 1; - if (test(DMUO_TANH)) - return 1; - if (test(DMUO_ASINH)) - return 1; - if (test(DMUO_ACOSH)) - return 1; - if (test(DMUO_ATANH)) - return 1; - if (test(DMUO_CEIL)) - return 1; - if (test(DMUO_FLOOR)) - return 1; - if (test(DMUO_ROUND)) - return 1; - if (test(DMUO_EXP)) - return 1; - return 0; -} diff --git a/libavfilter/tests/dnn-layer-maximum.c b/libavfilter/tests/dnn-layer-maximum.c deleted file mode 100644 index bf22f3719fd..00000000000 --- a/libavfilter/tests/dnn-layer-maximum.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2019 Guo Yejun - * - * 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 -#include -#include -#include "libavfilter/dnn/dnn_backend_native_layer_maximum.h" - -#define EPSON 0.00001 - -static int test(void) -{ - DnnLayerMaximumParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[1*1*2*3] = { - -3, 2.5, 2, -2.1, 7.8, 100 - }; - float *output; - - params.val.y = 2.3; - - operands[0].data = input; - operands[0].dims[0] = 1; - operands[0].dims[1] = 1; - operands[0].dims[2] = 2; - operands[0].dims[3] = 3; - operands[1].data = NULL; - - input_indexes[0] = 0; - ff_dnn_execute_layer_maximum(operands, input_indexes, 1, ¶ms, NULL); - - output = operands[1].data; - for (int i = 0; i < sizeof(input) / sizeof(float); i++) { - float expected_output = input[i] > params.val.y ? input[i] : params.val.y; - if (fabs(output[i] - expected_output) > EPSON) { - printf("at index %d, output: %f, expected_output: %f\n", i, output[i], expected_output); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; - -} - -int main(int argc, char **argv) -{ - if (test()) - return 1; - - return 0; -} diff --git a/libavfilter/tests/dnn-layer-pad.c b/libavfilter/tests/dnn-layer-pad.c deleted file mode 100644 index a8443ce3be0..00000000000 --- a/libavfilter/tests/dnn-layer-pad.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (c) 2019 Guo Yejun - * - * 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 -#include -#include -#include "libavfilter/dnn/dnn_backend_native_layer_pad.h" - -#define EPSON 0.00001 - -static int test_with_mode_symmetric(void) -{ - // the input data and expected data are generated with below python code. - /* - x = tf.placeholder(tf.float32, shape=[1, None, None, 3]) - y = tf.pad(x, [[0, 0], [2, 3], [3, 2], [0, 0]], 'SYMMETRIC') - data = np.arange(48).reshape(1, 4, 4, 3); - - sess=tf.Session() - sess.run(tf.global_variables_initializer()) - output = sess.run(y, feed_dict={x: data}) - - print(list(data.flatten())) - print(list(output.flatten())) - print(data.shape) - print(output.shape) - */ - - LayerPadParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[1*4*4*3] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47 - }; - float expected_output[1*9*9*3] = { - 18.0, 19.0, 20.0, 15.0, 16.0, 17.0, 12.0, 13.0, 14.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 21.0, 22.0, 23.0, 18.0, 19.0, 20.0, 6.0, 7.0, 8.0, 3.0, - 4.0, 5.0, 0.0, 1.0, 2.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 9.0, 10.0, 11.0, 6.0, 7.0, 8.0, 6.0, 7.0, 8.0, 3.0, 4.0, 5.0, 0.0, 1.0, 2.0, 0.0, 1.0, 2.0, 3.0, - 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 9.0, 10.0, 11.0, 6.0, 7.0, 8.0, 18.0, 19.0, 20.0, 15.0, 16.0, 17.0, 12.0, 13.0, 14.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, - 21.0, 22.0, 23.0, 21.0, 22.0, 23.0, 18.0, 19.0, 20.0, 30.0, 31.0, 32.0, 27.0, 28.0, 29.0, 24.0, 25.0, 26.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0, 31.0, 32.0, 33.0, 34.0, 35.0, 33.0, - 34.0, 35.0, 30.0, 31.0, 32.0, 42.0, 43.0, 44.0, 39.0, 40.0, 41.0, 36.0, 37.0, 38.0, 36.0, 37.0, 38.0, 39.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0, 47.0, 45.0, 46.0, 47.0, 42.0, 43.0, - 44.0, 42.0, 43.0, 44.0, 39.0, 40.0, 41.0, 36.0, 37.0, 38.0, 36.0, 37.0, 38.0, 39.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0, 47.0, 45.0, 46.0, 47.0, 42.0, 43.0, 44.0, 30.0, 31.0, 32.0, - 27.0, 28.0, 29.0, 24.0, 25.0, 26.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0, 31.0, 32.0, 33.0, 34.0, 35.0, 33.0, 34.0, 35.0, 30.0, 31.0, 32.0, 18.0, 19.0, 20.0, 15.0, 16.0, 17.0, 12.0, - 13.0, 14.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 21.0, 22.0, 23.0, 18.0, 19.0, 20.0 - }; - float *output; - - params.mode = LPMP_SYMMETRIC; - params.paddings[0][0] = 0; - params.paddings[0][1] = 0; - params.paddings[1][0] = 2; - params.paddings[1][1] = 3; - params.paddings[2][0] = 3; - params.paddings[2][1] = 2; - params.paddings[3][0] = 0; - params.paddings[3][1] = 0; - - operands[0].data = input; - operands[0].dims[0] = 1; - operands[0].dims[1] = 4; - operands[0].dims[2] = 4; - operands[0].dims[3] = 3; - operands[1].data = NULL; - - input_indexes[0] = 0; - ff_dnn_execute_layer_pad(operands, input_indexes, 1, ¶ms, NULL); - - output = operands[1].data; - for (int i = 0; i < sizeof(expected_output) / sizeof(float); i++) { - if (fabs(output[i] - expected_output[i]) > EPSON) { - printf("at index %d, output: %f, expected_output: %f\n", i, output[i], expected_output[i]); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; - -} - -static int test_with_mode_reflect(void) -{ - // the input data and expected data are generated with below python code. - /* - x = tf.placeholder(tf.float32, shape=[3, None, None, 3]) - y = tf.pad(x, [[1, 2], [0, 0], [0, 0], [0, 0]], 'REFLECT') - data = np.arange(36).reshape(3, 2, 2, 3); - - sess=tf.Session() - sess.run(tf.global_variables_initializer()) - output = sess.run(y, feed_dict={x: data}) - - print(list(data.flatten())) - print(list(output.flatten())) - print(data.shape) - print(output.shape) - */ - - LayerPadParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[3*2*2*3] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35 - }; - float expected_output[6*2*2*3] = { - 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, - 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0, 31.0, 32.0, 33.0, 34.0, - 35.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0 - }; - float *output; - - params.mode = LPMP_REFLECT; - params.paddings[0][0] = 1; - params.paddings[0][1] = 2; - params.paddings[1][0] = 0; - params.paddings[1][1] = 0; - params.paddings[2][0] = 0; - params.paddings[2][1] = 0; - params.paddings[3][0] = 0; - params.paddings[3][1] = 0; - - operands[0].data = input; - operands[0].dims[0] = 3; - operands[0].dims[1] = 2; - operands[0].dims[2] = 2; - operands[0].dims[3] = 3; - operands[1].data = NULL; - - input_indexes[0] = 0; - ff_dnn_execute_layer_pad(operands, input_indexes, 1, ¶ms, NULL); - - output = operands[1].data; - for (int i = 0; i < sizeof(expected_output) / sizeof(float); i++) { - if (fabs(output[i] - expected_output[i]) > EPSON) { - printf("at index %d, output: %f, expected_output: %f\n", i, output[i], expected_output[i]); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; - -} - -static int test_with_mode_constant(void) -{ - // the input data and expected data are generated with below python code. - /* - x = tf.placeholder(tf.float32, shape=[1, None, None, 3]) - y = tf.pad(x, [[0, 0], [1, 0], [0, 0], [1, 2]], 'CONSTANT', constant_values=728) - data = np.arange(12).reshape(1, 2, 2, 3); - - sess=tf.Session() - sess.run(tf.global_variables_initializer()) - output = sess.run(y, feed_dict={x: data}) - - print(list(data.flatten())) - print(list(output.flatten())) - print(data.shape) - print(output.shape) - */ - - LayerPadParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[1*2*2*3] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 - }; - float expected_output[1*3*2*6] = { - 728.0, 728.0, 728.0, 728.0, 728.0, 728.0, 728.0, 728.0, 728.0, 728.0, 728.0, - 728.0, 728.0, 0.0, 1.0, 2.0, 728.0, 728.0, 728.0, 3.0, 4.0, 5.0, 728.0, 728.0, - 728.0, 6.0, 7.0, 8.0, 728.0, 728.0, 728.0, 9.0, 10.0, 11.0, 728.0, 728.0 - }; - float *output; - - params.mode = LPMP_CONSTANT; - params.constant_values = 728; - params.paddings[0][0] = 0; - params.paddings[0][1] = 0; - params.paddings[1][0] = 1; - params.paddings[1][1] = 0; - params.paddings[2][0] = 0; - params.paddings[2][1] = 0; - params.paddings[3][0] = 1; - params.paddings[3][1] = 2; - - operands[0].data = input; - operands[0].dims[0] = 1; - operands[0].dims[1] = 2; - operands[0].dims[2] = 2; - operands[0].dims[3] = 3; - operands[1].data = NULL; - - input_indexes[0] = 0; - ff_dnn_execute_layer_pad(operands, input_indexes, 1, ¶ms, NULL); - - output = operands[1].data; - for (int i = 0; i < sizeof(expected_output) / sizeof(float); i++) { - if (fabs(output[i] - expected_output[i]) > EPSON) { - printf("at index %d, output: %f, expected_output: %f\n", i, output[i], expected_output[i]); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; - -} - -int main(int argc, char **argv) -{ - if (test_with_mode_symmetric()) - return 1; - - if (test_with_mode_reflect()) - return 1; - - if (test_with_mode_constant()) - return 1; -} diff --git a/libavfilter/tinterlace.h b/libavfilter/tinterlace.h index 37b6c10c08c..f9b3e630b8e 100644 --- a/libavfilter/tinterlace.h +++ b/libavfilter/tinterlace.h @@ -32,6 +32,7 @@ #include "libavutil/pixdesc.h" #include "drawutils.h" #include "avfilter.h" +#include "ccfifo.h" #define TINTERLACE_FLAG_VLPF 01 #define TINTERLACE_FLAG_CVLPF 2 @@ -77,6 +78,7 @@ typedef struct TInterlaceContext { const AVPixFmtDescriptor *csp; void (*lowpass_line)(uint8_t *dstp, ptrdiff_t width, const uint8_t *srcp, ptrdiff_t mref, ptrdiff_t pref, int clip_max); + CCFifo cc_fifo; } TInterlaceContext; void ff_tinterlace_init_x86(TInterlaceContext *interlace); diff --git a/libavfilter/trim.c b/libavfilter/trim.c index ee6e821cd24..4c1a2b4f483 100644 --- a/libavfilter/trim.c +++ b/libavfilter/trim.c @@ -31,6 +31,8 @@ #include "audio.h" #include "avfilter.h" #include "internal.h" +#include "filters.h" +#include "video.h" typedef struct TrimContext { const AVClass *class; @@ -68,6 +70,8 @@ typedef struct TrimContext { int64_t next_pts; int eof; + + int (*filter_frame)(AVFilterLink *inlink, AVFrame *frame); } TrimContext; static av_cold int init(AVFilterContext *ctx) @@ -79,47 +83,6 @@ static av_cold int init(AVFilterContext *ctx) return 0; } -static int config_input(AVFilterLink *inlink) -{ - AVFilterContext *ctx = inlink->dst; - TrimContext *s = ctx->priv; - AVRational tb = (inlink->type == AVMEDIA_TYPE_VIDEO) ? - inlink->time_base : (AVRational){ 1, inlink->sample_rate }; - - if (s->start_time != INT64_MAX) { - int64_t start_pts = av_rescale_q(s->start_time, AV_TIME_BASE_Q, tb); - if (s->start_pts == AV_NOPTS_VALUE || start_pts < s->start_pts) - s->start_pts = start_pts; - } - if (s->end_time != INT64_MAX) { - int64_t end_pts = av_rescale_q(s->end_time, AV_TIME_BASE_Q, tb); - if (s->end_pts == AV_NOPTS_VALUE || end_pts > s->end_pts) - s->end_pts = end_pts; - } - if (s->duration) - s->duration_tb = av_rescale_q(s->duration, AV_TIME_BASE_Q, tb); - - return 0; -} - -#define OFFSET(x) offsetof(TrimContext, x) -#define COMMON_OPTS \ - { "start", "Timestamp of the first frame that " \ - "should be passed", OFFSET(start_time), AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX }, INT64_MIN, INT64_MAX, FLAGS }, \ - { "starti", "Timestamp of the first frame that " \ - "should be passed", OFFSET(start_time), AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX }, INT64_MIN, INT64_MAX, FLAGS }, \ - { "end", "Timestamp of the first frame that " \ - "should be dropped again", OFFSET(end_time), AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX }, INT64_MIN, INT64_MAX, FLAGS }, \ - { "endi", "Timestamp of the first frame that " \ - "should be dropped again", OFFSET(end_time), AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX }, INT64_MIN, INT64_MAX, FLAGS }, \ - { "start_pts", "Timestamp of the first frame that should be " \ - " passed", OFFSET(start_pts), AV_OPT_TYPE_INT64, { .i64 = AV_NOPTS_VALUE }, INT64_MIN, INT64_MAX, FLAGS }, \ - { "end_pts", "Timestamp of the first frame that should be " \ - "dropped again", OFFSET(end_pts), AV_OPT_TYPE_INT64, { .i64 = AV_NOPTS_VALUE }, INT64_MIN, INT64_MAX, FLAGS }, \ - { "duration", "Maximum duration of the output", OFFSET(duration), AV_OPT_TYPE_DURATION, { .i64 = 0 }, 0, INT64_MAX, FLAGS }, \ - { "durationi", "Maximum duration of the output", OFFSET(duration), AV_OPT_TYPE_DURATION, { .i64 = 0 }, 0, INT64_MAX, FLAGS }, - - #if CONFIG_TRIM_FILTER static int trim_filter_frame(AVFilterLink *inlink, AVFrame *frame) { @@ -161,7 +124,8 @@ static int trim_filter_frame(AVFilterLink *inlink, AVFrame *frame) if (drop) { s->eof = 1; - ff_avfilter_link_set_out_status(inlink, AVERROR_EOF, AV_NOPTS_VALUE); + ff_inlink_set_status(inlink, AVERROR_EOF); + ff_outlink_set_status(ctx->outputs[0], AVERROR_EOF, frame->pts); goto drop; } } @@ -171,49 +135,12 @@ static int trim_filter_frame(AVFilterLink *inlink, AVFrame *frame) return ff_filter_frame(ctx->outputs[0], frame); drop: + if (!s->eof) + ff_filter_set_ready(ctx, 100); s->nb_frames++; av_frame_free(&frame); return 0; } - -#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM -static const AVOption trim_options[] = { - COMMON_OPTS - { "start_frame", "Number of the first frame that should be passed " - "to the output", OFFSET(start_frame), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, FLAGS }, - { "end_frame", "Number of the first frame that should be dropped " - "again", OFFSET(end_frame), AV_OPT_TYPE_INT64, { .i64 = INT64_MAX }, 0, INT64_MAX, FLAGS }, - { NULL } -}; -#undef FLAGS - -AVFILTER_DEFINE_CLASS(trim); - -static const AVFilterPad trim_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .filter_frame = trim_filter_frame, - .config_props = config_input, - }, -}; - -static const AVFilterPad trim_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - -const AVFilter ff_vf_trim = { - .name = "trim", - .description = NULL_IF_CONFIG_SMALL("Pick one continuous section from the input, drop the rest."), - .init = init, - .priv_size = sizeof(TrimContext), - .priv_class = &trim_class, - FILTER_INPUTS(trim_inputs), - FILTER_OUTPUTS(trim_outputs), -}; #endif // CONFIG_TRIM_FILTER #if CONFIG_ATRIM_FILTER @@ -290,7 +217,8 @@ static int atrim_filter_frame(AVFilterLink *inlink, AVFrame *frame) if (drop) { s->eof = 1; - ff_avfilter_link_set_out_status(inlink, AVERROR_EOF, AV_NOPTS_VALUE); + ff_inlink_set_status(inlink, AVERROR_EOF); + ff_outlink_set_status(ctx->outputs[0], AVERROR_EOF, frame->pts); goto drop; } } @@ -324,10 +252,124 @@ static int atrim_filter_frame(AVFilterLink *inlink, AVFrame *frame) return ff_filter_frame(ctx->outputs[0], frame); drop: + if (!s->eof) + ff_filter_set_ready(ctx, 100); s->nb_samples += frame->nb_samples; av_frame_free(&frame); return 0; } +#endif // CONFIG_ATRIM_FILTER + +static int config_input(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + TrimContext *s = ctx->priv; + AVRational tb = (inlink->type == AVMEDIA_TYPE_VIDEO) ? + inlink->time_base : (AVRational){ 1, inlink->sample_rate }; + +#if CONFIG_TRIM_FILTER + if (inlink->type == AVMEDIA_TYPE_VIDEO) + s->filter_frame = trim_filter_frame; +#endif +#if CONFIG_ATRIM_FILTER + if (inlink->type == AVMEDIA_TYPE_AUDIO) + s->filter_frame = atrim_filter_frame; +#endif + if (s->start_time != INT64_MAX) { + int64_t start_pts = av_rescale_q(s->start_time, AV_TIME_BASE_Q, tb); + if (s->start_pts == AV_NOPTS_VALUE || start_pts < s->start_pts) + s->start_pts = start_pts; + } + if (s->end_time != INT64_MAX) { + int64_t end_pts = av_rescale_q(s->end_time, AV_TIME_BASE_Q, tb); + if (s->end_pts == AV_NOPTS_VALUE || end_pts > s->end_pts) + s->end_pts = end_pts; + } + if (s->duration) + s->duration_tb = av_rescale_q(s->duration, AV_TIME_BASE_Q, tb); + + return 0; +} + +static int activate(AVFilterContext *ctx) +{ + TrimContext *s = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + + if (!s->eof && ff_inlink_queued_frames(inlink)) { + AVFrame *frame = NULL; + int ret; + + ret = ff_inlink_consume_frame(inlink, &frame); + if (ret < 0) + return ret; + if (ret > 0) + return s->filter_frame(inlink, frame); + } + + FF_FILTER_FORWARD_STATUS(inlink, outlink); + FF_FILTER_FORWARD_WANTED(outlink, inlink); + + return FFERROR_NOT_READY; +} + +#define OFFSET(x) offsetof(TrimContext, x) +#define COMMON_OPTS \ + { "start", "Timestamp of the first frame that " \ + "should be passed", OFFSET(start_time), AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX }, INT64_MIN, INT64_MAX, FLAGS }, \ + { "starti", "Timestamp of the first frame that " \ + "should be passed", OFFSET(start_time), AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX }, INT64_MIN, INT64_MAX, FLAGS }, \ + { "end", "Timestamp of the first frame that " \ + "should be dropped again", OFFSET(end_time), AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX }, INT64_MIN, INT64_MAX, FLAGS }, \ + { "endi", "Timestamp of the first frame that " \ + "should be dropped again", OFFSET(end_time), AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX }, INT64_MIN, INT64_MAX, FLAGS }, \ + { "start_pts", "Timestamp of the first frame that should be " \ + " passed", OFFSET(start_pts), AV_OPT_TYPE_INT64, { .i64 = AV_NOPTS_VALUE }, INT64_MIN, INT64_MAX, FLAGS }, \ + { "end_pts", "Timestamp of the first frame that should be " \ + "dropped again", OFFSET(end_pts), AV_OPT_TYPE_INT64, { .i64 = AV_NOPTS_VALUE }, INT64_MIN, INT64_MAX, FLAGS }, \ + { "duration", "Maximum duration of the output", OFFSET(duration), AV_OPT_TYPE_DURATION, { .i64 = 0 }, 0, INT64_MAX, FLAGS }, \ + { "durationi", "Maximum duration of the output", OFFSET(duration), AV_OPT_TYPE_DURATION, { .i64 = 0 }, 0, INT64_MAX, FLAGS }, + + +#if CONFIG_TRIM_FILTER + +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM +static const AVOption trim_options[] = { + COMMON_OPTS + { "start_frame", "Number of the first frame that should be passed " + "to the output", OFFSET(start_frame), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, FLAGS }, + { "end_frame", "Number of the first frame that should be dropped " + "again", OFFSET(end_frame), AV_OPT_TYPE_INT64, { .i64 = INT64_MAX }, 0, INT64_MAX, FLAGS }, + { NULL } +}; +#undef FLAGS + +AVFILTER_DEFINE_CLASS(trim); + +static const AVFilterPad trim_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_input, + }, +}; + +const AVFilter ff_vf_trim = { + .name = "trim", + .description = NULL_IF_CONFIG_SMALL("Pick one continuous section from the input, drop the rest."), + .init = init, + .activate = activate, + .priv_size = sizeof(TrimContext), + .priv_class = &trim_class, + FILTER_INPUTS(trim_inputs), + FILTER_OUTPUTS(ff_video_default_filterpad), +}; +#endif // CONFIG_TRIM_FILTER + +#if CONFIG_ATRIM_FILTER #define FLAGS AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM static const AVOption atrim_options[] = { @@ -346,26 +388,19 @@ static const AVFilterPad atrim_inputs[] = { { .name = "default", .type = AVMEDIA_TYPE_AUDIO, - .filter_frame = atrim_filter_frame, .config_props = config_input, }, }; -static const AVFilterPad atrim_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_atrim = { .name = "atrim", .description = NULL_IF_CONFIG_SMALL("Pick one continuous section from the input, drop the rest."), .init = init, + .activate = activate, .priv_size = sizeof(TrimContext), .priv_class = &atrim_class, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(atrim_inputs), - FILTER_OUTPUTS(atrim_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), }; #endif // CONFIG_ATRIM_FILTER diff --git a/libavfilter/vaapi_vpp.c b/libavfilter/vaapi_vpp.c index 1610df4c859..cf2592e0689 100644 --- a/libavfilter/vaapi_vpp.c +++ b/libavfilter/vaapi_vpp.c @@ -95,6 +95,7 @@ int ff_vaapi_vpp_config_input(AVFilterLink *inlink) int ff_vaapi_vpp_config_output(AVFilterLink *outlink) { AVFilterContext *avctx = outlink->src; + AVFilterLink *inlink = avctx->inputs[0]; VAAPIVPPContext *ctx = avctx->priv; AVVAAPIHWConfig *hwconfig = NULL; AVHWFramesConstraints *constraints = NULL; @@ -111,6 +112,17 @@ int ff_vaapi_vpp_config_output(AVFilterLink *outlink) if (!ctx->output_height) ctx->output_height = avctx->inputs[0]->h; + outlink->w = ctx->output_width; + outlink->h = ctx->output_height; + + if (ctx->passthrough) { + if (inlink->hw_frames_ctx) + outlink->hw_frames_ctx = av_buffer_ref(inlink->hw_frames_ctx); + av_log(ctx, AV_LOG_VERBOSE, "Using VAAPI filter passthrough mode.\n"); + + return 0; + } + av_assert0(ctx->input_frames); ctx->device_ref = av_buffer_ref(ctx->input_frames->device_ref); if (!ctx->device_ref) { @@ -214,9 +226,6 @@ int ff_vaapi_vpp_config_output(AVFilterLink *outlink) return AVERROR(EIO); } - outlink->w = ctx->output_width; - outlink->h = ctx->output_height; - if (ctx->build_filter_params) { err = ctx->build_filter_params(avctx); if (err < 0) @@ -518,7 +527,6 @@ int ff_vaapi_vpp_init_params(AVFilterContext *avctx, AVFrame *output_frame) { VAAPIVPPContext *ctx = avctx->priv; - VASurfaceID input_surface; int err; ctx->input_region = (VARectangle) { @@ -534,10 +542,8 @@ int ff_vaapi_vpp_init_params(AVFilterContext *avctx, output_frame->crop_left = 0; output_frame->crop_right = 0; - input_surface = (VASurfaceID)(uintptr_t)input_frame->data[3], - *params = (VAProcPipelineParameterBuffer) { - .surface = input_surface, + .surface = ff_vaapi_vpp_get_surface_id(input_frame), .surface_region = &ctx->input_region, .output_region = NULL, .output_background_color = VAAPI_VPP_BACKGROUND_BLACK, @@ -557,6 +563,10 @@ int ff_vaapi_vpp_init_params(AVFilterContext *avctx, if (err < 0) return err; + av_log(avctx, AV_LOG_DEBUG, "Filter frame from surface %#x to %#x.\n", + ff_vaapi_vpp_get_surface_id(input_frame), + ff_vaapi_vpp_get_surface_id(output_frame)); + return 0; } @@ -588,47 +598,65 @@ int ff_vaapi_vpp_make_param_buffers(AVFilterContext *avctx, return 0; } - -int ff_vaapi_vpp_render_picture(AVFilterContext *avctx, - VAProcPipelineParameterBuffer *params, - AVFrame *output_frame) +static int vaapi_vpp_render_single_pipeline_buffer(AVFilterContext *avctx, + VAProcPipelineParameterBuffer *params, + VABufferID *params_id) { VAAPIVPPContext *ctx = avctx->priv; - VASurfaceID output_surface; - VABufferID params_id; VAStatus vas; - int err; - - output_surface = (VASurfaceID)(uintptr_t)output_frame->data[3]; - - vas = vaBeginPicture(ctx->hwctx->display, - ctx->va_context, output_surface); - if (vas != VA_STATUS_SUCCESS) { - av_log(avctx, AV_LOG_ERROR, "Failed to attach new picture: " - "%d (%s).\n", vas, vaErrorStr(vas)); - err = AVERROR(EIO); - goto fail; - } vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context, VAProcPipelineParameterBufferType, - sizeof(*params), 1, params, ¶ms_id); + sizeof(*params), 1, params, params_id); if (vas != VA_STATUS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to create parameter buffer: " "%d (%s).\n", vas, vaErrorStr(vas)); - err = AVERROR(EIO); - goto fail_after_begin; + *params_id = VA_INVALID_ID; + + return AVERROR(EIO); } - av_log(avctx, AV_LOG_DEBUG, "Pipeline parameter buffer is %#x.\n", - params_id); + av_log(avctx, AV_LOG_DEBUG, "Pipeline parameter buffer is %#x.\n", *params_id); - vas = vaRenderPicture(ctx->hwctx->display, ctx->va_context, - ¶ms_id, 1); + vas = vaRenderPicture(ctx->hwctx->display, ctx->va_context, params_id, 1); if (vas != VA_STATUS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to render parameter buffer: " "%d (%s).\n", vas, vaErrorStr(vas)); + return AVERROR(EIO); + } + + return 0; +} + +int ff_vaapi_vpp_render_pictures(AVFilterContext *avctx, + VAProcPipelineParameterBuffer *params_list, + int cout, + AVFrame *output_frame) +{ + VAAPIVPPContext *ctx = avctx->priv; + VABufferID *params_ids; + VAStatus vas; + int err; + + params_ids = (VABufferID *)av_malloc_array(cout, sizeof(VABufferID)); + if (!params_ids) + return AVERROR(ENOMEM); + + for (int i = 0; i < cout; i++) + params_ids[i] = VA_INVALID_ID; + + vas = vaBeginPicture(ctx->hwctx->display, + ctx->va_context, ff_vaapi_vpp_get_surface_id(output_frame)); + if (vas != VA_STATUS_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to attach new picture: " + "%d (%s).\n", vas, vaErrorStr(vas)); err = AVERROR(EIO); - goto fail_after_begin; + goto fail; + } + + for (int i = 0; i < cout; i++) { + err = vaapi_vpp_render_single_pipeline_buffer(avctx, ¶ms_list[i], ¶ms_ids[i]); + if (err) + goto fail_after_begin; } vas = vaEndPicture(ctx->hwctx->display, ctx->va_context); @@ -641,14 +669,17 @@ int ff_vaapi_vpp_render_picture(AVFilterContext *avctx, if (CONFIG_VAAPI_1 || ctx->hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS) { - vas = vaDestroyBuffer(ctx->hwctx->display, params_id); - if (vas != VA_STATUS_SUCCESS) { - av_log(avctx, AV_LOG_ERROR, "Failed to free parameter buffer: " - "%d (%s).\n", vas, vaErrorStr(vas)); - // And ignore. + for (int i = 0; i < cout && params_ids[i] != VA_INVALID_ID; i++) { + vas = vaDestroyBuffer(ctx->hwctx->display, params_ids[i]); + if (vas != VA_STATUS_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to free parameter buffer: " + "%d (%s).\n", vas, vaErrorStr(vas)); + // And ignore. + } } } + av_freep(¶ms_ids); return 0; // We want to make sure that if vaBeginPicture has been called, we also @@ -656,13 +687,21 @@ int ff_vaapi_vpp_render_picture(AVFilterContext *avctx, // do something else nasty, but once we're in this failure case there // isn't much else we can do. fail_after_begin: - vaRenderPicture(ctx->hwctx->display, ctx->va_context, ¶ms_id, 1); + vaRenderPicture(ctx->hwctx->display, ctx->va_context, ¶ms_ids[0], 1); fail_after_render: vaEndPicture(ctx->hwctx->display, ctx->va_context); fail: + av_freep(¶ms_ids); return err; } +int ff_vaapi_vpp_render_picture(AVFilterContext *avctx, + VAProcPipelineParameterBuffer *params, + AVFrame *output_frame) +{ + return ff_vaapi_vpp_render_pictures(avctx, params, 1, output_frame); +} + void ff_vaapi_vpp_ctx_init(AVFilterContext *avctx) { int i; diff --git a/libavfilter/vaapi_vpp.h b/libavfilter/vaapi_vpp.h index c3da91717c9..6764ab0c390 100644 --- a/libavfilter/vaapi_vpp.h +++ b/libavfilter/vaapi_vpp.h @@ -27,6 +27,11 @@ #include "avfilter.h" +static inline VASurfaceID ff_vaapi_vpp_get_surface_id(const AVFrame *frame) +{ + return (uintptr_t)frame->data[3]; +} + // ARGB black, for VAProcPipelineParameterBuffer.output_background_color. #define VAAPI_VPP_BACKGROUND_BLACK 0xff000000 @@ -51,6 +56,8 @@ typedef struct VAAPIVPPContext { VABufferID filter_buffers[VAProcFilterCount]; int nb_filter_buffers; + int passthrough; + int (*build_filter_params)(AVFilterContext *avctx); void (*pipeline_uninit)(AVFilterContext *avctx); @@ -83,4 +90,9 @@ int ff_vaapi_vpp_render_picture(AVFilterContext *avctx, VAProcPipelineParameterBuffer *params, AVFrame *output_frame); +int ff_vaapi_vpp_render_pictures(AVFilterContext *avctx, + VAProcPipelineParameterBuffer *params_list, + int count, + AVFrame *output_frame); + #endif /* AVFILTER_VAAPI_VPP_H */ diff --git a/libavfilter/version.h b/libavfilter/version.h index 9fabc544b59..4a69d6be98f 100644 --- a/libavfilter/version.h +++ b/libavfilter/version.h @@ -31,7 +31,7 @@ #include "version_major.h" -#define LIBAVFILTER_VERSION_MINOR 53 +#define LIBAVFILTER_VERSION_MINOR 10 #define LIBAVFILTER_VERSION_MICRO 100 diff --git a/libavfilter/version_major.h b/libavfilter/version_major.h index de0cf6e9793..1decc4012ee 100644 --- a/libavfilter/version_major.h +++ b/libavfilter/version_major.h @@ -27,7 +27,7 @@ * Libavfilter version macros */ -#define LIBAVFILTER_VERSION_MAJOR 8 +#define LIBAVFILTER_VERSION_MAJOR 9 /** * FF_API_* defines may be placed below to indicate public API that will be @@ -35,8 +35,6 @@ * the public API and may change, break or disappear at any time. */ -#define FF_API_SWS_PARAM_OPTION (LIBAVFILTER_VERSION_MAJOR < 9) -#define FF_API_BUFFERSINK_ALLOC (LIBAVFILTER_VERSION_MAJOR < 9) -#define FF_API_PAD_COUNT (LIBAVFILTER_VERSION_MAJOR < 9) +#define FF_API_LIBPLACEBO_OPTS (LIBAVFILTER_VERSION_MAJOR < 10) #endif /* AVFILTER_VERSION_MAJOR_H */ diff --git a/libavfilter/vf_addroi.c b/libavfilter/vf_addroi.c index dd3daeda81b..e7ad916214b 100644 --- a/libavfilter/vf_addroi.c +++ b/libavfilter/vf_addroi.c @@ -21,6 +21,7 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "internal.h" +#include "video.h" enum { X, Y, W, H, @@ -246,13 +247,6 @@ static const AVFilterPad addroi_inputs[] = { }, }; -static const AVFilterPad addroi_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_addroi = { .name = "addroi", .description = NULL_IF_CONFIG_SMALL("Add region of interest to frame."), @@ -265,5 +259,5 @@ const AVFilter ff_vf_addroi = { .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(addroi_inputs), - FILTER_OUTPUTS(addroi_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), }; diff --git a/libavfilter/vf_amplify.c b/libavfilter/vf_amplify.c index 0891d264134..0ebca60ea28 100644 --- a/libavfilter/vf_amplify.c +++ b/libavfilter/vf_amplify.c @@ -19,12 +19,10 @@ */ #include "libavutil/imgutils.h" -#include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" diff --git a/libavfilter/vf_aspect.c b/libavfilter/vf_aspect.c index 0a10a5a1769..b453de70c55 100644 --- a/libavfilter/vf_aspect.c +++ b/libavfilter/vf_aspect.c @@ -41,7 +41,8 @@ static const char *const var_names[] = { "w", "h", - "a", "dar", + "a", + "dar", "sar", "hsub", "vsub", @@ -51,7 +52,8 @@ static const char *const var_names[] = { enum var_name { VAR_W, VAR_H, - VAR_A, VAR_DAR, + VAR_A, + VAR_DAR, VAR_SAR, VAR_HSUB, VAR_VSUB, @@ -105,8 +107,8 @@ static int get_aspect_ratio(AVFilterLink *inlink, AVRational *aspect_ratio) /* evaluate new aspect ratio*/ ret = av_expr_parse_and_eval(&res, s->ratio_expr, - var_names, var_values, - NULL, NULL, NULL, NULL, NULL, 0, ctx); + var_names, var_values, + NULL, NULL, NULL, NULL, NULL, 0, ctx); if (ret < 0) { ret = av_parse_ratio(aspect_ratio, s->ratio_expr, s->max, 0, ctx); } else diff --git a/libavfilter/vf_atadenoise.c b/libavfilter/vf_atadenoise.c index 2178c62faf5..eed12acfa41 100644 --- a/libavfilter/vf_atadenoise.c +++ b/libavfilter/vf_atadenoise.c @@ -34,7 +34,6 @@ #include "bufferqueue.h" #include "atadenoise.h" -#include "formats.h" #include "internal.h" #include "video.h" diff --git a/libavfilter/vf_avgblur.c b/libavfilter/vf_avgblur.c index bd4471cb8e7..8ff6111bcc8 100644 --- a/libavfilter/vf_avgblur.c +++ b/libavfilter/vf_avgblur.c @@ -25,7 +25,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -334,13 +333,6 @@ static const AVFilterPad avgblur_inputs[] = { }, }; -static const AVFilterPad avgblur_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_avgblur = { .name = "avgblur", .description = NULL_IF_CONFIG_SMALL("Apply Average Blur filter."), @@ -348,7 +340,7 @@ const AVFilter ff_vf_avgblur = { .priv_class = &avgblur_class, .uninit = uninit, FILTER_INPUTS(avgblur_inputs), - FILTER_OUTPUTS(avgblur_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, .process_command = process_command, diff --git a/libavfilter/vf_avgblur_opencl.c b/libavfilter/vf_avgblur_opencl.c index 7c0578b694b..c00d2f6363c 100644 --- a/libavfilter/vf_avgblur_opencl.c +++ b/libavfilter/vf_avgblur_opencl.c @@ -59,7 +59,7 @@ static int avgblur_opencl_init(AVFilterContext *avctx) cl_int cle; int err; - err = ff_opencl_filter_load_program(avctx, &ff_opencl_source_avgblur, 1); + err = ff_opencl_filter_load_program(avctx, &ff_source_avgblur_cl, 1); if (err < 0) goto fail; @@ -390,6 +390,7 @@ const AVFilter ff_vf_boxblur_opencl = { FILTER_OUTPUTS(avgblur_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; #endif /* CONFIG_BOXBLUR_OPENCL_FILTER */ diff --git a/libavfilter/vf_avgblur_vulkan.c b/libavfilter/vf_avgblur_vulkan.c index d118ce802ca..ae32cd7324f 100644 --- a/libavfilter/vf_avgblur_vulkan.c +++ b/libavfilter/vf_avgblur_vulkan.c @@ -1,4 +1,6 @@ /* + * Copyright (c) Lynne + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -19,23 +21,25 @@ #include "libavutil/random_seed.h" #include "libavutil/opt.h" #include "vulkan_filter.h" +#include "vulkan_spirv.h" #include "internal.h" - -#define CGS 32 +#include "video.h" typedef struct AvgBlurVulkanContext { FFVulkanContext vkctx; int initialized; + FFVkExecPool e; FFVkQueueFamilyCtx qf; - FFVkExecContext *exec; - FFVulkanPipeline *pl_hor; - FFVulkanPipeline *pl_ver; + VkSampler sampler; + FFVulkanPipeline pl; + FFVkSPIRVShader shd; - /* Shader updators, must be in the main filter struct */ - VkDescriptorImageInfo input_images[3]; - VkDescriptorImageInfo tmp_images[3]; - VkDescriptorImageInfo output_images[3]; + /* Push constants / options */ + struct { + float filter_norm[4]; + int32_t filter_len[2]; + } opts; int size_x; int size_y; @@ -43,46 +47,53 @@ typedef struct AvgBlurVulkanContext { } AvgBlurVulkanContext; static const char blur_kernel[] = { - C(0, shared vec4 cache[DIR(gl_WorkGroupSize) + FILTER_RADIUS*2 + 1]; ) - C(0, ) - C(0, void distort(const ivec2 pos, const int idx) ) - C(0, { ) - C(1, const uint cp = DIR(gl_LocalInvocationID) + FILTER_RADIUS; ) - C(0, ) - C(1, cache[cp] = texture(input_img[idx], pos); ) - C(0, ) - C(1, const ivec2 loc_l = pos - INC(FILTER_RADIUS); ) - C(1, cache[cp - FILTER_RADIUS] = texture(input_img[idx], loc_l); ) - C(0, ) - C(1, const ivec2 loc_h = pos + INC(DIR(gl_WorkGroupSize)); ) - C(1, cache[cp + DIR(gl_WorkGroupSize)] = texture(input_img[idx], loc_h); ) - C(0, ) - C(1, barrier(); ) - C(0, ) - C(1, vec4 sum = vec4(0); ) - C(1, for (int p = -FILTER_RADIUS; p <= FILTER_RADIUS; p++) ) - C(2, sum += cache[cp + p]; ) - C(0, ) - C(1, sum /= vec4(FILTER_RADIUS*2 + 1); ) - C(1, imageStore(output_img[idx], pos, sum); ) - C(0, } ) + C(0, void distort(const ivec2 pos, const int idx) ) + C(0, { ) + C(1, vec4 sum = vec4(0); ) + C(1, for (int y = -filter_len.y; y <= filter_len.y; y++) ) + C(1, for (int x = -filter_len.x; x <= filter_len.x; x++) ) + C(2, sum += texture(input_img[idx], pos + ivec2(x, y)); ) + C(0, ) + C(1, imageStore(output_img[idx], pos, sum * filter_norm); ) + C(0, } ) }; static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) { int err; - FFVkSPIRVShader *shd; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; AvgBlurVulkanContext *s = ctx->priv; FFVulkanContext *vkctx = &s->vkctx; const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); + FFVkSPIRVShader *shd; + FFVkSPIRVCompiler *spv; + FFVulkanDescriptorSetBinding *desc; + + spv = ff_vk_spirv_init(); + if (!spv) { + av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); + return AVERROR_EXTERNAL; + } - FFVulkanDescriptorSetBinding desc_i[2] = { + ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT); + RET(ff_vk_exec_pool_init(vkctx, &s->qf, &s->e, s->qf.nb_queues*4, 0, 0, 0, NULL)); + RET(ff_vk_init_sampler(vkctx, &s->sampler, 1, VK_FILTER_LINEAR)); + RET(ff_vk_shader_init(&s->pl, &s->shd, "avgblur_compute", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); + shd = &s->shd; + + ff_vk_shader_set_compute_sizes(shd, 32, 1, 1); + + desc = (FFVulkanDescriptorSetBinding []) { { .name = "input_img", .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .dimensions = 2, .elems = planes, .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), }, { .name = "output_img", @@ -95,244 +106,68 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) }, }; - ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT, 0); + RET(ff_vk_pipeline_descriptor_set_add(vkctx, &s->pl, shd, desc, 2, 0, 0)); - desc_i[0].sampler = ff_vk_init_sampler(vkctx, 1, VK_FILTER_LINEAR); - if (!desc_i[0].sampler) - return AVERROR_EXTERNAL; + GLSLC(0, layout(push_constant, std430) uniform pushConstants { ); + GLSLC(1, vec4 filter_norm; ); + GLSLC(1, ivec2 filter_len; ); + GLSLC(0, }; ); + GLSLC(0, ); - { /* Create shader for the horizontal pass */ - desc_i[0].updater = s->input_images; - desc_i[1].updater = s->tmp_images; - - s->pl_hor = ff_vk_create_pipeline(vkctx, &s->qf); - if (!s->pl_hor) - return AVERROR(ENOMEM); - - shd = ff_vk_init_shader(s->pl_hor, "avgblur_compute_hor", - VK_SHADER_STAGE_COMPUTE_BIT); - if (!shd) - return AVERROR(ENOMEM); - - ff_vk_set_compute_shader_sizes(shd, (int [3]){ CGS, 1, 1 }); - - RET(ff_vk_add_descriptor_set(vkctx, s->pl_hor, shd, desc_i, FF_ARRAY_ELEMS(desc_i), 0)); - - GLSLF(0, #define FILTER_RADIUS (%i) ,s->size_x - 1); - GLSLC(0, #define INC(x) (ivec2(x, 0)) ); - GLSLC(0, #define DIR(var) (var.x) ); - GLSLD( blur_kernel ); - GLSLC(0, void main() ); - GLSLC(0, { ); - GLSLC(1, ivec2 size; ); - GLSLC(1, const ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); - for (int i = 0; i < planes; i++) { - GLSLC(0, ); - GLSLF(1, size = imageSize(output_img[%i]); ,i); - GLSLC(1, if (IS_WITHIN(pos, size)) { ); - if (s->planes & (1 << i)) { - GLSLF(2, distort(pos, %i); ,i); - } else { - GLSLF(2, vec4 res = texture(input_img[%i], pos); ,i); - GLSLF(2, imageStore(output_img[%i], pos, res); ,i); - } - GLSLC(1, } ); - } - GLSLC(0, } ); - - RET(ff_vk_compile_shader(vkctx, shd, "main")); - - RET(ff_vk_init_pipeline_layout(vkctx, s->pl_hor)); - RET(ff_vk_init_compute_pipeline(vkctx, s->pl_hor)); - } + ff_vk_add_push_constant(&s->pl, 0, sizeof(s->opts), + VK_SHADER_STAGE_COMPUTE_BIT); - { /* Create shader for the vertical pass */ - desc_i[0].updater = s->tmp_images; - desc_i[1].updater = s->output_images; - - s->pl_ver = ff_vk_create_pipeline(vkctx, &s->qf); - if (!s->pl_ver) - return AVERROR(ENOMEM); - - shd = ff_vk_init_shader(s->pl_ver, "avgblur_compute_ver", - VK_SHADER_STAGE_COMPUTE_BIT); - if (!shd) - return AVERROR(ENOMEM); - - ff_vk_set_compute_shader_sizes(shd, (int [3]){ 1, CGS, 1 }); - - RET(ff_vk_add_descriptor_set(vkctx, s->pl_ver, shd, desc_i, FF_ARRAY_ELEMS(desc_i), 0)); - - GLSLF(0, #define FILTER_RADIUS (%i) ,s->size_y - 1); - GLSLC(0, #define INC(x) (ivec2(0, x)) ); - GLSLC(0, #define DIR(var) (var.y) ); - GLSLD( blur_kernel ); - GLSLC(0, void main() ); - GLSLC(0, { ); - GLSLC(1, ivec2 size; ); - GLSLC(1, const ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); - for (int i = 0; i < planes; i++) { - GLSLC(0, ); - GLSLF(1, size = imageSize(output_img[%i]); ,i); - GLSLC(1, if (IS_WITHIN(pos, size)) { ); - if (s->planes & (1 << i)) { - GLSLF(2, distort(pos, %i); ,i); - } else { - GLSLF(2, vec4 res = texture(input_img[%i], pos); ,i); - GLSLF(2, imageStore(output_img[%i], pos, res); ,i); - } - GLSLC(1, } ); + GLSLD( blur_kernel ); + GLSLC(0, void main() ); + GLSLC(0, { ); + GLSLC(1, ivec2 size; ); + GLSLC(1, const ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); + for (int i = 0; i < planes; i++) { + GLSLC(0, ); + GLSLF(1, size = imageSize(output_img[%i]); ,i); + GLSLC(1, if (!IS_WITHIN(pos, size)) ); + GLSLC(2, return; ); + if (s->planes & (1 << i)) { + GLSLF(1, distort(pos, %i); ,i); + } else { + GLSLF(1, vec4 res = texture(input_img[%i], pos); ,i); + GLSLF(1, imageStore(output_img[%i], pos, res); ,i); } - GLSLC(0, } ); - - RET(ff_vk_compile_shader(vkctx, shd, "main")); - - RET(ff_vk_init_pipeline_layout(vkctx, s->pl_ver)); - RET(ff_vk_init_compute_pipeline(vkctx, s->pl_ver)); } + GLSLC(0, } ); + + RET(spv->compile_shader(spv, ctx, &s->shd, &spv_data, &spv_len, "main", + &spv_opaque)); + RET(ff_vk_shader_create(vkctx, &s->shd, spv_data, spv_len, "main")); - /* Execution context */ - RET(ff_vk_create_exec_ctx(vkctx, &s->exec, &s->qf)); + RET(ff_vk_init_compute_pipeline(vkctx, &s->pl, &s->shd)); + RET(ff_vk_exec_pipeline_register(vkctx, &s->e, &s->pl)); s->initialized = 1; + s->opts.filter_len[0] = s->size_x - 1; + s->opts.filter_len[1] = s->size_y - 1; + + s->opts.filter_norm[0] = s->opts.filter_len[0]*2 + 1; + s->opts.filter_norm[0] = 1.0/(s->opts.filter_norm[0]*s->opts.filter_norm[0]); + s->opts.filter_norm[1] = s->opts.filter_norm[0]; + s->opts.filter_norm[2] = s->opts.filter_norm[0]; + s->opts.filter_norm[3] = s->opts.filter_norm[0]; return 0; fail: - return err; -} - -static int process_frames(AVFilterContext *avctx, AVFrame *out_f, AVFrame *tmp_f, AVFrame *in_f) -{ - int err; - VkCommandBuffer cmd_buf; - AvgBlurVulkanContext *s = avctx->priv; - FFVulkanContext *vkctx = &s->vkctx; - FFVulkanFunctions *vk = &vkctx->vkfn; - AVVkFrame *in = (AVVkFrame *)in_f->data[0]; - AVVkFrame *tmp = (AVVkFrame *)tmp_f->data[0]; - AVVkFrame *out = (AVVkFrame *)out_f->data[0]; - - const VkFormat *input_formats = av_vkfmt_from_pixfmt(s->vkctx.input_format); - const VkFormat *output_formats = av_vkfmt_from_pixfmt(s->vkctx.output_format); - - int planes = av_pix_fmt_count_planes(s->vkctx.output_format); - - /* Update descriptors and init the exec context */ - ff_vk_start_exec_recording(vkctx, s->exec); - cmd_buf = ff_vk_get_exec_buf(s->exec); - - for (int i = 0; i < planes; i++) { - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->input_images[i].imageView, in->img[i], - input_formats[i], - ff_comp_identity_map)); - - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->tmp_images[i].imageView, tmp->img[i], - output_formats[i], - ff_comp_identity_map)); - - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->output_images[i].imageView, out->img[i], - output_formats[i], - ff_comp_identity_map)); - - s->input_images[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - s->tmp_images[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; - s->output_images[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; - } - - ff_vk_update_descriptor_set(vkctx, s->pl_hor, 0); - ff_vk_update_descriptor_set(vkctx, s->pl_ver, 0); - - for (int i = 0; i < planes; i++) { - VkImageMemoryBarrier bar[] = { - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .oldLayout = in->layout[i], - .newLayout = s->input_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = in->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT, - .oldLayout = tmp->layout[i], - .newLayout = s->tmp_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = tmp->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .oldLayout = out->layout[i], - .newLayout = s->output_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = out->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - }; - - vk->CmdPipelineBarrier(cmd_buf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, - 0, NULL, 0, NULL, FF_ARRAY_ELEMS(bar), bar); - - in->layout[i] = bar[0].newLayout; - in->access[i] = bar[0].dstAccessMask; - - tmp->layout[i] = bar[1].newLayout; - tmp->access[i] = bar[1].dstAccessMask; - - out->layout[i] = bar[2].newLayout; - out->access[i] = bar[2].dstAccessMask; - } - - ff_vk_bind_pipeline_exec(vkctx, s->exec, s->pl_hor); - - vk->CmdDispatch(cmd_buf, FFALIGN(s->vkctx.output_width, CGS)/CGS, - s->vkctx.output_height, 1); - - ff_vk_bind_pipeline_exec(vkctx, s->exec, s->pl_ver); - - vk->CmdDispatch(cmd_buf, s->vkctx.output_width, - FFALIGN(s->vkctx.output_height, CGS)/CGS, 1); - - ff_vk_add_exec_dep(vkctx, s->exec, in_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - ff_vk_add_exec_dep(vkctx, s->exec, out_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - - err = ff_vk_submit_exec_queue(vkctx,s->exec); - if (err) - return err; - - ff_vk_qf_rotate(&s->qf); + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); + if (spv) + spv->uninit(&spv); return err; - -fail: - ff_vk_discard_exec_deps(s->exec); - return err; } static int avgblur_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) { int err; - AVFrame *tmp = NULL, *out = NULL; + AVFrame *out = NULL; AVFilterContext *ctx = link->dst; AvgBlurVulkanContext *s = ctx->priv; AVFilterLink *outlink = ctx->outputs[0]; @@ -343,29 +178,22 @@ static int avgblur_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) goto fail; } - tmp = ff_get_video_buffer(outlink, outlink->w, outlink->h); - if (!tmp) { - err = AVERROR(ENOMEM); - goto fail; - } - if (!s->initialized) RET(init_filter(ctx, in)); - RET(process_frames(ctx, out, tmp, in)); + RET(ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->pl, + out, in, s->sampler, &s->opts, sizeof(s->opts))); err = av_frame_copy_props(out, in); if (err < 0) goto fail; av_frame_free(&in); - av_frame_free(&tmp); return ff_filter_frame(outlink, out); fail: av_frame_free(&in); - av_frame_free(&tmp); av_frame_free(&out); return err; } @@ -373,6 +201,16 @@ static int avgblur_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) static void avgblur_vulkan_uninit(AVFilterContext *avctx) { AvgBlurVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + FFVulkanFunctions *vk = &vkctx->vkfn; + + ff_vk_exec_pool_free(vkctx, &s->e); + ff_vk_pipeline_free(vkctx, &s->pl); + ff_vk_shader_free(vkctx, &s->shd); + + if (s->sampler) + vk->DestroySampler(vkctx->hwctx->act_dev, s->sampler, + vkctx->hwctx->alloc); ff_vk_uninit(&s->vkctx); @@ -382,9 +220,9 @@ static void avgblur_vulkan_uninit(AVFilterContext *avctx) #define OFFSET(x) offsetof(AvgBlurVulkanContext, x) #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) static const AVOption avgblur_vulkan_options[] = { - { "sizeX", "Set horizontal radius", OFFSET(size_x), AV_OPT_TYPE_INT, {.i64 = 3}, 1, 32, .flags = FLAGS }, + { "sizeX", "Set horizontal radius", OFFSET(size_x), AV_OPT_TYPE_INT, { .i64 = 3 }, 1, 32, .flags = FLAGS }, + { "sizeY", "Set vertical radius", OFFSET(size_y), AV_OPT_TYPE_INT, { .i64 = 3 }, 1, 32, .flags = FLAGS }, { "planes", "Set planes to filter (bitmask)", OFFSET(planes), AV_OPT_TYPE_INT, {.i64 = 0xF}, 0, 0xF, .flags = FLAGS }, - { "sizeY", "Set vertical radius", OFFSET(size_y), AV_OPT_TYPE_INT, {.i64 = 3}, 1, 32, .flags = FLAGS }, { NULL }, }; @@ -418,4 +256,5 @@ const AVFilter ff_vf_avgblur_vulkan = { FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), .priv_class = &avgblur_vulkan_class, .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_backgroundkey.c b/libavfilter/vf_backgroundkey.c index 8c7c289d0bd..26fb08bf869 100644 --- a/libavfilter/vf_backgroundkey.c +++ b/libavfilter/vf_backgroundkey.c @@ -17,9 +17,8 @@ */ #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" diff --git a/libavfilter/vf_bilateral.c b/libavfilter/vf_bilateral.c index 41dc38d6f23..fe16419026d 100644 --- a/libavfilter/vf_bilateral.c +++ b/libavfilter/vf_bilateral.c @@ -25,7 +25,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -498,13 +497,6 @@ static const AVFilterPad bilateral_inputs[] = { }, }; -static const AVFilterPad bilateral_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_bilateral = { .name = "bilateral", .description = NULL_IF_CONFIG_SMALL("Apply Bilateral filter."), @@ -512,7 +504,7 @@ const AVFilter ff_vf_bilateral = { .priv_class = &bilateral_class, .uninit = uninit, FILTER_INPUTS(bilateral_inputs), - FILTER_OUTPUTS(bilateral_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_bilateral_cuda.c b/libavfilter/vf_bilateral_cuda.c index 7a271f1ce08..ba008b517af 100644 --- a/libavfilter/vf_bilateral_cuda.c +++ b/libavfilter/vf_bilateral_cuda.c @@ -20,9 +20,7 @@ #include #include -#include -#include "libavutil/avstring.h" #include "libavutil/common.h" #include "libavutil/hwcontext.h" #include "libavutil/hwcontext_cuda_internal.h" @@ -32,9 +30,7 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" -#include "video.h" #include "cuda/load_helper.h" diff --git a/libavfilter/vf_bitplanenoise.c b/libavfilter/vf_bitplanenoise.c index 5b5c429369f..32235ff2301 100644 --- a/libavfilter/vf_bitplanenoise.c +++ b/libavfilter/vf_bitplanenoise.c @@ -19,9 +19,8 @@ */ #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -197,19 +196,12 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_bitplanenoise = { .name = "bitplanenoise", .description = NULL_IF_CONFIG_SMALL("Measure bit plane noise."), .priv_size = sizeof(BPNContext), FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixfmts), .priv_class = &bitplanenoise_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_blackdetect.c b/libavfilter/vf_blackdetect.c index c9372481694..55033ba5ea3 100644 --- a/libavfilter/vf_blackdetect.c +++ b/libavfilter/vf_blackdetect.c @@ -30,6 +30,7 @@ #include "libavutil/timestamp.h" #include "avfilter.h" #include "internal.h" +#include "video.h" typedef struct BlackDetectContext { const AVClass *class; @@ -241,19 +242,12 @@ static const AVFilterPad blackdetect_inputs[] = { }, }; -static const AVFilterPad blackdetect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_blackdetect = { .name = "blackdetect", .description = NULL_IF_CONFIG_SMALL("Detect video intervals that are (almost) black."), .priv_size = sizeof(BlackDetectContext), FILTER_INPUTS(blackdetect_inputs), - FILTER_OUTPUTS(blackdetect_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .uninit = uninit, .priv_class = &blackdetect_class, diff --git a/libavfilter/vf_blackframe.c b/libavfilter/vf_blackframe.c index e5e185dbea9..10062c995c6 100644 --- a/libavfilter/vf_blackframe.c +++ b/libavfilter/vf_blackframe.c @@ -33,7 +33,6 @@ #include "libavutil/internal.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -72,7 +71,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) p += frame->linesize[0]; } - if (frame->key_frame) + if (frame->flags & AV_FRAME_FLAG_KEY) s->last_keyframe = s->frame; pblack = s->nblack * 100 / (inlink->w * inlink->h); @@ -115,13 +114,6 @@ static const AVFilterPad avfilter_vf_blackframe_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_blackframe_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO - }, -}; - const AVFilter ff_vf_blackframe = { .name = "blackframe", .description = NULL_IF_CONFIG_SMALL("Detect frames that are (almost) black."), @@ -129,6 +121,6 @@ const AVFilter ff_vf_blackframe = { .priv_class = &blackframe_class, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(avfilter_vf_blackframe_inputs), - FILTER_OUTPUTS(avfilter_vf_blackframe_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), }; diff --git a/libavfilter/vf_blend_vulkan.c b/libavfilter/vf_blend_vulkan.c index fcc21cbc8d5..9db4e0a2f3d 100644 --- a/libavfilter/vf_blend_vulkan.c +++ b/libavfilter/vf_blend_vulkan.c @@ -1,5 +1,7 @@ /* * copyright (c) 2021-2022 Wu Jianhua + * Copyright (c) Lynne + * * The blend modes are based on the blend.c. * * This file is part of FFmpeg. @@ -22,11 +24,11 @@ #include "libavutil/random_seed.h" #include "libavutil/opt.h" #include "vulkan_filter.h" +#include "vulkan_spirv.h" #include "internal.h" #include "framesync.h" #include "blend.h" - -#define CGS 32 +#include "video.h" #define IN_TOP 0 #define IN_BOTTOM 1 @@ -40,20 +42,18 @@ typedef struct FilterParamsVulkan { typedef struct BlendVulkanContext { FFVulkanContext vkctx; - FFVkQueueFamilyCtx qf; - FFVkExecContext *exec; - FFVulkanPipeline *pl; FFFrameSync fs; - VkDescriptorImageInfo top_images[3]; - VkDescriptorImageInfo bottom_images[3]; - VkDescriptorImageInfo output_images[3]; + int initialized; + FFVulkanPipeline pl; + FFVkExecPool e; + FFVkQueueFamilyCtx qf; + FFVkSPIRVShader shd; + VkSampler sampler; FilterParamsVulkan params[4]; double all_opacity; enum BlendMode all_mode; - - int initialized; } BlendVulkanContext; #define DEFINE_BLEND_MODE(MODE, EXPR) \ @@ -125,223 +125,103 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar static av_cold int init_filter(AVFilterContext *avctx) { int err = 0; - FFVkSampler *sampler; - FFVkSPIRVShader *shd; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; BlendVulkanContext *s = avctx->priv; FFVulkanContext *vkctx = &s->vkctx; const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); + FFVkSPIRVShader *shd = &s->shd; + FFVkSPIRVCompiler *spv; + FFVulkanDescriptorSetBinding *desc; - ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT, 0); - - sampler = ff_vk_init_sampler(vkctx, 1, VK_FILTER_LINEAR); - if (!sampler) + spv = ff_vk_spirv_init(); + if (!spv) { + av_log(avctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); return AVERROR_EXTERNAL; - - s->pl = ff_vk_create_pipeline(vkctx, &s->qf); - if (!s->pl) - return AVERROR(ENOMEM); - - { - FFVulkanDescriptorSetBinding image_descs[] = { - { - .name = "top_images", - .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .dimensions = 2, - .elems = planes, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->top_images, - .sampler = sampler, - }, - { - .name = "bottom_images", - .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .dimensions = 2, - .elems = planes, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->bottom_images, - .sampler = sampler, - }, - { - .name = "output_images", - .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format), - .mem_quali = "writeonly", - .dimensions = 2, - .elems = planes, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->output_images, - }, - }; - - shd = ff_vk_init_shader(s->pl, "blend_compute", image_descs[0].stages); - if (!shd) - return AVERROR(ENOMEM); - - ff_vk_set_compute_shader_sizes(shd, (int [3]){ CGS, CGS, 1 }); - RET(ff_vk_add_descriptor_set(vkctx, s->pl, shd, image_descs, FF_ARRAY_ELEMS(image_descs), 0)); - - for (int i = 0, j = 0; i < planes; i++) { - for (j = 0; j < i; j++) - if (s->params[i].blend_func == s->params[j].blend_func) - break; - /* note: the bracket is needed, for GLSLD is a macro with multiple statements. */ - if (j == i) { - GLSLD(s->params[i].blend_func); - } - } - - GLSLC(0, void main() ); - GLSLC(0, { ); - GLSLC(1, ivec2 size; ); - GLSLC(1, const ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); - for (int i = 0; i < planes; i++) { - GLSLC(0, ); - GLSLF(1, size = imageSize(output_images[%i]); ,i); - GLSLC(1, if (IS_WITHIN(pos, size)) { ); - GLSLF(2, const vec4 top = texture(top_images[%i], pos); ,i); - GLSLF(2, const vec4 bottom = texture(bottom_images[%i], pos); ,i); - GLSLF(2, const float opacity = %f; ,s->params[i].opacity); - GLSLF(2, vec4 dst = %s(top, bottom, opacity); ,s->params[i].blend); - GLSLC(0, ); - GLSLF(2, imageStore(output_images[%i], pos, dst); ,i); - GLSLC(1, } ); - } - GLSLC(0, } ); - - RET(ff_vk_compile_shader(vkctx, shd, "main")); - RET(ff_vk_init_pipeline_layout(vkctx, s->pl)); - RET(ff_vk_init_compute_pipeline(vkctx, s->pl)); } - RET(ff_vk_create_exec_ctx(vkctx, &s->exec, &s->qf)); - - s->initialized = 1; - -fail: - return err; -} - -static int process_frames(AVFilterContext *avctx, AVFrame *out_frame, AVFrame *top_frame, AVFrame *bottom_frame) -{ - int err = 0; - VkCommandBuffer cmd_buf; - BlendVulkanContext *s = avctx->priv; - FFVulkanContext *vkctx = &s->vkctx; - FFVulkanFunctions *vk = &s->vkctx.vkfn; - const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); - - AVVkFrame *out = (AVVkFrame *)out_frame->data[0]; - AVVkFrame *top = (AVVkFrame *)top_frame->data[0]; - AVVkFrame *bottom = (AVVkFrame *)bottom_frame->data[0]; - - AVHWFramesContext *top_fc = (AVHWFramesContext*)top_frame->hw_frames_ctx->data; - AVHWFramesContext *bottom_fc = (AVHWFramesContext*)bottom_frame->hw_frames_ctx->data; - - const VkFormat *top_formats = av_vkfmt_from_pixfmt(top_fc->sw_format); - const VkFormat *bottom_formats = av_vkfmt_from_pixfmt(bottom_fc->sw_format); - const VkFormat *output_formats = av_vkfmt_from_pixfmt(s->vkctx.output_format); - - ff_vk_start_exec_recording(vkctx, s->exec); - cmd_buf = ff_vk_get_exec_buf(s->exec); - - for (int i = 0; i < planes; i++) { - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->top_images[i].imageView, top->img[i], - top_formats[i], - ff_comp_identity_map)); - - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->bottom_images[i].imageView, bottom->img[i], - bottom_formats[i], - ff_comp_identity_map)); - - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->output_images[i].imageView, out->img[i], - output_formats[i], - ff_comp_identity_map)); - - s->top_images[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - s->bottom_images[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - s->output_images[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; + ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT); + RET(ff_vk_exec_pool_init(vkctx, &s->qf, &s->e, s->qf.nb_queues*4, 0, 0, 0, NULL)); + RET(ff_vk_init_sampler(vkctx, &s->sampler, 1, VK_FILTER_NEAREST)); + RET(ff_vk_shader_init(&s->pl, &s->shd, "blend_compute", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); + + ff_vk_shader_set_compute_sizes(&s->shd, 32, 32, 1); + + desc = (FFVulkanDescriptorSetBinding []) { + { + .name = "top_images", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), + }, + { + .name = "bottom_images", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), + }, + { + .name = "output_images", + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format), + .mem_quali = "writeonly", + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + }, + }; + + RET(ff_vk_pipeline_descriptor_set_add(vkctx, &s->pl, shd, desc, 3, 0, 0)); + + for (int i = 0, j = 0; i < planes; i++) { + for (j = 0; j < i; j++) + if (s->params[i].blend_func == s->params[j].blend_func) + break; + /* note: the bracket is needed, for GLSLD is a macro with multiple statements. */ + if (j == i) { + GLSLD(s->params[i].blend_func); + } } - ff_vk_update_descriptor_set(vkctx, s->pl, 0); - + GLSLC(0, void main() ); + GLSLC(0, { ); + GLSLC(1, ivec2 size; ); + GLSLC(1, const ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); for (int i = 0; i < planes; i++) { - VkImageMemoryBarrier barriers[] = { - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .oldLayout = top->layout[i], - .newLayout = s->top_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = top->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .oldLayout = bottom->layout[i], - .newLayout = s->bottom_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = bottom->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .oldLayout = out->layout[i], - .newLayout = s->output_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = out->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - }; - - vk->CmdPipelineBarrier(cmd_buf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, - 0, NULL, 0, NULL, FF_ARRAY_ELEMS(barriers), barriers); - - top->layout[i] = barriers[0].newLayout; - top->access[i] = barriers[0].dstAccessMask; - - bottom->layout[i] = barriers[1].newLayout; - bottom->access[i] = barriers[1].dstAccessMask; - - out->layout[i] = barriers[2].newLayout; - out->access[i] = barriers[2].dstAccessMask; + GLSLC(0, ); + GLSLF(1, size = imageSize(output_images[%i]); ,i); + GLSLC(1, if (IS_WITHIN(pos, size)) { ); + GLSLF(2, const vec4 top = texture(top_images[%i], pos); ,i); + GLSLF(2, const vec4 bottom = texture(bottom_images[%i], pos); ,i); + GLSLF(2, const float opacity = %f; ,s->params[i].opacity); + GLSLF(2, vec4 dst = %s(top, bottom, opacity); ,s->params[i].blend); + GLSLC(0, ); + GLSLF(2, imageStore(output_images[%i], pos, dst); ,i); + GLSLC(1, } ); } + GLSLC(0, } ); - ff_vk_bind_pipeline_exec(vkctx, s->exec, s->pl); - vk->CmdDispatch(cmd_buf, FFALIGN(s->vkctx.output_width, CGS) / CGS, - FFALIGN(s->vkctx.output_height, CGS) / CGS, 1); - - ff_vk_add_exec_dep(vkctx, s->exec, top_frame, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - ff_vk_add_exec_dep(vkctx, s->exec, bottom_frame, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - ff_vk_add_exec_dep(vkctx, s->exec, out_frame, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - - err = ff_vk_submit_exec_queue(vkctx, s->exec); - if (err) - return err; + RET(spv->compile_shader(spv, avctx, shd, &spv_data, &spv_len, "main", + &spv_opaque)); + RET(ff_vk_shader_create(vkctx, shd, spv_data, spv_len, "main")); - ff_vk_qf_rotate(&s->qf); + RET(ff_vk_init_compute_pipeline(vkctx, &s->pl, shd)); + RET(ff_vk_exec_pipeline_register(vkctx, &s->e, &s->pl)); - return 0; + s->initialized = 1; fail: - ff_vk_discard_exec_deps(s->exec); + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); + if (spv) + spv->uninit(&spv); + return err; } @@ -370,12 +250,15 @@ static int blend_frame(FFFrameSync *fs) if (top_fc->sw_format != bottom_fc->sw_format) { av_log(avctx, AV_LOG_ERROR, "Currently the sw format of the bottom video need to match the top!\n"); - return AVERROR(EINVAL); + err = AVERROR(EINVAL); + goto fail; } RET(init_filter(avctx)); } - RET(process_frames(avctx, out, top, bottom)); + RET(ff_vk_filter_process_Nin(&s->vkctx, &s->e, &s->pl, + out, (AVFrame *[]){ top, bottom }, 2, + s->sampler, NULL, 0)); return ff_filter_frame(outlink, out); @@ -396,10 +279,19 @@ static av_cold int init(AVFilterContext *avctx) static av_cold void uninit(AVFilterContext *avctx) { BlendVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + FFVulkanFunctions *vk = &vkctx->vkfn; - ff_framesync_uninit(&s->fs); + ff_vk_exec_pool_free(vkctx, &s->e); + ff_vk_pipeline_free(vkctx, &s->pl); + ff_vk_shader_free(vkctx, &s->shd); + + if (s->sampler) + vk->DestroySampler(vkctx->hwctx->act_dev, s->sampler, + vkctx->hwctx->alloc); ff_vk_uninit(&s->vkctx); + ff_framesync_uninit(&s->fs); s->initialized = 0; } @@ -500,5 +392,6 @@ const AVFilter ff_vf_blend_vulkan = { FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), .priv_class = &blend_vulkan_class, .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, .process_command = &process_command, }; diff --git a/libavfilter/vf_blockdetect.c b/libavfilter/vf_blockdetect.c index 27283590be1..d787aff5e45 100644 --- a/libavfilter/vf_blockdetect.c +++ b/libavfilter/vf_blockdetect.c @@ -32,6 +32,7 @@ #include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "internal.h" +#include "video.h" typedef struct BLKContext { const AVClass *class; @@ -272,13 +273,6 @@ static const AVFilterPad blockdetect_inputs[] = { }, }; -static const AVFilterPad blockdetect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_blockdetect = { .name = "blockdetect", .description = NULL_IF_CONFIG_SMALL("Blockdetect filter."), @@ -286,7 +280,7 @@ const AVFilter ff_vf_blockdetect = { .uninit = blockdetect_uninit, FILTER_PIXFMTS_ARRAY(pix_fmts), FILTER_INPUTS(blockdetect_inputs), - FILTER_OUTPUTS(blockdetect_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), .priv_class = &blockdetect_class, .flags = AVFILTER_FLAG_METADATA_ONLY, }; diff --git a/libavfilter/vf_blurdetect.c b/libavfilter/vf_blurdetect.c index db06efcce74..354a6b6100f 100644 --- a/libavfilter/vf_blurdetect.c +++ b/libavfilter/vf_blurdetect.c @@ -35,6 +35,7 @@ #include "libavutil/qsort.h" #include "internal.h" #include "edge_common.h" +#include "video.h" static int comp(const float *a,const float *b) { @@ -356,13 +357,6 @@ static const AVFilterPad blurdetect_inputs[] = { }, }; -static const AVFilterPad blurdetect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_blurdetect = { .name = "blurdetect", .description = NULL_IF_CONFIG_SMALL("Blurdetect filter."), @@ -371,7 +365,7 @@ const AVFilter ff_vf_blurdetect = { .uninit = blurdetect_uninit, FILTER_PIXFMTS_ARRAY(pix_fmts), FILTER_INPUTS(blurdetect_inputs), - FILTER_OUTPUTS(blurdetect_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), .priv_class = &blurdetect_class, .flags = AVFILTER_FLAG_METADATA_ONLY, }; diff --git a/libavfilter/vf_bm3d.c b/libavfilter/vf_bm3d.c index 14f94cf5358..cdced50ba3c 100644 --- a/libavfilter/vf_bm3d.c +++ b/libavfilter/vf_bm3d.c @@ -38,7 +38,6 @@ #include "libavutil/tx.h" #include "avfilter.h" #include "filters.h" -#include "formats.h" #include "framesync.h" #include "internal.h" #include "video.h" diff --git a/libavfilter/vf_boxblur.c b/libavfilter/vf_boxblur.c index e13c2472b37..60375463a61 100644 --- a/libavfilter/vf_boxblur.c +++ b/libavfilter/vf_boxblur.c @@ -295,13 +295,6 @@ static const AVFilterPad avfilter_vf_boxblur_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_boxblur_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_boxblur = { .name = "boxblur", .description = NULL_IF_CONFIG_SMALL("Blur the input."), @@ -309,7 +302,7 @@ const AVFilter ff_vf_boxblur = { .priv_class = &boxblur_class, .uninit = uninit, FILTER_INPUTS(avfilter_vf_boxblur_inputs), - FILTER_OUTPUTS(avfilter_vf_boxblur_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_bwdif.c b/libavfilter/vf_bwdif.c index 65c617ebb33..5f5f8e46106 100644 --- a/libavfilter/vf_bwdif.c +++ b/libavfilter/vf_bwdif.c @@ -31,11 +31,8 @@ #include "libavutil/common.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" -#include "libavutil/imgutils.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" -#include "video.h" #include "bwdif.h" /* @@ -122,8 +119,8 @@ typedef struct ThreadData { next2++; \ } -static void filter_intra(void *dst1, void *cur1, int w, int prefs, int mrefs, - int prefs3, int mrefs3, int parity, int clip_max) +void ff_bwdif_filter_intra_c(void *dst1, void *cur1, int w, int prefs, int mrefs, + int prefs3, int mrefs3, int parity, int clip_max) { uint8_t *dst = dst1; uint8_t *cur = cur1; @@ -132,10 +129,10 @@ static void filter_intra(void *dst1, void *cur1, int w, int prefs, int mrefs, FILTER_INTRA() } -static void filter_line_c(void *dst1, void *prev1, void *cur1, void *next1, - int w, int prefs, int mrefs, int prefs2, int mrefs2, - int prefs3, int mrefs3, int prefs4, int mrefs4, - int parity, int clip_max) +void ff_bwdif_filter_line_c(void *dst1, void *prev1, void *cur1, void *next1, + int w, int prefs, int mrefs, int prefs2, int mrefs2, + int prefs3, int mrefs3, int prefs4, int mrefs4, + int parity, int clip_max) { uint8_t *dst = dst1; uint8_t *prev = prev1; @@ -150,9 +147,34 @@ static void filter_line_c(void *dst1, void *prev1, void *cur1, void *next1, FILTER2() } -static void filter_edge(void *dst1, void *prev1, void *cur1, void *next1, - int w, int prefs, int mrefs, int prefs2, int mrefs2, - int parity, int clip_max, int spat) +#define NEXT_LINE()\ + dst += d_stride; \ + prev += prefs; \ + cur += prefs; \ + next += prefs; + +void ff_bwdif_filter_line3_c(void * dst1, int d_stride, + const void * prev1, const void * cur1, const void * next1, int s_stride, + int w, int parity, int clip_max) +{ + const int prefs = s_stride; + uint8_t * dst = dst1; + const uint8_t * prev = prev1; + const uint8_t * cur = cur1; + const uint8_t * next = next1; + + ff_bwdif_filter_line_c(dst, (void*)prev, (void*)cur, (void*)next, w, + prefs, -prefs, prefs * 2, - prefs * 2, prefs * 3, -prefs * 3, prefs * 4, -prefs * 4, parity, clip_max); + NEXT_LINE(); + memcpy(dst, cur, w); + NEXT_LINE(); + ff_bwdif_filter_line_c(dst, (void*)prev, (void*)cur, (void*)next, w, + prefs, -prefs, prefs * 2, - prefs * 2, prefs * 3, -prefs * 3, prefs * 4, -prefs * 4, parity, clip_max); +} + +void ff_bwdif_filter_edge_c(void *dst1, void *prev1, void *cur1, void *next1, + int w, int prefs, int mrefs, int prefs2, int mrefs2, + int parity, int clip_max, int spat) { uint8_t *dst = dst1; uint8_t *prev = prev1; @@ -212,6 +234,13 @@ static void filter_edge_16bit(void *dst1, void *prev1, void *cur1, void *next1, FILTER2() } +// Round job start line down to multiple of 4 so that if filter_line3 exists +// and the frame is a multiple of 4 high then filter_line will never be called +static inline int job_start(const int jobnr, const int nb_jobs, const int h) +{ + return jobnr >= nb_jobs ? h : ((h * jobnr) / nb_jobs) & ~3; +} + static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { BWDIFContext *s = ctx->priv; @@ -221,8 +250,8 @@ static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) int clip_max = (1 << (yadif->csp->comp[td->plane].depth)) - 1; int df = (yadif->csp->comp[td->plane].depth + 7) / 8; int refs = linesize / df; - int slice_start = (td->h * jobnr ) / nb_jobs; - int slice_end = (td->h * (jobnr+1)) / nb_jobs; + int slice_start = job_start(jobnr, nb_jobs, td->h); + int slice_end = job_start(jobnr + 1, nb_jobs, td->h); int y; for (y = slice_start; y < slice_end; y++) { @@ -244,6 +273,11 @@ static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) refs << 1, -(refs << 1), td->parity ^ td->tff, clip_max, (y < 2) || ((y + 3) > td->h) ? 0 : 1); + } else if (s->filter_line3 && y + 2 < slice_end && y + 6 < td->h) { + s->filter_line3(dst, td->frame->linesize[td->plane], + prev, cur, next, linesize, td->w, + td->parity ^ td->tff, clip_max); + y += 2; } else { s->filter_line(dst, prev, cur, next, td->w, refs, -refs, refs << 1, -(refs << 1), @@ -280,7 +314,7 @@ static void filter(AVFilterContext *ctx, AVFrame *dstpic, td.plane = i; ff_filter_execute(ctx, filter_slice, &td, NULL, - FFMIN(h, ff_filter_get_nb_threads(ctx))); + FFMIN((h+3)/4, ff_filter_get_nb_threads(ctx))); } if (yadif->current_field == YADIF_FIELD_END) { yadif->current_field = YADIF_FIELD_NORMAL; @@ -297,6 +331,7 @@ static av_cold void uninit(AVFilterContext *ctx) av_frame_free(&yadif->prev); av_frame_free(&yadif->cur ); av_frame_free(&yadif->next); + ff_ccfifo_uninit(&yadif->cc_fifo); } static const enum AVPixelFormat pix_fmts[] = { @@ -325,6 +360,7 @@ static int config_props(AVFilterLink *link) AVFilterContext *ctx = link->src; BWDIFContext *s = link->src->priv; YADIFContext *yadif = &s->yadif; + int ret; link->time_base = av_mul_q(ctx->inputs[0]->time_base, (AVRational){1, 2}); link->w = link->src->inputs[0]->w; @@ -332,6 +368,14 @@ static int config_props(AVFilterLink *link) if(yadif->mode&1) link->frame_rate = av_mul_q(link->src->inputs[0]->frame_rate, (AVRational){2,1}); + else + link->frame_rate = ctx->inputs[0]->frame_rate; + + ret = ff_ccfifo_init(&yadif->cc_fifo, link->frame_rate, ctx); + if (ret < 0 ) { + av_log(ctx, AV_LOG_ERROR, "Failure to setup CC FIFO queue\n"); + return ret; + } if (link->w < 3 || link->h < 4) { av_log(ctx, AV_LOG_ERROR, "Video of less than 3 columns or 4 lines is not supported\n"); @@ -340,21 +384,29 @@ static int config_props(AVFilterLink *link) yadif->csp = av_pix_fmt_desc_get(link->format); yadif->filter = filter; - if (yadif->csp->comp[0].depth > 8) { + ff_bwdif_init_filter_line(s, yadif->csp->comp[0].depth); + + return 0; +} + +av_cold void ff_bwdif_init_filter_line(BWDIFContext *s, int bit_depth) +{ + s->filter_line3 = 0; + if (bit_depth > 8) { s->filter_intra = filter_intra_16bit; s->filter_line = filter_line_c_16bit; s->filter_edge = filter_edge_16bit; } else { - s->filter_intra = filter_intra; - s->filter_line = filter_line_c; - s->filter_edge = filter_edge; + s->filter_intra = ff_bwdif_filter_intra_c; + s->filter_line = ff_bwdif_filter_line_c; + s->filter_edge = ff_bwdif_filter_edge_c; } #if ARCH_X86 - ff_bwdif_init_x86(s); + ff_bwdif_init_x86(s, bit_depth); +#elif ARCH_AARCH64 + ff_bwdif_init_aarch64(s, bit_depth); #endif - - return 0; } diff --git a/libavfilter/vf_bwdif_cuda.c b/libavfilter/vf_bwdif_cuda.c new file mode 100644 index 00000000000..a5ecfbadb64 --- /dev/null +++ b/libavfilter/vf_bwdif_cuda.c @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2019 Philip Langdale + * + * 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 "libavutil/avassert.h" +#include "libavutil/hwcontext.h" +#include "libavutil/hwcontext_cuda_internal.h" +#include "libavutil/cuda_check.h" +#include "internal.h" +#include "yadif.h" + +#include "cuda/load_helper.h" + +extern const unsigned char ff_vf_bwdif_cuda_ptx_data[]; +extern const unsigned int ff_vf_bwdif_cuda_ptx_len; + +typedef struct DeintCUDAContext { + YADIFContext yadif; + + AVCUDADeviceContext *hwctx; + AVBufferRef *device_ref; + AVBufferRef *input_frames_ref; + AVHWFramesContext *input_frames; + + CUmodule cu_module; + CUfunction cu_func_uchar; + CUfunction cu_func_uchar2; + CUfunction cu_func_ushort; + CUfunction cu_func_ushort2; +} DeintCUDAContext; + +#define DIV_UP(a, b) ( ((a) + (b) - 1) / (b) ) +#define ALIGN_UP(a, b) (((a) + (b) - 1) & ~((b) - 1)) +#define BLOCKX 32 +#define BLOCKY 16 + +#define CHECK_CU(x) FF_CUDA_CHECK_DL(ctx, s->hwctx->internal->cuda_dl, x) + +static CUresult call_kernel(AVFilterContext *ctx, CUfunction func, + CUdeviceptr prev, CUdeviceptr cur, CUdeviceptr next, + CUarray_format format, int channels, + int src_width, // Width is pixels per channel + int src_height, // Height is pixels per channel + int src_pitch, // Pitch is bytes + CUdeviceptr dst, + int dst_width, // Width is pixels per channel + int dst_height, // Height is pixels per channel + int dst_pitch, // Pitch is pixels per channel + int parity, int tff, int clip_max) +{ + DeintCUDAContext *s = ctx->priv; + YADIFContext *y = &s->yadif; + CudaFunctions *cu = s->hwctx->internal->cuda_dl; + CUtexObject tex_prev = 0, tex_cur = 0, tex_next = 0; + int is_field_end = y->current_field == YADIF_FIELD_END; + int ret; + + void *args[] = { &dst, &tex_prev, &tex_cur, &tex_next, + &dst_width, &dst_height, &dst_pitch, + &src_width, &src_height, &parity, &tff, + &is_field_end, &clip_max }; + + CUDA_TEXTURE_DESC tex_desc = { + .filterMode = CU_TR_FILTER_MODE_POINT, + .flags = CU_TRSF_READ_AS_INTEGER, + }; + + CUDA_RESOURCE_DESC res_desc = { + .resType = CU_RESOURCE_TYPE_PITCH2D, + .res.pitch2D.format = format, + .res.pitch2D.numChannels = channels, + .res.pitch2D.width = src_width, + .res.pitch2D.height = src_height, + .res.pitch2D.pitchInBytes = src_pitch, + }; + + res_desc.res.pitch2D.devPtr = (CUdeviceptr)prev; + ret = CHECK_CU(cu->cuTexObjectCreate(&tex_prev, &res_desc, &tex_desc, NULL)); + if (ret < 0) + goto exit; + + res_desc.res.pitch2D.devPtr = (CUdeviceptr)cur; + ret = CHECK_CU(cu->cuTexObjectCreate(&tex_cur, &res_desc, &tex_desc, NULL)); + if (ret < 0) + goto exit; + + res_desc.res.pitch2D.devPtr = (CUdeviceptr)next; + ret = CHECK_CU(cu->cuTexObjectCreate(&tex_next, &res_desc, &tex_desc, NULL)); + if (ret < 0) + goto exit; + + ret = CHECK_CU(cu->cuLaunchKernel(func, + DIV_UP(dst_width, BLOCKX), DIV_UP(dst_height, BLOCKY), 1, + BLOCKX, BLOCKY, 1, + 0, s->hwctx->stream, args, NULL)); + +exit: + if (tex_prev) + CHECK_CU(cu->cuTexObjectDestroy(tex_prev)); + if (tex_cur) + CHECK_CU(cu->cuTexObjectDestroy(tex_cur)); + if (tex_next) + CHECK_CU(cu->cuTexObjectDestroy(tex_next)); + + return ret; +} + +static void filter(AVFilterContext *ctx, AVFrame *dst, + int parity, int tff) +{ + DeintCUDAContext *s = ctx->priv; + YADIFContext *y = &s->yadif; + CudaFunctions *cu = s->hwctx->internal->cuda_dl; + CUcontext dummy; + int i, ret; + + ret = CHECK_CU(cu->cuCtxPushCurrent(s->hwctx->cuda_ctx)); + if (ret < 0) + return; + + for (i = 0; i < y->csp->nb_components; i++) { + CUfunction func; + CUarray_format format; + int pixel_size, channels, clip_max; + const AVComponentDescriptor *comp = &y->csp->comp[i]; + + if (comp->plane < i) { + // We process planes as a whole, so don't reprocess + // them for additional components + continue; + } + + pixel_size = (comp->depth + comp->shift) / 8; + channels = comp->step / pixel_size; + if (pixel_size > 2 || channels > 2) { + av_log(ctx, AV_LOG_ERROR, "Unsupported pixel format: %s\n", y->csp->name); + goto exit; + } + switch (pixel_size) { + case 1: + func = channels == 1 ? s->cu_func_uchar : s->cu_func_uchar2; + format = CU_AD_FORMAT_UNSIGNED_INT8; + break; + case 2: + func = channels == 1 ? s->cu_func_ushort : s->cu_func_ushort2; + format = CU_AD_FORMAT_UNSIGNED_INT16; + break; + default: + av_log(ctx, AV_LOG_ERROR, "Unsupported pixel format: %s\n", y->csp->name); + goto exit; + } + + clip_max = (1 << (comp->depth + comp->shift)) - 1; + + av_log(ctx, AV_LOG_TRACE, + "Deinterlacing plane %d: pixel_size: %d channels: %d\n", + comp->plane, pixel_size, channels); + call_kernel(ctx, func, + (CUdeviceptr)y->prev->data[i], + (CUdeviceptr)y->cur->data[i], + (CUdeviceptr)y->next->data[i], + format, channels, + AV_CEIL_RSHIFT(y->cur->width, i ? y->csp->log2_chroma_w : 0), + AV_CEIL_RSHIFT(y->cur->height, i ? y->csp->log2_chroma_h : 0), + y->cur->linesize[i], + (CUdeviceptr)dst->data[i], + AV_CEIL_RSHIFT(dst->width, i ? y->csp->log2_chroma_w : 0), + AV_CEIL_RSHIFT(dst->height, i ? y->csp->log2_chroma_h : 0), + dst->linesize[i] / comp->step, + parity, tff, clip_max); + } + + if (y->current_field == YADIF_FIELD_END) { + y->current_field = YADIF_FIELD_NORMAL; + } + +exit: + CHECK_CU(cu->cuCtxPopCurrent(&dummy)); + return; +} + +static av_cold void deint_cuda_uninit(AVFilterContext *ctx) +{ + CUcontext dummy; + DeintCUDAContext *s = ctx->priv; + YADIFContext *y = &s->yadif; + + if (s->hwctx && s->cu_module) { + CudaFunctions *cu = s->hwctx->internal->cuda_dl; + CHECK_CU(cu->cuCtxPushCurrent(s->hwctx->cuda_ctx)); + CHECK_CU(cu->cuModuleUnload(s->cu_module)); + CHECK_CU(cu->cuCtxPopCurrent(&dummy)); + } + + av_frame_free(&y->prev); + av_frame_free(&y->cur); + av_frame_free(&y->next); + + av_buffer_unref(&s->device_ref); + s->hwctx = NULL; + av_buffer_unref(&s->input_frames_ref); + s->input_frames = NULL; +} + +static int config_input(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + DeintCUDAContext *s = ctx->priv; + + if (!inlink->hw_frames_ctx) { + av_log(ctx, AV_LOG_ERROR, "A hardware frames reference is " + "required to associate the processing device.\n"); + return AVERROR(EINVAL); + } + + s->input_frames_ref = av_buffer_ref(inlink->hw_frames_ctx); + if (!s->input_frames_ref) { + av_log(ctx, AV_LOG_ERROR, "A input frames reference create " + "failed.\n"); + return AVERROR(ENOMEM); + } + s->input_frames = (AVHWFramesContext*)s->input_frames_ref->data; + + return 0; +} + +static int config_output(AVFilterLink *link) +{ + AVHWFramesContext *output_frames; + AVFilterContext *ctx = link->src; + DeintCUDAContext *s = ctx->priv; + YADIFContext *y = &s->yadif; + CudaFunctions *cu; + int ret = 0; + CUcontext dummy; + + av_assert0(s->input_frames); + s->device_ref = av_buffer_ref(s->input_frames->device_ref); + if (!s->device_ref) { + av_log(ctx, AV_LOG_ERROR, "A device reference create " + "failed.\n"); + return AVERROR(ENOMEM); + } + s->hwctx = ((AVHWDeviceContext*)s->device_ref->data)->hwctx; + cu = s->hwctx->internal->cuda_dl; + + link->hw_frames_ctx = av_hwframe_ctx_alloc(s->device_ref); + if (!link->hw_frames_ctx) { + av_log(ctx, AV_LOG_ERROR, "Failed to create HW frame context " + "for output.\n"); + ret = AVERROR(ENOMEM); + goto exit; + } + + output_frames = (AVHWFramesContext*)link->hw_frames_ctx->data; + + output_frames->format = AV_PIX_FMT_CUDA; + output_frames->sw_format = s->input_frames->sw_format; + output_frames->width = ctx->inputs[0]->w; + output_frames->height = ctx->inputs[0]->h; + + output_frames->initial_pool_size = 4; + + ret = ff_filter_init_hw_frames(ctx, link, 10); + if (ret < 0) + goto exit; + + ret = av_hwframe_ctx_init(link->hw_frames_ctx); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Failed to initialise CUDA frame " + "context for output: %d\n", ret); + goto exit; + } + + link->time_base = av_mul_q(ctx->inputs[0]->time_base, (AVRational){1, 2}); + link->w = ctx->inputs[0]->w; + link->h = ctx->inputs[0]->h; + + if(y->mode & 1) + link->frame_rate = av_mul_q(ctx->inputs[0]->frame_rate, + (AVRational){2, 1}); + + if (link->w < 3 || link->h < 3) { + av_log(ctx, AV_LOG_ERROR, "Video of less than 3 columns or lines is not supported\n"); + ret = AVERROR(EINVAL); + goto exit; + } + + y->csp = av_pix_fmt_desc_get(output_frames->sw_format); + y->filter = filter; + + ret = CHECK_CU(cu->cuCtxPushCurrent(s->hwctx->cuda_ctx)); + if (ret < 0) + goto exit; + + ret = ff_cuda_load_module(ctx, s->hwctx, &s->cu_module, ff_vf_bwdif_cuda_ptx_data, ff_vf_bwdif_cuda_ptx_len); + if (ret < 0) + goto exit; + + ret = CHECK_CU(cu->cuModuleGetFunction(&s->cu_func_uchar, s->cu_module, "bwdif_uchar")); + if (ret < 0) + goto exit; + + ret = CHECK_CU(cu->cuModuleGetFunction(&s->cu_func_uchar2, s->cu_module, "bwdif_uchar2")); + if (ret < 0) + goto exit; + + ret = CHECK_CU(cu->cuModuleGetFunction(&s->cu_func_ushort, s->cu_module, "bwdif_ushort")); + if (ret < 0) + goto exit; + + ret = CHECK_CU(cu->cuModuleGetFunction(&s->cu_func_ushort2, s->cu_module, "bwdif_ushort2")); + if (ret < 0) + goto exit; + +exit: + CHECK_CU(cu->cuCtxPopCurrent(&dummy)); + + return ret; +} + +static const AVClass bwdif_cuda_class = { + .class_name = "bwdif_cuda", + .item_name = av_default_item_name, + .option = ff_yadif_options, + .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_FILTER, +}; + +static const AVFilterPad deint_cuda_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = ff_yadif_filter_frame, + .config_props = config_input, + }, +}; + +static const AVFilterPad deint_cuda_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .request_frame = ff_yadif_request_frame, + .config_props = config_output, + }, +}; + +const AVFilter ff_vf_bwdif_cuda = { + .name = "bwdif_cuda", + .description = NULL_IF_CONFIG_SMALL("Deinterlace CUDA frames"), + .priv_size = sizeof(DeintCUDAContext), + .priv_class = &bwdif_cuda_class, + .uninit = deint_cuda_uninit, + FILTER_SINGLE_PIXFMT(AV_PIX_FMT_CUDA), + FILTER_INPUTS(deint_cuda_inputs), + FILTER_OUTPUTS(deint_cuda_outputs), + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, +}; diff --git a/libavfilter/vf_bwdif_cuda.cu b/libavfilter/vf_bwdif_cuda.cu new file mode 100644 index 00000000000..3d4c29d8c39 --- /dev/null +++ b/libavfilter/vf_bwdif_cuda.cu @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2019 Philip Langdale + * + * 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 + */ + +__device__ static const int coef_lf[2] = { 4309, 213 }; +__device__ static const int coef_hf[3] = { 5570, 3801, 1016 }; +__device__ static const int coef_sp[2] = { 5077, 981 }; + +template +__inline__ __device__ T max3(T a, T b, T c) +{ + T x = max(a, b); + return max(x, c); +} + +template +__inline__ __device__ T min3(T a, T b, T c) +{ + T x = min(a, b); + return min(x, c); +} + +template +__inline__ __device__ T clip(T a, T min, T max) +{ + if (a < min) { + return min; + } else if (a > max) { + return max; + } else { + return a; + } +} + +template +__inline__ __device__ T filter_intra(T cur_prefs3, T cur_prefs, + T cur_mrefs, T cur_mrefs3, + int clip_max) +{ + int final = (coef_sp[0] * (cur_mrefs + cur_prefs) - + coef_sp[1] * (cur_mrefs3 + cur_prefs3)) >> 13; + return clip(final, 0, clip_max); +} + +template +__inline__ __device__ T filter(T cur_prefs3, T cur_prefs, T cur_mrefs, T cur_mrefs3, + T prev2_prefs4, T prev2_prefs2, T prev2_0, T prev2_mrefs2, T prev2_mrefs4, + T prev_prefs, T prev_mrefs, T next_prefs, T next_mrefs, + T next2_prefs4, T next2_prefs2, T next2_0, T next2_mrefs2, T next2_mrefs4, + int clip_max) +{ + T final; + + int c = cur_mrefs; + int d = (prev2_0 + next2_0) >> 1; + int e = cur_prefs; + + int temporal_diff0 = abs(prev2_0 - next2_0); + int temporal_diff1 = (abs(prev_mrefs - c) + abs(prev_prefs - e)) >> 1; + int temporal_diff2 = (abs(next_mrefs - c) + abs(next_prefs - e)) >> 1; + int diff = max3(temporal_diff0 >> 1, temporal_diff1, temporal_diff2); + + if (!diff) { + final = d; + } else { + int b = ((prev2_mrefs2 + next2_mrefs2) >> 1) - c; + int f = ((prev2_prefs2 + next2_prefs2) >> 1) - e; + int dc = d - c; + int de = d - e; + int mmax = max3(de, dc, min(b, f)); + int mmin = min3(de, dc, max(b, f)); + diff = max3(diff, mmin, -mmax); + + int interpol; + if (abs(c - e) > temporal_diff0) { + interpol = (((coef_hf[0] * (prev2_0 + next2_0) + - coef_hf[1] * (prev2_mrefs2 + next2_mrefs2 + prev2_prefs2 + next2_prefs2) + + coef_hf[2] * (prev2_mrefs4 + next2_mrefs4 + prev2_prefs4 + next2_mrefs4)) >> 2) + + coef_lf[0] * (c + e) - coef_lf[1] * (cur_mrefs3 + cur_prefs3)) >> 13; + } else { + interpol = (coef_sp[0] * (c + e) - coef_sp[1] * (cur_mrefs3 + cur_prefs3)) >> 13; + } + + if (interpol > d + diff) { + interpol = d + diff; + } else if (interpol < d - diff) { + interpol = d - diff; + } + final = clip(interpol, 0, clip_max); + } + + return final; +} + +template +__inline__ __device__ void bwdif_single(T *dst, + cudaTextureObject_t prev, + cudaTextureObject_t cur, + cudaTextureObject_t next, + int dst_width, int dst_height, int dst_pitch, + int src_width, int src_height, + int parity, int tff, + int is_field_end, int clip_max) +{ + // Identify location + int xo = blockIdx.x * blockDim.x + threadIdx.x; + int yo = blockIdx.y * blockDim.y + threadIdx.y; + + if (xo >= dst_width || yo >= dst_height) { + return; + } + + // Don't modify the primary field + if (yo % 2 == parity) { + dst[yo*dst_pitch+xo] = tex2D(cur, xo, yo); + return; + } + + T cur_prefs3 = tex2D(cur, xo, yo + 3); + T cur_prefs = tex2D(cur, xo, yo + 1); + T cur_mrefs = tex2D(cur, xo, yo - 1); + T cur_mrefs3 = tex2D(cur, xo, yo - 3); + + if (is_field_end) { + dst[yo*dst_pitch+xo] = + filter_intra(cur_prefs3, cur_prefs, cur_mrefs, cur_mrefs3, clip_max); + return; + } + + // Calculate temporal prediction + int is_second_field = !(parity ^ tff); + + cudaTextureObject_t prev2 = prev; + cudaTextureObject_t prev1 = is_second_field ? cur : prev; + cudaTextureObject_t next1 = is_second_field ? next : cur; + cudaTextureObject_t next2 = next; + + T prev2_prefs4 = tex2D(prev2, xo, yo + 4); + T prev2_prefs2 = tex2D(prev2, xo, yo + 2); + T prev2_0 = tex2D(prev2, xo, yo + 0); + T prev2_mrefs2 = tex2D(prev2, xo, yo - 2); + T prev2_mrefs4 = tex2D(prev2, xo, yo - 4); + T prev_prefs = tex2D(prev1, xo, yo + 1); + T prev_mrefs = tex2D(prev1, xo, yo - 1); + T next_prefs = tex2D(next1, xo, yo + 1); + T next_mrefs = tex2D(next1, xo, yo - 1); + T next2_prefs4 = tex2D(next2, xo, yo + 4); + T next2_prefs2 = tex2D(next2, xo, yo + 2); + T next2_0 = tex2D(next2, xo, yo + 0); + T next2_mrefs2 = tex2D(next2, xo, yo - 2); + T next2_mrefs4 = tex2D(next2, xo, yo - 4); + + dst[yo*dst_pitch+xo] = filter(cur_prefs3, cur_prefs, cur_mrefs, cur_mrefs3, + prev2_prefs4, prev2_prefs2, prev2_0, prev2_mrefs2, prev2_mrefs4, + prev_prefs, prev_mrefs, next_prefs, next_mrefs, + next2_prefs4, next2_prefs2, next2_0, next2_mrefs2, next2_mrefs4, + clip_max); +} + +template +__inline__ __device__ void bwdif_double(T *dst, + cudaTextureObject_t prev, + cudaTextureObject_t cur, + cudaTextureObject_t next, + int dst_width, int dst_height, int dst_pitch, + int src_width, int src_height, + int parity, int tff, + int is_field_end, int clip_max) +{ + int xo = blockIdx.x * blockDim.x + threadIdx.x; + int yo = blockIdx.y * blockDim.y + threadIdx.y; + + if (xo >= dst_width || yo >= dst_height) { + return; + } + + if (yo % 2 == parity) { + // Don't modify the primary field + dst[yo*dst_pitch+xo] = tex2D(cur, xo, yo); + return; + } + + T cur_prefs3 = tex2D(cur, xo, yo + 3); + T cur_prefs = tex2D(cur, xo, yo + 1); + T cur_mrefs = tex2D(cur, xo, yo - 1); + T cur_mrefs3 = tex2D(cur, xo, yo - 3); + + if (is_field_end) { + T final; + final.x = filter_intra(cur_prefs3.x, cur_prefs.x, cur_mrefs.x, cur_mrefs3.x, + clip_max); + final.y = filter_intra(cur_prefs3.y, cur_prefs.y, cur_mrefs.y, cur_mrefs3.y, + clip_max); + dst[yo*dst_pitch+xo] = final; + return; + } + + int is_second_field = !(parity ^ tff); + + cudaTextureObject_t prev2 = prev; + cudaTextureObject_t prev1 = is_second_field ? cur : prev; + cudaTextureObject_t next1 = is_second_field ? next : cur; + cudaTextureObject_t next2 = next; + + T prev2_prefs4 = tex2D(prev2, xo, yo + 4); + T prev2_prefs2 = tex2D(prev2, xo, yo + 2); + T prev2_0 = tex2D(prev2, xo, yo + 0); + T prev2_mrefs2 = tex2D(prev2, xo, yo - 2); + T prev2_mrefs4 = tex2D(prev2, xo, yo - 4); + T prev_prefs = tex2D(prev1, xo, yo + 1); + T prev_mrefs = tex2D(prev1, xo, yo - 1); + T next_prefs = tex2D(next1, xo, yo + 1); + T next_mrefs = tex2D(next1, xo, yo - 1); + T next2_prefs4 = tex2D(next2, xo, yo + 4); + T next2_prefs2 = tex2D(next2, xo, yo + 2); + T next2_0 = tex2D(next2, xo, yo + 0); + T next2_mrefs2 = tex2D(next2, xo, yo - 2); + T next2_mrefs4 = tex2D(next2, xo, yo - 4); + + T final; + final.x = filter(cur_prefs3.x, cur_prefs.x, cur_mrefs.x, cur_mrefs3.x, + prev2_prefs4.x, prev2_prefs2.x, prev2_0.x, prev2_mrefs2.x, prev2_mrefs4.x, + prev_prefs.x, prev_mrefs.x, next_prefs.x, next_mrefs.x, + next2_prefs4.x, next2_prefs2.x, next2_0.x, next2_mrefs2.x, next2_mrefs4.x, + clip_max); + final.y = filter(cur_prefs3.y, cur_prefs.y, cur_mrefs.y, cur_mrefs3.y, + prev2_prefs4.y, prev2_prefs2.y, prev2_0.y, prev2_mrefs2.y, prev2_mrefs4.y, + prev_prefs.y, prev_mrefs.y, next_prefs.y, next_mrefs.y, + next2_prefs4.y, next2_prefs2.y, next2_0.y, next2_mrefs2.y, next2_mrefs4.y, + clip_max); + + dst[yo*dst_pitch+xo] = final; +} + +extern "C" { + +__global__ void bwdif_uchar(unsigned char *dst, + cudaTextureObject_t prev, + cudaTextureObject_t cur, + cudaTextureObject_t next, + int dst_width, int dst_height, int dst_pitch, + int src_width, int src_height, + int parity, int tff, int is_field_end, int clip_max) +{ + bwdif_single(dst, prev, cur, next, + dst_width, dst_height, dst_pitch, + src_width, src_height, + parity, tff, is_field_end, clip_max); +} + +__global__ void bwdif_ushort(unsigned short *dst, + cudaTextureObject_t prev, + cudaTextureObject_t cur, + cudaTextureObject_t next, + int dst_width, int dst_height, int dst_pitch, + int src_width, int src_height, + int parity, int tff, int is_field_end, int clip_max) +{ + bwdif_single(dst, prev, cur, next, + dst_width, dst_height, dst_pitch, + src_width, src_height, + parity, tff, is_field_end, clip_max); +} + +__global__ void bwdif_uchar2(uchar2 *dst, + cudaTextureObject_t prev, + cudaTextureObject_t cur, + cudaTextureObject_t next, + int dst_width, int dst_height, int dst_pitch, + int src_width, int src_height, + int parity, int tff, int is_field_end, int clip_max) +{ + bwdif_double(dst, prev, cur, next, + dst_width, dst_height, dst_pitch, + src_width, src_height, + parity, tff, is_field_end, clip_max); +} + +__global__ void bwdif_ushort2(ushort2 *dst, + cudaTextureObject_t prev, + cudaTextureObject_t cur, + cudaTextureObject_t next, + int dst_width, int dst_height, int dst_pitch, + int src_width, int src_height, + int parity, int tff, int is_field_end, int clip_max) +{ + bwdif_double(dst, prev, cur, next, + dst_width, dst_height, dst_pitch, + src_width, src_height, + parity, tff, is_field_end, clip_max); +} + +} /* extern "C" */ diff --git a/libavfilter/vf_bwdif_vulkan.c b/libavfilter/vf_bwdif_vulkan.c new file mode 100644 index 00000000000..db916b22cd0 --- /dev/null +++ b/libavfilter/vf_bwdif_vulkan.c @@ -0,0 +1,418 @@ +/* + * Copyright (c) Lynne + * Copyright (C) 2018 Philip Langdale + * Copyright (C) 2016 Thomas Mundt + * + * 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 "libavutil/random_seed.h" +#include "libavutil/opt.h" +#include "vulkan_filter.h" +#include "vulkan_spirv.h" +#include "yadif.h" +#include "internal.h" + +typedef struct BWDIFVulkanContext { + YADIFContext yadif; + FFVulkanContext vkctx; + + int initialized; + FFVkExecPool e; + FFVkQueueFamilyCtx qf; + VkSampler sampler; + FFVulkanPipeline pl; + FFVkSPIRVShader shd; +} BWDIFVulkanContext; + +typedef struct BWDIFParameters { + int parity; + int tff; + int current_field; +} BWDIFParameters; + +static const char filter_fn[] = { + "const vec4 coef_lf[2] = { vec4(4309), vec4(213), };\n" + "const vec4 coef_hf[3] = { vec4(5570), vec4(3801), vec4(1016) };\n" + "const vec4 coef_sp[2] = { vec4(5077), vec4(981), };\n" + C(0, ) + C(0, vec4 process_intra(vec4 cur[4]) ) + C(0, { ) + C(1, return (coef_sp[0]*(cur[1] + cur[2]) - coef_sp[1]*(cur[0] + cur[3])) / (1 << 13); ) + C(0, } ) + C(0, ) + C(0, vec4 process_line(vec4 prev2[5], vec4 prev1[2], vec4 cur[4], vec4 next1[2], vec4 next2[5]) ) + C(0, { ) + C(1, vec4 fc = cur[1]; ) + C(1, vec4 fe = cur[2]; ) + C(1, vec4 fs = prev2[2] + next2[2]; ) + C(1, vec4 fd = fs / 2; ) + C(0, ) + C(1, vec4 temp_diff[3]; ) + C(1, temp_diff[0] = abs(prev2[2] - next2[2]); ) + C(1, temp_diff[1] = (abs(prev1[0] - fc) + abs(prev1[1] - fe)) / 2; ) + C(1, temp_diff[1] = (abs(next1[0] - fc) + abs(next1[1] - fe)) / 2; ) + C(1, vec4 diff = max(temp_diff[0] / 2, max(temp_diff[1], temp_diff[2])); ) + C(1, bvec4 diff_mask = equal(diff, vec4(0)); ) + C(0, ) + C(1, vec4 fbs = prev2[1] + next2[1]; ) + C(1, vec4 ffs = prev2[3] + next2[3]; ) + C(1, vec4 fb = (fbs / 2) - fc; ) + C(1, vec4 ff = (ffs / 2) - fe; ) + C(1, vec4 dc = fd - fc; ) + C(1, vec4 de = fd - fe; ) + C(1, vec4 mmax = max(de, max(dc, min(fb, ff))); ) + C(1, vec4 mmin = min(de, min(dc, max(fb, ff))); ) + C(1, diff = max(diff, max(mmin, -mmax)); ) + C(0, ) +" vec4 interpolate_all = (((coef_hf[0]*(fs) - coef_hf[1]*(fbs + ffs) +\n" +" coef_hf[2]*(prev2[0] + next2[0] + prev2[4] + next2[4])) / 4) +\n" +" coef_lf[0]*(fc + fe) - coef_lf[1]*(cur[0] + cur[3])) / (1 << 13);\n" +" vec4 interpolate_cur = (coef_sp[0]*(fc + fe) - coef_sp[1]*(cur[0] + cur[3])) / (1 << 13);\n" + C(0, ) + C(1, bvec4 interpolate_cnd1 = greaterThan(abs(fc - fe), temp_diff[0]); ) + C(1, vec4 interpol = mix(interpolate_cur, interpolate_all, interpolate_cnd1); ) + C(1, interpol = clamp(interpol, fd - diff, fd + diff); ) + C(1, return mix(interpol, fd, diff_mask); ) + C(0, } ) +}; + +static av_cold int init_filter(AVFilterContext *ctx) +{ + int err; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; + BWDIFVulkanContext *s = ctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); + FFVkSPIRVShader *shd; + FFVkSPIRVCompiler *spv; + FFVulkanDescriptorSetBinding *desc; + + spv = ff_vk_spirv_init(); + if (!spv) { + av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); + return AVERROR_EXTERNAL; + } + + ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT); + RET(ff_vk_exec_pool_init(vkctx, &s->qf, &s->e, s->qf.nb_queues*4, 0, 0, 0, NULL)); + RET(ff_vk_init_sampler(vkctx, &s->sampler, 1, VK_FILTER_NEAREST)); + RET(ff_vk_shader_init(&s->pl, &s->shd, "bwdif_compute", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); + shd = &s->shd; + + ff_vk_shader_set_compute_sizes(shd, 1, 64, 1); + + desc = (FFVulkanDescriptorSetBinding []) { + { + .name = "prev", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), + }, + { + .name = "cur", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), + }, + { + .name = "next", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), + }, + { + .name = "dst", + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format), + .mem_quali = "writeonly", + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + }, + }; + + RET(ff_vk_pipeline_descriptor_set_add(vkctx, &s->pl, shd, desc, 4, 0, 0)); + + GLSLC(0, layout(push_constant, std430) uniform pushConstants { ); + GLSLC(1, int parity; ); + GLSLC(1, int tff; ); + GLSLC(1, int current_field; ); + GLSLC(0, }; ); + + ff_vk_add_push_constant(&s->pl, 0, sizeof(BWDIFParameters), + VK_SHADER_STAGE_COMPUTE_BIT); + + GLSLD( filter_fn ); + GLSLC(0, void main() ); + GLSLC(0, { ); + GLSLC(1, vec4 res; ); + GLSLC(1, ivec2 size; ); + GLSLC(1, vec4 dcur[4]; ); + GLSLC(1, vec4 prev1[2]; ); + GLSLC(1, vec4 next1[2]; ); + GLSLC(1, vec4 prev2[5]; ); + GLSLC(1, vec4 next2[5]; ); + GLSLC(1, const ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); + GLSLC(1, bool filter_field = ((pos.y ^ parity) & 1) == 1; ); + GLSLF(1, bool is_intra = filter_field && (current_field == %i); ,YADIF_FIELD_END); + GLSLC(1, bool field_parity = (parity ^ tff) != 0; ); + GLSLC(0, ); + + for (int i = 0; i < planes; i++) { + GLSLC(0, ); + GLSLF(1, size = imageSize(dst[%i]); ,i); + GLSLC(1, if (!IS_WITHIN(pos, size)) { ); + GLSLC(2, return; ); + GLSLC(1, } else if (is_intra) { ); + GLSLF(2, dcur[0] = texture(cur[%i], pos - ivec2(0, 3)); ,i); + GLSLF(2, dcur[1] = texture(cur[%i], pos - ivec2(0, 1)); ,i); + GLSLF(2, dcur[2] = texture(cur[%i], pos + ivec2(0, 1)); ,i); + GLSLF(2, dcur[3] = texture(cur[%i], pos + ivec2(0, 3)); ,i); + GLSLC(0, ); + GLSLC(2, res = process_intra(dcur); ); + GLSLF(2, imageStore(dst[%i], pos, res); ,i); + GLSLC(1, } else if (filter_field) { ); + GLSLF(2, dcur[0] = texture(cur[%i], pos - ivec2(0, 3)); ,i); + GLSLF(2, dcur[1] = texture(cur[%i], pos - ivec2(0, 1)); ,i); + GLSLF(2, dcur[2] = texture(cur[%i], pos + ivec2(0, 1)); ,i); + GLSLF(2, dcur[3] = texture(cur[%i], pos + ivec2(0, 3)); ,i); + GLSLC(0, ); + GLSLF(2, prev1[0] = texture(prev[%i], pos - ivec2(0, 1)); ,i); + GLSLF(2, prev1[1] = texture(prev[%i], pos + ivec2(0, 1)); ,i); + GLSLC(0, ); + GLSLF(2, next1[0] = texture(next[%i], pos - ivec2(0, 1)); ,i); + GLSLF(2, next1[1] = texture(next[%i], pos + ivec2(0, 1)); ,i); + GLSLC(0, ); + GLSLC(2, if (field_parity) { ); + GLSLF(3, prev2[0] = texture(prev[%i], pos - ivec2(0, 4)); ,i); + GLSLF(3, prev2[1] = texture(prev[%i], pos - ivec2(0, 2)); ,i); + GLSLF(3, prev2[2] = texture(prev[%i], pos); ,i); + GLSLF(3, prev2[3] = texture(prev[%i], pos + ivec2(0, 2)); ,i); + GLSLF(3, prev2[4] = texture(prev[%i], pos + ivec2(0, 4)); ,i); + GLSLC(0, ); + GLSLF(3, next2[0] = texture(cur[%i], pos - ivec2(0, 4)); ,i); + GLSLF(3, next2[1] = texture(cur[%i], pos - ivec2(0, 2)); ,i); + GLSLF(3, next2[2] = texture(cur[%i], pos); ,i); + GLSLF(3, next2[3] = texture(cur[%i], pos + ivec2(0, 2)); ,i); + GLSLF(3, next2[4] = texture(cur[%i], pos + ivec2(0, 4)); ,i); + GLSLC(2, } else { ); + GLSLF(3, prev2[0] = texture(cur[%i], pos - ivec2(0, 4)); ,i); + GLSLF(3, prev2[1] = texture(cur[%i], pos - ivec2(0, 2)); ,i); + GLSLF(3, prev2[2] = texture(cur[%i], pos); ,i); + GLSLF(3, prev2[3] = texture(cur[%i], pos + ivec2(0, 2)); ,i); + GLSLF(3, prev2[4] = texture(cur[%i], pos + ivec2(0, 4)); ,i); + GLSLC(0, ); + GLSLF(3, next2[0] = texture(next[%i], pos - ivec2(0, 4)); ,i); + GLSLF(3, next2[1] = texture(next[%i], pos - ivec2(0, 2)); ,i); + GLSLF(3, next2[2] = texture(next[%i], pos); ,i); + GLSLF(3, next2[3] = texture(next[%i], pos + ivec2(0, 2)); ,i); + GLSLF(3, next2[4] = texture(next[%i], pos + ivec2(0, 4)); ,i); + GLSLC(2, } ); + GLSLC(0, ); + GLSLC(2, res = process_line(prev2, prev1, dcur, next1, next2); ); + GLSLF(2, imageStore(dst[%i], pos, res); ,i); + GLSLC(1, } else { ); + GLSLF(2, res = texture(cur[%i], pos); ,i); + GLSLF(2, imageStore(dst[%i], pos, res); ,i); + GLSLC(1, } ); + } + + GLSLC(0, } ); + + RET(spv->compile_shader(spv, ctx, &s->shd, &spv_data, &spv_len, "main", + &spv_opaque)); + RET(ff_vk_shader_create(vkctx, &s->shd, spv_data, spv_len, "main")); + + RET(ff_vk_init_compute_pipeline(vkctx, &s->pl, &s->shd)); + RET(ff_vk_exec_pipeline_register(vkctx, &s->e, &s->pl)); + + s->initialized = 1; + + return 0; + +fail: + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); + if (spv) + spv->uninit(&spv); + + return err; +} + +static void bwdif_vulkan_filter_frame(AVFilterContext *ctx, AVFrame *dst, + int parity, int tff) +{ + BWDIFVulkanContext *s = ctx->priv; + YADIFContext *y = &s->yadif; + BWDIFParameters params = { + .parity = parity, + .tff = tff, + .current_field = y->current_field, + }; + + ff_vk_filter_process_Nin(&s->vkctx, &s->e, &s->pl, dst, + (AVFrame *[]){ y->prev, y->cur, y->next }, 3, + s->sampler, ¶ms, sizeof(params)); + + if (y->current_field == YADIF_FIELD_END) + y->current_field = YADIF_FIELD_NORMAL; +} + +static void bwdif_vulkan_uninit(AVFilterContext *avctx) +{ + BWDIFVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + FFVulkanFunctions *vk = &vkctx->vkfn; + + ff_vk_exec_pool_free(vkctx, &s->e); + ff_vk_pipeline_free(vkctx, &s->pl); + ff_vk_shader_free(vkctx, &s->shd); + + if (s->sampler) + vk->DestroySampler(vkctx->hwctx->act_dev, s->sampler, + vkctx->hwctx->alloc); + + ff_vk_uninit(&s->vkctx); + + s->initialized = 0; +} + +static int bwdif_vulkan_config_input(AVFilterLink *inlink) +{ + AVHWFramesContext *input_frames; + AVFilterContext *avctx = inlink->dst; + BWDIFVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + + if (!inlink->hw_frames_ctx) { + av_log(inlink->dst, AV_LOG_ERROR, "Vulkan filtering requires a " + "hardware frames context on the input.\n"); + return AVERROR(EINVAL); + } + + input_frames = (AVHWFramesContext *)inlink->hw_frames_ctx->data; + if (input_frames->format != AV_PIX_FMT_VULKAN) + return AVERROR(EINVAL); + + /* Extract the device and default output format from the first input. */ + if (avctx->inputs[0] != inlink) + return 0; + + /* Save the ref, without reffing it */ + vkctx->input_frames_ref = inlink->hw_frames_ctx; + + /* Defaults */ + vkctx->output_format = input_frames->sw_format; + vkctx->output_width = input_frames->width; + vkctx->output_height = input_frames->height; + + return 0; +} + +static int bwdif_vulkan_config_output(AVFilterLink *outlink) +{ + int err; + AVFilterContext *avctx = outlink->src; + BWDIFVulkanContext *s = avctx->priv; + YADIFContext *y = &s->yadif; + FFVulkanContext *vkctx = &s->vkctx; + + av_buffer_unref(&outlink->hw_frames_ctx); + + err = ff_vk_filter_init_context(avctx, vkctx, vkctx->input_frames_ref, + vkctx->output_width, vkctx->output_height, + vkctx->output_format); + if (err < 0) + return err; + + /* For logging */ + vkctx->class = y->class; + + outlink->hw_frames_ctx = av_buffer_ref(vkctx->frames_ref); + if (!outlink->hw_frames_ctx) + return AVERROR(ENOMEM); + + outlink->time_base = av_mul_q(avctx->inputs[0]->time_base, (AVRational){1, 2}); + outlink->w = vkctx->output_width; + outlink->h = vkctx->output_height; + + if (y->mode & 1) + outlink->frame_rate = av_mul_q(avctx->inputs[0]->frame_rate, + (AVRational){2, 1}); + + if (outlink->w < 4 || outlink->h < 4) { + av_log(avctx, AV_LOG_ERROR, "Video of less than 4 columns or lines is not " + "supported\n"); + return AVERROR(EINVAL); + } + + y->csp = av_pix_fmt_desc_get(vkctx->frames->sw_format); + y->filter = bwdif_vulkan_filter_frame; + + return init_filter(avctx); +} + +static const AVClass bwdif_vulkan_class = { + .class_name = "bwdif_vulkan", + .item_name = av_default_item_name, + .option = ff_yadif_options, + .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_FILTER, +}; + +static const AVFilterPad bwdif_vulkan_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = ff_yadif_filter_frame, + .config_props = &bwdif_vulkan_config_input, + }, +}; + +static const AVFilterPad bwdif_vulkan_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .request_frame = ff_yadif_request_frame, + .config_props = &bwdif_vulkan_config_output, + }, +}; + +const AVFilter ff_vf_bwdif_vulkan = { + .name = "bwdif_vulkan", + .description = NULL_IF_CONFIG_SMALL("Deinterlace Vulkan frames via bwdif"), + .priv_size = sizeof(BWDIFVulkanContext), + .init = &ff_vk_filter_init, + .uninit = &bwdif_vulkan_uninit, + FILTER_INPUTS(bwdif_vulkan_inputs), + FILTER_OUTPUTS(bwdif_vulkan_outputs), + FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), + .priv_class = &bwdif_vulkan_class, + .flags = AVFILTER_FLAG_HWDEVICE | + AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, +}; diff --git a/libavfilter/vf_cas.c b/libavfilter/vf_cas.c index c45529cef7e..5fa5055d761 100644 --- a/libavfilter/vf_cas.c +++ b/libavfilter/vf_cas.c @@ -19,7 +19,6 @@ #include "libavutil/opt.h" #include "libavutil/imgutils.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -255,13 +254,6 @@ static const AVFilterPad cas_inputs[] = { }, }; -static const AVFilterPad cas_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(CASContext, x) #define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -279,7 +271,7 @@ const AVFilter ff_vf_cas = { .priv_size = sizeof(CASContext), .priv_class = &cas_class, FILTER_INPUTS(cas_inputs), - FILTER_OUTPUTS(cas_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_ccrepack.c b/libavfilter/vf_ccrepack.c new file mode 100644 index 00000000000..5213eab82b3 --- /dev/null +++ b/libavfilter/vf_ccrepack.c @@ -0,0 +1,97 @@ +/* + * CEA-708 Closed Caption Repacker + * Copyright (c) 2023 LTN Global Communications + * + * Author: Devin Heitmueller + * + * 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 + */ + +/* + * Repackage CEA-708 arrays, which deals with incorrect cc_count for a given + * output framerate, and incorrect 708 padding. + * + * See CEA CEA-10-A "EIA-708-B Implementation Guidance", Section 26.5 + * "Grouping DTVCC Data Within user_data() Structure" + */ + +#include "avfilter.h" +#include "internal.h" +#include "ccfifo.h" +#include "video.h" +#include "libavutil/opt.h" + +typedef struct CCRepackContext +{ + const AVClass *class; + CCFifo cc_fifo; +} CCRepackContext; + +static const AVOption ccrepack_options[] = { + { NULL } +}; + +AVFILTER_DEFINE_CLASS(ccrepack); + +static int config_input(AVFilterLink *link) +{ + CCRepackContext *ctx = link->dst->priv; + + int ret = ff_ccfifo_init(&ctx->cc_fifo, link->frame_rate, ctx); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Failure to setup CC FIFO queue\n"); + return ret; + } + + return 0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + CCRepackContext *ctx = inlink->dst->priv; + AVFilterLink *outlink = inlink->dst->outputs[0]; + + ff_ccfifo_extract(&ctx->cc_fifo, frame); + ff_ccfifo_inject(&ctx->cc_fifo, frame); + + return ff_filter_frame(outlink, frame); +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + CCRepackContext *s = ctx->priv; + ff_ccfifo_uninit(&s->cc_fifo); +} + +static const AVFilterPad avfilter_vf_ccrepack_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = filter_frame, + .config_props = config_input, + }, +}; + +const AVFilter ff_vf_ccrepack = { + .name = "ccrepack", + .description = NULL_IF_CONFIG_SMALL("Repack CEA-708 closed caption metadata"), + .uninit = uninit, + .priv_size = sizeof(CCRepackContext), + .priv_class = &ccrepack_class, + FILTER_INPUTS(avfilter_vf_ccrepack_inputs), + FILTER_OUTPUTS(ff_video_default_filterpad), +}; diff --git a/libavfilter/vf_chromaber_vulkan.c b/libavfilter/vf_chromaber_vulkan.c index b9423e417e3..8b196f149e3 100644 --- a/libavfilter/vf_chromaber_vulkan.c +++ b/libavfilter/vf_chromaber_vulkan.c @@ -1,4 +1,6 @@ /* + * Copyright (c) Lynne + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -19,21 +21,19 @@ #include "libavutil/random_seed.h" #include "libavutil/opt.h" #include "vulkan_filter.h" +#include "vulkan_spirv.h" #include "internal.h" - -#define CGROUPS (int [3]){ 32, 32, 1 } +#include "video.h" typedef struct ChromaticAberrationVulkanContext { FFVulkanContext vkctx; int initialized; + FFVulkanPipeline pl; + FFVkExecPool e; FFVkQueueFamilyCtx qf; - FFVkExecContext *exec; - FFVulkanPipeline *pl; - - /* Shader updators, must be in the main filter struct */ - VkDescriptorImageInfo input_images[3]; - VkDescriptorImageInfo output_images[3]; + FFVkSPIRVShader shd; + VkSampler sampler; /* Push constants / options */ struct { @@ -59,7 +59,7 @@ static const char distort_chroma_kernel[] = { C(0, { ) C(1, vec2 p = ((vec2(pos)/vec2(size)) - 0.5f)*2.0f; ) C(1, float d = sqrt(p.x*p.x + p.y*p.y); ) - C(1, p *= d / (d* dist); ) + C(1, p *= d / (d*dist); ) C(1, vec4 res = texture(input_img[idx], (p/2.0f) + 0.5f); ) C(1, imageStore(output_img[idx], pos, res); ) C(0, } ) @@ -68,205 +68,102 @@ static const char distort_chroma_kernel[] = { static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) { int err; - FFVkSampler *sampler; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; ChromaticAberrationVulkanContext *s = ctx->priv; FFVulkanContext *vkctx = &s->vkctx; const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); - - ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT, 0); - - /* Create a sampler */ - sampler = ff_vk_init_sampler(vkctx, 0, VK_FILTER_LINEAR); - if (!sampler) - return AVERROR_EXTERNAL; - - s->pl = ff_vk_create_pipeline(vkctx, &s->qf); - if (!s->pl) - return AVERROR(ENOMEM); + FFVkSPIRVShader *shd = &s->shd; + FFVkSPIRVCompiler *spv; + FFVulkanDescriptorSetBinding *desc; /* Normalize options */ s->opts.dist[0] = (s->opts.dist[0] / 100.0f) + 1.0f; s->opts.dist[1] = (s->opts.dist[1] / 100.0f) + 1.0f; - { /* Create the shader */ - FFVulkanDescriptorSetBinding desc_i[2] = { - { - .name = "input_img", - .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .dimensions = 2, - .elems = planes, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->input_images, - .sampler = sampler, - }, - { - .name = "output_img", - .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format), - .mem_quali = "writeonly", - .dimensions = 2, - .elems = planes, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->output_images, - }, - }; - - FFVkSPIRVShader *shd = ff_vk_init_shader(s->pl, "chromaber_compute", - VK_SHADER_STAGE_COMPUTE_BIT); - if (!shd) - return AVERROR(ENOMEM); - - ff_vk_set_compute_shader_sizes(shd, CGROUPS); - - GLSLC(0, layout(push_constant, std430) uniform pushConstants { ); - GLSLC(1, vec2 dist; ); - GLSLC(0, }; ); - GLSLC(0, ); - - ff_vk_add_push_constant(s->pl, 0, sizeof(s->opts), - VK_SHADER_STAGE_COMPUTE_BIT); - - RET(ff_vk_add_descriptor_set(vkctx, s->pl, shd, desc_i, FF_ARRAY_ELEMS(desc_i), 0)); /* set 0 */ - - GLSLD( distort_chroma_kernel ); - GLSLC(0, void main() ); - GLSLC(0, { ); - GLSLC(1, ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); - if (planes == 1) { - GLSLC(1, distort_rgb(imageSize(output_img[0]), pos); ); - } else { - GLSLC(1, ivec2 size = imageSize(output_img[0]); ); - GLSLC(1, vec2 npos = vec2(pos)/vec2(size); ); - GLSLC(1, vec4 res = texture(input_img[0], npos); ); - GLSLC(1, imageStore(output_img[0], pos, res); ); - for (int i = 1; i < planes; i++) { - GLSLC(0, ); - GLSLF(1, size = imageSize(output_img[%i]); ,i); - GLSLC(1, if (IS_WITHIN(pos, size)) { ); - GLSLF(2, distort_chroma(%i, size, pos); ,i); - GLSLC(1, } else { ); - GLSLC(2, npos = vec2(pos)/vec2(size); ); - GLSLF(2, res = texture(input_img[%i], npos); ,i); - GLSLF(2, imageStore(output_img[%i], pos, res); ,i); - GLSLC(1, } ); - } - } - GLSLC(0, } ); + spv = ff_vk_spirv_init(); + if (!spv) { + av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); + return AVERROR_EXTERNAL; + } - RET(ff_vk_compile_shader(vkctx, shd, "main")); + ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT); + RET(ff_vk_exec_pool_init(vkctx, &s->qf, &s->e, s->qf.nb_queues*4, 0, 0, 0, NULL)); + RET(ff_vk_init_sampler(vkctx, &s->sampler, 0, VK_FILTER_LINEAR)); + RET(ff_vk_shader_init(&s->pl, &s->shd, "chromaber_compute", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); + + ff_vk_shader_set_compute_sizes(&s->shd, 32, 32, 1); + + GLSLC(0, layout(push_constant, std430) uniform pushConstants { ); + GLSLC(1, vec2 dist; ); + GLSLC(0, }; ); + GLSLC(0, ); + + ff_vk_add_push_constant(&s->pl, 0, sizeof(s->opts), + VK_SHADER_STAGE_COMPUTE_BIT); + + desc = (FFVulkanDescriptorSetBinding []) { + { + .name = "input_img", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), + }, + { + .name = "output_img", + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format), + .mem_quali = "writeonly", + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + }, + }; + + RET(ff_vk_pipeline_descriptor_set_add(vkctx, &s->pl, shd, desc, 2, 0, 0)); + + GLSLD( distort_chroma_kernel ); + GLSLC(0, void main() ); + GLSLC(0, { ); + GLSLC(1, ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); + if (planes == 1) { + GLSLC(1, distort_rgb(imageSize(output_img[0]), pos); ); + } else { + GLSLC(1, ivec2 size = imageSize(output_img[0]); ); + GLSLC(1, vec2 npos = vec2(pos)/vec2(size); ); + GLSLC(1, vec4 res = texture(input_img[0], npos); ); + GLSLC(1, imageStore(output_img[0], pos, res); ); + for (int i = 1; i < planes; i++) { + GLSLC(0, ); + GLSLF(1, size = imageSize(output_img[%i]); ,i); + GLSLC(1, if (!IS_WITHIN(pos, size)) ); + GLSLC(2, return; ); + GLSLF(1, distort_chroma(%i, size, pos); ,i); + } } + GLSLC(0, } ); - RET(ff_vk_init_pipeline_layout(vkctx, s->pl)); - RET(ff_vk_init_compute_pipeline(vkctx, s->pl)); + RET(spv->compile_shader(spv, ctx, shd, &spv_data, &spv_len, "main", + &spv_opaque)); + RET(ff_vk_shader_create(vkctx, shd, spv_data, spv_len, "main")); - /* Execution context */ - RET(ff_vk_create_exec_ctx(vkctx, &s->exec, &s->qf)); + RET(ff_vk_init_compute_pipeline(vkctx, &s->pl, shd)); + RET(ff_vk_exec_pipeline_register(vkctx, &s->e, &s->pl)); s->initialized = 1; return 0; fail: - return err; -} - -static int process_frames(AVFilterContext *avctx, AVFrame *out_f, AVFrame *in_f) -{ - int err = 0; - VkCommandBuffer cmd_buf; - ChromaticAberrationVulkanContext *s = avctx->priv; - FFVulkanContext *vkctx = &s->vkctx; - FFVulkanFunctions *vk = &vkctx->vkfn; - AVVkFrame *in = (AVVkFrame *)in_f->data[0]; - AVVkFrame *out = (AVVkFrame *)out_f->data[0]; - int planes = av_pix_fmt_count_planes(s->vkctx.output_format); - const VkFormat *input_formats = av_vkfmt_from_pixfmt(s->vkctx.input_format); - const VkFormat *ouput_formats = av_vkfmt_from_pixfmt(s->vkctx.output_format); - - /* Update descriptors and init the exec context */ - ff_vk_start_exec_recording(vkctx, s->exec); - cmd_buf = ff_vk_get_exec_buf(s->exec); - - for (int i = 0; i < planes; i++) { - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->input_images[i].imageView, in->img[i], - input_formats[i], - ff_comp_identity_map)); - - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->output_images[i].imageView, out->img[i], - ouput_formats[i], - ff_comp_identity_map)); - - s->input_images[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - s->output_images[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; - } - - ff_vk_update_descriptor_set(vkctx, s->pl, 0); - - for (int i = 0; i < planes; i++) { - VkImageMemoryBarrier bar[2] = { - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .oldLayout = in->layout[i], - .newLayout = s->input_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = in->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .oldLayout = out->layout[i], - .newLayout = s->output_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = out->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - }; - - vk->CmdPipelineBarrier(cmd_buf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, - 0, NULL, 0, NULL, FF_ARRAY_ELEMS(bar), bar); - - in->layout[i] = bar[0].newLayout; - in->access[i] = bar[0].dstAccessMask; - - out->layout[i] = bar[1].newLayout; - out->access[i] = bar[1].dstAccessMask; - } - - ff_vk_bind_pipeline_exec(vkctx, s->exec, s->pl); - - ff_vk_update_push_exec(vkctx, s->exec, VK_SHADER_STAGE_COMPUTE_BIT, - 0, sizeof(s->opts), &s->opts); - - vk->CmdDispatch(cmd_buf, - FFALIGN(s->vkctx.output_width, CGROUPS[0])/CGROUPS[0], - FFALIGN(s->vkctx.output_height, CGROUPS[1])/CGROUPS[1], 1); - - ff_vk_add_exec_dep(vkctx, s->exec, in_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - ff_vk_add_exec_dep(vkctx, s->exec, out_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - - err = ff_vk_submit_exec_queue(vkctx, s->exec); - if (err) - return err; + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); + if (spv) + spv->uninit(&spv); - ff_vk_qf_rotate(&s->qf); - - return err; - -fail: - ff_vk_discard_exec_deps(s->exec); return err; } @@ -286,7 +183,8 @@ static int chromaber_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) if (!s->initialized) RET(init_filter(ctx, in)); - RET(process_frames(ctx, out, in)); + RET(ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->pl, out, in, + s->sampler, &s->opts, sizeof(s->opts))); err = av_frame_copy_props(out, in); if (err < 0) @@ -305,6 +203,16 @@ static int chromaber_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) static void chromaber_vulkan_uninit(AVFilterContext *avctx) { ChromaticAberrationVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + FFVulkanFunctions *vk = &vkctx->vkfn; + + ff_vk_exec_pool_free(vkctx, &s->e); + ff_vk_pipeline_free(vkctx, &s->pl); + ff_vk_shader_free(vkctx, &s->shd); + + if (s->sampler) + vk->DestroySampler(vkctx->hwctx->act_dev, s->sampler, + vkctx->hwctx->alloc); ff_vk_uninit(&s->vkctx); @@ -349,4 +257,5 @@ const AVFilter ff_vf_chromaber_vulkan = { FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), .priv_class = &chromaber_vulkan_class, .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_chromakey.c b/libavfilter/vf_chromakey.c index fdd610cb19a..c204a583281 100644 --- a/libavfilter/vf_chromakey.c +++ b/libavfilter/vf_chromakey.c @@ -19,12 +19,10 @@ */ #include "libavutil/opt.h" -#include "libavutil/imgutils.h" #include "libavutil/intreadwrite.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" -#include "video.h" typedef struct ChromakeyContext { const AVClass *class; diff --git a/libavfilter/vf_chromakey_cuda.c b/libavfilter/vf_chromakey_cuda.c index 113484279ce..ac644caea7f 100644 --- a/libavfilter/vf_chromakey_cuda.c +++ b/libavfilter/vf_chromakey_cuda.c @@ -20,9 +20,7 @@ #include #include -#include -#include "libavutil/avstring.h" #include "libavutil/common.h" #include "libavutil/hwcontext.h" #include "libavutil/hwcontext_cuda_internal.h" @@ -32,9 +30,7 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" -#include "video.h" #include "cuda/load_helper.h" static const enum AVPixelFormat supported_formats[] = { diff --git a/libavfilter/vf_chromanr.c b/libavfilter/vf_chromanr.c index 36c29ed8cfc..dd49d8670a1 100644 --- a/libavfilter/vf_chromanr.c +++ b/libavfilter/vf_chromanr.c @@ -18,13 +18,11 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/avstring.h" #include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -289,13 +287,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - AVFILTER_DEFINE_CLASS(chromanr); const AVFilter ff_vf_chromanr = { @@ -303,7 +294,7 @@ const AVFilter ff_vf_chromanr = { .description = NULL_IF_CONFIG_SMALL("Reduce chrominance noise."), .priv_size = sizeof(ChromaNRContext), .priv_class = &chromanr_class, - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_INPUTS(inputs), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_chromashift.c b/libavfilter/vf_chromashift.c index 2ddfa617263..39b96a749ee 100644 --- a/libavfilter/vf_chromashift.c +++ b/libavfilter/vf_chromashift.c @@ -18,15 +18,11 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/avstring.h" -#include "libavutil/eval.h" #include "libavutil/imgutils.h" -#include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -382,13 +378,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - static const enum AVPixelFormat yuv_pix_fmts[] = { AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ422P,AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ411P, @@ -411,7 +400,7 @@ const AVFilter ff_vf_chromashift = { .description = NULL_IF_CONFIG_SMALL("Shift chroma."), .priv_size = sizeof(ChromaShiftContext), .priv_class = &chromashift_class, - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_INPUTS(inputs), FILTER_PIXFMTS_ARRAY(yuv_pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, @@ -448,7 +437,7 @@ const AVFilter ff_vf_rgbashift = { .description = NULL_IF_CONFIG_SMALL("Shift RGBA."), .priv_size = sizeof(ChromaShiftContext), .priv_class = &rgbashift_class, - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_INPUTS(inputs), FILTER_PIXFMTS_ARRAY(rgb_pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_ciescope.c b/libavfilter/vf_ciescope.c index a0caaa76e98..eebb3af2812 100644 --- a/libavfilter/vf_ciescope.c +++ b/libavfilter/vf_ciescope.c @@ -855,8 +855,6 @@ rgb_to_xy(float rc, *z = m[2][0] * rc + m[2][1] * gc + m[2][2] * bc; scale = *x + *y + *z; - if (scale == 0.f) - scale = 1.f; scale = 1.f / scale; *x = *x * scale; *y = *y * scale; @@ -1010,27 +1008,44 @@ static void draw_line(uint16_t *const pixels, int linesize, int w, int h, const uint16_t *const rgbcolor) { - int dx = FFABS(x1 - x0), sx = x0 < x1 ? 1 : -1; - int dy = FFABS(y1 - y0), sy = y0 < y1 ? 1 : -1; - int err = (dx > dy ? dx : -dy) / 2, e2; + int sx = x0 < x1 ? 1 : -1, sy = y0 < y1 ? 1 : -1, x2; + int dx = FFABS(x1-x0), dy = FFABS(y1-y0), err = dx * dx + dy * dy; + int e2 = err == 0 ? 1 : 0xffffff / (dx + dy); - for (;;) { - pixels[y0 * linesize + x0 * 4 + 0] = rgbcolor[0]; - pixels[y0 * linesize + x0 * 4 + 1] = rgbcolor[1]; - pixels[y0 * linesize + x0 * 4 + 2] = rgbcolor[2]; - pixels[y0 * linesize + x0 * 4 + 3] = rgbcolor[3]; + dx *= e2; + dy *= e2; + err = dx - dy; - if (x0 == x1 && y0 == y1) - break; + for (;;) { + pixels[y0 * linesize + x0 * 4 + 0] = rgbcolor[0]-(FFABS(err - dx + dy) >> 8); + pixels[y0 * linesize + x0 * 4 + 1] = rgbcolor[1]-(FFABS(err - dx + dy) >> 8); + pixels[y0 * linesize + x0 * 4 + 2] = rgbcolor[2]-(FFABS(err - dx + dy) >> 8); + pixels[y0 * linesize + x0 * 4 + 3] = rgbcolor[3]-(FFABS(err - dx + dy) >> 8); e2 = err; - - if (e2 >-dx) { + x2 = x0; + if (2 * e2 >= -dx) { + if (x0 == x1) + break; + if (e2 + dy < 0xff0000) { + pixels[(y0 + sy) * linesize + x0 * 4 + 0] = rgbcolor[0]-(FFABS(e2 + dy) >> 8); + pixels[(y0 + sy) * linesize + x0 * 4 + 1] = rgbcolor[1]-(FFABS(e2 + dy) >> 8); + pixels[(y0 + sy) * linesize + x0 * 4 + 2] = rgbcolor[2]-(FFABS(e2 + dy) >> 8); + pixels[(y0 + sy) * linesize + x0 * 4 + 3] = rgbcolor[3]-(FFABS(e2 + dy) >> 8); + } err -= dy; x0 += sx; } - if (e2 < dy) { + if (2 * e2 <= dy) { + if (y0 == y1) + break; + if (dx - e2 < 0xff0000) { + pixels[y0 * linesize + (x2 + sx) * 4 + 0] = rgbcolor[0]-(FFABS(dx - e2) >> 8); + pixels[y0 * linesize + (x2 + sx) * 4 + 1] = rgbcolor[1]-(FFABS(dx - e2) >> 8); + pixels[y0 * linesize + (x2 + sx) * 4 + 2] = rgbcolor[2]-(FFABS(dx - e2) >> 8); + pixels[y0 * linesize + (x2 + sx) * 4 + 3] = rgbcolor[3]-(FFABS(dx - e2) >> 8); + } err += dx; y0 += sy; } @@ -1253,11 +1268,11 @@ static void filter_rgb48(AVFilterContext *ctx, const uint8_t *ptr, float *cx, float *cy, int x, int y) { CiescopeContext *s = ctx->priv; - const float scale = 1. / 65535.; + const float scale = 1.f / 65535.f; const uint16_t *src = (const uint16_t*)(ptr + linesize * y + x * 6); - float r = src[0] * scale; - float g = src[1] * scale; - float b = src[2] * scale; + float r = (src[0] + 0.01f) * scale; + float g = (src[1] + 0.01f) * scale; + float b = (src[2] + 0.01f) * scale; float cz; rgb_to_xy(r, g, b, cx, cy, &cz, (const float (*)[3])s->m); @@ -1268,11 +1283,11 @@ static void filter_rgba64(AVFilterContext *ctx, const uint8_t *ptr, float *cx, float *cy, int x, int y) { CiescopeContext *s = ctx->priv; - const float scale = 1. / 65535.; + const float scale = 1.f / 65535.f; const uint16_t *src = (const uint16_t*)(ptr + linesize * y + x * 8); - float r = src[0] * scale; - float g = src[1] * scale; - float b = src[2] * scale; + float r = (src[0] + 0.01f) * scale; + float g = (src[1] + 0.01f) * scale; + float b = (src[2] + 0.01f) * scale; float cz; rgb_to_xy(r, g, b, cx, cy, &cz, (const float (*)[3])s->m); @@ -1283,11 +1298,11 @@ static void filter_rgb24(AVFilterContext *ctx, const uint8_t *ptr, float *cx, float *cy, int x, int y) { CiescopeContext *s = ctx->priv; - const float scale = 1. / 255.; + const float scale = 1.f / 255.f; const uint8_t *src = ptr + linesize * y + x * 3; - float r = src[0] * scale; - float g = src[1] * scale; - float b = src[2] * scale; + float r = (src[0] + 0.01f) * scale; + float g = (src[1] + 0.01f) * scale; + float b = (src[2] + 0.01f) * scale; float cz; rgb_to_xy(r, g, b, cx, cy, &cz, (const float (*)[3])s->m); @@ -1298,11 +1313,11 @@ static void filter_rgba(AVFilterContext *ctx, const uint8_t *ptr, float *cx, float *cy, int x, int y) { CiescopeContext *s = ctx->priv; - const float scale = 1. / 255.; + const float scale = 1.f / 255.f; const uint8_t *src = ptr + linesize * y + x * 4; - float r = src[0] * scale; - float g = src[1] * scale; - float b = src[2] * scale; + float r = (src[0] + 0.01f) * scale; + float g = (src[1] + 0.01f) * scale; + float b = (src[2] + 0.01f) * scale; float cz; rgb_to_xy(r, g, b, cx, cy, &cz, (const float (*)[3])s->m); @@ -1392,6 +1407,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) return AVERROR(ENOMEM); } out->pts = in->pts; + out->duration = in->duration; if (!s->background) { ret = draw_background(ctx); diff --git a/libavfilter/vf_codecview.c b/libavfilter/vf_codecview.c index cddb3e53685..0c1ef21e28c 100644 --- a/libavfilter/vf_codecview.c +++ b/libavfilter/vf_codecview.c @@ -36,6 +36,7 @@ #include "avfilter.h" #include "qp_table.h" #include "internal.h" +#include "video.h" #define MV_P_FOR (1<<0) #define MV_B_FOR (1<<1) @@ -334,19 +335,12 @@ static const AVFilterPad codecview_inputs[] = { }, }; -static const AVFilterPad codecview_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_codecview = { .name = "codecview", .description = NULL_IF_CONFIG_SMALL("Visualize information about some codecs."), .priv_size = sizeof(CodecViewContext), FILTER_INPUTS(codecview_inputs), - FILTER_OUTPUTS(codecview_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), // TODO: we can probably add way more pixel formats without any other // changes; anything with 8-bit luma in first plane should be working FILTER_SINGLE_PIXFMT(AV_PIX_FMT_YUV420P), diff --git a/libavfilter/vf_colorbalance.c b/libavfilter/vf_colorbalance.c index 0e626681b60..676e74c7705 100644 --- a/libavfilter/vf_colorbalance.c +++ b/libavfilter/vf_colorbalance.c @@ -22,7 +22,6 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" diff --git a/libavfilter/vf_colorchannelmixer.c b/libavfilter/vf_colorchannelmixer.c index 88dd7451dcf..884e9846a7c 100644 --- a/libavfilter/vf_colorchannelmixer.c +++ b/libavfilter/vf_colorchannelmixer.c @@ -24,7 +24,6 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "preserve_color.h" diff --git a/libavfilter/vf_colorconstancy.c b/libavfilter/vf_colorconstancy.c index db7e20df530..3d4d433cc19 100644 --- a/libavfilter/vf_colorconstancy.c +++ b/libavfilter/vf_colorconstancy.c @@ -28,14 +28,10 @@ * J. van de Weijer, Th. Gevers, A. Gijsenij "Edge-Based Color Constancy". */ -#include "config_components.h" - #include "libavutil/imgutils.h" #include "libavutil/opt.h" -#include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -719,15 +715,6 @@ static const AVFilterPad colorconstancy_inputs[] = { }, }; -static const AVFilterPad colorconstancy_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - -#if CONFIG_GREYEDGE_FILTER - static const AVOption greyedge_options[] = { { "difford", "set differentiation order", OFFSET(difford), AV_OPT_TYPE_INT, {.i64=1}, 0, 2, FLAGS }, { "minknorm", "set Minkowski norm", OFFSET(minknorm), AV_OPT_TYPE_INT, {.i64=1}, 0, 20, FLAGS }, @@ -744,11 +731,9 @@ const AVFilter ff_vf_greyedge = { .priv_class = &greyedge_class, .uninit = uninit, FILTER_INPUTS(colorconstancy_inputs), - FILTER_OUTPUTS(colorconstancy_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), // TODO: support more formats // FIXME: error when saving to .jpg FILTER_SINGLE_PIXFMT(AV_PIX_FMT_GBRP), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, }; - -#endif /* CONFIG_GREY_EDGE_FILTER */ diff --git a/libavfilter/vf_colorcontrast.c b/libavfilter/vf_colorcontrast.c index 7561d21259e..f05701c1a55 100644 --- a/libavfilter/vf_colorcontrast.c +++ b/libavfilter/vf_colorcontrast.c @@ -21,10 +21,9 @@ #include #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -359,13 +358,6 @@ static const AVFilterPad colorcontrast_inputs[] = { }, }; -static const AVFilterPad colorcontrast_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(ColorContrastContext, x) #define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -388,7 +380,7 @@ const AVFilter ff_vf_colorcontrast = { .priv_size = sizeof(ColorContrastContext), .priv_class = &colorcontrast_class, FILTER_INPUTS(colorcontrast_inputs), - FILTER_OUTPUTS(colorcontrast_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_colorcorrect.c b/libavfilter/vf_colorcorrect.c index ee97b62b0e2..0f2fe33c0f8 100644 --- a/libavfilter/vf_colorcorrect.c +++ b/libavfilter/vf_colorcorrect.c @@ -21,9 +21,8 @@ #include #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -510,13 +509,6 @@ static const AVFilterPad colorcorrect_inputs[] = { }, }; -static const AVFilterPad colorcorrect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(ColorCorrectContext, x) #define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -543,7 +535,7 @@ const AVFilter ff_vf_colorcorrect = { .priv_class = &colorcorrect_class, .uninit = uninit, FILTER_INPUTS(colorcorrect_inputs), - FILTER_OUTPUTS(colorcorrect_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_colorize.c b/libavfilter/vf_colorize.c index ba7b80dae77..ed14be9459c 100644 --- a/libavfilter/vf_colorize.c +++ b/libavfilter/vf_colorize.c @@ -17,9 +17,8 @@ */ #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -260,13 +259,6 @@ static const AVFilterPad colorize_inputs[] = { }, }; -static const AVFilterPad colorize_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(ColorizeContext, x) #define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -286,7 +278,7 @@ const AVFilter ff_vf_colorize = { .priv_size = sizeof(ColorizeContext), .priv_class = &colorize_class, FILTER_INPUTS(colorize_inputs), - FILTER_OUTPUTS(colorize_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_colorkey.c b/libavfilter/vf_colorkey.c index 9979f4ad0da..58dd513b31e 100644 --- a/libavfilter/vf_colorkey.c +++ b/libavfilter/vf_colorkey.c @@ -21,12 +21,10 @@ #include "config_components.h" #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" -#include "video.h" typedef struct ColorkeyContext { const AVClass *class; diff --git a/libavfilter/vf_colorkey_opencl.c b/libavfilter/vf_colorkey_opencl.c index 84be1999a89..72a36df2d33 100644 --- a/libavfilter/vf_colorkey_opencl.c +++ b/libavfilter/vf_colorkey_opencl.c @@ -17,9 +17,7 @@ */ #include "libavutil/opt.h" -#include "libavutil/imgutils.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "opencl.h" #include "opencl_source.h" @@ -52,7 +50,7 @@ static int colorkey_opencl_init(AVFilterContext *avctx) cl_int cle; int err; - err = ff_opencl_filter_load_program(avctx, &ff_opencl_source_colorkey, 1); + err = ff_opencl_filter_load_program(avctx, &ff_source_colorkey_cl, 1); if (err < 0) goto fail; @@ -238,5 +236,6 @@ const AVFilter ff_vf_colorkey_opencl = { FILTER_INPUTS(colorkey_opencl_inputs), FILTER_OUTPUTS(colorkey_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), - .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_colorlevels.c b/libavfilter/vf_colorlevels.c index 76adfbd8c8d..e1349c13a4e 100644 --- a/libavfilter/vf_colorlevels.c +++ b/libavfilter/vf_colorlevels.c @@ -18,12 +18,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "preserve_color.h" @@ -97,11 +95,13 @@ typedef struct ThreadData { int h; + float fimin[4]; + float fomin[4]; int imin[4]; int omin[4]; } ThreadData; -#define DO_COMMON(type, clip, preserve, planar) \ +#define DO_COMMON(type, ptype, clip, preserve, planar) \ const ThreadData *td = arg; \ const int linesize = s->linesize; \ const int step = s->step; \ @@ -118,14 +118,14 @@ typedef struct ThreadData { type *dst_g = (type *)(td->dstrow[G]) + src_linesize * slice_start; \ type *dst_b = (type *)(td->dstrow[B]) + src_linesize * slice_start; \ type *dst_a = (type *)(td->dstrow[A]) + src_linesize * slice_start; \ - const int imin_r = td->imin[R]; \ - const int imin_g = td->imin[G]; \ - const int imin_b = td->imin[B]; \ - const int imin_a = td->imin[A]; \ - const int omin_r = td->omin[R]; \ - const int omin_g = td->omin[G]; \ - const int omin_b = td->omin[B]; \ - const int omin_a = td->omin[A]; \ + const ptype imin_r = s->depth == 32 ? td->fimin[R] : td->imin[R]; \ + const ptype imin_g = s->depth == 32 ? td->fimin[G] : td->imin[G]; \ + const ptype imin_b = s->depth == 32 ? td->fimin[B] : td->imin[B]; \ + const ptype imin_a = s->depth == 32 ? td->fimin[A] : td->imin[A]; \ + const ptype omin_r = s->depth == 32 ? td->fomin[R] : td->omin[R]; \ + const ptype omin_g = s->depth == 32 ? td->fomin[G] : td->omin[G]; \ + const ptype omin_b = s->depth == 32 ? td->fomin[B] : td->omin[B]; \ + const ptype omin_a = s->depth == 32 ? td->fomin[A] : td->omin[A]; \ const float coeff_r = td->coeff[R]; \ const float coeff_g = td->coeff[G]; \ const float coeff_b = td->coeff[B]; \ @@ -133,12 +133,12 @@ typedef struct ThreadData { \ for (int y = slice_start; y < slice_end; y++) { \ for (int x = 0; x < linesize; x += step) { \ - int ir, ig, ib, or, og, ob; \ + ptype ir, ig, ib, or, og, ob; \ ir = src_r[x]; \ ig = src_g[x]; \ ib = src_b[x]; \ if (preserve) { \ - float ratio, icolor, ocolor, max = s->max; \ + float ratio, icolor, ocolor, max = s->depth==32 ? 1.f : s->max; \ \ or = (ir - imin_r) * coeff_r + omin_r; \ og = (ig - imin_g) * coeff_g + omin_g; \ @@ -180,39 +180,40 @@ typedef struct ThreadData { #define CLIP8(x, depth) av_clip_uint8(x) #define CLIP16(x, depth) av_clip_uint16(x) +#define NOCLIP(x, depth) (x) static int colorlevels_slice_8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { ColorLevelsContext *s = ctx->priv; - DO_COMMON(uint8_t, CLIP8, 0, 0) + DO_COMMON(uint8_t, int, CLIP8, 0, 0) return 0; } static int colorlevels_slice_16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { ColorLevelsContext *s = ctx->priv;\ - DO_COMMON(uint16_t, CLIP16, 0, 0) + DO_COMMON(uint16_t, int, CLIP16, 0, 0) return 0; } static int colorlevels_preserve_slice_8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { ColorLevelsContext *s = ctx->priv; - DO_COMMON(uint8_t, CLIP8, 1, 0) + DO_COMMON(uint8_t, int, CLIP8, 1, 0) return 0; } static int colorlevels_preserve_slice_16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { ColorLevelsContext *s = ctx->priv; - DO_COMMON(uint16_t, CLIP16, 1, 0) + DO_COMMON(uint16_t, int, CLIP16, 1, 0) return 0; } static int colorlevels_slice_8_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { ColorLevelsContext *s = ctx->priv; - DO_COMMON(uint8_t, CLIP8, 0, 1) + DO_COMMON(uint8_t, int, CLIP8, 0, 1) return 0; } @@ -220,7 +221,7 @@ static int colorlevels_slice_9_planar(AVFilterContext *ctx, void *arg, int jobnr { ColorLevelsContext *s = ctx->priv; const int depth = 9; - DO_COMMON(uint16_t, av_clip_uintp2, 0, 1) + DO_COMMON(uint16_t, int, av_clip_uintp2, 0, 1) return 0; } @@ -228,7 +229,7 @@ static int colorlevels_slice_10_planar(AVFilterContext *ctx, void *arg, int jobn { ColorLevelsContext *s = ctx->priv; const int depth = 10; - DO_COMMON(uint16_t, av_clip_uintp2, 0, 1) + DO_COMMON(uint16_t, int, av_clip_uintp2, 0, 1) return 0; } @@ -236,7 +237,7 @@ static int colorlevels_slice_12_planar(AVFilterContext *ctx, void *arg, int jobn { ColorLevelsContext *s = ctx->priv; const int depth = 12; - DO_COMMON(uint16_t, av_clip_uintp2, 0, 1) + DO_COMMON(uint16_t, int, av_clip_uintp2, 0, 1) return 0; } @@ -244,21 +245,28 @@ static int colorlevels_slice_14_planar(AVFilterContext *ctx, void *arg, int jobn { ColorLevelsContext *s = ctx->priv; const int depth = 14; - DO_COMMON(uint16_t, av_clip_uintp2, 0, 1) + DO_COMMON(uint16_t, int, av_clip_uintp2, 0, 1) return 0; } static int colorlevels_slice_16_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { ColorLevelsContext *s = ctx->priv; - DO_COMMON(uint16_t, CLIP16, 0, 1) + DO_COMMON(uint16_t, int, CLIP16, 0, 1) + return 0; +} + +static int colorlevels_slice_32_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + ColorLevelsContext *s = ctx->priv; + DO_COMMON(float, float, NOCLIP, 0, 1) return 0; } static int colorlevels_preserve_slice_8_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { ColorLevelsContext *s = ctx->priv; - DO_COMMON(uint8_t, CLIP8, 1, 1) + DO_COMMON(uint8_t, int, CLIP8, 1, 1) return 0; } @@ -266,7 +274,7 @@ static int colorlevels_preserve_slice_9_planar(AVFilterContext *ctx, void *arg, { ColorLevelsContext *s = ctx->priv; const int depth = 9; - DO_COMMON(uint16_t, av_clip_uintp2, 1, 1) + DO_COMMON(uint16_t, int, av_clip_uintp2, 1, 1) return 0; } @@ -274,7 +282,7 @@ static int colorlevels_preserve_slice_10_planar(AVFilterContext *ctx, void *arg, { ColorLevelsContext *s = ctx->priv; const int depth = 10; - DO_COMMON(uint16_t, av_clip_uintp2, 1, 1) + DO_COMMON(uint16_t, int, av_clip_uintp2, 1, 1) return 0; } @@ -282,7 +290,7 @@ static int colorlevels_preserve_slice_12_planar(AVFilterContext *ctx, void *arg, { ColorLevelsContext *s = ctx->priv; const int depth = 12; - DO_COMMON(uint16_t, av_clip_uintp2, 1, 1) + DO_COMMON(uint16_t, int, av_clip_uintp2, 1, 1) return 0; } @@ -290,14 +298,21 @@ static int colorlevels_preserve_slice_14_planar(AVFilterContext *ctx, void *arg, { ColorLevelsContext *s = ctx->priv; const int depth = 14; - DO_COMMON(uint16_t, av_clip_uintp2, 1, 1) + DO_COMMON(uint16_t, int, av_clip_uintp2, 1, 1) return 0; } static int colorlevels_preserve_slice_16_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { ColorLevelsContext *s = ctx->priv; - DO_COMMON(uint16_t, CLIP16, 1, 1) + DO_COMMON(uint16_t, int, CLIP16, 1, 1) + return 0; +} + +static int colorlevels_preserve_slice_32_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + ColorLevelsContext *s = ctx->priv; + DO_COMMON(float, float, NOCLIP, 1, 1) return 0; } @@ -349,6 +364,10 @@ static int config_input(AVFilterLink *inlink) s->colorlevels_slice[0] = colorlevels_slice_16_planar; s->colorlevels_slice[1] = colorlevels_preserve_slice_16_planar; break; + case 32: + s->colorlevels_slice[0] = colorlevels_slice_32_planar; + s->colorlevels_slice[1] = colorlevels_preserve_slice_32_planar; + break; } } @@ -479,6 +498,46 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) td.omin[i] = omin; } break; + case 4: + for (int i = 0; i < s->nb_comp; i++) { + Range *r = &s->range[i]; + const uint8_t offset = s->rgba_map[i]; + const uint8_t *srcrow = in->data[0]; + float imin = r->in_min; + float imax = r->in_max; + float omin = r->out_min; + float omax = r->out_max; + float coeff; + + if (imin < 0.f) { + imin = 1.f; + for (int y = 0; y < inlink->h; y++) { + const float *src = (const float *)srcrow; + + for (int x = 0; x < s->linesize; x += step) + imin = fminf(imin, src[x + offset]); + srcrow += in->linesize[0]; + } + } + if (imax < 0.f) { + srcrow = in->data[0]; + imax = 0.f; + for (int y = 0; y < inlink->h; y++) { + const float *src = (const float *)srcrow; + + for (int x = 0; x < s->linesize; x += step) + imax = fmaxf(imax, src[x + offset]); + srcrow += in->linesize[0]; + } + } + + coeff = (omax - omin) / (double)(imax - imin); + + td.coeff[i] = coeff; + td.fimin[i] = imin; + td.fomin[i] = omin; + } + break; } ff_filter_execute(ctx, s->colorlevels_slice[s->preserve_color > 0], &td, NULL, @@ -498,20 +557,13 @@ static const AVFilterPad colorlevels_inputs[] = { }, }; -static const AVFilterPad colorlevels_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_colorlevels = { .name = "colorlevels", .description = NULL_IF_CONFIG_SMALL("Adjust the color levels."), .priv_size = sizeof(ColorLevelsContext), .priv_class = &colorlevels_class, FILTER_INPUTS(colorlevels_inputs), - FILTER_OUTPUTS(colorlevels_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS(AV_PIX_FMT_0RGB, AV_PIX_FMT_0BGR, AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR, AV_PIX_FMT_RGB0, AV_PIX_FMT_BGR0, @@ -524,7 +576,8 @@ const AVFilter ff_vf_colorlevels = { AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRP14, - AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP16), + AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP16, + AV_PIX_FMT_GBRPF32, AV_PIX_FMT_GBRAPF32), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, }; diff --git a/libavfilter/vf_colormatrix.c b/libavfilter/vf_colormatrix.c index bee80c69cc8..81254b0463a 100644 --- a/libavfilter/vf_colormatrix.c +++ b/libavfilter/vf_colormatrix.c @@ -30,12 +30,10 @@ #include #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" -#include "libavutil/avstring.h" #define NS(n) ((n) < 0 ? (int)((n)*65536.0-0.5+DBL_EPSILON) : (int)((n)*65536.0+0.5)) #define CB(n) av_clip_uint8(n) @@ -483,20 +481,13 @@ static const AVFilterPad colormatrix_inputs[] = { }, }; -static const AVFilterPad colormatrix_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_colormatrix = { .name = "colormatrix", .description = NULL_IF_CONFIG_SMALL("Convert color matrix."), .priv_size = sizeof(ColorMatrixContext), .init = init, FILTER_INPUTS(colormatrix_inputs), - FILTER_OUTPUTS(colormatrix_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS(AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P, diff --git a/libavfilter/vf_colorspace.c b/libavfilter/vf_colorspace.c index 21916b7b7af..852dc11c6c7 100644 --- a/libavfilter/vf_colorspace.c +++ b/libavfilter/vf_colorspace.c @@ -572,15 +572,15 @@ static int create_filtergraph(AVFilterContext *ctx, if (s->out_csp == AVCOL_SPC_UNSPECIFIED) { if (s->user_all == CS_UNSPECIFIED) { av_log(ctx, AV_LOG_ERROR, - "Please specify output transfer characteristics\n"); + "Please specify output colorspace\n"); } else { av_log(ctx, AV_LOG_ERROR, "Unsupported output color property %d\n", s->user_all); } } else { av_log(ctx, AV_LOG_ERROR, - "Unsupported output transfer characteristics %d (%s)\n", - s->out_csp, av_color_space_name(s->out_csp)); + "Unsupported output colorspace %d (%s)\n", s->out_csp, + av_color_space_name(s->out_csp)); } return AVERROR(EINVAL); } diff --git a/libavfilter/vf_colorspace_cuda.c b/libavfilter/vf_colorspace_cuda.c index 07d4edd0d87..09fd4dd778c 100644 --- a/libavfilter/vf_colorspace_cuda.c +++ b/libavfilter/vf_colorspace_cuda.c @@ -22,7 +22,6 @@ #include -#include "libavutil/avstring.h" #include "libavutil/common.h" #include "libavutil/cuda_check.h" #include "libavutil/hwcontext.h" @@ -32,10 +31,7 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" -#include "scale_eval.h" -#include "video.h" #include "cuda/load_helper.h" diff --git a/libavfilter/vf_colortemperature.c b/libavfilter/vf_colortemperature.c index e6ac5f95c71..bac6743a345 100644 --- a/libavfilter/vf_colortemperature.c +++ b/libavfilter/vf_colortemperature.c @@ -21,10 +21,9 @@ #include #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -325,13 +324,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(ColorTemperatureContext, x) #define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -350,7 +342,7 @@ const AVFilter ff_vf_colortemperature = { .priv_size = sizeof(ColorTemperatureContext), .priv_class = &colortemperature_class, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_convolution.c b/libavfilter/vf_convolution.c index 7762fa2a053..c1a63c9aa95 100644 --- a/libavfilter/vf_convolution.c +++ b/libavfilter/vf_convolution.c @@ -29,7 +29,6 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" #include "convolution.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -873,13 +872,6 @@ static const AVFilterPad convolution_inputs[] = { }, }; -static const AVFilterPad convolution_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #if CONFIG_CONVOLUTION_FILTER const AVFilter ff_vf_convolution = { @@ -888,7 +880,7 @@ const AVFilter ff_vf_convolution = { .priv_size = sizeof(ConvolutionContext), .priv_class = &convolution_class, FILTER_INPUTS(convolution_inputs), - FILTER_OUTPUTS(convolution_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, @@ -914,7 +906,7 @@ const AVFilter ff_vf_prewitt = { .priv_size = sizeof(ConvolutionContext), .priv_class = &common_class, FILTER_INPUTS(convolution_inputs), - FILTER_OUTPUTS(convolution_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, @@ -930,7 +922,7 @@ const AVFilter ff_vf_sobel = { .priv_size = sizeof(ConvolutionContext), .priv_class = &common_class, FILTER_INPUTS(convolution_inputs), - FILTER_OUTPUTS(convolution_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, @@ -946,7 +938,7 @@ const AVFilter ff_vf_roberts = { .priv_size = sizeof(ConvolutionContext), .priv_class = &common_class, FILTER_INPUTS(convolution_inputs), - FILTER_OUTPUTS(convolution_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, @@ -962,7 +954,7 @@ const AVFilter ff_vf_kirsch = { .priv_size = sizeof(ConvolutionContext), .priv_class = &common_class, FILTER_INPUTS(convolution_inputs), - FILTER_OUTPUTS(convolution_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, @@ -978,7 +970,7 @@ const AVFilter ff_vf_scharr = { .priv_size = sizeof(ConvolutionContext), .priv_class = &common_class, FILTER_INPUTS(convolution_inputs), - FILTER_OUTPUTS(convolution_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, diff --git a/libavfilter/vf_convolution_opencl.c b/libavfilter/vf_convolution_opencl.c index de3d38b553b..0eff9f40d3b 100644 --- a/libavfilter/vf_convolution_opencl.c +++ b/libavfilter/vf_convolution_opencl.c @@ -62,7 +62,7 @@ static int convolution_opencl_init(AVFilterContext *avctx) cl_int cle; int err; - err = ff_opencl_filter_load_program(avctx, &ff_opencl_source_convolution, 1); + err = ff_opencl_filter_load_program(avctx, &ff_source_convolution_cl, 1); if (err < 0) goto fail; @@ -373,6 +373,7 @@ const AVFilter ff_vf_convolution_opencl = { FILTER_OUTPUTS(convolution_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; #endif /* CONFIG_CONVOLUTION_OPENCL_FILTER */ @@ -399,6 +400,7 @@ const AVFilter ff_vf_sobel_opencl = { FILTER_OUTPUTS(convolution_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; #endif /* CONFIG_SOBEL_OPENCL_FILTER */ @@ -425,6 +427,7 @@ const AVFilter ff_vf_prewitt_opencl = { FILTER_OUTPUTS(convolution_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; #endif /* CONFIG_PREWITT_OPENCL_FILTER */ @@ -451,6 +454,7 @@ const AVFilter ff_vf_roberts_opencl = { FILTER_OUTPUTS(convolution_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; #endif /* CONFIG_ROBERTS_OPENCL_FILTER */ diff --git a/libavfilter/vf_convolve.c b/libavfilter/vf_convolve.c index d9f4c4bd772..3246868888e 100644 --- a/libavfilter/vf_convolve.c +++ b/libavfilter/vf_convolve.c @@ -22,16 +22,13 @@ #include -#include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libavutil/tx.h" #include "avfilter.h" -#include "formats.h" #include "framesync.h" #include "internal.h" -#include "video.h" #define MAX_THREADS 16 diff --git a/libavfilter/vf_copy.c b/libavfilter/vf_copy.c index 2fbced354f5..52ac9fb0ec0 100644 --- a/libavfilter/vf_copy.c +++ b/libavfilter/vf_copy.c @@ -24,6 +24,7 @@ #include "libavutil/imgutils.h" #include "libavutil/internal.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" #include "video.h" @@ -65,18 +66,11 @@ static const AVFilterPad avfilter_vf_copy_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_copy_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_copy = { .name = "copy", .description = NULL_IF_CONFIG_SMALL("Copy the input video unchanged to the output."), .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(avfilter_vf_copy_inputs), - FILTER_OUTPUTS(avfilter_vf_copy_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/vf_coreimage.m b/libavfilter/vf_coreimage.m index b1959861ded..979eab3b01a 100644 --- a/libavfilter/vf_coreimage.m +++ b/libavfilter/vf_coreimage.m @@ -301,8 +301,14 @@ static int request_frame(AVFilterLink *link) frame->pts = ctx->pts; frame->duration = 1; +#if FF_API_FRAME_KEY frame->key_frame = 1; +#endif + frame->flags |= AV_FRAME_FLAG_KEY; +#if FF_API_INTERLACED_FRAME frame->interlaced_frame = 0; +#endif + frame->flags &= ~AV_FRAME_FLAG_INTERLACED; frame->pict_type = AV_PICTURE_TYPE_I; frame->sample_aspect_ratio = ctx->sar; diff --git a/libavfilter/vf_corr.c b/libavfilter/vf_corr.c index 65d8b09767c..fb2770539ed 100644 --- a/libavfilter/vf_corr.c +++ b/libavfilter/vf_corr.c @@ -26,10 +26,8 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "framesync.h" #include "internal.h" -#include "video.h" typedef struct CorrContext { const AVClass *class; diff --git a/libavfilter/vf_cover_rect.c b/libavfilter/vf_cover_rect.c index 01c9f2abbb6..7f343115892 100644 --- a/libavfilter/vf_cover_rect.c +++ b/libavfilter/vf_cover_rect.c @@ -24,7 +24,9 @@ #include "libavutil/imgutils.h" #include "libavutil/opt.h" +#include "filters.h" #include "internal.h" +#include "video.h" #include "lavfutils.h" @@ -125,7 +127,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) AVFilterContext *ctx = inlink->dst; CoverContext *cover = ctx->priv; AVDictionaryEntry *ex, *ey, *ew, *eh; - int x = -1, y = -1, w = -1, h = -1; + int ret, x = -1, y = -1, w = -1, h = -1; char *xendptr = NULL, *yendptr = NULL, *wendptr = NULL, *hendptr = NULL; ex = av_dict_get(in->metadata, "lavfi.rect.x", NULL, AV_DICT_MATCH_CASE); @@ -170,7 +172,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) x = av_clip(x, 0, in->width - w); y = av_clip(y, 0, in->height - h); - av_frame_make_writable(in); + ret = ff_inlink_make_frame_writable(inlink, &in); + if (ret < 0) { + av_frame_free(&in); + return ret; + } if (cover->mode == MODE_BLUR) { blur (cover, in, x, y); @@ -227,13 +233,6 @@ static const AVFilterPad cover_rect_inputs[] = { }, }; -static const AVFilterPad cover_rect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_cover_rect = { .name = "cover_rect", .description = NULL_IF_CONFIG_SMALL("Find and cover a user specified object."), @@ -241,7 +240,7 @@ const AVFilter ff_vf_cover_rect = { .init = init, .uninit = uninit, FILTER_INPUTS(cover_rect_inputs), - FILTER_OUTPUTS(cover_rect_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS(AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVJ420P), .priv_class = &cover_rect_class, }; diff --git a/libavfilter/vf_crop.c b/libavfilter/vf_crop.c index c7cbfa51ef9..6361209941a 100644 --- a/libavfilter/vf_crop.c +++ b/libavfilter/vf_crop.c @@ -50,7 +50,9 @@ static const char *const var_names[] = { "x", "y", "n", ///< number of frame +#if FF_API_FRAME_PKT "pos", ///< position in the file +#endif "t", ///< timestamp expressed in seconds NULL }; @@ -68,7 +70,9 @@ enum var_name { VAR_X, VAR_Y, VAR_N, +#if FF_API_FRAME_PKT VAR_POS, +#endif VAR_T, VAR_VARS_NB }; @@ -145,7 +149,9 @@ static int config_input(AVFilterLink *link) s->var_values[VAR_OUT_H] = s->var_values[VAR_OH] = NAN; s->var_values[VAR_N] = 0; s->var_values[VAR_T] = NAN; +#if FF_API_FRAME_PKT s->var_values[VAR_POS] = NAN; +#endif av_image_fill_max_pixsteps(s->max_step, NULL, pix_desc); @@ -257,8 +263,12 @@ static int filter_frame(AVFilterLink *link, AVFrame *frame) s->var_values[VAR_N] = link->frame_count_out; s->var_values[VAR_T] = frame->pts == AV_NOPTS_VALUE ? NAN : frame->pts * av_q2d(link->time_base); +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS s->var_values[VAR_POS] = frame->pkt_pos == -1 ? NAN : frame->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, NULL); s->var_values[VAR_Y] = av_expr_eval(s->y_pexpr, s->var_values, NULL); /* It is necessary if x is expressed from y */ @@ -280,8 +290,8 @@ static int filter_frame(AVFilterLink *link, AVFrame *frame) s->y &= ~((1 << s->vsub) - 1); } - av_log(ctx, AV_LOG_TRACE, "n:%d t:%f pos:%f x:%d y:%d x+w:%d y+h:%d\n", - (int)s->var_values[VAR_N], s->var_values[VAR_T], s->var_values[VAR_POS], + av_log(ctx, AV_LOG_TRACE, "n:%d t:%f x:%d y:%d x+w:%d y+h:%d\n", + (int)s->var_values[VAR_N], s->var_values[VAR_T], s->x, s->y, s->x+s->w, s->y+s->h); if (desc->flags & AV_PIX_FMT_FLAG_HWACCEL) { diff --git a/libavfilter/vf_cropdetect.c b/libavfilter/vf_cropdetect.c index 7e985fb2717..58755fc3567 100644 --- a/libavfilter/vf_cropdetect.c +++ b/libavfilter/vf_cropdetect.c @@ -30,7 +30,6 @@ #include "libavutil/qsort.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "edge_common.h" @@ -39,6 +38,7 @@ typedef struct CropDetectContext { const AVClass *class; int x1, y1, x2, y2; float limit; + float limit_upscaled; int round; int skip; int reset_count; @@ -48,6 +48,7 @@ typedef struct CropDetectContext { int mode; int window_size; int mv_threshold; + int bitdepth; float low, high; uint8_t low_u8, high_u8; uint8_t *filterbuf; @@ -207,8 +208,12 @@ static int config_input(AVFilterLink *inlink) av_image_fill_max_pixsteps(s->max_pixsteps, NULL, desc); + s->bitdepth = desc->comp[0].depth; + if (s->limit < 1.0) - s->limit *= (1 << desc->comp[0].depth) - 1; + s->limit_upscaled = s->limit * ((1 << s->bitdepth) - 1); + else + s->limit_upscaled = s->limit; s->x1 = inlink->w - 1; s->y1 = inlink->h - 1; @@ -243,7 +248,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) int w, h, x, y, shrink_by, i; AVDictionary **metadata; int outliers, last_y; - int limit = lrint(s->limit); + int limit_upscaled = lrint(s->limit_upscaled); + char limit_str[22]; const int inw = inlink->w; const int inh = inlink->h; @@ -278,7 +284,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) #define FIND(DST, FROM, NOEND, INC, STEP0, STEP1, LEN) \ outliers = 0;\ for (last_y = y = FROM; NOEND; y = y INC) {\ - if (checkline(ctx, frame->data[0] + STEP0 * y, STEP1, LEN, bpp) > limit) {\ + if (checkline(ctx, frame->data[0] + STEP0 * y, STEP1, LEN, bpp) > limit_upscaled) {\ if (++outliers > s->max_outliers) { \ DST = last_y;\ break;\ @@ -423,21 +429,46 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) SET_META("lavfi.cropdetect.x", x); SET_META("lavfi.cropdetect.y", y); + snprintf(limit_str, sizeof(limit_str), "%f", s->limit); + av_dict_set(metadata, "lavfi.cropdetect.limit", limit_str, 0); + av_log(ctx, AV_LOG_INFO, - "x1:%d x2:%d y1:%d y2:%d w:%d h:%d x:%d y:%d pts:%"PRId64" t:%f crop=%d:%d:%d:%d\n", + "x1:%d x2:%d y1:%d y2:%d w:%d h:%d x:%d y:%d pts:%"PRId64" t:%f limit:%f crop=%d:%d:%d:%d\n", s->x1, s->x2, s->y1, s->y2, w, h, x, y, frame->pts, frame->pts == AV_NOPTS_VALUE ? -1 : frame->pts * av_q2d(inlink->time_base), - w, h, x, y); + s->limit, w, h, x, y); } return ff_filter_frame(inlink->dst->outputs[0], frame); } +static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, + char *res, int res_len, int flags) +{ + CropDetectContext *s = ctx->priv; + float old_limit = s->limit; + int ret; + + if ((ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags)) < 0) + return ret; + + if (old_limit != s->limit) { + if (s->limit < 1.0) + s->limit_upscaled = s->limit * ((1 << s->bitdepth) - 1); + else + s->limit_upscaled = s->limit; + s->frame_nb = s->reset_count; + } + + return 0; +} + #define OFFSET(x) offsetof(CropDetectContext, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define TFLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_RUNTIME_PARAM static const AVOption cropdetect_options[] = { - { "limit", "Threshold below which the pixel is considered black", OFFSET(limit), AV_OPT_TYPE_FLOAT, { .dbl = 24.0/255 }, 0, 65535, FLAGS }, + { "limit", "Threshold below which the pixel is considered black", OFFSET(limit), AV_OPT_TYPE_FLOAT, { .dbl = 24.0/255 }, 0, 65535, TFLAGS }, { "round", "Value by which the width/height should be divisible", OFFSET(round), AV_OPT_TYPE_INT, { .i64 = 16 }, 0, INT_MAX, FLAGS }, { "reset", "Recalculate the crop area after this many frames", OFFSET(reset_count), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS }, { "skip", "Number of initial frames to skip", OFFSET(skip), AV_OPT_TYPE_INT, { .i64 = 2 }, 0, INT_MAX, FLAGS }, @@ -463,13 +494,6 @@ static const AVFilterPad avfilter_vf_cropdetect_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_cropdetect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO - }, -}; - const AVFilter ff_vf_cropdetect = { .name = "cropdetect", .description = NULL_IF_CONFIG_SMALL("Auto-detect crop size."), @@ -478,7 +502,8 @@ const AVFilter ff_vf_cropdetect = { .init = init, .uninit = uninit, FILTER_INPUTS(avfilter_vf_cropdetect_inputs), - FILTER_OUTPUTS(avfilter_vf_cropdetect_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_METADATA_ONLY, + .process_command = process_command, }; diff --git a/libavfilter/vf_curves.c b/libavfilter/vf_curves.c index 838942d745b..f77fae7eae8 100644 --- a/libavfilter/vf_curves.c +++ b/libavfilter/vf_curves.c @@ -1005,13 +1005,6 @@ static const AVFilterPad curves_inputs[] = { }, }; -static const AVFilterPad curves_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_curves = { .name = "curves", .description = NULL_IF_CONFIG_SMALL("Adjust components curves."), @@ -1019,7 +1012,7 @@ const AVFilter ff_vf_curves = { .init = curves_init, .uninit = curves_uninit, FILTER_INPUTS(curves_inputs), - FILTER_OUTPUTS(curves_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS(AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24, AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA, AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR, diff --git a/libavfilter/vf_datascope.c b/libavfilter/vf_datascope.c index 0a605985f1d..89b59f25106 100644 --- a/libavfilter/vf_datascope.c +++ b/libavfilter/vf_datascope.c @@ -322,7 +322,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) av_frame_free(&in); return AVERROR(ENOMEM); } - out->pts = in->pts; + av_frame_copy_props(out, in); ff_fill_rectangle(&s->draw, &s->black, out->data, out->linesize, 0, 0, outlink->w, outlink->h); @@ -728,20 +728,13 @@ static const AVFilterPad pixscope_inputs[] = { }, }; -static const AVFilterPad pixscope_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_pixscope = { .name = "pixscope", .description = NULL_IF_CONFIG_SMALL("Pixel data analysis."), .priv_size = sizeof(PixscopeContext), .priv_class = &pixscope_class, FILTER_INPUTS(pixscope_inputs), - FILTER_OUTPUTS(pixscope_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, .process_command = pixscope_process_command, @@ -1132,13 +1125,6 @@ static const AVFilterPad oscilloscope_inputs[] = { }, }; -static const AVFilterPad oscilloscope_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_oscilloscope = { .name = "oscilloscope", .description = NULL_IF_CONFIG_SMALL("2D Video Oscilloscope."), @@ -1146,7 +1132,7 @@ const AVFilter ff_vf_oscilloscope = { .priv_class = &oscilloscope_class, .uninit = oscilloscope_uninit, FILTER_INPUTS(oscilloscope_inputs), - FILTER_OUTPUTS(oscilloscope_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, .process_command = oscilloscope_process_command, diff --git a/libavfilter/vf_dblur.c b/libavfilter/vf_dblur.c index 131a3ae30ab..3110d192718 100644 --- a/libavfilter/vf_dblur.c +++ b/libavfilter/vf_dblur.c @@ -22,7 +22,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -47,7 +46,7 @@ typedef struct DBlurContext { static const AVOption dblur_options[] = { { "angle", "set angle", OFFSET(angle), AV_OPT_TYPE_FLOAT, {.dbl=45}, 0.0, 360, FLAGS }, - { "radius", "set radius", OFFSET(radius), AV_OPT_TYPE_FLOAT, {.dbl=5}, 1, 8192, FLAGS }, + { "radius", "set radius", OFFSET(radius), AV_OPT_TYPE_FLOAT, {.dbl=5}, 0, 8192, FLAGS }, { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=0xF}, 0, 0xF, FLAGS }, { NULL } }; @@ -67,7 +66,7 @@ static int filter_horizontally(AVFilterContext *ctx, int width, int height) float g; if (s->R3 > 0) { - for (int y = 1; y < height - 1; y++) { + for (int y = 1; y < height; y++) { g = q * f(y, 0) + c * f(y, 0); for (int x = 0; x < width; x++) { f(y, x) = b0 * f(y, x) + b1 * f(y - 1, x) + g; @@ -83,7 +82,7 @@ static int filter_horizontally(AVFilterContext *ctx, int width, int height) } } } else { - for (int y = 1; y < height - 1; y++) { + for (int y = 1; y < height; y++) { g = q * f(y, width - 1) + c * f(y, width - 1); for (int x = width - 1; x >= 0; x--) { f(y, x) = b0 * f(y, x) + b1 * f(y - 1, x) + g; @@ -299,13 +298,6 @@ static const AVFilterPad dblur_inputs[] = { }, }; -static const AVFilterPad dblur_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_dblur = { .name = "dblur", .description = NULL_IF_CONFIG_SMALL("Apply Directional Blur filter."), @@ -313,7 +305,7 @@ const AVFilter ff_vf_dblur = { .priv_class = &dblur_class, .uninit = uninit, FILTER_INPUTS(dblur_inputs), - FILTER_OUTPUTS(dblur_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_dctdnoiz.c b/libavfilter/vf_dctdnoiz.c index f1cad94ff1f..ab96dbe15e1 100644 --- a/libavfilter/vf_dctdnoiz.c +++ b/libavfilter/vf_dctdnoiz.c @@ -33,6 +33,7 @@ #include "libavutil/mem_internal.h" #include "libavutil/opt.h" #include "internal.h" +#include "video.h" static const char *const var_names[] = { "c", NULL }; enum { VAR_C, VAR_VARS_NB }; @@ -809,13 +810,6 @@ static const AVFilterPad dctdnoiz_inputs[] = { }, }; -static const AVFilterPad dctdnoiz_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_dctdnoiz = { .name = "dctdnoiz", .description = NULL_IF_CONFIG_SMALL("Denoise frames using 2D DCT."), @@ -823,7 +817,7 @@ const AVFilter ff_vf_dctdnoiz = { .init = init, .uninit = uninit, FILTER_INPUTS(dctdnoiz_inputs), - FILTER_OUTPUTS(dctdnoiz_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &dctdnoiz_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_deband.c b/libavfilter/vf_deband.c index ec91cb15486..177d888bed4 100644 --- a/libavfilter/vf_deband.c +++ b/libavfilter/vf_deband.c @@ -24,6 +24,7 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" #include "video.h" @@ -458,13 +459,6 @@ static const AVFilterPad avfilter_vf_deband_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_deband_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_deband = { .name = "deband", .description = NULL_IF_CONFIG_SMALL("Debands video."), @@ -472,7 +466,7 @@ const AVFilter ff_vf_deband = { .priv_class = &deband_class, .uninit = uninit, FILTER_INPUTS(avfilter_vf_deband_inputs), - FILTER_OUTPUTS(avfilter_vf_deband_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, diff --git a/libavfilter/vf_deblock.c b/libavfilter/vf_deblock.c index 770fce0eab2..d35b22e3be2 100644 --- a/libavfilter/vf_deblock.c +++ b/libavfilter/vf_deblock.c @@ -27,7 +27,6 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" diff --git a/libavfilter/vf_dedot.c b/libavfilter/vf_dedot.c index a0638f45b40..7aa25831849 100644 --- a/libavfilter/vf_dedot.c +++ b/libavfilter/vf_dedot.c @@ -18,13 +18,11 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" #include "filters.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -289,7 +287,7 @@ static int activate(AVFilterContext *ctx) s->frames[4]) { out = av_frame_clone(s->frames[2]); if (out && !ctx->is_disabled) { - ret = av_frame_make_writable(out); + ret = ff_inlink_make_frame_writable(inlink, &out); if (ret >= 0) { if (s->m & 1) ff_filter_execute(ctx, s->dedotcrawl, out, NULL, @@ -375,13 +373,6 @@ static const AVOption dedot_options[] = { { NULL }, }; -static const AVFilterPad inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - static const AVFilterPad outputs[] = { { .name = "default", @@ -399,7 +390,7 @@ const AVFilter ff_vf_dedot = { .priv_class = &dedot_class, .activate = activate, .uninit = uninit, - FILTER_INPUTS(inputs), + FILTER_INPUTS(ff_video_default_filterpad), FILTER_OUTPUTS(outputs), FILTER_PIXFMTS_ARRAY(pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_deflicker.c b/libavfilter/vf_deflicker.c index c01c057e0e8..25172135ed0 100644 --- a/libavfilter/vf_deflicker.c +++ b/libavfilter/vf_deflicker.c @@ -27,7 +27,6 @@ #define FF_BUFQUEUE_SIZE 129 #include "bufferqueue.h" -#include "formats.h" #include "internal.h" #include "video.h" diff --git a/libavfilter/vf_deinterlace_qsv.c b/libavfilter/vf_deinterlace_qsv.c deleted file mode 100644 index 6c94923f02f..00000000000 --- a/libavfilter/vf_deinterlace_qsv.c +++ /dev/null @@ -1,611 +0,0 @@ -/* - * 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 - * deinterlace video filter - QSV - */ - -#include - -#include -#include - -#include "libavutil/avstring.h" -#include "libavutil/common.h" -#include "libavutil/hwcontext.h" -#include "libavutil/hwcontext_qsv.h" -#include "libavutil/internal.h" -#include "libavutil/mathematics.h" -#include "libavutil/opt.h" -#include "libavutil/pixdesc.h" -#include "libavutil/time.h" -#include "libavfilter/qsvvpp.h" - -#include "avfilter.h" -#include "formats.h" -#include "internal.h" -#include "video.h" - -#define MFX_IMPL_VIA_MASK(impl) (0x0f00 & (impl)) - -enum { - QSVDEINT_MORE_OUTPUT = 1, - QSVDEINT_MORE_INPUT, -}; - -typedef struct QSVDeintContext { - const AVClass *class; - - AVBufferRef *hw_frames_ctx; - /* a clone of the main session, used internally for deinterlacing */ - mfxSession session; - - mfxMemId *mem_ids; - int nb_mem_ids; - - mfxFrameSurface1 **surface_ptrs; - int nb_surface_ptrs; - -#if QSV_HAVE_OPAQUE - mfxExtOpaqueSurfaceAlloc opaque_alloc; -#endif - mfxExtVPPDeinterlacing deint_conf; - mfxExtBuffer *ext_buffers[2]; - int num_ext_buffers; - - QSVFrame *work_frames; - - int64_t last_pts; - - int eof; - - /* option for Deinterlacing algorithm to be used */ - int mode; -} QSVDeintContext; - -static av_cold void qsvdeint_uninit(AVFilterContext *ctx) -{ - QSVDeintContext *s = ctx->priv; - QSVFrame *cur; - - if (s->session) { - MFXClose(s->session); - s->session = NULL; - } - av_buffer_unref(&s->hw_frames_ctx); - - cur = s->work_frames; - while (cur) { - s->work_frames = cur->next; - av_frame_free(&cur->frame); - av_freep(&cur); - cur = s->work_frames; - } - - av_freep(&s->mem_ids); - s->nb_mem_ids = 0; - - av_freep(&s->surface_ptrs); - s->nb_surface_ptrs = 0; -} - -static mfxStatus frame_alloc(mfxHDL pthis, mfxFrameAllocRequest *req, - mfxFrameAllocResponse *resp) -{ - AVFilterContext *ctx = pthis; - QSVDeintContext *s = ctx->priv; - - if (!(req->Type & MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET) || - !(req->Type & (MFX_MEMTYPE_FROM_VPPIN | MFX_MEMTYPE_FROM_VPPOUT)) || - !(req->Type & MFX_MEMTYPE_EXTERNAL_FRAME)) - return MFX_ERR_UNSUPPORTED; - - resp->mids = s->mem_ids; - resp->NumFrameActual = s->nb_mem_ids; - - return MFX_ERR_NONE; -} - -static mfxStatus frame_free(mfxHDL pthis, mfxFrameAllocResponse *resp) -{ - return MFX_ERR_NONE; -} - -static mfxStatus frame_lock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) -{ - return MFX_ERR_UNSUPPORTED; -} - -static mfxStatus frame_unlock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) -{ - return MFX_ERR_UNSUPPORTED; -} - -static mfxStatus frame_get_hdl(mfxHDL pthis, mfxMemId mid, mfxHDL *hdl) -{ - mfxHDLPair *pair_dst = (mfxHDLPair*)hdl; - mfxHDLPair *pair_src = (mfxHDLPair*)mid; - - pair_dst->first = pair_src->first; - - if (pair_src->second != (mfxMemId)MFX_INFINITE) - pair_dst->second = pair_src->second; - return MFX_ERR_NONE; -} - -static int init_out_session(AVFilterContext *ctx) -{ - - QSVDeintContext *s = ctx->priv; - AVHWFramesContext *hw_frames_ctx = (AVHWFramesContext*)s->hw_frames_ctx->data; - AVQSVFramesContext *hw_frames_hwctx = hw_frames_ctx->hwctx; - AVQSVDeviceContext *device_hwctx = hw_frames_ctx->device_ctx->hwctx; - int opaque = 0; - mfxHDL handle = NULL; - mfxHandleType handle_type; - mfxVersion ver; - mfxIMPL impl; - mfxVideoParam par; - mfxStatus err; - int i, ret; - -#if QSV_HAVE_OPAQUE - opaque = !!(hw_frames_hwctx->frame_type & MFX_MEMTYPE_OPAQUE_FRAME); -#endif - /* extract the properties of the "master" session given to us */ - err = MFXQueryIMPL(device_hwctx->session, &impl); - if (err == MFX_ERR_NONE) - err = MFXQueryVersion(device_hwctx->session, &ver); - if (err != MFX_ERR_NONE) { - av_log(ctx, AV_LOG_ERROR, "Error querying the session attributes\n"); - return AVERROR_UNKNOWN; - } - - if (MFX_IMPL_VIA_VAAPI == MFX_IMPL_VIA_MASK(impl)) { - handle_type = MFX_HANDLE_VA_DISPLAY; - } else if (MFX_IMPL_VIA_D3D11 == MFX_IMPL_VIA_MASK(impl)) { - handle_type = MFX_HANDLE_D3D11_DEVICE; - } else if (MFX_IMPL_VIA_D3D9 == MFX_IMPL_VIA_MASK(impl)) { - handle_type = MFX_HANDLE_D3D9_DEVICE_MANAGER; - } else { - av_log(ctx, AV_LOG_ERROR, "Error unsupported handle type\n"); - return AVERROR_UNKNOWN; - } - - err = MFXVideoCORE_GetHandle(device_hwctx->session, handle_type, &handle); - if (err < 0) - return ff_qsvvpp_print_error(ctx, err, "Error getting the session handle"); - else if (err > 0) { - ff_qsvvpp_print_warning(ctx, err, "Warning in getting the session handle"); - return AVERROR_UNKNOWN; - } - - /* create a "slave" session with those same properties, to be used for - * actual deinterlacing */ - ret = ff_qsvvpp_create_mfx_session(ctx, device_hwctx->loader, impl, &ver, - &s->session); - if (ret) - return ret; - - if (handle) { - err = MFXVideoCORE_SetHandle(s->session, handle_type, handle); - if (err != MFX_ERR_NONE) - return AVERROR_UNKNOWN; - } - - if (QSV_RUNTIME_VERSION_ATLEAST(ver, 1, 25)) { - err = MFXJoinSession(device_hwctx->session, s->session); - if (err != MFX_ERR_NONE) - return AVERROR_UNKNOWN; - } - - memset(&par, 0, sizeof(par)); - - s->deint_conf.Header.BufferId = MFX_EXTBUFF_VPP_DEINTERLACING; - s->deint_conf.Header.BufferSz = sizeof(s->deint_conf); - s->deint_conf.Mode = s->mode; - - s->ext_buffers[s->num_ext_buffers++] = (mfxExtBuffer *)&s->deint_conf; - - if (!opaque) { - mfxFrameAllocator frame_allocator = { - .pthis = ctx, - .Alloc = frame_alloc, - .Lock = frame_lock, - .Unlock = frame_unlock, - .GetHDL = frame_get_hdl, - .Free = frame_free, - }; - - s->mem_ids = av_calloc(hw_frames_hwctx->nb_surfaces, - sizeof(*s->mem_ids)); - if (!s->mem_ids) - return AVERROR(ENOMEM); - for (i = 0; i < hw_frames_hwctx->nb_surfaces; i++) - s->mem_ids[i] = hw_frames_hwctx->surfaces[i].Data.MemId; - s->nb_mem_ids = hw_frames_hwctx->nb_surfaces; - - err = MFXVideoCORE_SetFrameAllocator(s->session, &frame_allocator); - if (err != MFX_ERR_NONE) - return AVERROR_UNKNOWN; - - par.IOPattern = MFX_IOPATTERN_IN_VIDEO_MEMORY | MFX_IOPATTERN_OUT_VIDEO_MEMORY; - } -#if QSV_HAVE_OPAQUE - else { - s->surface_ptrs = av_calloc(hw_frames_hwctx->nb_surfaces, - sizeof(*s->surface_ptrs)); - - if (!s->surface_ptrs) - return AVERROR(ENOMEM); - for (i = 0; i < hw_frames_hwctx->nb_surfaces; i++) - s->surface_ptrs[i] = hw_frames_hwctx->surfaces + i; - s->nb_surface_ptrs = hw_frames_hwctx->nb_surfaces; - - s->opaque_alloc.In.Surfaces = s->surface_ptrs; - s->opaque_alloc.In.NumSurface = s->nb_surface_ptrs; - s->opaque_alloc.In.Type = hw_frames_hwctx->frame_type; - - s->opaque_alloc.Out = s->opaque_alloc.In; - - s->opaque_alloc.Header.BufferId = MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION; - s->opaque_alloc.Header.BufferSz = sizeof(s->opaque_alloc); - - s->ext_buffers[s->num_ext_buffers++] = (mfxExtBuffer *)&s->opaque_alloc; - - par.IOPattern = MFX_IOPATTERN_IN_OPAQUE_MEMORY | MFX_IOPATTERN_OUT_OPAQUE_MEMORY; - } -#endif - - par.ExtParam = s->ext_buffers; - par.NumExtParam = s->num_ext_buffers; - - par.AsyncDepth = 1; // TODO async - - par.vpp.In = hw_frames_hwctx->surfaces[0].Info; - - par.vpp.In.CropW = ctx->inputs[0]->w; - par.vpp.In.CropH = ctx->inputs[0]->h; - - if (ctx->inputs[0]->frame_rate.num) { - par.vpp.In.FrameRateExtN = ctx->inputs[0]->frame_rate.num; - par.vpp.In.FrameRateExtD = ctx->inputs[0]->frame_rate.den; - } else { - par.vpp.In.FrameRateExtN = ctx->inputs[0]->time_base.num; - par.vpp.In.FrameRateExtD = ctx->inputs[0]->time_base.den; - } - - par.vpp.Out = par.vpp.In; - - if (ctx->outputs[0]->frame_rate.num) { - par.vpp.Out.FrameRateExtN = ctx->outputs[0]->frame_rate.num; - par.vpp.Out.FrameRateExtD = ctx->outputs[0]->frame_rate.den; - } else { - par.vpp.Out.FrameRateExtN = ctx->outputs[0]->time_base.num; - par.vpp.Out.FrameRateExtD = ctx->outputs[0]->time_base.den; - } - - /* Print input memory mode */ - ff_qsvvpp_print_iopattern(ctx, par.IOPattern & 0x0F, "VPP"); - /* Print output memory mode */ - ff_qsvvpp_print_iopattern(ctx, par.IOPattern & 0xF0, "VPP"); - err = MFXVideoVPP_Init(s->session, &par); - if (err < 0) - return ff_qsvvpp_print_error(ctx, err, - "Error opening the VPP for deinterlacing"); - else if (err > 0) { - ff_qsvvpp_print_warning(ctx, err, - "Warning in VPP initialization"); - return AVERROR_UNKNOWN; - } - - return 0; -} - -static int qsvdeint_config_props(AVFilterLink *outlink) -{ - AVFilterContext *ctx = outlink->src; - AVFilterLink *inlink = ctx->inputs[0]; - QSVDeintContext *s = ctx->priv; - int ret; - - qsvdeint_uninit(ctx); - - s->last_pts = AV_NOPTS_VALUE; - outlink->frame_rate = av_mul_q(inlink->frame_rate, - (AVRational){ 2, 1 }); - outlink->time_base = av_mul_q(inlink->time_base, - (AVRational){ 1, 2 }); - - /* check that we have a hw context */ - if (!inlink->hw_frames_ctx) { - av_log(ctx, AV_LOG_ERROR, "No hw context provided on input\n"); - return AVERROR(EINVAL); - } - - s->hw_frames_ctx = av_buffer_ref(inlink->hw_frames_ctx); - if (!s->hw_frames_ctx) - return AVERROR(ENOMEM); - - av_buffer_unref(&outlink->hw_frames_ctx); - outlink->hw_frames_ctx = av_buffer_ref(inlink->hw_frames_ctx); - if (!outlink->hw_frames_ctx) { - qsvdeint_uninit(ctx); - return AVERROR(ENOMEM); - } - - ret = init_out_session(ctx); - if (ret < 0) - return ret; - - - return 0; -} - -static void clear_unused_frames(QSVDeintContext *s) -{ - QSVFrame *cur = s->work_frames; - while (cur) { - if (!cur->surface.Data.Locked) { - av_frame_free(&cur->frame); - cur->queued = 0; - } - cur = cur->next; - } -} - -static int get_free_frame(QSVDeintContext *s, QSVFrame **f) -{ - QSVFrame *frame, **last; - - clear_unused_frames(s); - - frame = s->work_frames; - last = &s->work_frames; - while (frame) { - if (!frame->queued) { - *f = frame; - return 0; - } - - last = &frame->next; - frame = frame->next; - } - - frame = av_mallocz(sizeof(*frame)); - if (!frame) - return AVERROR(ENOMEM); - *last = frame; - *f = frame; - - return 0; -} - -static int submit_frame(AVFilterContext *ctx, AVFrame *frame, - mfxFrameSurface1 **surface) -{ - QSVDeintContext *s = ctx->priv; - QSVFrame *qf; - int ret; - - ret = get_free_frame(s, &qf); - if (ret < 0) - return ret; - - qf->frame = frame; - - qf->surface = *(mfxFrameSurface1*)qf->frame->data[3]; - - qf->surface.Data.Locked = 0; - qf->surface.Info.CropW = qf->frame->width; - qf->surface.Info.CropH = qf->frame->height; - - qf->surface.Info.PicStruct = !qf->frame->interlaced_frame ? MFX_PICSTRUCT_PROGRESSIVE : - (qf->frame->top_field_first ? MFX_PICSTRUCT_FIELD_TFF : - MFX_PICSTRUCT_FIELD_BFF); - if (qf->frame->repeat_pict == 1) { - qf->surface.Info.PicStruct |= MFX_PICSTRUCT_FIELD_REPEATED; - qf->surface.Info.PicStruct |= qf->frame->top_field_first ? MFX_PICSTRUCT_FIELD_TFF : - MFX_PICSTRUCT_FIELD_BFF; - } else if (qf->frame->repeat_pict == 2) - qf->surface.Info.PicStruct |= MFX_PICSTRUCT_FRAME_DOUBLING; - else if (qf->frame->repeat_pict == 4) - qf->surface.Info.PicStruct |= MFX_PICSTRUCT_FRAME_TRIPLING; - - if (ctx->inputs[0]->frame_rate.num) { - qf->surface.Info.FrameRateExtN = ctx->inputs[0]->frame_rate.num; - qf->surface.Info.FrameRateExtD = ctx->inputs[0]->frame_rate.den; - } else { - qf->surface.Info.FrameRateExtN = ctx->inputs[0]->time_base.num; - qf->surface.Info.FrameRateExtD = ctx->inputs[0]->time_base.den; - } - - qf->surface.Data.TimeStamp = av_rescale_q(qf->frame->pts, - ctx->inputs[0]->time_base, - (AVRational){1, 90000}); - - *surface = &qf->surface; - qf->queued = 1; - - return 0; -} - -static int process_frame(AVFilterContext *ctx, const AVFrame *in, - mfxFrameSurface1 *surf_in) -{ - QSVDeintContext *s = ctx->priv; - AVFilterLink *inlink = ctx->inputs[0]; - AVFilterLink *outlink = ctx->outputs[0]; - - AVFrame *out; - mfxFrameSurface1 *surf_out; - mfxSyncPoint sync = NULL; - mfxStatus err; - int ret, again = 0; - - out = ff_get_video_buffer(outlink, outlink->w, outlink->h); - if (!out) { - ret = AVERROR(ENOMEM); - goto fail; - } - - surf_out = (mfxFrameSurface1*)out->data[3]; - surf_out->Info.CropW = outlink->w; - surf_out->Info.CropH = outlink->h; - surf_out->Info.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; - - do { - err = MFXVideoVPP_RunFrameVPPAsync(s->session, surf_in, surf_out, - NULL, &sync); - if (err == MFX_WRN_DEVICE_BUSY) - av_usleep(1); - } while (err == MFX_WRN_DEVICE_BUSY); - - if (err == MFX_ERR_MORE_DATA) { - av_frame_free(&out); - return QSVDEINT_MORE_INPUT; - } - - if (err < 0 && err != MFX_ERR_MORE_SURFACE) { - ret = ff_qsvvpp_print_error(ctx, err, "Error during deinterlacing"); - goto fail; - } - - if (!sync) { - av_log(ctx, AV_LOG_ERROR, "No sync during deinterlacing\n"); - ret = AVERROR_UNKNOWN; - goto fail; - } - if (err == MFX_ERR_MORE_SURFACE) - again = 1; - - do { - err = MFXVideoCORE_SyncOperation(s->session, sync, 1000); - } while (err == MFX_WRN_IN_EXECUTION); - if (err < 0) { - ret = ff_qsvvpp_print_error(ctx, err, "Error synchronizing the operation"); - goto fail; - } - - ret = av_frame_copy_props(out, in); - if (ret < 0) - goto fail; - - out->width = outlink->w; - out->height = outlink->h; - out->interlaced_frame = 0; - - out->pts = av_rescale_q(out->pts, inlink->time_base, outlink->time_base); - if (out->pts == s->last_pts) - out->pts++; - s->last_pts = out->pts; - - if (outlink->frame_rate.num && outlink->frame_rate.den) - out->duration = av_rescale_q(1, av_inv_q(outlink->frame_rate), outlink->time_base); - else - out->duration = 0; - - ret = ff_filter_frame(outlink, out); - if (ret < 0) - return ret; - - return again ? QSVDEINT_MORE_OUTPUT : 0; -fail: - av_frame_free(&out); - return ret; -} - -static int qsvdeint_filter_frame(AVFilterLink *link, AVFrame *in) -{ - AVFilterContext *ctx = link->dst; - - mfxFrameSurface1 *surf_in; - int ret; - - ret = submit_frame(ctx, in, &surf_in); - if (ret < 0) { - av_frame_free(&in); - return ret; - } - - do { - ret = process_frame(ctx, in, surf_in); - if (ret < 0) - return ret; - } while (ret == QSVDEINT_MORE_OUTPUT); - - return 0; -} - -static int qsvdeint_request_frame(AVFilterLink *outlink) -{ - AVFilterContext *ctx = outlink->src; - - return ff_request_frame(ctx->inputs[0]); -} - -#define OFFSET(x) offsetof(QSVDeintContext, x) -#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM -static const AVOption options[] = { - { "mode", "set deinterlace mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = MFX_DEINTERLACING_ADVANCED}, MFX_DEINTERLACING_BOB, MFX_DEINTERLACING_ADVANCED, FLAGS, "mode"}, - { "bob", "bob algorithm", 0, AV_OPT_TYPE_CONST, {.i64 = MFX_DEINTERLACING_BOB}, MFX_DEINTERLACING_BOB, MFX_DEINTERLACING_ADVANCED, FLAGS, "mode"}, - { "advanced", "Motion adaptive algorithm", 0, AV_OPT_TYPE_CONST, {.i64 = MFX_DEINTERLACING_ADVANCED}, MFX_DEINTERLACING_BOB, MFX_DEINTERLACING_ADVANCED, FLAGS, "mode"}, - { NULL }, -}; - -static const AVClass qsvdeint_class = { - .class_name = "deinterlace_qsv", - .item_name = av_default_item_name, - .option = options, - .version = LIBAVUTIL_VERSION_INT, -}; - -static const AVFilterPad qsvdeint_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .filter_frame = qsvdeint_filter_frame, - .get_buffer.video = ff_qsvvpp_get_video_buffer, - }, -}; - -static const AVFilterPad qsvdeint_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .config_props = qsvdeint_config_props, - .request_frame = qsvdeint_request_frame, - }, -}; - -const AVFilter ff_vf_deinterlace_qsv = { - .name = "deinterlace_qsv", - .description = NULL_IF_CONFIG_SMALL("QuickSync video deinterlacing"), - - .uninit = qsvdeint_uninit, - - .priv_size = sizeof(QSVDeintContext), - .priv_class = &qsvdeint_class, - - FILTER_INPUTS(qsvdeint_inputs), - FILTER_OUTPUTS(qsvdeint_outputs), - FILTER_SINGLE_PIXFMT(AV_PIX_FMT_QSV), - - .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, -}; diff --git a/libavfilter/vf_deinterlace_vaapi.c b/libavfilter/vf_deinterlace_vaapi.c index 13045610343..f085a7c10a0 100644 --- a/libavfilter/vf_deinterlace_vaapi.c +++ b/libavfilter/vf_deinterlace_vaapi.c @@ -23,7 +23,6 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "vaapi_vpp.h" @@ -252,7 +251,7 @@ static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame) if (err < 0) goto fail; - if (!ctx->auto_enable || input_frame->interlaced_frame) { + if (!ctx->auto_enable || (input_frame->flags & AV_FRAME_FLAG_INTERLACED)) { vas = vaMapBuffer(vpp_ctx->hwctx->display, vpp_ctx->filter_buffers[0], &filter_params_addr); if (vas != VA_STATUS_SUCCESS) { @@ -263,7 +262,7 @@ static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame) } filter_params = filter_params_addr; filter_params->flags = 0; - if (input_frame->top_field_first) { + if (input_frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) { filter_params->flags |= field ? VA_DEINTERLACING_BOTTOM_FIELD : 0; } else { filter_params->flags |= VA_DEINTERLACING_BOTTOM_FIELD_FIRST; @@ -303,7 +302,12 @@ static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame) output_frame->pts = input_frame->pts + ctx->frame_queue[current_frame_index + 1]->pts; } +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS output_frame->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + output_frame->flags &= ~AV_FRAME_FLAG_INTERLACED; av_log(avctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u (%"PRId64").\n", av_get_pix_fmt_name(output_frame->format), diff --git a/libavfilter/vf_delogo.c b/libavfilter/vf_delogo.c index cc71f25c597..c049f273b05 100644 --- a/libavfilter/vf_delogo.c +++ b/libavfilter/vf_delogo.c @@ -33,7 +33,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/eval.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" static const char * const var_names[] = { @@ -381,13 +380,6 @@ static const AVFilterPad avfilter_vf_delogo_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_delogo_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_delogo = { .name = "delogo", .description = NULL_IF_CONFIG_SMALL("Remove logo from input video."), @@ -396,7 +388,7 @@ const AVFilter ff_vf_delogo = { .init = init, .uninit = uninit, FILTER_INPUTS(avfilter_vf_delogo_inputs), - FILTER_OUTPUTS(avfilter_vf_delogo_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_derain.c b/libavfilter/vf_derain.c index 86e9eb8752b..35bc233ab16 100644 --- a/libavfilter/vf_derain.c +++ b/libavfilter/vf_derain.c @@ -24,12 +24,11 @@ * http://openaccess.thecvf.com/content_ECCV_2018/html/Xia_Li_Recurrent_Squeeze-and-Excitation_Context_ECCV_2018_paper.html */ -#include "libavformat/avio.h" #include "libavutil/opt.h" #include "avfilter.h" #include "dnn_filter_common.h" -#include "formats.h" #include "internal.h" +#include "video.h" typedef struct DRContext { const AVClass *class; @@ -43,8 +42,7 @@ static const AVOption derain_options[] = { { "filter_type", "filter type(derain/dehaze)", OFFSET(filter_type), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS, "type" }, { "derain", "derain filter flag", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, "type" }, { "dehaze", "dehaze filter flag", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "type" }, - { "dnn_backend", "DNN backend", OFFSET(dnnctx.backend_type), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS, "backend" }, - { "native", "native backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, "backend" }, + { "dnn_backend", "DNN backend", OFFSET(dnnctx.backend_type), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, FLAGS, "backend" }, #if (CONFIG_LIBTENSORFLOW == 1) { "tensorflow", "tensorflow backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "backend" }, #endif @@ -111,13 +109,6 @@ static const AVFilterPad derain_inputs[] = { }, }; -static const AVFilterPad derain_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_derain = { .name = "derain", .description = NULL_IF_CONFIG_SMALL("Apply derain filter to the input."), @@ -125,7 +116,7 @@ const AVFilter ff_vf_derain = { .init = init, .uninit = uninit, FILTER_INPUTS(derain_inputs), - FILTER_OUTPUTS(derain_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_RGB24), .priv_class = &derain_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_deshake.c b/libavfilter/vf_deshake.c index 142f88541d2..a10d59ad808 100644 --- a/libavfilter/vf_deshake.c +++ b/libavfilter/vf_deshake.c @@ -535,13 +535,6 @@ static const AVFilterPad deshake_inputs[] = { }, }; -static const AVFilterPad deshake_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_deshake = { .name = "deshake", .description = NULL_IF_CONFIG_SMALL("Stabilize shaky video."), @@ -549,7 +542,7 @@ const AVFilter ff_vf_deshake = { .init = init, .uninit = uninit, FILTER_INPUTS(deshake_inputs), - FILTER_OUTPUTS(deshake_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &deshake_class, }; diff --git a/libavfilter/vf_deshake_opencl.c b/libavfilter/vf_deshake_opencl.c index d488da7fa0c..cddd5558e28 100644 --- a/libavfilter/vf_deshake_opencl.c +++ b/libavfilter/vf_deshake_opencl.c @@ -48,17 +48,16 @@ #include #include #include "libavutil/opt.h" -#include "libavutil/imgutils.h" #include "libavutil/mem.h" #include "libavutil/fifo.h" #include "libavutil/common.h" #include "libavutil/avassert.h" +#include "libavutil/pixdesc.h" #include "libavutil/pixfmt.h" #include "avfilter.h" #include "framequeue.h" #include "filters.h" #include "transform.h" -#include "formats.h" #include "internal.h" #include "opencl.h" #include "opencl_source.h" @@ -1251,7 +1250,7 @@ static int deshake_opencl_init(AVFilterContext *avctx) } ctx->sw_format = hw_frames_ctx->sw_format; - err = ff_opencl_filter_load_program(avctx, &ff_opencl_source_deshake, 1); + err = ff_opencl_filter_load_program(avctx, &ff_source_deshake_cl, 1); if (err < 0) goto fail; @@ -2169,5 +2168,6 @@ const AVFilter ff_vf_deshake_opencl = { FILTER_INPUTS(deshake_opencl_inputs), FILTER_OUTPUTS(deshake_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), - .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_despill.c b/libavfilter/vf_despill.c index 483d9c85600..cbd08b6f335 100644 --- a/libavfilter/vf_despill.c +++ b/libavfilter/vf_despill.c @@ -19,11 +19,9 @@ */ #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" -#include "video.h" typedef struct DespillContext { const AVClass *class; diff --git a/libavfilter/vf_displace.c b/libavfilter/vf_displace.c index 11909949b16..8cc11f07bad 100644 --- a/libavfilter/vf_displace.c +++ b/libavfilter/vf_displace.c @@ -18,11 +18,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/imgutils.h" #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "framesync.h" #include "internal.h" #include "video.h" @@ -45,12 +43,11 @@ typedef struct DisplaceContext { uint8_t blank[4]; FFFrameSync fs; - void (*displace)(struct DisplaceContext *s, const AVFrame *in, - const AVFrame *xpic, const AVFrame *ypic, AVFrame *out); + int (*displace_slice)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); } DisplaceContext; #define OFFSET(x) offsetof(DisplaceContext, x) -#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption displace_options[] = { { "edge", "set edge mode", OFFSET(edge), AV_OPT_TYPE_INT, {.i64=EDGE_SMEAR}, 0, EDGE_NB-1, FLAGS, "edge" }, @@ -76,29 +73,38 @@ static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE }; -static void displace_planar(DisplaceContext *s, const AVFrame *in, - const AVFrame *xpic, const AVFrame *ypic, - AVFrame *out) +typedef struct ThreadData { + AVFrame *in, *xin, *yin, *out; +} ThreadData; + +static int displace_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { - int plane, x, y; + DisplaceContext *s = ctx->priv; + const ThreadData *td = arg; + const AVFrame *in = td->in; + const AVFrame *xin = td->xin; + const AVFrame *yin = td->yin; + const AVFrame *out = td->out; - for (plane = 0; plane < s->nb_planes; plane++) { + for (int plane = 0; plane < s->nb_planes; plane++) { const int h = s->height[plane]; const int w = s->width[plane]; + const int slice_start = (h * jobnr ) / nb_jobs; + const int slice_end = (h * (jobnr+1)) / nb_jobs; const int dlinesize = out->linesize[plane]; const int slinesize = in->linesize[plane]; - const int xlinesize = xpic->linesize[plane]; - const int ylinesize = ypic->linesize[plane]; + const int xlinesize = xin->linesize[plane]; + const int ylinesize = yin->linesize[plane]; const uint8_t *src = in->data[plane]; - const uint8_t *ysrc = ypic->data[plane]; - const uint8_t *xsrc = xpic->data[plane]; - uint8_t *dst = out->data[plane]; + const uint8_t *ysrc = yin->data[plane] + slice_start * ylinesize; + const uint8_t *xsrc = xin->data[plane] + slice_start * xlinesize; + uint8_t *dst = out->data[plane] + slice_start * dlinesize; const uint8_t blank = s->blank[plane]; - for (y = 0; y < h; y++) { + for (int y = slice_start; y < slice_end; y++) { switch (s->edge) { case EDGE_BLANK: - for (x = 0; x < w; x++) { + for (int x = 0; x < w; x++) { int Y = y + ysrc[x] - 128; int X = x + xsrc[x] - 128; @@ -109,14 +115,14 @@ static void displace_planar(DisplaceContext *s, const AVFrame *in, } break; case EDGE_SMEAR: - for (x = 0; x < w; x++) { + for (int x = 0; x < w; x++) { int Y = av_clip(y + ysrc[x] - 128, 0, h - 1); int X = av_clip(x + xsrc[x] - 128, 0, w - 1); dst[x] = src[Y * slinesize + X]; } break; case EDGE_WRAP: - for (x = 0; x < w; x++) { + for (int x = 0; x < w; x++) { int Y = (y + ysrc[x] - 128) % h; int X = (x + xsrc[x] - 128) % w; @@ -128,7 +134,7 @@ static void displace_planar(DisplaceContext *s, const AVFrame *in, } break; case EDGE_MIRROR: - for (x = 0; x < w; x++) { + for (int x = 0; x < w; x++) { int Y = y + ysrc[x] - 128; int X = x + xsrc[x] - 128; @@ -150,31 +156,37 @@ static void displace_planar(DisplaceContext *s, const AVFrame *in, dst += dlinesize; } } + return 0; } -static void displace_packed(DisplaceContext *s, const AVFrame *in, - const AVFrame *xpic, const AVFrame *ypic, - AVFrame *out) +static int displace_packed(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { + DisplaceContext *s = ctx->priv; + const ThreadData *td = arg; + const AVFrame *in = td->in; + const AVFrame *xin = td->xin; + const AVFrame *yin = td->yin; + const AVFrame *out = td->out; const int step = s->step; const int h = s->height[0]; const int w = s->width[0]; + const int slice_start = (h * jobnr ) / nb_jobs; + const int slice_end = (h * (jobnr+1)) / nb_jobs; const int dlinesize = out->linesize[0]; const int slinesize = in->linesize[0]; - const int xlinesize = xpic->linesize[0]; - const int ylinesize = ypic->linesize[0]; + const int xlinesize = xin->linesize[0]; + const int ylinesize = yin->linesize[0]; const uint8_t *src = in->data[0]; - const uint8_t *ysrc = ypic->data[0]; - const uint8_t *xsrc = xpic->data[0]; + const uint8_t *ysrc = yin->data[0] + slice_start * ylinesize; + const uint8_t *xsrc = xin->data[0] + slice_start * xlinesize; + uint8_t *dst = out->data[0] + slice_start * dlinesize; const uint8_t *blank = s->blank; - uint8_t *dst = out->data[0]; - int c, x, y; - for (y = 0; y < h; y++) { + for (int y = slice_start; y < slice_end; y++) { switch (s->edge) { case EDGE_BLANK: - for (x = 0; x < w; x++) { - for (c = 0; c < s->nb_components; c++) { + for (int x = 0; x < w; x++) { + for (int c = 0; c < s->nb_components; c++) { int Y = y + (ysrc[x * step + c] - 128); int X = x + (xsrc[x * step + c] - 128); @@ -186,8 +198,8 @@ static void displace_packed(DisplaceContext *s, const AVFrame *in, } break; case EDGE_SMEAR: - for (x = 0; x < w; x++) { - for (c = 0; c < s->nb_components; c++) { + for (int x = 0; x < w; x++) { + for (int c = 0; c < s->nb_components; c++) { int Y = av_clip(y + (ysrc[x * step + c] - 128), 0, h - 1); int X = av_clip(x + (xsrc[x * step + c] - 128), 0, w - 1); @@ -196,8 +208,8 @@ static void displace_packed(DisplaceContext *s, const AVFrame *in, } break; case EDGE_WRAP: - for (x = 0; x < w; x++) { - for (c = 0; c < s->nb_components; c++) { + for (int x = 0; x < w; x++) { + for (int c = 0; c < s->nb_components; c++) { int Y = (y + (ysrc[x * step + c] - 128)) % h; int X = (x + (xsrc[x * step + c] - 128)) % w; @@ -210,8 +222,8 @@ static void displace_packed(DisplaceContext *s, const AVFrame *in, } break; case EDGE_MIRROR: - for (x = 0; x < w; x++) { - for (c = 0; c < s->nb_components; c++) { + for (int x = 0; x < w; x++) { + for (int c = 0; c < s->nb_components; c++) { int Y = y + ysrc[x * step + c] - 128; int X = x + xsrc[x * step + c] - 128; @@ -233,6 +245,7 @@ static void displace_packed(DisplaceContext *s, const AVFrame *in, xsrc += xlinesize; dst += dlinesize; } + return 0; } static int process_frame(FFFrameSync *fs) @@ -240,12 +253,12 @@ static int process_frame(FFFrameSync *fs) AVFilterContext *ctx = fs->parent; DisplaceContext *s = fs->opaque; AVFilterLink *outlink = ctx->outputs[0]; - AVFrame *out, *in, *xpic, *ypic; + AVFrame *out, *in, *xin, *yin; int ret; - if ((ret = ff_framesync_get_frame(&s->fs, 0, &in, 0)) < 0 || - (ret = ff_framesync_get_frame(&s->fs, 1, &xpic, 0)) < 0 || - (ret = ff_framesync_get_frame(&s->fs, 2, &ypic, 0)) < 0) + if ((ret = ff_framesync_get_frame(&s->fs, 0, &in, 0)) < 0 || + (ret = ff_framesync_get_frame(&s->fs, 1, &xin, 0)) < 0 || + (ret = ff_framesync_get_frame(&s->fs, 2, &yin, 0)) < 0) return ret; if (ctx->is_disabled) { @@ -253,12 +266,19 @@ static int process_frame(FFFrameSync *fs) if (!out) return AVERROR(ENOMEM); } else { + ThreadData td; + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); if (!out) return AVERROR(ENOMEM); av_frame_copy_props(out, in); - s->displace(s, in, xpic, ypic, out); + td.in = in; + td.xin = xin; + td.yin = yin; + td.out = out; + ff_filter_execute(ctx, s->displace_slice, &td, NULL, + FFMIN(outlink->h, ff_filter_get_nb_threads(ctx))); } out->pts = av_rescale_q(s->fs.pts, s->fs.time_base, outlink->time_base); @@ -276,9 +296,9 @@ static int config_input(AVFilterLink *inlink) s->nb_components = desc->nb_components; if (s->nb_planes > 1 || s->nb_components == 1) - s->displace = displace_planar; + s->displace_slice = displace_planar; else - s->displace = displace_packed; + s->displace_slice = displace_packed; if (!(desc->flags & AV_PIX_FMT_FLAG_RGB)) { s->blank[1] = s->blank[2] = 128; @@ -398,5 +418,7 @@ const AVFilter ff_vf_displace = { FILTER_OUTPUTS(displace_outputs), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &displace_class, - .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | + AVFILTER_FLAG_SLICE_THREADS, + .process_command = ff_filter_process_command, }; diff --git a/libavfilter/vf_dnn_classify.c b/libavfilter/vf_dnn_classify.c index d242aebcfbb..e88e59d09cd 100644 --- a/libavfilter/vf_dnn_classify.c +++ b/libavfilter/vf_dnn_classify.c @@ -26,6 +26,7 @@ #include "filters.h" #include "dnn_filter_common.h" #include "internal.h" +#include "video.h" #include "libavutil/time.h" #include "libavutil/avstring.h" #include "libavutil/detection_bbox.h" @@ -44,9 +45,9 @@ typedef struct DnnClassifyContext { #define OFFSET2(x) offsetof(DnnClassifyContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM static const AVOption dnn_classify_options[] = { - { "dnn_backend", "DNN backend", OFFSET(backend_type), AV_OPT_TYPE_INT, { .i64 = 2 }, INT_MIN, INT_MAX, FLAGS, "backend" }, + { "dnn_backend", "DNN backend", OFFSET(backend_type), AV_OPT_TYPE_INT, { .i64 = DNN_OV }, INT_MIN, INT_MAX, FLAGS, "backend" }, #if (CONFIG_LIBOPENVINO == 1) - { "openvino", "openvino backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0, FLAGS, "backend" }, + { "openvino", "openvino backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = DNN_OV }, 0, 0, FLAGS, "backend" }, #endif DNN_COMMON_OPTIONS { "confidence", "threshold of confidence", OFFSET2(confidence), AV_OPT_TYPE_FLOAT, { .dbl = 0.5 }, 0, 1, FLAGS}, @@ -293,28 +294,14 @@ static av_cold void dnn_classify_uninit(AVFilterContext *context) free_classify_labels(ctx); } -static const AVFilterPad dnn_classify_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - -static const AVFilterPad dnn_classify_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_dnn_classify = { .name = "dnn_classify", .description = NULL_IF_CONFIG_SMALL("Apply DNN classify filter to the input."), .priv_size = sizeof(DnnClassifyContext), .init = dnn_classify_init, .uninit = dnn_classify_uninit, - FILTER_INPUTS(dnn_classify_inputs), - FILTER_OUTPUTS(dnn_classify_outputs), + FILTER_INPUTS(ff_video_default_filterpad), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &dnn_classify_class, .activate = dnn_classify_activate, diff --git a/libavfilter/vf_dnn_detect.c b/libavfilter/vf_dnn_detect.c index 7e133f6af55..b5dae42c650 100644 --- a/libavfilter/vf_dnn_detect.c +++ b/libavfilter/vf_dnn_detect.c @@ -26,6 +26,7 @@ #include "filters.h" #include "dnn_filter_common.h" #include "internal.h" +#include "video.h" #include "libavutil/time.h" #include "libavutil/avstring.h" #include "libavutil/detection_bbox.h" @@ -43,12 +44,12 @@ typedef struct DnnDetectContext { #define OFFSET2(x) offsetof(DnnDetectContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM static const AVOption dnn_detect_options[] = { - { "dnn_backend", "DNN backend", OFFSET(backend_type), AV_OPT_TYPE_INT, { .i64 = 2 }, INT_MIN, INT_MAX, FLAGS, "backend" }, + { "dnn_backend", "DNN backend", OFFSET(backend_type), AV_OPT_TYPE_INT, { .i64 = DNN_OV }, INT_MIN, INT_MAX, FLAGS, "backend" }, #if (CONFIG_LIBTENSORFLOW == 1) - { "tensorflow", "tensorflow backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "backend" }, + { "tensorflow", "tensorflow backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = DNN_TF }, 0, 0, FLAGS, "backend" }, #endif #if (CONFIG_LIBOPENVINO == 1) - { "openvino", "openvino backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0, FLAGS, "backend" }, + { "openvino", "openvino backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = DNN_OV }, 0, 0, FLAGS, "backend" }, #endif DNN_COMMON_OPTIONS { "confidence", "threshold of confidence", OFFSET2(confidence), AV_OPT_TYPE_FLOAT, { .dbl = 0.5 }, 0, 1, FLAGS}, @@ -106,12 +107,11 @@ static int dnn_detect_post_proc_ov(AVFrame *frame, DNNData *output, AVFilterCont float x1 = detections[i * detect_size + 5]; float y1 = detections[i * detect_size + 6]; - bbox = av_get_detection_bbox(header, i); - if (conf < conf_threshold) { continue; } + bbox = av_get_detection_bbox(header, header->nb_bboxes - nb_bboxes); bbox->x = (int)(x0 * frame->width); bbox->w = (int)(x1 * frame->width) - bbox->x; bbox->y = (int)(y0 * frame->height); @@ -436,28 +436,14 @@ static av_cold void dnn_detect_uninit(AVFilterContext *context) free_detect_labels(ctx); } -static const AVFilterPad dnn_detect_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - -static const AVFilterPad dnn_detect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_dnn_detect = { .name = "dnn_detect", .description = NULL_IF_CONFIG_SMALL("Apply DNN detect filter to the input."), .priv_size = sizeof(DnnDetectContext), .init = dnn_detect_init, .uninit = dnn_detect_uninit, - FILTER_INPUTS(dnn_detect_inputs), - FILTER_OUTPUTS(dnn_detect_outputs), + FILTER_INPUTS(ff_video_default_filterpad), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &dnn_detect_class, .activate = dnn_detect_activate, diff --git a/libavfilter/vf_dnn_processing.c b/libavfilter/vf_dnn_processing.c index 4462915073d..6829e945857 100644 --- a/libavfilter/vf_dnn_processing.c +++ b/libavfilter/vf_dnn_processing.c @@ -23,15 +23,14 @@ * implementing a generic image processing filter using deep learning networks. */ -#include "libavformat/avio.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libavutil/avassert.h" #include "libavutil/imgutils.h" #include "filters.h" #include "dnn_filter_common.h" -#include "formats.h" #include "internal.h" +#include "video.h" #include "libswscale/swscale.h" #include "libavutil/time.h" @@ -45,13 +44,12 @@ typedef struct DnnProcessingContext { #define OFFSET(x) offsetof(DnnProcessingContext, dnnctx.x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM static const AVOption dnn_processing_options[] = { - { "dnn_backend", "DNN backend", OFFSET(backend_type), AV_OPT_TYPE_INT, { .i64 = 0 }, INT_MIN, INT_MAX, FLAGS, "backend" }, - { "native", "native backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, "backend" }, + { "dnn_backend", "DNN backend", OFFSET(backend_type), AV_OPT_TYPE_INT, { .i64 = DNN_TF }, INT_MIN, INT_MAX, FLAGS, "backend" }, #if (CONFIG_LIBTENSORFLOW == 1) - { "tensorflow", "tensorflow backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "backend" }, + { "tensorflow", "tensorflow backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = DNN_TF }, 0, 0, FLAGS, "backend" }, #endif #if (CONFIG_LIBOPENVINO == 1) - { "openvino", "openvino backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0, FLAGS, "backend" }, + { "openvino", "openvino backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = DNN_OV }, 0, 0, FLAGS, "backend" }, #endif DNN_COMMON_OPTIONS { NULL } diff --git a/libavfilter/vf_drawbox.c b/libavfilter/vf_drawbox.c index 64eeeece121..27739dc89f3 100644 --- a/libavfilter/vf_drawbox.c +++ b/libavfilter/vf_drawbox.c @@ -36,7 +36,6 @@ #include "libavutil/detection_bbox.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -471,13 +470,6 @@ static const AVFilterPad drawbox_inputs[] = { }, }; -static const AVFilterPad drawbox_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_drawbox = { .name = "drawbox", .description = NULL_IF_CONFIG_SMALL("Draw a colored box on the input video."), @@ -485,7 +477,7 @@ const AVFilter ff_vf_drawbox = { .priv_class = &drawbox_class, .init = init, FILTER_INPUTS(drawbox_inputs), - FILTER_OUTPUTS(drawbox_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, @@ -553,13 +545,6 @@ static const AVFilterPad drawgrid_inputs[] = { }, }; -static const AVFilterPad drawgrid_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_drawgrid = { .name = "drawgrid", .description = NULL_IF_CONFIG_SMALL("Draw a colored grid on the input video."), @@ -567,7 +552,7 @@ const AVFilter ff_vf_drawgrid = { .priv_class = &drawgrid_class, .init = init, FILTER_INPUTS(drawgrid_inputs), - FILTER_OUTPUTS(drawgrid_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, .process_command = process_command, diff --git a/libavfilter/vf_drawtext.c b/libavfilter/vf_drawtext.c index 50012bb258a..ec8d215795e 100644 --- a/libavfilter/vf_drawtext.c +++ b/libavfilter/vf_drawtext.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2023 Francesco Carusi * Copyright (c) 2011 Stefano Sabatini * Copyright (c) 2010 S.N. Hemanth Meenakshisundaram * Copyright (c) 2003 Gustavo Sverzut Barbieri @@ -72,16 +73,26 @@ #include FT_GLYPH_H #include FT_STROKER_H +#include +#include + +// Ceiling operation for positive integers division +#define POS_CEIL(x, y) ((x)/(y) + ((x)%(y) != 0)) + static const char *const var_names[] = { "dar", "hsub", "vsub", - "line_h", "lh", ///< line height, same as max_glyph_h + "line_h", "lh", ///< line height "main_h", "h", "H", ///< height of the input video "main_w", "w", "W", ///< width of the input video - "max_glyph_a", "ascent", ///< max glyph ascent - "max_glyph_d", "descent", ///< min glyph descent + "max_glyph_a", "ascent", ///< max glyph ascender + "max_glyph_d", "descent", ///< min glyph descender "max_glyph_h", ///< max glyph height "max_glyph_w", ///< max glyph width + "font_a", ///< font-defined ascent + "font_d", ///< font-defined descent + "top_a", ///< max glyph ascender of the top line + "bottom_d", ///< max glyph descender of the bottom line "n", ///< number of frame "sar", "t", ///< timestamp expressed in seconds @@ -90,11 +101,15 @@ static const char *const var_names[] = { "x", "y", "pict_type", +#if FF_API_FRAME_PKT "pkt_pos", +#endif #if FF_API_PKT_DURATION "pkt_duration", #endif +#if FF_API_FRAME_PKT "pkt_size", +#endif "duration", NULL }; @@ -125,6 +140,10 @@ enum var_name { VAR_MAX_GLYPH_D, VAR_DESCENT, VAR_MAX_GLYPH_H, VAR_MAX_GLYPH_W, + VAR_FONT_A, + VAR_FONT_D, + VAR_TOP_A, + VAR_BOTTOM_D, VAR_N, VAR_SAR, VAR_T, @@ -133,11 +152,15 @@ enum var_name { VAR_X, VAR_Y, VAR_PICT_TYPE, +#if FF_API_FRAME_PKT VAR_PKT_POS, +#endif #if FF_API_PKT_DURATION VAR_PKT_DURATION, #endif +#if FF_API_FRAME_PKT VAR_PKT_SIZE, +#endif VAR_DURATION, VAR_VARS_NB }; @@ -148,12 +171,91 @@ enum expansion_mode { EXP_STRFTIME, }; +enum y_alignment { + YA_TEXT, + YA_BASELINE, + YA_FONT, +}; + +enum text_alignment { + TA_LEFT = (1 << 0), + TA_RIGHT = (1 << 1), + TA_TOP = (1 << 2), + TA_BOTTOM = (1 << 3), +}; + +typedef struct HarfbuzzData { + hb_buffer_t* buf; + hb_font_t* font; + unsigned int glyph_count; + hb_glyph_info_t* glyph_info; + hb_glyph_position_t* glyph_pos; +} HarfbuzzData; + +/** Information about a single glyph in a text line */ +typedef struct GlyphInfo { + uint32_t code; ///< the glyph code point + int x; ///< the x position of the glyph + int y; ///< the y position of the glyph + int shift_x64; ///< the horizontal shift of the glyph in 26.6 units + int shift_y64; ///< the vertical shift of the glyph in 26.6 units +} GlyphInfo; + +/** Information about a single line of text */ +typedef struct TextLine { + int offset_left64; ///< offset between the origin and + /// the leftmost pixel of the first glyph + int offset_right64; ///< maximum offset between the origin and + /// the rightmost pixel of the last glyph + int width64; ///< width of the line + HarfbuzzData hb_data; ///< libharfbuzz data of this text line + GlyphInfo* glyphs; ///< array of glyphs in this text line + int cluster_offset; ///< the offset at which this line begins +} TextLine; + +/** A glyph as loaded and rendered using libfreetype */ +typedef struct Glyph { + FT_Glyph glyph; + FT_Glyph border_glyph; + uint32_t code; + unsigned int fontsize; + /** Glyph bitmaps with 1/4 pixel precision in both directions */ + FT_BitmapGlyph bglyph[16]; + /** Outlined glyph bitmaps with 1/4 pixel precision in both directions */ + FT_BitmapGlyph border_bglyph[16]; + FT_BBox bbox; +} Glyph; + +/** Global text metrics */ +typedef struct TextMetrics { + int offset_top64; ///< ascender amount of the first line (in 26.6 units) + int offset_bottom64; ///< descender amount of the last line (in 26.6 units) + int offset_left64; ///< maximum offset between the origin and + /// the leftmost pixel of the first glyph + /// of each line (in 26.6 units) + int offset_right64; ///< maximum offset between the origin and + /// the rightmost pixel of the last glyph + /// of each line (in 26.6 units) + int line_height64; ///< the font-defined line height + int width; ///< width of the longest line - ceil(width64/64) + int height; ///< total height of the text - ceil(height64/64) + + int min_y64; ///< minimum value of bbox.yMin among glyphs (in 26.6 units) + int max_y64; ///< maximum value of bbox.yMax among glyphs (in 26.6 units) + int min_x64; ///< minimum value of bbox.xMin among glyphs (in 26.6 units) + int max_x64; ///< maximum value of bbox.xMax among glyphs (in 26.6 units) + + // Position of the background box (without borders) + int rect_x; ///< x position of the box + int rect_y; ///< y position of the box +} TextMetrics; + typedef struct DrawTextContext { const AVClass *class; int exp_mode; ///< expansion mode to use for the text int reinit; ///< tells if the filter is being reinited #if CONFIG_LIBFONTCONFIG - uint8_t *font; ///< font to be used + uint8_t *font; ///< font to be used #endif uint8_t *fontfile; ///< font to be used uint8_t *text; ///< text to be drawn @@ -161,11 +263,9 @@ typedef struct DrawTextContext { uint8_t *fontcolor_expr; ///< fontcolor expression to evaluate AVBPrint expanded_fontcolor; ///< used to contain the expanded fontcolor spec int ft_load_flags; ///< flags used for loading fonts, see FT_LOAD_* - FT_Vector *positions; ///< positions for each element in the text - size_t nb_positions; ///< number of elements of positions array char *textfile; ///< file with text to be drawn - int x; ///< x position to start drawing text - int y; ///< y position to start drawing text + double x; ///< x position to start drawing text + double y; ///< y position to start drawing text int max_glyph_w; ///< max glyph width int max_glyph_h; ///< max glyph height int shadowx, shadowy; @@ -177,8 +277,14 @@ typedef struct DrawTextContext { int line_spacing; ///< lines spacing in pixels short int draw_box; ///< draw box around text - true or false - int boxborderw; ///< box border width - int use_kerning; ///< font kerning is used - true/false + char *boxborderw; ///< box border width (padding) + /// allowed formats: "all", "vert|oriz", "top|right|bottom|left" + int bb_top; ///< the size of the top box border + int bb_right; ///< the size of the right box border + int bb_bottom; ///< the size of the bottom box border + int bb_left; ///< the size of the left box border + int box_width; ///< the width of box + int box_height; ///< the height of box int tabsize; ///< tab size int fix_bounds; ///< do we let it go out of frame bounds - t/f @@ -213,31 +319,59 @@ typedef struct DrawTextContext { int text_shaping; ///< 1 to shape the text before drawing it #endif AVDictionary *metadata; + + int boxw; ///< the value of the boxw parameter + int boxh; ///< the value of the boxh parameter + int text_align; ///< the horizontal and vertical text alignment + int y_align; ///< the value of the y_align parameter + + TextLine *lines; ///< computed information about text lines + int line_count; ///< the number of text lines + uint32_t *tab_clusters; ///< the position of tab characters in the text + int tab_count; ///< the number of tab characters + int blank_advance64; ///< the size of the space character + int tab_warning_printed; ///< ensure the tab warning to be printed only once } DrawTextContext; #define OFFSET(x) offsetof(DrawTextContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM +#define TFLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption drawtext_options[]= { - {"fontfile", "set font file", OFFSET(fontfile), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS}, - {"text", "set text", OFFSET(text), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS}, - {"textfile", "set text file", OFFSET(textfile), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS}, - {"fontcolor", "set foreground color", OFFSET(fontcolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, 0, 0, FLAGS}, + {"fontfile", "set font file", OFFSET(fontfile), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS}, + {"text", "set text", OFFSET(text), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, TFLAGS}, + {"textfile", "set text file", OFFSET(textfile), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS}, + {"fontcolor", "set foreground color", OFFSET(fontcolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, 0, 0, TFLAGS}, {"fontcolor_expr", "set foreground color expression", OFFSET(fontcolor_expr), AV_OPT_TYPE_STRING, {.str=""}, 0, 0, FLAGS}, - {"boxcolor", "set box color", OFFSET(boxcolor.rgba), AV_OPT_TYPE_COLOR, {.str="white"}, 0, 0, FLAGS}, - {"bordercolor", "set border color", OFFSET(bordercolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, 0, 0, FLAGS}, - {"shadowcolor", "set shadow color", OFFSET(shadowcolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, 0, 0, FLAGS}, - {"box", "set box", OFFSET(draw_box), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 , FLAGS}, - {"boxborderw", "set box border width", OFFSET(boxborderw), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX , FLAGS}, - {"line_spacing", "set line spacing in pixels", OFFSET(line_spacing), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX,FLAGS}, - {"fontsize", "set font size", OFFSET(fontsize_expr), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0 , FLAGS}, - {"x", "set x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, FLAGS}, - {"y", "set y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, FLAGS}, - {"shadowx", "set shadow x offset", OFFSET(shadowx), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX , FLAGS}, - {"shadowy", "set shadow y offset", OFFSET(shadowy), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX , FLAGS}, - {"borderw", "set border width", OFFSET(borderw), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX , FLAGS}, - {"tabsize", "set tab size", OFFSET(tabsize), AV_OPT_TYPE_INT, {.i64=4}, 0, INT_MAX , FLAGS}, - {"basetime", "set base time", OFFSET(basetime), AV_OPT_TYPE_INT64, {.i64=AV_NOPTS_VALUE}, INT64_MIN, INT64_MAX , FLAGS}, + {"boxcolor", "set box color", OFFSET(boxcolor.rgba), AV_OPT_TYPE_COLOR, {.str="white"}, 0, 0, TFLAGS}, + {"bordercolor", "set border color", OFFSET(bordercolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, 0, 0, TFLAGS}, + {"shadowcolor", "set shadow color", OFFSET(shadowcolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, 0, 0, TFLAGS}, + {"box", "set box", OFFSET(draw_box), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, TFLAGS}, + {"boxborderw", "set box borders width", OFFSET(boxborderw), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, TFLAGS}, + {"line_spacing", "set line spacing in pixels", OFFSET(line_spacing), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, TFLAGS}, + {"fontsize", "set font size", OFFSET(fontsize_expr), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, TFLAGS}, + {"text_align", "set text alignment", OFFSET(text_align), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, (TA_LEFT|TA_RIGHT|TA_TOP|TA_BOTTOM), TFLAGS, "text_align"}, + { "left", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TA_LEFT }, .flags = TFLAGS, .unit = "text_align" }, + { "L", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TA_LEFT }, .flags = TFLAGS, .unit = "text_align" }, + { "right", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TA_RIGHT }, .flags = TFLAGS, .unit = "text_align" }, + { "R", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TA_RIGHT }, .flags = TFLAGS, .unit = "text_align" }, + { "center", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = (TA_LEFT|TA_RIGHT) }, .flags = TFLAGS, .unit = "text_align" }, + { "C", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = (TA_LEFT|TA_RIGHT) }, .flags = TFLAGS, .unit = "text_align" }, + { "top", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TA_TOP }, .flags = TFLAGS, .unit = "text_align" }, + { "T", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TA_TOP }, .flags = TFLAGS, .unit = "text_align" }, + { "bottom", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TA_BOTTOM }, .flags = TFLAGS, .unit = "text_align" }, + { "B", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TA_BOTTOM }, .flags = TFLAGS, .unit = "text_align" }, + { "middle", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = (TA_TOP|TA_BOTTOM) }, .flags = TFLAGS, .unit = "text_align" }, + { "M", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = (TA_TOP|TA_BOTTOM) }, .flags = TFLAGS, .unit = "text_align" }, + {"x", "set x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, TFLAGS}, + {"y", "set y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, TFLAGS}, + {"boxw", "set box width", OFFSET(boxw), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, TFLAGS}, + {"boxh", "set box height", OFFSET(boxh), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, TFLAGS}, + {"shadowx", "set shadow x offset", OFFSET(shadowx), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, TFLAGS}, + {"shadowy", "set shadow y offset", OFFSET(shadowy), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, TFLAGS}, + {"borderw", "set border width", OFFSET(borderw), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, TFLAGS}, + {"tabsize", "set tab size", OFFSET(tabsize), AV_OPT_TYPE_INT, {.i64=4}, 0, INT_MAX, TFLAGS}, + {"basetime", "set base time", OFFSET(basetime), AV_OPT_TYPE_INT64, {.i64=AV_NOPTS_VALUE}, INT64_MIN, INT64_MAX, FLAGS}, #if CONFIG_LIBFONTCONFIG { "font", "Font name", OFFSET(font), AV_OPT_TYPE_STRING, { .str = "Sans" }, .flags = FLAGS }, #endif @@ -246,17 +380,21 @@ static const AVOption drawtext_options[]= { {"none", "set no expansion", OFFSET(exp_mode), AV_OPT_TYPE_CONST, {.i64=EXP_NONE}, 0, 0, FLAGS, "expansion"}, {"normal", "set normal expansion", OFFSET(exp_mode), AV_OPT_TYPE_CONST, {.i64=EXP_NORMAL}, 0, 0, FLAGS, "expansion"}, {"strftime", "set strftime expansion (deprecated)", OFFSET(exp_mode), AV_OPT_TYPE_CONST, {.i64=EXP_STRFTIME}, 0, 0, FLAGS, "expansion"}, + {"y_align", "set the y alignment", OFFSET(y_align), AV_OPT_TYPE_INT, {.i64=YA_TEXT}, 0, 2, TFLAGS, "y_align"}, + {"text", "y is referred to the top of the first text line", OFFSET(y_align), AV_OPT_TYPE_CONST, {.i64=YA_TEXT}, 0, 0, FLAGS, "y_align"}, + {"baseline", "y is referred to the baseline of the first line", OFFSET(y_align), AV_OPT_TYPE_CONST, {.i64=YA_BASELINE}, 0, 0, FLAGS, "y_align"}, + {"font", "y is referred to the font defined line metrics", OFFSET(y_align), AV_OPT_TYPE_CONST, {.i64=YA_FONT}, 0, 0, FLAGS, "y_align"}, {"timecode", "set initial timecode", OFFSET(tc_opt_string), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS}, - {"tc24hmax", "set 24 hours max (timecode only)", OFFSET(tc24hmax), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, - {"timecode_rate", "set rate (timecode only)", OFFSET(tc_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, FLAGS}, - {"r", "set rate (timecode only)", OFFSET(tc_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, FLAGS}, - {"rate", "set rate (timecode only)", OFFSET(tc_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, FLAGS}, - {"reload", "reload text file at specified frame interval", OFFSET(reload), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS}, - { "alpha", "apply alpha while rendering", OFFSET(a_expr), AV_OPT_TYPE_STRING, { .str = "1" }, .flags = FLAGS }, - {"fix_bounds", "check and fix text coords to avoid clipping", OFFSET(fix_bounds), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, - {"start_number", "start frame number for n/frame_num variable", OFFSET(start_number), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS}, - {"text_source", "the source of text", OFFSET(text_source_string), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS }, + {"tc24hmax", "set 24 hours max (timecode only)", OFFSET(tc24hmax), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"timecode_rate", "set rate (timecode only)", OFFSET(tc_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, FLAGS}, + {"r", "set rate (timecode only)", OFFSET(tc_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, FLAGS}, + {"rate", "set rate (timecode only)", OFFSET(tc_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, FLAGS}, + {"reload", "reload text file at specified frame interval", OFFSET(reload), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS}, + {"alpha", "apply alpha while rendering", OFFSET(a_expr), AV_OPT_TYPE_STRING, {.str = "1"}, .flags = TFLAGS}, + {"fix_bounds", "check and fix text coords to avoid clipping", OFFSET(fix_bounds), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"start_number", "start frame number for n/frame_num variable", OFFSET(start_number), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS}, + {"text_source", "the source of text", OFFSET(text_source_string), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS }, #if CONFIG_LIBFRIBIDI {"text_shaping", "attempt to shape text before drawing", OFFSET(text_shaping), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS}, @@ -297,99 +435,15 @@ static const struct ft_error { #define FT_ERRMSG(e) ft_errors[e].err_msg -typedef struct Glyph { - FT_Glyph glyph; - FT_Glyph border_glyph; - uint32_t code; - unsigned int fontsize; - FT_Bitmap bitmap; ///< array holding bitmaps of font - FT_Bitmap border_bitmap; ///< array holding bitmaps of font border - FT_BBox bbox; - int advance; - int bitmap_left; - int bitmap_top; -} Glyph; - static int glyph_cmp(const void *key, const void *b) { const Glyph *a = key, *bb = b; int64_t diff = (int64_t)a->code - (int64_t)bb->code; if (diff != 0) - return diff > 0 ? 1 : -1; + return diff > 0 ? 1 : -1; else - return FFDIFFSIGN((int64_t)a->fontsize, (int64_t)bb->fontsize); -} - -/** - * Load glyphs corresponding to the UTF-32 codepoint code. - */ -static int load_glyph(AVFilterContext *ctx, Glyph **glyph_ptr, uint32_t code) -{ - DrawTextContext *s = ctx->priv; - FT_BitmapGlyph bitmapglyph; - Glyph *glyph; - struct AVTreeNode *node = NULL; - int ret; - - /* load glyph into s->face->glyph */ - if (FT_Load_Char(s->face, code, s->ft_load_flags)) - return AVERROR(EINVAL); - - glyph = av_mallocz(sizeof(*glyph)); - if (!glyph) { - ret = AVERROR(ENOMEM); - goto error; - } - glyph->code = code; - glyph->fontsize = s->fontsize; - - if (FT_Get_Glyph(s->face->glyph, &glyph->glyph)) { - ret = AVERROR(EINVAL); - goto error; - } - if (s->borderw) { - glyph->border_glyph = glyph->glyph; - if (FT_Glyph_StrokeBorder(&glyph->border_glyph, s->stroker, 0, 0) || - FT_Glyph_To_Bitmap(&glyph->border_glyph, FT_RENDER_MODE_NORMAL, 0, 1)) { - ret = AVERROR_EXTERNAL; - goto error; - } - bitmapglyph = (FT_BitmapGlyph) glyph->border_glyph; - glyph->border_bitmap = bitmapglyph->bitmap; - } - if (FT_Glyph_To_Bitmap(&glyph->glyph, FT_RENDER_MODE_NORMAL, 0, 1)) { - ret = AVERROR_EXTERNAL; - goto error; - } - bitmapglyph = (FT_BitmapGlyph) glyph->glyph; - - glyph->bitmap = bitmapglyph->bitmap; - glyph->bitmap_left = bitmapglyph->left; - glyph->bitmap_top = bitmapglyph->top; - glyph->advance = s->face->glyph->advance.x >> 6; - - /* measure text height to calculate text_height (or the maximum text height) */ - FT_Glyph_Get_CBox(glyph->glyph, ft_glyph_bbox_pixels, &glyph->bbox); - - /* cache the newly created glyph */ - if (!(node = av_tree_node_alloc())) { - ret = AVERROR(ENOMEM); - goto error; - } - av_tree_insert(&s->glyphs, glyph, glyph_cmp, &node); - - if (glyph_ptr) - *glyph_ptr = glyph; - return 0; - -error: - if (glyph) - av_freep(&glyph->glyph); - - av_freep(&glyph); - av_freep(&node); - return ret; + return FFDIFFSIGN((int64_t)a->fontsize, (int64_t)bb->fontsize); } static av_cold int set_fontsize(AVFilterContext *ctx, unsigned int fontsize) @@ -439,7 +493,6 @@ static av_cold int update_fontsize(AVFilterContext *ctx) return err; size = av_expr_eval(s->fontsize_pexpr, s->var_values, &s->prng); - if (!isnan(size)) { roundedsize = round(size); // test for overflow before cast @@ -447,7 +500,6 @@ static av_cold int update_fontsize(AVFilterContext *ctx) av_log(ctx, AV_LOG_ERROR, "fontsize overflow\n"); return AVERROR(EINVAL); } - fontsize = roundedsize; } } @@ -548,7 +600,7 @@ static int load_font_fontconfig(AVFilterContext *ctx) goto fail; } - av_log(ctx, AV_LOG_INFO, "Using \"%s\"\n", filename); + av_log(ctx, AV_LOG_VERBOSE, "Using \"%s\"\n", filename); if (parse_err) s->default_fontsize = size + 0.5; @@ -690,6 +742,7 @@ static int shape_text(AVFilterContext *ctx) s->text = tmp; len = fribidi_unicode_to_charset(FRIBIDI_CHAR_SET_UTF8, unicodestr, len, s->text); + ret = 0; out: @@ -711,11 +764,131 @@ static enum AVFrameSideDataType text_source_string_parse(const char *text_source } } +static inline int get_subpixel_idx(int shift_x64, int shift_y64) +{ + int idx = (shift_x64 >> 2) + (shift_y64 >> 4); + return idx; +} + +// Loads and (optionally) renders a glyph +static int load_glyph(AVFilterContext *ctx, Glyph **glyph_ptr, uint32_t code, int8_t shift_x64, int8_t shift_y64) +{ + DrawTextContext *s = ctx->priv; + Glyph dummy = { 0 }; + Glyph *glyph; + FT_Vector shift; + struct AVTreeNode *node = NULL; + int ret = 0; + + /* get glyph */ + dummy.code = code; + dummy.fontsize = s->fontsize; + glyph = av_tree_find(s->glyphs, &dummy, glyph_cmp, NULL); + if (!glyph) { + if (FT_Load_Glyph(s->face, code, s->ft_load_flags)) { + return AVERROR(EINVAL); + } + glyph = av_mallocz(sizeof(*glyph)); + if (!glyph) { + ret = AVERROR(ENOMEM); + goto error; + } + glyph->code = code; + glyph->fontsize = s->fontsize; + if (FT_Get_Glyph(s->face->glyph, &glyph->glyph)) { + ret = AVERROR(EINVAL); + goto error; + } + if (s->borderw) { + glyph->border_glyph = glyph->glyph; + if (FT_Glyph_StrokeBorder(&glyph->border_glyph, s->stroker, 0, 0)) { + ret = AVERROR_EXTERNAL; + goto error; + } + } + /* measure text height to calculate text_height (or the maximum text height) */ + FT_Glyph_Get_CBox(glyph->glyph, FT_GLYPH_BBOX_SUBPIXELS, &glyph->bbox); + + /* cache the newly created glyph */ + if (!(node = av_tree_node_alloc())) { + ret = AVERROR(ENOMEM); + goto error; + } + av_tree_insert(&s->glyphs, glyph, glyph_cmp, &node); + } else { + if (s->borderw && !glyph->border_glyph) { + glyph->border_glyph = glyph->glyph; + if (FT_Glyph_StrokeBorder(&glyph->border_glyph, s->stroker, 0, 0)) { + ret = AVERROR_EXTERNAL; + goto error; + } + } + } + + // Check if a bitmap is needed + if (shift_x64 >= 0 && shift_y64 >= 0) { + // Get the bitmap subpixel index (0 -> 15) + int idx = get_subpixel_idx(shift_x64, shift_y64); + shift.x = shift_x64; + shift.y = shift_y64; + + if (!glyph->bglyph[idx]) { + FT_Glyph tmp_glyph = glyph->glyph; + if (FT_Glyph_To_Bitmap(&tmp_glyph, FT_RENDER_MODE_NORMAL, &shift, 0)) { + ret = AVERROR_EXTERNAL; + goto error; + } + glyph->bglyph[idx] = (FT_BitmapGlyph)tmp_glyph; + if (glyph->bglyph[idx]->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { + av_log(ctx, AV_LOG_ERROR, "Monocromatic (1bpp) fonts are not supported.\n"); + ret = AVERROR(EINVAL); + goto error; + } + } + if (s->borderw && !glyph->border_bglyph[idx]) { + FT_Glyph tmp_glyph = glyph->border_glyph; + if (FT_Glyph_To_Bitmap(&tmp_glyph, FT_RENDER_MODE_NORMAL, &shift, 0)) { + ret = AVERROR_EXTERNAL; + goto error; + } + glyph->border_bglyph[idx] = (FT_BitmapGlyph)tmp_glyph; + } + } + if (glyph_ptr) { + *glyph_ptr = glyph; + } + return 0; + +error: + if (glyph && glyph->glyph) + FT_Done_Glyph(glyph->glyph); + + av_freep(&glyph); + av_freep(&node); + return ret; +} + +// Convert a string formatted as "n1|n2|...|nN" into an integer array +static int string_to_array(const char *source, int *result, int result_size) +{ + int counter = 0, size = strlen(source) + 1; + char *saveptr, *curval, *dup = av_malloc(size); + if (!dup) + return 0; + av_strlcpy(dup, source, size); + if (result_size > 0 && (curval = av_strtok(dup, "|", &saveptr))) { + do { + result[counter++] = atoi(curval); + } while ((curval = av_strtok(NULL, "|", &saveptr)) && counter < result_size); + } + av_free(dup); + return counter; +} + static av_cold int init(AVFilterContext *ctx) { int err; DrawTextContext *s = ctx->priv; - Glyph *glyph; av_expr_free(s->fontsize_pexpr); s->fontsize_pexpr = NULL; @@ -795,26 +968,19 @@ static av_cold int init(AVFilterContext *ctx) if ((err = update_fontsize(ctx)) < 0) return err; + // Always init the stroker, may be needed if borderw is set via command + if (FT_Stroker_New(s->library, &s->stroker)) { + av_log(ctx, AV_LOG_ERROR, "Could not init FT stroker\n"); + return AVERROR_EXTERNAL; + } + if (s->borderw) { - if (FT_Stroker_New(s->library, &s->stroker)) { - av_log(ctx, AV_LOG_ERROR, "Coult not init FT stroker\n"); - return AVERROR_EXTERNAL; - } FT_Stroker_Set(s->stroker, s->borderw << 6, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); } - s->use_kerning = FT_HAS_KERNING(s->face); - /* load the fallback glyph with code 0 */ - load_glyph(ctx, NULL, 0); - - /* set the tabsize in pixels */ - if ((err = load_glyph(ctx, &glyph, ' ')) < 0) { - av_log(ctx, AV_LOG_ERROR, "Could not set tabsize.\n"); - return err; - } - s->tabsize *= glyph->advance; + load_glyph(ctx, NULL, 0, 0, 0); if (s->exp_mode == EXP_STRFTIME && (strchr(s->text, '%') || strchr(s->text, '\\'))) @@ -831,12 +997,37 @@ static int query_formats(AVFilterContext *ctx) return ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0)); } +static int glyph_enu_border_free(void *opaque, void *elem) +{ + Glyph *glyph = elem; + + if (glyph->border_glyph != NULL) { + for (int t = 0; t < 16; ++t) { + if (glyph->border_bglyph[t] != NULL) { + FT_Done_Glyph((FT_Glyph)glyph->border_bglyph[t]); + glyph->border_bglyph[t] = NULL; + } + } + FT_Done_Glyph(glyph->border_glyph); + glyph->border_glyph = NULL; + } + return 0; +} + static int glyph_enu_free(void *opaque, void *elem) { Glyph *glyph = elem; FT_Done_Glyph(glyph->glyph); FT_Done_Glyph(glyph->border_glyph); + for (int t = 0; t < 16; ++t) { + if (glyph->bglyph[t] != NULL) { + FT_Done_Glyph((FT_Glyph)glyph->bglyph[t]); + } + if (glyph->border_bglyph[t] != NULL) { + FT_Done_Glyph((FT_Glyph)glyph->border_bglyph[t]); + } + } av_free(elem); return 0; } @@ -852,9 +1043,6 @@ static av_cold void uninit(AVFilterContext *ctx) s->x_pexpr = s->y_pexpr = s->a_pexpr = s->fontsize_pexpr = NULL; - av_freep(&s->positions); - s->nb_positions = 0; - av_tree_enumerate(s->glyphs, NULL, NULL, glyph_enu_free); av_tree_destroy(s->glyphs); s->glyphs = NULL; @@ -880,15 +1068,15 @@ static int config_input(AVFilterLink *inlink) ff_draw_color(&s->dc, &s->bordercolor, s->bordercolor.rgba); ff_draw_color(&s->dc, &s->boxcolor, s->boxcolor.rgba); - s->var_values[VAR_w] = s->var_values[VAR_W] = s->var_values[VAR_MAIN_W] = inlink->w; - s->var_values[VAR_h] = s->var_values[VAR_H] = s->var_values[VAR_MAIN_H] = inlink->h; - s->var_values[VAR_SAR] = inlink->sample_aspect_ratio.num ? av_q2d(inlink->sample_aspect_ratio) : 1; - s->var_values[VAR_DAR] = (double)inlink->w / inlink->h * s->var_values[VAR_SAR]; - s->var_values[VAR_HSUB] = 1 << s->dc.hsub_max; - s->var_values[VAR_VSUB] = 1 << s->dc.vsub_max; - s->var_values[VAR_X] = NAN; - s->var_values[VAR_Y] = NAN; - s->var_values[VAR_T] = NAN; + s->var_values[VAR_w] = s->var_values[VAR_W] = s->var_values[VAR_MAIN_W] = inlink->w; + s->var_values[VAR_h] = s->var_values[VAR_H] = s->var_values[VAR_MAIN_H] = inlink->h; + s->var_values[VAR_SAR] = inlink->sample_aspect_ratio.num ? av_q2d(inlink->sample_aspect_ratio) : 1; + s->var_values[VAR_DAR] = (double)inlink->w / inlink->h * s->var_values[VAR_SAR]; + s->var_values[VAR_HSUB] = 1 << s->dc.hsub_max; + s->var_values[VAR_VSUB] = 1 << s->dc.vsub_max; + s->var_values[VAR_X] = NAN; + s->var_values[VAR_Y] = NAN; + s->var_values[VAR_T] = NAN; av_lfg_init(&s->prng, av_get_random_seed()); @@ -948,8 +1136,23 @@ static int command(AVFilterContext *ctx, const char *cmd, const char *arg, char ctx->priv = new; return config_input(ctx->inputs[0]); - } else - return AVERROR(ENOSYS); + } else { + int old_borderw = old->borderw; + if ((ret = ff_filter_process_command(ctx, cmd, arg, res, res_len, flags)) < 0) { + return ret; + } + if (old->borderw != old_borderw) { + FT_Stroker_Set(old->stroker, old->borderw << 6, FT_STROKER_LINECAP_ROUND, + FT_STROKER_LINEJOIN_ROUND, 0); + // Dispose the old border glyphs + av_tree_enumerate(old->glyphs, NULL, NULL, glyph_enu_border_free); + } else if (strcmp(cmd, "fontsize") == 0) { + av_expr_free(old->fontsize_pexpr); + old->fontsize_pexpr = NULL; + old->blank_advance64 = 0; + } + return config_input(ctx->inputs[0]); + } fail: av_log(ctx, AV_LOG_ERROR, "Failed to process command. Continuing with existing parameters.\n"); @@ -1318,91 +1521,310 @@ static int expand_text(AVFilterContext *ctx, char *text, AVBPrint *bp) return 0; } +static void update_color_with_alpha(DrawTextContext *s, FFDrawColor *color, const FFDrawColor incolor) +{ + *color = incolor; + color->rgba[3] = (color->rgba[3] * s->alpha) / 255; + ff_draw_color(&s->dc, color, color->rgba); +} + +static void update_alpha(DrawTextContext *s) +{ + double alpha = av_expr_eval(s->a_pexpr, s->var_values, &s->prng); + + if (isnan(alpha)) + return; + + if (alpha >= 1.0) + s->alpha = 255; + else if (alpha <= 0) + s->alpha = 0; + else + s->alpha = 256 * alpha; +} + static int draw_glyphs(DrawTextContext *s, AVFrame *frame, - int width, int height, FFDrawColor *color, + TextMetrics *metrics, int x, int y, int borderw) { - char *text = s->expanded_text.str; - uint32_t code = 0; - int i, x1, y1; - uint8_t *p; - Glyph *glyph = NULL; + int g, l, x1, y1, w1, h1, idx; + int dx = 0, dy = 0, pdx = 0; + GlyphInfo *info; + Glyph dummy = { 0 }, *glyph; + FT_Bitmap bitmap; + FT_BitmapGlyph b_glyph; + uint8_t j_left = 0, j_right = 0, j_top = 0, j_bottom = 0; + int line_w, offset_y = 0; + int clip_x = 0, clip_y = 0; + + j_left = !!(s->text_align & TA_LEFT); + j_right = !!(s->text_align & TA_RIGHT); + j_top = !!(s->text_align & TA_TOP); + j_bottom = !!(s->text_align & TA_BOTTOM); + + if (j_top && j_bottom) { + offset_y = (s->box_height - metrics->height) / 2; + } else if (j_bottom) { + offset_y = s->box_height - metrics->height; + } - for (i = 0, p = text; *p; i++) { - FT_Bitmap bitmap; - Glyph dummy = { 0 }; - GET_UTF8(code, *p ? *p++ : 0, code = 0xfffd; goto continue_on_invalid;); -continue_on_invalid: + if ((!j_left || j_right) && !s->tab_warning_printed && s->tab_count > 0) { + s->tab_warning_printed = 1; + av_log(s, AV_LOG_WARNING, "Tab characters are only supported with left horizontal alignment\n"); + } - /* skip new line chars, just go to new line */ - if (code == '\n' || code == '\r' || code == '\t') - continue; + clip_x = FFMIN(metrics->rect_x + s->box_width + s->bb_right, frame->width); + clip_y = FFMIN(metrics->rect_y + s->box_height + s->bb_bottom, frame->height); + + for (l = 0; l < s->line_count; ++l) { + TextLine *line = &s->lines[l]; + line_w = POS_CEIL(line->width64, 64); + for (g = 0; g < line->hb_data.glyph_count; ++g) { + info = &line->glyphs[g]; + dummy.fontsize = s->fontsize; + dummy.code = info->code; + glyph = av_tree_find(s->glyphs, &dummy, glyph_cmp, NULL); + if (!glyph) { + return AVERROR(EINVAL); + } - dummy.code = code; - dummy.fontsize = s->fontsize; - glyph = av_tree_find(s->glyphs, &dummy, glyph_cmp, NULL); + idx = get_subpixel_idx(info->shift_x64, info->shift_y64); + b_glyph = borderw ? glyph->border_bglyph[idx] : glyph->bglyph[idx]; + bitmap = b_glyph->bitmap; + x1 = x + info->x + b_glyph->left; + y1 = y + info->y - b_glyph->top + offset_y; + w1 = bitmap.width; + h1 = bitmap.rows; + + if (j_left && j_right) { + x1 += (s->box_width - line_w) / 2; + } else if (j_right) { + x1 += s->box_width - line_w; + } - bitmap = borderw ? glyph->border_bitmap : glyph->bitmap; + // Offset of the glyph's bitmap in the visible region + dx = dy = 0; + if (x1 < metrics->rect_x - s->bb_left) { + dx = metrics->rect_x - s->bb_left - x1; + x1 = metrics->rect_x - s->bb_left; + } + if (y1 < metrics->rect_y - s->bb_top) { + dy = metrics->rect_y - s->bb_top - y1; + y1 = metrics->rect_y - s->bb_top; + } - if (glyph->bitmap.pixel_mode != FT_PIXEL_MODE_MONO && - glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY) - return AVERROR(EINVAL); + // check if the glyph is empty or out of the clipping region + if (dx >= w1 || dy >= h1 || x1 >= clip_x || y1 >= clip_y) { + continue; + } - x1 = s->positions[i].x+s->x+x - borderw; - y1 = s->positions[i].y+s->y+y - borderw; + pdx = dx + dy * bitmap.pitch; + w1 = FFMIN(clip_x - x1, w1 - dx); + h1 = FFMIN(clip_y - y1, h1 - dy); - ff_blend_mask(&s->dc, color, - frame->data, frame->linesize, width, height, - bitmap.buffer, bitmap.pitch, - bitmap.width, bitmap.rows, - bitmap.pixel_mode == FT_PIXEL_MODE_MONO ? 0 : 3, - 0, x1, y1); + ff_blend_mask(&s->dc, color, frame->data, frame->linesize, clip_x, clip_y, + bitmap.buffer + pdx, bitmap.pitch, w1, h1, 3, 0, x1, y1); + } } return 0; } +// Shapes a line of text using libharfbuzz +static int shape_text_hb(DrawTextContext *s, HarfbuzzData* hb, const char* text, int textLen) +{ + hb->buf = hb_buffer_create(); + if(!hb_buffer_allocation_successful(hb->buf)) { + return AVERROR(ENOMEM); + } + hb_buffer_set_direction(hb->buf, HB_DIRECTION_LTR); + hb_buffer_set_script(hb->buf, HB_SCRIPT_LATIN); + hb_buffer_set_language(hb->buf, hb_language_from_string("en", -1)); + hb_buffer_guess_segment_properties(hb->buf); + hb->font = hb_ft_font_create(s->face, NULL); + if(hb->font == NULL) { + return AVERROR(ENOMEM); + } + hb_ft_font_set_funcs(hb->font); + hb_buffer_add_utf8(hb->buf, text, textLen, 0, -1); + hb_shape(hb->font, hb->buf, NULL, 0); + hb->glyph_info = hb_buffer_get_glyph_infos(hb->buf, &hb->glyph_count); + hb->glyph_pos = hb_buffer_get_glyph_positions(hb->buf, &hb->glyph_count); -static void update_color_with_alpha(DrawTextContext *s, FFDrawColor *color, const FFDrawColor incolor) + return 0; +} + +static void hb_destroy(HarfbuzzData *hb) { - *color = incolor; - color->rgba[3] = (color->rgba[3] * s->alpha) / 255; - ff_draw_color(&s->dc, color, color->rgba); + hb_buffer_destroy(hb->buf); + hb_font_destroy(hb->font); + hb->buf = NULL; + hb->font = NULL; + hb->glyph_info = NULL; + hb->glyph_pos = NULL; } -static void update_alpha(DrawTextContext *s) +static int measure_text(AVFilterContext *ctx, TextMetrics *metrics) { - double alpha = av_expr_eval(s->a_pexpr, s->var_values, &s->prng); + DrawTextContext *s = ctx->priv; + char *text = s->expanded_text.str; + char *textdup = NULL, *start = NULL; + int num_chars = 0; + int width64 = 0, w64 = 0; + int cur_min_y64 = 0, first_max_y64 = -32000; + int first_min_x64 = 32000, last_max_x64 = -32000; + int min_y64 = 32000, max_y64 = -32000, min_x64 = 32000, max_x64 = -32000; + int line_count = 0; + uint32_t code = 0; + Glyph *glyph = NULL; - if (isnan(alpha)) - return; + int i, tab_idx = 0, last_tab_idx = 0, line_offset = 0; + char* p; + int ret = 0; + + // Count the lines and the tab characters + s->tab_count = 0; + for (i = 0, p = text; 1; i++) { + GET_UTF8(code, *p ? *p++ : 0, code = 0xfffd; goto continue_on_failed;); +continue_on_failed: + if (is_newline(code) || code == 0) { + ++line_count; + if (code == 0) { + break; + } + } else if (code == '\t') { + ++s->tab_count; + } + } - if (alpha >= 1.0) - s->alpha = 255; - else if (alpha <= 0) - s->alpha = 0; - else - s->alpha = 256 * alpha; + // Evaluate the width of the space character if needed to replace tabs + if (s->tab_count > 0 && !s->blank_advance64) { + HarfbuzzData hb_data; + ret = shape_text_hb(s, &hb_data, " ", 1); + if(ret != 0) { + goto done; + } + s->blank_advance64 = hb_data.glyph_pos[0].x_advance; + hb_destroy(&hb_data); + } + + s->line_count = line_count; + s->lines = av_mallocz(line_count * sizeof(TextLine)); + s->tab_clusters = av_mallocz(s->tab_count * sizeof(uint32_t)); + for (i = 0; i < s->tab_count; ++i) { + s->tab_clusters[i] = -1; + } + + start = textdup = av_strdup(text); + if (textdup == NULL) { + ret = AVERROR(ENOMEM); + goto done; + } + line_count = 0; + for (i = 0, p = textdup; 1; i++) { + if (*p == '\t') { + s->tab_clusters[tab_idx++] = i; + *p = ' '; + } + GET_UTF8(code, *p ? *p++ : 0, code = 0xfffd; goto continue_on_failed2;); +continue_on_failed2: + if (is_newline(code) || code == 0) { + TextLine *cur_line = &s->lines[line_count]; + HarfbuzzData *hb = &cur_line->hb_data; + cur_line->cluster_offset = line_offset; + ret = shape_text_hb(s, hb, start, num_chars); + if (ret != 0) { + goto done; + } + w64 = 0; + cur_min_y64 = 32000; + for (int t = 0; t < hb->glyph_count; ++t) { + uint8_t is_tab = last_tab_idx < s->tab_count && + hb->glyph_info[t].cluster == s->tab_clusters[last_tab_idx] - line_offset; + if (is_tab) { + ++last_tab_idx; + } + ret = load_glyph(ctx, &glyph, hb->glyph_info[t].codepoint, -1, -1); + if (ret != 0) { + goto done; + } + if (line_count == 0) { + first_max_y64 = FFMAX(glyph->bbox.yMax, first_max_y64); + } + if (t == 0) { + cur_line->offset_left64 = glyph->bbox.xMin; + first_min_x64 = FFMIN(glyph->bbox.xMin, first_min_x64); + } + if (t == hb->glyph_count - 1) { + w64 += glyph->bbox.xMax; + last_max_x64 = FFMAX(glyph->bbox.xMax, last_max_x64); + cur_line->offset_right64 = glyph->bbox.xMax; + } else { + if (is_tab) { + int size = s->blank_advance64 * s->tabsize; + w64 = (w64 / size + 1) * size; + } else { + w64 += hb->glyph_pos[t].x_advance; + } + } + cur_min_y64 = FFMIN(glyph->bbox.yMin, cur_min_y64); + min_y64 = FFMIN(glyph->bbox.yMin, min_y64); + max_y64 = FFMAX(glyph->bbox.yMax, max_y64); + min_x64 = FFMIN(glyph->bbox.xMin, min_x64); + max_x64 = FFMAX(glyph->bbox.xMax, max_x64); + } + + cur_line->width64 = w64; + + av_log(s, AV_LOG_DEBUG, " Line: %d -- glyphs count: %d - width64: %d - offset_left64: %d - offset_right64: %d)\n", + line_count, hb->glyph_count, cur_line->width64, cur_line->offset_left64, cur_line->offset_right64); + + if (w64 > width64) { + width64 = w64; + } + num_chars = -1; + start = p; + ++line_count; + line_offset = i + 1; + } + + if (code == 0) break; + ++num_chars; + } + + metrics->line_height64 = s->face->size->metrics.height; + + metrics->width = POS_CEIL(width64, 64); + if (s->y_align == YA_FONT) { + metrics->height = POS_CEIL(metrics->line_height64 * line_count, 64); + } else { + int height64 = (metrics->line_height64 + s->line_spacing * 64) * + (FFMAX(0, line_count - 1)) + first_max_y64 - cur_min_y64; + metrics->height = POS_CEIL(height64, 64); + } + metrics->offset_top64 = first_max_y64; + metrics->offset_right64 = last_max_x64; + metrics->offset_bottom64 = cur_min_y64; + metrics->offset_left64 = first_min_x64; + metrics->min_x64 = min_x64; + metrics->min_y64 = min_y64; + metrics->max_x64 = max_x64; + metrics->max_y64 = max_y64; + +done: + av_free(textdup); + return ret; } -static int draw_text(AVFilterContext *ctx, AVFrame *frame, - int width, int height) +static int draw_text(AVFilterContext *ctx, AVFrame *frame) { DrawTextContext *s = ctx->priv; AVFilterLink *inlink = ctx->inputs[0]; - - uint32_t code = 0, prev_code = 0; - int x = 0, y = 0, i = 0, ret; - int max_text_line_w = 0, len; - int box_w, box_h; - char *text; - uint8_t *p; - int y_min = 32000, y_max = -32000; - int x_min = 32000, x_max = -32000; - FT_Vector delta; - Glyph *glyph = NULL, *prev_glyph = NULL; - Glyph dummy = { 0 }; + int x = 0, y = 0, ret; + int shift_x64, shift_y64; + int x64, y64; + Glyph *glyph = NULL; time_t now = time(0); struct tm ltime; @@ -1413,9 +1835,17 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame, FFDrawColor bordercolor; FFDrawColor boxcolor; + int width = frame->width; + int height = frame->height; + int rec_x = 0, rec_y = 0, rec_width = 0, rec_height = 0; + int is_outside = 0; + int last_tab_idx = 0; + + TextMetrics metrics; + av_bprint_clear(bp); - if(s->basetime != AV_NOPTS_VALUE) + if (s->basetime != AV_NOPTS_VALUE) now= frame->pts*av_q2d(ctx->inputs[0]->time_base) + s->basetime/1000000; switch (s->exp_mode) { @@ -1441,13 +1871,6 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame, if (!av_bprint_is_complete(bp)) return AVERROR(ENOMEM); - text = s->expanded_text.str; - if ((len = s->expanded_text.len) > s->nb_positions) { - if (!(s->positions = - av_realloc(s->positions, len*sizeof(*s->positions)))) - return AVERROR(ENOMEM); - s->nb_positions = len; - } if (s->fontcolor_expr[0]) { /* If expression is set, evaluate and replace the static value */ @@ -1463,85 +1886,30 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame, ff_draw_color(&s->dc, &s->fontcolor, s->fontcolor.rgba); } - x = 0; - y = 0; - - if ((ret = update_fontsize(ctx)) < 0) + if ((ret = update_fontsize(ctx)) < 0) { return ret; - - /* load and cache glyphs */ - for (i = 0, p = text; *p; i++) { - GET_UTF8(code, *p ? *p++ : 0, code = 0xfffd; goto continue_on_invalid;); -continue_on_invalid: - - /* get glyph */ - dummy.code = code; - dummy.fontsize = s->fontsize; - glyph = av_tree_find(s->glyphs, &dummy, glyph_cmp, NULL); - if (!glyph) { - ret = load_glyph(ctx, &glyph, code); - if (ret < 0) - return ret; - } - - y_min = FFMIN(glyph->bbox.yMin, y_min); - y_max = FFMAX(glyph->bbox.yMax, y_max); - x_min = FFMIN(glyph->bbox.xMin, x_min); - x_max = FFMAX(glyph->bbox.xMax, x_max); } - s->max_glyph_h = y_max - y_min; - s->max_glyph_w = x_max - x_min; - /* compute and save position for each glyph */ - glyph = NULL; - for (i = 0, p = text; *p; i++) { - GET_UTF8(code, *p ? *p++ : 0, code = 0xfffd; goto continue_on_invalid2;); -continue_on_invalid2: - - /* skip the \n in the sequence \r\n */ - if (prev_code == '\r' && code == '\n') - continue; - - prev_code = code; - if (is_newline(code)) { - - max_text_line_w = FFMAX(max_text_line_w, x); - y += s->max_glyph_h + s->line_spacing; - x = 0; - continue; - } - - /* get glyph */ - prev_glyph = glyph; - dummy.code = code; - dummy.fontsize = s->fontsize; - glyph = av_tree_find(s->glyphs, &dummy, glyph_cmp, NULL); - - /* kerning */ - if (s->use_kerning && prev_glyph && glyph->code) { - FT_Get_Kerning(s->face, prev_glyph->code, glyph->code, - ft_kerning_default, &delta); - x += delta.x >> 6; - } - - /* save position */ - s->positions[i].x = x + glyph->bitmap_left; - s->positions[i].y = y - glyph->bitmap_top + y_max; - if (code == '\t') x = (x / s->tabsize + 1)*s->tabsize; - else x += glyph->advance; + if ((ret = measure_text(ctx, &metrics)) < 0) { + return ret; } - max_text_line_w = FFMAX(x, max_text_line_w); + s->max_glyph_h = POS_CEIL(metrics.max_y64 - metrics.min_y64, 64); + s->max_glyph_w = POS_CEIL(metrics.max_x64 - metrics.min_x64, 64); - s->var_values[VAR_TW] = s->var_values[VAR_TEXT_W] = max_text_line_w; - s->var_values[VAR_TH] = s->var_values[VAR_TEXT_H] = y + s->max_glyph_h; + s->var_values[VAR_TW] = s->var_values[VAR_TEXT_W] = metrics.width; + s->var_values[VAR_TH] = s->var_values[VAR_TEXT_H] = metrics.height; s->var_values[VAR_MAX_GLYPH_W] = s->max_glyph_w; s->var_values[VAR_MAX_GLYPH_H] = s->max_glyph_h; - s->var_values[VAR_MAX_GLYPH_A] = s->var_values[VAR_ASCENT ] = y_max; - s->var_values[VAR_MAX_GLYPH_D] = s->var_values[VAR_DESCENT] = y_min; + s->var_values[VAR_MAX_GLYPH_A] = s->var_values[VAR_ASCENT] = POS_CEIL(metrics.max_y64, 64); + s->var_values[VAR_FONT_A] = s->face->size->metrics.ascender / 64; + s->var_values[VAR_MAX_GLYPH_D] = s->var_values[VAR_DESCENT] = POS_CEIL(metrics.min_y64, 64); + s->var_values[VAR_FONT_D] = -s->face->size->metrics.descender / 64; - s->var_values[VAR_LINE_H] = s->var_values[VAR_LH] = s->max_glyph_h; + s->var_values[VAR_TOP_A] = POS_CEIL(metrics.offset_top64, 64); + s->var_values[VAR_BOTTOM_D] = -POS_CEIL(metrics.offset_bottom64, 64); + s->var_values[VAR_LINE_H] = s->var_values[VAR_LH] = metrics.line_height64 / 64.; if (s->text_source == AV_FRAME_DATA_DETECTION_BBOXES) { s->var_values[VAR_X] = s->x; @@ -1559,56 +1927,169 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame, update_color_with_alpha(s, &bordercolor, s->bordercolor); update_color_with_alpha(s, &boxcolor , s->boxcolor ); - box_w = max_text_line_w; - box_h = y + s->max_glyph_h; + if (s->draw_box && s->boxborderw) { + int bbsize[4]; + int count; + count = string_to_array(s->boxborderw, bbsize, 4); + if (count == 1) { + s->bb_top = s->bb_right = s->bb_bottom = s->bb_left = bbsize[0]; + } else if (count == 2) { + s->bb_top = s->bb_bottom = bbsize[0]; + s->bb_right = s->bb_left = bbsize[1]; + } else if (count == 3) { + s->bb_top = bbsize[0]; + s->bb_right = s->bb_left = bbsize[1]; + s->bb_bottom = bbsize[2]; + } else if (count == 4) { + s->bb_top = bbsize[0]; + s->bb_right = bbsize[1]; + s->bb_bottom = bbsize[2]; + s->bb_left = bbsize[3]; + } + } else { + s->bb_top = s->bb_right = s->bb_bottom = s->bb_left = 0; + } if (s->fix_bounds) { - /* calculate footprint of text effects */ - int boxoffset = s->draw_box ? FFMAX(s->boxborderw, 0) : 0; int borderoffset = s->borderw ? FFMAX(s->borderw, 0) : 0; - int offsetleft = FFMAX3(boxoffset, borderoffset, + int offsetleft = FFMAX3(FFMAX(s->bb_left, 0), borderoffset, (s->shadowx < 0 ? FFABS(s->shadowx) : 0)); - int offsettop = FFMAX3(boxoffset, borderoffset, + int offsettop = FFMAX3(FFMAX(s->bb_top, 0), borderoffset, (s->shadowy < 0 ? FFABS(s->shadowy) : 0)); - - int offsetright = FFMAX3(boxoffset, borderoffset, + int offsetright = FFMAX3(FFMAX(s->bb_right, 0), borderoffset, (s->shadowx > 0 ? s->shadowx : 0)); - int offsetbottom = FFMAX3(boxoffset, borderoffset, + int offsetbottom = FFMAX3(FFMAX(s->bb_bottom, 0), borderoffset, (s->shadowy > 0 ? s->shadowy : 0)); - if (s->x - offsetleft < 0) s->x = offsetleft; if (s->y - offsettop < 0) s->y = offsettop; - if (s->x + box_w + offsetright > width) - s->x = FFMAX(width - box_w - offsetright, 0); - if (s->y + box_h + offsetbottom > height) - s->y = FFMAX(height - box_h - offsetbottom, 0); + if (s->x + metrics.width + offsetright > width) + s->x = FFMAX(width - metrics.width - offsetright, 0); + if (s->y + metrics.height + offsetbottom > height) + s->y = FFMAX(height - metrics.height - offsetbottom, 0); + } + + x = 0; + y = 0; + x64 = (int)(s->x * 64.); + if (s->y_align == YA_FONT) { + y64 = (int)(s->y * 64. + s->face->size->metrics.ascender); + } else if (s->y_align == YA_BASELINE) { + y64 = (int)(s->y * 64.); + } else { + y64 = (int)(s->y * 64. + metrics.offset_top64); + } + + for (int l = 0; l < s->line_count; ++l) { + TextLine *line = &s->lines[l]; + HarfbuzzData *hb = &line->hb_data; + line->glyphs = av_mallocz(hb->glyph_count * sizeof(GlyphInfo)); + + for (int t = 0; t < hb->glyph_count; ++t) { + GlyphInfo *g_info = &line->glyphs[t]; + uint8_t is_tab = last_tab_idx < s->tab_count && + hb->glyph_info[t].cluster == s->tab_clusters[last_tab_idx] - line->cluster_offset; + int true_x, true_y; + if (is_tab) { + ++last_tab_idx; + } + true_x = x + hb->glyph_pos[t].x_offset; + true_y = y + hb->glyph_pos[t].y_offset; + shift_x64 = (((x64 + true_x) >> 4) & 0b0011) << 4; + shift_y64 = ((4 - (((y64 + true_y) >> 4) & 0b0011)) & 0b0011) << 4; + + ret = load_glyph(ctx, &glyph, hb->glyph_info[t].codepoint, shift_x64, shift_y64); + if (ret != 0) { + return ret; + } + g_info->code = hb->glyph_info[t].codepoint; + g_info->x = (x64 + true_x) >> 6; + g_info->y = ((y64 + true_y) >> 6) + (shift_y64 > 0 ? 1 : 0); + g_info->shift_x64 = shift_x64; + g_info->shift_y64 = shift_y64; + + if (!is_tab) { + x += hb->glyph_pos[t].x_advance; + } else { + int size = s->blank_advance64 * s->tabsize; + x = (x / size + 1) * size; + } + y += hb->glyph_pos[t].y_advance; + } + + y += metrics.line_height64 + s->line_spacing * 64; + x = 0; } - /* draw box */ - if (s->draw_box) - ff_blend_rectangle(&s->dc, &boxcolor, - frame->data, frame->linesize, width, height, - s->x - s->boxborderw, s->y - s->boxborderw, - box_w + s->boxborderw * 2, box_h + s->boxborderw * 2); + metrics.rect_x = s->x; + if (s->y_align == YA_BASELINE) { + metrics.rect_y = s->y - metrics.offset_top64 / 64; + } else { + metrics.rect_y = s->y; + } - if (s->shadowx || s->shadowy) { - if ((ret = draw_glyphs(s, frame, width, height, - &shadowcolor, s->shadowx, s->shadowy, 0)) < 0) - return ret; + s->box_width = s->boxw == 0 ? metrics.width : s->boxw; + s->box_height = s->boxh == 0 ? metrics.height : s->boxh; + + if (!s->draw_box) { + // Create a border for the clipping region to take into account subpixel + // errors in text measurement and effects. + int borderoffset = s->borderw ? FFMAX(s->borderw, 0) : 0; + s->bb_left = borderoffset + (s->shadowx < 0 ? FFABS(s->shadowx) : 0) + 1; + s->bb_top = borderoffset + (s->shadowy < 0 ? FFABS(s->shadowy) : 0) + 1; + s->bb_right = borderoffset + (s->shadowx > 0 ? s->shadowx : 0) + 1; + s->bb_bottom = borderoffset + (s->shadowy > 0 ? s->shadowy : 0) + 1; } - if (s->borderw) { - if ((ret = draw_glyphs(s, frame, width, height, - &bordercolor, 0, 0, s->borderw)) < 0) + /* Check if the whole box is out of the frame */ + is_outside = metrics.rect_x - s->bb_left >= width || + metrics.rect_y - s->bb_top >= height || + metrics.rect_x + s->box_width + s->bb_right <= 0 || + metrics.rect_y + s->box_height + s->bb_bottom <= 0; + + if (!is_outside) { + /* draw box */ + if (s->draw_box) { + rec_x = metrics.rect_x - s->bb_left; + rec_y = metrics.rect_y - s->bb_top; + rec_width = s->box_width + s->bb_right + s->bb_left; + rec_height = s->box_height + s->bb_bottom + s->bb_top; + ff_blend_rectangle(&s->dc, &boxcolor, + frame->data, frame->linesize, width, height, + rec_x, rec_y, rec_width, rec_height); + } + + if (s->shadowx || s->shadowy) { + if ((ret = draw_glyphs(s, frame, &shadowcolor, &metrics, + s->shadowx, s->shadowy, s->borderw)) < 0) { + return ret; + } + } + + if (s->borderw) { + if ((ret = draw_glyphs(s, frame, &bordercolor, &metrics, + 0, 0, s->borderw)) < 0) { + return ret; + } + } + + if ((ret = draw_glyphs(s, frame, &fontcolor, &metrics, 0, + 0, 0)) < 0) { return ret; + } } - if ((ret = draw_glyphs(s, frame, width, height, - &fontcolor, 0, 0, 0)) < 0) - return ret; + + // FREE data structures + for (int l = 0; l < s->line_count; ++l) { + TextLine *line = &s->lines[l]; + av_freep(&line->glyphs); + hb_destroy(&line->hb_data); + } + av_freep(&s->lines); + av_freep(&s->tab_clusters); return 0; } @@ -1654,7 +2135,12 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) NAN : frame->pts * av_q2d(inlink->time_base); s->var_values[VAR_PICT_TYPE] = frame->pict_type; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS s->var_values[VAR_PKT_POS] = frame->pkt_pos; + s->var_values[VAR_PKT_SIZE] = frame->pkt_size; +FF_ENABLE_DEPRECATION_WARNINGS +#endif #if FF_API_PKT_DURATION FF_DISABLE_DEPRECATION_WARNINGS s->var_values[VAR_PKT_DURATION] = frame->pkt_duration * av_q2d(inlink->time_base); @@ -1665,7 +2151,6 @@ FF_DISABLE_DEPRECATION_WARNINGS FF_ENABLE_DEPRECATION_WARNINGS #endif s->var_values[VAR_DURATION] = frame->duration * av_q2d(inlink->time_base); - s->var_values[VAR_PKT_SIZE] = frame->pkt_size; s->metadata = frame->metadata; @@ -1680,14 +2165,9 @@ FF_ENABLE_DEPRECATION_WARNINGS s->x = bbox->x; s->y = bbox->y - s->fontsize; } - draw_text(ctx, frame, frame->width, frame->height); + draw_text(ctx, frame); } - av_log(ctx, AV_LOG_DEBUG, "n:%d t:%f text_w:%d text_h:%d x:%d y:%d\n", - (int)s->var_values[VAR_N], s->var_values[VAR_T], - (int)s->var_values[VAR_TEXT_W], (int)s->var_values[VAR_TEXT_H], - s->x, s->y); - return ff_filter_frame(outlink, frame); } @@ -1701,13 +2181,6 @@ static const AVFilterPad avfilter_vf_drawtext_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_drawtext_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_drawtext = { .name = "drawtext", .description = NULL_IF_CONFIG_SMALL("Draw text on top of video frames using libfreetype library."), @@ -1716,7 +2189,7 @@ const AVFilter ff_vf_drawtext = { .init = init, .uninit = uninit, FILTER_INPUTS(avfilter_vf_drawtext_inputs), - FILTER_OUTPUTS(avfilter_vf_drawtext_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .process_command = command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_edgedetect.c b/libavfilter/vf_edgedetect.c index 603f06f1419..1362f4f6772 100644 --- a/libavfilter/vf_edgedetect.c +++ b/libavfilter/vf_edgedetect.c @@ -249,13 +249,6 @@ static const AVFilterPad edgedetect_inputs[] = { }, }; -static const AVFilterPad edgedetect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_edgedetect = { .name = "edgedetect", .description = NULL_IF_CONFIG_SMALL("Detect and draw edge."), @@ -263,7 +256,7 @@ const AVFilter ff_vf_edgedetect = { .init = init, .uninit = uninit, FILTER_INPUTS(edgedetect_inputs), - FILTER_OUTPUTS(edgedetect_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .priv_class = &edgedetect_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_elbg.c b/libavfilter/vf_elbg.c index fc73346ae12..17947e226a1 100644 --- a/libavfilter/vf_elbg.c +++ b/libavfilter/vf_elbg.c @@ -30,6 +30,7 @@ #include "avfilter.h" #include "drawutils.h" +#include "formats.h" #include "internal.h" #include "video.h" @@ -187,7 +188,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) av_frame_free(&frame); return AVERROR(ENOMEM); } - out->pts = frame->pts; + av_frame_copy_props(out, frame); av_frame_free(&frame); pal = (uint32_t *)out->data[1]; p0 = (uint8_t *)out->data[0]; @@ -253,13 +254,6 @@ static const AVFilterPad elbg_inputs[] = { }, }; -static const AVFilterPad elbg_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_elbg = { .name = "elbg", .description = NULL_IF_CONFIG_SMALL("Apply posterize effect, using the ELBG algorithm."), @@ -268,6 +262,6 @@ const AVFilter ff_vf_elbg = { .init = init, .uninit = uninit, FILTER_INPUTS(elbg_inputs), - FILTER_OUTPUTS(elbg_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/vf_entropy.c b/libavfilter/vf_entropy.c index 893d07d8e6d..694a3ee872a 100644 --- a/libavfilter/vf_entropy.c +++ b/libavfilter/vf_entropy.c @@ -18,12 +18,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -176,20 +174,13 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_entropy = { .name = "entropy", .description = NULL_IF_CONFIG_SMALL("Measure video frames entropy."), .priv_size = sizeof(EntropyContext), .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixfmts), .priv_class = &entropy_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_METADATA_ONLY, diff --git a/libavfilter/vf_epx.c b/libavfilter/vf_epx.c index d706229ab85..bae1b898bc7 100644 --- a/libavfilter/vf_epx.c +++ b/libavfilter/vf_epx.c @@ -19,6 +19,7 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "internal.h" +#include "video.h" typedef struct EPXContext { const AVClass *class; diff --git a/libavfilter/vf_eq.c b/libavfilter/vf_eq.c index 46636dd29d3..00fd840dec8 100644 --- a/libavfilter/vf_eq.c +++ b/libavfilter/vf_eq.c @@ -27,12 +27,13 @@ * very simple video equalizer */ -#include "libavfilter/internal.h" #include "libavutil/common.h" #include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" +#include "internal.h" #include "vf_eq.h" +#include "video.h" static void create_lut(EQParameters *param) { @@ -221,7 +222,6 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) AVFilterLink *outlink = inlink->dst->outputs[0]; EQContext *eq = ctx->priv; AVFrame *out; - int64_t pos = in->pkt_pos; const AVPixFmtDescriptor *desc; int i; @@ -235,7 +235,14 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) desc = av_pix_fmt_desc_get(inlink->format); eq->var_values[VAR_N] = inlink->frame_count_out; - eq->var_values[VAR_POS] = pos == -1 ? NAN : pos; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS + { + int64_t pos = in->pkt_pos; + eq->var_values[VAR_POS] = pos == -1 ? NAN : pos; + } +FF_ENABLE_DEPRECATION_WARNINGS +#endif eq->var_values[VAR_T] = TS2T(in->pts, inlink->time_base); if (eq->eval_mode == EVAL_MODE_FRAME) { @@ -307,13 +314,6 @@ static const AVFilterPad eq_inputs[] = { }, }; -static const AVFilterPad eq_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(EQContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM #define TFLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -348,7 +348,7 @@ const AVFilter ff_vf_eq = { .priv_size = sizeof(EQContext), .priv_class = &eq_class, FILTER_INPUTS(eq_inputs), - FILTER_OUTPUTS(eq_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts_eq), .process_command = process_command, .init = initialize, diff --git a/libavfilter/vf_eq.h b/libavfilter/vf_eq.h index fc8d4a711a8..50850ea42c3 100644 --- a/libavfilter/vf_eq.h +++ b/libavfilter/vf_eq.h @@ -30,7 +30,9 @@ static const char *const var_names[] = { "n", // frame count +#if FF_API_FRAME_PKT "pos", // frame position +#endif "r", // frame rate "t", // timestamp expressed in seconds NULL @@ -38,7 +40,9 @@ static const char *const var_names[] = { enum var_name { VAR_N, +#if FF_API_FRAME_PKT VAR_POS, +#endif VAR_R, VAR_T, VAR_NB diff --git a/libavfilter/vf_estdif.c b/libavfilter/vf_estdif.c index 0164f4638ae..471def9fa7e 100644 --- a/libavfilter/vf_estdif.c +++ b/libavfilter/vf_estdif.c @@ -23,7 +23,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -35,9 +34,9 @@ typedef struct ESTDIFContext { int deint; ///< which frames to deinterlace int rslope; ///< best edge slope search radius int redge; ///< best edge match search radius - float ecost; ///< edge cost for edge matching - float mcost; ///< middle cost for edge matching - float dcost; ///< distance cost for edge matching + int ecost; ///< edge cost for edge matching + int mcost; ///< middle cost for edge matching + int dcost; ///< distance cost for edge matching int interp; ///< type of interpolation int linesize[4]; ///< bytes of pixel data per line for each plane int planewidth[4]; ///< width of each plane @@ -92,11 +91,11 @@ static const AVOption estdif_options[] = { { "deint", "specify which frames to deinterlace", OFFSET(deint), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "deint" }, CONST("all", "deinterlace all frames", 0, "deint"), CONST("interlaced", "only deinterlace frames marked as interlaced", 1, "deint"), - { "rslope", "specify the search radius for edge slope tracing", OFFSET(rslope), AV_OPT_TYPE_INT, {.i64=1}, 1, MAX_R, FLAGS, }, - { "redge", "specify the search radius for best edge matching", OFFSET(redge), AV_OPT_TYPE_INT, {.i64=2}, 0, MAX_R, FLAGS, }, - { "ecost", "specify the edge cost for edge matching", OFFSET(ecost), AV_OPT_TYPE_FLOAT,{.dbl=1},0,9,FLAGS, }, - { "mcost", "specify the middle cost for edge matching", OFFSET(mcost), AV_OPT_TYPE_FLOAT,{.dbl=0.5}, 0, 1, FLAGS, }, - { "dcost", "specify the distance cost for edge matching", OFFSET(dcost), AV_OPT_TYPE_FLOAT,{.dbl=0.5}, 0, 1, FLAGS, }, + { "rslope", "specify the search radius for edge slope tracing", OFFSET(rslope), AV_OPT_TYPE_INT, {.i64=1}, 1, MAX_R, FLAGS }, + { "redge", "specify the search radius for best edge matching", OFFSET(redge), AV_OPT_TYPE_INT, {.i64=2}, 0, MAX_R, FLAGS }, + { "ecost", "specify the edge cost for edge matching", OFFSET(ecost), AV_OPT_TYPE_INT, {.i64=2}, 0, 50, FLAGS }, + { "mcost", "specify the middle cost for edge matching", OFFSET(mcost), AV_OPT_TYPE_INT, {.i64=1}, 0, 50, FLAGS }, + { "dcost", "specify the distance cost for edge matching", OFFSET(dcost), AV_OPT_TYPE_INT, {.i64=1}, 0, 50, FLAGS }, { "interp", "specify the type of interpolation", OFFSET(interp), AV_OPT_TYPE_INT, {.i64=1}, 0, 2, FLAGS, "interp" }, CONST("2p", "two-point interpolation", 0, "interp"), CONST("4p", "four-point interpolation", 1, "interp"), @@ -265,12 +264,13 @@ static void interpolate_##ss(ESTDIFContext *s, uint8_t *ddst, \ const type *const next2_line = (const type *const)nnext2_line; \ const type *const next3_line = (const type *const)nnext3_line; \ const int interp = s->interp; \ - const int ecost = s->ecost * 32.f; \ - const int dcost = s->dcost * s->max; \ - const int end = width - 1; \ - const atype mcost = s->mcost * s->redge * 4.f; \ + const int ecost = s->ecost; \ + const int dcost = s->dcost; \ + const int mcost = s->mcost; \ atype sd[S], sD[S], di = 0; \ + const int end = width - 1; \ atype dmin = amax; \ + int id = 0, iD = 0; \ int k = *K; \ \ for (int i = -rslope; i <= rslope && abs(k) > rslope; i++) { \ @@ -288,7 +288,11 @@ static void interpolate_##ss(ESTDIFContext *s, uint8_t *ddst, \ sD[i + rslope] += mcost * cost_##ss(prev_line, next_line, end, x, i);\ sD[i + rslope] += dcost * abs(i); \ \ - dmin = FFMIN(sD[i + rslope], dmin); \ + if (dmin > sD[i + rslope]) { \ + dmin = sD[i + rslope]; \ + di = 1; \ + iD = i; \ + } \ } \ \ for (int i = -rslope; i <= rslope; i++) { \ @@ -306,23 +310,14 @@ static void interpolate_##ss(ESTDIFContext *s, uint8_t *ddst, \ sd[i + rslope] += mcost * cost_##ss(prev_line, next_line, end, x, k+i);\ sd[i + rslope] += dcost * abs(k + i); \ \ - dmin = FFMIN(sd[i + rslope], dmin); \ - } \ - \ - for (int i = -rslope; i <= rslope && abs(k) > rslope; i++) { \ - if (dmin == sD[i + rslope]) { \ - di = 1; \ - k = i; \ - break; \ + if (dmin > sd[i + rslope]) { \ + dmin = sd[i + rslope]; \ + di = 0; \ + id = i; \ } \ } \ \ - for (int i = -rslope; i <= rslope && !di; i++) { \ - if (dmin == sd[i + rslope]) { \ - k += i; \ - break; \ - } \ - } \ + k = di ? iD : k + id; \ \ dst[x] = s->mid_##ss[interp](prev_line, next_line, \ prev2_line, next2_line, \ @@ -345,8 +340,8 @@ static int deinterlace_slice(AVFilterContext *ctx, void *arg, const int rslope = s->rslope; const int redge = s->redge; const int depth = s->depth; - const int interlaced = in->interlaced_frame; - const int tff = (s->field == (s->parity == -1 ? interlaced ? in->top_field_first : 1 : + const int interlaced = !!(in->flags & AV_FRAME_FLAG_INTERLACED); + const int tff = (s->field == (s->parity == -1 ? interlaced ? !!(in->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) : 1 : s->parity ^ 1)); for (int plane = 0; plane < s->nb_planes; plane++) { @@ -443,7 +438,12 @@ static int filter(AVFilterContext *ctx, AVFrame *in, int64_t pts, int64_t durati if (!out) return AVERROR(ENOMEM); av_frame_copy_props(out, in); +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS out->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + out->flags &= ~AV_FRAME_FLAG_INTERLACED; out->pts = pts; out->duration = duration; @@ -502,7 +502,7 @@ static int config_input(AVFilterLink *inlink) return 0; } - if ((s->deint && !s->prev->interlaced_frame) || ctx->is_disabled) { + if ((s->deint && !(s->prev->flags & AV_FRAME_FLAG_INTERLACED)) || ctx->is_disabled) { s->prev->pts *= 2; s->prev->duration *= 2; ret = ff_filter_frame(ctx->outputs[0], s->prev); diff --git a/libavfilter/vf_exposure.c b/libavfilter/vf_exposure.c index bbe951967be..926d784a813 100644 --- a/libavfilter/vf_exposure.c +++ b/libavfilter/vf_exposure.c @@ -21,9 +21,7 @@ #include #include "libavutil/opt.h" -#include "libavutil/imgutils.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -38,43 +36,80 @@ typedef struct ExposureContext { int jobnr, int nb_jobs); } ExposureContext; +typedef struct ThreadData { + AVFrame *out, *in; +} ThreadData; + static int exposure_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { ExposureContext *s = ctx->priv; - AVFrame *frame = arg; - const int width = frame->width; - const int height = frame->height; + ThreadData *td = arg; + const int width = td->out->width; + const int height = td->out->height; const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; const float black = s->black; const float scale = s->scale; for (int p = 0; p < 3; p++) { - const int linesize = frame->linesize[p] / 4; - float *ptr = (float *)frame->data[p] + slice_start * linesize; + const int slinesize = td->in->linesize[p] / 4; + const int dlinesize = td->out->linesize[p] / 4; + const float *src = (const float *)td->in->data[p] + slice_start * slinesize; + float *ptr = (float *)td->out->data[p] + slice_start * dlinesize; for (int y = slice_start; y < slice_end; y++) { for (int x = 0; x < width; x++) - ptr[x] = (ptr[x] - black) * scale; + ptr[x] = (src[x] - black) * scale; + + ptr += dlinesize; + src += slinesize; + } + } - ptr += linesize; + if (td->in->data[3] && td->in->linesize[3] && td->in != td->out) { + const int slinesize = td->in->linesize[3] / 4; + const int dlinesize = td->out->linesize[3] / 4; + const float *src = (const float *)td->in->data[3] + slice_start * slinesize; + float *ptr = (float *)td->out->data[3] + slice_start * dlinesize; + for (int y = slice_start; y < slice_end; y++) { + memcpy(ptr, src, width * sizeof(*ptr)); + ptr += dlinesize; + src += slinesize; } } return 0; } -static int filter_frame(AVFilterLink *inlink, AVFrame *frame) +static int filter_frame(AVFilterLink *inlink, AVFrame *in) { AVFilterContext *ctx = inlink->dst; + AVFilterLink *outlink = ctx->outputs[0]; ExposureContext *s = ctx->priv; float diff = fabsf(exp2f(-s->exposure) - s->black); + ThreadData td; + AVFrame *out; + + if (av_frame_is_writable(in)) { + out = in; + } else { + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } + av_frame_copy_props(out, in); + } diff = diff > 0.f ? diff : 1.f / 1024.f; s->scale = 1.f / diff; - ff_filter_execute(ctx, s->do_slice, frame, NULL, - FFMIN(frame->height, ff_filter_get_nb_threads(ctx))); - - return ff_filter_frame(ctx->outputs[0], frame); + td.out = out; + td.in = in; + ff_filter_execute(ctx, s->do_slice, &td, NULL, + FFMIN(out->height, ff_filter_get_nb_threads(ctx))); + + if (out != in) + av_frame_free(&in); + return ff_filter_frame(outlink, out); } static av_cold int config_input(AVFilterLink *inlink) @@ -91,19 +126,11 @@ static const AVFilterPad exposure_inputs[] = { { .name = "default", .type = AVMEDIA_TYPE_VIDEO, - .flags = AVFILTERPAD_FLAG_NEEDS_WRITABLE, .filter_frame = filter_frame, .config_props = config_input, }, }; -static const AVFilterPad exposure_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(ExposureContext, x) #define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -121,7 +148,7 @@ const AVFilter ff_vf_exposure = { .priv_size = sizeof(ExposureContext), .priv_class = &exposure_class, FILTER_INPUTS(exposure_inputs), - FILTER_OUTPUTS(exposure_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS(AV_PIX_FMT_GBRPF32, AV_PIX_FMT_GBRAPF32), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_extractplanes.c b/libavfilter/vf_extractplanes.c index 08737d64156..453e51a1ea2 100644 --- a/libavfilter/vf_extractplanes.c +++ b/libavfilter/vf_extractplanes.c @@ -28,7 +28,9 @@ #include "avfilter.h" #include "drawutils.h" #include "filters.h" +#include "formats.h" #include "internal.h" +#include "video.h" #define PLANE_R 0x01 #define PLANE_G 0x02 diff --git a/libavfilter/vf_fade.c b/libavfilter/vf_fade.c index 868e7cc16a6..f903db04155 100644 --- a/libavfilter/vf_fade.c +++ b/libavfilter/vf_fade.c @@ -557,13 +557,6 @@ static const AVFilterPad avfilter_vf_fade_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_fade_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_fade = { .name = "fade", .description = NULL_IF_CONFIG_SMALL("Fade in/out input video."), @@ -571,7 +564,7 @@ const AVFilter ff_vf_fade = { .priv_size = sizeof(FadeContext), .priv_class = &fade_class, FILTER_INPUTS(avfilter_vf_fade_inputs), - FILTER_OUTPUTS(avfilter_vf_fade_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SLICE_THREADS | AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_feedback.c b/libavfilter/vf_feedback.c index 15cbf95c54d..33beae66ec1 100644 --- a/libavfilter/vf_feedback.c +++ b/libavfilter/vf_feedback.c @@ -27,6 +27,7 @@ #include "libavutil/internal.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" #include "video.h" @@ -163,11 +164,11 @@ static int activate(AVFilterContext *ctx) src->data[0] + y * src->linesize[0], src->width * s->max_step[0]); } - for (int i = 1; i < 3; i ++) { + for (int i = 1; i < 3; i++) { if (dst->data[i]) { for (int y = 0; y < src->height; y++) { - memmove(dst->data[i] + ((s->y + y) >> s->vsub) * dst->linesize[i] + ((s->x * s->max_step[i]) >> s->hsub), - src->data[i] + (y >> s->vsub) * src->linesize[i], (src->width * s->max_step[i]) >> s->hsub); + memmove(dst->data[i] + ((s->y + y) >> s->vsub) * dst->linesize[i] + (s->x >> s->hsub) * s->max_step[i], + src->data[i] + (y >> s->vsub) * src->linesize[i], (src->width >> s->hsub) * s->max_step[i]); } } } @@ -213,7 +214,7 @@ static int activate(AVFilterContext *ctx) for (int i = 1; i < 3; i ++) { if (frame->data[i]) { frame->data[i] += (s->y >> s->vsub) * frame->linesize[i]; - frame->data[i] += (s->x * s->max_step[i]) >> s->hsub; + frame->data[i] += (s->x >> s->hsub) * s->max_step[i]; } } diff --git a/libavfilter/vf_fftdnoiz.c b/libavfilter/vf_fftdnoiz.c index 17dd4f1e778..1489f3282b6 100644 --- a/libavfilter/vf_fftdnoiz.c +++ b/libavfilter/vf_fftdnoiz.c @@ -24,6 +24,7 @@ #include "libavutil/pixdesc.h" #include "libavutil/tx.h" #include "internal.h" +#include "video.h" #include "window_func.h" #define MAX_BLOCK 256 diff --git a/libavfilter/vf_fftfilt.c b/libavfilter/vf_fftfilt.c index d2a6db24c8c..aea83dc19cc 100644 --- a/libavfilter/vf_fftfilt.c +++ b/libavfilter/vf_fftfilt.c @@ -24,7 +24,8 @@ * FFT domain filtering. */ -#include "libavfilter/internal.h" +#include "internal.h" +#include "video.h" #include "libavutil/common.h" #include "libavutil/cpu.h" #include "libavutil/imgutils.h" @@ -592,20 +593,13 @@ static const AVFilterPad fftfilt_inputs[] = { }, }; -static const AVFilterPad fftfilt_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_fftfilt = { .name = "fftfilt", .description = NULL_IF_CONFIG_SMALL("Apply arbitrary expressions to pixels in frequency domain."), .priv_size = sizeof(FFTFILTContext), .priv_class = &fftfilt_class, FILTER_INPUTS(fftfilt_inputs), - FILTER_OUTPUTS(fftfilt_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts_fftfilt), .init = initialize, .uninit = uninit, diff --git a/libavfilter/vf_field.c b/libavfilter/vf_field.c index 8d06ffe6636..5c4ff0881c1 100644 --- a/libavfilter/vf_field.c +++ b/libavfilter/vf_field.c @@ -73,7 +73,12 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) int i; inpicref->height = outlink->h; +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS inpicref->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + inpicref->flags &= ~AV_FRAME_FLAG_INTERLACED; for (i = 0; i < field->nb_planes; i++) { if (field->type == FIELD_TYPE_BOTTOM) diff --git a/libavfilter/vf_fieldhint.c b/libavfilter/vf_fieldhint.c index 4af9e269255..8e1a4fc7aab 100644 --- a/libavfilter/vf_fieldhint.c +++ b/libavfilter/vf_fieldhint.c @@ -25,6 +25,7 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" #include "video.h" @@ -217,10 +218,20 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) switch (hint) { case '+': +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS out->interlaced_frame = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + out->flags |= AV_FRAME_FLAG_INTERLACED; break; case '-': +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS out->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + out->flags &= ~AV_FRAME_FLAG_INTERLACED; break; case '=': break; diff --git a/libavfilter/vf_fieldmatch.c b/libavfilter/vf_fieldmatch.c index bf946beec90..55671d8517a 100644 --- a/libavfilter/vf_fieldmatch.c +++ b/libavfilter/vf_fieldmatch.c @@ -38,7 +38,9 @@ #include "libavutil/timestamp.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" +#include "video.h" #define INPUT_MAIN 0 #define INPUT_CLEANSRC 1 @@ -714,7 +716,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) in = fm->src; /* parity */ - order = fm->order != FM_PARITY_AUTO ? fm->order : (in->interlaced_frame ? in->top_field_first : 1); + order = fm->order != FM_PARITY_AUTO ? fm->order : ((in->flags & AV_FRAME_FLAG_INTERLACED) ? + !!(in->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) : 1); field = fm->field != FM_PARITY_AUTO ? fm->field : order; av_assert0(order == 0 || order == 1 || field == 0 || field == 1); fxo = field ^ order ? fxo1m : fxo0m; @@ -819,16 +822,30 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) /* mark the frame we are unable to match properly as interlaced so a proper * de-interlacer can take the relay */ +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS dst->interlaced_frame = interlaced_frame; - if (dst->interlaced_frame) { +FF_ENABLE_DEPRECATION_WARNINGS +#endif + if (interlaced_frame) { + dst->flags |= AV_FRAME_FLAG_INTERLACED; av_log(ctx, AV_LOG_WARNING, "Frame #%"PRId64" at %s is still interlaced\n", outlink->frame_count_in, av_ts2timestr(in->pts, &inlink->time_base)); +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS dst->top_field_first = field; - } +FF_ENABLE_DEPRECATION_WARNINGS +#endif + if (field) + dst->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; + else + dst->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; + } else + dst->flags &= ~AV_FRAME_FLAG_INTERLACED; av_log(ctx, AV_LOG_DEBUG, "SC:%d | COMBS: %3d %3d %3d %3d %3d (combpel=%d)" " match=%d combed=%s\n", sc, combs[0], combs[1], combs[2], combs[3], combs[4], - fm->combpel, match, dst->interlaced_frame ? "YES" : "NO"); + fm->combpel, match, (dst->flags & AV_FRAME_FLAG_INTERLACED) ? "YES" : "NO"); fail: for (i = 0; i < FF_ARRAY_ELEMS(gen_frames); i++) diff --git a/libavfilter/vf_fieldorder.c b/libavfilter/vf_fieldorder.c index 52b4b3d8aa0..165b3590246 100644 --- a/libavfilter/vf_fieldorder.c +++ b/libavfilter/vf_fieldorder.c @@ -76,11 +76,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) uint8_t *dst, *src; AVFrame *out; - if (!frame->interlaced_frame || - frame->top_field_first == s->dst_tff) { + if (!(frame->flags & AV_FRAME_FLAG_INTERLACED) || + !!(frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) == s->dst_tff) { av_log(ctx, AV_LOG_VERBOSE, "Skipping %s.\n", - frame->interlaced_frame ? + (frame->flags & AV_FRAME_FLAG_INTERLACED) ? "frame with same field order" : "progressive frame"); return ff_filter_frame(outlink, frame); } @@ -140,7 +140,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) } } } +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS out->top_field_first = s->dst_tff; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + if (s->dst_tff) + out->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; + else + out->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; if (frame != out) av_frame_free(&frame); @@ -168,20 +176,13 @@ static const AVFilterPad avfilter_vf_fieldorder_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_fieldorder_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_fieldorder = { .name = "fieldorder", .description = NULL_IF_CONFIG_SMALL("Set the field order."), .priv_size = sizeof(FieldOrderContext), .priv_class = &fieldorder_class, FILTER_INPUTS(avfilter_vf_fieldorder_inputs), - FILTER_OUTPUTS(avfilter_vf_fieldorder_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_fillborders.c b/libavfilter/vf_fillborders.c index 83f206aeb16..32ca7219bb6 100644 --- a/libavfilter/vf_fillborders.c +++ b/libavfilter/vf_fillborders.c @@ -25,7 +25,6 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -707,20 +706,13 @@ static const AVFilterPad fillborders_inputs[] = { }, }; -static const AVFilterPad fillborders_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_fillborders = { .name = "fillborders", .description = NULL_IF_CONFIG_SMALL("Fill borders of the input video."), .priv_size = sizeof(FillBordersContext), .priv_class = &fillborders_class, FILTER_INPUTS(fillborders_inputs), - FILTER_OUTPUTS(fillborders_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, .process_command = process_command, diff --git a/libavfilter/vf_find_rect.c b/libavfilter/vf_find_rect.c index a536d669d10..9f4ee1e32fb 100644 --- a/libavfilter/vf_find_rect.c +++ b/libavfilter/vf_find_rect.c @@ -25,6 +25,7 @@ #include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "internal.h" +#include "video.h" #include "lavfutils.h" @@ -281,13 +282,6 @@ static const AVFilterPad foc_inputs[] = { }, }; -static const AVFilterPad foc_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_find_rect = { .name = "find_rect", .description = NULL_IF_CONFIG_SMALL("Find a user specified object."), @@ -296,7 +290,7 @@ const AVFilter ff_vf_find_rect = { .uninit = uninit, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(foc_inputs), - FILTER_OUTPUTS(foc_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS(AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVJ420P), .priv_class = &find_rect_class, }; diff --git a/libavfilter/vf_flip_vulkan.c b/libavfilter/vf_flip_vulkan.c index 0223786ef1f..ecd2567ebc2 100644 --- a/libavfilter/vf_flip_vulkan.c +++ b/libavfilter/vf_flip_vulkan.c @@ -1,5 +1,7 @@ /* * copyright (c) 2021 Wu Jianhua + * Copyright (c) Lynne + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -20,9 +22,9 @@ #include "libavutil/random_seed.h" #include "libavutil/opt.h" #include "vulkan_filter.h" +#include "vulkan_spirv.h" #include "internal.h" - -#define CGS 32 +#include "video.h" enum FlipType { FLIP_VERTICAL, @@ -32,32 +34,50 @@ enum FlipType { typedef struct FlipVulkanContext { FFVulkanContext vkctx; - FFVkQueueFamilyCtx qf; - FFVkExecContext *exec; - FFVulkanPipeline *pl; - - VkDescriptorImageInfo input_images[3]; - VkDescriptorImageInfo output_images[3]; int initialized; + FFVulkanPipeline pl; + FFVkExecPool e; + FFVkQueueFamilyCtx qf; + FFVkSPIRVShader shd; + VkSampler sampler; } FlipVulkanContext; static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in, enum FlipType type) { int err = 0; - FFVkSPIRVShader *shd; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; FlipVulkanContext *s = ctx->priv; FFVulkanContext *vkctx = &s->vkctx; const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); + FFVkSPIRVShader *shd = &s->shd; + FFVkSPIRVCompiler *spv; + FFVulkanDescriptorSetBinding *desc; + + spv = ff_vk_spirv_init(); + if (!spv) { + av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); + return AVERROR_EXTERNAL; + } + + ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT); + RET(ff_vk_exec_pool_init(vkctx, &s->qf, &s->e, s->qf.nb_queues*4, 0, 0, 0, NULL)); + RET(ff_vk_init_sampler(vkctx, &s->sampler, 1, VK_FILTER_LINEAR)); + RET(ff_vk_shader_init(&s->pl, &s->shd, "flip_compute", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); + + ff_vk_shader_set_compute_sizes(&s->shd, 32, 32, 1); - FFVulkanDescriptorSetBinding image_descs[] = { + desc = (FFVulkanDescriptorSetBinding []) { { .name = "input_image", .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .dimensions = 2, .elems = planes, .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->input_images, + .samplers = DUP_SAMPLER(s->sampler), }, { .name = "output_image", @@ -67,167 +87,75 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in, enum FlipType .dimensions = 2, .elems = planes, .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->output_images, }, }; - image_descs[0].sampler = ff_vk_init_sampler(vkctx, 1, VK_FILTER_LINEAR); - if (!image_descs[0].sampler) - return AVERROR_EXTERNAL; + RET(ff_vk_pipeline_descriptor_set_add(vkctx, &s->pl, shd, desc, 2, 0, 0)); - ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT, 0); - - { - s->pl = ff_vk_create_pipeline(vkctx, &s->qf); - if (!s->pl) - return AVERROR(ENOMEM); - - shd = ff_vk_init_shader(s->pl, "flip_compute", image_descs[0].stages); - if (!shd) - return AVERROR(ENOMEM); - - ff_vk_set_compute_shader_sizes(shd, (int [3]){ CGS, 1, 1 }); - RET(ff_vk_add_descriptor_set(vkctx, s->pl, shd, image_descs, FF_ARRAY_ELEMS(image_descs), 0)); - - GLSLC(0, void main() ); - GLSLC(0, { ); - GLSLC(1, ivec2 size; ); - GLSLC(1, const ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); - for (int i = 0; i < planes; i++) { - GLSLC(0, ); - GLSLF(1, size = imageSize(output_image[%i]); ,i); - GLSLC(1, if (IS_WITHIN(pos, size)) { ); - switch (type) - { - case FLIP_HORIZONTAL: - GLSLF(2, vec4 res = texture(input_image[%i], ivec2(size.x - pos.x, pos.y)); ,i); - break; - case FLIP_VERTICAL: - GLSLF(2, vec4 res = texture(input_image[%i], ivec2(pos.x, size.y - pos.y)); ,i); - break; - case FLIP_BOTH: - GLSLF(2, vec4 res = texture(input_image[%i], ivec2(size.xy - pos.xy));, i); - break; - default: - GLSLF(2, vec4 res = texture(input_image[%i], pos); ,i); - break; - } - GLSLF(2, imageStore(output_image[%i], pos, res); ,i); - GLSLC(1, } ); + GLSLC(0, void main() ); + GLSLC(0, { ); + GLSLC(1, ivec2 size; ); + GLSLC(1, const ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); + for (int i = 0; i < planes; i++) { + GLSLC(0, ); + GLSLF(1, size = imageSize(output_image[%i]); ,i); + GLSLC(1, if (IS_WITHIN(pos, size)) { ); + switch (type) + { + case FLIP_HORIZONTAL: + GLSLF(2, vec4 res = texture(input_image[%i], ivec2(size.x - pos.x, pos.y)); ,i); + break; + case FLIP_VERTICAL: + GLSLF(2, vec4 res = texture(input_image[%i], ivec2(pos.x, size.y - pos.y)); ,i); + break; + case FLIP_BOTH: + GLSLF(2, vec4 res = texture(input_image[%i], ivec2(size.xy - pos.xy));, i); + break; + default: + GLSLF(2, vec4 res = texture(input_image[%i], pos); ,i); + break; } - GLSLC(0, } ); - - RET(ff_vk_compile_shader(vkctx, shd, "main")); - RET(ff_vk_init_pipeline_layout(vkctx, s->pl)); - RET(ff_vk_init_compute_pipeline(vkctx, s->pl)); + GLSLF(2, imageStore(output_image[%i], pos, res); ,i); + GLSLC(1, } ); } + GLSLC(0, } ); + + RET(spv->compile_shader(spv, ctx, shd, &spv_data, &spv_len, "main", + &spv_opaque)); + RET(ff_vk_shader_create(vkctx, shd, spv_data, spv_len, "main")); + + RET(ff_vk_init_compute_pipeline(vkctx, &s->pl, shd)); + RET(ff_vk_exec_pipeline_register(vkctx, &s->e, &s->pl)); - RET(ff_vk_create_exec_ctx(vkctx, &s->exec, &s->qf)); s->initialized = 1; fail: + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); + if (spv) + spv->uninit(&spv); + return err; } static av_cold void flip_vulkan_uninit(AVFilterContext *avctx) { FlipVulkanContext *s = avctx->priv; - ff_vk_uninit(&s->vkctx); - s->initialized = 0; -} - -static int process_frames(AVFilterContext *avctx, AVFrame *outframe, AVFrame *inframe) -{ - int err = 0; - VkCommandBuffer cmd_buf; - FlipVulkanContext *s = avctx->priv; FFVulkanContext *vkctx = &s->vkctx; - FFVulkanFunctions *vk = &s->vkctx.vkfn; - AVVkFrame *in = (AVVkFrame *)inframe->data[0]; - AVVkFrame *out = (AVVkFrame *)outframe->data[0]; - const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); - const VkFormat *input_formats = av_vkfmt_from_pixfmt(s->vkctx.input_format); - const VkFormat *output_formats = av_vkfmt_from_pixfmt(s->vkctx.output_format); - - ff_vk_start_exec_recording(vkctx, s->exec); - cmd_buf = ff_vk_get_exec_buf(s->exec); - - for (int i = 0; i < planes; i++) { - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->input_images[i].imageView, in->img[i], - input_formats[i], - ff_comp_identity_map)); - - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->output_images[i].imageView, out->img[i], - output_formats[i], - ff_comp_identity_map)); - - s->input_images[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - s->output_images[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; - } + FFVulkanFunctions *vk = &vkctx->vkfn; - ff_vk_update_descriptor_set(vkctx, s->pl, 0); + ff_vk_exec_pool_free(vkctx, &s->e); + ff_vk_pipeline_free(vkctx, &s->pl); + ff_vk_shader_free(vkctx, &s->shd); - for (int i = 0; i < planes; i++) { - VkImageMemoryBarrier barriers[] = { - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .oldLayout = in->layout[i], - .newLayout = s->input_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = in->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .oldLayout = out->layout[i], - .newLayout = s->output_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = out->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - }; - - vk->CmdPipelineBarrier(cmd_buf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, - 0, NULL, 0, NULL, FF_ARRAY_ELEMS(barriers), barriers); - - in->layout[i] = barriers[0].newLayout; - in->access[i] = barriers[0].dstAccessMask; - - out->layout[i] = barriers[1].newLayout; - out->access[i] = barriers[1].dstAccessMask; - } - - ff_vk_bind_pipeline_exec(vkctx, s->exec, s->pl); - vk->CmdDispatch(cmd_buf, FFALIGN(s->vkctx.output_width, CGS)/CGS, - s->vkctx.output_height, 1); - - ff_vk_add_exec_dep(vkctx, s->exec, inframe, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - ff_vk_add_exec_dep(vkctx, s->exec, outframe, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - - err = ff_vk_submit_exec_queue(vkctx, s->exec); - if (err) - return err; + if (s->sampler) + vk->DestroySampler(vkctx->hwctx->act_dev, s->sampler, + vkctx->hwctx->alloc); - ff_vk_qf_rotate(&s->qf); + ff_vk_uninit(&s->vkctx); - return 0; -fail: - ff_vk_discard_exec_deps(s->exec); - return err; + s->initialized = 0; } static int filter_frame(AVFilterLink *link, AVFrame *in, enum FlipType type) @@ -247,7 +175,8 @@ static int filter_frame(AVFilterLink *link, AVFrame *in, enum FlipType type) if (!s->initialized) RET(init_filter(ctx, in, type)); - RET(process_frames(ctx, out, in)); + RET(ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->pl, out, in, + s->sampler, NULL, 0)); RET(av_frame_copy_props(out, in)); @@ -366,4 +295,5 @@ const AVFilter ff_vf_flip_vulkan = { FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), .priv_class = &flip_vulkan_class, .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_floodfill.c b/libavfilter/vf_floodfill.c index da747c9f9f1..d5fa42651ac 100644 --- a/libavfilter/vf_floodfill.c +++ b/libavfilter/vf_floodfill.c @@ -19,10 +19,10 @@ */ #include "libavutil/opt.h" -#include "libavutil/imgutils.h" #include "libavutil/intreadwrite.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" +#include "filters.h" #include "internal.h" #include "video.h" @@ -315,8 +315,10 @@ static int filter_frame(AVFilterLink *link, AVFrame *frame) s->front++; } - if (ret = av_frame_make_writable(frame)) + if (ret = ff_inlink_make_frame_writable(link, &frame)) { + av_frame_free(&frame); return ret; + } while (s->front > s->back) { int x, y; @@ -382,13 +384,6 @@ static const AVFilterPad floodfill_inputs[] = { }, }; -static const AVFilterPad floodfill_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(FloodfillContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM @@ -415,7 +410,7 @@ const AVFilter ff_vf_floodfill = { .priv_class = &floodfill_class, .uninit = uninit, FILTER_INPUTS(floodfill_inputs), - FILTER_OUTPUTS(floodfill_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_format.c b/libavfilter/vf_format.c index 24b1c9ca618..04c265f59ff 100644 --- a/libavfilter/vf_format.c +++ b/libavfilter/vf_format.c @@ -154,13 +154,6 @@ static const AVFilterPad avfilter_vf_format_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_format_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO - }, -}; - const AVFilter ff_vf_format = { .name = "format", .description = NULL_IF_CONFIG_SMALL("Convert the input video to one of the specified pixel formats."), @@ -174,7 +167,7 @@ const AVFilter ff_vf_format = { .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(avfilter_vf_format_inputs), - FILTER_OUTPUTS(avfilter_vf_format_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; @@ -190,13 +183,6 @@ static const AVFilterPad avfilter_vf_noformat_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_noformat_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO - }, -}; - const AVFilter ff_vf_noformat = { .name = "noformat", .description = NULL_IF_CONFIG_SMALL("Force libavfilter not to use any of the specified pixel formats for the input to the next filter."), @@ -210,7 +196,7 @@ const AVFilter ff_vf_noformat = { .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(avfilter_vf_noformat_inputs), - FILTER_OUTPUTS(avfilter_vf_noformat_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/vf_fps.c b/libavfilter/vf_fps.c index 051d278f54d..089f7f20212 100644 --- a/libavfilter/vf_fps.c +++ b/libavfilter/vf_fps.c @@ -34,8 +34,10 @@ #include "libavutil/mathematics.h" #include "libavutil/opt.h" #include "avfilter.h" +#include "ccfifo.h" #include "filters.h" #include "internal.h" +#include "video.h" enum EOFAction { EOF_ACTION_ROUND, @@ -85,6 +87,7 @@ typedef struct FPSContext { AVFrame *frames[2]; ///< buffered frames int frames_count; ///< number of buffered frames + CCFifo cc_fifo; ///< closed captions int64_t next_pts; ///< pts of the next frame to output @@ -165,6 +168,7 @@ static av_cold void uninit(AVFilterContext *ctx) frame = shift_frame(ctx, s); av_frame_free(&frame); } + ff_ccfifo_uninit(&s->cc_fifo); av_log(ctx, AV_LOG_VERBOSE, "%d frames in, %d frames out; %d frames dropped, " "%d frames duplicated.\n", s->frames_in, s->frames_out, s->drop, s->dup); @@ -210,6 +214,12 @@ static int config_props(AVFilterLink* outlink) s->in_pts_off, s->out_pts_off, s->start_time); } + ret = ff_ccfifo_init(&s->cc_fifo, outlink->frame_rate, ctx); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Failure to setup CC FIFO queue\n"); + return ret; + } + av_log(ctx, AV_LOG_VERBOSE, "fps=%d/%d\n", outlink->frame_rate.num, outlink->frame_rate.den); return 0; @@ -242,6 +252,7 @@ static int read_frame(AVFilterContext *ctx, FPSContext *s, AVFilterLink *inlink, av_log(ctx, AV_LOG_DEBUG, "Read frame with in pts %"PRId64", out pts %"PRId64"\n", in_pts, frame->pts); + ff_ccfifo_extract(&s->cc_fifo, frame); s->frames[s->frames_count++] = frame; s->frames_in++; @@ -289,7 +300,7 @@ static int write_frame(AVFilterContext *ctx, FPSContext *s, AVFilterLink *outlin if (!frame) return AVERROR(ENOMEM); // Make sure Closed Captions will not be duplicated - av_frame_remove_side_data(s->frames[0], AV_FRAME_DATA_A53_CC); + ff_ccfifo_inject(&s->cc_fifo, frame); frame->pts = s->next_pts++; frame->duration = 1; @@ -366,13 +377,6 @@ static int activate(AVFilterContext *ctx) return FFERROR_NOT_READY; } -static const AVFilterPad avfilter_vf_fps_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - static const AVFilterPad avfilter_vf_fps_outputs[] = { { .name = "default", @@ -390,6 +394,6 @@ const AVFilter ff_vf_fps = { .priv_class = &fps_class, .activate = activate, .flags = AVFILTER_FLAG_METADATA_ONLY, - FILTER_INPUTS(avfilter_vf_fps_inputs), + FILTER_INPUTS(ff_video_default_filterpad), FILTER_OUTPUTS(avfilter_vf_fps_outputs), }; diff --git a/libavfilter/vf_framepack.c b/libavfilter/vf_framepack.c index 6ea88df8b28..8693ad64889 100644 --- a/libavfilter/vf_framepack.c +++ b/libavfilter/vf_framepack.c @@ -34,7 +34,6 @@ #include "avfilter.h" #include "filters.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -142,7 +141,7 @@ static int config_output(AVFilterLink *outlink) height *= 2; break; default: - av_log(ctx, AV_LOG_ERROR, "Unknown packing mode."); + av_log(ctx, AV_LOG_ERROR, "Unknown packing mode.\n"); return AVERROR_INVALIDDATA; } @@ -405,14 +404,12 @@ static int activate(AVFilterContext *ctx) FF_FILTER_FORWARD_STATUS(ctx->inputs[1], outlink); if (ff_outlink_frame_wanted(ctx->outputs[0]) && - !ff_outlink_get_status(ctx->inputs[0]) && !s->input_views[0]) { ff_inlink_request_frame(ctx->inputs[0]); return 0; } if (ff_outlink_frame_wanted(ctx->outputs[0]) && - !ff_outlink_get_status(ctx->inputs[1]) && !s->input_views[1]) { ff_inlink_request_frame(ctx->inputs[1]); return 0; diff --git a/libavfilter/vf_framerate.c b/libavfilter/vf_framerate.c index 49bf6cdfff2..6ef5dca27a7 100644 --- a/libavfilter/vf_framerate.c +++ b/libavfilter/vf_framerate.c @@ -318,7 +318,7 @@ static int activate(AVFilterContext *ctx) return ret; if (inpicref) { - if (inpicref->interlaced_frame) + if (inpicref->flags & AV_FRAME_FLAG_INTERLACED) av_log(ctx, AV_LOG_WARNING, "Interlaced frame found - the output will not be correct.\n"); if (inpicref->pts == AV_NOPTS_VALUE) { diff --git a/libavfilter/vf_freezedetect.c b/libavfilter/vf_freezedetect.c index 31a80a6dc09..fb4e59b127d 100644 --- a/libavfilter/vf_freezedetect.c +++ b/libavfilter/vf_freezedetect.c @@ -29,6 +29,7 @@ #include "avfilter.h" #include "filters.h" #include "scene_sad.h" +#include "video.h" typedef struct FreezeDetectContext { const AVClass *class; @@ -204,13 +205,6 @@ static const AVFilterPad freezedetect_inputs[] = { }, }; -static const AVFilterPad freezedetect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_freezedetect = { .name = "freezedetect", .description = NULL_IF_CONFIG_SMALL("Detects frozen video input."), @@ -219,7 +213,7 @@ const AVFilter ff_vf_freezedetect = { .uninit = uninit, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(freezedetect_inputs), - FILTER_OUTPUTS(freezedetect_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .activate = activate, }; diff --git a/libavfilter/vf_frei0r.c b/libavfilter/vf_frei0r.c index 59873dc502e..7dccd5946f7 100644 --- a/libavfilter/vf_frei0r.c +++ b/libavfilter/vf_frei0r.c @@ -423,13 +423,6 @@ static const AVFilterPad avfilter_vf_frei0r_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_frei0r_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_frei0r = { .name = "frei0r", .description = NULL_IF_CONFIG_SMALL("Apply a frei0r effect."), @@ -438,7 +431,7 @@ const AVFilter ff_vf_frei0r = { .priv_size = sizeof(Frei0rContext), .priv_class = &frei0r_class, FILTER_INPUTS(avfilter_vf_frei0r_inputs), - FILTER_OUTPUTS(avfilter_vf_frei0r_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_fspp.c b/libavfilter/vf_fspp.c index 3e04fd01b94..88e887897ed 100644 --- a/libavfilter/vf_fspp.c +++ b/libavfilter/vf_fspp.c @@ -42,6 +42,7 @@ #include "internal.h" #include "qp_table.h" #include "vf_fspp.h" +#include "video.h" #define OFFSET(x) offsetof(FSPPContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM @@ -651,20 +652,13 @@ static const AVFilterPad fspp_inputs[] = { }, }; -static const AVFilterPad fspp_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_fspp = { .name = "fspp", .description = NULL_IF_CONFIG_SMALL("Apply Fast Simple Post-processing filter."), .priv_size = sizeof(FSPPContext), .uninit = uninit, FILTER_INPUTS(fspp_inputs), - FILTER_OUTPUTS(fspp_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &fspp_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, diff --git a/libavfilter/vf_gblur.c b/libavfilter/vf_gblur.c index ca1dcb3dab1..6e8ef962358 100644 --- a/libavfilter/vf_gblur.c +++ b/libavfilter/vf_gblur.c @@ -31,7 +31,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "gblur.h" #include "internal.h" #include "vf_gblur_init.h" @@ -315,13 +314,6 @@ static const AVFilterPad gblur_inputs[] = { }, }; -static const AVFilterPad gblur_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_gblur = { .name = "gblur", .description = NULL_IF_CONFIG_SMALL("Apply Gaussian Blur filter."), @@ -329,7 +321,7 @@ const AVFilter ff_vf_gblur = { .priv_class = &gblur_class, .uninit = uninit, FILTER_INPUTS(gblur_inputs), - FILTER_OUTPUTS(gblur_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_gblur_vulkan.c b/libavfilter/vf_gblur_vulkan.c index d61f3c778c0..bac05b87b89 100644 --- a/libavfilter/vf_gblur_vulkan.c +++ b/libavfilter/vf_gblur_vulkan.c @@ -1,5 +1,7 @@ /* * copyright (c) 2021-2022 Wu Jianhua + * Copyright (c) Lynne + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -20,33 +22,32 @@ #include "libavutil/random_seed.h" #include "libavutil/opt.h" #include "vulkan_filter.h" +#include "vulkan_spirv.h" #include "internal.h" +#include "video.h" #define CGS 32 #define GBLUR_MAX_KERNEL_SIZE 127 typedef struct GBlurVulkanContext { FFVulkanContext vkctx; - FFVkQueueFamilyCtx qf; - FFVkExecContext *exec; - FFVulkanPipeline *pl_hor; - FFVulkanPipeline *pl_ver; - FFVkBuffer params_buf_hor; - FFVkBuffer params_buf_ver; - - VkDescriptorImageInfo input_images[3]; - VkDescriptorImageInfo tmp_images[3]; - VkDescriptorImageInfo output_images[3]; - VkDescriptorBufferInfo params_desc_hor; - VkDescriptorBufferInfo params_desc_ver; int initialized; + FFVkExecPool e; + FFVkQueueFamilyCtx qf; + VkSampler sampler; + FFVulkanPipeline pl_hor; + FFVkSPIRVShader shd_hor; + FFVkBuffer params_hor; + FFVulkanPipeline pl_ver; + FFVkSPIRVShader shd_ver; + FFVkBuffer params_ver; + int size; int sizeV; int planes; float sigma; float sigmaV; - AVFrame *tmpframe; } GBlurVulkanContext; static const char gblur_func[] = { @@ -118,16 +119,17 @@ static av_cold void init_gaussian_params(GBlurVulkanContext *s) s->sizeV = s->size; else init_kernel_size(s, &s->sizeV); - - s->tmpframe = NULL; } -static int init_gblur_pipeline(GBlurVulkanContext *s, FFVulkanPipeline *pl, FFVkSPIRVShader *shd, - FFVkBuffer *params_buf, VkDescriptorBufferInfo *params_desc, - int ksize, float sigma) +static int init_gblur_pipeline(GBlurVulkanContext *s, FFVulkanPipeline *pl, + FFVkSPIRVShader *shd, FFVkBuffer *params_buf, + int ksize, float sigma, FFVkSPIRVCompiler *spv) { int err = 0; uint8_t *kernel_mapped; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); @@ -137,7 +139,6 @@ static int init_gblur_pipeline(GBlurVulkanContext *s, FFVulkanPipeline *pl, FFVk .mem_quali = "readonly", .mem_layout = "std430", .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = NULL, .buf_content = NULL, }; @@ -145,10 +146,9 @@ static int init_gblur_pipeline(GBlurVulkanContext *s, FFVulkanPipeline *pl, FFVk if (!kernel_def) return AVERROR(ENOMEM); - buf_desc.updater = params_desc; buf_desc.buf_content = kernel_def; - RET(ff_vk_add_descriptor_set(&s->vkctx, pl, shd, &buf_desc, 1, 0)); + RET(ff_vk_pipeline_descriptor_set_add(&s->vkctx, pl, shd, &buf_desc, 1, 1, 0)); GLSLD( gblur_func ); GLSLC(0, void main() ); @@ -157,38 +157,43 @@ static int init_gblur_pipeline(GBlurVulkanContext *s, FFVulkanPipeline *pl, FFVk GLSLC(1, const ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); for (int i = 0; i < planes; i++) { GLSLC(0, ); - GLSLF(1, size = imageSize(output_images[%i]); ,i); - GLSLC(1, if (IS_WITHIN(pos, size)) { ); + GLSLF(1, size = imageSize(output_images[%i]); ,i); + GLSLC(1, if (!IS_WITHIN(pos, size)) ); + GLSLC(2, return; ); if (s->planes & (1 << i)) { - GLSLF(2, gblur(pos, %i); ,i); + GLSLF(1, gblur(pos, %i); ,i); } else { - GLSLF(2, vec4 res = texture(input_images[%i], pos); ,i); - GLSLF(2, imageStore(output_images[%i], pos, res); ,i); + GLSLF(1, vec4 res = texture(input_images[%i], pos); ,i); + GLSLF(1, imageStore(output_images[%i], pos, res); ,i); } - GLSLC(1, } ); } GLSLC(0, } ); - RET(ff_vk_compile_shader(&s->vkctx, shd, "main")); + RET(spv->compile_shader(spv, s, shd, &spv_data, &spv_len, "main", + &spv_opaque)); + RET(ff_vk_shader_create(&s->vkctx, shd, spv_data, spv_len, "main")); - RET(ff_vk_init_pipeline_layout(&s->vkctx, pl)); - RET(ff_vk_init_compute_pipeline(&s->vkctx, pl)); + RET(ff_vk_init_compute_pipeline(&s->vkctx, pl, shd)); + RET(ff_vk_exec_pipeline_register(&s->vkctx, &s->e, pl)); - RET(ff_vk_create_buf(&s->vkctx, params_buf, sizeof(float) * ksize, - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)); - RET(ff_vk_map_buffers(&s->vkctx, params_buf, &kernel_mapped, 1, 0)); + RET(ff_vk_create_buf(&s->vkctx, params_buf, sizeof(float) * ksize, NULL, NULL, + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)); + RET(ff_vk_map_buffer(&s->vkctx, params_buf, &kernel_mapped, 0)); init_gaussian_kernel((float *)kernel_mapped, sigma, ksize); - RET(ff_vk_unmap_buffers(&s->vkctx, params_buf, 1, 1)); - - params_desc->buffer = params_buf->buf; - params_desc->range = VK_WHOLE_SIZE; + RET(ff_vk_unmap_buffer(&s->vkctx, params_buf, 1)); - ff_vk_update_descriptor_set(&s->vkctx, pl, 1); + RET(ff_vk_set_descriptor_buffer(&s->vkctx, pl, NULL, 1, 0, 0, + params_buf->address, params_buf->size, + VK_FORMAT_UNDEFINED)); fail: av_free(kernel_def); + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); return err; } @@ -196,16 +201,35 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) { int err = 0; GBlurVulkanContext *s = ctx->priv; - FFVkSPIRVShader *shd; + FFVulkanContext *vkctx = &s->vkctx; const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); - FFVulkanDescriptorSetBinding image_descs[] = { + FFVkSPIRVShader *shd; + FFVkSPIRVCompiler *spv; + FFVulkanDescriptorSetBinding *desc; + + spv = ff_vk_spirv_init(); + if (!spv) { + av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); + return AVERROR_EXTERNAL; + } + + ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT); + RET(ff_vk_exec_pool_init(vkctx, &s->qf, &s->e, s->qf.nb_queues*4, 0, 0, 0, NULL)); + RET(ff_vk_init_sampler(vkctx, &s->sampler, 1, VK_FILTER_LINEAR)); + RET(ff_vk_shader_init(&s->pl_hor, &s->shd_hor, "gblur_hor_compute", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); + RET(ff_vk_shader_init(&s->pl_ver, &s->shd_ver, "gblur_ver_compute", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); + + desc = (FFVulkanDescriptorSetBinding []) { { .name = "input_images", .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .dimensions = 2, .elems = planes, .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), }, { .name = "output_images", @@ -218,215 +242,64 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) }, }; - image_descs[0].sampler = ff_vk_init_sampler(&s->vkctx, 1, VK_FILTER_LINEAR); - if (!image_descs[0].sampler) - return AVERROR_EXTERNAL; - init_gaussian_params(s); - ff_vk_qf_init(&s->vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT, 0); - { - /* Create shader for the horizontal pass */ - image_descs[0].updater = s->input_images; - image_descs[1].updater = s->tmp_images; - - s->pl_hor = ff_vk_create_pipeline(&s->vkctx, &s->qf); - if (!s->pl_hor) { - err = AVERROR(ENOMEM); - goto fail; - } - - shd = ff_vk_init_shader(s->pl_hor, "gblur_compute_hor", image_descs[0].stages); - if (!shd) { - err = AVERROR(ENOMEM); - goto fail; - } + shd = &s->shd_hor; + ff_vk_shader_set_compute_sizes(shd, 32, 1, 1); - ff_vk_set_compute_shader_sizes(shd, (int [3]){ CGS, 1, 1 }); - RET(ff_vk_add_descriptor_set(&s->vkctx, s->pl_hor, shd, image_descs, FF_ARRAY_ELEMS(image_descs), 0)); + RET(ff_vk_pipeline_descriptor_set_add(vkctx, &s->pl_hor, shd, desc, 2, 0, 0)); GLSLC(0, #define OFFSET (vec2(i, 0.0))); - RET(init_gblur_pipeline(s, s->pl_hor, shd, &s->params_buf_hor, &s->params_desc_hor, - s->size, s->sigma)); + RET(init_gblur_pipeline(s, &s->pl_hor, shd, &s->params_hor, s->size, s->sigma, spv)); } { - /* Create shader for the vertical pass */ - image_descs[0].updater = s->tmp_images; - image_descs[1].updater = s->output_images; - - s->pl_ver = ff_vk_create_pipeline(&s->vkctx, &s->qf); - if (!s->pl_ver) { - err = AVERROR(ENOMEM); - goto fail; - } + shd = &s->shd_ver; + ff_vk_shader_set_compute_sizes(shd, 1, 32, 1); - shd = ff_vk_init_shader(s->pl_ver, "gblur_compute_ver", image_descs[0].stages); - if (!shd) { - err = AVERROR(ENOMEM); - goto fail; - } - - ff_vk_set_compute_shader_sizes(shd, (int [3]){ 1, CGS, 1 }); - RET(ff_vk_add_descriptor_set(&s->vkctx, s->pl_ver, shd, image_descs, FF_ARRAY_ELEMS(image_descs), 0)); + RET(ff_vk_pipeline_descriptor_set_add(vkctx, &s->pl_ver, shd, desc, 2, 0, 0)); GLSLC(0, #define OFFSET (vec2(0.0, i))); - RET(init_gblur_pipeline(s, s->pl_ver, shd, &s->params_buf_ver, &s->params_desc_ver, - s->sizeV, s->sigmaV)); + RET(init_gblur_pipeline(s, &s->pl_ver, shd, &s->params_ver, s->sizeV, s->sigmaV, spv)); } - RET(ff_vk_create_exec_ctx(&s->vkctx, &s->exec, &s->qf)); - s->initialized = 1; fail: + if (spv) + spv->uninit(&spv); + return err; } static av_cold void gblur_vulkan_uninit(AVFilterContext *avctx) { GBlurVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + FFVulkanFunctions *vk = &vkctx->vkfn; - av_frame_free(&s->tmpframe); + ff_vk_exec_pool_free(vkctx, &s->e); + ff_vk_pipeline_free(vkctx, &s->pl_hor); + ff_vk_pipeline_free(vkctx, &s->pl_ver); + ff_vk_shader_free(vkctx, &s->shd_hor); + ff_vk_shader_free(vkctx, &s->shd_ver); + ff_vk_free_buf(vkctx, &s->params_hor); + ff_vk_free_buf(vkctx, &s->params_ver); + + if (s->sampler) + vk->DestroySampler(vkctx->hwctx->act_dev, s->sampler, + vkctx->hwctx->alloc); - ff_vk_free_buf(&s->vkctx, &s->params_buf_hor); - ff_vk_free_buf(&s->vkctx, &s->params_buf_ver); ff_vk_uninit(&s->vkctx); s->initialized = 0; } -static int process_frames(AVFilterContext *avctx, AVFrame *outframe, AVFrame *inframe) -{ - int err; - VkCommandBuffer cmd_buf; - GBlurVulkanContext *s = avctx->priv; - FFVulkanFunctions *vk = &s->vkctx.vkfn; - - const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); - - AVVkFrame *in = (AVVkFrame *)inframe->data[0]; - AVVkFrame *out = (AVVkFrame *)outframe->data[0]; - AVVkFrame *tmp = (AVVkFrame *)s->tmpframe->data[0]; - - const VkFormat *input_formats = av_vkfmt_from_pixfmt(s->vkctx.input_format); - const VkFormat *output_formats = av_vkfmt_from_pixfmt(s->vkctx.output_format); - - ff_vk_start_exec_recording(&s->vkctx, s->exec); - cmd_buf = ff_vk_get_exec_buf(s->exec); - - for (int i = 0; i < planes; i++) { - RET(ff_vk_create_imageview(&s->vkctx, s->exec, &s->input_images[i].imageView, - in->img[i], - input_formats[i], - ff_comp_identity_map)); - - RET(ff_vk_create_imageview(&s->vkctx, s->exec, &s->tmp_images[i].imageView, - tmp->img[i], - output_formats[i], - ff_comp_identity_map)); - - RET(ff_vk_create_imageview(&s->vkctx, s->exec, &s->output_images[i].imageView, - out->img[i], - output_formats[i], - ff_comp_identity_map)); - - s->input_images[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - s->tmp_images[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; - s->output_images[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; - } - - ff_vk_update_descriptor_set(&s->vkctx, s->pl_hor, 0); - ff_vk_update_descriptor_set(&s->vkctx, s->pl_ver, 0); - - for (int i = 0; i < planes; i++) { - VkImageMemoryBarrier barriers[] = { - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .oldLayout = in->layout[i], - .newLayout = s->input_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = in->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT, - .oldLayout = tmp->layout[i], - .newLayout = s->tmp_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = tmp->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .oldLayout = out->layout[i], - .newLayout = s->output_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = out->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - }; - - vk->CmdPipelineBarrier(cmd_buf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, - 0, NULL, 0, NULL, FF_ARRAY_ELEMS(barriers), barriers); - - in->layout[i] = barriers[0].newLayout; - in->access[i] = barriers[0].dstAccessMask; - - tmp->layout[i] = barriers[1].newLayout; - tmp->access[i] = barriers[1].dstAccessMask; - - out->layout[i] = barriers[2].newLayout; - out->access[i] = barriers[2].dstAccessMask; - } - - ff_vk_bind_pipeline_exec(&s->vkctx, s->exec, s->pl_hor); - - vk->CmdDispatch(cmd_buf, FFALIGN(s->vkctx.output_width, CGS)/CGS, - s->vkctx.output_height, 1); - - ff_vk_bind_pipeline_exec(&s->vkctx, s->exec, s->pl_ver); - - vk->CmdDispatch(cmd_buf,s->vkctx.output_width, - FFALIGN(s->vkctx.output_height, CGS)/CGS, 1); - - ff_vk_add_exec_dep(&s->vkctx, s->exec, inframe, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - ff_vk_add_exec_dep(&s->vkctx, s->exec, outframe, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - - err = ff_vk_submit_exec_queue(&s->vkctx, s->exec); - if (err) - return err; - - ff_vk_qf_rotate(&s->qf); - - return 0; - -fail: - ff_vk_discard_exec_deps(s->exec); - return err; -} - static int gblur_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) { int err; - AVFrame *out = NULL; + AVFrame *tmp = NULL, *out = NULL; AVFilterContext *ctx = link->dst; GBlurVulkanContext *s = ctx->priv; AVFilterLink *outlink = ctx->outputs[0]; @@ -437,28 +310,32 @@ static int gblur_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) goto fail; } - if (!s->initialized) { - RET(init_filter(ctx, in)); - s->tmpframe = ff_get_video_buffer(outlink, outlink->w, outlink->h); - if (!s->tmpframe) { - err = AVERROR(ENOMEM); - goto fail; - } + tmp = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!tmp) { + err = AVERROR(ENOMEM); + goto fail; } - RET(process_frames(ctx, out, in)); + if (!s->initialized) + RET(init_filter(ctx, in)); - RET(av_frame_copy_props(out, in)); + RET(ff_vk_filter_process_2pass(&s->vkctx, &s->e, + (FFVulkanPipeline *[2]){ &s->pl_hor, &s->pl_ver }, + out, tmp, in, s->sampler, NULL, 0)); + + err = av_frame_copy_props(out, in); + if (err < 0) + goto fail; av_frame_free(&in); + av_frame_free(&tmp); return ff_filter_frame(outlink, out); fail: av_frame_free(&in); + av_frame_free(&tmp); av_frame_free(&out); - av_frame_free(&s->tmpframe); - return err; } @@ -503,4 +380,5 @@ const AVFilter ff_vf_gblur_vulkan = { FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), .priv_class = &gblur_vulkan_class, .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_geq.c b/libavfilter/vf_geq.c index 2b539802787..912a4dd51ca 100644 --- a/libavfilter/vf_geq.c +++ b/libavfilter/vf_geq.c @@ -31,7 +31,9 @@ #include "libavutil/eval.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" +#include "formats.h" #include "internal.h" +#include "video.h" #define MAX_NB_THREADS 32 #define NB_PLANES 4 @@ -515,13 +517,6 @@ static const AVFilterPad geq_inputs[] = { }, }; -static const AVFilterPad geq_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_geq = { .name = "geq", .description = NULL_IF_CONFIG_SMALL("Apply generic equation to each pixel."), @@ -529,7 +524,7 @@ const AVFilter ff_vf_geq = { .init = geq_init, .uninit = geq_uninit, FILTER_INPUTS(geq_inputs), - FILTER_OUTPUTS(geq_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(geq_query_formats), .priv_class = &geq_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_gradfun.c b/libavfilter/vf_gradfun.c index 71a5f9c787d..39971b31200 100644 --- a/libavfilter/vf_gradfun.c +++ b/libavfilter/vf_gradfun.c @@ -38,7 +38,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "gradfun.h" #include "internal.h" #include "video.h" @@ -236,13 +235,6 @@ static const AVFilterPad avfilter_vf_gradfun_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_gradfun_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_gradfun = { .name = "gradfun", .description = NULL_IF_CONFIG_SMALL("Debands video quickly using gradients."), @@ -251,7 +243,7 @@ const AVFilter ff_vf_gradfun = { .init = init, .uninit = uninit, FILTER_INPUTS(avfilter_vf_gradfun_inputs), - FILTER_OUTPUTS(avfilter_vf_gradfun_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_grayworld.c b/libavfilter/vf_grayworld.c index f20412cb104..e9c959416e1 100644 --- a/libavfilter/vf_grayworld.c +++ b/libavfilter/vf_grayworld.c @@ -27,10 +27,8 @@ #include "libavutil/imgutils.h" #include "libavutil/opt.h" -#include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -308,20 +306,13 @@ static const AVFilterPad grayworld_inputs[] = { } }; -static const AVFilterPad grayworld_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - } -}; - const AVFilter ff_vf_grayworld = { .name = "grayworld", .description = NULL_IF_CONFIG_SMALL("Adjust white balance using LAB gray world algorithm"), .priv_size = sizeof(GrayWorldContext), .priv_class = &grayworld_class, FILTER_INPUTS(grayworld_inputs), - FILTER_OUTPUTS(grayworld_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS(AV_PIX_FMT_GBRPF32, AV_PIX_FMT_GBRAPF32), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .uninit = uninit, diff --git a/libavfilter/vf_guided.c b/libavfilter/vf_guided.c index 58a1ff5a264..8c0e5e454e7 100644 --- a/libavfilter/vf_guided.c +++ b/libavfilter/vf_guided.c @@ -23,7 +23,6 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" #include "filters.h" -#include "formats.h" #include "framesync.h" #include "internal.h" #include "video.h" diff --git a/libavfilter/vf_hflip.c b/libavfilter/vf_hflip.c index 8517b87889c..09f4e08ea33 100644 --- a/libavfilter/vf_hflip.c +++ b/libavfilter/vf_hflip.c @@ -151,20 +151,13 @@ static const AVFilterPad avfilter_vf_hflip_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_hflip_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_hflip = { .name = "hflip", .description = NULL_IF_CONFIG_SMALL("Horizontally flip the input video."), .priv_size = sizeof(FlipContext), .priv_class = &hflip_class, FILTER_INPUTS(avfilter_vf_hflip_inputs), - FILTER_OUTPUTS(avfilter_vf_hflip_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SLICE_THREADS | AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_histeq.c b/libavfilter/vf_histeq.c index 3fee4c9480c..d1fc3a19ae4 100644 --- a/libavfilter/vf_histeq.c +++ b/libavfilter/vf_histeq.c @@ -34,7 +34,6 @@ #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -254,20 +253,13 @@ static const AVFilterPad histeq_inputs[] = { }, }; -static const AVFilterPad histeq_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_histeq = { .name = "histeq", .description = NULL_IF_CONFIG_SMALL("Apply global color histogram equalization."), .priv_size = sizeof(HisteqContext), .init = init, FILTER_INPUTS(histeq_inputs), - FILTER_OUTPUTS(histeq_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &histeq_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_histogram.c b/libavfilter/vf_histogram.c index ced7cda6f44..83477692cd5 100644 --- a/libavfilter/vf_histogram.c +++ b/libavfilter/vf_histogram.c @@ -593,7 +593,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) memset(s->histogram, 0, s->histogram_size * sizeof(unsigned)); } - out->pts = in->pts; + av_frame_copy_props(out, in); av_frame_free(&in); s->x_pos++; if (s->x_pos >= s->width) { diff --git a/libavfilter/vf_hqdn3d.c b/libavfilter/vf_hqdn3d.c index c796ea9ab4f..2aef7751c4e 100644 --- a/libavfilter/vf_hqdn3d.c +++ b/libavfilter/vf_hqdn3d.c @@ -36,7 +36,6 @@ #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "vf_hqdn3d.h" @@ -383,13 +382,6 @@ static const AVFilterPad avfilter_vf_hqdn3d_inputs[] = { }; -static const AVFilterPad avfilter_vf_hqdn3d_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO - }, -}; - const AVFilter ff_vf_hqdn3d = { .name = "hqdn3d", .description = NULL_IF_CONFIG_SMALL("Apply a High Quality 3D Denoiser."), @@ -398,7 +390,7 @@ const AVFilter ff_vf_hqdn3d = { .init = init, .uninit = uninit, FILTER_INPUTS(avfilter_vf_hqdn3d_inputs), - FILTER_OUTPUTS(avfilter_vf_hqdn3d_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, diff --git a/libavfilter/vf_hqx.c b/libavfilter/vf_hqx.c index bc70d984e98..4a8ed184f49 100644 --- a/libavfilter/vf_hqx.c +++ b/libavfilter/vf_hqx.c @@ -31,6 +31,7 @@ #include "libavutil/avassert.h" #include "libavutil/pixdesc.h" #include "internal.h" +#include "video.h" typedef int (*hqxfunc_t)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); diff --git a/libavfilter/vf_hsvkey.c b/libavfilter/vf_hsvkey.c index 8963bcf1f83..542ac1f3b2f 100644 --- a/libavfilter/vf_hsvkey.c +++ b/libavfilter/vf_hsvkey.c @@ -21,12 +21,10 @@ #include #include "libavutil/opt.h" -#include "libavutil/imgutils.h" #include "libavutil/intreadwrite.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" -#include "video.h" typedef struct HSVKeyContext { const AVClass *class; diff --git a/libavfilter/vf_hue.c b/libavfilter/vf_hue.c index 644c99b1b1a..8fea2128fa2 100644 --- a/libavfilter/vf_hue.c +++ b/libavfilter/vf_hue.c @@ -32,7 +32,6 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -500,13 +499,6 @@ static const AVFilterPad hue_inputs[] = { }, }; -static const AVFilterPad hue_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_hue = { .name = "hue", .description = NULL_IF_CONFIG_SMALL("Adjust the hue and saturation of the input video."), @@ -515,7 +507,7 @@ const AVFilter ff_vf_hue = { .uninit = uninit, .process_command = process_command, FILTER_INPUTS(hue_inputs), - FILTER_OUTPUTS(hue_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &hue_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_huesaturation.c b/libavfilter/vf_huesaturation.c index d4e3fea1c51..8dd81ad1a4f 100644 --- a/libavfilter/vf_huesaturation.c +++ b/libavfilter/vf_huesaturation.c @@ -17,10 +17,9 @@ */ #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -434,13 +433,6 @@ static const AVFilterPad huesaturation_inputs[] = { }, }; -static const AVFilterPad huesaturation_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(HueSaturationContext, x) #define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -472,7 +464,7 @@ const AVFilter ff_vf_huesaturation = { .priv_size = sizeof(HueSaturationContext), .priv_class = &huesaturation_class, FILTER_INPUTS(huesaturation_inputs), - FILTER_OUTPUTS(huesaturation_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_hwmap.c b/libavfilter/vf_hwmap.c index 2e03dfc1fec..e246b22603b 100644 --- a/libavfilter/vf_hwmap.c +++ b/libavfilter/vf_hwmap.c @@ -427,4 +427,5 @@ const AVFilter ff_vf_hwmap = { FILTER_OUTPUTS(hwmap_outputs), FILTER_QUERY_FUNC(hwmap_query_formats), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_hwupload.c b/libavfilter/vf_hwupload.c index dbc41734ccc..ef61bb41375 100644 --- a/libavfilter/vf_hwupload.c +++ b/libavfilter/vf_hwupload.c @@ -258,4 +258,5 @@ const AVFilter ff_vf_hwupload = { FILTER_OUTPUTS(hwupload_outputs), FILTER_QUERY_FUNC(hwupload_query_formats), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_hysteresis.c b/libavfilter/vf_hysteresis.c index a274ec49626..d2fd3011b8f 100644 --- a/libavfilter/vf_hysteresis.c +++ b/libavfilter/vf_hysteresis.c @@ -23,7 +23,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "framesync.h" diff --git a/libavfilter/vf_iccdetect.c b/libavfilter/vf_iccdetect.c index 7c08a2baaa1..5288b0320d2 100644 --- a/libavfilter/vf_iccdetect.c +++ b/libavfilter/vf_iccdetect.c @@ -31,6 +31,7 @@ #include "avfilter.h" #include "fflcms2.h" #include "internal.h" +#include "video.h" typedef struct IccDetectContext { const AVClass *class; @@ -123,13 +124,6 @@ static const AVFilterPad iccdetect_inputs[] = { }, }; -static const AVFilterPad iccdetect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_iccdetect = { .name = "iccdetect", .description = NULL_IF_CONFIG_SMALL("Detect and parse ICC profiles."), @@ -139,5 +133,5 @@ const AVFilter ff_vf_iccdetect = { .init = &iccdetect_init, .uninit = &iccdetect_uninit, FILTER_INPUTS(iccdetect_inputs), - FILTER_OUTPUTS(iccdetect_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), }; diff --git a/libavfilter/vf_iccgen.c b/libavfilter/vf_iccgen.c index 67d02f5bc20..80771edf9e8 100644 --- a/libavfilter/vf_iccgen.c +++ b/libavfilter/vf_iccgen.c @@ -30,6 +30,7 @@ #include "avfilter.h" #include "fflcms2.h" #include "internal.h" +#include "video.h" typedef struct IccGenContext { const AVClass *class; @@ -161,13 +162,6 @@ static const AVFilterPad iccgen_inputs[] = { }, }; -static const AVFilterPad iccgen_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_iccgen = { .name = "iccgen", .description = NULL_IF_CONFIG_SMALL("Generate and attach ICC profiles."), @@ -177,5 +171,5 @@ const AVFilter ff_vf_iccgen = { .init = &iccgen_init, .uninit = &iccgen_uninit, FILTER_INPUTS(iccgen_inputs), - FILTER_OUTPUTS(iccgen_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), }; diff --git a/libavfilter/vf_identity.c b/libavfilter/vf_identity.c index 84def7959be..d3a5ec14f6d 100644 --- a/libavfilter/vf_identity.c +++ b/libavfilter/vf_identity.c @@ -30,10 +30,8 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "framesync.h" #include "internal.h" -#include "video.h" #include "scene_sad.h" typedef struct IdentityContext { diff --git a/libavfilter/vf_idet.c b/libavfilter/vf_idet.c index 83d992add12..abd375230aa 100644 --- a/libavfilter/vf_idet.c +++ b/libavfilter/vf_idet.c @@ -183,13 +183,29 @@ static void filter(AVFilterContext *ctx) } if (idet->last_type == TFF){ +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS idet->cur->top_field_first = 1; idet->cur->interlaced_frame = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + idet->cur->flags |= (AV_FRAME_FLAG_INTERLACED | AV_FRAME_FLAG_TOP_FIELD_FIRST); }else if(idet->last_type == BFF){ +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS idet->cur->top_field_first = 0; idet->cur->interlaced_frame = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + idet->cur->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; + idet->cur->flags |= AV_FRAME_FLAG_INTERLACED; }else if(idet->last_type == PROGRESSIVE){ +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS idet->cur->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + idet->cur->flags &= ~AV_FRAME_FLAG_INTERLACED; } for(i=0; i<3; i++) @@ -238,13 +254,19 @@ static int filter_frame(AVFilterLink *link, AVFrame *picref) // initial frame(s) and not interlaced, just pass through for // the analyze_interlaced_flag mode if (idet->analyze_interlaced_flag && - !picref->interlaced_frame && + !(picref->flags & AV_FRAME_FLAG_INTERLACED) && !idet->next) { return ff_filter_frame(ctx->outputs[0], picref); } if (idet->analyze_interlaced_flag_done) { - if (picref->interlaced_frame && idet->interlaced_flag_accuracy < 0) + if ((picref->flags & AV_FRAME_FLAG_INTERLACED) && idet->interlaced_flag_accuracy < 0) { +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS picref->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + picref->flags &= ~AV_FRAME_FLAG_INTERLACED; + } return ff_filter_frame(ctx->outputs[0], picref); } @@ -282,8 +304,13 @@ static int filter_frame(AVFilterLink *link, AVFrame *picref) } if (idet->analyze_interlaced_flag) { - if (idet->cur->interlaced_frame) { + if (idet->cur->flags & AV_FRAME_FLAG_INTERLACED) { +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS idet->cur->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + idet->cur->flags &= ~AV_FRAME_FLAG_INTERLACED; filter(ctx); if (idet->last_type == PROGRESSIVE) { idet->interlaced_flag_accuracy --; @@ -295,8 +322,14 @@ static int filter_frame(AVFilterLink *link, AVFrame *picref) if (idet->analyze_interlaced_flag == 1) { ff_filter_frame(ctx->outputs[0], av_frame_clone(idet->cur)); - if (idet->next->interlaced_frame && idet->interlaced_flag_accuracy < 0) + if ((idet->next->flags & AV_FRAME_FLAG_INTERLACED) && idet->interlaced_flag_accuracy < 0) { +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS idet->next->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + idet->next->flags &= ~AV_FRAME_FLAG_INTERLACED; + } idet->analyze_interlaced_flag_done = 1; av_log(ctx, AV_LOG_INFO, "Final flag accuracy %d\n", idet->interlaced_flag_accuracy); return ff_filter_frame(ctx->outputs[0], av_frame_clone(idet->next)); diff --git a/libavfilter/vf_il.c b/libavfilter/vf_il.c index e1c380a2cf6..1190d79fc7b 100644 --- a/libavfilter/vf_il.c +++ b/libavfilter/vf_il.c @@ -28,7 +28,9 @@ #include "libavutil/imgutils.h" #include "libavutil/pixdesc.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" +#include "video.h" enum FilterMode { MODE_NONE, @@ -183,19 +185,12 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_il = { .name = "il", .description = NULL_IF_CONFIG_SMALL("Deinterleave or interleave fields."), .priv_size = sizeof(IlContext), FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .priv_class = &il_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_kerndeint.c b/libavfilter/vf_kerndeint.c index dd320fbebfa..bbad3f9bfc9 100644 --- a/libavfilter/vf_kerndeint.c +++ b/libavfilter/vf_kerndeint.c @@ -32,8 +32,8 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" +#include "video.h" typedef struct KerndeintContext { const AVClass *class; @@ -141,7 +141,12 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpic) return AVERROR(ENOMEM); } av_frame_copy_props(outpic, inpic); +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS outpic->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + outpic->flags &= ~AV_FRAME_FLAG_INTERLACED; for (plane = 0; plane < 4 && inpic->data[plane] && inpic->linesize[plane]; plane++) { h = plane == 0 ? inlink->h : AV_CEIL_RSHIFT(inlink->h, kerndeint->vsub); @@ -289,13 +294,6 @@ static const AVFilterPad kerndeint_inputs[] = { }, }; -static const AVFilterPad kerndeint_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_kerndeint = { .name = "kerndeint", @@ -304,6 +302,6 @@ const AVFilter ff_vf_kerndeint = { .priv_class = &kerndeint_class, .uninit = uninit, FILTER_INPUTS(kerndeint_inputs), - FILTER_OUTPUTS(kerndeint_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), }; diff --git a/libavfilter/vf_lagfun.c b/libavfilter/vf_lagfun.c index 69191ac1d36..20cdd9fa659 100644 --- a/libavfilter/vf_lagfun.c +++ b/libavfilter/vf_lagfun.c @@ -19,12 +19,10 @@ */ #include "libavutil/imgutils.h" -#include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -173,7 +171,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) av_frame_free(&in); return AVERROR(ENOMEM); } - out->pts = in->pts; + av_frame_copy_props(out, in); td.out = out; td.in = in; diff --git a/libavfilter/vf_lensfun.c b/libavfilter/vf_lensfun.c index 35c522a723d..b6af0834c86 100644 --- a/libavfilter/vf_lensfun.c +++ b/libavfilter/vf_lensfun.c @@ -28,11 +28,9 @@ #include #include -#include "libavutil/imgutils.h" #include "libavutil/opt.h" -#include "libswscale/swscale.h" #include "avfilter.h" -#include "formats.h" +#include "filters.h" #include "internal.h" #include "video.h" @@ -443,9 +441,14 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) AVFrame *out; VignettingThreadData vignetting_thread_data; DistortionCorrectionThreadData distortion_correction_thread_data; + int ret; if (lensfun->mode & VIGNETTING) { - av_frame_make_writable(in); + ret = ff_inlink_make_frame_writable(inlink, &in); + if (ret < 0) { + av_frame_free(&in); + return ret; + } vignetting_thread_data = (VignettingThreadData) { .width = inlink->w, @@ -516,13 +519,6 @@ static const AVFilterPad lensfun_inputs[] = { }, }; -static const AVFilterPad lensfun_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_lensfun = { .name = "lensfun", .description = NULL_IF_CONFIG_SMALL("Apply correction to an image based on info derived from the lensfun database."), @@ -530,7 +526,7 @@ const AVFilter ff_vf_lensfun = { .init = init, .uninit = uninit, FILTER_INPUTS(lensfun_inputs), - FILTER_OUTPUTS(lensfun_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_RGB24), .priv_class = &lensfun_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_libplacebo.c b/libavfilter/vf_libplacebo.c index d3c62d12a42..34879910538 100644 --- a/libavfilter/vf_libplacebo.c +++ b/libavfilter/vf_libplacebo.c @@ -16,19 +16,37 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/avassert.h" +#include "libavutil/eval.h" +#include "libavutil/fifo.h" #include "libavutil/file.h" #include "libavutil/opt.h" +#include "libavutil/parseutils.h" +#include "formats.h" #include "internal.h" +#include "filters.h" +#include "video.h" #include "vulkan_filter.h" #include "scale_eval.h" #include #include +#include #include +/* Backwards compatibility with older libplacebo */ +#if PL_API_VER < 276 +static inline AVFrame *pl_get_mapped_avframe(const struct pl_frame *frame) +{ + return frame->user_data; +} +#endif + enum { TONE_MAP_AUTO, TONE_MAP_CLIP, + TONE_MAP_ST2094_40, + TONE_MAP_ST2094_10, TONE_MAP_BT2390, TONE_MAP_BT2446A, TONE_MAP_SPLINE, @@ -40,19 +58,78 @@ enum { TONE_MAP_COUNT, }; -static const struct pl_tone_map_function * const tonemapping_funcs[TONE_MAP_COUNT] = { - [TONE_MAP_AUTO] = &pl_tone_map_auto, - [TONE_MAP_CLIP] = &pl_tone_map_clip, - [TONE_MAP_BT2390] = &pl_tone_map_bt2390, - [TONE_MAP_BT2446A] = &pl_tone_map_bt2446a, - [TONE_MAP_SPLINE] = &pl_tone_map_spline, - [TONE_MAP_REINHARD] = &pl_tone_map_reinhard, - [TONE_MAP_MOBIUS] = &pl_tone_map_mobius, - [TONE_MAP_HABLE] = &pl_tone_map_hable, - [TONE_MAP_GAMMA] = &pl_tone_map_gamma, - [TONE_MAP_LINEAR] = &pl_tone_map_linear, +enum { + GAMUT_MAP_CLIP, + GAMUT_MAP_PERCEPTUAL, + GAMUT_MAP_RELATIVE, + GAMUT_MAP_SATURATION, + GAMUT_MAP_ABSOLUTE, + GAMUT_MAP_DESATURATE, + GAMUT_MAP_DARKEN, + GAMUT_MAP_HIGHLIGHT, + GAMUT_MAP_LINEAR, + GAMUT_MAP_COUNT, }; +static const char *const var_names[] = { + "in_idx", "idx",///< index of input + "in_w", "iw", ///< width of the input video frame + "in_h", "ih", ///< height of the input video frame + "out_w", "ow", ///< width of the output video frame + "out_h", "oh", ///< height of the output video frame + "crop_w", "cw", ///< evaluated input crop width + "crop_h", "ch", ///< evaluated input crop height + "pos_w", "pw", ///< evaluated output placement width + "pos_h", "ph", ///< evaluated output placement height + "a", ///< iw/ih + "sar", ///< input pixel aspect ratio + "dar", ///< output pixel aspect ratio + "hsub", ///< input horizontal subsampling factor + "vsub", ///< input vertical subsampling factor + "ohsub", ///< output horizontal subsampling factor + "ovsub", ///< output vertical subsampling factor + "in_t", "t", ///< input frame pts + "out_t", "ot", ///< output frame pts + "n", ///< number of frame + NULL, +}; + +enum var_name { + VAR_IN_IDX, VAR_IDX, + VAR_IN_W, VAR_IW, + VAR_IN_H, VAR_IH, + VAR_OUT_W, VAR_OW, + VAR_OUT_H, VAR_OH, + VAR_CROP_W, VAR_CW, + VAR_CROP_H, VAR_CH, + VAR_POS_W, VAR_PW, + VAR_POS_H, VAR_PH, + VAR_A, + VAR_SAR, + VAR_DAR, + VAR_HSUB, + VAR_VSUB, + VAR_OHSUB, + VAR_OVSUB, + VAR_IN_T, VAR_T, + VAR_OUT_T, VAR_OT, + VAR_N, + VAR_VARS_NB +}; + +/* per-input dynamic filter state */ +typedef struct LibplaceboInput { + int idx; + pl_renderer renderer; + pl_queue queue; + enum pl_queue_status qstatus; + struct pl_frame_mix mix; ///< temporary storage + AVFilterLink *link; + AVFifo *out_pts; ///< timestamps of wanted output frames + int64_t status_pts; + int status; +} LibplaceboInput; + typedef struct LibplaceboContext { /* lavfi vulkan*/ FFVulkanContext vkctx; @@ -61,16 +138,32 @@ typedef struct LibplaceboContext { pl_log log; pl_vulkan vulkan; pl_gpu gpu; - pl_renderer renderer; - pl_tex tex[8]; + pl_tex tex[4]; + + /* input state */ + LibplaceboInput *inputs; + int nb_inputs; + int64_t status_pts; ///< tracks status of most recently used input + int status; /* settings */ char *out_format_string; enum AVPixelFormat out_format; + char *fillcolor; + double var_values[VAR_VARS_NB]; char *w_expr; char *h_expr; - AVRational target_sar; + char *fps_string; + AVRational fps; ///< parsed FPS, or 0/0 for "none" + char *crop_x_expr, *crop_y_expr; + char *crop_w_expr, *crop_h_expr; + char *pos_x_expr, *pos_y_expr; + char *pos_w_expr, *pos_h_expr; + // Parsed expressions for input/output crop + AVExpr *crop_x_pexpr, *crop_y_pexpr, *crop_w_pexpr, *crop_h_pexpr; + AVExpr *pos_x_pexpr, *pos_y_pexpr, *pos_w_pexpr, *pos_h_pexpr; float pad_crop_ratio; + float corner_rounding; int force_original_aspect_ratio; int force_divisible_by; int normalize_sar; @@ -82,8 +175,10 @@ typedef struct LibplaceboContext { int color_trc; /* pl_render_params */ + struct pl_render_params params; char *upscaler; char *downscaler; + char *frame_mixer; int lut_entries; float antiringing; int sigmoid; @@ -91,11 +186,11 @@ typedef struct LibplaceboContext { float polar_cutoff; int disable_linear; int disable_builtin; - int force_icc_lut; int force_dither; int disable_fbos; /* pl_deband_params */ + struct pl_deband_params deband_params; int deband; int deband_iterations; float deband_threshold; @@ -103,6 +198,7 @@ typedef struct LibplaceboContext { float deband_grain; /* pl_color_adjustment */ + struct pl_color_adjustment color_adjustment; float brightness; float contrast; float saturation; @@ -110,34 +206,46 @@ typedef struct LibplaceboContext { float gamma; /* pl_peak_detect_params */ + struct pl_peak_detect_params peak_detect_params; int peakdetect; float smoothing; float min_peak; float scene_low; float scene_high; - float overshoot; + float percentile; /* pl_color_map_params */ - int intent; + struct pl_color_map_params color_map_params; int gamut_mode; int tonemapping; float tonemapping_param; - int tonemapping_mode; int inverse_tonemapping; - float crosstalk; int tonemapping_lut_size; + float contrast_recovery; + float contrast_smoothness; + +#if FF_API_LIBPLACEBO_OPTS /* for backwards compatibility */ float desat_str; float desat_exp; int gamut_warning; int gamut_clipping; + int force_icc_lut; + int intent; + int tonemapping_mode; + float crosstalk; + float overshoot; + float hybrid_mix; +#endif - /* pl_dither_params */ + /* pl_dither_params */ + struct pl_dither_params dither_params; int dithering; int dither_lut_size; int dither_temporal; /* pl_cone_params */ + struct pl_cone_params cone_params; int cones; float cone_str; @@ -178,34 +286,69 @@ static void pl_av_log(void *log_ctx, enum pl_log_level level, const char *msg) av_log(log_ctx, av_lev, "%s\n", msg); } -static int parse_shader(AVFilterContext *avctx, const void *shader, size_t len) -{ - LibplaceboContext *s = avctx->priv; - const struct pl_hook *hook; +static const struct pl_tone_map_function *get_tonemapping_func(int tm) { + switch (tm) { + case TONE_MAP_AUTO: return &pl_tone_map_auto; + case TONE_MAP_CLIP: return &pl_tone_map_clip; +#if PL_API_VER >= 246 + case TONE_MAP_ST2094_40: return &pl_tone_map_st2094_40; + case TONE_MAP_ST2094_10: return &pl_tone_map_st2094_10; +#endif + case TONE_MAP_BT2390: return &pl_tone_map_bt2390; + case TONE_MAP_BT2446A: return &pl_tone_map_bt2446a; + case TONE_MAP_SPLINE: return &pl_tone_map_spline; + case TONE_MAP_REINHARD: return &pl_tone_map_reinhard; + case TONE_MAP_MOBIUS: return &pl_tone_map_mobius; + case TONE_MAP_HABLE: return &pl_tone_map_hable; + case TONE_MAP_GAMMA: return &pl_tone_map_gamma; + case TONE_MAP_LINEAR: return &pl_tone_map_linear; + default: av_assert0(0); + } +} - hook = pl_mpv_user_shader_parse(s->gpu, shader, len); - if (!hook) { - av_log(s, AV_LOG_ERROR, "Failed parsing custom shader!\n"); - return AVERROR(EINVAL); +static void set_gamut_mode(struct pl_color_map_params *p, int gamut_mode) +{ + switch (gamut_mode) { +#if PL_API_VER >= 269 + case GAMUT_MAP_CLIP: p->gamut_mapping = &pl_gamut_map_clip; return; + case GAMUT_MAP_PERCEPTUAL: p->gamut_mapping = &pl_gamut_map_perceptual; return; + case GAMUT_MAP_RELATIVE: p->gamut_mapping = &pl_gamut_map_relative; return; + case GAMUT_MAP_SATURATION: p->gamut_mapping = &pl_gamut_map_saturation; return; + case GAMUT_MAP_ABSOLUTE: p->gamut_mapping = &pl_gamut_map_absolute; return; + case GAMUT_MAP_DESATURATE: p->gamut_mapping = &pl_gamut_map_desaturate; return; + case GAMUT_MAP_DARKEN: p->gamut_mapping = &pl_gamut_map_darken; return; + case GAMUT_MAP_HIGHLIGHT: p->gamut_mapping = &pl_gamut_map_highlight; return; + case GAMUT_MAP_LINEAR: p->gamut_mapping = &pl_gamut_map_linear; return; +#else + case GAMUT_MAP_RELATIVE: p->intent = PL_INTENT_RELATIVE_COLORIMETRIC; return; + case GAMUT_MAP_SATURATION: p->intent = PL_INTENT_SATURATION; return; + case GAMUT_MAP_ABSOLUTE: p->intent = PL_INTENT_ABSOLUTE_COLORIMETRIC; return; + case GAMUT_MAP_DESATURATE: p->gamut_mode = PL_GAMUT_DESATURATE; return; + case GAMUT_MAP_DARKEN: p->gamut_mode = PL_GAMUT_DARKEN; return; + case GAMUT_MAP_HIGHLIGHT: p->gamut_mode = PL_GAMUT_WARN; return; + /* Use defaults for all other cases */ + default: return; +#endif } - s->hooks[s->num_hooks++] = hook; - return 0; -} + av_assert0(0); +}; static int find_scaler(AVFilterContext *avctx, const struct pl_filter_config **opt, - const char *name) + const char *name, int frame_mixing) { - const struct pl_filter_preset *preset; + const struct pl_filter_preset *preset, *presets_avail; + presets_avail = frame_mixing ? pl_frame_mixers : pl_scale_filters; + if (!strcmp(name, "help")) { av_log(avctx, AV_LOG_INFO, "Available scaler presets:\n"); - for (preset = pl_scale_filters; preset->name; preset++) + for (preset = presets_avail; preset->name; preset++) av_log(avctx, AV_LOG_INFO, " %s\n", preset->name); return AVERROR_EXIT; } - for (preset = pl_scale_filters; preset->name; preset++) { + for (preset = presets_avail; preset->name; preset++) { if (!strcmp(name, preset->name)) { *opt = preset->filter; return 0; @@ -216,10 +359,163 @@ static int find_scaler(AVFilterContext *avctx, return AVERROR(EINVAL); } +static int update_settings(AVFilterContext *ctx) +{ + int err = 0; + LibplaceboContext *s = ctx->priv; + int gamut_mode = s->gamut_mode; + uint8_t color_rgba[4]; + +#if FF_API_LIBPLACEBO_OPTS + float hybrid_mix = s->hybrid_mix; + /* backwards compatibility with older API */ + switch (s->tonemapping_mode) { + case 0: /*PL_TONE_MAP_AUTO*/ + if (s->desat_str >= 0.0f) + hybrid_mix = s->desat_str; + break; + case 1: /*PL_TONE_MAP_RGB*/ hybrid_mix = 1.0f; break; + case 2: /*PL_TONE_MAP_HYBRID*/ hybrid_mix = 0.2f; break; + case 3: /*PL_TONE_MAP_LUMA*/ hybrid_mix = 0.0f; break; + case 4: /*PL_TONE_MAP_MAX*/ hybrid_mix = 0.0f; break; + } + + switch (s->intent) { + case PL_INTENT_SATURATION: gamut_mode = GAMUT_MAP_SATURATION; break; + case PL_INTENT_RELATIVE_COLORIMETRIC: gamut_mode = GAMUT_MAP_RELATIVE; break; + case PL_INTENT_ABSOLUTE_COLORIMETRIC: gamut_mode = GAMUT_MAP_ABSOLUTE; break; + } + + if (s->gamut_warning) + gamut_mode = GAMUT_MAP_HIGHLIGHT; + if (s->gamut_clipping) + gamut_mode = GAMUT_MAP_DESATURATE; +#endif + + RET(av_parse_color(color_rgba, s->fillcolor, -1, s)); + + s->deband_params = *pl_deband_params( + .iterations = s->deband_iterations, + .threshold = s->deband_threshold, + .radius = s->deband_radius, + .grain = s->deband_grain, + ); + + s->color_adjustment = (struct pl_color_adjustment) { + .brightness = s->brightness, + .contrast = s->contrast, + .saturation = s->saturation, + .hue = s->hue, + .gamma = s->gamma, + }; + + s->peak_detect_params = *pl_peak_detect_params( + .smoothing_period = s->smoothing, + .minimum_peak = s->min_peak, + .scene_threshold_low = s->scene_low, + .scene_threshold_high = s->scene_high, +#if PL_API_VER >= 263 + .percentile = s->percentile, +#endif +#if FF_API_LIBPLACEBO_OPTS && PL_API_VER < 256 + .overshoot_margin = s->overshoot, +#endif + ); + + s->color_map_params = *pl_color_map_params( +#if FF_API_LIBPLACEBO_OPTS +# if PL_API_VER >= 269 + .hybrid_mix = hybrid_mix, +# else + .tone_mapping_mode = s->tonemapping_mode, + .tone_mapping_crosstalk = s->crosstalk, +# endif +#endif + .tone_mapping_function = get_tonemapping_func(s->tonemapping), + .tone_mapping_param = s->tonemapping_param, + .inverse_tone_mapping = s->inverse_tonemapping, + .lut_size = s->tonemapping_lut_size, +#if PL_API_VER >= 285 + .contrast_recovery = s->contrast_recovery, + .contrast_smoothness = s->contrast_smoothness, +#endif + ); + + set_gamut_mode(&s->color_map_params, gamut_mode); + + s->dither_params = *pl_dither_params( + .method = s->dithering, + .lut_size = s->dither_lut_size, + .temporal = s->dither_temporal, + ); + + s->cone_params = *pl_cone_params( + .cones = s->cones, + .strength = s->cone_str, + ); + + s->params = *pl_render_params( + .lut_entries = s->lut_entries, + .antiringing_strength = s->antiringing, + .background_transparency = 1.0f - (float) color_rgba[3] / UINT8_MAX, + .background_color = { + (float) color_rgba[0] / UINT8_MAX, + (float) color_rgba[1] / UINT8_MAX, + (float) color_rgba[2] / UINT8_MAX, + }, +#if PL_API_VER >= 277 + .corner_rounding = s->corner_rounding, +#endif + + .deband_params = s->deband ? &s->deband_params : NULL, + .sigmoid_params = s->sigmoid ? &pl_sigmoid_default_params : NULL, + .color_adjustment = &s->color_adjustment, + .peak_detect_params = s->peakdetect ? &s->peak_detect_params : NULL, + .color_map_params = &s->color_map_params, + .dither_params = s->dithering >= 0 ? &s->dither_params : NULL, + .cone_params = s->cones ? &s->cone_params : NULL, + + .hooks = s->hooks, + .num_hooks = s->num_hooks, + + .skip_anti_aliasing = s->skip_aa, + .polar_cutoff = s->polar_cutoff, + .disable_linear_scaling = s->disable_linear, + .disable_builtin_scalers = s->disable_builtin, + .force_dither = s->force_dither, + .disable_fbos = s->disable_fbos, + ); + + RET(find_scaler(ctx, &s->params.upscaler, s->upscaler, 0)); + RET(find_scaler(ctx, &s->params.downscaler, s->downscaler, 0)); + RET(find_scaler(ctx, &s->params.frame_mixer, s->frame_mixer, 1)); + return 0; + +fail: + return err; +} + +static int parse_shader(AVFilterContext *avctx, const void *shader, size_t len) +{ + LibplaceboContext *s = avctx->priv; + const struct pl_hook *hook; + + hook = pl_mpv_user_shader_parse(s->gpu, shader, len); + if (!hook) { + av_log(s, AV_LOG_ERROR, "Failed parsing custom shader!\n"); + return AVERROR(EINVAL); + } + + s->hooks[s->num_hooks++] = hook; + return update_settings(avctx); +} + static void libplacebo_uninit(AVFilterContext *avctx); +static int libplacebo_config_input(AVFilterLink *inlink); static int libplacebo_init(AVFilterContext *avctx) { + int err = 0; LibplaceboContext *s = avctx->priv; /* Create libplacebo log context */ @@ -244,68 +540,141 @@ static int libplacebo_init(AVFilterContext *avctx) s->out_format = AV_PIX_FMT_NONE; } + for (int i = 0; i < s->nb_inputs; i++) { + AVFilterPad pad = { + .name = av_asprintf("input%d", i), + .type = AVMEDIA_TYPE_VIDEO, + .config_props = &libplacebo_config_input, + }; + if (!pad.name) + return AVERROR(ENOMEM); + RET(ff_append_inpad_free_name(avctx, &pad)); + } + + RET(update_settings(avctx)); + RET(av_expr_parse(&s->crop_x_pexpr, s->crop_x_expr, var_names, + NULL, NULL, NULL, NULL, 0, s)); + RET(av_expr_parse(&s->crop_y_pexpr, s->crop_y_expr, var_names, + NULL, NULL, NULL, NULL, 0, s)); + RET(av_expr_parse(&s->crop_w_pexpr, s->crop_w_expr, var_names, + NULL, NULL, NULL, NULL, 0, s)); + RET(av_expr_parse(&s->crop_h_pexpr, s->crop_h_expr, var_names, + NULL, NULL, NULL, NULL, 0, s)); + RET(av_expr_parse(&s->pos_x_pexpr, s->pos_x_expr, var_names, + NULL, NULL, NULL, NULL, 0, s)); + RET(av_expr_parse(&s->pos_y_pexpr, s->pos_y_expr, var_names, + NULL, NULL, NULL, NULL, 0, s)); + RET(av_expr_parse(&s->pos_w_pexpr, s->pos_w_expr, var_names, + NULL, NULL, NULL, NULL, 0, s)); + RET(av_expr_parse(&s->pos_h_pexpr, s->pos_h_expr, var_names, + NULL, NULL, NULL, NULL, 0, s)); + + if (strcmp(s->fps_string, "none") != 0) + RET(av_parse_video_rate(&s->fps, s->fps_string)); + /* Note: s->vulkan etc. are initialized later, when hwctx is available */ return 0; + +fail: + return err; +} + +#if PL_API_VER >= 278 +static void lock_queue(void *priv, uint32_t qf, uint32_t qidx) +{ + AVHWDeviceContext *avhwctx = priv; + const AVVulkanDeviceContext *hwctx = avhwctx->hwctx; + hwctx->lock_queue(avhwctx, qf, qidx); } -static int init_vulkan(AVFilterContext *avctx) +static void unlock_queue(void *priv, uint32_t qf, uint32_t qidx) +{ + AVHWDeviceContext *avhwctx = priv; + const AVVulkanDeviceContext *hwctx = avhwctx->hwctx; + hwctx->unlock_queue(avhwctx, qf, qidx); +} +#endif + +static int input_init(AVFilterContext *avctx, AVFilterLink *link, + LibplaceboInput *input, int idx) +{ + LibplaceboContext *s = avctx->priv; + + input->out_pts = av_fifo_alloc2(1, sizeof(int64_t), AV_FIFO_FLAG_AUTO_GROW); + if (!input->out_pts) + return AVERROR(ENOMEM); + input->queue = pl_queue_create(s->gpu); + input->renderer = pl_renderer_create(s->log, s->gpu); + input->link = link; + input->idx = idx; + + return 0; +} + +static void input_uninit(LibplaceboInput *input) +{ + pl_renderer_destroy(&input->renderer); + pl_queue_destroy(&input->queue); + av_fifo_freep2(&input->out_pts); +} + +static int init_vulkan(AVFilterContext *avctx, const AVVulkanDeviceContext *hwctx) { int err = 0; LibplaceboContext *s = avctx->priv; - const AVHWDeviceContext *avhwctx; - const AVVulkanDeviceContext *hwctx; uint8_t *buf = NULL; size_t buf_len; - if (!avctx->hw_device_ctx) { - av_log(s, AV_LOG_ERROR, "Missing vulkan hwdevice for vf_libplacebo.\n"); - return AVERROR(EINVAL); - } - - avhwctx = avctx->hw_device_ctx->data; - - if (avhwctx->type != AV_HWDEVICE_TYPE_VULKAN) { - av_log(s, AV_LOG_ERROR, "Expected vulkan hwdevice for vf_libplacebo, got %s.\n", - av_hwdevice_get_type_name(avhwctx->type)); - return AVERROR(EINVAL); + if (hwctx) { +#if PL_API_VER >= 278 + /* Import libavfilter vulkan context into libplacebo */ + s->vulkan = pl_vulkan_import(s->log, pl_vulkan_import_params( + .instance = hwctx->inst, + .get_proc_addr = hwctx->get_proc_addr, + .phys_device = hwctx->phys_dev, + .device = hwctx->act_dev, + .extensions = hwctx->enabled_dev_extensions, + .num_extensions = hwctx->nb_enabled_dev_extensions, + .features = &hwctx->device_features, + .lock_queue = lock_queue, + .unlock_queue = unlock_queue, + .queue_ctx = avctx->hw_device_ctx->data, + .queue_graphics = { + .index = hwctx->queue_family_index, + .count = hwctx->nb_graphics_queues, + }, + .queue_compute = { + .index = hwctx->queue_family_comp_index, + .count = hwctx->nb_comp_queues, + }, + .queue_transfer = { + .index = hwctx->queue_family_tx_index, + .count = hwctx->nb_tx_queues, + }, + /* This is the highest version created by hwcontext_vulkan.c */ + .max_api_version = VK_API_VERSION_1_3, + )); +#else + av_log(s, AV_LOG_ERROR, "libplacebo version %s too old to import " + "Vulkan device, remove it or upgrade libplacebo to >= 5.278\n", + PL_VERSION); + err = AVERROR_EXTERNAL; + goto fail; +#endif + } else { + s->vulkan = pl_vulkan_create(s->log, pl_vulkan_params( + .queue_count = 0, /* enable all queues for parallelization */ + )); } - hwctx = avhwctx->hwctx; - - /* Import libavfilter vulkan context into libplacebo */ - s->vulkan = pl_vulkan_import(s->log, pl_vulkan_import_params( - .instance = hwctx->inst, - .get_proc_addr = hwctx->get_proc_addr, - .phys_device = hwctx->phys_dev, - .device = hwctx->act_dev, - .extensions = hwctx->enabled_dev_extensions, - .num_extensions = hwctx->nb_enabled_dev_extensions, - .features = &hwctx->device_features, - .queue_graphics = { - .index = hwctx->queue_family_index, - .count = hwctx->nb_graphics_queues, - }, - .queue_compute = { - .index = hwctx->queue_family_comp_index, - .count = hwctx->nb_comp_queues, - }, - .queue_transfer = { - .index = hwctx->queue_family_tx_index, - .count = hwctx->nb_tx_queues, - }, - /* This is the highest version created by hwcontext_vulkan.c */ - .max_api_version = VK_API_VERSION_1_2, - )); - if (!s->vulkan) { - av_log(s, AV_LOG_ERROR, "Failed importing vulkan device to libplacebo!\n"); + av_log(s, AV_LOG_ERROR, "Failed %s Vulkan device!\n", + hwctx ? "importing" : "creating"); err = AVERROR_EXTERNAL; goto fail; } - /* Create the renderer */ s->gpu = s->vulkan->gpu; - s->renderer = pl_renderer_create(s->log, s->gpu); /* Parse the user shaders, if requested */ if (s->shader_bin_len) @@ -316,6 +685,13 @@ static int init_vulkan(AVFilterContext *avctx) RET(parse_shader(avctx, buf, buf_len)); } + /* Initialize inputs */ + s->inputs = av_calloc(s->nb_inputs, sizeof(*s->inputs)); + if (!s->inputs) + return AVERROR(ENOMEM); + for (int i = 0; i < s->nb_inputs; i++) + RET(input_init(avctx, avctx->inputs[i], &s->inputs[i], i)); + /* fall through */ fail: if (buf) @@ -331,176 +707,144 @@ static void libplacebo_uninit(AVFilterContext *avctx) pl_tex_destroy(s->gpu, &s->tex[i]); for (int i = 0; i < s->num_hooks; i++) pl_mpv_user_shader_destroy(&s->hooks[i]); - pl_renderer_destroy(&s->renderer); + if (s->inputs) { + for (int i = 0; i < s->nb_inputs; i++) + input_uninit(&s->inputs[i]); + av_freep(&s->inputs); + } pl_vulkan_destroy(&s->vulkan); pl_log_destroy(&s->log); ff_vk_uninit(&s->vkctx); s->gpu = NULL; + + av_expr_free(s->crop_x_pexpr); + av_expr_free(s->crop_y_pexpr); + av_expr_free(s->crop_w_pexpr); + av_expr_free(s->crop_h_pexpr); + av_expr_free(s->pos_x_pexpr); + av_expr_free(s->pos_y_pexpr); + av_expr_free(s->pos_w_pexpr); + av_expr_free(s->pos_h_pexpr); } -static int process_frames(AVFilterContext *avctx, AVFrame *out, AVFrame *in) +static int libplacebo_process_command(AVFilterContext *ctx, const char *cmd, + const char *arg, char *res, int res_len, + int flags) { - int err = 0, ok; - LibplaceboContext *s = avctx->priv; - struct pl_render_params params; - enum pl_tone_map_mode tonemapping_mode = s->tonemapping_mode; - const AVPixFmtDescriptor *outdesc = av_pix_fmt_desc_get(out->format); - enum pl_gamut_mode gamut_mode = s->gamut_mode; - struct pl_frame image, target; - ok = pl_map_avframe_ex(s->gpu, &image, pl_avframe_params( - .frame = in, - .tex = s->tex, - .map_dovi = s->apply_dovi, - )); - - if (outdesc->flags & AV_PIX_FMT_FLAG_HWACCEL) { - ok &= pl_map_avframe_ex(s->gpu, &target, pl_avframe_params( - .frame = out, - .map_dovi = false, - )); - } else { - ok &= pl_frame_recreate_from_avframe(s->gpu, &target, s->tex + 4, out); - } - - if (!ok) { - err = AVERROR_EXTERNAL; - goto fail; - } + int err = 0; + RET(ff_filter_process_command(ctx, cmd, arg, res, res_len, flags)); + RET(update_settings(ctx)); + return 0; - if (!s->apply_filmgrain) - image.film_grain.type = PL_FILM_GRAIN_NONE; +fail: + return err; +} - if (s->target_sar.num) { - float aspect = pl_rect2df_aspect(&target.crop) * av_q2d(s->target_sar); - pl_rect2df_aspect_set(&target.crop, aspect, s->pad_crop_ratio); +static const AVFrame *ref_frame(const struct pl_frame_mix *mix) +{ + for (int i = 0; i < mix->num_frames; i++) { + if (i+1 == mix->num_frames || mix->timestamps[i+1] > 0) + return pl_get_mapped_avframe(mix->frames[i]); } + return NULL; +} - /* backwards compatibility with older API */ - if (!tonemapping_mode && (s->desat_str >= 0.0f || s->desat_exp >= 0.0f)) { - float str = s->desat_str < 0.0f ? 0.9f : s->desat_str; - float exp = s->desat_exp < 0.0f ? 0.2f : s->desat_exp; - if (str >= 0.9f && exp <= 0.1f) { - tonemapping_mode = PL_TONE_MAP_RGB; - } else if (str > 0.1f) { - tonemapping_mode = PL_TONE_MAP_HYBRID; - } else { - tonemapping_mode = PL_TONE_MAP_LUMA; +static void update_crops(AVFilterContext *ctx, LibplaceboInput *in, + struct pl_frame *target, double target_pts) +{ + LibplaceboContext *s = ctx->priv; + const AVFrame *ref = ref_frame(&in->mix); + + for (int i = 0; i < in->mix.num_frames; i++) { + // Mutate the `pl_frame.crop` fields in-place. This is fine because we + // own the entire pl_queue, and hence, the pointed-at frames. + struct pl_frame *image = (struct pl_frame *) in->mix.frames[i]; + const AVFrame *src = pl_get_mapped_avframe(image); + double image_pts = src->pts * av_q2d(in->link->time_base); + + /* Update dynamic variables */ + s->var_values[VAR_IN_IDX] = s->var_values[VAR_IDX] = in->idx; + s->var_values[VAR_IN_W] = s->var_values[VAR_IW] = in->link->w; + s->var_values[VAR_IN_H] = s->var_values[VAR_IH] = in->link->h; + s->var_values[VAR_A] = (double) in->link->w / in->link->h; + s->var_values[VAR_SAR] = in->link->sample_aspect_ratio.num ? + av_q2d(in->link->sample_aspect_ratio) : 1.0; + s->var_values[VAR_IN_T] = s->var_values[VAR_T] = image_pts; + s->var_values[VAR_OUT_T] = s->var_values[VAR_OT] = target_pts; + s->var_values[VAR_N] = ctx->outputs[0]->frame_count_out; + + /* Clear these explicitly to avoid leaking previous frames' state */ + s->var_values[VAR_CROP_W] = s->var_values[VAR_CW] = NAN; + s->var_values[VAR_CROP_H] = s->var_values[VAR_CH] = NAN; + s->var_values[VAR_POS_W] = s->var_values[VAR_PW] = NAN; + s->var_values[VAR_POS_H] = s->var_values[VAR_PH] = NAN; + + /* Compute dimensions first and placement second */ + s->var_values[VAR_CROP_W] = s->var_values[VAR_CW] = + av_expr_eval(s->crop_w_pexpr, s->var_values, NULL); + s->var_values[VAR_CROP_H] = s->var_values[VAR_CH] = + av_expr_eval(s->crop_h_pexpr, s->var_values, NULL); + s->var_values[VAR_CROP_W] = s->var_values[VAR_CW] = + av_expr_eval(s->crop_w_pexpr, s->var_values, NULL); + s->var_values[VAR_POS_W] = s->var_values[VAR_PW] = + av_expr_eval(s->pos_w_pexpr, s->var_values, NULL); + s->var_values[VAR_POS_H] = s->var_values[VAR_PH] = + av_expr_eval(s->pos_h_pexpr, s->var_values, NULL); + s->var_values[VAR_POS_W] = s->var_values[VAR_PW] = + av_expr_eval(s->pos_w_pexpr, s->var_values, NULL); + + image->crop.x0 = av_expr_eval(s->crop_x_pexpr, s->var_values, NULL); + image->crop.y0 = av_expr_eval(s->crop_y_pexpr, s->var_values, NULL); + image->crop.x1 = image->crop.x0 + s->var_values[VAR_CROP_W]; + image->crop.y1 = image->crop.y0 + s->var_values[VAR_CROP_H]; + + if (src == ref) { + /* Only update the target crop once, for the 'reference' frame */ + target->crop.x0 = av_expr_eval(s->pos_x_pexpr, s->var_values, NULL); + target->crop.y0 = av_expr_eval(s->pos_y_pexpr, s->var_values, NULL); + target->crop.x1 = target->crop.x0 + s->var_values[VAR_POS_W]; + target->crop.y1 = target->crop.y0 + s->var_values[VAR_POS_H]; + if (s->normalize_sar) { + float aspect = pl_rect2df_aspect(&image->crop); + aspect *= av_q2d(in->link->sample_aspect_ratio); + pl_rect2df_aspect_set(&target->crop, aspect, s->pad_crop_ratio); + } } } - - if (s->gamut_warning) - gamut_mode = PL_GAMUT_WARN; - if (s->gamut_clipping) - gamut_mode = PL_GAMUT_DESATURATE; - - /* Update render params */ - params = (struct pl_render_params) { - PL_RENDER_DEFAULTS - .lut_entries = s->lut_entries, - .antiringing_strength = s->antiringing, - - .deband_params = !s->deband ? NULL : pl_deband_params( - .iterations = s->deband_iterations, - .threshold = s->deband_threshold, - .radius = s->deband_radius, - .grain = s->deband_grain, - ), - - .sigmoid_params = s->sigmoid ? &pl_sigmoid_default_params : NULL, - - .color_adjustment = &(struct pl_color_adjustment) { - .brightness = s->brightness, - .contrast = s->contrast, - .saturation = s->saturation, - .hue = s->hue, - .gamma = s->gamma, - }, - - .peak_detect_params = !s->peakdetect ? NULL : pl_peak_detect_params( - .smoothing_period = s->smoothing, - .minimum_peak = s->min_peak, - .scene_threshold_low = s->scene_low, - .scene_threshold_high = s->scene_high, - .overshoot_margin = s->overshoot, - ), - - .color_map_params = pl_color_map_params( - .intent = s->intent, - .gamut_mode = gamut_mode, - .tone_mapping_function = tonemapping_funcs[s->tonemapping], - .tone_mapping_param = s->tonemapping_param, - .tone_mapping_mode = tonemapping_mode, - .inverse_tone_mapping = s->inverse_tonemapping, - .tone_mapping_crosstalk = s->crosstalk, - .lut_size = s->tonemapping_lut_size, - ), - - .dither_params = s->dithering < 0 ? NULL : pl_dither_params( - .method = s->dithering, - .lut_size = s->dither_lut_size, - .temporal = s->dither_temporal, - ), - - .cone_params = !s->cones ? NULL : pl_cone_params( - .cones = s->cones, - .strength = s->cone_str, - ), - - .hooks = s->hooks, - .num_hooks = s->num_hooks, - - .skip_anti_aliasing = s->skip_aa, - .polar_cutoff = s->polar_cutoff, - .disable_linear_scaling = s->disable_linear, - .disable_builtin_scalers = s->disable_builtin, - .force_icc_lut = s->force_icc_lut, - .force_dither = s->force_dither, - .disable_fbos = s->disable_fbos, - }; - - RET(find_scaler(avctx, ¶ms.upscaler, s->upscaler)); - RET(find_scaler(avctx, ¶ms.downscaler, s->downscaler)); - - pl_render_image(s->renderer, &image, &target, ¶ms); - pl_unmap_avframe(s->gpu, &image); - - if (outdesc->flags & AV_PIX_FMT_FLAG_HWACCEL) { - pl_unmap_avframe(s->gpu, &target); - } else if (!pl_download_avframe(s->gpu, &target, out)) { - err = AVERROR_EXTERNAL; - goto fail; - } - - /* Flush the command queues for performance */ - pl_gpu_flush(s->gpu); - return 0; - -fail: - pl_unmap_avframe(s->gpu, &image); - pl_unmap_avframe(s->gpu, &target); - return err; } -static int filter_frame(AVFilterLink *link, AVFrame *in) +/* Construct and emit an output frame for a given timestamp */ +static int output_frame(AVFilterContext *ctx, int64_t pts) { - int err, changed_csp; - AVFilterContext *ctx = link->dst; + int err = 0, ok, changed_csp; LibplaceboContext *s = ctx->priv; AVFilterLink *outlink = ctx->outputs[0]; - - AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h); - if (!out) { - err = AVERROR(ENOMEM); - goto fail; + const AVPixFmtDescriptor *outdesc = av_pix_fmt_desc_get(outlink->format); + struct pl_frame target; + const AVFrame *ref = NULL; + AVFrame *out; + + /* Use the first active input as metadata reference */ + for (int i = 0; i < s->nb_inputs; i++) { + const LibplaceboInput *in = &s->inputs[i]; + if (in->qstatus == PL_QUEUE_OK && (ref = ref_frame(&in->mix))) + break; } + if (!ref) + return 0; - pl_log_level_update(s->log, get_log_level()); + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) + return AVERROR(ENOMEM); - RET(av_frame_copy_props(out, in)); + RET(av_frame_copy_props(out, ref)); + out->pts = pts; out->width = outlink->w; out->height = outlink->h; + if (s->fps.num) + out->duration = 1; - if (s->apply_dovi && av_frame_get_side_data(in, AV_FRAME_DATA_DOVI_METADATA)) { + if (s->apply_dovi && av_frame_get_side_data(ref, AV_FRAME_DATA_DOVI_METADATA)) { /* Output of dovi reshaping is always BT.2020+PQ, so infer the correct * output colorspace defaults */ out->colorspace = AVCOL_SPC_BT2020_NCL; @@ -517,15 +861,23 @@ static int filter_frame(AVFilterLink *link, AVFrame *in) if (s->color_primaries >= 0) out->color_primaries = s->color_primaries; - changed_csp = in->colorspace != out->colorspace || - in->color_range != out->color_range || - in->color_trc != out->color_trc || - in->color_primaries != out->color_primaries; + /* Sanity colorspace overrides */ + if (outdesc->flags & AV_PIX_FMT_FLAG_RGB) { + out->colorspace = AVCOL_SPC_RGB; + } else if (out->colorspace == AVCOL_SPC_RGB) { + out->colorspace = AVCOL_SPC_UNSPECIFIED; + } + + changed_csp = ref->colorspace != out->colorspace || + ref->color_range != out->color_range || + ref->color_trc != out->color_trc || + ref->color_primaries != out->color_primaries; /* Strip side data if no longer relevant */ if (changed_csp) { av_frame_remove_side_data(out, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); av_frame_remove_side_data(out, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); + av_frame_remove_side_data(out, AV_FRAME_DATA_ICC_PROFILE); } if (s->apply_dovi || changed_csp) { av_frame_remove_side_data(out, AV_FRAME_DATA_DOVI_RPU_BUFFER); @@ -534,26 +886,227 @@ static int filter_frame(AVFilterLink *link, AVFrame *in) if (s->apply_filmgrain) av_frame_remove_side_data(out, AV_FRAME_DATA_FILM_GRAIN_PARAMS); - RET(process_frames(ctx, out, in)); + /* Map, render and unmap output frame */ + if (outdesc->flags & AV_PIX_FMT_FLAG_HWACCEL) { + ok = pl_map_avframe_ex(s->gpu, &target, pl_avframe_params( + .frame = out, + .map_dovi = false, + )); + } else { + ok = pl_frame_recreate_from_avframe(s->gpu, &target, s->tex, out); + } + if (!ok) { + err = AVERROR_EXTERNAL; + goto fail; + } - av_frame_free(&in); + /* Draw first frame opaque, others with blending */ + s->params.skip_target_clearing = false; + s->params.blend_params = NULL; + for (int i = 0; i < s->nb_inputs; i++) { + LibplaceboInput *in = &s->inputs[i]; + int high_fps = av_cmp_q(in->link->frame_rate, outlink->frame_rate) >= 0; + if (in->qstatus != PL_QUEUE_OK) + continue; + s->params.skip_caching_single_frame = high_fps; + update_crops(ctx, in, &target, out->pts * av_q2d(outlink->time_base)); + pl_render_image_mix(in->renderer, &in->mix, &target, &s->params); + s->params.skip_target_clearing = true; + s->params.blend_params = &pl_alpha_overlay; + } + if (outdesc->flags & AV_PIX_FMT_FLAG_HWACCEL) { + pl_unmap_avframe(s->gpu, &target); + } else if (!pl_download_avframe(s->gpu, &target, out)) { + err = AVERROR_EXTERNAL; + goto fail; + } return ff_filter_frame(outlink, out); fail: - av_frame_free(&in); av_frame_free(&out); return err; } +static bool map_frame(pl_gpu gpu, pl_tex *tex, + const struct pl_source_frame *src, + struct pl_frame *out) +{ + AVFrame *avframe = src->frame_data; + LibplaceboContext *s = avframe->opaque; + bool ok = pl_map_avframe_ex(gpu, out, pl_avframe_params( + .frame = avframe, + .tex = tex, + .map_dovi = s->apply_dovi, + )); + + if (!s->apply_filmgrain) + out->film_grain.type = PL_FILM_GRAIN_NONE; + + av_frame_free(&avframe); + return ok; +} + +static void unmap_frame(pl_gpu gpu, struct pl_frame *frame, + const struct pl_source_frame *src) +{ + pl_unmap_avframe(gpu, frame); +} + +static void discard_frame(const struct pl_source_frame *src) +{ + AVFrame *avframe = src->frame_data; + av_frame_free(&avframe); +} + +static int handle_input(AVFilterContext *ctx, LibplaceboInput *input) +{ + int ret, status; + LibplaceboContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + AVFrame *in; + int64_t pts; + + while ((ret = ff_inlink_consume_frame(input->link, &in)) > 0) { + in->opaque = s; + pl_queue_push(input->queue, &(struct pl_source_frame) { + .pts = in->pts * av_q2d(input->link->time_base), + .duration = in->duration * av_q2d(input->link->time_base), + .first_field = pl_field_from_avframe(in), + .frame_data = in, + .map = map_frame, + .unmap = unmap_frame, + .discard = discard_frame, + }); + + if (!s->fps.num) { + /* Internally queue an output frame for the same PTS */ + pts = av_rescale_q(in->pts, input->link->time_base, outlink->time_base); + av_fifo_write(input->out_pts, &pts, 1); + } + } + + if (ret < 0) + return ret; + + if (!input->status && ff_inlink_acknowledge_status(input->link, &status, &pts)) { + pts = av_rescale_q_rnd(pts, input->link->time_base, outlink->time_base, + AV_ROUND_UP); + pl_queue_push(input->queue, NULL); /* Signal EOF to pl_queue */ + input->status = status; + input->status_pts = pts; + if (!s->status || pts >= s->status_pts) { + /* Also propagate to output unless overwritten by later status change */ + s->status = status; + s->status_pts = pts; + } + } + + return 0; +} + +static void drain_input_pts(LibplaceboInput *in, int64_t until) +{ + int64_t pts; + while (av_fifo_peek(in->out_pts, &pts, 1, 0) >= 0 && pts <= until) + av_fifo_drain2(in->out_pts, 1); +} + +static int libplacebo_activate(AVFilterContext *ctx) +{ + int ret, ok = 0, retry = 0; + LibplaceboContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + int64_t pts, out_pts; + + FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx); + pl_log_level_update(s->log, get_log_level()); + + for (int i = 0; i < s->nb_inputs; i++) { + if ((ret = handle_input(ctx, &s->inputs[i])) < 0) + return ret; + } + + if (ff_outlink_frame_wanted(outlink)) { + if (s->fps.num) { + out_pts = outlink->frame_count_out; + } else { + /* Determine the PTS of the next frame from any active input */ + out_pts = INT64_MAX; + for (int i = 0; i < s->nb_inputs; i++) { + LibplaceboInput *in = &s->inputs[i]; + if (av_fifo_peek(in->out_pts, &pts, 1, 0) >= 0) { + out_pts = FFMIN(out_pts, pts); + } else if (!in->status) { + ff_inlink_request_frame(in->link); + retry = true; + } + } + + if (retry) /* some inputs are incomplete */ + return 0; + } + + /* Update all input queues to the chosen out_pts */ + for (int i = 0; i < s->nb_inputs; i++) { + LibplaceboInput *in = &s->inputs[i]; + if (in->status && out_pts >= in->status_pts) { + in->qstatus = PL_QUEUE_EOF; + continue; + } + + in->qstatus = pl_queue_update(in->queue, &in->mix, pl_queue_params( + .pts = out_pts * av_q2d(outlink->time_base), + .radius = pl_frame_mix_radius(&s->params), + .vsync_duration = av_q2d(av_inv_q(outlink->frame_rate)), + )); + + switch (in->qstatus) { + case PL_QUEUE_MORE: + ff_inlink_request_frame(in->link); + retry = true; + break; + case PL_QUEUE_OK: + ok = true; + break; + case PL_QUEUE_ERR: + return AVERROR_EXTERNAL; + } + } + + if (retry) { + return 0; + } else if (ok) { + /* Got any valid frame mixes, drain PTS queue and render output */ + for (int i = 0; i < s->nb_inputs; i++) + drain_input_pts(&s->inputs[i], out_pts); + return output_frame(ctx, out_pts); + } else if (s->status) { + ff_outlink_set_status(outlink, s->status, s->status_pts); + return 0; + } + + return AVERROR_BUG; + } + + return FFERROR_NOT_READY; +} + static int libplacebo_query_format(AVFilterContext *ctx) { - int err = 0; + int err; LibplaceboContext *s = ctx->priv; + const AVVulkanDeviceContext *vkhwctx = NULL; const AVPixFmtDescriptor *desc = NULL; - AVFilterFormats *formats = NULL; + AVFilterFormats *infmts = NULL, *outfmts = NULL; + + if (ctx->hw_device_ctx) { + const AVHWDeviceContext *avhwctx = (void *) ctx->hw_device_ctx->data; + if (avhwctx->type == AV_HWDEVICE_TYPE_VULKAN) + vkhwctx = avhwctx->hwctx; + } - RET(init_vulkan(ctx)); + RET(init_vulkan(ctx, vkhwctx)); while ((desc = av_pix_fmt_desc_next(desc))) { enum AVPixelFormat pixfmt = av_pix_fmt_desc_get_id(desc); @@ -565,29 +1118,58 @@ static int libplacebo_query_format(AVFilterContext *ctx) continue; #endif - if (pl_test_pixfmt(s->gpu, pixfmt)) { - if ((err = ff_add_format(&formats, pixfmt)) < 0) - return err; + if (pixfmt == AV_PIX_FMT_VULKAN) { + if (!vkhwctx || vkhwctx->act_dev != s->vulkan->device) + continue; } - } - RET(ff_formats_ref(formats, &ctx->inputs[0]->outcfg.formats)); + if (!pl_test_pixfmt(s->gpu, pixfmt)) + continue; - if (s->out_format != AV_PIX_FMT_NONE) { - /* Support only requested format, and hwaccel (vulkan) */ - const enum AVPixelFormat out_fmts[] = { - s->out_format, AV_PIX_FMT_VULKAN, AV_PIX_FMT_NONE, - }; - RET(ff_formats_ref(ff_make_format_list(out_fmts), - &ctx->outputs[0]->incfg.formats)); - } else { - /* Support all formats */ - RET(ff_formats_ref(formats, &ctx->outputs[0]->incfg.formats)); + RET(ff_add_format(&infmts, pixfmt)); + + /* Filter for supported output pixel formats */ + if (desc->flags & AV_PIX_FMT_FLAG_BE) + continue; /* BE formats are not supported by pl_download_avframe */ + + /* Mask based on user specified format */ + if (s->out_format != AV_PIX_FMT_NONE) { + if (pixfmt == AV_PIX_FMT_VULKAN && av_vkfmt_from_pixfmt(s->out_format)) { + /* OK */ + } else if (pixfmt == s->out_format) { + /* OK */ + } else { + continue; /* Not OK */ + } + } + +#if PL_API_VER >= 293 + if (!pl_test_pixfmt_caps(s->gpu, pixfmt, PL_FMT_CAP_RENDERABLE)) + continue; +#endif + + RET(ff_add_format(&outfmts, pixfmt)); } + if (!infmts || !outfmts) { + if (s->out_format) { + av_log(s, AV_LOG_ERROR, "Invalid output format '%s'!\n", + av_get_pix_fmt_name(s->out_format)); + } + err = AVERROR(EINVAL); + goto fail; + } + + for (int i = 0; i < s->nb_inputs; i++) + RET(ff_formats_ref(infmts, &ctx->inputs[i]->outcfg.formats)); + RET(ff_formats_ref(outfmts, &ctx->outputs[0]->incfg.formats)); return 0; fail: + if (infmts && !infmts->refcount) + ff_formats_unref(&infmts); + if (outfmts && !outfmts->refcount) + ff_formats_unref(&outfmts); return err; } @@ -605,16 +1187,23 @@ static int libplacebo_config_input(AVFilterLink *inlink) return 0; } +static inline AVRational max_q(AVRational a, AVRational b) +{ + return av_cmp_q(a, b) < 0 ? b : a; +} + static int libplacebo_config_output(AVFilterLink *outlink) { int err; AVFilterContext *avctx = outlink->src; LibplaceboContext *s = avctx->priv; AVFilterLink *inlink = outlink->src->inputs[0]; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + const AVPixFmtDescriptor *out_desc = av_pix_fmt_desc_get(outlink->format); AVHWFramesContext *hwfc; AVVulkanFramesContext *vkfc; - AVRational scale_sar; + /* Frame dimensions */ RET(ff_scale_eval_dimensions(s, s->w_expr, s->h_expr, inlink, outlink, &outlink->w, &outlink->h)); @@ -622,22 +1211,43 @@ static int libplacebo_config_output(AVFilterLink *outlink) s->force_original_aspect_ratio, s->force_divisible_by); - scale_sar = (AVRational){outlink->h * inlink->w, outlink->w * inlink->h}; - if (inlink->sample_aspect_ratio.num) - scale_sar = av_mul_q(scale_sar, inlink->sample_aspect_ratio); - - if (s->normalize_sar) { - /* Apply all SAR during scaling, so we don't need to set the out SAR */ + if (s->normalize_sar || s->nb_inputs > 1) { + /* SAR is normalized, or we have multiple inputs, set out to 1:1 */ outlink->sample_aspect_ratio = (AVRational){ 1, 1 }; - s->target_sar = scale_sar; } else { /* This is consistent with other scale_* filters, which only * set the outlink SAR to be equal to the scale SAR iff the input SAR * was set to something nonzero */ if (inlink->sample_aspect_ratio.num) - outlink->sample_aspect_ratio = scale_sar; + outlink->sample_aspect_ratio = inlink->sample_aspect_ratio; + } + + /* Frame rate */ + if (s->fps.num) { + outlink->frame_rate = s->fps; + outlink->time_base = av_inv_q(s->fps); + } else { + outlink->frame_rate = avctx->inputs[0]->frame_rate; + outlink->time_base = avctx->inputs[0]->time_base; + for (int i = 1; i < s->nb_inputs; i++) { + outlink->frame_rate = max_q(outlink->frame_rate, + avctx->inputs[i]->frame_rate); + outlink->time_base = av_gcd_q(outlink->time_base, + avctx->inputs[i]->time_base, + AV_TIME_BASE / 2, AV_TIME_BASE_Q); + } } + /* Static variables */ + s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = outlink->w; + s->var_values[VAR_OUT_H] = s->var_values[VAR_OH] = outlink->h; + s->var_values[VAR_DAR] = outlink->sample_aspect_ratio.num ? + av_q2d(outlink->sample_aspect_ratio) : 1.0; + s->var_values[VAR_HSUB] = 1 << desc->log2_chroma_w; + s->var_values[VAR_VSUB] = 1 << desc->log2_chroma_h; + s->var_values[VAR_OHSUB] = 1 << out_desc->log2_chroma_w; + s->var_values[VAR_OVSUB] = 1 << out_desc->log2_chroma_h; + if (outlink->format != AV_PIX_FMT_VULKAN) return 0; @@ -665,16 +1275,28 @@ static int libplacebo_config_output(AVFilterLink *outlink) #define DYNAMIC (STATIC | AV_OPT_FLAG_RUNTIME_PARAM) static const AVOption libplacebo_options[] = { - { "w", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, .flags = STATIC }, - { "h", "Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, .flags = STATIC }, + { "inputs", "Number of inputs", OFFSET(nb_inputs), AV_OPT_TYPE_INT, {.i64 = 1}, 1, INT_MAX, .flags = STATIC }, + { "w", "Output video frame width", OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, .flags = STATIC }, + { "h", "Output video frame height", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, .flags = STATIC }, + { "fps", "Output video frame rate", OFFSET(fps_string), AV_OPT_TYPE_STRING, {.str = "none"}, .flags = STATIC }, + { "crop_x", "Input video crop x", OFFSET(crop_x_expr), AV_OPT_TYPE_STRING, {.str = "(iw-cw)/2"}, .flags = DYNAMIC }, + { "crop_y", "Input video crop y", OFFSET(crop_y_expr), AV_OPT_TYPE_STRING, {.str = "(ih-ch)/2"}, .flags = DYNAMIC }, + { "crop_w", "Input video crop w", OFFSET(crop_w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, .flags = DYNAMIC }, + { "crop_h", "Input video crop h", OFFSET(crop_h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, .flags = DYNAMIC }, + { "pos_x", "Output video placement x", OFFSET(pos_x_expr), AV_OPT_TYPE_STRING, {.str = "(ow-pw)/2"}, .flags = DYNAMIC }, + { "pos_y", "Output video placement y", OFFSET(pos_y_expr), AV_OPT_TYPE_STRING, {.str = "(oh-ph)/2"}, .flags = DYNAMIC }, + { "pos_w", "Output video placement w", OFFSET(pos_w_expr), AV_OPT_TYPE_STRING, {.str = "ow"}, .flags = DYNAMIC }, + { "pos_h", "Output video placement h", OFFSET(pos_h_expr), AV_OPT_TYPE_STRING, {.str = "oh"}, .flags = DYNAMIC }, { "format", "Output video format", OFFSET(out_format_string), AV_OPT_TYPE_STRING, .flags = STATIC }, { "force_original_aspect_ratio", "decrease or increase w/h if necessary to keep the original AR", OFFSET(force_original_aspect_ratio), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, STATIC, "force_oar" }, { "disable", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, STATIC, "force_oar" }, { "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, STATIC, "force_oar" }, { "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, STATIC, "force_oar" }, { "force_divisible_by", "enforce that the output resolution is divisible by a defined integer when force_original_aspect_ratio is used", OFFSET(force_divisible_by), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, 256, STATIC }, - { "normalize_sar", "force SAR normalization to 1:1", OFFSET(normalize_sar), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, STATIC }, + { "normalize_sar", "force SAR normalization to 1:1 by adjusting pos_x/y/w/h", OFFSET(normalize_sar), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, STATIC }, { "pad_crop_ratio", "ratio between padding and cropping when normalizing SAR (0=pad, 1=crop)", OFFSET(pad_crop_ratio), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, 1.0, DYNAMIC }, + { "fillcolor", "Background fill color", OFFSET(fillcolor), AV_OPT_TYPE_STRING, {.str = "black"}, .flags = DYNAMIC }, + { "corner_rounding", "Corner rounding radius", OFFSET(corner_rounding), AV_OPT_TYPE_FLOAT, {.dbl = 0.0}, 0.0, 1.0, .flags = DYNAMIC }, {"colorspace", "select colorspace", OFFSET(colorspace), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_SPC_NB-1, DYNAMIC, "colorspace"}, {"auto", "keep the same colorspace", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, STATIC, "colorspace"}, @@ -735,6 +1357,7 @@ static const AVOption libplacebo_options[] = { { "upscaler", "Upscaler function", OFFSET(upscaler), AV_OPT_TYPE_STRING, {.str = "spline36"}, .flags = DYNAMIC }, { "downscaler", "Downscaler function", OFFSET(downscaler), AV_OPT_TYPE_STRING, {.str = "mitchell"}, .flags = DYNAMIC }, + { "frame_mixer", "Frame mixing function", OFFSET(frame_mixer), AV_OPT_TYPE_STRING, {.str = "none"}, .flags = DYNAMIC }, { "lut_entries", "Number of scaler LUT entries", OFFSET(lut_entries), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 256, DYNAMIC }, { "antiringing", "Antiringing strength (for non-EWA filters)", OFFSET(antiringing), AV_OPT_TYPE_FLOAT, {.dbl = 0.0}, 0.0, 1.0, DYNAMIC }, { "sigmoid", "Enable sigmoid upscaling", OFFSET(sigmoid), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, DYNAMIC }, @@ -758,21 +1381,25 @@ static const AVOption libplacebo_options[] = { { "minimum_peak", "Peak detection minimum peak", OFFSET(min_peak), AV_OPT_TYPE_FLOAT, {.dbl = 1.0}, 0.0, 100.0, DYNAMIC }, { "scene_threshold_low", "Scene change low threshold", OFFSET(scene_low), AV_OPT_TYPE_FLOAT, {.dbl = 5.5}, -1.0, 100.0, DYNAMIC }, { "scene_threshold_high", "Scene change high threshold", OFFSET(scene_high), AV_OPT_TYPE_FLOAT, {.dbl = 10.0}, -1.0, 100.0, DYNAMIC }, - { "overshoot", "Tone-mapping overshoot margin", OFFSET(overshoot), AV_OPT_TYPE_FLOAT, {.dbl = 0.05}, 0.0, 1.0, DYNAMIC }, - - { "intent", "Rendering intent", OFFSET(intent), AV_OPT_TYPE_INT, {.i64 = PL_INTENT_RELATIVE_COLORIMETRIC}, 0, 3, DYNAMIC, "intent" }, - { "perceptual", "Perceptual", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_PERCEPTUAL}, 0, 0, STATIC, "intent" }, - { "relative", "Relative colorimetric", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_RELATIVE_COLORIMETRIC}, 0, 0, STATIC, "intent" }, - { "absolute", "Absolute colorimetric", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_ABSOLUTE_COLORIMETRIC}, 0, 0, STATIC, "intent" }, - { "saturation", "Saturation mapping", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_SATURATION}, 0, 0, STATIC, "intent" }, - { "gamut_mode", "Gamut-mapping mode", OFFSET(gamut_mode), AV_OPT_TYPE_INT, {.i64 = PL_GAMUT_CLIP}, 0, PL_GAMUT_MODE_COUNT - 1, DYNAMIC, "gamut_mode" }, - { "clip", "Hard-clip gamut boundary", 0, AV_OPT_TYPE_CONST, {.i64 = PL_GAMUT_CLIP}, 0, 0, STATIC, "gamut_mode" }, - { "warn", "Highlight out-of-gamut colors", 0, AV_OPT_TYPE_CONST, {.i64 = PL_GAMUT_WARN}, 0, 0, STATIC, "gamut_mode" }, - { "darken", "Darken image to fit gamut", 0, AV_OPT_TYPE_CONST, {.i64 = PL_GAMUT_DARKEN}, 0, 0, STATIC, "gamut_mode" }, - { "desaturate", "Colorimetrically desaturate colors", 0, AV_OPT_TYPE_CONST, {.i64 = PL_GAMUT_DESATURATE}, 0, 0, STATIC, "gamut_mode" }, + { "percentile", "Peak detection percentile", OFFSET(percentile), AV_OPT_TYPE_FLOAT, {.dbl = 99.995}, 0.0, 100.0, DYNAMIC }, + + { "gamut_mode", "Gamut-mapping mode", OFFSET(gamut_mode), AV_OPT_TYPE_INT, {.i64 = GAMUT_MAP_PERCEPTUAL}, 0, GAMUT_MAP_COUNT - 1, DYNAMIC, "gamut_mode" }, + { "clip", "Hard-clip (RGB per-channel)", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_CLIP}, 0, 0, STATIC, "gamut_mode" }, + { "perceptual", "Colorimetric soft clipping", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_PERCEPTUAL}, 0, 0, STATIC, "gamut_mode" }, + { "relative", "Relative colorimetric clipping", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_RELATIVE}, 0, 0, STATIC, "gamut_mode" }, + { "saturation", "Saturation mapping (RGB -> RGB)", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_SATURATION}, 0, 0, STATIC, "gamut_mode" }, + { "absolute", "Absolute colorimetric clipping", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_ABSOLUTE}, 0, 0, STATIC, "gamut_mode" }, + { "desaturate", "Colorimetrically desaturate colors towards white", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_DESATURATE}, 0, 0, STATIC, "gamut_mode" }, + { "darken", "Colorimetric clip with bias towards darkening image to fit gamut", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_DARKEN}, 0, 0, STATIC, "gamut_mode" }, + { "warn", "Highlight out-of-gamut colors", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_HIGHLIGHT}, 0, 0, STATIC, "gamut_mode" }, + { "linear", "Linearly reduce chromaticity to fit gamut", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_LINEAR}, 0, 0, STATIC, "gamut_mode" }, { "tonemapping", "Tone-mapping algorithm", OFFSET(tonemapping), AV_OPT_TYPE_INT, {.i64 = TONE_MAP_AUTO}, 0, TONE_MAP_COUNT - 1, DYNAMIC, "tonemap" }, { "auto", "Automatic selection", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_AUTO}, 0, 0, STATIC, "tonemap" }, { "clip", "No tone mapping (clip", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_CLIP}, 0, 0, STATIC, "tonemap" }, +#if PL_API_VER >= 246 + { "st2094-40", "SMPTE ST 2094-40", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_ST2094_40}, 0, 0, STATIC, "tonemap" }, + { "st2094-10", "SMPTE ST 2094-10", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_ST2094_10}, 0, 0, STATIC, "tonemap" }, +#endif { "bt.2390", "ITU-R BT.2390 EETF", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_BT2390}, 0, 0, STATIC, "tonemap" }, { "bt.2446a", "ITU-R BT.2446 Method A", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_BT2446A}, 0, 0, STATIC, "tonemap" }, { "spline", "Single-pivot polynomial spline", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_SPLINE}, 0, 0, STATIC, "tonemap" }, @@ -782,20 +1409,32 @@ static const AVOption libplacebo_options[] = { { "gamma", "Gamma function with knee", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_GAMMA}, 0, 0, STATIC, "tonemap" }, { "linear", "Perceptually linear stretch", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_LINEAR}, 0, 0, STATIC, "tonemap" }, { "tonemapping_param", "Tunable parameter for some tone-mapping functions", OFFSET(tonemapping_param), AV_OPT_TYPE_FLOAT, {.dbl = 0.0}, 0.0, 100.0, .flags = DYNAMIC }, - { "tonemapping_mode", "Tone-mapping mode", OFFSET(tonemapping_mode), AV_OPT_TYPE_INT, {.i64 = PL_TONE_MAP_AUTO}, 0, PL_TONE_MAP_MODE_COUNT - 1, DYNAMIC, "tonemap_mode" }, - { "auto", "Automatic selection", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAP_AUTO}, 0, 0, STATIC, "tonemap_mode" }, - { "rgb", "Per-channel (RGB)", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAP_RGB}, 0, 0, STATIC, "tonemap_mode" }, - { "max", "Maximum component", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAP_MAX}, 0, 0, STATIC, "tonemap_mode" }, - { "hybrid", "Hybrid of Luma/RGB", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAP_HYBRID}, 0, 0, STATIC, "tonemap_mode" }, - { "luma", "Luminance", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAP_LUMA}, 0, 0, STATIC, "tonemap_mode" }, { "inverse_tonemapping", "Inverse tone mapping (range expansion)", OFFSET(inverse_tonemapping), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC }, - { "tonemapping_crosstalk", "Crosstalk factor for tone-mapping", OFFSET(crosstalk), AV_OPT_TYPE_FLOAT, {.dbl = 0.04}, 0.0, 0.30, DYNAMIC }, { "tonemapping_lut_size", "Tone-mapping LUT size", OFFSET(tonemapping_lut_size), AV_OPT_TYPE_INT, {.i64 = 256}, 2, 1024, DYNAMIC }, + { "contrast_recovery", "HDR contrast recovery strength", OFFSET(contrast_recovery), AV_OPT_TYPE_FLOAT, {.dbl = 0.30}, 0.0, 3.0, DYNAMIC }, + { "contrast_smoothness", "HDR contrast recovery smoothness", OFFSET(contrast_smoothness), AV_OPT_TYPE_FLOAT, {.dbl = 3.50}, 1.0, 32.0, DYNAMIC }, + +#if FF_API_LIBPLACEBO_OPTS /* deprecated options for backwards compatibility, defaulting to -1 to not override the new defaults */ { "desaturation_strength", "Desaturation strength", OFFSET(desat_str), AV_OPT_TYPE_FLOAT, {.dbl = -1.0}, -1.0, 1.0, DYNAMIC | AV_OPT_FLAG_DEPRECATED }, { "desaturation_exponent", "Desaturation exponent", OFFSET(desat_exp), AV_OPT_TYPE_FLOAT, {.dbl = -1.0}, -1.0, 10.0, DYNAMIC | AV_OPT_FLAG_DEPRECATED }, { "gamut_warning", "Highlight out-of-gamut colors", OFFSET(gamut_warning), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC | AV_OPT_FLAG_DEPRECATED }, - { "gamut_clipping", "Enable colorimetric gamut clipping", OFFSET(gamut_clipping), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC | AV_OPT_FLAG_DEPRECATED }, + { "gamut_clipping", "Enable desaturating colorimetric gamut clipping", OFFSET(gamut_clipping), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC | AV_OPT_FLAG_DEPRECATED }, + { "intent", "Rendering intent", OFFSET(intent), AV_OPT_TYPE_INT, {.i64 = PL_INTENT_PERCEPTUAL}, 0, 3, DYNAMIC | AV_OPT_FLAG_DEPRECATED, "intent" }, + { "perceptual", "Perceptual", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_PERCEPTUAL}, 0, 0, STATIC, "intent" }, + { "relative", "Relative colorimetric", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_RELATIVE_COLORIMETRIC}, 0, 0, STATIC, "intent" }, + { "absolute", "Absolute colorimetric", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_ABSOLUTE_COLORIMETRIC}, 0, 0, STATIC, "intent" }, + { "saturation", "Saturation mapping", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_SATURATION}, 0, 0, STATIC, "intent" }, + { "tonemapping_mode", "Tone-mapping mode", OFFSET(tonemapping_mode), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 4, DYNAMIC | AV_OPT_FLAG_DEPRECATED, "tonemap_mode" }, + { "auto", "Automatic selection", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, 0, 0, STATIC, "tonemap_mode" }, + { "rgb", "Per-channel (RGB)", 0, AV_OPT_TYPE_CONST, {.i64 = 1}, 0, 0, STATIC, "tonemap_mode" }, + { "max", "Maximum component", 0, AV_OPT_TYPE_CONST, {.i64 = 2}, 0, 0, STATIC, "tonemap_mode" }, + { "hybrid", "Hybrid of Luma/RGB", 0, AV_OPT_TYPE_CONST, {.i64 = 3}, 0, 0, STATIC, "tonemap_mode" }, + { "luma", "Luminance", 0, AV_OPT_TYPE_CONST, {.i64 = 4}, 0, 0, STATIC, "tonemap_mode" }, + { "tonemapping_crosstalk", "Crosstalk factor for tone-mapping", OFFSET(crosstalk), AV_OPT_TYPE_FLOAT, {.dbl = 0.04}, 0.0, 0.30, DYNAMIC | AV_OPT_FLAG_DEPRECATED }, + { "overshoot", "Tone-mapping overshoot margin", OFFSET(overshoot), AV_OPT_TYPE_FLOAT, {.dbl = 0.05}, 0.0, 1.0, DYNAMIC | AV_OPT_FLAG_DEPRECATED }, + { "hybrid_mix", "Tone-mapping hybrid LMS mixing coefficient", OFFSET(hybrid_mix), AV_OPT_TYPE_FLOAT, {.dbl = 0.20}, 0.0, 1.00, DYNAMIC }, +#endif { "dithering", "Dither method to use", OFFSET(dithering), AV_OPT_TYPE_INT, {.i64 = PL_DITHER_BLUE_NOISE}, -1, PL_DITHER_METHOD_COUNT - 1, DYNAMIC, "dither" }, { "none", "Disable dithering", 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, STATIC, "dither" }, @@ -816,11 +1455,13 @@ static const AVOption libplacebo_options[] = { { "custom_shader_bin", "Custom user shader as binary (mpv .hook format)", OFFSET(shader_bin), AV_OPT_TYPE_BINARY, .flags = STATIC }, /* Performance/quality tradeoff options */ - { "skip_aa", "Skip anti-aliasing", OFFSET(skip_aa), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 0, DYNAMIC }, + { "skip_aa", "Skip anti-aliasing", OFFSET(skip_aa), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC }, { "polar_cutoff", "Polar LUT cutoff", OFFSET(polar_cutoff), AV_OPT_TYPE_FLOAT, {.dbl = 0}, 0.0, 1.0, DYNAMIC }, { "disable_linear", "Disable linear scaling", OFFSET(disable_linear), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC }, { "disable_builtin", "Disable built-in scalers", OFFSET(disable_builtin), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC }, - { "force_icc_lut", "Force the use of a full ICC 3DLUT for color mapping", OFFSET(force_icc_lut), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC }, +#if FF_API_LIBPLACEBO_OPTS + { "force_icc_lut", "Deprecated, does nothing", OFFSET(force_icc_lut), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC | AV_OPT_FLAG_DEPRECATED }, +#endif { "force_dither", "Force dithering", OFFSET(force_dither), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC }, { "disable_fbos", "Force-disable FBOs", OFFSET(disable_fbos), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC }, { NULL }, @@ -828,15 +1469,6 @@ static const AVOption libplacebo_options[] = { AVFILTER_DEFINE_CLASS(libplacebo); -static const AVFilterPad libplacebo_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .filter_frame = &filter_frame, - .config_props = &libplacebo_config_input, - }, -}; - static const AVFilterPad libplacebo_outputs[] = { { .name = "default", @@ -851,10 +1483,11 @@ const AVFilter ff_vf_libplacebo = { .priv_size = sizeof(LibplaceboContext), .init = &libplacebo_init, .uninit = &libplacebo_uninit, - .process_command = &ff_filter_process_command, - FILTER_INPUTS(libplacebo_inputs), + .activate = &libplacebo_activate, + .process_command = &libplacebo_process_command, FILTER_OUTPUTS(libplacebo_outputs), FILTER_QUERY_FUNC(libplacebo_query_format), .priv_class = &libplacebo_class, .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE | AVFILTER_FLAG_DYNAMIC_INPUTS, }; diff --git a/libavfilter/vf_limitdiff.c b/libavfilter/vf_limitdiff.c index d2688c39f42..1e903d45a83 100644 --- a/libavfilter/vf_limitdiff.c +++ b/libavfilter/vf_limitdiff.c @@ -22,7 +22,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "framesync.h" diff --git a/libavfilter/vf_limiter.c b/libavfilter/vf_limiter.c index 67b734229fd..f67f590d600 100644 --- a/libavfilter/vf_limiter.c +++ b/libavfilter/vf_limiter.c @@ -22,7 +22,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "limiter.h" #include "video.h" @@ -231,13 +230,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_limiter = { .name = "limiter", .description = NULL_IF_CONFIG_SMALL("Limit pixels components to the specified range."), @@ -245,7 +237,7 @@ const AVFilter ff_vf_limiter = { .priv_class = &limiter_class, .init = init, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, diff --git a/libavfilter/vf_lumakey.c b/libavfilter/vf_lumakey.c index af1d85ec544..d426a5b67a8 100644 --- a/libavfilter/vf_lumakey.c +++ b/libavfilter/vf_lumakey.c @@ -19,9 +19,8 @@ */ #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -173,13 +172,6 @@ static const AVFilterPad lumakey_inputs[] = { }, }; -static const AVFilterPad lumakey_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(LumakeyContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -198,7 +190,7 @@ const AVFilter ff_vf_lumakey = { .priv_size = sizeof(LumakeyContext), .priv_class = &lumakey_class, FILTER_INPUTS(lumakey_inputs), - FILTER_OUTPUTS(lumakey_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, diff --git a/libavfilter/vf_lut.c b/libavfilter/vf_lut.c index 621291cdade..1a6ca065461 100644 --- a/libavfilter/vf_lut.c +++ b/libavfilter/vf_lut.c @@ -582,11 +582,6 @@ static const AVFilterPad inputs[] = { .config_props = config_props, }, }; -static const AVFilterPad outputs[] = { - { .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; #define DEFINE_LUT_FILTER(name_, description_, priv_class_) \ const AVFilter ff_vf_##name_ = { \ @@ -597,7 +592,7 @@ static const AVFilterPad outputs[] = { .init = name_##_init, \ .uninit = uninit, \ FILTER_INPUTS(inputs), \ - FILTER_OUTPUTS(outputs), \ + FILTER_OUTPUTS(ff_video_default_filterpad), \ FILTER_QUERY_FUNC(query_formats), \ .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | \ AVFILTER_FLAG_SLICE_THREADS, \ diff --git a/libavfilter/vf_lut3d.c b/libavfilter/vf_lut3d.c index 1ca448fcb3b..db38d419320 100644 --- a/libavfilter/vf_lut3d.c +++ b/libavfilter/vf_lut3d.c @@ -1301,13 +1301,6 @@ static const AVFilterPad lut3d_inputs[] = { }, }; -static const AVFilterPad lut3d_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_lut3d = { .name = "lut3d", .description = NULL_IF_CONFIG_SMALL("Adjust colors using a 3D LUT."), @@ -1315,7 +1308,7 @@ const AVFilter ff_vf_lut3d = { .init = lut3d_init, .uninit = lut3d_uninit, FILTER_INPUTS(lut3d_inputs), - FILTER_OUTPUTS(lut3d_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &lut3d_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, @@ -2232,20 +2225,13 @@ static const AVFilterPad lut1d_inputs[] = { }, }; -static const AVFilterPad lut1d_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_lut1d = { .name = "lut1d", .description = NULL_IF_CONFIG_SMALL("Adjust colors using a 1D LUT."), .priv_size = sizeof(LUT1DContext), .init = lut1d_init, FILTER_INPUTS(lut1d_inputs), - FILTER_OUTPUTS(lut1d_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &lut1d_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_maskedclamp.c b/libavfilter/vf_maskedclamp.c index 72f98703a5e..e6fbb1a6d58 100644 --- a/libavfilter/vf_maskedclamp.c +++ b/libavfilter/vf_maskedclamp.c @@ -22,7 +22,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "framesync.h" diff --git a/libavfilter/vf_maskedmerge.c b/libavfilter/vf_maskedmerge.c index 7adb8097607..4ca0c571c8e 100644 --- a/libavfilter/vf_maskedmerge.c +++ b/libavfilter/vf_maskedmerge.c @@ -22,7 +22,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "maskedmerge.h" diff --git a/libavfilter/vf_maskedminmax.c b/libavfilter/vf_maskedminmax.c index c7a05b42d91..b1c309cc7d9 100644 --- a/libavfilter/vf_maskedminmax.c +++ b/libavfilter/vf_maskedminmax.c @@ -22,7 +22,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "framesync.h" diff --git a/libavfilter/vf_maskedthreshold.c b/libavfilter/vf_maskedthreshold.c index 53731eefc98..36c4050b98c 100644 --- a/libavfilter/vf_maskedthreshold.c +++ b/libavfilter/vf_maskedthreshold.c @@ -22,7 +22,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "framesync.h" diff --git a/libavfilter/vf_maskfun.c b/libavfilter/vf_maskfun.c index 253e5e40f7a..1ac152fc8b8 100644 --- a/libavfilter/vf_maskfun.c +++ b/libavfilter/vf_maskfun.c @@ -22,7 +22,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -317,20 +316,13 @@ static const AVFilterPad maskfun_inputs[] = { }, }; -static const AVFilterPad maskfun_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_maskfun = { .name = "maskfun", .description = NULL_IF_CONFIG_SMALL("Create Mask."), .priv_size = sizeof(MaskFunContext), .uninit = uninit, FILTER_INPUTS(maskfun_inputs), - FILTER_OUTPUTS(maskfun_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &maskfun_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_mcdeint.c b/libavfilter/vf_mcdeint.c index e747521c0a8..66dfe755e4a 100644 --- a/libavfilter/vf_mcdeint.c +++ b/libavfilter/vf_mcdeint.c @@ -50,11 +50,10 @@ */ #include "libavutil/opt.h" -#include "libavutil/pixdesc.h" #include "libavcodec/avcodec.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" +#include "video.h" enum MCDeintMode { MODE_FAST = 0, @@ -75,6 +74,7 @@ typedef struct MCDeintContext { int parity; ///< MCDeintParity int qp; AVPacket *pkt; + AVFrame *frame_dec; AVCodecContext *enc_ctx; } MCDeintContext; @@ -116,6 +116,9 @@ static int config_props(AVFilterLink *inlink) mcdeint->pkt = av_packet_alloc(); if (!mcdeint->pkt) return AVERROR(ENOMEM); + mcdeint->frame_dec = av_frame_alloc(); + if (!mcdeint->frame_dec) + return AVERROR(ENOMEM); mcdeint->enc_ctx = avcodec_alloc_context3(enc); if (!mcdeint->enc_ctx) return AVERROR(ENOMEM); @@ -126,7 +129,7 @@ static int config_props(AVFilterLink *inlink) enc_ctx->gop_size = INT_MAX; enc_ctx->max_b_frames = 0; enc_ctx->pix_fmt = AV_PIX_FMT_YUV420P; - enc_ctx->flags = AV_CODEC_FLAG_QSCALE | AV_CODEC_FLAG_LOW_DELAY; + enc_ctx->flags = AV_CODEC_FLAG_QSCALE | AV_CODEC_FLAG_LOW_DELAY | AV_CODEC_FLAG_RECON_FRAME; enc_ctx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL; enc_ctx->global_quality = 1; enc_ctx->me_cmp = enc_ctx->me_sub_cmp = FF_CMP_SAD; @@ -160,15 +163,16 @@ static av_cold void uninit(AVFilterContext *ctx) av_packet_free(&mcdeint->pkt); avcodec_free_context(&mcdeint->enc_ctx); + av_frame_free(&mcdeint->frame_dec); } static int filter_frame(AVFilterLink *inlink, AVFrame *inpic) { MCDeintContext *mcdeint = inlink->dst->priv; AVFilterLink *outlink = inlink->dst->outputs[0]; - AVFrame *outpic, *frame_dec; + AVFrame *outpic, *frame_dec = mcdeint->frame_dec; AVPacket *pkt = mcdeint->pkt; - int x, y, i, ret, got_frame = 0; + int x, y, i, ret; outpic = ff_get_video_buffer(outlink, outlink->w, outlink->h); if (!outpic) { @@ -178,11 +182,22 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpic) av_frame_copy_props(outpic, inpic); inpic->quality = mcdeint->qp * FF_QP2LAMBDA; - ret = avcodec_encode_video2(mcdeint->enc_ctx, pkt, inpic, &got_frame); - if (ret < 0) + ret = avcodec_send_frame(mcdeint->enc_ctx, inpic); + if (ret < 0) { + av_log(mcdeint->enc_ctx, AV_LOG_ERROR, "Error sending a frame for encoding\n"); goto end; - - frame_dec = mcdeint->enc_ctx->coded_frame; + } + ret = avcodec_receive_packet(mcdeint->enc_ctx, pkt); + if (ret < 0) { + av_log(mcdeint->enc_ctx, AV_LOG_ERROR, "Error receiving a packet from encoding\n"); + goto end; + } + av_packet_unref(pkt); + ret = avcodec_receive_frame(mcdeint->enc_ctx, frame_dec); + if (ret < 0) { + av_log(mcdeint->enc_ctx, AV_LOG_ERROR, "Error receiving a frame from encoding\n"); + goto end; + } for (i = 0; i < 3; i++) { int is_chroma = !!i; @@ -284,20 +299,13 @@ static const AVFilterPad mcdeint_inputs[] = { }, }; -static const AVFilterPad mcdeint_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_mcdeint = { .name = "mcdeint", .description = NULL_IF_CONFIG_SMALL("Apply motion compensating deinterlacing."), .priv_size = sizeof(MCDeintContext), .uninit = uninit, FILTER_INPUTS(mcdeint_inputs), - FILTER_OUTPUTS(mcdeint_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_YUV420P), .priv_class = &mcdeint_class, }; diff --git a/libavfilter/vf_median.c b/libavfilter/vf_median.c index 11eccf608c6..57514f92890 100644 --- a/libavfilter/vf_median.c +++ b/libavfilter/vf_median.c @@ -24,7 +24,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "median.h" #include "video.h" @@ -270,13 +269,6 @@ static const AVFilterPad median_inputs[] = { }, }; -static const AVFilterPad median_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_median = { .name = "median", .description = NULL_IF_CONFIG_SMALL("Apply Median filter."), @@ -284,7 +276,7 @@ const AVFilter ff_vf_median = { .priv_class = &median_class, .uninit = uninit, FILTER_INPUTS(median_inputs), - FILTER_OUTPUTS(median_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, diff --git a/libavfilter/vf_mergeplanes.c b/libavfilter/vf_mergeplanes.c index 23b9f3d62f1..91bc0d2c553 100644 --- a/libavfilter/vf_mergeplanes.c +++ b/libavfilter/vf_mergeplanes.c @@ -24,8 +24,10 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" #include "framesync.h" +#include "video.h" typedef struct Mapping { int input; @@ -48,6 +50,7 @@ typedef struct MergePlanesContext { int planewidth[4]; int planeheight[4]; Mapping map[4]; + const AVPixFmtDescriptor *indesc[4]; const AVPixFmtDescriptor *outdesc; FFFrameSync fs; @@ -171,7 +174,8 @@ static int process_frame(FFFrameSync *fs) av_image_copy_plane(out->data[i], out->linesize[i], in[input]->data[plane], in[input]->linesize[plane], - s->planewidth[i], s->planeheight[i]); + s->planewidth[i] * ((s->indesc[input]->comp[plane].depth + 7) / 8), + s->planeheight[i]); } return ff_filter_frame(outlink, out); @@ -199,9 +203,9 @@ static int config_output(AVFilterLink *outlink) outlink->sample_aspect_ratio = ctx->inputs[0]->sample_aspect_ratio; s->planewidth[1] = - s->planewidth[2] = AV_CEIL_RSHIFT(((s->outdesc->comp[1].depth > 8) + 1) * outlink->w, s->outdesc->log2_chroma_w); + s->planewidth[2] = AV_CEIL_RSHIFT(outlink->w, s->outdesc->log2_chroma_w); s->planewidth[0] = - s->planewidth[3] = ((s->outdesc->comp[0].depth > 8) + 1) * outlink->w; + s->planewidth[3] = outlink->w; s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(outlink->h, s->outdesc->log2_chroma_h); s->planeheight[0] = @@ -210,8 +214,7 @@ static int config_output(AVFilterLink *outlink) for (i = 0; i < s->nb_inputs; i++) { InputParam *inputp = &inputsp[i]; AVFilterLink *inlink = ctx->inputs[i]; - const AVPixFmtDescriptor *indesc = av_pix_fmt_desc_get(inlink->format); - int j; + s->indesc[i] = av_pix_fmt_desc_get(inlink->format); if (outlink->sample_aspect_ratio.num != inlink->sample_aspect_ratio.num || outlink->sample_aspect_ratio.den != inlink->sample_aspect_ratio.den) { @@ -227,17 +230,17 @@ static int config_output(AVFilterLink *outlink) } inputp->planewidth[1] = - inputp->planewidth[2] = AV_CEIL_RSHIFT(((indesc->comp[1].depth > 8) + 1) * inlink->w, indesc->log2_chroma_w); + inputp->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, s->indesc[i]->log2_chroma_w); inputp->planewidth[0] = - inputp->planewidth[3] = ((indesc->comp[0].depth > 8) + 1) * inlink->w; + inputp->planewidth[3] = inlink->w; inputp->planeheight[1] = - inputp->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, indesc->log2_chroma_h); + inputp->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, s->indesc[i]->log2_chroma_h); inputp->planeheight[0] = inputp->planeheight[3] = inlink->h; inputp->nb_planes = av_pix_fmt_count_planes(inlink->format); - for (j = 0; j < inputp->nb_planes; j++) - inputp->depth[j] = indesc->comp[j].depth; + for (int j = 0; j < inputp->nb_planes; j++) + inputp->depth[j] = s->indesc[i]->comp[j].depth; in[i].time_base = inlink->time_base; in[i].sync = 1; diff --git a/libavfilter/vf_mestimate.c b/libavfilter/vf_mestimate.c index eff58f612e4..4ec34cd7fb3 100644 --- a/libavfilter/vf_mestimate.c +++ b/libavfilter/vf_mestimate.c @@ -21,12 +21,9 @@ #include "motion_estimation.h" #include "libavcodec/mathops.h" #include "libavutil/common.h" -#include "libavutil/imgutils.h" #include "libavutil/opt.h" -#include "libavutil/pixdesc.h" #include "libavutil/motion_vector.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -350,13 +347,6 @@ static const AVFilterPad mestimate_inputs[] = { }, }; -static const AVFilterPad mestimate_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_mestimate = { .name = "mestimate", .description = NULL_IF_CONFIG_SMALL("Generate motion vectors."), @@ -365,6 +355,6 @@ const AVFilter ff_vf_mestimate = { .uninit = uninit, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(mestimate_inputs), - FILTER_OUTPUTS(mestimate_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), }; diff --git a/libavfilter/vf_midequalizer.c b/libavfilter/vf_midequalizer.c index 8c0e02d888e..fae2b7ef193 100644 --- a/libavfilter/vf_midequalizer.c +++ b/libavfilter/vf_midequalizer.c @@ -22,7 +22,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "framesync.h" diff --git a/libavfilter/vf_minterpolate.c b/libavfilter/vf_minterpolate.c index f2de61ca39a..610ac6b1bb4 100644 --- a/libavfilter/vf_minterpolate.c +++ b/libavfilter/vf_minterpolate.c @@ -22,11 +22,9 @@ #include "motion_estimation.h" #include "libavcodec/mathops.h" #include "libavutil/common.h" -#include "libavutil/motion_vector.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "scene_sad.h" diff --git a/libavfilter/vf_misc_vaapi.c b/libavfilter/vf_misc_vaapi.c index db3e69679af..d68e18b52b2 100644 --- a/libavfilter/vf_misc_vaapi.c +++ b/libavfilter/vf_misc_vaapi.c @@ -21,9 +21,9 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "vaapi_vpp.h" +#include "video.h" // Denoise min/max/default Values #define DENOISE_MIN 0 @@ -131,6 +131,9 @@ static int misc_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame) av_get_pix_fmt_name(input_frame->format), input_frame->width, input_frame->height, input_frame->pts); + if (vpp_ctx->passthrough) + return ff_filter_frame(outlink, input_frame); + if (vpp_ctx->va_context == VA_INVALID_ID) return AVERROR(EINVAL); @@ -176,11 +179,14 @@ static int misc_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame) static av_cold int denoise_vaapi_init(AVFilterContext *avctx) { VAAPIVPPContext *vpp_ctx = avctx->priv; + DenoiseVAAPIContext *ctx = avctx->priv; ff_vaapi_vpp_ctx_init(avctx); vpp_ctx->pipeline_uninit = ff_vaapi_vpp_pipeline_uninit; vpp_ctx->build_filter_params = denoise_vaapi_build_filter_params; vpp_ctx->output_format = AV_PIX_FMT_NONE; + if (ctx->denoise == DENOISE_DEFAULT) + vpp_ctx->passthrough = 1; return 0; } @@ -188,11 +194,14 @@ static av_cold int denoise_vaapi_init(AVFilterContext *avctx) static av_cold int sharpness_vaapi_init(AVFilterContext *avctx) { VAAPIVPPContext *vpp_ctx = avctx->priv; + SharpnessVAAPIContext *ctx = avctx->priv; ff_vaapi_vpp_ctx_init(avctx); vpp_ctx->pipeline_uninit = ff_vaapi_vpp_pipeline_uninit; vpp_ctx->build_filter_params = sharpness_vaapi_build_filter_params; vpp_ctx->output_format = AV_PIX_FMT_NONE; + if (ctx->sharpness == SHARPNESS_DEFAULT) + vpp_ctx->passthrough = 1; return 0; } diff --git a/libavfilter/vf_mix.c b/libavfilter/vf_mix.c index 64d07bbbbb7..446df2c3263 100644 --- a/libavfilter/vf_mix.c +++ b/libavfilter/vf_mix.c @@ -43,8 +43,10 @@ typedef struct MixContext { float scale; float wfactor; + int fast; int tmix; int nb_frames; + int nb_unique_frames; int depth; int max; @@ -53,6 +55,8 @@ typedef struct MixContext { int linesizes[4]; int height[4]; + uint8_t *sum[4]; + uint8_t **data; int *linesize; @@ -81,6 +85,7 @@ static int parse_weights(AVFilterContext *ctx) char *p, *arg, *saveptr = NULL; int i, last = 0; + s->fast = 1; s->wfactor = 0.f; p = s->weights_str; for (i = 0; i < s->nb_inputs; i++) { @@ -93,6 +98,8 @@ static int parse_weights(AVFilterContext *ctx) return AVERROR(EINVAL); } s->wfactor += s->weights[i]; + if (i > 0) + s->fast &= s->weights[i] == s->weights[0]; last = i; } @@ -103,6 +110,8 @@ static int parse_weights(AVFilterContext *ctx) if (s->scale == 0) { s->wfactor = 1 / s->wfactor; } else { + if (s->scale != 1.f / s->wfactor) + s->fast = 0; s->wfactor = s->scale; } @@ -145,13 +154,52 @@ typedef struct ThreadData { AVFrame **in, *out; } ThreadData; +#define FAST_TMIX_SLICE(type, stype, round) \ + for (int p = 0; p < s->nb_planes; p++) { \ + const int slice_start = (s->height[p] * jobnr) / nb_jobs; \ + const int slice_end = (s->height[p] * (jobnr+1)) / nb_jobs; \ + const int width = s->linesizes[p] / sizeof(type); \ + stype *sum = (stype *)(s->sum[p] + slice_start * s->linesizes[p] * 2); \ + type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ + const ptrdiff_t sum_linesize = (s->linesizes[p] * 2) / sizeof(stype); \ + const ptrdiff_t dst_linesize = out->linesize[p] / sizeof(type); \ + const int idx = FFMAX(0, nb_inputs - nb_unique); \ + const ptrdiff_t src_linesize[2] = { in[idx]->linesize[p], \ + in[nb_inputs-1]->linesize[p] }; \ + const type *src[2]; \ + \ + if (!((1 << p) & s->planes)) { \ + av_image_copy_plane((uint8_t *)dst, out->linesize[p], \ + in[0]->data[p] + slice_start * in[0]->linesize[p], \ + in[0]->linesize[p], \ + s->linesizes[p], slice_end - slice_start); \ + continue; \ + } \ + \ + src[0] = (const type *)(in[idx]->data[p] + slice_start*src_linesize[0]); \ + src[1] = (const type *)(in[nb_inputs-1]->data[p] + slice_start * src_linesize[1]); \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + for (int x = 0; x < width; x++) { \ + sum[x] += src[1][x] * (1 + (nb_inputs - 1) * (idx == (nb_inputs - 1))); \ + dst[x] = (sum[x] + (round)) / nb_inputs; \ + sum[x] -= src[0][x]; \ + } \ + \ + dst += dst_linesize; \ + sum += sum_linesize; \ + src[0] += src_linesize[0] / sizeof(type); \ + src[1] += src_linesize[1] / sizeof(type); \ + } \ + } + #define MIX_SLICE(type, fun, clip) \ for (int p = 0; p < s->nb_planes; p++) { \ const int slice_start = (s->height[p] * jobnr) / nb_jobs; \ const int slice_end = (s->height[p] * (jobnr+1)) / nb_jobs; \ const int width = s->linesizes[p] / sizeof(type); \ type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ - ptrdiff_t dst_linesize = out->linesize[p] / sizeof(type); \ + const ptrdiff_t dst_linesize = out->linesize[p] / sizeof(type); \ \ if (!((1 << p) & s->planes)) { \ av_image_copy_plane((uint8_t *)dst, out->linesize[p], \ @@ -161,27 +209,27 @@ typedef struct ThreadData { continue; \ } \ \ - for (int i = 0; i < s->nb_inputs; i++) \ + for (int i = 0; i < nb_inputs; i++) \ linesize[i] = in[i]->linesize[p]; \ \ - for (int i = 0; i < s->nb_inputs; i++) \ + for (int i = 0; i < nb_inputs; i++) \ srcf[i] = in[i]->data[p] + slice_start * linesize[i]; \ \ for (int y = slice_start; y < slice_end; y++) { \ for (int x = 0; x < width; x++) { \ float val = 0.f; \ \ - for (int i = 0; i < s->nb_inputs; i++) { \ + for (int i = 0; i < nb_inputs; i++) { \ float src = *(type *)(srcf[i] + x * sizeof(type)); \ \ val += src * weights[i]; \ } \ \ - dst[x] = clip(fun(val * s->wfactor), 0, s->max); \ + dst[x] = clip(fun(val * wfactor), 0, max); \ } \ \ dst += dst_linesize; \ - for (int i = 0; i < s->nb_inputs; i++) \ + for (int i = 0; i < nb_inputs; i++) \ srcf[i] += linesize[i]; \ } \ } @@ -200,6 +248,22 @@ static int mix_frames(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) const float *weights = s->weights; uint8_t **srcf = s->data + jobnr * s->nb_inputs; int *linesize = s->linesize + jobnr * s->nb_inputs; + const int nb_unique = s->nb_unique_frames; + const int nb_inputs = s->nb_inputs; + const float wfactor = s->wfactor; + const int max = s->max; + + if (s->tmix && s->fast) { + if (s->depth <= 8) { + FAST_TMIX_SLICE(uint8_t, uint16_t, nb_inputs >> 1) + } else if (s->depth <= 16) { + FAST_TMIX_SLICE(uint16_t, uint32_t, nb_inputs >> 1) + } else { + FAST_TMIX_SLICE(float, float, 0.f) + } + + return 0; + } if (s->depth <= 8) { MIX_SLICE(uint8_t, lrintf, CLIP8) @@ -291,8 +355,14 @@ static int config_output(AVFilterLink *outlink) if (!s->linesize) return AVERROR(ENOMEM); - if (s->tmix) + if (s->tmix) { + for (int p = 0; p < s->nb_planes; p++) { + s->sum[p] = av_calloc(s->linesizes[p], s->height[p] * sizeof(*s->sum) * 2); + if (!s->sum[p]) + return AVERROR(ENOMEM); + } return 0; + } outlink->w = width; outlink->h = height; @@ -332,6 +402,8 @@ static av_cold void uninit(AVFilterContext *ctx) av_freep(&s->linesize); if (s->tmix) { + for (i = 0; i < 4; i++) + av_freep(&s->sum[i]); for (i = 0; i < s->nb_frames && s->frames; i++) av_frame_free(&s->frames[i]); } @@ -415,6 +487,7 @@ static int tmix_filter_frame(AVFilterLink *inlink, AVFrame *in) if (s->nb_frames < s->nb_inputs) { s->frames[s->nb_frames] = in; s->nb_frames++; + s->nb_unique_frames++; while (s->nb_frames < s->nb_inputs) { s->frames[s->nb_frames] = av_frame_clone(s->frames[s->nb_frames - 1]); if (!s->frames[s->nb_frames]) @@ -422,6 +495,7 @@ static int tmix_filter_frame(AVFilterLink *inlink, AVFrame *in) s->nb_frames++; } } else { + s->nb_unique_frames = FFMIN(s->nb_unique_frames + 1, s->nb_inputs); av_frame_free(&s->frames[0]); memmove(&s->frames[0], &s->frames[1], sizeof(*s->frames) * (s->nb_inputs - 1)); s->frames[s->nb_inputs - 1] = in; diff --git a/libavfilter/vf_monochrome.c b/libavfilter/vf_monochrome.c index c77c3b8f194..0e184e08b3d 100644 --- a/libavfilter/vf_monochrome.c +++ b/libavfilter/vf_monochrome.c @@ -21,9 +21,8 @@ #include #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -268,13 +267,6 @@ static const AVFilterPad monochrome_inputs[] = { }, }; -static const AVFilterPad monochrome_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(MonochromeContext, x) #define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -294,7 +286,7 @@ const AVFilter ff_vf_monochrome = { .priv_size = sizeof(MonochromeContext), .priv_class = &monochrome_class, FILTER_INPUTS(monochrome_inputs), - FILTER_OUTPUTS(monochrome_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_morpho.c b/libavfilter/vf_morpho.c index f91957ab810..bd1d6b230f3 100644 --- a/libavfilter/vf_morpho.c +++ b/libavfilter/vf_morpho.c @@ -29,7 +29,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "framesync.h" #include "internal.h" #include "video.h" @@ -95,6 +94,8 @@ typedef struct chord_set { unsigned nb_elements; } chord_set; +#define MAX_THREADS 64 + typedef struct MorphoContext { const AVClass *class; FFFrameSync fs; @@ -102,7 +103,7 @@ typedef struct MorphoContext { chord_set SE[4]; IPlane SEimg[4]; IPlane g[4], f[4], h[4]; - LUT Ty[2][4]; + LUT Ty[MAX_THREADS][2][4]; int mode; int planes; @@ -318,7 +319,7 @@ static void free_lut(LUT *table) } static int alloc_lut_if_necessary(LUT *Ty, IPlane *f, chord_set *SE, - int y, int num, enum MorphModes mode) + int num, enum MorphModes mode) { if (!Ty->arr || Ty->I != SE->Lnum || Ty->X != f->w || @@ -387,7 +388,7 @@ static void update_min_lut(IPlane *f, LUT *Ty, chord_set *SE, int y, int tid, in static int compute_min_lut(LUT *Ty, IPlane *f, chord_set *SE, int y, int num) { - int ret = alloc_lut_if_necessary(Ty, f, SE, y, num, ERODE); + int ret = alloc_lut_if_necessary(Ty, f, SE, num, ERODE); if (ret < 0) return ret; @@ -428,7 +429,7 @@ static void update_max_lut(IPlane *f, LUT *Ty, chord_set *SE, int y, int tid, in static int compute_max_lut(LUT *Ty, IPlane *f, chord_set *SE, int y, int num) { - int ret = alloc_lut_if_necessary(Ty, f, SE, y, num, DILATE); + int ret = alloc_lut_if_necessary(Ty, f, SE, num, DILATE); if (ret < 0) return ret; @@ -460,14 +461,14 @@ static void line_erode(IPlane *g, LUT *Ty, chord_set *SE, int y, int tid) } } -static int dilate(IPlane *g, IPlane *f, chord_set *SE, LUT *Ty) +static int dilate(IPlane *g, IPlane *f, chord_set *SE, LUT *Ty, int y0, int y1) { - int ret = compute_max_lut(Ty, f, SE, 0, 1); + int ret = compute_max_lut(Ty, f, SE, y0, 1); if (ret < 0) return ret; - line_dilate(g, Ty, SE, 0, 0); - for (int y = 1; y < f->h; y++) { + line_dilate(g, Ty, SE, y0, 0); + for (int y = y0 + 1; y < y1; y++) { update_max_lut(f, Ty, SE, y, 0, 1); line_dilate(g, Ty, SE, y, 0); } @@ -475,14 +476,14 @@ static int dilate(IPlane *g, IPlane *f, chord_set *SE, LUT *Ty) return 0; } -static int erode(IPlane *g, IPlane *f, chord_set *SE, LUT *Ty) +static int erode(IPlane *g, IPlane *f, chord_set *SE, LUT *Ty, int y0, int y1) { - int ret = compute_min_lut(Ty, f, SE, 0, 1); + int ret = compute_min_lut(Ty, f, SE, y0, 1); if (ret < 0) return ret; - line_erode(g, Ty, SE, 0, 0); - for (int y = 1; y < f->h; y++) { + line_erode(g, Ty, SE, y0, 0); + for (int y = y0 + 1; y < y1; y++) { update_min_lut(f, Ty, SE, y, 0, 1); line_erode(g, Ty, SE, y, 0); } @@ -490,15 +491,15 @@ static int erode(IPlane *g, IPlane *f, chord_set *SE, LUT *Ty) return 0; } -static void difference(IPlane *g, IPlane *f) +static void difference(IPlane *g, IPlane *f, int y0, int y1) { - for (int y = 0; y < f->h; y++) + for (int y = y0; y < y1; y++) f->diff_in_place(g->img[y], f->img[y], f->w); } -static void difference2(IPlane *g, IPlane *f) +static void difference2(IPlane *g, IPlane *f, int y0, int y1) { - for (int y = 0; y < f->h; y++) + for (int y = y0; y < y1; y++) f->diff_rin_place(g->img[y], f->img[y], f->w); } @@ -785,12 +786,133 @@ static int activate(AVFilterContext *ctx) return ff_framesync_activate(&s->fs); } +typedef struct ThreadData { + AVFrame *in, *out; +} ThreadData; + +static int morpho_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + MorphoContext *s = ctx->priv; + ThreadData *td = arg; + AVFrame *out = td->out; + AVFrame *in = td->in; + int ret; + + for (int p = 0; p < s->nb_planes; p++) { + const int width = s->planewidth[p]; + const int height = s->planeheight[p]; + const int y0 = (height * jobnr ) / nb_jobs; + const int y1 = (height * (jobnr+1)) / nb_jobs; + const int depth = s->depth; + + if (ctx->is_disabled || !(s->planes & (1 << p))) { +copy: + av_image_copy_plane(out->data[p] + y0 * out->linesize[p], + out->linesize[p], + in->data[p] + y0 * in->linesize[p], + in->linesize[p], + width * ((depth + 7) / 8), + y1 - y0); + continue; + } + + if (s->SE[p].minX == INT16_MAX || + s->SE[p].minY == INT16_MAX || + s->SE[p].maxX == INT16_MIN || + s->SE[p].maxY == INT16_MIN) + goto copy; + + switch (s->mode) { + case ERODE: + ret = erode(&s->g[p], &s->f[p], &s->SE[p], &s->Ty[jobnr][0][p], y0, y1); + break; + case DILATE: + case GRADIENT: + ret = dilate(&s->g[p], &s->f[p], &s->SE[p], &s->Ty[jobnr][0][p], y0, y1); + break; + case OPEN: + case TOPHAT: + ret = erode(&s->h[p], &s->f[p], &s->SE[p], &s->Ty[jobnr][0][p], y0, y1); + break; + case CLOSE: + case BLACKHAT: + ret = dilate(&s->h[p], &s->f[p], &s->SE[p], &s->Ty[jobnr][0][p], y0, y1); + break; + default: + av_assert0(0); + } + + if (ret < 0) + return ret; + } + + return 0; +} + +static int morpho_sliceX(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + MorphoContext *s = ctx->priv; + int ret; + + for (int p = 0; p < s->nb_planes; p++) { + const int height = s->planeheight[p]; + const int y0 = (height * jobnr ) / nb_jobs; + const int y1 = (height * (jobnr+1)) / nb_jobs; + + if (ctx->is_disabled || !(s->planes & (1 << p))) { +copy: + continue; + } + + if (s->SE[p].minX == INT16_MAX || + s->SE[p].minY == INT16_MAX || + s->SE[p].maxX == INT16_MIN || + s->SE[p].maxY == INT16_MIN) + goto copy; + + switch (s->mode) { + case OPEN: + ret = dilate(&s->g[p], &s->h[p], &s->SE[p], &s->Ty[jobnr][1][p], y0, y1); + break; + case CLOSE: + ret = erode(&s->g[p], &s->h[p], &s->SE[p], &s->Ty[jobnr][1][p], y0, y1); + break; + case GRADIENT: + ret = erode(&s->h[p], &s->f[p], &s->SE[p], &s->Ty[jobnr][1][p], y0, y1); + if (ret < 0) + break; + difference(&s->g[p], &s->h[p], y0, y1); + break; + case TOPHAT: + ret = dilate(&s->g[p], &s->h[p], &s->SE[p], &s->Ty[jobnr][1][p], y0, y1); + if (ret < 0) + break; + difference2(&s->g[p], &s->f[p], y0, y1); + break; + case BLACKHAT: + ret = erode(&s->g[p], &s->h[p], &s->SE[p], &s->Ty[jobnr][1][p], y0, y1); + if (ret < 0) + break; + difference(&s->g[p], &s->f[p], y0, y1); + break; + default: + av_assert0(0); + } + + if (ret < 0) + return ret; + } + + return 0; +} + static int do_morpho(FFFrameSync *fs) { AVFilterContext *ctx = fs->parent; AVFilterLink *outlink = ctx->outputs[0]; MorphoContext *s = ctx->priv; AVFrame *in = NULL, *structurepic = NULL; + ThreadData td; AVFrame *out; int ret; @@ -808,30 +930,19 @@ static int do_morpho(FFFrameSync *fs) av_frame_copy_props(out, in); for (int p = 0; p < s->nb_planes; p++) { - const uint8_t *src = in->data[p]; - int src_linesize = in->linesize[p]; const uint8_t *ssrc = structurepic->data[p]; const int ssrc_linesize = structurepic->linesize[p]; - uint8_t *dst = out->data[p]; - int dst_linesize = out->linesize[p]; const int swidth = s->splanewidth[p]; const int sheight = s->splaneheight[p]; + const uint8_t *src = in->data[p]; + int src_linesize = in->linesize[p]; + uint8_t *dst = out->data[p]; + int dst_linesize = out->linesize[p]; const int width = s->planewidth[p]; const int height = s->planeheight[p]; const int depth = s->depth; int type_size = s->type_size; - if (ctx->is_disabled || !(s->planes & (1 << p))) { -copy: - av_image_copy_plane(out->data[p] + 0 * out->linesize[p], - out->linesize[p], - in->data[p] + 0 * in->linesize[p], - in->linesize[p], - width * ((s->depth + 7) / 8), - height); - continue; - } - if (!s->got_structure[p] || s->structures) { free_chord_set(&s->SE[p]); @@ -844,12 +955,6 @@ static int do_morpho(FFFrameSync *fs) s->got_structure[p] = 1; } - if (s->SE[p].minX == INT16_MAX || - s->SE[p].minY == INT16_MAX || - s->SE[p].maxX == INT16_MIN || - s->SE[p].maxY == INT16_MIN) - goto copy; - ret = read_iplane(&s->f[p], src, src_linesize, width, height, 1, type_size, depth); if (ret < 0) goto fail; @@ -859,74 +964,29 @@ static int do_morpho(FFFrameSync *fs) goto fail; switch (s->mode) { - case ERODE: - ret = erode(&s->g[p], &s->f[p], &s->SE[p], &s->Ty[0][p]); - break; - case DILATE: - ret = dilate(&s->g[p], &s->f[p], &s->SE[p], &s->Ty[0][p]); - break; case OPEN: - ret = read_iplane(&s->h[p], s->temp->data[p], s->temp->linesize[p], width, height, 1, type_size, depth); - if (ret < 0) - break; - ret = erode(&s->h[p], &s->f[p], &s->SE[p], &s->Ty[0][p]); - if (ret < 0) - break; - ret = dilate(&s->g[p], &s->h[p], &s->SE[p], &s->Ty[1][p]); - break; case CLOSE: - ret = read_iplane(&s->h[p], s->temp->data[p], s->temp->linesize[p], width, height, 1, type_size, depth); - if (ret < 0) - break; - ret = dilate(&s->h[p], &s->f[p], &s->SE[p], &s->Ty[0][p]); - if (ret < 0) - break; - ret = erode(&s->g[p], &s->h[p], &s->SE[p], &s->Ty[1][p]); - break; case GRADIENT: - ret = read_iplane(&s->h[p], s->temp->data[p], s->temp->linesize[p], width, height, 1, type_size, depth); - if (ret < 0) - break; - ret = dilate(&s->g[p], &s->f[p], &s->SE[p], &s->Ty[0][p]); - if (ret < 0) - break; - ret = erode(&s->h[p], &s->f[p], &s->SE[p], &s->Ty[1][p]); - if (ret < 0) - break; - difference(&s->g[p], &s->h[p]); - break; case TOPHAT: - ret = read_iplane(&s->h[p], s->temp->data[p], s->temp->linesize[p], width, height, 1, type_size, depth); - if (ret < 0) - break; - ret = erode(&s->h[p], &s->f[p], &s->SE[p], &s->Ty[0][p]); - if (ret < 0) - break; - ret = dilate(&s->g[p], &s->h[p], &s->SE[p], &s->Ty[1][p]); - if (ret < 0) - break; - difference2(&s->g[p], &s->f[p]); - break; case BLACKHAT: ret = read_iplane(&s->h[p], s->temp->data[p], s->temp->linesize[p], width, height, 1, type_size, depth); - if (ret < 0) - break; - ret = dilate(&s->h[p], &s->f[p], &s->SE[p], &s->Ty[0][p]); - if (ret < 0) - break; - ret = erode(&s->g[p], &s->h[p], &s->SE[p], &s->Ty[1][p]); - if (ret < 0) - break; - difference(&s->g[p], &s->f[p]); break; - default: - av_assert0(0); } if (ret < 0) goto fail; } + td.in = in; td.out = out; + ret = ff_filter_execute(ctx, morpho_slice, &td, NULL, + FFMIN3(s->planeheight[1], s->planeheight[2], + FFMIN(MAX_THREADS, ff_filter_get_nb_threads(ctx)))); + if (ret == 0 && (s->mode != ERODE && s->mode != DILATE)) { + ff_filter_execute(ctx, morpho_sliceX, NULL, NULL, + FFMIN3(s->planeheight[1], s->planeheight[2], + FFMIN(MAX_THREADS, ff_filter_get_nb_threads(ctx)))); + } + av_frame_free(&in); out->pts = av_rescale_q(s->fs.pts, s->fs.time_base, outlink->time_base); return ff_filter_frame(outlink, out); @@ -979,8 +1039,10 @@ static av_cold void uninit(AVFilterContext *ctx) free_iplane(&s->g[p]); free_iplane(&s->h[p]); free_chord_set(&s->SE[p]); - free_lut(&s->Ty[0][p]); - free_lut(&s->Ty[1][p]); + for (int n = 0; n < MAX_THREADS; n++) { + free_lut(&s->Ty[n][0][p]); + free_lut(&s->Ty[n][1][p]); + } } ff_framesync_uninit(&s->fs); @@ -1022,6 +1084,7 @@ const AVFilter ff_vf_morpho = { FILTER_INPUTS(morpho_inputs), FILTER_OUTPUTS(morpho_outputs), FILTER_PIXFMTS_ARRAY(pix_fmts), - .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | + AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, }; diff --git a/libavfilter/vf_mpdecimate.c b/libavfilter/vf_mpdecimate.c index 71f673cb646..8c7824ed227 100644 --- a/libavfilter/vf_mpdecimate.c +++ b/libavfilter/vf_mpdecimate.c @@ -30,7 +30,6 @@ #include "libavutil/timestamp.h" #include "avfilter.h" #include "internal.h" -#include "formats.h" #include "video.h" typedef struct DecimateContext { @@ -46,6 +45,9 @@ typedef struct DecimateContext { int drop_count; ///< if positive: number of frames sequentially dropped ///< if negative: number of sequential frames which were not dropped + int max_keep_count; ///< number of similar frames to ignore before to start dropping them + int keep_count; ///< number of similar frames already ignored + int hsub, vsub; ///< chroma subsampling values AVFrame *ref; ///< reference picture av_pixelutils_sad_fn sad; ///< sum of absolute difference function @@ -57,6 +59,8 @@ typedef struct DecimateContext { static const AVOption mpdecimate_options[] = { { "max", "set the maximum number of consecutive dropped frames (positive), or the minimum interval between dropped frames (negative)", OFFSET(max_drop_count), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGS }, + { "keep", "set the number of similar consecutive frames to be kept before starting to drop similar frames", + OFFSET(max_keep_count), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS }, { "hi", "set high dropping threshold", OFFSET(hi), AV_OPT_TYPE_INT, {.i64=64*12}, INT_MIN, INT_MAX, FLAGS }, { "lo", "set low dropping threshold", OFFSET(lo), AV_OPT_TYPE_INT, {.i64=64*5}, INT_MIN, INT_MAX, FLAGS }, { "frac", "set fraction dropping threshold", OFFSET(frac), AV_OPT_TYPE_FLOAT, {.dbl=0.33}, 0, 1, FLAGS }, @@ -112,6 +116,12 @@ static int decimate_frame(AVFilterContext *ctx, DecimateContext *decimate = ctx->priv; int plane; + if (decimate->max_keep_count > 0 && + decimate->keep_count > -1 && + decimate->keep_count < decimate->max_keep_count) { + decimate->keep_count++; + return 0; + } if (decimate->max_drop_count > 0 && decimate->drop_count >= decimate->max_drop_count) return 0; @@ -196,20 +206,24 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *cur) if (decimate->ref && decimate_frame(inlink->dst, cur, decimate->ref)) { decimate->drop_count = FFMAX(1, decimate->drop_count+1); + decimate->keep_count = -1; // do not keep any more frames until non-similar frames are detected } else { av_frame_free(&decimate->ref); decimate->ref = cur; decimate->drop_count = FFMIN(-1, decimate->drop_count-1); + if (decimate->keep_count < 0) // re-enable counting similiar frames to ignore before dropping + decimate->keep_count = 0; if ((ret = ff_filter_frame(outlink, av_frame_clone(cur))) < 0) return ret; } av_log(inlink->dst, AV_LOG_DEBUG, - "%s pts:%s pts_time:%s drop_count:%d\n", + "%s pts:%s pts_time:%s drop_count:%d keep_count:%d\n", decimate->drop_count > 0 ? "drop" : "keep", av_ts2str(cur->pts), av_ts2timestr(cur->pts, &inlink->time_base), - decimate->drop_count); + decimate->drop_count, + decimate->keep_count); if (decimate->drop_count > 0) av_frame_free(&cur); @@ -226,13 +240,6 @@ static const AVFilterPad mpdecimate_inputs[] = { }, }; -static const AVFilterPad mpdecimate_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_mpdecimate = { .name = "mpdecimate", .description = NULL_IF_CONFIG_SMALL("Remove near-duplicate frames."), @@ -241,6 +248,6 @@ const AVFilter ff_vf_mpdecimate = { .priv_size = sizeof(DecimateContext), .priv_class = &mpdecimate_class, FILTER_INPUTS(mpdecimate_inputs), - FILTER_OUTPUTS(mpdecimate_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), }; diff --git a/libavfilter/vf_multiply.c b/libavfilter/vf_multiply.c index 979b885eb19..54fbeff4831 100644 --- a/libavfilter/vf_multiply.c +++ b/libavfilter/vf_multiply.c @@ -22,7 +22,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "framesync.h" diff --git a/libavfilter/vf_negate.c b/libavfilter/vf_negate.c index d782e63d3a2..4887fc4860a 100644 --- a/libavfilter/vf_negate.c +++ b/libavfilter/vf_negate.c @@ -16,14 +16,12 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/attributes.h" #include "libavutil/common.h" #include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -355,20 +353,13 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_negate = { .name = "negate", .description = NULL_IF_CONFIG_SMALL("Negate input video."), .priv_size = sizeof(NegateContext), .priv_class = &negate_class, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, diff --git a/libavfilter/vf_neighbor.c b/libavfilter/vf_neighbor.c index d6638779e59..915347d6ba9 100644 --- a/libavfilter/vf_neighbor.c +++ b/libavfilter/vf_neighbor.c @@ -26,7 +26,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -341,13 +340,6 @@ static const AVFilterPad neighbor_inputs[] = { }, }; -static const AVFilterPad neighbor_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(NContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -358,7 +350,7 @@ const AVFilter ff_vf_##name_ = { \ .priv_class = &priv_class_##_class, \ .priv_size = sizeof(NContext), \ FILTER_INPUTS(neighbor_inputs), \ - FILTER_OUTPUTS(neighbor_outputs), \ + FILTER_OUTPUTS(ff_video_default_filterpad), \ FILTER_PIXFMTS_ARRAY(pix_fmts), \ .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC| \ AVFILTER_FLAG_SLICE_THREADS, \ diff --git a/libavfilter/vf_neighbor_opencl.c b/libavfilter/vf_neighbor_opencl.c index 3b83cee3da6..b2939f841a2 100644 --- a/libavfilter/vf_neighbor_opencl.c +++ b/libavfilter/vf_neighbor_opencl.c @@ -55,7 +55,7 @@ static int neighbor_opencl_init(AVFilterContext *avctx) cl_int cle; int err; - err = ff_opencl_filter_load_program(avctx, &ff_opencl_source_neighbor, 1); + err = ff_opencl_filter_load_program(avctx, &ff_source_neighbor_cl, 1); if (err < 0) goto fail; @@ -312,6 +312,7 @@ const AVFilter ff_vf_dilation_opencl = { FILTER_OUTPUTS(neighbor_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; #endif /* CONFIG_DILATION_OPENCL_FILTER */ diff --git a/libavfilter/vf_nlmeans.c b/libavfilter/vf_nlmeans.c index 2fc3adacca1..dc935538f05 100644 --- a/libavfilter/vf_nlmeans.c +++ b/libavfilter/vf_nlmeans.c @@ -33,7 +33,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "vf_nlmeans.h" #include "vf_nlmeans_init.h" @@ -472,13 +471,6 @@ static const AVFilterPad nlmeans_inputs[] = { }, }; -static const AVFilterPad nlmeans_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_nlmeans = { .name = "nlmeans", .description = NULL_IF_CONFIG_SMALL("Non-local means denoiser."), @@ -486,7 +478,7 @@ const AVFilter ff_vf_nlmeans = { .init = init, .uninit = uninit, FILTER_INPUTS(nlmeans_inputs), - FILTER_OUTPUTS(nlmeans_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &nlmeans_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_nlmeans_opencl.c b/libavfilter/vf_nlmeans_opencl.c index c0ac8bf95dd..5149be02ca4 100644 --- a/libavfilter/vf_nlmeans_opencl.c +++ b/libavfilter/vf_nlmeans_opencl.c @@ -98,7 +98,7 @@ static int nlmeans_opencl_init(AVFilterContext *avctx, int width, int height) if (!ctx->patch_size_uv) ctx->patch_size_uv = ctx->patch_size; - err = ff_opencl_filter_load_program(avctx, &ff_opencl_source_nlmeans, 1); + err = ff_opencl_filter_load_program(avctx, &ff_source_nlmeans_cl, 1); if (err < 0) goto fail; @@ -438,4 +438,5 @@ const AVFilter ff_vf_nlmeans_opencl = { FILTER_OUTPUTS(nlmeans_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_nlmeans_vulkan.c b/libavfilter/vf_nlmeans_vulkan.c new file mode 100644 index 00000000000..7da31b73f56 --- /dev/null +++ b/libavfilter/vf_nlmeans_vulkan.c @@ -0,0 +1,1123 @@ +/* + * Copyright (c) Lynne + * + * 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 "libavutil/random_seed.h" +#include "libavutil/opt.h" +#include "vulkan_filter.h" +#include "vulkan_spirv.h" +#include "internal.h" +#include "video.h" + +#define TYPE_NAME "vec4" +#define TYPE_ELEMS 4 +#define TYPE_SIZE (TYPE_ELEMS*4) + +typedef struct NLMeansVulkanContext { + FFVulkanContext vkctx; + + int initialized; + FFVkExecPool e; + FFVkQueueFamilyCtx qf; + VkSampler sampler; + + AVBufferPool *integral_buf_pool; + AVBufferPool *state_buf_pool; + AVBufferPool *ws_buf_pool; + + int pl_weights_rows; + FFVulkanPipeline pl_weights; + FFVkSPIRVShader shd_weights; + + FFVulkanPipeline pl_denoise; + FFVkSPIRVShader shd_denoise; + + int *xoffsets; + int *yoffsets; + int nb_offsets; + float strength[4]; + int patch[4]; + + struct nlmeans_opts { + int r; + double s; + double sc[4]; + int p; + int pc[4]; + int t; + } opts; +} NLMeansVulkanContext; + +extern const char *ff_source_prefix_sum_comp; + +static void insert_first(FFVkSPIRVShader *shd, int r, int horiz, int plane, int comp) +{ + GLSLF(2, s1 = texture(input_img[%i], ivec2(x + %i, y + %i))[%i]; + ,plane, horiz ? r : 0, !horiz ? r : 0, comp); + + if (TYPE_ELEMS == 4) { + GLSLF(2, s2[0] = texture(input_img[%i], ivec2(x + %i + xoffs[0], y + %i + yoffs[0]))[%i]; + ,plane, horiz ? r : 0, !horiz ? r : 0, comp); + GLSLF(2, s2[1] = texture(input_img[%i], ivec2(x + %i + xoffs[1], y + %i + yoffs[1]))[%i]; + ,plane, horiz ? r : 0, !horiz ? r : 0, comp); + GLSLF(2, s2[2] = texture(input_img[%i], ivec2(x + %i + xoffs[2], y + %i + yoffs[2]))[%i]; + ,plane, horiz ? r : 0, !horiz ? r : 0, comp); + GLSLF(2, s2[3] = texture(input_img[%i], ivec2(x + %i + xoffs[3], y + %i + yoffs[3]))[%i]; + ,plane, horiz ? r : 0, !horiz ? r : 0, comp); + } else { + for (int i = 0; i < 16; i++) { + GLSLF(2, s2[%i][%i] = texture(input_img[%i], ivec2(x + %i + xoffs[%i], y + %i + yoffs[%i]))[%i]; + ,i / 4, i % 4, plane, horiz ? r : 0, i, !horiz ? r : 0, i, comp); + } + } + + GLSLC(2, s2 = (s1 - s2) * (s1 - s2); ); +} + +static void insert_horizontal_pass(FFVkSPIRVShader *shd, int nb_rows, int first, int plane, int comp) +{ + GLSLF(1, x = int(gl_GlobalInvocationID.x) * %i; ,nb_rows); + if (!first) { + GLSLC(1, controlBarrier(gl_ScopeWorkgroup, gl_ScopeWorkgroup, + gl_StorageSemanticsBuffer, + gl_SemanticsAcquireRelease | + gl_SemanticsMakeAvailable | + gl_SemanticsMakeVisible); ); + } + GLSLC(1, for (y = 0; y < height[0]; y++) { ); + GLSLC(2, offset = uint64_t(int_stride)*y*T_ALIGN; ); + GLSLC(2, dst = DataBuffer(uint64_t(integral_data) + offset); ); + GLSLC(0, ); + if (first) { + for (int r = 0; r < nb_rows; r++) { + insert_first(shd, r, 1, plane, comp); + GLSLF(2, dst.v[x + %i] = s2; ,r); + GLSLC(0, ); + } + } + GLSLC(2, barrier(); ); + GLSLC(2, prefix_sum(dst, 1, dst, 1); ); + GLSLC(1, } ); + GLSLC(0, ); +} + +static void insert_vertical_pass(FFVkSPIRVShader *shd, int nb_rows, int first, int plane, int comp) +{ + GLSLF(1, y = int(gl_GlobalInvocationID.x) * %i; ,nb_rows); + if (!first) { + GLSLC(1, controlBarrier(gl_ScopeWorkgroup, gl_ScopeWorkgroup, + gl_StorageSemanticsBuffer, + gl_SemanticsAcquireRelease | + gl_SemanticsMakeAvailable | + gl_SemanticsMakeVisible); ); + } + GLSLC(1, for (x = 0; x < width[0]; x++) { ); + GLSLC(2, dst = DataBuffer(uint64_t(integral_data) + x*T_ALIGN); ); + + for (int r = 0; r < nb_rows; r++) { + if (first) { + insert_first(shd, r, 0, plane, comp); + GLSLF(2, integral_data.v[(y + %i)*int_stride + x] = s2; ,r); + GLSLC(0, ); + } + } + + GLSLC(2, barrier(); ); + GLSLC(2, prefix_sum(dst, int_stride, dst, int_stride); ); + GLSLC(1, } ); + GLSLC(0, ); +} + +static void insert_weights_pass(FFVkSPIRVShader *shd, int nb_rows, int vert, + int t, int dst_comp, int plane, int comp) +{ + GLSLF(1, p = patch_size[%i]; ,dst_comp); + GLSLC(0, ); + GLSLC(1, controlBarrier(gl_ScopeWorkgroup, gl_ScopeWorkgroup, + gl_StorageSemanticsBuffer, + gl_SemanticsAcquireRelease | + gl_SemanticsMakeAvailable | + gl_SemanticsMakeVisible); ); + GLSLC(1, barrier(); ); + if (!vert) { + GLSLC(1, for (y = 0; y < height[0]; y++) { ); + GLSLF(2, if (gl_GlobalInvocationID.x*%i >= width[%i]) ,nb_rows, plane); + GLSLC(3, break; ); + GLSLF(2, for (r = 0; r < %i; r++) { ,nb_rows); + GLSLF(3, x = int(gl_GlobalInvocationID.x) * %i + r; ,nb_rows); + } else { + GLSLC(1, for (x = 0; x < width[0]; x++) { ); + GLSLF(2, if (gl_GlobalInvocationID.x*%i >= height[%i]) ,nb_rows, plane); + GLSLC(3, break; ); + GLSLF(2, for (r = 0; r < %i; r++) { ,nb_rows); + GLSLF(3, y = int(gl_GlobalInvocationID.x) * %i + r; ,nb_rows); + } + GLSLC(0, ); + GLSLC(3, a = DTYPE(0); ); + GLSLC(3, b = DTYPE(0); ); + GLSLC(3, c = DTYPE(0); ); + GLSLC(3, d = DTYPE(0); ); + GLSLC(0, ); + GLSLC(3, lt = ((x - p) < 0) || ((y - p) < 0); ); + GLSLC(0, ); + if (TYPE_ELEMS == 4) { + GLSLF(3, src[0] = texture(input_img[%i], ivec2(x + xoffs[0], y + yoffs[0]))[%i]; ,plane, comp); + GLSLF(3, src[1] = texture(input_img[%i], ivec2(x + xoffs[1], y + yoffs[1]))[%i]; ,plane, comp); + GLSLF(3, src[2] = texture(input_img[%i], ivec2(x + xoffs[2], y + yoffs[2]))[%i]; ,plane, comp); + GLSLF(3, src[3] = texture(input_img[%i], ivec2(x + xoffs[3], y + yoffs[3]))[%i]; ,plane, comp); + } else { + for (int i = 0; i < 16; i++) + GLSLF(3, src[%i][%i] = texture(input_img[%i], ivec2(x + xoffs[%i], y + yoffs[%i]))[%i]; + ,i / 4, i % 4, plane, i, i, comp); + + } + GLSLC(0, ); + GLSLC(3, if (lt == false) { ); + GLSLC(4, a = integral_data.v[(y - p)*int_stride + x - p]; ); + GLSLC(4, c = integral_data.v[(y - p)*int_stride + x + p]; ); + GLSLC(4, b = integral_data.v[(y + p)*int_stride + x - p]; ); + GLSLC(4, d = integral_data.v[(y + p)*int_stride + x + p]; ); + GLSLC(3, } ); + GLSLC(0, ); + GLSLC(3, patch_diff = d + a - b - c; ); + if (TYPE_ELEMS == 4) { + GLSLF(3, w = exp(patch_diff * strength[%i]); ,dst_comp); + GLSLC(3, w_sum = w[0] + w[1] + w[2] + w[3]; ); + GLSLC(3, sum = dot(w, src*255); ); + } else { + for (int i = 0; i < 4; i++) + GLSLF(3, w[%i] = exp(patch_diff[%i] * strength[%i]); ,i,i,dst_comp); + for (int i = 0; i < 4; i++) + GLSLF(3, w_sum %s w[%i][0] + w[%i][1] + w[%i][2] + w[%i][3]; + ,!i ? "=" : "+=", i, i, i, i); + for (int i = 0; i < 4; i++) + GLSLF(3, sum %s dot(w[%i], src[%i]*255); + ,!i ? "=" : "+=", i, i); + } + GLSLC(0, ); + if (t > 1) { + GLSLF(3, atomicAdd(weights_%i[y*ws_stride[%i] + x], w_sum); ,dst_comp, dst_comp); + GLSLF(3, atomicAdd(sums_%i[y*ws_stride[%i] + x], sum); ,dst_comp, dst_comp); + } else { + GLSLF(3, weights_%i[y*ws_stride[%i] + x] += w_sum; ,dst_comp, dst_comp); + GLSLF(3, sums_%i[y*ws_stride[%i] + x] += sum; ,dst_comp, dst_comp); + } + GLSLC(2, } ); + GLSLC(1, } ); +} + +typedef struct HorizontalPushData { + VkDeviceAddress integral_data; + VkDeviceAddress state_data; + int32_t xoffs[TYPE_ELEMS]; + int32_t yoffs[TYPE_ELEMS]; + uint32_t width[4]; + uint32_t height[4]; + uint32_t ws_stride[4]; + int32_t patch_size[4]; + float strength[4]; + uint32_t int_stride; +} HorizontalPushData; + +static av_cold int init_weights_pipeline(FFVulkanContext *vkctx, FFVkExecPool *exec, + FFVulkanPipeline *pl, FFVkSPIRVShader *shd, + VkSampler sampler, FFVkSPIRVCompiler *spv, + int width, int height, int t, + const AVPixFmtDescriptor *desc, + int planes, int *nb_rows) +{ + int err; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; + FFVulkanDescriptorSetBinding *desc_set; + int max_dim = FFMAX(width, height); + uint32_t max_wg = vkctx->props.properties.limits.maxComputeWorkGroupSize[0]; + int max_shm = vkctx->props.properties.limits.maxComputeSharedMemorySize; + int wg_size, wg_rows; + + /* Round the max workgroup size to the previous power of two */ + max_wg = 1 << (31 - ff_clz(max_wg)); + wg_size = max_wg; + wg_rows = 1; + + if (max_wg > max_dim) { + wg_size = max_wg / (max_wg / max_dim); + } else if (max_wg < max_dim) { + /* First, make it fit */ + while (wg_size*wg_rows < max_dim) + wg_rows++; + + /* Second, make sure there's enough shared memory */ + while ((wg_size * TYPE_SIZE + TYPE_SIZE + 2*4) > max_shm) { + wg_size >>= 1; + wg_rows++; + } + } + + RET(ff_vk_shader_init(pl, shd, "nlmeans_weights", VK_SHADER_STAGE_COMPUTE_BIT, 0)); + ff_vk_shader_set_compute_sizes(shd, wg_size, 1, 1); + *nb_rows = wg_rows; + + if (t > 1) + GLSLC(0, #extension GL_EXT_shader_atomic_float : require ); + GLSLC(0, #extension GL_ARB_gpu_shader_int64 : require ); + GLSLC(0, #pragma use_vulkan_memory_model ); + GLSLC(0, #extension GL_KHR_memory_scope_semantics : enable ); + GLSLC(0, ); + GLSLF(0, #define N_ROWS %i ,*nb_rows); + GLSLC(0, #define WG_SIZE (gl_WorkGroupSize.x) ); + GLSLF(0, #define LG_WG_SIZE %i ,ff_log2(shd->local_size[0])); + GLSLC(0, #define PARTITION_SIZE (N_ROWS*WG_SIZE) ); + GLSLF(0, #define DTYPE %s ,TYPE_NAME); + GLSLF(0, #define T_ALIGN %i ,TYPE_SIZE); + GLSLC(0, ); + GLSLC(0, layout(buffer_reference, buffer_reference_align = T_ALIGN) coherent buffer DataBuffer { ); + GLSLC(1, DTYPE v[]; ); + GLSLC(0, }; ); + GLSLC(0, ); + GLSLC(0, layout(buffer_reference) buffer StateData; ); + GLSLC(0, ); + GLSLC(0, layout(push_constant, std430) uniform pushConstants { ); + GLSLC(1, coherent DataBuffer integral_data; ); + GLSLC(1, StateData state; ); + GLSLF(1, uint xoffs[%i]; ,TYPE_ELEMS); + GLSLF(1, uint yoffs[%i]; ,TYPE_ELEMS); + GLSLC(1, uvec4 width; ); + GLSLC(1, uvec4 height; ); + GLSLC(1, uvec4 ws_stride; ); + GLSLC(1, ivec4 patch_size; ); + GLSLC(1, vec4 strength; ); + GLSLC(1, uint int_stride; ); + GLSLC(0, }; ); + GLSLC(0, ); + + ff_vk_add_push_constant(pl, 0, sizeof(HorizontalPushData), VK_SHADER_STAGE_COMPUTE_BIT); + + desc_set = (FFVulkanDescriptorSetBinding []) { + { + .name = "input_img", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(sampler), + }, + { + .name = "weights_buffer_0", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float weights_0[];", + }, + { + .name = "sums_buffer_0", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float sums_0[];", + }, + { + .name = "weights_buffer_1", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float weights_1[];", + }, + { + .name = "sums_buffer_1", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float sums_1[];", + }, + { + .name = "weights_buffer_2", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float weights_2[];", + }, + { + .name = "sums_buffer_2", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float sums_2[];", + }, + { + .name = "weights_buffer_3", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float weights_3[];", + }, + { + .name = "sums_buffer_3", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float sums_3[];", + }, + }; + RET(ff_vk_pipeline_descriptor_set_add(vkctx, pl, shd, desc_set, 1 + 2*desc->nb_components, 0, 0)); + + GLSLD( ff_source_prefix_sum_comp ); + GLSLC(0, ); + GLSLC(0, void main() ); + GLSLC(0, { ); + GLSLC(1, uint64_t offset; ); + GLSLC(1, DataBuffer dst; ); + GLSLC(1, float s1; ); + GLSLC(1, DTYPE s2; ); + GLSLC(1, int r; ); + GLSLC(1, int x; ); + GLSLC(1, int y; ); + GLSLC(1, int p; ); + GLSLC(0, ); + GLSLC(1, DTYPE a; ); + GLSLC(1, DTYPE b; ); + GLSLC(1, DTYPE c; ); + GLSLC(1, DTYPE d; ); + GLSLC(0, ); + GLSLC(1, DTYPE patch_diff; ); + if (TYPE_ELEMS == 4) { + GLSLC(1, vec4 src; ); + GLSLC(1, vec4 w; ); + } else { + GLSLC(1, vec4 src[4]; ); + GLSLC(1, vec4 w[4]; ); + } + GLSLC(1, float w_sum; ); + GLSLC(1, float sum; ); + GLSLC(0, ); + GLSLC(1, bool lt; ); + GLSLC(1, bool gt; ); + GLSLC(0, ); + + for (int i = 0; i < desc->nb_components; i++) { + int off = desc->comp[i].offset / (FFALIGN(desc->comp[i].depth, 8)/8); + if (width > height) { + insert_horizontal_pass(shd, *nb_rows, 1, desc->comp[i].plane, off); + insert_vertical_pass(shd, *nb_rows, 0, desc->comp[i].plane, off); + insert_weights_pass(shd, *nb_rows, 0, t, i, desc->comp[i].plane, off); + } else { + insert_vertical_pass(shd, *nb_rows, 1, desc->comp[i].plane, off); + insert_horizontal_pass(shd, *nb_rows, 0, desc->comp[i].plane, off); + insert_weights_pass(shd, *nb_rows, 1, t, i, desc->comp[i].plane, off); + } + } + + GLSLC(0, } ); + + RET(spv->compile_shader(spv, vkctx, shd, &spv_data, &spv_len, "main", &spv_opaque)); + RET(ff_vk_shader_create(vkctx, shd, spv_data, spv_len, "main")); + + RET(ff_vk_init_compute_pipeline(vkctx, pl, shd)); + RET(ff_vk_exec_pipeline_register(vkctx, exec, pl)); + + return 0; + +fail: + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); + + return err; +} + +typedef struct DenoisePushData { + uint32_t ws_stride[4]; +} DenoisePushData; + +static av_cold int init_denoise_pipeline(FFVulkanContext *vkctx, FFVkExecPool *exec, + FFVulkanPipeline *pl, FFVkSPIRVShader *shd, + VkSampler sampler, FFVkSPIRVCompiler *spv, + const AVPixFmtDescriptor *desc, int planes) +{ + int err; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; + FFVulkanDescriptorSetBinding *desc_set; + + RET(ff_vk_shader_init(pl, shd, "nlmeans_denoise", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); + + ff_vk_shader_set_compute_sizes(shd, 32, 32, 1); + + GLSLC(0, layout(push_constant, std430) uniform pushConstants { ); + GLSLC(1, uvec4 ws_stride; ); + GLSLC(0, }; ); + + ff_vk_add_push_constant(pl, 0, sizeof(DenoisePushData), VK_SHADER_STAGE_COMPUTE_BIT); + + desc_set = (FFVulkanDescriptorSetBinding []) { + { + .name = "input_img", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(sampler), + }, + { + .name = "output_img", + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .mem_layout = ff_vk_shader_rep_fmt(vkctx->output_format), + .mem_quali = "writeonly", + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + }, + { + .name = "weights_buffer_0", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .mem_quali = "readonly", + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float weights_0[];", + }, + { + .name = "sums_buffer_0", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .mem_quali = "readonly", + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float sums_0[];", + }, + { + .name = "weights_buffer_1", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .mem_quali = "readonly", + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float weights_1[];", + }, + { + .name = "sums_buffer_1", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .mem_quali = "readonly", + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float sums_1[];", + }, + { + .name = "weights_buffer_2", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .mem_quali = "readonly", + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float weights_2[];", + }, + { + .name = "sums_buffer_2", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .mem_quali = "readonly", + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float sums_2[];", + }, + { + .name = "weights_buffer_3", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .mem_quali = "readonly", + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float weights_3[];", + }, + { + .name = "sums_buffer_3", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .mem_quali = "readonly", + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float sums_3[];", + }, + }; + RET(ff_vk_pipeline_descriptor_set_add(vkctx, pl, shd, desc_set, 2 + 2*desc->nb_components, 0, 0)); + + GLSLC(0, void main() ); + GLSLC(0, { ); + GLSLC(1, ivec2 size; ); + GLSLC(1, const ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); + GLSLC(0, ); + GLSLC(1, float w_sum; ); + GLSLC(1, float sum; ); + GLSLC(1, vec4 src; ); + GLSLC(1, vec4 r; ); + GLSLC(0, ); + + for (int i = 0; i < planes; i++) { + GLSLF(1, src = texture(input_img[%i], pos); ,i); + for (int c = 0; c < desc->nb_components; c++) { + if (desc->comp[c].plane == i) { + int off = desc->comp[c].offset / (FFALIGN(desc->comp[c].depth, 8)/8); + GLSLF(1, w_sum = weights_%i[pos.y*ws_stride[%i] + pos.x]; ,c, c); + GLSLF(1, sum = sums_%i[pos.y*ws_stride[%i] + pos.x]; ,c, c); + GLSLF(1, r[%i] = (sum + src[%i]*255) / (1.0 + w_sum) / 255; ,off, off); + GLSLC(0, ); + } + } + GLSLF(1, imageStore(output_img[%i], pos, r); ,i); + GLSLC(0, ); + } + + GLSLC(0, } ); + + RET(spv->compile_shader(spv, vkctx, shd, &spv_data, &spv_len, "main", &spv_opaque)); + RET(ff_vk_shader_create(vkctx, shd, spv_data, spv_len, "main")); + + RET(ff_vk_init_compute_pipeline(vkctx, pl, shd)); + RET(ff_vk_exec_pipeline_register(vkctx, exec, pl)); + + return 0; + +fail: + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); + + return err; +} + +static av_cold int init_filter(AVFilterContext *ctx) +{ + int rad, err; + int xcnt = 0, ycnt = 0; + NLMeansVulkanContext *s = ctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); + FFVkSPIRVCompiler *spv; + + const AVPixFmtDescriptor *desc; + desc = av_pix_fmt_desc_get(vkctx->output_format); + if (!desc) + return AVERROR(EINVAL); + + if (!(s->opts.r & 1)) { + s->opts.r |= 1; + av_log(ctx, AV_LOG_WARNING, "Research size should be odd, setting to %i", + s->opts.r); + } + + if (!(s->opts.p & 1)) { + s->opts.p |= 1; + av_log(ctx, AV_LOG_WARNING, "Patch size should be odd, setting to %i", + s->opts.p); + } + + for (int i = 0; i < 4; i++) { + double str = (s->opts.sc[i] > 1.0) ? s->opts.sc[i] : s->opts.s; + int ps = (s->opts.pc[i] ? s->opts.pc[i] : s->opts.p); + str = 10.0f*str; + str *= -str; + str = 255.0*255.0 / str; + s->strength[i] = str; + if (!(ps & 1)) { + ps |= 1; + av_log(ctx, AV_LOG_WARNING, "Patch size should be odd, setting to %i", + ps); + } + s->patch[i] = ps / 2; + } + + rad = s->opts.r/2; + s->nb_offsets = (2*rad + 1)*(2*rad + 1) - 1; + s->xoffsets = av_malloc(s->nb_offsets*sizeof(*s->xoffsets)); + s->yoffsets = av_malloc(s->nb_offsets*sizeof(*s->yoffsets)); + s->nb_offsets = 0; + + for (int x = -rad; x <= rad; x++) { + for (int y = -rad; y <= rad; y++) { + if (!x && !y) + continue; + + s->xoffsets[xcnt++] = x; + s->yoffsets[ycnt++] = y; + s->nb_offsets++; + } + } + + s->opts.t = FFMIN(s->opts.t, (FFALIGN(s->nb_offsets, TYPE_ELEMS) / TYPE_ELEMS)); + if (!vkctx->atomic_float_feats.shaderBufferFloat32AtomicAdd) { + av_log(ctx, AV_LOG_WARNING, "Device doesn't support atomic float adds, " + "disabling dispatch parallelism\n"); + s->opts.t = 1; + } + + if (!vkctx->feats_12.vulkanMemoryModel) { + av_log(ctx, AV_LOG_ERROR, "Device doesn't support the Vulkan memory model!"); + return AVERROR(EINVAL);; + } + + spv = ff_vk_spirv_init(); + if (!spv) { + av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); + return AVERROR_EXTERNAL; + } + + ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT); + RET(ff_vk_exec_pool_init(vkctx, &s->qf, &s->e, 1, 0, 0, 0, NULL)); + RET(ff_vk_init_sampler(vkctx, &s->sampler, 1, VK_FILTER_NEAREST)); + + RET(init_weights_pipeline(vkctx, &s->e, &s->pl_weights, &s->shd_weights, s->sampler, + spv, s->vkctx.output_width, s->vkctx.output_height, + s->opts.t, desc, planes, &s->pl_weights_rows)); + + RET(init_denoise_pipeline(vkctx, &s->e, &s->pl_denoise, &s->shd_denoise, s->sampler, + spv, desc, planes)); + + av_log(ctx, AV_LOG_VERBOSE, "Filter initialized, %i x/y offsets, %i dispatches, %i parallel\n", + s->nb_offsets, (FFALIGN(s->nb_offsets, TYPE_ELEMS) / TYPE_ELEMS) + 1, s->opts.t); + + s->initialized = 1; + + return 0; + +fail: + if (spv) + spv->uninit(&spv); + + return err; +} + +static int denoise_pass(NLMeansVulkanContext *s, FFVkExecContext *exec, + FFVkBuffer *ws_vk, uint32_t ws_stride[4]) +{ + FFVulkanContext *vkctx = &s->vkctx; + FFVulkanFunctions *vk = &vkctx->vkfn; + VkBufferMemoryBarrier2 buf_bar[8]; + int nb_buf_bar = 0; + + /* Denoise pass pipeline */ + ff_vk_exec_bind_pipeline(vkctx, exec, &s->pl_denoise); + + /* Push data */ + ff_vk_update_push_exec(vkctx, exec, &s->pl_denoise, VK_SHADER_STAGE_COMPUTE_BIT, + 0, sizeof(DenoisePushData), &(DenoisePushData) { + { ws_stride[0], ws_stride[1], ws_stride[2], ws_stride[3] }, + }); + + buf_bar[nb_buf_bar++] = (VkBufferMemoryBarrier2) { + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2, + .srcStageMask = ws_vk->stage, + .dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + .srcAccessMask = ws_vk->access, + .dstAccessMask = VK_ACCESS_2_SHADER_STORAGE_READ_BIT, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = ws_vk->buf, + .size = ws_vk->size, + .offset = 0, + }; + + vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pBufferMemoryBarriers = buf_bar, + .bufferMemoryBarrierCount = nb_buf_bar, + }); + ws_vk->stage = buf_bar[0].dstStageMask; + ws_vk->access = buf_bar[0].dstAccessMask; + + /* End of denoise pass */ + vk->CmdDispatch(exec->buf, + FFALIGN(vkctx->output_width, s->pl_denoise.wg_size[0])/s->pl_denoise.wg_size[0], + FFALIGN(vkctx->output_height, s->pl_denoise.wg_size[1])/s->pl_denoise.wg_size[1], + 1); + + return 0; +} + +static int nlmeans_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) +{ + int err; + AVFrame *out = NULL; + AVFilterContext *ctx = link->dst; + NLMeansVulkanContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + FFVulkanContext *vkctx = &s->vkctx; + FFVulkanFunctions *vk = &vkctx->vkfn; + + const AVPixFmtDescriptor *desc; + int plane_widths[4]; + int plane_heights[4]; + + /* Integral */ + AVBufferRef *state_buf; + FFVkBuffer *state_vk; + AVBufferRef *integral_buf; + FFVkBuffer *integral_vk; + uint32_t int_stride; + size_t int_size; + size_t state_size; + int t_offset = 0; + + /* Weights/sums */ + AVBufferRef *ws_buf; + FFVkBuffer *ws_vk; + VkDeviceAddress weights_addr[4]; + VkDeviceAddress sums_addr[4]; + uint32_t ws_stride[4]; + size_t ws_size[4]; + size_t ws_total_size = 0; + + FFVkExecContext *exec; + VkImageView in_views[AV_NUM_DATA_POINTERS]; + VkImageView out_views[AV_NUM_DATA_POINTERS]; + VkImageMemoryBarrier2 img_bar[8]; + int nb_img_bar = 0; + VkBufferMemoryBarrier2 buf_bar[8]; + int nb_buf_bar = 0; + + if (!s->initialized) + RET(init_filter(ctx)); + + desc = av_pix_fmt_desc_get(vkctx->output_format); + if (!desc) + return AVERROR(EINVAL); + + /* Integral image */ + int_stride = s->pl_weights.wg_size[0]*s->pl_weights_rows; + int_size = int_stride * int_stride * TYPE_SIZE; + state_size = int_stride * 3 *TYPE_SIZE; + + /* Plane dimensions */ + for (int i = 0; i < desc->nb_components; i++) { + plane_widths[i] = !i || (i == 3) ? vkctx->output_width : AV_CEIL_RSHIFT(vkctx->output_width, desc->log2_chroma_w); + plane_heights[i] = !i || (i == 3) ? vkctx->output_height : AV_CEIL_RSHIFT(vkctx->output_height, desc->log2_chroma_w); + plane_widths[i] = FFALIGN(plane_widths[i], s->pl_denoise.wg_size[0]); + plane_heights[i] = FFALIGN(plane_heights[i], s->pl_denoise.wg_size[1]); + + ws_stride[i] = plane_widths[i]; + ws_size[i] = ws_stride[i] * plane_heights[i] * sizeof(float); + ws_total_size += ws_size[i]; + } + + /* Buffers */ + err = ff_vk_get_pooled_buffer(&s->vkctx, &s->integral_buf_pool, &integral_buf, + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + NULL, + s->opts.t * int_size, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + if (err < 0) + return err; + integral_vk = (FFVkBuffer *)integral_buf->data; + + err = ff_vk_get_pooled_buffer(&s->vkctx, &s->state_buf_pool, &state_buf, + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + NULL, + s->opts.t * state_size, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + if (err < 0) + return err; + state_vk = (FFVkBuffer *)state_buf->data; + + err = ff_vk_get_pooled_buffer(&s->vkctx, &s->ws_buf_pool, &ws_buf, + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT | + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + NULL, + ws_total_size * 2, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + if (err < 0) + return err; + ws_vk = (FFVkBuffer *)ws_buf->data; + + weights_addr[0] = ws_vk->address; + sums_addr[0] = ws_vk->address + ws_total_size; + for (int i = 1; i < desc->nb_components; i++) { + weights_addr[i] = weights_addr[i - 1] + ws_size[i - 1]; + sums_addr[i] = sums_addr[i - 1] + ws_size[i - 1]; + } + + /* Output frame */ + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + err = AVERROR(ENOMEM); + goto fail; + } + + /* Execution context */ + exec = ff_vk_exec_get(&s->e); + ff_vk_exec_start(vkctx, exec); + + /* Dependencies */ + RET(ff_vk_exec_add_dep_frame(vkctx, exec, in, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT)); + RET(ff_vk_exec_add_dep_frame(vkctx, exec, out, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT)); + RET(ff_vk_exec_add_dep_buf(vkctx, exec, &integral_buf, 1, 0)); + RET(ff_vk_exec_add_dep_buf(vkctx, exec, &state_buf, 1, 0)); + RET(ff_vk_exec_add_dep_buf(vkctx, exec, &ws_buf, 1, 0)); + + /* Input frame prep */ + RET(ff_vk_create_imageviews(vkctx, exec, in_views, in)); + ff_vk_update_descriptor_img_array(vkctx, &s->pl_weights, exec, in, in_views, 0, 0, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + s->sampler); + ff_vk_frame_barrier(vkctx, exec, in, img_bar, &nb_img_bar, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_SHADER_READ_BIT, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_QUEUE_FAMILY_IGNORED); + + /* Output frame prep */ + RET(ff_vk_create_imageviews(vkctx, exec, out_views, out)); + ff_vk_frame_barrier(vkctx, exec, out, img_bar, &nb_img_bar, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_SHADER_WRITE_BIT, + VK_IMAGE_LAYOUT_GENERAL, + VK_QUEUE_FAMILY_IGNORED); + + buf_bar[nb_buf_bar++] = (VkBufferMemoryBarrier2) { + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2, + .srcStageMask = ws_vk->stage, + .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, + .srcAccessMask = ws_vk->access, + .dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = ws_vk->buf, + .size = ws_vk->size, + .offset = 0, + }; + + vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pImageMemoryBarriers = img_bar, + .imageMemoryBarrierCount = nb_img_bar, + .pBufferMemoryBarriers = buf_bar, + .bufferMemoryBarrierCount = nb_buf_bar, + }); + ws_vk->stage = buf_bar[0].dstStageMask; + ws_vk->access = buf_bar[0].dstAccessMask; + + /* Weights/sums buffer zeroing */ + vk->CmdFillBuffer(exec->buf, ws_vk->buf, 0, ws_vk->size, 0x0); + + buf_bar[nb_buf_bar++] = (VkBufferMemoryBarrier2) { + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2, + .srcStageMask = ws_vk->stage, + .dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + .srcAccessMask = ws_vk->access, + .dstAccessMask = VK_ACCESS_2_SHADER_STORAGE_READ_BIT | + VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = ws_vk->buf, + .size = ws_vk->size, + .offset = 0, + }; + + vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pBufferMemoryBarriers = buf_bar, + .bufferMemoryBarrierCount = nb_buf_bar, + }); + ws_vk->stage = buf_bar[0].dstStageMask; + ws_vk->access = buf_bar[0].dstAccessMask; + + /* Update weights descriptors */ + ff_vk_update_descriptor_img_array(vkctx, &s->pl_weights, exec, in, in_views, 0, 0, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + s->sampler); + for (int i = 0; i < desc->nb_components; i++) { + RET(ff_vk_set_descriptor_buffer(&s->vkctx, &s->pl_weights, exec, 0, 1 + i*2 + 0, 0, + weights_addr[i], ws_size[i], + VK_FORMAT_UNDEFINED)); + RET(ff_vk_set_descriptor_buffer(&s->vkctx, &s->pl_weights, exec, 0, 1 + i*2 + 1, 0, + sums_addr[i], ws_size[i], + VK_FORMAT_UNDEFINED)); + } + + /* Update denoise descriptors */ + ff_vk_update_descriptor_img_array(vkctx, &s->pl_denoise, exec, in, in_views, 0, 0, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + s->sampler); + ff_vk_update_descriptor_img_array(vkctx, &s->pl_denoise, exec, out, out_views, 0, 1, + VK_IMAGE_LAYOUT_GENERAL, s->sampler); + for (int i = 0; i < desc->nb_components; i++) { + RET(ff_vk_set_descriptor_buffer(&s->vkctx, &s->pl_denoise, exec, 0, 2 + i*2 + 0, 0, + weights_addr[i], ws_size[i], + VK_FORMAT_UNDEFINED)); + RET(ff_vk_set_descriptor_buffer(&s->vkctx, &s->pl_denoise, exec, 0, 2 + i*2 + 1, 0, + sums_addr[i], ws_size[i], + VK_FORMAT_UNDEFINED)); + } + + /* Weights pipeline */ + ff_vk_exec_bind_pipeline(vkctx, exec, &s->pl_weights); + + for (int i = 0; i < s->nb_offsets; i += TYPE_ELEMS) { + int *xoffs = s->xoffsets + i; + int *yoffs = s->yoffsets + i; + HorizontalPushData pd = { + integral_vk->address + t_offset*int_size, + state_vk->address + t_offset*state_size, + { 0 }, + { 0 }, + { plane_widths[0], plane_widths[1], plane_widths[2], plane_widths[3] }, + { plane_heights[0], plane_heights[1], plane_heights[2], plane_heights[3] }, + { ws_stride[0], ws_stride[1], ws_stride[2], ws_stride[3] }, + { s->patch[0], s->patch[1], s->patch[2], s->patch[3] }, + { s->strength[0], s->strength[1], s->strength[2], s->strength[2], }, + int_stride, + }; + + memcpy(pd.xoffs, xoffs, sizeof(pd.xoffs)); + memcpy(pd.yoffs, yoffs, sizeof(pd.yoffs)); + + /* Put a barrier once we run out of parallelism buffers */ + if (!t_offset) { + nb_buf_bar = 0; + /* Buffer prep/sync */ + buf_bar[nb_buf_bar++] = (VkBufferMemoryBarrier2) { + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2, + .srcStageMask = integral_vk->stage, + .dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + .srcAccessMask = integral_vk->access, + .dstAccessMask = VK_ACCESS_2_SHADER_STORAGE_READ_BIT | + VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = integral_vk->buf, + .size = integral_vk->size, + .offset = 0, + }; + buf_bar[nb_buf_bar++] = (VkBufferMemoryBarrier2) { + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2, + .srcStageMask = state_vk->stage, + .dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + .srcAccessMask = state_vk->access, + .dstAccessMask = VK_ACCESS_2_SHADER_STORAGE_READ_BIT | + VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = state_vk->buf, + .size = state_vk->size, + .offset = 0, + }; + + vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pBufferMemoryBarriers = buf_bar, + .bufferMemoryBarrierCount = nb_buf_bar, + }); + integral_vk->stage = buf_bar[0].dstStageMask; + integral_vk->access = buf_bar[0].dstAccessMask; + state_vk->stage = buf_bar[1].dstStageMask; + state_vk->access = buf_bar[1].dstAccessMask; + } + t_offset = (t_offset + 1) % s->opts.t; + + /* Push data */ + ff_vk_update_push_exec(vkctx, exec, &s->pl_weights, VK_SHADER_STAGE_COMPUTE_BIT, + 0, sizeof(pd), &pd); + + /* End of horizontal pass */ + vk->CmdDispatch(exec->buf, 1, 1, 1); + } + + RET(denoise_pass(s, exec, ws_vk, ws_stride)); + + err = ff_vk_exec_submit(vkctx, exec); + if (err < 0) + return err; + + err = av_frame_copy_props(out, in); + if (err < 0) + goto fail; + + av_frame_free(&in); + + return ff_filter_frame(outlink, out); + +fail: + av_frame_free(&in); + av_frame_free(&out); + return err; +} + +static void nlmeans_vulkan_uninit(AVFilterContext *avctx) +{ + NLMeansVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + FFVulkanFunctions *vk = &vkctx->vkfn; + + ff_vk_exec_pool_free(vkctx, &s->e); + ff_vk_pipeline_free(vkctx, &s->pl_weights); + ff_vk_shader_free(vkctx, &s->shd_weights); + ff_vk_pipeline_free(vkctx, &s->pl_denoise); + ff_vk_shader_free(vkctx, &s->shd_denoise); + + av_buffer_pool_uninit(&s->integral_buf_pool); + av_buffer_pool_uninit(&s->state_buf_pool); + av_buffer_pool_uninit(&s->ws_buf_pool); + + if (s->sampler) + vk->DestroySampler(vkctx->hwctx->act_dev, s->sampler, + vkctx->hwctx->alloc); + + ff_vk_uninit(&s->vkctx); + + s->initialized = 0; +} + +#define OFFSET(x) offsetof(NLMeansVulkanContext, x) +#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) +static const AVOption nlmeans_vulkan_options[] = { + { "s", "denoising strength for all components", OFFSET(opts.s), AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 }, 1.0, 100.0, FLAGS }, + { "p", "patch size for all components", OFFSET(opts.p), AV_OPT_TYPE_INT, { .i64 = 3*2+1 }, 0, 99, FLAGS }, + { "r", "research window radius", OFFSET(opts.r), AV_OPT_TYPE_INT, { .i64 = 7*2+1 }, 0, 99, FLAGS }, + { "t", "parallelism", OFFSET(opts.t), AV_OPT_TYPE_INT, { .i64 = 36 }, 1, 168, FLAGS }, + + { "s1", "denoising strength for component 1", OFFSET(opts.sc[0]), AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 }, 1.0, 100.0, FLAGS }, + { "s2", "denoising strength for component 2", OFFSET(opts.sc[1]), AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 }, 1.0, 100.0, FLAGS }, + { "s3", "denoising strength for component 3", OFFSET(opts.sc[2]), AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 }, 1.0, 100.0, FLAGS }, + { "s4", "denoising strength for component 4", OFFSET(opts.sc[3]), AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 }, 1.0, 100.0, FLAGS }, + + { "p1", "patch size for component 1", OFFSET(opts.pc[0]), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 99, FLAGS }, + { "p2", "patch size for component 2", OFFSET(opts.pc[1]), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 99, FLAGS }, + { "p3", "patch size for component 3", OFFSET(opts.pc[2]), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 99, FLAGS }, + { "p4", "patch size for component 4", OFFSET(opts.pc[3]), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 99, FLAGS }, + + { NULL } +}; + +AVFILTER_DEFINE_CLASS(nlmeans_vulkan); + +static const AVFilterPad nlmeans_vulkan_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = &nlmeans_vulkan_filter_frame, + .config_props = &ff_vk_filter_config_input, + }, +}; + +static const AVFilterPad nlmeans_vulkan_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = &ff_vk_filter_config_output, + }, +}; + +const AVFilter ff_vf_nlmeans_vulkan = { + .name = "nlmeans_vulkan", + .description = NULL_IF_CONFIG_SMALL("Non-local means denoiser (Vulkan)"), + .priv_size = sizeof(NLMeansVulkanContext), + .init = &ff_vk_filter_init, + .uninit = &nlmeans_vulkan_uninit, + FILTER_INPUTS(nlmeans_vulkan_inputs), + FILTER_OUTPUTS(nlmeans_vulkan_outputs), + FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), + .priv_class = &nlmeans_vulkan_class, + .flags = AVFILTER_FLAG_HWDEVICE, + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, +}; diff --git a/libavfilter/vf_nnedi.c b/libavfilter/vf_nnedi.c index 63b83e5efdc..1bb8c21e10e 100644 --- a/libavfilter/vf_nnedi.c +++ b/libavfilter/vf_nnedi.c @@ -540,8 +540,8 @@ static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) const float in_scale = s->in_scale; const float out_scale = s->out_scale; const int depth = s->depth; - const int interlaced = in->interlaced_frame; - const int tff = s->field_n == (s->field < 0 ? interlaced ? in->top_field_first : 1 : + const int interlaced = !!(in->flags & AV_FRAME_FLAG_INTERLACED); + const int tff = s->field_n == (s->field < 0 ? interlaced ? (in->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) : 1 : (s->field & 1) ^ 1); @@ -665,7 +665,12 @@ static int get_frame(AVFilterContext *ctx, int is_second) if (!dst) return AVERROR(ENOMEM); av_frame_copy_props(dst, s->prev); +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS dst->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + dst->flags &= ~AV_FRAME_FLAG_INTERLACED; dst->pts = s->pts; ff_filter_execute(ctx, filter_slice, dst, NULL, @@ -688,7 +693,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) return 0; } - if ((s->deint && !s->prev->interlaced_frame) || ctx->is_disabled) { + if ((s->deint && !(s->prev->flags & AV_FRAME_FLAG_INTERLACED)) || ctx->is_disabled) { s->prev->pts *= 2; ret = ff_filter_frame(ctx->outputs[0], s->prev); s->prev = in; diff --git a/libavfilter/vf_noise.c b/libavfilter/vf_noise.c index 8ed12f74098..ba843eed4eb 100644 --- a/libavfilter/vf_noise.c +++ b/libavfilter/vf_noise.c @@ -330,13 +330,6 @@ static const AVFilterPad noise_inputs[] = { }, }; -static const AVFilterPad noise_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_noise = { .name = "noise", .description = NULL_IF_CONFIG_SMALL("Add noise."), @@ -344,7 +337,7 @@ const AVFilter ff_vf_noise = { .init = init, .uninit = uninit, FILTER_INPUTS(noise_inputs), - FILTER_OUTPUTS(noise_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .priv_class = &noise_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_normalize.c b/libavfilter/vf_normalize.c index 43ed3c67b3f..d81b2d64572 100644 --- a/libavfilter/vf_normalize.c +++ b/libavfilter/vf_normalize.c @@ -72,13 +72,11 @@ * over-processed look. The default is full strength. */ -#include "libavutil/imgutils.h" #include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -511,13 +509,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_normalize = { .name = "normalize", .description = NULL_IF_CONFIG_SMALL("Normalize RGB video."), @@ -525,7 +516,7 @@ const AVFilter ff_vf_normalize = { .priv_class = &normalize_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_null.c b/libavfilter/vf_null.c index fa80e2a1026..1502774f988 100644 --- a/libavfilter/vf_null.c +++ b/libavfilter/vf_null.c @@ -26,24 +26,10 @@ #include "internal.h" #include "video.h" -static const AVFilterPad avfilter_vf_null_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - -static const AVFilterPad avfilter_vf_null_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_null = { .name = "null", .description = NULL_IF_CONFIG_SMALL("Pass the source unchanged to the output."), .flags = AVFILTER_FLAG_METADATA_ONLY, - FILTER_INPUTS(avfilter_vf_null_inputs), - FILTER_OUTPUTS(avfilter_vf_null_outputs), + FILTER_INPUTS(ff_video_default_filterpad), + FILTER_OUTPUTS(ff_video_default_filterpad), }; diff --git a/libavfilter/vf_ocr.c b/libavfilter/vf_ocr.c index 1648afa0f9b..3cece91edf5 100644 --- a/libavfilter/vf_ocr.c +++ b/libavfilter/vf_ocr.c @@ -22,7 +22,6 @@ #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -129,13 +128,6 @@ static const AVFilterPad ocr_inputs[] = { }, }; -static const AVFilterPad ocr_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_ocr = { .name = "ocr", .description = NULL_IF_CONFIG_SMALL("Optical Character Recognition."), @@ -145,6 +137,6 @@ const AVFilter ff_vf_ocr = { .uninit = uninit, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(ocr_inputs), - FILTER_OUTPUTS(ocr_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), }; diff --git a/libavfilter/vf_overlay.c b/libavfilter/vf_overlay.c index e201e07c159..fa39abb23a1 100644 --- a/libavfilter/vf_overlay.c +++ b/libavfilter/vf_overlay.c @@ -55,7 +55,9 @@ static const char *const var_names[] = { "x", "y", "n", ///< number of frame +#if FF_API_FRAME_PKT "pos", ///< position in the file +#endif "t", ///< timestamp expressed in seconds NULL }; @@ -154,7 +156,7 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar static const enum AVPixelFormat alpha_pix_fmts[] = { AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P, - AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, + AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR, AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA, AV_PIX_FMT_GBRAP, AV_PIX_FMT_NONE }; @@ -202,6 +204,13 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_YUVA444P, AV_PIX_FMT_NONE }; + static const enum AVPixelFormat main_pix_fmts_yuv444p10[] = { + AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_NONE + }; + static const enum AVPixelFormat overlay_pix_fmts_yuv444p10[] = { + AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_NONE + }; + static const enum AVPixelFormat main_pix_fmts_gbrp[] = { AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP, AV_PIX_FMT_NONE }; @@ -246,6 +255,10 @@ static int query_formats(AVFilterContext *ctx) main_formats = main_pix_fmts_yuv444; overlay_formats = overlay_pix_fmts_yuv444; break; + case OVERLAY_FORMAT_YUV444P10: + main_formats = main_pix_fmts_yuv444p10; + overlay_formats = overlay_pix_fmts_yuv444p10; + break; case OVERLAY_FORMAT_RGB: main_formats = main_pix_fmts_rgb; overlay_formats = overlay_pix_fmts_rgb; @@ -290,7 +303,9 @@ static int config_input_overlay(AVFilterLink *inlink) s->var_values[VAR_Y] = NAN; s->var_values[VAR_N] = 0; s->var_values[VAR_T] = NAN; +#if FF_API_FRAME_PKT s->var_values[VAR_POS] = NAN; +#endif if ((ret = set_expr(&s->x_pexpr, s->x_expr, "x", ctx)) < 0 || (ret = set_expr(&s->y_pexpr, s->y_expr, "y", ctx)) < 0) @@ -755,6 +770,22 @@ static int blend_slice_yuva444(AVFilterContext *ctx, void *arg, int jobnr, int n return 0; } +static int blend_slice_yuv444p10(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + OverlayContext *s = ctx->priv; + ThreadData *td = arg; + blend_slice_yuv_16_10bits(ctx, td->dst, td->src, 0, 0, 0, s->x, s->y, 1, jobnr, nb_jobs); + return 0; +} + +static int blend_slice_yuva444p10(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + OverlayContext *s = ctx->priv; + ThreadData *td = arg; + blend_slice_yuv_16_10bits(ctx, td->dst, td->src, 0, 0, 1, s->x, s->y, 1, jobnr, nb_jobs); + return 0; +} + static int blend_slice_gbrp(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { OverlayContext *s = ctx->priv; @@ -898,6 +929,9 @@ static int config_input_main(AVFilterLink *inlink) case OVERLAY_FORMAT_YUV444: s->blend_slice = s->main_has_alpha ? blend_slice_yuva444 : blend_slice_yuv444; break; + case OVERLAY_FORMAT_YUV444P10: + s->blend_slice = s->main_has_alpha ? blend_slice_yuva444p10 : blend_slice_yuv444p10; + break; case OVERLAY_FORMAT_RGB: s->blend_slice = s->main_has_alpha ? blend_slice_rgba : blend_slice_rgb; break; @@ -921,6 +955,9 @@ static int config_input_main(AVFilterLink *inlink) case AV_PIX_FMT_YUVA444P: s->blend_slice = blend_slice_yuva444; break; + case AV_PIX_FMT_YUVA444P10: + s->blend_slice = blend_slice_yuva444p10; + break; case AV_PIX_FMT_ARGB: case AV_PIX_FMT_RGBA: case AV_PIX_FMT_BGRA: @@ -1007,12 +1044,18 @@ static int do_blend(FFFrameSync *fs) return ff_filter_frame(ctx->outputs[0], mainpic); if (s->eval_mode == EVAL_MODE_FRAME) { - int64_t pos = mainpic->pkt_pos; s->var_values[VAR_N] = inlink->frame_count_out; s->var_values[VAR_T] = mainpic->pts == AV_NOPTS_VALUE ? NAN : mainpic->pts * av_q2d(inlink->time_base); - s->var_values[VAR_POS] = pos == -1 ? NAN : pos; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS + { + int64_t pos = mainpic->pkt_pos; + s->var_values[VAR_POS] = pos == -1 ? NAN : pos; + } +FF_ENABLE_DEPRECATION_WARNINGS +#endif s->var_values[VAR_OVERLAY_W] = s->var_values[VAR_OW] = second->width; s->var_values[VAR_OVERLAY_H] = s->var_values[VAR_OH] = second->height; @@ -1020,8 +1063,8 @@ static int do_blend(FFFrameSync *fs) s->var_values[VAR_MAIN_H ] = s->var_values[VAR_MH] = mainpic->height; eval_expr(ctx); - av_log(ctx, AV_LOG_DEBUG, "n:%f t:%f pos:%f x:%f xi:%d y:%f yi:%d\n", - s->var_values[VAR_N], s->var_values[VAR_T], s->var_values[VAR_POS], + av_log(ctx, AV_LOG_DEBUG, "n:%f t:%f x:%f xi:%d y:%f yi:%d\n", + s->var_values[VAR_N], s->var_values[VAR_T], s->var_values[VAR_X], s->x, s->var_values[VAR_Y], s->y); } @@ -1074,6 +1117,7 @@ static const AVOption overlay_options[] = { { "yuv422", "", 0, AV_OPT_TYPE_CONST, {.i64=OVERLAY_FORMAT_YUV422}, .flags = FLAGS, .unit = "format" }, { "yuv422p10", "", 0, AV_OPT_TYPE_CONST, {.i64=OVERLAY_FORMAT_YUV422P10}, .flags = FLAGS, .unit = "format" }, { "yuv444", "", 0, AV_OPT_TYPE_CONST, {.i64=OVERLAY_FORMAT_YUV444}, .flags = FLAGS, .unit = "format" }, + { "yuv444p10", "", 0, AV_OPT_TYPE_CONST, {.i64=OVERLAY_FORMAT_YUV444P10}, .flags = FLAGS, .unit = "format" }, { "rgb", "", 0, AV_OPT_TYPE_CONST, {.i64=OVERLAY_FORMAT_RGB}, .flags = FLAGS, .unit = "format" }, { "gbrp", "", 0, AV_OPT_TYPE_CONST, {.i64=OVERLAY_FORMAT_GBRP}, .flags = FLAGS, .unit = "format" }, { "auto", "", 0, AV_OPT_TYPE_CONST, {.i64=OVERLAY_FORMAT_AUTO}, .flags = FLAGS, .unit = "format" }, diff --git a/libavfilter/vf_overlay.h b/libavfilter/vf_overlay.h index 30a1a7371ca..59749648c32 100644 --- a/libavfilter/vf_overlay.h +++ b/libavfilter/vf_overlay.h @@ -34,7 +34,9 @@ enum var_name { VAR_X, VAR_Y, VAR_N, +#if FF_API_FRAME_PKT VAR_POS, +#endif VAR_T, VAR_VARS_NB }; @@ -45,6 +47,7 @@ enum OverlayFormat { OVERLAY_FORMAT_YUV422, OVERLAY_FORMAT_YUV422P10, OVERLAY_FORMAT_YUV444, + OVERLAY_FORMAT_YUV444P10, OVERLAY_FORMAT_RGB, OVERLAY_FORMAT_GBRP, OVERLAY_FORMAT_AUTO, diff --git a/libavfilter/vf_overlay_cuda.c b/libavfilter/vf_overlay_cuda.c index 68c00405fb8..e9455ffcfff 100644 --- a/libavfilter/vf_overlay_cuda.c +++ b/libavfilter/vf_overlay_cuda.c @@ -32,6 +32,7 @@ #include "libavutil/eval.h" #include "avfilter.h" +#include "filters.h" #include "framesync.h" #include "internal.h" @@ -67,7 +68,9 @@ enum var_name { VAR_X, VAR_Y, VAR_N, +#if FF_API_FRAME_PKT VAR_POS, +#endif VAR_T, VAR_VARS_NB }; @@ -86,7 +89,9 @@ static const char *const var_names[] = { "x", "y", "n", ///< number of frame +#if FF_API_FRAME_PKT "pos", ///< position in the file +#endif "t", ///< timestamp expressed in seconds NULL }; @@ -237,8 +242,6 @@ static int overlay_cuda_blend(FFFrameSync *fs) AVFrame *input_main, *input_overlay; - int pos = 0; - ctx->cu_ctx = cuda_ctx; // read main and overlay frames from inputs @@ -252,7 +255,7 @@ static int overlay_cuda_blend(FFFrameSync *fs) if (!input_overlay) return ff_filter_frame(outlink, input_main); - ret = av_frame_make_writable(input_main); + ret = ff_inlink_make_frame_writable(inlink, &input_main); if (ret < 0) { av_frame_free(&input_main); return ret; @@ -267,11 +270,19 @@ static int overlay_cuda_blend(FFFrameSync *fs) } if (ctx->eval_mode == EVAL_MODE_FRAME) { - pos = input_main->pkt_pos; ctx->var_values[VAR_N] = inlink->frame_count_out; ctx->var_values[VAR_T] = input_main->pts == AV_NOPTS_VALUE ? NAN : input_main->pts * av_q2d(inlink->time_base); - ctx->var_values[VAR_POS] = pos == -1 ? NAN : pos; + +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS + { + int64_t pos = input_main->pkt_pos; + ctx->var_values[VAR_POS] = pos == -1 ? NAN : pos; + } +FF_ENABLE_DEPRECATION_WARNINGS +#endif + ctx->var_values[VAR_OVERLAY_W] = ctx->var_values[VAR_OW] = input_overlay->width; ctx->var_values[VAR_OVERLAY_H] = ctx->var_values[VAR_OH] = input_overlay->height; ctx->var_values[VAR_MAIN_W ] = ctx->var_values[VAR_MW] = input_main->width; @@ -279,8 +290,8 @@ static int overlay_cuda_blend(FFFrameSync *fs) eval_expr(avctx); - av_log(avctx, AV_LOG_DEBUG, "n:%f t:%f pos:%f x:%f xi:%d y:%f yi:%d\n", - ctx->var_values[VAR_N], ctx->var_values[VAR_T], ctx->var_values[VAR_POS], + av_log(avctx, AV_LOG_DEBUG, "n:%f t:%f x:%f xi:%d y:%f yi:%d\n", + ctx->var_values[VAR_N], ctx->var_values[VAR_T], ctx->var_values[VAR_X], ctx->x_position, ctx->var_values[VAR_Y], ctx->y_position); } @@ -354,7 +365,9 @@ static int config_input_overlay(AVFilterLink *inlink) s->var_values[VAR_Y] = NAN; s->var_values[VAR_N] = 0; s->var_values[VAR_T] = NAN; +#if FF_API_FRAME_PKT s->var_values[VAR_POS] = NAN; +#endif if ((ret = set_expr(&s->x_pexpr, s->x_expr, "x", ctx)) < 0 || (ret = set_expr(&s->y_pexpr, s->y_expr, "y", ctx)) < 0) diff --git a/libavfilter/vf_overlay_opencl.c b/libavfilter/vf_overlay_opencl.c index 1e3ad903e11..9beb09f05a1 100644 --- a/libavfilter/vf_overlay_opencl.c +++ b/libavfilter/vf_overlay_opencl.c @@ -51,7 +51,7 @@ static int overlay_opencl_load(AVFilterContext *avctx, { OverlayOpenCLContext *ctx = avctx->priv; cl_int cle; - const char *source = ff_opencl_source_overlay; + const char *source = ff_source_overlay_cl; const char *kernel; const AVPixFmtDescriptor *main_desc, *overlay_desc; int err, i, main_planes, overlay_planes; @@ -322,4 +322,5 @@ const AVFilter ff_vf_overlay_opencl = { FILTER_OUTPUTS(overlay_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_overlay_qsv.c b/libavfilter/vf_overlay_qsv.c index 1a2c1b1e96f..edc7f79c865 100644 --- a/libavfilter/vf_overlay_qsv.c +++ b/libavfilter/vf_overlay_qsv.c @@ -57,10 +57,9 @@ enum var_name { }; typedef struct QSVOverlayContext { - const AVClass *class; + QSVVPPContext qsv; FFFrameSync fs; - QSVVPPContext *qsv; QSVVPPParam qsv_param; mfxExtVPPComposite comp_conf; double var_values[VAR_VARS_NB]; @@ -230,14 +229,14 @@ static int config_overlay_input(AVFilterLink *inlink) static int process_frame(FFFrameSync *fs) { AVFilterContext *ctx = fs->parent; - QSVOverlayContext *s = fs->opaque; + QSVVPPContext *qsv = fs->opaque; AVFrame *frame = NULL; int ret = 0, i; for (i = 0; i < ctx->nb_inputs; i++) { ret = ff_framesync_get_frame(fs, i, &frame, 0); if (ret == 0) - ret = ff_qsvvpp_filter_frame(s->qsv, ctx->inputs[i], frame); + ret = ff_qsvvpp_filter_frame(qsv, ctx->inputs[i], frame); if (ret < 0 && ret != AVERROR(EAGAIN)) break; } @@ -301,7 +300,7 @@ static int config_output(AVFilterLink *outlink) if (ret < 0) return ret; - return ff_qsvvpp_create(ctx, &vpp->qsv, &vpp->qsv_param); + return ff_qsvvpp_init(ctx, &vpp->qsv_param); } /* @@ -350,7 +349,7 @@ static av_cold void overlay_qsv_uninit(AVFilterContext *ctx) { QSVOverlayContext *vpp = ctx->priv; - ff_qsvvpp_free(&vpp->qsv); + ff_qsvvpp_close(ctx); ff_framesync_uninit(&vpp->fs); av_freep(&vpp->comp_conf.InputStream); av_freep(&vpp->qsv_param.ext_buf); @@ -430,4 +429,5 @@ const AVFilter ff_vf_overlay_qsv = { FILTER_QUERY_FUNC(overlay_qsv_query_formats), .priv_class = &overlay_qsv_class, .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_overlay_vaapi.c b/libavfilter/vf_overlay_vaapi.c index 307f3cf7fc7..979bbaa5e98 100644 --- a/libavfilter/vf_overlay_vaapi.c +++ b/libavfilter/vf_overlay_vaapi.c @@ -17,16 +17,14 @@ */ #include -#include "libavutil/avassert.h" -#include "libavutil/mem.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" #include "framesync.h" -#include "formats.h" #include "internal.h" #include "vaapi_vpp.h" +#include "video.h" #include "libavutil/eval.h" enum var_name { @@ -159,106 +157,6 @@ static int overlay_vaapi_build_filter_params(AVFilterContext *avctx) return 0; } -static int overlay_vaapi_render_picture(AVFilterContext *avctx, - VAProcPipelineParameterBuffer *params, - VAProcPipelineParameterBuffer *subpic_params, - AVFrame *output_frame) -{ - VAAPIVPPContext *ctx = avctx->priv; - VASurfaceID output_surface; - VABufferID params_id; - VABufferID subpic_params_id; - VAStatus vas; - int err = 0; - - output_surface = (VASurfaceID)(uintptr_t)output_frame->data[3]; - - vas = vaBeginPicture(ctx->hwctx->display, - ctx->va_context, output_surface); - if (vas != VA_STATUS_SUCCESS) { - av_log(avctx, AV_LOG_ERROR, "Failed to attach new picture: " - "%d (%s).\n", vas, vaErrorStr(vas)); - err = AVERROR(EIO); - goto fail; - } - - vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context, - VAProcPipelineParameterBufferType, - sizeof(*params), 1, params, ¶ms_id); - if (vas != VA_STATUS_SUCCESS) { - av_log(avctx, AV_LOG_ERROR, "Failed to create parameter buffer: " - "%d (%s).\n", vas, vaErrorStr(vas)); - err = AVERROR(EIO); - goto fail_after_begin; - } - av_log(avctx, AV_LOG_DEBUG, "Pipeline parameter buffer is %#x.\n", - params_id); - - - vas = vaRenderPicture(ctx->hwctx->display, ctx->va_context, - ¶ms_id, 1); - if (vas != VA_STATUS_SUCCESS) { - av_log(avctx, AV_LOG_ERROR, "Failed to render parameter buffer: " - "%d (%s).\n", vas, vaErrorStr(vas)); - err = AVERROR(EIO); - goto fail_after_begin; - } - - if (subpic_params) { - vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context, - VAProcPipelineParameterBufferType, - sizeof(*subpic_params), 1, subpic_params, &subpic_params_id); - if (vas != VA_STATUS_SUCCESS) { - av_log(avctx, AV_LOG_ERROR, "Failed to create parameter buffer: " - "%d (%s).\n", vas, vaErrorStr(vas)); - err = AVERROR(EIO); - goto fail_after_begin; - } - av_log(avctx, AV_LOG_DEBUG, "Pipeline subpic parameter buffer is %#x.\n", - subpic_params_id); - - vas = vaRenderPicture(ctx->hwctx->display, ctx->va_context, - &subpic_params_id, 1); - if (vas != VA_STATUS_SUCCESS) { - av_log(avctx, AV_LOG_ERROR, "Failed to render subpic parameter buffer: " - "%d (%s).\n", vas, vaErrorStr(vas)); - err = AVERROR(EIO); - goto fail_after_begin; - } - } - - vas = vaEndPicture(ctx->hwctx->display, ctx->va_context); - if (vas != VA_STATUS_SUCCESS) { - av_log(avctx, AV_LOG_ERROR, "Failed to start picture processing: " - "%d (%s).\n", vas, vaErrorStr(vas)); - err = AVERROR(EIO); - goto fail_after_render; - } - - if (CONFIG_VAAPI_1 || ctx->hwctx->driver_quirks & - AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS) { - vas = vaDestroyBuffer(ctx->hwctx->display, params_id); - if (vas != VA_STATUS_SUCCESS) { - av_log(avctx, AV_LOG_ERROR, "Failed to free parameter buffer: " - "%d (%s).\n", vas, vaErrorStr(vas)); - // And ignore. - } - } - - return 0; - - // We want to make sure that if vaBeginPicture has been called, we also - // call vaRenderPicture and vaEndPicture. These calls may well fail or - // do something else nasty, but once we're in this failure case there - // isn't much else we can do. -fail_after_begin: - vaRenderPicture(ctx->hwctx->display, ctx->va_context, ¶ms_id, 1); -fail_after_render: - vaEndPicture(ctx->hwctx->display, ctx->va_context); -fail: - return err; -} - static int overlay_vaapi_blend(FFFrameSync *fs) { AVFilterContext *avctx = fs->parent; @@ -267,7 +165,7 @@ static int overlay_vaapi_blend(FFFrameSync *fs) VAAPIVPPContext *vpp_ctx = avctx->priv; AVFrame *input_main, *input_overlay; AVFrame *output; - VAProcPipelineParameterBuffer params, subpic_params; + VAProcPipelineParameterBuffer params[2]; VABlendState blend_state = { 0 }; /**< Blend State */ VARectangle overlay_region, output_region; int err; @@ -296,7 +194,7 @@ static int overlay_vaapi_blend(FFFrameSync *fs) if (err < 0) goto fail; - err = ff_vaapi_vpp_init_params(avctx, ¶ms, + err = ff_vaapi_vpp_init_params(avctx, ¶ms[0], input_main, output); if (err < 0) goto fail; @@ -308,11 +206,8 @@ static int overlay_vaapi_blend(FFFrameSync *fs) .height = output->height, }; - params.filters = &vpp_ctx->filter_buffers[0]; - params.num_filters = vpp_ctx->nb_filter_buffers; - - params.output_region = &output_region; - params.output_background_color = VAAPI_VPP_BACKGROUND_BLACK; + params[0].output_region = &output_region; + params[0].output_background_color = VAAPI_VPP_BACKGROUND_BLACK; if (input_overlay) { av_log(avctx, AV_LOG_DEBUG, "Filter overlay: %s, %ux%u (%"PRId64").\n", @@ -333,17 +228,18 @@ static int overlay_vaapi_blend(FFFrameSync *fs) "will crop the overlay image according based on the main image.\n"); } - memcpy(&subpic_params, ¶ms, sizeof(subpic_params)); + memcpy(¶ms[1], ¶ms[0], sizeof(params[0])); blend_state.flags = ctx->blend_flags; blend_state.global_alpha = ctx->blend_alpha; - subpic_params.blend_state = &blend_state; + params[1].blend_state = &blend_state; - subpic_params.surface = (VASurfaceID)(uintptr_t)input_overlay->data[3]; - subpic_params.output_region = &overlay_region; + params[1].surface = (VASurfaceID)(uintptr_t)input_overlay->data[3]; + params[1].surface_region = NULL; + params[1].output_region = &overlay_region; } - err = overlay_vaapi_render_picture(avctx, ¶ms, input_overlay ? &subpic_params : NULL, output); + err = ff_vaapi_vpp_render_pictures(avctx, params, input_overlay ? 2 : 1, output); if (err < 0) goto fail; diff --git a/libavfilter/vf_overlay_vulkan.c b/libavfilter/vf_overlay_vulkan.c index e87ee830007..c09de24142f 100644 --- a/libavfilter/vf_overlay_vulkan.c +++ b/libavfilter/vf_overlay_vulkan.c @@ -1,4 +1,6 @@ /* + * Copyright (c) Lynne + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -19,26 +21,27 @@ #include "libavutil/random_seed.h" #include "libavutil/opt.h" #include "vulkan_filter.h" +#include "vulkan_spirv.h" #include "internal.h" #include "framesync.h" - -#define CGROUPS (int [3]){ 32, 32, 1 } +#include "video.h" typedef struct OverlayVulkanContext { FFVulkanContext vkctx; + FFFrameSync fs; int initialized; + FFVulkanPipeline pl; + FFVkExecPool e; FFVkQueueFamilyCtx qf; - FFVkExecContext *exec; - FFVulkanPipeline *pl; - FFFrameSync fs; - FFVkBuffer params_buf; + FFVkSPIRVShader shd; + VkSampler sampler; - /* Shader updators, must be in the main filter struct */ - VkDescriptorImageInfo main_images[3]; - VkDescriptorImageInfo overlay_images[3]; - VkDescriptorImageInfo output_images[3]; - VkDescriptorBufferInfo params_desc; + /* Push constants / options */ + struct { + int32_t o_offset[2*3]; + int32_t o_size[2*3]; + } opts; int overlay_x; int overlay_y; @@ -80,279 +83,114 @@ static const char overlay_alpha[] = { static av_cold int init_filter(AVFilterContext *ctx) { int err; - FFVkSampler *sampler; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; OverlayVulkanContext *s = ctx->priv; FFVulkanContext *vkctx = &s->vkctx; const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); - - ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT, 0); - - sampler = ff_vk_init_sampler(vkctx, 1, VK_FILTER_NEAREST); - if (!sampler) + const int ialpha = av_pix_fmt_desc_get(s->vkctx.input_format)->flags & AV_PIX_FMT_FLAG_ALPHA; + const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(s->vkctx.output_format); + FFVkSPIRVShader *shd = &s->shd; + FFVkSPIRVCompiler *spv; + FFVulkanDescriptorSetBinding *desc; + + spv = ff_vk_spirv_init(); + if (!spv) { + av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); return AVERROR_EXTERNAL; - - s->pl = ff_vk_create_pipeline(vkctx, &s->qf); - if (!s->pl) - return AVERROR(ENOMEM); - - { /* Create the shader */ - const int ialpha = av_pix_fmt_desc_get(s->vkctx.input_format)->flags & AV_PIX_FMT_FLAG_ALPHA; - - FFVulkanDescriptorSetBinding desc_i[3] = { - { - .name = "main_img", - .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .dimensions = 2, - .elems = planes, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->main_images, - .sampler = sampler, - }, - { - .name = "overlay_img", - .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .dimensions = 2, - .elems = planes, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->overlay_images, - .sampler = sampler, - }, - { - .name = "output_img", - .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format), - .mem_quali = "writeonly", - .dimensions = 2, - .elems = planes, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->output_images, - }, - }; - - FFVulkanDescriptorSetBinding desc_b = { - .name = "params", - .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .mem_quali = "readonly", - .mem_layout = "std430", - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = &s->params_desc, - .buf_content = "ivec2 o_offset[3], o_size[3];", - }; - - FFVkSPIRVShader *shd = ff_vk_init_shader(s->pl, "overlay_compute", - VK_SHADER_STAGE_COMPUTE_BIT); - if (!shd) - return AVERROR(ENOMEM); - - ff_vk_set_compute_shader_sizes(shd, CGROUPS); - - RET(ff_vk_add_descriptor_set(vkctx, s->pl, shd, desc_i, FF_ARRAY_ELEMS(desc_i), 0)); /* set 0 */ - RET(ff_vk_add_descriptor_set(vkctx, s->pl, shd, &desc_b, 1, 0)); /* set 1 */ - - GLSLD( overlay_noalpha ); - GLSLD( overlay_alpha ); - GLSLC(0, void main() ); - GLSLC(0, { ); - GLSLC(1, ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); - GLSLF(1, int planes = %i; ,planes); - GLSLC(1, for (int i = 0; i < planes; i++) { ); - if (ialpha) - GLSLC(2, overlay_alpha_opaque(i, pos); ); - else - GLSLC(2, overlay_noalpha(i, pos); ); - GLSLC(1, } ); - GLSLC(0, } ); - - RET(ff_vk_compile_shader(vkctx, shd, "main")); - } - - RET(ff_vk_init_pipeline_layout(vkctx, s->pl)); - RET(ff_vk_init_compute_pipeline(vkctx, s->pl)); - - { /* Create and update buffer */ - const AVPixFmtDescriptor *desc; - - /* NOTE: std430 requires the same identical struct layout, padding and - * alignment as C, so we're allowed to do this, as this will map - * exactly to what the shader recieves */ - struct { - int32_t o_offset[2*3]; - int32_t o_size[2*3]; - } *par; - - err = ff_vk_create_buf(vkctx, &s->params_buf, - sizeof(*par), - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); - if (err) - return err; - - err = ff_vk_map_buffers(vkctx, &s->params_buf, (uint8_t **)&par, 1, 0); - if (err) - return err; - - desc = av_pix_fmt_desc_get(s->vkctx.output_format); - - par->o_offset[0] = s->overlay_x; - par->o_offset[1] = s->overlay_y; - par->o_offset[2] = par->o_offset[0] >> desc->log2_chroma_w; - par->o_offset[3] = par->o_offset[1] >> desc->log2_chroma_h; - par->o_offset[4] = par->o_offset[0] >> desc->log2_chroma_w; - par->o_offset[5] = par->o_offset[1] >> desc->log2_chroma_h; - - par->o_size[0] = s->overlay_w; - par->o_size[1] = s->overlay_h; - par->o_size[2] = par->o_size[0] >> desc->log2_chroma_w; - par->o_size[3] = par->o_size[1] >> desc->log2_chroma_h; - par->o_size[4] = par->o_size[0] >> desc->log2_chroma_w; - par->o_size[5] = par->o_size[1] >> desc->log2_chroma_h; - - err = ff_vk_unmap_buffers(vkctx, &s->params_buf, 1, 1); - if (err) - return err; - - s->params_desc.buffer = s->params_buf.buf; - s->params_desc.range = VK_WHOLE_SIZE; - - ff_vk_update_descriptor_set(vkctx, s->pl, 1); } - /* Execution context */ - RET(ff_vk_create_exec_ctx(vkctx, &s->exec, &s->qf)); + ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT); + RET(ff_vk_exec_pool_init(vkctx, &s->qf, &s->e, s->qf.nb_queues*4, 0, 0, 0, NULL)); + RET(ff_vk_init_sampler(vkctx, &s->sampler, 1, VK_FILTER_NEAREST)); + RET(ff_vk_shader_init(&s->pl, &s->shd, "overlay_compute", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); + + ff_vk_shader_set_compute_sizes(&s->shd, 32, 32, 1); + + GLSLC(0, layout(push_constant, std430) uniform pushConstants { ); + GLSLC(1, ivec2 o_offset[3]; ); + GLSLC(1, ivec2 o_size[3]; ); + GLSLC(0, }; ); + GLSLC(0, ); + + ff_vk_add_push_constant(&s->pl, 0, sizeof(s->opts), + VK_SHADER_STAGE_COMPUTE_BIT); + + desc = (FFVulkanDescriptorSetBinding []) { + { + .name = "main_img", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), + }, + { + .name = "overlay_img", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), + }, + { + .name = "output_img", + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format), + .mem_quali = "writeonly", + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + }, + }; + + RET(ff_vk_pipeline_descriptor_set_add(vkctx, &s->pl, shd, desc, 3, 0, 0)); + + GLSLD( overlay_noalpha ); + GLSLD( overlay_alpha ); + GLSLC(0, void main() ); + GLSLC(0, { ); + GLSLC(1, ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); + GLSLF(1, int planes = %i; ,planes); + GLSLC(1, for (int i = 0; i < planes; i++) { ); + if (ialpha) + GLSLC(2, overlay_alpha_opaque(i, pos); ); + else + GLSLC(2, overlay_noalpha(i, pos); ); + GLSLC(1, } ); + GLSLC(0, } ); + + RET(spv->compile_shader(spv, ctx, shd, &spv_data, &spv_len, "main", + &spv_opaque)); + RET(ff_vk_shader_create(vkctx, shd, spv_data, spv_len, "main")); + + RET(ff_vk_init_compute_pipeline(vkctx, &s->pl, shd)); + RET(ff_vk_exec_pipeline_register(vkctx, &s->e, &s->pl)); + + s->opts.o_offset[0] = s->overlay_x; + s->opts.o_offset[1] = s->overlay_y; + s->opts.o_offset[2] = s->opts.o_offset[0] >> pix_desc->log2_chroma_w; + s->opts.o_offset[3] = s->opts.o_offset[1] >> pix_desc->log2_chroma_h; + s->opts.o_offset[4] = s->opts.o_offset[0] >> pix_desc->log2_chroma_w; + s->opts.o_offset[5] = s->opts.o_offset[1] >> pix_desc->log2_chroma_h; + + s->opts.o_size[0] = s->overlay_w; + s->opts.o_size[1] = s->overlay_h; + s->opts.o_size[2] = s->opts.o_size[0] >> pix_desc->log2_chroma_w; + s->opts.o_size[3] = s->opts.o_size[1] >> pix_desc->log2_chroma_h; + s->opts.o_size[4] = s->opts.o_size[0] >> pix_desc->log2_chroma_w; + s->opts.o_size[5] = s->opts.o_size[1] >> pix_desc->log2_chroma_h; s->initialized = 1; - return 0; - fail: - return err; -} - -static int process_frames(AVFilterContext *avctx, AVFrame *out_f, - AVFrame *main_f, AVFrame *overlay_f) -{ - int err; - VkCommandBuffer cmd_buf; - OverlayVulkanContext *s = avctx->priv; - FFVulkanContext *vkctx = &s->vkctx; - FFVulkanFunctions *vk = &vkctx->vkfn; - int planes = av_pix_fmt_count_planes(s->vkctx.output_format); - - AVVkFrame *out = (AVVkFrame *)out_f->data[0]; - AVVkFrame *main = (AVVkFrame *)main_f->data[0]; - AVVkFrame *overlay = (AVVkFrame *)overlay_f->data[0]; - - AVHWFramesContext *main_fc = (AVHWFramesContext*)main_f->hw_frames_ctx->data; - AVHWFramesContext *overlay_fc = (AVHWFramesContext*)overlay_f->hw_frames_ctx->data; - - const VkFormat *output_formats = av_vkfmt_from_pixfmt(s->vkctx.output_format); - const VkFormat *main_sw_formats = av_vkfmt_from_pixfmt(main_fc->sw_format); - const VkFormat *overlay_sw_formats = av_vkfmt_from_pixfmt(overlay_fc->sw_format); - - /* Update descriptors and init the exec context */ - ff_vk_start_exec_recording(vkctx, s->exec); - cmd_buf = ff_vk_get_exec_buf(s->exec); - - for (int i = 0; i < planes; i++) { - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->main_images[i].imageView, main->img[i], - main_sw_formats[i], - ff_comp_identity_map)); - - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->overlay_images[i].imageView, overlay->img[i], - overlay_sw_formats[i], - ff_comp_identity_map)); - - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->output_images[i].imageView, out->img[i], - output_formats[i], - ff_comp_identity_map)); - - s->main_images[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - s->overlay_images[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - s->output_images[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; - } - - ff_vk_update_descriptor_set(vkctx, s->pl, 0); - - for (int i = 0; i < planes; i++) { - VkImageMemoryBarrier bar[3] = { - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .oldLayout = main->layout[i], - .newLayout = s->main_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = main->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .oldLayout = overlay->layout[i], - .newLayout = s->overlay_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = overlay->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .oldLayout = out->layout[i], - .newLayout = s->output_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = out->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - }; - - vk->CmdPipelineBarrier(cmd_buf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, - 0, NULL, 0, NULL, FF_ARRAY_ELEMS(bar), bar); - - main->layout[i] = bar[0].newLayout; - main->access[i] = bar[0].dstAccessMask; - - overlay->layout[i] = bar[1].newLayout; - overlay->access[i] = bar[1].dstAccessMask; - - out->layout[i] = bar[2].newLayout; - out->access[i] = bar[2].dstAccessMask; - } - - ff_vk_bind_pipeline_exec(vkctx, s->exec, s->pl); - - vk->CmdDispatch(cmd_buf, - FFALIGN(s->vkctx.output_width, CGROUPS[0])/CGROUPS[0], - FFALIGN(s->vkctx.output_height, CGROUPS[1])/CGROUPS[1], 1); - - ff_vk_add_exec_dep(vkctx, s->exec, main_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - ff_vk_add_exec_dep(vkctx, s->exec, overlay_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - ff_vk_add_exec_dep(vkctx, s->exec, out_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - - err = ff_vk_submit_exec_queue(vkctx, s->exec); - if (err) - return err; + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); + if (spv) + spv->uninit(&spv); - ff_vk_qf_rotate(&s->qf); - - return err; - -fail: - ff_vk_discard_exec_deps(s->exec); return err; } @@ -394,7 +232,9 @@ static int overlay_vulkan_blend(FFFrameSync *fs) goto fail; } - RET(process_frames(ctx, out, input_main, input_overlay)); + RET(ff_vk_filter_process_Nin(&s->vkctx, &s->e, &s->pl, + out, (AVFrame *[]){ input_main, input_overlay }, 2, + s->sampler, &s->opts, sizeof(s->opts))); err = av_frame_copy_props(out, input_main); if (err < 0) @@ -443,8 +283,17 @@ static av_cold int overlay_vulkan_init(AVFilterContext *avctx) static void overlay_vulkan_uninit(AVFilterContext *avctx) { OverlayVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + FFVulkanFunctions *vk = &vkctx->vkfn; + + ff_vk_exec_pool_free(vkctx, &s->e); + ff_vk_pipeline_free(vkctx, &s->pl); + ff_vk_shader_free(vkctx, &s->shd); + + if (s->sampler) + vk->DestroySampler(vkctx->hwctx->act_dev, s->sampler, + vkctx->hwctx->alloc); - ff_vk_free_buf(&s->vkctx, &s->params_buf); ff_vk_uninit(&s->vkctx); ff_framesync_uninit(&s->fs); @@ -494,4 +343,5 @@ const AVFilter ff_vf_overlay_vulkan = { FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), .priv_class = &overlay_vulkan_class, .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_owdenoise.c b/libavfilter/vf_owdenoise.c index bb99e8f33c4..fad85bfa8ef 100644 --- a/libavfilter/vf_owdenoise.c +++ b/libavfilter/vf_owdenoise.c @@ -39,6 +39,7 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" #include "internal.h" +#include "video.h" typedef struct OWDenoiseContext { const AVClass *class; @@ -354,20 +355,13 @@ static const AVFilterPad owdenoise_inputs[] = { }, }; -static const AVFilterPad owdenoise_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_owdenoise = { .name = "owdenoise", .description = NULL_IF_CONFIG_SMALL("Denoise using wavelets."), .priv_size = sizeof(OWDenoiseContext), .uninit = uninit, FILTER_INPUTS(owdenoise_inputs), - FILTER_OUTPUTS(owdenoise_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &owdenoise_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_pad_opencl.c b/libavfilter/vf_pad_opencl.c index 728e850f720..7c163a1d0e4 100644 --- a/libavfilter/vf_pad_opencl.c +++ b/libavfilter/vf_pad_opencl.c @@ -19,10 +19,9 @@ #include "libavutil/colorspace.h" #include "libavutil/eval.h" #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "opencl.h" #include "opencl_source.h" @@ -93,7 +92,7 @@ static int pad_opencl_init(AVFilterContext *avctx, AVFrame *input_frame) ctx->hsub = desc->log2_chroma_w; ctx->vsub = desc->log2_chroma_h; - err = ff_opencl_filter_load_program(avctx, &ff_opencl_source_pad, 1); + err = ff_opencl_filter_load_program(avctx, &ff_source_pad_cl, 1); if (err < 0) goto fail; @@ -391,5 +390,6 @@ const AVFilter ff_vf_pad_opencl = { FILTER_INPUTS(pad_opencl_inputs), FILTER_OUTPUTS(pad_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), - .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c index 4b69d3c63b2..8985b42bcc5 100644 --- a/libavfilter/vf_palettegen.c +++ b/libavfilter/vf_palettegen.c @@ -29,8 +29,10 @@ #include "libavutil/opt.h" #include "libavutil/intreadwrite.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" #include "palette.h" +#include "video.h" /* Reference a color and how much it's used */ struct color_ref { diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c index 944ff5c74d3..79d8fb2e512 100644 --- a/libavfilter/vf_paletteuse.c +++ b/libavfilter/vf_paletteuse.c @@ -31,9 +31,11 @@ #include "libavutil/qsort.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "framesync.h" #include "internal.h" #include "palette.h" +#include "video.h" enum dithering_mode { DITHERING_NONE, @@ -783,7 +785,7 @@ static int apply_palette(AVFilterLink *inlink, AVFrame *in, AVFrame **outf) av_frame_unref(s->last_out); if ((ret = av_frame_ref(s->last_in, in)) < 0 || (ret = av_frame_ref(s->last_out, out)) < 0 || - (ret = av_frame_make_writable(s->last_in)) < 0) { + (ret = ff_inlink_make_frame_writable(inlink, &s->last_in)) < 0) { av_frame_free(&out); *outf = NULL; return ret; diff --git a/libavfilter/vf_perspective.c b/libavfilter/vf_perspective.c index da720dcb545..f06c44b33ab 100644 --- a/libavfilter/vf_perspective.c +++ b/libavfilter/vf_perspective.c @@ -25,7 +25,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -495,13 +494,6 @@ static const AVFilterPad perspective_inputs[] = { }, }; -static const AVFilterPad perspective_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_perspective = { .name = "perspective", .description = NULL_IF_CONFIG_SMALL("Correct the perspective of video."), @@ -509,7 +501,7 @@ const AVFilter ff_vf_perspective = { .init = init, .uninit = uninit, FILTER_INPUTS(perspective_inputs), - FILTER_OUTPUTS(perspective_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &perspective_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_phase.c b/libavfilter/vf_phase.c index 1cb71e0e432..02dd08e0021 100644 --- a/libavfilter/vf_phase.c +++ b/libavfilter/vf_phase.c @@ -23,7 +23,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -218,13 +217,6 @@ static const AVFilterPad phase_inputs[] = { }, }; -static const AVFilterPad phase_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_phase = { .name = "phase", .description = NULL_IF_CONFIG_SMALL("Phase shift fields."), @@ -232,7 +224,7 @@ const AVFilter ff_vf_phase = { .priv_class = &phase_class, .uninit = uninit, FILTER_INPUTS(phase_inputs), - FILTER_OUTPUTS(phase_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_photosensitivity.c b/libavfilter/vf_photosensitivity.c index 1bb984cc93e..e7920b32425 100644 --- a/libavfilter/vf_photosensitivity.c +++ b/libavfilter/vf_photosensitivity.c @@ -20,12 +20,10 @@ #include -#include "libavutil/imgutils.h" #include "libavutil/opt.h" -#include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" +#include "filters.h" #include "internal.h" #include "video.h" @@ -243,7 +241,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) /* just duplicate the frame */ s->history[s->history_pos] = 0; /* frame was duplicated, thus, delta is zero */ } else { - res = av_frame_make_writable(s->last_frame_av); + res = ff_inlink_make_frame_writable(inlink, &s->last_frame_av); if (res) { av_frame_free(&in); return res; @@ -309,13 +307,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_photosensitivity = { .name = "photosensitivity", .description = NULL_IF_CONFIG_SMALL("Filter out photosensitive epilepsy seizure-inducing flashes."), @@ -323,6 +314,6 @@ const AVFilter ff_vf_photosensitivity = { .priv_class = &photosensitivity_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS(AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24), }; diff --git a/libavfilter/vf_pixdesctest.c b/libavfilter/vf_pixdesctest.c index 12815aea9c4..c2638d058f3 100644 --- a/libavfilter/vf_pixdesctest.c +++ b/libavfilter/vf_pixdesctest.c @@ -115,18 +115,11 @@ static const AVFilterPad avfilter_vf_pixdesctest_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_pixdesctest_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_pixdesctest = { .name = "pixdesctest", .description = NULL_IF_CONFIG_SMALL("Test pixel format definitions."), .priv_size = sizeof(PixdescTestContext), .uninit = uninit, FILTER_INPUTS(avfilter_vf_pixdesctest_inputs), - FILTER_OUTPUTS(avfilter_vf_pixdesctest_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), }; diff --git a/libavfilter/vf_pp.c b/libavfilter/vf_pp.c index 13a013a12da..aa37325a243 100644 --- a/libavfilter/vf_pp.c +++ b/libavfilter/vf_pp.c @@ -29,6 +29,7 @@ #include "internal.h" #include "qp_table.h" +#include "video.h" #include "libpostproc/postprocess.h" @@ -174,13 +175,6 @@ static const AVFilterPad pp_inputs[] = { }, }; -static const AVFilterPad pp_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_pp = { .name = "pp", .description = NULL_IF_CONFIG_SMALL("Filter video using libpostproc."), @@ -188,7 +182,7 @@ const AVFilter ff_vf_pp = { .init = pp_init, .uninit = pp_uninit, FILTER_INPUTS(pp_inputs), - FILTER_OUTPUTS(pp_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .process_command = pp_process_command, .priv_class = &pp_class, diff --git a/libavfilter/vf_pp7.c b/libavfilter/vf_pp7.c index b7c7cf9dde5..f44a5396ea8 100644 --- a/libavfilter/vf_pp7.c +++ b/libavfilter/vf_pp7.c @@ -34,6 +34,7 @@ #include "internal.h" #include "qp_table.h" #include "vf_pp7.h" +#include "video.h" enum mode { MODE_HARD, @@ -385,20 +386,13 @@ static const AVFilterPad pp7_inputs[] = { }, }; -static const AVFilterPad pp7_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_pp7 = { .name = "pp7", .description = NULL_IF_CONFIG_SMALL("Apply Postprocessing 7 filter."), .priv_size = sizeof(PP7Context), .uninit = uninit, FILTER_INPUTS(pp7_inputs), - FILTER_OUTPUTS(pp7_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &pp7_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, diff --git a/libavfilter/vf_procamp_vaapi.c b/libavfilter/vf_procamp_vaapi.c index 4a3b9d0766f..b535a36d33e 100644 --- a/libavfilter/vf_procamp_vaapi.c +++ b/libavfilter/vf_procamp_vaapi.c @@ -21,9 +21,9 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "vaapi_vpp.h" +#include "video.h" // ProcAmp Min/Max/Default Values #define BRIGHTNESS_MIN -100.0F @@ -136,6 +136,9 @@ static int procamp_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame av_get_pix_fmt_name(input_frame->format), input_frame->width, input_frame->height, input_frame->pts); + if (vpp_ctx->passthrough) + return ff_filter_frame(outlink, input_frame); + if (vpp_ctx->va_context == VA_INVALID_ID) return AVERROR(EINVAL); @@ -179,11 +182,18 @@ static int procamp_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame static av_cold int procamp_vaapi_init(AVFilterContext *avctx) { VAAPIVPPContext *vpp_ctx = avctx->priv; + ProcampVAAPIContext *ctx = avctx->priv; + float eps = 1.0e-10f; ff_vaapi_vpp_ctx_init(avctx); vpp_ctx->pipeline_uninit = ff_vaapi_vpp_pipeline_uninit; vpp_ctx->build_filter_params = procamp_vaapi_build_filter_params; vpp_ctx->output_format = AV_PIX_FMT_NONE; + if (fabs(ctx->saturation - SATURATION_DEFAULT) < eps && + fabs(ctx->bright - BRIGHTNESS_DEFAULT) < eps && + fabs(ctx->contrast - CONTRAST_DEFAULT) < eps && + fabs(ctx->hue - HUE_DEFAULT) < eps) + vpp_ctx->passthrough = 1; return 0; } diff --git a/libavfilter/vf_program_opencl.c b/libavfilter/vf_program_opencl.c index 55c1e6547c4..8a4881b38e4 100644 --- a/libavfilter/vf_program_opencl.c +++ b/libavfilter/vf_program_opencl.c @@ -362,7 +362,8 @@ const AVFilter ff_vf_program_opencl = { .description = NULL_IF_CONFIG_SMALL("Filter video using an OpenCL program"), .priv_size = sizeof(ProgramOpenCLContext), .priv_class = &program_opencl_class, - .flags = AVFILTER_FLAG_DYNAMIC_INPUTS, + .flags = AVFILTER_FLAG_DYNAMIC_INPUTS | + AVFILTER_FLAG_HWDEVICE, .preinit = &program_opencl_framesync_preinit, .init = &program_opencl_init, .uninit = &program_opencl_uninit, @@ -421,6 +422,7 @@ const AVFilter ff_vsrc_openclsrc = { FILTER_OUTPUTS(openclsrc_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; #endif diff --git a/libavfilter/vf_pseudocolor.c b/libavfilter/vf_pseudocolor.c index 89dc2f0901f..81b2e5b9bcc 100644 --- a/libavfilter/vf_pseudocolor.c +++ b/libavfilter/vf_pseudocolor.c @@ -25,7 +25,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -68,6 +67,12 @@ enum Curves { CIVIDIS, SOLAR, SPECTRAL, + COOL, + HEAT, + FIERY, + BLUES, + GREEN, + HELIX, NB_CURVES, }; @@ -87,6 +92,12 @@ enum Presets { PRESET_PREFERRED, PRESET_TOTAL, PRESET_SPECTRAL, + PRESET_COOL, + PRESET_HEAT, + PRESET_FIERY, + PRESET_BLUES, + PRESET_GREEN, + PRESET_HELIX, NB_PRESETS, }; @@ -96,6 +107,7 @@ typedef struct Curve { double coef[3][8]; double offset[3]; curve_fun fun[3]; + int yuv; } Curve; typedef struct Fill { @@ -137,6 +149,46 @@ static double solarfun(double x) return 0.5 * sin(x) + 0.5; } +static double coolfunu(double x) +{ + return 0.25 * sin(2.0 * x * M_PI - M_PI) + 0.5; +} + +static double coolfunv(double x) +{ + return 0.25 * sin(2.0 * x * M_PI) + 0.5; +} + +static double heatfunu(double x) +{ + return 0.25 * cos(2.0 * x * M_PI + M_PI) + 0.75; +} + +static double heatfunv(double x) +{ + return 0.25 * sin(2.0 * x * M_PI) + 0.5; +} + +static double fieryfunu(double x) +{ + return 0.75 - 0.25 * cos(2.0 * x * M_PI); +} + +static double fieryfunv(double x) +{ + return 0.25 + 0.25 * cos(2.0 * x * M_PI); +} + +static double helixfunu(double x) +{ + return 0.5 + 0.15 * sin(5.0 * x * M_PI + M_PI); +} + +static double helixfunv(double x) +{ + return 0.5 + 0.15 * cos(6.0 * x * M_PI + M_PI_2); +} + static const Curve curves[] = { [MAGMA] = {{ @@ -181,6 +233,54 @@ static const Curve curves[] = { 1.2526e-15, -1.2203e-12, 4.7013e-10, -8.9360e-08, 8.3839e-06, -3.6642e-04, 1.4784e-02, -9.8075e-03 }, { 1.4755e-15, -1.6765e-12, 7.3188e-10, -1.5522e-07, 1.6406e-05, -7.7883e-04, 1.4502e-02, 2.1597e-01 }, }, .fun = { limit, limit, limit }, }, + [COOL] = {{ + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + }, + .offset = { 0., 0., 0 }, + .yuv = 1, + .fun = { coolfunu, limit, coolfunv }, }, + [HEAT] = {{ + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + }, + .offset = { 0., 0., 0 }, + .yuv = 1, + .fun = { heatfunu, limit, heatfunv }, }, + [FIERY] = {{ + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + }, + .offset = { 0., 0., 0 }, + .yuv = 1, + .fun = { fieryfunu, limit, fieryfunv }, }, + [BLUES] = {{ + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + }, + .offset = { 0., 0., 0 }, + .yuv = 1, + .fun = { fieryfunv, limit, fieryfunu }, }, + [GREEN] = {{ + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + }, + .offset = { 0., 0., 0 }, + .yuv = 1, + .fun = { fieryfunv, limit, fieryfunv }, }, + [HELIX] = {{ + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + }, + .offset = { 0., 0., 0 }, + .yuv = 1, + .fun = { helixfunu, limit, helixfunv }, }, }; static const Preset presets[] = @@ -200,6 +300,12 @@ static const Preset presets[] = [PRESET_HIGHLIGHTS] = { 3, highlights_range, NULL, highlights_fills }, [PRESET_SOLAR] = { 1, &full_range, &curves[SOLAR], NULL }, [PRESET_SPECTRAL]= { 1, &full_range, &curves[SPECTRAL],NULL }, + [PRESET_COOL] = { 1, &full_range, &curves[COOL], NULL }, + [PRESET_HEAT] = { 1, &full_range, &curves[HEAT], NULL }, + [PRESET_FIERY] = { 1, &full_range, &curves[FIERY], NULL }, + [PRESET_BLUES] = { 1, &full_range, &curves[BLUES], NULL }, + [PRESET_GREEN] = { 1, &full_range, &curves[GREEN], NULL }, + [PRESET_HELIX] = { 1, &full_range, &curves[HELIX], NULL }, }; typedef struct PseudoColorContext { @@ -255,6 +361,12 @@ static const AVOption pseudocolor_options[] = { { "preferred", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_PREFERRED},.flags=FLAGS,"preset" }, { "total", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_TOTAL}, .flags=FLAGS, "preset" }, { "spectral", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_SPECTRAL},.flags = FLAGS, "preset" }, + { "cool", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_COOL}, .flags = FLAGS, "preset" }, + { "heat", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_HEAT}, .flags = FLAGS, "preset" }, + { "fiery", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_FIERY}, .flags = FLAGS, "preset" }, + { "blues", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_BLUES}, .flags = FLAGS, "preset" }, + { "green", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_GREEN}, .flags = FLAGS, "preset" }, + { "helix", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_HELIX}, .flags = FLAGS, "preset" }, { "opacity", "set pseudocolor opacity",OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=1}, 0, 1, .flags = FLAGS }, { NULL } }; @@ -571,6 +683,19 @@ static void pseudocolor_filter_16_11d(int max, int width, int height, ((0.50000*224.0/255.0) * r1 - (0.45415*224.0/255.0) * g1 - \ (0.04585*224.0/255.0) * b1 + max * 0.5) +#define Wr 0.2126 +#define Wb 0.0722 +#define Wg (1 - Wr - Wb) +#define Umax 0.436 +#define Vmax 0.615 + +#define YUV_BT709_TO_R(y, u, v, max) \ + ((y + v * (1 - Wr) / Vmax) * max) +#define YUV_BT709_TO_G(y, u, v, max) \ + ((y - (u * Wb * (1 - Wb) / (Umax * Wg)) - (v * Wr * (1 - Wr) / (Vmax * Wg))) * max) +#define YUV_BT709_TO_B(y, u, v, max) \ + ((y + u * (1 - Wb) / Umax) * max) + static double poly_eval(const double *const poly, double x, curve_fun fun) { double res = 0.; @@ -696,11 +821,17 @@ static int config_input(AVFilterLink *inlink) const double lf = i / (double)s->max * 256.; double r, g, b; - g = poly_eval(curve.coef[1], lf + curve.offset[1], curve.fun[1]) * s->max; - b = poly_eval(curve.coef[2], lf + curve.offset[2], curve.fun[2]) * s->max; - r = poly_eval(curve.coef[0], lf + curve.offset[0], curve.fun[0]) * s->max; + g = poly_eval(curve.coef[1], lf + curve.offset[1], curve.fun[1]); + b = poly_eval(curve.coef[2], lf + curve.offset[2], curve.fun[2]); + r = poly_eval(curve.coef[0], lf + curve.offset[0], curve.fun[0]); + + if (!curve.yuv || !rgb) { + g *= s->max; + b *= s->max; + r *= s->max; + } - if (!rgb) { + if (!rgb && !curve.yuv) { double y = RGB_TO_Y_BT709(r, g, b); double u = RGB_TO_U_BT709(r, g, b, s->max); double v = RGB_TO_V_BT709(r, g, b, s->max); @@ -708,6 +839,14 @@ static int config_input(AVFilterLink *inlink) r = v; g = y; b = u; + } else if (rgb && curve.yuv) { + double y = g; + double u = b - 0.5; + double v = r - 0.5; + + r = av_clipd(YUV_BT709_TO_R(y, u, v, s->max), 0, s->max); + g = av_clipd(YUV_BT709_TO_G(y, u, v, s->max), 0, s->max); + b = av_clipd(YUV_BT709_TO_B(y, u, v, s->max), 0, s->max); } s->lut[0][i] = g; @@ -900,13 +1039,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - static av_cold void uninit(AVFilterContext *ctx) { PseudoColorContext *s = ctx->priv; @@ -927,7 +1059,7 @@ const AVFilter ff_vf_pseudocolor = { .priv_class = &pseudocolor_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, diff --git a/libavfilter/vf_pullup.c b/libavfilter/vf_pullup.c index 054e3f90a91..5a00673c411 100644 --- a/libavfilter/vf_pullup.c +++ b/libavfilter/vf_pullup.c @@ -23,7 +23,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "vf_pullup.h" @@ -670,7 +669,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) (const uint8_t**)in->data, in->linesize, inlink->format, inlink->w, inlink->h); - p = in->interlaced_frame ? !in->top_field_first : 0; + p = (in->flags & AV_FRAME_FLAG_INTERLACED) ? + !(in->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) : 0; pullup_submit_field(s, b, p ); pullup_submit_field(s, b, p^1); @@ -748,13 +748,6 @@ static const AVFilterPad pullup_inputs[] = { }, }; -static const AVFilterPad pullup_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_pullup = { .name = "pullup", .description = NULL_IF_CONFIG_SMALL("Pullup from field sequence to frames."), @@ -762,6 +755,6 @@ const AVFilter ff_vf_pullup = { .priv_class = &pullup_class, .uninit = uninit, FILTER_INPUTS(pullup_inputs), - FILTER_OUTPUTS(pullup_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), }; diff --git a/libavfilter/vf_qp.c b/libavfilter/vf_qp.c index a771b51ae14..37e575447dd 100644 --- a/libavfilter/vf_qp.c +++ b/libavfilter/vf_qp.c @@ -20,13 +20,10 @@ #include #include "libavutil/eval.h" -#include "libavutil/imgutils.h" -#include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "libavutil/video_enc_params.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -184,19 +181,12 @@ static const AVFilterPad qp_inputs[] = { }, }; -static const AVFilterPad qp_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_qp = { .name = "qp", .description = NULL_IF_CONFIG_SMALL("Change video quantization parameters."), .priv_size = sizeof(QPContext), FILTER_INPUTS(qp_inputs), - FILTER_OUTPUTS(qp_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), .priv_class = &qp_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_METADATA_ONLY, diff --git a/libavfilter/vf_random.c b/libavfilter/vf_random.c index b3acdd1fcf6..8c7cf8b8066 100644 --- a/libavfilter/vf_random.c +++ b/libavfilter/vf_random.c @@ -22,9 +22,7 @@ #include "libavutil/opt.h" #include "libavutil/random_seed.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" -#include "video.h" #define MAX_FRAMES 512 @@ -37,6 +35,7 @@ typedef struct RandomContext { int nb_frames_filled; AVFrame *frames[MAX_FRAMES]; int64_t pts[MAX_FRAMES]; + int64_t duration[MAX_FRAMES]; int flush_idx; } RandomContext; @@ -74,6 +73,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) if (s->nb_frames_filled < s->nb_frames) { s->frames[s->nb_frames_filled] = in; + s->duration[s->nb_frames_filled] = in->duration; s->pts[s->nb_frames_filled++] = in->pts; return 0; } @@ -82,9 +82,12 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) out = s->frames[idx]; out->pts = s->pts[0]; + out->duration = s->duration[0]; memmove(&s->pts[0], &s->pts[1], (s->nb_frames - 1) * sizeof(s->pts[0])); + memmove(&s->duration[0], &s->duration[1], (s->nb_frames - 1) * sizeof(s->duration[0])); s->frames[idx] = in; s->pts[s->nb_frames - 1] = in->pts; + s->duration[s->nb_frames - 1] = in->duration; return ff_filter_frame(outlink, out); } @@ -104,6 +107,7 @@ static int request_frame(AVFilterLink *outlink) s->nb_frames--; goto next; } + out->duration = s->duration[s->flush_idx]; out->pts = s->pts[s->flush_idx++]; ret = ff_filter_frame(outlink, out); s->frames[s->nb_frames - 1] = NULL; diff --git a/libavfilter/vf_readeia608.c b/libavfilter/vf_readeia608.c index d85012564fc..50bf915765c 100644 --- a/libavfilter/vf_readeia608.c +++ b/libavfilter/vf_readeia608.c @@ -29,10 +29,8 @@ #include "libavutil/internal.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" -#include "libavutil/timestamp.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -539,20 +537,13 @@ static const AVFilterPad readeia608_inputs[] = { }, }; -static const AVFilterPad readeia608_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_readeia608 = { .name = "readeia608", .description = NULL_IF_CONFIG_SMALL("Read EIA-608 Closed Caption codes from input video and write them to frame metadata."), .priv_size = sizeof(ReadEIA608Context), .priv_class = &readeia608_class, FILTER_INPUTS(readeia608_inputs), - FILTER_OUTPUTS(readeia608_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .uninit = uninit, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | diff --git a/libavfilter/vf_readvitc.c b/libavfilter/vf_readvitc.c index d0fba234f74..cb63a4d127f 100644 --- a/libavfilter/vf_readvitc.c +++ b/libavfilter/vf_readvitc.c @@ -27,11 +27,10 @@ #include "libavutil/common.h" #include "libavutil/internal.h" #include "libavutil/opt.h" -#include "libavutil/pixdesc.h" #include "libavutil/timecode.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" +#include "video.h" #define LINE_DATA_SIZE 9 @@ -230,13 +229,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_readvitc = { .name = "readvitc", .description = NULL_IF_CONFIG_SMALL("Read vertical interval timecode and write it to frame metadata."), @@ -244,7 +236,7 @@ const AVFilter ff_vf_readvitc = { .priv_class = &readvitc_class, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .init = init, }; diff --git a/libavfilter/vf_remap_opencl.c b/libavfilter/vf_remap_opencl.c index f3f84bde64a..b4fe111c0ae 100644 --- a/libavfilter/vf_remap_opencl.c +++ b/libavfilter/vf_remap_opencl.c @@ -19,12 +19,10 @@ */ #include "libavutil/colorspace.h" -#include "libavutil/imgutils.h" #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "framesync.h" #include "internal.h" #include "opencl.h" @@ -73,7 +71,7 @@ static int remap_opencl_load(AVFilterContext *avctx, { RemapOpenCLContext *ctx = avctx->priv; cl_int cle; - const char *source = ff_opencl_source_remap; + const char *source = ff_source_remap_cl; const char *kernel = kernels[ctx->interp]; const AVPixFmtDescriptor *main_desc; int err, main_planes; @@ -351,4 +349,5 @@ const AVFilter ff_vf_remap_opencl = { FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .priv_class = &remap_opencl_class, .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_removegrain.c b/libavfilter/vf_removegrain.c index 0d4b070cd56..6e6e99198c9 100644 --- a/libavfilter/vf_removegrain.c +++ b/libavfilter/vf_removegrain.c @@ -26,7 +26,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/qsort.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "removegrain.h" #include "video.h" @@ -632,19 +631,12 @@ static const AVFilterPad removegrain_inputs[] = { }, }; -static const AVFilterPad removegrain_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_removegrain = { .name = "removegrain", .description = NULL_IF_CONFIG_SMALL("Remove grain."), .priv_size = sizeof(RemoveGrainContext), FILTER_INPUTS(removegrain_inputs), - FILTER_OUTPUTS(removegrain_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &removegrain_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_removelogo.c b/libavfilter/vf_removelogo.c index c323b5eff38..b2930543094 100644 --- a/libavfilter/vf_removelogo.c +++ b/libavfilter/vf_removelogo.c @@ -72,7 +72,6 @@ #include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "bbox.h" @@ -555,13 +554,6 @@ static const AVFilterPad removelogo_inputs[] = { }, }; -static const AVFilterPad removelogo_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_removelogo = { .name = "removelogo", .description = NULL_IF_CONFIG_SMALL("Remove a TV logo based on a mask image."), @@ -569,7 +561,7 @@ const AVFilter ff_vf_removelogo = { .init = init, .uninit = uninit, FILTER_INPUTS(removelogo_inputs), - FILTER_OUTPUTS(removelogo_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_YUV420P), .priv_class = &removelogo_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_repeatfields.c b/libavfilter/vf_repeatfields.c index 9c02c61631d..bf0edb5440b 100644 --- a/libavfilter/vf_repeatfields.c +++ b/libavfilter/vf_repeatfields.c @@ -20,7 +20,9 @@ #include "libavutil/imgutils.h" #include "avfilter.h" +#include "filters.h" #include "internal.h" +#include "video.h" typedef struct RepeatFieldsContext { const AVClass *class; @@ -75,28 +77,29 @@ static void update_pts(AVFilterLink *link, AVFrame *f, int64_t pts, int fields) f->pts = AV_NOPTS_VALUE; } -static int filter_frame(AVFilterLink *inlink, AVFrame *in) { +static int filter_frame(AVFilterLink *inlink, AVFrame *in) +{ AVFilterContext *ctx = inlink->dst; AVFilterLink *outlink = inlink->dst->outputs[0]; RepeatFieldsContext *s = ctx->priv; - AVFrame *out; int ret, i; int state = s->state; if (!s->frame) { s->frame = av_frame_clone(in); - if (!s->frame) + if (!s->frame) { + av_frame_free(&in); return AVERROR(ENOMEM); + } s->frame->pts = AV_NOPTS_VALUE; } - out = s->frame; - - if ((state == 0 && !in->top_field_first) || - (state == 1 && in->top_field_first)) { + if ((state == 0 && !(in->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)) || + (state == 1 && (in->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST))) { av_log(ctx, AV_LOG_WARNING, "Unexpected field flags: " "state=%d top_field_first=%d repeat_first_field=%d\n", - state, in->top_field_first, in->repeat_pict); + state, !!(in->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST), + in->repeat_pict); state ^= 1; } @@ -104,16 +107,22 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) { AVFrame *new; new = av_frame_clone(in); - if (!new) + if (!new) { + av_frame_free(&in); return AVERROR(ENOMEM); + } ret = ff_filter_frame(outlink, new); if (in->repeat_pict) { - av_frame_make_writable(out); - update_pts(outlink, out, in->pts, 2); + ret = ff_inlink_make_frame_writable(inlink, &s->frame); + if (ret < 0) { + av_frame_free(&in); + return ret; + } + update_pts(outlink, s->frame, in->pts, 2); for (i = 0; i < s->nb_planes; i++) { - av_image_copy_plane(out->data[i], out->linesize[i] * 2, + av_image_copy_plane(s->frame->data[i], s->frame->linesize[i] * 2, in->data[i], in->linesize[i] * 2, s->linesize[i], s->planeheight[i] / 2); } @@ -121,28 +130,38 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) { } } else { for (i = 0; i < s->nb_planes; i++) { - av_frame_make_writable(out); - av_image_copy_plane(out->data[i] + out->linesize[i], out->linesize[i] * 2, + ret = ff_inlink_make_frame_writable(inlink, &s->frame); + if (ret < 0) { + av_frame_free(&in); + return ret; + } + av_image_copy_plane(s->frame->data[i] + s->frame->linesize[i], s->frame->linesize[i] * 2, in->data[i] + in->linesize[i], in->linesize[i] * 2, s->linesize[i], s->planeheight[i] / 2); } - ret = ff_filter_frame(outlink, av_frame_clone(out)); + ret = ff_filter_frame(outlink, av_frame_clone(s->frame)); if (in->repeat_pict) { AVFrame *new; new = av_frame_clone(in); - if (!new) + if (!new) { + av_frame_free(&in); return AVERROR(ENOMEM); + } ret = ff_filter_frame(outlink, new); state = 0; } else { - av_frame_make_writable(out); - update_pts(outlink, out, in->pts, 1); + ret = ff_inlink_make_frame_writable(inlink, &s->frame); + if (ret < 0) { + av_frame_free(&in); + return ret; + } + update_pts(outlink, s->frame, in->pts, 1); for (i = 0; i < s->nb_planes; i++) { - av_image_copy_plane(out->data[i], out->linesize[i] * 2, + av_image_copy_plane(s->frame->data[i], s->frame->linesize[i] * 2, in->data[i], in->linesize[i] * 2, s->linesize[i], s->planeheight[i] / 2); } @@ -164,19 +183,12 @@ static const AVFilterPad repeatfields_inputs[] = { }, }; -static const AVFilterPad repeatfields_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_repeatfields = { .name = "repeatfields", .description = NULL_IF_CONFIG_SMALL("Hard repeat fields based on MPEG repeat field flag."), .priv_size = sizeof(RepeatFieldsContext), .uninit = uninit, FILTER_INPUTS(repeatfields_inputs), - FILTER_OUTPUTS(repeatfields_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts_eq), }; diff --git a/libavfilter/vf_sab.c b/libavfilter/vf_sab.c index a70d309633c..5e0687c9a20 100644 --- a/libavfilter/vf_sab.c +++ b/libavfilter/vf_sab.c @@ -28,8 +28,8 @@ #include "libswscale/swscale.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" +#include "video.h" typedef struct FilterParam { float radius; @@ -307,13 +307,6 @@ static const AVFilterPad sab_inputs[] = { }, }; -static const AVFilterPad sab_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_sab = { .name = "sab", .description = NULL_IF_CONFIG_SMALL("Apply shape adaptive blur."), @@ -321,7 +314,7 @@ const AVFilter ff_vf_sab = { .init = init, .uninit = uninit, FILTER_INPUTS(sab_inputs), - FILTER_OUTPUTS(sab_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &sab_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_scale.c b/libavfilter/vf_scale.c index 85047e35248..b0221e85386 100644 --- a/libavfilter/vf_scale.c +++ b/libavfilter/vf_scale.c @@ -56,7 +56,9 @@ static const char *const var_names[] = { "ovsub", "n", "t", +#if FF_API_FRAME_PKT "pos", +#endif "main_w", "main_h", "main_a", @@ -84,7 +86,9 @@ enum var_name { VAR_OVSUB, VAR_N, VAR_T, +#if FF_API_FRAME_PKT VAR_POS, +#endif VAR_S2R_MAIN_W, VAR_S2R_MAIN_H, VAR_S2R_MAIN_A, @@ -205,7 +209,9 @@ static int check_exprs(AVFilterContext *ctx) if (scale->eval_mode == EVAL_MODE_INIT && (vars_w[VAR_N] || vars_h[VAR_N] || vars_w[VAR_T] || vars_h[VAR_T] || +#if FF_API_FRAME_PKT vars_w[VAR_POS] || vars_h[VAR_POS] || +#endif vars_w[VAR_S2R_MAIN_N] || vars_h[VAR_S2R_MAIN_N] || vars_w[VAR_S2R_MAIN_T] || vars_h[VAR_S2R_MAIN_T] || vars_w[VAR_S2R_MAIN_POS] || vars_h[VAR_S2R_MAIN_POS]) ) { @@ -738,8 +744,16 @@ static int scale_frame(AVFilterLink *link, AVFrame *in, AVFrame **frame_out) if (scale->eval_mode == EVAL_MODE_FRAME && !frame_changed && ctx->filter != &ff_vf_scale2ref && - !(vars_w[VAR_N] || vars_w[VAR_T] || vars_w[VAR_POS]) && - !(vars_h[VAR_N] || vars_h[VAR_T] || vars_h[VAR_POS]) && + !(vars_w[VAR_N] || vars_w[VAR_T] +#if FF_API_FRAME_PKT + || vars_w[VAR_POS] +#endif + ) && + !(vars_h[VAR_N] || vars_h[VAR_T] +#if FF_API_FRAME_PKT + || vars_h[VAR_POS] +#endif + ) && scale->w && scale->h) goto scale; @@ -761,11 +775,19 @@ static int scale_frame(AVFilterLink *link, AVFrame *in, AVFrame **frame_out) if (ctx->filter == &ff_vf_scale2ref) { scale->var_values[VAR_S2R_MAIN_N] = link->frame_count_out; scale->var_values[VAR_S2R_MAIN_T] = TS2T(in->pts, link->time_base); +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS scale->var_values[VAR_S2R_MAIN_POS] = in->pkt_pos == -1 ? NAN : in->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } else { scale->var_values[VAR_N] = link->frame_count_out; scale->var_values[VAR_T] = TS2T(in->pts, link->time_base); +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS scale->var_values[VAR_POS] = in->pkt_pos == -1 ? NAN : in->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } link->dst->inputs[0]->format = in->format; @@ -862,7 +884,8 @@ static int scale_frame(AVFilterLink *link, AVFrame *in, AVFrame **frame_out) (int64_t)in->sample_aspect_ratio.den * outlink->w * link->h, INT_MAX); - if (scale->interlaced>0 || (scale->interlaced<0 && in->interlaced_frame)) { + if (scale->interlaced>0 || (scale->interlaced<0 && + (in->flags & AV_FRAME_FLAG_INTERLACED))) { ret = scale_field(scale, out, in, 0); if (ret >= 0) ret = scale_field(scale, out, in, 1); @@ -915,7 +938,11 @@ static int filter_frame_ref(AVFilterLink *link, AVFrame *in) if (scale->eval_mode == EVAL_MODE_FRAME) { scale->var_values[VAR_N] = link->frame_count_out; scale->var_values[VAR_T] = TS2T(in->pts, link->time_base); +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS scale->var_values[VAR_POS] = in->pkt_pos == -1 ? NAN : in->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } return ff_filter_frame(outlink, in); diff --git a/libavfilter/vf_scale_cuda.c b/libavfilter/vf_scale_cuda.c index 1c99befec8e..4cd21bbf259 100644 --- a/libavfilter/vf_scale_cuda.c +++ b/libavfilter/vf_scale_cuda.c @@ -22,9 +22,7 @@ #include #include -#include -#include "libavutil/avstring.h" #include "libavutil/common.h" #include "libavutil/hwcontext.h" #include "libavutil/hwcontext_cuda_internal.h" @@ -34,7 +32,6 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "scale_eval.h" #include "video.h" @@ -51,6 +48,8 @@ static const enum AVPixelFormat supported_formats[] = { AV_PIX_FMT_YUV444P16, AV_PIX_FMT_0RGB32, AV_PIX_FMT_0BGR32, + AV_PIX_FMT_RGB32, + AV_PIX_FMT_BGR32, }; #define DIV_UP(a, b) ( ((a) + (b) - 1) / (b) ) diff --git a/libavfilter/vf_scale_cuda.cu b/libavfilter/vf_scale_cuda.cu index c9c6cafdb6e..de06ba94331 100644 --- a/libavfilter/vf_scale_cuda.cu +++ b/libavfilter/vf_scale_cuda.cu @@ -853,9 +853,67 @@ struct Convert_yuv444p16le_yuv444p16le } }; -// bgr0->X - -struct Convert_bgr0_bgr0 +#define DEF_CONVERT_IDENTITY(fmt1, fmt2)\ + \ +struct Convert_##fmt1##_##fmt2 \ +{ \ + static const int in_bit_depth = 8; \ + typedef uchar4 in_T; \ + typedef uchar in_T_uv; \ + typedef uchar4 out_T; \ + typedef uchar out_T_uv; \ + \ + DEF_F(Convert, out_T) \ + { \ + DEFAULT_DST(0) = SUB_F(y, 0); \ + } \ + \ + DEF_F(Convert_uv, out_T_uv) \ + { \ + } \ +}; \ + +#define DEF_CONVERT_REORDER(fmt1, fmt2) \ + \ +struct Convert_##fmt1##_##fmt2 \ +{ \ + static const int in_bit_depth = 8; \ + typedef uchar4 in_T; \ + typedef uchar in_T_uv; \ + typedef uchar4 out_T; \ + typedef uchar out_T_uv; \ + \ + DEF_F(Convert, out_T) \ + { \ + uchar4 res = SUB_F(y, 0); \ + DEFAULT_DST(0) = make_uchar4( \ + res.z, \ + res.y, \ + res.x, \ + res.w \ + ); \ + } \ + \ + DEF_F(Convert_uv, out_T_uv) \ + { \ + } \ +}; \ + +#define DEF_CONVERT_RGB(fmt1, fmt2) \ + \ +DEF_CONVERT_IDENTITY(fmt1, fmt1) \ +DEF_CONVERT_REORDER (fmt1, fmt2) \ +DEF_CONVERT_REORDER (fmt2, fmt1) \ +DEF_CONVERT_IDENTITY(fmt2, fmt2) + +DEF_CONVERT_RGB(rgb0, bgr0) +DEF_CONVERT_RGB(rgba, bgra) +DEF_CONVERT_IDENTITY(rgba, rgb0) +DEF_CONVERT_IDENTITY(bgra, bgr0) +DEF_CONVERT_REORDER(rgba, bgr0) +DEF_CONVERT_REORDER(bgra, rgb0) + +struct Convert_bgr0_bgra { static const int in_bit_depth = 8; typedef uchar4 in_T; @@ -865,7 +923,13 @@ struct Convert_bgr0_bgr0 DEF_F(Convert, out_T) { - DEFAULT_DST(0) = SUB_F(y, 0); + uchar4 res = SUB_F(y, 0); + DEFAULT_DST(0) = make_uchar4( + res.x, + res.y, + res.z, + 1 + ); } DEF_F(Convert_uv, out_T_uv) @@ -873,7 +937,7 @@ struct Convert_bgr0_bgr0 } }; -struct Convert_bgr0_rgb0 +struct Convert_bgr0_rgba { static const int in_bit_depth = 8; typedef uchar4 in_T; @@ -888,7 +952,7 @@ struct Convert_bgr0_rgb0 res.z, res.y, res.x, - res.w + 1 ); } @@ -897,9 +961,7 @@ struct Convert_bgr0_rgb0 } }; -// rgb0->X - -struct Convert_rgb0_bgr0 +struct Convert_rgb0_bgra { static const int in_bit_depth = 8; typedef uchar4 in_T; @@ -914,7 +976,7 @@ struct Convert_rgb0_bgr0 res.z, res.y, res.x, - res.w + 1 ); } @@ -923,7 +985,7 @@ struct Convert_rgb0_bgr0 } }; -struct Convert_rgb0_rgb0 +struct Convert_rgb0_rgba { static const int in_bit_depth = 8; typedef uchar4 in_T; @@ -933,7 +995,13 @@ struct Convert_rgb0_rgb0 DEF_F(Convert, out_T) { - DEFAULT_DST(0) = SUB_F(y, 0); + uchar4 res = SUB_F(y, 0); + DEFAULT_DST(0) = make_uchar4( + res.x, + res.y, + res.z, + 1 + ); } DEF_F(Convert_uv, out_T_uv) @@ -1117,6 +1185,12 @@ extern "C" { NEAREST_KERNEL_RAW(p016le_ ## C) \ NEAREST_KERNEL_RAW(yuv444p16le_ ## C) +#define NEAREST_KERNELS_RGB(C) \ + NEAREST_KERNEL_RAW(rgb0_ ## C) \ + NEAREST_KERNEL_RAW(bgr0_ ## C) \ + NEAREST_KERNEL_RAW(rgba_ ## C) \ + NEAREST_KERNEL_RAW(bgra_ ## C) \ + NEAREST_KERNELS(yuv420p) NEAREST_KERNELS(nv12) NEAREST_KERNELS(yuv444p) @@ -1124,11 +1198,10 @@ NEAREST_KERNELS(p010le) NEAREST_KERNELS(p016le) NEAREST_KERNELS(yuv444p16le) -NEAREST_KERNEL_RAW(bgr0_bgr0) -NEAREST_KERNEL_RAW(rgb0_rgb0) -NEAREST_KERNEL_RAW(bgr0_rgb0) -NEAREST_KERNEL_RAW(rgb0_bgr0) - +NEAREST_KERNELS_RGB(rgb0) +NEAREST_KERNELS_RGB(bgr0) +NEAREST_KERNELS_RGB(rgba) +NEAREST_KERNELS_RGB(bgra) #define BILINEAR_KERNEL(C, S) \ __global__ void Subsample_Bilinear_##C##S( \ @@ -1152,6 +1225,12 @@ NEAREST_KERNEL_RAW(rgb0_bgr0) BILINEAR_KERNEL_RAW(p016le_ ## C) \ BILINEAR_KERNEL_RAW(yuv444p16le_ ## C) +#define BILINEAR_KERNELS_RGB(C) \ + BILINEAR_KERNEL_RAW(rgb0_ ## C) \ + BILINEAR_KERNEL_RAW(bgr0_ ## C) \ + BILINEAR_KERNEL_RAW(rgba_ ## C) \ + BILINEAR_KERNEL_RAW(bgra_ ## C) + BILINEAR_KERNELS(yuv420p) BILINEAR_KERNELS(nv12) BILINEAR_KERNELS(yuv444p) @@ -1159,10 +1238,10 @@ BILINEAR_KERNELS(p010le) BILINEAR_KERNELS(p016le) BILINEAR_KERNELS(yuv444p16le) -BILINEAR_KERNEL_RAW(bgr0_bgr0) -BILINEAR_KERNEL_RAW(rgb0_rgb0) -BILINEAR_KERNEL_RAW(bgr0_rgb0) -BILINEAR_KERNEL_RAW(rgb0_bgr0) +BILINEAR_KERNELS_RGB(rgb0) +BILINEAR_KERNELS_RGB(bgr0) +BILINEAR_KERNELS_RGB(rgba) +BILINEAR_KERNELS_RGB(bgra) #define BICUBIC_KERNEL(C, S) \ __global__ void Subsample_Bicubic_##C##S( \ @@ -1186,6 +1265,12 @@ BILINEAR_KERNEL_RAW(rgb0_bgr0) BICUBIC_KERNEL_RAW(p016le_ ## C) \ BICUBIC_KERNEL_RAW(yuv444p16le_ ## C) +#define BICUBIC_KERNELS_RGB(C) \ + BICUBIC_KERNEL_RAW(rgb0_ ## C) \ + BICUBIC_KERNEL_RAW(bgr0_ ## C) \ + BICUBIC_KERNEL_RAW(rgba_ ## C) \ + BICUBIC_KERNEL_RAW(bgra_ ## C) + BICUBIC_KERNELS(yuv420p) BICUBIC_KERNELS(nv12) BICUBIC_KERNELS(yuv444p) @@ -1193,11 +1278,10 @@ BICUBIC_KERNELS(p010le) BICUBIC_KERNELS(p016le) BICUBIC_KERNELS(yuv444p16le) -BICUBIC_KERNEL_RAW(bgr0_bgr0) -BICUBIC_KERNEL_RAW(rgb0_rgb0) -BICUBIC_KERNEL_RAW(bgr0_rgb0) -BICUBIC_KERNEL_RAW(rgb0_bgr0) - +BICUBIC_KERNELS_RGB(rgb0) +BICUBIC_KERNELS_RGB(bgr0) +BICUBIC_KERNELS_RGB(rgba) +BICUBIC_KERNELS_RGB(bgra) #define LANCZOS_KERNEL(C, S) \ __global__ void Subsample_Lanczos_##C##S( \ @@ -1221,6 +1305,12 @@ BICUBIC_KERNEL_RAW(rgb0_bgr0) LANCZOS_KERNEL_RAW(p016le_ ## C) \ LANCZOS_KERNEL_RAW(yuv444p16le_ ## C) +#define LANCZOS_KERNELS_RGB(C) \ + LANCZOS_KERNEL_RAW(rgb0_ ## C) \ + LANCZOS_KERNEL_RAW(bgr0_ ## C) \ + LANCZOS_KERNEL_RAW(rgba_ ## C) \ + LANCZOS_KERNEL_RAW(bgra_ ## C) + LANCZOS_KERNELS(yuv420p) LANCZOS_KERNELS(nv12) LANCZOS_KERNELS(yuv444p) @@ -1228,9 +1318,8 @@ LANCZOS_KERNELS(p010le) LANCZOS_KERNELS(p016le) LANCZOS_KERNELS(yuv444p16le) -LANCZOS_KERNEL_RAW(bgr0_bgr0) -LANCZOS_KERNEL_RAW(rgb0_rgb0) -LANCZOS_KERNEL_RAW(bgr0_rgb0) -LANCZOS_KERNEL_RAW(rgb0_bgr0) - +LANCZOS_KERNELS_RGB(rgb0) +LANCZOS_KERNELS_RGB(bgr0) +LANCZOS_KERNELS_RGB(rgba) +LANCZOS_KERNELS_RGB(bgra) } diff --git a/libavfilter/vf_scale_npp.c b/libavfilter/vf_scale_npp.c index 6ce82e53027..3cfead82bf9 100644 --- a/libavfilter/vf_scale_npp.c +++ b/libavfilter/vf_scale_npp.c @@ -84,7 +84,9 @@ static const char *const var_names[] = { "dar", "n", "t", +#if FF_API_FRAME_PKT "pos", +#endif "main_w", "main_h", "main_a", @@ -92,7 +94,9 @@ static const char *const var_names[] = { "main_dar", "mdar", "main_n", "main_t", +#if FF_API_FRAME_PKT "main_pos", +#endif NULL }; @@ -106,7 +110,9 @@ enum var_name { VAR_DAR, VAR_N, VAR_T, +#if FF_API_FRAME_PKT VAR_POS, +#endif VAR_S2R_MAIN_W, VAR_S2R_MAIN_H, VAR_S2R_MAIN_A, @@ -114,7 +120,9 @@ enum var_name { VAR_S2R_MAIN_DAR, VAR_S2R_MDAR, VAR_S2R_MAIN_N, VAR_S2R_MAIN_T, +#if FF_API_FRAME_PKT VAR_S2R_MAIN_POS, +#endif VARS_NB }; @@ -204,8 +212,11 @@ static int check_exprs(AVFilterContext* ctx) vars_w[VAR_S2R_MAIN_DAR] || vars_h[VAR_S2R_MAIN_DAR] || vars_w[VAR_S2R_MDAR] || vars_h[VAR_S2R_MDAR] || vars_w[VAR_S2R_MAIN_N] || vars_h[VAR_S2R_MAIN_N] || - vars_w[VAR_S2R_MAIN_T] || vars_h[VAR_S2R_MAIN_T] || - vars_w[VAR_S2R_MAIN_POS] || vars_h[VAR_S2R_MAIN_POS])) { + vars_w[VAR_S2R_MAIN_T] || vars_h[VAR_S2R_MAIN_T] +#if FF_API_FRAME_PKT + || vars_w[VAR_S2R_MAIN_POS] || vars_h[VAR_S2R_MAIN_POS] +#endif + )) { av_log(ctx, AV_LOG_ERROR, "Expressions with scale2ref_npp variables are not valid in scale_npp filter.\n"); return AVERROR(EINVAL); } @@ -213,11 +224,16 @@ static int check_exprs(AVFilterContext* ctx) if (scale->eval_mode == EVAL_MODE_INIT && (vars_w[VAR_N] || vars_h[VAR_N] || vars_w[VAR_T] || vars_h[VAR_T] || +#if FF_API_FRAME_PKT vars_w[VAR_POS] || vars_h[VAR_POS] || +#endif vars_w[VAR_S2R_MAIN_N] || vars_h[VAR_S2R_MAIN_N] || - vars_w[VAR_S2R_MAIN_T] || vars_h[VAR_S2R_MAIN_T] || - vars_w[VAR_S2R_MAIN_POS] || vars_h[VAR_S2R_MAIN_POS]) ) { - av_log(ctx, AV_LOG_ERROR, "Expressions with frame variables 'n', 't', 'pos' are not valid in init eval_mode.\n"); + vars_w[VAR_S2R_MAIN_T] || vars_h[VAR_S2R_MAIN_T] +#if FF_API_FRAME_PKT + || vars_w[VAR_S2R_MAIN_POS] || vars_h[VAR_S2R_MAIN_POS] +#endif + ) ) { + av_log(ctx, AV_LOG_ERROR, "Expressions with frame variables 'n', 't', are not valid in init eval_mode.\n"); return AVERROR(EINVAL); } @@ -790,9 +806,16 @@ static int nppscale_scale(AVFilterLink *link, AVFrame *out, AVFrame *in) av_expr_count_vars(s->h_pexpr, vars_h, VARS_NB); if (s->eval_mode == EVAL_MODE_FRAME && !frame_changed && ctx->filter != &ff_vf_scale2ref_npp && - !(vars_w[VAR_N] || vars_w[VAR_T] || vars_w[VAR_POS]) && - !(vars_h[VAR_N] || vars_h[VAR_T] || vars_h[VAR_POS]) && - s->w && s->h) + !(vars_w[VAR_N] || vars_w[VAR_T] +#if FF_API_FRAME_PKT + || vars_w[VAR_POS] +#endif + ) && + !(vars_h[VAR_N] || vars_h[VAR_T] +#if FF_API_FRAME_PKT + || vars_h[VAR_POS] +#endif + ) && s->w && s->h) goto scale; if (s->eval_mode == EVAL_MODE_INIT) { @@ -813,11 +836,19 @@ static int nppscale_scale(AVFilterLink *link, AVFrame *out, AVFrame *in) if (ctx->filter == &ff_vf_scale2ref_npp) { s->var_values[VAR_S2R_MAIN_N] = link->frame_count_out; s->var_values[VAR_S2R_MAIN_T] = TS2T(in->pts, link->time_base); +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS s->var_values[VAR_S2R_MAIN_POS] = in->pkt_pos == -1 ? NAN : in->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } else { s->var_values[VAR_N] = link->frame_count_out; s->var_values[VAR_T] = TS2T(in->pts, link->time_base); +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS s->var_values[VAR_POS] = in->pkt_pos == -1 ? NAN : in->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } link->format = in->format; @@ -932,7 +963,11 @@ static int nppscale_filter_frame_ref(AVFilterLink *link, AVFrame *in) if (scale->eval_mode == EVAL_MODE_FRAME) { scale->var_values[VAR_N] = link->frame_count_out; scale->var_values[VAR_T] = TS2T(in->pts, link->time_base); +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS scale->var_values[VAR_POS] = in->pkt_pos == -1 ? NAN : in->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } return ff_filter_frame(outlink, in); diff --git a/libavfilter/vf_scale_qsv.c b/libavfilter/vf_scale_qsv.c deleted file mode 100644 index a89a3ba6e6d..00000000000 --- a/libavfilter/vf_scale_qsv.c +++ /dev/null @@ -1,663 +0,0 @@ -/* - * 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 - * scale video filter - QSV - */ - -#include - -#include -#include - -#include "libavutil/avstring.h" -#include "libavutil/common.h" -#include "libavutil/eval.h" -#include "libavutil/hwcontext.h" -#include "libavutil/hwcontext_qsv.h" -#include "libavutil/internal.h" -#include "libavutil/mathematics.h" -#include "libavutil/opt.h" -#include "libavutil/pixdesc.h" -#include "libavutil/time.h" -#include "libavfilter/qsvvpp.h" - -#include "avfilter.h" -#include "formats.h" -#include "internal.h" -#include "video.h" - -static const char *const var_names[] = { - "in_w", "iw", - "in_h", "ih", - "out_w", "ow", - "out_h", "oh", - "a", "dar", - "sar", - NULL -}; - -enum var_name { - VAR_IN_W, VAR_IW, - VAR_IN_H, VAR_IH, - VAR_OUT_W, VAR_OW, - VAR_OUT_H, VAR_OH, - VAR_A, VAR_DAR, - VAR_SAR, - VARS_NB -}; - -#define MFX_IMPL_VIA_MASK(impl) (0x0f00 & (impl)) - -typedef struct QSVScaleContext { - const AVClass *class; - - /* a clone of the main session, used internally for scaling */ - mfxSession session; - - mfxMemId *mem_ids_in; - int nb_mem_ids_in; - - mfxMemId *mem_ids_out; - int nb_mem_ids_out; - - mfxFrameSurface1 **surface_ptrs_in; - int nb_surface_ptrs_in; - - mfxFrameSurface1 **surface_ptrs_out; - int nb_surface_ptrs_out; - -#if QSV_HAVE_OPAQUE - mfxExtOpaqueSurfaceAlloc opaque_alloc; -#endif - - mfxExtVPPScaling scale_conf; - int mode; - - mfxExtBuffer *ext_buffers[2]; - int num_ext_buf; - - int shift_width, shift_height; - - /** - * New dimensions. Special values are: - * 0 = original width/height - * -1 = keep original aspect - */ - int w, h; - - /** - * Output sw format. AV_PIX_FMT_NONE for no conversion. - */ - enum AVPixelFormat format; - - char *w_expr; ///< width expression string - char *h_expr; ///< height expression string - char *format_str; -} QSVScaleContext; - -static av_cold int qsvscale_init(AVFilterContext *ctx) -{ - QSVScaleContext *s = ctx->priv; - - if (!strcmp(s->format_str, "same")) { - s->format = AV_PIX_FMT_NONE; - } else { - s->format = av_get_pix_fmt(s->format_str); - if (s->format == AV_PIX_FMT_NONE) { - av_log(ctx, AV_LOG_ERROR, "Unrecognized pixel format: %s\n", s->format_str); - return AVERROR(EINVAL); - } - } - - return 0; -} - -static av_cold void qsvscale_uninit(AVFilterContext *ctx) -{ - QSVScaleContext *s = ctx->priv; - - if (s->session) { - MFXClose(s->session); - s->session = NULL; - } - - av_freep(&s->mem_ids_in); - av_freep(&s->mem_ids_out); - s->nb_mem_ids_in = 0; - s->nb_mem_ids_out = 0; - - av_freep(&s->surface_ptrs_in); - av_freep(&s->surface_ptrs_out); - s->nb_surface_ptrs_in = 0; - s->nb_surface_ptrs_out = 0; -} - -static int init_out_pool(AVFilterContext *ctx, - int out_width, int out_height) -{ - QSVScaleContext *s = ctx->priv; - AVFilterLink *outlink = ctx->outputs[0]; - - AVHWFramesContext *in_frames_ctx; - AVHWFramesContext *out_frames_ctx; - AVQSVFramesContext *in_frames_hwctx; - AVQSVFramesContext *out_frames_hwctx; - enum AVPixelFormat in_format; - enum AVPixelFormat out_format; - int i, ret; - - /* check that we have a hw context */ - if (!ctx->inputs[0]->hw_frames_ctx) { - av_log(ctx, AV_LOG_ERROR, "No hw context provided on input\n"); - return AVERROR(EINVAL); - } - in_frames_ctx = (AVHWFramesContext*)ctx->inputs[0]->hw_frames_ctx->data; - in_frames_hwctx = in_frames_ctx->hwctx; - - in_format = in_frames_ctx->sw_format; - out_format = (s->format == AV_PIX_FMT_NONE) ? in_format : s->format; - - outlink->hw_frames_ctx = av_hwframe_ctx_alloc(in_frames_ctx->device_ref); - if (!outlink->hw_frames_ctx) - return AVERROR(ENOMEM); - out_frames_ctx = (AVHWFramesContext*)outlink->hw_frames_ctx->data; - out_frames_hwctx = out_frames_ctx->hwctx; - - out_frames_ctx->format = AV_PIX_FMT_QSV; - out_frames_ctx->width = FFALIGN(out_width, 16); - out_frames_ctx->height = FFALIGN(out_height, 16); - out_frames_ctx->sw_format = out_format; - out_frames_ctx->initial_pool_size = 4; - - out_frames_hwctx->frame_type = in_frames_hwctx->frame_type | MFX_MEMTYPE_FROM_VPPOUT; - - ret = ff_filter_init_hw_frames(ctx, outlink, 32); - if (ret < 0) - return ret; - - ret = av_hwframe_ctx_init(outlink->hw_frames_ctx); - if (ret < 0) - return ret; - - for (i = 0; i < out_frames_hwctx->nb_surfaces; i++) { - mfxFrameInfo *info = &out_frames_hwctx->surfaces[i].Info; - info->CropW = out_width; - info->CropH = out_height; - } - - return 0; -} - -static mfxStatus frame_alloc(mfxHDL pthis, mfxFrameAllocRequest *req, - mfxFrameAllocResponse *resp) -{ - AVFilterContext *ctx = pthis; - QSVScaleContext *s = ctx->priv; - - if (!(req->Type & MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET) || - !(req->Type & (MFX_MEMTYPE_FROM_VPPIN | MFX_MEMTYPE_FROM_VPPOUT)) || - !(req->Type & MFX_MEMTYPE_EXTERNAL_FRAME)) - return MFX_ERR_UNSUPPORTED; - - if (req->Type & MFX_MEMTYPE_FROM_VPPIN) { - resp->mids = s->mem_ids_in; - resp->NumFrameActual = s->nb_mem_ids_in; - } else { - resp->mids = s->mem_ids_out; - resp->NumFrameActual = s->nb_mem_ids_out; - } - - return MFX_ERR_NONE; -} - -static mfxStatus frame_free(mfxHDL pthis, mfxFrameAllocResponse *resp) -{ - return MFX_ERR_NONE; -} - -static mfxStatus frame_lock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) -{ - return MFX_ERR_UNSUPPORTED; -} - -static mfxStatus frame_unlock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) -{ - return MFX_ERR_UNSUPPORTED; -} - -static mfxStatus frame_get_hdl(mfxHDL pthis, mfxMemId mid, mfxHDL *hdl) -{ - mfxHDLPair *pair_dst = (mfxHDLPair*)hdl; - mfxHDLPair *pair_src = (mfxHDLPair*)mid; - - pair_dst->first = pair_src->first; - - if (pair_src->second != (mfxMemId)MFX_INFINITE) - pair_dst->second = pair_src->second; - return MFX_ERR_NONE; -} - -static int init_out_session(AVFilterContext *ctx) -{ - - QSVScaleContext *s = ctx->priv; - AVHWFramesContext *in_frames_ctx = (AVHWFramesContext*)ctx->inputs[0]->hw_frames_ctx->data; - AVHWFramesContext *out_frames_ctx = (AVHWFramesContext*)ctx->outputs[0]->hw_frames_ctx->data; - AVQSVFramesContext *in_frames_hwctx = in_frames_ctx->hwctx; - AVQSVFramesContext *out_frames_hwctx = out_frames_ctx->hwctx; - AVQSVDeviceContext *device_hwctx = in_frames_ctx->device_ctx->hwctx; - - int opaque = 0; - - mfxHDL handle = NULL; - mfxHandleType handle_type; - mfxVersion ver; - mfxIMPL impl; - mfxVideoParam par; - mfxStatus err; - int i, ret; - -#if QSV_HAVE_OPAQUE - opaque = !!(in_frames_hwctx->frame_type & MFX_MEMTYPE_OPAQUE_FRAME); -#endif - s->num_ext_buf = 0; - - /* extract the properties of the "master" session given to us */ - err = MFXQueryIMPL(device_hwctx->session, &impl); - if (err == MFX_ERR_NONE) - err = MFXQueryVersion(device_hwctx->session, &ver); - if (err != MFX_ERR_NONE) { - av_log(ctx, AV_LOG_ERROR, "Error querying the session attributes\n"); - return AVERROR_UNKNOWN; - } - - if (MFX_IMPL_VIA_VAAPI == MFX_IMPL_VIA_MASK(impl)) { - handle_type = MFX_HANDLE_VA_DISPLAY; - } else if (MFX_IMPL_VIA_D3D11 == MFX_IMPL_VIA_MASK(impl)) { - handle_type = MFX_HANDLE_D3D11_DEVICE; - } else if (MFX_IMPL_VIA_D3D9 == MFX_IMPL_VIA_MASK(impl)) { - handle_type = MFX_HANDLE_D3D9_DEVICE_MANAGER; - } else { - av_log(ctx, AV_LOG_ERROR, "Error unsupported handle type\n"); - return AVERROR_UNKNOWN; - } - - err = MFXVideoCORE_GetHandle(device_hwctx->session, handle_type, &handle); - if (err < 0) - return ff_qsvvpp_print_error(ctx, err, "Error getting the session handle"); - else if (err > 0) { - ff_qsvvpp_print_warning(ctx, err, "Warning in getting the session handle"); - return AVERROR_UNKNOWN; - } - - /* create a "slave" session with those same properties, to be used for - * actual scaling */ - ret = ff_qsvvpp_create_mfx_session(ctx, device_hwctx->loader, impl, &ver, - &s->session); - if (ret) - return ret; - - if (handle) { - err = MFXVideoCORE_SetHandle(s->session, handle_type, handle); - if (err != MFX_ERR_NONE) - return AVERROR_UNKNOWN; - } - - if (QSV_RUNTIME_VERSION_ATLEAST(ver, 1, 25)) { - err = MFXJoinSession(device_hwctx->session, s->session); - if (err != MFX_ERR_NONE) - return AVERROR_UNKNOWN; - } - - memset(&par, 0, sizeof(par)); - - if (!opaque) { - mfxFrameAllocator frame_allocator = { - .pthis = ctx, - .Alloc = frame_alloc, - .Lock = frame_lock, - .Unlock = frame_unlock, - .GetHDL = frame_get_hdl, - .Free = frame_free, - }; - - s->mem_ids_in = av_calloc(in_frames_hwctx->nb_surfaces, - sizeof(*s->mem_ids_in)); - if (!s->mem_ids_in) - return AVERROR(ENOMEM); - for (i = 0; i < in_frames_hwctx->nb_surfaces; i++) - s->mem_ids_in[i] = in_frames_hwctx->surfaces[i].Data.MemId; - s->nb_mem_ids_in = in_frames_hwctx->nb_surfaces; - - s->mem_ids_out = av_calloc(out_frames_hwctx->nb_surfaces, - sizeof(*s->mem_ids_out)); - if (!s->mem_ids_out) - return AVERROR(ENOMEM); - for (i = 0; i < out_frames_hwctx->nb_surfaces; i++) - s->mem_ids_out[i] = out_frames_hwctx->surfaces[i].Data.MemId; - s->nb_mem_ids_out = out_frames_hwctx->nb_surfaces; - - err = MFXVideoCORE_SetFrameAllocator(s->session, &frame_allocator); - if (err != MFX_ERR_NONE) - return AVERROR_UNKNOWN; - - par.IOPattern = MFX_IOPATTERN_IN_VIDEO_MEMORY | MFX_IOPATTERN_OUT_VIDEO_MEMORY; - } -#if QSV_HAVE_OPAQUE - else { - s->surface_ptrs_in = av_calloc(in_frames_hwctx->nb_surfaces, - sizeof(*s->surface_ptrs_in)); - if (!s->surface_ptrs_in) - return AVERROR(ENOMEM); - for (i = 0; i < in_frames_hwctx->nb_surfaces; i++) - s->surface_ptrs_in[i] = in_frames_hwctx->surfaces + i; - s->nb_surface_ptrs_in = in_frames_hwctx->nb_surfaces; - - s->surface_ptrs_out = av_calloc(out_frames_hwctx->nb_surfaces, - sizeof(*s->surface_ptrs_out)); - if (!s->surface_ptrs_out) - return AVERROR(ENOMEM); - for (i = 0; i < out_frames_hwctx->nb_surfaces; i++) - s->surface_ptrs_out[i] = out_frames_hwctx->surfaces + i; - s->nb_surface_ptrs_out = out_frames_hwctx->nb_surfaces; - - s->opaque_alloc.In.Surfaces = s->surface_ptrs_in; - s->opaque_alloc.In.NumSurface = s->nb_surface_ptrs_in; - s->opaque_alloc.In.Type = in_frames_hwctx->frame_type; - - s->opaque_alloc.Out.Surfaces = s->surface_ptrs_out; - s->opaque_alloc.Out.NumSurface = s->nb_surface_ptrs_out; - s->opaque_alloc.Out.Type = out_frames_hwctx->frame_type; - - s->opaque_alloc.Header.BufferId = MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION; - s->opaque_alloc.Header.BufferSz = sizeof(s->opaque_alloc); - - s->ext_buffers[s->num_ext_buf++] = (mfxExtBuffer*)&s->opaque_alloc; - - par.IOPattern = MFX_IOPATTERN_IN_OPAQUE_MEMORY | MFX_IOPATTERN_OUT_OPAQUE_MEMORY; - } -#endif - - memset(&s->scale_conf, 0, sizeof(mfxExtVPPScaling)); - s->scale_conf.Header.BufferId = MFX_EXTBUFF_VPP_SCALING; - s->scale_conf.Header.BufferSz = sizeof(mfxExtVPPScaling); - s->scale_conf.ScalingMode = s->mode; - s->ext_buffers[s->num_ext_buf++] = (mfxExtBuffer*)&s->scale_conf; - av_log(ctx, AV_LOG_VERBOSE, "Scaling mode: %d\n", s->mode); - - par.ExtParam = s->ext_buffers; - par.NumExtParam = s->num_ext_buf; - - par.AsyncDepth = 1; // TODO async - - par.vpp.In = in_frames_hwctx->surfaces[0].Info; - par.vpp.Out = out_frames_hwctx->surfaces[0].Info; - - /* Apparently VPP requires the frame rate to be set to some value, otherwise - * init will fail (probably for the framerate conversion filter). Since we - * are only doing scaling here, we just invent an arbitrary - * value */ - par.vpp.In.FrameRateExtN = 25; - par.vpp.In.FrameRateExtD = 1; - par.vpp.Out.FrameRateExtN = 25; - par.vpp.Out.FrameRateExtD = 1; - - /* Print input memory mode */ - ff_qsvvpp_print_iopattern(ctx, par.IOPattern & 0x0F, "VPP"); - /* Print output memory mode */ - ff_qsvvpp_print_iopattern(ctx, par.IOPattern & 0xF0, "VPP"); - err = MFXVideoVPP_Init(s->session, &par); - if (err < 0) - return ff_qsvvpp_print_error(ctx, err, - "Error opening the VPP for scaling"); - else if (err > 0) { - ff_qsvvpp_print_warning(ctx, err, - "Warning in VPP initialization"); - return AVERROR_UNKNOWN; - } - - return 0; -} - -static int init_scale_session(AVFilterContext *ctx, int in_width, int in_height, - int out_width, int out_height) -{ - int ret; - - qsvscale_uninit(ctx); - - ret = init_out_pool(ctx, out_width, out_height); - if (ret < 0) - return ret; - - ret = init_out_session(ctx); - if (ret < 0) - return ret; - - return 0; -} - -static int qsvscale_config_props(AVFilterLink *outlink) -{ - AVFilterContext *ctx = outlink->src; - AVFilterLink *inlink = outlink->src->inputs[0]; - QSVScaleContext *s = ctx->priv; - int64_t w, h; - double var_values[VARS_NB], res; - char *expr; - int ret; - - var_values[VAR_IN_W] = var_values[VAR_IW] = inlink->w; - var_values[VAR_IN_H] = var_values[VAR_IH] = inlink->h; - var_values[VAR_OUT_W] = var_values[VAR_OW] = NAN; - var_values[VAR_OUT_H] = var_values[VAR_OH] = NAN; - var_values[VAR_A] = (double) inlink->w / inlink->h; - var_values[VAR_SAR] = inlink->sample_aspect_ratio.num ? - (double) inlink->sample_aspect_ratio.num / inlink->sample_aspect_ratio.den : 1; - var_values[VAR_DAR] = var_values[VAR_A] * var_values[VAR_SAR]; - - /* evaluate width and height */ - av_expr_parse_and_eval(&res, (expr = s->w_expr), - var_names, var_values, - NULL, NULL, NULL, NULL, NULL, 0, ctx); - s->w = var_values[VAR_OUT_W] = var_values[VAR_OW] = res; - if ((ret = av_expr_parse_and_eval(&res, (expr = s->h_expr), - var_names, var_values, - NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0) - goto fail; - s->h = var_values[VAR_OUT_H] = var_values[VAR_OH] = res; - /* evaluate again the width, as it may depend on the output height */ - if ((ret = av_expr_parse_and_eval(&res, (expr = s->w_expr), - var_names, var_values, - NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0) - goto fail; - s->w = res; - - w = s->w; - h = s->h; - - /* sanity check params */ - if (w < -1 || h < -1) { - av_log(ctx, AV_LOG_ERROR, "Size values less than -1 are not acceptable.\n"); - return AVERROR(EINVAL); - } - if (w == -1 && h == -1) - s->w = s->h = 0; - - if (!(w = s->w)) - w = inlink->w; - if (!(h = s->h)) - h = inlink->h; - if (w == -1) - w = av_rescale(h, inlink->w, inlink->h); - if (h == -1) - h = av_rescale(w, inlink->h, inlink->w); - - if (w > INT_MAX || h > INT_MAX || - (h * inlink->w) > INT_MAX || - (w * inlink->h) > INT_MAX) - av_log(ctx, AV_LOG_ERROR, "Rescaled value for width or height is too big.\n"); - - outlink->w = w; - outlink->h = h; - - ret = init_scale_session(ctx, inlink->w, inlink->h, w, h); - if (ret < 0) - return ret; - - av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d -> w:%d h:%d\n", - inlink->w, inlink->h, outlink->w, outlink->h); - - if (inlink->sample_aspect_ratio.num) - outlink->sample_aspect_ratio = av_mul_q((AVRational){outlink->h*inlink->w, - outlink->w*inlink->h}, - inlink->sample_aspect_ratio); - else - outlink->sample_aspect_ratio = inlink->sample_aspect_ratio; - - return 0; - -fail: - av_log(ctx, AV_LOG_ERROR, - "Error when evaluating the expression '%s'\n", expr); - return ret; -} - -static int qsvscale_filter_frame(AVFilterLink *link, AVFrame *in) -{ - AVFilterContext *ctx = link->dst; - QSVScaleContext *s = ctx->priv; - AVFilterLink *outlink = ctx->outputs[0]; - - mfxSyncPoint sync = NULL; - mfxStatus err; - - AVFrame *out = NULL; - int ret = 0; - - out = ff_get_video_buffer(outlink, outlink->w, outlink->h); - if (!out) { - ret = AVERROR(ENOMEM); - goto fail; - } - - do { - err = MFXVideoVPP_RunFrameVPPAsync(s->session, - (mfxFrameSurface1*)in->data[3], - (mfxFrameSurface1*)out->data[3], - NULL, &sync); - if (err == MFX_WRN_DEVICE_BUSY) - av_usleep(1); - } while (err == MFX_WRN_DEVICE_BUSY); - - if (err < 0) { - ret = ff_qsvvpp_print_error(ctx, err, "Error during scaling"); - goto fail; - } - - if (!sync) { - av_log(ctx, AV_LOG_ERROR, "No sync during scaling\n"); - ret = AVERROR_UNKNOWN; - goto fail; - } - - do { - err = MFXVideoCORE_SyncOperation(s->session, sync, 1000); - } while (err == MFX_WRN_IN_EXECUTION); - if (err < 0) { - ret = ff_qsvvpp_print_error(ctx, err, "Error synchronizing the operation"); - goto fail; - } - - ret = av_frame_copy_props(out, in); - if (ret < 0) - goto fail; - - out->width = outlink->w; - out->height = outlink->h; - - av_reduce(&out->sample_aspect_ratio.num, &out->sample_aspect_ratio.den, - (int64_t)in->sample_aspect_ratio.num * outlink->h * link->w, - (int64_t)in->sample_aspect_ratio.den * outlink->w * link->h, - INT_MAX); - - av_frame_free(&in); - return ff_filter_frame(outlink, out); -fail: - av_frame_free(&in); - av_frame_free(&out); - return ret; -} - -#define OFFSET(x) offsetof(QSVScaleContext, x) -#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM -static const AVOption options[] = { - { "w", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, { .str = "iw" }, .flags = FLAGS }, - { "h", "Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, { .str = "ih" }, .flags = FLAGS }, - { "format", "Output pixel format", OFFSET(format_str), AV_OPT_TYPE_STRING, { .str = "same" }, .flags = FLAGS }, - - { "mode", "set scaling mode", OFFSET(mode), AV_OPT_TYPE_INT, { .i64 = MFX_SCALING_MODE_DEFAULT}, MFX_SCALING_MODE_DEFAULT, MFX_SCALING_MODE_QUALITY, FLAGS, "mode"}, - { "low_power", "low power mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_LOWPOWER}, INT_MIN, INT_MAX, FLAGS, "mode"}, - { "hq", "high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_QUALITY}, INT_MIN, INT_MAX, FLAGS, "mode"}, - - { NULL }, -}; - -static const AVClass qsvscale_class = { - .class_name = "scale_qsv", - .item_name = av_default_item_name, - .option = options, - .version = LIBAVUTIL_VERSION_INT, -}; - -static const AVFilterPad qsvscale_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .filter_frame = qsvscale_filter_frame, - .get_buffer.video = ff_qsvvpp_get_video_buffer, - }, -}; - -static const AVFilterPad qsvscale_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .config_props = qsvscale_config_props, - }, -}; - -const AVFilter ff_vf_scale_qsv = { - .name = "scale_qsv", - .description = NULL_IF_CONFIG_SMALL("QuickSync video scaling and format conversion"), - - .init = qsvscale_init, - .uninit = qsvscale_uninit, - - .priv_size = sizeof(QSVScaleContext), - .priv_class = &qsvscale_class, - - FILTER_INPUTS(qsvscale_inputs), - FILTER_OUTPUTS(qsvscale_outputs), - - FILTER_SINGLE_PIXFMT(AV_PIX_FMT_QSV), - - .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, -}; diff --git a/libavfilter/vf_scale_vaapi.c b/libavfilter/vf_scale_vaapi.c index a371077ee02..54c055f0a93 100644 --- a/libavfilter/vf_scale_vaapi.c +++ b/libavfilter/vf_scale_vaapi.c @@ -22,7 +22,6 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "scale_eval.h" #include "video.h" @@ -85,6 +84,16 @@ static int scale_vaapi_config_output(AVFilterLink *outlink) ff_scale_adjust_dimensions(inlink, &vpp_ctx->output_width, &vpp_ctx->output_height, ctx->force_original_aspect_ratio, ctx->force_divisible_by); + if (inlink->w == vpp_ctx->output_width && inlink->h == vpp_ctx->output_height && + (vpp_ctx->input_frames->sw_format == vpp_ctx->output_format || + vpp_ctx->output_format == AV_PIX_FMT_NONE) && + ctx->colour_primaries == AVCOL_PRI_UNSPECIFIED && + ctx->colour_transfer == AVCOL_TRC_UNSPECIFIED && + ctx->colour_matrix == AVCOL_SPC_UNSPECIFIED && + ctx->colour_range == AVCOL_RANGE_UNSPECIFIED && + ctx->chroma_location == AVCHROMA_LOC_UNSPECIFIED) + vpp_ctx->passthrough = 1; + err = ff_vaapi_vpp_config_output(outlink); if (err < 0) return err; @@ -111,6 +120,9 @@ static int scale_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame) av_get_pix_fmt_name(input_frame->format), input_frame->width, input_frame->height, input_frame->pts); + if (vpp_ctx->passthrough) + return ff_filter_frame(outlink, input_frame); + if (vpp_ctx->va_context == VA_INVALID_ID) return AVERROR(EINVAL); diff --git a/libavfilter/vf_scale_vt.c b/libavfilter/vf_scale_vt.c new file mode 100644 index 00000000000..d5b4f122911 --- /dev/null +++ b/libavfilter/vf_scale_vt.c @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2023 Zhao Zhili + * + * 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 + +#include "libavutil/hwcontext.h" +#include "libavutil/hwcontext_videotoolbox.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "internal.h" +#include "scale_eval.h" +#include "video.h" + +typedef struct ScaleVtContext { + AVClass *class; + + VTPixelTransferSessionRef transfer; + int output_width; + int output_height; + char *w_expr; + char *h_expr; + + enum AVColorPrimaries colour_primaries; + enum AVColorTransferCharacteristic colour_transfer; + enum AVColorSpace colour_matrix; + char *colour_primaries_string; + char *colour_transfer_string; + char *colour_matrix_string; +} ScaleVtContext; + +static av_cold int scale_vt_init(AVFilterContext *avctx) +{ + ScaleVtContext *s = avctx->priv; + int ret; + CFStringRef value; + + ret = VTPixelTransferSessionCreate(kCFAllocatorDefault, &s->transfer); + if (ret != noErr) { + av_log(avctx, AV_LOG_ERROR, "transfer session create failed, %d\n", ret); + return AVERROR_EXTERNAL; + } + +#define STRING_OPTION(var_name, func_name, default_value) \ + do { \ + if (s->var_name##_string) { \ + int var = av_##func_name##_from_name(s->var_name##_string); \ + if (var < 0) { \ + av_log(avctx, AV_LOG_ERROR, "Invalid %s.\n", #var_name); \ + return AVERROR(EINVAL); \ + } \ + s->var_name = var; \ + } else { \ + s->var_name = default_value; \ + } \ + } while (0) + + STRING_OPTION(colour_primaries, color_primaries, AVCOL_PRI_UNSPECIFIED); + STRING_OPTION(colour_transfer, color_transfer, AVCOL_TRC_UNSPECIFIED); + STRING_OPTION(colour_matrix, color_space, AVCOL_SPC_UNSPECIFIED); + + if (s->colour_primaries != AVCOL_PRI_UNSPECIFIED) { + value = av_map_videotoolbox_color_primaries_from_av(s->colour_primaries); + if (!value) { + av_log(avctx, AV_LOG_ERROR, + "Doesn't support converting to colour primaries %s\n", + s->colour_primaries_string); + return AVERROR(ENOTSUP); + } + VTSessionSetProperty(s->transfer, kVTPixelTransferPropertyKey_DestinationColorPrimaries, value); + } + + if (s->colour_transfer != AVCOL_TRC_UNSPECIFIED) { + value = av_map_videotoolbox_color_trc_from_av(s->colour_transfer); + if (!value) { + av_log(avctx, AV_LOG_ERROR, + "Doesn't support converting to trc %s\n", + s->colour_transfer_string); + return AVERROR(ENOTSUP); + } + VTSessionSetProperty(s->transfer, kVTPixelTransferPropertyKey_DestinationTransferFunction, value); + } + + if (s->colour_matrix != AVCOL_SPC_UNSPECIFIED) { + value = av_map_videotoolbox_color_matrix_from_av(s->colour_matrix); + if (!value) { + av_log(avctx, AV_LOG_ERROR, + "Doesn't support converting to colorspace %s\n", + s->colour_matrix_string); + return AVERROR(ENOTSUP); + } + VTSessionSetProperty(s->transfer, kVTPixelTransferPropertyKey_DestinationYCbCrMatrix, value); + } + + return 0; +} + +static av_cold void scale_vt_uninit(AVFilterContext *avctx) +{ + ScaleVtContext *s = avctx->priv; + + if (s->transfer) { + VTPixelTransferSessionInvalidate(s->transfer); + CFRelease(s->transfer); + s->transfer = NULL; + } +} + +static int scale_vt_filter_frame(AVFilterLink *link, AVFrame *in) +{ + int ret; + AVFilterContext *ctx = link->dst; + ScaleVtContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + CVPixelBufferRef src; + CVPixelBufferRef dst; + + AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + ret = AVERROR(ENOMEM); + goto fail; + } + + ret = av_frame_copy_props(out, in); + if (ret < 0) + goto fail; + + av_reduce(&out->sample_aspect_ratio.num, &out->sample_aspect_ratio.den, + (int64_t)in->sample_aspect_ratio.num * outlink->h * link->w, + (int64_t)in->sample_aspect_ratio.den * outlink->w * link->h, + INT_MAX); + if (s->colour_primaries != AVCOL_PRI_UNSPECIFIED) + out->color_primaries = s->colour_primaries; + if (s->colour_transfer != AVCOL_TRC_UNSPECIFIED) + out->color_trc = s->colour_transfer; + if (s->colour_matrix != AVCOL_SPC_UNSPECIFIED) + out->colorspace = s->colour_matrix; + + src = (CVPixelBufferRef)in->data[3]; + dst = (CVPixelBufferRef)out->data[3]; + ret = VTPixelTransferSessionTransferImage(s->transfer, src, dst); + if (ret != noErr) { + av_log(ctx, AV_LOG_ERROR, "transfer image failed, %d\n", ret); + ret = AVERROR_EXTERNAL; + goto fail; + } + + av_frame_free(&in); + + return ff_filter_frame(outlink, out); + +fail: + av_frame_free(&in); + av_frame_free(&out); + return ret; +} + +static int scale_vt_config_output(AVFilterLink *outlink) +{ + int err; + AVFilterContext *avctx = outlink->src; + ScaleVtContext *s = avctx->priv; + AVFilterLink *inlink = outlink->src->inputs[0]; + + err = ff_scale_eval_dimensions(s, s->w_expr, s->h_expr, inlink, outlink, + &s->output_width, + &s->output_height); + if (err < 0) + return err; + + outlink->w = s->output_width; + outlink->h = s->output_height; + + if (inlink->sample_aspect_ratio.num) { + AVRational r = {outlink->h * inlink->w, outlink->w * inlink->h}; + outlink->sample_aspect_ratio = av_mul_q(r, inlink->sample_aspect_ratio); + } else { + outlink->sample_aspect_ratio = inlink->sample_aspect_ratio; + } + + return 0; +} + +#define OFFSET(x) offsetof(ScaleVtContext, x) +#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) +static const AVOption scale_vt_options[] = { + { "w", "Output video width", + OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, .flags = FLAGS }, + { "h", "Output video height", + OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, .flags = FLAGS }, + { "color_matrix", "Output colour matrix coefficient set", + OFFSET(colour_matrix_string), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS }, + { "color_primaries", "Output colour primaries", + OFFSET(colour_primaries_string), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS }, + { "color_transfer", "Output colour transfer characteristics", + OFFSET(colour_transfer_string), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(scale_vt); + +static const AVFilterPad scale_vt_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = &scale_vt_filter_frame, + }, +}; + +static const AVFilterPad scale_vt_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = &scale_vt_config_output, + }, +}; + +const AVFilter ff_vf_scale_vt = { + .name = "scale_vt", + .description = NULL_IF_CONFIG_SMALL("Scale Videotoolbox frames"), + .priv_size = sizeof(ScaleVtContext), + .init = scale_vt_init, + .uninit = scale_vt_uninit, + FILTER_INPUTS(scale_vt_inputs), + FILTER_OUTPUTS(scale_vt_outputs), + FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VIDEOTOOLBOX), + .priv_class = &scale_vt_class, + .flags = AVFILTER_FLAG_HWDEVICE, +}; diff --git a/libavfilter/vf_scale_vulkan.c b/libavfilter/vf_scale_vulkan.c index c1404208962..6699bab934d 100644 --- a/libavfilter/vf_scale_vulkan.c +++ b/libavfilter/vf_scale_vulkan.c @@ -1,4 +1,6 @@ /* + * Copyright (c) Lynne + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -19,11 +21,11 @@ #include "libavutil/random_seed.h" #include "libavutil/opt.h" #include "vulkan_filter.h" +#include "vulkan_spirv.h" #include "scale_eval.h" #include "internal.h" #include "colorspace.h" - -#define CGROUPS (int [3]){ 32, 32, 1 } +#include "video.h" enum ScalerFunc { F_BILINEAR = 0, @@ -35,15 +37,17 @@ enum ScalerFunc { typedef struct ScaleVulkanContext { FFVulkanContext vkctx; + int initialized; + FFVulkanPipeline pl; + FFVkExecPool e; FFVkQueueFamilyCtx qf; - FFVkExecContext *exec; - FFVulkanPipeline *pl; - FFVkBuffer params_buf; + FFVkSPIRVShader shd; + VkSampler sampler; - /* Shader updators, must be in the main filter struct */ - VkDescriptorImageInfo input_images[3]; - VkDescriptorImageInfo output_images[3]; - VkDescriptorBufferInfo params_desc; + /* Push constants / options */ + struct { + float yuv_matrix[4][4]; + } opts; char *out_format_string; char *w_expr; @@ -51,8 +55,6 @@ typedef struct ScaleVulkanContext { enum ScalerFunc scaler; enum AVColorRange out_range; - - int initialized; } ScaleVulkanContext; static const char scale_bilinear[] = { @@ -110,10 +112,15 @@ static const char write_444[] = { static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) { int err; - FFVkSampler *sampler; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; VkFilter sampler_mode; ScaleVulkanContext *s = ctx->priv; FFVulkanContext *vkctx = &s->vkctx; + FFVkSPIRVShader *shd = &s->shd; + FFVkSPIRVCompiler *spv; + FFVulkanDescriptorSetBinding *desc; int crop_x = in->crop_left; int crop_y = in->crop_top; @@ -121,8 +128,6 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) int crop_h = in->height - (in->crop_top + in->crop_bottom); int in_planes = av_pix_fmt_count_planes(s->vkctx.input_format); - ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT, 0); - switch (s->scaler) { case F_NEAREST: sampler_mode = VK_FILTER_NEAREST; @@ -132,264 +137,134 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) break; }; - /* Create a sampler */ - sampler = ff_vk_init_sampler(vkctx, 0, sampler_mode); - if (!sampler) + spv = ff_vk_spirv_init(); + if (!spv) { + av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); return AVERROR_EXTERNAL; + } - s->pl = ff_vk_create_pipeline(vkctx, &s->qf); - if (!s->pl) - return AVERROR(ENOMEM); - - { /* Create the shader */ - FFVulkanDescriptorSetBinding desc_i[2] = { - { - .name = "input_img", - .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .dimensions = 2, - .elems = in_planes, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->input_images, - .sampler = sampler, - }, - { - .name = "output_img", - .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format), - .mem_quali = "writeonly", - .dimensions = 2, - .elems = av_pix_fmt_count_planes(s->vkctx.output_format), - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->output_images, - }, - }; - - FFVulkanDescriptorSetBinding desc_b = { - .name = "params", - .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .mem_quali = "readonly", - .mem_layout = "std430", - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = &s->params_desc, - .buf_content = "mat4 yuv_matrix;", - }; - - FFVkSPIRVShader *shd = ff_vk_init_shader(s->pl, "scale_compute", - VK_SHADER_STAGE_COMPUTE_BIT); - if (!shd) - return AVERROR(ENOMEM); - - ff_vk_set_compute_shader_sizes(shd, CGROUPS); - - RET(ff_vk_add_descriptor_set(vkctx, s->pl, shd, desc_i, FF_ARRAY_ELEMS(desc_i), 0)); /* set 0 */ - RET(ff_vk_add_descriptor_set(vkctx, s->pl, shd, &desc_b, 1, 0)); /* set 1 */ - - GLSLD( scale_bilinear ); - - if (s->vkctx.output_format != s->vkctx.input_format) { - GLSLD( rgb2yuv ); - } + ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT); + RET(ff_vk_exec_pool_init(vkctx, &s->qf, &s->e, s->qf.nb_queues*4, 0, 0, 0, NULL)); + RET(ff_vk_init_sampler(vkctx, &s->sampler, 0, sampler_mode)); + RET(ff_vk_shader_init(&s->pl, &s->shd, "scale_compute", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); + + ff_vk_shader_set_compute_sizes(&s->shd, 32, 32, 1); + + GLSLC(0, layout(push_constant, std430) uniform pushConstants { ); + GLSLC(1, mat4 yuv_matrix; ); + GLSLC(0, }; ); + GLSLC(0, ); + + ff_vk_add_push_constant(&s->pl, 0, sizeof(s->opts), + VK_SHADER_STAGE_COMPUTE_BIT); + + desc = (FFVulkanDescriptorSetBinding []) { + { + .name = "input_img", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = in_planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), + }, + { + .name = "output_img", + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format), + .mem_quali = "writeonly", + .dimensions = 2, + .elems = av_pix_fmt_count_planes(s->vkctx.output_format), + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + }, + }; - switch (s->vkctx.output_format) { - case AV_PIX_FMT_NV12: GLSLD(write_nv12); break; - case AV_PIX_FMT_YUV420P: GLSLD( write_420); break; - case AV_PIX_FMT_YUV444P: GLSLD( write_444); break; - default: break; - } + RET(ff_vk_pipeline_descriptor_set_add(vkctx, &s->pl, shd, desc, 2, 0, 0)); - GLSLC(0, void main() ); - GLSLC(0, { ); - GLSLC(1, ivec2 size; ); - GLSLC(1, ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); - GLSLF(1, vec2 in_d = vec2(%i, %i); ,in->width, in->height); - GLSLF(1, vec2 c_r = vec2(%i, %i) / in_d; ,crop_w, crop_h); - GLSLF(1, vec2 c_o = vec2(%i, %i) / in_d; ,crop_x,crop_y); - GLSLC(0, ); - - if (s->vkctx.output_format == s->vkctx.input_format) { - for (int i = 0; i < desc_i[1].elems; i++) { - GLSLF(1, size = imageSize(output_img[%i]); ,i); - GLSLC(1, if (IS_WITHIN(pos, size)) { ); - switch (s->scaler) { - case F_NEAREST: - case F_BILINEAR: - GLSLF(2, vec4 res = scale_bilinear(%i, pos, c_r, c_o); ,i); - GLSLF(2, imageStore(output_img[%i], pos, res); ,i); - break; - }; - GLSLC(1, } ); - } - } else { - GLSLC(1, vec4 res = scale_bilinear(0, pos, c_r, c_o); ); - GLSLF(1, res = rgb2yuv(res, %i); ,s->out_range == AVCOL_RANGE_JPEG); - switch (s->vkctx.output_format) { - case AV_PIX_FMT_NV12: GLSLC(1, write_nv12(res, pos); ); break; - case AV_PIX_FMT_YUV420P: GLSLC(1, write_420(res, pos); ); break; - case AV_PIX_FMT_YUV444P: GLSLC(1, write_444(res, pos); ); break; - default: return AVERROR(EINVAL); - } - } + GLSLD( scale_bilinear ); + + if (s->vkctx.output_format != s->vkctx.input_format) { + GLSLD( rgb2yuv ); + } - GLSLC(0, } ); + switch (s->vkctx.output_format) { + case AV_PIX_FMT_NV12: GLSLD(write_nv12); break; + case AV_PIX_FMT_YUV420P: GLSLD( write_420); break; + case AV_PIX_FMT_YUV444P: GLSLD( write_444); break; + default: break; + } - RET(ff_vk_compile_shader(vkctx, shd, "main")); + GLSLC(0, void main() ); + GLSLC(0, { ); + GLSLC(1, ivec2 size; ); + GLSLC(1, ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); + GLSLF(1, vec2 in_d = vec2(%i, %i); ,in->width, in->height); + GLSLF(1, vec2 c_r = vec2(%i, %i) / in_d; ,crop_w, crop_h); + GLSLF(1, vec2 c_o = vec2(%i, %i) / in_d; ,crop_x,crop_y); + GLSLC(0, ); + + if (s->vkctx.output_format == s->vkctx.input_format) { + for (int i = 0; i < desc[i].elems; i++) { + GLSLF(1, size = imageSize(output_img[%i]); ,i); + GLSLC(1, if (IS_WITHIN(pos, size)) { ); + switch (s->scaler) { + case F_NEAREST: + case F_BILINEAR: + GLSLF(2, vec4 res = scale_bilinear(%i, pos, c_r, c_o); ,i); + GLSLF(2, imageStore(output_img[%i], pos, res); ,i); + break; + }; + GLSLC(1, } ); + } + } else { + GLSLC(1, vec4 res = scale_bilinear(0, pos, c_r, c_o); ); + GLSLF(1, res = rgb2yuv(res, %i); ,s->out_range == AVCOL_RANGE_JPEG); + switch (s->vkctx.output_format) { + case AV_PIX_FMT_NV12: GLSLC(1, write_nv12(res, pos); ); break; + case AV_PIX_FMT_YUV420P: GLSLC(1, write_420(res, pos); ); break; + case AV_PIX_FMT_YUV444P: GLSLC(1, write_444(res, pos); ); break; + default: return AVERROR(EINVAL); + } } - RET(ff_vk_init_pipeline_layout(vkctx, s->pl)); - RET(ff_vk_init_compute_pipeline(vkctx, s->pl)); + GLSLC(0, } ); if (s->vkctx.output_format != s->vkctx.input_format) { const AVLumaCoefficients *lcoeffs; double tmp_mat[3][3]; - struct { - float yuv_matrix[4][4]; - } *par; - lcoeffs = av_csp_luma_coeffs_from_avcsp(in->colorspace); if (!lcoeffs) { av_log(ctx, AV_LOG_ERROR, "Unsupported colorspace\n"); return AVERROR(EINVAL); } - RET(ff_vk_create_buf(vkctx, &s->params_buf, - sizeof(*par), - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)); - - RET(ff_vk_map_buffers(vkctx, &s->params_buf, (uint8_t **)&par, 1, 0)); - ff_fill_rgb2yuv_table(lcoeffs, tmp_mat); - memset(par, 0, sizeof(*par)); - for (int y = 0; y < 3; y++) for (int x = 0; x < 3; x++) - par->yuv_matrix[x][y] = tmp_mat[x][y]; - - par->yuv_matrix[3][3] = 1.0; - - RET(ff_vk_unmap_buffers(vkctx, &s->params_buf, 1, 1)); - - s->params_desc.buffer = s->params_buf.buf; - s->params_desc.range = VK_WHOLE_SIZE; - - ff_vk_update_descriptor_set(vkctx, s->pl, 1); + s->opts.yuv_matrix[x][y] = tmp_mat[x][y]; + s->opts.yuv_matrix[3][3] = 1.0; } - /* Execution context */ - RET(ff_vk_create_exec_ctx(vkctx, &s->exec, &s->qf)); + RET(spv->compile_shader(spv, ctx, shd, &spv_data, &spv_len, "main", + &spv_opaque)); + RET(ff_vk_shader_create(vkctx, shd, spv_data, spv_len, "main")); + + RET(ff_vk_init_compute_pipeline(vkctx, &s->pl, shd)); + RET(ff_vk_exec_pipeline_register(vkctx, &s->e, &s->pl)); s->initialized = 1; return 0; fail: - return err; -} - -static int process_frames(AVFilterContext *avctx, AVFrame *out_f, AVFrame *in_f) -{ - int err = 0; - VkCommandBuffer cmd_buf; - ScaleVulkanContext *s = avctx->priv; - FFVulkanContext *vkctx = &s->vkctx; - FFVulkanFunctions *vk = &vkctx->vkfn; - AVVkFrame *in = (AVVkFrame *)in_f->data[0]; - AVVkFrame *out = (AVVkFrame *)out_f->data[0]; - VkImageMemoryBarrier barriers[AV_NUM_DATA_POINTERS*2]; - int barrier_count = 0; - const int planes = av_pix_fmt_count_planes(s->vkctx.input_format); - const VkFormat *input_formats = av_vkfmt_from_pixfmt(s->vkctx.input_format); - const VkFormat *output_formats = av_vkfmt_from_pixfmt(s->vkctx.output_format); - - /* Update descriptors and init the exec context */ - ff_vk_start_exec_recording(vkctx, s->exec); - cmd_buf = ff_vk_get_exec_buf(s->exec); - - for (int i = 0; i < planes; i++) { - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->input_images[i].imageView, in->img[i], - input_formats[i], - ff_comp_identity_map)); - - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->output_images[i].imageView, out->img[i], - output_formats[i], - ff_comp_identity_map)); - - s->input_images[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - s->output_images[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; - } - - ff_vk_update_descriptor_set(vkctx, s->pl, 0); - - for (int i = 0; i < planes; i++) { - VkImageMemoryBarrier bar = { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .oldLayout = in->layout[i], - .newLayout = s->input_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = in->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }; - - memcpy(&barriers[barrier_count++], &bar, sizeof(VkImageMemoryBarrier)); - - in->layout[i] = bar.newLayout; - in->access[i] = bar.dstAccessMask; - } - - for (int i = 0; i < av_pix_fmt_count_planes(s->vkctx.output_format); i++) { - VkImageMemoryBarrier bar = { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .oldLayout = out->layout[i], - .newLayout = s->output_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = out->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }; - - memcpy(&barriers[barrier_count++], &bar, sizeof(VkImageMemoryBarrier)); - - out->layout[i] = bar.newLayout; - out->access[i] = bar.dstAccessMask; - } - - vk->CmdPipelineBarrier(cmd_buf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, - 0, NULL, 0, NULL, barrier_count, barriers); - - ff_vk_bind_pipeline_exec(vkctx, s->exec, s->pl); - - vk->CmdDispatch(cmd_buf, - FFALIGN(vkctx->output_width, CGROUPS[0])/CGROUPS[0], - FFALIGN(vkctx->output_height, CGROUPS[1])/CGROUPS[1], 1); - - ff_vk_add_exec_dep(vkctx, s->exec, in_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - ff_vk_add_exec_dep(vkctx, s->exec, out_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - - err = ff_vk_submit_exec_queue(vkctx, s->exec); - if (err) - return err; - - ff_vk_qf_rotate(&s->qf); + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); + if (spv) + spv->uninit(&spv); return err; - -fail: - ff_vk_discard_exec_deps(s->exec); - return err; } static int scale_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) @@ -408,7 +283,8 @@ static int scale_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) if (!s->initialized) RET(init_filter(ctx, in)); - RET(process_frames(ctx, out, in)); + RET(ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->pl, out, in, + s->sampler, &s->opts, sizeof(s->opts))); err = av_frame_copy_props(out, in); if (err < 0) @@ -475,8 +351,17 @@ static int scale_vulkan_config_output(AVFilterLink *outlink) static void scale_vulkan_uninit(AVFilterContext *avctx) { ScaleVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + FFVulkanFunctions *vk = &vkctx->vkfn; + + ff_vk_exec_pool_free(vkctx, &s->e); + ff_vk_pipeline_free(vkctx, &s->pl); + ff_vk_shader_free(vkctx, &s->shd); + + if (s->sampler) + vk->DestroySampler(vkctx->hwctx->act_dev, s->sampler, + vkctx->hwctx->alloc); - ff_vk_free_buf(&s->vkctx, &s->params_buf); ff_vk_uninit(&s->vkctx); s->initialized = 0; @@ -531,4 +416,5 @@ const AVFilter ff_vf_scale_vulkan = { FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), .priv_class = &scale_vulkan_class, .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_scdet.c b/libavfilter/vf_scdet.c index 1576759c24a..6de84a43a0f 100644 --- a/libavfilter/vf_scdet.c +++ b/libavfilter/vf_scdet.c @@ -29,6 +29,7 @@ #include "avfilter.h" #include "filters.h" #include "scene_sad.h" +#include "video.h" typedef struct SCDetContext { const AVClass *class; @@ -163,14 +164,14 @@ static int activate(AVFilterContext *ctx) snprintf(buf, sizeof(buf), "%0.3f", s->scene_score); set_meta(s, frame, "lavfi.scd.score", buf); - if (s->scene_score > s->threshold) { + if (s->scene_score >= s->threshold) { av_log(s, AV_LOG_INFO, "lavfi.scd.score: %.3f, lavfi.scd.time: %s\n", s->scene_score, av_ts2timestr(frame->pts, &inlink->time_base)); set_meta(s, frame, "lavfi.scd.time", av_ts2timestr(frame->pts, &inlink->time_base)); } if (s->sc_pass) { - if (s->scene_score > s->threshold) + if (s->scene_score >= s->threshold) return ff_filter_frame(outlink, frame); else { av_frame_free(&frame); @@ -193,13 +194,6 @@ static const AVFilterPad scdet_inputs[] = { }, }; -static const AVFilterPad scdet_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_scdet = { .name = "scdet", .description = NULL_IF_CONFIG_SMALL("Detect video scene change"), @@ -208,7 +202,7 @@ const AVFilter ff_vf_scdet = { .uninit = uninit, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(scdet_inputs), - FILTER_OUTPUTS(scdet_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .activate = activate, }; diff --git a/libavfilter/vf_scroll.c b/libavfilter/vf_scroll.c index 107686cb02b..eebf12e902b 100644 --- a/libavfilter/vf_scroll.c +++ b/libavfilter/vf_scroll.c @@ -22,8 +22,8 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" +#include "video.h" typedef struct ScrollContext { const AVClass *class; @@ -194,20 +194,13 @@ static const AVFilterPad scroll_inputs[] = { }, }; -static const AVFilterPad scroll_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_scroll = { .name = "scroll", .description = NULL_IF_CONFIG_SMALL("Scroll input video."), .priv_size = sizeof(ScrollContext), .priv_class = &scroll_class, FILTER_INPUTS(scroll_inputs), - FILTER_OUTPUTS(scroll_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_selectivecolor.c b/libavfilter/vf_selectivecolor.c index ea4b5dfee25..315f9e0eeda 100644 --- a/libavfilter/vf_selectivecolor.c +++ b/libavfilter/vf_selectivecolor.c @@ -32,7 +32,6 @@ #include "libavcodec/mathops.h" // for mid_pred(), which is a macro so no link dependency #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -474,19 +473,12 @@ static const AVFilterPad selectivecolor_inputs[] = { }, }; -static const AVFilterPad selectivecolor_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_selectivecolor = { .name = "selectivecolor", .description = NULL_IF_CONFIG_SMALL("Apply CMYK adjustments to specific color ranges."), .priv_size = sizeof(SelectiveColorContext), FILTER_INPUTS(selectivecolor_inputs), - FILTER_OUTPUTS(selectivecolor_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &selectivecolor_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_separatefields.c b/libavfilter/vf_separatefields.c index 7db64c54798..b7ddb263770 100644 --- a/libavfilter/vf_separatefields.c +++ b/libavfilter/vf_separatefields.c @@ -22,6 +22,7 @@ #include "avfilter.h" #include "filters.h" #include "internal.h" +#include "video.h" typedef struct SeparateFieldsContext { int nb_planes; @@ -70,14 +71,19 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) int ret; inpicref->height = outlink->h; +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS inpicref->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + inpicref->flags &= ~AV_FRAME_FLAG_INTERLACED; if (!s->second) { goto clone; } else { AVFrame *second = s->second; - extract_field(second, s->nb_planes, second->top_field_first); + extract_field(second, s->nb_planes, !!(second->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)); if (second->pts != AV_NOPTS_VALUE && inpicref->pts != AV_NOPTS_VALUE) @@ -94,7 +100,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) return AVERROR(ENOMEM); } - extract_field(inpicref, s->nb_planes, !inpicref->top_field_first); + extract_field(inpicref, s->nb_planes, !(inpicref->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)); if (inpicref->pts != AV_NOPTS_VALUE) inpicref->pts *= 2; @@ -110,7 +116,7 @@ static int flush_frame(AVFilterLink *outlink, int64_t pts, int64_t *out_pts) if (s->second) { *out_pts = s->second->pts += pts; - extract_field(s->second, s->nb_planes, s->second->top_field_first); + extract_field(s->second, s->nb_planes, !!(s->second->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)); ret = ff_filter_frame(outlink, s->second); s->second = NULL; } @@ -156,13 +162,6 @@ static av_cold void uninit(AVFilterContext *ctx) av_frame_free(&s->second); } -static const AVFilterPad separatefields_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - static const AVFilterPad separatefields_outputs[] = { { .name = "default", @@ -177,6 +176,6 @@ const AVFilter ff_vf_separatefields = { .priv_size = sizeof(SeparateFieldsContext), .activate = activate, .uninit = uninit, - FILTER_INPUTS(separatefields_inputs), + FILTER_INPUTS(ff_video_default_filterpad), FILTER_OUTPUTS(separatefields_outputs), }; diff --git a/libavfilter/vf_setparams.c b/libavfilter/vf_setparams.c index 95a2d15c02b..ae4c937518a 100644 --- a/libavfilter/vf_setparams.c +++ b/libavfilter/vf_setparams.c @@ -127,10 +127,24 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) /* set field */ if (s->field_mode == MODE_PROG) { +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS frame->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + frame->flags &= ~AV_FRAME_FLAG_INTERLACED; } else if (s->field_mode != MODE_AUTO) { +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS frame->interlaced_frame = 1; frame->top_field_first = s->field_mode; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + frame->flags |= AV_FRAME_FLAG_INTERLACED; + if (s->field_mode) + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; + else + frame->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; } /* set range */ @@ -155,13 +169,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_setparams = { .name = "setparams", .description = NULL_IF_CONFIG_SMALL("Force field, or color property for the output video frame."), @@ -169,7 +176,7 @@ const AVFilter ff_vf_setparams = { .priv_class = &setparams_class, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), }; #if CONFIG_SETRANGE_FILTER @@ -209,7 +216,7 @@ const AVFilter ff_vf_setrange = { .priv_class = &setrange_class, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), }; #endif /* CONFIG_SETRANGE_FILTER */ @@ -244,6 +251,6 @@ const AVFilter ff_vf_setfield = { .priv_class = &setfield_class, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), }; #endif /* CONFIG_SETFIELD_FILTER */ diff --git a/libavfilter/vf_showinfo.c b/libavfilter/vf_showinfo.c index d5edbd2f0ac..bf8580bc8d0 100644 --- a/libavfilter/vf_showinfo.c +++ b/libavfilter/vf_showinfo.c @@ -42,6 +42,7 @@ #include "libavutil/mastering_display_metadata.h" #include "libavutil/video_enc_params.h" #include "libavutil/detection_bbox.h" +#include "libavutil/ambient_viewing_environment.h" #include "libavutil/uuid.h" #include "avfilter.h" @@ -353,19 +354,20 @@ static void dump_dynamic_hdr_vivid(AVFilterContext *ctx, AVFrameSideData *sd) av_log(ctx, AV_LOG_INFO, "3Spline_enable_flag[%d][%d]: %d, ", w, i, tm_params->three_Spline_enable_flag); if (tm_params->three_Spline_enable_flag) { - av_log(ctx, AV_LOG_INFO, "3Spline_TH_mode[%d][%d]: %d, ", w, i, tm_params->three_Spline_TH_mode); - for (int j = 0; j < tm_params->three_Spline_num; j++) { - av_log(ctx, AV_LOG_INFO, "3Spline_TH_enable_MB[%d][%d][%d]: %.4f, ", - w, i, j, av_q2d(tm_params->three_Spline_TH_enable_MB)); + const AVHDRVivid3SplineParams *three_spline = &tm_params->three_spline[j]; + av_log(ctx, AV_LOG_INFO, "3Spline_TH_mode[%d][%d]: %d, ", w, i, three_spline->th_mode); + if (three_spline->th_mode == 0 || three_spline->th_mode == 2) + av_log(ctx, AV_LOG_INFO, "3Spline_TH_enable_MB[%d][%d][%d]: %.4f, ", + w, i, j, av_q2d(three_spline->th_enable_mb)); av_log(ctx, AV_LOG_INFO, "3Spline_TH_enable[%d][%d][%d]: %.4f, ", - w, i, j, av_q2d(tm_params->three_Spline_TH_enable)); + w, i, j, av_q2d(three_spline->th_enable)); av_log(ctx, AV_LOG_INFO, "3Spline_TH_Delta1[%d][%d][%d]: %.4f, ", - w, i, j, av_q2d(tm_params->three_Spline_TH_Delta1)); + w, i, j, av_q2d(three_spline->th_delta1)); av_log(ctx, AV_LOG_INFO, "3Spline_TH_Delta2[%d][%d][%d]: %.4f, ", - w, i, j, av_q2d(tm_params->three_Spline_TH_Delta2)); + w, i, j, av_q2d(three_spline->th_delta2)); av_log(ctx, AV_LOG_INFO, "3Spline_enable_Strength[%d][%d][%d]: %.4f, ", - w, i, j, av_q2d(tm_params->three_Spline_enable_Strength)); + w, i, j, av_q2d(three_spline->enable_strength)); } } } @@ -601,6 +603,17 @@ static void dump_dovi_metadata(AVFilterContext *ctx, const AVFrameSideData *sd) av_log(ctx, AV_LOG_INFO, "source_diagonal=%"PRIu16"; ", color->source_diagonal); } +static void dump_ambient_viewing_environment(AVFilterContext *ctx, const AVFrameSideData *sd) +{ + const AVAmbientViewingEnvironment *ambient_viewing_environment = + (const AVAmbientViewingEnvironment *)sd->data; + + av_log(ctx, AV_LOG_INFO, "ambient_illuminance=%f, ambient_light_x=%f, ambient_light_y=%f", + av_q2d(ambient_viewing_environment->ambient_illuminance), + av_q2d(ambient_viewing_environment->ambient_light_x), + av_q2d(ambient_viewing_environment->ambient_light_y)); +} + static void dump_color_property(AVFilterContext *ctx, AVFrame *frame) { const char *color_range_str = av_color_range_name(frame->color_range); @@ -701,18 +714,17 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) av_log(ctx, AV_LOG_INFO, "n:%4"PRId64" pts:%7s pts_time:%-7s duration:%7"PRId64 - " duration_time:%-7s pos:%9"PRId64" " + " duration_time:%-7s " "fmt:%s sar:%d/%d s:%dx%d i:%c iskey:%d type:%c ", inlink->frame_count_out, av_ts2str(frame->pts), av_ts2timestr(frame->pts, &inlink->time_base), frame->duration, av_ts2timestr(frame->duration, &inlink->time_base), - frame->pkt_pos, desc->name, frame->sample_aspect_ratio.num, frame->sample_aspect_ratio.den, frame->width, frame->height, - !frame->interlaced_frame ? 'P' : /* Progressive */ - frame->top_field_first ? 'T' : 'B', /* Top / Bottom */ - frame->key_frame, + !(frame->flags & AV_FRAME_FLAG_INTERLACED) ? 'P' : /* Progressive */ + (frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? 'T' : 'B', /* Top / Bottom */ + !!(frame->flags & AV_FRAME_FLAG_KEY), av_get_picture_type_char(frame->pict_type)); if (s->calculate_checksums) { @@ -797,6 +809,9 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) case AV_FRAME_DATA_DOVI_METADATA: dump_dovi_metadata(ctx, sd); break; + case AV_FRAME_DATA_AMBIENT_VIEWING_ENVIRONMENT: + dump_ambient_viewing_environment(ctx, sd); + break; default: if (name) av_log(ctx, AV_LOG_INFO, diff --git a/libavfilter/vf_shuffleframes.c b/libavfilter/vf_shuffleframes.c index 14b90e64461..cf8f197e27d 100644 --- a/libavfilter/vf_shuffleframes.c +++ b/libavfilter/vf_shuffleframes.c @@ -146,13 +146,6 @@ static const AVFilterPad shuffleframes_inputs[] = { }, }; -static const AVFilterPad shuffleframes_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_shuffleframes = { .name = "shuffleframes", .description = NULL_IF_CONFIG_SMALL("Shuffle video frames."), @@ -161,6 +154,6 @@ const AVFilter ff_vf_shuffleframes = { .init = init, .uninit = uninit, FILTER_INPUTS(shuffleframes_inputs), - FILTER_OUTPUTS(shuffleframes_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_shuffleplanes.c b/libavfilter/vf_shuffleplanes.c index b2f64ad0763..fb2f85cf550 100644 --- a/libavfilter/vf_shuffleplanes.c +++ b/libavfilter/vf_shuffleplanes.c @@ -24,6 +24,7 @@ #include "libavutil/pixfmt.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" #include "video.h" @@ -153,20 +154,13 @@ static const AVFilterPad shuffleplanes_inputs[] = { }, }; -static const AVFilterPad shuffleplanes_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_shuffleplanes = { .name = "shuffleplanes", .description = NULL_IF_CONFIG_SMALL("Shuffle video planes."), .priv_size = sizeof(ShufflePlanesContext), .priv_class = &shuffleplanes_class, FILTER_INPUTS(shuffleplanes_inputs), - FILTER_OUTPUTS(shuffleplanes_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_signalstats.c b/libavfilter/vf_signalstats.c index e6f84be9baa..b4d10292960 100644 --- a/libavfilter/vf_signalstats.c +++ b/libavfilter/vf_signalstats.c @@ -23,6 +23,7 @@ #include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" +#include "filters.h" #include "internal.h" enum FilterMode { @@ -565,7 +566,7 @@ static int filter_frame8(AVFilterLink *link, AVFrame *in) int tothue = 0; int dify = 0, difu = 0, difv = 0; uint16_t masky = 0, masku = 0, maskv = 0; - + int ret; int filtot[FILT_NUMB] = {0}; AVFrame *prev; @@ -588,7 +589,16 @@ static int filter_frame8(AVFilterLink *link, AVFrame *in) if (s->outfilter != FILTER_NONE) { out = av_frame_clone(in); - av_frame_make_writable(out); + if (!out) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } + ret = ff_inlink_make_frame_writable(link, &out); + if (ret < 0) { + av_frame_free(&out); + av_frame_free(&in); + return ret; + } } ff_filter_execute(ctx, compute_sat_hue_metrics8, &td_huesat, @@ -790,7 +800,7 @@ static int filter_frame16(AVFilterLink *link, AVFrame *in) int filtot[FILT_NUMB] = {0}; AVFrame *prev; - + int ret; AVFrame *sat = s->frame_sat; AVFrame *hue = s->frame_hue; const uint16_t *p_sat = (uint16_t *)sat->data[0]; @@ -810,7 +820,16 @@ static int filter_frame16(AVFilterLink *link, AVFrame *in) if (s->outfilter != FILTER_NONE) { out = av_frame_clone(in); - av_frame_make_writable(out); + if (!out) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } + ret = ff_inlink_make_frame_writable(link, &out); + if (ret < 0) { + av_frame_free(&out); + av_frame_free(&in); + return ret; + } } ff_filter_execute(ctx, compute_sat_hue_metrics16, &td_huesat, diff --git a/libavfilter/vf_siti.c b/libavfilter/vf_siti.c index 738affa5e84..1b6ff32d6e2 100644 --- a/libavfilter/vf_siti.c +++ b/libavfilter/vf_siti.c @@ -31,7 +31,6 @@ #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -328,13 +327,6 @@ static const AVFilterPad avfilter_vf_siti_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_siti_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO - }, -}; - const AVFilter ff_vf_siti = { .name = "siti", .description = NULL_IF_CONFIG_SMALL("Calculate spatial information (SI) and temporal information (TI)."), @@ -345,5 +337,5 @@ const AVFilter ff_vf_siti = { .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_PIXFMTS_ARRAY(pix_fmts), FILTER_INPUTS(avfilter_vf_siti_inputs), - FILTER_OUTPUTS(avfilter_vf_siti_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), }; diff --git a/libavfilter/vf_smartblur.c b/libavfilter/vf_smartblur.c index 48c28d2760c..85d8d502e1b 100644 --- a/libavfilter/vf_smartblur.c +++ b/libavfilter/vf_smartblur.c @@ -30,8 +30,8 @@ #include "libswscale/swscale.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" +#include "video.h" #define RADIUS_MIN 0.1 #define RADIUS_MAX 5.0 @@ -274,13 +274,6 @@ static const AVFilterPad smartblur_inputs[] = { }, }; -static const AVFilterPad smartblur_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_smartblur = { .name = "smartblur", .description = NULL_IF_CONFIG_SMALL("Blur the input video without impacting the outlines."), @@ -288,7 +281,7 @@ const AVFilter ff_vf_smartblur = { .init = init, .uninit = uninit, FILTER_INPUTS(smartblur_inputs), - FILTER_OUTPUTS(smartblur_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &smartblur_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_spp.c b/libavfilter/vf_spp.c index 8442bcc5974..83f062b3bf3 100644 --- a/libavfilter/vf_spp.c +++ b/libavfilter/vf_spp.c @@ -38,6 +38,7 @@ #include "internal.h" #include "qp_table.h" #include "vf_spp.h" +#include "video.h" enum mode { MODE_HARD, @@ -484,13 +485,6 @@ static const AVFilterPad spp_inputs[] = { }, }; -static const AVFilterPad spp_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_spp = { .name = "spp", .description = NULL_IF_CONFIG_SMALL("Apply a simple post processing filter."), @@ -498,7 +492,7 @@ const AVFilter ff_vf_spp = { .preinit = preinit, .uninit = uninit, FILTER_INPUTS(spp_inputs), - FILTER_OUTPUTS(spp_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .process_command = process_command, .priv_class = &spp_class, diff --git a/libavfilter/vf_sr.c b/libavfilter/vf_sr.c index cb24c096ced..05a54b0933a 100644 --- a/libavfilter/vf_sr.c +++ b/libavfilter/vf_sr.c @@ -26,11 +26,10 @@ */ #include "avfilter.h" -#include "formats.h" #include "internal.h" +#include "video.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" -#include "libavformat/avio.h" #include "libswscale/swscale.h" #include "dnn_filter_common.h" @@ -46,8 +45,7 @@ typedef struct SRContext { #define OFFSET(x) offsetof(SRContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM static const AVOption sr_options[] = { - { "dnn_backend", "DNN backend used for model execution", OFFSET(dnnctx.backend_type), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS, "backend" }, - { "native", "native backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, "backend" }, + { "dnn_backend", "DNN backend used for model execution", OFFSET(dnnctx.backend_type), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, FLAGS, "backend" }, #if (CONFIG_LIBTENSORFLOW == 1) { "tensorflow", "tensorflow backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "backend" }, #endif diff --git a/libavfilter/vf_ssim360.c b/libavfilter/vf_ssim360.c new file mode 100644 index 00000000000..3dec430bfff --- /dev/null +++ b/libavfilter/vf_ssim360.c @@ -0,0 +1,1760 @@ +/** + * Copyright (c) 2015-2021, Facebook, Inc. + * All rights reserved. + * + * 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 + */ + +/* Computes the Structural Similarity Metric between two 360 video streams. + * original SSIM algorithm: + * Z. Wang, A. C. Bovik, H. R. Sheikh and E. P. Simoncelli, + * "Image quality assessment: From error visibility to structural similarity," + * IEEE Transactions on Image Processing, vol. 13, no. 4, pp. 600-612, Apr. 2004. + * + * To improve speed, this implementation uses the standard approximation of + * overlapped 8x8 block sums, rather than the original gaussian weights. + * + * To address warping from 360 projections for videos with same + * projection and resolution, the 8x8 blocks sampled are weighted by + * their location in the image. + * + * To apply SSIM across projections and video sizes, we render the video on to + * a flat "tape" from which the 8x8 are selected and compared. + */ + +/* + * @file + * Caculate the SSIM between two input 360 videos. + */ + +#include + +#include "libavutil/avstring.h" +#include "libavutil/file_open.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" + +#include "avfilter.h" +#include "drawutils.h" +#include "internal.h" +#include "framesync.h" + +#define RIGHT 0 +#define LEFT 1 +#define TOP 2 +#define BOTTOM 3 +#define FRONT 4 +#define BACK 5 + +#define DEFAULT_HEATMAP_W 32 +#define DEFAULT_HEATMAP_H 16 + +#define M_PI_F ((float)M_PI) +#define M_PI_2_F ((float)M_PI_2) +#define M_PI_4_F ((float)M_PI_4) +#define M_SQRT2_F ((float)M_SQRT2) + +#define DEFAULT_EXPANSION_COEF 1.01f + +#define BARREL_THETA_RANGE (DEFAULT_EXPANSION_COEF * 2.0f * M_PI_F) +#define BARREL_PHI_RANGE (DEFAULT_EXPANSION_COEF * M_PI_2_F) + +// Use fixed-point with 16 bit precision for fast bilinear math +#define FIXED_POINT_PRECISION 16 + +// Use 1MB per channel for the histogram to get 5-digit precise SSIM value +#define SSIM360_HIST_SIZE 131072 + +// The last number is a marker < 0 to mark end of list +static const double PERCENTILE_LIST[] = { + 1.0, 0.9, 0.8, 0.7, 0.6, + 0.5, 0.4, 0.3, 0.2, 0.1, 0, -1 +}; + +typedef enum StereoFormat { + STEREO_FORMAT_TB, + STEREO_FORMAT_LR, + STEREO_FORMAT_MONO, + STEREO_FORMAT_N +} StereoFormat; + +typedef enum Projection { + PROJECTION_CUBEMAP32, + PROJECTION_CUBEMAP23, + PROJECTION_BARREL, + PROJECTION_BARREL_SPLIT, + PROJECTION_EQUIRECT, + PROJECTION_N +} Projection; + +typedef struct Map2D { + int w, h; + double *value; +} Map2D; + +typedef struct HeatmapList { + Map2D map; + struct HeatmapList *next; +} HeatmapList; + +typedef struct SampleParams { + int stride; + int planewidth; + int planeheight; + int x_image_offset; + int y_image_offset; + int x_image_range; + int y_image_range; + int projection; + float expand_coef; +} SampleParams; + +typedef struct BilinearMap { + // Indices to the 4 samples to compute bilinear + int tli; + int tri; + int bli; + int bri; + + // Fixed point factors with which the above 4 sample vector's + // dot product needs to be computed for the final bilinear value + int tlf; + int trf; + int blf; + int brf; +} BilinearMap; + +typedef struct SSIM360Context { + const AVClass *class; + + FFFrameSync fs; + // Stats file configuration + FILE *stats_file; + char *stats_file_str; + + // Component properties + int nb_components; + double coefs[4]; + char comps[4]; + int max; + + // Channel configuration & properties + int compute_chroma; + + int is_rgb; + uint8_t rgba_map[4]; + + // Standard SSIM computation configuration & workspace + uint64_t frame_skip_ratio; + + int *temp; + uint64_t nb_ssim_frames; + uint64_t nb_net_frames; + double ssim360[4], ssim360_total; + double *ssim360_hist[4]; + double ssim360_hist_net[4]; + double ssim360_percentile_sum[4][256]; + + // 360 projection configuration & workspace + int ref_projection; + int main_projection; + int ref_stereo_format; + int main_stereo_format; + float ref_pad; + float main_pad; + int use_tape; + char *heatmap_str; + int default_heatmap_w; + int default_heatmap_h; + + Map2D density; + HeatmapList *heatmaps; + int ref_planewidth[4]; + int ref_planeheight[4]; + int main_planewidth[4]; + int main_planeheight[4]; + int tape_length[4]; + BilinearMap *ref_tape_map[4][2]; + BilinearMap *main_tape_map[4][2]; + float angular_resolution[4][2]; + double (*ssim360_plane)( + uint8_t *main, int main_stride, + uint8_t *ref, int ref_stride, + int width, int height, void *temp, + int max, Map2D density); +} SSIM360Context; + +#define OFFSET(x) offsetof(SSIM360Context, x) +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM + +static const AVOption ssim360_options[] = { + { "stats_file", "Set file where to store per-frame difference information", + OFFSET(stats_file_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, + { "f", "Set file where to store per-frame difference information", + OFFSET(stats_file_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, + + { "compute_chroma", + "Specifies if non-luma channels must be computed", + OFFSET(compute_chroma), AV_OPT_TYPE_INT, {.i64 = 1}, + 0, 1, .flags = FLAGS }, + + { "frame_skip_ratio", + "Specifies the number of frames to be skipped from evaluation, for every evaluated frame", + OFFSET(frame_skip_ratio), AV_OPT_TYPE_INT, {.i64 = 0}, + 0, 1000000, .flags = FLAGS }, + + { "ref_projection", "projection of the reference video", + OFFSET(ref_projection), AV_OPT_TYPE_INT, {.i64 = PROJECTION_EQUIRECT}, + 0, PROJECTION_N - 1, .flags = FLAGS, "projection" }, + + { "e", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64 = PROJECTION_EQUIRECT}, 0, 0, FLAGS, "projection" }, + { "equirect", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64 = PROJECTION_EQUIRECT}, 0, 0, FLAGS, "projection" }, + { "c3x2", "cubemap 3x2", 0, AV_OPT_TYPE_CONST, {.i64 = PROJECTION_CUBEMAP32}, 0, 0, FLAGS, "projection" }, + { "c2x3", "cubemap 2x3", 0, AV_OPT_TYPE_CONST, {.i64 = PROJECTION_CUBEMAP23}, 0, 0, FLAGS, "projection" }, + { "barrel", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64 = PROJECTION_BARREL}, 0, 0, FLAGS, "projection" }, + { "barrelsplit", "barrel split facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64 = PROJECTION_BARREL_SPLIT}, 0, 0, FLAGS, "projection" }, + + { "main_projection", "projection of the main video", + OFFSET(main_projection), AV_OPT_TYPE_INT, {.i64 = PROJECTION_N}, + 0, PROJECTION_N, .flags = FLAGS, "projection" }, + + { "ref_stereo", "stereo format of the reference video", + OFFSET(ref_stereo_format), AV_OPT_TYPE_INT, {.i64 = STEREO_FORMAT_MONO}, + 0, STEREO_FORMAT_N - 1, .flags = FLAGS, "stereo_format" }, + + { "mono", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = STEREO_FORMAT_MONO }, 0, 0, FLAGS, "stereo_format" }, + { "tb", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = STEREO_FORMAT_TB }, 0, 0, FLAGS, "stereo_format" }, + { "lr", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = STEREO_FORMAT_LR }, 0, 0, FLAGS, "stereo_format" }, + + { "main_stereo", "stereo format of main video", + OFFSET(main_stereo_format), AV_OPT_TYPE_INT, {.i64 = STEREO_FORMAT_N}, + 0, STEREO_FORMAT_N, .flags = FLAGS, "stereo_format" }, + + { "ref_pad", + "Expansion (padding) coefficient for each cube face of the reference video", + OFFSET(ref_pad), AV_OPT_TYPE_FLOAT, {.dbl = .0f}, 0, 10, .flags = FLAGS }, + + { "main_pad", + "Expansion (padding) coeffiecient for each cube face of the main video", + OFFSET(main_pad), AV_OPT_TYPE_FLOAT, {.dbl = .0f}, 0, 10, .flags = FLAGS }, + + { "use_tape", + "Specifies if the tape based SSIM 360 algorithm must be used independent of the input video types", + OFFSET(use_tape), AV_OPT_TYPE_INT, {.i64 = 0}, + 0, 1, .flags = FLAGS }, + + { "heatmap_str", + "Heatmap data for view-based evaluation. For heatmap file format, please refer to EntSphericalVideoHeatmapData.", + OFFSET(heatmap_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, .flags = FLAGS }, + + { "default_heatmap_width", + "Default heatmap dimension. Will be used when dimension is not specified in heatmap data.", + OFFSET(default_heatmap_w), AV_OPT_TYPE_INT, {.i64 = 32}, 1, 4096, .flags = FLAGS }, + + { "default_heatmap_height", + "Default heatmap dimension. Will be used when dimension is not specified in heatmap data.", + OFFSET(default_heatmap_h), AV_OPT_TYPE_INT, {.i64 = 16}, 1, 4096, .flags = FLAGS }, + + { NULL } +}; + +FRAMESYNC_DEFINE_CLASS(ssim360, SSIM360Context, fs); + +static void set_meta(AVDictionary **metadata, const char *key, char comp, float d) +{ + char value[128]; + snprintf(value, sizeof(value), "%0.2f", d); + if (comp) { + char key2[128]; + snprintf(key2, sizeof(key2), "%s%c", key, comp); + av_dict_set(metadata, key2, value, 0); + } else { + av_dict_set(metadata, key, value, 0); + } +} + +static void map_uninit(Map2D *map) +{ + av_freep(&map->value); +} + +static int map_init(Map2D *map, int w, int h) +{ + map->value = av_calloc(h * w, sizeof(*map->value)); + if (!map->value) + return AVERROR(ENOMEM); + + map->h = h; + map->w = w; + + return 0; +} + +static void map_list_free(HeatmapList **pl) +{ + HeatmapList *l = *pl; + + while (l) { + HeatmapList *next = l->next; + map_uninit(&l->map); + av_freep(&l); + l = next; + } + + *pl = NULL; +} + +static int map_alloc(HeatmapList **pl, int w, int h) +{ + HeatmapList *l; + int ret; + + l = av_mallocz(sizeof(*l)); + if (!l) + return AVERROR(ENOMEM); + + ret = map_init(&l->map, w, h); + if (ret < 0) { + av_freep(&l); + return ret; + } + + *pl = l; + return 0; +} + +static void +ssim360_4x4xn_16bit(const uint8_t *main8, ptrdiff_t main_stride, + const uint8_t *ref8, ptrdiff_t ref_stride, + int64_t (*sums)[4], int width) +{ + const uint16_t *main16 = (const uint16_t *)main8; + const uint16_t *ref16 = (const uint16_t *)ref8; + + main_stride >>= 1; + ref_stride >>= 1; + + for (int z = 0; z < width; z++) { + uint64_t s1 = 0, s2 = 0, ss = 0, s12 = 0; + + for (int y = 0; y < 4; y++) { + for (int x = 0; x < 4; x++) { + unsigned a = main16[x + y * main_stride]; + unsigned b = ref16[x + y * ref_stride]; + + s1 += a; + s2 += b; + ss += a*a; + ss += b*b; + s12 += a*b; + } + } + + sums[z][0] = s1; + sums[z][1] = s2; + sums[z][2] = ss; + sums[z][3] = s12; + main16 += 4; + ref16 += 4; + } +} + +static void +ssim360_4x4xn_8bit(const uint8_t *main, ptrdiff_t main_stride, + const uint8_t *ref, ptrdiff_t ref_stride, + int (*sums)[4], int width) +{ + for (int z = 0; z < width; z++) { + uint32_t s1 = 0, s2 = 0, ss = 0, s12 = 0; + + for (int y = 0; y < 4; y++) { + for (int x = 0; x < 4; x++) { + int a = main[x + y * main_stride]; + int b = ref[x + y * ref_stride]; + + s1 += a; + s2 += b; + ss += a*a; + ss += b*b; + s12 += a*b; + } + } + + sums[z][0] = s1; + sums[z][1] = s2; + sums[z][2] = ss; + sums[z][3] = s12; + main += 4; + ref += 4; + } +} + +static float ssim360_end1x(int64_t s1, int64_t s2, int64_t ss, int64_t s12, int max) +{ + int64_t ssim_c1 = (int64_t)(.01 * .01 * max * max * 64 + .5); + int64_t ssim_c2 = (int64_t)(.03 * .03 * max * max * 64 * 63 + .5); + + int64_t fs1 = s1; + int64_t fs2 = s2; + int64_t fss = ss; + int64_t fs12 = s12; + int64_t vars = fss * 64 - fs1 * fs1 - fs2 * fs2; + int64_t covar = fs12 * 64 - fs1 * fs2; + + return (float)(2 * fs1 * fs2 + ssim_c1) * (float)(2 * covar + ssim_c2) + / ((float)(fs1 * fs1 + fs2 * fs2 + ssim_c1) * (float)(vars + ssim_c2)); +} + +static float ssim360_end1(int s1, int s2, int ss, int s12) +{ + static const int ssim_c1 = (int)(.01*.01*255*255*64 + .5); + static const int ssim_c2 = (int)(.03*.03*255*255*64*63 + .5); + + int fs1 = s1; + int fs2 = s2; + int fss = ss; + int fs12 = s12; + int vars = fss * 64 - fs1 * fs1 - fs2 * fs2; + int covar = fs12 * 64 - fs1 * fs2; + + return (float)(2 * fs1 * fs2 + ssim_c1) * (float)(2 * covar + ssim_c2) + / ((float)(fs1 * fs1 + fs2 * fs2 + ssim_c1) * (float)(vars + ssim_c2)); +} + +static double +ssim360_endn_16bit(const int64_t (*sum0)[4], const int64_t (*sum1)[4], + int width, int max, + double *density_map, int map_width, double *total_weight) +{ + double ssim360 = 0.0, weight; + + for (int i = 0; i < width; i++) { + weight = density_map ? density_map[(int) ((0.5 + i) / width * map_width)] : 1.0; + ssim360 += weight * ssim360_end1x( + sum0[i][0] + sum0[i + 1][0] + sum1[i][0] + sum1[i + 1][0], + sum0[i][1] + sum0[i + 1][1] + sum1[i][1] + sum1[i + 1][1], + sum0[i][2] + sum0[i + 1][2] + sum1[i][2] + sum1[i + 1][2], + sum0[i][3] + sum0[i + 1][3] + sum1[i][3] + sum1[i + 1][3], + max); + *total_weight += weight; + } + return ssim360; +} + +static double +ssim360_endn_8bit(const int (*sum0)[4], const int (*sum1)[4], int width, + double *density_map, int map_width, double *total_weight) +{ + double ssim360 = 0.0, weight; + + for (int i = 0; i < width; i++) { + weight = density_map ? density_map[(int) ((0.5 + i) / width * map_width)] : 1.0; + ssim360 += weight * ssim360_end1( + sum0[i][0] + sum0[i + 1][0] + sum1[i][0] + sum1[i + 1][0], + sum0[i][1] + sum0[i + 1][1] + sum1[i][1] + sum1[i + 1][1], + sum0[i][2] + sum0[i + 1][2] + sum1[i][2] + sum1[i + 1][2], + sum0[i][3] + sum0[i + 1][3] + sum1[i][3] + sum1[i + 1][3]); + *total_weight += weight; + } + return ssim360; +} + +static double +ssim360_plane_16bit(uint8_t *main, int main_stride, + uint8_t *ref, int ref_stride, + int width, int height, void *temp, + int max, Map2D density) +{ + int z = 0; + double ssim360 = 0.0; + int64_t (*sum0)[4] = temp; + int64_t (*sum1)[4] = sum0 + (width >> 2) + 3; + double total_weight = 0.0; + + width >>= 2; + height >>= 2; + + for (int y = 1; y < height; y++) { + for (; z <= y; z++) { + FFSWAP(void*, sum0, sum1); + ssim360_4x4xn_16bit(&main[4 * z * main_stride], main_stride, + &ref[4 * z * ref_stride], ref_stride, + sum0, width); + } + ssim360 += ssim360_endn_16bit( + (const int64_t (*)[4])sum0, (const int64_t (*)[4])sum1, + width - 1, max, + density.value ? density.value + density.w * ((int) ((z - 1.0) / height * density.h)) : NULL, + density.w, &total_weight); + } + + return (double) (ssim360 / total_weight); +} + +static double +ssim360_plane_8bit(uint8_t *main, int main_stride, + uint8_t *ref, int ref_stride, + int width, int height, void *temp, + int max, Map2D density) +{ + int z = 0; + double ssim360 = 0.0; + int (*sum0)[4] = temp; + int (*sum1)[4] = sum0 + (width >> 2) + 3; + double total_weight = 0.0; + + width >>= 2; + height >>= 2; + + for (int y = 1; y < height; y++) { + for (; z <= y; z++) { + FFSWAP(void*, sum0, sum1); + ssim360_4x4xn_8bit( + &main[4 * z * main_stride], main_stride, + &ref[4 * z * ref_stride], ref_stride, + sum0, width); + } + ssim360 += ssim360_endn_8bit( + (const int (*)[4])sum0, (const int (*)[4])sum1, width - 1, + density.value ? density.value + density.w * ((int) ((z - 1.0) / height * density.h)) : NULL, + density.w, &total_weight); + } + + return (double) (ssim360 / total_weight); +} + +static double ssim360_db(double ssim360, double weight) +{ + return 10 * log10(weight / (weight - ssim360)); +} + +static int get_bilinear_sample(const uint8_t *data, BilinearMap *m, int max_value) +{ + static const int fixed_point_half = 1 << (FIXED_POINT_PRECISION - 1); + static const int inv_byte_mask = UINT_MAX << 8; + + int tl, tr, bl, br, v; + + if (max_value & inv_byte_mask) { + uint16_t *data16 = (uint16_t *)data; + tl = data16[m->tli]; + tr = data16[m->tri]; + bl = data16[m->bli]; + br = data16[m->bri]; + } else { + tl = data[m->tli]; + tr = data[m->tri]; + bl = data[m->bli]; + br = data[m->bri]; + } + + v = m->tlf * tl + + m->trf * tr + + m->blf * bl + + m->brf * br; + + // Round by half, and revert the fixed-point offset + return ((v + fixed_point_half) >> FIXED_POINT_PRECISION) & max_value; +} + +static void +ssim360_4x4x2_tape(const uint8_t *main, BilinearMap *main_maps, + const uint8_t *ref, BilinearMap *ref_maps, + int offset_y, int max_value, int (*sums)[4]) +{ + int offset_x = 0; + + // Two blocks along the width + for (int z = 0; z < 2; z++) { + int s1 = 0, s2 = 0, ss = 0, s12 = 0; + + // 4 pixel block from (offset_x, offset_y) + for (int y = offset_y; y < offset_y + 4; y++) { + int y_stride = y << 3; + for (int x = offset_x; x < offset_x + 4; x++) { + int map_index = x + y_stride; + int a = get_bilinear_sample(main, main_maps + map_index, max_value); + int b = get_bilinear_sample(ref, ref_maps + map_index, max_value); + + s1 += a; + s2 += b; + ss += a*a; + ss += b*b; + s12 += a*b; + } + } + + sums[z][0] = s1; + sums[z][1] = s2; + sums[z][2] = ss; + sums[z][3] = s12; + + offset_x += 4; + } +} + +static float get_radius_between_negative_and_positive_pi(float theta) +{ + int floor_theta_by_2pi, floor_theta_by_pi; + + // Convert theta to range [0, 2*pi] + floor_theta_by_2pi = (int)(theta / (2.0f * M_PI_F)) - (theta < 0.0f); + theta -= 2.0f * M_PI_F * floor_theta_by_2pi; + + // Convert theta to range [-pi, pi] + floor_theta_by_pi = theta / M_PI_F; + theta -= 2.0f * M_PI_F * floor_theta_by_pi; + return FFMIN(M_PI_F, FFMAX(-M_PI_F, theta)); +} + +static float get_heat(HeatmapList *heatmaps, float angular_resoluation, float norm_tape_pos) +{ + float pitch, yaw, norm_pitch, norm_yaw; + int w, h; + + if (!heatmaps) + return 1.0f; + + pitch = asinf(norm_tape_pos*2); + yaw = M_PI_2_F * pitch / angular_resoluation; + yaw = get_radius_between_negative_and_positive_pi(yaw); + + // normalize into [0,1] + norm_pitch = 1.0f - (pitch / M_PI_F + 0.5f); + norm_yaw = yaw / 2.0f / M_PI_F + 0.5f; + + // get heat on map + w = FFMIN(heatmaps->map.w - 1, FFMAX(0, heatmaps->map.w * norm_yaw)); + h = FFMIN(heatmaps->map.h - 1, FFMAX(0, heatmaps->map.h * norm_pitch)); + return heatmaps->map.value[h * heatmaps->map.w + w]; +} + +static double +ssim360_tape(uint8_t *main, BilinearMap *main_maps, + uint8_t *ref, BilinearMap *ref_maps, + int tape_length, int max_value, void *temp, + double *ssim360_hist, double *ssim360_hist_net, + float angular_resolution, HeatmapList *heatmaps) +{ + int horizontal_block_count = 2; + int vertical_block_count = tape_length >> 2; + + int z = 0, y; + // Since the tape will be very long and we need to average over all 8x8 blocks, use double + double ssim360 = 0.0; + double sum_weight = 0.0; + + int (*sum0)[4] = temp; + int (*sum1)[4] = sum0 + horizontal_block_count + 3; + + for (y = 1; y < vertical_block_count; y++) { + int fs1, fs2, fss, fs12, hist_index; + float norm_tape_pos, weight; + double sample_ssim360; + + for (; z <= y; z++) { + FFSWAP(void*, sum0, sum1); + ssim360_4x4x2_tape(main, main_maps, ref, ref_maps, z*4, max_value, sum0); + } + + // Given we have only one 8x8 block, following sums fit within 26 bits even for 10bit videos + fs1 = sum0[0][0] + sum0[1][0] + sum1[0][0] + sum1[1][0]; + fs2 = sum0[0][1] + sum0[1][1] + sum1[0][1] + sum1[1][1]; + fss = sum0[0][2] + sum0[1][2] + sum1[0][2] + sum1[1][2]; + fs12 = sum0[0][3] + sum0[1][3] + sum1[0][3] + sum1[1][3]; + + if (max_value > 255) { + // Since we need high precision to multiply fss / fs12 by 64, use double + double ssim_c1_d = .01*.01*64*max_value*max_value; + double ssim_c2_d = .03*.03*64*63*max_value*max_value; + + double vars = 64. * fss - 1. * fs1 * fs1 - 1. * fs2 * fs2; + double covar = 64. * fs12 - 1.*fs1 * fs2; + sample_ssim360 = (2. * fs1 * fs2 + ssim_c1_d) * (2. * covar + ssim_c2_d) + / ((1. * fs1 * fs1 + 1. * fs2 * fs2 + ssim_c1_d) * (1. * vars + ssim_c2_d)); + } else { + static const int ssim_c1 = (int)(.01*.01*255*255*64 + .5); + static const int ssim_c2 = (int)(.03*.03*255*255*64*63 + .5); + + int vars = fss * 64 - fs1 * fs1 - fs2 * fs2; + int covar = fs12 * 64 - fs1 * fs2; + sample_ssim360 = (double)(2 * fs1 * fs2 + ssim_c1) * (double)(2 * covar + ssim_c2) + / ((double)(fs1 * fs1 + fs2 * fs2 + ssim_c1) * (double)(vars + ssim_c2)); + } + + hist_index = (int)(sample_ssim360 * ((double)SSIM360_HIST_SIZE - .5)); + hist_index = av_clip(hist_index, 0, SSIM360_HIST_SIZE - 1); + + norm_tape_pos = (y - 0.5f) / (vertical_block_count - 1.0f) - 0.5f; + // weight from an input heatmap if available, otherwise weight = 1.0 + weight = get_heat(heatmaps, angular_resolution, norm_tape_pos); + ssim360_hist[hist_index] += weight; + *ssim360_hist_net += weight; + + ssim360 += (sample_ssim360 * weight); + sum_weight += weight; + } + + return ssim360 / sum_weight; +} + +static void compute_bilinear_map(SampleParams *p, BilinearMap *m, float x, float y) +{ + float fixed_point_scale = (float)(1 << FIXED_POINT_PRECISION); + + // All operations in here will fit in the 22 bit mantissa of floating point, + // since the fixed point precision is well under 22 bits + float x_image = av_clipf(x * p->x_image_range, 0, p->x_image_range) + p->x_image_offset; + float y_image = av_clipf(y * p->y_image_range, 0, p->y_image_range) + p->y_image_offset; + + int x_floor = x_image; + int y_floor = y_image; + float x_diff = x_image - x_floor; + float y_diff = y_image - y_floor; + + int x_ceil = x_floor + (x_diff > 1e-6); + int y_ceil = y_floor + (y_diff > 1e-6); + float x_inv_diff = 1.0f - x_diff; + float y_inv_diff = 1.0f - y_diff; + + // Indices of the 4 samples from source frame + m->tli = x_floor + y_floor * p->stride; + m->tri = x_ceil + y_floor * p->stride; + m->bli = x_floor + y_ceil * p->stride; + m->bri = x_ceil + y_ceil * p->stride; + + // Scale to be applied to each of the 4 samples from source frame + m->tlf = x_inv_diff * y_inv_diff * fixed_point_scale; + m->trf = x_diff * y_inv_diff * fixed_point_scale; + m->blf = x_inv_diff * y_diff * fixed_point_scale; + m->brf = x_diff * y_diff * fixed_point_scale; +} + +static void get_equirect_map(float phi, float theta, float *x, float *y) +{ + *x = 0.5f + theta / (2.0f * M_PI_F); + // y increases downwards + *y = 0.5f - phi / M_PI_F; +} + +static void get_barrel_map(float phi, float theta, float *x, float *y) +{ + float abs_phi = FFABS(phi); + + if (abs_phi <= M_PI_4_F) { + // Equirect region + *x = 0.8f * (0.5f + theta / BARREL_THETA_RANGE); + // y increases downwards + *y = 0.5f - phi / BARREL_PHI_RANGE; + } else { + // Radial ratio on a unit circle = cot(abs_phi) / (expansion_cefficient). + // Using cos(abs_phi)/sin(abs_phi) explicitly to avoid division by zero + float radial_ratio = cosf(abs_phi) / (sinf(abs_phi) * DEFAULT_EXPANSION_COEF); + float circle_x = radial_ratio * sinf(theta); + float circle_y = radial_ratio * cosf(theta); + float offset_y = 0.25f; + if (phi < 0) { + // Bottom circle: theta increases clockwise, and front is upward + circle_y *= -1.0f; + offset_y += 0.5f; + } + + *x = 0.8f + 0.1f * (1.0f + circle_x); + *y = offset_y + 0.25f * circle_y; + } +} + +static void get_barrel_split_map(float phi, float theta, float expand_coef, float *x, float *y) +{ + float abs_phi = FFABS(phi); + + // Front Face [-PI/2, PI/2] -> [0,1]. + // Back Face [PI/2, PI] and [-PI, -PI/2] -> [1, 2] + float radian_pi_theta = theta / M_PI_F + 0.5f; + int vFace; + + if (radian_pi_theta < 0.0f) + radian_pi_theta += 2.0f; + + // Front face at top (= 0), back face at bottom (= 1). + vFace = radian_pi_theta >= 1.0f; + + if (abs_phi <= M_PI_4_F) { + // Equirect region + *x = 2.0f / 3.0f * (0.5f + (radian_pi_theta - vFace - 0.5f) / expand_coef); + // y increases downwards + *y = 0.25f + 0.5f * vFace - phi / (M_PI_F * expand_coef); + } else { + // Radial ratio on a unit circle = cot(abs_phi) / (expansion_cefficient). + // Using cos(abs_phi)/sin(abs_phi) explicitly to avoid division by zero + float radial_ratio = cosf(abs_phi) / (sinf(abs_phi) * expand_coef); + float circle_x = radial_ratio * sinf(theta); + float circle_y = radial_ratio * cosf(theta); + float offset_y = 0.25f; + + if (vFace == 1) { + // Back Face: Flip + circle_x *= -1.0f; + circle_y = (circle_y >= 0.0f) ? (1 - circle_y) : (-1 - circle_y); + offset_y += 0.5f; + + // Bottom circle: theta increases clockwise + if (phi < 0) + circle_y *= -1.0f; + } else { + // Front Face + // Bottom circle: theta increases clockwise + if (phi < 0) + circle_y *= -1.0f; + } + + *x = 2.0f / 3.0f + 0.5f / 3.0f * (1.0f + circle_x); + *y = offset_y + 0.25f * circle_y / expand_coef; // y direction of expand_coeff (margin) + } +} + +// Returns cube face, and provided face_x & face_y will range from [0, 1] +static int get_cubemap_face_map(float axis_vec_x, float axis_vec_y, float axis_vec_z, float *face_x, float *face_y) +{ + // To check if phi, theta hits the top / bottom faces, we check the hit point of + // the axis vector on planes y = 1 and y = -1, and see if x & z are within [-1, 1] + + // 0.577 < 1 / sqrt(3), which is less than the smallest sin(phi) falling on top/bottom faces + // This angle check will save computation from unnecessarily checking the top/bottom faces + if (FFABS(axis_vec_y) > 0.577f) { + float x_hit = axis_vec_x / FFABS(axis_vec_y); + float z_hit = axis_vec_z / axis_vec_y; + + if (FFABS(x_hit) <= 1.f && FFABS(z_hit) <= 1.f) { + *face_x = x_hit; + // y increases downwards + *face_y = z_hit; + return axis_vec_y > 0 ? TOP : BOTTOM; + } + } + + // Check for left / right faces + if (FFABS(axis_vec_x) > 0.577f) { + float z_hit = -axis_vec_z / axis_vec_x; + float y_hit = axis_vec_y / FFABS(axis_vec_x); + + if (FFABS(z_hit) <= 1.f && FFABS(y_hit) <= 1.f) { + *face_x = z_hit; + // y increases downwards + *face_y = -y_hit; + return axis_vec_x > 0 ? RIGHT : LEFT; + } + } + + // Front / back faces + *face_x = axis_vec_x / axis_vec_z; + // y increases downwards + *face_y = -axis_vec_y / FFABS(axis_vec_z); + + return axis_vec_z > 0 ? FRONT : BACK; +} + +static void get_cubemap32_map(float phi, float theta, float *x, float *y) +{ + // face_projection_map maps each cube face to an index representing the face on the projection + // The indices 0->5 for cubemap 32 goes as: + // [0, 1, 2] as row 1, left to right + // [3, 4, 5] as row 2, left to right + static const int face_projection_map[] = { + [RIGHT] = 0, [LEFT] = 1, [TOP] = 2, + [BOTTOM] = 3, [FRONT] = 4, [BACK] = 5, + }; + + float axis_vec_x = cosf(phi) * sinf(theta); + float axis_vec_y = sinf(phi); + float axis_vec_z = cosf(phi) * cosf(theta); + float face_x = 0, face_y = 0; + int face_index = get_cubemap_face_map(axis_vec_x, axis_vec_y, axis_vec_z, &face_x, &face_y); + + float x_offset = 1.f / 3.f * (face_projection_map[face_index] % 3); + float y_offset = .5f * (face_projection_map[face_index] / 3); + + *x = x_offset + (face_x / DEFAULT_EXPANSION_COEF + 1.f) / 6.f; + *y = y_offset + (face_y / DEFAULT_EXPANSION_COEF + 1.f) / 4.f; +} + +static void get_rotated_cubemap_map(float phi, float theta, float expand_coef, float *x, float *y) +{ + // face_projection_map maps each cube face to an index representing the face on the projection + // The indices 0->5 for rotated cubemap goes as: + // [0, 1] as row 1, left to right + // [2, 3] as row 2, left to right + // [4, 5] as row 3, left to right + static const int face_projection_map[] = { + [LEFT] = 0, [TOP] = 1, + [FRONT] = 2, [BACK] = 3, + [RIGHT] = 4, [BOTTOM] = 5, + }; + + float axis_yaw_vec_x, axis_yaw_vec_y, axis_yaw_vec_z; + float axis_pitch_vec_z, axis_pitch_vec_y; + float x_offset, y_offset; + float face_x = 0, face_y = 0; + int face_index; + + // Unrotate the cube and fix the face map: + // First undo the 45 degree yaw + theta += M_PI_4_F; + + // Now we are looking at the middle of an edge. So convert to axis vector & undo the pitch + axis_yaw_vec_x = cosf(phi) * sinf(theta); + axis_yaw_vec_y = sinf(phi); + axis_yaw_vec_z = cosf(phi) * cosf(theta); + + // The pitch axis is along +x, and has value of -45 degree. So, only y and z components change + axis_pitch_vec_z = (axis_yaw_vec_z - axis_yaw_vec_y) / M_SQRT2_F; + axis_pitch_vec_y = (axis_yaw_vec_y + axis_yaw_vec_z) / M_SQRT2_F; + + face_index = get_cubemap_face_map(axis_yaw_vec_x, axis_pitch_vec_y, axis_pitch_vec_z, &face_x, &face_y); + + // Correct for the orientation of the axes on the faces + if (face_index == LEFT || face_index == FRONT || face_index == RIGHT) { + // x increases downwards & y increases towards left + float upright_y = face_y; + face_y = face_x; + face_x = -upright_y; + } else if (face_index == TOP || face_index == BOTTOM) { + // turn the face upside-down for top and bottom + face_x *= -1.f; + face_y *= -1.f; + } + + x_offset = .5f * (face_projection_map[face_index] & 1); + y_offset = 1.f / 3.f * (face_projection_map[face_index] >> 1); + + *x = x_offset + (face_x / expand_coef + 1.f) / 4.f; + *y = y_offset + (face_y / expand_coef + 1.f) / 6.f; +} + +static void get_projected_map(float phi, float theta, SampleParams *p, BilinearMap *m) +{ + float x = 0, y = 0; + switch(p->projection) { +// TODO: Calculate for CDS + case PROJECTION_CUBEMAP23: + get_rotated_cubemap_map(phi, theta, p->expand_coef, &x, &y); + break; + case PROJECTION_CUBEMAP32: + get_cubemap32_map(phi, theta, &x, &y); + break; + case PROJECTION_BARREL: + get_barrel_map(phi, theta, &x, &y); + break; + case PROJECTION_BARREL_SPLIT: + get_barrel_split_map(phi, theta, p->expand_coef, &x, &y); + break; + // Assume PROJECTION_EQUIRECT as the default + case PROJECTION_EQUIRECT: + default: + get_equirect_map(phi, theta, &x, &y); + break; + } + compute_bilinear_map(p, m, x, y); +} + +static int tape_supports_projection(int projection) +{ + switch(projection) { + case PROJECTION_CUBEMAP23: + case PROJECTION_CUBEMAP32: + case PROJECTION_BARREL: + case PROJECTION_BARREL_SPLIT: + case PROJECTION_EQUIRECT: + return 1; + default: + return 0; + } +} + +static float get_tape_angular_resolution(int projection, float expand_coef, int image_width, int image_height) +{ + // NOTE: The angular resolution of a projected sphere is defined as + // the maximum possible horizontal angle of a pixel on the equator. + // We apply an intentional bias to the horizon as opposed to the meridian, + // since the view direction of most content is rarely closer to the poles + + switch(projection) { +// TODO: Calculate for CDS + case PROJECTION_CUBEMAP23: + // Approximating atanf(pixel_width / (half_edge_width * sqrt2)) = pixel_width / (half_face_width * sqrt2) + return expand_coef / (M_SQRT2_F * image_width / 4.f); + case PROJECTION_CUBEMAP32: + // Approximating atanf(pixel_width / half_face_width) = pixel_width / half_face_width + return DEFAULT_EXPANSION_COEF / (image_width / 6.f); + case PROJECTION_BARREL: + return FFMAX(BARREL_THETA_RANGE / (0.8f * image_width), BARREL_PHI_RANGE / image_height); + case PROJECTION_BARREL_SPLIT: + return FFMAX((expand_coef * M_PI_F) / (2.0f / 3.0f * image_width), + expand_coef * M_PI_2_F / (image_height / 2.0f)); + // Assume PROJECTION_EQUIRECT as the default + case PROJECTION_EQUIRECT: + default: + return FFMAX(2.0f * M_PI_F / image_width, M_PI_F / image_height); + } +} + +static int +generate_eye_tape_map(SSIM360Context *s, + int plane, int eye, + SampleParams *ref_sample_params, + SampleParams *main_sample_params) +{ + int ref_image_width = ref_sample_params->x_image_range + 1; + int ref_image_height = ref_sample_params->y_image_range + 1; + + float angular_resolution = + get_tape_angular_resolution(s->ref_projection, 1.f + s->ref_pad, + ref_image_width, ref_image_height); + + float conversion_factor = M_PI_2_F / (angular_resolution * angular_resolution); + float start_phi = -M_PI_2_F + 4.0f * angular_resolution; + float start_x = conversion_factor * sinf(start_phi); + float end_phi = M_PI_2_F - 3.0f * angular_resolution; + float end_x = conversion_factor * sinf(end_phi); + float x_range = end_x - start_x; + + // Ensure tape length is a multiple of 4, for full SSIM block coverage + int tape_length = s->tape_length[plane] = ((int)ROUNDED_DIV(x_range, 4)) << 2; + + s->ref_tape_map[plane][eye] = av_malloc_array(tape_length * 8, sizeof(BilinearMap)); + s->main_tape_map[plane][eye] = av_malloc_array(tape_length * 8, sizeof(BilinearMap)); + if (!s->ref_tape_map[plane][eye] || !s->main_tape_map[plane][eye]) + return AVERROR(ENOMEM); + + s->angular_resolution[plane][eye] = angular_resolution; + + // For easy memory access, we navigate the tape lengthwise on y + for (int y_index = 0; y_index < tape_length; y_index ++) { + int y_stride = y_index << 3; + + float x = start_x + x_range * (y_index / (tape_length - 1.0f)); + // phi will be in range [-pi/2, pi/2] + float mid_phi = asinf(x / conversion_factor); + + float theta = mid_phi * M_PI_2_F / angular_resolution; + theta = get_radius_between_negative_and_positive_pi(theta); + + for (int x_index = 0; x_index < 8; x_index ++) { + float phi = mid_phi + angular_resolution * (3.0f - x_index); + int tape_index = y_stride + x_index; + get_projected_map(phi, theta, ref_sample_params, &s->ref_tape_map [plane][eye][tape_index]); + get_projected_map(phi, theta, main_sample_params, &s->main_tape_map[plane][eye][tape_index]); + } + } + + return 0; +} + +static int generate_tape_maps(SSIM360Context *s, AVFrame *main, const AVFrame *ref) +{ + // A tape is a long segment with 8 pixels thickness, with the angular center at the middle (below 4th pixel). + // When it takes a full loop around a sphere, it will overlap the starting point at half the width from above. + int ref_stereo_format = s->ref_stereo_format; + int main_stereo_format = s->main_stereo_format; + int are_both_stereo = (main_stereo_format != STEREO_FORMAT_MONO) && (ref_stereo_format != STEREO_FORMAT_MONO); + int min_eye_count = 1 + are_both_stereo; + int ret; + + for (int i = 0; i < s->nb_components; i ++) { + int ref_width = s->ref_planewidth[i]; + int ref_height = s->ref_planeheight[i]; + int main_width = s->main_planewidth[i]; + int main_height = s->main_planeheight[i]; + + int is_ref_LR = (ref_stereo_format == STEREO_FORMAT_LR); + int is_ref_TB = (ref_stereo_format == STEREO_FORMAT_TB); + int is_main_LR = (main_stereo_format == STEREO_FORMAT_LR); + int is_main_TB = (main_stereo_format == STEREO_FORMAT_TB); + + int ref_image_width = is_ref_LR ? ref_width >> 1 : ref_width; + int ref_image_height = is_ref_TB ? ref_height >> 1 : ref_height; + int main_image_width = is_main_LR ? main_width >> 1 : main_width; + int main_image_height = is_main_TB ? main_height >> 1 : main_height; + + for (int eye = 0; eye < min_eye_count; eye ++) { + SampleParams ref_sample_params = { + .stride = ref->linesize[i], + .planewidth = ref_width, + .planeheight = ref_height, + .x_image_range = ref_image_width - 1, + .y_image_range = ref_image_height - 1, + .x_image_offset = is_ref_LR * eye * ref_image_width, + .y_image_offset = is_ref_TB * eye * ref_image_height, + .projection = s->ref_projection, + .expand_coef = 1.f + s->ref_pad, + }; + + SampleParams main_sample_params = { + .stride = main->linesize[i], + .planewidth = main_width, + .planeheight = main_height, + .x_image_range = main_image_width - 1, + .y_image_range = main_image_height - 1, + .x_image_offset = is_main_LR * eye * main_image_width, + .y_image_offset = is_main_TB * eye * main_image_height, + .projection = s->main_projection, + .expand_coef = 1.f + s->main_pad, + }; + + ret = generate_eye_tape_map(s, i, eye, &ref_sample_params, &main_sample_params); + if (ret < 0) + return ret; + } + } + + return 0; +} + +static int do_ssim360(FFFrameSync *fs) +{ + AVFilterContext *ctx = fs->parent; + SSIM360Context *s = ctx->priv; + AVFrame *master, *ref; + AVDictionary **metadata; + double c[4], ssim360v = 0.0, ssim360p50 = 0.0; + int i, ret; + int need_frame_skip = s->nb_net_frames % (s->frame_skip_ratio + 1); + HeatmapList* h_ptr = NULL; + + ret = ff_framesync_dualinput_get(fs, &master, &ref); + if (ret < 0) + return ret; + + s->nb_net_frames++; + + if (need_frame_skip) + return ff_filter_frame(ctx->outputs[0], master); + + metadata = &master->metadata; + + if (s->use_tape && !s->tape_length[0]) { + ret = generate_tape_maps(s, master, ref); + if (ret < 0) + return ret; + } + + for (i = 0; i < s->nb_components; i++) { + if (s->use_tape) { + c[i] = ssim360_tape(master->data[i], s->main_tape_map[i][0], + ref->data[i], s->ref_tape_map [i][0], + s->tape_length[i], s->max, s->temp, + s->ssim360_hist[i], &s->ssim360_hist_net[i], + s->angular_resolution[i][0], s->heatmaps); + + if (s->ref_tape_map[i][1]) { + c[i] += ssim360_tape(master->data[i], s->main_tape_map[i][1], + ref->data[i], s->ref_tape_map[i][1], + s->tape_length[i], s->max, s->temp, + s->ssim360_hist[i], &s->ssim360_hist_net[i], + s->angular_resolution[i][1], s->heatmaps); + c[i] /= 2.f; + } + } else { + c[i] = s->ssim360_plane(master->data[i], master->linesize[i], + ref->data[i], ref->linesize[i], + s->ref_planewidth[i], s->ref_planeheight[i], + s->temp, s->max, s->density); + } + + s->ssim360[i] += c[i]; + ssim360v += s->coefs[i] * c[i]; + } + + s->nb_ssim_frames++; + if (s->heatmaps) { + map_uninit(&s->heatmaps->map); + h_ptr = s->heatmaps; + s->heatmaps = s->heatmaps->next; + av_freep(&h_ptr); + } + s->ssim360_total += ssim360v; + + // Record percentiles from histogram and attach metadata when using tape + if (s->use_tape) { + int i, p, hist_indices[4]; + double hist_weight[4]; + + for (i = 0; i < s->nb_components; i++) { + hist_indices[i] = SSIM360_HIST_SIZE - 1; + hist_weight[i] = 0; + } + + for (p = 0; PERCENTILE_LIST[p] >= 0.0; p ++) { + for (i = 0; i < s->nb_components; i++) { + double target_weight, ssim360p; + + // Target weight = total number of samples above the specified percentile + target_weight = (1. - PERCENTILE_LIST[p]) * s->ssim360_hist_net[i]; + target_weight = FFMAX(target_weight, 1); + while(hist_indices[i] >= 0 && hist_weight[i] < target_weight) { + hist_weight[i] += s->ssim360_hist[i][hist_indices[i]]; + hist_indices[i] --; + } + + ssim360p = (double)(hist_indices[i] + 1) / (double)(SSIM360_HIST_SIZE - 1); + if (PERCENTILE_LIST[p] == 0.5) + ssim360p50 += s->coefs[i] * ssim360p; + s->ssim360_percentile_sum[i][p] += ssim360p; + } + } + + for (i = 0; i < s->nb_components; i++) { + memset(s->ssim360_hist[i], 0, SSIM360_HIST_SIZE * sizeof(double)); + s->ssim360_hist_net[i] = 0; + } + + for (i = 0; i < s->nb_components; i++) { + int cidx = s->is_rgb ? s->rgba_map[i] : i; + set_meta(metadata, "lavfi.ssim360.", s->comps[i], c[cidx]); + } + + // Use p50 as the aggregated value + set_meta(metadata, "lavfi.ssim360.All", 0, ssim360p50); + set_meta(metadata, "lavfi.ssim360.dB", 0, ssim360_db(ssim360p50, 1.0)); + + if (s->stats_file) { + fprintf(s->stats_file, "n:%"PRId64" ", s->nb_ssim_frames); + + for (i = 0; i < s->nb_components; i++) { + int cidx = s->is_rgb ? s->rgba_map[i] : i; + fprintf(s->stats_file, "%c:%f ", s->comps[i], c[cidx]); + } + + fprintf(s->stats_file, "All:%f (%f)\n", ssim360p50, ssim360_db(ssim360p50, 1.0)); + } + } + + return ff_filter_frame(ctx->outputs[0], master); +} + +static int parse_heatmaps(void *logctx, HeatmapList **proot, + const char *data, int w, int h) +{ + HeatmapList *root = NULL; + HeatmapList **next = &root; + + int ret; + + // skip video id line + data = strchr(data, '\n'); + if (!data) { + av_log(logctx, AV_LOG_ERROR, "Invalid heatmap syntax\n"); + return AVERROR(EINVAL); + } + data++; + + while (*data) { + HeatmapList *cur; + char *line = av_get_token(&data, "\n"); + char *saveptr, *val; + int i; + + if (!line) { + ret = AVERROR(ENOMEM); + goto fail; + } + + // first value is frame id + av_strtok(line, ",", &saveptr); + + ret = map_alloc(next, w, h); + if (ret < 0) + goto line_fail; + + cur = *next; + next = &cur->next; + + i = 0; + while ((val = av_strtok(NULL, ",", &saveptr))) { + if (i >= w * h) { + av_log(logctx, AV_LOG_ERROR, "Too many entries in a heat map\n"); + ret = AVERROR(EINVAL); + goto line_fail; + } + + cur->map.value[i++] = atof(val); + } + +line_fail: + av_freep(&line); + if (ret < 0) + goto fail; + } + + *proot = root; + + return 0; +fail: + map_list_free(&root); + return ret; +} + +static av_cold int init(AVFilterContext *ctx) +{ + SSIM360Context *s = ctx->priv; + int err; + + if (s->stats_file_str) { + if (!strcmp(s->stats_file_str, "-")) { + s->stats_file = stdout; + } else { + s->stats_file = avpriv_fopen_utf8(s->stats_file_str, "w"); + if (!s->stats_file) { + char buf[128]; + + err = AVERROR(errno); + av_strerror(err, buf, sizeof(buf)); + av_log(ctx, AV_LOG_ERROR, "Could not open stats file %s: %s\n", + s->stats_file_str, buf); + return err; + } + } + } + + if (s->use_tape && s->heatmap_str) { + err = parse_heatmaps(ctx, &s->heatmaps, s->heatmap_str, + s->default_heatmap_w, s->default_heatmap_h); + if (err < 0) + return err; + } + + s->fs.on_event = do_ssim360; + return 0; +} + +static int config_input_main(AVFilterLink *inlink) +{ + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + AVFilterContext *ctx = inlink->dst; + SSIM360Context *s = ctx->priv; + + s->main_planeheight[0] = inlink->h; + s->main_planeheight[3] = inlink->h; + s->main_planeheight[1] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h); + s->main_planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h); + + s->main_planewidth[0] = inlink->w; + s->main_planewidth[3] = inlink->w; + s->main_planewidth[1] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w); + s->main_planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w); + + // If main projection is unindentified, assume it is same as reference + if (s->main_projection == PROJECTION_N) + s->main_projection = s->ref_projection; + + // If main stereo format is unindentified, assume it is same as reference + if (s->main_stereo_format == STEREO_FORMAT_N) + s->main_stereo_format = s->ref_stereo_format; + + return 0; +} + +static int generate_density_map(SSIM360Context *s, int w, int h) +{ + double d, r_square, cos_square; + int ow, oh, ret; + + ret = map_init(&s->density, w, h); + if (ret < 0) + return ret; + + switch (s->ref_stereo_format) { + case STEREO_FORMAT_TB: + h >>= 1; + break; + case STEREO_FORMAT_LR: + w >>= 1; + break; + } + + switch (s->ref_projection) { + case PROJECTION_EQUIRECT: + for (int i = 0; i < h; i++) { + d = cos(((0.5 + i) / h - 0.5) * M_PI); + for (int j = 0; j < w; j++) + s->density.value[i * w + j] = d; + } + break; + case PROJECTION_CUBEMAP32: + // for one quater of a face + for (int i = 0; i < h / 4; i++) { + for (int j = 0; j < w / 6; j++) { + // r = normalized distance to the face center + r_square = + (0.5 + i) / (h / 2) * (0.5 + i) / (h / 2) + + (0.5 + j) / (w / 3) * (0.5 + j) / (w / 3); + r_square /= DEFAULT_EXPANSION_COEF * DEFAULT_EXPANSION_COEF; + cos_square = 0.25 / (r_square + 0.25); + d = pow(cos_square, 1.5); + + for (int face = 0; face < 6; face++) { + // center of a face + switch (face) { + case 0: + oh = h / 4; + ow = w / 6; + break; + case 1: + oh = h / 4; + ow = w / 6 + w / 3; + break; + case 2: + oh = h / 4; + ow = w / 6 + 2 * w / 3; + break; + case 3: + oh = h / 4 + h / 2; + ow = w / 6; + break; + case 4: + oh = h / 4 + h / 2; + ow = w / 6 + w / 3; + break; + case 5: + oh = h / 4 + h / 2; + ow = w / 6 + 2 * w / 3; + break; + } + s->density.value[(oh - 1 - i) * w + ow - 1 - j] = d; + s->density.value[(oh - 1 - i) * w + ow + j] = d; + s->density.value[(oh + i) * w + ow - 1 - j] = d; + s->density.value[(oh + i) * w + ow + j] = d; + } + } + } + break; + case PROJECTION_CUBEMAP23: + // for one quater of a face + for (int i = 0; i < h / 6; i++) { + for (int j = 0; j < w / 4; j++) { + // r = normalized distance to the face center + r_square = + (0.5 + i) / (h / 3) * (0.5 + i) / (h / 3) + + (0.5 + j) / (w / 2) * (0.5 + j) / (w / 2); + r_square /= (1.f + s->ref_pad) * (1.f + s->ref_pad); + cos_square = 0.25 / (r_square + 0.25); + d = pow(cos_square, 1.5); + + for (int face = 0; face < 6; face++) { + // center of a face + switch (face) { + case 0: + ow = w / 4; + oh = h / 6; + break; + case 1: + ow = w / 4; + oh = h / 6 + h / 3; + break; + case 2: + ow = w / 4; + oh = h / 6 + 2 * h / 3; + break; + case 3: + ow = w / 4 + w / 2; + oh = h / 6; + break; + case 4: + ow = w / 4 + w / 2; + oh = h / 6 + h / 3; + break; + case 5: + ow = w / 4 + w / 2; + oh = h / 6 + 2 * h / 3; + break; + } + s->density.value[(oh - 1 - i) * w + ow - 1 - j] = d; + s->density.value[(oh - 1 - i) * w + ow + j] = d; + s->density.value[(oh + i) * w + ow - 1 - j] = d; + s->density.value[(oh + i) * w + ow + j] = d; + } + } + } + break; + case PROJECTION_BARREL: + // side face + for (int i = 0; i < h; i++) { + for (int j = 0; j < w * 4 / 5; j++) { + d = cos(((0.5 + i) / h - 0.5) * DEFAULT_EXPANSION_COEF * M_PI_2); + s->density.value[i * w + j] = d * d * d; + } + } + // top and bottom + for (int i = 0; i < h; i++) { + for (int j = w * 4 / 5; j < w; j++) { + double dx = DEFAULT_EXPANSION_COEF * (0.5 + j - w * 0.90) / (w * 0.10); + double dx_squared = dx * dx; + + double top_dy = DEFAULT_EXPANSION_COEF * (0.5 + i - h * 0.25) / (h * 0.25); + double top_dy_squared = top_dy * top_dy; + + double bottom_dy = DEFAULT_EXPANSION_COEF * (0.5 + i - h * 0.75) / (h * 0.25); + double bottom_dy_squared = bottom_dy * bottom_dy; + + // normalized distance to the circle center + r_square = (i < h / 2 ? top_dy_squared : bottom_dy_squared) + dx_squared; + if (r_square > 1.0) + continue; + + cos_square = 1.0 / (r_square + 1.0); + d = pow(cos_square, 1.5); + s->density.value[i * w + j] = d; + } + } + break; + default: + // TODO: SSIM360_v1 + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) + s->density.value[i * w + j] = 0; + } + } + + switch (s->ref_stereo_format) { + case STEREO_FORMAT_TB: + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) + s->density.value[(i + h) * w + j] = s->density.value[i * w + j]; + } + break; + case STEREO_FORMAT_LR: + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) + s->density.value[i * w + j + w] = s->density.value[i * w + j]; + } + } + + return 0; +} + +static int config_input_ref(AVFilterLink *inlink) +{ + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + AVFilterContext *ctx = inlink->dst; + SSIM360Context *s = ctx->priv; + int sum = 0; + + s->nb_components = desc->nb_components; + + s->ref_planeheight[0] = inlink->h; + s->ref_planeheight[3] = inlink->h; + s->ref_planeheight[1] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h); + s->ref_planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h); + + s->ref_planewidth[0] = inlink->w; + s->ref_planewidth[3] = inlink->w; + s->ref_planewidth[1] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w); + s->ref_planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w); + + s->is_rgb = ff_fill_rgba_map(s->rgba_map, inlink->format) >= 0; + s->comps[0] = s->is_rgb ? 'R' : 'Y'; + s->comps[1] = s->is_rgb ? 'G' : 'U'; + s->comps[2] = s->is_rgb ? 'B' : 'V'; + s->comps[3] = 'A'; + + // If chroma computation is disabled, and the format is YUV, skip U & V channels + if (!s->is_rgb && !s->compute_chroma) + s->nb_components = 1; + + s->max = (1 << desc->comp[0].depth) - 1; + + s->ssim360_plane = desc->comp[0].depth > 8 ? ssim360_plane_16bit : ssim360_plane_8bit; + + for (int i = 0; i < s->nb_components; i++) + sum += s->ref_planeheight[i] * s->ref_planewidth[i]; + for (int i = 0; i < s->nb_components; i++) + s->coefs[i] = (double) s->ref_planeheight[i] * s->ref_planewidth[i] / sum; + + return 0; +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + SSIM360Context *s = ctx->priv; + AVFilterLink *mainlink = ctx->inputs[0]; + AVFilterLink *reflink = ctx->inputs[0]; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format); + int ret; + + // Use tape algorithm if any of frame sizes, projections or stereo format are not equal + if (ctx->inputs[0]->w != ctx->inputs[1]->w || ctx->inputs[0]->h != ctx->inputs[1]->h || + s->ref_projection != s->main_projection || s->ref_stereo_format != s->main_stereo_format) + s->use_tape = 1; + + // Finally, if we have decided to / forced to use tape, check if tape supports both input and output projection + if (s->use_tape && + !(tape_supports_projection(s->main_projection) && + tape_supports_projection(s->ref_projection))) { + av_log(ctx, AV_LOG_ERROR, "Projection is unsupported for the tape based algorithm\n"); + return AVERROR(EINVAL); + } + + if (s->use_tape) { + // s->temp will be allocated for the tape width = 8. The tape is long downwards + s->temp = av_malloc_array((2 * 8 + 12), sizeof(*s->temp)); + if (!s->temp) + return AVERROR(ENOMEM); + + memset(s->ssim360_percentile_sum, 0, sizeof(s->ssim360_percentile_sum)); + + for (int i = 0; i < s->nb_components; i++) { + FF_ALLOCZ_TYPED_ARRAY(s->ssim360_hist[i], SSIM360_HIST_SIZE); + if (!s->ssim360_hist[i]) + return AVERROR(ENOMEM); + } + } else { + s->temp = av_malloc_array((2 * reflink->w + 12), sizeof(*s->temp) * (1 + (desc->comp[0].depth > 8))); + if (!s->temp) + return AVERROR(ENOMEM); + + if (!s->density.value) { + ret = generate_density_map(s, reflink->w, reflink->h); + if (ret < 0) + return ret; + } + } + + ret = ff_framesync_init_dualinput(&s->fs, ctx); + if (ret < 0) + return ret; + + outlink->w = mainlink->w; + outlink->h = mainlink->h; + outlink->time_base = mainlink->time_base; + outlink->sample_aspect_ratio = mainlink->sample_aspect_ratio; + outlink->frame_rate = mainlink->frame_rate; + + s->fs.opt_shortest = 1; + s->fs.opt_repeatlast = 1; + + ret = ff_framesync_configure(&s->fs); + if (ret < 0) + return ret; + + return 0; +} + +static int activate(AVFilterContext *ctx) +{ + SSIM360Context *s = ctx->priv; + return ff_framesync_activate(&s->fs); +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + SSIM360Context *s = ctx->priv; + + if (s->nb_ssim_frames > 0) { + char buf[256]; + buf[0] = 0; + // Log average SSIM360 values + for (int i = 0; i < s->nb_components; i++) { + int c = s->is_rgb ? s->rgba_map[i] : i; + av_strlcatf(buf, sizeof(buf), " %c:%f (%f)", s->comps[i], s->ssim360[c] / s->nb_ssim_frames, + ssim360_db(s->ssim360[c], s->nb_ssim_frames)); + } + av_log(ctx, AV_LOG_INFO, "SSIM360%s All:%f (%f)\n", buf, + s->ssim360_total / s->nb_ssim_frames, ssim360_db(s->ssim360_total, s->nb_ssim_frames)); + + // Log percentiles from histogram when using tape + if (s->use_tape) { + for (int p = 0; PERCENTILE_LIST[p] >= 0.0; p++) { + buf[0] = 0; + for (int i = 0; i < s->nb_components; i++) { + int c = s->is_rgb ? s->rgba_map[i] : i; + double ssim360p = s->ssim360_percentile_sum[i][p] / (double)(s->nb_ssim_frames); + av_strlcatf(buf, sizeof(buf), " %c:%f (%f)", s->comps[c], ssim360p, ssim360_db(ssim360p, 1)); + } + av_log(ctx, AV_LOG_INFO, "SSIM360_p%d%s\n", (int)(PERCENTILE_LIST[p] * 100.), buf); + } + } + } + + // free density map + map_uninit(&s->density); + + map_list_free(&s->heatmaps); + + for (int i = 0; i < s->nb_components; i++) { + for (int eye = 0; eye < 2; eye++) { + av_freep(&s->ref_tape_map[i][eye]); + av_freep(&s->main_tape_map[i][eye]); + } + av_freep(&s->ssim360_hist[i]); + } + + ff_framesync_uninit(&s->fs); + + if (s->stats_file && s->stats_file != stdout) + fclose(s->stats_file); + + av_freep(&s->temp); +} + +#define PF(suf) AV_PIX_FMT_YUV420##suf, AV_PIX_FMT_YUV422##suf, AV_PIX_FMT_YUV444##suf, AV_PIX_FMT_GBR##suf +static const enum AVPixelFormat ssim360_pixfmts[] = { + AV_PIX_FMT_GRAY8, + AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P, + AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P, + AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, + AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ444P, + AV_PIX_FMT_GBRP, + PF(P9), PF(P10), PF(P12), PF(P14), PF(P16), + AV_PIX_FMT_NONE +}; +#undef PF + +static const AVFilterPad ssim360_inputs[] = { + { + .name = "main", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_input_main, + }, + { + .name = "reference", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_input_ref, + }, +}; + +static const AVFilterPad ssim360_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_output, + }, +}; + +const AVFilter ff_vf_ssim360 = { + .name = "ssim360", + .description = NULL_IF_CONFIG_SMALL("Calculate the SSIM between two 360 video streams."), + .preinit = ssim360_framesync_preinit, + .init = init, + .uninit = uninit, + .activate = activate, + .priv_size = sizeof(SSIM360Context), + .priv_class = &ssim360_class, + FILTER_INPUTS(ssim360_inputs), + FILTER_OUTPUTS(ssim360_outputs), + FILTER_PIXFMTS_ARRAY(ssim360_pixfmts), +}; diff --git a/libavfilter/vf_stack_qsv.c b/libavfilter/vf_stack_qsv.c new file mode 100644 index 00000000000..3e6aefe44bf --- /dev/null +++ b/libavfilter/vf_stack_qsv.c @@ -0,0 +1,254 @@ +/* + * 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 + * Hardware accelerated hstack, vstack and xstack filters based on Intel Quick Sync Video VPP + */ + +#include "config_components.h" + +#include "libavutil/opt.h" +#include "libavutil/common.h" +#include "libavutil/pixdesc.h" +#include "libavutil/eval.h" +#include "libavutil/hwcontext.h" +#include "libavutil/avstring.h" +#include "libavutil/avassert.h" +#include "libavutil/imgutils.h" +#include "libavutil/mathematics.h" +#include "libavutil/parseutils.h" + +#include "internal.h" +#include "filters.h" +#include "formats.h" +#include "video.h" + +#include "framesync.h" +#include "qsvvpp.h" + +#define HSTACK_NAME "hstack_qsv" +#define VSTACK_NAME "vstack_qsv" +#define XSTACK_NAME "xstack_qsv" +#define HWContext QSVVPPContext +#define StackHWContext StackQSVContext +#include "stack_internal.h" + +typedef struct StackQSVContext { + StackBaseContext base; + + QSVVPPParam qsv_param; + mfxExtVPPComposite comp_conf; +} StackQSVContext; + +static void rgb2yuv(float r, float g, float b, int *y, int *u, int *v, int depth) +{ + *y = ((0.21260*219.0/255.0) * r + (0.71520*219.0/255.0) * g + + (0.07220*219.0/255.0) * b) * ((1 << depth) - 1); + *u = (-(0.11457*224.0/255.0) * r - (0.38543*224.0/255.0) * g + + (0.50000*224.0/255.0) * b + 0.5) * ((1 << depth) - 1); + *v = ((0.50000*224.0/255.0) * r - (0.45415*224.0/255.0) * g - + (0.04585*224.0/255.0) * b + 0.5) * ((1 << depth) - 1); +} + +static int process_frame(FFFrameSync *fs) +{ + AVFilterContext *ctx = fs->parent; + QSVVPPContext *qsv = fs->opaque; + AVFrame *frame = NULL; + int ret = 0; + + for (int i = 0; i < ctx->nb_inputs; i++) { + ret = ff_framesync_get_frame(fs, i, &frame, 0); + if (ret == 0) + ret = ff_qsvvpp_filter_frame(qsv, ctx->inputs[i], frame); + if (ret < 0 && ret != AVERROR(EAGAIN)) + break; + } + + if (ret == 0 && qsv->got_frame == 0) { + for (int i = 0; i < ctx->nb_inputs; i++) + FF_FILTER_FORWARD_WANTED(ctx->outputs[0], ctx->inputs[i]); + + ret = FFERROR_NOT_READY; + } + + return ret; +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + StackQSVContext *sctx = ctx->priv; + AVFilterLink *inlink0 = ctx->inputs[0]; + enum AVPixelFormat in_format; + int depth = 8, ret; + mfxVPPCompInputStream *is = sctx->comp_conf.InputStream; + + if (inlink0->format == AV_PIX_FMT_QSV) { + if (!inlink0->hw_frames_ctx || !inlink0->hw_frames_ctx->data) + return AVERROR(EINVAL); + + in_format = ((AVHWFramesContext*)inlink0->hw_frames_ctx->data)->sw_format; + } else + in_format = inlink0->format; + + sctx->qsv_param.out_sw_format = in_format; + + for (int i = 1; i < sctx->base.nb_inputs; i++) { + AVFilterLink *inlink = ctx->inputs[i]; + + if (inlink0->format == AV_PIX_FMT_QSV) { + AVHWFramesContext *hwfc0 = (AVHWFramesContext *)inlink0->hw_frames_ctx->data; + AVHWFramesContext *hwfc = (AVHWFramesContext *)inlink->hw_frames_ctx->data; + + if (inlink0->format != inlink->format) { + av_log(ctx, AV_LOG_ERROR, "Mixing hardware and software pixel formats is not supported.\n"); + + return AVERROR(EINVAL); + } else if (hwfc0->device_ctx != hwfc->device_ctx) { + av_log(ctx, AV_LOG_ERROR, "Inputs with different underlying QSV devices are forbidden.\n"); + + return AVERROR(EINVAL); + } + } + } + + if (in_format == AV_PIX_FMT_P010) + depth = 10; + + if (sctx->base.fillcolor_enable) { + int Y, U, V; + + rgb2yuv(sctx->base.fillcolor[0] / 255.0, sctx->base.fillcolor[1] / 255.0, + sctx->base.fillcolor[2] / 255.0, &Y, &U, &V, depth); + sctx->comp_conf.Y = Y; + sctx->comp_conf.U = U; + sctx->comp_conf.V = V; + } + + ret = config_comm_output(outlink); + if (ret < 0) + return ret; + + for (int i = 0; i < sctx->base.nb_inputs; i++) { + is[i].DstX = sctx->base.regions[i].x; + is[i].DstY = sctx->base.regions[i].y; + is[i].DstW = sctx->base.regions[i].width; + is[i].DstH = sctx->base.regions[i].height; + is[i].GlobalAlpha = 255; + is[i].GlobalAlphaEnable = 0; + is[i].PixelAlphaEnable = 0; + } + + return ff_qsvvpp_init(ctx, &sctx->qsv_param); +} + +/* + * Callback for qsvvpp + * @Note: qsvvpp composition does not generate PTS for result frame. + * so we assign the PTS from framesync to the output frame. + */ + +static int filter_callback(AVFilterLink *outlink, AVFrame *frame) +{ + StackQSVContext *sctx = outlink->src->priv; + + frame->pts = av_rescale_q(sctx->base.fs.pts, + sctx->base.fs.time_base, outlink->time_base); + return ff_filter_frame(outlink, frame); +} + + +static int qsv_stack_init(AVFilterContext *ctx) +{ + StackQSVContext *sctx = ctx->priv; + int ret; + + ret = stack_init(ctx); + if (ret) + return ret; + + /* fill composite config */ + sctx->comp_conf.Header.BufferId = MFX_EXTBUFF_VPP_COMPOSITE; + sctx->comp_conf.Header.BufferSz = sizeof(sctx->comp_conf); + sctx->comp_conf.NumInputStream = sctx->base.nb_inputs; + sctx->comp_conf.InputStream = av_calloc(sctx->base.nb_inputs, + sizeof(*sctx->comp_conf.InputStream)); + if (!sctx->comp_conf.InputStream) + return AVERROR(ENOMEM); + + /* initialize QSVVPP params */ + sctx->qsv_param.filter_frame = filter_callback; + sctx->qsv_param.ext_buf = av_mallocz(sizeof(*sctx->qsv_param.ext_buf)); + + if (!sctx->qsv_param.ext_buf) + return AVERROR(ENOMEM); + + sctx->qsv_param.ext_buf[0] = (mfxExtBuffer *)&sctx->comp_conf; + sctx->qsv_param.num_ext_buf = 1; + sctx->qsv_param.num_crop = 0; + + return 0; +} + +static av_cold void qsv_stack_uninit(AVFilterContext *ctx) +{ + StackQSVContext *sctx = ctx->priv; + + stack_uninit(ctx); + + ff_qsvvpp_close(ctx); + av_freep(&sctx->comp_conf.InputStream); + av_freep(&sctx->qsv_param.ext_buf); +} + +static int qsv_stack_query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pixel_formats[] = { + AV_PIX_FMT_NV12, + AV_PIX_FMT_P010, + AV_PIX_FMT_QSV, + AV_PIX_FMT_NONE, + }; + + return ff_set_common_formats_from_list(ctx, pixel_formats); +} + +#include "stack_internal.c" + +#if CONFIG_HSTACK_QSV_FILTER + +DEFINE_HSTACK_OPTIONS(qsv); +DEFINE_STACK_FILTER(hstack, qsv, "Quick Sync Video", AVFILTER_FLAG_HWDEVICE); + +#endif + +#if CONFIG_VSTACK_QSV_FILTER + +DEFINE_VSTACK_OPTIONS(qsv); +DEFINE_STACK_FILTER(vstack, qsv, "Quick Sync Video", AVFILTER_FLAG_HWDEVICE); + +#endif + +#if CONFIG_XSTACK_QSV_FILTER + +DEFINE_XSTACK_OPTIONS(qsv); +DEFINE_STACK_FILTER(xstack, qsv, "Quick Sync Video", AVFILTER_FLAG_HWDEVICE); + +#endif diff --git a/libavfilter/vf_stack_vaapi.c b/libavfilter/vf_stack_vaapi.c new file mode 100644 index 00000000000..8e9471e6d7b --- /dev/null +++ b/libavfilter/vf_stack_vaapi.c @@ -0,0 +1,253 @@ +/* + * 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 + * Hardware accelerated hstack, vstack and xstack filters based on VA-API + */ + +#include "config_components.h" + +#include "libavutil/opt.h" +#include "libavutil/common.h" +#include "libavutil/pixdesc.h" +#include "libavutil/eval.h" +#include "libavutil/hwcontext.h" +#include "libavutil/avstring.h" +#include "libavutil/avassert.h" +#include "libavutil/imgutils.h" +#include "libavutil/mathematics.h" +#include "libavutil/parseutils.h" +#include "libavutil/mem.h" + +#include "internal.h" +#include "filters.h" +#include "formats.h" +#include "video.h" +#include "framesync.h" +#include "vaapi_vpp.h" + +#define HSTACK_NAME "hstack_vaapi" +#define VSTACK_NAME "vstack_vaapi" +#define XSTACK_NAME "xstack_vaapi" +#define HWContext VAAPIVPPContext +#define StackHWContext StackVAAPIContext +#include "stack_internal.h" + +typedef struct StackVAAPIContext { + StackBaseContext base; + + VARectangle *rects; +} StackVAAPIContext; + +static int process_frame(FFFrameSync *fs) +{ + AVFilterContext *avctx = fs->parent; + AVFilterLink *outlink = avctx->outputs[0]; + StackVAAPIContext *sctx = fs->opaque; + VAAPIVPPContext *vppctx = fs->opaque; + AVFrame *oframe, *iframe; + VAProcPipelineParameterBuffer *params = NULL; + VARectangle *irect = NULL; + int ret = 0; + + if (vppctx->va_context == VA_INVALID_ID) + return AVERROR(EINVAL); + + oframe = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!oframe) + return AVERROR(ENOMEM); + + irect = av_calloc(avctx->nb_inputs, sizeof(*irect)); + params = av_calloc(avctx->nb_inputs, sizeof(*params)); + if (!irect || !params) { + ret = AVERROR(ENOMEM); + goto fail; + } + + for (int i = 0; i < avctx->nb_inputs; i++) { + ret = ff_framesync_get_frame(fs, i, &iframe, 0); + if (ret) + goto fail; + + if (i == 0) { + ret = av_frame_copy_props(oframe, iframe); + if (ret < 0) + goto fail; + } + + ret = ff_vaapi_vpp_init_params(avctx, ¶ms[i], iframe, oframe); + if (ret) + goto fail; + + av_log(avctx, AV_LOG_DEBUG, "stack input %d: %s, %ux%u (%"PRId64").\n", + i, av_get_pix_fmt_name(iframe->format), + iframe->width, iframe->height, iframe->pts); + irect[i].x = 0; + irect[i].y = 0; + irect[i].width = iframe->width; + irect[i].height = iframe->height; + params[i].surface_region = &irect[i]; + params[i].surface = (VASurfaceID)(uintptr_t)iframe->data[3]; + params[i].output_region = &sctx->rects[i]; + + if (sctx->base.fillcolor_enable) + params[i].output_background_color = (sctx->base.fillcolor[3] << 24 | + sctx->base.fillcolor[0] << 16 | + sctx->base.fillcolor[1] << 8 | + sctx->base.fillcolor[2]); + } + + oframe->pts = av_rescale_q(sctx->base.fs.pts, sctx->base.fs.time_base, outlink->time_base); + oframe->sample_aspect_ratio = outlink->sample_aspect_ratio; + + ret = ff_vaapi_vpp_render_pictures(avctx, params, avctx->nb_inputs, oframe); + if (ret) + goto fail; + + av_freep(&irect); + av_freep(¶ms); + return ff_filter_frame(outlink, oframe); + +fail: + av_freep(&irect); + av_freep(¶ms); + av_frame_free(&oframe); + return ret; +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *avctx = outlink->src; + StackVAAPIContext *sctx = avctx->priv; + VAAPIVPPContext *vppctx = avctx->priv; + AVFilterLink *inlink0 = avctx->inputs[0]; + AVHWFramesContext *hwfc0 = NULL; + int ret; + + if (inlink0->format != AV_PIX_FMT_VAAPI || !inlink0->hw_frames_ctx || !inlink0->hw_frames_ctx->data) { + av_log(avctx, AV_LOG_ERROR, "Software pixel format is not supported.\n"); + return AVERROR(EINVAL); + } + + hwfc0 = (AVHWFramesContext *)inlink0->hw_frames_ctx->data; + + for (int i = 1; i < sctx->base.nb_inputs; i++) { + AVFilterLink *inlink = avctx->inputs[i]; + AVHWFramesContext *hwfc = NULL; + + if (inlink->format != AV_PIX_FMT_VAAPI || !inlink->hw_frames_ctx || !inlink->hw_frames_ctx->data) { + av_log(avctx, AV_LOG_ERROR, "Software pixel format is not supported.\n"); + return AVERROR(EINVAL); + } + + hwfc = (AVHWFramesContext *)inlink->hw_frames_ctx->data; + + if (hwfc0->sw_format != hwfc->sw_format) { + av_log(avctx, AV_LOG_ERROR, "All inputs should have the same underlying software pixel format.\n"); + return AVERROR(EINVAL); + } + + if (hwfc0->device_ctx != hwfc->device_ctx) { + av_log(avctx, AV_LOG_ERROR, "All inputs should have the same underlying vaapi devices.\n"); + return AVERROR(EINVAL); + } + } + + ff_vaapi_vpp_config_input(inlink0); + vppctx->output_format = hwfc0->sw_format; + + ret = config_comm_output(outlink); + if (ret < 0) + return ret; + + for (int i = 0; i < sctx->base.nb_inputs; i++) { + sctx->rects[i].x = sctx->base.regions[i].x; + sctx->rects[i].y = sctx->base.regions[i].y; + sctx->rects[i].width = sctx->base.regions[i].width; + sctx->rects[i].height = sctx->base.regions[i].height; + } + + vppctx->output_width = outlink->w; + vppctx->output_height = outlink->h; + + return ff_vaapi_vpp_config_output(outlink); +} + +static int vaapi_stack_init(AVFilterContext *avctx) +{ + StackVAAPIContext *sctx = avctx->priv; + VAAPIVPPContext *vppctx = avctx->priv; + int ret; + + ret = stack_init(avctx); + if (ret) + return ret; + + /* stack region */ + sctx->rects = av_calloc(sctx->base.nb_inputs, sizeof(*sctx->rects)); + if (!sctx->rects) + return AVERROR(ENOMEM); + + ff_vaapi_vpp_ctx_init(avctx); + vppctx->output_format = AV_PIX_FMT_NONE; + + return 0; +} + +static av_cold void vaapi_stack_uninit(AVFilterContext *avctx) +{ + StackVAAPIContext *sctx = avctx->priv; + + stack_uninit(avctx); + + av_freep(&sctx->rects); +} + +static int vaapi_stack_query_formats(AVFilterContext *avctx) +{ + static const enum AVPixelFormat pixel_formats[] = { + AV_PIX_FMT_VAAPI, + AV_PIX_FMT_NONE, + }; + + return ff_set_common_formats_from_list(avctx, pixel_formats); +} + +#include "stack_internal.c" + +#if CONFIG_HSTACK_VAAPI_FILTER + +DEFINE_HSTACK_OPTIONS(vaapi); +DEFINE_STACK_FILTER(hstack, vaapi, "VA-API", 0); + +#endif + +#if CONFIG_VSTACK_VAAPI_FILTER + +DEFINE_VSTACK_OPTIONS(vaapi); +DEFINE_STACK_FILTER(vstack, vaapi, "VA-API", 0); + +#endif + +#if CONFIG_XSTACK_VAAPI_FILTER + +DEFINE_XSTACK_OPTIONS(vaapi); +DEFINE_STACK_FILTER(xstack, vaapi, "VA-API", 0); + +#endif diff --git a/libavfilter/vf_subtitles.c b/libavfilter/vf_subtitles.c index 82e140e9863..e2857e90c0c 100644 --- a/libavfilter/vf_subtitles.c +++ b/libavfilter/vf_subtitles.c @@ -45,6 +45,8 @@ #include "formats.h" #include "video.h" +#define FF_ASS_FEATURE_WRAP_UNICODE (LIBASS_VERSION >= 0x01600010) + typedef struct AssContext { const AVClass *class; ASS_Library *library; @@ -61,6 +63,7 @@ typedef struct AssContext { int original_w, original_h; int shaping; FFDrawContext draw; + int wrap_unicode; } AssContext; #define OFFSET(x) offsetof(AssContext, x) @@ -209,13 +212,6 @@ static const AVFilterPad ass_inputs[] = { }, }; -static const AVFilterPad ass_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #if CONFIG_ASS_FILTER static const AVOption ass_options[] = { @@ -257,7 +253,7 @@ const AVFilter ff_vf_ass = { .init = init_ass, .uninit = uninit, FILTER_INPUTS(ass_inputs), - FILTER_OUTPUTS(ass_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .priv_class = &ass_class, }; @@ -271,6 +267,9 @@ static const AVOption subtitles_options[] = { {"stream_index", "set stream index", OFFSET(stream_index), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS}, {"si", "set stream index", OFFSET(stream_index), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS}, {"force_style", "force subtitle style", OFFSET(force_style), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS}, +#if FF_ASS_FEATURE_WRAP_UNICODE + {"wrap_unicode", "break lines according to the Unicode Line Breaking Algorithm", OFFSET(wrap_unicode), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, FLAGS }, +#endif {NULL}, }; @@ -432,6 +431,18 @@ static av_cold int init_subtitles(AVFilterContext *ctx) if (ret < 0) goto end; +#if FF_ASS_FEATURE_WRAP_UNICODE + /* Don't overwrite wrap automatically for native ASS */ + if (ass->wrap_unicode == -1) + ass->wrap_unicode = st->codecpar->codec_id != AV_CODEC_ID_ASS; + if (ass->wrap_unicode) { + ret = ass_track_set_feature(ass->track, ASS_FEATURE_WRAP_UNICODE, 1); + if (ret < 0) + av_log(ctx, AV_LOG_WARNING, + "libass wasn't built with ASS_FEATURE_WRAP_UNICODE support\n"); + } +#endif + if (ass->force_style) { char **list = NULL; char *temp = NULL; @@ -497,7 +508,7 @@ const AVFilter ff_vf_subtitles = { .init = init_subtitles, .uninit = uninit, FILTER_INPUTS(ass_inputs), - FILTER_OUTPUTS(ass_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .priv_class = &subtitles_class, }; diff --git a/libavfilter/vf_super2xsai.c b/libavfilter/vf_super2xsai.c index d932e2ba4b4..65144cbf1b0 100644 --- a/libavfilter/vf_super2xsai.c +++ b/libavfilter/vf_super2xsai.c @@ -29,7 +29,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/intreadwrite.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" diff --git a/libavfilter/vf_swaprect.c b/libavfilter/vf_swaprect.c index b76e3bb99dc..13ae149afdb 100644 --- a/libavfilter/vf_swaprect.c +++ b/libavfilter/vf_swaprect.c @@ -64,8 +64,16 @@ static int query_formats(AVFilterContext *ctx) return ff_set_common_formats(ctx, ff_formats_pixdesc_filter(0, reject_flags)); } -static const char *const var_names[] = { "w", "h", "a", "n", "t", "pos", "sar", "dar", NULL }; -enum { VAR_W, VAR_H, VAR_A, VAR_N, VAR_T, VAR_POS, VAR_SAR, VAR_DAR, VAR_VARS_NB }; +static const char *const var_names[] = { "w", "h", "a", "n", "t", +#if FF_API_FRAME_PKT + "pos", +#endif + "sar", "dar", NULL }; +enum { VAR_W, VAR_H, VAR_A, VAR_N, VAR_T, +#if FF_API_FRAME_PKT + VAR_POS, +#endif + VAR_SAR, VAR_DAR, VAR_VARS_NB }; static int filter_frame(AVFilterLink *inlink, AVFrame *in) { @@ -90,7 +98,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) var_values[VAR_DAR] = var_values[VAR_A] * var_values[VAR_SAR]; var_values[VAR_N] = inlink->frame_count_out; var_values[VAR_T] = in->pts == AV_NOPTS_VALUE ? NAN : in->pts * av_q2d(inlink->time_base); +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS var_values[VAR_POS] = in->pkt_pos == -1 ? NAN : in->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif ret = av_expr_parse_and_eval(&dw, s->w, var_names, &var_values[0], @@ -225,13 +237,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_swaprect = { .name = "swaprect", .description = NULL_IF_CONFIG_SMALL("Swap 2 rectangular objects in video."), @@ -239,7 +244,7 @@ const AVFilter ff_vf_swaprect = { .priv_class = &swaprect_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_swapuv.c b/libavfilter/vf_swapuv.c index 44520282621..df04631d200 100644 --- a/libavfilter/vf_swapuv.c +++ b/libavfilter/vf_swapuv.c @@ -101,20 +101,13 @@ static const AVFilterPad swapuv_inputs[] = { }, }; -static const AVFilterPad swapuv_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_swapuv = { .name = "swapuv", .description = NULL_IF_CONFIG_SMALL("Swap U and V components."), .priv_size = sizeof(SwapUVContext), .priv_class = &swapuv_class, FILTER_INPUTS(swapuv_inputs), - FILTER_OUTPUTS(swapuv_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_telecine.c b/libavfilter/vf_telecine.c index e8de63bbcfe..8343b114669 100644 --- a/libavfilter/vf_telecine.c +++ b/libavfilter/vf_telecine.c @@ -29,6 +29,7 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" +#include "filters.h" #include "formats.h" #include "internal.h" #include "video.h" @@ -182,7 +183,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) } if (s->occupied) { - av_frame_make_writable(s->frame[nout]); + ret = ff_inlink_make_frame_writable(inlink, &s->frame[nout]); + if (ret < 0) { + av_frame_free(&inpicref); + return ret; + } for (i = 0; i < s->nb_planes; i++) { // fill in the EARLIER field from the buffered pic av_image_copy_plane(s->frame[nout]->data[i] + s->frame[nout]->linesize[i] * s->first_field, @@ -199,8 +204,17 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) s->stride[i], (s->planeheight[i] - !s->first_field + 1) / 2); } +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS s->frame[nout]->interlaced_frame = 1; s->frame[nout]->top_field_first = !s->first_field; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + s->frame[nout]->flags |= AV_FRAME_FLAG_INTERLACED; + if (s->first_field) + s->frame[nout]->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; + else + s->frame[nout]->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; nout++; len--; s->occupied = 0; @@ -208,14 +222,23 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) while (len >= 2) { // output THIS image as-is - av_frame_make_writable(s->frame[nout]); + ret = ff_inlink_make_frame_writable(inlink, &s->frame[nout]); + if (ret < 0) { + av_frame_free(&inpicref); + return ret; + } for (i = 0; i < s->nb_planes; i++) av_image_copy_plane(s->frame[nout]->data[i], s->frame[nout]->linesize[i], inpicref->data[i], inpicref->linesize[i], s->stride[i], s->planeheight[i]); +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS s->frame[nout]->interlaced_frame = inpicref->interlaced_frame; s->frame[nout]->top_field_first = inpicref->top_field_first; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + s->frame[nout]->flags |= (inpicref->flags & (AV_FRAME_FLAG_INTERLACED | AV_FRAME_FLAG_TOP_FIELD_FIRST)); nout++; len -= 2; } @@ -232,8 +255,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) for (i = 0; i < nout; i++) { AVFrame *frame = av_frame_clone(s->frame[i]); - int interlaced = frame ? frame->interlaced_frame : 0; - int tff = frame ? frame->top_field_first : 0; + int interlaced = frame ? !!(frame->flags & AV_FRAME_FLAG_INTERLACED) : 0; + int tff = frame ? !!(frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) : 0; if (!frame) { av_frame_free(&inpicref); @@ -241,8 +264,20 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) } av_frame_copy_props(frame, inpicref); +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS frame->interlaced_frame = interlaced; frame->top_field_first = tff; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + if (interlaced) + frame->flags |= AV_FRAME_FLAG_INTERLACED; + else + frame->flags &= ~AV_FRAME_FLAG_INTERLACED; + if (tff) + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; + else + frame->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; frame->pts = ((s->start_time == AV_NOPTS_VALUE) ? 0 : s->start_time) + av_rescale(outlink->frame_count_in, s->ts_unit.num, s->ts_unit.den); diff --git a/libavfilter/vf_tinterlace.c b/libavfilter/vf_tinterlace.c index 032629279ad..e221a6f9419 100644 --- a/libavfilter/vf_tinterlace.c +++ b/libavfilter/vf_tinterlace.c @@ -32,6 +32,7 @@ #include "avfilter.h" #include "internal.h" #include "tinterlace.h" +#include "video.h" #define OFFSET(x) offsetof(TInterlaceContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM @@ -203,6 +204,7 @@ static av_cold void uninit(AVFilterContext *ctx) av_frame_free(&tinterlace->next); av_freep(&tinterlace->black_data[0][0]); av_freep(&tinterlace->black_data[1][0]); + ff_ccfifo_uninit(&tinterlace->cc_fifo); } static int config_out_props(AVFilterLink *outlink) @@ -211,7 +213,7 @@ static int config_out_props(AVFilterLink *outlink) AVFilterLink *inlink = outlink->src->inputs[0]; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format); TInterlaceContext *tinterlace = ctx->priv; - int i; + int ret, i; tinterlace->vsub = desc->log2_chroma_h; outlink->w = inlink->w; @@ -223,7 +225,6 @@ static int config_out_props(AVFilterLink *outlink) if (tinterlace->mode == MODE_PAD) { uint8_t black[4] = { 0, 0, 0, 16 }; - int ret; ff_draw_init(&tinterlace->draw, outlink->format, 0); ff_draw_color(&tinterlace->draw, &tinterlace->color, black); /* limited range */ @@ -291,6 +292,12 @@ static int config_out_props(AVFilterLink *outlink) #endif } + ret = ff_ccfifo_init(&tinterlace->cc_fifo, outlink->frame_rate, ctx); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Failure to setup CC FIFO queue\n"); + return ret; + } + av_log(ctx, AV_LOG_VERBOSE, "mode:%d filter:%s h:%d -> h:%d\n", tinterlace->mode, (tinterlace->flags & TINTERLACE_FLAG_CVLPF) ? "complex" : (tinterlace->flags & TINTERLACE_FLAG_VLPF) ? "linear" : "off", @@ -375,6 +382,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *picref) tinterlace->cur = tinterlace->next; tinterlace->next = picref; + ff_ccfifo_extract(&tinterlace->cc_fifo, picref); + cur = tinterlace->cur; next = tinterlace->next; /* we need at least two frames */ @@ -391,8 +400,13 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *picref) return AVERROR(ENOMEM); av_frame_copy_props(out, cur); out->height = outlink->h; +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS out->interlaced_frame = 1; out->top_field_first = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + out->flags |= AV_FRAME_FLAG_INTERLACED | AV_FRAME_FLAG_TOP_FIELD_FIRST; out->sample_aspect_ratio = av_mul_q(cur->sample_aspect_ratio, av_make_q(2, 1)); /* write odd frame lines into the upper field of the new frame */ @@ -444,13 +458,14 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *picref) * halving the frame rate and preserving image height */ case MODE_INTERLEAVE_TOP: /* top field first */ case MODE_INTERLEAVE_BOTTOM: /* bottom field first */ - if ((tinterlace->flags & TINTERLACE_FLAG_BYPASS_IL) && cur->interlaced_frame) { + if ((tinterlace->flags & TINTERLACE_FLAG_BYPASS_IL) && (cur->flags & AV_FRAME_FLAG_INTERLACED)) { av_log(ctx, AV_LOG_WARNING, "video is already interlaced, adjusting framerate only\n"); out = av_frame_clone(cur); if (!out) return AVERROR(ENOMEM); out->pts /= 2; // adjust pts to new framerate + ff_ccfifo_inject(&tinterlace->cc_fifo, out); ret = ff_filter_frame(outlink, out); return ret; } @@ -459,8 +474,17 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *picref) if (!out) return AVERROR(ENOMEM); av_frame_copy_props(out, cur); +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS out->interlaced_frame = 1; out->top_field_first = tff; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + out->flags |= AV_FRAME_FLAG_INTERLACED; + if (tff) + out->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; + else + out->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; /* copy upper/lower field from cur */ copy_picture_field(tinterlace, out->data, out->linesize, @@ -481,22 +505,37 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *picref) out = av_frame_clone(cur); if (!out) return AVERROR(ENOMEM); +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS out->interlaced_frame = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + out->flags |= AV_FRAME_FLAG_INTERLACED; if (cur->pts != AV_NOPTS_VALUE) out->pts = cur->pts*2; out->pts = av_rescale_q(out->pts, tinterlace->preout_time_base, outlink->time_base); + ff_ccfifo_inject(&tinterlace->cc_fifo, out); if ((ret = ff_filter_frame(outlink, out)) < 0) return ret; /* output mix of current and next frame */ - tff = next->top_field_first; + tff = !!(next->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST); out = ff_get_video_buffer(outlink, outlink->w, outlink->h); if (!out) return AVERROR(ENOMEM); av_frame_copy_props(out, next); +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS out->interlaced_frame = 1; out->top_field_first = !tff; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + out->flags |= AV_FRAME_FLAG_INTERLACED; + if (tff) + out->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; + else + out->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; if (next->pts != AV_NOPTS_VALUE && cur->pts != AV_NOPTS_VALUE) out->pts = cur->pts + next->pts; @@ -521,6 +560,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *picref) out->pts = av_rescale_q(out->pts, tinterlace->preout_time_base, outlink->time_base); out->duration = av_rescale_q(1, av_inv_q(outlink->frame_rate), outlink->time_base); + ff_ccfifo_inject(&tinterlace->cc_fifo, out); ret = ff_filter_frame(outlink, out); return ret; diff --git a/libavfilter/vf_tmidequalizer.c b/libavfilter/vf_tmidequalizer.c index b5d497b637c..650aa36636d 100644 --- a/libavfilter/vf_tmidequalizer.c +++ b/libavfilter/vf_tmidequalizer.c @@ -22,7 +22,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" diff --git a/libavfilter/vf_tonemap.c b/libavfilter/vf_tonemap.c index d1087e6bd95..2970307918a 100644 --- a/libavfilter/vf_tonemap.c +++ b/libavfilter/vf_tonemap.c @@ -25,7 +25,6 @@ #include #include -#include #include "libavutil/csp.h" #include "libavutil/imgutils.h" @@ -36,7 +35,6 @@ #include "avfilter.h" #include "colorspace.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -307,13 +305,6 @@ static const AVFilterPad tonemap_inputs[] = { }, }; -static const AVFilterPad tonemap_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_tonemap = { .name = "tonemap", .description = NULL_IF_CONFIG_SMALL("Conversion to/from different dynamic ranges."), @@ -321,7 +312,7 @@ const AVFilter ff_vf_tonemap = { .priv_size = sizeof(TonemapContext), .priv_class = &tonemap_class, FILTER_INPUTS(tonemap_inputs), - FILTER_OUTPUTS(tonemap_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS(AV_PIX_FMT_GBRPF32, AV_PIX_FMT_GBRAPF32), .flags = AVFILTER_FLAG_SLICE_THREADS, }; diff --git a/libavfilter/vf_tonemap_opencl.c b/libavfilter/vf_tonemap_opencl.c index f6ebb694a81..84bf394e75a 100644 --- a/libavfilter/vf_tonemap_opencl.c +++ b/libavfilter/vf_tonemap_opencl.c @@ -240,8 +240,8 @@ static int tonemap_opencl_init(AVFilterContext *avctx) av_log(avctx, AV_LOG_DEBUG, "Generated OpenCL header:\n%s\n", header.str); opencl_sources[0] = header.str; - opencl_sources[1] = ff_opencl_source_tonemap; - opencl_sources[2] = ff_opencl_source_colorspace_common; + opencl_sources[1] = ff_source_tonemap_cl; + opencl_sources[2] = ff_source_colorspace_common_cl; err = ff_opencl_filter_load_program(avctx, opencl_sources, OPENCL_SOURCE_NB); av_bprint_finalize(&header, NULL); @@ -547,4 +547,5 @@ const AVFilter ff_vf_tonemap_opencl = { FILTER_OUTPUTS(tonemap_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_tonemap_vaapi.c b/libavfilter/vf_tonemap_vaapi.c index cd2f4c21950..0333cd3f6a7 100644 --- a/libavfilter/vf_tonemap_vaapi.c +++ b/libavfilter/vf_tonemap_vaapi.c @@ -22,9 +22,9 @@ #include "libavutil/mastering_display_metadata.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "vaapi_vpp.h" +#include "video.h" typedef struct HDRVAAPIContext { VAAPIVPPContext vpp_ctx; // must be the first field @@ -309,6 +309,9 @@ static int tonemap_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame av_get_pix_fmt_name(output_frame->format), output_frame->width, output_frame->height, output_frame->pts); + av_frame_remove_side_data(output_frame, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); + av_frame_remove_side_data(output_frame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); + return ff_filter_frame(outlink, output_frame); fail: diff --git a/libavfilter/vf_tpad.c b/libavfilter/vf_tpad.c index c8704781581..7990403e813 100644 --- a/libavfilter/vf_tpad.c +++ b/libavfilter/vf_tpad.c @@ -26,6 +26,13 @@ #include "internal.h" #include "formats.h" #include "drawutils.h" +#include "video.h" + +enum PadMode { + MODE_ADD = 0, + MODE_CLONE, + NB_MODE +}; typedef struct TPadContext { const AVClass *class; @@ -51,10 +58,10 @@ typedef struct TPadContext { static const AVOption tpad_options[] = { { "start", "set the number of frames to delay input", OFFSET(pad_start), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, VF }, { "stop", "set the number of frames to add after input finished", OFFSET(pad_stop), AV_OPT_TYPE_INT, {.i64=0}, -1, INT_MAX, VF }, - { "start_mode", "set the mode of added frames to start", OFFSET(start_mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, VF, "mode" }, - { "add", "add solid-color frames", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, VF, "mode" }, - { "clone", "clone first/last frame", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, VF, "mode" }, - { "stop_mode", "set the mode of added frames to end", OFFSET(stop_mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, VF, "mode" }, + { "start_mode", "set the mode of added frames to start", OFFSET(start_mode), AV_OPT_TYPE_INT, {.i64=MODE_ADD}, 0, NB_MODE-1, VF, "mode" }, + { "add", "add solid-color frames", 0, AV_OPT_TYPE_CONST, {.i64=MODE_ADD}, 0, 0, VF, "mode" }, + { "clone", "clone first/last frame", 0, AV_OPT_TYPE_CONST, {.i64=MODE_CLONE}, 0, 0, VF, "mode" }, + { "stop_mode", "set the mode of added frames to end", OFFSET(stop_mode), AV_OPT_TYPE_INT, {.i64=MODE_ADD}, 0, NB_MODE-1, VF, "mode" }, { "start_duration", "set the duration to delay input", OFFSET(start_duration), AV_OPT_TYPE_DURATION, {.i64=0}, 0, INT64_MAX, VF }, { "stop_duration", "set the duration to pad input", OFFSET(stop_duration), AV_OPT_TYPE_DURATION, {.i64=0}, 0, INT64_MAX, VF }, { "color", "set the color of the added frames", OFFSET(rgba_color), AV_OPT_TYPE_COLOR, {.str="black"}, 0, 0, VF }, @@ -65,7 +72,12 @@ AVFILTER_DEFINE_CLASS(tpad); static int query_formats(AVFilterContext *ctx) { - return ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0)); + TPadContext *s = ctx->priv; + if ((s->stop_mode == MODE_ADD && s->pad_stop != 0) || + (s->start_mode == MODE_ADD && s->pad_start != 0)) + return ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0)); + + return ff_set_common_formats(ctx, ff_all_formats(AVMEDIA_TYPE_VIDEO)); } static int activate(AVFilterContext *ctx) @@ -75,7 +87,7 @@ static int activate(AVFilterContext *ctx) TPadContext *s = ctx->priv; AVFrame *frame = NULL; int ret, status; - int64_t pts; + int64_t duration, pts; FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); @@ -91,20 +103,22 @@ static int activate(AVFilterContext *ctx) } } - if (s->start_mode == 0 && s->pad_start > 0 && ff_outlink_frame_wanted(outlink)) { + if (s->start_mode == MODE_ADD && s->pad_start > 0 && ff_outlink_frame_wanted(outlink)) { frame = ff_get_video_buffer(outlink, outlink->w, outlink->h); if (!frame) return AVERROR(ENOMEM); ff_fill_rectangle(&s->draw, &s->color, frame->data, frame->linesize, 0, 0, frame->width, frame->height); + duration = av_rescale_q(1, av_inv_q(outlink->frame_rate), outlink->time_base); frame->pts = s->pts; - s->pts += av_rescale_q(1, av_inv_q(outlink->frame_rate), outlink->time_base); + frame->duration = duration; + s->pts += duration; s->pad_start--; return ff_filter_frame(outlink, frame); } - if (s->start_mode == 1 && s->pad_start > 0) { + if (s->start_mode == MODE_CLONE && s->pad_start > 0) { if (s->eof) { ff_outlink_set_status(outlink, AVERROR_EOF, 0); return 0; @@ -116,8 +130,10 @@ static int activate(AVFilterContext *ctx) frame = av_frame_clone(s->cache_start); if (!frame) return AVERROR(ENOMEM); + duration = av_rescale_q(1, av_inv_q(outlink->frame_rate), outlink->time_base); frame->pts = s->pts; - s->pts += av_rescale_q(1, av_inv_q(outlink->frame_rate), outlink->time_base); + frame->duration = duration; + s->pts += duration; s->pad_start--; if (s->pad_start == 0) s->cache_start = NULL; @@ -129,7 +145,7 @@ static int activate(AVFilterContext *ctx) if (ret < 0) return ret; if (ret > 0) { - if (s->stop_mode == 1 && s->pad_stop != 0) { + if (s->stop_mode == MODE_CLONE && s->pad_stop != 0) { av_frame_free(&s->cache_stop); s->cache_stop = av_frame_clone(frame); } @@ -143,14 +159,14 @@ static int activate(AVFilterContext *ctx) ff_outlink_set_status(outlink, AVERROR_EOF, s->pts); return 0; } - if (s->stop_mode == 0) { + if (s->stop_mode == MODE_ADD) { frame = ff_get_video_buffer(outlink, outlink->w, outlink->h); if (!frame) return AVERROR(ENOMEM); ff_fill_rectangle(&s->draw, &s->color, frame->data, frame->linesize, 0, 0, frame->width, frame->height); - } else if (s->stop_mode == 1) { + } else if (s->stop_mode == MODE_CLONE) { if (!s->cache_stop) { s->pad_stop = 0; ff_outlink_set_status(outlink, AVERROR_EOF, s->pts); @@ -160,8 +176,10 @@ static int activate(AVFilterContext *ctx) if (!frame) return AVERROR(ENOMEM); } + duration = av_rescale_q(1, av_inv_q(outlink->frame_rate), outlink->time_base); frame->pts = s->pts; - s->pts += av_rescale_q(1, av_inv_q(outlink->frame_rate), outlink->time_base); + frame->duration = duration; + s->pts += duration; if (s->pad_stop > 0) s->pad_stop--; return ff_filter_frame(outlink, frame); @@ -178,8 +196,11 @@ static int config_input(AVFilterLink *inlink) AVFilterContext *ctx = inlink->dst; TPadContext *s = ctx->priv; - ff_draw_init(&s->draw, inlink->format, 0); - ff_draw_color(&s->draw, &s->color, s->rgba_color); + if ((s->stop_mode == MODE_ADD && s->pad_stop != 0) || + (s->start_mode == MODE_ADD && s->pad_start != 0)) { + ff_draw_init(&s->draw, inlink->format, 0); + ff_draw_color(&s->draw, &s->color, s->rgba_color); + } if (s->start_duration) s->pad_start = av_rescale_q(s->start_duration, inlink->frame_rate, av_inv_q(AV_TIME_BASE_Q)); @@ -204,13 +225,6 @@ static const AVFilterPad tpad_inputs[] = { }, }; -static const AVFilterPad tpad_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_tpad = { .name = "tpad", .description = NULL_IF_CONFIG_SMALL("Temporarily pad video frames."), @@ -219,6 +233,6 @@ const AVFilter ff_vf_tpad = { .activate = activate, .uninit = uninit, FILTER_INPUTS(tpad_inputs), - FILTER_OUTPUTS(tpad_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/vf_transpose_opencl.c b/libavfilter/vf_transpose_opencl.c index c6e8bd2f531..b2128049537 100644 --- a/libavfilter/vf_transpose_opencl.c +++ b/libavfilter/vf_transpose_opencl.c @@ -44,7 +44,7 @@ static int transpose_opencl_init(AVFilterContext *avctx) cl_int cle; int err; - err = ff_opencl_filter_load_program(avctx, &ff_opencl_source_transpose, 1); + err = ff_opencl_filter_load_program(avctx, &ff_source_transpose_cl, 1); if (err < 0) goto fail; @@ -281,4 +281,5 @@ const AVFilter ff_vf_transpose_opencl = { FILTER_OUTPUTS(transpose_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_transpose_vaapi.c b/libavfilter/vf_transpose_vaapi.c index 5f1829dd053..07f4b2a5498 100644 --- a/libavfilter/vf_transpose_vaapi.c +++ b/libavfilter/vf_transpose_vaapi.c @@ -21,10 +21,10 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "transpose.h" #include "vaapi_vpp.h" +#include "video.h" typedef struct TransposeVAAPIContext { VAAPIVPPContext vpp_ctx; // must be the first field diff --git a/libavfilter/vf_transpose_vt.c b/libavfilter/vf_transpose_vt.c new file mode 100644 index 00000000000..b5b07e9ef13 --- /dev/null +++ b/libavfilter/vf_transpose_vt.c @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2023 Zhao Zhili + * + * 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 + +#include "libavutil/hwcontext.h" +#include "libavutil/hwcontext_videotoolbox.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "internal.h" +#include "transpose.h" +#include "video.h" + +typedef struct TransposeVtContext { + AVClass *class; + + VTPixelRotationSessionRef session; + int dir; + int passthrough; +} TransposeVtContext; + +static av_cold int transpose_vt_init(AVFilterContext *avctx) +{ + TransposeVtContext *s = avctx->priv; + int ret; + + ret = VTPixelRotationSessionCreate(kCFAllocatorDefault, &s->session); + if (ret != noErr) { + av_log(avctx, AV_LOG_ERROR, "Rotation session create failed, %d\n", ret); + return AVERROR_EXTERNAL; + } + + return 0; +} + +static av_cold void transpose_vt_uninit(AVFilterContext *avctx) +{ + TransposeVtContext *s = avctx->priv; + + if (s->session) { + VTPixelRotationSessionInvalidate(s->session); + CFRelease(s->session); + s->session = NULL; + } +} + +static int transpose_vt_filter_frame(AVFilterLink *link, AVFrame *in) +{ + int ret; + AVFilterContext *ctx = link->dst; + TransposeVtContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + CVPixelBufferRef src; + CVPixelBufferRef dst; + + if (s->passthrough) + return ff_filter_frame(outlink, in); + + AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + ret = AVERROR(ENOMEM); + goto fail; + } + + ret = av_frame_copy_props(out, in); + if (ret < 0) + goto fail; + + src = (CVPixelBufferRef)in->data[3]; + dst = (CVPixelBufferRef)out->data[3]; + ret = VTPixelRotationSessionRotateImage(s->session, src, dst); + if (ret != noErr) { + av_log(ctx, AV_LOG_ERROR, "transfer image failed, %d\n", ret); + ret = AVERROR_EXTERNAL; + goto fail; + } + + av_frame_free(&in); + + return ff_filter_frame(outlink, out); + +fail: + av_frame_free(&in); + av_frame_free(&out); + return ret; +} + +static int transpose_vt_config_output(AVFilterLink *outlink) +{ + int err; + AVFilterContext *avctx = outlink->src; + TransposeVtContext *s = avctx->priv; + AVFilterLink *inlink = outlink->src->inputs[0]; + CFStringRef rotation = kVTRotation_0; + CFBooleanRef vflip = kCFBooleanFalse; + CFBooleanRef hflip = kCFBooleanFalse; + int swap_w_h = 0; + + if ((inlink->w >= inlink->h && s->passthrough == TRANSPOSE_PT_TYPE_LANDSCAPE) || + (inlink->w <= inlink->h && s->passthrough == TRANSPOSE_PT_TYPE_PORTRAIT)) { + av_log(avctx, AV_LOG_VERBOSE, + "w:%d h:%d -> w:%d h:%d (passthrough mode)\n", + inlink->w, inlink->h, inlink->w, inlink->h); + return 0; + } + + s->passthrough = TRANSPOSE_PT_TYPE_NONE; + + switch (s->dir) { + case TRANSPOSE_CCLOCK_FLIP: + rotation = kVTRotation_CCW90; + vflip = kCFBooleanTrue; + swap_w_h = 1; + break; + case TRANSPOSE_CCLOCK: + rotation = kVTRotation_CCW90; + swap_w_h = 1; + break; + case TRANSPOSE_CLOCK: + rotation = kVTRotation_CW90; + swap_w_h = 1; + break; + case TRANSPOSE_CLOCK_FLIP: + rotation = kVTRotation_CW90; + vflip = kCFBooleanTrue; + swap_w_h = 1; + break; + case TRANSPOSE_REVERSAL: + rotation = kVTRotation_180; + break; + case TRANSPOSE_HFLIP: + hflip = kCFBooleanTrue; + break; + case TRANSPOSE_VFLIP: + vflip = kCFBooleanTrue; + break; + default: + av_log(avctx, AV_LOG_ERROR, "Failed to set direction to %d\n", s->dir); + return AVERROR(EINVAL); + } + + err = VTSessionSetProperty(s->session, kVTPixelRotationPropertyKey_Rotation, + rotation); + if (err != noErr) { + av_log(avctx, AV_LOG_ERROR, "Set rotation property failed, %d\n", err); + return AVERROR_EXTERNAL; + } + err = VTSessionSetProperty(s->session, kVTPixelRotationPropertyKey_FlipVerticalOrientation, + vflip); + if (err != noErr) { + av_log(avctx, AV_LOG_ERROR, "Set vertical flip property failed, %d\n", err); + return AVERROR_EXTERNAL; + } + err = VTSessionSetProperty(s->session, kVTPixelRotationPropertyKey_FlipHorizontalOrientation, + hflip); + if (err != noErr) { + av_log(avctx, AV_LOG_ERROR, "Set horizontal flip property failed, %d\n", err); + return AVERROR_EXTERNAL; + } + + if (swap_w_h) { + outlink->w = inlink->h; + outlink->h = inlink->w; + } + + return 0; +} + +#define OFFSET(x) offsetof(TransposeVtContext, x) +#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) +static const AVOption transpose_vt_options[] = { + { "dir", "set transpose direction", + OFFSET(dir), AV_OPT_TYPE_INT, { .i64 = TRANSPOSE_CCLOCK_FLIP }, 0, 6, FLAGS, "dir" }, + { "cclock_flip", "rotate counter-clockwise with vertical flip", + 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK_FLIP }, .flags=FLAGS, .unit = "dir" }, + { "clock", "rotate clockwise", + 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK }, .flags=FLAGS, .unit = "dir" }, + { "cclock", "rotate counter-clockwise", + 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK }, .flags=FLAGS, .unit = "dir" }, + { "clock_flip", "rotate clockwise with vertical flip", + 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK_FLIP }, .flags=FLAGS, .unit = "dir" }, + { "reversal", "rotate by half-turn", + 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_REVERSAL }, .flags=FLAGS, .unit = "dir" }, + { "hflip", "flip horizontally", + 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_HFLIP }, .flags=FLAGS, .unit = "dir" }, + { "vflip", "flip vertically", + 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_VFLIP }, .flags=FLAGS, .unit = "dir" }, + + { "passthrough", "do not apply transposition if the input matches the specified geometry", + OFFSET(passthrough), AV_OPT_TYPE_INT, { .i64=TRANSPOSE_PT_TYPE_NONE }, 0, INT_MAX, FLAGS, "passthrough" }, + { "none", "always apply transposition", + 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_PT_TYPE_NONE }, INT_MIN, INT_MAX, FLAGS, "passthrough" }, + { "portrait", "preserve portrait geometry", + 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_PT_TYPE_PORTRAIT }, INT_MIN, INT_MAX, FLAGS, "passthrough" }, + { "landscape", "preserve landscape geometry", + 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_PT_TYPE_LANDSCAPE }, INT_MIN, INT_MAX, FLAGS, "passthrough" }, + + { NULL } +}; + +AVFILTER_DEFINE_CLASS(transpose_vt); + +static const AVFilterPad transpose_vt_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = &transpose_vt_filter_frame, + }, +}; + +static const AVFilterPad transpose_vt_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = &transpose_vt_config_output, + }, +}; + +const AVFilter ff_vf_transpose_vt = { + .name = "transpose_vt", + .description = NULL_IF_CONFIG_SMALL("Transpose Videotoolbox frames"), + .priv_size = sizeof(TransposeVtContext), + .init = transpose_vt_init, + .uninit = transpose_vt_uninit, + FILTER_INPUTS(transpose_vt_inputs), + FILTER_OUTPUTS(transpose_vt_outputs), + FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VIDEOTOOLBOX), + .priv_class = &transpose_vt_class, + .flags = AVFILTER_FLAG_HWDEVICE, +}; diff --git a/libavfilter/vf_transpose_vulkan.c b/libavfilter/vf_transpose_vulkan.c index 30d052e08cc..3abe93be0ba 100644 --- a/libavfilter/vf_transpose_vulkan.c +++ b/libavfilter/vf_transpose_vulkan.c @@ -1,5 +1,7 @@ /* * copyright (c) 2021 Wu Jianhua + * Copyright (c) Lynne + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -20,41 +22,61 @@ #include "libavutil/random_seed.h" #include "libavutil/opt.h" #include "vulkan_filter.h" +#include "vulkan_spirv.h" #include "internal.h" #include "transpose.h" - -#define CGS 32 +#include "video.h" typedef struct TransposeVulkanContext { FFVulkanContext vkctx; - FFVkQueueFamilyCtx qf; - FFVkExecContext *exec; - FFVulkanPipeline *pl; - VkDescriptorImageInfo input_images[3]; - VkDescriptorImageInfo output_images[3]; + int initialized; + FFVulkanPipeline pl; + FFVkExecPool e; + FFVkQueueFamilyCtx qf; + FFVkSPIRVShader shd; + VkSampler sampler; int dir; int passthrough; - int initialized; } TransposeVulkanContext; static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) { - int err = 0; - FFVkSPIRVShader *shd; + int err; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; TransposeVulkanContext *s = ctx->priv; FFVulkanContext *vkctx = &s->vkctx; + const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); + FFVkSPIRVShader *shd = &s->shd; + FFVkSPIRVCompiler *spv; + FFVulkanDescriptorSetBinding *desc; + + spv = ff_vk_spirv_init(); + if (!spv) { + av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); + return AVERROR_EXTERNAL; + } + + ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT); + RET(ff_vk_exec_pool_init(vkctx, &s->qf, &s->e, s->qf.nb_queues*4, 0, 0, 0, NULL)); + RET(ff_vk_init_sampler(vkctx, &s->sampler, 1, VK_FILTER_LINEAR)); + RET(ff_vk_shader_init(&s->pl, &s->shd, "transpose_compute", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); - FFVulkanDescriptorSetBinding image_descs[] = { + ff_vk_shader_set_compute_sizes(&s->shd, 32, 1, 1); + + desc = (FFVulkanDescriptorSetBinding []) { { .name = "input_images", .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .dimensions = 2, .elems = planes, .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->input_images, + .samplers = DUP_SAMPLER(s->sampler), }, { .name = "output_images", @@ -64,154 +86,49 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) .dimensions = 2, .elems = planes, .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->output_images, }, }; - image_descs[0].sampler = ff_vk_init_sampler(vkctx, 1, VK_FILTER_LINEAR); - if (!image_descs[0].sampler) - return AVERROR_EXTERNAL; - - ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT, 0); - - { - s->pl = ff_vk_create_pipeline(vkctx, &s->qf); - if (!s->pl) - return AVERROR(ENOMEM); - - shd = ff_vk_init_shader(s->pl, "transpose_compute", image_descs[0].stages); - if (!shd) - return AVERROR(ENOMEM); - - ff_vk_set_compute_shader_sizes(shd, (int [3]){ CGS, 1, 1 }); - RET(ff_vk_add_descriptor_set(vkctx, s->pl, shd, image_descs, FF_ARRAY_ELEMS(image_descs), 0)); - - GLSLC(0, void main() ); - GLSLC(0, { ); - GLSLC(1, ivec2 size; ); - GLSLC(1, ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); - for (int i = 0; i < planes; i++) { - GLSLC(0, ); - GLSLF(1, size = imageSize(output_images[%i]); ,i); - GLSLC(1, if (IS_WITHIN(pos, size)) { ); - if (s->dir == TRANSPOSE_CCLOCK) - GLSLF(2, vec4 res = texture(input_images[%i], ivec2(size.y - pos.y, pos.x)); ,i); - else if (s->dir == TRANSPOSE_CLOCK_FLIP || s->dir == TRANSPOSE_CLOCK) { - GLSLF(2, vec4 res = texture(input_images[%i], ivec2(size.yx - pos.yx)); ,i); - if (s->dir == TRANSPOSE_CLOCK) - GLSLC(2, pos = ivec2(pos.x, size.y - pos.y); ); - } else - GLSLF(2, vec4 res = texture(input_images[%i], pos.yx); ,i); - GLSLF(2, imageStore(output_images[%i], pos, res); ,i); - GLSLC(1, } ); - } - GLSLC(0, } ); - - RET(ff_vk_compile_shader(vkctx, shd, "main")); - RET(ff_vk_init_pipeline_layout(vkctx, s->pl)); - RET(ff_vk_init_compute_pipeline(vkctx, s->pl)); - } - - RET(ff_vk_create_exec_ctx(vkctx, &s->exec, &s->qf)); - s->initialized = 1; - -fail: - return err; -} - -static int process_frames(AVFilterContext *avctx, AVFrame *outframe, AVFrame *inframe) -{ - int err = 0; - VkCommandBuffer cmd_buf; - TransposeVulkanContext *s = avctx->priv; - FFVulkanContext *vkctx = &s->vkctx; - FFVulkanFunctions *vk = &s->vkctx.vkfn; - const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); - - AVVkFrame *in = (AVVkFrame *)inframe->data[0]; - AVVkFrame *out = (AVVkFrame *)outframe->data[0]; - - const VkFormat *input_formats = av_vkfmt_from_pixfmt(s->vkctx.input_format); - const VkFormat *output_formats = av_vkfmt_from_pixfmt(s->vkctx.output_format); - - ff_vk_start_exec_recording(vkctx, s->exec); - cmd_buf = ff_vk_get_exec_buf(s->exec); - - for (int i = 0; i < planes; i++) { - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->input_images[i].imageView, in->img[i], - input_formats[i], - ff_comp_identity_map)); - - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->output_images[i].imageView, out->img[i], - output_formats[i], - ff_comp_identity_map)); - - s->input_images[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - s->output_images[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; - } - - ff_vk_update_descriptor_set(vkctx, s->pl, 0); + RET(ff_vk_pipeline_descriptor_set_add(vkctx, &s->pl, shd, desc, 2, 0, 0)); + GLSLC(0, void main() ); + GLSLC(0, { ); + GLSLC(1, ivec2 size; ); + GLSLC(1, ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); for (int i = 0; i < planes; i++) { - VkImageMemoryBarrier barriers[] = { - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .oldLayout = in->layout[i], - .newLayout = s->input_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = in->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .oldLayout = out->layout[i], - .newLayout = s->output_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = out->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - }; - - vk->CmdPipelineBarrier(cmd_buf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, - 0, NULL, 0, NULL, FF_ARRAY_ELEMS(barriers), barriers); - - in->layout[i] = barriers[0].newLayout; - in->access[i] = barriers[0].dstAccessMask; - - out->layout[i] = barriers[1].newLayout; - out->access[i] = barriers[1].dstAccessMask; + GLSLC(0, ); + GLSLF(1, size = imageSize(output_images[%i]); ,i); + GLSLC(1, if (IS_WITHIN(pos, size)) { ); + if (s->dir == TRANSPOSE_CCLOCK) + GLSLF(2, vec4 res = texture(input_images[%i], ivec2(size.y - pos.y, pos.x)); ,i); + else if (s->dir == TRANSPOSE_CLOCK_FLIP || s->dir == TRANSPOSE_CLOCK) { + GLSLF(2, vec4 res = texture(input_images[%i], ivec2(size.yx - pos.yx)); ,i); + if (s->dir == TRANSPOSE_CLOCK) + GLSLC(2, pos = ivec2(pos.x, size.y - pos.y); ); + } else + GLSLF(2, vec4 res = texture(input_images[%i], pos.yx); ,i); + GLSLF(2, imageStore(output_images[%i], pos, res); ,i); + GLSLC(1, } ); } + GLSLC(0, } ); - ff_vk_bind_pipeline_exec(vkctx, s->exec, s->pl); - vk->CmdDispatch(cmd_buf, FFALIGN(s->vkctx.output_width, CGS)/CGS, - s->vkctx.output_height, 1); + RET(spv->compile_shader(spv, ctx, shd, &spv_data, &spv_len, "main", + &spv_opaque)); + RET(ff_vk_shader_create(vkctx, shd, spv_data, spv_len, "main")); - ff_vk_add_exec_dep(vkctx, s->exec, inframe, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - ff_vk_add_exec_dep(vkctx, s->exec, outframe, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); + RET(ff_vk_init_compute_pipeline(vkctx, &s->pl, shd)); + RET(ff_vk_exec_pipeline_register(vkctx, &s->e, &s->pl)); - err = ff_vk_submit_exec_queue(vkctx, s->exec); - if (err) - return err; - - ff_vk_qf_rotate(&s->qf); + s->initialized = 1; return 0; fail: - ff_vk_discard_exec_deps(s->exec); + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); + if (spv) + spv->uninit(&spv); + return err; } @@ -235,7 +152,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) if (!s->initialized) RET(init_filter(ctx, in)); - RET(process_frames(ctx, out, in)); + RET(ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->pl, out, in, + s->sampler, NULL, 0)); RET(av_frame_copy_props(out, in)); @@ -259,6 +177,17 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) static av_cold void transpose_vulkan_uninit(AVFilterContext *avctx) { TransposeVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + FFVulkanFunctions *vk = &vkctx->vkfn; + + ff_vk_exec_pool_free(vkctx, &s->e); + ff_vk_pipeline_free(vkctx, &s->pl); + ff_vk_shader_free(vkctx, &s->shd); + + if (s->sampler) + vk->DestroySampler(vkctx->hwctx->act_dev, s->sampler, + vkctx->hwctx->alloc); + ff_vk_uninit(&s->vkctx); s->initialized = 0; @@ -343,4 +272,5 @@ const AVFilter ff_vf_transpose_vulkan = { FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), .priv_class = &transpose_vulkan_class, .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_unsharp.c b/libavfilter/vf_unsharp.c index e88e732c9ed..2705ac52703 100644 --- a/libavfilter/vf_unsharp.c +++ b/libavfilter/vf_unsharp.c @@ -37,7 +37,6 @@ */ #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "libavutil/common.h" @@ -171,7 +170,10 @@ static int apply_unsharp_c(AVFilterContext *ctx, AVFrame *in, AVFrame *out) return 0; } -static void set_filter_param(UnsharpFilterParam *fp, int msize_x, int msize_y, float amount) +#define MAX_SCALEBITS 25 + +static int set_filter_param(AVFilterContext *ctx, const char *name, const char *short_name, + UnsharpFilterParam *fp, int msize_x, int msize_y, float amount) { fp->msize_x = msize_x; fp->msize_y = msize_y; @@ -181,20 +183,31 @@ static void set_filter_param(UnsharpFilterParam *fp, int msize_x, int msize_y, f fp->steps_y = msize_y / 2; fp->scalebits = (fp->steps_x + fp->steps_y) * 2; fp->halfscale = 1 << (fp->scalebits - 1); + + if (fp->scalebits > MAX_SCALEBITS) { + av_log(ctx, AV_LOG_ERROR, "%s matrix size (%sx/2+%sy/2)*2=%d greater than maximum value %d\n", + name, short_name, short_name, fp->scalebits, MAX_SCALEBITS); + return AVERROR(EINVAL); + } + + return 0; } static av_cold int init(AVFilterContext *ctx) { UnsharpContext *s = ctx->priv; + int ret; - set_filter_param(&s->luma, s->lmsize_x, s->lmsize_y, s->lamount); - set_filter_param(&s->chroma, s->cmsize_x, s->cmsize_y, s->camount); - set_filter_param(&s->alpha, s->amsize_x, s->amsize_y, s->aamount); +#define SET_FILTER_PARAM(name_, short_) \ + ret = set_filter_param(ctx, #name_, #short_, &s->name_, \ + s->short_##msize_x, s->short_##msize_y, s->short_##amount); \ + if (ret < 0) \ + return ret; \ + + SET_FILTER_PARAM(luma, l); + SET_FILTER_PARAM(chroma, c); + SET_FILTER_PARAM(alpha, a); - if (s->luma.scalebits >= 26 || s->chroma.scalebits >= 26 || s->alpha.scalebits >= 26) { - av_log(ctx, AV_LOG_ERROR, "luma or chroma or alpha matrix size too big\n"); - return AVERROR(EINVAL); - } s->apply_unsharp = apply_unsharp_c; return 0; } @@ -352,13 +365,6 @@ static const AVFilterPad avfilter_vf_unsharp_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_unsharp_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_unsharp = { .name = "unsharp", .description = NULL_IF_CONFIG_SMALL("Sharpen or blur the input video."), @@ -367,7 +373,7 @@ const AVFilter ff_vf_unsharp = { .init = init, .uninit = uninit, FILTER_INPUTS(avfilter_vf_unsharp_inputs), - FILTER_OUTPUTS(avfilter_vf_unsharp_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, }; diff --git a/libavfilter/vf_unsharp_opencl.c b/libavfilter/vf_unsharp_opencl.c index 53e310de099..09398464ca3 100644 --- a/libavfilter/vf_unsharp_opencl.c +++ b/libavfilter/vf_unsharp_opencl.c @@ -69,7 +69,7 @@ static int unsharp_opencl_init(AVFilterContext *avctx) cl_int cle; int err; - err = ff_opencl_filter_load_program(avctx, &ff_opencl_source_unsharp, 1); + err = ff_opencl_filter_load_program(avctx, &ff_source_unsharp_cl, 1); if (err < 0) goto fail; @@ -407,4 +407,5 @@ const AVFilter ff_vf_unsharp_opencl = { FILTER_OUTPUTS(unsharp_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_untile.c b/libavfilter/vf_untile.c index 5d7709d68c1..f32f3e186be 100644 --- a/libavfilter/vf_untile.c +++ b/libavfilter/vf_untile.c @@ -24,6 +24,7 @@ #include "avfilter.h" #include "formats.h" #include "filters.h" +#include "video.h" typedef struct UntileContext { const AVClass *class; @@ -134,8 +135,8 @@ static int activate(AVFilterContext *ctx) if (!(s->desc->flags & AV_PIX_FMT_FLAG_PAL)) { for (i = 1; i < 3; i ++) { if (out->data[i]) { - out->data[i] += (y >> s->desc->log2_chroma_w) * out->linesize[i]; - out->data[i] += (x >> s->desc->log2_chroma_h) * s->max_step[i]; + out->data[i] += (y >> s->desc->log2_chroma_h) * out->linesize[i]; + out->data[i] += (x >> s->desc->log2_chroma_w) * s->max_step[i]; } } } @@ -162,13 +163,6 @@ static av_cold void uninit(AVFilterContext *ctx) av_frame_free(&s->frame); } -static const AVFilterPad untile_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - static const AVFilterPad untile_outputs[] = { { .name = "default", @@ -184,7 +178,7 @@ const AVFilter ff_vf_untile = { .uninit = uninit, .activate = activate, .priv_size = sizeof(UntileContext), - FILTER_INPUTS(untile_inputs), + FILTER_INPUTS(ff_video_default_filterpad), FILTER_OUTPUTS(untile_outputs), FILTER_QUERY_FUNC(query_formats), .priv_class = &untile_class, diff --git a/libavfilter/vf_uspp.c b/libavfilter/vf_uspp.c index 051de007716..10c8aaeb6c9 100644 --- a/libavfilter/vf_uspp.c +++ b/libavfilter/vf_uspp.c @@ -37,6 +37,7 @@ #include "internal.h" #include "qp_table.h" #include "avfilter.h" +#include "video.h" #define MAX_LEVEL 8 /* quality levels */ #define BLOCK 16 @@ -44,8 +45,10 @@ typedef struct USPPContext { const AVClass *av_class; int log2_count; + int count; int hsub, vsub; int qp; + char *codec_name; enum AVVideoEncParamsType qscale_type; int temp_stride[3]; uint8_t *src[3]; @@ -53,12 +56,14 @@ typedef struct USPPContext { int outbuf_size; uint8_t *outbuf; AVCodecContext *avctx_enc[BLOCK*BLOCK]; - AVPacket *pkt; - AVFrame *frame; - AVFrame *frame_dec; + AVCodecContext *avctx_dec[BLOCK*BLOCK]; + AVPacket *pkt [BLOCK*BLOCK]; + AVFrame *frame [BLOCK*BLOCK]; + AVFrame *frame_dec [BLOCK*BLOCK]; int8_t *non_b_qp_table; int non_b_qp_stride; int use_bframe_qp; + int quality; } USPPContext; #define OFFSET(x) offsetof(USPPContext, x) @@ -67,6 +72,7 @@ static const AVOption uspp_options[] = { { "quality", "set quality", OFFSET(log2_count), AV_OPT_TYPE_INT, {.i64 = 3}, 0, MAX_LEVEL, FLAGS }, { "qp", "force a constant quantizer parameter", OFFSET(qp), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 63, FLAGS }, { "use_bframe_qp", "use B-frames' QP", OFFSET(use_bframe_qp), AV_OPT_TYPE_BOOL,{.i64 = 0}, 0, 1, FLAGS }, + { "codec", "Codec name", OFFSET(codec_name), AV_OPT_TYPE_STRING, {.str = "snow"}, 0, 0, FLAGS }, { NULL } }; @@ -185,13 +191,96 @@ static void store_slice_c(uint8_t *dst, const uint16_t *src, } } -static void filter(USPPContext *p, uint8_t *dst[3], uint8_t *src[3], +static int filter_1phase(AVFilterContext *ctx, void *arg, int i, int nb_jobs) +{ + USPPContext *p = ctx->priv; + int ret, x, y; + int width = ctx->inputs[0]->w; + int height = ctx->inputs[0]->h; + + const int x1 = offset[i+nb_jobs-1][0]; + const int y1 = offset[i+nb_jobs-1][1]; + const int x1c = x1 >> p->hsub; + const int y1c = y1 >> p->vsub; + const int BLOCKc = BLOCK >> p->hsub; + int offset; + AVPacket *pkt = p->pkt[i]; + + av_packet_unref(pkt); + pkt->data = p->outbuf; + pkt->size = p->outbuf_size; + + p->frame[i]->linesize[0] = p->temp_stride[0]; + p->frame[i]->linesize[1] = p->temp_stride[1]; + p->frame[i]->linesize[2] = p->temp_stride[2]; + p->frame[i]->height = height + BLOCK; + p->frame[i]->width = width + BLOCK; + p->frame[i]->data[0] = p->src[0] + x1 + y1 * p->frame[i]->linesize[0]; + p->frame[i]->data[1] = p->src[1] + x1c + y1c * p->frame[i]->linesize[1]; + p->frame[i]->data[2] = p->src[2] + x1c + y1c * p->frame[i]->linesize[2]; + p->frame[i]->format = p->avctx_enc[i]->pix_fmt; + p->frame[i]->quality = p->quality; + + ret = avcodec_send_frame(p->avctx_enc[i], p->frame[i]); + if (ret < 0) { + av_log(p->avctx_enc[i], AV_LOG_ERROR, "Error sending a frame for encoding\n"); + return ret; + } + ret = avcodec_receive_packet(p->avctx_enc[i], pkt); + if (ret < 0) { + av_log(p->avctx_enc[i], AV_LOG_ERROR, "Error receiving a packet from encoding\n"); + return ret; + } + + if (p->avctx_enc[i]->flags & AV_CODEC_FLAG_RECON_FRAME) { + av_packet_unref(pkt); + ret = avcodec_receive_frame(p->avctx_enc[i], p->frame_dec[i]); + if (ret < 0) { + av_log(p->avctx_dec[i], AV_LOG_ERROR, "Error receiving a frame from encoding\n"); + return ret; + } + } else { + ret = avcodec_send_packet(p->avctx_dec[i], pkt); + av_packet_unref(pkt); + if (ret < 0) { + av_log(p->avctx_dec[i], AV_LOG_ERROR, "Error sending a packet for decoding\n"); + return ret; + } + ret = avcodec_receive_frame(p->avctx_dec[i], p->frame_dec[i]); + if (ret < 0) { + av_log(p->avctx_dec[i], AV_LOG_ERROR, "Error receiving a frame from decoding\n"); + return ret; + } + } + + offset = (BLOCK-x1) + (BLOCK-y1) * p->frame_dec[i]->linesize[0]; + + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + p->temp[0][x + y * p->temp_stride[0]] += p->frame_dec[i]->data[0][x + y * p->frame_dec[i]->linesize[0] + offset]; + + + if (!p->frame_dec[i]->data[2] || !p->temp[2]) + return 0; + + offset = (BLOCKc-x1c) + (BLOCKc-y1c) * p->frame_dec[i]->linesize[1]; + + for (y = 0; y < AV_CEIL_RSHIFT(height, p->vsub); y++) { + for (x = 0; x < AV_CEIL_RSHIFT(width, p->hsub); x++) { + p->temp[1][x + y * p->temp_stride[1]] += p->frame_dec[i]->data[1][x + y * p->frame_dec[i]->linesize[1] + offset]; + p->temp[2][x + y * p->temp_stride[2]] += p->frame_dec[i]->data[2][x + y * p->frame_dec[i]->linesize[2] + offset]; + } + } + + return 0; +} + +static void filter(AVFilterContext *ctx, uint8_t *dst[3], uint8_t *src[3], int dst_stride[3], int src_stride[3], int width, int height, uint8_t *qp_store, int qp_stride) { + USPPContext *p = ctx->priv; int x, y, i, j; - const int count = 1<log2_count; - int ret; for (i = 0; i < 3; i++) { int is_chroma = !!i; @@ -216,12 +305,11 @@ static void filter(USPPContext *p, uint8_t *dst[3], uint8_t *src[3], memcpy(p->src[i] + (h+block +y) * stride, p->src[i] + (h-y+block-1) * stride, stride); } - p->frame->linesize[i] = stride; memset(p->temp[i], 0, (h + 2 * block) * stride * sizeof(int16_t)); } if (p->qp) - p->frame->quality = p->qp * FF_QP2LAMBDA; + p->quality = p->qp * FF_QP2LAMBDA; else { int qpsum=0; int qpcount = (height>>4) * (height>>4); @@ -230,58 +318,11 @@ static void filter(USPPContext *p, uint8_t *dst[3], uint8_t *src[3], for (x = 0; x < (width>>4); x++) qpsum += qp_store[x + y * qp_stride]; } - p->frame->quality = ff_norm_qscale((qpsum + qpcount/2) / qpcount, p->qscale_type) * FF_QP2LAMBDA; + p->quality = ff_norm_qscale((qpsum + qpcount/2) / qpcount, p->qscale_type) * FF_QP2LAMBDA; } // init per MB qscale stuff FIXME - p->frame->height = height + BLOCK; - p->frame->width = width + BLOCK; - - for (i = 0; i < count; i++) { - const int x1 = offset[i+count-1][0]; - const int y1 = offset[i+count-1][1]; - const int x1c = x1 >> p->hsub; - const int y1c = y1 >> p->vsub; - const int BLOCKc = BLOCK >> p->hsub; - int offset; - AVPacket *pkt = p->pkt; - int got_pkt_ptr; - av_packet_unref(pkt); - pkt->data = p->outbuf; - pkt->size = p->outbuf_size; - - p->frame->data[0] = p->src[0] + x1 + y1 * p->frame->linesize[0]; - p->frame->data[1] = p->src[1] + x1c + y1c * p->frame->linesize[1]; - p->frame->data[2] = p->src[2] + x1c + y1c * p->frame->linesize[2]; - p->frame->format = p->avctx_enc[i]->pix_fmt; - - ret = avcodec_encode_video2(p->avctx_enc[i], pkt, p->frame, &got_pkt_ptr); - if (ret < 0) { - av_log(p->avctx_enc[i], AV_LOG_ERROR, "Encoding failed\n"); - continue; - } - av_packet_unref(pkt); - - p->frame_dec = p->avctx_enc[i]->coded_frame; - - offset = (BLOCK-x1) + (BLOCK-y1) * p->frame_dec->linesize[0]; - - for (y = 0; y < height; y++) - for (x = 0; x < width; x++) - p->temp[0][x + y * p->temp_stride[0]] += p->frame_dec->data[0][x + y * p->frame_dec->linesize[0] + offset]; - - if (!src[2] || !dst[2]) - continue; - - offset = (BLOCKc-x1c) + (BLOCKc-y1c) * p->frame_dec->linesize[1]; - - for (y = 0; y < AV_CEIL_RSHIFT(height, p->vsub); y++) { - for (x = 0; x < AV_CEIL_RSHIFT(width, p->hsub); x++) { - p->temp[1][x + y * p->temp_stride[1]] += p->frame_dec->data[1][x + y * p->frame_dec->linesize[1] + offset]; - p->temp[2][x + y * p->temp_stride[2]] += p->frame_dec->data[2][x + y * p->frame_dec->linesize[2] + offset]; - } - } - } + ff_filter_execute(ctx, filter_1phase, NULL, NULL, p->count); for (j = 0; j < 3; j++) { int is_chroma = !!j; @@ -313,15 +354,20 @@ static int config_input(AVFilterLink *inlink) const int width = inlink->w; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); int i; - - const AVCodec *enc = avcodec_find_encoder(AV_CODEC_ID_SNOW); + const AVCodec *enc = avcodec_find_encoder_by_name(uspp->codec_name); + const AVCodec *dec = avcodec_find_decoder_by_name(uspp->codec_name); if (!enc) { - av_log(ctx, AV_LOG_ERROR, "SNOW encoder not found.\n"); + av_log(ctx, AV_LOG_ERROR, "encoder %s not found.\n", uspp->codec_name); + return AVERROR(EINVAL); + } + if (!dec) { + av_log(ctx, AV_LOG_ERROR, "decoder %s not found.\n", uspp->codec_name); return AVERROR(EINVAL); } uspp->hsub = desc->log2_chroma_w; uspp->vsub = desc->log2_chroma_h; + uspp->count = 1<log2_count; for (i = 0; i < 3; i++) { int is_chroma = !!i; @@ -340,8 +386,8 @@ static int config_input(AVFilterLink *inlink) return AVERROR(ENOMEM); } - for (i = 0; i < (1<log2_count); i++) { - AVCodecContext *avctx_enc; + for (i = 0; i < uspp->count; i++) { + AVCodecContext *avctx_enc, *avctx_dec; AVDictionary *opts = NULL; int ret; @@ -356,21 +402,41 @@ static int config_input(AVFilterLink *inlink) avctx_enc->max_b_frames = 0; avctx_enc->pix_fmt = inlink->format; avctx_enc->flags = AV_CODEC_FLAG_QSCALE | AV_CODEC_FLAG_LOW_DELAY; + if (enc->capabilities & AV_CODEC_CAP_ENCODER_RECON_FRAME) { + avctx_enc->flags |= AV_CODEC_FLAG_RECON_FRAME; + av_dict_set(&opts, "no_bitstream", "1", 0); + } avctx_enc->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL; avctx_enc->global_quality = 123; - av_dict_set(&opts, "no_bitstream", "1", 0); + avctx_enc->thread_count = 1; // We do threading in the filter with muiltiple codecs ret = avcodec_open2(avctx_enc, enc, &opts); av_dict_free(&opts); if (ret < 0) return ret; av_assert0(avctx_enc->codec); + + + if (!(enc->capabilities & AV_CODEC_CAP_ENCODER_RECON_FRAME)) { + if (!(uspp->avctx_dec[i] = avcodec_alloc_context3(NULL))) + return AVERROR(ENOMEM); + avctx_dec = uspp->avctx_dec[i]; + avctx_dec->width = avctx_enc->width; + avctx_dec->height = avctx_enc->height; + avctx_dec->thread_count = 1; + ret = avcodec_open2(avctx_dec, dec, NULL); + if (ret < 0) + return ret; + } + + if (!(uspp->frame[i] = av_frame_alloc())) + return AVERROR(ENOMEM); + if (!(uspp->frame_dec[i] = av_frame_alloc())) + return AVERROR(ENOMEM); + if (!(uspp->pkt[i] = av_packet_alloc())) + return AVERROR(ENOMEM); } uspp->outbuf_size = (width + BLOCK) * (height + BLOCK) * 10; - if (!(uspp->frame = av_frame_alloc())) - return AVERROR(ENOMEM); - if (!(uspp->pkt = av_packet_alloc())) - return AVERROR(ENOMEM); if (!(uspp->outbuf = av_malloc(uspp->outbuf_size))) return AVERROR(ENOMEM); @@ -432,7 +498,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) out->height = in->height; } - filter(uspp, out->data, in->data, out->linesize, in->linesize, + filter(ctx, out->data, in->data, out->linesize, in->linesize, inlink->w, inlink->h, qp_table, qp_stride); } } @@ -460,13 +526,16 @@ static av_cold void uninit(AVFilterContext *ctx) av_freep(&uspp->src[i]); } - for (i = 0; i < (1 << uspp->log2_count); i++) + for (i = 0; i < uspp->count; i++) { avcodec_free_context(&uspp->avctx_enc[i]); + avcodec_free_context(&uspp->avctx_dec[i]); + av_frame_free(&uspp->frame[i]); + av_frame_free(&uspp->frame_dec[i]); + av_packet_free(&uspp->pkt[i]); + } av_freep(&uspp->non_b_qp_table); av_freep(&uspp->outbuf); - av_packet_free(&uspp->pkt); - av_frame_free(&uspp->frame); } static const AVFilterPad uspp_inputs[] = { @@ -478,21 +547,14 @@ static const AVFilterPad uspp_inputs[] = { }, }; -static const AVFilterPad uspp_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_uspp = { .name = "uspp", .description = NULL_IF_CONFIG_SMALL("Apply Ultra Simple / Slow Post-processing filter."), .priv_size = sizeof(USPPContext), .uninit = uninit, FILTER_INPUTS(uspp_inputs), - FILTER_OUTPUTS(uspp_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &uspp_class, - .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, }; diff --git a/libavfilter/vf_vaguedenoiser.c b/libavfilter/vf_vaguedenoiser.c index fb66f0dea1c..bfb6e9e262c 100644 --- a/libavfilter/vf_vaguedenoiser.c +++ b/libavfilter/vf_vaguedenoiser.c @@ -24,11 +24,9 @@ #include "libavutil/attributes.h" #include "libavutil/common.h" #include "libavutil/pixdesc.h" -#include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -594,13 +592,6 @@ static const AVFilterPad vaguedenoiser_inputs[] = { }; -static const AVFilterPad vaguedenoiser_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO - }, -}; - const AVFilter ff_vf_vaguedenoiser = { .name = "vaguedenoiser", .description = NULL_IF_CONFIG_SMALL("Apply a Wavelet based Denoiser."), @@ -609,7 +600,7 @@ const AVFilter ff_vf_vaguedenoiser = { .init = init, .uninit = uninit, FILTER_INPUTS(vaguedenoiser_inputs), - FILTER_OUTPUTS(vaguedenoiser_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_varblur.c b/libavfilter/vf_varblur.c index 3c020a8c3ec..f6f8382adcd 100644 --- a/libavfilter/vf_varblur.c +++ b/libavfilter/vf_varblur.c @@ -22,7 +22,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "framesync.h" #include "internal.h" #include "video.h" @@ -39,7 +38,8 @@ typedef struct VarBlurContext { int planewidth[4]; int planeheight[4]; - AVFrame *sat; + uint8_t *sat[4]; + int sat_linesize[4]; int nb_planes; void (*compute_sat)(const uint8_t *ssrc, @@ -90,6 +90,7 @@ static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16, AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16, + AV_PIX_FMT_GBRPF32, AV_PIX_FMT_GBRAPF32, AV_PIX_FMT_NONE }; @@ -104,7 +105,7 @@ static void compute_sat##depth(const uint8_t *ssrc, \ stype *dst = (stype *)dstp; \ \ linesize /= (depth / 8); \ - dst_linesize /= (depth / 2); \ + dst_linesize /= sizeof(stype); \ dst += dst_linesize; \ \ for (int y = 0; y < h; y++) { \ @@ -122,6 +123,7 @@ static void compute_sat##depth(const uint8_t *ssrc, \ COMPUTE_SAT(uint8_t, uint32_t, 8) COMPUTE_SAT(uint16_t, uint64_t, 16) +COMPUTE_SAT(float, double, 32) typedef struct ThreadData { AVFrame *in, *out, *radius; @@ -144,12 +146,12 @@ static int blur_plane##bits(AVFilterContext *ctx, \ int slice_start, int slice_end) \ { \ VarBlurContext *s = ctx->priv; \ - const int ddepth = s->depth; \ + const int ddepth = (bits == 32) ? 1 : s->depth; \ const int dst_linesize = ddst_linesize / (bits / 8); \ - const int ptr_linesize = pptr_linesize / (bits / 2); \ + const int ptr_linesize = pptr_linesize / sizeof(stype); \ const int rptr_linesize = rrptr_linesize / (bits / 8); \ - const type *rptr = (const type *)rrptr + slice_start * rptr_linesize; \ - type *dst = (type *)ddst + slice_start * dst_linesize; \ + const type *rptr = ((const type *)rrptr) + slice_start * rptr_linesize; \ + type *dst = ((type *)ddst) + slice_start * dst_linesize; \ const stype *ptr = (stype *)pptr; \ const float minr = 2.f * s->min_radius + 1.f; \ const float maxr = 2.f * s->max_radius + 1.f; \ @@ -182,7 +184,10 @@ static int blur_plane##bits(AVFilterContext *ctx, \ stype p0 = (br + tl - bl - tr) / div; \ stype n0 = (nbr + ntl - nbl - ntr) / ndiv; \ \ - dst[x] = av_clip_uintp2_c(lrintf( \ + if (bits == 32) \ + dst[x] = lerpf(p0, n0, factor); \ + else \ + dst[x] = av_clip_uintp2_c(lrintf( \ lerpf(p0, n0, factor)), \ ddepth); \ } \ @@ -196,6 +201,7 @@ static int blur_plane##bits(AVFilterContext *ctx, \ BLUR_PLANE(uint8_t, uint32_t, 8) BLUR_PLANE(uint16_t, uint64_t, 16) +BLUR_PLANE(float, double, 32) static int blur_planes(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) @@ -215,8 +221,8 @@ static int blur_planes(AVFilterContext *ctx, void *arg, const int dst_linesize = out->linesize[plane]; const uint8_t *rptr = radius->data[plane]; const int rptr_linesize = radius->linesize[plane]; - uint8_t *ptr = s->sat->data[plane]; - const int ptr_linesize = s->sat->linesize[plane]; + uint8_t *ptr = s->sat[plane]; + const int ptr_linesize = s->sat_linesize[plane]; const uint8_t *src = in->data[plane]; uint8_t *dst = out->data[plane]; @@ -263,8 +269,8 @@ static int blur_frame(AVFilterContext *ctx, AVFrame *in, AVFrame *radius) const int height = s->planeheight[plane]; const int width = s->planewidth[plane]; const int linesize = in->linesize[plane]; - uint8_t *ptr = s->sat->data[plane]; - const int ptr_linesize = s->sat->linesize[plane]; + uint8_t *ptr = s->sat[plane]; + const int ptr_linesize = s->sat_linesize[plane]; const uint8_t *src = in->data[plane]; if (!(s->planes & (1 << plane))) @@ -333,8 +339,8 @@ static int config_output(AVFilterLink *outlink) outlink->frame_rate = inlink->frame_rate; s->depth = desc->comp[0].depth; - s->blur_plane = s->depth <= 8 ? blur_plane8 : blur_plane16; - s->compute_sat = s->depth <= 8 ? compute_sat8 : compute_sat16; + s->blur_plane = s->depth <= 8 ? blur_plane8 : s->depth <= 16 ? blur_plane16 : blur_plane32; + s->compute_sat = s->depth <= 8 ? compute_sat8 : s->depth <= 16 ? compute_sat16 : compute_sat32; s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(outlink->w, desc->log2_chroma_w); s->planewidth[0] = s->planewidth[3] = outlink->w; @@ -343,9 +349,12 @@ static int config_output(AVFilterLink *outlink) s->nb_planes = av_pix_fmt_count_planes(outlink->format); - s->sat = ff_get_video_buffer(outlink, (outlink->w + 1) * 4 * ((s->depth + 7) / 8), outlink->h + 1); - if (!s->sat) - return AVERROR(ENOMEM); + for (int p = 0; p < s->nb_planes; p++) { + s->sat_linesize[p] = (outlink->w + 1) * (4 + 4 * (s->depth > 8)); + s->sat[p] = av_calloc(s->sat_linesize[p], outlink->h + 1); + if (!s->sat[p]) + return AVERROR(ENOMEM); + } s->fs.on_event = varblur_frame; if ((ret = ff_framesync_init_dualinput(&s->fs, ctx)) < 0) @@ -362,7 +371,8 @@ static av_cold void uninit(AVFilterContext *ctx) VarBlurContext *s = ctx->priv; ff_framesync_uninit(&s->fs); - av_frame_free(&s->sat); + for (int p = 0; p < 4; p++) + av_freep(&s->sat[p]); } static const AVFilterPad varblur_inputs[] = { diff --git a/libavfilter/vf_vflip.c b/libavfilter/vf_vflip.c index 0d624512f9f..8d6724ed370 100644 --- a/libavfilter/vf_vflip.c +++ b/libavfilter/vf_vflip.c @@ -135,19 +135,12 @@ static const AVFilterPad avfilter_vf_vflip_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_vflip_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_vflip = { .name = "vflip", .description = NULL_IF_CONFIG_SMALL("Flip the input video vertically."), .priv_size = sizeof(FlipContext), .priv_class = &vflip_class, FILTER_INPUTS(avfilter_vf_vflip_inputs), - FILTER_OUTPUTS(avfilter_vf_vflip_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_vfrdet.c b/libavfilter/vf_vfrdet.c index 0ca221b5b86..8d641dd3688 100644 --- a/libavfilter/vf_vfrdet.c +++ b/libavfilter/vf_vfrdet.c @@ -21,6 +21,7 @@ #include "libavutil/common.h" #include "libavutil/opt.h" #include "internal.h" +#include "video.h" typedef struct VFRDETContext { const AVClass *class; @@ -95,13 +96,6 @@ static const AVFilterPad vfrdet_inputs[] = { }, }; -static const AVFilterPad vfrdet_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_vfrdet = { .name = "vfrdet", .description = NULL_IF_CONFIG_SMALL("Variable frame rate detect filter."), @@ -110,5 +104,5 @@ const AVFilter ff_vf_vfrdet = { .uninit = uninit, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(vfrdet_inputs), - FILTER_OUTPUTS(vfrdet_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), }; diff --git a/libavfilter/vf_vibrance.c b/libavfilter/vf_vibrance.c index 81ae63902cb..e17fce07c0a 100644 --- a/libavfilter/vf_vibrance.c +++ b/libavfilter/vf_vibrance.c @@ -19,16 +19,16 @@ */ #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" #define R 0 #define G 1 #define B 2 +#define A 3 typedef struct VibranceContext { const AVClass *class; @@ -51,10 +51,16 @@ static inline float lerpf(float v0, float v1, float f) return v0 + (v1 - v0) * f; } +typedef struct ThreadData { + AVFrame *out, *in; +} ThreadData; + static int vibrance_slice8(AVFilterContext *avctx, void *arg, int jobnr, int nb_jobs) { VibranceContext *s = avctx->priv; - AVFrame *frame = arg; + ThreadData *td = arg; + AVFrame *frame = td->out; + AVFrame *in = td->in; const int width = frame->width; const int height = frame->height; const float scale = 1.f / 255.f; @@ -74,15 +80,25 @@ static int vibrance_slice8(AVFilterContext *avctx, void *arg, int jobnr, int nb_ const int glinesize = frame->linesize[0]; const int blinesize = frame->linesize[1]; const int rlinesize = frame->linesize[2]; + const int alinesize = frame->linesize[3]; + const int gslinesize = in->linesize[0]; + const int bslinesize = in->linesize[1]; + const int rslinesize = in->linesize[2]; + const int aslinesize = in->linesize[3]; + const uint8_t *gsrc = in->data[0] + slice_start * glinesize; + const uint8_t *bsrc = in->data[1] + slice_start * blinesize; + const uint8_t *rsrc = in->data[2] + slice_start * rlinesize; uint8_t *gptr = frame->data[0] + slice_start * glinesize; uint8_t *bptr = frame->data[1] + slice_start * blinesize; uint8_t *rptr = frame->data[2] + slice_start * rlinesize; + const uint8_t *asrc = in->data[3]; + uint8_t *aptr = frame->data[3]; for (int y = slice_start; y < slice_end; y++) { for (int x = 0; x < width; x++) { - float g = gptr[x] * scale; - float b = bptr[x] * scale; - float r = rptr[x] * scale; + float g = gsrc[x] * scale; + float b = bsrc[x] * scale; + float r = rsrc[x] * scale; float max_color = FFMAX3(r, g, b); float min_color = FFMIN3(r, g, b); float color_saturation = max_color - min_color; @@ -100,6 +116,12 @@ static int vibrance_slice8(AVFilterContext *avctx, void *arg, int jobnr, int nb_ rptr[x] = av_clip_uint8(r * 255.f); } + if (aptr && alinesize && frame != in) + memcpy(aptr + alinesize * y, asrc + aslinesize * y, width); + + gsrc += gslinesize; + bsrc += bslinesize; + rsrc += rslinesize; gptr += glinesize; bptr += blinesize; rptr += rlinesize; @@ -111,7 +133,9 @@ static int vibrance_slice8(AVFilterContext *avctx, void *arg, int jobnr, int nb_ static int vibrance_slice16(AVFilterContext *avctx, void *arg, int jobnr, int nb_jobs) { VibranceContext *s = avctx->priv; - AVFrame *frame = arg; + ThreadData *td = arg; + AVFrame *frame = td->out; + AVFrame *in = td->in; const int depth = s->depth; const float max = (1 << depth) - 1; const float scale = 1.f / max; @@ -130,18 +154,28 @@ static int vibrance_slice16(AVFilterContext *avctx, void *arg, int jobnr, int nb const float srintensity = alternate * FFSIGN(rintensity); const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; + const int gslinesize = in->linesize[0] / 2; + const int bslinesize = in->linesize[1] / 2; + const int rslinesize = in->linesize[2] / 2; + const int aslinesize = in->linesize[3] / 2; const int glinesize = frame->linesize[0] / 2; const int blinesize = frame->linesize[1] / 2; const int rlinesize = frame->linesize[2] / 2; + const int alinesize = frame->linesize[3] / 2; + const uint16_t *gsrc = (const uint16_t *)in->data[0] + slice_start * gslinesize; + const uint16_t *bsrc = (const uint16_t *)in->data[1] + slice_start * bslinesize; + const uint16_t *rsrc = (const uint16_t *)in->data[2] + slice_start * rslinesize; uint16_t *gptr = (uint16_t *)frame->data[0] + slice_start * glinesize; uint16_t *bptr = (uint16_t *)frame->data[1] + slice_start * blinesize; uint16_t *rptr = (uint16_t *)frame->data[2] + slice_start * rlinesize; + const uint16_t *asrc = (const uint16_t *)in->data[3]; + uint16_t *aptr = (uint16_t *)frame->data[3]; for (int y = slice_start; y < slice_end; y++) { for (int x = 0; x < width; x++) { - float g = gptr[x] * scale; - float b = bptr[x] * scale; - float r = rptr[x] * scale; + float g = gsrc[x] * scale; + float b = bsrc[x] * scale; + float r = rsrc[x] * scale; float max_color = FFMAX3(r, g, b); float min_color = FFMIN3(r, g, b); float color_saturation = max_color - min_color; @@ -159,6 +193,12 @@ static int vibrance_slice16(AVFilterContext *avctx, void *arg, int jobnr, int nb rptr[x] = av_clip_uintp2_c(r * max, depth); } + if (aptr && alinesize && frame != in) + memcpy(aptr + alinesize * y, asrc + aslinesize * y, width * 2); + + gsrc += gslinesize; + bsrc += bslinesize; + rsrc += rslinesize; gptr += glinesize; bptr += blinesize; rptr += rlinesize; @@ -170,7 +210,9 @@ static int vibrance_slice16(AVFilterContext *avctx, void *arg, int jobnr, int nb static int vibrance_slice8p(AVFilterContext *avctx, void *arg, int jobnr, int nb_jobs) { VibranceContext *s = avctx->priv; - AVFrame *frame = arg; + ThreadData *td = arg; + AVFrame *frame = td->out; + AVFrame *in = td->in; const int step = s->step; const int width = frame->width; const int height = frame->height; @@ -181,6 +223,7 @@ static int vibrance_slice8p(AVFilterContext *avctx, void *arg, int jobnr, int nb const uint8_t roffset = s->rgba_map[R]; const uint8_t goffset = s->rgba_map[G]; const uint8_t boffset = s->rgba_map[B]; + const uint8_t aoffset = s->rgba_map[A]; const float intensity = s->intensity; const float alternate = s->alternate ? 1.f : -1.f; const float gintensity = intensity * s->balance[0]; @@ -192,13 +235,15 @@ static int vibrance_slice8p(AVFilterContext *avctx, void *arg, int jobnr, int nb const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; const int linesize = frame->linesize[0]; + const int slinesize = in->linesize[0]; + const uint8_t *src = in->data[0] + slice_start * slinesize; uint8_t *ptr = frame->data[0] + slice_start * linesize; for (int y = slice_start; y < slice_end; y++) { for (int x = 0; x < width; x++) { - float g = ptr[x * step + goffset] * scale; - float b = ptr[x * step + boffset] * scale; - float r = ptr[x * step + roffset] * scale; + float g = src[x * step + goffset] * scale; + float b = src[x * step + boffset] * scale; + float r = src[x * step + roffset] * scale; float max_color = FFMAX3(r, g, b); float min_color = FFMIN3(r, g, b); float color_saturation = max_color - min_color; @@ -214,9 +259,13 @@ static int vibrance_slice8p(AVFilterContext *avctx, void *arg, int jobnr, int nb ptr[x * step + goffset] = av_clip_uint8(g * 255.f); ptr[x * step + boffset] = av_clip_uint8(b * 255.f); ptr[x * step + roffset] = av_clip_uint8(r * 255.f); + + if (frame != in) + ptr[x * step + aoffset] = src[x * step + aoffset]; } ptr += linesize; + src += slinesize; } return 0; @@ -225,7 +274,9 @@ static int vibrance_slice8p(AVFilterContext *avctx, void *arg, int jobnr, int nb static int vibrance_slice16p(AVFilterContext *avctx, void *arg, int jobnr, int nb_jobs) { VibranceContext *s = avctx->priv; - AVFrame *frame = arg; + ThreadData *td = arg; + AVFrame *frame = td->out; + AVFrame *in = td->in; const int step = s->step; const int depth = s->depth; const float max = (1 << depth) - 1; @@ -236,6 +287,7 @@ static int vibrance_slice16p(AVFilterContext *avctx, void *arg, int jobnr, int n const uint8_t roffset = s->rgba_map[R]; const uint8_t goffset = s->rgba_map[G]; const uint8_t boffset = s->rgba_map[B]; + const uint8_t aoffset = s->rgba_map[A]; const int width = frame->width; const int height = frame->height; const float intensity = s->intensity; @@ -249,13 +301,15 @@ static int vibrance_slice16p(AVFilterContext *avctx, void *arg, int jobnr, int n const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; const int linesize = frame->linesize[0] / 2; + const int slinesize = in->linesize[0] / 2; + const uint16_t *src = (const uint16_t *)in->data[0] + slice_start * slinesize; uint16_t *ptr = (uint16_t *)frame->data[0] + slice_start * linesize; for (int y = slice_start; y < slice_end; y++) { for (int x = 0; x < width; x++) { - float g = ptr[x * step + goffset] * scale; - float b = ptr[x * step + boffset] * scale; - float r = ptr[x * step + roffset] * scale; + float g = src[x * step + goffset] * scale; + float b = src[x * step + boffset] * scale; + float r = src[x * step + roffset] * scale; float max_color = FFMAX3(r, g, b); float min_color = FFMIN3(r, g, b); float color_saturation = max_color - min_color; @@ -271,25 +325,46 @@ static int vibrance_slice16p(AVFilterContext *avctx, void *arg, int jobnr, int n ptr[x * step + goffset] = av_clip_uintp2_c(g * max, depth); ptr[x * step + boffset] = av_clip_uintp2_c(b * max, depth); ptr[x * step + roffset] = av_clip_uintp2_c(r * max, depth); + if (frame != in) + ptr[x * step + aoffset] = src[x * step + aoffset]; } ptr += linesize; + src += slinesize; } return 0; } -static int filter_frame(AVFilterLink *link, AVFrame *frame) +static int filter_frame(AVFilterLink *link, AVFrame *in) { AVFilterContext *avctx = link->dst; + AVFilterLink *outlink = avctx->outputs[0]; VibranceContext *s = avctx->priv; + ThreadData td; + AVFrame *out; int res; - if (res = ff_filter_execute(avctx, s->do_slice, frame, NULL, - FFMIN(frame->height, ff_filter_get_nb_threads(avctx)))) + if (av_frame_is_writable(in)) { + out = in; + } else { + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } + av_frame_copy_props(out, in); + } + + td.out = out; + td.in = in; + if (res = ff_filter_execute(avctx, s->do_slice, &td, NULL, + FFMIN(out->height, ff_filter_get_nb_threads(avctx)))) return res; - return ff_filter_frame(avctx->outputs[0], frame); + if (out != in) + av_frame_free(&in); + return ff_filter_frame(outlink, out); } static const enum AVPixelFormat pixel_fmts[] = { @@ -335,19 +410,11 @@ static const AVFilterPad vibrance_inputs[] = { { .name = "default", .type = AVMEDIA_TYPE_VIDEO, - .flags = AVFILTERPAD_FLAG_NEEDS_WRITABLE, .filter_frame = filter_frame, .config_props = config_input, }, }; -static const AVFilterPad vibrance_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(VibranceContext, x) #define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -371,7 +438,7 @@ const AVFilter ff_vf_vibrance = { .priv_size = sizeof(VibranceContext), .priv_class = &vibrance_class, FILTER_INPUTS(vibrance_inputs), - FILTER_OUTPUTS(vibrance_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_vidstabdetect.c b/libavfilter/vf_vidstabdetect.c index 62b998e1715..a2c6d895037 100644 --- a/libavfilter/vf_vidstabdetect.c +++ b/libavfilter/vf_vidstabdetect.c @@ -27,7 +27,9 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" +#include "filters.h" #include "internal.h" +#include "video.h" #include "vidstabutils.h" @@ -149,10 +151,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) AVFilterLink *outlink = inlink->dst->outputs[0]; VSFrame frame; - int plane; + int plane, ret; - if (s->conf.show > 0 && !av_frame_is_writable(in)) - av_frame_make_writable(in); + if (s->conf.show > 0 && !av_frame_is_writable(in)) { + ret = ff_inlink_make_frame_writable(inlink, &in); + if (ret < 0) { + av_frame_free(&in); + return ret; + } + } for (plane = 0; plane < md->fi.planes; plane++) { frame.data[plane] = in->data[plane]; @@ -182,13 +189,6 @@ static const AVFilterPad avfilter_vf_vidstabdetect_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_vidstabdetect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_vidstabdetect = { .name = "vidstabdetect", .description = NULL_IF_CONFIG_SMALL("Extract relative transformations, " @@ -199,7 +199,7 @@ const AVFilter ff_vf_vidstabdetect = { .uninit = uninit, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(avfilter_vf_vidstabdetect_inputs), - FILTER_OUTPUTS(avfilter_vf_vidstabdetect_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(ff_vidstab_pix_fmts), .priv_class = &vidstabdetect_class, }; diff --git a/libavfilter/vf_vidstabtransform.c b/libavfilter/vf_vidstabtransform.c index 1914d7b3486..8a66a463b46 100644 --- a/libavfilter/vf_vidstabtransform.c +++ b/libavfilter/vf_vidstabtransform.c @@ -28,6 +28,7 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" #include "internal.h" +#include "video.h" #include "vidstabutils.h" @@ -282,13 +283,6 @@ static const AVFilterPad avfilter_vf_vidstabtransform_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_vidstabtransform_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_vidstabtransform = { .name = "vidstabtransform", .description = NULL_IF_CONFIG_SMALL("Transform the frames, " @@ -298,7 +292,7 @@ const AVFilter ff_vf_vidstabtransform = { .init = init, .uninit = uninit, FILTER_INPUTS(avfilter_vf_vidstabtransform_inputs), - FILTER_OUTPUTS(avfilter_vf_vidstabtransform_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(ff_vidstab_pix_fmts), .priv_class = &vidstabtransform_class, }; diff --git a/libavfilter/vf_vif.c b/libavfilter/vf_vif.c index 1aea4a73c64..3c662491b20 100644 --- a/libavfilter/vf_vif.c +++ b/libavfilter/vf_vif.c @@ -27,15 +27,11 @@ #include -#include "libavutil/avstring.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" #include "framesync.h" -#include "drawutils.h" -#include "formats.h" #include "internal.h" -#include "video.h" #define NUM_DATA_BUFS 13 diff --git a/libavfilter/vf_vignette.c b/libavfilter/vf_vignette.c index 56764b49d11..bfbf986f3c4 100644 --- a/libavfilter/vf_vignette.c +++ b/libavfilter/vf_vignette.c @@ -24,7 +24,6 @@ #include "libavutil/eval.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -325,13 +324,6 @@ static const AVFilterPad vignette_inputs[] = { }, }; -static const AVFilterPad vignette_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_vignette = { .name = "vignette", .description = NULL_IF_CONFIG_SMALL("Make or reverse a vignette effect."), @@ -339,7 +331,7 @@ const AVFilter ff_vf_vignette = { .init = init, .uninit = uninit, FILTER_INPUTS(vignette_inputs), - FILTER_OUTPUTS(vignette_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &vignette_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_vmafmotion.c b/libavfilter/vf_vmafmotion.c index 137afd92453..022816e896f 100644 --- a/libavfilter/vf_vmafmotion.c +++ b/libavfilter/vf_vmafmotion.c @@ -30,6 +30,7 @@ #include "avfilter.h" #include "formats.h" #include "internal.h" +#include "video.h" #include "vmaf_motion.h" #define BIT_SHIFT 15 @@ -350,13 +351,6 @@ static const AVFilterPad vmafmotion_inputs[] = { }, }; -static const AVFilterPad vmafmotion_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_vmafmotion = { .name = "vmafmotion", .description = NULL_IF_CONFIG_SMALL("Calculate the VMAF Motion score."), @@ -366,6 +360,6 @@ const AVFilter ff_vf_vmafmotion = { .priv_class = &vmafmotion_class, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(vmafmotion_inputs), - FILTER_OUTPUTS(vmafmotion_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/vf_vpp_qsv.c b/libavfilter/vf_vpp_qsv.c index 317ae06c122..92ef0f1d890 100644 --- a/libavfilter/vf_vpp_qsv.c +++ b/libavfilter/vf_vpp_qsv.c @@ -23,12 +23,15 @@ #include +#include "config_components.h" + #include "libavutil/opt.h" #include "libavutil/eval.h" #include "libavutil/hwcontext.h" #include "libavutil/hwcontext_qsv.h" #include "libavutil/pixdesc.h" #include "libavutil/mathematics.h" +#include "libavutil/mastering_display_metadata.h" #include "formats.h" #include "internal.h" @@ -45,9 +48,7 @@ #define ENH_FILTERS_COUNT (8) typedef struct VPPContext{ - const AVClass *class; - - QSVVPPContext *qsv; + QSVVPPContext qsv; /* Video Enhancement Algorithms */ mfxExtVPPDeinterlacing deinterlace_conf; @@ -58,7 +59,21 @@ typedef struct VPPContext{ mfxExtVPPRotation rotation_conf; mfxExtVPPMirroring mirroring_conf; mfxExtVPPScaling scale_conf; +#if QSV_ONEVPL + /** Video signal info attached on the input frame */ + mfxExtVideoSignalInfo invsi_conf; + /** Video signal info attached on the output frame */ + mfxExtVideoSignalInfo outvsi_conf; + /** HDR parameters attached on the input frame */ + mfxExtMasteringDisplayColourVolume mdcv_conf; + mfxExtContentLightLevelInfo clli_conf; +#endif + /** + * New dimensions. Special values are: + * 0 = original width/height + * -1 = keep original aspect + */ int out_width; int out_height; /** @@ -95,51 +110,20 @@ typedef struct VPPContext{ char *ow, *oh; char *output_format_str; - int async_depth; - int eof; -} VPPContext; + /** The color properties for output */ + char *color_primaries_str; + char *color_transfer_str; + char *color_matrix_str; -static const AVOption options[] = { - { "deinterlace", "deinterlace mode: 0=off, 1=bob, 2=advanced", OFFSET(deinterlace), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, MFX_DEINTERLACING_ADVANCED, .flags = FLAGS, "deinterlace" }, - { "bob", "Bob deinterlace mode.", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_DEINTERLACING_BOB }, .flags = FLAGS, "deinterlace" }, - { "advanced", "Advanced deinterlace mode. ", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_DEINTERLACING_ADVANCED }, .flags = FLAGS, "deinterlace" }, + int color_range; + enum AVColorPrimaries color_primaries; + enum AVColorTransferCharacteristic color_transfer; + enum AVColorSpace color_matrix; - { "denoise", "denoise level [0, 100]", OFFSET(denoise), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 100, .flags = FLAGS }, - { "detail", "enhancement level [0, 100]", OFFSET(detail), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 100, .flags = FLAGS }, - { "framerate", "output framerate", OFFSET(framerate), AV_OPT_TYPE_RATIONAL, { .dbl = 0.0 },0, DBL_MAX, .flags = FLAGS }, - { "procamp", "Enable ProcAmp", OFFSET(procamp), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = FLAGS}, - { "hue", "ProcAmp hue", OFFSET(hue), AV_OPT_TYPE_FLOAT, { .dbl = 0.0 }, -180.0, 180.0, .flags = FLAGS}, - { "saturation", "ProcAmp saturation", OFFSET(saturation), AV_OPT_TYPE_FLOAT, { .dbl = 1.0 }, 0.0, 10.0, .flags = FLAGS}, - { "contrast", "ProcAmp contrast", OFFSET(contrast), AV_OPT_TYPE_FLOAT, { .dbl = 1.0 }, 0.0, 10.0, .flags = FLAGS}, - { "brightness", "ProcAmp brightness", OFFSET(brightness), AV_OPT_TYPE_FLOAT, { .dbl = 0.0 }, -100.0, 100.0, .flags = FLAGS}, - - { "transpose", "set transpose direction", OFFSET(transpose), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 6, FLAGS, "transpose"}, - { "cclock_hflip", "rotate counter-clockwise with horizontal flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK_FLIP }, .flags=FLAGS, .unit = "transpose" }, - { "clock", "rotate clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK }, .flags=FLAGS, .unit = "transpose" }, - { "cclock", "rotate counter-clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK }, .flags=FLAGS, .unit = "transpose" }, - { "clock_hflip", "rotate clockwise with horizontal flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK_FLIP }, .flags=FLAGS, .unit = "transpose" }, - { "reversal", "rotate by half-turn", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_REVERSAL }, .flags=FLAGS, .unit = "transpose" }, - { "hflip", "flip horizontally", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_HFLIP }, .flags=FLAGS, .unit = "transpose" }, - { "vflip", "flip vertically", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_VFLIP }, .flags=FLAGS, .unit = "transpose" }, - - { "cw", "set the width crop area expression", OFFSET(cw), AV_OPT_TYPE_STRING, { .str = "iw" }, 0, 0, FLAGS }, - { "ch", "set the height crop area expression", OFFSET(ch), AV_OPT_TYPE_STRING, { .str = "ih" }, 0, 0, FLAGS }, - { "cx", "set the x crop area expression", OFFSET(cx), AV_OPT_TYPE_STRING, { .str = "(in_w-out_w)/2" }, 0, 0, FLAGS }, - { "cy", "set the y crop area expression", OFFSET(cy), AV_OPT_TYPE_STRING, { .str = "(in_h-out_h)/2" }, 0, 0, FLAGS }, - - { "w", "Output video width", OFFSET(ow), AV_OPT_TYPE_STRING, { .str="cw" }, 0, 255, .flags = FLAGS }, - { "width", "Output video width", OFFSET(ow), AV_OPT_TYPE_STRING, { .str="cw" }, 0, 255, .flags = FLAGS }, - { "h", "Output video height", OFFSET(oh), AV_OPT_TYPE_STRING, { .str="w*ch/cw" }, 0, 255, .flags = FLAGS }, - { "height", "Output video height", OFFSET(oh), AV_OPT_TYPE_STRING, { .str="w*ch/cw" }, 0, 255, .flags = FLAGS }, - { "format", "Output pixel format", OFFSET(output_format_str), AV_OPT_TYPE_STRING, { .str = "same" }, .flags = FLAGS }, - { "async_depth", "Internal parallelization depth, the higher the value the higher the latency.", OFFSET(async_depth), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, .flags = FLAGS }, - { "scale_mode", "scale & format conversion mode: 0=auto, 1=low power, 2=high quality", OFFSET(scale_mode), AV_OPT_TYPE_INT, { .i64 = MFX_SCALING_MODE_DEFAULT }, MFX_SCALING_MODE_DEFAULT, MFX_SCALING_MODE_QUALITY, .flags = FLAGS, "scale mode" }, - { "auto", "auto mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_DEFAULT}, INT_MIN, INT_MAX, FLAGS, "scale mode"}, - { "low_power", "low power mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_LOWPOWER}, INT_MIN, INT_MAX, FLAGS, "scale mode"}, - { "hq", "high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_QUALITY}, INT_MIN, INT_MAX, FLAGS, "scale mode"}, - - { NULL } -}; + int has_passthrough; /* apply pass through mode if possible */ + int field_rate; /* Generate output at frame rate or field rate for deinterlace mode, 0: frame, 1: field */ + int tonemap; /* 1: perform tonemapping if the input has HDR metadata, 0: always disable tonemapping */ +} VPPContext; static const char *const var_names[] = { "iw", "in_w", @@ -150,32 +134,41 @@ static const char *const var_names[] = { "ch", "cx", "cy", + "a", "dar", + "sar", NULL }; enum var_name { - VAR_iW, VAR_IN_W, - VAR_iH, VAR_IN_H, - VAR_oW, VAR_OUT_W, VAR_W, - VAR_oH, VAR_OUT_H, VAR_H, - CW, - CH, - CX, - CY, + VAR_IW, VAR_IN_W, + VAR_IH, VAR_IN_H, + VAR_OW, VAR_OUT_W, VAR_W, + VAR_OH, VAR_OUT_H, VAR_H, + VAR_CW, + VAR_CH, + VAR_CX, + VAR_CY, + VAR_A, VAR_DAR, + VAR_SAR, VAR_VARS_NB }; static int eval_expr(AVFilterContext *ctx) { #define PASS_EXPR(e, s) {\ - ret = av_expr_parse(&e, s, var_names, NULL, NULL, NULL, NULL, 0, ctx); \ - if (ret < 0) {\ - av_log(ctx, AV_LOG_ERROR, "Error when passing '%s'.\n", s);\ - goto release;\ + if (s) {\ + ret = av_expr_parse(&e, s, var_names, NULL, NULL, NULL, NULL, 0, ctx); \ + if (ret < 0) { \ + av_log(ctx, AV_LOG_ERROR, "Error when passing '%s'.\n", s); \ + goto release; \ + } \ }\ } -#define CALC_EXPR(e, v, i) {\ - i = v = av_expr_eval(e, var_values, NULL); \ +#define CALC_EXPR(e, v, i, d) {\ + if (e)\ + i = v = av_expr_eval(e, var_values, NULL); \ + else\ + i = v = d;\ } VPPContext *vpp = ctx->priv; double var_values[VAR_VARS_NB] = { NAN }; @@ -193,39 +186,43 @@ static int eval_expr(AVFilterContext *ctx) PASS_EXPR(cx_expr, vpp->cx); PASS_EXPR(cy_expr, vpp->cy); - var_values[VAR_iW] = + var_values[VAR_IW] = var_values[VAR_IN_W] = ctx->inputs[0]->w; - var_values[VAR_iH] = + var_values[VAR_IH] = var_values[VAR_IN_H] = ctx->inputs[0]->h; + var_values[VAR_A] = (double)var_values[VAR_IN_W] / var_values[VAR_IN_H]; + var_values[VAR_SAR] = ctx->inputs[0]->sample_aspect_ratio.num ? + (double)ctx->inputs[0]->sample_aspect_ratio.num / ctx->inputs[0]->sample_aspect_ratio.den : 1; + var_values[VAR_DAR] = var_values[VAR_A] * var_values[VAR_SAR]; + /* crop params */ - CALC_EXPR(cw_expr, var_values[CW], vpp->crop_w); - CALC_EXPR(ch_expr, var_values[CH], vpp->crop_h); + CALC_EXPR(cw_expr, var_values[VAR_CW], vpp->crop_w, var_values[VAR_IW]); + CALC_EXPR(ch_expr, var_values[VAR_CH], vpp->crop_h, var_values[VAR_IH]); /* calc again in case cw is relative to ch */ - CALC_EXPR(cw_expr, var_values[CW], vpp->crop_w); + CALC_EXPR(cw_expr, var_values[VAR_CW], vpp->crop_w, var_values[VAR_IW]); CALC_EXPR(w_expr, - var_values[VAR_OUT_W] = var_values[VAR_oW] = var_values[VAR_W], - vpp->out_width); + var_values[VAR_OUT_W] = var_values[VAR_OW] = var_values[VAR_W], + vpp->out_width, var_values[VAR_CW]); CALC_EXPR(h_expr, - var_values[VAR_OUT_H] = var_values[VAR_oH] = var_values[VAR_H], - vpp->out_height); + var_values[VAR_OUT_H] = var_values[VAR_OH] = var_values[VAR_H], + vpp->out_height, var_values[VAR_CH]); /* calc again in case ow is relative to oh */ CALC_EXPR(w_expr, - var_values[VAR_OUT_W] = var_values[VAR_oW] = var_values[VAR_W], - vpp->out_width); + var_values[VAR_OUT_W] = var_values[VAR_OW] = var_values[VAR_W], + vpp->out_width, var_values[VAR_CW]); - - CALC_EXPR(cx_expr, var_values[CX], vpp->crop_x); - CALC_EXPR(cy_expr, var_values[CY], vpp->crop_y); + CALC_EXPR(cx_expr, var_values[VAR_CX], vpp->crop_x, (var_values[VAR_IW] - var_values[VAR_OW]) / 2); + CALC_EXPR(cy_expr, var_values[VAR_CY], vpp->crop_y, (var_values[VAR_IH] - var_values[VAR_OH]) / 2); /* calc again in case cx is relative to cy */ - CALC_EXPR(cx_expr, var_values[CX], vpp->crop_x); + CALC_EXPR(cx_expr, var_values[VAR_CX], vpp->crop_x, (var_values[VAR_IW] - var_values[VAR_OW]) / 2); - if ((vpp->crop_w != var_values[VAR_iW]) || (vpp->crop_h != var_values[VAR_iH])) + if ((vpp->crop_w != var_values[VAR_IW]) || (vpp->crop_h != var_values[VAR_IH])) vpp->use_crop = 1; release: @@ -241,11 +238,31 @@ static int eval_expr(AVFilterContext *ctx) return ret; } +static av_cold int vpp_preinit(AVFilterContext *ctx) +{ + VPPContext *vpp = ctx->priv; + /* For AV_OPT_TYPE_STRING options, NULL is handled in other way so + * we needn't set default value here + */ + vpp->saturation = 1.0; + vpp->contrast = 1.0; + vpp->transpose = -1; + + vpp->color_range = AVCOL_RANGE_UNSPECIFIED; + vpp->color_primaries = AVCOL_PRI_UNSPECIFIED; + vpp->color_transfer = AVCOL_TRC_UNSPECIFIED; + vpp->color_matrix = AVCOL_SPC_UNSPECIFIED; + + vpp->has_passthrough = 1; + + return 0; +} + static av_cold int vpp_init(AVFilterContext *ctx) { VPPContext *vpp = ctx->priv; - if (!strcmp(vpp->output_format_str, "same")) { + if (!vpp->output_format_str || !strcmp(vpp->output_format_str, "same")) { vpp->out_format = AV_PIX_FMT_NONE; } else { vpp->out_format = av_get_pix_fmt(vpp->output_format_str); @@ -255,6 +272,24 @@ static av_cold int vpp_init(AVFilterContext *ctx) } } +#define STRING_OPTION(var_name, func_name, default_value) do { \ + if (vpp->var_name ## _str) { \ + int var = av_ ## func_name ## _from_name(vpp->var_name ## _str); \ + if (var < 0) { \ + av_log(ctx, AV_LOG_ERROR, "Invalid %s.\n", #var_name); \ + return AVERROR(EINVAL); \ + } \ + vpp->var_name = var; \ + } else { \ + vpp->var_name = default_value; \ + } \ + } while (0) + + STRING_OPTION(color_primaries, color_primaries, AVCOL_PRI_UNSPECIFIED); + STRING_OPTION(color_transfer, color_transfer, AVCOL_TRC_UNSPECIFIED); + STRING_OPTION(color_matrix, color_space, AVCOL_SPC_UNSPECIFIED); + +#undef STRING_OPTION return 0; } @@ -263,10 +298,16 @@ static int config_input(AVFilterLink *inlink) AVFilterContext *ctx = inlink->dst; VPPContext *vpp = ctx->priv; int ret; + int64_t ow, oh; - if (vpp->framerate.den == 0 || vpp->framerate.num == 0) + if (vpp->framerate.den == 0 || vpp->framerate.num == 0) { vpp->framerate = inlink->frame_rate; + if (vpp->deinterlace && vpp->field_rate) + vpp->framerate = av_mul_q(inlink->frame_rate, + (AVRational){ 2, 1 }); + } + if (av_cmp_q(vpp->framerate, inlink->frame_rate)) vpp->use_frc = 1; @@ -276,11 +317,38 @@ static int config_input(AVFilterLink *inlink) return ret; } - if (vpp->out_height == 0 || vpp->out_width == 0) { - vpp->out_width = inlink->w; - vpp->out_height = inlink->h; + ow = vpp->out_width; + oh = vpp->out_height; + + /* sanity check params */ + if (ow < -1 || oh < -1) { + av_log(ctx, AV_LOG_ERROR, "Size values less than -1 are not acceptable.\n"); + return AVERROR(EINVAL); } + if (ow == -1 && oh == -1) + vpp->out_width = vpp->out_height = 0; + + if (!(ow = vpp->out_width)) + ow = inlink->w; + + if (!(oh = vpp->out_height)) + oh = inlink->h; + + if (ow == -1) + ow = av_rescale(oh, inlink->w, inlink->h); + + if (oh == -1) + oh = av_rescale(ow, inlink->h, inlink->w); + + if (ow > INT_MAX || oh > INT_MAX || + (oh * inlink->w) > INT_MAX || + (ow * inlink->h) > INT_MAX) + av_log(ctx, AV_LOG_ERROR, "Rescaled value for width or height is too big.\n"); + + vpp->out_width = ow; + vpp->out_height = oh; + if (vpp->use_crop) { vpp->crop_x = FFMAX(vpp->crop_x, 0); vpp->crop_y = FFMAX(vpp->crop_y, 0); @@ -320,6 +388,140 @@ static mfxStatus get_mfx_version(const AVFilterContext *ctx, mfxVersion *mfx_ver return MFXQueryVersion(device_hwctx->session, mfx_version); } +static int vpp_set_frame_ext_params(AVFilterContext *ctx, const AVFrame *in, AVFrame *out, QSVVPPFrameParam *fp) +{ +#if QSV_ONEVPL + VPPContext *vpp = ctx->priv; + QSVVPPContext *qsvvpp = &vpp->qsv; + mfxExtVideoSignalInfo invsi_conf, outvsi_conf; + mfxExtMasteringDisplayColourVolume mdcv_conf; + mfxExtContentLightLevelInfo clli_conf; + AVFrameSideData *sd; + int tm = 0; + + fp->num_ext_buf = 0; + + if (!in || !out || + !QSV_RUNTIME_VERSION_ATLEAST(qsvvpp->ver, 2, 0)) + return 0; + + memset(&invsi_conf, 0, sizeof(mfxExtVideoSignalInfo)); + invsi_conf.Header.BufferId = MFX_EXTBUFF_VIDEO_SIGNAL_INFO_IN; + invsi_conf.Header.BufferSz = sizeof(mfxExtVideoSignalInfo); + invsi_conf.VideoFullRange = (in->color_range == AVCOL_RANGE_JPEG); + invsi_conf.ColourPrimaries = (in->color_primaries == AVCOL_PRI_UNSPECIFIED) ? AVCOL_PRI_BT709 : in->color_primaries; + invsi_conf.TransferCharacteristics = (in->color_trc == AVCOL_TRC_UNSPECIFIED) ? AVCOL_TRC_BT709 : in->color_trc; + invsi_conf.MatrixCoefficients = (in->colorspace == AVCOL_SPC_UNSPECIFIED) ? AVCOL_SPC_BT709 : in->colorspace; + invsi_conf.ColourDescriptionPresent = 1; + + memset(&mdcv_conf, 0, sizeof(mfxExtMasteringDisplayColourVolume)); + sd = av_frame_get_side_data(in, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); + if (vpp->tonemap && sd) { + AVMasteringDisplayMetadata *mdm = (AVMasteringDisplayMetadata *)sd->data; + + if (mdm->has_primaries && mdm->has_luminance) { + const int mapping[3] = {1, 2, 0}; + const int chroma_den = 50000; + const int luma_den = 10000; + int i; + + mdcv_conf.Header.BufferId = MFX_EXTBUFF_MASTERING_DISPLAY_COLOUR_VOLUME_IN; + mdcv_conf.Header.BufferSz = sizeof(mfxExtMasteringDisplayColourVolume); + + for (i = 0; i < 3; i++) { + const int j = mapping[i]; + + mdcv_conf.DisplayPrimariesX[i] = + FFMIN(lrint(chroma_den * + av_q2d(mdm->display_primaries[j][0])), + chroma_den); + mdcv_conf.DisplayPrimariesY[i] = + FFMIN(lrint(chroma_den * + av_q2d(mdm->display_primaries[j][1])), + chroma_den); + } + + mdcv_conf.WhitePointX = + FFMIN(lrint(chroma_den * av_q2d(mdm->white_point[0])), + chroma_den); + mdcv_conf.WhitePointY = + FFMIN(lrint(chroma_den * av_q2d(mdm->white_point[1])), + chroma_den); + + /* MaxDisplayMasteringLuminance is in the unit of 1 nits however + * MinDisplayMasteringLuminance is in the unit of 0.0001 nits + */ + mdcv_conf.MaxDisplayMasteringLuminance = + lrint(av_q2d(mdm->max_luminance)); + mdcv_conf.MinDisplayMasteringLuminance = + lrint(luma_den * av_q2d(mdm->min_luminance)); + tm = 1; + } + } + + memset(&clli_conf, 0, sizeof(mfxExtContentLightLevelInfo)); + sd = av_frame_get_side_data(in, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); + if (vpp->tonemap && sd) { + AVContentLightMetadata *clm = (AVContentLightMetadata *)sd->data; + + clli_conf.Header.BufferId = MFX_EXTBUFF_CONTENT_LIGHT_LEVEL_INFO; + clli_conf.Header.BufferSz = sizeof(mfxExtContentLightLevelInfo); + clli_conf.MaxContentLightLevel = FFMIN(clm->MaxCLL, 65535); + clli_conf.MaxPicAverageLightLevel = FFMIN(clm->MaxFALL, 65535); + tm = 1; + } + + if (tm) { + av_frame_remove_side_data(out, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); + av_frame_remove_side_data(out, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); + + out->color_primaries = AVCOL_PRI_BT709; + out->color_trc = AVCOL_TRC_BT709; + out->colorspace = AVCOL_SPC_BT709; + out->color_range = AVCOL_RANGE_MPEG; + } + + if (vpp->color_range != AVCOL_RANGE_UNSPECIFIED) + out->color_range = vpp->color_range; + if (vpp->color_primaries != AVCOL_PRI_UNSPECIFIED) + out->color_primaries = vpp->color_primaries; + if (vpp->color_transfer != AVCOL_TRC_UNSPECIFIED) + out->color_trc = vpp->color_transfer; + if (vpp->color_matrix != AVCOL_SPC_UNSPECIFIED) + out->colorspace = vpp->color_matrix; + + memset(&outvsi_conf, 0, sizeof(mfxExtVideoSignalInfo)); + outvsi_conf.Header.BufferId = MFX_EXTBUFF_VIDEO_SIGNAL_INFO_OUT; + outvsi_conf.Header.BufferSz = sizeof(mfxExtVideoSignalInfo); + outvsi_conf.VideoFullRange = (out->color_range == AVCOL_RANGE_JPEG); + outvsi_conf.ColourPrimaries = (out->color_primaries == AVCOL_PRI_UNSPECIFIED) ? AVCOL_PRI_BT709 : out->color_primaries; + outvsi_conf.TransferCharacteristics = (out->color_trc == AVCOL_TRC_UNSPECIFIED) ? AVCOL_TRC_BT709 : out->color_trc; + outvsi_conf.MatrixCoefficients = (out->colorspace == AVCOL_SPC_UNSPECIFIED) ? AVCOL_SPC_BT709 : out->colorspace; + outvsi_conf.ColourDescriptionPresent = 1; + + if (memcmp(&vpp->invsi_conf, &invsi_conf, sizeof(mfxExtVideoSignalInfo)) || + memcmp(&vpp->mdcv_conf, &mdcv_conf, sizeof(mfxExtMasteringDisplayColourVolume)) || + memcmp(&vpp->clli_conf, &clli_conf, sizeof(mfxExtContentLightLevelInfo)) || + memcmp(&vpp->outvsi_conf, &outvsi_conf, sizeof(mfxExtVideoSignalInfo))) { + vpp->invsi_conf = invsi_conf; + fp->ext_buf[fp->num_ext_buf++] = (mfxExtBuffer*)&vpp->invsi_conf; + + vpp->outvsi_conf = outvsi_conf; + fp->ext_buf[fp->num_ext_buf++] = (mfxExtBuffer*)&vpp->outvsi_conf; + + vpp->mdcv_conf = mdcv_conf; + if (mdcv_conf.Header.BufferId) + fp->ext_buf[fp->num_ext_buf++] = (mfxExtBuffer*)&vpp->mdcv_conf; + + vpp->clli_conf = clli_conf; + if (clli_conf.Header.BufferId) + fp->ext_buf[fp->num_ext_buf++] = (mfxExtBuffer*)&vpp->clli_conf; + } +#endif + + return 0; +} + static int config_output(AVFilterLink *outlink) { AVFilterContext *ctx = outlink->src; @@ -334,12 +536,12 @@ static int config_output(AVFilterLink *outlink) outlink->w = vpp->out_width; outlink->h = vpp->out_height; outlink->frame_rate = vpp->framerate; - outlink->time_base = inlink->time_base; + outlink->time_base = av_inv_q(vpp->framerate); param.filter_frame = NULL; + param.set_frame_ext_params = vpp_set_frame_ext_params; param.num_ext_buf = 0; param.ext_buf = ext_buf; - param.async_depth = vpp->async_depth; if (get_mfx_version(ctx, &mfx_version) != MFX_ERR_NONE) { av_log(ctx, AV_LOG_ERROR, "Failed to query mfx version.\n"); @@ -481,8 +683,15 @@ static int config_output(AVFilterLink *outlink) if (inlink->w != outlink->w || inlink->h != outlink->h || in_format != vpp->out_format) { if (QSV_RUNTIME_VERSION_ATLEAST(mfx_version, 1, 19)) { + int mode = vpp->scale_mode; + +#if QSV_ONEVPL + if (mode > 2) + mode = MFX_SCALING_MODE_VENDOR + mode - 2; +#endif + INIT_MFX_EXTBUF(scale_conf, MFX_EXTBUFF_VPP_SCALING); - SET_MFX_PARAM_FIELD(scale_conf, ScalingMode, vpp->scale_mode); + SET_MFX_PARAM_FIELD(scale_conf, ScalingMode, mode); } else av_log(ctx, AV_LOG_WARNING, "The QSV VPP Scale & format conversion " "option is not supported with this MSDK version.\n"); @@ -493,9 +702,16 @@ static int config_output(AVFilterLink *outlink) if (vpp->use_frc || vpp->use_crop || vpp->deinterlace || vpp->denoise || vpp->detail || vpp->procamp || vpp->rotate || vpp->hflip || - inlink->w != outlink->w || inlink->h != outlink->h || in_format != vpp->out_format) - return ff_qsvvpp_create(ctx, &vpp->qsv, ¶m); + inlink->w != outlink->w || inlink->h != outlink->h || in_format != vpp->out_format || + vpp->color_range != AVCOL_RANGE_UNSPECIFIED || + vpp->color_primaries != AVCOL_PRI_UNSPECIFIED || + vpp->color_transfer != AVCOL_TRC_UNSPECIFIED || + vpp->color_matrix != AVCOL_SPC_UNSPECIFIED || + vpp->tonemap || + !vpp->has_passthrough) + return ff_qsvvpp_init(ctx, ¶m); else { + /* No MFX session is created in this case */ av_log(ctx, AV_LOG_VERBOSE, "qsv vpp pass through mode.\n"); if (inlink->hw_frames_ctx) outlink->hw_frames_ctx = av_buffer_ref(inlink->hw_frames_ctx); @@ -508,29 +724,27 @@ static int activate(AVFilterContext *ctx) { AVFilterLink *inlink = ctx->inputs[0]; AVFilterLink *outlink = ctx->outputs[0]; - VPPContext *s =ctx->priv; - QSVVPPContext *qsv = s->qsv; + QSVVPPContext *qsv = ctx->priv; AVFrame *in = NULL; int ret, status = 0; int64_t pts = AV_NOPTS_VALUE; FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); - if (!s->eof) { + if (!qsv->eof) { ret = ff_inlink_consume_frame(inlink, &in); if (ret < 0) return ret; if (ff_inlink_acknowledge_status(inlink, &status, &pts)) { if (status == AVERROR_EOF) { - s->eof = 1; + qsv->eof = 1; } } } - if (qsv) { - if (in || s->eof) { - qsv->eof = s->eof; + if (qsv->session) { + if (in || qsv->eof) { ret = ff_qsvvpp_filter_frame(qsv, inlink, in); av_frame_free(&in); if (ret == AVERROR(EAGAIN)) @@ -538,7 +752,7 @@ static int activate(AVFilterContext *ctx) else if (ret < 0) return ret; - if (s->eof) + if (qsv->eof) goto eof; if (qsv->got_frame) { @@ -547,15 +761,21 @@ static int activate(AVFilterContext *ctx) } } } else { + /* No MFX session is created in pass-through mode */ if (in) { if (in->pts != AV_NOPTS_VALUE) in->pts = av_rescale_q(in->pts, inlink->time_base, outlink->time_base); + if (outlink->frame_rate.num && outlink->frame_rate.den) + in->duration = av_rescale_q(1, av_inv_q(outlink->frame_rate), outlink->time_base); + else + in->duration = 0; + ret = ff_filter_frame(outlink, in); if (ret < 0) return ret; - if (s->eof) + if (qsv->eof) goto eof; return 0; @@ -563,7 +783,7 @@ static int activate(AVFilterContext *ctx) } not_ready: - if (s->eof) + if (qsv->eof) goto eof; FF_FILTER_FORWARD_WANTED(outlink, inlink); @@ -571,78 +791,241 @@ static int activate(AVFilterContext *ctx) return FFERROR_NOT_READY; eof: + pts = av_rescale_q(pts, inlink->time_base, outlink->time_base); ff_outlink_set_status(outlink, status, pts); return 0; } -static int query_formats(AVFilterContext *ctx) +static av_cold void vpp_uninit(AVFilterContext *ctx) { - int ret; + ff_qsvvpp_close(ctx); +} + +static const AVFilterPad vpp_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_input, + .get_buffer.video = ff_qsvvpp_get_video_buffer, + }, +}; + +static const AVFilterPad vpp_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_output, + }, +}; + +#define DEFINE_QSV_FILTER(x, sn, ln, fmts) \ +static const AVClass x##_class = { \ + .class_name = #sn "_qsv", \ + .item_name = av_default_item_name, \ + .option = x##_options, \ + .version = LIBAVUTIL_VERSION_INT, \ +}; \ +const AVFilter ff_vf_##sn##_qsv = { \ + .name = #sn "_qsv", \ + .description = NULL_IF_CONFIG_SMALL("Quick Sync Video " #ln), \ + .preinit = x##_preinit, \ + .init = vpp_init, \ + .uninit = vpp_uninit, \ + .priv_size = sizeof(VPPContext), \ + .priv_class = &x##_class, \ + FILTER_INPUTS(vpp_inputs), \ + FILTER_OUTPUTS(vpp_outputs), \ + fmts, \ + .activate = activate, \ + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, \ + .flags = AVFILTER_FLAG_HWDEVICE, \ +}; + +#if CONFIG_VPP_QSV_FILTER + +static const AVOption vpp_options[] = { + { "deinterlace", "deinterlace mode: 0=off, 1=bob, 2=advanced", OFFSET(deinterlace), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, MFX_DEINTERLACING_ADVANCED, .flags = FLAGS, "deinterlace" }, + { "bob", "Bob deinterlace mode.", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_DEINTERLACING_BOB }, .flags = FLAGS, "deinterlace" }, + { "advanced", "Advanced deinterlace mode. ", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_DEINTERLACING_ADVANCED }, .flags = FLAGS, "deinterlace" }, + + { "denoise", "denoise level [0, 100]", OFFSET(denoise), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 100, .flags = FLAGS }, + { "detail", "enhancement level [0, 100]", OFFSET(detail), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 100, .flags = FLAGS }, + { "framerate", "output framerate", OFFSET(framerate), AV_OPT_TYPE_RATIONAL, { .dbl = 0.0 },0, DBL_MAX, .flags = FLAGS }, + { "procamp", "Enable ProcAmp", OFFSET(procamp), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = FLAGS}, + { "hue", "ProcAmp hue", OFFSET(hue), AV_OPT_TYPE_FLOAT, { .dbl = 0.0 }, -180.0, 180.0, .flags = FLAGS}, + { "saturation", "ProcAmp saturation", OFFSET(saturation), AV_OPT_TYPE_FLOAT, { .dbl = 1.0 }, 0.0, 10.0, .flags = FLAGS}, + { "contrast", "ProcAmp contrast", OFFSET(contrast), AV_OPT_TYPE_FLOAT, { .dbl = 1.0 }, 0.0, 10.0, .flags = FLAGS}, + { "brightness", "ProcAmp brightness", OFFSET(brightness), AV_OPT_TYPE_FLOAT, { .dbl = 0.0 }, -100.0, 100.0, .flags = FLAGS}, + + { "transpose", "set transpose direction", OFFSET(transpose), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 6, FLAGS, "transpose"}, + { "cclock_hflip", "rotate counter-clockwise with horizontal flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK_FLIP }, .flags=FLAGS, .unit = "transpose" }, + { "clock", "rotate clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK }, .flags=FLAGS, .unit = "transpose" }, + { "cclock", "rotate counter-clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK }, .flags=FLAGS, .unit = "transpose" }, + { "clock_hflip", "rotate clockwise with horizontal flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK_FLIP }, .flags=FLAGS, .unit = "transpose" }, + { "reversal", "rotate by half-turn", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_REVERSAL }, .flags=FLAGS, .unit = "transpose" }, + { "hflip", "flip horizontally", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_HFLIP }, .flags=FLAGS, .unit = "transpose" }, + { "vflip", "flip vertically", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_VFLIP }, .flags=FLAGS, .unit = "transpose" }, + + { "cw", "set the width crop area expression", OFFSET(cw), AV_OPT_TYPE_STRING, { .str = "iw" }, 0, 0, FLAGS }, + { "ch", "set the height crop area expression", OFFSET(ch), AV_OPT_TYPE_STRING, { .str = "ih" }, 0, 0, FLAGS }, + { "cx", "set the x crop area expression", OFFSET(cx), AV_OPT_TYPE_STRING, { .str = "(in_w-out_w)/2" }, 0, 0, FLAGS }, + { "cy", "set the y crop area expression", OFFSET(cy), AV_OPT_TYPE_STRING, { .str = "(in_h-out_h)/2" }, 0, 0, FLAGS }, + + { "w", "Output video width(0=input video width, -1=keep input video aspect)", OFFSET(ow), AV_OPT_TYPE_STRING, { .str="cw" }, 0, 255, .flags = FLAGS }, + { "width", "Output video width(0=input video width, -1=keep input video aspect)", OFFSET(ow), AV_OPT_TYPE_STRING, { .str="cw" }, 0, 255, .flags = FLAGS }, + { "h", "Output video height(0=input video height, -1=keep input video aspect)", OFFSET(oh), AV_OPT_TYPE_STRING, { .str="w*ch/cw" }, 0, 255, .flags = FLAGS }, + { "height", "Output video height(0=input video height, -1=keep input video aspect)", OFFSET(oh), AV_OPT_TYPE_STRING, { .str="w*ch/cw" }, 0, 255, .flags = FLAGS }, + { "format", "Output pixel format", OFFSET(output_format_str), AV_OPT_TYPE_STRING, { .str = "same" }, .flags = FLAGS }, + { "async_depth", "Internal parallelization depth, the higher the value the higher the latency.", OFFSET(qsv.async_depth), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, .flags = FLAGS }, +#if QSV_ONEVPL + { "scale_mode", "scaling & format conversion mode (mode compute(3), vd(4) and ve(5) are only available on some platforms)", OFFSET(scale_mode), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 5, .flags = FLAGS, "scale mode" }, +#else + { "scale_mode", "scaling & format conversion mode", OFFSET(scale_mode), AV_OPT_TYPE_INT, { .i64 = MFX_SCALING_MODE_DEFAULT }, MFX_SCALING_MODE_DEFAULT, MFX_SCALING_MODE_QUALITY, .flags = FLAGS, "scale mode" }, +#endif + { "auto", "auto mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_DEFAULT}, INT_MIN, INT_MAX, FLAGS, "scale mode"}, + { "low_power", "low power mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_LOWPOWER}, INT_MIN, INT_MAX, FLAGS, "scale mode"}, + { "hq", "high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_QUALITY}, INT_MIN, INT_MAX, FLAGS, "scale mode"}, +#if QSV_ONEVPL + { "compute", "compute", 0, AV_OPT_TYPE_CONST, { .i64 = 3}, INT_MIN, INT_MAX, FLAGS, "scale mode"}, + { "vd", "vd", 0, AV_OPT_TYPE_CONST, { .i64 = 4}, INT_MIN, INT_MAX, FLAGS, "scale mode"}, + { "ve", "ve", 0, AV_OPT_TYPE_CONST, { .i64 = 5}, INT_MIN, INT_MAX, FLAGS, "scale mode"}, +#endif + + { "rate", "Generate output at frame rate or field rate, available only for deinterlace mode", + OFFSET(field_rate), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS, "rate" }, + { "frame", "Output at frame rate (one frame of output for each field-pair)", + 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, "rate" }, + { "field", "Output at field rate (one frame of output for each field)", + 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "rate" }, + + { "out_range", "Output color range", + OFFSET(color_range), AV_OPT_TYPE_INT, { .i64 = AVCOL_RANGE_UNSPECIFIED }, + AVCOL_RANGE_UNSPECIFIED, AVCOL_RANGE_JPEG, FLAGS, "range" }, + { "full", "Full range", + 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, "range" }, + { "limited", "Limited range", + 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, "range" }, + { "jpeg", "Full range", + 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, "range" }, + { "mpeg", "Limited range", + 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, "range" }, + { "tv", "Limited range", + 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, "range" }, + { "pc", "Full range", + 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, "range" }, + { "out_color_matrix", "Output color matrix coefficient set", + OFFSET(color_matrix_str), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS }, + { "out_color_primaries", "Output color primaries", + OFFSET(color_primaries_str), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS }, + { "out_color_transfer", "Output color transfer characteristics", + OFFSET(color_transfer_str), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS }, + + {"tonemap", "Perform tonemapping (0=disable tonemapping, 1=perform tonemapping if the input has HDR metadata)", OFFSET(tonemap), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, .flags = FLAGS}, + + { NULL } +}; + +static int vpp_query_formats(AVFilterContext *ctx) +{ + VPPContext *vpp = ctx->priv; + int ret, i = 0; static const enum AVPixelFormat in_pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NV12, AV_PIX_FMT_YUYV422, AV_PIX_FMT_RGB32, AV_PIX_FMT_P010, +#if CONFIG_VAAPI + AV_PIX_FMT_UYVY422, +#endif AV_PIX_FMT_QSV, AV_PIX_FMT_NONE }; - static const enum AVPixelFormat out_pix_fmts[] = { - AV_PIX_FMT_NV12, - AV_PIX_FMT_P010, - AV_PIX_FMT_QSV, - AV_PIX_FMT_NONE - }; + static enum AVPixelFormat out_pix_fmts[4]; ret = ff_formats_ref(ff_make_format_list(in_pix_fmts), &ctx->inputs[0]->outcfg.formats); if (ret < 0) return ret; + + /* User specifies the output format */ + if (vpp->out_format == AV_PIX_FMT_NV12 || + vpp->out_format == AV_PIX_FMT_P010) + out_pix_fmts[i++] = vpp->out_format; + else { + out_pix_fmts[i++] = AV_PIX_FMT_NV12; + out_pix_fmts[i++] = AV_PIX_FMT_P010; + } + + out_pix_fmts[i++] = AV_PIX_FMT_QSV; + out_pix_fmts[i++] = AV_PIX_FMT_NONE; + return ff_formats_ref(ff_make_format_list(out_pix_fmts), &ctx->outputs[0]->incfg.formats); } -static av_cold void vpp_uninit(AVFilterContext *ctx) +DEFINE_QSV_FILTER(vpp, vpp, "VPP", FILTER_QUERY_FUNC(vpp_query_formats)); + +#endif + +#if CONFIG_SCALE_QSV_FILTER + +static const AVOption qsvscale_options[] = { + { "w", "Output video width(0=input video width, -1=keep input video aspect)", OFFSET(ow), AV_OPT_TYPE_STRING, { .str = "iw" }, .flags = FLAGS }, + { "h", "Output video height(0=input video height, -1=keep input video aspect)", OFFSET(oh), AV_OPT_TYPE_STRING, { .str = "ih" }, .flags = FLAGS }, + { "format", "Output pixel format", OFFSET(output_format_str), AV_OPT_TYPE_STRING, { .str = "same" }, .flags = FLAGS }, + +#if QSV_ONEVPL + { "mode", "scaling & format conversion mode (mode compute(3), vd(4) and ve(5) are only available on some platforms)", OFFSET(scale_mode), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 5, FLAGS, "mode"}, +#else + { "mode", "scaling & format conversion mode", OFFSET(scale_mode), AV_OPT_TYPE_INT, { .i64 = MFX_SCALING_MODE_DEFAULT}, MFX_SCALING_MODE_DEFAULT, MFX_SCALING_MODE_QUALITY, FLAGS, "mode"}, +#endif + { "low_power", "low power mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_LOWPOWER}, INT_MIN, INT_MAX, FLAGS, "mode"}, + { "hq", "high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_QUALITY}, INT_MIN, INT_MAX, FLAGS, "mode"}, +#if QSV_ONEVPL + { "compute", "compute", 0, AV_OPT_TYPE_CONST, { .i64 = 3}, INT_MIN, INT_MAX, FLAGS, "mode"}, + { "vd", "vd", 0, AV_OPT_TYPE_CONST, { .i64 = 4}, INT_MIN, INT_MAX, FLAGS, "mode"}, + { "ve", "ve", 0, AV_OPT_TYPE_CONST, { .i64 = 5}, INT_MIN, INT_MAX, FLAGS, "mode"}, +#endif + + { NULL }, +}; + +static av_cold int qsvscale_preinit(AVFilterContext *ctx) { - VPPContext *vpp = ctx->priv; + VPPContext *vpp = ctx->priv; + + vpp_preinit(ctx); + vpp->has_passthrough = 0; - ff_qsvvpp_free(&vpp->qsv); + return 0; } -static const AVClass vpp_class = { - .class_name = "vpp_qsv", - .item_name = av_default_item_name, - .option = options, - .version = LIBAVUTIL_VERSION_INT, -}; +DEFINE_QSV_FILTER(qsvscale, scale, "scaling and format conversion", FILTER_SINGLE_PIXFMT(AV_PIX_FMT_QSV)); -static const AVFilterPad vpp_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .config_props = config_input, - .get_buffer.video = ff_qsvvpp_get_video_buffer, - }, -}; +#endif -static const AVFilterPad vpp_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .config_props = config_output, - }, -}; +#if CONFIG_DEINTERLACE_QSV_FILTER + +static const AVOption qsvdeint_options[] = { + { "mode", "set deinterlace mode", OFFSET(deinterlace), AV_OPT_TYPE_INT, {.i64 = MFX_DEINTERLACING_ADVANCED}, MFX_DEINTERLACING_BOB, MFX_DEINTERLACING_ADVANCED, FLAGS, "mode"}, + { "bob", "bob algorithm", 0, AV_OPT_TYPE_CONST, {.i64 = MFX_DEINTERLACING_BOB}, MFX_DEINTERLACING_BOB, MFX_DEINTERLACING_ADVANCED, FLAGS, "mode"}, + { "advanced", "Motion adaptive algorithm", 0, AV_OPT_TYPE_CONST, {.i64 = MFX_DEINTERLACING_ADVANCED}, MFX_DEINTERLACING_BOB, MFX_DEINTERLACING_ADVANCED, FLAGS, "mode"}, -const AVFilter ff_vf_vpp_qsv = { - .name = "vpp_qsv", - .description = NULL_IF_CONFIG_SMALL("Quick Sync Video VPP."), - .priv_size = sizeof(VPPContext), - .init = vpp_init, - .uninit = vpp_uninit, - FILTER_INPUTS(vpp_inputs), - FILTER_OUTPUTS(vpp_outputs), - FILTER_QUERY_FUNC(query_formats), - .activate = activate, - .priv_class = &vpp_class, - .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + { NULL }, }; + +static av_cold int qsvdeint_preinit(AVFilterContext *ctx) +{ + VPPContext *vpp = ctx->priv; + + vpp_preinit(ctx); + vpp->has_passthrough = 0; + vpp->field_rate = 1; + + return 0; +} + +DEFINE_QSV_FILTER(qsvdeint, deinterlace, "deinterlacing", FILTER_SINGLE_PIXFMT(AV_PIX_FMT_QSV)) + +#endif diff --git a/libavfilter/vf_w3fdif.c b/libavfilter/vf_w3fdif.c index 512c8070c7f..f2264efe2b9 100644 --- a/libavfilter/vf_w3fdif.c +++ b/libavfilter/vf_w3fdif.c @@ -26,7 +26,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "w3fdif.h" @@ -379,8 +378,8 @@ static int deinterlace_plane_slice(AVFilterContext *ctx, void *arg, const int start = (height * jobnr) / nb_jobs; const int end = (height * (jobnr+1)) / nb_jobs; const int max = s->max; - const int interlaced = cur->interlaced_frame; - const int tff = s->field == (s->parity == -1 ? interlaced ? cur->top_field_first : 1 : + const int interlaced = !!(cur->flags & AV_FRAME_FLAG_INTERLACED); + const int tff = s->field == (s->parity == -1 ? interlaced ? !!(cur->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) : 1 : s->parity ^ 1); int j, y_in, y_out; @@ -486,7 +485,12 @@ static int filter(AVFilterContext *ctx, int is_second) if (!out) return AVERROR(ENOMEM); av_frame_copy_props(out, s->cur); +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS out->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + out->flags &= ~AV_FRAME_FLAG_INTERLACED; if (!is_second) { if (out->pts != AV_NOPTS_VALUE) @@ -533,7 +537,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) if (!s->prev) return 0; - if ((s->deint && !s->cur->interlaced_frame) || ctx->is_disabled) { + if ((s->deint && !(s->cur->flags & AV_FRAME_FLAG_INTERLACED)) || ctx->is_disabled) { AVFrame *out = av_frame_clone(s->cur); if (!out) return AVERROR(ENOMEM); diff --git a/libavfilter/vf_waveform.c b/libavfilter/vf_waveform.c index 37eb0679d0d..a6a8ec78e74 100644 --- a/libavfilter/vf_waveform.c +++ b/libavfilter/vf_waveform.c @@ -120,6 +120,7 @@ typedef struct WaveformContext { float ftint[2]; int tint[2]; int fitmode; + int input; int (*waveform_slice)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); @@ -196,6 +197,9 @@ static const AVOption waveform_options[] = { { "fm", "set fit mode", OFFSET(fitmode), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_FITMODES-1, FLAGS, "fitmode" }, { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_NONE}, 0, 0, FLAGS, "fitmode" }, { "size", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_SIZE}, 0, 0, FLAGS, "fitmode" }, + { "input", "set input formats selection", OFFSET(input), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, "input" }, + { "all", "try to select from all available formats", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "input" }, + { "first", "pick first available format", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "input" }, { NULL } }; @@ -356,7 +360,7 @@ static int query_formats(AVFilterContext *ctx) depth2 = desc2->comp[0].depth; if (ncomp != ncomp2 || depth != depth2) return AVERROR(EAGAIN); - for (i = 1; i < avff->nb_formats; i++) { + for (i = 1; i < avff->nb_formats && !s->input; i++) { desc = av_pix_fmt_desc_get(avff->formats[i]); if (rgb != (desc->flags & AV_PIX_FMT_FLAG_RGB) || depth != desc->comp[0].depth) @@ -745,7 +749,7 @@ static av_always_inline void lowpass16(WaveformContext *s, dst_data += dst_linesize * step; } - if (s->display != OVERLAY && column && !s->rgb) { + if (s->display != OVERLAY && column && !s->rgb && out->data[1] && out->data[2]) { const int mult = s->max / 256; const int bg = s->bg_color[0] * mult; const int t0 = s->tint[0]; @@ -769,7 +773,7 @@ static av_always_inline void lowpass16(WaveformContext *s, dst0 += dst_linesize; dst1 += dst_linesize; } - } else if (s->display != OVERLAY && !s->rgb) { + } else if (s->display != OVERLAY && !s->rgb && out->data[1] && out->data[2]) { const int mult = s->max / 256; const int bg = s->bg_color[0] * mult; const int t0 = s->tint[0]; diff --git a/libavfilter/vf_weave.c b/libavfilter/vf_weave.c index 2bd3994e5ee..84f3c5f337d 100644 --- a/libavfilter/vf_weave.c +++ b/libavfilter/vf_weave.c @@ -22,7 +22,9 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" +#include "video.h" typedef struct WeaveContext { const AVClass *class; @@ -148,8 +150,17 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) FFMIN(s->planeheight[1], ff_filter_get_nb_threads(ctx))); out->pts = s->double_weave ? s->prev->pts : in->pts / 2; +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS out->interlaced_frame = 1; out->top_field_first = !s->first_field; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + out->flags |= AV_FRAME_FLAG_INTERLACED; + if (s->first_field) + out->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; + else + out->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; if (!s->double_weave) av_frame_free(&in); diff --git a/libavfilter/vf_xbr.c b/libavfilter/vf_xbr.c index 6ae5310c9f4..1750da043e0 100644 --- a/libavfilter/vf_xbr.c +++ b/libavfilter/vf_xbr.c @@ -31,6 +31,7 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "internal.h" +#include "video.h" #define LB_MASK 0x00FEFEFE #define RED_BLUE_MASK 0x00FF00FF diff --git a/libavfilter/vf_xfade.c b/libavfilter/vf_xfade.c index 9f669273657..890995a6088 100644 --- a/libavfilter/vf_xfade.c +++ b/libavfilter/vf_xfade.c @@ -18,12 +18,11 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/imgutils.h" #include "libavutil/eval.h" #include "libavutil/opt.h" +#include "libavutil/pixdesc.h" #include "libavutil/pixfmt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "filters.h" #include "video.h" @@ -76,6 +75,18 @@ enum XFadeTransitions { ZOOMIN, FADEFAST, FADESLOW, + HLWIND, + HRWIND, + VUWIND, + VDWIND, + COVERLEFT, + COVERRIGHT, + COVERUP, + COVERDOWN, + REVEALLEFT, + REVEALRIGHT, + REVEALUP, + REVEALDOWN, NB_TRANSITIONS, }; @@ -91,14 +102,23 @@ typedef struct XFadeContext { int depth; int is_rgb; + // PTS when the fade should start (in first inputs timebase) + int64_t start_pts; + + // PTS offset between first and second input + int64_t inputs_offset_pts; + + // Duration of the transition int64_t duration_pts; - int64_t offset_pts; - int64_t first_pts; - int64_t last_pts; + + // Current PTS of the first input int64_t pts; - int xfade_is_over; - int need_second; - int eof[2]; + + // If frames are currently just passed through unmodified, + // like before and after the actual transition. + int passthrough; + + int status[2]; AVFrame *xf[2]; int max_value; uint16_t black[4]; @@ -197,6 +217,18 @@ static const AVOption xfade_options[] = { { "zoomin", "zoom in transition", 0, AV_OPT_TYPE_CONST, {.i64=ZOOMIN}, 0, 0, FLAGS, "transition" }, { "fadefast", "fast fade transition", 0, AV_OPT_TYPE_CONST, {.i64=FADEFAST}, 0, 0, FLAGS, "transition" }, { "fadeslow", "slow fade transition", 0, AV_OPT_TYPE_CONST, {.i64=FADESLOW}, 0, 0, FLAGS, "transition" }, + { "hlwind", "hl wind transition", 0, AV_OPT_TYPE_CONST, {.i64=HLWIND}, 0, 0, FLAGS, "transition" }, + { "hrwind", "hr wind transition", 0, AV_OPT_TYPE_CONST, {.i64=HRWIND}, 0, 0, FLAGS, "transition" }, + { "vuwind", "vu wind transition", 0, AV_OPT_TYPE_CONST, {.i64=VUWIND}, 0, 0, FLAGS, "transition" }, + { "vdwind", "vd wind transition", 0, AV_OPT_TYPE_CONST, {.i64=VDWIND}, 0, 0, FLAGS, "transition" }, + { "coverleft", "cover left transition", 0, AV_OPT_TYPE_CONST, {.i64=COVERLEFT}, 0, 0, FLAGS, "transition" }, + { "coverright", "cover right transition", 0, AV_OPT_TYPE_CONST, {.i64=COVERRIGHT}, 0, 0, FLAGS, "transition" }, + { "coverup", "cover up transition", 0, AV_OPT_TYPE_CONST, {.i64=COVERUP}, 0, 0, FLAGS, "transition" }, + { "coverdown", "cover down transition", 0, AV_OPT_TYPE_CONST, {.i64=COVERDOWN}, 0, 0, FLAGS, "transition" }, + { "revealleft", "reveal left transition", 0, AV_OPT_TYPE_CONST, {.i64=REVEALLEFT}, 0, 0, FLAGS, "transition" }, + { "revealright","reveal right transition",0, AV_OPT_TYPE_CONST, {.i64=REVEALRIGHT},0, 0, FLAGS, "transition" }, + { "revealup", "reveal up transition", 0, AV_OPT_TYPE_CONST, {.i64=REVEALUP}, 0, 0, FLAGS, "transition" }, + { "revealdown", "reveal down transition", 0, AV_OPT_TYPE_CONST, {.i64=REVEALDOWN}, 0, 0, FLAGS, "transition" }, { "duration", "set cross fade duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64=1000000}, 0, 60000000, FLAGS }, { "offset", "set cross fade start relative to first input stream", OFFSET(offset), AV_OPT_TYPE_DURATION, {.i64=0}, INT64_MIN, INT64_MAX, FLAGS }, { "expr", "set expression for custom transition", OFFSET(custom_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, @@ -213,9 +245,10 @@ static void custom##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ + const int width = out->width; \ \ double values[VAR_VARS_NB]; \ - values[VAR_W] = out->width; \ + values[VAR_W] = width; \ values[VAR_H] = out->height; \ values[VAR_PROGRESS] = progress; \ \ @@ -228,7 +261,7 @@ static void custom##name##_transition(AVFilterContext *ctx, \ for (int y = 0; y < height; y++) { \ values[VAR_Y] = slice_start + y; \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ values[VAR_X] = x; \ values[VAR_A] = xf0[x]; \ values[VAR_B] = xf1[x]; \ @@ -272,6 +305,7 @@ static void fade##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ + const int width = out->width; \ \ for (int p = 0; p < s->nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \ @@ -279,7 +313,7 @@ static void fade##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = mix(xf0[x], xf1[x], progress); \ } \ \ @@ -301,7 +335,8 @@ static void wipeleft##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ - const int z = out->width * progress; \ + const int width = out->width; \ + const int z = width * progress; \ \ for (int p = 0; p < s->nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \ @@ -309,7 +344,7 @@ static void wipeleft##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = x > z ? xf1[x] : xf0[x]; \ } \ \ @@ -331,7 +366,8 @@ static void wiperight##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ - const int z = out->width * (1.f - progress); \ + const int width = out->width; \ + const int z = width * (1.f - progress); \ \ for (int p = 0; p < s->nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \ @@ -339,7 +375,7 @@ static void wiperight##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = x > z ? xf0[x] : xf1[x]; \ } \ \ @@ -361,6 +397,7 @@ static void wipeup##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ + const int width = out->width; \ const int z = out->height * progress; \ \ for (int p = 0; p < s->nb_planes; p++) { \ @@ -369,7 +406,7 @@ static void wipeup##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = slice_start + y > z ? xf1[x] : xf0[x]; \ } \ \ @@ -391,6 +428,7 @@ static void wipedown##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ + const int width = out->width; \ const int z = out->height * (1.f - progress); \ \ for (int p = 0; p < s->nb_planes; p++) { \ @@ -399,7 +437,7 @@ static void wipedown##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = slice_start + y > z ? xf0[x] : xf1[x]; \ } \ \ @@ -463,7 +501,7 @@ static void slideright##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ const int zx = z + x; \ const int zz = zx % width + width * (zx < 0); \ dst[x] = (zx >= 0) && (zx < width) ? xf1[zz] : xf0[zz]; \ @@ -487,6 +525,7 @@ static void slideup##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = out->height; \ + const int width = out->width; \ const int z = -progress * height; \ \ for (int p = 0; p < s->nb_planes; p++) { \ @@ -498,7 +537,7 @@ static void slideup##name##_transition(AVFilterContext *ctx, const type *xf0 = (const type *)(a->data[p] + zz * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + zz * b->linesize[p]); \ \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = (zy >= 0) && (zy < height) ? xf1[x] : xf0[x]; \ } \ \ @@ -518,6 +557,7 @@ static void slidedown##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = out->height; \ + const int width = out->width; \ const int z = progress * height; \ \ for (int p = 0; p < s->nb_planes; p++) { \ @@ -529,7 +569,7 @@ static void slidedown##name##_transition(AVFilterContext *ctx, const type *xf0 = (const type *)(a->data[p] + zz * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + zz * b->linesize[p]); \ \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = (zy >= 0) && (zy < height) ? xf1[x] : xf0[x]; \ } \ \ @@ -652,6 +692,7 @@ static void fadeblack##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ + const int width = out->width; \ const float phase = 0.2f; \ \ for (int p = 0; p < s->nb_planes; p++) { \ @@ -661,7 +702,7 @@ static void fadeblack##name##_transition(AVFilterContext *ctx, const int bg = s->black[p]; \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = mix(mix(xf0[x], bg, smoothstep(1.f-phase, 1.f, progress)), \ mix(bg, xf1[x], smoothstep(phase, 1.f, progress)), \ progress); \ @@ -685,6 +726,7 @@ static void fadewhite##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ + const int width = out->width; \ const float phase = 0.2f; \ \ for (int p = 0; p < s->nb_planes; p++) { \ @@ -694,7 +736,7 @@ static void fadewhite##name##_transition(AVFilterContext *ctx, const int bg = s->white[p]; \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = mix(mix(xf0[x], bg, smoothstep(1.f-phase, 1.f, progress)), \ mix(bg, xf1[x], smoothstep(phase, 1.f, progress)), \ progress); \ @@ -940,13 +982,14 @@ static void vertclose##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ const float w2 = out->width / 2; \ \ for (int y = slice_start; y < slice_end; y++) { \ for (int x = 0; x < width; x++) { \ const float smooth = 1.f + fabsf((x - w2) / w2) - progress * 2.f; \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -967,13 +1010,14 @@ static void horzopen##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ const float h2 = out->height / 2; \ \ for (int y = slice_start; y < slice_end; y++) { \ const float smooth = 2.f - fabsf((y - h2) / h2) - progress * 2.f; \ for (int x = 0; x < width; x++) { \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -994,13 +1038,14 @@ static void horzclose##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ const float h2 = out->height / 2; \ \ for (int y = slice_start; y < slice_end; y++) { \ const float smooth = 1.f + fabsf((y - h2) / h2) - progress * 2.f; \ for (int x = 0; x < width; x++) { \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -1028,12 +1073,13 @@ static void dissolve##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ \ for (int y = slice_start; y < slice_end; y++) { \ for (int x = 0; x < width; x++) { \ const float smooth = frand(x, y) * 2.f + progress * 2.f - 1.5f; \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -1054,6 +1100,7 @@ static void pixelize##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int w = out->width; \ const int h = out->height; \ const float d = fminf(progress, 1.f - progress); \ @@ -1065,7 +1112,7 @@ static void pixelize##name##_transition(AVFilterContext *ctx, for (int x = 0; x < w; x++) { \ int sx = dist > 0.f ? FFMIN((floorf(x / sqx) + .5f) * sqx, w - 1) : x; \ int sy = dist > 0.f ? FFMIN((floorf(y / sqy) + .5f) * sqy, h - 1) : y; \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + sy * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + sy * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -1086,6 +1133,7 @@ static void diagtl##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ const float w = width; \ const float h = out->height; \ @@ -1094,7 +1142,7 @@ static void diagtl##name##_transition(AVFilterContext *ctx, for (int x = 0; x < width; x++) { \ const float smooth = 1.f + x / w * y / h - progress * 2.f; \ \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -1115,6 +1163,7 @@ static void diagtr##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ const float w = width; \ const float h = out->height; \ @@ -1123,7 +1172,7 @@ static void diagtr##name##_transition(AVFilterContext *ctx, for (int x = 0; x < width; x++) { \ const float smooth = 1.f + (w - 1 - x) / w * y / h - progress * 2.f; \ \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -1144,6 +1193,7 @@ static void diagbl##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ const float w = width; \ const float h = out->height; \ @@ -1152,7 +1202,7 @@ static void diagbl##name##_transition(AVFilterContext *ctx, for (int x = 0; x < width; x++) { \ const float smooth = 1.f + x / w * (h - 1 - y) / h - progress * 2.f; \ \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -1173,6 +1223,7 @@ static void diagbr##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ const float w = width; \ const float h = out->height; \ @@ -1182,7 +1233,7 @@ static void diagbr##name##_transition(AVFilterContext *ctx, const float smooth = 1.f + (w - 1 - x) / w * (h - 1 - y) / h - \ progress * 2.f; \ \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -1203,6 +1254,7 @@ static void hlslice##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ const float w = width; \ \ @@ -1211,7 +1263,7 @@ static void hlslice##name##_transition(AVFilterContext *ctx, const float smooth = smoothstep(-0.5f, 0.f, x / w - progress * 1.5f); \ const float ss = smooth <= fract(10.f * x / w) ? 0.f : 1.f; \ \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -1232,6 +1284,7 @@ static void hrslice##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ const float w = width; \ \ @@ -1241,7 +1294,7 @@ static void hrslice##name##_transition(AVFilterContext *ctx, const float smooth = smoothstep(-0.5f, 0.f, xx - progress * 1.5f); \ const float ss = smooth <= fract(10.f * xx) ? 0.f : 1.f; \ \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -1262,6 +1315,7 @@ static void vuslice##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ const float h = out->height; \ \ @@ -1270,7 +1324,7 @@ static void vuslice##name##_transition(AVFilterContext *ctx, const float ss = smooth <= fract(10.f * y / h) ? 0.f : 1.f; \ \ for (int x = 0; x < width; x++) { \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -1291,6 +1345,7 @@ static void vdslice##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ const float h = out->height; \ \ @@ -1300,7 +1355,7 @@ static void vdslice##name##_transition(AVFilterContext *ctx, const float ss = smooth <= fract(10.f * yy) ? 0.f : 1.f; \ \ for (int x = 0; x < width; x++) { \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -1321,12 +1376,13 @@ static void hblur##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ const float prog = progress <= 0.5f ? progress * 2.f : (1.f - progress) * 2.f; \ const int size = 1 + (width / 2) * prog; \ \ for (int y = slice_start; y < slice_end; y++) { \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -1374,6 +1430,7 @@ static void fadegrays##name##_transition(AVFilterContext *ctx, for (int x = 0; x < width; x++) { \ int bg[2][4]; \ if (is_rgb) { \ + bg[0][0] = bg[1][0] = 0; \ for (int p = 0; p < s->nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + \ y * a->linesize[p]); \ @@ -1435,6 +1492,7 @@ static void wipetl##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ + const int width = out->width; \ const int zw = out->width * progress; \ const int zh = out->height * progress; \ \ @@ -1444,7 +1502,7 @@ static void wipetl##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = slice_start + y <= zh && \ x <= zw ? xf0[x] : xf1[x]; \ } \ @@ -1467,7 +1525,8 @@ static void wipetr##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ - const int zw = out->width * (1.f - progress); \ + const int width = out->width; \ + const int zw = width * (1.f - progress); \ const int zh = out->height * progress; \ \ for (int p = 0; p < s->nb_planes; p++) { \ @@ -1476,7 +1535,7 @@ static void wipetr##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = slice_start + y <= zh && \ x > zw ? xf0[x] : xf1[x]; \ } \ @@ -1499,7 +1558,8 @@ static void wipebl##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ - const int zw = out->width * progress; \ + const int width = out->width; \ + const int zw = width * progress; \ const int zh = out->height * (1.f - progress); \ \ for (int p = 0; p < s->nb_planes; p++) { \ @@ -1508,7 +1568,7 @@ static void wipebl##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = slice_start + y > zh && \ x <= zw ? xf0[x] : xf1[x]; \ } \ @@ -1532,7 +1592,8 @@ static void wipebr##name##_transition(AVFilterContext *ctx, XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ const int zh = out->height * (1.f - progress); \ - const int zw = out->width * (1.f - progress); \ + const int width = out->width; \ + const int zw = width * (1.f - progress); \ \ for (int p = 0; p < s->nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \ @@ -1540,7 +1601,7 @@ static void wipebr##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = slice_start + y > zh && \ x > zw ? xf0[x] : xf1[x]; \ } \ @@ -1564,6 +1625,7 @@ static void squeezeh##name##_transition(AVFilterContext *ctx, XFadeContext *s = ctx->priv; \ const float h = out->height; \ const int height = slice_end - slice_start; \ + const int width = out->width; \ \ for (int p = 0; p < s->nb_planes; p++) { \ const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \ @@ -1573,13 +1635,13 @@ static void squeezeh##name##_transition(AVFilterContext *ctx, const float z = .5f + ((slice_start + y) / h - .5f) / progress; \ \ if (z < 0.f || z > 1.f) { \ - for (int x = 0; x < out->width; x++) \ + for (int x = 0; x < width; x++) \ dst[x] = xf1[x]; \ } else { \ const int yy = lrintf(z * (h - 1.f)); \ const type *xf0 = (const type *)(a->data[p] + yy * a->linesize[p]); \ \ - for (int x = 0; x < out->width; x++) \ + for (int x = 0; x < width; x++) \ dst[x] = xf0[x]; \ } \ \ @@ -1599,7 +1661,8 @@ static void squeezev##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ - const float w = out->width; \ + const int width = out->width; \ + const float w = width; \ const int height = slice_end - slice_start; \ \ for (int p = 0; p < s->nb_planes; p++) { \ @@ -1608,7 +1671,7 @@ static void squeezev##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ const float z = .5f + (x / w - .5f) / progress; \ \ if (z < 0.f || z > 1.f) { \ @@ -1643,7 +1706,8 @@ static void zoomin##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ - const float w = out->width; \ + const int width = out->width; \ + const float w = width; \ const float h = out->height; \ const float zf = smoothstep(0.5f, 1.f, progress); \ \ @@ -1653,7 +1717,7 @@ static void zoomin##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = slice_start; y < slice_end; y++) { \ - for (int x = 0; x < w; x++) { \ + for (int x = 0; x < width; x++) { \ float zv, u, v; \ int iu, iv; \ \ @@ -1682,6 +1746,7 @@ static void fadefast##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ + const int width = out->width; \ const float imax = 1.f / s->max_value; \ \ for (int p = 0; p < s->nb_planes; p++) { \ @@ -1690,7 +1755,7 @@ static void fadefast##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = mix(xf0[x], xf1[x], powf(progress, 1.f + \ logf(1.f+FFABS(xf0[x]-xf1[x])*imax)\ )); \ @@ -1714,6 +1779,7 @@ static void fadeslow##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ + const int width = out->width; \ const float imax = 1.f / s->max_value; \ \ for (int p = 0; p < s->nb_planes; p++) { \ @@ -1722,7 +1788,7 @@ static void fadeslow##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = mix(xf0[x], xf1[x], powf(progress, 1.f + \ logf(2.f-FFABS(xf0[x]-xf1[x])*imax)\ )); \ @@ -1738,6 +1804,204 @@ static void fadeslow##name##_transition(AVFilterContext *ctx, FADESLOW_TRANSITION(8, uint8_t, 1) FADESLOW_TRANSITION(16, uint16_t, 2) +#define HWIND_TRANSITION(name, z, type, div, expr) \ +static void h##z##wind##name##_transition(AVFilterContext *ctx, \ + const AVFrame *a, const AVFrame *b, AVFrame *out, \ + float progress, \ + int slice_start, int slice_end, int jobnr) \ +{ \ + XFadeContext *s = ctx->priv; \ + const int width = out->width; \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + const float r = frand(0, y); \ + for (int x = 0; x < width; x++) { \ + const float fx = expr x / (float)width; \ + for (int p = 0; p < s->nb_planes; p++) { \ + const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ + const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ + type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ + \ + dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f,-0.2f, fx * (1.f - 0.2f)\ + + 0.2f * r - (1.f - progress)\ + * (1.f + 0.2f))); \ + } \ + } \ + } \ +} + +HWIND_TRANSITION(8, l, uint8_t, 1, 1.f - ) +HWIND_TRANSITION(16, l, uint16_t, 2, 1.f - ) +HWIND_TRANSITION(8, r, uint8_t, 1, ) +HWIND_TRANSITION(16, r, uint16_t, 2, ) + +#define VWIND_TRANSITION(name, z, type, div, expr) \ +static void v##z##wind##name##_transition(AVFilterContext *ctx, \ + const AVFrame *a, const AVFrame *b, AVFrame *out, \ + float progress, \ + int slice_start, int slice_end, int jobnr) \ +{ \ + XFadeContext *s = ctx->priv; \ + const int width = out->width; \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + const float fy = expr y / (float)out->height; \ + for (int x = 0; x < width; x++) { \ + const float r = frand(x, 0); \ + for (int p = 0; p < s->nb_planes; p++) { \ + const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ + const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ + type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ + \ + dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f,-0.2f, fy * (1.f - 0.2f) \ + + 0.2f * r - (1.f - progress)\ + * (1.f + 0.2f))); \ + } \ + } \ + } \ +} + +VWIND_TRANSITION(8, u, uint8_t, 1, 1.f - ) +VWIND_TRANSITION(16, u, uint16_t, 2, 1.f - ) +VWIND_TRANSITION(8, d, uint8_t, 1, ) +VWIND_TRANSITION(16, d, uint16_t, 2, ) + +#define COVERH_TRANSITION(dir, name, type, div, expr) \ +static void cover##dir##name##_transition(AVFilterContext *ctx, \ + const AVFrame *a, const AVFrame *b, AVFrame *out, \ + float progress, \ + int slice_start, int slice_end, int jobnr) \ +{ \ + XFadeContext *s = ctx->priv; \ + const int height = slice_end - slice_start; \ + const int width = out->width; \ + const int z = (expr progress) * width; \ + \ + for (int p = 0; p < s->nb_planes; p++) { \ + const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \ + const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \ + type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ + \ + for (int y = 0; y < height; y++) { \ + for (int x = 0; x < width; x++) { \ + const int zx = z + x; \ + const int zz = zx % width + width * (zx < 0); \ + dst[x] = (zx >= 0) && (zx < width) ? xf1[zz] : xf0[x]; \ + } \ + \ + dst += out->linesize[p] / div; \ + xf0 += a->linesize[p] / div; \ + xf1 += b->linesize[p] / div; \ + } \ + } \ +} + +COVERH_TRANSITION(left, 8, uint8_t, 1, -) +COVERH_TRANSITION(left, 16, uint16_t, 2, -) +COVERH_TRANSITION(right, 8, uint8_t, 1, ) +COVERH_TRANSITION(right, 16, uint16_t, 2, ) + +#define COVERV_TRANSITION(dir, name, type, div, expr) \ +static void cover##dir##name##_transition(AVFilterContext *ctx, \ + const AVFrame *a, const AVFrame *b, AVFrame *out, \ + float progress, \ + int slice_start, int slice_end, int jobnr) \ +{ \ + XFadeContext *s = ctx->priv; \ + const int height = out->height; \ + const int width = out->width; \ + const int z = (expr progress) * height; \ + \ + for (int p = 0; p < s->nb_planes; p++) { \ + type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + const int zy = z + y; \ + const int zz = zy % height + height * (zy < 0); \ + const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ + const type *xf1 = (const type *)(b->data[p] + zz * b->linesize[p]); \ + \ + for (int x = 0; x < width; x++) \ + dst[x] = (zy >= 0) && (zy < height) ? xf1[x] : xf0[x]; \ + \ + dst += out->linesize[p] / div; \ + } \ + } \ +} + +COVERV_TRANSITION(up, 8, uint8_t, 1, -) +COVERV_TRANSITION(up, 16, uint16_t, 2, -) +COVERV_TRANSITION(down, 8, uint8_t, 1, ) +COVERV_TRANSITION(down, 16, uint16_t, 2, ) + +#define REVEALH_TRANSITION(dir, name, type, div, expr) \ +static void reveal##dir##name##_transition(AVFilterContext *ctx, \ + const AVFrame *a, const AVFrame *b, AVFrame *out, \ + float progress, \ + int slice_start, int slice_end, int jobnr) \ +{ \ + XFadeContext *s = ctx->priv; \ + const int height = slice_end - slice_start; \ + const int width = out->width; \ + const int z = (expr progress) * width; \ + \ + for (int p = 0; p < s->nb_planes; p++) { \ + const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \ + const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \ + type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ + \ + for (int y = 0; y < height; y++) { \ + for (int x = 0; x < width; x++) { \ + const int zx = z + x; \ + const int zz = zx % width + width * (zx < 0); \ + dst[x] = (zx >= 0) && (zx < width) ? xf1[x] : xf0[zz]; \ + } \ + \ + dst += out->linesize[p] / div; \ + xf0 += a->linesize[p] / div; \ + xf1 += b->linesize[p] / div; \ + } \ + } \ +} + +REVEALH_TRANSITION(left, 8, uint8_t, 1, -) +REVEALH_TRANSITION(left, 16, uint16_t, 2, -) +REVEALH_TRANSITION(right, 8, uint8_t, 1, ) +REVEALH_TRANSITION(right, 16, uint16_t, 2, ) + +#define REVEALV_TRANSITION(dir, name, type, div, expr) \ +static void reveal##dir##name##_transition(AVFilterContext *ctx, \ + const AVFrame *a, const AVFrame *b, AVFrame *out, \ + float progress, \ + int slice_start, int slice_end, int jobnr) \ +{ \ + XFadeContext *s = ctx->priv; \ + const int height = out->height; \ + const int width = out->width; \ + const int z = (expr progress) * height; \ + \ + for (int p = 0; p < s->nb_planes; p++) { \ + type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + const int zy = z + y; \ + const int zz = zy % height + height * (zy < 0); \ + const type *xf0 = (const type *)(a->data[p] + zz * a->linesize[p]); \ + const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ + \ + for (int x = 0; x < width; x++) \ + dst[x] = (zy >= 0) && (zy < height) ? xf1[x] : xf0[x]; \ + \ + dst += out->linesize[p] / div; \ + } \ + } \ +} + +REVEALV_TRANSITION(up, 8, uint8_t, 1, -) +REVEALV_TRANSITION(up, 16, uint16_t, 2, -) +REVEALV_TRANSITION(down, 8, uint8_t, 1, ) +REVEALV_TRANSITION(down, 16, uint16_t, 2, ) + static inline double getpix(void *priv, double x, double y, int plane, int nb) { XFadeContext *s = priv; @@ -1831,12 +2095,10 @@ static int config_output(AVFilterLink *outlink) s->white[0] = s->white[3] = s->max_value; s->white[1] = s->white[2] = s->is_rgb ? s->max_value : s->max_value / 2; - s->first_pts = s->last_pts = s->pts = AV_NOPTS_VALUE; + s->start_pts = s->inputs_offset_pts = AV_NOPTS_VALUE; if (s->duration) s->duration_pts = av_rescale_q(s->duration, AV_TIME_BASE_Q, outlink->time_base); - if (s->offset) - s->offset_pts = av_rescale_q(s->offset, AV_TIME_BASE_Q, outlink->time_base); switch (s->transition) { case CUSTOM: s->transitionf = s->depth <= 8 ? custom8_transition : custom16_transition; break; @@ -1886,6 +2148,18 @@ static int config_output(AVFilterLink *outlink) case ZOOMIN: s->transitionf = s->depth <= 8 ? zoomin8_transition : zoomin16_transition; break; case FADEFAST: s->transitionf = s->depth <= 8 ? fadefast8_transition : fadefast16_transition; break; case FADESLOW: s->transitionf = s->depth <= 8 ? fadeslow8_transition : fadeslow16_transition; break; + case HLWIND: s->transitionf = s->depth <= 8 ? hlwind8_transition : hlwind16_transition; break; + case HRWIND: s->transitionf = s->depth <= 8 ? hrwind8_transition : hrwind16_transition; break; + case VUWIND: s->transitionf = s->depth <= 8 ? vuwind8_transition : vuwind16_transition; break; + case VDWIND: s->transitionf = s->depth <= 8 ? vdwind8_transition : vdwind16_transition; break; + case COVERLEFT: s->transitionf = s->depth <= 8 ? coverleft8_transition : coverleft16_transition; break; + case COVERRIGHT: s->transitionf = s->depth <= 8 ? coverright8_transition : coverright16_transition; break; + case COVERUP: s->transitionf = s->depth <= 8 ? coverup8_transition : coverup16_transition; break; + case COVERDOWN: s->transitionf = s->depth <= 8 ? coverdown8_transition : coverdown16_transition; break; + case REVEALLEFT: s->transitionf = s->depth <= 8 ? revealleft8_transition : revealleft16_transition; break; + case REVEALRIGHT:s->transitionf = s->depth <= 8 ? revealright8_transition: revealright16_transition;break; + case REVEALUP: s->transitionf = s->depth <= 8 ? revealup8_transition : revealup16_transition; break; + case REVEALDOWN: s->transitionf = s->depth <= 8 ? revealdown8_transition : revealdown16_transition; break; default: return AVERROR_BUG; } @@ -1929,7 +2203,7 @@ static int xfade_frame(AVFilterContext *ctx, AVFrame *a, AVFrame *b) { XFadeContext *s = ctx->priv; AVFilterLink *outlink = ctx->outputs[0]; - float progress = av_clipf(1.f - ((float)(s->pts - s->first_pts - s->offset_pts) / s->duration_pts), 0.f, 1.f); + float progress = av_clipf(1.f - ((float)(s->pts - s->start_pts) / s->duration_pts), 0.f, 1.f); ThreadData td; AVFrame *out; @@ -1947,109 +2221,153 @@ static int xfade_frame(AVFilterContext *ctx, AVFrame *a, AVFrame *b) return ff_filter_frame(outlink, out); } -static int xfade_activate(AVFilterContext *ctx) +static int forward_frame(XFadeContext *s, + AVFilterLink *inlink, AVFilterLink *outlink) { - XFadeContext *s = ctx->priv; - AVFilterLink *outlink = ctx->outputs[0]; - AVFrame *in = NULL; + int64_t status_pts; int ret = 0, status; - int64_t pts; + AVFrame *frame = NULL; - FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx); + ret = ff_inlink_consume_frame(inlink, &frame); + if (ret < 0) + return ret; - if (s->xfade_is_over) { - if (!s->eof[0]) { - ret = ff_inlink_consume_frame(ctx->inputs[0], &in); - if (ret > 0) - av_frame_free(&in); - } - ret = ff_inlink_consume_frame(ctx->inputs[1], &in); - if (ret < 0) { - return ret; - } else if (ret > 0) { - in->pts = (in->pts - s->last_pts) + s->pts; - return ff_filter_frame(outlink, in); - } else if (ff_inlink_acknowledge_status(ctx->inputs[1], &status, &pts)) { - ff_outlink_set_status(outlink, status, s->pts); - return 0; - } else if (!ret) { - if (ff_outlink_frame_wanted(outlink)) - ff_inlink_request_frame(ctx->inputs[1]); - return 0; - } + if (ret > 0) { + // If we do not have an offset yet, it's because we + // never got a first input. Just offset to 0 + if (s->inputs_offset_pts == AV_NOPTS_VALUE) + s->inputs_offset_pts = -frame->pts; + + // We got a frame, nothing to do other than adjusting the timestamp + frame->pts += s->inputs_offset_pts; + return ff_filter_frame(outlink, frame); } - if (ff_inlink_queued_frames(ctx->inputs[0]) > 0) { - s->xf[0] = ff_inlink_peek_frame(ctx->inputs[0], 0); - if (s->xf[0]) { - if (s->first_pts == AV_NOPTS_VALUE) { - s->first_pts = s->xf[0]->pts; - } - s->pts = s->xf[0]->pts; - if (s->first_pts + s->offset_pts > s->xf[0]->pts) { - s->xf[0] = NULL; - s->need_second = 0; - ff_inlink_consume_frame(ctx->inputs[0], &in); - return ff_filter_frame(outlink, in); + // Forward status with our timestamp + if (ff_inlink_acknowledge_status(inlink, &status, &status_pts)) { + if (s->inputs_offset_pts == AV_NOPTS_VALUE) + s->inputs_offset_pts = -status_pts; + + ff_outlink_set_status(outlink, status, status_pts + s->inputs_offset_pts); + return 0; + } + + // No frame available, request one if needed + if (ff_outlink_frame_wanted(outlink)) + ff_inlink_request_frame(inlink); + + return 0; +} + +static int xfade_activate(AVFilterContext *avctx) +{ + XFadeContext *s = avctx->priv; + AVFilterLink *in_a = avctx->inputs[0]; + AVFilterLink *in_b = avctx->inputs[1]; + AVFilterLink *outlink = avctx->outputs[0]; + int64_t status_pts; + + FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, avctx); + + // Check if we already transitioned or first input ended prematurely, + // in which case just forward the frames from second input with adjusted + // timestamps until EOF. + if (s->status[0] && !s->status[1]) + return forward_frame(s, in_b, outlink); + + // We did not finish transitioning yet and the first stream + // did not end either, so check if there are more frames to consume. + if (ff_inlink_check_available_frame(in_a)) { + AVFrame *peeked_frame = ff_inlink_peek_frame(in_a, 0); + s->pts = peeked_frame->pts; + + if (s->start_pts == AV_NOPTS_VALUE) + s->start_pts = + s->pts + av_rescale_q(s->offset, AV_TIME_BASE_Q, in_a->time_base); + + // Check if we are not yet transitioning, in which case + // just request and forward the input frame. + if (s->start_pts > s->pts) { + s->passthrough = 1; + ff_inlink_consume_frame(in_a, &s->xf[0]); + return ff_filter_frame(outlink, s->xf[0]); + } + s->passthrough = 0; + + // We are transitioning, so we need a frame from second input + if (ff_inlink_check_available_frame(in_b)) { + int ret; + ff_inlink_consume_frame(avctx->inputs[0], &s->xf[0]); + ff_inlink_consume_frame(avctx->inputs[1], &s->xf[1]); + + // Calculate PTS offset to first input + if (s->inputs_offset_pts == AV_NOPTS_VALUE) + s->inputs_offset_pts = s->pts - s->xf[1]->pts; + + // Check if we finished transitioning, in which case we + // report back EOF to first input as it is no longer needed. + if (s->pts - s->start_pts > s->duration_pts) { + s->status[0] = AVERROR_EOF; + ff_inlink_set_status(in_a, AVERROR_EOF); + s->passthrough = 1; } + ret = xfade_frame(avctx, s->xf[0], s->xf[1]); + av_frame_free(&s->xf[0]); + av_frame_free(&s->xf[1]); + return ret; + } - s->need_second = 1; + // We did not get a frame from second input, check its status. + if (ff_inlink_acknowledge_status(in_b, &s->status[1], &status_pts)) { + // We should transition, but second input is EOF so just report EOF output now. + ff_outlink_set_status(outlink, s->status[1], s->pts); + return 0; } - } - if (s->xf[0] && ff_inlink_queued_frames(ctx->inputs[1]) > 0) { - ff_inlink_consume_frame(ctx->inputs[0], &s->xf[0]); - ff_inlink_consume_frame(ctx->inputs[1], &s->xf[1]); - - s->last_pts = s->xf[1]->pts; - s->pts = s->xf[0]->pts; - if (s->xf[0]->pts - (s->first_pts + s->offset_pts) > s->duration_pts) - s->xfade_is_over = 1; - ret = xfade_frame(ctx, s->xf[0], s->xf[1]); - av_frame_free(&s->xf[0]); - av_frame_free(&s->xf[1]); - return ret; + // We did not get a frame for second input but no EOF either, so just request more. + if (ff_outlink_frame_wanted(outlink)) { + ff_inlink_request_frame(in_b); + return 0; + } } - if (ff_inlink_queued_frames(ctx->inputs[0]) > 0 && - ff_inlink_queued_frames(ctx->inputs[1]) > 0) { - ff_filter_set_ready(ctx, 100); + // We did not get a frame from first input, check its status. + if (ff_inlink_acknowledge_status(in_a, &s->status[0], &status_pts)) { + // No more frames from first input, do not report EOF though, we will just + // forward the second input frames in the next activate calls. + s->passthrough = 1; + ff_filter_set_ready(avctx, 100); return 0; } + // We have no frames yet from first input and no EOF, so request some. if (ff_outlink_frame_wanted(outlink)) { - if (!s->eof[0] && ff_outlink_get_status(ctx->inputs[0])) { - s->eof[0] = 1; - s->xfade_is_over = 1; - } - if (!s->eof[1] && ff_outlink_get_status(ctx->inputs[1])) { - s->eof[1] = 1; - } - if (!s->eof[0] && !s->xf[0] && ff_inlink_queued_frames(ctx->inputs[0]) == 0) - ff_inlink_request_frame(ctx->inputs[0]); - if (!s->eof[1] && (s->need_second || s->eof[0]) && ff_inlink_queued_frames(ctx->inputs[1]) == 0) - ff_inlink_request_frame(ctx->inputs[1]); - if (s->eof[0] && s->eof[1] && ( - ff_inlink_queued_frames(ctx->inputs[0]) <= 0 && - ff_inlink_queued_frames(ctx->inputs[1]) <= 0)) { - ff_outlink_set_status(outlink, AVERROR_EOF, AV_NOPTS_VALUE); - } else if (s->xfade_is_over) { - ff_filter_set_ready(ctx, 100); - } + ff_inlink_request_frame(in_a); return 0; } return FFERROR_NOT_READY; } +static AVFrame *get_video_buffer(AVFilterLink *inlink, int w, int h) +{ + XFadeContext *s = inlink->dst->priv; + + return s->passthrough ? + ff_null_get_video_buffer (inlink, w, h) : + ff_default_get_video_buffer(inlink, w, h); +} + static const AVFilterPad xfade_inputs[] = { { .name = "main", .type = AVMEDIA_TYPE_VIDEO, + .get_buffer.video = get_video_buffer, }, { .name = "xfade", .type = AVMEDIA_TYPE_VIDEO, + .get_buffer.video = get_video_buffer, }, }; diff --git a/libavfilter/vf_xfade_opencl.c b/libavfilter/vf_xfade_opencl.c index cca4980e012..fb567aa7fd8 100644 --- a/libavfilter/vf_xfade_opencl.c +++ b/libavfilter/vf_xfade_opencl.c @@ -93,7 +93,7 @@ static int xfade_opencl_load(AVFilterContext *avctx, if (ctx->transition == CUSTOM) { err = ff_opencl_filter_load_program_from_file(avctx, ctx->source_file); } else { - err = ff_opencl_filter_load_program(avctx, &ff_opencl_source_xfade, 1); + err = ff_opencl_filter_load_program(avctx, &ff_source_xfade_cl, 1); } if (err < 0) return err; @@ -433,4 +433,5 @@ const AVFilter ff_vf_xfade_opencl = { FILTER_OUTPUTS(xfade_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_xfade_vulkan.c b/libavfilter/vf_xfade_vulkan.c new file mode 100644 index 00000000000..f393dde202b --- /dev/null +++ b/libavfilter/vf_xfade_vulkan.c @@ -0,0 +1,585 @@ +/* + * 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 "libavutil/avassert.h" +#include "libavutil/random_seed.h" +#include "libavutil/opt.h" +#include "vulkan_filter.h" +#include "vulkan_spirv.h" +#include "filters.h" +#include "internal.h" +#include "video.h" + +#define IN_A 0 +#define IN_B 1 +#define IN_NB 2 + +typedef struct XFadeParameters { + float progress; +} XFadeParameters; + +typedef struct XFadeVulkanContext { + FFVulkanContext vkctx; + + int transition; + int64_t duration; + int64_t offset; + + int initialized; + FFVulkanPipeline pl; + FFVkExecPool e; + FFVkQueueFamilyCtx qf; + FFVkSPIRVShader shd; + VkSampler sampler; + + // PTS when the fade should start (in IN_A timebase) + int64_t start_pts; + + // PTS offset between IN_A and IN_B + int64_t inputs_offset_pts; + + // Duration of the transition + int64_t duration_pts; + + // Current PTS of the first input (IN_A) + int64_t pts; + + // If frames are currently just passed through + // unmodified, like before and after the actual + // transition. + int passthrough; + + int status[IN_NB]; +} XFadeVulkanContext; + +enum XFadeTransitions { + FADE, + WIPELEFT, + WIPERIGHT, + WIPEUP, + WIPEDOWN, + SLIDEDOWN, + SLIDEUP, + SLIDELEFT, + SLIDERIGHT, + NB_TRANSITIONS, +}; + +static const char transition_fade[] = { + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, vec4 a = texture(a_images[idx], pos); ) + C(1, vec4 b = texture(b_images[idx], pos); ) + C(1, imageStore(output_images[idx], pos, mix(a, b, progress)); ) + C(0, } ) +}; + +static const char transition_wipeleft[] = { + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, ivec2 size = imageSize(output_images[idx]); ) + C(1, int s = int(size.x * (1.0 - progress)); ) + C(1, vec4 a = texture(a_images[idx], pos); ) + C(1, vec4 b = texture(b_images[idx], pos); ) + C(1, imageStore(output_images[idx], pos, pos.x > s ? b : a); ) + C(0, } ) +}; + +static const char transition_wiperight[] = { + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, ivec2 size = imageSize(output_images[idx]); ) + C(1, int s = int(size.x * progress); ) + C(1, vec4 a = texture(a_images[idx], pos); ) + C(1, vec4 b = texture(b_images[idx], pos); ) + C(1, imageStore(output_images[idx], pos, pos.x > s ? a : b); ) + C(0, } ) +}; + +static const char transition_wipeup[] = { + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, ivec2 size = imageSize(output_images[idx]); ) + C(1, int s = int(size.y * (1.0 - progress)); ) + C(1, vec4 a = texture(a_images[idx], pos); ) + C(1, vec4 b = texture(b_images[idx], pos); ) + C(1, imageStore(output_images[idx], pos, pos.y > s ? b : a); ) + C(0, } ) +}; + +static const char transition_wipedown[] = { + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, ivec2 size = imageSize(output_images[idx]); ) + C(1, int s = int(size.y * progress); ) + C(1, vec4 a = texture(a_images[idx], pos); ) + C(1, vec4 b = texture(b_images[idx], pos); ) + C(1, imageStore(output_images[idx], pos, pos.y > s ? a : b); ) + C(0, } ) +}; + +#define SHADER_SLIDE_COMMON \ + C(0, void slide(int idx, ivec2 pos, float progress, ivec2 direction) ) \ + C(0, { ) \ + C(1, ivec2 size = imageSize(output_images[idx]); ) \ + C(1, ivec2 pi = ivec2(progress * size); ) \ + C(1, ivec2 p = pos + pi * direction; ) \ + C(1, ivec2 f = p % size; ) \ + C(1, f = f + size * ivec2(f.x < 0, f.y < 0); ) \ + C(1, vec4 a = texture(a_images[idx], f); ) \ + C(1, vec4 b = texture(b_images[idx], f); ) \ + C(1, vec4 r = (p.y >= 0 && p.x >= 0 && size.y > p.y && size.x > p.x) ? a : b; ) \ + C(1, imageStore(output_images[idx], pos, r); ) \ + C(0, } ) + +static const char transition_slidedown[] = { + SHADER_SLIDE_COMMON + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, slide(idx, pos, progress, ivec2(0, -1)); ) + C(0, } ) +}; + +static const char transition_slideup[] = { + SHADER_SLIDE_COMMON + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, slide(idx, pos, progress, ivec2(0, +1)); ) + C(0, } ) +}; + +static const char transition_slideleft[] = { + SHADER_SLIDE_COMMON + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, slide(idx, pos, progress, ivec2(+1, 0)); ) + C(0, } ) +}; + +static const char transition_slideright[] = { + SHADER_SLIDE_COMMON + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, slide(idx, pos, progress, ivec2(-1, 0)); ) + C(0, } ) +}; + +static const char* transitions_map[NB_TRANSITIONS] = { + [FADE] = transition_fade, + [WIPELEFT] = transition_wipeleft, + [WIPERIGHT] = transition_wiperight, + [WIPEUP] = transition_wipeup, + [WIPEDOWN] = transition_wipedown, + [SLIDEDOWN] = transition_slidedown, + [SLIDEUP] = transition_slideup, + [SLIDELEFT] = transition_slideleft, + [SLIDERIGHT]= transition_slideright, +}; + +static av_cold int init_vulkan(AVFilterContext *avctx) +{ + int err = 0; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; + XFadeVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); + FFVkSPIRVShader *shd = &s->shd; + FFVkSPIRVCompiler *spv; + FFVulkanDescriptorSetBinding *desc; + + spv = ff_vk_spirv_init(); + if (!spv) { + av_log(avctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); + return AVERROR_EXTERNAL; + } + + ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT); + RET(ff_vk_exec_pool_init(vkctx, &s->qf, &s->e, s->qf.nb_queues*4, 0, 0, 0, NULL)); + RET(ff_vk_init_sampler(vkctx, &s->sampler, 1, VK_FILTER_NEAREST)); + RET(ff_vk_shader_init(&s->pl, &s->shd, "xfade_compute", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); + + ff_vk_shader_set_compute_sizes(&s->shd, 32, 32, 1); + + desc = (FFVulkanDescriptorSetBinding []) { + { + .name = "a_images", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), + }, + { + .name = "b_images", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), + }, + { + .name = "output_images", + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format), + .mem_quali = "writeonly", + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + }, + }; + + RET(ff_vk_pipeline_descriptor_set_add(vkctx, &s->pl, shd, desc, 3, 0, 0)); + + GLSLC(0, layout(push_constant, std430) uniform pushConstants { ); + GLSLC(1, float progress; ); + GLSLC(0, }; ); + + ff_vk_add_push_constant(&s->pl, 0, sizeof(XFadeParameters), + VK_SHADER_STAGE_COMPUTE_BIT); + + // Add the right transition type function to the shader + GLSLD(transitions_map[s->transition]); + + GLSLC(0, void main() ); + GLSLC(0, { ); + GLSLC(1, ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); + GLSLF(1, int planes = %i; ,planes); + GLSLC(1, for (int i = 0; i < planes; i++) { ); + GLSLC(2, transition(i, pos, progress); ); + GLSLC(1, } ); + GLSLC(0, } ); + + RET(spv->compile_shader(spv, avctx, shd, &spv_data, &spv_len, "main", + &spv_opaque)); + RET(ff_vk_shader_create(vkctx, shd, spv_data, spv_len, "main")); + + RET(ff_vk_init_compute_pipeline(vkctx, &s->pl, shd)); + RET(ff_vk_exec_pipeline_register(vkctx, &s->e, &s->pl)); + + s->initialized = 1; + +fail: + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); + if (spv) + spv->uninit(&spv); + + return err; +} + +static int xfade_frame(AVFilterContext *avctx, AVFrame *frame_a, AVFrame *frame_b) +{ + int err; + AVFilterLink *outlink = avctx->outputs[0]; + XFadeVulkanContext *s = avctx->priv; + float progress; + + AVFrame *output = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!output) { + err = AVERROR(ENOMEM); + goto fail; + } + + if (!s->initialized) { + AVHWFramesContext *a_fc = (AVHWFramesContext*)frame_a->hw_frames_ctx->data; + AVHWFramesContext *b_fc = (AVHWFramesContext*)frame_b->hw_frames_ctx->data; + if (a_fc->sw_format != b_fc->sw_format) { + av_log(avctx, AV_LOG_ERROR, + "Currently the sw format of the first input needs to match the second!\n"); + return AVERROR(EINVAL); + } + RET(init_vulkan(avctx)); + } + + RET(av_frame_copy_props(output, frame_a)); + output->pts = s->pts; + + progress = av_clipf((float)(s->pts - s->start_pts) / s->duration_pts, + 0.f, 1.f); + + RET(ff_vk_filter_process_Nin(&s->vkctx, &s->e, &s->pl, output, + (AVFrame *[]){ frame_a, frame_b }, 2, s->sampler, + &(XFadeParameters){ progress }, sizeof(XFadeParameters))); + + return ff_filter_frame(outlink, output); + +fail: + av_frame_free(&output); + return err; +} + +static int config_props_output(AVFilterLink *outlink) +{ + int err; + AVFilterContext *avctx = outlink->src; + XFadeVulkanContext *s = avctx->priv; + AVFilterLink *inlink_a = avctx->inputs[IN_A]; + AVFilterLink *inlink_b = avctx->inputs[IN_B]; + + if (inlink_a->w != inlink_b->w || inlink_a->h != inlink_b->h) { + av_log(avctx, AV_LOG_ERROR, "First input link %s parameters " + "(size %dx%d) do not match the corresponding " + "second input link %s parameters (size %dx%d)\n", + avctx->input_pads[IN_A].name, inlink_a->w, inlink_a->h, + avctx->input_pads[IN_B].name, inlink_b->w, inlink_b->h); + return AVERROR(EINVAL); + } + + if (inlink_a->time_base.num != inlink_b->time_base.num || + inlink_a->time_base.den != inlink_b->time_base.den) { + av_log(avctx, AV_LOG_ERROR, "First input link %s timebase " + "(%d/%d) does not match the corresponding " + "second input link %s timebase (%d/%d)\n", + avctx->input_pads[IN_A].name, inlink_a->time_base.num, inlink_a->time_base.den, + avctx->input_pads[IN_B].name, inlink_b->time_base.num, inlink_b->time_base.den); + return AVERROR(EINVAL); + } + + s->start_pts = s->inputs_offset_pts = AV_NOPTS_VALUE; + + outlink->time_base = inlink_a->time_base; + outlink->frame_rate = inlink_a->frame_rate; + outlink->sample_aspect_ratio = inlink_a->sample_aspect_ratio; + + if (s->duration) + s->duration_pts = av_rescale_q(s->duration, AV_TIME_BASE_Q, inlink_a->time_base); + RET(ff_vk_filter_config_output(outlink)); + +fail: + return err; +} + +static int forward_frame(XFadeVulkanContext *s, + AVFilterLink *inlink, AVFilterLink *outlink) +{ + int64_t status_pts; + int ret = 0, status; + AVFrame *frame = NULL; + + ret = ff_inlink_consume_frame(inlink, &frame); + if (ret < 0) + return ret; + + if (ret > 0) { + // If we do not have an offset yet, it's because we + // never got a first input. Just offset to 0 + if (s->inputs_offset_pts == AV_NOPTS_VALUE) + s->inputs_offset_pts = -frame->pts; + + // We got a frame, nothing to do other than adjusting the timestamp + frame->pts += s->inputs_offset_pts; + return ff_filter_frame(outlink, frame); + } + + // Forward status with our timestamp + if (ff_inlink_acknowledge_status(inlink, &status, &status_pts)) { + if (s->inputs_offset_pts == AV_NOPTS_VALUE) + s->inputs_offset_pts = -status_pts; + + ff_outlink_set_status(outlink, status, status_pts + s->inputs_offset_pts); + return 0; + } + + // No frame available, request one if needed + if (ff_outlink_frame_wanted(outlink)) + ff_inlink_request_frame(inlink); + + return 0; +} + +static int activate(AVFilterContext *avctx) +{ + XFadeVulkanContext *s = avctx->priv; + AVFilterLink *in_a = avctx->inputs[IN_A]; + AVFilterLink *in_b = avctx->inputs[IN_B]; + AVFilterLink *outlink = avctx->outputs[0]; + int64_t status_pts; + + FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, avctx); + + // Check if we already transitioned or IN_A ended prematurely, + // in which case just forward the frames from IN_B with adjusted + // timestamps until EOF. + if (s->status[IN_A] && !s->status[IN_B]) + return forward_frame(s, in_b, outlink); + + // We did not finish transitioning yet and the first stream + // did not end either, so check if there are more frames to consume. + if (ff_inlink_check_available_frame(in_a)) { + AVFrame *peeked_frame = ff_inlink_peek_frame(in_a, 0); + s->pts = peeked_frame->pts; + + if (s->start_pts == AV_NOPTS_VALUE) + s->start_pts = + s->pts + av_rescale_q(s->offset, AV_TIME_BASE_Q, in_a->time_base); + + // Check if we are not yet transitioning, in which case + // just request and forward the input frame. + if (s->start_pts > s->pts) { + AVFrame *frame_a = NULL; + s->passthrough = 1; + ff_inlink_consume_frame(in_a, &frame_a); + return ff_filter_frame(outlink, frame_a); + } + s->passthrough = 0; + + // We are transitioning, so we need a frame from IN_B + if (ff_inlink_check_available_frame(in_b)) { + int ret; + AVFrame *frame_a = NULL, *frame_b = NULL; + ff_inlink_consume_frame(avctx->inputs[IN_A], &frame_a); + ff_inlink_consume_frame(avctx->inputs[IN_B], &frame_b); + + // Calculate PTS offset to first input + if (s->inputs_offset_pts == AV_NOPTS_VALUE) + s->inputs_offset_pts = s->pts - frame_b->pts; + + // Check if we finished transitioning, in which case we + // report back EOF to IN_A as it is no longer needed. + if (s->pts - s->start_pts > s->duration_pts) { + s->status[IN_A] = AVERROR_EOF; + ff_inlink_set_status(in_a, AVERROR_EOF); + s->passthrough = 1; + } + ret = xfade_frame(avctx, frame_a, frame_b); + av_frame_free(&frame_a); + av_frame_free(&frame_b); + return ret; + } + + // We did not get a frame from IN_B, check its status. + if (ff_inlink_acknowledge_status(in_b, &s->status[IN_B], &status_pts)) { + // We should transition, but IN_B is EOF so just report EOF output now. + ff_outlink_set_status(outlink, s->status[IN_B], s->pts); + return 0; + } + + // We did not get a frame for IN_B but no EOF either, so just request more. + if (ff_outlink_frame_wanted(outlink)) { + ff_inlink_request_frame(in_b); + return 0; + } + } + + // We did not get a frame from IN_A, check its status. + if (ff_inlink_acknowledge_status(in_a, &s->status[IN_A], &status_pts)) { + // No more frames from IN_A, do not report EOF though, we will just + // forward the IN_B frames in the next activate calls. + s->passthrough = 1; + ff_filter_set_ready(avctx, 100); + return 0; + } + + // We have no frames yet from IN_A and no EOF, so request some. + if (ff_outlink_frame_wanted(outlink)) { + ff_inlink_request_frame(in_a); + return 0; + } + + return FFERROR_NOT_READY; +} + +static av_cold void uninit(AVFilterContext *avctx) +{ + XFadeVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + FFVulkanFunctions *vk = &vkctx->vkfn; + + ff_vk_exec_pool_free(vkctx, &s->e); + ff_vk_pipeline_free(vkctx, &s->pl); + ff_vk_shader_free(vkctx, &s->shd); + + if (s->sampler) + vk->DestroySampler(vkctx->hwctx->act_dev, s->sampler, + vkctx->hwctx->alloc); + + ff_vk_uninit(&s->vkctx); + + s->initialized = 0; +} + +static AVFrame *get_video_buffer(AVFilterLink *inlink, int w, int h) +{ + XFadeVulkanContext *s = inlink->dst->priv; + + return s->passthrough ? + ff_null_get_video_buffer (inlink, w, h) : + ff_default_get_video_buffer(inlink, w, h); +} + +#define OFFSET(x) offsetof(XFadeVulkanContext, x) +#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) + +static const AVOption xfade_vulkan_options[] = { + { "transition", "set cross fade transition", OFFSET(transition), AV_OPT_TYPE_INT, {.i64=FADE}, 0, NB_TRANSITIONS-1, FLAGS, "transition" }, + { "fade", "fade transition", 0, AV_OPT_TYPE_CONST, {.i64=FADE}, 0, 0, FLAGS, "transition" }, + { "wipeleft", "wipe left transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPELEFT}, 0, 0, FLAGS, "transition" }, + { "wiperight", "wipe right transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPERIGHT}, 0, 0, FLAGS, "transition" }, + { "wipeup", "wipe up transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEUP}, 0, 0, FLAGS, "transition" }, + { "wipedown", "wipe down transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEDOWN}, 0, 0, FLAGS, "transition" }, + { "slidedown", "slide down transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDEDOWN}, 0, 0, FLAGS, "transition" }, + { "slideup", "slide up transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDEUP}, 0, 0, FLAGS, "transition" }, + { "slideleft", "slide left transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDELEFT}, 0, 0, FLAGS, "transition" }, + { "slideright","slide right transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDERIGHT}, 0, 0, FLAGS, "transition" }, + { "duration", "set cross fade duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64=1000000}, 0, 60000000, FLAGS }, + { "offset", "set cross fade start relative to first input stream", OFFSET(offset), AV_OPT_TYPE_DURATION, {.i64=0}, INT64_MIN, INT64_MAX, FLAGS }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(xfade_vulkan); + +static const AVFilterPad xfade_vulkan_inputs[] = { + { + .name = "main", + .type = AVMEDIA_TYPE_VIDEO, + .get_buffer.video = &get_video_buffer, + .config_props = &ff_vk_filter_config_input, + }, + { + .name = "xfade", + .type = AVMEDIA_TYPE_VIDEO, + .get_buffer.video = &get_video_buffer, + .config_props = &ff_vk_filter_config_input, + }, +}; + +static const AVFilterPad xfade_vulkan_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = &config_props_output, + }, +}; + +const AVFilter ff_vf_xfade_vulkan = { + .name = "xfade_vulkan", + .description = NULL_IF_CONFIG_SMALL("Cross fade one video with another video."), + .priv_size = sizeof(XFadeVulkanContext), + .init = &ff_vk_filter_init, + .uninit = &uninit, + .activate = &activate, + FILTER_INPUTS(xfade_vulkan_inputs), + FILTER_OUTPUTS(xfade_vulkan_outputs), + FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), + .priv_class = &xfade_vulkan_class, + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, +}; diff --git a/libavfilter/vf_xmedian.c b/libavfilter/vf_xmedian.c index 7f7f3de12a8..0a02ca065e3 100644 --- a/libavfilter/vf_xmedian.c +++ b/libavfilter/vf_xmedian.c @@ -22,13 +22,11 @@ #include "libavutil/avstring.h" #include "libavutil/imgutils.h" -#include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libavutil/qsort.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "framesync.h" #include "video.h" diff --git a/libavfilter/vf_yadif.c b/libavfilter/vf_yadif.c index afa4d1d53db..a5a856bf5f1 100644 --- a/libavfilter/vf_yadif.c +++ b/libavfilter/vf_yadif.c @@ -21,11 +21,8 @@ #include "libavutil/common.h" #include "libavutil/pixdesc.h" -#include "libavutil/imgutils.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" -#include "video.h" #include "yadif.h" typedef struct ThreadData { @@ -252,8 +249,6 @@ static void filter(AVFilterContext *ctx, AVFrame *dstpic, ff_filter_execute(ctx, filter_slice, &td, NULL, FFMIN(h, ff_filter_get_nb_threads(ctx))); } - - emms_c(); } static av_cold void uninit(AVFilterContext *ctx) @@ -263,6 +258,7 @@ static av_cold void uninit(AVFilterContext *ctx) av_frame_free(&yadif->prev); av_frame_free(&yadif->cur ); av_frame_free(&yadif->next); + ff_ccfifo_uninit(&yadif->cc_fifo); } static const enum AVPixelFormat pix_fmts[] = { @@ -287,6 +283,7 @@ static int config_output(AVFilterLink *outlink) { AVFilterContext *ctx = outlink->src; YADIFContext *s = ctx->priv; + int ret; outlink->time_base = av_mul_q(ctx->inputs[0]->time_base, (AVRational){1, 2}); outlink->w = ctx->inputs[0]->w; @@ -295,6 +292,14 @@ static int config_output(AVFilterLink *outlink) if(s->mode & 1) outlink->frame_rate = av_mul_q(ctx->inputs[0]->frame_rate, (AVRational){2, 1}); + else + outlink->frame_rate = ctx->inputs[0]->frame_rate; + + ret = ff_ccfifo_init(&s->cc_fifo, outlink->frame_rate, ctx); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Failure to setup CC FIFO queue\n"); + return ret; + } if (outlink->w < 3 || outlink->h < 3) { av_log(ctx, AV_LOG_ERROR, "Video of less than 3 columns or lines is not supported\n"); diff --git a/libavfilter/vf_yadif_cuda.c b/libavfilter/vf_yadif_cuda.c index 685b8a20355..d777757e656 100644 --- a/libavfilter/vf_yadif_cuda.c +++ b/libavfilter/vf_yadif_cuda.c @@ -38,8 +38,6 @@ typedef struct DeintCUDAContext { AVBufferRef *input_frames_ref; AVHWFramesContext *input_frames; - CUcontext cu_ctx; - CUstream stream; CUmodule cu_module; CUfunction cu_func_uchar; CUfunction cu_func_uchar2; @@ -109,7 +107,7 @@ static CUresult call_kernel(AVFilterContext *ctx, CUfunction func, ret = CHECK_CU(cu->cuLaunchKernel(func, DIV_UP(dst_width, BLOCKX), DIV_UP(dst_height, BLOCKY), 1, BLOCKX, BLOCKY, 1, - 0, s->stream, args, NULL)); + 0, s->hwctx->stream, args, NULL)); exit: if (tex_prev) @@ -131,7 +129,7 @@ static void filter(AVFilterContext *ctx, AVFrame *dst, CUcontext dummy; int i, ret; - ret = CHECK_CU(cu->cuCtxPushCurrent(s->cu_ctx)); + ret = CHECK_CU(cu->cuCtxPushCurrent(s->hwctx->cuda_ctx)); if (ret < 0) return; @@ -197,7 +195,7 @@ static av_cold void deint_cuda_uninit(AVFilterContext *ctx) if (s->hwctx && s->cu_module) { CudaFunctions *cu = s->hwctx->internal->cuda_dl; - CHECK_CU(cu->cuCtxPushCurrent(s->cu_ctx)); + CHECK_CU(cu->cuCtxPushCurrent(s->hwctx->cuda_ctx)); CHECK_CU(cu->cuModuleUnload(s->cu_module)); CHECK_CU(cu->cuCtxPopCurrent(&dummy)); } @@ -205,6 +203,7 @@ static av_cold void deint_cuda_uninit(AVFilterContext *ctx) av_frame_free(&y->prev); av_frame_free(&y->cur); av_frame_free(&y->next); + ff_ccfifo_uninit(&y->cc_fifo); av_buffer_unref(&s->device_ref); s->hwctx = NULL; @@ -252,8 +251,6 @@ static int config_output(AVFilterLink *link) return AVERROR(ENOMEM); } s->hwctx = ((AVHWDeviceContext*)s->device_ref->data)->hwctx; - s->cu_ctx = s->hwctx->cuda_ctx; - s->stream = s->hwctx->stream; cu = s->hwctx->internal->cuda_dl; link->hw_frames_ctx = av_hwframe_ctx_alloc(s->device_ref); @@ -291,6 +288,14 @@ static int config_output(AVFilterLink *link) if(y->mode & 1) link->frame_rate = av_mul_q(ctx->inputs[0]->frame_rate, (AVRational){2, 1}); + else + link->frame_rate = ctx->inputs[0]->frame_rate; + + ret = ff_ccfifo_init(&y->cc_fifo, link->frame_rate, ctx); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Failure to setup CC FIFO queue\n"); + goto exit; + } if (link->w < 3 || link->h < 3) { av_log(ctx, AV_LOG_ERROR, "Video of less than 3 columns or lines is not supported\n"); @@ -301,7 +306,7 @@ static int config_output(AVFilterLink *link) y->csp = av_pix_fmt_desc_get(output_frames->sw_format); y->filter = filter; - ret = CHECK_CU(cu->cuCtxPushCurrent(s->cu_ctx)); + ret = CHECK_CU(cu->cuCtxPushCurrent(s->hwctx->cuda_ctx)); if (ret < 0) goto exit; diff --git a/libavfilter/vf_yaepblur.c b/libavfilter/vf_yaepblur.c index 28a74a85a0c..b39738b577a 100644 --- a/libavfilter/vf_yaepblur.c +++ b/libavfilter/vf_yaepblur.c @@ -31,6 +31,7 @@ #include "libavutil/imgutils.h" #include "avfilter.h" #include "internal.h" +#include "video.h" typedef struct YAEPContext { const AVClass *class; @@ -309,13 +310,6 @@ static const AVFilterPad yaep_inputs[] = { }, }; -static const AVFilterPad yaep_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(YAEPContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -338,7 +332,7 @@ const AVFilter ff_vf_yaepblur = { .priv_class = &yaepblur_class, .uninit = uninit, FILTER_INPUTS(yaep_inputs), - FILTER_OUTPUTS(yaep_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_zoompan.c b/libavfilter/vf_zoompan.c index cca979e01ac..e729bda56de 100644 --- a/libavfilter/vf_zoompan.c +++ b/libavfilter/vf_zoompan.c @@ -23,7 +23,6 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" #include "filters.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "libswscale/swscale.h" @@ -351,13 +350,6 @@ static av_cold void uninit(AVFilterContext *ctx) av_frame_free(&s->in); } -static const AVFilterPad inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - static const AVFilterPad outputs[] = { { .name = "default", @@ -374,7 +366,7 @@ const AVFilter ff_vf_zoompan = { .init = init, .uninit = uninit, .activate = activate, - FILTER_INPUTS(inputs), + FILTER_INPUTS(ff_video_default_filterpad), FILTER_OUTPUTS(outputs), FILTER_PIXFMTS_ARRAY(pix_fmts), }; diff --git a/libavfilter/video.c b/libavfilter/video.c index 7683ef6fd45..42eeb98c284 100644 --- a/libavfilter/video.c +++ b/libavfilter/video.c @@ -33,6 +33,13 @@ #include "internal.h" #include "video.h" +const AVFilterPad ff_video_default_filterpad[1] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + } +}; + AVFrame *ff_null_get_video_buffer(AVFilterLink *link, int w, int h) { return ff_get_video_buffer(link->dst->outputs[0], w, h); diff --git a/libavfilter/video.h b/libavfilter/video.h index f37bab9d031..81331c3199f 100644 --- a/libavfilter/video.h +++ b/libavfilter/video.h @@ -22,6 +22,13 @@ #define AVFILTER_VIDEO_H #include "avfilter.h" +#include "internal.h" + +/** + * An AVFilterPad array whose only entry has name "default" + * and is of type AVMEDIA_TYPE_VIDEO. + */ +extern const AVFilterPad ff_video_default_filterpad[1]; AVFrame *ff_default_get_video_buffer(AVFilterLink *link, int w, int h); AVFrame *ff_default_get_video_buffer2(AVFilterLink *link, int w, int h, int align); diff --git a/libavfilter/vsrc_cellauto.c b/libavfilter/vsrc_cellauto.c index 226182daf27..b469f1bf208 100644 --- a/libavfilter/vsrc_cellauto.c +++ b/libavfilter/vsrc_cellauto.c @@ -29,12 +29,10 @@ #include "libavutil/internal.h" #include "libavutil/lfg.h" #include "libavutil/opt.h" -#include "libavutil/parseutils.h" #include "libavutil/random_seed.h" #include "libavutil/avstring.h" #include "avfilter.h" #include "internal.h" -#include "formats.h" #include "video.h" typedef struct CellAutoContext { @@ -302,6 +300,7 @@ static int request_frame(AVFilterLink *outlink) evolve(outlink->src); picref->pts = s->pts++; + picref->duration = 1; #ifdef DEBUG show_cellauto_row(outlink->src); diff --git a/libavfilter/vsrc_ddagrab.c b/libavfilter/vsrc_ddagrab.c index 00c72187eaa..b65261f6853 100644 --- a/libavfilter/vsrc_ddagrab.c +++ b/libavfilter/vsrc_ddagrab.c @@ -44,7 +44,6 @@ #include "compat/w32dlfcn.h" #include "avfilter.h" #include "internal.h" -#include "formats.h" #include "video.h" #include "vsrc_ddagrab_shaders.h" @@ -533,8 +532,23 @@ static int update_mouse_pointer(AVFilterContext *avctx, DXGI_OUTDUPL_FRAME_INFO return 0; if (frame_info->PointerPosition.Visible) { - dda->mouse_x = frame_info->PointerPosition.Position.x; - dda->mouse_y = frame_info->PointerPosition.Position.y; + switch (dda->output_desc.Rotation) { + case DXGI_MODE_ROTATION_ROTATE90: + dda->mouse_x = frame_info->PointerPosition.Position.y; + dda->mouse_y = dda->output_desc.DesktopCoordinates.right - dda->output_desc.DesktopCoordinates.left - frame_info->PointerPosition.Position.x - 1; + break; + case DXGI_MODE_ROTATION_ROTATE180: + dda->mouse_x = dda->output_desc.DesktopCoordinates.right - dda->output_desc.DesktopCoordinates.left - frame_info->PointerPosition.Position.x - 1; + dda->mouse_y = dda->output_desc.DesktopCoordinates.bottom - dda->output_desc.DesktopCoordinates.top - frame_info->PointerPosition.Position.y - 1; + break; + case DXGI_MODE_ROTATION_ROTATE270: + dda->mouse_x = dda->output_desc.DesktopCoordinates.bottom - dda->output_desc.DesktopCoordinates.top - frame_info->PointerPosition.Position.y - 1; + dda->mouse_y = frame_info->PointerPosition.Position.x; + break; + default: + dda->mouse_x = frame_info->PointerPosition.Position.x; + dda->mouse_y = frame_info->PointerPosition.Position.y; + } } else { dda->mouse_x = dda->mouse_y = -1; } @@ -585,7 +599,7 @@ static int update_mouse_pointer(AVFilterContext *avctx, DXGI_OUTDUPL_FRAME_INFO return 0; } -static int next_frame_internal(AVFilterContext *avctx, ID3D11Texture2D **desktop_texture) +static int next_frame_internal(AVFilterContext *avctx, ID3D11Texture2D **desktop_texture, int need_frame) { DXGI_OUTDUPL_FRAME_INFO frame_info; DdagrabContext *dda = avctx->priv; @@ -608,18 +622,32 @@ static int next_frame_internal(AVFilterContext *avctx, ID3D11Texture2D **desktop if (dda->draw_mouse) { ret = update_mouse_pointer(avctx, &frame_info); if (ret < 0) - return ret; + goto error; + } + + if (need_frame && (!frame_info.LastPresentTime.QuadPart || !frame_info.AccumulatedFrames)) { + ret = AVERROR(EAGAIN); + goto error; } hr = IDXGIResource_QueryInterface(desktop_resource, &IID_ID3D11Texture2D, (void**)desktop_texture); - IDXGIResource_Release(desktop_resource); - desktop_resource = NULL; + release_resource(&desktop_resource); if (FAILED(hr)) { av_log(avctx, AV_LOG_ERROR, "DXGIResource QueryInterface failed\n"); - return AVERROR_EXTERNAL; + ret = AVERROR_EXTERNAL; + goto error; } return 0; + +error: + release_resource(&desktop_resource); + + hr = IDXGIOutputDuplication_ReleaseFrame(dda->dxgi_outdupl); + if (FAILED(hr)) + av_log(avctx, AV_LOG_ERROR, "DDA error ReleaseFrame failed!\n"); + + return ret; } static int probe_output_format(AVFilterContext *avctx) @@ -631,7 +659,7 @@ static int probe_output_format(AVFilterContext *avctx) av_assert1(!dda->probed_texture); do { - ret = next_frame_internal(avctx, &dda->probed_texture); + ret = next_frame_internal(avctx, &dda->probed_texture, 1); } while(ret == AVERROR(EAGAIN)); if (ret < 0) return ret; @@ -839,6 +867,41 @@ static int draw_mouse_pointer(AVFilterContext *avctx, AVFrame *frame) D3D11_SUBRESOURCE_DATA init_data = { 0 }; D3D11_BUFFER_DESC buf_desc = { 0 }; + switch (dda->output_desc.Rotation) { + case DXGI_MODE_ROTATION_ROTATE90: + vertices[ 0] = x; vertices[ 1] = y; + vertices[ 5] = x; vertices[ 6] = y - tex_desc.Width; + vertices[10] = x + tex_desc.Height; vertices[11] = y; + vertices[15] = x + tex_desc.Height; vertices[16] = y - tex_desc.Width; + vertices[ 3] = 0.0f; vertices[ 4] = 0.0f; + vertices[ 8] = 1.0f; vertices[ 9] = 0.0f; + vertices[13] = 0.0f; vertices[14] = 1.0f; + vertices[18] = 1.0f; vertices[19] = 1.0f; + break; + case DXGI_MODE_ROTATION_ROTATE180: + vertices[ 0] = x - tex_desc.Width; vertices[ 1] = y; + vertices[ 5] = x - tex_desc.Width; vertices[ 6] = y - tex_desc.Height; + vertices[10] = x; vertices[11] = y; + vertices[15] = x; vertices[16] = y - tex_desc.Height; + vertices[ 3] = 1.0f; vertices[ 4] = 0.0f; + vertices[ 8] = 1.0f; vertices[ 9] = 1.0f; + vertices[13] = 0.0f; vertices[14] = 0.0f; + vertices[18] = 0.0f; vertices[19] = 1.0f; + break; + case DXGI_MODE_ROTATION_ROTATE270: + vertices[ 0] = x - tex_desc.Height; vertices[ 1] = y + tex_desc.Width; + vertices[ 5] = x - tex_desc.Height; vertices[ 6] = y; + vertices[10] = x; vertices[11] = y + tex_desc.Width; + vertices[15] = x; vertices[16] = y; + vertices[ 3] = 1.0f; vertices[ 4] = 1.0f; + vertices[ 8] = 0.0f; vertices[ 9] = 1.0f; + vertices[13] = 1.0f; vertices[14] = 0.0f; + vertices[18] = 0.0f; vertices[19] = 0.0f; + break; + default: + break; + } + num_vertices = sizeof(vertices) / (sizeof(FLOAT) * 5); buf_desc.Usage = D3D11_USAGE_DEFAULT; @@ -918,7 +981,7 @@ static int ddagrab_request_frame(AVFilterLink *outlink) now -= dda->first_pts; if (!dda->probed_texture) { - ret = next_frame_internal(avctx, &cur_texture); + ret = next_frame_internal(avctx, &cur_texture, 0); } else { cur_texture = dda->probed_texture; dda->probed_texture = NULL; @@ -1056,4 +1119,5 @@ const AVFilter ff_vsrc_ddagrab = { FILTER_OUTPUTS(ddagrab_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_D3D11), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vsrc_gradients.c b/libavfilter/vsrc_gradients.c index c005613d422..10510c08bdd 100644 --- a/libavfilter/vsrc_gradients.c +++ b/libavfilter/vsrc_gradients.c @@ -20,13 +20,10 @@ #include "avfilter.h" #include "filters.h" -#include "formats.h" #include "video.h" #include "internal.h" #include "libavutil/imgutils.h" -#include "libavutil/intreadwrite.h" #include "libavutil/opt.h" -#include "libavutil/parseutils.h" #include "libavutil/lfg.h" #include "libavutil/random_seed.h" #include @@ -111,13 +108,13 @@ static uint64_t lerp_color16(uint8_t c0[4], uint8_t c1[4], float x) { const float y = 1.f - x; - return (llrintf((c0[0] * y + c1[0] * x) * 256)) << 0 | - (llrintf((c0[1] * y + c1[1] * x) * 256)) << 16 | - (llrintf((c0[2] * y + c1[2] * x) * 256)) << 32 | - (llrintf((c0[3] * y + c1[3] * x) * 256)) << 48; + return ((uint64_t)llrintf((c0[0] * y + c1[0] * x) * 256)) << 0 | + ((uint64_t)llrintf((c0[1] * y + c1[1] * x) * 256)) << 16 | + ((uint64_t)llrintf((c0[2] * y + c1[2] * x) * 256)) << 32 | + ((uint64_t)llrintf((c0[3] * y + c1[3] * x) * 256)) << 48; } -static uint32_t lerp_colors(uint8_t arr[3][4], int nb_colors, int nb_wrap_colors, float step) +static uint32_t lerp_colors(uint8_t arr[8][4], int nb_colors, int nb_wrap_colors, float step) { float scl; int i, j; @@ -140,7 +137,7 @@ static uint32_t lerp_colors(uint8_t arr[3][4], int nb_colors, int nb_wrap_colors return lerp_color(arr[i], arr[j], scl - i); } -static uint64_t lerp_colors16(uint8_t arr[3][4], int nb_colors, int nb_wrap_colors, float step) +static uint64_t lerp_colors16(uint8_t arr[8][4], int nb_colors, int nb_wrap_colors, float step) { float scl; int i, j; @@ -163,7 +160,7 @@ static uint64_t lerp_colors16(uint8_t arr[3][4], int nb_colors, int nb_wrap_colo return lerp_color16(arr[i], arr[j], scl - i); } -static void lerp_colors32(float arr[3][4], int nb_colors, +static void lerp_colors32(float arr[8][4], int nb_colors, int nb_wrap_colors, float step, float *r, float *g, float *b, float *a) { @@ -187,12 +184,12 @@ static void lerp_colors32(float arr[3][4], int nb_colors, scl = step * (nb_wrap_colors - 1); i = floorf(scl); - x = scl - i; j = i + 1; if (i >= nb_colors - 1) { i = nb_colors - 1; j = 0; } + x = scl - i; *r = lerpf(arr[i][0], arr[j][0], x); *g = lerpf(arr[i][1], arr[j][1], x); @@ -253,11 +250,12 @@ static int draw_gradients_slice(AVFilterContext *ctx, void *arg, int job, int nb const int end = (height * (job+1)) / nb_jobs; const int linesize = frame->linesize[0] / 4; uint32_t *dst = (uint32_t *)frame->data[0] + start * linesize; + const int type = s->type; for (int y = start; y < end; y++) { for (int x = 0; x < width; x++) { - float factor = project(s->fx0, s->fy0, s->fx1, s->fy1, x, y, s->type); - dst[x] = lerp_colors(s->color_rgba, s->nb_colors, s->nb_colors + (s->type >= 2), factor); + float factor = project(s->fx0, s->fy0, s->fx1, s->fy1, x, y, type); + dst[x] = lerp_colors(s->color_rgba, s->nb_colors, s->nb_colors + (type >= 2), factor); } dst += linesize; @@ -276,11 +274,12 @@ static int draw_gradients_slice16(AVFilterContext *ctx, void *arg, int job, int const int end = (height * (job+1)) / nb_jobs; const int linesize = frame->linesize[0] / 8; uint64_t *dst = (uint64_t *)frame->data[0] + start * linesize; + const int type = s->type; for (int y = start; y < end; y++) { for (int x = 0; x < width; x++) { - float factor = project(s->fx0, s->fy0, s->fx1, s->fy1, x, y, s->type); - dst[x] = lerp_colors16(s->color_rgba, s->nb_colors, s->nb_colors + s->type >= 2, factor); + float factor = project(s->fx0, s->fy0, s->fx1, s->fy1, x, y, type); + dst[x] = lerp_colors16(s->color_rgba, s->nb_colors, s->nb_colors + (type >= 2), factor); } dst += linesize; @@ -302,14 +301,15 @@ static int draw_gradients_slice32_planar(AVFilterContext *ctx, void *arg, int jo const int linesize_r = frame->linesize[2] / 4; const int linesize_a = frame->linesize[3] / 4; float *dst_g = (float *)frame->data[0] + start * linesize_g; - float *dst_b = (float *)frame->data[0] + start * linesize_b; - float *dst_r = (float *)frame->data[0] + start * linesize_r; - float *dst_a = (float *)frame->data[0] + start * linesize_a; + float *dst_b = (float *)frame->data[1] + start * linesize_b; + float *dst_r = (float *)frame->data[2] + start * linesize_r; + float *dst_a = (float *)frame->data[3] + start * linesize_a; + const int type = s->type; for (int y = start; y < end; y++) { for (int x = 0; x < width; x++) { - float factor = project(s->fx0, s->fy0, s->fx1, s->fy1, x, y, s->type); - lerp_colors32(s->color_rgbaf, s->nb_colors, s->nb_colors + s->type >= 2 ,factor, + float factor = project(s->fx0, s->fy0, s->fx1, s->fy1, x, y, type); + lerp_colors32(s->color_rgbaf, s->nb_colors, s->nb_colors + (type >= 2), factor, &dst_r[x], &dst_g[x], &dst_b[x], &dst_a[x]); } @@ -397,11 +397,23 @@ static int activate(AVFilterContext *ctx) if (!frame) return AVERROR(ENOMEM); +#if FF_API_FRAME_KEY +FF_DISABLE_DEPRECATION_WARNINGS frame->key_frame = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + frame->flags |= AV_FRAME_FLAG_KEY; +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS frame->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + frame->flags &= ~AV_FRAME_FLAG_INTERLACED; frame->pict_type = AV_PICTURE_TYPE_I; frame->sample_aspect_ratio = (AVRational) {1, 1}; frame->pts = s->pts++; + frame->duration = 1; ff_filter_execute(ctx, s->draw_slice, frame, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx))); diff --git a/libavfilter/vsrc_life.c b/libavfilter/vsrc_life.c index d40c38b953e..9f83ca5e189 100644 --- a/libavfilter/vsrc_life.c +++ b/libavfilter/vsrc_life.c @@ -404,6 +404,7 @@ static int request_frame(AVFilterLink *outlink) return AVERROR(ENOMEM); picref->sample_aspect_ratio = (AVRational) {1, 1}; picref->pts = life->pts++; + picref->duration = 1; life->draw(outlink->src, picref); evolve(outlink->src); diff --git a/libavfilter/vsrc_mandelbrot.c b/libavfilter/vsrc_mandelbrot.c index d4d7e2fa492..8b42588f1c1 100644 --- a/libavfilter/vsrc_mandelbrot.c +++ b/libavfilter/vsrc_mandelbrot.c @@ -27,12 +27,10 @@ */ #include "avfilter.h" -#include "formats.h" #include "video.h" #include "internal.h" #include "libavutil/imgutils.h" #include "libavutil/opt.h" -#include "libavutil/parseutils.h" #include #include @@ -395,6 +393,7 @@ static int request_frame(AVFilterLink *link) picref->sample_aspect_ratio = (AVRational) {1, 1}; picref->pts = s->pts++; + picref->duration = 1; draw_mandelbrot(link->src, (uint32_t*)picref->data[0], picref->linesize[0]/4, picref->pts); return ff_filter_frame(link, picref); diff --git a/libavfilter/vsrc_mptestsrc.c b/libavfilter/vsrc_mptestsrc.c index 03073dcf5a3..9c422baeea5 100644 --- a/libavfilter/vsrc_mptestsrc.c +++ b/libavfilter/vsrc_mptestsrc.c @@ -23,13 +23,10 @@ * MP test source, ported from MPlayer libmpcodecs/vf_test.c */ -#include "libavutil/avstring.h" #include "libavutil/opt.h" -#include "libavutil/parseutils.h" #include "libavutil/pixdesc.h" #include "avfilter.h" #include "internal.h" -#include "formats.h" #include "video.h" #define WIDTH 512 @@ -308,6 +305,7 @@ static int request_frame(AVFilterLink *outlink) if (!picref) return AVERROR(ENOMEM); picref->pts = test->pts++; + picref->duration = 1; // clean image for (i = 0; i < h; i++) diff --git a/libavfilter/vsrc_sierpinski.c b/libavfilter/vsrc_sierpinski.c index 898fb5ae54a..e7b2b8afec3 100644 --- a/libavfilter/vsrc_sierpinski.c +++ b/libavfilter/vsrc_sierpinski.c @@ -24,13 +24,11 @@ */ #include "avfilter.h" -#include "formats.h" #include "video.h" #include "internal.h" #include "libavutil/imgutils.h" #include "libavutil/intreadwrite.h" #include "libavutil/opt.h" -#include "libavutil/parseutils.h" #include "libavutil/lfg.h" #include "libavutil/random_seed.h" #include @@ -196,6 +194,7 @@ static int sierpinski_request_frame(AVFilterLink *link) frame->sample_aspect_ratio = (AVRational) {1, 1}; frame->pts = s->pts++; + frame->duration = 1; draw_sierpinski(link->src, frame); diff --git a/libavfilter/vsrc_testsrc.c b/libavfilter/vsrc_testsrc.c index e7d8d9ea1a0..d24481e6c45 100644 --- a/libavfilter/vsrc_testsrc.c +++ b/libavfilter/vsrc_testsrc.c @@ -88,6 +88,15 @@ typedef struct TestSourceContext { /* only used by haldclut */ int level; + + /* only used by zoneplate */ + int k0, kx, ky, kt; + int kxt, kyt, kxy; + int kx2, ky2, kt2; + int xo, yo, to, kU, kV; + int lut_precision; + uint8_t *lut; + int (*fill_slice_fn)(AVFilterContext *ctx, void *arg, int job, int nb_jobs); } TestSourceContext; #define OFFSET(x) offsetof(TestSourceContext, x) @@ -135,6 +144,7 @@ static av_cold void uninit(AVFilterContext *ctx) TestSourceContext *test = ctx->priv; av_frame_free(&test->picref); + av_freep(&test->lut); } static int config_props(AVFilterLink *outlink) @@ -183,8 +193,19 @@ static int activate(AVFilterContext *ctx) if (!frame) return AVERROR(ENOMEM); frame->pts = test->pts; + frame->duration = 1; +#if FF_API_PKT_DURATION +FF_DISABLE_DEPRECATION_WARNINGS frame->key_frame = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + frame->flags |= AV_FRAME_FLAG_KEY; +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS frame->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + frame->flags &= ~AV_FRAME_FLAG_INTERLACED; frame->pict_type = AV_PICTURE_TYPE_I; frame->sample_aspect_ratio = test->sar; if (!test->draw_once) @@ -2038,3 +2059,191 @@ const AVFilter ff_vsrc_colorchart = { }; #endif /* CONFIG_COLORCHART_FILTER */ + +#if CONFIG_ZONEPLATE_FILTER + +static const AVOption zoneplate_options[] = { + COMMON_OPTIONS + { "precision", "set LUT precision", OFFSET(lut_precision), AV_OPT_TYPE_INT, {.i64=10}, 4, 16, FLAGS }, + { "xo", "set X-axis offset", OFFSET(xo), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "yo", "set Y-axis offset", OFFSET(yo), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "to", "set T-axis offset", OFFSET(to), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "k0", "set 0-order phase", OFFSET(k0), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "kx", "set 1-order X-axis phase", OFFSET(kx), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "ky", "set 1-order Y-axis phase", OFFSET(ky), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "kt", "set 1-order T-axis phase", OFFSET(kt), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "kxt", "set X-axis*T-axis product phase", OFFSET(kxt), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "kyt", "set Y-axis*T-axis product phase", OFFSET(kyt), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "kxy", "set X-axis*Y-axis product phase", OFFSET(kxy), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "kx2", "set 2-order X-axis phase", OFFSET(kx2), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "ky2", "set 2-order Y-axis phase", OFFSET(ky2), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "kt2", "set 2-order T-axis phase", OFFSET(kt2), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "ku", "set 0-order U-color phase", OFFSET(kU), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "kv", "set 0-order V-color phase", OFFSET(kV), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(zoneplate); + +#define ZONEPLATE_SLICE(name, type) \ +static int zoneplate_fill_slice_##name(AVFilterContext *ctx, \ + void *arg, int job, \ + int nb_jobs) \ +{ \ + TestSourceContext *test = ctx->priv; \ + AVFrame *frame = arg; \ + const int w = frame->width; \ + const int h = frame->height; \ + const int kxt = test->kxt, kyt = test->kyt, kx2 = test->kx2; \ + const int t = test->pts + test->to, k0 = test->k0; \ + const int kt = test->kt, kt2 = test->kt2, ky2 = test->ky2; \ + const int ky = test->ky, kx = test->kx, kxy = test->kxy; \ + const int lut_mask = (1 << test->lut_precision) - 1; \ + const int nkt2t = kt2 * t * t, nktt = kt * t; \ + const int start = (h * job ) / nb_jobs; \ + const int end = (h * (job+1)) / nb_jobs; \ + const int ylinesize = frame->linesize[0] / sizeof(type); \ + const int ulinesize = frame->linesize[1] / sizeof(type); \ + const int vlinesize = frame->linesize[2] / sizeof(type); \ + const int xreset = -(w / 2) - test->xo; \ + const int yreset = -(h / 2) - test->yo + start; \ + const int kU = test->kU, kV = test->kV; \ + const int skxy = 0xffff / (w / 2); \ + const int skx2 = 0xffff / w; \ + const int dkxt = kxt * t; \ + type *ydst = ((type *)frame->data[0]) + start * ylinesize; \ + type *udst = ((type *)frame->data[1]) + start * ulinesize; \ + type *vdst = ((type *)frame->data[2]) + start * vlinesize; \ + const type *lut = (const type *)test->lut; \ + int akx, akxt, aky, akyt; \ + \ + aky = start * ky; \ + akyt = start * kyt * t; \ + \ + for (int j = start, y = yreset; j < end; j++, y++) { \ + const int dkxy = kxy * y * skxy; \ + const int nky2kt2 = (ky2 * y * y) / h + (nkt2t >> 1); \ + int akxy = dkxy * xreset; \ + \ + akx = 0; \ + akxt = 0; \ + aky += ky; \ + akyt += kyt * t; \ + \ + for (int i = 0, x = xreset; i < w; i++, x++) { \ + int phase = k0, uphase = kU, vphase = kV; \ + \ + akx += kx; \ + phase += akx + aky + nktt; \ + \ + akxt += dkxt; \ + akxy += dkxy; \ + phase += akxt + akyt; \ + phase += akxy >> 16; \ + phase += ((kx2 * x * x * skx2) >> 16) + nky2kt2; \ + uphase += phase; \ + vphase += phase; \ + \ + ydst[i] = lut[phase & lut_mask]; \ + udst[i] = lut[uphase & lut_mask]; \ + vdst[i] = lut[vphase & lut_mask]; \ + } \ + \ + ydst += ylinesize; \ + udst += ulinesize; \ + vdst += vlinesize; \ + } \ + \ + return 0; \ +} + +ZONEPLATE_SLICE( 8, uint8_t) +ZONEPLATE_SLICE( 9, uint16_t) +ZONEPLATE_SLICE(10, uint16_t) +ZONEPLATE_SLICE(12, uint16_t) +ZONEPLATE_SLICE(14, uint16_t) +ZONEPLATE_SLICE(16, uint16_t) + +static void zoneplate_fill_picture(AVFilterContext *ctx, AVFrame *frame) +{ + TestSourceContext *test = ctx->priv; + frame->color_range = AVCOL_RANGE_JPEG; + ff_filter_execute(ctx, test->fill_slice_fn, frame, NULL, + FFMIN(frame->height, ff_filter_get_nb_threads(ctx))); +} + +static int zoneplate_config_props(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + TestSourceContext *test = ctx->priv; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format); + const int lut_size = 1 << test->lut_precision; + const int depth = desc->comp[0].depth; + uint16_t *lut16; + uint8_t *lut8; + + if (av_image_check_size(test->w, test->h, 0, ctx) < 0) + return AVERROR(EINVAL); + + test->lut = av_calloc(lut_size, sizeof(*test->lut) * ((depth + 7) / 8)); + if (!test->lut) + return AVERROR(ENOMEM); + + lut8 = test->lut; + lut16 = (uint16_t *)test->lut; + switch (depth) { + case 8: + for (int i = 0; i < lut_size; i++) + lut8[i] = lrintf(255.f * (0.5f + 0.5f * sinf((2.f * M_PI * i) / lut_size))); + break; + default: + for (int i = 0; i < lut_size; i++) + lut16[i] = lrintf(((1 << depth) - 1) * (0.5f + 0.5f * sinf((2.f * M_PI * i) / lut_size))); + break; + } + + test->draw_once = 0; + test->fill_picture_fn = zoneplate_fill_picture; + + switch (depth) { + case 8: test->fill_slice_fn = zoneplate_fill_slice_8; break; + case 9: test->fill_slice_fn = zoneplate_fill_slice_9; break; + case 10: test->fill_slice_fn = zoneplate_fill_slice_10; break; + case 12: test->fill_slice_fn = zoneplate_fill_slice_12; break; + case 14: test->fill_slice_fn = zoneplate_fill_slice_14; break; + case 16: test->fill_slice_fn = zoneplate_fill_slice_16; break; + } + return config_props(outlink); +} + +static const enum AVPixelFormat zoneplate_pix_fmts[] = { + AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV444P9, + AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV444P12, + AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV444P16, + AV_PIX_FMT_NONE, +}; + +static const AVFilterPad avfilter_vsrc_zoneplate_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = zoneplate_config_props, + }, +}; + +const AVFilter ff_vsrc_zoneplate = { + .name = "zoneplate", + .description = NULL_IF_CONFIG_SMALL("Generate zone-plate."), + .priv_size = sizeof(TestSourceContext), + .priv_class = &zoneplate_class, + .init = init, + .uninit = uninit, + .activate = activate, + .inputs = NULL, + FILTER_OUTPUTS(avfilter_vsrc_zoneplate_outputs), + FILTER_PIXFMTS_ARRAY(zoneplate_pix_fmts), + .flags = AVFILTER_FLAG_SLICE_THREADS, + .process_command = ff_filter_process_command, +}; + +#endif /* CONFIG_ZONEPLATE_FILTER */ diff --git a/libavfilter/vsrc_testsrc_vulkan.c b/libavfilter/vsrc_testsrc_vulkan.c new file mode 100644 index 00000000000..8485fa498d5 --- /dev/null +++ b/libavfilter/vsrc_testsrc_vulkan.c @@ -0,0 +1,378 @@ +/* + * Copyright (c) Lynne + * + * 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 "libavutil/random_seed.h" +#include "libavutil/csp.h" +#include "libavutil/opt.h" +#include "vulkan_filter.h" +#include "vulkan_spirv.h" +#include "internal.h" +#include "filters.h" +#include "colorspace.h" +#include "video.h" + +enum TestSrcVulkanMode { + TESTSRC_COLOR, +}; + +typedef struct TestSrcVulkanPushData { + float color_comp[4]; +} TestSrcVulkanPushData; + +typedef struct TestSrcVulkanContext { + FFVulkanContext vkctx; + + int initialized; + FFVulkanPipeline pl; + FFVkExecPool e; + FFVkQueueFamilyCtx qf; + FFVkSPIRVShader shd; + + /* Only used by color_vulkan */ + uint8_t color_rgba[4]; + + TestSrcVulkanPushData opts; + + int w, h; + int pw, ph; + char *out_format_string; + enum AVColorRange out_range; + unsigned int nb_frame; + AVRational time_base, frame_rate; + int64_t pts; + int64_t duration; ///< duration expressed in microseconds + AVRational sar; ///< sample aspect ratio + int draw_once; ///< draw only the first frame, always put out the same picture + int draw_once_reset; ///< draw only the first frame or in case of reset + AVFrame *picref; ///< cached reference containing the painted picture +} TestSrcVulkanContext; + +static av_cold int init_filter(AVFilterContext *ctx, enum TestSrcVulkanMode mode) +{ + int err; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; + TestSrcVulkanContext *s = ctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); + FFVkSPIRVShader *shd = &s->shd; + FFVkSPIRVCompiler *spv; + FFVulkanDescriptorSetBinding *desc_set; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(s->vkctx.output_format); + + spv = ff_vk_spirv_init(); + if (!spv) { + av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); + return AVERROR_EXTERNAL; + } + + ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT); + RET(ff_vk_exec_pool_init(vkctx, &s->qf, &s->e, s->qf.nb_queues*4, 0, 0, 0, NULL)); + RET(ff_vk_shader_init(&s->pl, &s->shd, "testsrc_compute", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); + + ff_vk_shader_set_compute_sizes(&s->shd, 32, 32, 1); + + GLSLC(0, layout(push_constant, std430) uniform pushConstants { ); + GLSLC(1, vec4 color_comp; ); + GLSLC(0, }; ); + GLSLC(0, ); + + ff_vk_add_push_constant(&s->pl, 0, sizeof(s->opts), + VK_SHADER_STAGE_COMPUTE_BIT); + + desc_set = (FFVulkanDescriptorSetBinding []) { + { + .name = "output_img", + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format), + .mem_quali = "writeonly", + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + }, + }; + + RET(ff_vk_pipeline_descriptor_set_add(vkctx, &s->pl, shd, desc_set, 1, 0, 0)); + + GLSLC(0, void main() ); + GLSLC(0, { ); + GLSLC(1, ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); + if (mode == TESTSRC_COLOR) { + double rgb2yuv[3][3]; + double rgbad[4]; + double yuvad[4]; + + enum AVColorSpace csp; + const AVLumaCoefficients *luma = NULL; + + s->draw_once = 1; + + if (desc->flags & AV_PIX_FMT_FLAG_RGB) + csp = AVCOL_SPC_RGB; + else + csp = AVCOL_SPC_SMPTE170M; + + if (!(desc->flags & AV_PIX_FMT_FLAG_RGB) && !(luma = av_csp_luma_coeffs_from_avcsp(csp))) + return AVERROR(EINVAL); + else if (!(desc->flags & AV_PIX_FMT_FLAG_RGB)) + ff_fill_rgb2yuv_table(luma, rgb2yuv); + + for (int i = 0; i < 4; i++) + rgbad[i] = s->color_rgba[i] / 255.0; + + if (!(desc->flags & AV_PIX_FMT_FLAG_RGB)) + ff_matrix_mul_3x3_vec(yuvad, rgbad, rgb2yuv); + else + memcpy(yuvad, rgbad, sizeof(rgbad)); + + yuvad[3] = rgbad[3]; + + if (!(desc->flags & AV_PIX_FMT_FLAG_RGB)) { + for (int i = 0; i < 3; i++) { + int chroma = (!(desc->flags & AV_PIX_FMT_FLAG_RGB) && i > 0); + if (s->out_range == AVCOL_RANGE_MPEG) { + yuvad[i] *= (chroma ? 224.0 : 219.0) / 255.0; + yuvad[i] += (chroma ? 128.0 : 16.0) / 255.0; + } else if (chroma) { + yuvad[i] += 0.5; + } + } + } + + /* Ensure we place the alpha appropriately for gray formats */ + if (desc->nb_components <= 2) + yuvad[1] = yuvad[3]; + + for (int i = 0; i < 4; i++) + s->opts.color_comp[i] = yuvad[i]; + + GLSLC(1, vec4 r; ); + GLSLC(0, ); + for (int i = 0, c_off = 0; i < planes; i++) { + for (int c = 0; c < desc->nb_components; c++) { + if (desc->comp[c].plane == i) { + int off = desc->comp[c].offset / (FFALIGN(desc->comp[c].depth, 8)/8); + GLSLF(1, r[%i] = color_comp[%i]; ,off, c_off++); + } + } + GLSLF(1, imageStore(output_img[%i], pos, r); ,i); + GLSLC(0, ); + } + } + GLSLC(0, } ); + + RET(spv->compile_shader(spv, ctx, shd, &spv_data, &spv_len, "main", + &spv_opaque)); + RET(ff_vk_shader_create(vkctx, shd, spv_data, spv_len, "main")); + + RET(ff_vk_init_compute_pipeline(vkctx, &s->pl, shd)); + RET(ff_vk_exec_pipeline_register(vkctx, &s->e, &s->pl)); + + s->initialized = 1; + + return 0; + +fail: + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); + if (spv) + spv->uninit(&spv); + + return err; +} + +static int testsrc_vulkan_activate(AVFilterContext *ctx) +{ + int err; + AVFilterLink *outlink = ctx->outputs[0]; + TestSrcVulkanContext *s = ctx->priv; + AVFrame *frame; + + if (!s->initialized) { + enum TestSrcVulkanMode mode = TESTSRC_COLOR; + err = init_filter(ctx, mode); + if (err < 0) + return err; + } + + if (!ff_outlink_frame_wanted(outlink)) + return FFERROR_NOT_READY; + if (s->duration >= 0 && + av_rescale_q(s->pts, s->time_base, AV_TIME_BASE_Q) >= s->duration) { + ff_outlink_set_status(outlink, AVERROR_EOF, s->pts); + return 0; + } + + if (s->draw_once) { + if (s->draw_once_reset) { + av_frame_free(&s->picref); + s->draw_once_reset = 0; + } + if (!s->picref) { + s->picref = ff_get_video_buffer(outlink, s->w, s->h); + if (!s->picref) + return AVERROR(ENOMEM); + + err = ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->pl, s->picref, NULL, + NULL, &s->opts, sizeof(s->opts)); + if (err < 0) + return err; + } + frame = av_frame_clone(s->picref); + } else { + frame = ff_get_video_buffer(outlink, s->w, s->h); + } + + if (!frame) + return AVERROR(ENOMEM); + + frame->pts = s->pts; + frame->duration = 1; + frame->flags = AV_FRAME_FLAG_KEY; + frame->pict_type = AV_PICTURE_TYPE_I; + frame->sample_aspect_ratio = s->sar; + if (!s->draw_once) { + err = ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->pl, frame, NULL, + NULL, &s->opts, sizeof(s->opts)); + if (err < 0) { + av_frame_free(&frame); + return err; + } + } + + s->pts++; + s->nb_frame++; + + return ff_filter_frame(outlink, frame); +} + +static int testsrc_vulkan_config_props(AVFilterLink *outlink) +{ + int err; + TestSrcVulkanContext *s = outlink->src->priv; + FFVulkanContext *vkctx = &s->vkctx; + + if (!s->out_format_string) { + vkctx->output_format = AV_PIX_FMT_YUV444P; + } else { + vkctx->output_format = av_get_pix_fmt(s->out_format_string); + if (vkctx->output_format == AV_PIX_FMT_NONE) { + av_log(vkctx, AV_LOG_ERROR, "Invalid output format.\n"); + return AVERROR(EINVAL); + } + } + + err = ff_vk_filter_init_context(outlink->src, vkctx, NULL, + s->w, s->h, vkctx->output_format); + if (err < 0) + return err; + + outlink->hw_frames_ctx = av_buffer_ref(vkctx->frames_ref); + if (!outlink->hw_frames_ctx) + return AVERROR(ENOMEM); + + s->time_base = av_inv_q(s->frame_rate); + s->nb_frame = 0; + s->pts = 0; + + s->vkctx.output_width = s->w; + s->vkctx.output_height = s->h; + outlink->w = s->w; + outlink->h = s->h; + outlink->sample_aspect_ratio = s->sar; + outlink->frame_rate = s->frame_rate; + outlink->time_base = s->time_base; + + return 0; +} + +static void testsrc_vulkan_uninit(AVFilterContext *avctx) +{ + TestSrcVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + + av_frame_free(&s->picref); + + ff_vk_exec_pool_free(vkctx, &s->e); + ff_vk_pipeline_free(vkctx, &s->pl); + ff_vk_shader_free(vkctx, &s->shd); + + ff_vk_uninit(&s->vkctx); + + s->initialized = 0; +} + +#define OFFSET(x) offsetof(TestSrcVulkanContext, x) +#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) + +#define COMMON_OPTS \ + { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, { .str = "1920x1080" }, 0, 0, FLAGS }, \ + { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, { .str = "1920x1080" }, 0, 0, FLAGS }, \ + \ + { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, { .str = "60" }, 0, INT_MAX, FLAGS }, \ + { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, { .str = "60" }, 0, INT_MAX, FLAGS }, \ + \ + { "duration", "set video duration", OFFSET(duration), AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT64_MAX, FLAGS }, \ + { "d", "set video duration", OFFSET(duration), AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT64_MAX, FLAGS }, \ + \ + { "sar", "set video sample aspect ratio", OFFSET(sar), AV_OPT_TYPE_RATIONAL, { .dbl = 1 }, 0, INT_MAX, FLAGS }, \ + \ + { "format", "Output video format (software format of hardware frames)", OFFSET(out_format_string), AV_OPT_TYPE_STRING, .flags = FLAGS }, + +static const AVOption color_vulkan_options[] = { + { "color", "set color", OFFSET(color_rgba), AV_OPT_TYPE_COLOR, {.str = "black"}, 0, 0, FLAGS }, + { "c", "set color", OFFSET(color_rgba), AV_OPT_TYPE_COLOR, {.str = "black"}, 0, 0, FLAGS }, + COMMON_OPTS + { "out_range", "Output colour range (from 0 to 2) (default 0)", OFFSET(out_range), AV_OPT_TYPE_INT, {.i64 = AVCOL_RANGE_UNSPECIFIED}, AVCOL_RANGE_UNSPECIFIED, AVCOL_RANGE_JPEG, .flags = FLAGS, "range" }, + { "full", "Full range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, "range" }, + { "limited", "Limited range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, "range" }, + { "jpeg", "Full range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, "range" }, + { "mpeg", "Limited range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, "range" }, + { "tv", "Limited range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, "range" }, + { "pc", "Full range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, "range" }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(color_vulkan); + +static const AVFilterPad testsrc_vulkan_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = testsrc_vulkan_config_props, + }, +}; + +const AVFilter ff_vsrc_color_vulkan = { + .name = "color_vulkan", + .description = NULL_IF_CONFIG_SMALL("Generate a constant color (Vulkan)"), + .priv_size = sizeof(TestSrcVulkanContext), + .init = &ff_vk_filter_init, + .uninit = &testsrc_vulkan_uninit, + .inputs = NULL, + .flags = AVFILTER_FLAG_HWDEVICE, + .activate = testsrc_vulkan_activate, + FILTER_OUTPUTS(testsrc_vulkan_outputs), + FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), + .priv_class = &color_vulkan_class, + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, +}; diff --git a/libavfilter/vulkan/.gitignore b/libavfilter/vulkan/.gitignore new file mode 100644 index 00000000000..064a8d8ef55 --- /dev/null +++ b/libavfilter/vulkan/.gitignore @@ -0,0 +1 @@ +*.c diff --git a/libavfilter/vulkan/prefix_sum.comp b/libavfilter/vulkan/prefix_sum.comp new file mode 100644 index 00000000000..9147cd82fbd --- /dev/null +++ b/libavfilter/vulkan/prefix_sum.comp @@ -0,0 +1,151 @@ +#extension GL_EXT_buffer_reference : require +#extension GL_EXT_buffer_reference2 : require + +#define ACQUIRE gl_StorageSemanticsBuffer, gl_SemanticsAcquire +#define RELEASE gl_StorageSemanticsBuffer, gl_SemanticsRelease + +// These correspond to X, A, P respectively in the prefix sum paper. +#define FLAG_NOT_READY 0u +#define FLAG_AGGREGATE_READY 1u +#define FLAG_PREFIX_READY 2u + +layout(buffer_reference, buffer_reference_align = T_ALIGN) nonprivate buffer StateData { + DTYPE aggregate; + DTYPE prefix; + uint flag; +}; + +shared DTYPE sh_scratch[WG_SIZE]; +shared DTYPE sh_prefix; +shared uint sh_part_ix; +shared uint sh_flag; + +void prefix_sum(DataBuffer dst, uint dst_stride, DataBuffer src, uint src_stride) +{ + DTYPE local[N_ROWS]; + // Determine partition to process by atomic counter (described in Section 4.4 of prefix sum paper). + if (gl_GlobalInvocationID.x == 0) + sh_part_ix = gl_WorkGroupID.x; +// sh_part_ix = atomicAdd(part_counter, 1); + + barrier(); + uint part_ix = sh_part_ix; + + uint ix = part_ix * PARTITION_SIZE + gl_LocalInvocationID.x * N_ROWS; + + // TODO: gate buffer read? (evaluate whether shader check or CPU-side padding is better) + local[0] = src.v[ix*src_stride]; + for (uint i = 1; i < N_ROWS; i++) + local[i] = local[i - 1] + src.v[(ix + i)*src_stride]; + + DTYPE agg = local[N_ROWS - 1]; + sh_scratch[gl_LocalInvocationID.x] = agg; + for (uint i = 0; i < LG_WG_SIZE; i++) { + barrier(); + if (gl_LocalInvocationID.x >= (1u << i)) + agg += sh_scratch[gl_LocalInvocationID.x - (1u << i)]; + barrier(); + + sh_scratch[gl_LocalInvocationID.x] = agg; + } + + // Publish aggregate for this partition + if (gl_LocalInvocationID.x == WG_SIZE - 1) { + state[part_ix].aggregate = agg; + if (part_ix == 0) + state[0].prefix = agg; + } + + // Write flag with release semantics + if (gl_LocalInvocationID.x == WG_SIZE - 1) { + uint flag = part_ix == 0 ? FLAG_PREFIX_READY : FLAG_AGGREGATE_READY; + atomicStore(state[part_ix].flag, flag, gl_ScopeDevice, RELEASE); + } + + DTYPE exclusive = DTYPE(0); + if (part_ix != 0) { + // step 4 of paper: decoupled lookback + uint look_back_ix = part_ix - 1; + + DTYPE their_agg; + uint their_ix = 0; + while (true) { + // Read flag with acquire semantics. + if (gl_LocalInvocationID.x == WG_SIZE - 1) + sh_flag = atomicLoad(state[look_back_ix].flag, gl_ScopeDevice, ACQUIRE); + + // The flag load is done only in the last thread. However, because the + // translation of memoryBarrierBuffer to Metal requires uniform control + // flow, we broadcast it to all threads. + barrier(); + + uint flag = sh_flag; + barrier(); + + if (flag == FLAG_PREFIX_READY) { + if (gl_LocalInvocationID.x == WG_SIZE - 1) { + DTYPE their_prefix = state[look_back_ix].prefix; + exclusive = their_prefix + exclusive; + } + break; + } else if (flag == FLAG_AGGREGATE_READY) { + if (gl_LocalInvocationID.x == WG_SIZE - 1) { + their_agg = state[look_back_ix].aggregate; + exclusive = their_agg + exclusive; + } + look_back_ix--; + their_ix = 0; + continue; + } // else spins + + if (gl_LocalInvocationID.x == WG_SIZE - 1) { + // Unfortunately there's no guarantee of forward progress of other + // workgroups, so compute a bit of the aggregate before trying again. + // In the worst case, spinning stops when the aggregate is complete. + DTYPE m = src.v[(look_back_ix * PARTITION_SIZE + their_ix)*src_stride]; + if (their_ix == 0) + their_agg = m; + else + their_agg += m; + + their_ix++; + if (their_ix == PARTITION_SIZE) { + exclusive = their_agg + exclusive; + if (look_back_ix == 0) { + sh_flag = FLAG_PREFIX_READY; + } else { + look_back_ix--; + their_ix = 0; + } + } + } + barrier(); + flag = sh_flag; + barrier(); + if (flag == FLAG_PREFIX_READY) + break; + } + + // step 5 of paper: compute inclusive prefix + if (gl_LocalInvocationID.x == WG_SIZE - 1) { + DTYPE inclusive_prefix = exclusive + agg; + sh_prefix = exclusive; + state[part_ix].prefix = inclusive_prefix; + } + + if (gl_LocalInvocationID.x == WG_SIZE - 1) + atomicStore(state[part_ix].flag, FLAG_PREFIX_READY, gl_ScopeDevice, RELEASE); + } + + barrier(); + if (part_ix != 0) + exclusive = sh_prefix; + + DTYPE row = exclusive; + if (gl_LocalInvocationID.x > 0) + row += sh_scratch[gl_LocalInvocationID.x - 1]; + + // note - may overwrite + for (uint i = 0; i < N_ROWS; i++) + dst.v[(ix + i)*dst_stride] = row + local[i]; +} diff --git a/libavfilter/vulkan_filter.c b/libavfilter/vulkan_filter.c index e22541bd23a..b4d8f952b53 100644 --- a/libavfilter/vulkan_filter.c +++ b/libavfilter/vulkan_filter.c @@ -1,4 +1,6 @@ /* + * Copyright (c) Lynne + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -18,107 +20,186 @@ #include "vulkan_filter.h" -static int vulkan_filter_set_device(AVFilterContext *avctx, - AVBufferRef *device) +int ff_vk_filter_init_context(AVFilterContext *avctx, FFVulkanContext *s, + AVBufferRef *frames_ref, + int width, int height, enum AVPixelFormat sw_format) { - FFVulkanContext *s = avctx->priv; + int err; + AVHWFramesContext *frames_ctx; + AVHWDeviceContext *device_ctx; + AVVulkanFramesContext *vk_frames; + AVVulkanDeviceContext *vk_dev; + AVBufferRef *device_ref = avctx->hw_device_ctx; + + /* Check if context is reusable as-is */ + if (frames_ref) { + int no_storage = 0; + FFVulkanFunctions *vk; + const VkFormat *sub = av_vkfmt_from_pixfmt(sw_format); + + frames_ctx = (AVHWFramesContext *)frames_ref->data; + device_ctx = (AVHWDeviceContext *)frames_ctx->device_ref->data; + vk_frames = frames_ctx->hwctx; + vk_dev = device_ctx->hwctx; + + /* Basic format validation */ + if (width != frames_ctx->width || + height != frames_ctx->height || + sw_format != frames_ctx->sw_format || + (vk_frames->tiling != VK_IMAGE_TILING_LINEAR && + vk_frames->tiling != VK_IMAGE_TILING_OPTIMAL) || + !(vk_frames->usage & VK_IMAGE_USAGE_SAMPLED_BIT)) { + goto skip; + } - av_buffer_unref(&s->device_ref); + if (vk_frames->usage & VK_IMAGE_USAGE_STORAGE_BIT) + goto accept; - s->device_ref = av_buffer_ref(device); - if (!s->device_ref) - return AVERROR(ENOMEM); + s->extensions = ff_vk_extensions_to_mask(vk_dev->enabled_dev_extensions, + vk_dev->nb_enabled_dev_extensions); + err = ff_vk_load_functions(device_ctx, &s->vkfn, s->extensions, 1, 1); + if (err < 0) + return err; + vk = &s->vkfn; + + /* Check if the subformats can do storage */ + for (int i = 0; sub[i] != VK_FORMAT_UNDEFINED; i++) { + VkFormatProperties2 prop = { + .sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2, + }; + vk->GetPhysicalDeviceFormatProperties2(vk_dev->phys_dev, sub[i], + &prop); + + if (vk_frames->tiling == VK_IMAGE_TILING_LINEAR) { + no_storage |= !(prop.formatProperties.linearTilingFeatures & + VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT); + } else { + no_storage |= !(prop.formatProperties.optimalTilingFeatures & + VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT); + } + } - s->device = (AVHWDeviceContext*)s->device_ref->data; - s->hwctx = s->device->hwctx; + /* Check if it's usable */ + if (no_storage) { +skip: + device_ref = frames_ctx->device_ref; + frames_ref = NULL; + } else { +accept: + frames_ref = av_buffer_ref(frames_ref); + if (!frames_ref) + return AVERROR(ENOMEM); + } + } - return 0; -} + if (!frames_ref) { + if (!device_ref) { + av_log(avctx, AV_LOG_ERROR, + "Vulkan filtering requires a device context!\n"); + return AVERROR(EINVAL); + } -static int vulkan_filter_set_frames(AVFilterContext *avctx, - AVBufferRef *frames) -{ - FFVulkanContext *s = avctx->priv; + frames_ref = av_hwframe_ctx_alloc(device_ref); - av_buffer_unref(&s->frames_ref); + frames_ctx = (AVHWFramesContext *)frames_ref->data; + frames_ctx->format = AV_PIX_FMT_VULKAN; + frames_ctx->sw_format = sw_format; + frames_ctx->width = width; + frames_ctx->height = height; - s->frames_ref = av_buffer_ref(frames); - if (!s->frames_ref) - return AVERROR(ENOMEM); + vk_frames = frames_ctx->hwctx; + vk_frames->tiling = VK_IMAGE_TILING_OPTIMAL; + vk_frames->usage = VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT; - return 0; + err = av_hwframe_ctx_init(frames_ref); + if (err < 0) { + av_buffer_unref(&frames_ref); + return err; + } + + device_ctx = (AVHWDeviceContext *)frames_ctx->device_ref->data; + vk_dev = device_ctx->hwctx; + } + + s->extensions = ff_vk_extensions_to_mask(vk_dev->enabled_dev_extensions, + vk_dev->nb_enabled_dev_extensions); + + /** + * libplacebo does not use descriptor buffers. + */ + if (!(s->extensions & FF_VK_EXT_DESCRIPTOR_BUFFER) && + strcmp(avctx->filter->name, "libplacebo")) { + av_log(avctx, AV_LOG_ERROR, "Vulkan filtering requires that " + "the %s extension is supported!\n", + VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME); + av_buffer_unref(&frames_ref); + return AVERROR(EINVAL); + } + + err = ff_vk_load_functions(device_ctx, &s->vkfn, s->extensions, 1, 1); + if (err < 0) { + av_buffer_unref(&frames_ref); + return err; + } + + s->frames_ref = frames_ref; + s->frames = frames_ctx; + s->hwfc = vk_frames; + s->device = device_ctx; + s->hwctx = device_ctx->hwctx; + + err = ff_vk_load_props(s); + if (err < 0) + av_buffer_unref(&s->frames_ref); + + return err; } int ff_vk_filter_config_input(AVFilterLink *inlink) { - int err; - AVFilterContext *avctx = inlink->dst; - FFVulkanContext *s = avctx->priv; - FFVulkanFunctions *vk = &s->vkfn; AVHWFramesContext *input_frames; + AVFilterContext *avctx = inlink->dst; + FFVulkanContext *s = inlink->dst->priv; if (!inlink->hw_frames_ctx) { - av_log(avctx, AV_LOG_ERROR, "Vulkan filtering requires a " + av_log(inlink->dst, AV_LOG_ERROR, "Vulkan filtering requires a " "hardware frames context on the input.\n"); return AVERROR(EINVAL); } - /* Extract the device and default output format from the first input. */ - if (avctx->inputs[0] != inlink) - return 0; - input_frames = (AVHWFramesContext *)inlink->hw_frames_ctx->data; if (input_frames->format != AV_PIX_FMT_VULKAN) return AVERROR(EINVAL); - err = vulkan_filter_set_device(avctx, input_frames->device_ref); - if (err < 0) - return err; - err = vulkan_filter_set_frames(avctx, inlink->hw_frames_ctx); - if (err < 0) - return err; - - s->extensions = ff_vk_extensions_to_mask(s->hwctx->enabled_dev_extensions, - s->hwctx->nb_enabled_dev_extensions); - - err = ff_vk_load_functions(s->device, &s->vkfn, s->extensions, 1, 1); - if (err < 0) - return err; + /* Extract the device and default output format from the first input. */ + if (avctx->inputs[0] != inlink) + return 0; - vk->GetPhysicalDeviceProperties(s->hwctx->phys_dev, &s->props); - vk->GetPhysicalDeviceMemoryProperties(s->hwctx->phys_dev, &s->mprops); + /* Save the ref, without reffing it */ + s->input_frames_ref = inlink->hw_frames_ctx; - /* Default output parameters match input parameters. */ - s->input_format = input_frames->sw_format; - if (s->output_format == AV_PIX_FMT_NONE) - s->output_format = input_frames->sw_format; - if (!s->output_width) - s->output_width = inlink->w; - if (!s->output_height) - s->output_height = inlink->h; + /* Defaults */ + s->output_format = input_frames->sw_format; + s->output_width = inlink->w; + s->output_height = inlink->h; return 0; } -int ff_vk_filter_config_output_inplace(AVFilterLink *outlink) +int ff_vk_filter_config_output(AVFilterLink *outlink) { int err; - AVFilterContext *avctx = outlink->src; - FFVulkanContext *s = avctx->priv; + FFVulkanContext *s = outlink->src->priv; av_buffer_unref(&outlink->hw_frames_ctx); - if (!s->device_ref) { - if (!avctx->hw_device_ctx) { - av_log(avctx, AV_LOG_ERROR, "Vulkan filtering requires a " - "Vulkan device.\n"); - return AVERROR(EINVAL); - } - - err = vulkan_filter_set_device(avctx, avctx->hw_device_ctx); - if (err < 0) - return err; - } + err = ff_vk_filter_init_context(outlink->src, s, s->input_frames_ref, + s->output_width, s->output_height, + s->output_format); + if (err < 0) + return err; outlink->hw_frames_ctx = av_buffer_ref(s->frames_ref); if (!outlink->hw_frames_ctx) @@ -127,65 +208,246 @@ int ff_vk_filter_config_output_inplace(AVFilterLink *outlink) outlink->w = s->output_width; outlink->h = s->output_height; - return 0; + return err; } -int ff_vk_filter_config_output(AVFilterLink *outlink) +int ff_vk_filter_init(AVFilterContext *avctx) { - int err; - AVFilterContext *avctx = outlink->src; FFVulkanContext *s = avctx->priv; - AVBufferRef *output_frames_ref; - AVHWFramesContext *output_frames; - - av_buffer_unref(&outlink->hw_frames_ctx); - if (!s->device_ref) { - if (!avctx->hw_device_ctx) { - av_log(avctx, AV_LOG_ERROR, "Vulkan filtering requires a " - "Vulkan device.\n"); - return AVERROR(EINVAL); - } + s->output_format = AV_PIX_FMT_NONE; - err = vulkan_filter_set_device(avctx, avctx->hw_device_ctx); - if (err < 0) - return err; - } + return 0; +} - output_frames_ref = av_hwframe_ctx_alloc(s->device_ref); - if (!output_frames_ref) { - err = AVERROR(ENOMEM); - goto fail; +int ff_vk_filter_process_simple(FFVulkanContext *vkctx, FFVkExecPool *e, + FFVulkanPipeline *pl, AVFrame *out_f, AVFrame *in_f, + VkSampler sampler, void *push_src, size_t push_size) +{ + int err = 0; + FFVulkanFunctions *vk = &vkctx->vkfn; + VkImageView in_views[AV_NUM_DATA_POINTERS]; + VkImageView out_views[AV_NUM_DATA_POINTERS]; + VkImageMemoryBarrier2 img_bar[37]; + int nb_img_bar = 0; + + /* Update descriptors and init the exec context */ + FFVkExecContext *exec = ff_vk_exec_get(e); + ff_vk_exec_start(vkctx, exec); + + ff_vk_exec_bind_pipeline(vkctx, exec, pl); + + if (push_src) + ff_vk_update_push_exec(vkctx, exec, pl, VK_SHADER_STAGE_COMPUTE_BIT, + 0, push_size, push_src); + + if (in_f) { + RET(ff_vk_exec_add_dep_frame(vkctx, exec, in_f, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT)); + RET(ff_vk_create_imageviews(vkctx, exec, in_views, in_f)); + ff_vk_update_descriptor_img_array(vkctx, pl, exec, in_f, in_views, 0, 0, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + sampler); + ff_vk_frame_barrier(vkctx, exec, in_f, img_bar, &nb_img_bar, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_SHADER_READ_BIT, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_QUEUE_FAMILY_IGNORED); } - output_frames = (AVHWFramesContext*)output_frames_ref->data; - output_frames->format = AV_PIX_FMT_VULKAN; - output_frames->sw_format = s->output_format; - output_frames->width = s->output_width; - output_frames->height = s->output_height; + RET(ff_vk_exec_add_dep_frame(vkctx, exec, out_f, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT)); + RET(ff_vk_create_imageviews(vkctx, exec, out_views, out_f)); + ff_vk_update_descriptor_img_array(vkctx, pl, exec, out_f, out_views, 0, !!in_f, + VK_IMAGE_LAYOUT_GENERAL, + VK_NULL_HANDLE); + ff_vk_frame_barrier(vkctx, exec, out_f, img_bar, &nb_img_bar, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_SHADER_WRITE_BIT, + VK_IMAGE_LAYOUT_GENERAL, + VK_QUEUE_FAMILY_IGNORED); + + vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pImageMemoryBarriers = img_bar, + .imageMemoryBarrierCount = nb_img_bar, + }); + + vk->CmdDispatch(exec->buf, + FFALIGN(vkctx->output_width, pl->wg_size[0])/pl->wg_size[0], + FFALIGN(vkctx->output_height, pl->wg_size[1])/pl->wg_size[1], + pl->wg_size[2]); + + return ff_vk_exec_submit(vkctx, exec); +fail: + ff_vk_exec_discard_deps(vkctx, exec); + return err; +} - err = av_hwframe_ctx_init(output_frames_ref); - if (err < 0) { - av_log(avctx, AV_LOG_ERROR, "Failed to initialise output " - "frames: %d.\n", err); - goto fail; +int ff_vk_filter_process_2pass(FFVulkanContext *vkctx, FFVkExecPool *e, + FFVulkanPipeline *pls[2], + AVFrame *out, AVFrame *tmp, AVFrame *in, + VkSampler sampler, void *push_src, size_t push_size) +{ + int err = 0; + FFVulkanFunctions *vk = &vkctx->vkfn; + VkImageView in_views[AV_NUM_DATA_POINTERS]; + VkImageView tmp_views[AV_NUM_DATA_POINTERS]; + VkImageView out_views[AV_NUM_DATA_POINTERS]; + VkImageMemoryBarrier2 img_bar[37]; + int nb_img_bar = 0; + + /* Update descriptors and init the exec context */ + FFVkExecContext *exec = ff_vk_exec_get(e); + ff_vk_exec_start(vkctx, exec); + + RET(ff_vk_exec_add_dep_frame(vkctx, exec, in, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT)); + RET(ff_vk_exec_add_dep_frame(vkctx, exec, tmp, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT)); + RET(ff_vk_exec_add_dep_frame(vkctx, exec, out, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT)); + + RET(ff_vk_create_imageviews(vkctx, exec, in_views, in)); + RET(ff_vk_create_imageviews(vkctx, exec, tmp_views, tmp)); + RET(ff_vk_create_imageviews(vkctx, exec, out_views, out)); + + ff_vk_frame_barrier(vkctx, exec, in, img_bar, &nb_img_bar, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_SHADER_READ_BIT, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_QUEUE_FAMILY_IGNORED); + ff_vk_frame_barrier(vkctx, exec, tmp, img_bar, &nb_img_bar, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + VK_IMAGE_LAYOUT_GENERAL, + VK_QUEUE_FAMILY_IGNORED); + ff_vk_frame_barrier(vkctx, exec, out, img_bar, &nb_img_bar, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_SHADER_WRITE_BIT, + VK_IMAGE_LAYOUT_GENERAL, + VK_QUEUE_FAMILY_IGNORED); + + vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pImageMemoryBarriers = img_bar, + .imageMemoryBarrierCount = nb_img_bar, + }); + + for (int i = 0; i < 2; i++) { + FFVulkanPipeline *pl = pls[i]; + AVFrame *src_f = !i ? in : tmp; + AVFrame *dst_f = !i ? tmp : out; + VkImageView *src_views = !i ? in_views : tmp_views; + VkImageView *dst_views = !i ? tmp_views : out_views; + + ff_vk_exec_bind_pipeline(vkctx, exec, pl); + + if (push_src) + ff_vk_update_push_exec(vkctx, exec, pl, VK_SHADER_STAGE_COMPUTE_BIT, + 0, push_size, push_src); + + ff_vk_update_descriptor_img_array(vkctx, pl, exec, src_f, src_views, 0, 0, + !i ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : + VK_IMAGE_LAYOUT_GENERAL, + sampler); + ff_vk_update_descriptor_img_array(vkctx, pl, exec, dst_f, dst_views, 0, 1, + VK_IMAGE_LAYOUT_GENERAL, + VK_NULL_HANDLE); + + vk->CmdDispatch(exec->buf, + FFALIGN(vkctx->output_width, pl->wg_size[0])/pl->wg_size[0], + FFALIGN(vkctx->output_height, pl->wg_size[1])/pl->wg_size[1], + pl->wg_size[2]); } - outlink->hw_frames_ctx = output_frames_ref; - outlink->w = s->output_width; - outlink->h = s->output_height; - - return 0; + return ff_vk_exec_submit(vkctx, exec); fail: - av_buffer_unref(&output_frames_ref); + ff_vk_exec_discard_deps(vkctx, exec); return err; } -int ff_vk_filter_init(AVFilterContext *avctx) +int ff_vk_filter_process_Nin(FFVulkanContext *vkctx, FFVkExecPool *e, + FFVulkanPipeline *pl, + AVFrame *out, AVFrame *in[], int nb_in, + VkSampler sampler, void *push_src, size_t push_size) { - FFVulkanContext *s = avctx->priv; - - s->output_format = AV_PIX_FMT_NONE; + int err = 0; + FFVulkanFunctions *vk = &vkctx->vkfn; + VkImageView in_views[16][AV_NUM_DATA_POINTERS]; + VkImageView out_views[AV_NUM_DATA_POINTERS]; + VkImageMemoryBarrier2 img_bar[128]; + int nb_img_bar = 0; + + /* Update descriptors and init the exec context */ + FFVkExecContext *exec = ff_vk_exec_get(e); + ff_vk_exec_start(vkctx, exec); + + /* Inputs */ + for (int i = 0; i < nb_in; i++) { + RET(ff_vk_exec_add_dep_frame(vkctx, exec, in[i], + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT)); + RET(ff_vk_create_imageviews(vkctx, exec, in_views[i], in[i])); + + ff_vk_frame_barrier(vkctx, exec, in[i], img_bar, &nb_img_bar, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_SHADER_READ_BIT, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_QUEUE_FAMILY_IGNORED); + } - return 0; + /* Output */ + RET(ff_vk_exec_add_dep_frame(vkctx, exec, out, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT)); + RET(ff_vk_create_imageviews(vkctx, exec, out_views, out)); + ff_vk_frame_barrier(vkctx, exec, out, img_bar, &nb_img_bar, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_SHADER_WRITE_BIT, + VK_IMAGE_LAYOUT_GENERAL, + VK_QUEUE_FAMILY_IGNORED); + + vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pImageMemoryBarriers = img_bar, + .imageMemoryBarrierCount = nb_img_bar, + }); + + ff_vk_exec_bind_pipeline(vkctx, exec, pl); + + if (push_src) + ff_vk_update_push_exec(vkctx, exec, pl, VK_SHADER_STAGE_COMPUTE_BIT, + 0, push_size, push_src); + + for (int i = 0; i < nb_in; i++) + ff_vk_update_descriptor_img_array(vkctx, pl, exec, in[i], in_views[i], 0, i, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + sampler); + + ff_vk_update_descriptor_img_array(vkctx, pl, exec, out, out_views, 0, nb_in, + VK_IMAGE_LAYOUT_GENERAL, + VK_NULL_HANDLE); + + vk->CmdDispatch(exec->buf, + FFALIGN(vkctx->output_width, pl->wg_size[0])/pl->wg_size[0], + FFALIGN(vkctx->output_height, pl->wg_size[1])/pl->wg_size[1], + pl->wg_size[2]); + + return ff_vk_exec_submit(vkctx, exec); +fail: + ff_vk_exec_discard_deps(vkctx, exec); + return err; } diff --git a/libavfilter/vulkan_filter.h b/libavfilter/vulkan_filter.h index bfdb9b2d7d8..d2c14601d94 100644 --- a/libavfilter/vulkan_filter.h +++ b/libavfilter/vulkan_filter.h @@ -1,4 +1,6 @@ /* + * Copyright (c) Lynne + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -26,9 +28,38 @@ /** * General lavfi IO functions */ -int ff_vk_filter_init (AVFilterContext *avctx); -int ff_vk_filter_config_input (AVFilterLink *inlink); -int ff_vk_filter_config_output (AVFilterLink *outlink); -int ff_vk_filter_config_output_inplace(AVFilterLink *outlink); +int ff_vk_filter_init (AVFilterContext *avctx); +int ff_vk_filter_config_input (AVFilterLink *inlink); +int ff_vk_filter_config_output(AVFilterLink *outlink); + +/** + * Can be called manually, if not using ff_vk_filter_config_output. + */ +int ff_vk_filter_init_context(AVFilterContext *avctx, FFVulkanContext *s, + AVBufferRef *frames_ref, + int width, int height, enum AVPixelFormat sw_format); + +/** + * Submit a compute shader with a zero/one input and single out for execution. + */ +int ff_vk_filter_process_simple(FFVulkanContext *vkctx, FFVkExecPool *e, + FFVulkanPipeline *pl, AVFrame *out_f, AVFrame *in_f, + VkSampler sampler, void *push_src, size_t push_size); + +/** + * Submit a compute shader with a single in and single out with 2 stages. + */ +int ff_vk_filter_process_2pass(FFVulkanContext *vkctx, FFVkExecPool *e, + FFVulkanPipeline *pls[2], + AVFrame *out, AVFrame *tmp, AVFrame *in, + VkSampler sampler, void *push_src, size_t push_size); + +/** + * Up to 16 inputs, one output + */ +int ff_vk_filter_process_Nin(FFVulkanContext *vkctx, FFVkExecPool *e, + FFVulkanPipeline *pl, + AVFrame *out, AVFrame *in[], int nb_in, + VkSampler sampler, void *push_src, size_t push_size); #endif /* AVFILTER_VULKAN_FILTER_H */ diff --git a/libavutil/vulkan_glslang.c b/libavfilter/vulkan_glslang.c similarity index 95% rename from libavutil/vulkan_glslang.c rename to libavfilter/vulkan_glslang.c index e7785f6d405..845a530ee0d 100644 --- a/libavutil/vulkan_glslang.c +++ b/libavfilter/vulkan_glslang.c @@ -21,8 +21,9 @@ #include #include -#include "mem.h" -#include "avassert.h" +#include "vulkan_spirv.h" +#include "libavutil/mem.h" +#include "libavutil/avassert.h" static pthread_mutex_t glslc_mutex = PTHREAD_MUTEX_INITIALIZER; static int glslc_refcount = 0; @@ -176,11 +177,13 @@ static int glslc_shader_compile(FFVkSPIRVCompiler *ctx, void *avctx, av_assert0(glslc_refcount); + *opaque = NULL; + if (!(glslc_shader = glslang_shader_create(&glslc_input))) return AVERROR(ENOMEM); if (!glslang_shader_preprocess(glslc_shader, &glslc_input)) { - ff_vk_print_shader(avctx, shd, AV_LOG_WARNING); + ff_vk_shader_print(avctx, shd, AV_LOG_WARNING); av_log(avctx, AV_LOG_ERROR, "Unable to preprocess shader: %s (%s)!\n", glslang_shader_get_info_log(glslc_shader), glslang_shader_get_info_debug_log(glslc_shader)); @@ -189,7 +192,7 @@ static int glslc_shader_compile(FFVkSPIRVCompiler *ctx, void *avctx, } if (!glslang_shader_parse(glslc_shader, &glslc_input)) { - ff_vk_print_shader(avctx, shd, AV_LOG_WARNING); + ff_vk_shader_print(avctx, shd, AV_LOG_WARNING); av_log(avctx, AV_LOG_ERROR, "Unable to parse shader: %s (%s)!\n", glslang_shader_get_info_log(glslc_shader), glslang_shader_get_info_debug_log(glslc_shader)); @@ -206,7 +209,7 @@ static int glslc_shader_compile(FFVkSPIRVCompiler *ctx, void *avctx, if (!glslang_program_link(glslc_program, GLSLANG_MSG_SPV_RULES_BIT | GLSLANG_MSG_VULKAN_RULES_BIT)) { - ff_vk_print_shader(avctx, shd, AV_LOG_WARNING); + ff_vk_shader_print(avctx, shd, AV_LOG_WARNING); av_log(avctx, AV_LOG_ERROR, "Unable to link shader: %s (%s)!\n", glslang_program_get_info_log(glslc_program), glslang_program_get_info_debug_log(glslc_program)); @@ -219,10 +222,10 @@ static int glslc_shader_compile(FFVkSPIRVCompiler *ctx, void *avctx, messages = glslang_program_SPIRV_get_messages(glslc_program); if (messages) { - ff_vk_print_shader(avctx, shd, AV_LOG_WARNING); + ff_vk_shader_print(avctx, shd, AV_LOG_WARNING); av_log(avctx, AV_LOG_WARNING, "%s\n", messages); } else { - ff_vk_print_shader(avctx, shd, AV_LOG_VERBOSE); + ff_vk_shader_print(avctx, shd, AV_LOG_VERBOSE); } glslang_shader_delete(glslc_shader); @@ -257,7 +260,7 @@ static void glslc_uninit(FFVkSPIRVCompiler **ctx) av_freep(ctx); } -static FFVkSPIRVCompiler *ff_vk_glslang_init(void) +FFVkSPIRVCompiler *ff_vk_glslang_init(void) { FFVkSPIRVCompiler *ret = av_mallocz(sizeof(*ret)); if (!ret) diff --git a/libavutil/vulkan_shaderc.c b/libavfilter/vulkan_shaderc.c similarity index 96% rename from libavutil/vulkan_shaderc.c rename to libavfilter/vulkan_shaderc.c index bd40edf1876..38be1030ad2 100644 --- a/libavutil/vulkan_shaderc.c +++ b/libavfilter/vulkan_shaderc.c @@ -18,7 +18,8 @@ #include -#include "mem.h" +#include "libavutil/mem.h" +#include "vulkan_spirv.h" static int shdc_shader_compile(FFVkSPIRVCompiler *ctx, void *avctx, FFVkSPIRVShader *shd, uint8_t **data, @@ -43,6 +44,7 @@ static int shdc_shader_compile(FFVkSPIRVCompiler *ctx, void *avctx, }; shaderc_compile_options_t opts = shaderc_compile_options_initialize(); + *opaque = NULL; if (!opts) return AVERROR(ENOMEM); @@ -65,7 +67,7 @@ static int shdc_shader_compile(FFVkSPIRVCompiler *ctx, void *avctx, loglevel = err ? AV_LOG_ERROR : warn ? AV_LOG_WARNING : AV_LOG_VERBOSE; - ff_vk_print_shader(avctx, shd, loglevel); + ff_vk_shader_print(avctx, shd, loglevel); if (message && (err || warn)) av_log(avctx, loglevel, "%s\n", message); status = ret < FF_ARRAY_ELEMS(shdc_result) ? shdc_result[ret] : "unknown"; @@ -104,7 +106,7 @@ static void shdc_uninit(FFVkSPIRVCompiler **ctx) av_freep(ctx); } -static FFVkSPIRVCompiler *ff_vk_shaderc_init(void) +FFVkSPIRVCompiler *ff_vk_shaderc_init(void) { FFVkSPIRVCompiler *ret = av_mallocz(sizeof(*ret)); if (!ret) diff --git a/libavfilter/vulkan_spirv.h b/libavfilter/vulkan_spirv.h new file mode 100644 index 00000000000..5638cd9696a --- /dev/null +++ b/libavfilter/vulkan_spirv.h @@ -0,0 +1,45 @@ +/* + * 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 AVFILTER_VULKAN_SPIRV_H +#define AVFILTER_VULKAN_SPIRV_H + +#include "libavutil/vulkan.h" + +#include "vulkan.h" +#include "config.h" + +typedef struct FFVkSPIRVCompiler { + void *priv; + int (*compile_shader)(struct FFVkSPIRVCompiler *ctx, void *avctx, + struct FFVkSPIRVShader *shd, uint8_t **data, + size_t *size, const char *entrypoint, void **opaque); + void (*free_shader)(struct FFVkSPIRVCompiler *ctx, void **opaque); + void (*uninit)(struct FFVkSPIRVCompiler **ctx); +} FFVkSPIRVCompiler; + +#if CONFIG_LIBGLSLANG +FFVkSPIRVCompiler *ff_vk_glslang_init(void); +#define ff_vk_spirv_init ff_vk_glslang_init +#endif +#if CONFIG_LIBSHADERC +FFVkSPIRVCompiler *ff_vk_shaderc_init(void); +#define ff_vk_spirv_init ff_vk_shaderc_init +#endif + +#endif /* AVFILTER_VULKAN_H */ diff --git a/libavfilter/window_func.h b/libavfilter/window_func.h index 02b5def9dd1..d0de63b5a81 100644 --- a/libavfilter/window_func.h +++ b/libavfilter/window_func.h @@ -59,19 +59,6 @@ enum WindowFunc { WFUNC_RECT, WFUNC_HANNING, WFUNC_HAMMING, WFUNC_BLACKMAN, { "bohman", "Bohman", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BOHMAN}, 0, 0, flag, "win_func" }, \ { "kaiser", "Kaiser", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_KAISER}, 0, 0, flag, "win_func" } -static inline double get_i0(double x) -{ - double y = 1.0, prev = 1.0, i = 1.0; - - while (fabs(prev) > 1e-20) { - double summand = prev * x * x / (4 * i * i); - y += summand; - prev = summand; - i++; - } - - return y; -} static inline void generate_window_func(float *lut, int N, int win_func, float *overlap) @@ -232,13 +219,15 @@ static inline void generate_window_func(float *lut, int N, int win_func, *overlap = 0.75; break; case WFUNC_KAISER: + { + double scale = 1.0 / av_bessel_i0(12.); for (n = 0; n < N; n++) { double x = 2.0 / (double)(N - 1); - - lut[n] = get_i0(12. * sqrt(1. - SQR(n * x - 1.))) / get_i0(12.); + lut[n] = av_bessel_i0(12. * sqrt(1. - SQR(n * x - 1.))) * scale; } *overlap = 0.75; break; + } default: av_assert0(0); } diff --git a/libavfilter/x86/af_volume.asm b/libavfilter/x86/af_volume.asm index 723ab1f8fb6..35a00784a2d 100644 --- a/libavfilter/x86/af_volume.asm +++ b/libavfilter/x86/af_volume.asm @@ -56,7 +56,7 @@ cglobal scale_samples_s16, 4,4,4, dst, src, len, volume mova [dstq+lenq], m3 sub lenq, mmsize jge .loop - REP_RET + RET ;------------------------------------------------------------------------------ ; void ff_scale_samples_s32(uint8_t *dst, const uint8_t *src, int len, @@ -93,7 +93,7 @@ cglobal scale_samples_s32, 4,4,4, dst, src, len, volume %endif sub lenq, mmsize jge .loop - REP_RET + RET %endmacro INIT_XMM sse2 @@ -137,4 +137,4 @@ cglobal scale_samples_s32, 4,4,8, dst, src, len, volume mova [dstq+lenq], m0 sub lenq, mmsize jge .loop - REP_RET + RET diff --git a/libavfilter/x86/avf_showcqt.asm b/libavfilter/x86/avf_showcqt.asm index 63e58408cda..16af0de9b0e 100644 --- a/libavfilter/x86/avf_showcqt.asm +++ b/libavfilter/x86/avf_showcqt.asm @@ -127,7 +127,7 @@ cglobal showcqt_cqt_calc, 5, 10, 12, dst, src, coeffs, len, fft_len, x, coeffs_v lea dstq, [dstq + 16] lea coeffsq, [coeffsq + 2*Coeffs.sizeof] jnz .loop_k - REP_RET + RET align 16 .check_loop_a: cmp xd, [coeffsq + Coeffs.len] @@ -170,7 +170,7 @@ cglobal showcqt_cqt_calc, 4, 7, 8, dst, src, coeffs, len, x, coeffs_val, i lea dstq, [dstq + 8] lea coeffsq, [coeffsq + Coeffs.sizeof] jnz .loop_k - REP_RET + RET %endif ; ARCH_X86_64 %endmacro ; DECLARE_CQT_CALC diff --git a/libavfilter/x86/scene_sad.asm b/libavfilter/x86/scene_sad.asm index d38d71ccca1..bf7236b3a3b 100644 --- a/libavfilter/x86/scene_sad.asm +++ b/libavfilter/x86/scene_sad.asm @@ -53,7 +53,7 @@ cglobal scene_sad, 6, 7, 2, src1, stride1, src2, stride2, width, end, x mov r0q, r6mp movu [r0q], m1 ; sum -REP_RET +RET %endmacro diff --git a/libavfilter/x86/vf_blend.asm b/libavfilter/x86/vf_blend.asm index 277b100e4d5..362020ec959 100644 --- a/libavfilter/x86/vf_blend.asm +++ b/libavfilter/x86/vf_blend.asm @@ -63,7 +63,7 @@ cglobal blend_%1, 5, 7, %2, top, top_linesize, bottom, bottom_linesize, dst, end add dstq, dst_linesizeq sub endd, 1 jg .nextrow -REP_RET +RET %endmacro %macro BLEND_SIMPLE 2-3 0 diff --git a/libavfilter/x86/vf_bwdif.asm b/libavfilter/x86/vf_bwdif.asm index 0b453da53b3..c93b41ec488 100644 --- a/libavfilter/x86/vf_bwdif.asm +++ b/libavfilter/x86/vf_bwdif.asm @@ -26,18 +26,22 @@ %include "libavutil/x86/x86util.asm" -SECTION_RODATA +SECTION_RODATA 32 -pw_coefhf: times 4 dw 1016, 5570 -pw_coefhf1: times 8 dw -3801 -pw_coefsp: times 4 dw 5077, -981 -pw_splfdif: times 4 dw -768, 768 +pw_coefhf: times 8 dw 1016, 5570 +pw_coefhf1: times 16 dw -3801 +pw_coefsp: times 8 dw 5077, -981 +pw_splfdif: times 8 dw -768, 768 SECTION .text %macro LOAD8 2 + %if mmsize == 32 + pmovzxbw %1, %2 + %else movh %1, %2 punpcklbw %1, m7 + %endif %endmacro %macro LOAD12 2 @@ -45,8 +49,14 @@ SECTION .text %endmacro %macro DISP8 0 + %if mmsize == 32 + vextracti128 xm1, m2, 1 + packuswb xm2, xm1 + movu [dstq], xm2 + %else packuswb m2, m2 movh [dstq], m2 + %endif %endmacro %macro DISP12 0 @@ -244,8 +254,12 @@ cglobal bwdif_filter_line_12bit, 4, 9, 13, 0, dst, prev, cur, next, w, \ prefs, mrefs, prefs2, mrefs2, \ prefs3, mrefs3, prefs4, \ mrefs4, parity, clip_max + %if mmsize == 32 + vpbroadcastw m12, WORD clip_maxm + %else movd m12, DWORD clip_maxm SPLATW m12, m12, 0 + %endif %else cglobal bwdif_filter_line_12bit, 4, 6, 8, 80, dst, prev, cur, next, w, \ prefs, mrefs, prefs2, mrefs2, \ @@ -264,3 +278,8 @@ INIT_XMM ssse3 BWDIF INIT_XMM sse2 BWDIF + +%if HAVE_AVX2_EXTERNAL && ARCH_X86_64 +INIT_YMM avx2 +BWDIF +%endif diff --git a/libavfilter/x86/vf_bwdif_init.c b/libavfilter/x86/vf_bwdif_init.c index e24e5cd9b1c..57f908a8efb 100644 --- a/libavfilter/x86/vf_bwdif_init.c +++ b/libavfilter/x86/vf_bwdif_init.c @@ -32,6 +32,10 @@ void ff_bwdif_filter_line_ssse3(void *dst, void *prev, void *cur, void *next, int w, int prefs, int mrefs, int prefs2, int mrefs2, int prefs3, int mrefs3, int prefs4, int mrefs4, int parity, int clip_max); +void ff_bwdif_filter_line_avx2(void *dst, void *prev, void *cur, void *next, + int w, int prefs, int mrefs, int prefs2, + int mrefs2, int prefs3, int mrefs3, int prefs4, + int mrefs4, int parity, int clip_max); void ff_bwdif_filter_line_12bit_sse2(void *dst, void *prev, void *cur, void *next, int w, int prefs, int mrefs, int prefs2, @@ -41,22 +45,28 @@ void ff_bwdif_filter_line_12bit_ssse3(void *dst, void *prev, void *cur, void *ne int w, int prefs, int mrefs, int prefs2, int mrefs2, int prefs3, int mrefs3, int prefs4, int mrefs4, int parity, int clip_max); +void ff_bwdif_filter_line_12bit_avx2(void *dst, void *prev, void *cur, void *next, + int w, int prefs, int mrefs, int prefs2, + int mrefs2, int prefs3, int mrefs3, int prefs4, + int mrefs4, int parity, int clip_max); -av_cold void ff_bwdif_init_x86(BWDIFContext *bwdif) +av_cold void ff_bwdif_init_x86(BWDIFContext *bwdif, int bit_depth) { - YADIFContext *yadif = &bwdif->yadif; int cpu_flags = av_get_cpu_flags(); - int bit_depth = (!yadif->csp) ? 8 : yadif->csp->comp[0].depth; if (bit_depth <= 8) { if (EXTERNAL_SSE2(cpu_flags)) bwdif->filter_line = ff_bwdif_filter_line_sse2; if (EXTERNAL_SSSE3(cpu_flags)) bwdif->filter_line = ff_bwdif_filter_line_ssse3; + if (ARCH_X86_64 && EXTERNAL_AVX2_FAST(cpu_flags)) + bwdif->filter_line = ff_bwdif_filter_line_avx2; } else if (bit_depth <= 12) { if (EXTERNAL_SSE2(cpu_flags)) bwdif->filter_line = ff_bwdif_filter_line_12bit_sse2; if (EXTERNAL_SSSE3(cpu_flags)) bwdif->filter_line = ff_bwdif_filter_line_12bit_ssse3; + if (ARCH_X86_64 && EXTERNAL_AVX2_FAST(cpu_flags)) + bwdif->filter_line = ff_bwdif_filter_line_12bit_avx2; } } diff --git a/libavfilter/x86/vf_framerate.asm b/libavfilter/x86/vf_framerate.asm index 7a30c870bd5..b5505b4ff8c 100644 --- a/libavfilter/x86/vf_framerate.asm +++ b/libavfilter/x86/vf_framerate.asm @@ -84,7 +84,7 @@ cglobal blend_frames%1, 5, 7, 5, src1, src1_linesize, src2, src2_linesize, dst, add dstq, dst_linesizeq sub endd, 1 jg .nextrow -REP_RET +RET %endmacro diff --git a/libavfilter/x86/vf_gradfun.asm b/libavfilter/x86/vf_gradfun.asm index 3581f89fe88..d106d521008 100644 --- a/libavfilter/x86/vf_gradfun.asm +++ b/libavfilter/x86/vf_gradfun.asm @@ -64,7 +64,7 @@ cglobal gradfun_filter_line, 6, 6 add r0, 4 jl .loop .end: - REP_RET + RET INIT_XMM ssse3 cglobal gradfun_filter_line, 6, 6, 8 @@ -78,7 +78,7 @@ cglobal gradfun_filter_line, 6, 6, 8 FILTER_LINE m4 add r0, 8 jl .loop - REP_RET + RET %macro BLUR_LINE 1 cglobal gradfun_blur_line_%1, 6, 6, 8 @@ -102,7 +102,7 @@ cglobal gradfun_blur_line_%1, 6, 6, 8 mova [r3+r0], m0 add r0, 16 jl .loop - REP_RET + RET %endmacro INIT_XMM sse2 diff --git a/libavfilter/x86/vf_hqdn3d.asm b/libavfilter/x86/vf_hqdn3d.asm index e3b1bdca53b..2c0ca455718 100644 --- a/libavfilter/x86/vf_hqdn3d.asm +++ b/libavfilter/x86/vf_hqdn3d.asm @@ -97,7 +97,7 @@ ALIGN 16 inc xq jl .loop je .loop2 - REP_RET + RET %endmacro ; HQDN3D_ROW HQDN3D_ROW 8 diff --git a/libavfilter/x86/vf_interlace.asm b/libavfilter/x86/vf_interlace.asm index f4a405c7543..c28f9fbe3e5 100644 --- a/libavfilter/x86/vf_interlace.asm +++ b/libavfilter/x86/vf_interlace.asm @@ -73,7 +73,7 @@ SECTION .text jl .loop .end: - REP_RET + RET %endmacro %macro LOWPASS_LINE 0 @@ -146,7 +146,7 @@ cglobal lowpass_line_complex, 5, 5, 8, dst, h, src, mref, pref add srcq, mmsize sub hd, mmsize jg .loop -REP_RET +RET cglobal lowpass_line_complex_12, 5, 5, 8, 16, dst, h, src, mref, pref, clip_max movd m7, DWORD clip_maxm @@ -208,7 +208,7 @@ cglobal lowpass_line_complex_12, 5, 5, 8, 16, dst, h, src, mref, pref, clip_max add srcq, 2*mmsize sub hd, mmsize jg .loop -REP_RET +RET %endmacro INIT_XMM sse2 diff --git a/libavfilter/x86/vf_maskedmerge.asm b/libavfilter/x86/vf_maskedmerge.asm index 10282990874..d9bd4688fd1 100644 --- a/libavfilter/x86/vf_maskedmerge.asm +++ b/libavfilter/x86/vf_maskedmerge.asm @@ -81,4 +81,4 @@ cglobal maskedmerge8, 5, 7, 8, bsrc, osrc, msrc, dst, blinesize, w, x add dstq, dlinesizeq sub hd, 1 jg .nextrow -REP_RET +RET diff --git a/libavfilter/x86/vf_stereo3d.asm b/libavfilter/x86/vf_stereo3d.asm index a057e495f18..b6a293b18e2 100644 --- a/libavfilter/x86/vf_stereo3d.asm +++ b/libavfilter/x86/vf_stereo3d.asm @@ -213,4 +213,4 @@ cglobal anaglyph, 3, 6, 8, 2*9*mmsize, dst, lsrc, rsrc, dst_linesize, o, cnt add rsrcq, r_linesizeq sub heightd, 1 jg .nextrow -REP_RET +RET diff --git a/libavfilter/x86/vf_w3fdif.asm b/libavfilter/x86/vf_w3fdif.asm index 52628c38d79..3010469f97f 100644 --- a/libavfilter/x86/vf_w3fdif.asm +++ b/libavfilter/x86/vf_w3fdif.asm @@ -38,7 +38,7 @@ cglobal w3fdif_scale, 3, 3, 2, 0, out_pixel, work_pixel, linesize add work_pixelq, mmsize*2 sub linesized, mmsize/2 jg .loop -REP_RET +RET cglobal w3fdif_simple_low, 4, 5, 6, 0, work_line, in_lines_cur0, coef, linesize, offset movd m1, [coefq] @@ -63,7 +63,7 @@ cglobal w3fdif_simple_low, 4, 5, 6, 0, work_line, in_lines_cur0, coef, linesize, add offsetq, mmsize/2 sub linesized, mmsize/2 jg .loop -REP_RET +RET cglobal w3fdif_complex_low, 4, 7, 8, 0, work_line, in_lines_cur0, coef, linesize movq m0, [coefq] @@ -99,7 +99,7 @@ cglobal w3fdif_complex_low, 4, 7, 8, 0, work_line, in_lines_cur0, coef, linesize add offsetq, mmsize/2 sub linesized, mmsize/2 jg .loop -REP_RET +RET %if ARCH_X86_64 cglobal w3fdif_simple_high, 5, 9, 8, 0, work_line, in_lines_cur0, in_lines_adj0, coef, linesize @@ -179,7 +179,7 @@ cglobal w3fdif_simple_high, 4, 7, 8, 0, work_line, in_lines_cur0, in_lines_adj0, add offsetq, mmsize/2 sub linesized, mmsize/2 jg .loop -REP_RET +RET %if ARCH_X86_64 @@ -254,6 +254,6 @@ cglobal w3fdif_complex_high, 5, 13, 10, 0, work_line, in_lines_cur0, in_lines_ad add offsetq, mmsize/2 sub linesized, mmsize/2 jg .loop -REP_RET +RET %endif diff --git a/libavfilter/yadif.h b/libavfilter/yadif.h index c928911b357..fbb99fd46ef 100644 --- a/libavfilter/yadif.h +++ b/libavfilter/yadif.h @@ -22,6 +22,7 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" +#include "ccfifo.h" enum YADIFMode { YADIF_MODE_SEND_FRAME = 0, ///< send 1 frame for each frame @@ -76,6 +77,7 @@ typedef struct YADIFContext { int eof; uint8_t *temp_line; int temp_line_size; + CCFifo cc_fifo; /* * An algorithm that treats first and/or last fields in a sequence diff --git a/libavfilter/yadif_common.c b/libavfilter/yadif_common.c index a10cf7a17fd..561659e3463 100644 --- a/libavfilter/yadif_common.c +++ b/libavfilter/yadif_common.c @@ -22,6 +22,7 @@ #include "libavutil/avassert.h" #include "libavutil/imgutils.h" #include "internal.h" +#include "video.h" #include "yadif.h" static int return_frame(AVFilterContext *ctx, int is_second) @@ -31,8 +32,8 @@ static int return_frame(AVFilterContext *ctx, int is_second) int tff, ret; if (yadif->parity == -1) { - tff = yadif->cur->interlaced_frame ? - yadif->cur->top_field_first : 1; + tff = (yadif->cur->flags & AV_FRAME_FLAG_INTERLACED) ? + !!(yadif->cur->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) : 1; } else { tff = yadif->parity ^ 1; } @@ -43,7 +44,12 @@ static int return_frame(AVFilterContext *ctx, int is_second) return AVERROR(ENOMEM); av_frame_copy_props(yadif->out, yadif->cur); +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS yadif->out->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + yadif->out->flags &= ~AV_FRAME_FLAG_INTERLACED; if (yadif->current_field == YADIF_FIELD_BACK_END) yadif->current_field = YADIF_FIELD_END; } @@ -60,6 +66,8 @@ static int return_frame(AVFilterContext *ctx, int is_second) yadif->out->pts = AV_NOPTS_VALUE; } } + + ff_ccfifo_inject(&yadif->cc_fifo, yadif->out); ret = ff_filter_frame(ctx->outputs[0], yadif->out); yadif->frame_pending = (yadif->mode&1) && !is_second; @@ -96,6 +104,8 @@ int ff_yadif_filter_frame(AVFilterLink *link, AVFrame *frame) av_assert0(frame); + ff_ccfifo_extract(&yadif->cc_fifo, frame); + if (yadif->frame_pending) return_frame(ctx, 1); @@ -128,15 +138,16 @@ int ff_yadif_filter_frame(AVFilterLink *link, AVFrame *frame) if (!yadif->prev) return 0; - if ((yadif->deint && !yadif->cur->interlaced_frame) || + if ((yadif->deint && !(yadif->cur->flags & AV_FRAME_FLAG_INTERLACED)) || ctx->is_disabled || - (yadif->deint && !yadif->prev->interlaced_frame && yadif->prev->repeat_pict) || - (yadif->deint && !yadif->next->interlaced_frame && yadif->next->repeat_pict) + (yadif->deint && !(yadif->prev->flags & AV_FRAME_FLAG_INTERLACED) && yadif->prev->repeat_pict) || + (yadif->deint && !(yadif->next->flags & AV_FRAME_FLAG_INTERLACED) && yadif->next->repeat_pict) ) { yadif->out = av_frame_clone(yadif->cur); if (!yadif->out) return AVERROR(ENOMEM); + ff_ccfifo_inject(&yadif->cc_fifo, yadif->out); av_frame_free(&yadif->prev); if (yadif->out->pts != AV_NOPTS_VALUE) yadif->out->pts *= 2; @@ -148,7 +159,12 @@ int ff_yadif_filter_frame(AVFilterLink *link, AVFrame *frame) return AVERROR(ENOMEM); av_frame_copy_props(yadif->out, yadif->cur); +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS yadif->out->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + yadif->out->flags &= ~AV_FRAME_FLAG_INTERLACED; if (yadif->out->pts != AV_NOPTS_VALUE) yadif->out->pts *= 2; diff --git a/libavformat/Makefile b/libavformat/Makefile index fa71ec12f72..bd78c206b98 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -77,6 +77,8 @@ OBJS-$(CONFIG_AAC_DEMUXER) += aacdec.o apetag.o img2.o rawdec.o OBJS-$(CONFIG_AAX_DEMUXER) += aaxdec.o OBJS-$(CONFIG_AC3_DEMUXER) += ac3dec.o rawdec.o OBJS-$(CONFIG_AC3_MUXER) += rawenc.o +OBJS-$(CONFIG_AC4_DEMUXER) += ac4dec.o +OBJS-$(CONFIG_AC4_MUXER) += ac4enc.o OBJS-$(CONFIG_ACE_DEMUXER) += acedec.o OBJS-$(CONFIG_ACM_DEMUXER) += acm.o rawdec.o OBJS-$(CONFIG_ACT_DEMUXER) += act.o @@ -214,7 +216,7 @@ OBJS-$(CONFIG_FLAC_MUXER) += flacenc.o flacenc_header.o \ OBJS-$(CONFIG_FLIC_DEMUXER) += flic.o OBJS-$(CONFIG_FLV_DEMUXER) += flvdec.o OBJS-$(CONFIG_LIVE_FLV_DEMUXER) += flvdec.o -OBJS-$(CONFIG_FLV_MUXER) += flvenc.o avc.o +OBJS-$(CONFIG_FLV_MUXER) += flvenc.o avc.o hevc.o av1.o vpcc.o OBJS-$(CONFIG_FOURXM_DEMUXER) += 4xm.o OBJS-$(CONFIG_FRAMECRC_MUXER) += framecrcenc.o framehash.o OBJS-$(CONFIG_FRAMEHASH_MUXER) += hashenc.o framehash.o @@ -251,6 +253,8 @@ OBJS-$(CONFIG_HCOM_DEMUXER) += hcom.o pcm.o OBJS-$(CONFIG_HDS_MUXER) += hdsenc.o OBJS-$(CONFIG_HEVC_DEMUXER) += hevcdec.o rawdec.o OBJS-$(CONFIG_HEVC_MUXER) += rawenc.o +OBJS-$(CONFIG_EVC_DEMUXER) += evcdec.o rawdec.o +OBJS-$(CONFIG_EVC_MUXER) += rawenc.o OBJS-$(CONFIG_HLS_DEMUXER) += hls.o hls_sample_encryption.o OBJS-$(CONFIG_HLS_MUXER) += hlsenc.o hlsplaylist.o avc.o OBJS-$(CONFIG_HNM_DEMUXER) += hnm.o @@ -316,6 +320,7 @@ OBJS-$(CONFIG_IVF_MUXER) += ivfenc.o OBJS-$(CONFIG_IVR_DEMUXER) += rmdec.o rm.o rmsipr.o OBJS-$(CONFIG_JACOSUB_DEMUXER) += jacosubdec.o subtitles.o OBJS-$(CONFIG_JACOSUB_MUXER) += jacosubenc.o rawenc.o +OBJS-$(CONFIG_JPEGXL_ANIM_DEMUXER) += jpegxl_anim_dec.o jpegxl_probe.o OBJS-$(CONFIG_JV_DEMUXER) += jvdec.o OBJS-$(CONFIG_KUX_DEMUXER) += flvdec.o OBJS-$(CONFIG_KVAG_DEMUXER) += kvag.o @@ -361,7 +366,7 @@ OBJS-$(CONFIG_MOV_DEMUXER) += mov.o mov_chan.o mov_esds.o \ OBJS-$(CONFIG_MOV_MUXER) += movenc.o av1.o avc.o hevc.o vpcc.o \ movenchint.o mov_chan.o rtp.o \ movenccenc.o movenc_ttml.o rawutils.o \ - dovi_isom.o + dovi_isom.o evc.o OBJS-$(CONFIG_MP2_MUXER) += rawenc.o OBJS-$(CONFIG_MP3_DEMUXER) += mp3dec.o replaygain.o OBJS-$(CONFIG_MP3_MUXER) += mp3enc.o rawenc.o id3v2enc.o @@ -471,6 +476,7 @@ OBJS-$(CONFIG_PCM_U8_DEMUXER) += pcmdec.o pcm.o OBJS-$(CONFIG_PCM_U8_MUXER) += pcmenc.o rawenc.o OBJS-$(CONFIG_PCM_VIDC_DEMUXER) += pcmdec.o pcm.o OBJS-$(CONFIG_PCM_VIDC_MUXER) += pcmenc.o rawenc.o +OBJS-$(CONFIG_PDV_DEMUXER) += pdvdec.o OBJS-$(CONFIG_PJS_DEMUXER) += pjsdec.o subtitles.o OBJS-$(CONFIG_PMP_DEMUXER) += pmpdec.o OBJS-$(CONFIG_PP_BNK_DEMUXER) += pp_bnk.o @@ -482,6 +488,7 @@ OBJS-$(CONFIG_RAWVIDEO_DEMUXER) += rawvideodec.o OBJS-$(CONFIG_RAWVIDEO_MUXER) += rawenc.o OBJS-$(CONFIG_REALTEXT_DEMUXER) += realtextdec.o subtitles.o OBJS-$(CONFIG_REDSPARK_DEMUXER) += redspark.o +OBJS-$(CONFIG_RKA_DEMUXER) += rka.o apetag.o img2.o OBJS-$(CONFIG_RL2_DEMUXER) += rl2.o OBJS-$(CONFIG_RM_DEMUXER) += rmdec.o rm.o rmsipr.o OBJS-$(CONFIG_RM_MUXER) += rmenc.o rm.o @@ -523,6 +530,7 @@ OBJS-$(CONFIG_SBG_DEMUXER) += sbgdec.o OBJS-$(CONFIG_SCC_DEMUXER) += sccdec.o subtitles.o OBJS-$(CONFIG_SCC_MUXER) += sccenc.o OBJS-$(CONFIG_SCD_DEMUXER) += scd.o +OBJS-$(CONFIG_SDNS_DEMUXER) += sdns.o OBJS-$(CONFIG_SDP_DEMUXER) += rtsp.o OBJS-$(CONFIG_SDR2_DEMUXER) += sdr2.o OBJS-$(CONFIG_SDS_DEMUXER) += sdsdec.o @@ -595,8 +603,12 @@ OBJS-$(CONFIG_VOC_MUXER) += vocenc.o voc.o OBJS-$(CONFIG_VPK_DEMUXER) += vpk.o OBJS-$(CONFIG_VPLAYER_DEMUXER) += vplayerdec.o subtitles.o OBJS-$(CONFIG_VQF_DEMUXER) += vqf.o +OBJS-$(CONFIG_VVC_DEMUXER) += vvcdec.o rawdec.o +OBJS-$(CONFIG_VVC_MUXER) += rawenc.o OBJS-$(CONFIG_W64_DEMUXER) += wavdec.o w64.o pcm.o OBJS-$(CONFIG_W64_MUXER) += wavenc.o w64.o +OBJS-$(CONFIG_WADY_DEMUXER) += wady.o pcm.o +OBJS-$(CONFIG_WAVARC_DEMUXER) += wavarc.o OBJS-$(CONFIG_WAV_DEMUXER) += wavdec.o pcm.o OBJS-$(CONFIG_WAV_MUXER) += wavenc.o OBJS-$(CONFIG_WC3_DEMUXER) += wc3movie.o @@ -620,6 +632,7 @@ OBJS-$(CONFIG_WVE_DEMUXER) += wvedec.o pcm.o OBJS-$(CONFIG_WV_MUXER) += wvenc.o wv.o apetag.o img2.o OBJS-$(CONFIG_XA_DEMUXER) += xa.o OBJS-$(CONFIG_XBIN_DEMUXER) += bintext.o sauce.o +OBJS-$(CONFIG_XMD_DEMUXER) += xmd.o pcm.o OBJS-$(CONFIG_XMV_DEMUXER) += xmv.o OBJS-$(CONFIG_XVAG_DEMUXER) += xvag.o OBJS-$(CONFIG_XWMA_DEMUXER) += xwma.o @@ -707,7 +720,8 @@ SHLIBOBJS-$(CONFIG_HLS_DEMUXER) += ac3_channel_layout_tab.o SHLIBOBJS-$(CONFIG_MATROSKA_DEMUXER) += mpeg4audio_sample_rates.o SHLIBOBJS-$(CONFIG_MOV_DEMUXER) += ac3_channel_layout_tab.o SHLIBOBJS-$(CONFIG_MP3_MUXER) += mpegaudiotabs.o -SHLIBOBJS-$(CONFIG_MXF_MUXER) += golomb_tab.o +SHLIBOBJS-$(CONFIG_MXF_MUXER) += golomb_tab.o \ + rangecoder_dec.o SHLIBOBJS-$(CONFIG_NUT_MUXER) += mpegaudiotabs.o SHLIBOBJS-$(CONFIG_RTPDEC) += jpegtables.o SHLIBOBJS-$(CONFIG_RTP_MUXER) += golomb_tab.o jpegtables.o \ diff --git a/libavformat/a64.c b/libavformat/a64.c index a66f2542b7e..23b20fc8b79 100644 --- a/libavformat/a64.c +++ b/libavformat/a64.c @@ -23,6 +23,7 @@ #include "libavcodec/codec_id.h" #include "libavcodec/codec_par.h" #include "avformat.h" +#include "mux.h" #include "rawenc.h" static int a64_write_header(AVFormatContext *s) @@ -59,11 +60,11 @@ static int a64_write_header(AVFormatContext *s) return 0; } -const AVOutputFormat ff_a64_muxer = { - .name = "a64", - .long_name = NULL_IF_CONFIG_SMALL("a64 - video for Commodore 64"), - .extensions = "a64, A64", - .video_codec = AV_CODEC_ID_A64_MULTI, +const FFOutputFormat ff_a64_muxer = { + .p.name = "a64", + .p.long_name = NULL_IF_CONFIG_SMALL("a64 - video for Commodore 64"), + .p.extensions = "a64, A64", + .p.video_codec = AV_CODEC_ID_A64_MULTI, .write_header = a64_write_header, .write_packet = ff_raw_write_packet, }; diff --git a/libavformat/ac4dec.c b/libavformat/ac4dec.c new file mode 100644 index 00000000000..71950f52dcd --- /dev/null +++ b/libavformat/ac4dec.c @@ -0,0 +1,104 @@ +/* + * RAW AC-4 demuxer + * Copyright (c) 2019 Paul B Mahol + * + * 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 "libavutil/avassert.h" +#include "libavutil/crc.h" +#include "avformat.h" +#include "rawdec.h" + +static int ac4_probe(const AVProbeData *p) +{ + const uint8_t *buf = p->buf; + int left = p->buf_size; + int max_frames = 0; + + while (left > 7) { + int size; + + if (buf[0] == 0xAC && + (buf[1] == 0x40 || + buf[1] == 0x41)) { + size = (buf[2] << 8) | buf[3]; + if (size == 0xFFFF) + size = 3 + ((buf[4] << 16) | (buf[5] << 8) | buf[6]); + size += 4; + if (buf[1] == 0x41) + size += 2; + max_frames++; + left -= size; + buf += size; + } else { + break; + } + } + + return FFMIN(AVPROBE_SCORE_MAX, max_frames * 7); +} + +static int ac4_read_header(AVFormatContext *s) +{ + AVStream *st; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; + st->codecpar->codec_id = AV_CODEC_ID_AC4; + + return 0; +} + +static int ac4_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVIOContext *pb = s->pb; + int64_t pos; + uint16_t sync; + int ret, size; + + if (avio_feof(s->pb)) + return AVERROR_EOF; + + pos = avio_tell(s->pb); + sync = avio_rb16(pb); + size = avio_rb16(pb); + if (size == 0xffff) + size = avio_rb24(pb); + + ret = av_get_packet(pb, pkt, size); + pkt->pos = pos; + pkt->stream_index = 0; + + if (sync == 0xAC41) + avio_skip(pb, 2); + + return ret; +} + +const AVInputFormat ff_ac4_demuxer = { + .name = "ac4", + .long_name = NULL_IF_CONFIG_SMALL("raw AC-4"), + .read_probe = ac4_probe, + .read_header = ac4_read_header, + .read_packet = ac4_read_packet, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "ac4", +}; diff --git a/libavformat/ac4enc.c b/libavformat/ac4enc.c new file mode 100644 index 00000000000..aefbfc26845 --- /dev/null +++ b/libavformat/ac4enc.c @@ -0,0 +1,102 @@ +/* + * Raw AC-4 muxer + * + * 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 "libavcodec/codec_id.h" +#include "libavcodec/codec_par.h" +#include "libavcodec/packet.h" +#include "libavutil/crc.h" +#include "libavutil/opt.h" +#include "avformat.h" +#include "mux.h" + +typedef struct AC4Context { + AVClass *class; + int write_crc; +} AC4Context; + +static int ac4_init(AVFormatContext *s) +{ + AVCodecParameters *par = s->streams[0]->codecpar; + + if (s->nb_streams != 1 || par->codec_id != AV_CODEC_ID_AC4) { + av_log(s, AV_LOG_ERROR, "Only one AC-4 stream can be muxed by the AC-4 muxer\n"); + return AVERROR(EINVAL); + } + + return 0; +} + +static int ac4_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + AC4Context *ac4 = s->priv_data; + AVIOContext *pb = s->pb; + + if (!pkt->size) + return 0; + + if (ac4->write_crc) + avio_wb16(pb, 0xAC41); + else + avio_wb16(pb, 0xAC40); + + if (pkt->size >= 0xffff) { + avio_wb16(pb, 0xffff); + avio_wb24(pb, pkt->size); + } else { + avio_wb16(pb, pkt->size); + } + + avio_write(pb, pkt->data, pkt->size); + + if (ac4->write_crc) { + uint16_t crc = av_crc(av_crc_get_table(AV_CRC_16_ANSI), 0, pkt->data, pkt->size); + avio_wl16(pb, crc); + } + + return 0; +} + +#define ENC AV_OPT_FLAG_ENCODING_PARAM +#define OFFSET(obj) offsetof(AC4Context, obj) +static const AVOption ac4_options[] = { + { "write_crc", "enable checksum", OFFSET(write_crc), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, ENC}, + { NULL }, +}; + +static const AVClass ac4_muxer_class = { + .class_name = "AC4 muxer", + .item_name = av_default_item_name, + .option = ac4_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const FFOutputFormat ff_ac4_muxer = { + .p.name = "ac4", + .p.long_name = NULL_IF_CONFIG_SMALL("raw AC-4"), + .p.mime_type = "audio/ac4", + .p.extensions = "ac4", + .priv_data_size = sizeof(AC4Context), + .p.audio_codec = AV_CODEC_ID_AC4, + .p.video_codec = AV_CODEC_ID_NONE, + .init = ac4_init, + .write_packet = ac4_write_packet, + .p.priv_class = &ac4_muxer_class, + .p.flags = AVFMT_NOTIMESTAMPS, +}; diff --git a/libavformat/adtsenc.c b/libavformat/adtsenc.c index 5f2461cc6c6..c5765cc92ee 100644 --- a/libavformat/adtsenc.c +++ b/libavformat/adtsenc.c @@ -31,6 +31,7 @@ #include "avformat.h" #include "apetag.h" #include "id3v2.h" +#include "mux.h" #define ADTS_HEADER_SIZE 7 @@ -126,14 +127,14 @@ static int adts_write_header(AVFormatContext *s) return 0; } -static int adts_write_frame_header(ADTSContext *ctx, +static int adts_write_frame_header(AVFormatContext *s, ADTSContext *ctx, uint8_t *buf, int size, int pce_size) { PutBitContext pb; unsigned full_frame_size = (unsigned)ADTS_HEADER_SIZE + size + pce_size; if (full_frame_size > ADTS_MAX_FRAME_BYTES) { - av_log(NULL, AV_LOG_ERROR, "ADTS frame size too large: %u (max %d)\n", + av_log(s, AV_LOG_ERROR, "frame size too large: %u (max %d)\n", full_frame_size, ADTS_MAX_FRAME_BYTES); return AVERROR_INVALIDDATA; } @@ -191,7 +192,7 @@ static int adts_write_packet(AVFormatContext *s, AVPacket *pkt) } } if (adts->write_adts) { - int err = adts_write_frame_header(adts, buf, pkt->size, + int err = adts_write_frame_header(s, adts, buf, pkt->size, adts->pce_size); if (err < 0) return err; @@ -232,18 +233,18 @@ static const AVClass adts_muxer_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVOutputFormat ff_adts_muxer = { - .name = "adts", - .long_name = NULL_IF_CONFIG_SMALL("ADTS AAC (Advanced Audio Coding)"), - .mime_type = "audio/aac", - .extensions = "aac,adts", +const FFOutputFormat ff_adts_muxer = { + .p.name = "adts", + .p.long_name = NULL_IF_CONFIG_SMALL("ADTS AAC (Advanced Audio Coding)"), + .p.mime_type = "audio/aac", + .p.extensions = "aac,adts", .priv_data_size = sizeof(ADTSContext), - .audio_codec = AV_CODEC_ID_AAC, - .video_codec = AV_CODEC_ID_NONE, + .p.audio_codec = AV_CODEC_ID_AAC, + .p.video_codec = AV_CODEC_ID_NONE, .init = adts_init, .write_header = adts_write_header, .write_packet = adts_write_packet, .write_trailer = adts_write_trailer, - .priv_class = &adts_muxer_class, - .flags = AVFMT_NOTIMESTAMPS, + .p.priv_class = &adts_muxer_class, + .p.flags = AVFMT_NOTIMESTAMPS, }; diff --git a/libavformat/aea.c b/libavformat/aea.c index f4b39e4f9ec..d16217381b6 100644 --- a/libavformat/aea.c +++ b/libavformat/aea.c @@ -90,13 +90,7 @@ static int aea_read_header(AVFormatContext *s) static int aea_read_packet(AVFormatContext *s, AVPacket *pkt) { - int ret = av_get_packet(s->pb, pkt, s->streams[0]->codecpar->block_align); - - pkt->stream_index = 0; - if (ret <= 0) - return AVERROR(EIO); - - return ret; + return av_get_packet(s->pb, pkt, s->streams[0]->codecpar->block_align); } const AVInputFormat ff_aea_demuxer = { diff --git a/libavformat/aiff.c b/libavformat/aiff.c index 0f25b436bd0..1ceac69a702 100644 --- a/libavformat/aiff.c +++ b/libavformat/aiff.c @@ -46,6 +46,7 @@ const AVCodecTag ff_codec_aiff_tags[] = { { AV_CODEC_ID_QDMC, MKTAG('Q','D','M','C') }, { AV_CODEC_ID_QDM2, MKTAG('Q','D','M','2') }, { AV_CODEC_ID_QCELP, MKTAG('Q','c','l','p') }, + { AV_CODEC_ID_CBD2_DPCM, MKTAG('C','B','D','2') }, { AV_CODEC_ID_SDX2_DPCM, MKTAG('S','D','X','2') }, { AV_CODEC_ID_ADPCM_IMA_WS, MKTAG('A','D','P','4') }, { AV_CODEC_ID_NONE, 0 }, diff --git a/libavformat/aiffdec.c b/libavformat/aiffdec.c index 80733e58010..1cde12c1931 100644 --- a/libavformat/aiffdec.c +++ b/libavformat/aiffdec.c @@ -164,6 +164,7 @@ static int get_aiff_header(AVFormatContext *s, int64_t size, case AV_CODEC_ID_ADPCM_IMA_WS: case AV_CODEC_ID_ADPCM_G722: case AV_CODEC_ID_MACE6: + case AV_CODEC_ID_CBD2_DPCM: case AV_CODEC_ID_SDX2_DPCM: par->block_align = 1 * channels; break; diff --git a/libavformat/aiffenc.c b/libavformat/aiffenc.c index bdaf5c2c3e4..11a5b18d573 100644 --- a/libavformat/aiffenc.c +++ b/libavformat/aiffenc.c @@ -30,6 +30,7 @@ #include "avio_internal.h" #include "isom.h" #include "id3v2.h" +#include "mux.h" typedef struct AIFFOutputContext { const AVClass *class; @@ -284,18 +285,18 @@ static const AVClass aiff_muxer_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVOutputFormat ff_aiff_muxer = { - .name = "aiff", - .long_name = NULL_IF_CONFIG_SMALL("Audio IFF"), - .mime_type = "audio/aiff", - .extensions = "aif,aiff,afc,aifc", +const FFOutputFormat ff_aiff_muxer = { + .p.name = "aiff", + .p.long_name = NULL_IF_CONFIG_SMALL("Audio IFF"), + .p.mime_type = "audio/aiff", + .p.extensions = "aif,aiff,afc,aifc", .priv_data_size = sizeof(AIFFOutputContext), - .audio_codec = AV_CODEC_ID_PCM_S16BE, - .video_codec = AV_CODEC_ID_PNG, + .p.audio_codec = AV_CODEC_ID_PCM_S16BE, + .p.video_codec = AV_CODEC_ID_PNG, .write_header = aiff_write_header, .write_packet = aiff_write_packet, .write_trailer = aiff_write_trailer, .deinit = aiff_deinit, - .codec_tag = ff_aiff_codec_tags_list, - .priv_class = &aiff_muxer_class, + .p.codec_tag = ff_aiff_codec_tags_list, + .p.priv_class = &aiff_muxer_class, }; diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 62262ae9352..6324952bd2c 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -25,82 +25,85 @@ #include "libavformat/internal.h" #include "avformat.h" +#include "mux.h" /* (de)muxers */ -extern const AVOutputFormat ff_a64_muxer; +extern const FFOutputFormat ff_a64_muxer; extern const AVInputFormat ff_aa_demuxer; extern const AVInputFormat ff_aac_demuxer; extern const AVInputFormat ff_aax_demuxer; extern const AVInputFormat ff_ac3_demuxer; -extern const AVOutputFormat ff_ac3_muxer; +extern const FFOutputFormat ff_ac3_muxer; +extern const AVInputFormat ff_ac4_demuxer; +extern const FFOutputFormat ff_ac4_muxer; extern const AVInputFormat ff_ace_demuxer; extern const AVInputFormat ff_acm_demuxer; extern const AVInputFormat ff_act_demuxer; extern const AVInputFormat ff_adf_demuxer; extern const AVInputFormat ff_adp_demuxer; extern const AVInputFormat ff_ads_demuxer; -extern const AVOutputFormat ff_adts_muxer; +extern const FFOutputFormat ff_adts_muxer; extern const AVInputFormat ff_adx_demuxer; -extern const AVOutputFormat ff_adx_muxer; +extern const FFOutputFormat ff_adx_muxer; extern const AVInputFormat ff_aea_demuxer; extern const AVInputFormat ff_afc_demuxer; extern const AVInputFormat ff_aiff_demuxer; -extern const AVOutputFormat ff_aiff_muxer; +extern const FFOutputFormat ff_aiff_muxer; extern const AVInputFormat ff_aix_demuxer; extern const AVInputFormat ff_alp_demuxer; -extern const AVOutputFormat ff_alp_muxer; +extern const FFOutputFormat ff_alp_muxer; extern const AVInputFormat ff_amr_demuxer; -extern const AVOutputFormat ff_amr_muxer; +extern const FFOutputFormat ff_amr_muxer; extern const AVInputFormat ff_amrnb_demuxer; extern const AVInputFormat ff_amrwb_demuxer; -extern const AVOutputFormat ff_amv_muxer; +extern const FFOutputFormat ff_amv_muxer; extern const AVInputFormat ff_anm_demuxer; extern const AVInputFormat ff_apac_demuxer; extern const AVInputFormat ff_apc_demuxer; extern const AVInputFormat ff_ape_demuxer; extern const AVInputFormat ff_apm_demuxer; -extern const AVOutputFormat ff_apm_muxer; +extern const FFOutputFormat ff_apm_muxer; extern const AVInputFormat ff_apng_demuxer; -extern const AVOutputFormat ff_apng_muxer; +extern const FFOutputFormat ff_apng_muxer; extern const AVInputFormat ff_aptx_demuxer; -extern const AVOutputFormat ff_aptx_muxer; +extern const FFOutputFormat ff_aptx_muxer; extern const AVInputFormat ff_aptx_hd_demuxer; -extern const AVOutputFormat ff_aptx_hd_muxer; +extern const FFOutputFormat ff_aptx_hd_muxer; extern const AVInputFormat ff_aqtitle_demuxer; extern const AVInputFormat ff_argo_asf_demuxer; -extern const AVOutputFormat ff_argo_asf_muxer; +extern const FFOutputFormat ff_argo_asf_muxer; extern const AVInputFormat ff_argo_brp_demuxer; extern const AVInputFormat ff_argo_cvg_demuxer; -extern const AVOutputFormat ff_argo_cvg_muxer; +extern const FFOutputFormat ff_argo_cvg_muxer; extern const AVInputFormat ff_asf_demuxer; -extern const AVOutputFormat ff_asf_muxer; +extern const FFOutputFormat ff_asf_muxer; extern const AVInputFormat ff_asf_o_demuxer; extern const AVInputFormat ff_ass_demuxer; -extern const AVOutputFormat ff_ass_muxer; +extern const FFOutputFormat ff_ass_muxer; extern const AVInputFormat ff_ast_demuxer; -extern const AVOutputFormat ff_ast_muxer; -extern const AVOutputFormat ff_asf_stream_muxer; +extern const FFOutputFormat ff_ast_muxer; +extern const FFOutputFormat ff_asf_stream_muxer; extern const AVInputFormat ff_au_demuxer; -extern const AVOutputFormat ff_au_muxer; +extern const FFOutputFormat ff_au_muxer; extern const AVInputFormat ff_av1_demuxer; extern const AVInputFormat ff_avi_demuxer; -extern const AVOutputFormat ff_avi_muxer; -extern const AVOutputFormat ff_avif_muxer; +extern const FFOutputFormat ff_avi_muxer; +extern const FFOutputFormat ff_avif_muxer; extern const AVInputFormat ff_avisynth_demuxer; -extern const AVOutputFormat ff_avm2_muxer; +extern const FFOutputFormat ff_avm2_muxer; extern const AVInputFormat ff_avr_demuxer; extern const AVInputFormat ff_avs_demuxer; extern const AVInputFormat ff_avs2_demuxer; -extern const AVOutputFormat ff_avs2_muxer; +extern const FFOutputFormat ff_avs2_muxer; extern const AVInputFormat ff_avs3_demuxer; -extern const AVOutputFormat ff_avs3_muxer; +extern const FFOutputFormat ff_avs3_muxer; extern const AVInputFormat ff_bethsoftvid_demuxer; extern const AVInputFormat ff_bfi_demuxer; extern const AVInputFormat ff_bintext_demuxer; extern const AVInputFormat ff_bink_demuxer; extern const AVInputFormat ff_binka_demuxer; extern const AVInputFormat ff_bit_demuxer; -extern const AVOutputFormat ff_bit_muxer; +extern const FFOutputFormat ff_bit_muxer; extern const AVInputFormat ff_bitpacked_demuxer; extern const AVInputFormat ff_bmv_demuxer; extern const AVInputFormat ff_bfstm_demuxer; @@ -109,190 +112,193 @@ extern const AVInputFormat ff_boa_demuxer; extern const AVInputFormat ff_bonk_demuxer; extern const AVInputFormat ff_c93_demuxer; extern const AVInputFormat ff_caf_demuxer; -extern const AVOutputFormat ff_caf_muxer; +extern const FFOutputFormat ff_caf_muxer; extern const AVInputFormat ff_cavsvideo_demuxer; -extern const AVOutputFormat ff_cavsvideo_muxer; +extern const FFOutputFormat ff_cavsvideo_muxer; extern const AVInputFormat ff_cdg_demuxer; extern const AVInputFormat ff_cdxl_demuxer; extern const AVInputFormat ff_cine_demuxer; extern const AVInputFormat ff_codec2_demuxer; -extern const AVOutputFormat ff_codec2_muxer; +extern const FFOutputFormat ff_codec2_muxer; extern const AVInputFormat ff_codec2raw_demuxer; -extern const AVOutputFormat ff_codec2raw_muxer; +extern const FFOutputFormat ff_codec2raw_muxer; extern const AVInputFormat ff_concat_demuxer; -extern const AVOutputFormat ff_crc_muxer; +extern const FFOutputFormat ff_crc_muxer; extern const AVInputFormat ff_dash_demuxer; -extern const AVOutputFormat ff_dash_muxer; +extern const FFOutputFormat ff_dash_muxer; extern const AVInputFormat ff_data_demuxer; -extern const AVOutputFormat ff_data_muxer; +extern const FFOutputFormat ff_data_muxer; extern const AVInputFormat ff_daud_demuxer; -extern const AVOutputFormat ff_daud_muxer; +extern const FFOutputFormat ff_daud_muxer; extern const AVInputFormat ff_dcstr_demuxer; extern const AVInputFormat ff_derf_demuxer; extern const AVInputFormat ff_dfa_demuxer; extern const AVInputFormat ff_dfpwm_demuxer; -extern const AVOutputFormat ff_dfpwm_muxer; +extern const FFOutputFormat ff_dfpwm_muxer; extern const AVInputFormat ff_dhav_demuxer; extern const AVInputFormat ff_dirac_demuxer; -extern const AVOutputFormat ff_dirac_muxer; +extern const FFOutputFormat ff_dirac_muxer; extern const AVInputFormat ff_dnxhd_demuxer; -extern const AVOutputFormat ff_dnxhd_muxer; +extern const FFOutputFormat ff_dnxhd_muxer; extern const AVInputFormat ff_dsf_demuxer; extern const AVInputFormat ff_dsicin_demuxer; extern const AVInputFormat ff_dss_demuxer; extern const AVInputFormat ff_dts_demuxer; -extern const AVOutputFormat ff_dts_muxer; +extern const FFOutputFormat ff_dts_muxer; extern const AVInputFormat ff_dtshd_demuxer; extern const AVInputFormat ff_dv_demuxer; -extern const AVOutputFormat ff_dv_muxer; +extern const FFOutputFormat ff_dv_muxer; extern const AVInputFormat ff_dvbsub_demuxer; extern const AVInputFormat ff_dvbtxt_demuxer; extern const AVInputFormat ff_dxa_demuxer; extern const AVInputFormat ff_ea_demuxer; extern const AVInputFormat ff_ea_cdata_demuxer; extern const AVInputFormat ff_eac3_demuxer; -extern const AVOutputFormat ff_eac3_muxer; +extern const FFOutputFormat ff_eac3_muxer; extern const AVInputFormat ff_epaf_demuxer; -extern const AVOutputFormat ff_f4v_muxer; +extern const AVInputFormat ff_evc_demuxer; +extern const FFOutputFormat ff_evc_muxer; +extern const FFOutputFormat ff_f4v_muxer; extern const AVInputFormat ff_ffmetadata_demuxer; -extern const AVOutputFormat ff_ffmetadata_muxer; -extern const AVOutputFormat ff_fifo_muxer; -extern const AVOutputFormat ff_fifo_test_muxer; +extern const FFOutputFormat ff_ffmetadata_muxer; +extern const FFOutputFormat ff_fifo_muxer; +extern const FFOutputFormat ff_fifo_test_muxer; extern const AVInputFormat ff_filmstrip_demuxer; -extern const AVOutputFormat ff_filmstrip_muxer; +extern const FFOutputFormat ff_filmstrip_muxer; extern const AVInputFormat ff_fits_demuxer; -extern const AVOutputFormat ff_fits_muxer; +extern const FFOutputFormat ff_fits_muxer; extern const AVInputFormat ff_flac_demuxer; -extern const AVOutputFormat ff_flac_muxer; +extern const FFOutputFormat ff_flac_muxer; extern const AVInputFormat ff_flic_demuxer; extern const AVInputFormat ff_flv_demuxer; -extern const AVOutputFormat ff_flv_muxer; +extern const FFOutputFormat ff_flv_muxer; extern const AVInputFormat ff_live_flv_demuxer; extern const AVInputFormat ff_fourxm_demuxer; -extern const AVOutputFormat ff_framecrc_muxer; -extern const AVOutputFormat ff_framehash_muxer; -extern const AVOutputFormat ff_framemd5_muxer; +extern const FFOutputFormat ff_framecrc_muxer; +extern const FFOutputFormat ff_framehash_muxer; +extern const FFOutputFormat ff_framemd5_muxer; extern const AVInputFormat ff_frm_demuxer; extern const AVInputFormat ff_fsb_demuxer; extern const AVInputFormat ff_fwse_demuxer; extern const AVInputFormat ff_g722_demuxer; -extern const AVOutputFormat ff_g722_muxer; +extern const FFOutputFormat ff_g722_muxer; extern const AVInputFormat ff_g723_1_demuxer; -extern const AVOutputFormat ff_g723_1_muxer; +extern const FFOutputFormat ff_g723_1_muxer; extern const AVInputFormat ff_g726_demuxer; -extern const AVOutputFormat ff_g726_muxer; +extern const FFOutputFormat ff_g726_muxer; extern const AVInputFormat ff_g726le_demuxer; -extern const AVOutputFormat ff_g726le_muxer; +extern const FFOutputFormat ff_g726le_muxer; extern const AVInputFormat ff_g729_demuxer; extern const AVInputFormat ff_gdv_demuxer; extern const AVInputFormat ff_genh_demuxer; extern const AVInputFormat ff_gif_demuxer; -extern const AVOutputFormat ff_gif_muxer; +extern const FFOutputFormat ff_gif_muxer; extern const AVInputFormat ff_gsm_demuxer; -extern const AVOutputFormat ff_gsm_muxer; +extern const FFOutputFormat ff_gsm_muxer; extern const AVInputFormat ff_gxf_demuxer; -extern const AVOutputFormat ff_gxf_muxer; +extern const FFOutputFormat ff_gxf_muxer; extern const AVInputFormat ff_h261_demuxer; -extern const AVOutputFormat ff_h261_muxer; +extern const FFOutputFormat ff_h261_muxer; extern const AVInputFormat ff_h263_demuxer; -extern const AVOutputFormat ff_h263_muxer; +extern const FFOutputFormat ff_h263_muxer; extern const AVInputFormat ff_h264_demuxer; -extern const AVOutputFormat ff_h264_muxer; -extern const AVOutputFormat ff_hash_muxer; +extern const FFOutputFormat ff_h264_muxer; +extern const FFOutputFormat ff_hash_muxer; extern const AVInputFormat ff_hca_demuxer; extern const AVInputFormat ff_hcom_demuxer; -extern const AVOutputFormat ff_hds_muxer; +extern const FFOutputFormat ff_hds_muxer; extern const AVInputFormat ff_hevc_demuxer; -extern const AVOutputFormat ff_hevc_muxer; +extern const FFOutputFormat ff_hevc_muxer; extern const AVInputFormat ff_hls_demuxer; -extern const AVOutputFormat ff_hls_muxer; +extern const FFOutputFormat ff_hls_muxer; extern const AVInputFormat ff_hnm_demuxer; extern const AVInputFormat ff_ico_demuxer; -extern const AVOutputFormat ff_ico_muxer; +extern const FFOutputFormat ff_ico_muxer; extern const AVInputFormat ff_idcin_demuxer; extern const AVInputFormat ff_idf_demuxer; extern const AVInputFormat ff_iff_demuxer; extern const AVInputFormat ff_ifv_demuxer; extern const AVInputFormat ff_ilbc_demuxer; -extern const AVOutputFormat ff_ilbc_muxer; +extern const FFOutputFormat ff_ilbc_muxer; extern const AVInputFormat ff_image2_demuxer; -extern const AVOutputFormat ff_image2_muxer; +extern const FFOutputFormat ff_image2_muxer; extern const AVInputFormat ff_image2pipe_demuxer; -extern const AVOutputFormat ff_image2pipe_muxer; +extern const FFOutputFormat ff_image2pipe_muxer; extern const AVInputFormat ff_image2_alias_pix_demuxer; extern const AVInputFormat ff_image2_brender_pix_demuxer; extern const AVInputFormat ff_imf_demuxer; extern const AVInputFormat ff_ingenient_demuxer; extern const AVInputFormat ff_ipmovie_demuxer; -extern const AVOutputFormat ff_ipod_muxer; +extern const FFOutputFormat ff_ipod_muxer; extern const AVInputFormat ff_ipu_demuxer; extern const AVInputFormat ff_ircam_demuxer; -extern const AVOutputFormat ff_ircam_muxer; -extern const AVOutputFormat ff_ismv_muxer; +extern const FFOutputFormat ff_ircam_muxer; +extern const FFOutputFormat ff_ismv_muxer; extern const AVInputFormat ff_iss_demuxer; extern const AVInputFormat ff_iv8_demuxer; extern const AVInputFormat ff_ivf_demuxer; -extern const AVOutputFormat ff_ivf_muxer; +extern const FFOutputFormat ff_ivf_muxer; extern const AVInputFormat ff_ivr_demuxer; extern const AVInputFormat ff_jacosub_demuxer; -extern const AVOutputFormat ff_jacosub_muxer; +extern const FFOutputFormat ff_jacosub_muxer; extern const AVInputFormat ff_jv_demuxer; +extern const AVInputFormat ff_jpegxl_anim_demuxer; extern const AVInputFormat ff_kux_demuxer; extern const AVInputFormat ff_kvag_demuxer; -extern const AVOutputFormat ff_kvag_muxer; +extern const FFOutputFormat ff_kvag_muxer; extern const AVInputFormat ff_laf_demuxer; -extern const AVOutputFormat ff_latm_muxer; +extern const FFOutputFormat ff_latm_muxer; extern const AVInputFormat ff_lmlm4_demuxer; extern const AVInputFormat ff_loas_demuxer; extern const AVInputFormat ff_luodat_demuxer; extern const AVInputFormat ff_lrc_demuxer; -extern const AVOutputFormat ff_lrc_muxer; +extern const FFOutputFormat ff_lrc_muxer; extern const AVInputFormat ff_lvf_demuxer; extern const AVInputFormat ff_lxf_demuxer; extern const AVInputFormat ff_m4v_demuxer; -extern const AVOutputFormat ff_m4v_muxer; +extern const FFOutputFormat ff_m4v_muxer; extern const AVInputFormat ff_mca_demuxer; extern const AVInputFormat ff_mcc_demuxer; -extern const AVOutputFormat ff_md5_muxer; +extern const FFOutputFormat ff_md5_muxer; extern const AVInputFormat ff_matroska_demuxer; -extern const AVOutputFormat ff_matroska_muxer; -extern const AVOutputFormat ff_matroska_audio_muxer; +extern const FFOutputFormat ff_matroska_muxer; +extern const FFOutputFormat ff_matroska_audio_muxer; extern const AVInputFormat ff_mgsts_demuxer; extern const AVInputFormat ff_microdvd_demuxer; -extern const AVOutputFormat ff_microdvd_muxer; +extern const FFOutputFormat ff_microdvd_muxer; extern const AVInputFormat ff_mjpeg_demuxer; -extern const AVOutputFormat ff_mjpeg_muxer; +extern const FFOutputFormat ff_mjpeg_muxer; extern const AVInputFormat ff_mjpeg_2000_demuxer; extern const AVInputFormat ff_mlp_demuxer; -extern const AVOutputFormat ff_mlp_muxer; +extern const FFOutputFormat ff_mlp_muxer; extern const AVInputFormat ff_mlv_demuxer; extern const AVInputFormat ff_mm_demuxer; extern const AVInputFormat ff_mmf_demuxer; -extern const AVOutputFormat ff_mmf_muxer; +extern const FFOutputFormat ff_mmf_muxer; extern const AVInputFormat ff_mods_demuxer; extern const AVInputFormat ff_moflex_demuxer; extern const AVInputFormat ff_mov_demuxer; -extern const AVOutputFormat ff_mov_muxer; -extern const AVOutputFormat ff_mp2_muxer; +extern const FFOutputFormat ff_mov_muxer; +extern const FFOutputFormat ff_mp2_muxer; extern const AVInputFormat ff_mp3_demuxer; -extern const AVOutputFormat ff_mp3_muxer; -extern const AVOutputFormat ff_mp4_muxer; +extern const FFOutputFormat ff_mp3_muxer; +extern const FFOutputFormat ff_mp4_muxer; extern const AVInputFormat ff_mpc_demuxer; extern const AVInputFormat ff_mpc8_demuxer; -extern const AVOutputFormat ff_mpeg1system_muxer; -extern const AVOutputFormat ff_mpeg1vcd_muxer; -extern const AVOutputFormat ff_mpeg1video_muxer; -extern const AVOutputFormat ff_mpeg2dvd_muxer; -extern const AVOutputFormat ff_mpeg2svcd_muxer; -extern const AVOutputFormat ff_mpeg2video_muxer; -extern const AVOutputFormat ff_mpeg2vob_muxer; +extern const FFOutputFormat ff_mpeg1system_muxer; +extern const FFOutputFormat ff_mpeg1vcd_muxer; +extern const FFOutputFormat ff_mpeg1video_muxer; +extern const FFOutputFormat ff_mpeg2dvd_muxer; +extern const FFOutputFormat ff_mpeg2svcd_muxer; +extern const FFOutputFormat ff_mpeg2video_muxer; +extern const FFOutputFormat ff_mpeg2vob_muxer; extern const AVInputFormat ff_mpegps_demuxer; extern const AVInputFormat ff_mpegts_demuxer; -extern const AVOutputFormat ff_mpegts_muxer; +extern const FFOutputFormat ff_mpegts_muxer; extern const AVInputFormat ff_mpegtsraw_demuxer; extern const AVInputFormat ff_mpegvideo_demuxer; extern const AVInputFormat ff_mpjpeg_demuxer; -extern const AVOutputFormat ff_mpjpeg_muxer; +extern const FFOutputFormat ff_mpjpeg_muxer; extern const AVInputFormat ff_mpl2_demuxer; extern const AVInputFormat ff_mpsub_demuxer; extern const AVInputFormat ff_msf_demuxer; @@ -304,114 +310,117 @@ extern const AVInputFormat ff_musx_demuxer; extern const AVInputFormat ff_mv_demuxer; extern const AVInputFormat ff_mvi_demuxer; extern const AVInputFormat ff_mxf_demuxer; -extern const AVOutputFormat ff_mxf_muxer; -extern const AVOutputFormat ff_mxf_d10_muxer; -extern const AVOutputFormat ff_mxf_opatom_muxer; +extern const FFOutputFormat ff_mxf_muxer; +extern const FFOutputFormat ff_mxf_d10_muxer; +extern const FFOutputFormat ff_mxf_opatom_muxer; extern const AVInputFormat ff_mxg_demuxer; extern const AVInputFormat ff_nc_demuxer; extern const AVInputFormat ff_nistsphere_demuxer; extern const AVInputFormat ff_nsp_demuxer; extern const AVInputFormat ff_nsv_demuxer; -extern const AVOutputFormat ff_null_muxer; +extern const FFOutputFormat ff_null_muxer; extern const AVInputFormat ff_nut_demuxer; -extern const AVOutputFormat ff_nut_muxer; +extern const FFOutputFormat ff_nut_muxer; extern const AVInputFormat ff_nuv_demuxer; extern const AVInputFormat ff_obu_demuxer; -extern const AVOutputFormat ff_obu_muxer; -extern const AVOutputFormat ff_oga_muxer; +extern const FFOutputFormat ff_obu_muxer; +extern const FFOutputFormat ff_oga_muxer; extern const AVInputFormat ff_ogg_demuxer; -extern const AVOutputFormat ff_ogg_muxer; -extern const AVOutputFormat ff_ogv_muxer; +extern const FFOutputFormat ff_ogg_muxer; +extern const FFOutputFormat ff_ogv_muxer; extern const AVInputFormat ff_oma_demuxer; -extern const AVOutputFormat ff_oma_muxer; -extern const AVOutputFormat ff_opus_muxer; +extern const FFOutputFormat ff_oma_muxer; +extern const FFOutputFormat ff_opus_muxer; extern const AVInputFormat ff_paf_demuxer; extern const AVInputFormat ff_pcm_alaw_demuxer; -extern const AVOutputFormat ff_pcm_alaw_muxer; +extern const FFOutputFormat ff_pcm_alaw_muxer; extern const AVInputFormat ff_pcm_mulaw_demuxer; -extern const AVOutputFormat ff_pcm_mulaw_muxer; +extern const FFOutputFormat ff_pcm_mulaw_muxer; extern const AVInputFormat ff_pcm_vidc_demuxer; -extern const AVOutputFormat ff_pcm_vidc_muxer; +extern const FFOutputFormat ff_pcm_vidc_muxer; extern const AVInputFormat ff_pcm_f64be_demuxer; -extern const AVOutputFormat ff_pcm_f64be_muxer; +extern const FFOutputFormat ff_pcm_f64be_muxer; extern const AVInputFormat ff_pcm_f64le_demuxer; -extern const AVOutputFormat ff_pcm_f64le_muxer; +extern const FFOutputFormat ff_pcm_f64le_muxer; extern const AVInputFormat ff_pcm_f32be_demuxer; -extern const AVOutputFormat ff_pcm_f32be_muxer; +extern const FFOutputFormat ff_pcm_f32be_muxer; extern const AVInputFormat ff_pcm_f32le_demuxer; -extern const AVOutputFormat ff_pcm_f32le_muxer; +extern const FFOutputFormat ff_pcm_f32le_muxer; extern const AVInputFormat ff_pcm_s32be_demuxer; -extern const AVOutputFormat ff_pcm_s32be_muxer; +extern const FFOutputFormat ff_pcm_s32be_muxer; extern const AVInputFormat ff_pcm_s32le_demuxer; -extern const AVOutputFormat ff_pcm_s32le_muxer; +extern const FFOutputFormat ff_pcm_s32le_muxer; extern const AVInputFormat ff_pcm_s24be_demuxer; -extern const AVOutputFormat ff_pcm_s24be_muxer; +extern const FFOutputFormat ff_pcm_s24be_muxer; extern const AVInputFormat ff_pcm_s24le_demuxer; -extern const AVOutputFormat ff_pcm_s24le_muxer; +extern const FFOutputFormat ff_pcm_s24le_muxer; extern const AVInputFormat ff_pcm_s16be_demuxer; -extern const AVOutputFormat ff_pcm_s16be_muxer; +extern const FFOutputFormat ff_pcm_s16be_muxer; extern const AVInputFormat ff_pcm_s16le_demuxer; -extern const AVOutputFormat ff_pcm_s16le_muxer; +extern const FFOutputFormat ff_pcm_s16le_muxer; extern const AVInputFormat ff_pcm_s8_demuxer; -extern const AVOutputFormat ff_pcm_s8_muxer; +extern const FFOutputFormat ff_pcm_s8_muxer; extern const AVInputFormat ff_pcm_u32be_demuxer; -extern const AVOutputFormat ff_pcm_u32be_muxer; +extern const FFOutputFormat ff_pcm_u32be_muxer; extern const AVInputFormat ff_pcm_u32le_demuxer; -extern const AVOutputFormat ff_pcm_u32le_muxer; +extern const FFOutputFormat ff_pcm_u32le_muxer; extern const AVInputFormat ff_pcm_u24be_demuxer; -extern const AVOutputFormat ff_pcm_u24be_muxer; +extern const FFOutputFormat ff_pcm_u24be_muxer; extern const AVInputFormat ff_pcm_u24le_demuxer; -extern const AVOutputFormat ff_pcm_u24le_muxer; +extern const FFOutputFormat ff_pcm_u24le_muxer; extern const AVInputFormat ff_pcm_u16be_demuxer; -extern const AVOutputFormat ff_pcm_u16be_muxer; +extern const FFOutputFormat ff_pcm_u16be_muxer; extern const AVInputFormat ff_pcm_u16le_demuxer; -extern const AVOutputFormat ff_pcm_u16le_muxer; +extern const FFOutputFormat ff_pcm_u16le_muxer; extern const AVInputFormat ff_pcm_u8_demuxer; -extern const AVOutputFormat ff_pcm_u8_muxer; +extern const FFOutputFormat ff_pcm_u8_muxer; +extern const AVInputFormat ff_pdv_demuxer; extern const AVInputFormat ff_pjs_demuxer; extern const AVInputFormat ff_pmp_demuxer; extern const AVInputFormat ff_pp_bnk_demuxer; -extern const AVOutputFormat ff_psp_muxer; +extern const FFOutputFormat ff_psp_muxer; extern const AVInputFormat ff_pva_demuxer; extern const AVInputFormat ff_pvf_demuxer; extern const AVInputFormat ff_qcp_demuxer; extern const AVInputFormat ff_r3d_demuxer; extern const AVInputFormat ff_rawvideo_demuxer; -extern const AVOutputFormat ff_rawvideo_muxer; +extern const FFOutputFormat ff_rawvideo_muxer; extern const AVInputFormat ff_realtext_demuxer; extern const AVInputFormat ff_redspark_demuxer; +extern const AVInputFormat ff_rka_demuxer; extern const AVInputFormat ff_rl2_demuxer; extern const AVInputFormat ff_rm_demuxer; -extern const AVOutputFormat ff_rm_muxer; +extern const FFOutputFormat ff_rm_muxer; extern const AVInputFormat ff_roq_demuxer; -extern const AVOutputFormat ff_roq_muxer; +extern const FFOutputFormat ff_roq_muxer; extern const AVInputFormat ff_rpl_demuxer; extern const AVInputFormat ff_rsd_demuxer; extern const AVInputFormat ff_rso_demuxer; -extern const AVOutputFormat ff_rso_muxer; +extern const FFOutputFormat ff_rso_muxer; extern const AVInputFormat ff_rtp_demuxer; -extern const AVOutputFormat ff_rtp_muxer; -extern const AVOutputFormat ff_rtp_mpegts_muxer; +extern const FFOutputFormat ff_rtp_muxer; +extern const FFOutputFormat ff_rtp_mpegts_muxer; extern const AVInputFormat ff_rtsp_demuxer; -extern const AVOutputFormat ff_rtsp_muxer; +extern const FFOutputFormat ff_rtsp_muxer; extern const AVInputFormat ff_s337m_demuxer; extern const AVInputFormat ff_sami_demuxer; extern const AVInputFormat ff_sap_demuxer; -extern const AVOutputFormat ff_sap_muxer; +extern const FFOutputFormat ff_sap_muxer; extern const AVInputFormat ff_sbc_demuxer; -extern const AVOutputFormat ff_sbc_muxer; +extern const FFOutputFormat ff_sbc_muxer; extern const AVInputFormat ff_sbg_demuxer; extern const AVInputFormat ff_scc_demuxer; -extern const AVOutputFormat ff_scc_muxer; +extern const FFOutputFormat ff_scc_muxer; extern const AVInputFormat ff_scd_demuxer; +extern const AVInputFormat ff_sdns_demuxer; extern const AVInputFormat ff_sdp_demuxer; extern const AVInputFormat ff_sdr2_demuxer; extern const AVInputFormat ff_sds_demuxer; extern const AVInputFormat ff_sdx_demuxer; extern const AVInputFormat ff_segafilm_demuxer; -extern const AVOutputFormat ff_segafilm_muxer; -extern const AVOutputFormat ff_segment_muxer; -extern const AVOutputFormat ff_stream_segment_muxer; +extern const FFOutputFormat ff_segafilm_muxer; +extern const FFOutputFormat ff_segment_muxer; +extern const FFOutputFormat ff_stream_segment_muxer; extern const AVInputFormat ff_ser_demuxer; extern const AVInputFormat ff_sga_demuxer; extern const AVInputFormat ff_shorten_demuxer; @@ -420,92 +429,97 @@ extern const AVInputFormat ff_simbiosis_imx_demuxer; extern const AVInputFormat ff_sln_demuxer; extern const AVInputFormat ff_smacker_demuxer; extern const AVInputFormat ff_smjpeg_demuxer; -extern const AVOutputFormat ff_smjpeg_muxer; -extern const AVOutputFormat ff_smoothstreaming_muxer; +extern const FFOutputFormat ff_smjpeg_muxer; +extern const FFOutputFormat ff_smoothstreaming_muxer; extern const AVInputFormat ff_smush_demuxer; extern const AVInputFormat ff_sol_demuxer; extern const AVInputFormat ff_sox_demuxer; -extern const AVOutputFormat ff_sox_muxer; -extern const AVOutputFormat ff_spx_muxer; +extern const FFOutputFormat ff_sox_muxer; +extern const FFOutputFormat ff_spx_muxer; extern const AVInputFormat ff_spdif_demuxer; -extern const AVOutputFormat ff_spdif_muxer; +extern const FFOutputFormat ff_spdif_muxer; extern const AVInputFormat ff_srt_demuxer; -extern const AVOutputFormat ff_srt_muxer; +extern const FFOutputFormat ff_srt_muxer; extern const AVInputFormat ff_str_demuxer; extern const AVInputFormat ff_stl_demuxer; -extern const AVOutputFormat ff_streamhash_muxer; +extern const FFOutputFormat ff_streamhash_muxer; extern const AVInputFormat ff_subviewer1_demuxer; extern const AVInputFormat ff_subviewer_demuxer; extern const AVInputFormat ff_sup_demuxer; -extern const AVOutputFormat ff_sup_muxer; +extern const FFOutputFormat ff_sup_muxer; extern const AVInputFormat ff_svag_demuxer; extern const AVInputFormat ff_svs_demuxer; extern const AVInputFormat ff_swf_demuxer; -extern const AVOutputFormat ff_swf_muxer; +extern const FFOutputFormat ff_swf_muxer; extern const AVInputFormat ff_tak_demuxer; -extern const AVOutputFormat ff_tee_muxer; +extern const FFOutputFormat ff_tee_muxer; extern const AVInputFormat ff_tedcaptions_demuxer; -extern const AVOutputFormat ff_tg2_muxer; -extern const AVOutputFormat ff_tgp_muxer; +extern const FFOutputFormat ff_tg2_muxer; +extern const FFOutputFormat ff_tgp_muxer; extern const AVInputFormat ff_thp_demuxer; extern const AVInputFormat ff_threedostr_demuxer; extern const AVInputFormat ff_tiertexseq_demuxer; -extern const AVOutputFormat ff_mkvtimestamp_v2_muxer; +extern const FFOutputFormat ff_mkvtimestamp_v2_muxer; extern const AVInputFormat ff_tmv_demuxer; extern const AVInputFormat ff_truehd_demuxer; -extern const AVOutputFormat ff_truehd_muxer; +extern const FFOutputFormat ff_truehd_muxer; extern const AVInputFormat ff_tta_demuxer; -extern const AVOutputFormat ff_tta_muxer; -extern const AVOutputFormat ff_ttml_muxer; +extern const FFOutputFormat ff_tta_muxer; +extern const FFOutputFormat ff_ttml_muxer; extern const AVInputFormat ff_txd_demuxer; extern const AVInputFormat ff_tty_demuxer; extern const AVInputFormat ff_ty_demuxer; -extern const AVOutputFormat ff_uncodedframecrc_muxer; +extern const FFOutputFormat ff_uncodedframecrc_muxer; extern const AVInputFormat ff_v210_demuxer; extern const AVInputFormat ff_v210x_demuxer; extern const AVInputFormat ff_vag_demuxer; extern const AVInputFormat ff_vc1_demuxer; -extern const AVOutputFormat ff_vc1_muxer; +extern const FFOutputFormat ff_vc1_muxer; extern const AVInputFormat ff_vc1t_demuxer; -extern const AVOutputFormat ff_vc1t_muxer; +extern const FFOutputFormat ff_vc1t_muxer; extern const AVInputFormat ff_vividas_demuxer; extern const AVInputFormat ff_vivo_demuxer; extern const AVInputFormat ff_vmd_demuxer; extern const AVInputFormat ff_vobsub_demuxer; extern const AVInputFormat ff_voc_demuxer; -extern const AVOutputFormat ff_voc_muxer; +extern const FFOutputFormat ff_voc_muxer; extern const AVInputFormat ff_vpk_demuxer; extern const AVInputFormat ff_vplayer_demuxer; extern const AVInputFormat ff_vqf_demuxer; +extern const AVInputFormat ff_vvc_demuxer; +extern const FFOutputFormat ff_vvc_muxer; extern const AVInputFormat ff_w64_demuxer; -extern const AVOutputFormat ff_w64_muxer; +extern const FFOutputFormat ff_w64_muxer; +extern const AVInputFormat ff_wady_demuxer; +extern const AVInputFormat ff_wavarc_demuxer; extern const AVInputFormat ff_wav_demuxer; -extern const AVOutputFormat ff_wav_muxer; +extern const FFOutputFormat ff_wav_muxer; extern const AVInputFormat ff_wc3_demuxer; -extern const AVOutputFormat ff_webm_muxer; +extern const FFOutputFormat ff_webm_muxer; extern const AVInputFormat ff_webm_dash_manifest_demuxer; -extern const AVOutputFormat ff_webm_dash_manifest_muxer; -extern const AVOutputFormat ff_webm_chunk_muxer; -extern const AVOutputFormat ff_webp_muxer; +extern const FFOutputFormat ff_webm_dash_manifest_muxer; +extern const FFOutputFormat ff_webm_chunk_muxer; +extern const FFOutputFormat ff_webp_muxer; extern const AVInputFormat ff_webvtt_demuxer; -extern const AVOutputFormat ff_webvtt_muxer; +extern const FFOutputFormat ff_webvtt_muxer; extern const AVInputFormat ff_wsaud_demuxer; -extern const AVOutputFormat ff_wsaud_muxer; +extern const FFOutputFormat ff_wsaud_muxer; extern const AVInputFormat ff_wsd_demuxer; extern const AVInputFormat ff_wsvqa_demuxer; extern const AVInputFormat ff_wtv_demuxer; -extern const AVOutputFormat ff_wtv_muxer; +extern const FFOutputFormat ff_wtv_muxer; extern const AVInputFormat ff_wve_demuxer; extern const AVInputFormat ff_wv_demuxer; -extern const AVOutputFormat ff_wv_muxer; +extern const FFOutputFormat ff_wv_muxer; extern const AVInputFormat ff_xa_demuxer; extern const AVInputFormat ff_xbin_demuxer; +extern const AVInputFormat ff_xmd_demuxer; extern const AVInputFormat ff_xmv_demuxer; extern const AVInputFormat ff_xvag_demuxer; extern const AVInputFormat ff_xwma_demuxer; extern const AVInputFormat ff_yop_demuxer; extern const AVInputFormat ff_yuv4mpegpipe_demuxer; -extern const AVOutputFormat ff_yuv4mpegpipe_muxer; +extern const FFOutputFormat ff_yuv4mpegpipe_muxer; /* image demuxers */ extern const AVInputFormat ff_image_bmp_pipe_demuxer; extern const AVInputFormat ff_image_cri_pipe_demuxer; @@ -545,7 +559,7 @@ extern const AVInputFormat ff_image_xpm_pipe_demuxer; extern const AVInputFormat ff_image_xwd_pipe_demuxer; /* external libraries */ -extern const AVOutputFormat ff_chromaprint_muxer; +extern const FFOutputFormat ff_chromaprint_muxer; extern const AVInputFormat ff_libgme_demuxer; extern const AVInputFormat ff_libmodplug_demuxer; extern const AVInputFormat ff_libopenmpt_demuxer; @@ -561,19 +575,21 @@ const AVOutputFormat *av_muxer_iterate(void **opaque) { static const uintptr_t size = sizeof(muxer_list)/sizeof(muxer_list[0]) - 1; uintptr_t i = (uintptr_t)*opaque; - const AVOutputFormat *f = NULL; + const FFOutputFormat *f = NULL; uintptr_t tmp; if (i < size) { f = muxer_list[i]; } else if (tmp = atomic_load_explicit(&outdev_list_intptr, memory_order_relaxed)) { - const AVOutputFormat *const *outdev_list = (const AVOutputFormat *const *)tmp; + const FFOutputFormat *const *outdev_list = (const FFOutputFormat *const *)tmp; f = outdev_list[i - size]; } - if (f) + if (f) { *opaque = (void*)(i + 1); - return f; + return &f->p; + } + return NULL; } const AVInputFormat *av_demuxer_iterate(void **opaque) @@ -595,7 +611,7 @@ const AVInputFormat *av_demuxer_iterate(void **opaque) return f; } -void avpriv_register_devices(const AVOutputFormat * const o[], const AVInputFormat * const i[]) +void avpriv_register_devices(const FFOutputFormat * const o[], const AVInputFormat * const i[]) { atomic_store_explicit(&outdev_list_intptr, (uintptr_t)o, memory_order_relaxed); atomic_store_explicit(&indev_list_intptr, (uintptr_t)i, memory_order_relaxed); diff --git a/libavformat/alp.c b/libavformat/alp.c index 40e4890a387..8c6066a59c6 100644 --- a/libavformat/alp.c +++ b/libavformat/alp.c @@ -25,6 +25,7 @@ #include "libavutil/channel_layout.h" #include "avformat.h" #include "internal.h" +#include "mux.h" #include "rawenc.h" #include "libavutil/intreadwrite.h" #include "libavutil/internal.h" @@ -290,16 +291,16 @@ static const AVClass alp_muxer_class = { .version = LIBAVUTIL_VERSION_INT }; -const AVOutputFormat ff_alp_muxer = { - .name = "alp", - .long_name = NULL_IF_CONFIG_SMALL("LEGO Racers ALP"), - .extensions = "tun,pcm", - .audio_codec = AV_CODEC_ID_ADPCM_IMA_ALP, - .video_codec = AV_CODEC_ID_NONE, +const FFOutputFormat ff_alp_muxer = { + .p.name = "alp", + .p.long_name = NULL_IF_CONFIG_SMALL("LEGO Racers ALP"), + .p.extensions = "tun,pcm", + .p.audio_codec = AV_CODEC_ID_ADPCM_IMA_ALP, + .p.video_codec = AV_CODEC_ID_NONE, + .p.priv_class = &alp_muxer_class, .init = alp_write_init, .write_header = alp_write_header, .write_packet = ff_raw_write_packet, - .priv_class = &alp_muxer_class, .priv_data_size = sizeof(ALPMuxContext) }; #endif diff --git a/libavformat/amr.c b/libavformat/amr.c index 88f07ce0bac..b6615d8295c 100644 --- a/libavformat/amr.c +++ b/libavformat/amr.c @@ -30,6 +30,7 @@ Write and read amr data according to RFC3267, http://www.ietf.org/rfc/rfc3267.tx #include "avformat.h" #include "avio_internal.h" #include "internal.h" +#include "mux.h" #include "rawdec.h" #include "rawenc.h" @@ -266,15 +267,15 @@ const AVInputFormat ff_amrwb_demuxer = { #endif #if CONFIG_AMR_MUXER -const AVOutputFormat ff_amr_muxer = { - .name = "amr", - .long_name = NULL_IF_CONFIG_SMALL("3GPP AMR"), - .mime_type = "audio/amr", - .extensions = "amr", - .audio_codec = AV_CODEC_ID_AMR_NB, - .video_codec = AV_CODEC_ID_NONE, +const FFOutputFormat ff_amr_muxer = { + .p.name = "amr", + .p.long_name = NULL_IF_CONFIG_SMALL("3GPP AMR"), + .p.mime_type = "audio/amr", + .p.extensions = "amr", + .p.audio_codec = AV_CODEC_ID_AMR_NB, + .p.video_codec = AV_CODEC_ID_NONE, + .p.flags = AVFMT_NOTIMESTAMPS, .write_header = amr_write_header, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, }; #endif diff --git a/libavformat/amvenc.c b/libavformat/amvenc.c index 28d4879b19e..e1b1ffd42e2 100644 --- a/libavformat/amvenc.c +++ b/libavformat/amvenc.c @@ -20,6 +20,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avformat.h" +#include "mux.h" #include "riff.h" #include "internal.h" #include "avio_internal.h" @@ -401,14 +402,14 @@ static int amv_write_trailer(AVFormatContext *s) return 0; } -const AVOutputFormat ff_amv_muxer = { - .name = "amv", - .long_name = NULL_IF_CONFIG_SMALL("AMV"), - .mime_type = "video/amv", - .extensions = "amv", +const FFOutputFormat ff_amv_muxer = { + .p.name = "amv", + .p.long_name = NULL_IF_CONFIG_SMALL("AMV"), + .p.mime_type = "video/amv", + .p.extensions = "amv", .priv_data_size = sizeof(AMVContext), - .audio_codec = AV_CODEC_ID_ADPCM_IMA_AMV, - .video_codec = AV_CODEC_ID_AMV, + .p.audio_codec = AV_CODEC_ID_ADPCM_IMA_AMV, + .p.video_codec = AV_CODEC_ID_AMV, .init = amv_init, .deinit = amv_deinit, .write_header = amv_write_header, diff --git a/libavformat/anm.c b/libavformat/anm.c index 7feba4ed1e7..f2ac6958a9a 100644 --- a/libavformat/anm.c +++ b/libavformat/anm.c @@ -172,7 +172,7 @@ static int read_packet(AVFormatContext *s, int tmp, record_size; if (avio_feof(s->pb)) - return AVERROR(EIO); + return AVERROR_EOF; if (anm->page < 0) return anm->page; diff --git a/libavformat/apm.c b/libavformat/apm.c index a3ddc08e83b..ccb8e22437e 100644 --- a/libavformat/apm.c +++ b/libavformat/apm.c @@ -24,6 +24,7 @@ #include "avformat.h" #include "internal.h" +#include "mux.h" #include "rawenc.h" #include "libavutil/channel_layout.h" #include "libavutil/internal.h" @@ -303,12 +304,12 @@ static int apm_write_trailer(AVFormatContext *s) return 0; } -const AVOutputFormat ff_apm_muxer = { - .name = "apm", - .long_name = NULL_IF_CONFIG_SMALL("Ubisoft Rayman 2 APM"), - .extensions = "apm", - .audio_codec = AV_CODEC_ID_ADPCM_IMA_APM, - .video_codec = AV_CODEC_ID_NONE, +const FFOutputFormat ff_apm_muxer = { + .p.name = "apm", + .p.long_name = NULL_IF_CONFIG_SMALL("Ubisoft Rayman 2 APM"), + .p.extensions = "apm", + .p.audio_codec = AV_CODEC_ID_ADPCM_IMA_APM, + .p.video_codec = AV_CODEC_ID_NONE, .init = apm_write_init, .write_header = apm_write_header, .write_packet = ff_raw_write_packet, diff --git a/libavformat/apngenc.c b/libavformat/apngenc.c index cddb148d50b..a0af916f141 100644 --- a/libavformat/apngenc.c +++ b/libavformat/apngenc.c @@ -22,6 +22,7 @@ */ #include "avformat.h" +#include "mux.h" #include "libavutil/avassert.h" #include "libavutil/crc.h" #include "libavutil/intreadwrite.h" @@ -306,18 +307,18 @@ static const AVClass apng_muxer_class = { .option = options, }; -const AVOutputFormat ff_apng_muxer = { - .name = "apng", - .long_name = NULL_IF_CONFIG_SMALL("Animated Portable Network Graphics"), - .mime_type = "image/png", - .extensions = "apng", +const FFOutputFormat ff_apng_muxer = { + .p.name = "apng", + .p.long_name = NULL_IF_CONFIG_SMALL("Animated Portable Network Graphics"), + .p.mime_type = "image/png", + .p.extensions = "apng", .priv_data_size = sizeof(APNGMuxContext), - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_APNG, + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_APNG, .write_header = apng_write_header, .write_packet = apng_write_packet, .write_trailer = apng_write_trailer, .deinit = apng_deinit, - .priv_class = &apng_muxer_class, - .flags = AVFMT_VARIABLE_FPS, + .p.priv_class = &apng_muxer_class, + .p.flags = AVFMT_VARIABLE_FPS, }; diff --git a/libavformat/argo_asf.c b/libavformat/argo_asf.c index 39a60a0dde4..5f38b68b6ac 100644 --- a/libavformat/argo_asf.c +++ b/libavformat/argo_asf.c @@ -25,6 +25,7 @@ #include "libavutil/avstring.h" #include "avformat.h" #include "internal.h" +#include "mux.h" #include "libavutil/channel_layout.h" #include "libavutil/intreadwrite.h" #include "libavutil/avassert.h" @@ -470,20 +471,20 @@ static const AVClass argo_asf_muxer_class = { .version = LIBAVUTIL_VERSION_INT }; -const AVOutputFormat ff_argo_asf_muxer = { - .name = "argo_asf", - .long_name = NULL_IF_CONFIG_SMALL("Argonaut Games ASF"), +const FFOutputFormat ff_argo_asf_muxer = { + .p.name = "argo_asf", + .p.long_name = NULL_IF_CONFIG_SMALL("Argonaut Games ASF"), /* * NB: Can't do this as it conflicts with the actual ASF format. - * .extensions = "asf", + * .p.extensions = "asf", */ - .audio_codec = AV_CODEC_ID_ADPCM_ARGO, - .video_codec = AV_CODEC_ID_NONE, + .p.audio_codec = AV_CODEC_ID_ADPCM_ARGO, + .p.video_codec = AV_CODEC_ID_NONE, + .p.priv_class = &argo_asf_muxer_class, .init = argo_asf_write_init, .write_header = argo_asf_write_header, .write_packet = argo_asf_write_packet, .write_trailer = argo_asf_write_trailer, - .priv_class = &argo_asf_muxer_class, .priv_data_size = sizeof(ArgoASFMuxContext) }; #endif diff --git a/libavformat/argo_cvg.c b/libavformat/argo_cvg.c index aedc7c4a329..2c74200b7d8 100644 --- a/libavformat/argo_cvg.c +++ b/libavformat/argo_cvg.c @@ -26,6 +26,7 @@ #include "libavutil/channel_layout.h" #include "avformat.h" #include "internal.h" +#include "mux.h" #include "libavutil/opt.h" #include "libavutil/intreadwrite.h" @@ -400,17 +401,17 @@ static const AVClass argo_cvg_muxer_class = { .version = LIBAVUTIL_VERSION_INT }; -const AVOutputFormat ff_argo_cvg_muxer = { - .name = "argo_cvg", - .long_name = NULL_IF_CONFIG_SMALL("Argonaut Games CVG"), - .extensions = "cvg", - .audio_codec = AV_CODEC_ID_ADPCM_PSX, - .video_codec = AV_CODEC_ID_NONE, +const FFOutputFormat ff_argo_cvg_muxer = { + .p.name = "argo_cvg", + .p.long_name = NULL_IF_CONFIG_SMALL("Argonaut Games CVG"), + .p.extensions = "cvg", + .p.audio_codec = AV_CODEC_ID_ADPCM_PSX, + .p.video_codec = AV_CODEC_ID_NONE, + .p.priv_class = &argo_cvg_muxer_class, .init = argo_cvg_write_init, .write_header = argo_cvg_write_header, .write_packet = argo_cvg_write_packet, .write_trailer = argo_cvg_write_trailer, - .priv_class = &argo_cvg_muxer_class, .priv_data_size = sizeof(ArgoCVGMuxContext), }; #endif diff --git a/libavformat/asfdec_f.c b/libavformat/asfdec_f.c index bdbd4271c8c..54059564670 100644 --- a/libavformat/asfdec_f.c +++ b/libavformat/asfdec_f.c @@ -445,6 +445,8 @@ static int asf_read_stream_properties(AVFormatContext *s, int64_t size) st->codecpar->codec_tag = tag1; st->codecpar->codec_id = ff_codec_get_id(ff_codec_bmp_tags, tag1); + if (!st->codecpar->codec_id) + st->codecpar->codec_id = ff_codec_get_id(ff_codec_bmp_tags_unofficial, tag1); if (tag1 == MKTAG('D', 'V', 'R', ' ')) { sti->need_parsing = AVSTREAM_PARSE_FULL; /* issue658 contains wrong w/h and MS even puts a fake seq header @@ -458,7 +460,9 @@ static int asf_read_stream_properties(AVFormatContext *s, int64_t size) if (st->codecpar->codec_id == AV_CODEC_ID_H264) sti->need_parsing = AVSTREAM_PARSE_FULL_ONCE; if (st->codecpar->codec_id == AV_CODEC_ID_MPEG4) - sti->need_parsing = AVSTREAM_PARSE_FULL_ONCE; + sti->need_parsing = AVSTREAM_PARSE_FULL; + if (st->codecpar->codec_id == AV_CODEC_ID_HEVC) + sti->need_parsing = AVSTREAM_PARSE_FULL; } pos2 = avio_tell(pb); avio_skip(pb, size - (pos2 - pos1 + 24)); diff --git a/libavformat/asfenc.c b/libavformat/asfenc.c index 47240fc0a80..244c7e7a271 100644 --- a/libavformat/asfenc.c +++ b/libavformat/asfenc.c @@ -1129,39 +1129,39 @@ static const AVClass asf_muxer_class = { }; #if CONFIG_ASF_MUXER -const AVOutputFormat ff_asf_muxer = { - .name = "asf", - .long_name = NULL_IF_CONFIG_SMALL("ASF (Advanced / Active Streaming Format)"), - .mime_type = "video/x-ms-asf", - .extensions = "asf,wmv,wma", +const FFOutputFormat ff_asf_muxer = { + .p.name = "asf", + .p.long_name = NULL_IF_CONFIG_SMALL("ASF (Advanced / Active Streaming Format)"), + .p.mime_type = "video/x-ms-asf", + .p.extensions = "asf,wmv,wma", + .p.audio_codec = AV_CODEC_ID_WMAV2, + .p.video_codec = AV_CODEC_ID_MSMPEG4V3, + .p.flags = AVFMT_GLOBALHEADER, + .p.codec_tag = asf_codec_tags, + .p.priv_class = &asf_muxer_class, .priv_data_size = sizeof(ASFContext), - .audio_codec = AV_CODEC_ID_WMAV2, - .video_codec = AV_CODEC_ID_MSMPEG4V3, .write_header = asf_write_header, .write_packet = asf_write_packet, .write_trailer = asf_write_trailer, - .flags = AVFMT_GLOBALHEADER, - .codec_tag = asf_codec_tags, - .priv_class = &asf_muxer_class, .deinit = asf_deinit, }; #endif /* CONFIG_ASF_MUXER */ #if CONFIG_ASF_STREAM_MUXER -const AVOutputFormat ff_asf_stream_muxer = { - .name = "asf_stream", - .long_name = NULL_IF_CONFIG_SMALL("ASF (Advanced / Active Streaming Format)"), - .mime_type = "video/x-ms-asf", - .extensions = "asf,wmv,wma", +const FFOutputFormat ff_asf_stream_muxer = { + .p.name = "asf_stream", + .p.long_name = NULL_IF_CONFIG_SMALL("ASF (Advanced / Active Streaming Format)"), + .p.mime_type = "video/x-ms-asf", + .p.extensions = "asf,wmv,wma", .priv_data_size = sizeof(ASFContext), - .audio_codec = AV_CODEC_ID_WMAV2, - .video_codec = AV_CODEC_ID_MSMPEG4V3, + .p.audio_codec = AV_CODEC_ID_WMAV2, + .p.video_codec = AV_CODEC_ID_MSMPEG4V3, .write_header = asf_write_stream_header, .write_packet = asf_write_packet, .write_trailer = asf_write_trailer, - .flags = AVFMT_GLOBALHEADER, - .codec_tag = asf_codec_tags, - .priv_class = &asf_muxer_class, + .p.flags = AVFMT_GLOBALHEADER, + .p.codec_tag = asf_codec_tags, + .p.priv_class = &asf_muxer_class, .deinit = asf_deinit, }; #endif /* CONFIG_ASF_STREAM_MUXER */ diff --git a/libavformat/assdec.c b/libavformat/assdec.c index 0915f6fafd7..bf7b8a73a24 100644 --- a/libavformat/assdec.c +++ b/libavformat/assdec.c @@ -73,6 +73,8 @@ static int read_dialogue(ASSContext *ass, AVBPrint *dst, const uint8_t *p, av_bprint_clear(dst); av_bprintf(dst, "%u,%d,%s", ass->readorder++, layer, p + pos); + if (!av_bprint_is_complete(dst)) + return AVERROR(ENOMEM); /* right strip the buffer */ while (dst->len > 0 && @@ -135,7 +137,7 @@ static int ass_read_header(AVFormatContext *s) av_bprintf(&header, "%s", line.str); continue; } - sub = ff_subtitles_queue_insert(&ass->q, rline.str, rline.len, 0); + sub = ff_subtitles_queue_insert_bprint(&ass->q, &rline, 0); if (!sub) { res = AVERROR(ENOMEM); goto end; diff --git a/libavformat/assenc.c b/libavformat/assenc.c index 1600f0a02b2..62ea0745a42 100644 --- a/libavformat/assenc.c +++ b/libavformat/assenc.c @@ -22,6 +22,7 @@ #include "libavutil/avstring.h" #include "avformat.h" #include "internal.h" +#include "mux.h" #include "libavutil/opt.h" @@ -62,15 +63,16 @@ static int write_header(AVFormatContext *s) if (trailer) trailer = strstr(trailer, "\n"); - if (trailer++) { - header_size = (trailer - par->extradata); + if (trailer) { + header_size = (++trailer - par->extradata); ass->trailer_size = par->extradata_size - header_size; if (ass->trailer_size) ass->trailer = trailer; } + header_size = av_strnlen(par->extradata, header_size); avio_write(s->pb, par->extradata, header_size); - if (par->extradata[header_size - 1] != '\n') + if (header_size && par->extradata[header_size - 1] != '\n') avio_write(s->pb, "\r\n", 2); ass->ssa_mode = !strstr(par->extradata, "\n[V4+ Styles]"); if (!strstr(par->extradata, "\n[Events]")) @@ -226,16 +228,16 @@ static const AVClass ass_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVOutputFormat ff_ass_muxer = { - .name = "ass", - .long_name = NULL_IF_CONFIG_SMALL("SSA (SubStation Alpha) subtitle"), - .mime_type = "text/x-ass", - .extensions = "ass,ssa", +const FFOutputFormat ff_ass_muxer = { + .p.name = "ass", + .p.long_name = NULL_IF_CONFIG_SMALL("SSA (SubStation Alpha) subtitle"), + .p.mime_type = "text/x-ass", + .p.extensions = "ass,ssa", + .p.subtitle_codec = AV_CODEC_ID_ASS, + .p.flags = AVFMT_GLOBALHEADER | AVFMT_NOTIMESTAMPS | AVFMT_TS_NONSTRICT, + .p.priv_class = &ass_class, .priv_data_size = sizeof(ASSContext), - .subtitle_codec = AV_CODEC_ID_ASS, .write_header = write_header, .write_packet = write_packet, .write_trailer = write_trailer, - .flags = AVFMT_GLOBALHEADER | AVFMT_NOTIMESTAMPS | AVFMT_TS_NONSTRICT, - .priv_class = &ass_class, }; diff --git a/libavformat/astenc.c b/libavformat/astenc.c index b29cfc4aaf0..9dd388040f9 100644 --- a/libavformat/astenc.c +++ b/libavformat/astenc.c @@ -23,6 +23,7 @@ #include "avio_internal.h" #include "internal.h" #include "ast.h" +#include "mux.h" #include "libavutil/mathematics.h" #include "libavutil/opt.h" @@ -194,16 +195,16 @@ static const AVClass ast_muxer_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVOutputFormat ff_ast_muxer = { - .name = "ast", - .long_name = NULL_IF_CONFIG_SMALL("AST (Audio Stream)"), - .extensions = "ast", +const FFOutputFormat ff_ast_muxer = { + .p.name = "ast", + .p.long_name = NULL_IF_CONFIG_SMALL("AST (Audio Stream)"), + .p.extensions = "ast", .priv_data_size = sizeof(ASTMuxContext), - .audio_codec = AV_CODEC_ID_PCM_S16BE_PLANAR, - .video_codec = AV_CODEC_ID_NONE, + .p.audio_codec = AV_CODEC_ID_PCM_S16BE_PLANAR, + .p.video_codec = AV_CODEC_ID_NONE, .write_header = ast_write_header, .write_packet = ast_write_packet, .write_trailer = ast_write_trailer, - .priv_class = &ast_muxer_class, - .codec_tag = ff_ast_codec_tags_list, + .p.priv_class = &ast_muxer_class, + .p.codec_tag = ff_ast_codec_tags_list, }; diff --git a/libavformat/au.c b/libavformat/au.c index f7c47d53331..3bf21502584 100644 --- a/libavformat/au.c +++ b/libavformat/au.c @@ -33,6 +33,7 @@ #include "avformat.h" #include "internal.h" #include "avio_internal.h" +#include "mux.h" #include "pcm.h" #include "libavutil/avassert.h" @@ -331,19 +332,19 @@ static int au_write_trailer(AVFormatContext *s) return 0; } -const AVOutputFormat ff_au_muxer = { - .name = "au", - .long_name = NULL_IF_CONFIG_SMALL("Sun AU"), - .mime_type = "audio/basic", - .extensions = "au", +const FFOutputFormat ff_au_muxer = { + .p.name = "au", + .p.long_name = NULL_IF_CONFIG_SMALL("Sun AU"), + .p.mime_type = "audio/basic", + .p.extensions = "au", + .p.codec_tag = au_codec_tags, + .p.audio_codec = AV_CODEC_ID_PCM_S16BE, + .p.video_codec = AV_CODEC_ID_NONE, + .p.flags = AVFMT_NOTIMESTAMPS, .priv_data_size = sizeof(AUContext), - .audio_codec = AV_CODEC_ID_PCM_S16BE, - .video_codec = AV_CODEC_ID_NONE, .write_header = au_write_header, .write_packet = ff_raw_write_packet, .write_trailer = au_write_trailer, - .codec_tag = au_codec_tags, - .flags = AVFMT_NOTIMESTAMPS, }; #endif /* CONFIG_AU_MUXER */ diff --git a/libavformat/av1dec.c b/libavformat/av1dec.c index d4b430af7e1..2883b320a19 100644 --- a/libavformat/av1dec.c +++ b/libavformat/av1dec.c @@ -23,7 +23,6 @@ #include "libavutil/common.h" #include "libavutil/opt.h" -#include "libavcodec/avcodec.h" #include "libavcodec/av1_parse.h" #include "libavcodec/bsf.h" #include "avformat.h" @@ -80,7 +79,7 @@ static int av1_read_header(AVFormatContext *s) st->codecpar->codec_id = AV_CODEC_ID_AV1; sti->need_parsing = AVSTREAM_PARSE_HEADERS; - sti->avctx->framerate = c->framerate; + st->avg_frame_rate = c->framerate; // taken from rawvideo demuxers avpriv_set_pts_info(st, 64, 1, 1200000); @@ -124,13 +123,16 @@ static const AVClass av1_demuxer_class = { #if CONFIG_AV1_DEMUXER -static int leb(AVIOContext *pb, uint32_t *len) { +static int leb(AVIOContext *pb, uint32_t *len, int eof) { int more, i = 0; - uint8_t byte; *len = 0; do { unsigned bits; - byte = avio_r8(pb); + int byte = avio_r8(pb); + if (pb->error) + return pb->error; + if (pb->eof_reached) + return (eof && !i) ? AVERROR_EOF : AVERROR_INVALIDDATA; more = byte & 0x80; bits = byte & 0x7f; if (i <= 3 || (i == 4 && bits < (1 << 4))) @@ -139,8 +141,6 @@ static int leb(AVIOContext *pb, uint32_t *len) { return AVERROR_INVALIDDATA; if (++i == 8 && more) return AVERROR_INVALIDDATA; - if (pb->eof_reached || pb->error) - return pb->error ? pb->error : AVERROR(EIO); } while (more); return i; } @@ -170,15 +170,15 @@ static int annexb_probe(const AVProbeData *p) ffio_init_context(&ctx, p->buf, p->buf_size, 0, NULL, NULL, NULL, NULL); - ret = leb(pb, &temporal_unit_size); + ret = leb(pb, &temporal_unit_size, 1); if (ret < 0) return 0; cnt += ret; - ret = leb(pb, &frame_unit_size); + ret = leb(pb, &frame_unit_size, 0); if (ret < 0 || ((int64_t)frame_unit_size + ret) > temporal_unit_size) return 0; cnt += ret; - ret = leb(pb, &obu_unit_size); + ret = leb(pb, &obu_unit_size, 0); if (ret < 0 || ((int64_t)obu_unit_size + ret) >= frame_unit_size) return 0; cnt += ret; @@ -196,7 +196,7 @@ static int annexb_probe(const AVProbeData *p) cnt += obu_unit_size; do { - ret = leb(pb, &obu_unit_size); + ret = leb(pb, &obu_unit_size, 0); if (ret < 0 || ((int64_t)obu_unit_size + ret) > frame_unit_size) return 0; cnt += ret; @@ -229,31 +229,36 @@ static int annexb_read_packet(AVFormatContext *s, AVPacket *pkt) retry: if (avio_feof(s->pb)) { if (c->temporal_unit_size || c->frame_unit_size) - return AVERROR(EIO); + return AVERROR_INVALIDDATA; goto end; } if (!c->temporal_unit_size) { - len = leb(s->pb, &c->temporal_unit_size); - if (len < 0) return AVERROR_INVALIDDATA; + len = leb(s->pb, &c->temporal_unit_size, 1); + if (len == AVERROR_EOF) goto end; + else if (len < 0) return len; } if (!c->frame_unit_size) { - len = leb(s->pb, &c->frame_unit_size); - if (len < 0 || ((int64_t)c->frame_unit_size + len) > c->temporal_unit_size) + len = leb(s->pb, &c->frame_unit_size, 0); + if (len < 0) + return len; + if (((int64_t)c->frame_unit_size + len) > c->temporal_unit_size) return AVERROR_INVALIDDATA; c->temporal_unit_size -= len; } - len = leb(s->pb, &obu_unit_size); - if (len < 0 || ((int64_t)obu_unit_size + len) > c->frame_unit_size) + len = leb(s->pb, &obu_unit_size, 0); + if (len < 0) + return len; + if (((int64_t)obu_unit_size + len) > c->frame_unit_size) return AVERROR_INVALIDDATA; ret = av_get_packet(s->pb, pkt, obu_unit_size); if (ret < 0) return ret; if (ret != obu_unit_size) - return AVERROR(EIO); + return AVERROR_INVALIDDATA; c->temporal_unit_size -= obu_unit_size + len; c->frame_unit_size -= obu_unit_size + len; @@ -287,7 +292,7 @@ const AVInputFormat ff_av1_demuxer = { .read_packet = annexb_read_packet, .read_close = av1_read_close, .extensions = "obu", - .flags = AVFMT_GENERIC_INDEX, + .flags = AVFMT_GENERIC_INDEX | AVFMT_NOTIMESTAMPS, .priv_class = &av1_demuxer_class, }; #endif @@ -432,7 +437,7 @@ const AVInputFormat ff_obu_demuxer = { .read_packet = obu_read_packet, .read_close = av1_read_close, .extensions = "obu", - .flags = AVFMT_GENERIC_INDEX | AVFMT_NO_BYTE_SEEK, + .flags = AVFMT_GENERIC_INDEX | AVFMT_NO_BYTE_SEEK | AVFMT_NOTIMESTAMPS, .priv_class = &av1_demuxer_class, }; #endif diff --git a/libavformat/avformat.c b/libavformat/avformat.c index 19c72194714..356b4de931e 100644 --- a/libavformat/avformat.c +++ b/libavformat/avformat.c @@ -35,6 +35,7 @@ #include "avformat.h" #include "avio.h" #include "demux.h" +#include "mux.h" #include "internal.h" void ff_free_stream(AVStream **pst) @@ -100,8 +101,8 @@ void avformat_free_context(AVFormatContext *s) return; si = ffformatcontext(s); - if (s->oformat && s->oformat->deinit && si->initialized) - s->oformat->deinit(s); + if (s->oformat && ffofmt(s->oformat)->deinit && si->initialized) + ffofmt(s->oformat)->deinit(s); av_opt_free(s); if (s->iformat && s->iformat->priv_class && s->priv_data) @@ -678,6 +679,7 @@ AVRational av_guess_sample_aspect_ratio(AVFormatContext *format, AVStream *strea AVRational av_guess_frame_rate(AVFormatContext *format, AVStream *st, AVFrame *frame) { AVRational fr = st->r_frame_rate; + const AVCodecDescriptor *desc = cffstream(st)->codec_desc; AVCodecContext *const avctx = ffstream(st)->avctx; AVRational codec_fr = avctx->framerate; AVRational avg_fr = st->avg_frame_rate; @@ -687,7 +689,7 @@ AVRational av_guess_frame_rate(AVFormatContext *format, AVStream *st, AVFrame *f fr = avg_fr; } - if (avctx->ticks_per_frame > 1) { + if (desc && (desc->props & AV_CODEC_PROP_FIELDS)) { if ( codec_fr.num > 0 && codec_fr.den > 0 && (fr.num == 0 || av_q2d(codec_fr) < av_q2d(fr)*0.7 && fabs(1.0 - av_q2d(av_div_q(avg_fr, fr))) > 0.1)) fr = codec_fr; @@ -700,9 +702,14 @@ int avformat_transfer_internal_stream_timing_info(const AVOutputFormat *ofmt, AVStream *ost, const AVStream *ist, enum AVTimebaseSource copy_tb) { + const AVCodecDescriptor *desc = cffstream(ist)->codec_desc; const AVCodecContext *const dec_ctx = cffstream(ist)->avctx; AVCodecContext *const enc_ctx = ffstream(ost)->avctx; + AVRational mul = (AVRational){ desc && (desc->props & AV_CODEC_PROP_FIELDS) ? 2 : 1, 1 }; + AVRational dec_ctx_tb = dec_ctx->framerate.num ? av_inv_q(av_mul_q(dec_ctx->framerate, mul)) + : (ist->codecpar->codec_type == AVMEDIA_TYPE_AUDIO ? (AVRational){0, 1} + : ist->time_base); enc_ctx->time_base = ist->time_base; /* * Avi is a special case here because it supports variable fps but @@ -714,38 +721,53 @@ int avformat_transfer_internal_stream_timing_info(const AVOutputFormat *ofmt, if (copy_tb == AVFMT_TBCF_AUTO && ist->r_frame_rate.num && av_q2d(ist->r_frame_rate) >= av_q2d(ist->avg_frame_rate) && 0.5/av_q2d(ist->r_frame_rate) > av_q2d(ist->time_base) - && 0.5/av_q2d(ist->r_frame_rate) > av_q2d(dec_ctx->time_base) - && av_q2d(ist->time_base) < 1.0/500 && av_q2d(dec_ctx->time_base) < 1.0/500 + && 0.5/av_q2d(ist->r_frame_rate) > av_q2d(dec_ctx_tb) + && av_q2d(ist->time_base) < 1.0/500 && av_q2d(dec_ctx_tb) < 1.0/500 || copy_tb == AVFMT_TBCF_R_FRAMERATE) { enc_ctx->time_base.num = ist->r_frame_rate.den; enc_ctx->time_base.den = 2*ist->r_frame_rate.num; +#if FF_API_TICKS_PER_FRAME +FF_DISABLE_DEPRECATION_WARNINGS enc_ctx->ticks_per_frame = 2; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } else #endif - if (copy_tb == AVFMT_TBCF_AUTO && av_q2d(dec_ctx->time_base)*dec_ctx->ticks_per_frame > 2*av_q2d(ist->time_base) + if (copy_tb == AVFMT_TBCF_AUTO && dec_ctx->framerate.num && + av_q2d(av_inv_q(dec_ctx->framerate)) > 2*av_q2d(ist->time_base) && av_q2d(ist->time_base) < 1.0/500 - || copy_tb == AVFMT_TBCF_DECODER) { - enc_ctx->time_base = dec_ctx->time_base; - enc_ctx->time_base.num *= dec_ctx->ticks_per_frame; + || (copy_tb == AVFMT_TBCF_DECODER && + (dec_ctx->framerate.num || ist->codecpar->codec_type == AVMEDIA_TYPE_AUDIO))) { + enc_ctx->time_base = dec_ctx_tb; enc_ctx->time_base.den *= 2; +#if FF_API_TICKS_PER_FRAME +FF_DISABLE_DEPRECATION_WARNINGS + enc_ctx->time_base.num *= dec_ctx->ticks_per_frame; enc_ctx->ticks_per_frame = 2; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } } else if (!(ofmt->flags & AVFMT_VARIABLE_FPS) && !av_match_name(ofmt->name, "mov,mp4,3gp,3g2,psp,ipod,ismv,f4v")) { - if (copy_tb == AVFMT_TBCF_AUTO && dec_ctx->time_base.den - && av_q2d(dec_ctx->time_base)*dec_ctx->ticks_per_frame > av_q2d(ist->time_base) + if (copy_tb == AVFMT_TBCF_AUTO && dec_ctx->framerate.num + && av_q2d(av_inv_q(dec_ctx->framerate)) > av_q2d(ist->time_base) && av_q2d(ist->time_base) < 1.0/500 - || copy_tb == AVFMT_TBCF_DECODER) { - enc_ctx->time_base = dec_ctx->time_base; + || (copy_tb == AVFMT_TBCF_DECODER && + (dec_ctx->framerate.num || ist->codecpar->codec_type == AVMEDIA_TYPE_AUDIO))) { + enc_ctx->time_base = dec_ctx_tb; +#if FF_API_TICKS_PER_FRAME +FF_DISABLE_DEPRECATION_WARNINGS enc_ctx->time_base.num *= dec_ctx->ticks_per_frame; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } } if ((enc_ctx->codec_tag == AV_RL32("tmcd") || ost->codecpar->codec_tag == AV_RL32("tmcd")) - && dec_ctx->time_base.num < dec_ctx->time_base.den - && dec_ctx->time_base.num > 0 - && 121LL*dec_ctx->time_base.num > dec_ctx->time_base.den) { - enc_ctx->time_base = dec_ctx->time_base; + && dec_ctx_tb.num < dec_ctx_tb.den + && dec_ctx_tb.num > 0 + && 121LL*dec_ctx_tb.num > dec_ctx_tb.den) { + enc_ctx->time_base = dec_ctx_tb; } av_reduce(&enc_ctx->time_base.num, &enc_ctx->time_base.den, @@ -846,10 +868,16 @@ int ff_format_io_close(AVFormatContext *s, AVIOContext **pb) { int ret = 0; if (*pb) { +#if FF_API_AVFORMAT_IO_CLOSE +FF_DISABLE_DEPRECATION_WARNINGS if (s->io_close == ff_format_io_close_default || s->io_close == NULL) +#endif ret = s->io_close2(s, *pb); +#if FF_API_AVFORMAT_IO_CLOSE else s->io_close(s, *pb); +FF_ENABLE_DEPRECATION_WARNINGS +#endif } *pb = NULL; return ret; diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 1d97d56ac58..1916aa2dc55 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -328,10 +328,8 @@ #endif struct AVFormatContext; -struct AVStream; struct AVDeviceInfoList; -struct AVDeviceCapabilitiesQuery; /** * @defgroup metadata_api Public Metadata API @@ -536,114 +534,6 @@ typedef struct AVOutputFormat { const AVClass *priv_class; ///< AVClass for the private context - - /***************************************************************** - * No fields below this line are part of the public API. They - * may not be used outside of libavformat and can be changed and - * removed at will. - * New public fields should be added right above. - ***************************************************************** - */ - /** - * size of private data so that it can be allocated in the wrapper - */ - int priv_data_size; - - /** - * Internal flags. See FF_FMT_FLAG_* in internal.h. - */ - int flags_internal; - - int (*write_header)(struct AVFormatContext *); - /** - * Write a packet. If AVFMT_ALLOW_FLUSH is set in flags, - * pkt can be NULL in order to flush data buffered in the muxer. - * When flushing, return 0 if there still is more data to flush, - * or 1 if everything was flushed and there is no more buffered - * data. - */ - int (*write_packet)(struct AVFormatContext *, AVPacket *pkt); - int (*write_trailer)(struct AVFormatContext *); - /** - * A format-specific function for interleavement. - * If unset, packets will be interleaved by dts. - * - * @param s An AVFormatContext for output. pkt will be added to - * resp. taken from its packet buffer. - * @param[in,out] pkt A packet to be interleaved if has_packet is set; - * also used to return packets. If no packet is returned - * (e.g. on error), pkt is blank on return. - * @param flush 1 if no further packets are available as input and - * all remaining packets should be output. - * @param has_packet If set, pkt contains a packet to be interleaved - * on input; otherwise pkt is blank on input. - * @return 1 if a packet was output, 0 if no packet could be output, - * < 0 if an error occurred - */ - int (*interleave_packet)(struct AVFormatContext *s, AVPacket *pkt, - int flush, int has_packet); - /** - * Test if the given codec can be stored in this container. - * - * @return 1 if the codec is supported, 0 if it is not. - * A negative number if unknown. - * MKTAG('A', 'P', 'I', 'C') if the codec is only supported as AV_DISPOSITION_ATTACHED_PIC - */ - int (*query_codec)(enum AVCodecID id, int std_compliance); - - void (*get_output_timestamp)(struct AVFormatContext *s, int stream, - int64_t *dts, int64_t *wall); - /** - * Allows sending messages from application to device. - */ - int (*control_message)(struct AVFormatContext *s, int type, - void *data, size_t data_size); - - /** - * Write an uncoded AVFrame. - * - * See av_write_uncoded_frame() for details. - * - * The library will free *frame afterwards, but the muxer can prevent it - * by setting the pointer to NULL. - */ - int (*write_uncoded_frame)(struct AVFormatContext *, int stream_index, - AVFrame **frame, unsigned flags); - /** - * Returns device list with it properties. - * @see avdevice_list_devices() for more details. - */ - int (*get_device_list)(struct AVFormatContext *s, struct AVDeviceInfoList *device_list); - enum AVCodecID data_codec; /**< default data codec */ - /** - * Initialize format. May allocate data here, and set any AVFormatContext or - * AVStream parameters that need to be set before packets are sent. - * This method must not write output. - * - * Return 0 if streams were fully configured, 1 if not, negative AVERROR on failure - * - * Any allocations made here must be freed in deinit(). - */ - int (*init)(struct AVFormatContext *); - /** - * Deinitialize format. If present, this is called whenever the muxer is being - * destroyed, regardless of whether or not the header has been written. - * - * If a trailer is being written, this is called after write_trailer(). - * - * This is called if init() fails as well. - */ - void (*deinit)(struct AVFormatContext *); - /** - * Set up any necessary bitstream filtering and extract any extra data needed - * for the global header. - * - * @note pkt might have been directly forwarded by a meta-muxer; therefore - * pkt->stream_index as well as the pkt's timebase might be invalid. - * Return 0 if more packets from this stream must be checked; 1 if not. - */ - int (*check_bitstream)(struct AVFormatContext *s, struct AVStream *st, - const AVPacket *pkt); } AVOutputFormat; /** * @} @@ -948,12 +838,10 @@ const char *av_disposition_to_string(int disposition); * sizeof(AVStream) must not be used outside libav*. */ typedef struct AVStream { -#if FF_API_AVSTREAM_CLASS /** * A class for @ref avoptions. Set on stream creation. */ const AVClass *av_class; -#endif int index; /**< stream index in AVFormatContext */ /** @@ -963,6 +851,17 @@ typedef struct AVStream { */ int id; + /** + * Codec parameters associated with this stream. Allocated and freed by + * libavformat in avformat_new_stream() and avformat_free_context() + * respectively. + * + * - demuxing: filled by libavformat on stream creation or in + * avformat_find_stream_info() + * - muxing: filled by the caller before avformat_write_header() + */ + AVCodecParameters *codecpar; + void *priv_data; /** @@ -1098,17 +997,6 @@ typedef struct AVStream { */ AVRational r_frame_rate; - /** - * Codec parameters associated with this stream. Allocated and freed by - * libavformat in avformat_new_stream() and avformat_free_context() - * respectively. - * - * - demuxing: filled by libavformat on stream creation or in - * avformat_find_stream_info() - * - muxing: filled by the caller before avformat_write_header() - */ - AVCodecParameters *codecpar; - /** * Number of bits in timestamps. Used for wrapping control. * @@ -1352,9 +1240,6 @@ typedef struct AVFormatContext { */ #define AVFMT_FLAG_BITEXACT 0x0400 #define AVFMT_FLAG_SORT_DTS 0x10000 ///< try to interleave outputted packets by dts (using this flag can slow demuxing down) -#if FF_API_LAVF_PRIV_OPT -#define AVFMT_FLAG_PRIV_OPT 0x20000 ///< Enable use of private options by delaying codec open (deprecated, does nothing) -#endif #define AVFMT_FLAG_FAST_SEEK 0x80000 ///< Enable fast, but inaccurate seeks for some formats #define AVFMT_FLAG_SHORTEST 0x100000 ///< Stop muxing when the shortest stream stops. #define AVFMT_FLAG_AUTO_BSF 0x200000 ///< Add bitstream filters as requested by the muxer @@ -1778,10 +1663,15 @@ typedef struct AVFormatContext { int (*io_open)(struct AVFormatContext *s, AVIOContext **pb, const char *url, int flags, AVDictionary **options); +#if FF_API_AVFORMAT_IO_CLOSE /** * A callback for closing the streams opened with AVFormatContext.io_open(). + * + * @deprecated use io_close2 */ + attribute_deprecated void (*io_close)(struct AVFormatContext *s, AVIOContext *pb); +#endif /** * ',' separated list of disallowed protocols. diff --git a/libavformat/avidec.c b/libavformat/avidec.c index 7a3fad6392b..00bd7a98a9d 100644 --- a/libavformat/avidec.c +++ b/libavformat/avidec.c @@ -1869,13 +1869,20 @@ static int avi_read_seek(AVFormatContext *s, int stream_index, st = s->streams[stream_index]; sti = ffstream(st); ast = st->priv_data; - index = av_index_search_timestamp(st, - timestamp * FFMAX(ast->sample_size, 1), - flags); + + if (avi->dv_demux) { + // index entries are in the AVI scale/rate timebase, which does + // not match DV demuxer's stream timebase + timestamp = av_rescale_q(timestamp, st->time_base, + (AVRational){ ast->scale, ast->rate }); + } else + timestamp *= FFMAX(ast->sample_size, 1); + + index = av_index_search_timestamp(st, timestamp, flags); if (index < 0) { if (sti->nb_index_entries > 0) av_log(s, AV_LOG_DEBUG, "Failed to find timestamp %"PRId64 " in index %"PRId64 " .. %"PRId64 "\n", - timestamp * FFMAX(ast->sample_size, 1), + timestamp, sti->index_entries[0].timestamp, sti->index_entries[sti->nb_index_entries - 1].timestamp); return AVERROR_INVALIDDATA; @@ -1883,7 +1890,7 @@ static int avi_read_seek(AVFormatContext *s, int stream_index, /* find the position */ pos = sti->index_entries[index].pos; - timestamp = sti->index_entries[index].timestamp / FFMAX(ast->sample_size, 1); + timestamp = sti->index_entries[index].timestamp; av_log(s, AV_LOG_TRACE, "XX %"PRId64" %d %"PRId64"\n", timestamp, index, sti->index_entries[index].timestamp); @@ -1898,11 +1905,14 @@ static int avi_read_seek(AVFormatContext *s, int stream_index, /* Feed the DV video stream version of the timestamp to the */ /* DV demux so it can synthesize correct timestamps. */ - ff_dv_offset_reset(avi->dv_demux, timestamp); + ff_dv_ts_reset(avi->dv_demux, + av_rescale_q(timestamp, (AVRational){ ast->scale, ast->rate }, + st->time_base)); avi->stream_index = -1; return 0; } + timestamp /= FFMAX(ast->sample_size, 1); pos_min = pos; for (i = 0; i < s->nb_streams; i++) { diff --git a/libavformat/avienc.c b/libavformat/avienc.c index 14115b3e2b8..a61e5c31095 100644 --- a/libavformat/avienc.c +++ b/libavformat/avienc.c @@ -28,6 +28,7 @@ #include "config_components.h" #include "riff.h" #include "mpegts.h" +#include "mux.h" #include "rawutils.h" #include "libavformat/avlanguage.h" #include "libavutil/avstring.h" @@ -1003,19 +1004,19 @@ static const AVClass avi_muxer_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVOutputFormat ff_avi_muxer = { - .name = "avi", - .long_name = NULL_IF_CONFIG_SMALL("AVI (Audio Video Interleaved)"), - .mime_type = "video/x-msvideo", - .extensions = "avi", +const FFOutputFormat ff_avi_muxer = { + .p.name = "avi", + .p.long_name = NULL_IF_CONFIG_SMALL("AVI (Audio Video Interleaved)"), + .p.mime_type = "video/x-msvideo", + .p.extensions = "avi", .priv_data_size = sizeof(AVIContext), - .audio_codec = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_AC3, - .video_codec = AV_CODEC_ID_MPEG4, + .p.audio_codec = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_AC3, + .p.video_codec = AV_CODEC_ID_MPEG4, .init = avi_init, .deinit = avi_deinit, .write_header = avi_write_header, .write_packet = avi_write_packet, .write_trailer = avi_write_trailer, - .codec_tag = ff_riff_codec_tags_list, - .priv_class = &avi_muxer_class, + .p.codec_tag = ff_riff_codec_tags_list, + .p.priv_class = &avi_muxer_class, }; diff --git a/libavformat/avio.h b/libavformat/avio.h index 4bf6b1fbdaa..5f13e0622d3 100644 --- a/libavformat/avio.h +++ b/libavformat/avio.h @@ -295,16 +295,6 @@ typedef struct AVIOContext { */ int ignore_boundary_point; -#if FF_API_AVIOCONTEXT_WRITTEN - /** - * @deprecated field utilized privately by libavformat. For a public - * statistic of how many bytes were written out, see - * AVIOContext::bytes_written. - */ - attribute_deprecated - int64_t written; -#endif - /** * Maximum reached position before a backward seek in the write buffer, * used keeping track of already written data for a later flush. diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c index 257535a9642..4ad734a3c3e 100644 --- a/libavformat/aviobuf.c +++ b/libavformat/aviobuf.c @@ -125,11 +125,6 @@ void ffio_init_context(FFIOContext *ctx, ctx->current_type = AVIO_DATA_MARKER_UNKNOWN; ctx->last_time = AV_NOPTS_VALUE; ctx->short_seek_get = NULL; -#if FF_API_AVIOCONTEXT_WRITTEN -FF_DISABLE_DEPRECATION_WARNINGS - s->written = 0; -FF_ENABLE_DEPRECATION_WARNINGS -#endif } AVIOContext *avio_alloc_context( @@ -174,11 +169,6 @@ static void writeout(AVIOContext *s, const uint8_t *data, int len) if (s->pos + len > ctx->written_output_size) { ctx->written_output_size = s->pos + len; -#if FF_API_AVIOCONTEXT_WRITTEN -FF_DISABLE_DEPRECATION_WARNINGS - s->written = ctx->written_output_size; -FF_ENABLE_DEPRECATION_WARNINGS -#endif } } } diff --git a/libavformat/avisynth.c b/libavformat/avisynth.c index b426ac343e6..027e8c63f68 100644 --- a/libavformat/avisynth.c +++ b/libavformat/avisynth.c @@ -62,6 +62,7 @@ typedef struct AviSynthLibrary { AVSC_DECLARE_FUNC(avs_create_script_environment); AVSC_DECLARE_FUNC(avs_delete_script_environment); AVSC_DECLARE_FUNC(avs_get_audio); + AVSC_DECLARE_FUNC(avs_get_channel_mask); AVSC_DECLARE_FUNC(avs_get_error); AVSC_DECLARE_FUNC(avs_get_frame); AVSC_DECLARE_FUNC(avs_get_version); @@ -157,6 +158,7 @@ static av_cold int avisynth_load_library(void) LOAD_AVS_FUNC(avs_create_script_environment, 0); LOAD_AVS_FUNC(avs_delete_script_environment, 0); LOAD_AVS_FUNC(avs_get_audio, 0); + LOAD_AVS_FUNC(avs_get_channel_mask, 1); LOAD_AVS_FUNC(avs_get_error, 1); // New to AviSynth 2.6 LOAD_AVS_FUNC(avs_get_frame, 0); LOAD_AVS_FUNC(avs_get_version, 0); @@ -793,6 +795,10 @@ static int avisynth_create_stream_audio(AVFormatContext *s, AVStream *st) st->duration = avs->vi->num_audio_samples; avpriv_set_pts_info(st, 64, 1, avs->vi->audio_samples_per_second); + if (avs_library.avs_get_version(avs->clip) >= 10) + av_channel_layout_from_mask(&st->codecpar->ch_layout, + avs_library.avs_get_channel_mask(avs->vi)); + switch (avs->vi->sample_type) { case AVS_SAMPLE_INT8: st->codecpar->codec_id = AV_CODEC_ID_PCM_U8; diff --git a/libavformat/avr.c b/libavformat/avr.c index 1cc4d56bfbd..dce977b6ac1 100644 --- a/libavformat/avr.c +++ b/libavformat/avr.c @@ -70,6 +70,9 @@ static int avr_read_header(AVFormatContext *s) avio_skip(s->pb, 1); // replay speed st->codecpar->sample_rate = avio_rb24(s->pb); + if (st->codecpar->sample_rate == 0) + return AVERROR_INVALIDDATA; + avio_skip(s->pb, 4 * 3); avio_skip(s->pb, 2 * 3); avio_skip(s->pb, 20); diff --git a/libavformat/bit.c b/libavformat/bit.c index 9efb4ac0708..c3b9cf4d3dc 100644 --- a/libavformat/bit.c +++ b/libavformat/bit.c @@ -23,6 +23,7 @@ #include "avformat.h" #include "internal.h" +#include "mux.h" #include "libavcodec/get_bits.h" #include "libavcodec/put_bits.h" @@ -158,13 +159,13 @@ static int write_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -const AVOutputFormat ff_bit_muxer = { - .name = "bit", - .long_name = NULL_IF_CONFIG_SMALL("G.729 BIT file format"), - .mime_type = "audio/bit", - .extensions = "bit", - .audio_codec = AV_CODEC_ID_G729, - .video_codec = AV_CODEC_ID_NONE, +const FFOutputFormat ff_bit_muxer = { + .p.name = "bit", + .p.long_name = NULL_IF_CONFIG_SMALL("G.729 BIT file format"), + .p.mime_type = "audio/bit", + .p.extensions = "bit", + .p.audio_codec = AV_CODEC_ID_G729, + .p.video_codec = AV_CODEC_ID_NONE, .write_header = write_header, .write_packet = write_packet, }; diff --git a/libavformat/bonk.c b/libavformat/bonk.c index 7ce895fa965..bd99c553e70 100644 --- a/libavformat/bonk.c +++ b/libavformat/bonk.c @@ -59,7 +59,7 @@ static int bonk_read_header(AVFormatContext *s) AVStream *st; int ret; - for (int i = 0; !avio_feof(s->pb); i++) { + while (!avio_feof(s->pb)) { const int b = avio_r8(s->pb); if (!b) { uint32_t t; diff --git a/libavformat/cafenc.c b/libavformat/cafenc.c index b8317cd5edc..67be59806c0 100644 --- a/libavformat/cafenc.c +++ b/libavformat/cafenc.c @@ -276,16 +276,16 @@ static int caf_write_trailer(AVFormatContext *s) return 0; } -const AVOutputFormat ff_caf_muxer = { - .name = "caf", - .long_name = NULL_IF_CONFIG_SMALL("Apple CAF (Core Audio Format)"), - .mime_type = "audio/x-caf", - .extensions = "caf", +const FFOutputFormat ff_caf_muxer = { + .p.name = "caf", + .p.long_name = NULL_IF_CONFIG_SMALL("Apple CAF (Core Audio Format)"), + .p.mime_type = "audio/x-caf", + .p.extensions = "caf", .priv_data_size = sizeof(CAFContext), - .audio_codec = AV_CODEC_ID_PCM_S16BE, - .video_codec = AV_CODEC_ID_NONE, + .p.audio_codec = AV_CODEC_ID_PCM_S16BE, + .p.video_codec = AV_CODEC_ID_NONE, .write_header = caf_write_header, .write_packet = caf_write_packet, .write_trailer = caf_write_trailer, - .codec_tag = ff_caf_codec_tags_list, + .p.codec_tag = ff_caf_codec_tags_list, }; diff --git a/libavformat/cdg.c b/libavformat/cdg.c index 36f25e7f66b..f5982859114 100644 --- a/libavformat/cdg.c +++ b/libavformat/cdg.c @@ -26,6 +26,22 @@ #define CDG_COMMAND 0x09 #define CDG_MASK 0x3F +static int read_probe(const AVProbeData *p) +{ + const int cnt = p->buf_size / CDG_PACKET_SIZE; + int score = 0; + + for (int i = 0; i < cnt; i++) { + const int x = p->buf[i * CDG_PACKET_SIZE] & CDG_MASK; + + score += x == CDG_COMMAND; + if (x != CDG_COMMAND && x != 0) + return 0; + } + + return FFMIN(score, AVPROBE_SCORE_MAX); +} + static int read_header(AVFormatContext *s) { AVStream *vst; @@ -70,6 +86,7 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) const AVInputFormat ff_cdg_demuxer = { .name = "cdg", .long_name = NULL_IF_CONFIG_SMALL("CD Graphics"), + .read_probe = read_probe, .read_header = read_header, .read_packet = read_packet, .flags = AVFMT_GENERIC_INDEX, diff --git a/libavformat/chromaprint.c b/libavformat/chromaprint.c index 3953a5ced30..9e5fd780c1e 100644 --- a/libavformat/chromaprint.c +++ b/libavformat/chromaprint.c @@ -21,6 +21,7 @@ #include "avformat.h" #include "internal.h" +#include "mux.h" #include "libavutil/opt.h" #include @@ -176,15 +177,15 @@ static const AVClass chromaprint_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVOutputFormat ff_chromaprint_muxer = { - .name = "chromaprint", - .long_name = NULL_IF_CONFIG_SMALL("Chromaprint"), +const FFOutputFormat ff_chromaprint_muxer = { + .p.name = "chromaprint", + .p.long_name = NULL_IF_CONFIG_SMALL("Chromaprint"), .priv_data_size = sizeof(ChromaprintMuxContext), - .audio_codec = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE), + .p.audio_codec = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE), .write_header = write_header, .write_packet = write_packet, .write_trailer = write_trailer, .deinit = deinit, - .flags = AVFMT_NOTIMESTAMPS, - .priv_class = &chromaprint_class, + .p.flags = AVFMT_NOTIMESTAMPS, + .p.priv_class = &chromaprint_class, }; diff --git a/libavformat/codec2.c b/libavformat/codec2.c index 400c5acbdb6..f0f7b892533 100644 --- a/libavformat/codec2.c +++ b/libavformat/codec2.c @@ -28,6 +28,7 @@ #include "avio_internal.h" #include "avformat.h" #include "internal.h" +#include "mux.h" #include "rawenc.h" #include "pcm.h" @@ -309,16 +310,16 @@ const AVInputFormat ff_codec2_demuxer = { #endif #if CONFIG_CODEC2_MUXER -const AVOutputFormat ff_codec2_muxer = { - .name = "codec2", - .long_name = NULL_IF_CONFIG_SMALL("codec2 .c2 muxer"), +const FFOutputFormat ff_codec2_muxer = { + .p.name = "codec2", + .p.long_name = NULL_IF_CONFIG_SMALL("codec2 .c2 muxer"), + .p.extensions = "c2", + .p.audio_codec = AV_CODEC_ID_CODEC2, + .p.video_codec = AV_CODEC_ID_NONE, + .p.flags = AVFMT_NOTIMESTAMPS, .priv_data_size = sizeof(Codec2Context), - .extensions = "c2", - .audio_codec = AV_CODEC_ID_CODEC2, - .video_codec = AV_CODEC_ID_NONE, .write_header = codec2_write_header, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, }; #endif diff --git a/libavformat/concat.c b/libavformat/concat.c index dc0985e40cf..825e43a7fad 100644 --- a/libavformat/concat.c +++ b/libavformat/concat.c @@ -296,6 +296,8 @@ static av_cold int concatf_open(URLContext *h, const char *uri, int flags) av_bprint_finalize(&bp, NULL); data->length = i; + if (!data->length) + err = AVERROR_INVALIDDATA; if (err < 0) concat_close(h); diff --git a/libavformat/crcenc.c b/libavformat/crcenc.c index 9f40dd4ec0d..1779bdd91c0 100644 --- a/libavformat/crcenc.c +++ b/libavformat/crcenc.c @@ -23,6 +23,7 @@ #include "libavutil/adler32.h" #include "avformat.h" +#include "mux.h" typedef struct CRCState { uint32_t crcval; @@ -54,14 +55,14 @@ static int crc_write_trailer(struct AVFormatContext *s) return 0; } -const AVOutputFormat ff_crc_muxer = { - .name = "crc", - .long_name = NULL_IF_CONFIG_SMALL("CRC testing"), +const FFOutputFormat ff_crc_muxer = { + .p.name = "crc", + .p.long_name = NULL_IF_CONFIG_SMALL("CRC testing"), .priv_data_size = sizeof(CRCState), - .audio_codec = AV_CODEC_ID_PCM_S16LE, - .video_codec = AV_CODEC_ID_RAWVIDEO, + .p.audio_codec = AV_CODEC_ID_PCM_S16LE, + .p.video_codec = AV_CODEC_ID_RAWVIDEO, .init = crc_init, .write_packet = crc_write_packet, .write_trailer = crc_write_trailer, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 8e725a0d3fb..17fe5f430c6 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -1551,7 +1551,11 @@ static int dash_init(AVFormatContext *s) return AVERROR_MUXER_NOT_FOUND; ctx->interrupt_callback = s->interrupt_callback; ctx->opaque = s->opaque; +#if FF_API_AVFORMAT_IO_CLOSE +FF_DISABLE_DEPRECATION_WARNINGS ctx->io_close = s->io_close; +FF_ENABLE_DEPRECATION_WARNINGS +#endif ctx->io_close2 = s->io_close2; ctx->io_open = s->io_open; ctx->strict_std_compliance = s->strict_std_compliance; @@ -2346,10 +2350,10 @@ static int dash_check_bitstream(AVFormatContext *s, AVStream *st, DASHContext *c = s->priv_data; OutputStream *os = &c->streams[st->index]; AVFormatContext *oc = os->ctx; - if (oc->oformat->check_bitstream) { + if (ffofmt(oc->oformat)->check_bitstream) { AVStream *const ost = oc->streams[0]; int ret; - ret = oc->oformat->check_bitstream(oc, ost, avpkt); + ret = ffofmt(oc->oformat)->check_bitstream(oc, ost, avpkt); if (ret == 1) { FFStream *const sti = ffstream(st); FFStream *const osti = ffstream(ost); @@ -2419,19 +2423,19 @@ static const AVClass dash_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVOutputFormat ff_dash_muxer = { - .name = "dash", - .long_name = NULL_IF_CONFIG_SMALL("DASH Muxer"), - .extensions = "mpd", +const FFOutputFormat ff_dash_muxer = { + .p.name = "dash", + .p.long_name = NULL_IF_CONFIG_SMALL("DASH Muxer"), + .p.extensions = "mpd", + .p.audio_codec = AV_CODEC_ID_AAC, + .p.video_codec = AV_CODEC_ID_H264, + .p.flags = AVFMT_GLOBALHEADER | AVFMT_NOFILE | AVFMT_TS_NEGATIVE, + .p.priv_class = &dash_class, .priv_data_size = sizeof(DASHContext), - .audio_codec = AV_CODEC_ID_AAC, - .video_codec = AV_CODEC_ID_H264, - .flags = AVFMT_GLOBALHEADER | AVFMT_NOFILE | AVFMT_TS_NEGATIVE, .init = dash_init, .write_header = dash_write_header, .write_packet = dash_write_packet, .write_trailer = dash_write_trailer, .deinit = dash_free, .check_bitstream = dash_check_bitstream, - .priv_class = &dash_class, }; diff --git a/libavformat/dauddec.c b/libavformat/dauddec.c index dbbd39a3b40..7e411091eca 100644 --- a/libavformat/dauddec.c +++ b/libavformat/dauddec.c @@ -21,6 +21,7 @@ #include "libavutil/channel_layout.h" #include "avformat.h" +#include "internal.h" static int daud_header(AVFormatContext *s) { AVStream *st = avformat_new_stream(s, NULL); @@ -34,6 +35,9 @@ static int daud_header(AVFormatContext *s) { st->codecpar->bit_rate = 3 * 6 * 96000 * 8; st->codecpar->block_align = 3 * 6; st->codecpar->bits_per_coded_sample = 24; + + avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); + return 0; } @@ -41,7 +45,7 @@ static int daud_packet(AVFormatContext *s, AVPacket *pkt) { AVIOContext *pb = s->pb; int ret, size; if (avio_feof(pb)) - return AVERROR(EIO); + return AVERROR_EOF; size = avio_rb16(pb); avio_rb16(pb); // unknown ret = av_get_packet(pb, pkt, size); diff --git a/libavformat/daudenc.c b/libavformat/daudenc.c index 2e252449e92..2d84b16650e 100644 --- a/libavformat/daudenc.c +++ b/libavformat/daudenc.c @@ -20,6 +20,7 @@ */ #include "avformat.h" +#include "mux.h" static int daud_init(struct AVFormatContext *s) { @@ -42,13 +43,13 @@ static int daud_write_packet(struct AVFormatContext *s, AVPacket *pkt) return 0; } -const AVOutputFormat ff_daud_muxer = { - .name = "daud", - .long_name = NULL_IF_CONFIG_SMALL("D-Cinema audio"), - .extensions = "302", - .audio_codec = AV_CODEC_ID_PCM_S24DAUD, - .video_codec = AV_CODEC_ID_NONE, +const FFOutputFormat ff_daud_muxer = { + .p.name = "daud", + .p.long_name = NULL_IF_CONFIG_SMALL("D-Cinema audio"), + .p.extensions = "302", + .p.audio_codec = AV_CODEC_ID_PCM_S24DAUD, + .p.video_codec = AV_CODEC_ID_NONE, + .p.flags = AVFMT_NOTIMESTAMPS, .init = daud_init, .write_packet = daud_write_packet, - .flags = AVFMT_NOTIMESTAMPS, }; diff --git a/libavformat/demux.c b/libavformat/demux.c index 2dfd82a63ca..b218f645745 100644 --- a/libavformat/demux.c +++ b/libavformat/demux.c @@ -36,6 +36,7 @@ #include "libavcodec/avcodec.h" #include "libavcodec/bsf.h" +#include "libavcodec/codec_desc.h" #include "libavcodec/internal.h" #include "libavcodec/packet_internal.h" #include "libavcodec/raw.h" @@ -120,6 +121,8 @@ static int set_codec_from_probe_data(AVFormatContext *s, AVStream *st, { "mp3", AV_CODEC_ID_MP3, AVMEDIA_TYPE_AUDIO }, { "mpegvideo", AV_CODEC_ID_MPEG2VIDEO, AVMEDIA_TYPE_VIDEO }, { "truehd", AV_CODEC_ID_TRUEHD, AVMEDIA_TYPE_AUDIO }, + { "evc", AV_CODEC_ID_EVC, AVMEDIA_TYPE_VIDEO }, + { "vvc", AV_CODEC_ID_VVC, AVMEDIA_TYPE_VIDEO }, { 0 } }; int score; @@ -213,6 +216,8 @@ FF_ENABLE_DEPRECATION_WARNINGS if (ret < 0) return ret; + sti->codec_desc = avcodec_descriptor_get(sti->avctx->codec_id); + sti->need_context_update = 0; } return 0; @@ -668,14 +673,20 @@ static void compute_frame_duration(AVFormatContext *s, int *pnum, int *pden, if (st->r_frame_rate.num && (!pc || !codec_framerate.num)) { *pnum = st->r_frame_rate.den; *pden = st->r_frame_rate.num; + } else if ((s->iformat->flags & AVFMT_NOTIMESTAMPS) && + !codec_framerate.num && + st->avg_frame_rate.num && st->avg_frame_rate.den) { + *pnum = st->avg_frame_rate.den; + *pden = st->avg_frame_rate.num; } else if (st->time_base.num * 1000LL > st->time_base.den) { *pnum = st->time_base.num; *pden = st->time_base.den; } else if (codec_framerate.den * 1000LL > codec_framerate.num) { - av_assert0(sti->avctx->ticks_per_frame); + int ticks_per_frame = (sti->codec_desc && + (sti->codec_desc->props & AV_CODEC_PROP_FIELDS)) ? 2 : 1; av_reduce(pnum, pden, codec_framerate.den, - codec_framerate.num * (int64_t)sti->avctx->ticks_per_frame, + codec_framerate.num * (int64_t)ticks_per_frame, INT_MAX); if (pc && pc->repeat_pict) { @@ -687,7 +698,8 @@ static void compute_frame_duration(AVFormatContext *s, int *pnum, int *pden, /* If this codec can be interlaced or progressive then we need * a parser to compute duration of a packet. Thus if we have * no parser in such case leave duration undefined. */ - if (sti->avctx->ticks_per_frame > 1 && !pc) + if (sti->codec_desc && + (sti->codec_desc->props & AV_CODEC_PROP_FIELDS) && !pc) *pnum = *pden = 0; } break; @@ -743,7 +755,8 @@ static int64_t select_from_pts_buffer(AVStream *st, int64_t *pts_buffer, int64_t { FFStream *const sti = ffstream(st); int onein_oneout = st->codecpar->codec_id != AV_CODEC_ID_H264 && - st->codecpar->codec_id != AV_CODEC_ID_HEVC; + st->codecpar->codec_id != AV_CODEC_ID_HEVC && + st->codecpar->codec_id != AV_CODEC_ID_VVC; if (!onein_oneout) { int delay = sti->avctx->has_b_frames; @@ -933,7 +946,8 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st, int64_t offset; AVRational duration; int onein_oneout = st->codecpar->codec_id != AV_CODEC_ID_H264 && - st->codecpar->codec_id != AV_CODEC_ID_HEVC; + st->codecpar->codec_id != AV_CODEC_ID_HEVC && + st->codecpar->codec_id != AV_CODEC_ID_VVC; if (s->flags & AVFMT_FLAG_NOFILLIN) return; @@ -1186,6 +1200,11 @@ static int parse_packet(AVFormatContext *s, AVPacket *pkt, st->time_base, AV_ROUND_DOWN); } + } else if ((s->iformat->flags & AVFMT_NOTIMESTAMPS) && st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + if (st->time_base.num > 0 && st->time_base.den > 0 && + sti->parser->duration) { + out_pkt->duration = sti->parser->duration; + } } out_pkt->stream_index = st->index; @@ -1283,6 +1302,8 @@ static int read_frame_internal(AVFormatContext *s, AVPacket *pkt) return ret; } + sti->codec_desc = avcodec_descriptor_get(sti->avctx->codec_id); + sti->need_context_update = 0; } @@ -1984,7 +2005,7 @@ static int has_codec_parameters(const AVStream *st, const char **errmsg_ptr) /* returns 1 or 0 if or if not decoded data was returned, or a negative error */ static int try_decode_frame(AVFormatContext *s, AVStream *st, - const AVPacket *avpkt, AVDictionary **options) + const AVPacket *pkt, AVDictionary **options) { FFStream *const sti = ffstream(st); AVCodecContext *const avctx = sti->avctx; @@ -1992,9 +2013,9 @@ static int try_decode_frame(AVFormatContext *s, AVStream *st, int got_picture = 1, ret = 0; AVFrame *frame = av_frame_alloc(); AVSubtitle subtitle; - AVPacket pkt = *avpkt; int do_skip_frame = 0; enum AVDiscard skip_frame; + int pkt_to_send = pkt->size > 0; if (!frame) return AVERROR(ENOMEM); @@ -2043,7 +2064,7 @@ static int try_decode_frame(AVFormatContext *s, AVStream *st, avctx->skip_frame = AVDISCARD_ALL; } - while ((pkt.size > 0 || (!pkt.data && got_picture)) && + while ((pkt_to_send || (!pkt->data && got_picture)) && ret >= 0 && (!has_codec_parameters(st, NULL) || !has_decode_delay_been_guessed(st) || (!sti->codec_info_nb_frames && @@ -2051,11 +2072,11 @@ static int try_decode_frame(AVFormatContext *s, AVStream *st, got_picture = 0; if (avctx->codec_type == AVMEDIA_TYPE_VIDEO || avctx->codec_type == AVMEDIA_TYPE_AUDIO) { - ret = avcodec_send_packet(avctx, &pkt); + ret = avcodec_send_packet(avctx, pkt); if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) break; if (ret >= 0) - pkt.size = 0; + pkt_to_send = 0; ret = avcodec_receive_frame(avctx, frame); if (ret >= 0) got_picture = 1; @@ -2063,11 +2084,11 @@ static int try_decode_frame(AVFormatContext *s, AVStream *st, ret = 0; } else if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) { ret = avcodec_decode_subtitle2(avctx, &subtitle, - &got_picture, &pkt); + &got_picture, pkt); if (got_picture) avsubtitle_free(&subtitle); if (ret >= 0) - pkt.size = 0; + pkt_to_send = 0; } if (ret >= 0) { if (got_picture) @@ -2156,10 +2177,20 @@ static int get_std_framerate(int i) * Old DivX and Xvid often have nonsense timebases like 1fps or 2fps. * MPEG-2 commonly misuses field repeat flags to store different framerates. * And there are "variable" fps files this needs to detect as well. */ -static int tb_unreliable(AVCodecContext *c) +static int tb_unreliable(AVFormatContext *ic, AVStream *st) { - if (c->time_base.den >= 101LL * c->time_base.num || - c->time_base.den < 5LL * c->time_base.num || + FFStream *const sti = ffstream(st); + const AVCodecDescriptor *desc = sti->codec_desc; + AVCodecContext *c = sti->avctx; + AVRational mul = (AVRational){ desc && (desc->props & AV_CODEC_PROP_FIELDS) ? 2 : 1, 1 }; + AVRational time_base = c->framerate.num ? av_inv_q(av_mul_q(c->framerate, mul)) + /* NOHEADER check added to not break existing behavior */ + : (((ic->ctx_flags & AVFMTCTX_NOHEADER) || + st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) ? (AVRational){0, 1} + : st->time_base); + + if (time_base.den >= 101LL * time_base.num || + time_base.den < 5LL * time_base.num || // c->codec_tag == AV_RL32("DIVX") || // c->codec_tag == AV_RL32("XVID") || c->codec_tag == AV_RL32("mp4v") || @@ -2243,11 +2274,11 @@ void ff_rfps_calculate(AVFormatContext *ic) // the check for tb_unreliable() is not completely correct, since this is not about handling // an unreliable/inexact time base, but a time base that is finer than necessary, as e.g. // ipmovie.c produces. - if (tb_unreliable(sti->avctx) && sti->info->duration_count > 15 && sti->info->duration_gcd > FFMAX(1, st->time_base.den/(500LL*st->time_base.num)) && !st->r_frame_rate.num && + if (tb_unreliable(ic, st) && sti->info->duration_count > 15 && sti->info->duration_gcd > FFMAX(1, st->time_base.den/(500LL*st->time_base.num)) && !st->r_frame_rate.num && sti->info->duration_gcd < INT64_MAX / st->time_base.num) av_reduce(&st->r_frame_rate.num, &st->r_frame_rate.den, st->time_base.den, st->time_base.num * sti->info->duration_gcd, INT_MAX); if (sti->info->duration_count > 1 && !st->r_frame_rate.num - && tb_unreliable(sti->avctx)) { + && tb_unreliable(ic, st)) { int num = 0; double best_error = 0.01; AVRational ref_rate = st->r_frame_rate.num ? st->r_frame_rate : av_inv_q(st->time_base); @@ -2459,14 +2490,6 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) FFStream *const sti = ffstream(st); AVCodecContext *const avctx = sti->avctx; - if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || - st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) { -/* if (!st->time_base.num) - st->time_base = */ - if (!avctx->time_base.num) - avctx->time_base = st->time_base; - } - /* check if the caller has overridden the codec id */ // only for the split stuff if (!sti->parser && !(ic->flags & AVFMT_FLAG_NOPARSE) && sti->request_probe <= 0) { @@ -2544,7 +2567,7 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) * the correct fps. */ if (av_q2d(st->time_base) > 0.0005) fps_analyze_framecount *= 2; - if (!tb_unreliable(sti->avctx)) + if (!tb_unreliable(ic, st)) fps_analyze_framecount = 0; if (ic->fps_probe_size >= 0) fps_analyze_framecount = ic->fps_probe_size; @@ -2568,7 +2591,7 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) extract_extradata_check(st)) break; if (sti->first_dts == AV_NOPTS_VALUE && - !(ic->iformat->flags & AVFMT_NOTIMESTAMPS) && + (!(ic->iformat->flags & AVFMT_NOTIMESTAMPS) || sti->need_parsing == AVSTREAM_PARSE_FULL_RAW) && sti->codec_info_nb_frames < ((st->disposition & AV_DISPOSITION_ATTACHED_PIC) ? 1 : ic->max_ts_probe) && (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)) @@ -2712,13 +2735,14 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) break; } if (pkt->duration > 0) { + const int fields = sti->codec_desc && (sti->codec_desc->props & AV_CODEC_PROP_FIELDS); if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE && pkt->pts != AV_NOPTS_VALUE && st->start_time != AV_NOPTS_VALUE && pkt->pts >= st->start_time && (uint64_t)pkt->pts - st->start_time < INT64_MAX ) { sti->info->codec_info_duration = FFMIN(pkt->pts - st->start_time, sti->info->codec_info_duration + pkt->duration); } else sti->info->codec_info_duration += pkt->duration; - sti->info->codec_info_duration_fields += sti->parser && sti->need_parsing && avctx->ticks_per_frame == 2 + sti->info->codec_info_duration_fields += sti->parser && sti->need_parsing && fields ? sti->parser->repeat_pict + 1 : 2; } } @@ -2857,17 +2881,19 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den, best_fps, 12 * 1001, INT_MAX); } - if (!st->r_frame_rate.num) { - if ( avctx->time_base.den * (int64_t) st->time_base.num - <= avctx->time_base.num * (uint64_t)avctx->ticks_per_frame * st->time_base.den) { - av_reduce(&st->r_frame_rate.num, &st->r_frame_rate.den, - avctx->time_base.den, (int64_t)avctx->time_base.num * avctx->ticks_per_frame, INT_MAX); + const AVCodecDescriptor *desc = sti->codec_desc; + AVRational mul = (AVRational){ desc && (desc->props & AV_CODEC_PROP_FIELDS) ? 2 : 1, 1 }; + AVRational fr = av_mul_q(avctx->framerate, mul); + + if (fr.num && fr.den && av_cmp_q(st->time_base, av_inv_q(fr)) <= 0) { + st->r_frame_rate = fr; } else { st->r_frame_rate.num = st->time_base.den; st->r_frame_rate.den = st->time_base.num; } } + st->codecpar->framerate = avctx->framerate; if (sti->display_aspect_ratio.num && sti->display_aspect_ratio.den) { AVRational hw_ratio = { avctx->height, avctx->width }; st->sample_aspect_ratio = av_mul_q(sti->display_aspect_ratio, diff --git a/libavformat/dovi_isom.c b/libavformat/dovi_isom.c index 76681b94513..c8fdf566e40 100644 --- a/libavformat/dovi_isom.c +++ b/libavformat/dovi_isom.c @@ -28,7 +28,8 @@ #include "avformat.h" #include "dovi_isom.h" -int ff_isom_parse_dvcc_dvvc(AVFormatContext *s, AVStream *st, const uint8_t *buf_ptr, uint64_t size) +int ff_isom_parse_dvcc_dvvc(void *logctx, AVStream *st, + const uint8_t *buf_ptr, uint64_t size) { uint32_t buf; AVDOVIDecoderConfigurationRecord *dovi; @@ -70,7 +71,7 @@ int ff_isom_parse_dvcc_dvvc(AVFormatContext *s, AVStream *st, const uint8_t *buf return ret; } - av_log(s, AV_LOG_TRACE, "DOVI in dvcC/dvvC/dvwC box, version: %d.%d, profile: %d, level: %d, " + av_log(logctx, AV_LOG_TRACE, "DOVI in dvcC/dvvC/dvwC box, version: %d.%d, profile: %d, level: %d, " "rpu flag: %d, el flag: %d, bl flag: %d, compatibility id: %d\n", dovi->dv_version_major, dovi->dv_version_minor, dovi->dv_profile, dovi->dv_level, @@ -82,8 +83,8 @@ int ff_isom_parse_dvcc_dvvc(AVFormatContext *s, AVStream *st, const uint8_t *buf return 0; } -void ff_isom_put_dvcc_dvvc(AVFormatContext *s, uint8_t out[ISOM_DVCC_DVVC_SIZE], - AVDOVIDecoderConfigurationRecord *dovi) +void ff_isom_put_dvcc_dvvc(void *logctx, uint8_t out[ISOM_DVCC_DVVC_SIZE], + const AVDOVIDecoderConfigurationRecord *dovi) { PutBitContext pb; @@ -106,7 +107,8 @@ void ff_isom_put_dvcc_dvvc(AVFormatContext *s, uint8_t out[ISOM_DVCC_DVVC_SIZE], flush_put_bits(&pb); - av_log(s, AV_LOG_DEBUG, "DOVI in %s box, version: %d.%d, profile: %d, level: %d, " + av_log(logctx, AV_LOG_DEBUG, + "DOVI in %s box, version: %d.%d, profile: %d, level: %d, " "rpu flag: %d, el flag: %d, bl flag: %d, compatibility id: %d\n", dovi->dv_profile > 10 ? "dvwC" : (dovi->dv_profile > 7 ? "dvvC" : "dvcC"), dovi->dv_version_major, dovi->dv_version_minor, diff --git a/libavformat/dovi_isom.h b/libavformat/dovi_isom.h index 15261643191..1221a527935 100644 --- a/libavformat/dovi_isom.h +++ b/libavformat/dovi_isom.h @@ -28,8 +28,9 @@ #define ISOM_DVCC_DVVC_SIZE 24 -int ff_isom_parse_dvcc_dvvc(AVFormatContext *s, AVStream *st, const uint8_t *buf_ptr, uint64_t size); -void ff_isom_put_dvcc_dvvc(AVFormatContext *s, uint8_t out[ISOM_DVCC_DVVC_SIZE], - AVDOVIDecoderConfigurationRecord *dovi); +int ff_isom_parse_dvcc_dvvc(void *logctx, AVStream *st, + const uint8_t *buf_ptr, uint64_t size); +void ff_isom_put_dvcc_dvvc(void *logctx, uint8_t out[ISOM_DVCC_DVVC_SIZE], + const AVDOVIDecoderConfigurationRecord *dovi); #endif /* AVFORMAT_DOVI_ISOM_H */ diff --git a/libavformat/dtshddec.c b/libavformat/dtshddec.c index edd02b4561f..a3dea0668fe 100644 --- a/libavformat/dtshddec.c +++ b/libavformat/dtshddec.c @@ -55,7 +55,7 @@ static int dtshd_read_header(AVFormatContext *s) DTSHDDemuxContext *dtshd = s->priv_data; AVIOContext *pb = s->pb; uint64_t chunk_type, chunk_size; - int64_t duration, data_start; + int64_t duration, orig_nb_samples, data_start; AVStream *st; int ret; char *value; @@ -103,9 +103,12 @@ static int dtshd_read_header(AVFormatContext *s) duration = avio_rb32(pb); // num_frames duration *= avio_rb16(pb); // samples_per_frames st->duration = duration; - avio_skip(pb, 5); + orig_nb_samples = avio_rb32(pb); + orig_nb_samples <<= 8; + orig_nb_samples |= avio_r8(pb); st->codecpar->ch_layout.nb_channels = ff_dca_count_chs_for_mask(avio_rb16(pb)); st->codecpar->initial_padding = avio_rb16(pb); + st->codecpar->trailing_padding = FFMAX(st->duration - orig_nb_samples - st->codecpar->initial_padding, 0); avio_skip(pb, chunk_size - 21); break; case FILEINFO: diff --git a/libavformat/dv.c b/libavformat/dv.c index ffed1a7a90e..6df93fe4162 100644 --- a/libavformat/dv.c +++ b/libavformat/dv.c @@ -69,6 +69,9 @@ struct DVDemuxContext { uint8_t audio_buf[4][8192]; int ach; int frames; + + int64_t next_pts_video; + int64_t next_pts_audio; }; static inline uint16_t dv_audio_12to16(uint16_t sample) @@ -280,7 +283,7 @@ static int dv_extract_audio_info(DVDemuxContext *c, const uint8_t *frame) if (!c->ast[i]) return AVERROR(ENOMEM); - avpriv_set_pts_info(c->ast[i], 64, c->sys->time_base.num, c->sys->time_base.den); + avpriv_set_pts_info(c->ast[i], 64, 1, DV_TIMESCALE_AUDIO); c->ast[i]->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; c->ast[i]->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE; c->ast[i]->codecpar->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO; @@ -314,8 +317,6 @@ static int dv_extract_video_info(DVDemuxContext *c, const uint8_t *frame) par = c->vst->codecpar; - avpriv_set_pts_info(c->vst, 64, c->sys->time_base.num, - c->sys->time_base.den); c->vst->avg_frame_rate = av_inv_q(c->vst->time_base); /* finding out SAR is a little bit messy */ @@ -357,9 +358,10 @@ static int dv_init_demux(AVFormatContext *s, DVDemuxContext *c) c->fctx = s; c->vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; c->vst->codecpar->codec_id = AV_CODEC_ID_DVVIDEO; - c->vst->codecpar->bit_rate = 25000000; c->vst->start_time = 0; + avpriv_set_pts_info(c->vst, 64, 1, DV_TIMESCALE_VIDEO); + /* Audio streams are added later as they are encountered. */ s->ctx_flags |= AVFMTCTX_NOHEADER; @@ -419,6 +421,7 @@ int avpriv_dv_get_packet(DVDemuxContext *c, AVPacket *pkt) int avpriv_dv_produce_packet(DVDemuxContext *c, AVPacket *pkt, uint8_t *buf, int buf_size, int64_t pos) { + int64_t pts, duration; int size, i; uint8_t *ppcm[5] = { 0 }; @@ -434,11 +437,31 @@ int avpriv_dv_produce_packet(DVDemuxContext *c, AVPacket *pkt, if (size < 0) return size; + if (c->ach) { + int64_t next_pts_video = av_rescale_q(c->next_pts_video, c->vst->time_base, + c->ast[0]->time_base); + + duration = av_rescale_q(size / 4, + (AVRational){ 1, c->audio_pkt[0].sample_rate }, + c->ast[0]->time_base); + + // if audio timestamps are more than one frame away from video, + // assume desync happened (e.g. due to dropped audio frames) and + // resynchronize + pts = (FFABS(next_pts_video - c->next_pts_audio) >= duration) ? + next_pts_video : c->next_pts_audio; + + c->next_pts_audio = pts + duration; + } + for (i = 0; i < c->ach; i++) { - c->audio_pkt[i].pos = pos; - c->audio_pkt[i].size = size; - c->audio_pkt[i].pts = (c->sys->height == 720) ? (c->frames & ~1) : c->frames; - c->audio_pkt[i].duration = 1; + DVPacket *dpkt = &c->audio_pkt[i]; + + dpkt->pos = pos; + dpkt->size = size; + dpkt->pts = pts; + dpkt->duration = duration; + ppcm[i] = c->audio_buf[i]; } if (c->ach) @@ -463,7 +486,10 @@ int avpriv_dv_produce_packet(DVDemuxContext *c, AVPacket *pkt, pkt->size = size; pkt->flags |= AV_PKT_FLAG_KEY; pkt->stream_index = c->vst->index; - pkt->pts = c->frames; + pkt->pts = c->next_pts_video; + pkt->duration = av_rescale_q(1, c->sys->time_base, c->vst->time_base); + + c->next_pts_video += pkt->duration; } c->frames++; @@ -472,28 +498,36 @@ int avpriv_dv_produce_packet(DVDemuxContext *c, AVPacket *pkt, } static int64_t dv_frame_offset(AVFormatContext *s, DVDemuxContext *c, - int64_t timestamp, int flags) + int64_t *timestamp) { // FIXME: sys may be wrong if last dv_read_packet() failed (buffer is junk) FFFormatContext *const si = ffformatcontext(s); const int frame_size = c->sys->frame_size; + int64_t frame_count = av_rescale_q(*timestamp, c->vst->time_base, c->sys->time_base); int64_t offset; int64_t size = avio_size(s->pb) - si->data_offset; int64_t max_offset = ((size - 1) / frame_size) * frame_size; - offset = frame_size * timestamp; + offset = frame_size * frame_count; if (size >= 0 && offset > max_offset) offset = max_offset; else if (offset < 0) offset = 0; + *timestamp = av_rescale_q(offset / frame_size, c->sys->time_base, c->vst->time_base); + return offset + si->data_offset; } -void ff_dv_offset_reset(DVDemuxContext *c, int64_t frame_offset) +void ff_dv_ts_reset(DVDemuxContext *c, int64_t ts) { - c->frames = frame_offset; + c->frames = !c->sys ? 0 : + av_rescale_q(ts, c->vst->time_base, c->sys->time_base); + c->next_pts_video = ts; + c->next_pts_audio = (!c->sys || !c->ast[0]) ? AV_NOPTS_VALUE : + av_rescale_q(ts, c->vst->time_base, c->ast[0]->time_base); + c->audio_pkt[0].size = c->audio_pkt[1].size = 0; c->audio_pkt[2].size = c->audio_pkt[3].size = 0; } @@ -618,12 +652,19 @@ static int dv_read_seek(AVFormatContext *s, int stream_index, { RawDVContext *r = s->priv_data; DVDemuxContext *c = &r->dv_demux; - int64_t offset = dv_frame_offset(s, c, timestamp, flags); + int64_t offset; + + // seek using the video stream + if (stream_index != c->vst->index) + timestamp = av_rescale_q(timestamp, s->streams[stream_index]->time_base, + c->vst->time_base); + + offset = dv_frame_offset(s, c, ×tamp); if (avio_seek(s->pb, offset, SEEK_SET) < 0) return -1; - ff_dv_offset_reset(c, offset / c->sys->frame_size); + ff_dv_ts_reset(c, timestamp); return 0; } diff --git a/libavformat/dv.h b/libavformat/dv.h index efced6ccf04..d21ea19e024 100644 --- a/libavformat/dv.h +++ b/libavformat/dv.h @@ -34,6 +34,6 @@ typedef struct DVDemuxContext DVDemuxContext; DVDemuxContext* avpriv_dv_init_demux(AVFormatContext* s); int avpriv_dv_get_packet(DVDemuxContext*, AVPacket *); int avpriv_dv_produce_packet(DVDemuxContext*, AVPacket*, uint8_t*, int, int64_t); -void ff_dv_offset_reset(DVDemuxContext *c, int64_t frame_offset); +void ff_dv_ts_reset(DVDemuxContext *c, int64_t ts_video); #endif /* AVFORMAT_DV_H */ diff --git a/libavformat/dvenc.c b/libavformat/dvenc.c index 11947aa4934..29d2dc47acf 100644 --- a/libavformat/dvenc.c +++ b/libavformat/dvenc.c @@ -442,13 +442,13 @@ static void dv_deinit(AVFormatContext *s) av_fifo_freep2(&c->audio_data[i]); } -const AVOutputFormat ff_dv_muxer = { - .name = "dv", - .long_name = NULL_IF_CONFIG_SMALL("DV (Digital Video)"), - .extensions = "dv", +const FFOutputFormat ff_dv_muxer = { + .p.name = "dv", + .p.long_name = NULL_IF_CONFIG_SMALL("DV (Digital Video)"), + .p.extensions = "dv", .priv_data_size = sizeof(DVMuxContext), - .audio_codec = AV_CODEC_ID_PCM_S16LE, - .video_codec = AV_CODEC_ID_DVVIDEO, + .p.audio_codec = AV_CODEC_ID_PCM_S16LE, + .p.video_codec = AV_CODEC_ID_DVVIDEO, .write_header = dv_write_header, .write_packet = dv_write_packet, .deinit = dv_deinit, diff --git a/libavformat/evc.c b/libavformat/evc.c new file mode 100644 index 00000000000..95f07266b03 --- /dev/null +++ b/libavformat/evc.c @@ -0,0 +1,388 @@ +/* + * EVC helper functions for muxers + * Copyright (c) 2022 Dawid Kozinski + * + * 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 "libavutil/intreadwrite.h" +#include "libavcodec/get_bits.h" +#include "libavcodec/golomb.h" +#include "libavcodec/evc.h" +#include "avformat.h" +#include "avio.h" +#include "evc.h" +#include "avio_internal.h" + +// @see ISO/IEC 14496-15:2021 Coding of audio-visual objects - Part 15: section 12.3.3.1 +enum { + SPS_INDEX, + PPS_INDEX, + APS_INDEX, + SEI_INDEX, + NB_ARRAYS +}; + +// @see ISO/IEC 14496-15:2021 Coding of audio-visual objects - Part 15: section 12.3.3.3 +typedef struct EVCNALUnitArray { + uint8_t array_completeness; // when equal to 1 indicates that all NAL units of the given type are in the following array + uint8_t NAL_unit_type; // indicates the type of the NAL units in the following array + uint16_t numNalus; // indicates the number of NAL units of the indicated type + uint16_t *nalUnitLength; // indicates the length in bytes of the NAL unit + uint8_t **nalUnit; // contains an SPS, PPS, APS or a SEI NAL unit, as specified in ISO/IEC 23094-1 +} EVCNALUnitArray; + +/** + * @brief Specifies the decoder configuration information for ISO/IEC 23094-1 video content. + * @see ISO/IEC 14496-15:2021 Coding of audio-visual objects - Part 15: section 12.3.3.2 + * Carriage of network abstraction layer (NAL) unit structured video in the ISO base media file format + */ +typedef struct EVCDecoderConfigurationRecord { + uint8_t configurationVersion; // 8 bits + uint8_t profile_idc; // 8 bits + uint8_t level_idc; // 8 bits + uint32_t toolset_idc_h; // 32 bits + uint32_t toolset_idc_l; // 32 bits + uint8_t chroma_format_idc; // 2 bits + uint8_t bit_depth_luma_minus8; // 3 bits + uint8_t bit_depth_chroma_minus8; // 3 bits + uint16_t pic_width_in_luma_samples; // 16 bits + uint16_t pic_height_in_luma_samples; // 16 bits + uint8_t reserved; // 6 bits '000000'b + uint8_t lengthSizeMinusOne; // 2 bits + uint8_t num_of_arrays; // 8 bits + EVCNALUnitArray arrays[NB_ARRAYS]; +} EVCDecoderConfigurationRecord; + +typedef struct NALU { + int offset; + uint32_t size; +} NALU; + +typedef struct NALUList { + NALU *nalus; + unsigned nalus_array_size; + unsigned nb_nalus; ///< valid entries in nalus +} NALUList; + +// @see ISO_IEC_23094-1 (7.3.2.1 SPS RBSP syntax) +static int evcc_parse_sps(const uint8_t *bs, int bs_size, EVCDecoderConfigurationRecord *evcc) +{ + GetBitContext gb; + unsigned sps_seq_parameter_set_id; + int ret; + + bs += EVC_NALU_HEADER_SIZE; + bs_size -= EVC_NALU_HEADER_SIZE; + + ret = init_get_bits8(&gb, bs, bs_size); + if (ret < 0) + return ret; + + sps_seq_parameter_set_id = get_ue_golomb_long(&gb); + + if (sps_seq_parameter_set_id >= EVC_MAX_SPS_COUNT) + return AVERROR_INVALIDDATA; + + // the Baseline profile is indicated by profile_idc eqal to 0 + // the Main profile is indicated by profile_idc eqal to 1 + evcc->profile_idc = get_bits(&gb, 8); + + evcc->level_idc = get_bits(&gb, 8); + + evcc->toolset_idc_h = get_bits_long(&gb, 32); + evcc->toolset_idc_l = get_bits_long(&gb, 32); + + // 0 - monochrome + // 1 - 4:2:0 + // 2 - 4:2:2 + // 3 - 4:4:4 + evcc->chroma_format_idc = get_ue_golomb_long(&gb); + if (evcc->chroma_format_idc > 3) + return AVERROR_INVALIDDATA; + + evcc->pic_width_in_luma_samples = get_ue_golomb_long(&gb); + evcc->pic_height_in_luma_samples = get_ue_golomb_long(&gb); + + evcc->bit_depth_luma_minus8 = get_ue_golomb_long(&gb); + evcc->bit_depth_chroma_minus8 = get_ue_golomb_long(&gb); + // EVCDecoderConfigurationRecord can't store values > 7. Limit it to bit depth 14. + if (evcc->bit_depth_luma_minus8 > 6 || evcc->bit_depth_chroma_minus8 > 6) + return AVERROR_INVALIDDATA; + + return 0; +} + +// @see ISO/IEC 14496-15:2021 Coding of audio-visual objects - Part 15: section 12.3.3.3 +static int evcc_array_add_nal_unit(const uint8_t *nal_buf, uint32_t nal_size, + uint8_t nal_type, int ps_array_completeness, + EVCNALUnitArray *array) +{ + int ret; + uint16_t numNalus = array->numNalus; + + ret = av_reallocp_array(&array->nalUnit, numNalus + 1, sizeof(uint8_t *)); + if (ret < 0) + return ret; + + ret = av_reallocp_array(&array->nalUnitLength, numNalus + 1, sizeof(uint16_t)); + if (ret < 0) + return ret; + + array->nalUnit [numNalus] = (uint8_t *)nal_buf; + array->nalUnitLength[numNalus] = nal_size; + array->NAL_unit_type = nal_type; + array->numNalus++; + + /* + * When the sample entry name is 'evc1', the default and mandatory value of + * array_completeness is 1 for arrays of all types of parameter sets, and 0 + * for all other arrays. + */ + if (nal_type == EVC_SPS_NUT || nal_type == EVC_PPS_NUT || nal_type == EVC_APS_NUT) + array->array_completeness = ps_array_completeness; + + return 0; +} + +static void evcc_init(EVCDecoderConfigurationRecord *evcc) +{ + memset(evcc, 0, sizeof(EVCDecoderConfigurationRecord)); + evcc->configurationVersion = 1; + evcc->lengthSizeMinusOne = 3; // 4 bytes +} + +static void evcc_close(EVCDecoderConfigurationRecord *evcc) +{ + for (unsigned i = 0; i < FF_ARRAY_ELEMS(evcc->arrays); i++) { + EVCNALUnitArray *const array = &evcc->arrays[i]; + array->numNalus = 0; + av_freep(&array->nalUnit); + av_freep(&array->nalUnitLength); + } +} + +static int evcc_write(AVIOContext *pb, EVCDecoderConfigurationRecord *evcc) +{ + uint16_t sps_count; + + av_log(NULL, AV_LOG_TRACE, "configurationVersion: %"PRIu8"\n", + evcc->configurationVersion); + av_log(NULL, AV_LOG_TRACE, "profile_idc: %"PRIu8"\n", + evcc->profile_idc); + av_log(NULL, AV_LOG_TRACE, "level_idc: %"PRIu8"\n", + evcc->level_idc); + av_log(NULL, AV_LOG_TRACE, "toolset_idc_h: %"PRIu32"\n", + evcc->toolset_idc_h); + av_log(NULL, AV_LOG_TRACE, "toolset_idc_l: %"PRIu32"\n", + evcc->toolset_idc_l); + av_log(NULL, AV_LOG_TRACE, "chroma_format_idc: %"PRIu8"\n", + evcc->chroma_format_idc); + av_log(NULL, AV_LOG_TRACE, "bit_depth_luma_minus8: %"PRIu8"\n", + evcc->bit_depth_luma_minus8); + av_log(NULL, AV_LOG_TRACE, "bit_depth_chroma_minus8: %"PRIu8"\n", + evcc->bit_depth_chroma_minus8); + av_log(NULL, AV_LOG_TRACE, "pic_width_in_luma_samples: %"PRIu16"\n", + evcc->pic_width_in_luma_samples); + av_log(NULL, AV_LOG_TRACE, "pic_height_in_luma_samples: %"PRIu16"\n", + evcc->pic_height_in_luma_samples); + av_log(NULL, AV_LOG_TRACE, "lengthSizeMinusOne: %"PRIu8"\n", + evcc->lengthSizeMinusOne); + av_log(NULL, AV_LOG_TRACE, "num_of_arrays: %"PRIu8"\n", + evcc->num_of_arrays); + for (unsigned i = 0; i < FF_ARRAY_ELEMS(evcc->arrays); i++) { + const EVCNALUnitArray *const array = &evcc->arrays[i]; + + if(array->numNalus == 0) + continue; + + av_log(NULL, AV_LOG_TRACE, "array_completeness[%"PRIu8"]: %"PRIu8"\n", + i, array->array_completeness); + av_log(NULL, AV_LOG_TRACE, "NAL_unit_type[%"PRIu8"]: %"PRIu8"\n", + i, array->NAL_unit_type); + av_log(NULL, AV_LOG_TRACE, "numNalus[%"PRIu8"]: %"PRIu16"\n", + i, array->numNalus); + for ( unsigned j = 0; j < array->numNalus; j++) + av_log(NULL, AV_LOG_TRACE, + "nalUnitLength[%"PRIu8"][%"PRIu16"]: %"PRIu16"\n", + i, j, array->nalUnitLength[j]); + } + + /* + * We need at least one SPS. + */ + sps_count = evcc->arrays[SPS_INDEX].numNalus; + if (!sps_count || sps_count > EVC_MAX_SPS_COUNT) + return AVERROR_INVALIDDATA; + + /* unsigned int(8) configurationVersion = 1; */ + avio_w8(pb, evcc->configurationVersion); + + /* unsigned int(8) profile_idc */ + avio_w8(pb, evcc->profile_idc); + + /* unsigned int(8) profile_idc */ + avio_w8(pb, evcc->level_idc); + + /* unsigned int(32) toolset_idc_h */ + avio_wb32(pb, evcc->toolset_idc_h); + + /* unsigned int(32) toolset_idc_l */ + avio_wb32(pb, evcc->toolset_idc_l); + + /* + * unsigned int(2) chroma_format_idc; + * unsigned int(3) bit_depth_luma_minus8; + * unsigned int(3) bit_depth_chroma_minus8; + */ + avio_w8(pb, evcc->chroma_format_idc << 6 | + evcc->bit_depth_luma_minus8 << 3 | + evcc->bit_depth_chroma_minus8); + + /* unsigned int(16) pic_width_in_luma_samples; */ + avio_wb16(pb, evcc->pic_width_in_luma_samples); + + /* unsigned int(16) pic_width_in_luma_samples; */ + avio_wb16(pb, evcc->pic_height_in_luma_samples); + + /* + * bit(6) reserved = '111111'b; + * unsigned int(2) chromaFormat; + */ + avio_w8(pb, evcc->lengthSizeMinusOne | 0xfc); + + /* unsigned int(8) numOfArrays; */ + avio_w8(pb, evcc->num_of_arrays); + + for (unsigned i = 0; i < FF_ARRAY_ELEMS(evcc->arrays); i++) { + const EVCNALUnitArray *const array = &evcc->arrays[i]; + + if (!array->numNalus) + continue; + + /* + * bit(1) array_completeness; + * unsigned int(1) reserved = 0; + * unsigned int(6) NAL_unit_type; + */ + avio_w8(pb, array->array_completeness << 7 | + array->NAL_unit_type & 0x3f); + + /* unsigned int(16) numNalus; */ + avio_wb16(pb, array->numNalus); + + for (unsigned j = 0; j < array->numNalus; j++) { + /* unsigned int(16) nalUnitLength; */ + avio_wb16(pb, array->nalUnitLength[j]); + + /* bit(8*nalUnitLength) nalUnit; */ + avio_write(pb, array->nalUnit[j], + array->nalUnitLength[j]); + } + } + + return 0; +} + +int ff_isom_write_evcc(AVIOContext *pb, const uint8_t *data, + int size, int ps_array_completeness) +{ + EVCDecoderConfigurationRecord evcc; + int nalu_type; + size_t nalu_size; + int bytes_to_read = size; + unsigned array_index; + + int ret = 0; + + if (size < 8) { + /* We can't write a valid evcC from the provided data */ + return AVERROR_INVALIDDATA; + } else if (*data == 1) { + /* Data is already evcC-formatted */ + avio_write(pb, data, size); + return 0; + } + + evcc_init(&evcc); + + while (bytes_to_read > EVC_NALU_LENGTH_PREFIX_SIZE) { + nalu_size = evc_read_nal_unit_length(data, EVC_NALU_LENGTH_PREFIX_SIZE); + if (nalu_size == 0) break; + + data += EVC_NALU_LENGTH_PREFIX_SIZE; + bytes_to_read -= EVC_NALU_LENGTH_PREFIX_SIZE; + + if (bytes_to_read < nalu_size) break; + + nalu_type = evc_get_nalu_type(data, bytes_to_read); + if (nalu_type < EVC_NOIDR_NUT || nalu_type > EVC_UNSPEC_NUT62) { + ret = AVERROR_INVALIDDATA; + goto end; + } + + // @see ISO/IEC 14496-15:2021 Coding of audio-visual objects - Part 15: section 12.3.3.3 + // NAL_unit_type indicates the type of the NAL units in the following array (which shall be all of that type); + // - it takes a value as defined in ISO/IEC 23094-1; + // - it is restricted to take one of the values indicating a SPS, PPS, APS, or SEI NAL unit. + switch (nalu_type) { + case EVC_SPS_NUT: + array_index = SPS_INDEX; + break; + case EVC_PPS_NUT: + array_index = PPS_INDEX; + break; + case EVC_APS_NUT: + array_index = APS_INDEX; + break; + case EVC_SEI_NUT: + array_index = SEI_INDEX; + break; + default: + array_index = -1; + break; + } + + if( (array_index == SPS_INDEX) || + (array_index == PPS_INDEX) || + (array_index == APS_INDEX) || + (array_index == SEI_INDEX) ) { + + ret = evcc_array_add_nal_unit(data, nalu_size, nalu_type, ps_array_completeness, &(evcc.arrays[array_index])); + + if (ret < 0) + goto end; + if (evcc.arrays[array_index].numNalus == 1) + evcc.num_of_arrays++; + + if(nalu_type == EVC_SPS_NUT) { + ret = evcc_parse_sps(data, nalu_size, &evcc); + if (ret < 0) + goto end; + } + } + + data += nalu_size; + bytes_to_read -= nalu_size; + } + + ret = evcc_write(pb, &evcc); + +end: + evcc_close(&evcc); + return ret; +} diff --git a/libavformat/evc.h b/libavformat/evc.h new file mode 100644 index 00000000000..f30831257d1 --- /dev/null +++ b/libavformat/evc.h @@ -0,0 +1,69 @@ +/* + * EVC helper functions for muxers + * Copyright (c) 2022 Dawid Kozinski + * + * 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 AVFORMAT_EVC_H +#define AVFORMAT_EVC_H + +#include + +#include "libavutil/intreadwrite.h" +#include "libavutil/rational.h" +#include "libavcodec/evc.h" +#include "avio.h" + +static inline int evc_get_nalu_type(const uint8_t *p, int bits_size) +{ + int unit_type_plus1 = 0; + + if (bits_size >= EVC_NALU_HEADER_SIZE) { + // forbidden_zero_bit + if ((p[0] & 0x80) != 0) // Cannot get bitstream information. Malformed bitstream. + return -1; + + // nal_unit_type + unit_type_plus1 = (p[0] >> 1) & 0x3F; + } + + return unit_type_plus1 - 1; +} + +static inline uint32_t evc_read_nal_unit_length(const uint8_t *bits, int bits_size) +{ + if (bits_size >= EVC_NALU_LENGTH_PREFIX_SIZE) + return AV_RB32(bits); + + return 0; +} + +/** + * Writes EVC sample metadata to the provided AVIOContext. + * + * @param pb pointer to the AVIOContext where the evc sample metadata shall be written + * @param buf input data buffer + * @param size size in bytes of the input data buffer + * @param ps_array_completeness @see ISO/IEC 14496-15:2021 Coding of audio-visual objects - Part 15: section 12.3.3.3 + * + * @return 0 in case of success, a negative error code in case of failure + */ +int ff_isom_write_evcc(AVIOContext *pb, const uint8_t *data, + int size, int ps_array_completeness); + +#endif // AVFORMAT_EVC_H diff --git a/libavformat/evcdec.c b/libavformat/evcdec.c new file mode 100644 index 00000000000..5ace604db61 --- /dev/null +++ b/libavformat/evcdec.c @@ -0,0 +1,217 @@ +/* + * RAW EVC video demuxer + * + * Copyright (c) 2021 Dawid Kozinski + * + * 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 "libavcodec/evc.h" +#include "libavcodec/bsf.h" + +#include "libavutil/opt.h" + +#include "avformat.h" +#include "avio_internal.h" +#include "evc.h" +#include "internal.h" + + +#define RAW_PACKET_SIZE 1024 + +typedef struct EVCDemuxContext { + const AVClass *class; + AVRational framerate; + + AVBSFContext *bsf; + +} EVCDemuxContext; + +#define DEC AV_OPT_FLAG_DECODING_PARAM +#define OFFSET(x) offsetof(EVCDemuxContext, x) +static const AVOption evc_options[] = { + { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, DEC}, + { NULL }, +}; +#undef OFFSET + +static const AVClass evc_demuxer_class = { + .class_name = "EVC Annex B demuxer", + .item_name = av_default_item_name, + .option = evc_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static int annexb_probe(const AVProbeData *p) +{ + int nalu_type; + size_t nalu_size; + int got_sps = 0, got_pps = 0, got_idr = 0, got_nonidr = 0; + const unsigned char *bits = p->buf; + int bytes_to_read = p->buf_size; + + while (bytes_to_read > EVC_NALU_LENGTH_PREFIX_SIZE) { + + nalu_size = evc_read_nal_unit_length(bits, EVC_NALU_LENGTH_PREFIX_SIZE); + if (nalu_size == 0) break; + + bits += EVC_NALU_LENGTH_PREFIX_SIZE; + bytes_to_read -= EVC_NALU_LENGTH_PREFIX_SIZE; + + if(bytes_to_read < nalu_size) break; + + nalu_type = evc_get_nalu_type(bits, bytes_to_read); + + if (nalu_type == EVC_SPS_NUT) + got_sps++; + else if (nalu_type == EVC_PPS_NUT) + got_pps++; + else if (nalu_type == EVC_IDR_NUT ) + got_idr++; + else if (nalu_type == EVC_NOIDR_NUT) + got_nonidr++; + + bits += nalu_size; + bytes_to_read -= nalu_size; + } + + if (got_sps && got_pps && (got_idr || got_nonidr > 3)) + return AVPROBE_SCORE_EXTENSION + 1; // 1 more than .mpg + + return 0; +} + +static int evc_read_header(AVFormatContext *s) +{ + AVStream *st; + FFStream *sti; + const AVBitStreamFilter *filter = av_bsf_get_by_name("evc_frame_merge"); + EVCDemuxContext *c = s->priv_data; + int ret = 0; + + st = avformat_new_stream(s, NULL); + if (!st) { + ret = AVERROR(ENOMEM); + goto fail; + } + sti = ffstream(st); + + st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; + st->codecpar->codec_id = AV_CODEC_ID_EVC; + + // This causes sending to the parser full frames, not chunks of data + // The flag PARSER_FLAG_COMPLETE_FRAMES will be set in demux.c (demux.c: 1316) + sti->need_parsing = AVSTREAM_PARSE_HEADERS; + + st->avg_frame_rate = c->framerate; + + // taken from rawvideo demuxers + avpriv_set_pts_info(st, 64, 1, 1200000); + + ret = av_bsf_alloc(filter, &c->bsf); + if (ret < 0) + return ret; + + ret = avcodec_parameters_copy(c->bsf->par_in, st->codecpar); + if (ret < 0) + return ret; + + ret = av_bsf_init(c->bsf); + if (ret < 0) + return ret; + +fail: + return ret; +} + +static int evc_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int ret; + uint32_t nalu_size; + int au_end_found = 0; + EVCDemuxContext *const c = s->priv_data; + + while(!au_end_found) { + uint8_t buf[EVC_NALU_LENGTH_PREFIX_SIZE]; + + if (avio_feof(s->pb)) + goto end; + + ret = ffio_ensure_seekback(s->pb, EVC_NALU_LENGTH_PREFIX_SIZE); + if (ret < 0) + return ret; + + ret = avio_read(s->pb, buf, EVC_NALU_LENGTH_PREFIX_SIZE); + if (ret < 0) + return ret; + if (ret != EVC_NALU_LENGTH_PREFIX_SIZE) + return AVERROR_INVALIDDATA; + + nalu_size = evc_read_nal_unit_length(buf, EVC_NALU_LENGTH_PREFIX_SIZE); + if (!nalu_size || nalu_size > INT_MAX) + return AVERROR_INVALIDDATA; + + avio_seek(s->pb, -EVC_NALU_LENGTH_PREFIX_SIZE, SEEK_CUR); + + ret = av_get_packet(s->pb, pkt, nalu_size + EVC_NALU_LENGTH_PREFIX_SIZE); + if (ret < 0) + return ret; + if (ret != (nalu_size + EVC_NALU_LENGTH_PREFIX_SIZE)) + return AVERROR_INVALIDDATA; + +end: + ret = av_bsf_send_packet(c->bsf, pkt); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Failed to send packet to " + "evc_frame_merge filter\n"); + return ret; + } + + ret = av_bsf_receive_packet(c->bsf, pkt); + if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) + av_log(s, AV_LOG_ERROR, "evc_frame_merge filter failed to " + "send output packet\n"); + + if (ret != AVERROR(EAGAIN)) + au_end_found = 1; + } + + return ret; +} + +static int evc_read_close(AVFormatContext *s) +{ + EVCDemuxContext *const c = s->priv_data; + + av_bsf_free(&c->bsf); + return 0; +} + +const AVInputFormat ff_evc_demuxer = { + .name = "evc", + .long_name = NULL_IF_CONFIG_SMALL("EVC Annex B"), + .read_probe = annexb_probe, + .read_header = evc_read_header, // annexb_read_header + .read_packet = evc_read_packet, // annexb_read_packet + .read_close = evc_read_close, + .extensions = "evc", + .flags = AVFMT_GENERIC_INDEX | AVFMT_NOTIMESTAMPS, + .flags_internal = FF_FMT_INIT_CLEANUP, + .raw_codec_id = AV_CODEC_ID_EVC, + .priv_data_size = sizeof(EVCDemuxContext), + .priv_class = &evc_demuxer_class, +}; diff --git a/libavformat/ffmetaenc.c b/libavformat/ffmetaenc.c index f27ac1ac502..ef076407b5d 100644 --- a/libavformat/ffmetaenc.c +++ b/libavformat/ffmetaenc.c @@ -23,6 +23,7 @@ #include "avformat.h" #include "ffmeta.h" +#include "mux.h" #include "libavutil/dict.h" @@ -87,12 +88,12 @@ static int write_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -const AVOutputFormat ff_ffmetadata_muxer = { - .name = "ffmetadata", - .long_name = NULL_IF_CONFIG_SMALL("FFmpeg metadata in text"), - .extensions = "ffmeta", +const FFOutputFormat ff_ffmetadata_muxer = { + .p.name = "ffmetadata", + .p.long_name = NULL_IF_CONFIG_SMALL("FFmpeg metadata in text"), + .p.extensions = "ffmeta", .write_header = write_header, .write_packet = write_packet, .write_trailer = write_trailer, - .flags = AVFMT_NOTIMESTAMPS | AVFMT_NOSTREAMS, + .p.flags = AVFMT_NOTIMESTAMPS | AVFMT_NOSTREAMS, }; diff --git a/libavformat/fifo.c b/libavformat/fifo.c index c125a97b0dd..9a3a23729c9 100644 --- a/libavformat/fifo.c +++ b/libavformat/fifo.c @@ -501,7 +501,11 @@ static int fifo_mux_init(AVFormatContext *avf, const AVOutputFormat *oformat, if (ret < 0) return ret; avf2->opaque = avf->opaque; +#if FF_API_AVFORMAT_IO_CLOSE +FF_DISABLE_DEPRECATION_WARNINGS avf2->io_close = avf->io_close; +FF_ENABLE_DEPRECATION_WARNINGS +#endif avf2->io_close2 = avf->io_close2; avf2->io_open = avf->io_open; avf2->flags = avf->flags; @@ -707,15 +711,15 @@ static const AVClass fifo_muxer_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVOutputFormat ff_fifo_muxer = { - .name = "fifo", - .long_name = NULL_IF_CONFIG_SMALL("FIFO queue pseudo-muxer"), +const FFOutputFormat ff_fifo_muxer = { + .p.name = "fifo", + .p.long_name = NULL_IF_CONFIG_SMALL("FIFO queue pseudo-muxer"), + .p.priv_class = &fifo_muxer_class, + .p.flags = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, .priv_data_size = sizeof(FifoContext), .init = fifo_init, .write_header = fifo_write_header, .write_packet = fifo_write_packet, .write_trailer = fifo_write_trailer, .deinit = fifo_deinit, - .priv_class = &fifo_muxer_class, - .flags = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, }; diff --git a/libavformat/fifo_test.c b/libavformat/fifo_test.c index 80c0c846129..0f12d88b0fd 100644 --- a/libavformat/fifo_test.c +++ b/libavformat/fifo_test.c @@ -25,6 +25,7 @@ #include "libavutil/time.h" #include "avformat.h" +#include "mux.h" #include "url.h" /* Implementation of mock muxer to simulate real muxer failures */ @@ -137,15 +138,15 @@ static const AVClass failing_muxer_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVOutputFormat ff_fifo_test_muxer = { - .name = "fifo_test", - .long_name = NULL_IF_CONFIG_SMALL("Fifo test muxer"), +const FFOutputFormat ff_fifo_test_muxer = { + .p.name = "fifo_test", + .p.long_name = NULL_IF_CONFIG_SMALL("Fifo test muxer"), .priv_data_size = sizeof(FailingMuxerContext), .write_header = failing_write_header, .write_packet = failing_write_packet, .write_trailer = failing_write_trailer, .deinit = failing_deinit, - .priv_class = &failing_muxer_class, - .flags = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH, + .p.priv_class = &failing_muxer_class, + .p.flags = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH, }; diff --git a/libavformat/filmstripdec.c b/libavformat/filmstripdec.c index 2b6ba63fcf0..000f807181d 100644 --- a/libavformat/filmstripdec.c +++ b/libavformat/filmstripdec.c @@ -86,7 +86,7 @@ static int read_packet(AVFormatContext *s, AVStream *st = s->streams[0]; if (avio_feof(s->pb)) - return AVERROR(EIO); + return AVERROR_EOF; pkt->dts = avio_tell(s->pb) / (st->codecpar->width * (int64_t)(st->codecpar->height + film->leading) * 4); pkt->size = av_get_packet(s->pb, pkt, st->codecpar->width * st->codecpar->height * 4); avio_skip(s->pb, st->codecpar->width * (int64_t) film->leading * 4); diff --git a/libavformat/filmstripenc.c b/libavformat/filmstripenc.c index ebb7294175f..9033dba6925 100644 --- a/libavformat/filmstripenc.c +++ b/libavformat/filmstripenc.c @@ -27,6 +27,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" #include "avio_internal.h" +#include "mux.h" #include "rawenc.h" #define RAND_TAG MKBETAG('R','a','n','d') @@ -59,12 +60,12 @@ static int write_trailer(AVFormatContext *s) return 0; } -const AVOutputFormat ff_filmstrip_muxer = { - .name = "filmstrip", - .long_name = NULL_IF_CONFIG_SMALL("Adobe Filmstrip"), - .extensions = "flm", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_RAWVIDEO, +const FFOutputFormat ff_filmstrip_muxer = { + .p.name = "filmstrip", + .p.long_name = NULL_IF_CONFIG_SMALL("Adobe Filmstrip"), + .p.extensions = "flm", + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_RAWVIDEO, .write_header = write_header, .write_packet = ff_raw_write_packet, .write_trailer = write_trailer, diff --git a/libavformat/fitsdec.c b/libavformat/fitsdec.c index 54412c60ff8..e0f502e4e33 100644 --- a/libavformat/fitsdec.c +++ b/libavformat/fitsdec.c @@ -37,7 +37,6 @@ typedef struct FITSContext { const AVClass *class; AVRational framerate; int first_image; - int64_t pts; } FITSContext; static int fits_probe(const AVProbeData *p) @@ -61,7 +60,6 @@ static int fits_read_header(AVFormatContext *s) st->codecpar->codec_id = AV_CODEC_ID_FITS; avpriv_set_pts_info(st, 64, fits->framerate.den, fits->framerate.num); - fits->pts = 0; fits->first_image = 1; return 0; } @@ -196,13 +194,11 @@ static int fits_read_packet(AVFormatContext *s, AVPacket *pkt) pkt->size = avbuf.len - 80; av_freep(&buf); ret = avio_read(s->pb, pkt->data + pkt->size, size); - if (ret < 0) { + if (ret < 0) return ret; - } pkt->size += ret; - pkt->pts = fits->pts; - fits->pts++; + pkt->duration = 1; return 0; @@ -221,6 +217,7 @@ static const AVClass fits_demuxer_class = { .item_name = av_default_item_name, .option = fits_options, .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_DEMUXER, }; const AVInputFormat ff_fits_demuxer = { @@ -231,5 +228,5 @@ const AVInputFormat ff_fits_demuxer = { .read_header = fits_read_header, .read_packet = fits_read_packet, .priv_class = &fits_demuxer_class, - .raw_codec_id = AV_CODEC_ID_FITS, + .flags = AVFMT_NOTIMESTAMPS, }; diff --git a/libavformat/fitsenc.c b/libavformat/fitsenc.c index 37ee10bb139..14a4572f70a 100644 --- a/libavformat/fitsenc.c +++ b/libavformat/fitsenc.c @@ -26,6 +26,7 @@ #include "avio_internal.h" #include "internal.h" +#include "mux.h" typedef struct FITSContext { int first_image; @@ -83,48 +84,48 @@ static int write_image_header(AVFormatContext *s) float datamax, datamin; switch (encctx->format) { - case AV_PIX_FMT_GRAY8: - bitpix = 8; - naxis = 2; - datamin = 0; - datamax = 255; - break; - case AV_PIX_FMT_GRAY16BE: - bitpix = 16; - naxis = 2; - bzero = 32768; - datamin = 0; - datamax = 65535; - break; - case AV_PIX_FMT_GBRP: - case AV_PIX_FMT_GBRAP: - bitpix = 8; - naxis = 3; - rgb = 1; - if (encctx->format == AV_PIX_FMT_GBRP) { - naxis3 = 3; - } else { - naxis3 = 4; - } - datamin = 0; - datamax = 255; - break; - case AV_PIX_FMT_GBRP16BE: - case AV_PIX_FMT_GBRAP16BE: - bitpix = 16; - naxis = 3; - rgb = 1; - if (encctx->format == AV_PIX_FMT_GBRP16BE) { - naxis3 = 3; - } else { - naxis3 = 4; - } - bzero = 32768; - datamin = 0; - datamax = 65535; - break; - default: - return AVERROR(EINVAL); + case AV_PIX_FMT_GRAY8: + bitpix = 8; + naxis = 2; + datamin = 0; + datamax = 255; + break; + case AV_PIX_FMT_GRAY16BE: + bitpix = 16; + naxis = 2; + bzero = 32768; + datamin = 0; + datamax = 65535; + break; + case AV_PIX_FMT_GBRP: + case AV_PIX_FMT_GBRAP: + bitpix = 8; + naxis = 3; + rgb = 1; + if (encctx->format == AV_PIX_FMT_GBRP) { + naxis3 = 3; + } else { + naxis3 = 4; + } + datamin = 0; + datamax = 255; + break; + case AV_PIX_FMT_GBRP16BE: + case AV_PIX_FMT_GBRAP16BE: + bitpix = 16; + naxis = 3; + rgb = 1; + if (encctx->format == AV_PIX_FMT_GBRP16BE) { + naxis3 = 3; + } else { + naxis3 = 4; + } + bzero = 32768; + datamin = 0; + datamax = 65535; + break; + default: + return AVERROR(EINVAL); } if (fitsctx->first_image) { @@ -191,13 +192,14 @@ static int fits_write_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -const AVOutputFormat ff_fits_muxer = { - .name = "fits", - .long_name = NULL_IF_CONFIG_SMALL("Flexible Image Transport System"), - .extensions = "fits", +const FFOutputFormat ff_fits_muxer = { + .p.name = "fits", + .p.long_name = NULL_IF_CONFIG_SMALL("Flexible Image Transport System"), + .p.extensions = "fits", + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_FITS, .priv_data_size = sizeof(FITSContext), - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_FITS, - .write_header = fits_write_header, - .write_packet = fits_write_packet, + .write_header = fits_write_header, + .write_packet = fits_write_packet, + .p.flags = AVFMT_NOTIMESTAMPS, }; diff --git a/libavformat/flacenc.c b/libavformat/flacenc.c index d7930f4a6e2..a8beec7750b 100644 --- a/libavformat/flacenc.c +++ b/libavformat/flacenc.c @@ -30,6 +30,7 @@ #include "flacenc.h" #include "id3v2.h" #include "internal.h" +#include "mux.h" #include "version.h" #include "vorbiscomment.h" @@ -413,19 +414,19 @@ static const AVClass flac_muxer_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVOutputFormat ff_flac_muxer = { - .name = "flac", - .long_name = NULL_IF_CONFIG_SMALL("raw FLAC"), +const FFOutputFormat ff_flac_muxer = { + .p.name = "flac", + .p.long_name = NULL_IF_CONFIG_SMALL("raw FLAC"), .priv_data_size = sizeof(FlacMuxerContext), - .mime_type = "audio/x-flac", - .extensions = "flac", - .audio_codec = AV_CODEC_ID_FLAC, - .video_codec = AV_CODEC_ID_PNG, + .p.mime_type = "audio/x-flac", + .p.extensions = "flac", + .p.audio_codec = AV_CODEC_ID_FLAC, + .p.video_codec = AV_CODEC_ID_PNG, .init = flac_init, .write_header = flac_write_header, .write_packet = flac_write_packet, .write_trailer = flac_write_trailer, .deinit = flac_deinit, - .flags = AVFMT_NOTIMESTAMPS, - .priv_class = &flac_muxer_class, + .p.flags = AVFMT_NOTIMESTAMPS, + .p.priv_class = &flac_muxer_class, }; diff --git a/libavformat/flv.h b/libavformat/flv.h index 3571b90279c..91e0a4140cb 100644 --- a/libavformat/flv.h +++ b/libavformat/flv.h @@ -35,6 +35,12 @@ #define FLV_VIDEO_FRAMETYPE_OFFSET 4 +/* Extended VideoTagHeader + * defined in reference link: + * https://github.com/veovera/enhanced-rtmp/blob/main/enhanced-rtmp-v1.pdf + * */ +#define FLV_IS_EX_HEADER 0x80 + /* bitmasks to isolate specific values */ #define FLV_AUDIO_CHANNEL_MASK 0x01 #define FLV_AUDIO_SAMPLESIZE_MASK 0x02 @@ -112,6 +118,15 @@ enum { FLV_CODECID_MPEG4 = 9, }; +enum { + PacketTypeSequenceStart = 0, + PacketTypeCodedFrames = 1, + PacketTypeSequenceEnd = 2, + PacketTypeCodedFramesX = 3, + PacketTypeMetadata = 4, + PacketTypeMPEG2TSSequenceStart = 5, +}; + enum { FLV_FRAME_KEY = 1 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< key frame (for AVC, a seekable frame) FLV_FRAME_INTER = 2 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< inter frame (for AVC, a non-seekable frame) diff --git a/libavformat/flvdec.c b/libavformat/flvdec.c index d83edff727c..bdcf96b4aeb 100644 --- a/libavformat/flvdec.c +++ b/libavformat/flvdec.c @@ -79,6 +79,8 @@ typedef struct FLVContext { int64_t last_ts; int64_t time_offset; int64_t time_pos; + + uint8_t exheader; } FLVContext; /* AMF date type */ @@ -302,14 +304,18 @@ static void flv_set_audio_codec(AVFormatContext *s, AVStream *astream, } } -static int flv_same_video_codec(AVCodecParameters *vpar, int flags) +static int flv_same_video_codec(AVCodecParameters *vpar, uint32_t flv_codecid) { - int flv_codecid = flags & FLV_VIDEO_CODECID_MASK; - if (!vpar->codec_id && !vpar->codec_tag) return 1; switch (flv_codecid) { + case MKBETAG('h', 'v', 'c', '1'): + return vpar->codec_id == AV_CODEC_ID_HEVC; + case MKBETAG('a', 'v', '0', '1'): + return vpar->codec_id == AV_CODEC_ID_AV1; + case MKBETAG('v', 'p', '0', '9'): + return vpar->codec_id == AV_CODEC_ID_VP9; case FLV_CODECID_H263: return vpar->codec_id == AV_CODEC_ID_FLV1; case FLV_CODECID_SCREEN: @@ -328,13 +334,26 @@ static int flv_same_video_codec(AVCodecParameters *vpar, int flags) } static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream, - int flv_codecid, int read) + uint32_t flv_codecid, int read) { FFStream *const vstreami = ffstream(vstream); int ret = 0; AVCodecParameters *par = vstream->codecpar; enum AVCodecID old_codec_id = vstream->codecpar->codec_id; + switch (flv_codecid) { + case MKBETAG('h', 'v', 'c', '1'): + par->codec_id = AV_CODEC_ID_HEVC; + vstreami->need_parsing = AVSTREAM_PARSE_HEADERS; + break; + case MKBETAG('a', 'v', '0', '1'): + par->codec_id = AV_CODEC_ID_AV1; + vstreami->need_parsing = AVSTREAM_PARSE_HEADERS; + break; + case MKBETAG('v', 'p', '0', '9'): + par->codec_id = AV_CODEC_ID_VP9; + vstreami->need_parsing = AVSTREAM_PARSE_HEADERS; + break; case FLV_CODECID_H263: par->codec_id = AV_CODEC_ID_FLV1; break; @@ -366,11 +385,9 @@ static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream, case FLV_CODECID_H264: par->codec_id = AV_CODEC_ID_H264; vstreami->need_parsing = AVSTREAM_PARSE_HEADERS; - ret = 3; // not 4, reading packet type will consume one byte break; case FLV_CODECID_MPEG4: par->codec_id = AV_CODEC_ID_MPEG4; - ret = 3; break; default: avpriv_request_sample(s, "Video codec (%x)", flv_codecid); @@ -796,6 +813,7 @@ static int flv_read_header(AVFormatContext *s) s->start_time = 0; flv->sum_flv_tag_size = 0; flv->last_keyframe_stream_index = -1; + flv->exheader = 0; return 0; } @@ -1025,6 +1043,7 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) AVStream *st = NULL; int last = -1; int orig_size; + uint32_t video_codec_id = 0; retry: /* pkt size is repeated at end. skip it */ @@ -1071,7 +1090,17 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) } else if (type == FLV_TAG_TYPE_VIDEO) { stream_type = FLV_STREAM_TYPE_VIDEO; flags = avio_r8(s->pb); + video_codec_id = flags & FLV_VIDEO_CODECID_MASK; + /* + * Reference Enhancing FLV 2023-03-v1.0.0-B.8 + * https://github.com/veovera/enhanced-rtmp/blob/main/enhanced-rtmp-v1.pdf + * */ + flv->exheader = (flags >> 7) & 1; size--; + if (flv->exheader) { + video_codec_id = avio_rb32(s->pb); + size -= 4; + } if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_VIDEO_INFO_CMD) goto skip; } else if (type == FLV_TAG_TYPE_META) { @@ -1129,7 +1158,7 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) break; } else if (stream_type == FLV_STREAM_TYPE_VIDEO) { if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && - (s->video_codec_id || flv_same_video_codec(st->codecpar, flags))) + (s->video_codec_id || flv_same_video_codec(st->codecpar, video_codec_id))) break; } else if (stream_type == FLV_STREAM_TYPE_SUBTITLE) { if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) @@ -1230,7 +1259,7 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) avcodec_parameters_free(&par); } } else if (stream_type == FLV_STREAM_TYPE_VIDEO) { - int ret = flv_set_video_codec(s, st, flags & FLV_VIDEO_CODECID_MASK, 1); + int ret = flv_set_video_codec(s, st, video_codec_id, 1); if (ret < 0) return ret; size -= ret; @@ -1242,16 +1271,25 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) if (st->codecpar->codec_id == AV_CODEC_ID_AAC || st->codecpar->codec_id == AV_CODEC_ID_H264 || - st->codecpar->codec_id == AV_CODEC_ID_MPEG4) { - int type = avio_r8(s->pb); - size--; + st->codecpar->codec_id == AV_CODEC_ID_MPEG4 || + st->codecpar->codec_id == AV_CODEC_ID_HEVC || + st->codecpar->codec_id == AV_CODEC_ID_AV1 || + st->codecpar->codec_id == AV_CODEC_ID_VP9) { + int type = 0; + if (flv->exheader && stream_type == FLV_STREAM_TYPE_VIDEO) { + type = flags & 0x0F; + } else { + type = avio_r8(s->pb); + size--; + } if (size < 0) { ret = AVERROR_INVALIDDATA; goto leave; } - if (st->codecpar->codec_id == AV_CODEC_ID_H264 || st->codecpar->codec_id == AV_CODEC_ID_MPEG4) { + if (st->codecpar->codec_id == AV_CODEC_ID_H264 || st->codecpar->codec_id == AV_CODEC_ID_MPEG4 || + (st->codecpar->codec_id == AV_CODEC_ID_HEVC && type == PacketTypeCodedFrames)) { // sign extension int32_t cts = (avio_rb24(s->pb) + 0xff800000) ^ 0xff800000; pts = av_sat_add64(dts, cts); @@ -1265,9 +1303,11 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) "invalid timestamps %"PRId64" %"PRId64"\n", dts, pts); dts = pts = AV_NOPTS_VALUE; } + size -= 3; } if (type == 0 && (!st->codecpar->extradata || st->codecpar->codec_id == AV_CODEC_ID_AAC || - st->codecpar->codec_id == AV_CODEC_ID_H264)) { + st->codecpar->codec_id == AV_CODEC_ID_H264 || st->codecpar->codec_id == AV_CODEC_ID_HEVC || + st->codecpar->codec_id == AV_CODEC_ID_AV1 || st->codecpar->codec_id == AV_CODEC_ID_VP9)) { AVDictionaryEntry *t; if (st->codecpar->extradata) { diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c index 128ae8ebc0e..f6d10f331c5 100644 --- a/libavformat/flvenc.c +++ b/libavformat/flvenc.c @@ -28,6 +28,9 @@ #include "libavcodec/mpeg4audio.h" #include "avio.h" #include "avc.h" +#include "av1.h" +#include "vpcc.h" +#include "hevc.h" #include "avformat.h" #include "flv.h" #include "internal.h" @@ -46,6 +49,9 @@ static const AVCodecTag flv_video_codec_ids[] = { { AV_CODEC_ID_VP6, FLV_CODECID_VP6 }, { AV_CODEC_ID_VP6A, FLV_CODECID_VP6A }, { AV_CODEC_ID_H264, FLV_CODECID_H264 }, + { AV_CODEC_ID_HEVC, MKBETAG('h', 'v', 'c', '1') }, + { AV_CODEC_ID_AV1, MKBETAG('a', 'v', '0', '1') }, + { AV_CODEC_ID_VP9, MKBETAG('v', 'p', '0', '9') }, { AV_CODEC_ID_NONE, 0 } }; @@ -117,12 +123,9 @@ typedef struct FLVContext { AVCodecParameters *data_par; int flags; + int64_t last_ts[FLV_STREAM_TYPE_NB]; } FLVContext; -typedef struct FLVStreamContext { - int64_t last_ts; ///< last timestamp for each stream -} FLVStreamContext; - static int get_audio_flags(AVFormatContext *s, AVCodecParameters *par) { int flags = (par->bits_per_coded_sample == 16) ? FLV_SAMPLESSIZE_16BIT @@ -235,13 +238,16 @@ static void put_timestamp(AVIOContext *pb, int64_t ts) { avio_w8(pb, (ts >> 24) & 0x7F); } -static void put_avc_eos_tag(AVIOContext *pb, unsigned ts) +static void put_eos_tag(AVIOContext *pb, unsigned ts, enum AVCodecID codec_id) { + uint32_t tag = ff_codec_get_tag(flv_video_codec_ids, codec_id); + /* ub[4] FrameType = 1, ub[4] CodecId */ + tag |= 1 << 4; avio_w8(pb, FLV_TAG_TYPE_VIDEO); avio_wb24(pb, 5); /* Tag Data Size */ put_timestamp(pb, ts); avio_wb24(pb, 0); /* StreamId = 0 */ - avio_w8(pb, 23); /* ub[4] FrameType = 1, ub[4] CodecId = 7 */ + avio_w8(pb, tag); avio_w8(pb, 2); /* AVC end of sequence */ avio_wb24(pb, 0); /* Always 0 for AVC EOS. */ avio_wb32(pb, 16); /* Size of FLV tag */ @@ -489,7 +495,8 @@ static void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par, i FLVContext *flv = s->priv_data; if (par->codec_id == AV_CODEC_ID_AAC || par->codec_id == AV_CODEC_ID_H264 - || par->codec_id == AV_CODEC_ID_MPEG4) { + || par->codec_id == AV_CODEC_ID_MPEG4 || par->codec_id == AV_CODEC_ID_HEVC + || par->codec_id == AV_CODEC_ID_AV1 || par->codec_id == AV_CODEC_ID_VP9) { int64_t pos; avio_w8(pb, par->codec_type == AVMEDIA_TYPE_VIDEO ? @@ -505,18 +512,18 @@ static void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par, i if (!par->extradata_size && (flv->flags & FLV_AAC_SEQ_HEADER_DETECT)) { PutBitContext pbc; int samplerate_index; - int channels = flv->audio_par->ch_layout.nb_channels - - (flv->audio_par->ch_layout.nb_channels == 8 ? 1 : 0); + int channels = par->ch_layout.nb_channels + - (par->ch_layout.nb_channels == 8 ? 1 : 0); uint8_t data[2]; for (samplerate_index = 0; samplerate_index < 16; samplerate_index++) - if (flv->audio_par->sample_rate + if (par->sample_rate == ff_mpeg4audio_sample_rates[samplerate_index]) break; init_put_bits(&pbc, data, sizeof(data)); - put_bits(&pbc, 5, flv->audio_par->profile + 1); //profile + put_bits(&pbc, 5, par->profile + 1); //profile put_bits(&pbc, 4, samplerate_index); //sample rate index put_bits(&pbc, 4, channels); put_bits(&pbc, 1, 0); //frame length - 1024 samples @@ -532,10 +539,26 @@ static void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par, i } avio_write(pb, par->extradata, par->extradata_size); } else { - avio_w8(pb, par->codec_tag | FLV_FRAME_KEY); // flags - avio_w8(pb, 0); // AVC sequence header - avio_wb24(pb, 0); // composition time - ff_isom_write_avcc(pb, par->extradata, par->extradata_size); + if (par->codec_id == AV_CODEC_ID_HEVC) { + avio_w8(pb, FLV_IS_EX_HEADER | PacketTypeSequenceStart | FLV_FRAME_KEY); // ExVideoTagHeader mode with PacketTypeSequenceStart + avio_write(pb, "hvc1", 4); + } else if (par->codec_id == AV_CODEC_ID_AV1 || par->codec_id == AV_CODEC_ID_VP9) { + avio_w8(pb, FLV_IS_EX_HEADER | PacketTypeSequenceStart | FLV_FRAME_KEY); + avio_write(pb, par->codec_id == AV_CODEC_ID_AV1 ? "av01" : "vp09", 4); + } else { + avio_w8(pb, par->codec_tag | FLV_FRAME_KEY); // flags + avio_w8(pb, 0); // AVC sequence header + avio_wb24(pb, 0); // composition time + } + + if (par->codec_id == AV_CODEC_ID_HEVC) + ff_isom_write_hvcc(pb, par->extradata, par->extradata_size, 0); + else if (par->codec_id == AV_CODEC_ID_AV1) + ff_isom_write_av1c(pb, par->extradata, par->extradata_size, 1); + else if (par->codec_id == AV_CODEC_ID_VP9) + ff_isom_write_vpcc(s, pb, par->extradata, par->extradata_size, par); + else + ff_isom_write_avcc(pb, par->extradata, par->extradata_size); } data_size = avio_tell(pb) - pos; avio_seek(pb, -data_size - 10, SEEK_CUR); @@ -606,9 +629,15 @@ static int flv_init(struct AVFormatContext *s) int i; FLVContext *flv = s->priv_data; + if (s->nb_streams > FLV_STREAM_TYPE_NB) { + av_log(s, AV_LOG_ERROR, "invalid number of streams %d\n", + s->nb_streams); + return AVERROR(EINVAL); + } + for (i = 0; i < s->nb_streams; i++) { AVCodecParameters *par = s->streams[i]->codecpar; - FLVStreamContext *sc; + switch (par->codec_type) { case AVMEDIA_TYPE_VIDEO: if (s->streams[i]->avg_frame_rate.den && @@ -672,12 +701,7 @@ static int flv_init(struct AVFormatContext *s) return AVERROR(EINVAL); } avpriv_set_pts_info(s->streams[i], 32, 1, 1000); /* 32 bit pts in ms */ - - sc = av_mallocz(sizeof(FLVStreamContext)); - if (!sc) - return AVERROR(ENOMEM); - s->streams[i]->priv_data = sc; - sc->last_ts = -1; + flv->last_ts[i] = -1; } flv->delay = AV_NOPTS_VALUE; @@ -780,10 +804,9 @@ static int flv_write_trailer(AVFormatContext *s) /* Add EOS tag */ for (i = 0; i < s->nb_streams; i++) { AVCodecParameters *par = s->streams[i]->codecpar; - FLVStreamContext *sc = s->streams[i]->priv_data; if (par->codec_type == AVMEDIA_TYPE_VIDEO && (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4)) - put_avc_eos_tag(pb, sc->last_ts); + put_eos_tag(pb, flv->last_ts[i], par->codec_id); } } @@ -818,10 +841,10 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) AVIOContext *pb = s->pb; AVCodecParameters *par = s->streams[pkt->stream_index]->codecpar; FLVContext *flv = s->priv_data; - FLVStreamContext *sc = s->streams[pkt->stream_index]->priv_data; unsigned ts; int size = pkt->size; uint8_t *data = NULL; + uint8_t frametype = pkt->flags & AV_PKT_FLAG_KEY ? FLV_FRAME_KEY : FLV_FRAME_INTER; int flags = -1, flags_size, ret = 0; int64_t cur_offset = avio_tell(pb); @@ -833,13 +856,19 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) if (par->codec_id == AV_CODEC_ID_VP6F || par->codec_id == AV_CODEC_ID_VP6A || par->codec_id == AV_CODEC_ID_VP6 || par->codec_id == AV_CODEC_ID_AAC) flags_size = 2; - else if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4) + else if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4 || + par->codec_id == AV_CODEC_ID_HEVC || par->codec_id == AV_CODEC_ID_AV1 || + par->codec_id == AV_CODEC_ID_VP9) flags_size = 5; else flags_size = 1; + if (par->codec_id == AV_CODEC_ID_HEVC && pkt->pts != pkt->dts) + flags_size += 3; + if (par->codec_id == AV_CODEC_ID_AAC || par->codec_id == AV_CODEC_ID_H264 - || par->codec_id == AV_CODEC_ID_MPEG4) { + || par->codec_id == AV_CODEC_ID_MPEG4 || par->codec_id == AV_CODEC_ID_HEVC + || par->codec_id == AV_CODEC_ID_AV1 || par->codec_id == AV_CODEC_ID_VP9) { size_t side_size; uint8_t *side = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, &side_size); if (side && side_size > 0 && (side_size != par->extradata_size || memcmp(side, par->extradata, side_size))) { @@ -859,7 +888,9 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) "Packets are not in the proper order with respect to DTS\n"); return AVERROR(EINVAL); } - if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4) { + if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4 || + par->codec_id == AV_CODEC_ID_HEVC || par->codec_id == AV_CODEC_ID_AV1 || + par->codec_id == AV_CODEC_ID_VP9) { if (pkt->pts == AV_NOPTS_VALUE) { av_log(s, AV_LOG_ERROR, "Packet is missing PTS\n"); return AVERROR(EINVAL); @@ -882,7 +913,7 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) flags = ff_codec_get_tag(flv_video_codec_ids, par->codec_id); - flags |= pkt->flags & AV_PKT_FLAG_KEY ? FLV_FRAME_KEY : FLV_FRAME_INTER; + flags |= frametype; break; case AVMEDIA_TYPE_AUDIO: flags = get_audio_flags(s, par); @@ -904,6 +935,10 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) if (par->extradata_size > 0 && *(uint8_t*)par->extradata != 1) if ((ret = ff_avc_parse_nal_units_buf(pkt->data, &data, &size)) < 0) return ret; + } else if (par->codec_id == AV_CODEC_ID_HEVC) { + if (par->extradata_size > 0 && *(uint8_t*)par->extradata != 1) + if ((ret = ff_hevc_annexb2mp4_buf(pkt->data, &data, &size, 0, NULL)) < 0) + return ret; } else if (par->codec_id == AV_CODEC_ID_AAC && pkt->size > 2 && (AV_RB16(pkt->data) & 0xfff0) == 0xfff0) { if (!s->streams[pkt->stream_index]->nb_frames) { @@ -916,13 +951,13 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) } /* check Speex packet duration */ - if (par->codec_id == AV_CODEC_ID_SPEEX && ts - sc->last_ts > 160) + if (par->codec_id == AV_CODEC_ID_SPEEX && ts - flv->last_ts[pkt->stream_index] > 160) av_log(s, AV_LOG_WARNING, "Warning: Speex stream has more than " "8 frames per packet. Adobe Flash " "Player cannot handle this!\n"); - if (sc->last_ts < ts) - sc->last_ts = ts; + if (flv->last_ts[pkt->stream_index] < ts) + flv->last_ts[pkt->stream_index] = ts; if (size + flags_size >= 1<<24) { av_log(s, AV_LOG_ERROR, "Too large packet with size %u >= %u\n", @@ -965,7 +1000,18 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) avio_wb32(pb, data_size + 11); } else { av_assert1(flags>=0); - avio_w8(pb,flags); + if (par->codec_id == AV_CODEC_ID_HEVC) { + int pkttype = (pkt->pts != pkt->dts) ? PacketTypeCodedFrames : PacketTypeCodedFramesX; + avio_w8(pb, FLV_IS_EX_HEADER | pkttype | frametype); // ExVideoTagHeader mode with PacketTypeCodedFrames(X) + avio_write(pb, "hvc1", 4); + if (pkttype == PacketTypeCodedFrames) + avio_wb24(pb, pkt->pts - pkt->dts); + } else if (par->codec_id == AV_CODEC_ID_AV1 || par->codec_id == AV_CODEC_ID_VP9) { + avio_w8(pb, FLV_IS_EX_HEADER | PacketTypeCodedFrames | frametype); + avio_write(pb, par->codec_id == AV_CODEC_ID_AV1 ? "av01" : "vp09", 4); + } else { + avio_w8(pb, flags); + } if (par->codec_id == AV_CODEC_ID_VP6) avio_w8(pb,0); if (par->codec_id == AV_CODEC_ID_VP6F || par->codec_id == AV_CODEC_ID_VP6A) { @@ -1020,13 +1066,15 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) static int flv_check_bitstream(AVFormatContext *s, AVStream *st, const AVPacket *pkt) { - int ret = 1; - if (st->codecpar->codec_id == AV_CODEC_ID_AAC) { if (pkt->size > 2 && (AV_RB16(pkt->data) & 0xfff0) == 0xfff0) - ret = ff_stream_add_bitstream_filter(st, "aac_adtstoasc", NULL); + return ff_stream_add_bitstream_filter(st, "aac_adtstoasc", NULL); } - return ret; + if (!st->codecpar->extradata_size && + (st->codecpar->codec_id == AV_CODEC_ID_H264 || + st->codecpar->codec_id == AV_CODEC_ID_MPEG4)) + return ff_stream_add_bitstream_filter(st, "extract_extradata", NULL); + return 1; } static void flv_deinit(AVFormatContext *s) @@ -1060,24 +1108,24 @@ static const AVClass flv_muxer_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVOutputFormat ff_flv_muxer = { - .name = "flv", - .long_name = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"), - .mime_type = "video/x-flv", - .extensions = "flv", +const FFOutputFormat ff_flv_muxer = { + .p.name = "flv", + .p.long_name = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"), + .p.mime_type = "video/x-flv", + .p.extensions = "flv", .priv_data_size = sizeof(FLVContext), - .audio_codec = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_ADPCM_SWF, - .video_codec = AV_CODEC_ID_FLV1, + .p.audio_codec = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_ADPCM_SWF, + .p.video_codec = AV_CODEC_ID_FLV1, .init = flv_init, .write_header = flv_write_header, .write_packet = flv_write_packet, .write_trailer = flv_write_trailer, .deinit = flv_deinit, .check_bitstream= flv_check_bitstream, - .codec_tag = (const AVCodecTag* const []) { + .p.codec_tag = (const AVCodecTag* const []) { flv_video_codec_ids, flv_audio_codec_ids, 0 }, - .flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS | + .p.flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT, - .priv_class = &flv_muxer_class, + .p.priv_class = &flv_muxer_class, }; diff --git a/libavformat/format.c b/libavformat/format.c index 4b1f3c2986e..c91f71057a5 100644 --- a/libavformat/format.c +++ b/libavformat/format.c @@ -50,6 +50,31 @@ int av_match_ext(const char *filename, const char *extensions) return 0; } +int ff_match_url_ext(const char *url, const char *extensions) +{ + const char *ext; + URLComponents uc; + int ret; + char scratchpad[128]; + + if (!url) + return 0; + + ret = ff_url_decompose(&uc, url, NULL); + if (ret < 0 || !URL_COMPONENT_HAVE(uc, scheme)) + return ret; + for (ext = uc.query; *ext != '.' && ext > uc.path; ext--) + ; + + if (*ext != '.') + return 0; + if (uc.query - ext > sizeof(scratchpad)) + return AVERROR(ENOMEM); //not enough memory in our scratchpad + av_strlcpy(scratchpad, ext + 1, uc.query - ext); + + return av_match_name(scratchpad, extensions); +} + const AVOutputFormat *av_guess_format(const char *short_name, const char *filename, const char *mime_type) { @@ -111,8 +136,6 @@ enum AVCodecID av_guess_codec(const AVOutputFormat *fmt, const char *short_name, return fmt->audio_codec; else if (type == AVMEDIA_TYPE_SUBTITLE) return fmt->subtitle_codec; - else if (type == AVMEDIA_TYPE_DATA) - return fmt->data_codec; else return AV_CODEC_ID_NONE; } diff --git a/libavformat/framecrcenc.c b/libavformat/framecrcenc.c index ab79903e318..ce306a6c498 100644 --- a/libavformat/framecrcenc.c +++ b/libavformat/framecrcenc.c @@ -30,6 +30,7 @@ #include "avformat.h" #include "internal.h" +#include "mux.h" static int framecrc_write_header(struct AVFormatContext *s) { @@ -69,13 +70,13 @@ static int framecrc_write_packet(struct AVFormatContext *s, AVPacket *pkt) return 0; } -const AVOutputFormat ff_framecrc_muxer = { - .name = "framecrc", - .long_name = NULL_IF_CONFIG_SMALL("framecrc testing"), - .audio_codec = AV_CODEC_ID_PCM_S16LE, - .video_codec = AV_CODEC_ID_RAWVIDEO, +const FFOutputFormat ff_framecrc_muxer = { + .p.name = "framecrc", + .p.long_name = NULL_IF_CONFIG_SMALL("framecrc testing"), + .p.audio_codec = AV_CODEC_ID_PCM_S16LE, + .p.video_codec = AV_CODEC_ID_RAWVIDEO, .write_header = framecrc_write_header, .write_packet = framecrc_write_packet, - .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | + .p.flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | AVFMT_TS_NEGATIVE, }; diff --git a/libavformat/gif.c b/libavformat/gif.c index b52ff4dd395..568867cc5d5 100644 --- a/libavformat/gif.c +++ b/libavformat/gif.c @@ -23,6 +23,7 @@ #include "avformat.h" #include "internal.h" +#include "mux.h" #include "libavutil/imgutils.h" #include "libavutil/log.h" #include "libavutil/opt.h" @@ -87,6 +88,8 @@ static int gif_get_delay(GIFContext *gif, AVPacket *prev, AVPacket *new) gif->duration = av_clip_uint16(new->pts - prev->pts); else if (!new && gif->last_delay >= 0) gif->duration = gif->last_delay; + else if (prev->duration) + gif->duration = prev->duration; return gif->duration; } @@ -202,17 +205,17 @@ static const AVClass gif_muxer_class = { .option = options, }; -const AVOutputFormat ff_gif_muxer = { - .name = "gif", - .long_name = NULL_IF_CONFIG_SMALL("CompuServe Graphics Interchange Format (GIF)"), - .mime_type = "image/gif", - .extensions = "gif", +const FFOutputFormat ff_gif_muxer = { + .p.name = "gif", + .p.long_name = NULL_IF_CONFIG_SMALL("CompuServe Graphics Interchange Format (GIF)"), + .p.mime_type = "image/gif", + .p.extensions = "gif", .priv_data_size = sizeof(GIFContext), - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_GIF, + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_GIF, .write_header = gif_write_header, .write_packet = gif_write_packet, .write_trailer = gif_write_trailer, - .priv_class = &gif_muxer_class, - .flags = AVFMT_VARIABLE_FPS, + .p.priv_class = &gif_muxer_class, + .p.flags = AVFMT_VARIABLE_FPS, }; diff --git a/libavformat/gifdec.c b/libavformat/gifdec.c index 1977f46e3aa..774358e1fae 100644 --- a/libavformat/gifdec.c +++ b/libavformat/gifdec.c @@ -28,9 +28,12 @@ #include "libavutil/bprint.h" #include "libavutil/intreadwrite.h" #include "libavutil/opt.h" +#include "avio_internal.h" #include "internal.h" #include "libavcodec/gif.h" +#define GIF_PACKET_SIZE 1024 + typedef struct GIFDemuxContext { const AVClass *class; /** @@ -53,9 +56,6 @@ typedef struct GIFDemuxContext { int total_iter; int iter_count; int ignore_loop; - - int nb_frames; - int last_duration; } GIFDemuxContext; /** @@ -84,8 +84,8 @@ static int gif_probe(const AVProbeData *p) static int resync(AVIOContext *pb) { - int i; - for (i = 0; i < 6; i++) { + ffio_ensure_seekback(pb, 13); + for (int i = 0; i < 6; i++) { int b = avio_r8(pb); if (b != gif87a_sig[i] && b != gif89a_sig[i]) i = -(b != 'G'); @@ -113,11 +113,12 @@ static int gif_read_header(AVFormatContext *s) AVIOContext *pb = s->pb; AVStream *st; int type, width, height, ret, n, flags; - int64_t nb_frames = 0, duration = 0; + int64_t nb_frames = 0, duration = 0, pos; if ((ret = resync(pb)) < 0) return ret; + pos = avio_tell(pb); gdc->delay = gdc->default_delay; width = avio_rl16(pb); height = avio_rl16(pb); @@ -132,6 +133,9 @@ static int gif_read_header(AVFormatContext *s) if (!st) return AVERROR(ENOMEM); + if (!(pb->seekable & AVIO_SEEKABLE_NORMAL)) + goto skip; + if (flags & 0x80) avio_skip(pb, 3 * (1 << ((flags & 0x07) + 1))); @@ -158,15 +162,37 @@ static int gif_read_header(AVFormatContext *s) avio_skip(pb, 1); delay = avio_rl16(pb); - if (delay < gdc->min_delay) - delay = gdc->default_delay; - delay = FFMIN(delay, gdc->max_delay); + delay = delay ? delay : gdc->default_delay; duration += delay; avio_skip(pb, 1); } else { avio_skip(pb, block_size); } gif_skip_subblocks(pb); + } else if (subtype == GIF_APP_EXT_LABEL) { + uint8_t data[256]; + int sb_size; + + sb_size = avio_r8(pb); + ret = avio_read(pb, data, sb_size); + if (ret < 0 || !sb_size) + break; + + if (sb_size == strlen(NETSCAPE_EXT_STR)) { + sb_size = avio_r8(pb); + ret = avio_read(pb, data, sb_size); + if (ret < 0 || !sb_size) + break; + + if (sb_size == 3 && data[0] == 1) { + gdc->total_iter = AV_RL16(data+1); + av_log(s, AV_LOG_DEBUG, "Loop count is %d\n", gdc->total_iter); + + if (gdc->total_iter == 0) + gdc->total_iter = -1; + } + } + gif_skip_subblocks(pb); } else { gif_skip_subblocks(pb); } @@ -183,203 +209,57 @@ static int gif_read_header(AVFormatContext *s) } } +skip: + /* jump to start because gif decoder needs header data too */ + if (avio_seek(pb, pos - 6, SEEK_SET) != pos - 6) + return AVERROR(EIO); + /* GIF format operates with time in "hundredths of second", * therefore timebase is 1/100 */ avpriv_set_pts_info(st, 64, 1, 100); + ffstream(st)->need_parsing = AVSTREAM_PARSE_FULL_RAW; st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; st->codecpar->codec_id = AV_CODEC_ID_GIF; st->codecpar->width = width; st->codecpar->height = height; + if (nb_frames > 1) { + av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den, + 100, duration / nb_frames, INT_MAX); + } else if (duration) { + st->avg_frame_rate = (AVRational) { 100, duration }; + } st->start_time = 0; st->duration = duration; st->nb_frames = nb_frames; - if (n) { - st->codecpar->sample_aspect_ratio.num = n + 15; - st->codecpar->sample_aspect_ratio.den = 64; - } - - /* jump to start because gif decoder needs header data too */ - if (avio_seek(pb, 0, SEEK_SET) != 0) - return AVERROR(EIO); + if (n) + st->codecpar->sample_aspect_ratio = av_make_q(n + 15, 64); return 0; } -static int gif_read_ext(AVFormatContext *s) +static int gif_read_packet(AVFormatContext *s, AVPacket *pkt) { GIFDemuxContext *gdc = s->priv_data; AVIOContext *pb = s->pb; - int sb_size, ext_label = avio_r8(pb); int ret; - if (ext_label == GIF_GCE_EXT_LABEL) { - if ((sb_size = avio_r8(pb)) < 4) { - av_log(s, AV_LOG_FATAL, "Graphic Control Extension block's size less than 4.\n"); - return AVERROR_INVALIDDATA; - } - - /* skip packed fields */ - if ((ret = avio_skip(pb, 1)) < 0) - return ret; - - gdc->delay = avio_rl16(pb); - - if (gdc->delay < gdc->min_delay) - gdc->delay = gdc->default_delay; - gdc->delay = FFMIN(gdc->delay, gdc->max_delay); - - /* skip the rest of the Graphic Control Extension block */ - if ((ret = avio_skip(pb, sb_size - 3)) < 0 ) - return ret; - } else if (ext_label == GIF_APP_EXT_LABEL) { - uint8_t data[256]; - - sb_size = avio_r8(pb); - ret = avio_read(pb, data, sb_size); - if (ret < 0 || !sb_size) - return ret; - - if (sb_size == strlen(NETSCAPE_EXT_STR)) { - sb_size = avio_r8(pb); - ret = avio_read(pb, data, sb_size); - if (ret < 0 || !sb_size) - return ret; - - if (sb_size == 3 && data[0] == 1) { - gdc->total_iter = AV_RL16(data+1); - av_log(s, AV_LOG_DEBUG, "Loop count is %d\n", gdc->total_iter); - - if (gdc->total_iter == 0) - gdc->total_iter = -1; - } - } + if ((pb->seekable & AVIO_SEEKABLE_NORMAL) && + !gdc->ignore_loop && avio_feof(pb) && + (gdc->total_iter < 0 || (++gdc->iter_count < gdc->total_iter))) { + avio_seek(pb, 0, SEEK_SET); } - - if ((ret = gif_skip_subblocks(pb)) < 0) + if ((ret = av_new_packet(pkt, GIF_PACKET_SIZE)) < 0) return ret; - return 0; -} - -static int gif_read_packet(AVFormatContext *s, AVPacket *pkt) -{ - GIFDemuxContext *gdc = s->priv_data; - AVIOContext *pb = s->pb; - int packed_fields, block_label, ct_size, - keyframe, frame_parsed = 0, ret; - int64_t frame_start = avio_tell(pb), frame_end; - unsigned char buf[6]; - - if ((ret = avio_read(pb, buf, 6)) == 6) { - keyframe = memcmp(buf, gif87a_sig, 6) == 0 || - memcmp(buf, gif89a_sig, 6) == 0; - } else if (ret < 0) { + pkt->pos = avio_tell(pb); + pkt->stream_index = 0; + ret = avio_read_partial(pb, pkt->data, GIF_PACKET_SIZE); + if (ret < 0) { + av_packet_unref(pkt); return ret; - } else { - keyframe = 0; - } - - if (keyframe) { -parse_keyframe: - /* skip 2 bytes of width and 2 of height */ - if ((ret = avio_skip(pb, 4)) < 0) - return ret; - - packed_fields = avio_r8(pb); - - /* skip 1 byte of Background Color Index and 1 byte of Pixel Aspect Ratio */ - if ((ret = avio_skip(pb, 2)) < 0) - return ret; - - /* global color table presence */ - if (packed_fields & 0x80) { - ct_size = 3 * (1 << ((packed_fields & 0x07) + 1)); - - if ((ret = avio_skip(pb, ct_size)) < 0) - return ret; - } - } else { - avio_seek(pb, -ret, SEEK_CUR); - ret = AVERROR_EOF; - } - - while (GIF_TRAILER != (block_label = avio_r8(pb)) && !avio_feof(pb)) { - if (block_label == GIF_EXTENSION_INTRODUCER) { - if ((ret = gif_read_ext (s)) < 0 ) - goto resync; - } else if (block_label == GIF_IMAGE_SEPARATOR) { - /* skip to last byte of Image Descriptor header */ - if ((ret = avio_skip(pb, 8)) < 0) - return ret; - - packed_fields = avio_r8(pb); - - /* local color table presence */ - if (packed_fields & 0x80) { - ct_size = 3 * (1 << ((packed_fields & 0x07) + 1)); - - if ((ret = avio_skip(pb, ct_size)) < 0) - return ret; - } - - /* read LZW Minimum Code Size */ - if (avio_r8(pb) < 1) { - av_log(s, AV_LOG_ERROR, "lzw minimum code size must be >= 1\n"); - goto resync; - } - - if ((ret = gif_skip_subblocks(pb)) < 0) - goto resync; - - frame_end = avio_tell(pb); - - if (avio_seek(pb, frame_start, SEEK_SET) != frame_start) - return AVERROR(EIO); - - ret = av_get_packet(pb, pkt, frame_end - frame_start); - if (ret < 0) - return ret; - - if (keyframe) - pkt->flags |= AV_PKT_FLAG_KEY; - - pkt->stream_index = 0; - pkt->duration = gdc->delay; - - gdc->nb_frames ++; - gdc->last_duration = pkt->duration; - - /* Graphic Control Extension's scope is single frame. - * Remove its influence. */ - gdc->delay = gdc->default_delay; - frame_parsed = 1; - - break; - } else { - av_log(s, AV_LOG_ERROR, "invalid block label\n"); -resync: - if (!keyframe) - avio_seek(pb, frame_start, SEEK_SET); - if ((ret = resync(pb)) < 0) - return ret; - frame_start = avio_tell(pb) - 6; - keyframe = 1; - goto parse_keyframe; - } } - - if ((ret >= 0 && !frame_parsed) || ret == AVERROR_EOF) { - if (gdc->nb_frames == 1) { - s->streams[0]->r_frame_rate = (AVRational) {100, gdc->last_duration}; - } - /* This might happen when there is no image block - * between extension blocks and GIF_TRAILER or EOF */ - if (!gdc->ignore_loop && (block_label == GIF_TRAILER || avio_feof(pb)) - && (gdc->total_iter < 0 || ++gdc->iter_count < gdc->total_iter)) - return avio_seek(pb, 0, SEEK_SET); - return AVERROR_EOF; - } else - return ret; + av_shrink_packet(pkt, ret); + return ret; } static const AVOption options[] = { @@ -405,6 +285,7 @@ const AVInputFormat ff_gif_demuxer = { .read_probe = gif_probe, .read_header = gif_read_header, .read_packet = gif_read_packet, - .flags = AVFMT_GENERIC_INDEX, + .flags = AVFMT_GENERIC_INDEX | AVFMT_NOTIMESTAMPS, + .extensions = "gif", .priv_class = &demuxer_class, }; diff --git a/libavformat/gxfenc.c b/libavformat/gxfenc.c index 0f971c039a4..74959247229 100644 --- a/libavformat/gxfenc.c +++ b/libavformat/gxfenc.c @@ -1009,13 +1009,13 @@ static int gxf_interleave_packet(AVFormatContext *s, AVPacket *pkt, return ff_interleave_packet_per_dts(s, pkt, flush, 0); } -const AVOutputFormat ff_gxf_muxer = { - .name = "gxf", - .long_name = NULL_IF_CONFIG_SMALL("GXF (General eXchange Format)"), - .extensions = "gxf", +const FFOutputFormat ff_gxf_muxer = { + .p.name = "gxf", + .p.long_name = NULL_IF_CONFIG_SMALL("GXF (General eXchange Format)"), + .p.extensions = "gxf", .priv_data_size = sizeof(GXFContext), - .audio_codec = AV_CODEC_ID_PCM_S16LE, - .video_codec = AV_CODEC_ID_MPEG2VIDEO, + .p.audio_codec = AV_CODEC_ID_PCM_S16LE, + .p.video_codec = AV_CODEC_ID_MPEG2VIDEO, .write_header = gxf_write_header, .write_packet = gxf_write_packet, .write_trailer = gxf_write_trailer, diff --git a/libavformat/hashenc.c b/libavformat/hashenc.c index 1aaba0b306e..17ee2f7e9fb 100644 --- a/libavformat/hashenc.c +++ b/libavformat/hashenc.c @@ -27,6 +27,7 @@ #include "libavutil/opt.h" #include "avformat.h" #include "internal.h" +#include "mux.h" struct HashContext { const AVClass *avclass; @@ -172,19 +173,19 @@ static void hash_free(struct AVFormatContext *s) } #if CONFIG_HASH_MUXER -const AVOutputFormat ff_hash_muxer = { - .name = "hash", - .long_name = NULL_IF_CONFIG_SMALL("Hash testing"), +const FFOutputFormat ff_hash_muxer = { + .p.name = "hash", + .p.long_name = NULL_IF_CONFIG_SMALL("Hash testing"), .priv_data_size = sizeof(struct HashContext), - .audio_codec = AV_CODEC_ID_PCM_S16LE, - .video_codec = AV_CODEC_ID_RAWVIDEO, + .p.audio_codec = AV_CODEC_ID_PCM_S16LE, + .p.video_codec = AV_CODEC_ID_RAWVIDEO, .init = hash_init, .write_packet = hash_write_packet, .write_trailer = hash_write_trailer, .deinit = hash_free, - .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | + .p.flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | AVFMT_TS_NEGATIVE, - .priv_class = &hash_streamhashenc_class, + .p.priv_class = &hash_streamhashenc_class, }; #endif @@ -196,36 +197,36 @@ static const AVClass md5enc_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVOutputFormat ff_md5_muxer = { - .name = "md5", - .long_name = NULL_IF_CONFIG_SMALL("MD5 testing"), +const FFOutputFormat ff_md5_muxer = { + .p.name = "md5", + .p.long_name = NULL_IF_CONFIG_SMALL("MD5 testing"), .priv_data_size = sizeof(struct HashContext), - .audio_codec = AV_CODEC_ID_PCM_S16LE, - .video_codec = AV_CODEC_ID_RAWVIDEO, + .p.audio_codec = AV_CODEC_ID_PCM_S16LE, + .p.video_codec = AV_CODEC_ID_RAWVIDEO, .init = hash_init, .write_packet = hash_write_packet, .write_trailer = hash_write_trailer, .deinit = hash_free, - .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | + .p.flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | AVFMT_TS_NEGATIVE, - .priv_class = &md5enc_class, + .p.priv_class = &md5enc_class, }; #endif #if CONFIG_STREAMHASH_MUXER -const AVOutputFormat ff_streamhash_muxer = { - .name = "streamhash", - .long_name = NULL_IF_CONFIG_SMALL("Per-stream hash testing"), +const FFOutputFormat ff_streamhash_muxer = { + .p.name = "streamhash", + .p.long_name = NULL_IF_CONFIG_SMALL("Per-stream hash testing"), .priv_data_size = sizeof(struct HashContext), - .audio_codec = AV_CODEC_ID_PCM_S16LE, - .video_codec = AV_CODEC_ID_RAWVIDEO, + .p.audio_codec = AV_CODEC_ID_PCM_S16LE, + .p.video_codec = AV_CODEC_ID_RAWVIDEO, .init = streamhash_init, .write_packet = hash_write_packet, .write_trailer = hash_write_trailer, .deinit = hash_free, - .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | + .p.flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | AVFMT_TS_NEGATIVE, - .priv_class = &hash_streamhashenc_class, + .p.priv_class = &hash_streamhashenc_class, }; #endif @@ -324,19 +325,19 @@ static const AVClass framehash_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVOutputFormat ff_framehash_muxer = { - .name = "framehash", - .long_name = NULL_IF_CONFIG_SMALL("Per-frame hash testing"), +const FFOutputFormat ff_framehash_muxer = { + .p.name = "framehash", + .p.long_name = NULL_IF_CONFIG_SMALL("Per-frame hash testing"), .priv_data_size = sizeof(struct HashContext), - .audio_codec = AV_CODEC_ID_PCM_S16LE, - .video_codec = AV_CODEC_ID_RAWVIDEO, + .p.audio_codec = AV_CODEC_ID_PCM_S16LE, + .p.video_codec = AV_CODEC_ID_RAWVIDEO, .init = framehash_init, .write_header = framehash_write_header, .write_packet = framehash_write_packet, .deinit = hash_free, - .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | + .p.flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | AVFMT_TS_NEGATIVE, - .priv_class = &framehash_class, + .p.priv_class = &framehash_class, }; #endif @@ -348,18 +349,18 @@ static const AVClass framemd5_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVOutputFormat ff_framemd5_muxer = { - .name = "framemd5", - .long_name = NULL_IF_CONFIG_SMALL("Per-frame MD5 testing"), +const FFOutputFormat ff_framemd5_muxer = { + .p.name = "framemd5", + .p.long_name = NULL_IF_CONFIG_SMALL("Per-frame MD5 testing"), .priv_data_size = sizeof(struct HashContext), - .audio_codec = AV_CODEC_ID_PCM_S16LE, - .video_codec = AV_CODEC_ID_RAWVIDEO, + .p.audio_codec = AV_CODEC_ID_PCM_S16LE, + .p.video_codec = AV_CODEC_ID_RAWVIDEO, .init = framehash_init, .write_header = framehash_write_header, .write_packet = framehash_write_packet, .deinit = hash_free, - .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | + .p.flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | AVFMT_TS_NEGATIVE, - .priv_class = &framemd5_class, + .p.priv_class = &framemd5_class, }; #endif diff --git a/libavformat/hdsenc.c b/libavformat/hdsenc.c index a8f340ac460..080a873ee8a 100644 --- a/libavformat/hdsenc.c +++ b/libavformat/hdsenc.c @@ -564,16 +564,16 @@ static const AVClass hds_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVOutputFormat ff_hds_muxer = { - .name = "hds", - .long_name = NULL_IF_CONFIG_SMALL("HDS Muxer"), +const FFOutputFormat ff_hds_muxer = { + .p.name = "hds", + .p.long_name = NULL_IF_CONFIG_SMALL("HDS Muxer"), + .p.audio_codec = AV_CODEC_ID_AAC, + .p.video_codec = AV_CODEC_ID_H264, + .p.flags = AVFMT_GLOBALHEADER | AVFMT_NOFILE, + .p.priv_class = &hds_class, .priv_data_size = sizeof(HDSContext), - .audio_codec = AV_CODEC_ID_AAC, - .video_codec = AV_CODEC_ID_H264, - .flags = AVFMT_GLOBALHEADER | AVFMT_NOFILE, .write_header = hds_write_header, .write_packet = hds_write_packet, .write_trailer = hds_write_trailer, .deinit = hds_free, - .priv_class = &hds_class, }; diff --git a/libavformat/hls.c b/libavformat/hls.c index 926d053939c..2a2fe28a540 100644 --- a/libavformat/hls.c +++ b/libavformat/hls.c @@ -225,6 +225,7 @@ typedef struct HLSContext { int http_persistent; int http_multiple; int http_seekable; + int seg_max_retry; AVIOContext *playlist_pb; HLSCryptoContext crypto_ctx; } HLSContext; @@ -1472,6 +1473,7 @@ static int read_data(void *opaque, uint8_t *buf, int buf_size) int ret; int just_opened = 0; int reload_count = 0; + int segment_retries = 0; struct segment *seg; restart: @@ -1563,9 +1565,18 @@ static int read_data(void *opaque, uint8_t *buf, int buf_size) av_log(v->parent, AV_LOG_WARNING, "Failed to open segment %"PRId64" of playlist %d\n", v->cur_seq_no, v->index); - v->cur_seq_no += 1; + if (segment_retries >= c->seg_max_retry) { + av_log(v->parent, AV_LOG_WARNING, "Segment %"PRId64" of playlist %d failed too many times, skipping\n", + v->cur_seq_no, + v->index); + v->cur_seq_no++; + segment_retries = 0; + } else { + segment_retries++; + } goto reload; } + segment_retries = 0; just_opened = 1; } @@ -1838,6 +1849,8 @@ static int set_stream_info_from_input_stream(AVStream *st, struct playlist *pls, // copy disposition st->disposition = ist->disposition; + av_dict_copy(&st->metadata, ist->metadata, 0); + // copy side data for (int i = 0; i < ist->nb_side_data; i++) { const AVPacketSideData *sd_src = &ist->side_data[i]; @@ -2521,8 +2534,30 @@ static int hls_probe(const AVProbeData *p) if (strstr(p->buf, "#EXT-X-STREAM-INF:") || strstr(p->buf, "#EXT-X-TARGETDURATION:") || - strstr(p->buf, "#EXT-X-MEDIA-SEQUENCE:")) + strstr(p->buf, "#EXT-X-MEDIA-SEQUENCE:")) { + + int mime_ok = p->mime_type && !( + av_strcasecmp(p->mime_type, "application/vnd.apple.mpegurl") && + av_strcasecmp(p->mime_type, "audio/mpegurl") + ); + + int mime_x = p->mime_type && !( + av_strcasecmp(p->mime_type, "audio/x-mpegurl") && + av_strcasecmp(p->mime_type, "application/x-mpegurl") + ); + + if (!mime_ok && + !mime_x && + !av_match_ext (p->filename, "m3u8,m3u") && + ff_match_url_ext(p->filename, "m3u8,m3u") <= 0) { + av_log(NULL, AV_LOG_ERROR, "Not detecting m3u8/hls with non standard extension and non standard mime type\n"); + return 0; + } + if (mime_x) + av_log(NULL, AV_LOG_WARNING, "mime type is not rfc8216 compliant\n"); + return AVPROBE_SCORE_MAX; + } return 0; } @@ -2549,6 +2584,8 @@ static const AVOption hls_options[] = { OFFSET(http_seekable), AV_OPT_TYPE_BOOL, { .i64 = -1}, -1, 1, FLAGS}, {"seg_format_options", "Set options for segment demuxer", OFFSET(seg_format_opts), AV_OPT_TYPE_DICT, {.str = NULL}, 0, 0, FLAGS}, + {"seg_max_retry", "Maximum number of times to reload a segment on error.", + OFFSET(seg_max_retry), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, FLAGS}, {NULL} }; diff --git a/libavformat/hls_sample_encryption.c b/libavformat/hls_sample_encryption.c index 089662905b4..d5b4c11b66d 100644 --- a/libavformat/hls_sample_encryption.c +++ b/libavformat/hls_sample_encryption.c @@ -105,8 +105,7 @@ int ff_hls_senc_parse_audio_setup_info(AVStream *st, HLSAudioSetupInfo *info) ret = avpriv_ac3_parse_header(&ac3hdr, info->setup_data, info->setup_data_length); if (ret < 0) { - if (ret != AVERROR(ENOMEM)) - av_free(ac3hdr); + av_free(ac3hdr); return ret; } @@ -317,8 +316,7 @@ static int get_next_ac3_eac3_sync_frame(CodecParserContext *ctx, AudioFrame *fra ret = avpriv_ac3_parse_header(&hdr, frame->data, ctx->buf_end - frame->data); if (ret < 0) { - if (ret != AVERROR(ENOMEM)) - av_free(hdr); + av_free(hdr); return ret; } diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 39df9becc7a..27d97f5f729 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -27,12 +27,6 @@ #include #endif -#if CONFIG_GCRYPT -#include -#elif CONFIG_OPENSSL -#include -#endif - #include "libavutil/avassert.h" #include "libavutil/mathematics.h" #include "libavutil/avstring.h" @@ -40,6 +34,7 @@ #include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "libavutil/log.h" +#include "libavutil/random_seed.h" #include "libavutil/time.h" #include "libavutil/time_internal.h" @@ -355,9 +350,19 @@ static void write_codec_attr(AVStream *st, VariantStream *vs) if (st->codecpar->codec_id == AV_CODEC_ID_H264) { uint8_t *data = st->codecpar->extradata; - if (data && (data[0] | data[1] | data[2]) == 0 && data[3] == 1 && (data[4] & 0x1F) == 7) { + if (data) { + const uint8_t *p; + + if (AV_RB32(data) == 0x01 && (data[4] & 0x1F) == 7) + p = &data[5]; + else if (AV_RB24(data) == 0x01 && (data[3] & 0x1F) == 7) + p = &data[4]; + else if (data[0] == 0x01) /* avcC */ + p = &data[1]; + else + goto fail; snprintf(attr, sizeof(attr), - "avc1.%02x%02x%02x", data[5], data[6], data[7]); + "avc1.%02x%02x%02x", p[0], p[1], p[2]); } else { goto fail; } @@ -700,20 +705,6 @@ static int hls_delete_old_segments(AVFormatContext *s, HLSContext *hls, return ret; } -static int randomize(uint8_t *buf, int len) -{ -#if CONFIG_GCRYPT - gcry_randomize(buf, len, GCRY_VERY_STRONG_RANDOM); - return 0; -#elif CONFIG_OPENSSL - if (RAND_bytes(buf, len)) - return 0; -#else - return AVERROR(ENOSYS); -#endif - return AVERROR(EINVAL); -} - static int do_encrypt(AVFormatContext *s, VariantStream *vs) { HLSContext *hls = s->priv_data; @@ -765,7 +756,7 @@ static int do_encrypt(AVFormatContext *s, VariantStream *vs) if (!*hls->key_string) { AVDictionary *options = NULL; if (!hls->key) { - if ((ret = randomize(key, sizeof(key))) < 0) { + if ((ret = av_random_bytes(key, sizeof(key))) < 0) { av_log(s, AV_LOG_ERROR, "Cannot generate a strong random key\n"); return ret; } @@ -869,7 +860,11 @@ static int hls_mux_init(AVFormatContext *s, VariantStream *vs) oc->max_delay = s->max_delay; oc->opaque = s->opaque; oc->io_open = s->io_open; +#if FF_API_AVFORMAT_IO_CLOSE +FF_DISABLE_DEPRECATION_WARNINGS oc->io_close = s->io_close; +FF_ENABLE_DEPRECATION_WARNINGS +#endif oc->io_close2 = s->io_close2; oc->strict_std_compliance = s->strict_std_compliance; av_dict_copy(&oc->metadata, s->metadata, 0); @@ -2943,7 +2938,7 @@ static int hls_init(AVFormatContext *s) av_log(hls, AV_LOG_DEBUG, "start_number evaluated to %"PRId64"\n", hls->start_sequence); } - hls->recording_time = hls->init_time ? hls->init_time : hls->time; + hls->recording_time = hls->init_time && hls->max_nb_segments > 0 ? hls->init_time : hls->time; if (hls->flags & HLS_SPLIT_BY_TIME && hls->flags & HLS_INDEPENDENT_SEGMENTS) { // Independent segments cannot be guaranteed when splitting by time @@ -3116,9 +3111,6 @@ static const AVOption options[] = { {"hls_init_time", "set segment length at init list", OFFSET(init_time), AV_OPT_TYPE_DURATION, {.i64 = 0}, 0, INT64_MAX, E}, {"hls_list_size", "set maximum number of playlist entries", OFFSET(max_nb_segments), AV_OPT_TYPE_INT, {.i64 = 5}, 0, INT_MAX, E}, {"hls_delete_threshold", "set number of unreferenced segments to keep before deleting", OFFSET(hls_delete_threshold), AV_OPT_TYPE_INT, {.i64 = 1}, 1, INT_MAX, E}, -#if FF_HLS_TS_OPTIONS - {"hls_ts_options","set hls mpegts list of options for the container format used for hls (deprecated, use hls_segment_options instead of it.)", OFFSET(format_options), AV_OPT_TYPE_DICT, {.str = NULL}, 0, 0, E | AV_OPT_FLAG_DEPRECATED}, -#endif {"hls_vtt_options","set hls vtt list of options for the container format used for hls", OFFSET(vtt_format_options_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, {"hls_allow_cache", "explicitly set whether the client MAY (1) or MUST NOT (0) cache media segments", OFFSET(allowcache), AV_OPT_TYPE_INT, {.i64 = -1}, INT_MIN, INT_MAX, E}, {"hls_base_url", "url to prepend to each playlist entry", OFFSET(baseurl), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, @@ -3183,19 +3175,19 @@ static const AVClass hls_class = { }; -const AVOutputFormat ff_hls_muxer = { - .name = "hls", - .long_name = NULL_IF_CONFIG_SMALL("Apple HTTP Live Streaming"), - .extensions = "m3u8", +const FFOutputFormat ff_hls_muxer = { + .p.name = "hls", + .p.long_name = NULL_IF_CONFIG_SMALL("Apple HTTP Live Streaming"), + .p.extensions = "m3u8", + .p.audio_codec = AV_CODEC_ID_AAC, + .p.video_codec = AV_CODEC_ID_H264, + .p.subtitle_codec = AV_CODEC_ID_WEBVTT, + .p.flags = AVFMT_NOFILE | AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_NODIMENSIONS, + .p.priv_class = &hls_class, .priv_data_size = sizeof(HLSContext), - .audio_codec = AV_CODEC_ID_AAC, - .video_codec = AV_CODEC_ID_H264, - .subtitle_codec = AV_CODEC_ID_WEBVTT, - .flags = AVFMT_NOFILE | AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_NODIMENSIONS, .init = hls_init, .write_header = hls_write_header, .write_packet = hls_write_packet, .write_trailer = hls_write_trailer, .deinit = hls_deinit, - .priv_class = &hls_class, }; diff --git a/libavformat/http.c b/libavformat/http.c index 7bce8215354..fd931c2d8e7 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -1205,7 +1205,7 @@ static int process_line(URLContext *h, char *line, int line_count) } } else if (!av_strcasecmp(tag, "Content-Type")) { av_free(s->mime_type); - s->mime_type = av_strdup(p); + s->mime_type = av_get_token((const char **)&p, ";"); } else if (!av_strcasecmp(tag, "Set-Cookie")) { if (parse_cookie(s, p, &s->cookie_dict)) av_log(h, AV_LOG_WARNING, "Unable to parse '%s'\n", p); @@ -1293,9 +1293,9 @@ static int get_cookies(HTTPContext *s, char **cookies, const char *path, goto skip_cookie; } - // ensure this cookie matches the path + // if a cookie path is provided, ensure the request path is within that path e = av_dict_get(cookie_params, "path", NULL, 0); - if (!e || av_strncasecmp(path, e->value, strlen(e->value))) + if (e && av_strncasecmp(path, e->value, strlen(e->value))) goto skip_cookie; // cookie parameters match, so copy the value diff --git a/libavformat/icoenc.c b/libavformat/icoenc.c index d684f677072..caa3ba4965a 100644 --- a/libavformat/icoenc.c +++ b/libavformat/icoenc.c @@ -31,6 +31,7 @@ #include "avformat.h" #include "avio_internal.h" +#include "mux.h" typedef struct { int offset; @@ -193,17 +194,17 @@ static void ico_deinit(AVFormatContext *s) av_freep(&ico->images); } -const AVOutputFormat ff_ico_muxer = { - .name = "ico", - .long_name = NULL_IF_CONFIG_SMALL("Microsoft Windows ICO"), +const FFOutputFormat ff_ico_muxer = { + .p.name = "ico", + .p.long_name = NULL_IF_CONFIG_SMALL("Microsoft Windows ICO"), .priv_data_size = sizeof(IcoMuxContext), - .mime_type = "image/vnd.microsoft.icon", - .extensions = "ico", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_BMP, + .p.mime_type = "image/vnd.microsoft.icon", + .p.extensions = "ico", + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_BMP, .write_header = ico_write_header, .write_packet = ico_write_packet, .write_trailer = ico_write_trailer, .deinit = ico_deinit, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; diff --git a/libavformat/id3v2.c b/libavformat/id3v2.c index cb318640455..69193933e0b 100644 --- a/libavformat/id3v2.c +++ b/libavformat/id3v2.c @@ -246,7 +246,7 @@ static int decode_str(AVFormatContext *s, AVIOContext *pb, int encoding, int ret; uint8_t tmp; uint32_t ch = 1; - int left = *maxread; + int left = *maxread, dynsize; unsigned int (*get)(AVIOContext*) = avio_rb16; AVIOContext *dynbuf; @@ -308,7 +308,11 @@ static int decode_str(AVFormatContext *s, AVIOContext *pb, int encoding, if (ch) avio_w8(dynbuf, 0); - avio_close_dyn_buf(dynbuf, dst); + dynsize = avio_close_dyn_buf(dynbuf, dst); + if (dynsize <= 0) { + av_freep(dst); + return AVERROR(ENOMEM); + } *maxread = left; return 0; diff --git a/libavformat/idroqdec.c b/libavformat/idroqdec.c index c9fc972780a..01ea2bb77ba 100644 --- a/libavformat/idroqdec.c +++ b/libavformat/idroqdec.c @@ -107,7 +107,7 @@ static int roq_read_packet(AVFormatContext *s, while (!packet_read) { if (avio_feof(s->pb)) - return AVERROR(EIO); + return AVERROR_EOF; /* get the next chunk preamble */ if ((ret = avio_read(pb, preamble, RoQ_CHUNK_PREAMBLE_SIZE)) != diff --git a/libavformat/idroqenc.c b/libavformat/idroqenc.c index 57dd7f024ef..9baf9ad1b1b 100644 --- a/libavformat/idroqenc.c +++ b/libavformat/idroqenc.c @@ -20,6 +20,7 @@ */ #include "avformat.h" +#include "mux.h" #include "rawenc.h" @@ -59,12 +60,12 @@ static int roq_write_header(struct AVFormatContext *s) return 0; } -const AVOutputFormat ff_roq_muxer = { - .name = "roq", - .long_name = NULL_IF_CONFIG_SMALL("raw id RoQ"), - .extensions = "roq", - .audio_codec = AV_CODEC_ID_ROQ_DPCM, - .video_codec = AV_CODEC_ID_ROQ, +const FFOutputFormat ff_roq_muxer = { + .p.name = "roq", + .p.long_name = NULL_IF_CONFIG_SMALL("raw id RoQ"), + .p.extensions = "roq", + .p.audio_codec = AV_CODEC_ID_ROQ_DPCM, + .p.video_codec = AV_CODEC_ID_ROQ, .write_header = roq_write_header, .write_packet = ff_raw_write_packet, }; diff --git a/libavformat/ilbc.c b/libavformat/ilbc.c index 26336ef2ac3..6b5bb33b620 100644 --- a/libavformat/ilbc.c +++ b/libavformat/ilbc.c @@ -23,6 +23,7 @@ #include "avformat.h" #include "internal.h" +#include "mux.h" #include "rawenc.h" static const char mode20_header[] = "#!iLBC20\n"; @@ -120,14 +121,14 @@ const AVInputFormat ff_ilbc_demuxer = { }; #if CONFIG_ILBC_MUXER -const AVOutputFormat ff_ilbc_muxer = { - .name = "ilbc", - .long_name = NULL_IF_CONFIG_SMALL("iLBC storage"), - .mime_type = "audio/iLBC", - .extensions = "lbc", - .audio_codec = AV_CODEC_ID_ILBC, +const FFOutputFormat ff_ilbc_muxer = { + .p.name = "ilbc", + .p.long_name = NULL_IF_CONFIG_SMALL("iLBC storage"), + .p.mime_type = "audio/iLBC", + .p.extensions = "lbc", + .p.audio_codec = AV_CODEC_ID_ILBC, + .p.flags = AVFMT_NOTIMESTAMPS, .write_header = ilbc_write_header, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, }; #endif diff --git a/libavformat/imf.h b/libavformat/imf.h index 70ed007312c..ef124bf4124 100644 --- a/libavformat/imf.h +++ b/libavformat/imf.h @@ -140,6 +140,7 @@ typedef struct FFIMFCPL { /** * Parse an IMF CompositionPlaylist element into the FFIMFCPL data structure. + * @param[in] log_ctx Logging context (points to an instance of AVClass). May be NULL. * @param[in] doc An XML document from which the CPL is read. * @param[out] cpl Pointer to a memory area (allocated by the client), where the * function writes a pointer to the newly constructed FFIMFCPL structure (or @@ -147,10 +148,11 @@ typedef struct FFIMFCPL { * the FFIMFCPL structure using ff_imf_cpl_free(). * @return A non-zero value in case of an error. */ -int ff_imf_parse_cpl_from_xml_dom(xmlDocPtr doc, FFIMFCPL **cpl); +int ff_imf_parse_cpl_from_xml_dom(void *log_ctx, xmlDocPtr doc, FFIMFCPL **cpl); /** * Parse an IMF Composition Playlist document into the FFIMFCPL data structure. + * @param[in] log_ctx Logging context (points to an instance of AVClass). May be NULL. * @param[in] in The context from which the CPL is read. * @param[out] cpl Pointer to a memory area (allocated by the client), where the * function writes a pointer to the newly constructed FFIMFCPL structure (or @@ -158,7 +160,7 @@ int ff_imf_parse_cpl_from_xml_dom(xmlDocPtr doc, FFIMFCPL **cpl); * the FFIMFCPL structure using ff_imf_cpl_free(). * @return A non-zero value in case of an error. */ -int ff_imf_parse_cpl(AVIOContext *in, FFIMFCPL **cpl); +int ff_imf_parse_cpl(void *log_ctx, AVIOContext *in, FFIMFCPL **cpl); /** * Allocates and initializes an FFIMFCPL data structure. diff --git a/libavformat/imf_cpl.c b/libavformat/imf_cpl.c index ad84a68b13b..5f1a67443f2 100644 --- a/libavformat/imf_cpl.c +++ b/libavformat/imf_cpl.c @@ -75,11 +75,11 @@ int ff_imf_xml_read_uuid(xmlNodePtr element, AVUUID uuid) int ret = 0; xmlChar *element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1); + if (!element_text) + return AVERROR_INVALIDDATA; ret = av_uuid_urn_parse(element_text, uuid); - if (ret) { - av_log(NULL, AV_LOG_ERROR, "Invalid UUID\n"); + if (ret) ret = AVERROR_INVALIDDATA; - } xmlFree(element_text); return ret; @@ -90,10 +90,8 @@ int ff_imf_xml_read_rational(xmlNodePtr element, AVRational *rational) int ret = 0; xmlChar *element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1); - if (sscanf(element_text, "%i %i", &rational->num, &rational->den) != 2) { - av_log(NULL, AV_LOG_ERROR, "Invalid rational number\n"); + if (element_text == NULL || sscanf(element_text, "%i %i", &rational->num, &rational->den) != 2) ret = AVERROR_INVALIDDATA; - } xmlFree(element_text); return ret; @@ -104,10 +102,8 @@ int ff_imf_xml_read_uint32(xmlNodePtr element, uint32_t *number) int ret = 0; xmlChar *element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1); - if (sscanf(element_text, "%" PRIu32, number) != 1) { - av_log(NULL, AV_LOG_ERROR, "Invalid unsigned 32-bit integer"); + if (element_text == NULL || sscanf(element_text, "%" PRIu32, number) != 1) ret = AVERROR_INVALIDDATA; - } xmlFree(element_text); return ret; @@ -181,13 +177,15 @@ static int fill_content_title(xmlNodePtr cpl_element, FFIMFCPL *cpl) { xmlNodePtr element = NULL; - if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "ContentTitle"))) { - av_log(NULL, AV_LOG_ERROR, "ContentTitle element not found in the IMF CPL\n"); + if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "ContentTitle"))) return AVERROR_INVALIDDATA; - } cpl->content_title_utf8 = xmlNodeListGetString(cpl_element->doc, element->xmlChildrenNode, 1); + if (!cpl->content_title_utf8) + cpl->content_title_utf8 = xmlStrdup(""); + if (!cpl->content_title_utf8) + return AVERROR(ENOMEM); return 0; } @@ -242,24 +240,19 @@ static int fill_timecode(xmlNodePtr cpl_element, FFIMFCPL *cpl) return 0; element = ff_imf_xml_get_child_element_by_name(tc_element, "TimecodeDropFrame"); - if (!element) { - av_log(NULL, AV_LOG_ERROR, "CompositionTimecode element is missing\ - a TimecodeDropFrame child element\n"); + if (!element) return AVERROR_INVALIDDATA; - } - if (ff_imf_xml_read_boolean(element, &df)) { - av_log(NULL, AV_LOG_ERROR, "TimecodeDropFrame element is invalid\n"); + if (ff_imf_xml_read_boolean(element, &df)) return AVERROR_INVALIDDATA; - } + element = ff_imf_xml_get_child_element_by_name(tc_element, "TimecodeStartAddress"); - if (!element) { - av_log(NULL, AV_LOG_ERROR, "CompositionTimecode element is missing\ - a TimecodeStartAddress child element\n"); + if (!element) return AVERROR_INVALIDDATA; - } tc_str = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1); + if (!tc_str) + return AVERROR_INVALIDDATA; ret = parse_cpl_tc_type(tc_str, comps); xmlFree(tc_str); if (ret) @@ -280,10 +273,8 @@ static int fill_edit_rate(xmlNodePtr cpl_element, FFIMFCPL *cpl) { xmlNodePtr element = NULL; - if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "EditRate"))) { - av_log(NULL, AV_LOG_ERROR, "EditRate element not found in the IMF CPL\n"); + if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "EditRate"))) return AVERROR_INVALIDDATA; - } return ff_imf_xml_read_rational(element, &cpl->edit_rate); } @@ -292,10 +283,8 @@ static int fill_id(xmlNodePtr cpl_element, FFIMFCPL *cpl) { xmlNodePtr element = NULL; - if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "Id"))) { - av_log(NULL, AV_LOG_ERROR, "Id element not found in the IMF CPL\n"); + if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "Id"))) return AVERROR_INVALIDDATA; - } return ff_imf_xml_read_uuid(element, cpl->id_uuid); } @@ -306,22 +295,19 @@ static int fill_marker(xmlNodePtr marker_elem, FFIMFMarker *marker) int ret = 0; /* read Offset */ - if (!(element = ff_imf_xml_get_child_element_by_name(marker_elem, "Offset"))) { - av_log(NULL, AV_LOG_ERROR, "Offset element not found in a Marker\n"); + if (!(element = ff_imf_xml_get_child_element_by_name(marker_elem, "Offset"))) return AVERROR_INVALIDDATA; - } + if ((ret = ff_imf_xml_read_uint32(element, &marker->offset))) return ret; /* read Label and Scope */ - if (!(element = ff_imf_xml_get_child_element_by_name(marker_elem, "Label"))) { - av_log(NULL, AV_LOG_ERROR, "Label element not found in a Marker\n"); + if (!(element = ff_imf_xml_get_child_element_by_name(marker_elem, "Label"))) return AVERROR_INVALIDDATA; - } - if (!(marker->label_utf8 = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1))) { - av_log(NULL, AV_LOG_ERROR, "Empty Label element found in a Marker\n"); + + if (!(marker->label_utf8 = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1))) return AVERROR_INVALIDDATA; - } + if (!(marker->scope_utf8 = xmlGetNoNsProp(element, "scope"))) { marker->scope_utf8 = xmlCharStrdup("http://www.smpte-ra.org/schemas/2067-3/2013#standard-markers"); @@ -334,7 +320,8 @@ static int fill_marker(xmlNodePtr marker_elem, FFIMFMarker *marker) return ret; } -static int fill_base_resource(xmlNodePtr resource_elem, FFIMFBaseResource *resource, FFIMFCPL *cpl) +static int fill_base_resource(void *log_ctx, xmlNodePtr resource_elem, + FFIMFBaseResource *resource, FFIMFCPL *cpl) { xmlNodePtr element = NULL; int ret = 0; @@ -343,14 +330,14 @@ static int fill_base_resource(xmlNodePtr resource_elem, FFIMFBaseResource *resou if (!(element = ff_imf_xml_get_child_element_by_name(resource_elem, "EditRate"))) { resource->edit_rate = cpl->edit_rate; } else if ((ret = ff_imf_xml_read_rational(element, &resource->edit_rate))) { - av_log(NULL, AV_LOG_ERROR, "Invalid EditRate element found in a Resource\n"); + av_log(log_ctx, AV_LOG_ERROR, "Invalid EditRate element found in a Resource\n"); return ret; } /* read EntryPoint */ if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "EntryPoint"))) { if ((ret = ff_imf_xml_read_uint32(element, &resource->entry_point))) { - av_log(NULL, AV_LOG_ERROR, "Invalid EntryPoint element found in a Resource\n"); + av_log(log_ctx, AV_LOG_ERROR, "Invalid EntryPoint element found in a Resource\n"); return ret; } } else { @@ -359,11 +346,11 @@ static int fill_base_resource(xmlNodePtr resource_elem, FFIMFBaseResource *resou /* read IntrinsicDuration */ if (!(element = ff_imf_xml_get_child_element_by_name(resource_elem, "IntrinsicDuration"))) { - av_log(NULL, AV_LOG_ERROR, "IntrinsicDuration element missing from Resource\n"); + av_log(log_ctx, AV_LOG_ERROR, "IntrinsicDuration element missing from Resource\n"); return AVERROR_INVALIDDATA; } if ((ret = ff_imf_xml_read_uint32(element, &resource->duration))) { - av_log(NULL, AV_LOG_ERROR, "Invalid IntrinsicDuration element found in a Resource\n"); + av_log(log_ctx, AV_LOG_ERROR, "Invalid IntrinsicDuration element found in a Resource\n"); return ret; } resource->duration -= resource->entry_point; @@ -371,7 +358,7 @@ static int fill_base_resource(xmlNodePtr resource_elem, FFIMFBaseResource *resou /* read SourceDuration */ if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "SourceDuration"))) { if ((ret = ff_imf_xml_read_uint32(element, &resource->duration))) { - av_log(NULL, AV_LOG_ERROR, "SourceDuration element missing from Resource\n"); + av_log(log_ctx, AV_LOG_ERROR, "SourceDuration element missing from Resource\n"); return ret; } } @@ -383,38 +370,38 @@ static int fill_base_resource(xmlNodePtr resource_elem, FFIMFBaseResource *resou return ret; } -static int fill_trackfile_resource(xmlNodePtr tf_resource_elem, +static int fill_trackfile_resource(void *log_ctx, xmlNodePtr tf_resource_elem, FFIMFTrackFileResource *tf_resource, FFIMFCPL *cpl) { xmlNodePtr element = NULL; int ret = 0; - if ((ret = fill_base_resource(tf_resource_elem, (FFIMFBaseResource *)tf_resource, cpl))) + if ((ret = fill_base_resource(log_ctx, tf_resource_elem, (FFIMFBaseResource *)tf_resource, cpl))) return ret; /* read TrackFileId */ if ((element = ff_imf_xml_get_child_element_by_name(tf_resource_elem, "TrackFileId"))) { if ((ret = ff_imf_xml_read_uuid(element, tf_resource->track_file_uuid))) { - av_log(NULL, AV_LOG_ERROR, "Invalid TrackFileId element found in Resource\n"); + av_log(log_ctx, AV_LOG_ERROR, "Invalid TrackFileId element found in Resource\n"); return ret; } } else { - av_log(NULL, AV_LOG_ERROR, "TrackFileId element missing from Resource\n"); + av_log(log_ctx, AV_LOG_ERROR, "TrackFileId element missing from Resource\n"); return AVERROR_INVALIDDATA; } return ret; } -static int fill_marker_resource(xmlNodePtr marker_resource_elem, +static int fill_marker_resource(void *log_ctx, xmlNodePtr marker_resource_elem, FFIMFMarkerResource *marker_resource, FFIMFCPL *cpl) { xmlNodePtr element = NULL; int ret = 0; - if ((ret = fill_base_resource(marker_resource_elem, (FFIMFBaseResource *)marker_resource, cpl))) + if ((ret = fill_base_resource(log_ctx, marker_resource_elem, (FFIMFBaseResource *)marker_resource, cpl))) return ret; /* read markers */ @@ -436,8 +423,10 @@ static int fill_marker_resource(xmlNodePtr marker_resource_elem, ret = fill_marker(element, &marker_resource->markers[marker_resource->marker_count]); marker_resource->marker_count++; - if (ret) + if (ret) { + av_log(log_ctx, AV_LOG_ERROR, "Invalid Marker element\n"); return ret; + } } element = xmlNextElementSibling(element); @@ -446,7 +435,7 @@ static int fill_marker_resource(xmlNodePtr marker_resource_elem, return ret; } -static int push_marker_sequence(xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl) +static int push_marker_sequence(void *log_ctx, xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl) { int ret = 0; AVUUID uuid; @@ -458,14 +447,14 @@ static int push_marker_sequence(xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl) /* read TrackID element */ if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(marker_sequence_elem, "TrackId"))) { - av_log(NULL, AV_LOG_ERROR, "TrackId element missing from Sequence\n"); + av_log(log_ctx, AV_LOG_ERROR, "TrackId element missing from Sequence\n"); return AVERROR_INVALIDDATA; } if (ff_imf_xml_read_uuid(track_id_elem, uuid)) { - av_log(NULL, AV_LOG_ERROR, "Invalid TrackId element found in Sequence\n"); + av_log(log_ctx, AV_LOG_ERROR, "Invalid TrackId element found in Sequence\n"); return AVERROR_INVALIDDATA; } - av_log(NULL, + av_log(log_ctx, AV_LOG_DEBUG, "Processing IMF CPL Marker Sequence for Virtual Track " AV_PRI_UUID "\n", AV_UUID_ARG(uuid)); @@ -479,7 +468,7 @@ static int push_marker_sequence(xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl) av_uuid_copy(cpl->main_markers_track->base.id_uuid, uuid); } else if (!av_uuid_equal(cpl->main_markers_track->base.id_uuid, uuid)) { - av_log(NULL, AV_LOG_ERROR, "Multiple marker virtual tracks were found\n"); + av_log(log_ctx, AV_LOG_ERROR, "Multiple marker virtual tracks were found\n"); return AVERROR_INVALIDDATA; } @@ -496,7 +485,7 @@ static int push_marker_sequence(xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl) cpl->main_markers_track->resource_count + resource_elem_count, sizeof(FFIMFMarkerResource)); if (!tmp) { - av_log(NULL, AV_LOG_ERROR, "Cannot allocate Marker Resources\n"); + av_log(log_ctx, AV_LOG_ERROR, "Cannot allocate Marker Resources\n"); return AVERROR(ENOMEM); } cpl->main_markers_track->resources = tmp; @@ -504,7 +493,7 @@ static int push_marker_sequence(xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl) resource_elem = xmlFirstElementChild(resource_list_elem); while (resource_elem) { imf_marker_resource_init(&cpl->main_markers_track->resources[cpl->main_markers_track->resource_count]); - ret = fill_marker_resource(resource_elem, + ret = fill_marker_resource(log_ctx, resource_elem, &cpl->main_markers_track->resources[cpl->main_markers_track->resource_count], cpl); cpl->main_markers_track->resource_count++; @@ -533,7 +522,7 @@ static int has_stereo_resources(xmlNodePtr element) return 0; } -static int push_main_audio_sequence(xmlNodePtr audio_sequence_elem, FFIMFCPL *cpl) +static int push_main_audio_sequence(void *log_ctx, xmlNodePtr audio_sequence_elem, FFIMFCPL *cpl) { int ret = 0; AVUUID uuid; @@ -546,14 +535,14 @@ static int push_main_audio_sequence(xmlNodePtr audio_sequence_elem, FFIMFCPL *cp /* read TrackID element */ if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(audio_sequence_elem, "TrackId"))) { - av_log(NULL, AV_LOG_ERROR, "TrackId element missing from audio sequence\n"); + av_log(log_ctx, AV_LOG_ERROR, "TrackId element missing from audio sequence\n"); return AVERROR_INVALIDDATA; } if ((ret = ff_imf_xml_read_uuid(track_id_elem, uuid))) { - av_log(NULL, AV_LOG_ERROR, "Invalid TrackId element found in audio sequence\n"); + av_log(log_ctx, AV_LOG_ERROR, "Invalid TrackId element found in audio sequence\n"); return ret; } - av_log(NULL, + av_log(log_ctx, AV_LOG_DEBUG, "Processing IMF CPL Audio Sequence for Virtual Track " AV_PRI_UUID "\n", AV_UUID_ARG(uuid)); @@ -597,7 +586,7 @@ static int push_main_audio_sequence(xmlNodePtr audio_sequence_elem, FFIMFCPL *cp (vt->resource_count + resource_elem_count) * sizeof(FFIMFTrackFileResource)); if (!tmp) { - av_log(NULL, AV_LOG_ERROR, "Cannot allocate Main Audio Resources\n"); + av_log(log_ctx, AV_LOG_ERROR, "Cannot allocate Main Audio Resources\n"); return AVERROR(ENOMEM); } vt->resources = tmp; @@ -605,14 +594,13 @@ static int push_main_audio_sequence(xmlNodePtr audio_sequence_elem, FFIMFCPL *cp resource_elem = xmlFirstElementChild(resource_list_elem); while (resource_elem) { imf_trackfile_resource_init(&vt->resources[vt->resource_count]); - ret = fill_trackfile_resource(resource_elem, + ret = fill_trackfile_resource(log_ctx, resource_elem, &vt->resources[vt->resource_count], cpl); - vt->resource_count++; - if (ret) { - av_log(NULL, AV_LOG_ERROR, "Invalid Resource\n"); - continue; - } + if (ret) + av_log(log_ctx, AV_LOG_ERROR, "Invalid Resource\n"); + else + vt->resource_count++; resource_elem = xmlNextElementSibling(resource_elem); } @@ -620,7 +608,7 @@ static int push_main_audio_sequence(xmlNodePtr audio_sequence_elem, FFIMFCPL *cp return ret; } -static int push_main_image_2d_sequence(xmlNodePtr image_sequence_elem, FFIMFCPL *cpl) +static int push_main_image_2d_sequence(void *log_ctx, xmlNodePtr image_sequence_elem, FFIMFCPL *cpl) { int ret = 0; AVUUID uuid; @@ -632,17 +620,17 @@ static int push_main_image_2d_sequence(xmlNodePtr image_sequence_elem, FFIMFCPL /* skip stereoscopic resources */ if (has_stereo_resources(image_sequence_elem)) { - av_log(NULL, AV_LOG_ERROR, "Stereoscopic 3D image virtual tracks not supported\n"); + av_log(log_ctx, AV_LOG_ERROR, "Stereoscopic 3D image virtual tracks not supported\n"); return AVERROR_PATCHWELCOME; } /* read TrackId element*/ if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(image_sequence_elem, "TrackId"))) { - av_log(NULL, AV_LOG_ERROR, "TrackId element missing from audio sequence\n"); + av_log(log_ctx, AV_LOG_ERROR, "TrackId element missing from audio sequence\n"); return AVERROR_INVALIDDATA; } if ((ret = ff_imf_xml_read_uuid(track_id_elem, uuid))) { - av_log(NULL, AV_LOG_ERROR, "Invalid TrackId element found in audio sequence\n"); + av_log(log_ctx, AV_LOG_ERROR, "Invalid TrackId element found in audio sequence\n"); return ret; } @@ -655,10 +643,10 @@ static int push_main_image_2d_sequence(xmlNodePtr image_sequence_elem, FFIMFCPL av_uuid_copy(cpl->main_image_2d_track->base.id_uuid, uuid); } else if (!av_uuid_equal(cpl->main_image_2d_track->base.id_uuid, uuid)) { - av_log(NULL, AV_LOG_ERROR, "Multiple MainImage virtual tracks found\n"); + av_log(log_ctx, AV_LOG_ERROR, "Multiple MainImage virtual tracks found\n"); return AVERROR_INVALIDDATA; } - av_log(NULL, + av_log(log_ctx, AV_LOG_DEBUG, "Processing IMF CPL Main Image Sequence for Virtual Track " AV_PRI_UUID "\n", AV_UUID_ARG(uuid)); @@ -679,7 +667,7 @@ static int push_main_image_2d_sequence(xmlNodePtr image_sequence_elem, FFIMFCPL (cpl->main_image_2d_track->resource_count + resource_elem_count) * sizeof(FFIMFTrackFileResource)); if (!tmp) { - av_log(NULL, AV_LOG_ERROR, "Cannot allocate Main Image Resources\n"); + av_log(log_ctx, AV_LOG_ERROR, "Cannot allocate Main Image Resources\n"); return AVERROR(ENOMEM); } cpl->main_image_2d_track->resources = tmp; @@ -688,14 +676,13 @@ static int push_main_image_2d_sequence(xmlNodePtr image_sequence_elem, FFIMFCPL while (resource_elem) { imf_trackfile_resource_init( &cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count]); - ret = fill_trackfile_resource(resource_elem, + ret = fill_trackfile_resource(log_ctx, resource_elem, &cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count], cpl); - cpl->main_image_2d_track->resource_count++; - if (ret) { - av_log(NULL, AV_LOG_ERROR, "Invalid Resource\n"); - continue; - } + if (ret) + av_log(log_ctx, AV_LOG_ERROR, "Invalid Resource\n"); + else + cpl->main_image_2d_track->resource_count++; resource_elem = xmlNextElementSibling(resource_elem); } @@ -703,7 +690,7 @@ static int push_main_image_2d_sequence(xmlNodePtr image_sequence_elem, FFIMFCPL return 0; } -static int fill_virtual_tracks(xmlNodePtr cpl_element, FFIMFCPL *cpl) +static int fill_virtual_tracks(void *log_ctx, xmlNodePtr cpl_element, FFIMFCPL *cpl) { int ret = 0; xmlNodePtr segment_list_elem = NULL; @@ -712,14 +699,14 @@ static int fill_virtual_tracks(xmlNodePtr cpl_element, FFIMFCPL *cpl) xmlNodePtr sequence_elem = NULL; if (!(segment_list_elem = ff_imf_xml_get_child_element_by_name(cpl_element, "SegmentList"))) { - av_log(NULL, AV_LOG_ERROR, "SegmentList element missing\n"); + av_log(log_ctx, AV_LOG_ERROR, "SegmentList element missing\n"); return AVERROR_INVALIDDATA; } /* process sequences */ segment_elem = xmlFirstElementChild(segment_list_elem); while (segment_elem) { - av_log(NULL, AV_LOG_DEBUG, "Processing IMF CPL Segment\n"); + av_log(log_ctx, AV_LOG_DEBUG, "Processing IMF CPL Segment\n"); sequence_list_elem = ff_imf_xml_get_child_element_by_name(segment_elem, "SequenceList"); if (!sequence_list_elem) @@ -728,16 +715,16 @@ static int fill_virtual_tracks(xmlNodePtr cpl_element, FFIMFCPL *cpl) sequence_elem = xmlFirstElementChild(sequence_list_elem); while (sequence_elem) { if (xmlStrcmp(sequence_elem->name, "MarkerSequence") == 0) - ret = push_marker_sequence(sequence_elem, cpl); + ret = push_marker_sequence(log_ctx, sequence_elem, cpl); else if (xmlStrcmp(sequence_elem->name, "MainImageSequence") == 0) - ret = push_main_image_2d_sequence(sequence_elem, cpl); + ret = push_main_image_2d_sequence(log_ctx, sequence_elem, cpl); else if (xmlStrcmp(sequence_elem->name, "MainAudioSequence") == 0) - ret = push_main_audio_sequence(sequence_elem, cpl); + ret = push_main_audio_sequence(log_ctx, sequence_elem, cpl); else - av_log(NULL, + av_log(log_ctx, AV_LOG_INFO, "The following Sequence is not supported and is ignored: %s\n", sequence_elem->name); @@ -755,7 +742,7 @@ static int fill_virtual_tracks(xmlNodePtr cpl_element, FFIMFCPL *cpl) return ret; } -int ff_imf_parse_cpl_from_xml_dom(xmlDocPtr doc, FFIMFCPL **cpl) +int ff_imf_parse_cpl_from_xml_dom(void *log_ctx, xmlDocPtr doc, FFIMFCPL **cpl) { int ret = 0; xmlNodePtr cpl_element = NULL; @@ -768,20 +755,28 @@ int ff_imf_parse_cpl_from_xml_dom(xmlDocPtr doc, FFIMFCPL **cpl) cpl_element = xmlDocGetRootElement(doc); if (!cpl_element || xmlStrcmp(cpl_element->name, "CompositionPlaylist")) { - av_log(NULL, AV_LOG_ERROR, "The root element of the CPL is not CompositionPlaylist\n"); + av_log(log_ctx, AV_LOG_ERROR, "The root element of the CPL is not CompositionPlaylist\n"); ret = AVERROR_INVALIDDATA; goto cleanup; } - if ((ret = fill_content_title(cpl_element, *cpl))) + if ((ret = fill_content_title(cpl_element, *cpl))) { + av_log(log_ctx, AV_LOG_ERROR, "Cannot read the ContentTitle element from the IMF CPL\n"); goto cleanup; - if ((ret = fill_id(cpl_element, *cpl))) + } + if ((ret = fill_id(cpl_element, *cpl))) { + av_log(log_ctx, AV_LOG_ERROR, "Id element not found in the IMF CPL\n"); goto cleanup; - if ((ret = fill_edit_rate(cpl_element, *cpl))) + } + if ((ret = fill_edit_rate(cpl_element, *cpl))) { + av_log(log_ctx, AV_LOG_ERROR, "EditRate element not found in the IMF CPL\n"); goto cleanup; - if ((ret = fill_timecode(cpl_element, *cpl))) + } + if ((ret = fill_timecode(cpl_element, *cpl))) { + av_log(log_ctx, AV_LOG_ERROR, "Invalid CompositionTimecode element found in the IMF CPL\n"); goto cleanup; - if ((ret = fill_virtual_tracks(cpl_element, *cpl))) + } + if ((ret = fill_virtual_tracks(log_ctx, cpl_element, *cpl))) goto cleanup; cleanup: @@ -877,7 +872,7 @@ void ff_imf_cpl_free(FFIMFCPL *cpl) av_freep(&cpl); } -int ff_imf_parse_cpl(AVIOContext *in, FFIMFCPL **cpl) +int ff_imf_parse_cpl(void *log_ctx, AVIOContext *in, FFIMFCPL **cpl) { AVBPrint buf; xmlDoc *doc = NULL; @@ -887,7 +882,7 @@ int ff_imf_parse_cpl(AVIOContext *in, FFIMFCPL **cpl) ret = avio_read_to_bprint(in, &buf, SIZE_MAX); if (ret < 0 || !avio_feof(in)) { - av_log(NULL, AV_LOG_ERROR, "Cannot read IMF CPL\n"); + av_log(log_ctx, AV_LOG_ERROR, "Cannot read IMF CPL\n"); if (ret == 0) ret = AVERROR_INVALIDDATA; goto clean_up; @@ -897,21 +892,21 @@ int ff_imf_parse_cpl(AVIOContext *in, FFIMFCPL **cpl) doc = xmlReadMemory(buf.str, buf.len, NULL, NULL, 0); if (!doc) { - av_log(NULL, + av_log(log_ctx, AV_LOG_ERROR, "XML parsing failed when reading the IMF CPL\n"); ret = AVERROR_INVALIDDATA; goto clean_up; } - if ((ret = ff_imf_parse_cpl_from_xml_dom(doc, cpl))) { - av_log(NULL, AV_LOG_ERROR, "Cannot parse IMF CPL\n"); + if ((ret = ff_imf_parse_cpl_from_xml_dom(log_ctx, doc, cpl))) { + av_log(log_ctx, AV_LOG_ERROR, "Cannot parse IMF CPL\n"); } else { - av_log(NULL, + av_log(log_ctx, AV_LOG_INFO, "IMF CPL ContentTitle: %s\n", (*cpl)->content_title_utf8); - av_log(NULL, + av_log(log_ctx, AV_LOG_INFO, "IMF CPL Id: " AV_PRI_UUID "\n", AV_UUID_ARG((*cpl)->id_uuid)); diff --git a/libavformat/imfdec.c b/libavformat/imfdec.c index 03de9ce151d..818b5e590bb 100644 --- a/libavformat/imfdec.c +++ b/libavformat/imfdec.c @@ -379,7 +379,11 @@ static int open_track_resource_context(AVFormatContext *s, return AVERROR(ENOMEM); track_resource->ctx->io_open = s->io_open; +#if FF_API_AVFORMAT_IO_CLOSE +FF_DISABLE_DEPRECATION_WARNINGS track_resource->ctx->io_close = s->io_close; +FF_ENABLE_DEPRECATION_WARNINGS +#endif track_resource->ctx->io_close2 = s->io_close2; track_resource->ctx->flags |= s->flags & ~AVFMT_FLAG_CUSTOM_IO; @@ -645,7 +649,7 @@ static int imf_read_header(AVFormatContext *s) av_log(s, AV_LOG_DEBUG, "start parsing IMF CPL: %s\n", s->url); - if ((ret = ff_imf_parse_cpl(s->pb, &c->cpl)) < 0) + if ((ret = ff_imf_parse_cpl(s, s->pb, &c->cpl)) < 0) return ret; tcr = av_dict_get(s->metadata, "timecode", NULL, 0); @@ -1018,7 +1022,7 @@ static const AVClass imf_class = { const AVInputFormat ff_imf_demuxer = { .name = "imf", .long_name = NULL_IF_CONFIG_SMALL("IMF (Interoperable Master Format)"), - .flags = AVFMT_EXPERIMENTAL | AVFMT_NO_BYTE_SEEK, + .flags = AVFMT_NO_BYTE_SEEK, .flags_internal = FF_FMT_INIT_CLEANUP, .priv_class = &imf_class, .priv_data_size = sizeof(IMFContext), diff --git a/libavformat/img2dec.c b/libavformat/img2dec.c index 5a63d7c81d8..b986d3a502d 100644 --- a/libavformat/img2dec.c +++ b/libavformat/img2dec.c @@ -850,7 +850,7 @@ static int jpegxl_probe(const AVProbeData *p) if (AV_RL16(b) != FF_JPEGXL_CODESTREAM_SIGNATURE_LE) return 0; #if CONFIG_IMAGE_JPEGXL_PIPE_DEMUXER - if (ff_jpegxl_verify_codestream_header(p->buf, p->buf_size) >= 0) + if (ff_jpegxl_verify_codestream_header(p->buf, p->buf_size, 1) >= 0) return AVPROBE_SCORE_MAX - 2; #endif return 0; @@ -964,8 +964,13 @@ static int svg_probe(const AVProbeData *p) { const uint8_t *b = p->buf; const uint8_t *end = p->buf + p->buf_size; - - if (memcmp(p->buf, "= end - 5) + return 0; + if (!memcmp(b, "buf, "codecpar->codec_id = ff_codec_get_id(ff_codec_bmp_tags, st->codecpar->codec_tag); st->codecpar->width = avio_rl16(s->pb); st->codecpar->height = avio_rl16(s->pb); - time_base.den = avio_rl32(s->pb); - time_base.num = avio_rl32(s->pb); - st->duration = avio_rl32(s->pb); + time_base.den = avio_rl32(s->pb); + time_base.num = avio_rl32(s->pb); + st->nb_frames = avio_rl32(s->pb); avio_skip(s->pb, 4); // unused + // Infer duration from nb_frames, in order to be backward compatible with + // previous IVF demuxer. + // It is popular to configure time_base to 1/frame_rate by IVF muxer, that + // the duration happens to be the same with nb_frames. See + // `https://chromium.googlesource.com/webm/vp8-test-vectors/+/refs/heads/main` + st->duration = st->nb_frames; + ffstream(st)->need_parsing = AVSTREAM_PARSE_HEADERS; if (!time_base.den || !time_base.num) { diff --git a/libavformat/ivfenc.c b/libavformat/ivfenc.c index ed5ec90ce94..88399099d45 100644 --- a/libavformat/ivfenc.c +++ b/libavformat/ivfenc.c @@ -72,7 +72,8 @@ static int ivf_write_header(AVFormatContext *s) avio_wl16(pb, par->height); avio_wl32(pb, s->streams[0]->time_base.den); avio_wl32(pb, s->streams[0]->time_base.num); - avio_wl64(pb, 0xFFFFFFFFFFFFFFFFULL); // length is overwritten at the end of muxing + avio_wl32(pb, 0xFFFFFFFF); // "number of frames" is overwritten at the end of muxing + avio_wl32(pb, 0); // unused return 0; } @@ -99,16 +100,12 @@ static int ivf_write_trailer(AVFormatContext *s) AVIOContext *pb = s->pb; IVFEncContext *ctx = s->priv_data; - if ((pb->seekable & AVIO_SEEKABLE_NORMAL) && - (ctx->frame_cnt > 1 || (ctx->frame_cnt == 1 && ctx->last_pkt_duration))) { + // overwrite the "number of frames" + if ((pb->seekable & AVIO_SEEKABLE_NORMAL)) { int64_t end = avio_tell(pb); avio_seek(pb, 24, SEEK_SET); - // overwrite the "length" field (duration) - avio_wl32(pb, ctx->last_pkt_duration ? - ctx->sum_delta_pts + ctx->last_pkt_duration : - ctx->frame_cnt * ctx->sum_delta_pts / (ctx->frame_cnt - 1)); - avio_wl32(pb, 0); // zero out unused bytes + avio_wl32(pb, ctx->frame_cnt); avio_seek(pb, end, SEEK_SET); } @@ -122,16 +119,16 @@ static const AVCodecTag codec_ivf_tags[] = { { AV_CODEC_ID_NONE, 0 } }; -const AVOutputFormat ff_ivf_muxer = { +const FFOutputFormat ff_ivf_muxer = { + .p.name = "ivf", + .p.long_name = NULL_IF_CONFIG_SMALL("On2 IVF"), + .p.extensions = "ivf", + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_VP8, + .p.codec_tag = (const AVCodecTag* const []){ codec_ivf_tags, 0 }, .priv_data_size = sizeof(IVFEncContext), - .name = "ivf", - .long_name = NULL_IF_CONFIG_SMALL("On2 IVF"), - .extensions = "ivf", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_VP8, .init = ivf_init, .write_header = ivf_write_header, .write_packet = ivf_write_packet, .write_trailer = ivf_write_trailer, - .codec_tag = (const AVCodecTag* const []){ codec_ivf_tags, 0 }, }; diff --git a/libavformat/jacosubenc.c b/libavformat/jacosubenc.c index f0548bb2820..fa0f9fdaa2a 100644 --- a/libavformat/jacosubenc.c +++ b/libavformat/jacosubenc.c @@ -17,6 +17,7 @@ */ #include "avformat.h" +#include "mux.h" #include "rawenc.h" static int jacosub_write_header(AVFormatContext *s) @@ -29,13 +30,13 @@ static int jacosub_write_header(AVFormatContext *s) return 0; } -const AVOutputFormat ff_jacosub_muxer = { - .name = "jacosub", - .long_name = NULL_IF_CONFIG_SMALL("JACOsub subtitle format"), - .mime_type = "text/x-jacosub", - .extensions = "jss,js", +const FFOutputFormat ff_jacosub_muxer = { + .p.name = "jacosub", + .p.long_name = NULL_IF_CONFIG_SMALL("JACOsub subtitle format"), + .p.mime_type = "text/x-jacosub", + .p.extensions = "jss,js", + .p.flags = AVFMT_TS_NONSTRICT, + .p.subtitle_codec = AV_CODEC_ID_JACOSUB, .write_header = jacosub_write_header, .write_packet = ff_raw_write_packet, - .flags = AVFMT_TS_NONSTRICT, - .subtitle_codec = AV_CODEC_ID_JACOSUB, }; diff --git a/libavformat/jpegxl_anim_dec.c b/libavformat/jpegxl_anim_dec.c new file mode 100644 index 00000000000..956b56c1d8e --- /dev/null +++ b/libavformat/jpegxl_anim_dec.c @@ -0,0 +1,271 @@ +/* + * Animated JPEG XL Demuxer + * Copyright (c) 2023 Leo Izen (thebombzen) + * + * 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 + * Animated JPEG XL Demuxer + * @see ISO/IEC 18181-1 and 18181-2 + */ + +#include +#include + +#include "libavcodec/bytestream.h" +#define BITSTREAM_READER_LE +#include "libavcodec/get_bits.h" + +#include "libavutil/intreadwrite.h" +#include "libavutil/opt.h" + +#include "avformat.h" +#include "internal.h" +#include "jpegxl_probe.h" + +typedef struct JXLAnimDemuxContext { + AVBufferRef *initial; +} JXLAnimDemuxContext; + +/* + * copies as much of the codestream into the buffer as possible + * pass a shorter buflen to request less + * returns the number of bytes consumed from input, may be greater than input_len + * if the input doesn't end on an ISOBMFF-box boundary + */ +static int jpegxl_collect_codestream_header(const uint8_t *input_buffer, int input_len, + uint8_t *buffer, int buflen, int *copied) { + GetByteContext gb; + *copied = 0; + bytestream2_init(&gb, input_buffer, input_len); + + while (1) { + uint64_t size; + uint32_t tag; + int head_size = 8; + + if (bytestream2_get_bytes_left(&gb) < 16) + break; + + size = bytestream2_get_be32(&gb); + if (size == 1) { + size = bytestream2_get_be64(&gb); + head_size = 16; + } + /* invalid ISOBMFF size */ + if (size && size <= head_size) + return AVERROR_INVALIDDATA; + if (size) + size -= head_size; + + tag = bytestream2_get_le32(&gb); + if (tag == MKTAG('j', 'x', 'l', 'p')) { + if (bytestream2_get_bytes_left(&gb) < 4) + break; + bytestream2_skip(&gb, 4); + if (size) { + if (size <= 4) + return AVERROR_INVALIDDATA; + size -= 4; + } + } + /* + * size = 0 means "until EOF". this is legal but uncommon + * here we just set it to the remaining size of the probe buffer + */ + if (!size) + size = bytestream2_get_bytes_left(&gb); + + if (tag == MKTAG('j', 'x', 'l', 'c') || tag == MKTAG('j', 'x', 'l', 'p')) { + if (size > buflen - *copied) + size = buflen - *copied; + /* + * arbitrary chunking of the payload makes this memcpy hard to avoid + * in practice this will only be performed one or two times at most + */ + *copied += bytestream2_get_buffer(&gb, buffer + *copied, size); + } else { + bytestream2_skip(&gb, size); + } + if (bytestream2_get_bytes_left(&gb) <= 0 || *copied >= buflen) + break; + } + + return bytestream2_tell(&gb); +} + +static int jpegxl_anim_probe(const AVProbeData *p) +{ + uint8_t buffer[4096 + AV_INPUT_BUFFER_PADDING_SIZE]; + int copied; + + /* this is a raw codestream */ + if (AV_RL16(p->buf) == FF_JPEGXL_CODESTREAM_SIGNATURE_LE) { + if (ff_jpegxl_verify_codestream_header(p->buf, p->buf_size, 1) >= 1) + return AVPROBE_SCORE_MAX; + + return 0; + } + + /* not a JPEG XL file at all */ + if (AV_RL64(p->buf) != FF_JPEGXL_CONTAINER_SIGNATURE_LE) + return 0; + + if (jpegxl_collect_codestream_header(p->buf, p->buf_size, buffer, sizeof(buffer) - AV_INPUT_BUFFER_PADDING_SIZE, &copied) <= 0 || copied <= 0) + return 0; + + if (ff_jpegxl_verify_codestream_header(buffer, copied, 0) >= 1) + return AVPROBE_SCORE_MAX; + + return 0; +} + +static int jpegxl_anim_read_header(AVFormatContext *s) +{ + JXLAnimDemuxContext *ctx = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st; + int offset = 0; + uint8_t head[256 + AV_INPUT_BUFFER_PADDING_SIZE]; + const int sizeofhead = sizeof(head) - AV_INPUT_BUFFER_PADDING_SIZE; + int headsize = 0; + int ctrl; + AVRational tb; + GetBitContext gbi, *gb = &gbi; + + uint64_t sig16 = avio_rl16(pb); + if (sig16 == FF_JPEGXL_CODESTREAM_SIGNATURE_LE) { + AV_WL16(head, sig16); + headsize = avio_read(s->pb, head + 2, sizeofhead - 2); + if (headsize < 0) + return headsize; + headsize += 2; + ctx->initial = av_buffer_alloc(headsize); + if (!ctx->initial) + return AVERROR(ENOMEM); + memcpy(ctx->initial->data, head, headsize); + } else { + uint64_t sig64 = avio_rl64(pb); + sig64 = (sig64 << 16) | sig16; + if (sig64 != FF_JPEGXL_CONTAINER_SIGNATURE_LE) + return AVERROR_INVALIDDATA; + avio_skip(pb, 2); // first box always 12 bytes + while (1) { + int copied; + uint8_t buf[4096]; + int read = avio_read(pb, buf, sizeof(buf)); + if (read < 0) + return read; + if (!ctx->initial) { + ctx->initial = av_buffer_alloc(read + 12); + if (!ctx->initial) + return AVERROR(ENOMEM); + AV_WL64(ctx->initial->data, FF_JPEGXL_CONTAINER_SIGNATURE_LE); + AV_WL32(ctx->initial->data + 8, 0x0a870a0d); + } else { + /* this only should be happening zero or one times in practice */ + if (av_buffer_realloc(&ctx->initial, ctx->initial->size + read) < 0) + return AVERROR(ENOMEM); + } + jpegxl_collect_codestream_header(buf, read, head + headsize, sizeofhead - headsize, &copied); + memcpy(ctx->initial->data + (ctx->initial->size - read), buf, read); + headsize += copied; + if (headsize >= sizeofhead || read < sizeof(buf)) + break; + } + } + /* offset in bits of the animation header */ + offset = ff_jpegxl_verify_codestream_header(head, headsize, 0); + if (offset <= 0) + return AVERROR_INVALIDDATA; + if (init_get_bits8(gb, head, headsize) < 0) + return AVERROR_INVALIDDATA; + skip_bits_long(gb, offset); + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; + st->codecpar->codec_id = AV_CODEC_ID_JPEGXL; + ctrl = get_bits(gb, 2); + tb.den = (const uint32_t[]){100, 1000, 1, 1}[ctrl] + get_bits_long(gb, (const uint32_t[]){0, 0, 10, 30}[ctrl]); + ctrl = get_bits(gb, 2); + tb.num = (const uint32_t[]){1, 1001, 1, 1}[ctrl] + get_bits_long(gb, (const uint32_t[]){0, 0, 8, 10}[ctrl]); + avpriv_set_pts_info(st, 1, tb.num, tb.den); + + return 0; +} + +/* the decoder requires the full input file as a single packet */ +static int jpegxl_anim_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + JXLAnimDemuxContext *ctx = s->priv_data; + AVIOContext *pb = s->pb; + int ret; + int64_t size; + size_t offset = 0; + + if ((size = avio_size(pb)) < 0) + return size; + + /* animated JXL this big should not exist */ + if (size > INT_MAX) + return AVERROR_INVALIDDATA; + + if (ctx->initial && size < ctx->initial->size) + size = ctx->initial->size; + + if ((ret = av_new_packet(pkt, size)) < 0) + return ret; + + if (ctx->initial) { + offset = ctx->initial->size; + memcpy(pkt->data, ctx->initial->data, offset); + av_buffer_unref(&ctx->initial); + } + + if ((ret = avio_read(pb, pkt->data + offset, size - offset)) < 0) + return ret; + + return 0; +} + +static int jpegxl_anim_close(AVFormatContext *s) +{ + JXLAnimDemuxContext *ctx = s->priv_data; + if (ctx->initial) + av_buffer_unref(&ctx->initial); + + return 0; +} + +const AVInputFormat ff_jpegxl_anim_demuxer = { + .name = "jpegxl_anim", + .long_name = NULL_IF_CONFIG_SMALL("Animated JPEG XL"), + .priv_data_size = sizeof(JXLAnimDemuxContext), + .read_probe = jpegxl_anim_probe, + .read_header = jpegxl_anim_read_header, + .read_packet = jpegxl_anim_read_packet, + .read_close = jpegxl_anim_close, + .flags_internal = FF_FMT_INIT_CLEANUP, + .flags = AVFMT_GENERIC_INDEX, + .mime_type = "image/jxl", + .extensions = "jxl", +}; diff --git a/libavformat/jpegxl_probe.c b/libavformat/jpegxl_probe.c index 3de002f0045..88492cb772d 100644 --- a/libavformat/jpegxl_probe.c +++ b/libavformat/jpegxl_probe.c @@ -21,6 +21,7 @@ #include "jpegxl_probe.h" +#define UNCHECKED_BITSTREAM_READER 0 #define BITSTREAM_READER_LE #include "libavcodec/get_bits.h" @@ -57,49 +58,50 @@ enum JpegXLPrimaries { FF_JPEGXL_PR_P3 = 11, }; -#define jxl_bits(n) get_bits_long(gb, (n)) -#define jxl_bits_skip(n) skip_bits_long(gb, (n)) -#define jxl_u32(c0, c1, c2, c3, u0, u1, u2, u3) jpegxl_u32(gb, \ - (const uint32_t[]){c0, c1, c2, c3}, (const uint32_t[]){u0, u1, u2, u3}) -#define jxl_u64() jpegxl_u64(gb) -#define jxl_enum() jxl_u32(0, 1, 2, 18, 0, 0, 4, 6) - /* read a U32(c_i + u(u_i)) */ -static uint32_t jpegxl_u32(GetBitContext *gb, - const uint32_t constants[4], const uint32_t ubits[4]) +static av_always_inline uint32_t jxl_u32(GetBitContext *gb, + uint32_t c0, uint32_t c1, uint32_t c2, uint32_t c3, + uint32_t u0, uint32_t u1, uint32_t u2, uint32_t u3) { - uint32_t ret, choice = jxl_bits(2); + const uint32_t constants[4] = {c0, c1, c2, c3}; + const uint32_t ubits [4] = {u0, u1, u2, u3}; + uint32_t ret, choice = get_bits(gb, 2); ret = constants[choice]; if (ubits[choice]) - ret += jxl_bits(ubits[choice]); + ret += get_bits_long(gb, ubits[choice]); return ret; } +static av_always_inline uint32_t jxl_enum(GetBitContext *gb) +{ + return jxl_u32(gb, 0, 1, 2, 18, 0, 0, 4, 6); +} + /* read a U64() */ static uint64_t jpegxl_u64(GetBitContext *gb) { uint64_t shift = 12, ret; - switch (jxl_bits(2)) { + switch (get_bits(gb, 2)) { case 0: ret = 0; break; case 1: - ret = 1 + jxl_bits(4); + ret = 1 + get_bits(gb, 4); break; case 2: - ret = 17 + jxl_bits(8); + ret = 17 + get_bits(gb, 8); break; case 3: - ret = jxl_bits(12); - while (jxl_bits(1)) { + ret = get_bits(gb, 12); + while (get_bits1(gb)) { if (shift < 60) { - ret |= (uint64_t)jxl_bits(8) << shift; + ret |= (uint64_t)get_bits(gb, 8) << shift; shift += 8; } else { - ret |= (uint64_t)jxl_bits(4) << shift; + ret |= (uint64_t)get_bits(gb, 4) << shift; break; } } @@ -142,18 +144,18 @@ static int jpegxl_read_size_header(GetBitContext *gb) { uint32_t width, height; - if (jxl_bits(1)) { + if (get_bits1(gb)) { /* small size header */ - height = (jxl_bits(5) + 1) << 3; - width = jpegxl_width_from_ratio(height, jxl_bits(3)); + height = (get_bits(gb, 5) + 1) << 3; + width = jpegxl_width_from_ratio(height, get_bits(gb, 3)); if (!width) - width = (jxl_bits(5) + 1) << 3; + width = (get_bits(gb, 5) + 1) << 3; } else { /* large size header */ - height = 1 + jxl_u32(0, 0, 0, 0, 9, 13, 18, 30); - width = jpegxl_width_from_ratio(height, jxl_bits(3)); + height = 1 + jxl_u32(gb, 0, 0, 0, 0, 9, 13, 18, 30); + width = jpegxl_width_from_ratio(height, get_bits(gb, 3)); if (!width) - width = 1 + jxl_u32(0, 0, 0, 0, 9, 13, 18, 30); + width = 1 + jxl_u32(gb, 0, 0, 0, 0, 9, 13, 18, 30); } if (width > (1 << 18) || height > (1 << 18) || (width >> 4) * (height >> 4) > (1 << 20)) @@ -170,18 +172,18 @@ static int jpegxl_read_preview_header(GetBitContext *gb) { uint32_t width, height; - if (jxl_bits(1)) { + if (get_bits1(gb)) { /* coded height and width divided by eight */ - height = jxl_u32(16, 32, 1, 33, 0, 0, 5, 9) << 3; - width = jpegxl_width_from_ratio(height, jxl_bits(3)); + height = jxl_u32(gb, 16, 32, 1, 33, 0, 0, 5, 9) << 3; + width = jpegxl_width_from_ratio(height, get_bits(gb, 3)); if (!width) - width = jxl_u32(16, 32, 1, 33, 0, 0, 5, 9) << 3; + width = jxl_u32(gb, 16, 32, 1, 33, 0, 0, 5, 9) << 3; } else { /* full height and width coded */ - height = jxl_u32(1, 65, 321, 1345, 6, 8, 10, 12); - width = jpegxl_width_from_ratio(height, jxl_bits(3)); + height = jxl_u32(gb, 1, 65, 321, 1345, 6, 8, 10, 12); + width = jpegxl_width_from_ratio(height, get_bits(gb, 3)); if (!width) - width = jxl_u32(1, 65, 321, 1345, 6, 8, 10, 12); + width = jxl_u32(gb, 1, 65, 321, 1345, 6, 8, 10, 12); } if (width > 4096 || height > 4096) return -1; @@ -194,13 +196,13 @@ static int jpegxl_read_preview_header(GetBitContext *gb) */ static void jpegxl_skip_bit_depth(GetBitContext *gb) { - if (jxl_bits(1)) { + if (get_bits1(gb)) { /* float samples */ - jxl_u32(32, 16, 24, 1, 0, 0, 0, 6); /* mantissa */ - jxl_bits_skip(4); /* exponent */ + jxl_u32(gb, 32, 16, 24, 1, 0, 0, 0, 6); /* mantissa */ + skip_bits_long(gb, 4); /* exponent */ } else { /* integer samples */ - jxl_u32(8, 10, 12, 1, 0, 0, 0, 6); + jxl_u32(gb, 8, 10, 12, 1, 0, 0, 0, 6); } } @@ -208,46 +210,46 @@ static void jpegxl_skip_bit_depth(GetBitContext *gb) * validate a Jpeg XL Extra Channel Info bundle * @return >= 0 upon valid, < 0 upon invalid */ -static int jpegxl_read_extra_channel_info(GetBitContext *gb) +static int jpegxl_read_extra_channel_info(GetBitContext *gb, int validate_level) { - int all_default = jxl_bits(1); + int all_default = get_bits1(gb); uint32_t type, name_len = 0; if (!all_default) { - type = jxl_enum(); + type = jxl_enum(gb); if (type > 63) return -1; /* enum types cannot be 64+ */ - if (type == FF_JPEGXL_CT_BLACK) + if (type == FF_JPEGXL_CT_BLACK && validate_level) return -1; jpegxl_skip_bit_depth(gb); - jxl_u32(0, 3, 4, 1, 0, 0, 0, 3); /* dim-shift */ + jxl_u32(gb, 0, 3, 4, 1, 0, 0, 0, 3); /* dim-shift */ /* max of name_len is 1071 = 48 + 2^10 - 1 */ - name_len = jxl_u32(0, 0, 16, 48, 0, 4, 5, 10); + name_len = jxl_u32(gb, 0, 0, 16, 48, 0, 4, 5, 10); } else { type = FF_JPEGXL_CT_ALPHA; } /* skip over the name */ - jxl_bits_skip(8 * name_len); + skip_bits_long(gb, 8 * name_len); if (!all_default && type == FF_JPEGXL_CT_ALPHA) - jxl_bits_skip(1); + skip_bits1(gb); if (type == FF_JPEGXL_CT_SPOT_COLOR) - jxl_bits_skip(16 * 4); + skip_bits_long(gb, 16 * 4); if (type == FF_JPEGXL_CT_CFA) - jxl_u32(1, 0, 3, 19, 0, 2, 4, 8); + jxl_u32(gb, 1, 0, 3, 19, 0, 2, 4, 8); return 0; } -/* verify that a codestream header is valid */ -int ff_jpegxl_verify_codestream_header(const uint8_t *buf, int buflen) +int ff_jpegxl_verify_codestream_header(const uint8_t *buf, int buflen, int validate_level) { GetBitContext gbi, *gb = &gbi; int all_default, extra_fields = 0; int xyb_encoded = 1, have_icc_profile = 0; + int animation_offset = 0; uint32_t num_extra_channels; uint64_t extensions; int ret; @@ -256,141 +258,155 @@ int ff_jpegxl_verify_codestream_header(const uint8_t *buf, int buflen) if (ret < 0) return ret; - if (jxl_bits(16) != FF_JPEGXL_CODESTREAM_SIGNATURE_LE) + if (get_bits_long(gb, 16) != FF_JPEGXL_CODESTREAM_SIGNATURE_LE) return -1; - if (jpegxl_read_size_header(gb) < 0) - return -1; + if ((ret = jpegxl_read_size_header(gb)) < 0 && validate_level) + return ret; - all_default = jxl_bits(1); + all_default = get_bits1(gb); if (!all_default) - extra_fields = jxl_bits(1); + extra_fields = get_bits1(gb); if (extra_fields) { - jxl_bits_skip(3); /* orientation */ + skip_bits_long(gb, 3); /* orientation */ /* * intrinstic size * any size header here is valid, but as it * is variable length we have to read it */ - if (jxl_bits(1)) + if (get_bits1(gb)) jpegxl_read_size_header(gb); /* preview header */ - if (jxl_bits(1)) { - if (jpegxl_read_preview_header(gb) < 0) - return -1; + if (get_bits1(gb)) { + ret = jpegxl_read_preview_header(gb); + if (ret < 0) + return ret; } /* animation header */ - if (jxl_bits(1)) { - jxl_u32(100, 1000, 1, 1, 0, 0, 10, 30); - jxl_u32(1, 1001, 1, 1, 0, 0, 8, 10); - jxl_u32(0, 0, 0, 0, 0, 3, 16, 32); - jxl_bits_skip(1); + if (get_bits1(gb)) { + animation_offset = get_bits_count(gb); + jxl_u32(gb, 100, 1000, 1, 1, 0, 0, 10, 30); + jxl_u32(gb, 1, 1001, 1, 1, 0, 0, 8, 10); + jxl_u32(gb, 0, 0, 0, 0, 0, 3, 16, 32); + skip_bits_long(gb, 1); } } + if (get_bits_left(gb) < 1) + return AVERROR_INVALIDDATA; if (!all_default) { jpegxl_skip_bit_depth(gb); /* modular_16bit_buffers must equal 1 */ - if (!jxl_bits(1)) + if (!get_bits1(gb) && validate_level) return -1; - num_extra_channels = jxl_u32(0, 1, 2, 1, 0, 0, 4, 12); - if (num_extra_channels > 4) + num_extra_channels = jxl_u32(gb, 0, 1, 2, 1, 0, 0, 4, 12); + if (num_extra_channels > 4 && validate_level) return -1; for (uint32_t i = 0; i < num_extra_channels; i++) { - if (jpegxl_read_extra_channel_info(gb) < 0) - return -1; + ret = jpegxl_read_extra_channel_info(gb, validate_level); + if (ret < 0) + return ret; + if (get_bits_left(gb) < 1) + return AVERROR_INVALIDDATA; } - xyb_encoded = jxl_bits(1); + xyb_encoded = get_bits1(gb); /* color encoding bundle */ - if (!jxl_bits(1)) { + if (!get_bits1(gb)) { uint32_t color_space; - have_icc_profile = jxl_bits(1); - color_space = jxl_enum(); + have_icc_profile = get_bits1(gb); + color_space = jxl_enum(gb); if (color_space > 63) return -1; if (!have_icc_profile) { if (color_space != FF_JPEGXL_CS_XYB) { - uint32_t white_point = jxl_enum(); + uint32_t white_point = jxl_enum(gb); if (white_point > 63) return -1; if (white_point == FF_JPEGXL_WP_CUSTOM) { /* ux and uy values */ - jxl_u32(0, 524288, 1048576, 2097152, 19, 19, 20, 21); - jxl_u32(0, 524288, 1048576, 2097152, 19, 19, 20, 21); + jxl_u32(gb, 0, 524288, 1048576, 2097152, 19, 19, 20, 21); + jxl_u32(gb, 0, 524288, 1048576, 2097152, 19, 19, 20, 21); } if (color_space != FF_JPEGXL_CS_GRAY) { /* primaries */ - uint32_t primaries = jxl_enum(); + uint32_t primaries = jxl_enum(gb); if (primaries > 63) return -1; if (primaries == FF_JPEGXL_PR_CUSTOM) { /* ux/uy values for r,g,b */ - for (int i = 0; i < 6; i++) - jxl_u32(0, 524288, 1048576, 2097152, 19, 19, 20, 21); + for (int i = 0; i < 6; i++) { + jxl_u32(gb, 0, 524288, 1048576, 2097152, 19, 19, 20, 21); + if (get_bits_left(gb) < 1) + return AVERROR_INVALIDDATA; + } } } } /* transfer characteristics */ - if (jxl_bits(1)) { + if (get_bits1(gb)) { /* gamma */ - jxl_bits_skip(24); + skip_bits_long(gb, 24); } else { /* transfer function */ - if (jxl_enum() > 63) + if (jxl_enum(gb) > 63) return -1; } /* rendering intent */ - if (jxl_enum() > 63) + if (jxl_enum(gb) > 63) return -1; } } /* tone mapping bundle */ - if (extra_fields && !jxl_bits(1)) - jxl_bits_skip(16 + 16 + 1 + 16); + if (extra_fields && !get_bits1(gb)) + skip_bits_long(gb, 16 + 16 + 1 + 16); - extensions = jxl_u64(); + extensions = jpegxl_u64(gb); + if (get_bits_left(gb) < 1) + return AVERROR_INVALIDDATA; if (extensions) { for (int i = 0; i < 64; i++) { if (extensions & (UINT64_C(1) << i)) - jxl_u64(); + jpegxl_u64(gb); + if (get_bits_left(gb) < 1) + return AVERROR_INVALIDDATA; } } } /* default transform */ - if (!jxl_bits(1)) { + if (!get_bits1(gb)) { /* opsin inverse matrix */ - if (xyb_encoded && !jxl_bits(1)) - jxl_bits_skip(16 * 16); + if (xyb_encoded && !get_bits1(gb)) + skip_bits_long(gb, 16 * 16); /* cw_mask and default weights */ - if (jxl_bits(1)) - jxl_bits_skip(16 * 15); - if (jxl_bits(1)) - jxl_bits_skip(16 * 55); - if (jxl_bits(1)) - jxl_bits_skip(16 * 210); + if (get_bits1(gb)) + skip_bits_long(gb, 16 * 15); + if (get_bits1(gb)) + skip_bits_long(gb, 16 * 55); + if (get_bits1(gb)) + skip_bits_long(gb, 16 * 210); } if (!have_icc_profile) { int bits_remaining = 7 - (get_bits_count(gb) - 1) % 8; - if (bits_remaining && jxl_bits(bits_remaining)) + if (bits_remaining && get_bits(gb, bits_remaining)) return -1; } if (get_bits_left(gb) < 0) return -1; - return 0; + return animation_offset; } diff --git a/libavformat/jpegxl_probe.h b/libavformat/jpegxl_probe.h index 2960e81e11c..496445fbce2 100644 --- a/libavformat/jpegxl_probe.h +++ b/libavformat/jpegxl_probe.h @@ -27,6 +27,11 @@ #define FF_JPEGXL_CODESTREAM_SIGNATURE_LE 0x0aff #define FF_JPEGXL_CONTAINER_SIGNATURE_LE 0x204c584a0c000000 -int ff_jpegxl_verify_codestream_header(const uint8_t *buf, int buflen); +/** + * @brief verify that a codestream header is valid + * @return Negative upon error, 0 upon verifying that the codestream is not animated, + * and 1 upon verifying that it is animated + */ +int ff_jpegxl_verify_codestream_header(const uint8_t *buf, int buflen, int validate_level); #endif /* AVFORMAT_JPEGXL_PROBE_H */ diff --git a/libavformat/kvag.c b/libavformat/kvag.c index 7c3816ab828..9487e7dd0eb 100644 --- a/libavformat/kvag.c +++ b/libavformat/kvag.c @@ -26,6 +26,7 @@ #include "avformat.h" #include "avio_internal.h" #include "internal.h" +#include "mux.h" #include "rawenc.h" #include "libavutil/intreadwrite.h" @@ -188,12 +189,12 @@ static int kvag_write_trailer(AVFormatContext *s) return 0; } -const AVOutputFormat ff_kvag_muxer = { - .name = "kvag", - .long_name = NULL_IF_CONFIG_SMALL("Simon & Schuster Interactive VAG"), - .extensions = "vag", - .audio_codec = AV_CODEC_ID_ADPCM_IMA_SSI, - .video_codec = AV_CODEC_ID_NONE, +const FFOutputFormat ff_kvag_muxer = { + .p.name = "kvag", + .p.long_name = NULL_IF_CONFIG_SMALL("Simon & Schuster Interactive VAG"), + .p.extensions = "vag", + .p.audio_codec = AV_CODEC_ID_ADPCM_IMA_SSI, + .p.video_codec = AV_CODEC_ID_NONE, .init = kvag_write_init, .write_header = kvag_write_header, .write_packet = ff_raw_write_packet, diff --git a/libavformat/lafdec.c b/libavformat/lafdec.c index d02b479c4d1..59a59dcfe9c 100644 --- a/libavformat/lafdec.c +++ b/libavformat/lafdec.c @@ -21,6 +21,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "avio_internal.h" #include "internal.h" #define MAX_STREAMS 4096 @@ -131,6 +132,8 @@ static int laf_read_header(AVFormatContext *ctx) codec_id = AV_CODEC_ID_PCM_S24LE; bpp = 3; break; + default: + return AVERROR_INVALIDDATA; } s->index = 0; @@ -184,7 +187,9 @@ static int laf_read_packet(AVFormatContext *ctx, AVPacket *pkt) if (s->index >= ctx->nb_streams) { int cur_st = 0, st_count = 0, st_index = 0; - avio_read(pb, s->header, s->header_len); + ret = ffio_read_size(pb, s->header, s->header_len); + if (ret < 0) + return ret; for (int i = 0; i < s->header_len; i++) { uint8_t val = s->header[i]; @@ -205,7 +210,7 @@ static int laf_read_packet(AVFormatContext *ctx, AVPacket *pkt) s->nb_stored = st_count; if (!st_count) return AVERROR_INVALIDDATA; - ret = avio_read(pb, s->data, st_count * st->codecpar->sample_rate * bpp); + ret = ffio_read_size(pb, s->data, st_count * st->codecpar->sample_rate * bpp); if (ret < 0) return ret; } @@ -251,6 +256,15 @@ static int laf_read_packet(AVFormatContext *ctx, AVPacket *pkt) return 0; } +static int laf_read_close(AVFormatContext *ctx) +{ + LAFContext *s = ctx->priv_data; + + av_freep(&s->data); + + return 0; +} + static int laf_read_seek(AVFormatContext *ctx, int stream_index, int64_t timestamp, int flags) { @@ -268,7 +282,9 @@ const AVInputFormat ff_laf_demuxer = { .read_probe = laf_probe, .read_header = laf_read_header, .read_packet = laf_read_packet, + .read_close = laf_read_close, .read_seek = laf_read_seek, .extensions = "laf", .flags = AVFMT_GENERIC_INDEX, + .flags_internal = FF_FMT_INIT_CLEANUP, }; diff --git a/libavformat/latmenc.c b/libavformat/latmenc.c index 6fd36d1484f..8eec632c546 100644 --- a/libavformat/latmenc.c +++ b/libavformat/latmenc.c @@ -260,17 +260,17 @@ static int latm_check_bitstream(AVFormatContext *s, AVStream *st, return ret; } -const AVOutputFormat ff_latm_muxer = { - .name = "latm", - .long_name = NULL_IF_CONFIG_SMALL("LOAS/LATM"), - .mime_type = "audio/MP4A-LATM", - .extensions = "latm,loas", +const FFOutputFormat ff_latm_muxer = { + .p.name = "latm", + .p.long_name = NULL_IF_CONFIG_SMALL("LOAS/LATM"), + .p.mime_type = "audio/MP4A-LATM", + .p.extensions = "latm,loas", .priv_data_size = sizeof(LATMContext), - .audio_codec = AV_CODEC_ID_AAC, - .video_codec = AV_CODEC_ID_NONE, + .p.audio_codec = AV_CODEC_ID_AAC, + .p.video_codec = AV_CODEC_ID_NONE, .write_header = latm_write_header, .write_packet = latm_write_packet, - .priv_class = &latm_muxer_class, + .p.priv_class = &latm_muxer_class, .check_bitstream= latm_check_bitstream, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; diff --git a/libavformat/lrcdec.c b/libavformat/lrcdec.c index fff39495f87..f43e9dccf53 100644 --- a/libavformat/lrcdec.c +++ b/libavformat/lrcdec.c @@ -170,8 +170,11 @@ static int lrc_read_header(AVFormatContext *s) av_bprint_init(&line, 0, AV_BPRINT_SIZE_UNLIMITED); while(!avio_feof(s->pb)) { - int64_t pos = read_line(&line, s->pb); - int64_t header_offset = find_header(line.str); + int64_t header_offset, pos = read_line(&line, s->pb); + + if (!av_bprint_is_complete(&line)) + goto err_nomem_out; + header_offset = find_header(line.str); if(header_offset >= 0) { char *comma_offset = strchr(line.str, ':'); if(comma_offset) { @@ -205,7 +208,7 @@ static int lrc_read_header(AVFormatContext *s) sub = ff_subtitles_queue_insert(&lrc->q, line.str + ts_strlength, line.len - ts_strlength, 0); if (!sub) - return AVERROR(ENOMEM); + goto err_nomem_out; sub->pos = pos; sub->pts = ts_start - lrc->ts_offset; sub->duration = -1; @@ -216,6 +219,9 @@ static int lrc_read_header(AVFormatContext *s) ff_metadata_conv_ctx(s, NULL, ff_lrc_metadata_conv); av_bprint_finalize(&line, NULL); return 0; +err_nomem_out: + av_bprint_finalize(&line, NULL); + return AVERROR(ENOMEM); } const AVInputFormat ff_lrc_demuxer = { diff --git a/libavformat/lrcenc.c b/libavformat/lrcenc.c index 2d6ca33e380..d66be9a8faa 100644 --- a/libavformat/lrcenc.c +++ b/libavformat/lrcenc.c @@ -125,14 +125,14 @@ static int lrc_write_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -const AVOutputFormat ff_lrc_muxer = { - .name = "lrc", - .long_name = NULL_IF_CONFIG_SMALL("LRC lyrics"), - .extensions = "lrc", +const FFOutputFormat ff_lrc_muxer = { + .p.name = "lrc", + .p.long_name = NULL_IF_CONFIG_SMALL("LRC lyrics"), + .p.extensions = "lrc", + .p.flags = AVFMT_VARIABLE_FPS | AVFMT_GLOBALHEADER | + AVFMT_TS_NEGATIVE | AVFMT_TS_NONSTRICT, + .p.subtitle_codec = AV_CODEC_ID_SUBRIP, .priv_data_size = 0, .write_header = lrc_write_header, .write_packet = lrc_write_packet, - .flags = AVFMT_VARIABLE_FPS | AVFMT_GLOBALHEADER | - AVFMT_TS_NEGATIVE | AVFMT_TS_NONSTRICT, - .subtitle_codec = AV_CODEC_ID_SUBRIP }; diff --git a/libavformat/matroska.c b/libavformat/matroska.c index 90d94b65bf7..79b2d099841 100644 --- a/libavformat/matroska.c +++ b/libavformat/matroska.c @@ -76,6 +76,7 @@ const CodecTags ff_mkv_codec_tags[]={ {"S_DVBSUB" , AV_CODEC_ID_DVB_SUBTITLE}, {"S_HDMV/PGS" , AV_CODEC_ID_HDMV_PGS_SUBTITLE}, {"S_HDMV/TEXTST" , AV_CODEC_ID_HDMV_TEXT_SUBTITLE}, + {"S_ARIBSUB" , AV_CODEC_ID_ARIB_CAPTION}, {"V_AV1" , AV_CODEC_ID_AV1}, {"V_AVS2" , AV_CODEC_ID_AVS2}, diff --git a/libavformat/matroska.h b/libavformat/matroska.h index 45077ed33f9..ead7c085b9a 100644 --- a/libavformat/matroska.h +++ b/libavformat/matroska.h @@ -358,6 +358,17 @@ typedef enum { MATROSKA_VIDEO_PROJECTION_TYPE_MESH = 3, } MatroskaVideoProjectionType; +typedef enum { + MATROSKA_BLOCK_ADD_ID_TYPE_DEFAULT = 0, + MATROSKA_BLOCK_ADD_ID_TYPE_OPAQUE = 1, + MATROSKA_BLOCK_ADD_ID_TYPE_ITU_T_T35 = 4, + MATROSKA_BLOCK_ADD_ID_TYPE_DVCC = 0x64766343, // MKBETAG('d','v','c','C') + MATROSKA_BLOCK_ADD_ID_TYPE_DVVC = 0x64767643, // MKBETAG('d','v','v','C') +} MatroskaBlockAddIDType; + +#define MATROSKA_BLOCK_ADD_ID_OPAQUE 1 +#define MATROSKA_BLOCK_ADD_ID_ITU_T_T35 4 + /* * Matroska Codec IDs, strings */ diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c index d582f566a21..49950956b63 100644 --- a/libavformat/matroskadec.c +++ b/libavformat/matroskadec.c @@ -40,6 +40,7 @@ #include "libavutil/dict.h" #include "libavutil/dict_internal.h" #include "libavutil/display.h" +#include "libavutil/hdr_dynamic_metadata.h" #include "libavutil/intfloat.h" #include "libavutil/intreadwrite.h" #include "libavutil/lzo.h" @@ -50,6 +51,7 @@ #include "libavutil/time_internal.h" #include "libavutil/spherical.h" +#include "libavcodec/avcodec.h" #include "libavcodec/bytestream.h" #include "libavcodec/flac.h" #include "libavcodec/mpeg4audio.h" @@ -348,13 +350,17 @@ typedef struct MatroskaLevel { uint64_t length; } MatroskaLevel; +typedef struct MatroskaBlockMore { + uint64_t additional_id; + EbmlBin additional; +} MatroskaBlockMore; + typedef struct MatroskaBlock { uint64_t duration; CountedElement reference; uint64_t non_simple; EbmlBin bin; - uint64_t additional_id; - EbmlBin additional; + EbmlList blockmore; int64_t discard_padding; } MatroskaBlock; @@ -418,6 +424,8 @@ typedef struct MatroskaDemuxContext { MatroskaCluster current_cluster; + int is_webm; + /* WebM DASH Manifest live flag */ int is_live; @@ -586,7 +594,7 @@ static EbmlSyntax matroska_track_operation[] = { static EbmlSyntax matroska_block_addition_mapping[] = { { MATROSKA_ID_BLKADDIDVALUE, EBML_UINT, 0, 0, offsetof(MatroskaBlockAdditionMapping, value) }, { MATROSKA_ID_BLKADDIDNAME, EBML_STR, 0, 0, offsetof(MatroskaBlockAdditionMapping, name) }, - { MATROSKA_ID_BLKADDIDTYPE, EBML_UINT, 0, 0, offsetof(MatroskaBlockAdditionMapping, type) }, + { MATROSKA_ID_BLKADDIDTYPE, EBML_UINT, 0, 0, offsetof(MatroskaBlockAdditionMapping, type), { .u = MATROSKA_BLOCK_ADD_ID_TYPE_DEFAULT } }, { MATROSKA_ID_BLKADDIDEXTRADATA, EBML_BIN, 0, 0, offsetof(MatroskaBlockAdditionMapping, extradata) }, CHILD_OF(matroska_track) }; @@ -758,13 +766,13 @@ static EbmlSyntax matroska_segments[] = { }; static EbmlSyntax matroska_blockmore[] = { - { MATROSKA_ID_BLOCKADDID, EBML_UINT, 0, 0, offsetof(MatroskaBlock,additional_id), { .u = 1 } }, - { MATROSKA_ID_BLOCKADDITIONAL, EBML_BIN, 0, 0, offsetof(MatroskaBlock,additional) }, + { MATROSKA_ID_BLOCKADDID, EBML_UINT, 0, 0, offsetof(MatroskaBlockMore,additional_id), { .u = MATROSKA_BLOCK_ADD_ID_OPAQUE } }, + { MATROSKA_ID_BLOCKADDITIONAL, EBML_BIN, 0, 0, offsetof(MatroskaBlockMore,additional) }, CHILD_OF(matroska_blockadditions) }; static EbmlSyntax matroska_blockadditions[] = { - { MATROSKA_ID_BLOCKMORE, EBML_NEST, 0, 0, 0, {.n = matroska_blockmore} }, + { MATROSKA_ID_BLOCKMORE, EBML_NEST, 0, sizeof(MatroskaBlockMore), offsetof(MatroskaBlock, blockmore), { .n = matroska_blockmore } }, CHILD_OF(matroska_blockgroup) }; @@ -2373,7 +2381,7 @@ static int mkv_parse_dvcc_dvvc(AVFormatContext *s, AVStream *st, const MatroskaT return ff_isom_parse_dvcc_dvvc(s, st, bin->data, bin->size); } -static int mkv_parse_block_addition_mappings(AVFormatContext *s, AVStream *st, const MatroskaTrack *track) +static int mkv_parse_block_addition_mappings(AVFormatContext *s, AVStream *st, MatroskaTrack *track) { const EbmlList *mappings_list = &track->block_addition_mappings; MatroskaBlockAdditionMapping *mappings = mappings_list->elem; @@ -2381,18 +2389,47 @@ static int mkv_parse_block_addition_mappings(AVFormatContext *s, AVStream *st, c for (int i = 0; i < mappings_list->nb_elem; i++) { MatroskaBlockAdditionMapping *mapping = &mappings[i]; + uint64_t type = mapping->type; switch (mapping->type) { - case MKBETAG('d','v','c','C'): - case MKBETAG('d','v','v','C'): + case MATROSKA_BLOCK_ADD_ID_TYPE_DEFAULT: + av_log(s, AV_LOG_DEBUG, + "Explicit block Addition Mapping type \"Use BlockAddIDValue\", value %"PRIu64"," + " name \"%s\" found.\n", mapping->value, mapping->name ? mapping->name : ""); + type = MATROSKA_BLOCK_ADD_ID_TYPE_OPAQUE; + // fall-through + case MATROSKA_BLOCK_ADD_ID_TYPE_OPAQUE: + case MATROSKA_BLOCK_ADD_ID_TYPE_ITU_T_T35: + if (mapping->value != type) { + int strict = s->strict_std_compliance >= FF_COMPLIANCE_STRICT; + av_log(s, strict ? AV_LOG_ERROR : AV_LOG_WARNING, + "Invalid Block Addition Value 0x%"PRIx64" for Block Addition Mapping Type " + "0x%"PRIx64", name \"%s\"\n", mapping->value, mapping->type, + mapping->name ? mapping->name : ""); + if (strict) + return AVERROR_INVALIDDATA; + } + break; + case MATROSKA_BLOCK_ADD_ID_TYPE_DVCC: + case MATROSKA_BLOCK_ADD_ID_TYPE_DVVC: if ((ret = mkv_parse_dvcc_dvvc(s, st, track, &mapping->extradata)) < 0) return ret; break; default: av_log(s, AV_LOG_DEBUG, - "Unknown block additional mapping type 0x%"PRIx64", value %"PRIu64", name \"%s\"\n", + "Unknown Block Addition Mapping type 0x%"PRIx64", value %"PRIu64", name \"%s\"\n", mapping->type, mapping->value, mapping->name ? mapping->name : ""); + if (mapping->value < 2) { + int strict = s->strict_std_compliance >= FF_COMPLIANCE_STRICT; + av_log(s, strict ? AV_LOG_ERROR : AV_LOG_WARNING, + "Invalid Block Addition value 0x%"PRIu64" for unknown Block Addition Mapping " + "type %"PRIx64", name \"%s\"\n", mapping->value, mapping->type, + mapping->name ? mapping->name : ""); + if (strict) + return AVERROR_INVALIDDATA; + } + break; } } @@ -2809,10 +2846,39 @@ static int matroska_parse_tracks(AVFormatContext *s) AV_WL16(extradata, 0x410); } else if (codec_id == AV_CODEC_ID_PRORES && track->codec_priv.size == 4) { fourcc = AV_RL32(track->codec_priv.data); - } else if (codec_id == AV_CODEC_ID_VP9 && track->codec_priv.size) { + } else if (codec_id == AV_CODEC_ID_VP9) { /* we don't need any value stored in CodecPrivate. make sure that it's not exported as extradata. */ track->codec_priv.size = 0; + } else if (codec_id == AV_CODEC_ID_ARIB_CAPTION && track->codec_priv.size == 3) { + int component_tag = track->codec_priv.data[0]; + int data_component_id = AV_RB16(track->codec_priv.data + 1); + + switch (data_component_id) { + case 0x0008: + // [0x30..0x37] are component tags utilized for + // non-mobile captioning service ("profile A"). + if (component_tag >= 0x30 && component_tag <= 0x37) { + st->codecpar->profile = FF_PROFILE_ARIB_PROFILE_A; + } + break; + case 0x0012: + // component tag 0x87 signifies a mobile/partial reception + // (1seg) captioning service ("profile C"). + if (component_tag == 0x87) { + st->codecpar->profile = FF_PROFILE_ARIB_PROFILE_C; + } + break; + default: + break; + } + + if (st->codecpar->profile == FF_PROFILE_UNKNOWN) + av_log(matroska->ctx, AV_LOG_WARNING, + "Unknown ARIB caption profile utilized: %02x / %04x\n", + component_tag, data_component_id); + + track->codec_priv.size = 0; } track->codec_priv.size -= extradata_offset; @@ -3047,6 +3113,8 @@ static int matroska_read_header(AVFormatContext *s) return AVERROR_INVALIDDATA; } } + matroska->is_webm = !strcmp(ebml.doctype, "webm"); + ebml_free(ebml_syntax, &ebml); matroska->pkt = si->parse_pkt; @@ -3580,12 +3648,101 @@ static int matroska_parse_webvtt(MatroskaDemuxContext *matroska, return 0; } +static int matroska_parse_block_additional(MatroskaDemuxContext *matroska, + MatroskaTrack *track, AVPacket *pkt, + const uint8_t *data, int size, uint64_t id) +{ + const EbmlList *mappings_list = &track->block_addition_mappings; + MatroskaBlockAdditionMapping *mappings = mappings_list->elem, *mapping = NULL; + uint8_t *side_data; + int res; + + if (!matroska->is_webm && track->max_block_additional_id && id > track->max_block_additional_id) { + int strict = matroska->ctx->strict_std_compliance >= FF_COMPLIANCE_STRICT; + av_log(matroska->ctx, strict ? AV_LOG_ERROR : AV_LOG_WARNING, + "BlockAddID %"PRIu64" is higher than the reported MaxBlockAdditionID %"PRIu64" " + "for Track with TrackNumber %"PRIu64"\n", id, track->max_block_additional_id, + track->num); + if (strict) + return AVERROR_INVALIDDATA; + } + + for (int i = 0; i < mappings_list->nb_elem; i++) { + if (id != mappings[i].value) + continue; + mapping = &mappings[i]; + break; + } + + if (id != 1 && !matroska->is_webm && !mapping) { + av_log(matroska->ctx, AV_LOG_WARNING, "BlockAddID %"PRIu64" has no mapping. Skipping\n", id); + return 0; + } + + if (mapping && mapping->type) + id = mapping->type; + + switch (id) { + case MATROSKA_BLOCK_ADD_ID_TYPE_ITU_T_T35: { + GetByteContext bc; + int country_code, provider_code; + int provider_oriented_code, application_identifier; + size_t hdrplus_size; + AVDynamicHDRPlus *hdrplus; + + if (size < 6) + break; //ignore + + bytestream2_init(&bc, data, size); + + /* ITU-T T.35 metadata */ + country_code = bytestream2_get_byteu(&bc); + provider_code = bytestream2_get_be16u(&bc); + + if (country_code != 0xB5 || provider_code != 0x3C) + break; // ignore + + provider_oriented_code = bytestream2_get_be16u(&bc); + application_identifier = bytestream2_get_byteu(&bc); + + if (provider_oriented_code != 1 || application_identifier != 4) + break; // ignore + + hdrplus = av_dynamic_hdr_plus_alloc(&hdrplus_size); + if (!hdrplus) + return AVERROR(ENOMEM); + + if ((res = av_dynamic_hdr_plus_from_t35(hdrplus, bc.buffer, + bytestream2_get_bytes_left(&bc))) < 0 || + (res = av_packet_add_side_data(pkt, AV_PKT_DATA_DYNAMIC_HDR10_PLUS, + (uint8_t *)hdrplus, hdrplus_size)) < 0) { + av_free(hdrplus); + return res; + } + + return 0; + } + default: + break; + } + + side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL, + size + (size_t)8); + if (!side_data) + return AVERROR(ENOMEM); + + AV_WB64(side_data, id); + memcpy(side_data + 8, data, size); + + return 0; +} + static int matroska_parse_frame(MatroskaDemuxContext *matroska, MatroskaTrack *track, AVStream *st, AVBufferRef *buf, uint8_t *data, int pkt_size, uint64_t timecode, uint64_t lace_duration, int64_t pos, int is_keyframe, - uint8_t *additional, uint64_t additional_id, int additional_size, + MatroskaBlockMore *blockmore, int nb_blockmore, int64_t discard_padding) { uint8_t *pkt_data = data; @@ -3617,9 +3774,20 @@ static int matroska_parse_frame(MatroskaDemuxContext *matroska, buf = NULL; } - if (!pkt_size && !additional_size) + if (!pkt_size && !nb_blockmore) goto no_output; + if (!matroska->is_webm && nb_blockmore && !track->max_block_additional_id) { + int strict = matroska->ctx->strict_std_compliance >= FF_COMPLIANCE_STRICT; + av_log(matroska->ctx, strict ? AV_LOG_ERROR : AV_LOG_WARNING, + "Unexpected BlockAdditions found in a Block from Track with TrackNumber %"PRIu64" " + "where MaxBlockAdditionID is 0\n", track->num); + if (strict) { + res = AVERROR_INVALIDDATA; + goto fail; + } + } + if (!buf) pkt->buf = av_buffer_create(pkt_data, pkt_size + AV_INPUT_BUFFER_PADDING_SIZE, NULL, NULL, 0); @@ -3636,16 +3804,18 @@ static int matroska_parse_frame(MatroskaDemuxContext *matroska, pkt->flags = is_keyframe; pkt->stream_index = st->index; - if (additional_size > 0) { - uint8_t *side_data = av_packet_new_side_data(pkt, - AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL, - additional_size + 8); - if (!side_data) { + for (int i = 0; i < nb_blockmore; i++) { + MatroskaBlockMore *more = &blockmore[i]; + + if (!more->additional.size) + continue; + + res = matroska_parse_block_additional(matroska, track, pkt, more->additional.data, + more->additional.size, more->additional_id); + if (res < 0) { av_packet_unref(pkt); - return AVERROR(ENOMEM); + return res; } - AV_WB64(side_data, additional_id); - memcpy(side_data + 8, additional, additional_size); } if (discard_padding) { @@ -3691,7 +3861,7 @@ static int matroska_parse_frame(MatroskaDemuxContext *matroska, static int matroska_parse_block(MatroskaDemuxContext *matroska, AVBufferRef *buf, uint8_t *data, int size, int64_t pos, uint64_t cluster_time, uint64_t block_duration, int is_keyframe, - uint8_t *additional, uint64_t additional_id, int additional_size, + MatroskaBlockMore *blockmore, int nb_blockmore, int64_t cluster_pos, int64_t discard_padding) { uint64_t timecode = AV_NOPTS_VALUE; @@ -3826,7 +3996,7 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, AVBufferRef *buf res = matroska_parse_frame(matroska, track, st, buf, out_data, out_size, timecode, lace_duration, pos, !n ? is_keyframe : 0, - additional, additional_id, additional_size, + blockmore, nb_blockmore, discard_padding); if (res) return res; @@ -3867,14 +4037,12 @@ static int matroska_parse_cluster(MatroskaDemuxContext *matroska) if (res >= 0 && block->bin.size > 0) { int is_keyframe = block->non_simple ? block->reference.count == 0 : -1; - uint8_t* additional = block->additional.size > 0 ? - block->additional.data : NULL; res = matroska_parse_block(matroska, block->bin.buf, block->bin.data, block->bin.size, block->bin.pos, cluster->timecode, block->duration, - is_keyframe, additional, block->additional_id, - block->additional.size, cluster->pos, + is_keyframe, block->blockmore.elem, + block->blockmore.nb_elem, cluster->pos, block->discard_padding); } diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c index 2deb4284e84..e813ef86cf7 100644 --- a/libavformat/matroskaenc.c +++ b/libavformat/matroskaenc.c @@ -44,6 +44,7 @@ #include "libavutil/channel_layout.h" #include "libavutil/crc.h" #include "libavutil/dict.h" +#include "libavutil/hdr_dynamic_metadata.h" #include "libavutil/intfloat.h" #include "libavutil/intreadwrite.h" #include "libavutil/lfg.h" @@ -58,6 +59,8 @@ #include "libavutil/stereo3d.h" #include "libavcodec/av1.h" +#include "libavcodec/avcodec.h" +#include "libavcodec/bytestream.h" #include "libavcodec/codec_desc.h" #include "libavcodec/xiph.h" #include "libavcodec/mpeg4audio.h" @@ -187,6 +190,8 @@ typedef struct mkv_track { int64_t last_timestamp; int64_t duration; int64_t duration_offset; + uint64_t max_blockaddid; + int64_t blockadditionmapping_offset; int codecpriv_offset; unsigned codecpriv_size; ///< size reserved for CodecPrivate excluding header+length field int64_t ts_offset; @@ -259,8 +264,12 @@ typedef struct MatroskaMuxContext { /** 4 * (1-byte EBML ID, 1-byte EBML size, 8-byte uint max) */ #define MAX_CUETRACKPOS_SIZE 40 -/** 2 + 1 Simpletag header, 2 + 1 + 8 Name "DURATION", 23B for TagString */ -#define DURATION_SIMPLETAG_SIZE (2 + 1 + (2 + 1 + 8) + 23) +/** DURATION_STRING_LENGTH must be <= 112 or the containing + * simpletag will need more than one byte for its length field. */ +#define DURATION_STRING_LENGTH 19 + +/** 2 + 1 Simpletag header, 2 + 1 + 8 Name "DURATION", rest for TagString */ +#define DURATION_SIMPLETAG_SIZE (2 + 1 + (2 + 1 + 8) + (2 + 1 + DURATION_STRING_LENGTH)) /** Seek preroll value for opus */ #define OPUS_SEEK_PREROLL 80000000 @@ -1142,6 +1151,27 @@ static int mkv_assemble_native_codecprivate(AVFormatContext *s, AVIOContext *dyn else *size_to_reserve = MAX_PCE_SIZE; break; + case AV_CODEC_ID_ARIB_CAPTION: { + unsigned stream_identifier, data_component_id; + switch (par->profile) { + case FF_PROFILE_ARIB_PROFILE_A: + stream_identifier = 0x30; + data_component_id = 0x0008; + break; + case FF_PROFILE_ARIB_PROFILE_C: + stream_identifier = 0x87; + data_component_id = 0x0012; + break; + default: + av_log(s, AV_LOG_ERROR, + "Unset/unknown ARIB caption profile %d utilized!\n", + par->profile); + return AVERROR_INVALIDDATA; + } + avio_w8(dyn_cp, stream_identifier); + avio_wb16(dyn_cp, data_component_id); + break; + } #endif default: if (CONFIG_MATROSKA_MUXER && par->codec_id == AV_CODEC_ID_PRORES && @@ -1377,25 +1407,75 @@ static void mkv_write_video_color(EbmlWriter *writer, const AVStream *st, } #define MAX_VIDEO_PROJECTION_ELEMS 6 -static void mkv_write_video_projection(AVFormatContext *s, EbmlWriter *writer, - const AVStream *st, uint8_t private[]) +static void mkv_handle_rotation(void *logctx, const AVStream *st, + double *yaw, double *roll) +{ + const int32_t *matrix = + (const int32_t*)av_stream_get_side_data(st, AV_PKT_DATA_DISPLAYMATRIX, NULL); + + if (!matrix) + return; + + /* Check whether this is an affine transformation */ + if (matrix[2] || matrix[5]) + goto ignore; + + /* This together with the checks below test whether + * the upper-left 2x2 matrix is nonsingular. */ + if (!matrix[0] && !matrix[1]) + goto ignore; + + /* We ignore the translation part of the matrix (matrix[6] and matrix[7]) + * as well as any scaling, i.e. we only look at the upper left 2x2 matrix. + * We only accept matrices that are an exact multiple of an orthogonal one. + * Apart from the multiple, every such matrix can be obtained by + * potentially flipping in the x-direction (corresponding to yaw = 180) + * followed by a rotation of (say) an angle phi in the counterclockwise + * direction. The upper-left 2x2 matrix then looks like this: + * | (+/-)cos(phi) (-/+)sin(phi) | + * scale * | | + * | sin(phi) cos(phi) | + * The first set of signs in the first row apply in case of no flipping, + * the second set applies in case of flipping. */ + + /* The casts to int64_t are needed because -INT32_MIN doesn't fit + * in an int32_t. */ + if (matrix[0] == matrix[4] && -(int64_t)matrix[1] == matrix[3]) { + /* No flipping case */ + *yaw = 0; + } else if (-(int64_t)matrix[0] == matrix[4] && matrix[1] == matrix[3]) { + /* Horizontal flip */ + *yaw = 180; + } else { +ignore: + av_log(logctx, AV_LOG_INFO, "Ignoring display matrix indicating " + "non-orthogonal transformation.\n"); + return; + } + *roll = 180 / M_PI * atan2(matrix[3], matrix[4]); + + /* We do not write a ProjectionType element indicating "rectangular", + * because this is the default value. */ +} + +static int mkv_handle_spherical(void *logctx, EbmlWriter *writer, + const AVStream *st, uint8_t private[], + double *yaw, double *pitch, double *roll) { const AVSphericalMapping *spherical = (const AVSphericalMapping *)av_stream_get_side_data(st, AV_PKT_DATA_SPHERICAL, NULL); if (!spherical) - return; + return 0; if (spherical->projection != AV_SPHERICAL_EQUIRECTANGULAR && spherical->projection != AV_SPHERICAL_EQUIRECTANGULAR_TILE && spherical->projection != AV_SPHERICAL_CUBEMAP) { - av_log(s, AV_LOG_WARNING, "Unknown projection type\n"); - return; + av_log(logctx, AV_LOG_WARNING, "Unknown projection type\n"); + return 0; } - ebml_writer_open_master(writer, MATROSKA_ID_VIDEOPROJECTION); - switch (spherical->projection) { case AV_SPHERICAL_EQUIRECTANGULAR: case AV_SPHERICAL_EQUIRECTANGULAR_TILE: @@ -1429,17 +1509,33 @@ static void mkv_write_video_projection(AVFormatContext *s, EbmlWriter *writer, av_assert0(0); } - if (spherical->yaw) - ebml_writer_add_float(writer, MATROSKA_ID_VIDEOPROJECTIONPOSEYAW, - (double) spherical->yaw / (1 << 16)); - if (spherical->pitch) - ebml_writer_add_float(writer, MATROSKA_ID_VIDEOPROJECTIONPOSEPITCH, - (double) spherical->pitch / (1 << 16)); - if (spherical->roll) - ebml_writer_add_float(writer, MATROSKA_ID_VIDEOPROJECTIONPOSEROLL, - (double) spherical->roll / (1 << 16)); + *yaw = (double) spherical->yaw / (1 << 16); + *pitch = (double) spherical->pitch / (1 << 16); + *roll = (double) spherical->roll / (1 << 16); - ebml_writer_close_master(writer); + return 1; /* Projection included */ +} + +static void mkv_write_video_projection(void *logctx, EbmlWriter *wr, + const AVStream *st, uint8_t private[]) +{ + double yaw = 0, pitch = 0, roll = 0; + int ret; + + ebml_writer_open_master(wr, MATROSKA_ID_VIDEOPROJECTION); + + ret = mkv_handle_spherical(logctx, wr, st, private, &yaw, &pitch, &roll); + if (!ret) + mkv_handle_rotation(logctx, st, &yaw, &roll); + + if (yaw) + ebml_writer_add_float(wr, MATROSKA_ID_VIDEOPROJECTIONPOSEYAW, yaw); + if (pitch) + ebml_writer_add_float(wr, MATROSKA_ID_VIDEOPROJECTIONPOSEPITCH, pitch); + if (roll) + ebml_writer_add_float(wr, MATROSKA_ID_VIDEOPROJECTIONPOSEROLL, roll); + + ebml_writer_close_or_discard_master(wr); } #define MAX_FIELD_ORDER_ELEMS 2 @@ -1575,12 +1671,30 @@ static int mkv_write_stereo_mode(AVFormatContext *s, EbmlWriter *writer, return 0; } -static void mkv_write_dovi(AVFormatContext *s, AVIOContext *pb, AVStream *st) +static void mkv_write_blockadditionmapping(AVFormatContext *s, const MatroskaMuxContext *mkv, + const AVCodecParameters *par, AVIOContext *pb, + mkv_track *track, const AVStream *st) { #if CONFIG_MATROSKA_MUXER - AVDOVIDecoderConfigurationRecord *dovi = (AVDOVIDecoderConfigurationRecord *) - av_stream_get_side_data(st, AV_PKT_DATA_DOVI_CONF, NULL); + const AVDOVIDecoderConfigurationRecord *dovi; + + if (IS_SEEKABLE(s->pb, mkv)) { + track->blockadditionmapping_offset = avio_tell(pb); + // We can't know at this point if there will be a block with BlockAdditions, so + // we either write the default value here, or a void element. Either of them will + // be overwritten when finishing the track. + put_ebml_uint(pb, MATROSKA_ID_TRACKMAXBLKADDID, 0); + if (par->codec_type == AVMEDIA_TYPE_VIDEO) { + // Similarly, reserve space for an eventual + // HDR10+ ITU T.35 metadata BlockAdditionMapping. + put_ebml_void(pb, 3 /* BlockAdditionMapping */ + + 4 /* BlockAddIDValue */ + + 4 /* BlockAddIDType */); + } + } + dovi = (const AVDOVIDecoderConfigurationRecord *) + av_stream_get_side_data(st, AV_PKT_DATA_DOVI_CONF, NULL); if (dovi && dovi->dv_profile <= 10) { ebml_master mapping; uint8_t buf[ISOM_DVCC_DVVC_SIZE]; @@ -1590,9 +1704,9 @@ static void mkv_write_dovi(AVFormatContext *s, AVIOContext *pb, AVStream *st) + (2 + 1 + 4) + (2 + 1 + ISOM_DVCC_DVVC_SIZE); if (dovi->dv_profile > 7) { - type = MKBETAG('d', 'v', 'v', 'C'); + type = MATROSKA_BLOCK_ADD_ID_TYPE_DVVC; } else { - type = MKBETAG('d', 'v', 'c', 'C'); + type = MATROSKA_BLOCK_ADD_ID_TYPE_DVCC; } ff_isom_put_dvcc_dvvc(s, buf, dovi); @@ -1726,7 +1840,7 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, if (IS_WEBM(mkv)) { const char *codec_id; - if (par->codec_type != AVMEDIA_TYPE_SUBTITLE) { + if (par->codec_id != AV_CODEC_ID_WEBVTT) { for (j = 0; ff_webm_codec_tags[j].id != AV_CODEC_ID_NONE; j++) { if (ff_webm_codec_tags[j].id == par->codec_id) { codec_id = ff_webm_codec_tags[j].str; @@ -1734,7 +1848,7 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, break; } } - } else if (par->codec_id == AV_CODEC_ID_WEBVTT) { + } else { if (st->disposition & AV_DISPOSITION_CAPTIONS) { codec_id = "D_WEBVTT/CAPTIONS"; native_id = MATROSKA_TRACK_TYPE_SUBTITLE; @@ -1772,9 +1886,13 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, // look for a codec ID string specific to mkv to use, // if none are found, use AVI codes - if (par->codec_id != AV_CODEC_ID_RAWVIDEO || par->codec_tag) { + if (par->codec_id == AV_CODEC_ID_FFV1) { + /* FFV1 is actually supported natively in Matroska, + * yet we use the VfW way to mux it for compatibility + * with old demuxers. (FIXME: Are they really important?) */ + } else if (par->codec_id != AV_CODEC_ID_RAWVIDEO || par->codec_tag) { for (j = 0; ff_mkv_codec_tags[j].id != AV_CODEC_ID_NONE; j++) { - if (ff_mkv_codec_tags[j].id == par->codec_id && par->codec_id != AV_CODEC_ID_FFV1) { + if (ff_mkv_codec_tags[j].id == par->codec_id) { put_ebml_string(pb, MATROSKA_ID_CODECID, ff_mkv_codec_tags[j].str); native_id = 1; break; @@ -1824,9 +1942,6 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, if (ret < 0) return ret; - if (!IS_WEBM(mkv)) - mkv_write_dovi(s, pb, st); - break; case AVMEDIA_TYPE_AUDIO: @@ -1902,6 +2017,9 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, return AVERROR(EINVAL); } + if (!IS_WEBM(mkv)) + mkv_write_blockadditionmapping(s, mkv, par, pb, track, st); + if (!IS_WEBM(mkv) || par->codec_id != AV_CODEC_ID_WEBVTT) { uint8_t *codecpriv; int codecpriv_size, max_payload_size; @@ -2585,17 +2703,28 @@ static int webm_reformat_vtt(MatroskaMuxContext *mkv, AVIOContext *pb, return 0; } +static void mkv_write_blockadditional(EbmlWriter *writer, const uint8_t *buf, + size_t size, uint64_t additional_id) +{ + ebml_writer_open_master(writer, MATROSKA_ID_BLOCKMORE); + ebml_writer_add_uint(writer, MATROSKA_ID_BLOCKADDID, additional_id); + ebml_writer_add_bin (writer, MATROSKA_ID_BLOCKADDITIONAL, buf, size); + ebml_writer_close_master(writer); +} + static int mkv_write_block(void *logctx, MatroskaMuxContext *mkv, AVIOContext *pb, const AVCodecParameters *par, mkv_track *track, const AVPacket *pkt, int keyframe, int64_t ts, uint64_t duration, int force_blockgroup, int64_t relative_packet_pos) { + uint8_t t35_buf[6 + AV_HDR_PLUS_MAX_PAYLOAD_SIZE]; uint8_t *side_data; size_t side_data_size; uint64_t additional_id; unsigned track_number = track->track_num; - EBML_WRITER(9); + EBML_WRITER(12); + int ret; mkv->cur_block.track = track; mkv->cur_block.pkt = pkt; @@ -2630,23 +2759,45 @@ static int mkv_write_block(void *logctx, MatroskaMuxContext *mkv, } } + ebml_writer_open_master(&writer, MATROSKA_ID_BLOCKADDITIONS); side_data = av_packet_get_side_data(pkt, AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL, &side_data_size); if (side_data && side_data_size >= 8 && // Only the Codec-specific BlockMore (id == 1) is currently supported. - (additional_id = AV_RB64(side_data)) == 1) { - ebml_writer_open_master(&writer, MATROSKA_ID_BLOCKADDITIONS); - ebml_writer_open_master(&writer, MATROSKA_ID_BLOCKMORE); - /* Until dbc50f8a our demuxer used a wrong default value - * of BlockAddID, so we write it unconditionally. */ - ebml_writer_add_uint(&writer, MATROSKA_ID_BLOCKADDID, additional_id); - ebml_writer_add_bin (&writer, MATROSKA_ID_BLOCKADDITIONAL, - side_data + 8, side_data_size - 8); - ebml_writer_close_master(&writer); - ebml_writer_close_master(&writer); + (additional_id = AV_RB64(side_data)) == MATROSKA_BLOCK_ADD_ID_TYPE_OPAQUE) { + mkv_write_blockadditional(&writer, side_data + 8, side_data_size - 8, + additional_id); + track->max_blockaddid = FFMAX(track->max_blockaddid, additional_id); + } + + if (par->codec_type == AVMEDIA_TYPE_VIDEO) { + side_data = av_packet_get_side_data(pkt, + AV_PKT_DATA_DYNAMIC_HDR10_PLUS, + &side_data_size); + if (side_data && side_data_size) { + uint8_t *payload = t35_buf; + size_t payload_size = sizeof(t35_buf) - 6; + + bytestream_put_byte(&payload, 0xB5); // country_code + bytestream_put_be16(&payload, 0x3C); // provider_code + bytestream_put_be16(&payload, 0x01); // provider_oriented_code + bytestream_put_byte(&payload, 0x04); // application_identifier + + ret = av_dynamic_hdr_plus_to_t35((AVDynamicHDRPlus *)side_data, &payload, + &payload_size); + if (ret < 0) + return ret; + + mkv_write_blockadditional(&writer, t35_buf, payload_size + 6, + MATROSKA_BLOCK_ADD_ID_ITU_T_T35); + track->max_blockaddid = FFMAX(track->max_blockaddid, + MATROSKA_BLOCK_ADD_ID_ITU_T_T35); + } } + ebml_writer_close_or_discard_master(&writer); + if (!force_blockgroup && writer.nb_elements == 2) { /* Nothing except the BlockGroup + Block. Can use a SimpleBlock. */ writer.elements++; // Skip the BlockGroup. @@ -3048,6 +3199,33 @@ static int mkv_write_trailer(AVFormatContext *s) if (mkv->track.bc) { // write Tracks master + if (!IS_WEBM(mkv)) { + AVIOContext *track_bc = mkv->track.bc; + + for (unsigned i = 0; i < s->nb_streams; i++) { + const mkv_track *track = &mkv->tracks[i]; + + if (!track->max_blockaddid) + continue; + + // We reserved a single byte to write this value. + av_assert0(track->max_blockaddid <= 0xFF); + + avio_seek(track_bc, track->blockadditionmapping_offset, SEEK_SET); + + put_ebml_uint(track_bc, MATROSKA_ID_TRACKMAXBLKADDID, + track->max_blockaddid); + if (track->max_blockaddid == MATROSKA_BLOCK_ADD_ID_ITU_T_T35) { + ebml_master mapping_master = start_ebml_master(track_bc, MATROSKA_ID_TRACKBLKADDMAPPING, 8); + put_ebml_uint(track_bc, MATROSKA_ID_BLKADDIDTYPE, + MATROSKA_BLOCK_ADD_ID_TYPE_ITU_T_T35); + put_ebml_uint(track_bc, MATROSKA_ID_BLKADDIDVALUE, + MATROSKA_BLOCK_ADD_ID_ITU_T_T35); + end_ebml_master(track_bc, mapping_master); + } + } + } + avio_seek(pb, mkv->track.pos, SEEK_SET); ret = end_ebml_master_crc32(pb, &mkv->track.bc, mkv, MATROSKA_ID_TRACKS, 0, 0, 0); @@ -3065,7 +3243,7 @@ static int mkv_write_trailer(AVFormatContext *s) if (track->duration_offset > 0) { double duration_sec = track->duration * av_q2d(st->time_base); - char duration_string[20] = ""; + char duration_string[DURATION_STRING_LENGTH + 1] = ""; ebml_master simpletag; av_log(s, AV_LOG_DEBUG, "stream %d end duration = %" PRIu64 "\n", i, @@ -3076,11 +3254,12 @@ static int mkv_write_trailer(AVFormatContext *s) 2 + 1 + 8 + 23); put_ebml_string(tags_bc, MATROSKA_ID_TAGNAME, "DURATION"); - snprintf(duration_string, 20, "%02d:%02d:%012.9f", + snprintf(duration_string, sizeof(duration_string), "%02d:%02d:%012.9f", (int) duration_sec / 3600, ((int) duration_sec / 60) % 60, fmod(duration_sec, 60)); - put_ebml_binary(tags_bc, MATROSKA_ID_TAGSTRING, duration_string, 20); + put_ebml_binary(tags_bc, MATROSKA_ID_TAGSTRING, + duration_string, DURATION_STRING_LENGTH); end_ebml_master(tags_bc, simpletag); } } @@ -3131,7 +3310,8 @@ static int mkv_init(struct AVFormatContext *s) s->streams[i]->codecpar->codec_id == AV_CODEC_ID_RA_288 || s->streams[i]->codecpar->codec_id == AV_CODEC_ID_SIPR || s->streams[i]->codecpar->codec_id == AV_CODEC_ID_RV10 || - s->streams[i]->codecpar->codec_id == AV_CODEC_ID_RV20) { + s->streams[i]->codecpar->codec_id == AV_CODEC_ID_RV20 || + s->streams[i]->codecpar->codec_id == AV_CODEC_ID_RV30) { av_log(s, AV_LOG_ERROR, "The Matroska muxer does not yet support muxing %s\n", avcodec_get_name(s->streams[i]->codecpar->codec_id)); @@ -3257,23 +3437,15 @@ static const AVCodecTag additional_audio_tags[] = { { AV_CODEC_ID_QDMC, 0xFFFFFFFF }, { AV_CODEC_ID_QDM2, 0xFFFFFFFF }, { AV_CODEC_ID_RA_144, 0xFFFFFFFF }, - { AV_CODEC_ID_RA_288, 0xFFFFFFFF }, - { AV_CODEC_ID_COOK, 0xFFFFFFFF }, { AV_CODEC_ID_TRUEHD, 0xFFFFFFFF }, { AV_CODEC_ID_NONE, 0xFFFFFFFF } }; -static const AVCodecTag additional_video_tags[] = { - { AV_CODEC_ID_RV10, 0xFFFFFFFF }, - { AV_CODEC_ID_RV20, 0xFFFFFFFF }, - { AV_CODEC_ID_RV30, 0xFFFFFFFF }, - { AV_CODEC_ID_NONE, 0xFFFFFFFF } -}; - static const AVCodecTag additional_subtitle_tags[] = { { AV_CODEC_ID_DVB_SUBTITLE, 0xFFFFFFFF }, { AV_CODEC_ID_DVD_SUBTITLE, 0xFFFFFFFF }, { AV_CODEC_ID_HDMV_PGS_SUBTITLE, 0xFFFFFFFF }, + { AV_CODEC_ID_ARIB_CAPTION, 0xFFFFFFFF }, { AV_CODEC_ID_NONE, 0xFFFFFFFF } }; @@ -3321,31 +3493,31 @@ static int mkv_query_codec(enum AVCodecID codec_id, int std_compliance) return 0; } -const AVOutputFormat ff_matroska_muxer = { - .name = "matroska", - .long_name = NULL_IF_CONFIG_SMALL("Matroska"), - .mime_type = "video/x-matroska", - .extensions = "mkv", +const FFOutputFormat ff_matroska_muxer = { + .p.name = "matroska", + .p.long_name = NULL_IF_CONFIG_SMALL("Matroska"), + .p.mime_type = "video/x-matroska", + .p.extensions = "mkv", .priv_data_size = sizeof(MatroskaMuxContext), - .audio_codec = CONFIG_LIBVORBIS_ENCODER ? + .p.audio_codec = CONFIG_LIBVORBIS_ENCODER ? AV_CODEC_ID_VORBIS : AV_CODEC_ID_AC3, - .video_codec = CONFIG_LIBX264_ENCODER ? + .p.video_codec = CONFIG_LIBX264_ENCODER ? AV_CODEC_ID_H264 : AV_CODEC_ID_MPEG4, .init = mkv_init, .deinit = mkv_deinit, .write_header = mkv_write_header, .write_packet = mkv_write_flush_packet, .write_trailer = mkv_write_trailer, - .flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS | + .p.flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | AVFMT_ALLOW_FLUSH, - .codec_tag = (const AVCodecTag* const []){ + .p.codec_tag = (const AVCodecTag* const []){ ff_codec_bmp_tags, ff_codec_wav_tags, - additional_audio_tags, additional_video_tags, additional_subtitle_tags, 0 + additional_audio_tags, additional_subtitle_tags, 0 }, - .subtitle_codec = AV_CODEC_ID_ASS, + .p.subtitle_codec = AV_CODEC_ID_ASS, .query_codec = mkv_query_codec, .check_bitstream = mkv_check_bitstream, - .priv_class = &matroska_webm_class, + .p.priv_class = &matroska_webm_class, }; #endif @@ -3359,15 +3531,15 @@ static int webm_query_codec(enum AVCodecID codec_id, int std_compliance) return 0; } -const AVOutputFormat ff_webm_muxer = { - .name = "webm", - .long_name = NULL_IF_CONFIG_SMALL("WebM"), - .mime_type = "video/webm", - .extensions = "webm", +const FFOutputFormat ff_webm_muxer = { + .p.name = "webm", + .p.long_name = NULL_IF_CONFIG_SMALL("WebM"), + .p.mime_type = "video/webm", + .p.extensions = "webm", .priv_data_size = sizeof(MatroskaMuxContext), - .audio_codec = CONFIG_LIBOPUS_ENCODER ? AV_CODEC_ID_OPUS : AV_CODEC_ID_VORBIS, - .video_codec = CONFIG_LIBVPX_VP9_ENCODER? AV_CODEC_ID_VP9 : AV_CODEC_ID_VP8, - .subtitle_codec = AV_CODEC_ID_WEBVTT, + .p.audio_codec = CONFIG_LIBOPUS_ENCODER ? AV_CODEC_ID_OPUS : AV_CODEC_ID_VORBIS, + .p.video_codec = CONFIG_LIBVPX_VP9_ENCODER? AV_CODEC_ID_VP9 : AV_CODEC_ID_VP8, + .p.subtitle_codec = AV_CODEC_ID_WEBVTT, .init = mkv_init, .deinit = mkv_deinit, .write_header = mkv_write_header, @@ -3375,33 +3547,33 @@ const AVOutputFormat ff_webm_muxer = { .write_trailer = mkv_write_trailer, .query_codec = webm_query_codec, .check_bitstream = mkv_check_bitstream, - .flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS | + .p.flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | AVFMT_ALLOW_FLUSH, - .priv_class = &matroska_webm_class, + .p.priv_class = &matroska_webm_class, }; #endif #if CONFIG_MATROSKA_AUDIO_MUXER -const AVOutputFormat ff_matroska_audio_muxer = { - .name = "matroska", - .long_name = NULL_IF_CONFIG_SMALL("Matroska Audio"), - .mime_type = "audio/x-matroska", - .extensions = "mka", +const FFOutputFormat ff_matroska_audio_muxer = { + .p.name = "matroska", + .p.long_name = NULL_IF_CONFIG_SMALL("Matroska Audio"), + .p.mime_type = "audio/x-matroska", + .p.extensions = "mka", .priv_data_size = sizeof(MatroskaMuxContext), - .audio_codec = CONFIG_LIBVORBIS_ENCODER ? + .p.audio_codec = CONFIG_LIBVORBIS_ENCODER ? AV_CODEC_ID_VORBIS : AV_CODEC_ID_AC3, - .video_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_NONE, .init = mkv_init, .deinit = mkv_deinit, .write_header = mkv_write_header, .write_packet = mkv_write_flush_packet, .write_trailer = mkv_write_trailer, .check_bitstream = mkv_check_bitstream, - .flags = AVFMT_GLOBALHEADER | AVFMT_TS_NONSTRICT | + .p.flags = AVFMT_GLOBALHEADER | AVFMT_TS_NONSTRICT | AVFMT_ALLOW_FLUSH, - .codec_tag = (const AVCodecTag* const []){ + .p.codec_tag = (const AVCodecTag* const []){ ff_codec_wav_tags, additional_audio_tags, 0 }, - .priv_class = &matroska_webm_class, + .p.priv_class = &matroska_webm_class, }; #endif diff --git a/libavformat/microdvdenc.c b/libavformat/microdvdenc.c index 4db5e701619..950309981bb 100644 --- a/libavformat/microdvdenc.c +++ b/libavformat/microdvdenc.c @@ -22,6 +22,7 @@ #include #include "avformat.h" #include "internal.h" +#include "mux.h" static int microdvd_write_header(struct AVFormatContext *s) { @@ -55,13 +56,13 @@ static int microdvd_write_packet(AVFormatContext *avf, AVPacket *pkt) return 0; } -const AVOutputFormat ff_microdvd_muxer = { - .name = "microdvd", - .long_name = NULL_IF_CONFIG_SMALL("MicroDVD subtitle format"), - .mime_type = "text/x-microdvd", - .extensions = "sub", +const FFOutputFormat ff_microdvd_muxer = { + .p.name = "microdvd", + .p.long_name = NULL_IF_CONFIG_SMALL("MicroDVD subtitle format"), + .p.mime_type = "text/x-microdvd", + .p.extensions = "sub", + .p.flags = AVFMT_NOTIMESTAMPS, + .p.subtitle_codec = AV_CODEC_ID_MICRODVD, .write_header = microdvd_write_header, .write_packet = microdvd_write_packet, - .flags = AVFMT_NOTIMESTAMPS, - .subtitle_codec = AV_CODEC_ID_MICRODVD, }; diff --git a/libavformat/mkvtimestamp_v2.c b/libavformat/mkvtimestamp_v2.c index 27c7d7a68be..dde431ab7d0 100644 --- a/libavformat/mkvtimestamp_v2.c +++ b/libavformat/mkvtimestamp_v2.c @@ -21,6 +21,7 @@ #include "avformat.h" #include "internal.h" +#include "mux.h" static int write_header(AVFormatContext *s) { @@ -40,11 +41,11 @@ static int write_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -const AVOutputFormat ff_mkvtimestamp_v2_muxer = { - .name = "mkvtimestamp_v2", - .long_name = NULL_IF_CONFIG_SMALL("extract pts as timecode v2 format, as defined by mkvtoolnix"), - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_RAWVIDEO, +const FFOutputFormat ff_mkvtimestamp_v2_muxer = { + .p.name = "mkvtimestamp_v2", + .p.long_name = NULL_IF_CONFIG_SMALL("extract pts as timecode v2 format, as defined by mkvtoolnix"), + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_RAWVIDEO, .write_header = write_header, .write_packet = write_packet, }; diff --git a/libavformat/mmf.c b/libavformat/mmf.c index 3f20d5bc0b7..5cac4381f45 100644 --- a/libavformat/mmf.c +++ b/libavformat/mmf.c @@ -25,6 +25,7 @@ #include "avformat.h" #include "avio_internal.h" #include "internal.h" +#include "mux.h" #include "pcm.h" #include "rawenc.h" #include "riff.h" @@ -309,14 +310,14 @@ const AVInputFormat ff_mmf_demuxer = { #endif #if CONFIG_MMF_MUXER -const AVOutputFormat ff_mmf_muxer = { - .name = "mmf", - .long_name = NULL_IF_CONFIG_SMALL("Yamaha SMAF"), - .mime_type = "application/vnd.smaf", - .extensions = "mmf", +const FFOutputFormat ff_mmf_muxer = { + .p.name = "mmf", + .p.long_name = NULL_IF_CONFIG_SMALL("Yamaha SMAF"), + .p.mime_type = "application/vnd.smaf", + .p.extensions = "mmf", .priv_data_size = sizeof(MMFContext), - .audio_codec = AV_CODEC_ID_ADPCM_YAMAHA, - .video_codec = AV_CODEC_ID_NONE, + .p.audio_codec = AV_CODEC_ID_ADPCM_YAMAHA, + .p.video_codec = AV_CODEC_ID_NONE, .write_header = mmf_write_header, .write_packet = ff_raw_write_packet, .write_trailer = mmf_write_trailer, diff --git a/libavformat/mov.c b/libavformat/mov.c index e0b7df28153..be9975f297e 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -940,6 +940,88 @@ static int mov_read_chan(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; } +static int mov_read_chnl(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + int64_t end = av_sat_add64(avio_tell(pb), atom.size); + int stream_structure; + int version, flags; + int ret = 0; + AVStream *st; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + + version = avio_r8(pb); + flags = avio_rb24(pb); + if (version != 0 || flags != 0) { + av_log(c->fc, AV_LOG_ERROR, + "Unsupported 'chnl' box with version %d, flags: %#x", + version, flags); + return AVERROR_INVALIDDATA; + } + + stream_structure = avio_r8(pb); + + // stream carries channels + if (stream_structure & 1) { + int layout = avio_r8(pb); + + av_log(c->fc, AV_LOG_TRACE, "'chnl' layout %d\n", layout); + if (!layout) { + uint8_t *positions = av_malloc(st->codecpar->ch_layout.nb_channels); + + if (!positions) + return AVERROR(ENOMEM); + for (int i = 0; i < st->codecpar->ch_layout.nb_channels; i++) { + int speaker_pos = avio_r8(pb); + + av_log(c->fc, AV_LOG_TRACE, "speaker_position %d\n", speaker_pos); + if (speaker_pos == 126) { // explicit position + avpriv_request_sample(c->fc, "explicit position"); + av_freep(&positions); + return AVERROR_PATCHWELCOME; + } else { + positions[i] = speaker_pos; + } + } + + ret = ff_mov_get_layout_from_channel_positions(positions, + st->codecpar->ch_layout.nb_channels, + &st->codecpar->ch_layout); + av_freep(&positions); + if (ret) { + av_log(c->fc, AV_LOG_ERROR, + "get channel layout from speaker positions failed, %s\n", + av_err2str(ret)); + return ret; + } + } else { + uint64_t omitted_channel_map = avio_rb64(pb); + + if (omitted_channel_map) { + avpriv_request_sample(c->fc, "omitted_channel_map 0x%" PRIx64 " != 0", + omitted_channel_map); + return AVERROR_PATCHWELCOME; + } + ff_mov_get_channel_layout_from_config(layout, &st->codecpar->ch_layout); + } + } + + // stream carries objects + if (stream_structure & 2) { + int obj_count = avio_r8(pb); + av_log(c->fc, AV_LOG_TRACE, "'chnl' with object_count %d\n", obj_count); + } + + if (avio_tell(pb) != end) { + av_log(c->fc, AV_LOG_WARNING, "skip %" PRId64 " bytes of unknown data inside chnl\n", + end - avio_tell(pb)); + avio_seek(pb, end, SEEK_SET); + } + return ret; +} + static int mov_read_wfex(MOVContext *c, AVIOContext *pb, MOVAtom atom) { AVStream *st; @@ -1433,14 +1515,29 @@ static int mov_read_moof(MOVContext *c, AVIOContext *pb, MOVAtom atom) return mov_read_default(c, pb, atom); } -static void mov_metadata_creation_time(AVDictionary **metadata, int64_t time, void *logctx) +static void mov_metadata_creation_time(MOVContext *c, AVIOContext *pb, AVDictionary **metadata, int version) { + int64_t time; + if (version == 1) { + time = avio_rb64(pb); + avio_rb64(pb); + if (time < 0) { + av_log(c->fc, AV_LOG_DEBUG, "creation_time is negative\n"); + return; + } + } else { + time = avio_rb32(pb); + avio_rb32(pb); /* modification time */ + if (time > 0 && time < 2082844800) { + av_log(c->fc, AV_LOG_WARNING, "Detected creation time before 1970, parsing as unix timestamp.\n"); + time += 2082844800; + } + } if (time) { - if (time >= 2082844800) - time -= 2082844800; /* seconds between 1904-01-01 and Epoch */ + time -= 2082844800; /* seconds between 1904-01-01 and Epoch */ if ((int64_t)(time * 1000000ULL) / 1000000 != time) { - av_log(logctx, AV_LOG_DEBUG, "creation_time is not representable\n"); + av_log(c->fc, AV_LOG_DEBUG, "creation_time is not representable\n"); return; } @@ -1455,7 +1552,6 @@ static int mov_read_mdhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) int version; char language[4] = {0}; unsigned lang; - int64_t creation_time; if (c->fc->nb_streams < 1) return 0; @@ -1473,14 +1569,7 @@ static int mov_read_mdhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) return AVERROR_PATCHWELCOME; } avio_rb24(pb); /* flags */ - if (version == 1) { - creation_time = avio_rb64(pb); - avio_rb64(pb); - } else { - creation_time = avio_rb32(pb); - avio_rb32(pb); /* modification time */ - } - mov_metadata_creation_time(&st->metadata, creation_time, c->fc); + mov_metadata_creation_time(c, pb, &st->metadata, version); sc->time_scale = avio_rb32(pb); if (sc->time_scale <= 0) { @@ -1505,18 +1594,10 @@ static int mov_read_mdhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) static int mov_read_mvhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) { int i; - int64_t creation_time; int version = avio_r8(pb); /* version */ avio_rb24(pb); /* flags */ - if (version == 1) { - creation_time = avio_rb64(pb); - avio_rb64(pb); - } else { - creation_time = avio_rb32(pb); - avio_rb32(pb); /* modification time */ - } - mov_metadata_creation_time(&c->fc->metadata, creation_time, c->fc); + mov_metadata_creation_time(c, pb, &c->fc->metadata, version); c->time_scale = avio_rb32(pb); /* time scale */ if (c->time_scale <= 0) { av_log(c->fc, AV_LOG_ERROR, "Invalid mvhd time scale %d, defaulting to 1\n", c->time_scale); @@ -1590,16 +1671,74 @@ static int mov_read_enda(MOVContext *c, AVIOContext *pb, MOVAtom atom) static int mov_read_pcmc(MOVContext *c, AVIOContext *pb, MOVAtom atom) { int format_flags; + int version, flags; + int pcm_sample_size; + AVFormatContext *fc = c->fc; + AVStream *st; + MOVStreamContext *sc; if (atom.size < 6) { av_log(c->fc, AV_LOG_ERROR, "Empty pcmC box\n"); return AVERROR_INVALIDDATA; } - avio_r8(pb); // version - avio_rb24(pb); // flags + version = avio_r8(pb); + flags = avio_rb24(pb); + + if (version != 0 || flags != 0) { + av_log(c->fc, AV_LOG_ERROR, + "Unsupported 'pcmC' box with version %d, flags: %x", + version, flags); + return AVERROR_INVALIDDATA; + } + format_flags = avio_r8(pb); - if (format_flags == 1) // indicates little-endian format. If not present, big-endian format is used + pcm_sample_size = avio_r8(pb); + + if (fc->nb_streams < 1) + return AVERROR_INVALIDDATA; + + st = fc->streams[fc->nb_streams - 1]; + sc = st->priv_data; + + if (sc->format == MOV_MP4_FPCM_TAG) { + switch (pcm_sample_size) { + case 32: + st->codecpar->codec_id = AV_CODEC_ID_PCM_F32BE; + break; + case 64: + st->codecpar->codec_id = AV_CODEC_ID_PCM_F64BE; + break; + default: + av_log(fc, AV_LOG_ERROR, "invalid pcm_sample_size %d for %s\n", + pcm_sample_size, + av_fourcc2str(sc->format)); + return AVERROR_INVALIDDATA; + } + } else if (sc->format == MOV_MP4_IPCM_TAG) { + switch (pcm_sample_size) { + case 16: + st->codecpar->codec_id = AV_CODEC_ID_PCM_S16BE; + break; + case 24: + st->codecpar->codec_id = AV_CODEC_ID_PCM_S24BE; + break; + case 32: + st->codecpar->codec_id = AV_CODEC_ID_PCM_S32BE; + break; + default: + av_log(fc, AV_LOG_ERROR, "invalid pcm_sample_size %d for %s\n", + pcm_sample_size, + av_fourcc2str(sc->format)); + return AVERROR_INVALIDDATA; + } + } else { + av_log(fc, AV_LOG_ERROR, "'pcmC' with invalid sample entry '%s'\n", + av_fourcc2str(sc->format)); + return AVERROR_INVALIDDATA; + } + + if (format_flags & 1) // indicates little-endian format. If not present, big-endian format is used set_last_stream_little_endian(c->fc); return 0; @@ -1772,7 +1911,7 @@ static int mov_read_alac(MOVContext *c, AVIOContext *pb, MOVAtom atom) static int mov_read_avss(MOVContext *c, AVIOContext *pb, MOVAtom atom) { - return mov_read_extradata(c, pb, atom, AV_CODEC_ID_AVS); + return mov_read_extradata(c, pb, atom, AV_CODEC_ID_CAVS); } static int mov_read_jp2h(MOVContext *c, AVIOContext *pb, MOVAtom atom) @@ -2124,6 +2263,11 @@ static int mov_codec_id(AVStream *st, uint32_t format) (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE && st->codecpar->codec_id == AV_CODEC_ID_NONE)) { id = ff_codec_get_id(ff_codec_movsubtitle_tags, format); + if (id <= 0) { + id = (format == MOV_MP4_TTML_TAG || format == MOV_ISMV_TTML_TAG) ? + AV_CODEC_ID_TTML : id; + } + if (id > 0) st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE; else @@ -2318,8 +2462,10 @@ static void mov_parse_stsd_subtitle(MOVContext *c, AVIOContext *pb, // ttxt stsd contains display flags, justification, background // color, fonts, and default styles, so fake an atom to read it MOVAtom fake_atom = { .size = size }; - // mp4s contains a regular esds atom - if (st->codecpar->codec_tag != AV_RL32("mp4s")) + // mp4s contains a regular esds atom, dfxp ISMV TTML has no content + // in extradata unlike stpp MP4 TTML. + if (st->codecpar->codec_tag != AV_RL32("mp4s") && + st->codecpar->codec_tag != MOV_ISMV_TTML_TAG) mov_read_glbl(c, pb, fake_atom); st->codecpar->width = sc->width; st->codecpar->height = sc->height; @@ -2507,6 +2653,7 @@ static int mov_finalize_stsd_codec(MOVContext *c, AVIOContext *pb, case AV_CODEC_ID_VP9: sti->need_parsing = AVSTREAM_PARSE_FULL; break; + case AV_CODEC_ID_EVC: case AV_CODEC_ID_AV1: /* field_order detection of H264 requires parsing */ case AV_CODEC_ID_H264: @@ -4063,10 +4210,15 @@ static void mov_build_index(MOVContext *mov, AVStream *st) } } - if (multiple_edits && !mov->advanced_editlist) - av_log(mov->fc, AV_LOG_WARNING, "multiple edit list entries, " - "Use -advanced_editlist to correctly decode otherwise " - "a/v desync might occur\n"); + if (multiple_edits && !mov->advanced_editlist) { + if (mov->advanced_editlist_autodisabled) + av_log(mov->fc, AV_LOG_WARNING, "multiple edit list entries, " + "not supported in fragmented MP4 files\n"); + else + av_log(mov->fc, AV_LOG_WARNING, "multiple edit list entries, " + "Use -advanced_editlist to correctly decode otherwise " + "a/v desync might occur\n"); + } /* adjust first dts according to edit list */ if ((empty_duration || start_time) && mov->time_scale > 0) { @@ -4187,6 +4339,13 @@ static void mov_build_index(MOVContext *mov, AVStream *st) if (keyframe) distance = 0; sample_size = sc->stsz_sample_size > 0 ? sc->stsz_sample_size : sc->sample_sizes[current_sample]; + if (current_offset > INT64_MAX - sample_size) { + av_log(mov->fc, AV_LOG_ERROR, "Current offset %"PRId64" or sample size %u is too large\n", + current_offset, + sample_size); + return; + } + if (sc->pseudo_stream_id == -1 || sc->stsc_data[stsc_index].id - 1 == sc->pseudo_stream_id) { AVIndexEntry *e; @@ -4503,14 +4662,13 @@ static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom) * Advanced edit list support does not work with fragemented MP4s, which * have stsc, stsz, stco, and stts with zero entries in the moov atom. * In these files, trun atoms may be streamed in. - * - * It cannot be used with use_mfra_for = {pts,dts} either, as the index - * is not complete, but filled in as more trun atoms are read, as well. */ - if (!sc->stts_count || c->use_mfra_for != FF_MOV_FLAG_MFRA_AUTO) { - av_log(c->fc, AV_LOG_WARNING, "advanced_editlist does not work with fragmented " + if (!sc->stts_count && c->advanced_editlist) { + + av_log(c->fc, AV_LOG_VERBOSE, "advanced_editlist does not work with fragmented " "MP4. disabling.\n"); c->advanced_editlist = 0; + c->advanced_editlist_autodisabled = 1; } mov_build_index(c, st); @@ -6499,38 +6657,6 @@ static int mov_parse_auxiliary_info(MOVContext *c, MOVStreamContext *sc, AVIOCon return ret; } -/** - * Tries to read the given number of bytes from the stream and puts it in a - * newly allocated buffer. This reads in small chunks to avoid allocating large - * memory if the file contains an invalid/malicious size value. - */ -static int mov_try_read_block(AVIOContext *pb, size_t size, uint8_t **data) -{ - const unsigned int block_size = 1024 * 1024; - uint8_t *buffer = NULL; - unsigned int alloc_size = 0, offset = 0; - while (offset < size) { - unsigned int new_size = - alloc_size >= INT_MAX - block_size ? INT_MAX : alloc_size + block_size; - uint8_t *new_buffer = av_fast_realloc(buffer, &alloc_size, new_size); - unsigned int to_read = FFMIN(size, alloc_size) - offset; - if (!new_buffer) { - av_free(buffer); - return AVERROR(ENOMEM); - } - buffer = new_buffer; - - if (avio_read(pb, buffer + offset, to_read) != to_read) { - av_free(buffer); - return AVERROR_INVALIDDATA; - } - offset += to_read; - } - - *data = buffer; - return 0; -} - static int mov_read_saiz(MOVContext *c, AVIOContext *pb, MOVAtom atom) { MOVEncryptionIndex *encryption_index; @@ -6586,15 +6712,24 @@ static int mov_read_saiz(MOVContext *c, AVIOContext *pb, MOVAtom atom) encryption_index->auxiliary_info_default_size = avio_r8(pb); sample_count = avio_rb32(pb); - encryption_index->auxiliary_info_sample_count = sample_count; if (encryption_index->auxiliary_info_default_size == 0) { - ret = mov_try_read_block(pb, sample_count, &encryption_index->auxiliary_info_sizes); - if (ret < 0) { - av_log(c->fc, AV_LOG_ERROR, "Failed to read the auxiliary info\n"); + encryption_index->auxiliary_info_sizes = av_malloc(sample_count); + if (!encryption_index->auxiliary_info_sizes) + return AVERROR(ENOMEM); + + ret = avio_read(pb, encryption_index->auxiliary_info_sizes, sample_count); + if (ret != sample_count) { + av_freep(&encryption_index->auxiliary_info_sizes); + + if (ret >= 0) + ret = AVERROR_INVALIDDATA; + av_log(c->fc, AV_LOG_ERROR, "Failed to read the auxiliary info, %s\n", + av_err2str(ret)); return ret; } } + encryption_index->auxiliary_info_sample_count = sample_count; if (encryption_index->auxiliary_offsets_count) { return mov_parse_auxiliary_info(c, sc, pb, encryption_index); @@ -6763,9 +6898,19 @@ static int mov_read_pssh(MOVContext *c, AVIOContext *pb, MOVAtom atom) } extra_data_size = avio_rb32(pb); - ret = mov_try_read_block(pb, extra_data_size, &extra_data); - if (ret < 0) + extra_data = av_malloc(extra_data_size); + if (!extra_data) { + ret = AVERROR(ENOMEM); goto finish; + } + ret = avio_read(pb, extra_data, extra_data_size); + if (ret != extra_data_size) { + av_free(extra_data); + + if (ret >= 0) + ret = AVERROR_INVALIDDATA; + goto finish; + } av_freep(&info->data); // malloc(0) may still allocate something. info->data = extra_data; @@ -7744,7 +7889,8 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG('w','i','d','e'), mov_read_wide }, /* place holder */ { MKTAG('w','f','e','x'), mov_read_wfex }, { MKTAG('c','m','o','v'), mov_read_cmov }, -{ MKTAG('c','h','a','n'), mov_read_chan }, /* channel layout */ +{ MKTAG('c','h','a','n'), mov_read_chan }, /* channel layout from quicktime */ +{ MKTAG('c','h','n','l'), mov_read_chnl }, /* channel layout from ISO-14496-12 */ { MKTAG('d','v','c','1'), mov_read_dvc1 }, { MKTAG('s','g','p','d'), mov_read_sgpd }, { MKTAG('s','b','g','p'), mov_read_sbgp }, @@ -7781,6 +7927,7 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG('i','l','o','c'), mov_read_iloc }, { MKTAG('p','c','m','C'), mov_read_pcmc }, /* PCM configuration box */ { MKTAG('p','i','t','m'), mov_read_pitm }, +{ MKTAG('e','v','c','C'), mov_read_glbl }, { 0, NULL } }; @@ -7960,6 +8107,8 @@ static int mov_probe(const AVProbeData *p) score = FFMAX(score, AVPROBE_SCORE_MAX - 5); break; case MKTAG(0x82,0x82,0x7f,0x7d): + score = FFMAX(score, AVPROBE_SCORE_EXTENSION - 5); + break; case MKTAG('s','k','i','p'): case MKTAG('u','u','i','d'): case MKTAG('p','r','f','l'): diff --git a/libavformat/mov_chan.c b/libavformat/mov_chan.c index f66bf0df7fd..f3d51899e1f 100644 --- a/libavformat/mov_chan.c +++ b/libavformat/mov_chan.c @@ -551,3 +551,299 @@ int ff_mov_read_chan(AVFormatContext *s, AVIOContext *pb, AVStream *st, return 0; } + +/* ISO/IEC 23001-8, 8.2 */ +static const AVChannelLayout iso_channel_configuration[] = { + // 0: any setup + {0}, + + // 1: centre front + AV_CHANNEL_LAYOUT_MONO, + + // 2: left front, right front + AV_CHANNEL_LAYOUT_STEREO, + + // 3: centre front, left front, right front + AV_CHANNEL_LAYOUT_SURROUND, + + // 4: centre front, left front, right front, rear centre + AV_CHANNEL_LAYOUT_4POINT0, + + // 5: centre front, left front, right front, left surround, right surround + AV_CHANNEL_LAYOUT_5POINT0, + + // 6: 5 + LFE + AV_CHANNEL_LAYOUT_5POINT1, + + // 7: centre front, left front centre, right front centre, + // left front, right front, left surround, right surround, LFE + AV_CHANNEL_LAYOUT_7POINT1_WIDE, + + // 8: channel1, channel2 + AV_CHANNEL_LAYOUT_STEREO_DOWNMIX, + + // 9: left front, right front, rear centre + AV_CHANNEL_LAYOUT_2_1, + + // 10: left front, right front, left surround, right surround + AV_CHANNEL_LAYOUT_2_2, + + // 11: centre front, left front, right front, left surround, right surround, rear centre, LFE + AV_CHANNEL_LAYOUT_6POINT1, + + // 12: centre front, left front, right front + // left surround, right surround + // rear surround left, rear surround right + // LFE + AV_CHANNEL_LAYOUT_7POINT1, + + // 13: + AV_CHANNEL_LAYOUT_22POINT2, + + // 14: + AV_CHANNEL_LAYOUT_7POINT1_TOP_BACK, + + // TODO: 15 - 20 +}; + +/* ISO/IEC 23001-8, table 8 */ +static const enum AVChannel iso_channel_position[] = { + // 0: left front + AV_CHAN_FRONT_LEFT, + + // 1: right front + AV_CHAN_FRONT_RIGHT, + + // 2: centre front + AV_CHAN_FRONT_CENTER, + + // 3: low frequence enhancement + AV_CHAN_LOW_FREQUENCY, + + // 4: left surround + // TODO + AV_CHAN_NONE, + + // 5: right surround + // TODO + AV_CHAN_NONE, + + // 6: left front centre + AV_CHAN_FRONT_LEFT_OF_CENTER, + + // 7: right front centre + AV_CHAN_FRONT_RIGHT_OF_CENTER, + + // 8: rear surround left + AV_CHAN_BACK_LEFT, + + // 9: rear surround right + AV_CHAN_BACK_RIGHT, + + // 10: rear centre + AV_CHAN_BACK_CENTER, + + // 11: left surround direct + AV_CHAN_SURROUND_DIRECT_LEFT, + + // 12: right surround direct + AV_CHAN_SURROUND_DIRECT_RIGHT, + + // 13: left side surround + AV_CHAN_SIDE_LEFT, + + // 14: right side surround + AV_CHAN_SIDE_RIGHT, + + // 15: left wide front + AV_CHAN_WIDE_LEFT, + + // 16: right wide front + AV_CHAN_WIDE_RIGHT, + + // 17: left front vertical height + AV_CHAN_TOP_FRONT_LEFT, + + // 18: right front vertical height + AV_CHAN_TOP_FRONT_RIGHT, + + // 19: centre front vertical height + AV_CHAN_TOP_FRONT_CENTER, + + // 20: left surround vertical height rear + AV_CHAN_TOP_BACK_LEFT, + + // 21: right surround vertical height rear + AV_CHAN_TOP_BACK_RIGHT, + + // 22: centre vertical height rear + AV_CHAN_TOP_BACK_CENTER, + + // 23: left vertical height side surround + AV_CHAN_TOP_SIDE_LEFT, + + // 24: right vertical height side surround + AV_CHAN_TOP_SIDE_RIGHT, + + // 25: top centre surround + AV_CHAN_TOP_CENTER, + + // 26: low frequency enhancement 2 + AV_CHAN_LOW_FREQUENCY_2, + + // 27: left front vertical bottom + AV_CHAN_BOTTOM_FRONT_LEFT, + + // 28: right front vertical bottom + AV_CHAN_BOTTOM_FRONT_RIGHT, + + // 29: centre front vertical bottom + AV_CHAN_BOTTOM_FRONT_CENTER, + + // 30: left vertical height surround + // TODO + AV_CHAN_NONE, + + // 31: right vertical height surround + // TODO + AV_CHAN_NONE, + + // 32, 33, 34, 35, reserved + AV_CHAN_NONE, + AV_CHAN_NONE, + AV_CHAN_NONE, + AV_CHAN_NONE, + + // 36: low frequency enhancement 3 + AV_CHAN_NONE, + + // 37: left edge of screen + AV_CHAN_NONE, + // 38: right edge of screen + AV_CHAN_NONE, + // 39: half-way between centre of screen and left edge of screen + AV_CHAN_NONE, + // 40: half-way between centre of screen and right edge of screen + AV_CHAN_NONE, + + // 41: left back surround + AV_CHAN_NONE, + + // 42: right back surround + AV_CHAN_NONE, + + // 43 - 125: reserved + // 126: explicit position + // 127: unknown /undefined +}; + +int ff_mov_get_channel_config_from_layout(const AVChannelLayout *layout, int *config) +{ + // Set default value which means any setup in 23001-8 + *config = 0; + for (int i = 0; i < FF_ARRAY_ELEMS(iso_channel_configuration); i++) { + if (!av_channel_layout_compare(layout, iso_channel_configuration + i)) { + *config = i; + break; + } + } + + return 0; +} + +int ff_mov_get_channel_layout_from_config(int config, AVChannelLayout *layout) +{ + if (config > 0 && config < FF_ARRAY_ELEMS(iso_channel_configuration)) { + av_channel_layout_copy(layout, &iso_channel_configuration[config]); + return 0; + } + + return -1; +} + +int ff_mov_get_channel_positions_from_layout(const AVChannelLayout *layout, + uint8_t *position, int position_num) +{ + enum AVChannel channel; + + if (position_num < layout->nb_channels) + return AVERROR(EINVAL); + + for (int i = 0; i < layout->nb_channels; i++) { + position[i] = 127; + channel = av_channel_layout_channel_from_index(layout, i); + if (channel == AV_CHAN_NONE) + return AVERROR(EINVAL); + + for (int j = 0; j < FF_ARRAY_ELEMS(iso_channel_position); j++) { + if (iso_channel_position[j] == channel) { + position[i] = j; + break; + } + } + if (position[i] == 127) + return AVERROR(EINVAL); + } + + return 0; +} + +int ff_mov_get_layout_from_channel_positions(const uint8_t *position, int position_num, + AVChannelLayout *layout) +{ + int ret; + enum AVChannel channel; + + av_channel_layout_uninit(layout); + + if (position_num <= 63) { + layout->order = AV_CHANNEL_ORDER_NATIVE; + layout->nb_channels = position_num; + for (int i = 0; i < position_num; i++) { + if (position[i] >= FF_ARRAY_ELEMS(iso_channel_position)) { + ret = AVERROR_PATCHWELCOME; + goto error; + } + + channel = iso_channel_position[position[i]]; + // unsupported layout + if (channel == AV_CHAN_NONE) { + ret = AVERROR_PATCHWELCOME; + goto error; + } + + layout->u.mask |= 1ULL << channel; + } + } else { + layout->order = AV_CHANNEL_ORDER_CUSTOM; + layout->nb_channels = position_num; + layout->u.map = av_calloc(position_num, sizeof(*layout->u.map)); + if (!layout->u.map) { + ret = AVERROR(ENOMEM); + goto error; + } + + for (int i = 0; i < position_num; i++) { + if (position[i] >= FF_ARRAY_ELEMS(iso_channel_position)) { + ret = AVERROR_PATCHWELCOME; + goto error; + } + + channel = iso_channel_position[position[i]]; + // unsupported layout + if (channel == AV_CHAN_NONE) { + ret = AVERROR_PATCHWELCOME; + goto error; + } + + layout->u.map[i].id = channel; + } + } + + + return 0; + +error: + av_channel_layout_uninit(layout); + return ret; +} diff --git a/libavformat/mov_chan.h b/libavformat/mov_chan.h index 93d98787986..8c807798ab4 100644 --- a/libavformat/mov_chan.h +++ b/libavformat/mov_chan.h @@ -163,4 +163,30 @@ int ff_mov_get_channel_layout_tag(const AVCodecParameters *par, int ff_mov_read_chan(AVFormatContext *s, AVIOContext *pb, AVStream *st, int64_t size); +/** + * Get ISO/IEC 23001-8 ChannelConfiguration from AVChannelLayout. + * + */ +int ff_mov_get_channel_config_from_layout(const AVChannelLayout *layout, int *config); + +/** + * Get AVChannelLayout from ISO/IEC 23001-8 ChannelConfiguration. + * + * @return 0 for success, -1 for doesn't match, layout is untouched on failure + */ + +int ff_mov_get_channel_layout_from_config(int config, AVChannelLayout *layout); + +/** + * Get ISO/IEC 23001-8 OutputChannelPosition from AVChannelLayout. + */ +int ff_mov_get_channel_positions_from_layout(const AVChannelLayout *layout, + uint8_t *position, int position_num); + +/** + * Get AVChannelLayout from ISO/IEC 23001-8 OutputChannelPosition. + */ +int ff_mov_get_layout_from_channel_positions(const uint8_t *position, int position_num, + AVChannelLayout *layout); + #endif /* AVFORMAT_MOV_CHAN_H */ diff --git a/libavformat/movenc.c b/libavformat/movenc.c index 8d31317838b..7ef6cef46ac 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -35,6 +35,7 @@ #include "isom.h" #include "av1.h" #include "avc.h" +#include "evc.h" #include "libavcodec/ac3_parser_internal.h" #include "libavcodec/dnxhddata.h" #include "libavcodec/flac.h" @@ -47,6 +48,7 @@ #include "internal.h" #include "libavutil/avstring.h" #include "libavutil/channel_layout.h" +#include "libavutil/csp.h" #include "libavutil/intfloat.h" #include "libavutil/mathematics.h" #include "libavutil/libm.h" @@ -56,7 +58,6 @@ #include "libavutil/stereo3d.h" #include "libavutil/timecode.h" #include "libavutil/dovi_meta.h" -#include "libavutil/color_utils.h" #include "libavutil/uuid.h" #include "hevc.h" #include "rtpenc.h" @@ -1179,6 +1180,73 @@ static int mov_write_btrt_tag(AVIOContext *pb, MOVTrack *track) return update_size(pb, pos); } +static int mov_write_chnl_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + int config = 0; + int ret; + uint8_t *speaker_pos = NULL; + const AVChannelLayout *layout = &track->par->ch_layout; + + ret = ff_mov_get_channel_config_from_layout(layout, &config); + if (ret || !config) { + config = 0; + speaker_pos = av_malloc(layout->nb_channels); + ret = ff_mov_get_channel_positions_from_layout(layout, + speaker_pos, layout->nb_channels); + if (ret) { + char buf[128] = {0}; + + av_freep(&speaker_pos); + av_channel_layout_describe(layout, buf, sizeof(buf)); + av_log(s, AV_LOG_ERROR, "unsupported channel layout %s\n", buf); + return ret; + } + } + + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "chnl"); + avio_wb32(pb, 0); /* version & flags */ + + avio_w8(pb, 1); /* stream_structure */ + avio_w8(pb, config); + if (config) { + avio_wb64(pb, 0); + } else { + for (int i = 0; i < layout->nb_channels; i++) + avio_w8(pb, speaker_pos[i]); + av_freep(&speaker_pos); + } + + return update_size(pb, pos); +} + +static int mov_write_pcmc_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + int format_flags; + int sample_size; + + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "pcmC"); + avio_wb32(pb, 0); /* version & flags */ + + /* 0x01: indicates little-endian format */ + format_flags = (track->par->codec_id == AV_CODEC_ID_PCM_F32LE || + track->par->codec_id == AV_CODEC_ID_PCM_F64LE || + track->par->codec_id == AV_CODEC_ID_PCM_S16LE || + track->par->codec_id == AV_CODEC_ID_PCM_S24LE || + track->par->codec_id == AV_CODEC_ID_PCM_S32LE); + avio_w8(pb, format_flags); + sample_size = track->par->bits_per_raw_sample; + if (!sample_size) + sample_size = av_get_exact_bits_per_sample(track->par->codec_id); + av_assert0(sample_size); + avio_w8(pb, sample_size); + + return update_size(pb, pos); +} + static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track) { int64_t pos = avio_tell(pb); @@ -1241,13 +1309,7 @@ static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex avio_wb16(pb, 16); avio_wb16(pb, track->audio_vbr ? -2 : 0); /* compression ID */ } else { /* reserved for mp4/3gp */ - if (track->par->codec_id == AV_CODEC_ID_FLAC || - track->par->codec_id == AV_CODEC_ID_ALAC || - track->par->codec_id == AV_CODEC_ID_OPUS) { - avio_wb16(pb, track->par->ch_layout.nb_channels); - } else { - avio_wb16(pb, 2); - } + avio_wb16(pb, track->par->ch_layout.nb_channels); if (track->par->codec_id == AV_CODEC_ID_FLAC || track->par->codec_id == AV_CODEC_ID_ALAC) { avio_wb16(pb, track->par->bits_per_raw_sample); @@ -1311,7 +1373,13 @@ static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex ret = mov_write_dops_tag(s, pb, track); else if (track->par->codec_id == AV_CODEC_ID_TRUEHD) ret = mov_write_dmlp_tag(s, pb, track); - else if (track->vos_len > 0) + else if (tag == MOV_MP4_IPCM_TAG || tag == MOV_MP4_FPCM_TAG) { + if (track->par->ch_layout.nb_channels > 1) + ret = mov_write_chnl_tag(s, pb, track); + if (ret < 0) + return ret; + ret = mov_write_pcmc_tag(s, pb, track); + } else if (track->vos_len > 0) ret = mov_write_glbl_tag(pb, track); if (ret < 0) @@ -1393,6 +1461,21 @@ static int mov_write_hvcc_tag(AVIOContext *pb, MOVTrack *track) return update_size(pb, pos); } +static int mov_write_evcc_tag(AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + + avio_wb32(pb, 0); + ffio_wfourcc(pb, "evcC"); + + if (track->tag == MKTAG('e','v','c','1')) + ff_isom_write_evcc(pb, track->vos_data, track->vos_len, 1); + else + ff_isom_write_evcc(pb, track->vos_data, track->vos_len, 0); + + return update_size(pb, pos); +} + /* also used by all avid codecs (dv, imx, meridien) and their variants */ static int mov_write_avid_tag(AVIOContext *pb, MOVTrack *track) { @@ -1642,6 +1725,16 @@ static int mov_get_h264_codec_tag(AVFormatContext *s, MOVTrack *track) return tag; } +static int mov_get_evc_codec_tag(AVFormatContext *s, MOVTrack *track) +{ + int tag = track->par->codec_tag; + + if (!tag) + tag = MKTAG('e', 'v', 'c', '1'); + + return tag; +} + static const struct { enum AVPixelFormat pix_fmt; uint32_t tag; @@ -1723,6 +1816,8 @@ static unsigned int mov_get_codec_tag(AVFormatContext *s, MOVTrack *track) tag = mov_get_mpeg2_xdcam_codec_tag(s, track); else if (track->par->codec_id == AV_CODEC_ID_H264) tag = mov_get_h264_codec_tag(s, track); + else if (track->par->codec_id == AV_CODEC_ID_EVC) + tag = mov_get_evc_codec_tag(s, track); else if (track->par->codec_id == AV_CODEC_ID_DNXHD) tag = mov_get_dnxhd_codec_tag(s, track); else if (track->par->codec_type == AVMEDIA_TYPE_VIDEO) { @@ -2011,9 +2106,8 @@ static int mov_write_pasp_tag(AVIOContext *pb, MOVTrack *track) static int mov_write_gama_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track, double gamma) { uint32_t gama = 0; - if (gamma <= 0.0) { - gamma = avpriv_get_gamma_from_trc(track->par->color_trc); - } + if (gamma <= 0.0) + gamma = av_csp_approximate_trc_gamma(track->par->color_trc); av_log(s, AV_LOG_DEBUG, "gamma value %g\n", gamma); if (gamma > 1e-6) { @@ -2291,6 +2385,9 @@ static int mov_write_video_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex mov_write_avcc_tag(pb, track); if (track->mode == MODE_IPOD) mov_write_uuid_tag_ipod(pb); + } + else if (track->par->codec_id ==AV_CODEC_ID_EVC) { + mov_write_evcc_tag(pb, track); } else if (track->par->codec_id == AV_CODEC_ID_VP9) { mov_write_vpcc_tag(mov->fc, pb, track); } else if (track->par->codec_id == AV_CODEC_ID_AV1) { @@ -3208,14 +3305,20 @@ static int64_t calc_pts_duration(MOVMuxContext *mov, MOVTrack *track) return end - start; } +static int mov_mdhd_mvhd_tkhd_version(MOVMuxContext *mov, MOVTrack *track, int64_t duration) +{ + if (track && track->mode == MODE_ISM) + return 1; + if (duration < INT32_MAX) + return 0; + return 1; +} + static int mov_write_mdhd_tag(AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track) { int64_t duration = calc_samples_pts_duration(mov, track); - int version = duration < INT32_MAX ? 0 : 1; - - if (track->mode == MODE_ISM) - version = 1; + int version = mov_mdhd_mvhd_tkhd_version(mov, track, duration); (version == 1) ? avio_wb32(pb, 44) : avio_wb32(pb, 32); /* size */ ffio_wfourcc(pb, "mdhd"); @@ -3301,8 +3404,6 @@ static int mov_write_tkhd_tag(AVIOContext *pb, MOVMuxContext *mov, else duration *= mov->avif_loop_count; - version = duration < INT32_MAX ? 0 : 1; - if (st) { if (mov->per_stream_grouping) group = st->index; @@ -3318,8 +3419,7 @@ static int mov_write_tkhd_tag(AVIOContext *pb, MOVMuxContext *mov, if (track->flags & MOV_TRACK_ENABLED) flags |= MOV_TKHD_FLAG_ENABLED; - if (track->mode == MODE_ISM) - version = 1; + version = mov_mdhd_mvhd_tkhd_version(mov, track, duration); (version == 1) ? avio_wb32(pb, 104) : avio_wb32(pb, 92); /* size */ ffio_wfourcc(pb, "tkhd"); @@ -3802,7 +3902,7 @@ static int mov_write_mvhd_tag(AVIOContext *pb, MOVMuxContext *mov) max_track_id = 1; } - version = max_track_len < UINT32_MAX ? 0 : 1; + version = mov_mdhd_mvhd_tkhd_version(mov, NULL, max_track_len); avio_wb32(pb, version == 1 ? 120 : 108); /* size */ ffio_wfourcc(pb, "mvhd"); @@ -3960,13 +4060,13 @@ static int mov_write_loci_tag(AVFormatContext *s, AVIOContext *pb) return 0; ptr = t->value; - longitude = strtod(ptr, &end); + latitude = strtod(ptr, &end); if (end == ptr) { av_log(s, AV_LOG_WARNING, "malformed location metadata\n"); return 0; } ptr = end; - latitude = strtod(ptr, &end); + longitude = strtod(ptr, &end); if (end == ptr) { av_log(s, AV_LOG_WARNING, "malformed location metadata\n"); return 0; @@ -3987,8 +4087,8 @@ static int mov_write_loci_tag(AVFormatContext *s, AVIOContext *pb) avio_wb16(pb, lang); avio_write(pb, place, strlen(place) + 1); avio_w8(pb, 0); /* role of place (0 == shooting location, 1 == real location, 2 == fictional location) */ - avio_wb32(pb, latitude_fix); avio_wb32(pb, longitude_fix); + avio_wb32(pb, latitude_fix); avio_wb32(pb, altitude_fix); avio_write(pb, astronomical_body, strlen(astronomical_body) + 1); avio_w8(pb, 0); /* additional notes, null terminated string */ @@ -4797,8 +4897,8 @@ static int mov_write_trun_tag(AVIOContext *pb, MOVMuxContext *mov, if (i > first && get_sample_flags(track, &track->cluster[i]) != track->default_sample_flags) flags |= MOV_TRUN_SAMPLE_FLAGS; } - if (!(flags & MOV_TRUN_SAMPLE_FLAGS) && track->entry > 0 && - get_sample_flags(track, &track->cluster[0]) != track->default_sample_flags) + if (!(flags & MOV_TRUN_SAMPLE_FLAGS) && track->entry > first && + get_sample_flags(track, &track->cluster[first]) != track->default_sample_flags) flags |= MOV_TRUN_FIRST_SAMPLE_FLAGS; if (track->flags & MOV_TRACK_CTTS) flags |= MOV_TRUN_SAMPLE_CTS; @@ -6062,6 +6162,7 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_HEVC || par->codec_id == AV_CODEC_ID_VP9 || + par->codec_id == AV_CODEC_ID_EVC || par->codec_id == AV_CODEC_ID_TRUEHD) && !trk->vos_len && !TAG_IS_AVCI(trk->tag)) { /* copy frame to create needed atoms */ @@ -7721,6 +7822,7 @@ static const AVCodecTag codec_mp4_tags[] = { { AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '3') }, { AV_CODEC_ID_HEVC, MKTAG('h', 'e', 'v', '1') }, { AV_CODEC_ID_HEVC, MKTAG('h', 'v', 'c', '1') }, + { AV_CODEC_ID_EVC, MKTAG('e', 'v', 'c', '1') }, { AV_CODEC_ID_MPEG2VIDEO, MKTAG('m', 'p', '4', 'v') }, { AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', '4', 'v') }, { AV_CODEC_ID_MJPEG, MKTAG('m', 'p', '4', 'v') }, @@ -7751,6 +7853,20 @@ static const AVCodecTag codec_mp4_tags[] = { { AV_CODEC_ID_MPEGH_3D_AUDIO, MKTAG('m', 'h', 'm', '1') }, { AV_CODEC_ID_TTML, MOV_MP4_TTML_TAG }, { AV_CODEC_ID_TTML, MOV_ISMV_TTML_TAG }, + + /* ISO/IEC 23003-5 integer formats */ + { AV_CODEC_ID_PCM_S16BE, MOV_MP4_IPCM_TAG }, + { AV_CODEC_ID_PCM_S16LE, MOV_MP4_IPCM_TAG }, + { AV_CODEC_ID_PCM_S24BE, MOV_MP4_IPCM_TAG }, + { AV_CODEC_ID_PCM_S24LE, MOV_MP4_IPCM_TAG }, + { AV_CODEC_ID_PCM_S32BE, MOV_MP4_IPCM_TAG }, + { AV_CODEC_ID_PCM_S32LE, MOV_MP4_IPCM_TAG }, + /* ISO/IEC 23003-5 floating-point formats */ + { AV_CODEC_ID_PCM_F32BE, MOV_MP4_FPCM_TAG }, + { AV_CODEC_ID_PCM_F32LE, MOV_MP4_FPCM_TAG }, + { AV_CODEC_ID_PCM_F64BE, MOV_MP4_FPCM_TAG }, + { AV_CODEC_ID_PCM_F64LE, MOV_MP4_FPCM_TAG }, + { AV_CODEC_ID_NONE, 0 }, }; #if CONFIG_MP4_MUXER || CONFIG_PSP_MUXER @@ -7805,182 +7921,182 @@ static const AVClass mov_avif_muxer_class = { #endif #if CONFIG_MOV_MUXER -const AVOutputFormat ff_mov_muxer = { - .name = "mov", - .long_name = NULL_IF_CONFIG_SMALL("QuickTime / MOV"), - .extensions = "mov", +const FFOutputFormat ff_mov_muxer = { + .p.name = "mov", + .p.long_name = NULL_IF_CONFIG_SMALL("QuickTime / MOV"), + .p.extensions = "mov", .priv_data_size = sizeof(MOVMuxContext), - .audio_codec = AV_CODEC_ID_AAC, - .video_codec = CONFIG_LIBX264_ENCODER ? + .p.audio_codec = AV_CODEC_ID_AAC, + .p.video_codec = CONFIG_LIBX264_ENCODER ? AV_CODEC_ID_H264 : AV_CODEC_ID_MPEG4, .init = mov_init, .write_header = mov_write_header, .write_packet = mov_write_packet, .write_trailer = mov_write_trailer, .deinit = mov_free, - .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, - .codec_tag = (const AVCodecTag* const []){ + .p.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, + .p.codec_tag = (const AVCodecTag* const []){ ff_codec_movvideo_tags, ff_codec_movaudio_tags, ff_codec_movsubtitle_tags, 0 }, .check_bitstream = mov_check_bitstream, - .priv_class = &mov_isobmff_muxer_class, + .p.priv_class = &mov_isobmff_muxer_class, }; #endif #if CONFIG_TGP_MUXER -const AVOutputFormat ff_tgp_muxer = { - .name = "3gp", - .long_name = NULL_IF_CONFIG_SMALL("3GP (3GPP file format)"), - .extensions = "3gp", +const FFOutputFormat ff_tgp_muxer = { + .p.name = "3gp", + .p.long_name = NULL_IF_CONFIG_SMALL("3GP (3GPP file format)"), + .p.extensions = "3gp", .priv_data_size = sizeof(MOVMuxContext), - .audio_codec = AV_CODEC_ID_AMR_NB, - .video_codec = AV_CODEC_ID_H263, + .p.audio_codec = AV_CODEC_ID_AMR_NB, + .p.video_codec = AV_CODEC_ID_H263, .init = mov_init, .write_header = mov_write_header, .write_packet = mov_write_packet, .write_trailer = mov_write_trailer, .deinit = mov_free, - .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, - .codec_tag = codec_3gp_tags_list, + .p.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, + .p.codec_tag = codec_3gp_tags_list, .check_bitstream = mov_check_bitstream, - .priv_class = &mov_isobmff_muxer_class, + .p.priv_class = &mov_isobmff_muxer_class, }; #endif #if CONFIG_MP4_MUXER -const AVOutputFormat ff_mp4_muxer = { - .name = "mp4", - .long_name = NULL_IF_CONFIG_SMALL("MP4 (MPEG-4 Part 14)"), - .mime_type = "video/mp4", - .extensions = "mp4", +const FFOutputFormat ff_mp4_muxer = { + .p.name = "mp4", + .p.long_name = NULL_IF_CONFIG_SMALL("MP4 (MPEG-4 Part 14)"), + .p.mime_type = "video/mp4", + .p.extensions = "mp4", .priv_data_size = sizeof(MOVMuxContext), - .audio_codec = AV_CODEC_ID_AAC, - .video_codec = CONFIG_LIBX264_ENCODER ? + .p.audio_codec = AV_CODEC_ID_AAC, + .p.video_codec = CONFIG_LIBX264_ENCODER ? AV_CODEC_ID_H264 : AV_CODEC_ID_MPEG4, .init = mov_init, .write_header = mov_write_header, .write_packet = mov_write_packet, .write_trailer = mov_write_trailer, .deinit = mov_free, - .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, - .codec_tag = mp4_codec_tags_list, + .p.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, + .p.codec_tag = mp4_codec_tags_list, .check_bitstream = mov_check_bitstream, - .priv_class = &mov_isobmff_muxer_class, + .p.priv_class = &mov_isobmff_muxer_class, }; #endif #if CONFIG_PSP_MUXER -const AVOutputFormat ff_psp_muxer = { - .name = "psp", - .long_name = NULL_IF_CONFIG_SMALL("PSP MP4 (MPEG-4 Part 14)"), - .extensions = "mp4,psp", +const FFOutputFormat ff_psp_muxer = { + .p.name = "psp", + .p.long_name = NULL_IF_CONFIG_SMALL("PSP MP4 (MPEG-4 Part 14)"), + .p.extensions = "mp4,psp", .priv_data_size = sizeof(MOVMuxContext), - .audio_codec = AV_CODEC_ID_AAC, - .video_codec = CONFIG_LIBX264_ENCODER ? + .p.audio_codec = AV_CODEC_ID_AAC, + .p.video_codec = CONFIG_LIBX264_ENCODER ? AV_CODEC_ID_H264 : AV_CODEC_ID_MPEG4, .init = mov_init, .write_header = mov_write_header, .write_packet = mov_write_packet, .write_trailer = mov_write_trailer, .deinit = mov_free, - .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, - .codec_tag = mp4_codec_tags_list, + .p.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, + .p.codec_tag = mp4_codec_tags_list, .check_bitstream = mov_check_bitstream, - .priv_class = &mov_isobmff_muxer_class, + .p.priv_class = &mov_isobmff_muxer_class, }; #endif #if CONFIG_TG2_MUXER -const AVOutputFormat ff_tg2_muxer = { - .name = "3g2", - .long_name = NULL_IF_CONFIG_SMALL("3GP2 (3GPP2 file format)"), - .extensions = "3g2", +const FFOutputFormat ff_tg2_muxer = { + .p.name = "3g2", + .p.long_name = NULL_IF_CONFIG_SMALL("3GP2 (3GPP2 file format)"), + .p.extensions = "3g2", .priv_data_size = sizeof(MOVMuxContext), - .audio_codec = AV_CODEC_ID_AMR_NB, - .video_codec = AV_CODEC_ID_H263, + .p.audio_codec = AV_CODEC_ID_AMR_NB, + .p.video_codec = AV_CODEC_ID_H263, .init = mov_init, .write_header = mov_write_header, .write_packet = mov_write_packet, .write_trailer = mov_write_trailer, .deinit = mov_free, - .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, - .codec_tag = codec_3gp_tags_list, + .p.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, + .p.codec_tag = codec_3gp_tags_list, .check_bitstream = mov_check_bitstream, - .priv_class = &mov_isobmff_muxer_class, + .p.priv_class = &mov_isobmff_muxer_class, }; #endif #if CONFIG_IPOD_MUXER -const AVOutputFormat ff_ipod_muxer = { - .name = "ipod", - .long_name = NULL_IF_CONFIG_SMALL("iPod H.264 MP4 (MPEG-4 Part 14)"), - .mime_type = "video/mp4", - .extensions = "m4v,m4a,m4b", +const FFOutputFormat ff_ipod_muxer = { + .p.name = "ipod", + .p.long_name = NULL_IF_CONFIG_SMALL("iPod H.264 MP4 (MPEG-4 Part 14)"), + .p.mime_type = "video/mp4", + .p.extensions = "m4v,m4a,m4b", .priv_data_size = sizeof(MOVMuxContext), - .audio_codec = AV_CODEC_ID_AAC, - .video_codec = AV_CODEC_ID_H264, + .p.audio_codec = AV_CODEC_ID_AAC, + .p.video_codec = AV_CODEC_ID_H264, .init = mov_init, .write_header = mov_write_header, .write_packet = mov_write_packet, .write_trailer = mov_write_trailer, .deinit = mov_free, - .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, - .codec_tag = (const AVCodecTag* const []){ codec_ipod_tags, 0 }, + .p.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, + .p.codec_tag = (const AVCodecTag* const []){ codec_ipod_tags, 0 }, .check_bitstream = mov_check_bitstream, - .priv_class = &mov_isobmff_muxer_class, + .p.priv_class = &mov_isobmff_muxer_class, }; #endif #if CONFIG_ISMV_MUXER -const AVOutputFormat ff_ismv_muxer = { - .name = "ismv", - .long_name = NULL_IF_CONFIG_SMALL("ISMV/ISMA (Smooth Streaming)"), - .mime_type = "video/mp4", - .extensions = "ismv,isma", +const FFOutputFormat ff_ismv_muxer = { + .p.name = "ismv", + .p.long_name = NULL_IF_CONFIG_SMALL("ISMV/ISMA (Smooth Streaming)"), + .p.mime_type = "video/mp4", + .p.extensions = "ismv,isma", .priv_data_size = sizeof(MOVMuxContext), - .audio_codec = AV_CODEC_ID_AAC, - .video_codec = AV_CODEC_ID_H264, + .p.audio_codec = AV_CODEC_ID_AAC, + .p.video_codec = AV_CODEC_ID_H264, .init = mov_init, .write_header = mov_write_header, .write_packet = mov_write_packet, .write_trailer = mov_write_trailer, .deinit = mov_free, - .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, - .codec_tag = (const AVCodecTag* const []){ + .p.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, + .p.codec_tag = (const AVCodecTag* const []){ codec_mp4_tags, codec_ism_tags, 0 }, .check_bitstream = mov_check_bitstream, - .priv_class = &mov_isobmff_muxer_class, + .p.priv_class = &mov_isobmff_muxer_class, }; #endif #if CONFIG_F4V_MUXER -const AVOutputFormat ff_f4v_muxer = { - .name = "f4v", - .long_name = NULL_IF_CONFIG_SMALL("F4V Adobe Flash Video"), - .mime_type = "application/f4v", - .extensions = "f4v", +const FFOutputFormat ff_f4v_muxer = { + .p.name = "f4v", + .p.long_name = NULL_IF_CONFIG_SMALL("F4V Adobe Flash Video"), + .p.mime_type = "application/f4v", + .p.extensions = "f4v", .priv_data_size = sizeof(MOVMuxContext), - .audio_codec = AV_CODEC_ID_AAC, - .video_codec = AV_CODEC_ID_H264, + .p.audio_codec = AV_CODEC_ID_AAC, + .p.video_codec = AV_CODEC_ID_H264, .init = mov_init, .write_header = mov_write_header, .write_packet = mov_write_packet, .write_trailer = mov_write_trailer, .deinit = mov_free, - .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH, - .codec_tag = (const AVCodecTag* const []){ codec_f4v_tags, 0 }, + .p.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH, + .p.codec_tag = (const AVCodecTag* const []){ codec_f4v_tags, 0 }, .check_bitstream = mov_check_bitstream, - .priv_class = &mov_isobmff_muxer_class, + .p.priv_class = &mov_isobmff_muxer_class, }; #endif #if CONFIG_AVIF_MUXER -const AVOutputFormat ff_avif_muxer = { - .name = "avif", - .long_name = NULL_IF_CONFIG_SMALL("AVIF"), - .mime_type = "image/avif", - .extensions = "avif", +const FFOutputFormat ff_avif_muxer = { + .p.name = "avif", + .p.long_name = NULL_IF_CONFIG_SMALL("AVIF"), + .p.mime_type = "image/avif", + .p.extensions = "avif", .priv_data_size = sizeof(MOVMuxContext), - .video_codec = AV_CODEC_ID_AV1, + .p.video_codec = AV_CODEC_ID_AV1, .init = mov_init, .write_header = mov_write_header, .write_packet = mov_write_packet, .write_trailer = avif_write_trailer, .deinit = mov_free, - .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH, - .codec_tag = codec_avif_tags_list, - .priv_class = &mov_avif_muxer_class, + .p.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH, + .p.codec_tag = codec_avif_tags_list, + .p.priv_class = &mov_avif_muxer_class, }; #endif diff --git a/libavformat/mp3enc.c b/libavformat/mp3enc.c index 91874fa14ef..5e81f72a59a 100644 --- a/libavformat/mp3enc.c +++ b/libavformat/mp3enc.c @@ -23,6 +23,7 @@ #include "avio_internal.h" #include "id3v1.h" #include "id3v2.h" +#include "mux.h" #include "rawenc.h" #include "libavutil/avstring.h" #include "libavcodec/mpegaudio.h" @@ -636,20 +637,20 @@ static void mp3_deinit(struct AVFormatContext *s) av_freep(&mp3->xing_frame); } -const AVOutputFormat ff_mp3_muxer = { - .name = "mp3", - .long_name = NULL_IF_CONFIG_SMALL("MP3 (MPEG audio layer 3)"), - .mime_type = "audio/mpeg", - .extensions = "mp3", +const FFOutputFormat ff_mp3_muxer = { + .p.name = "mp3", + .p.long_name = NULL_IF_CONFIG_SMALL("MP3 (MPEG audio layer 3)"), + .p.mime_type = "audio/mpeg", + .p.extensions = "mp3", .priv_data_size = sizeof(MP3Context), - .audio_codec = AV_CODEC_ID_MP3, - .video_codec = AV_CODEC_ID_PNG, + .p.audio_codec = AV_CODEC_ID_MP3, + .p.video_codec = AV_CODEC_ID_PNG, .init = mp3_init, .write_header = mp3_write_header, .write_packet = mp3_write_packet, .write_trailer = mp3_write_trailer, .deinit = mp3_deinit, .query_codec = query_codec, - .flags = AVFMT_NOTIMESTAMPS, - .priv_class = &mp3_muxer_class, + .p.flags = AVFMT_NOTIMESTAMPS, + .p.priv_class = &mp3_muxer_class, }; diff --git a/libavformat/mpeg.c b/libavformat/mpeg.c index 864b08d8f8b..781c3162d6d 100644 --- a/libavformat/mpeg.c +++ b/libavformat/mpeg.c @@ -549,6 +549,9 @@ static int mpegps_read_packet(AVFormatContext *s, } else if (es_type == STREAM_TYPE_AUDIO_AC3) { codec_id = AV_CODEC_ID_AC3; type = AVMEDIA_TYPE_AUDIO; + } else if (es_type == 0x90) { + codec_id = AV_CODEC_ID_PCM_ALAW; + type = AVMEDIA_TYPE_AUDIO; } else if (m->imkh_cctv && es_type == 0x91) { codec_id = AV_CODEC_ID_PCM_MULAW; type = AVMEDIA_TYPE_AUDIO; diff --git a/libavformat/mpegenc.c b/libavformat/mpegenc.c index 3ab4bd3f9b2..c06e3082963 100644 --- a/libavformat/mpegenc.c +++ b/libavformat/mpegenc.c @@ -35,6 +35,7 @@ #include "avio_internal.h" #include "internal.h" #include "mpeg.h" +#include "mux.h" #define MAX_PAYLOAD_SIZE 4096 @@ -86,10 +87,10 @@ typedef struct MpegMuxContext { int preload; } MpegMuxContext; -extern const AVOutputFormat ff_mpeg1vcd_muxer; -extern const AVOutputFormat ff_mpeg2dvd_muxer; -extern const AVOutputFormat ff_mpeg2svcd_muxer; -extern const AVOutputFormat ff_mpeg2vob_muxer; +extern const FFOutputFormat ff_mpeg1vcd_muxer; +extern const FFOutputFormat ff_mpeg2dvd_muxer; +extern const FFOutputFormat ff_mpeg2svcd_muxer; +extern const FFOutputFormat ff_mpeg2vob_muxer; static int put_pack_header(AVFormatContext *ctx, uint8_t *buf, int64_t timestamp) @@ -307,12 +308,12 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx) int video_bitrate; s->packet_number = 0; - s->is_vcd = (CONFIG_MPEG1VCD_MUXER && ctx->oformat == &ff_mpeg1vcd_muxer); - s->is_svcd = (CONFIG_MPEG2SVCD_MUXER && ctx->oformat == &ff_mpeg2svcd_muxer); - s->is_mpeg2 = ((CONFIG_MPEG2VOB_MUXER && ctx->oformat == &ff_mpeg2vob_muxer) || - (CONFIG_MPEG2DVD_MUXER && ctx->oformat == &ff_mpeg2dvd_muxer) || - (CONFIG_MPEG2SVCD_MUXER && ctx->oformat == &ff_mpeg2svcd_muxer)); - s->is_dvd = (CONFIG_MPEG2DVD_MUXER && ctx->oformat == &ff_mpeg2dvd_muxer); + s->is_vcd = (CONFIG_MPEG1VCD_MUXER && ctx->oformat == &ff_mpeg1vcd_muxer.p); + s->is_svcd = (CONFIG_MPEG2SVCD_MUXER && ctx->oformat == &ff_mpeg2svcd_muxer.p); + s->is_mpeg2 = ((CONFIG_MPEG2VOB_MUXER && ctx->oformat == &ff_mpeg2vob_muxer.p) || + (CONFIG_MPEG2DVD_MUXER && ctx->oformat == &ff_mpeg2dvd_muxer.p) || + (CONFIG_MPEG2SVCD_MUXER && ctx->oformat == &ff_mpeg2svcd_muxer.p)); + s->is_dvd = (CONFIG_MPEG2DVD_MUXER && ctx->oformat == &ff_mpeg2dvd_muxer.p); if (ctx->packet_size) { if (ctx->packet_size < 20 || ctx->packet_size > (1 << 23) + 10) { @@ -1293,87 +1294,87 @@ static const AVClass mpeg_class = { }; #if CONFIG_MPEG1SYSTEM_MUXER -const AVOutputFormat ff_mpeg1system_muxer = { - .name = "mpeg", - .long_name = NULL_IF_CONFIG_SMALL("MPEG-1 Systems / MPEG program stream"), - .mime_type = "video/mpeg", - .extensions = "mpg,mpeg", +const FFOutputFormat ff_mpeg1system_muxer = { + .p.name = "mpeg", + .p.long_name = NULL_IF_CONFIG_SMALL("MPEG-1 Systems / MPEG program stream"), + .p.mime_type = "video/mpeg", + .p.extensions = "mpg,mpeg", .priv_data_size = sizeof(MpegMuxContext), - .audio_codec = AV_CODEC_ID_MP2, - .video_codec = AV_CODEC_ID_MPEG1VIDEO, + .p.audio_codec = AV_CODEC_ID_MP2, + .p.video_codec = AV_CODEC_ID_MPEG1VIDEO, .write_header = mpeg_mux_init, .write_packet = mpeg_mux_write_packet, .write_trailer = mpeg_mux_end, .deinit = mpeg_mux_deinit, - .priv_class = &mpeg_class, + .p.priv_class = &mpeg_class, }; #endif #if CONFIG_MPEG1VCD_MUXER -const AVOutputFormat ff_mpeg1vcd_muxer = { - .name = "vcd", - .long_name = NULL_IF_CONFIG_SMALL("MPEG-1 Systems / MPEG program stream (VCD)"), - .mime_type = "video/mpeg", +const FFOutputFormat ff_mpeg1vcd_muxer = { + .p.name = "vcd", + .p.long_name = NULL_IF_CONFIG_SMALL("MPEG-1 Systems / MPEG program stream (VCD)"), + .p.mime_type = "video/mpeg", .priv_data_size = sizeof(MpegMuxContext), - .audio_codec = AV_CODEC_ID_MP2, - .video_codec = AV_CODEC_ID_MPEG1VIDEO, + .p.audio_codec = AV_CODEC_ID_MP2, + .p.video_codec = AV_CODEC_ID_MPEG1VIDEO, .write_header = mpeg_mux_init, .write_packet = mpeg_mux_write_packet, .write_trailer = mpeg_mux_end, .deinit = mpeg_mux_deinit, - .priv_class = &mpeg_class, + .p.priv_class = &mpeg_class, }; #endif #if CONFIG_MPEG2VOB_MUXER -const AVOutputFormat ff_mpeg2vob_muxer = { - .name = "vob", - .long_name = NULL_IF_CONFIG_SMALL("MPEG-2 PS (VOB)"), - .mime_type = "video/mpeg", - .extensions = "vob", +const FFOutputFormat ff_mpeg2vob_muxer = { + .p.name = "vob", + .p.long_name = NULL_IF_CONFIG_SMALL("MPEG-2 PS (VOB)"), + .p.mime_type = "video/mpeg", + .p.extensions = "vob", .priv_data_size = sizeof(MpegMuxContext), - .audio_codec = AV_CODEC_ID_MP2, - .video_codec = AV_CODEC_ID_MPEG2VIDEO, + .p.audio_codec = AV_CODEC_ID_MP2, + .p.video_codec = AV_CODEC_ID_MPEG2VIDEO, .write_header = mpeg_mux_init, .write_packet = mpeg_mux_write_packet, .write_trailer = mpeg_mux_end, .deinit = mpeg_mux_deinit, - .priv_class = &mpeg_class, + .p.priv_class = &mpeg_class, }; #endif /* Same as mpeg2vob_mux except that the pack size is 2324 */ #if CONFIG_MPEG2SVCD_MUXER -const AVOutputFormat ff_mpeg2svcd_muxer = { - .name = "svcd", - .long_name = NULL_IF_CONFIG_SMALL("MPEG-2 PS (SVCD)"), - .mime_type = "video/mpeg", - .extensions = "vob", +const FFOutputFormat ff_mpeg2svcd_muxer = { + .p.name = "svcd", + .p.long_name = NULL_IF_CONFIG_SMALL("MPEG-2 PS (SVCD)"), + .p.mime_type = "video/mpeg", + .p.extensions = "vob", .priv_data_size = sizeof(MpegMuxContext), - .audio_codec = AV_CODEC_ID_MP2, - .video_codec = AV_CODEC_ID_MPEG2VIDEO, + .p.audio_codec = AV_CODEC_ID_MP2, + .p.video_codec = AV_CODEC_ID_MPEG2VIDEO, .write_header = mpeg_mux_init, .write_packet = mpeg_mux_write_packet, .write_trailer = mpeg_mux_end, .deinit = mpeg_mux_deinit, - .priv_class = &mpeg_class, + .p.priv_class = &mpeg_class, }; #endif /* Same as mpeg2vob_mux except the 'is_dvd' flag is set to produce NAV pkts */ #if CONFIG_MPEG2DVD_MUXER -const AVOutputFormat ff_mpeg2dvd_muxer = { - .name = "dvd", - .long_name = NULL_IF_CONFIG_SMALL("MPEG-2 PS (DVD VOB)"), - .mime_type = "video/mpeg", - .extensions = "dvd", +const FFOutputFormat ff_mpeg2dvd_muxer = { + .p.name = "dvd", + .p.long_name = NULL_IF_CONFIG_SMALL("MPEG-2 PS (DVD VOB)"), + .p.mime_type = "video/mpeg", + .p.extensions = "dvd", .priv_data_size = sizeof(MpegMuxContext), - .audio_codec = AV_CODEC_ID_MP2, - .video_codec = AV_CODEC_ID_MPEG2VIDEO, + .p.audio_codec = AV_CODEC_ID_MP2, + .p.video_codec = AV_CODEC_ID_MPEG2VIDEO, .write_header = mpeg_mux_init, .write_packet = mpeg_mux_write_packet, .write_trailer = mpeg_mux_end, .deinit = mpeg_mux_deinit, - .priv_class = &mpeg_class, + .p.priv_class = &mpeg_class, }; #endif diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c index d97702fcd7d..0b3edda8179 100644 --- a/libavformat/mpegts.c +++ b/libavformat/mpegts.c @@ -859,6 +859,7 @@ static const StreamType HLS_SAMPLE_ENC_types[] = { static const StreamType REGD_types[] = { { MKTAG('d', 'r', 'a', 'c'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC }, { MKTAG('A', 'C', '-', '3'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3 }, + { MKTAG('A', 'C', '-', '4'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC4 }, { MKTAG('B', 'S', 'S', 'D'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_S302M }, { MKTAG('D', 'T', 'S', '1'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, { MKTAG('D', 'T', 'S', '2'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, @@ -866,6 +867,7 @@ static const StreamType REGD_types[] = { { MKTAG('E', 'A', 'C', '3'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_EAC3 }, { MKTAG('H', 'E', 'V', 'C'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_HEVC }, { MKTAG('K', 'L', 'V', 'A'), AVMEDIA_TYPE_DATA, AV_CODEC_ID_SMPTE_KLV }, + { MKTAG('V', 'A', 'N', 'C'), AVMEDIA_TYPE_DATA, AV_CODEC_ID_SMPTE_2038 }, { MKTAG('I', 'D', '3', ' '), AVMEDIA_TYPE_DATA, AV_CODEC_ID_TIMED_ID3 }, { MKTAG('V', 'C', '-', '1'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VC1 }, { MKTAG('O', 'p', 'u', 's'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_OPUS }, @@ -1303,7 +1305,7 @@ static int mpegts_push_data(MpegTSFilter *filter, p += sl_header_bytes; buf_size -= sl_header_bytes; } - if (pes->stream_type == 0x15 && buf_size >= 5) { + if (pes->st->codecpar->codec_id == AV_CODEC_ID_SMPTE_KLV && buf_size >= 5) { /* skip metadata access unit header */ pes->pes_header_size += 5; p += 5; @@ -2175,8 +2177,12 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE; st->codecpar->codec_id = AV_CODEC_ID_ARIB_CAPTION; - st->codecpar->profile = picked_profile; + if (st->codecpar->profile != picked_profile) { + st->codecpar->profile = picked_profile; + sti->need_context_update = 1; + } sti->request_probe = 0; + sti->need_parsing = 0; } break; case 0xb0: /* DOVI video stream descriptor */ diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c index 48d39e6a7da..700fc549df9 100644 --- a/libavformat/mpegtsenc.c +++ b/libavformat/mpegtsenc.c @@ -29,6 +29,8 @@ #include "libavcodec/ac3_parser_internal.h" #include "libavcodec/avcodec.h" +#include "libavcodec/bytestream.h" +#include "libavcodec/h264.h" #include "libavcodec/startcode.h" #include "avformat.h" @@ -420,6 +422,9 @@ static int get_dvb_stream_type(AVFormatContext *s, AVStream *st) case AV_CODEC_ID_TIMED_ID3: stream_type = STREAM_TYPE_METADATA; break; + case AV_CODEC_ID_SMPTE_2038: + stream_type = STREAM_TYPE_PRIVATE_DATA; + break; case AV_CODEC_ID_DVB_SUBTITLE: case AV_CODEC_ID_DVB_TELETEXT: case AV_CODEC_ID_ARIB_CAPTION: @@ -802,6 +807,8 @@ static int mpegts_write_pmt(AVFormatContext *s, MpegTSService *service) case AVMEDIA_TYPE_DATA: if (codec_id == AV_CODEC_ID_SMPTE_KLV) { put_registration_descriptor(&q, MKTAG('K', 'L', 'V', 'A')); + } else if (codec_id == AV_CODEC_ID_SMPTE_2038) { + put_registration_descriptor(&q, MKTAG('V', 'A', 'N', 'C')); } else if (codec_id == AV_CODEC_ID_TIMED_ID3) { const char *tag = "ID3 "; *q++ = METADATA_DESCRIPTOR; @@ -1453,7 +1460,8 @@ static int get_pes_stream_id(AVFormatContext *s, AVStream *st, int stream_id, in st->codecpar->codec_id == AV_CODEC_ID_TIMED_ID3) { return STREAM_ID_PRIVATE_STREAM_1; } else if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA) { - if (stream_id == STREAM_ID_PRIVATE_STREAM_1) /* asynchronous KLV */ + if (st->codecpar->codec_id == AV_CODEC_ID_SMPTE_KLV && + stream_id == STREAM_ID_PRIVATE_STREAM_1) /* asynchronous KLV */ *async = 1; return stream_id != -1 ? stream_id : STREAM_ID_METADATA_STREAM; } else { @@ -1877,6 +1885,7 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) if (st->codecpar->codec_id == AV_CODEC_ID_H264) { const uint8_t *p = buf, *buf_end = p + size; + const uint8_t *found_aud = NULL, *found_aud_end = NULL; uint32_t state = -1; int extradd = (pkt->flags & AV_PKT_FLAG_KEY) ? st->codecpar->extradata_size : 0; int ret = ff_check_h264_startcode(s, st, pkt); @@ -1886,27 +1895,58 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) if (extradd && AV_RB24(st->codecpar->extradata) > 1) extradd = 0; + /* Ensure that all pictures are prefixed with an AUD, and that + * IDR pictures are also prefixed with SPS and PPS. SPS and PPS + * are assumed to be available in 'extradata' if not found in-band. */ do { p = avpriv_find_start_code(p, buf_end, &state); av_log(s, AV_LOG_TRACE, "nal %"PRId32"\n", state & 0x1f); - if ((state & 0x1f) == 7) + if ((state & 0x1f) == H264_NAL_SPS) extradd = 0; - } while (p < buf_end && (state & 0x1f) != 9 && - (state & 0x1f) != 5 && (state & 0x1f) != 1); - - if ((state & 0x1f) != 5) + if ((state & 0x1f) == H264_NAL_AUD) { + found_aud = p - 4; // start of the 0x000001 start code. + found_aud_end = p + 1; // first byte past the AUD. + if (found_aud < buf) + found_aud = buf; + if (buf_end < found_aud_end) + found_aud_end = buf_end; + } + } while (p < buf_end + && (state & 0x1f) != H264_NAL_IDR_SLICE + && (state & 0x1f) != H264_NAL_SLICE + && (extradd > 0 || !found_aud)); + if ((state & 0x1f) != H264_NAL_IDR_SLICE) extradd = 0; - if ((state & 0x1f) != 9) { // AUD NAL + + if (!found_aud) { + /* Prefix 'buf' with the missing AUD, and extradata if needed. */ data = av_malloc(pkt->size + 6 + extradd); if (!data) return AVERROR(ENOMEM); memcpy(data + 6, st->codecpar->extradata, extradd); memcpy(data + 6 + extradd, pkt->data, pkt->size); AV_WB32(data, 0x00000001); - data[4] = 0x09; + data[4] = H264_NAL_AUD; data[5] = 0xf0; // any slice type (0xe) + rbsp stop one bit buf = data; size = pkt->size + 6 + extradd; + } else if (extradd != 0) { + /* Move the AUD up to the beginning of the frame, where the H.264 + * spec requires it to appear. Emit the extradata after it. */ + PutByteContext pb; + const int new_pkt_size = pkt->size + 1 + extradd; + data = av_malloc(new_pkt_size); + if (!data) + return AVERROR(ENOMEM); + bytestream2_init_writer(&pb, data, new_pkt_size); + bytestream2_put_byte(&pb, 0x00); + bytestream2_put_buffer(&pb, found_aud, found_aud_end - found_aud); + bytestream2_put_buffer(&pb, st->codecpar->extradata, extradd); + bytestream2_put_buffer(&pb, pkt->data, found_aud - pkt->data); + bytestream2_put_buffer(&pb, found_aud_end, buf_end - found_aud_end); + av_assert0(new_pkt_size == bytestream2_tell_p(&pb)); + buf = data; + size = new_pkt_size; } } else if (st->codecpar->codec_id == AV_CODEC_ID_AAC) { if (pkt->size < 2) { @@ -2308,19 +2348,19 @@ static const AVClass mpegts_muxer_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVOutputFormat ff_mpegts_muxer = { - .name = "mpegts", - .long_name = NULL_IF_CONFIG_SMALL("MPEG-TS (MPEG-2 Transport Stream)"), - .mime_type = "video/MP2T", - .extensions = "ts,m2t,m2ts,mts", +const FFOutputFormat ff_mpegts_muxer = { + .p.name = "mpegts", + .p.long_name = NULL_IF_CONFIG_SMALL("MPEG-TS (MPEG-2 Transport Stream)"), + .p.mime_type = "video/MP2T", + .p.extensions = "ts,m2t,m2ts,mts", .priv_data_size = sizeof(MpegTSWrite), - .audio_codec = AV_CODEC_ID_MP2, - .video_codec = AV_CODEC_ID_MPEG2VIDEO, + .p.audio_codec = AV_CODEC_ID_MP2, + .p.video_codec = AV_CODEC_ID_MPEG2VIDEO, .init = mpegts_init, .write_packet = mpegts_write_packet, .write_trailer = mpegts_write_end, .deinit = mpegts_deinit, .check_bitstream = mpegts_check_bitstream, - .flags = AVFMT_ALLOW_FLUSH | AVFMT_VARIABLE_FPS | AVFMT_NODIMENSIONS, - .priv_class = &mpegts_muxer_class, + .p.flags = AVFMT_ALLOW_FLUSH | AVFMT_VARIABLE_FPS | AVFMT_NODIMENSIONS, + .p.priv_class = &mpegts_muxer_class, }; diff --git a/libavformat/mpjpeg.c b/libavformat/mpjpeg.c index c5e5d1e2864..81ace8e9ee1 100644 --- a/libavformat/mpjpeg.c +++ b/libavformat/mpjpeg.c @@ -20,6 +20,7 @@ */ #include "libavutil/opt.h" #include "avformat.h" +#include "mux.h" /* Multipart JPEG */ @@ -61,16 +62,16 @@ static const AVClass mpjpeg_muxer_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVOutputFormat ff_mpjpeg_muxer = { - .name = "mpjpeg", - .long_name = NULL_IF_CONFIG_SMALL("MIME multipart JPEG"), - .mime_type = "multipart/x-mixed-replace;boundary=" BOUNDARY_TAG, - .extensions = "mjpg", +const FFOutputFormat ff_mpjpeg_muxer = { + .p.name = "mpjpeg", + .p.long_name = NULL_IF_CONFIG_SMALL("MIME multipart JPEG"), + .p.mime_type = "multipart/x-mixed-replace;boundary=" BOUNDARY_TAG, + .p.extensions = "mjpg", .priv_data_size = sizeof(MPJPEGContext), - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_MJPEG, + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_MJPEG, .write_header = mpjpeg_write_header, .write_packet = mpjpeg_write_packet, - .flags = AVFMT_NOTIMESTAMPS, - .priv_class = &mpjpeg_muxer_class, + .p.flags = AVFMT_NOTIMESTAMPS, + .p.priv_class = &mpjpeg_muxer_class, }; diff --git a/libavformat/mpsubdec.c b/libavformat/mpsubdec.c index d290a41fb9c..03745635753 100644 --- a/libavformat/mpsubdec.c +++ b/libavformat/mpsubdec.c @@ -116,9 +116,10 @@ static int mpsub_read_header(AVFormatContext *s) AVPacket *sub; const int64_t pos = avio_tell(s->pb); - ff_subtitles_read_chunk(s->pb, &buf); + res = ff_subtitles_read_chunk(s->pb, &buf); + if (res < 0) goto end; if (buf.len) { - sub = ff_subtitles_queue_insert(&mpsub->q, buf.str, buf.len, 0); + sub = ff_subtitles_queue_insert_bprint(&mpsub->q, &buf, 0); if (!sub) { res = AVERROR(ENOMEM); goto end; diff --git a/libavformat/mux.c b/libavformat/mux.c index b2b5be63b44..415bd3948f5 100644 --- a/libavformat/mux.c +++ b/libavformat/mux.c @@ -102,7 +102,7 @@ int avformat_alloc_output_context2(AVFormatContext **avctx, const AVOutputFormat if (format) { oformat = av_guess_format(format, NULL, NULL); if (!oformat) { - av_log(s, AV_LOG_ERROR, "Requested output format '%s' is not a suitable output format\n", format); + av_log(s, AV_LOG_ERROR, "Requested output format '%s' is not known.\n", format); ret = AVERROR(EINVAL); goto error; } @@ -110,16 +110,18 @@ int avformat_alloc_output_context2(AVFormatContext **avctx, const AVOutputFormat oformat = av_guess_format(NULL, filename, NULL); if (!oformat) { ret = AVERROR(EINVAL); - av_log(s, AV_LOG_ERROR, "Unable to find a suitable output format for '%s'\n", - filename); + av_log(s, AV_LOG_ERROR, + "Unable to choose an output format for '%s'; " + "use a standard extension for the filename or specify " + "the format manually.\n", filename); goto error; } } } s->oformat = oformat; - if (s->oformat->priv_data_size > 0) { - s->priv_data = av_mallocz(s->oformat->priv_data_size); + if (ffofmt(s->oformat)->priv_data_size > 0) { + s->priv_data = av_mallocz(ffofmt(s->oformat)->priv_data_size); if (!s->priv_data) goto nomem; if (s->oformat->priv_class) { @@ -182,7 +184,7 @@ static int init_muxer(AVFormatContext *s, AVDictionary **options) { FFFormatContext *const si = ffformatcontext(s); AVDictionary *tmp = NULL; - const AVOutputFormat *of = s->oformat; + const FFOutputFormat *of = ffofmt(s->oformat); AVDictionaryEntry *e; int ret = 0; @@ -201,7 +203,7 @@ static int init_muxer(AVFormatContext *s, AVDictionary **options) } // some sanity checks - if (s->nb_streams == 0 && !(of->flags & AVFMT_NOSTREAMS)) { + if (s->nb_streams == 0 && !(of->p.flags & AVFMT_NOSTREAMS)) { av_log(s, AV_LOG_ERROR, "No streams to mux were specified\n"); ret = AVERROR(EINVAL); goto fail; @@ -251,7 +253,7 @@ FF_ENABLE_DEPRECATION_WARNINGS break; case AVMEDIA_TYPE_VIDEO: if ((par->width <= 0 || par->height <= 0) && - !(of->flags & AVFMT_NODIMENSIONS)) { + !(of->p.flags & AVFMT_NODIMENSIONS)) { av_log(s, AV_LOG_ERROR, "dimensions not set\n"); ret = AVERROR(EINVAL); goto fail; @@ -281,11 +283,11 @@ FF_ENABLE_DEPRECATION_WARNINGS sti->is_intra_only = ff_is_intra_only(par->codec_id); - if (of->codec_tag) { + if (of->p.codec_tag) { if ( par->codec_tag && par->codec_id == AV_CODEC_ID_RAWVIDEO - && ( av_codec_get_tag(of->codec_tag, par->codec_id) == 0 - || av_codec_get_tag(of->codec_tag, par->codec_id) == MKTAG('r', 'a', 'w', ' ')) + && ( av_codec_get_tag(of->p.codec_tag, par->codec_id) == 0 + || av_codec_get_tag(of->p.codec_tag, par->codec_id) == MKTAG('r', 'a', 'w', ' ')) && !validate_codec_tag(s, st)) { // the current rawvideo encoding system ends up setting // the wrong codec_tag for avi/mov, we override it here @@ -301,10 +303,11 @@ FF_ENABLE_DEPRECATION_WARNINGS goto fail; } } else - par->codec_tag = av_codec_get_tag(of->codec_tag, par->codec_id); + par->codec_tag = av_codec_get_tag(of->p.codec_tag, par->codec_id); } - if (par->codec_type != AVMEDIA_TYPE_ATTACHMENT) + if (par->codec_type != AVMEDIA_TYPE_ATTACHMENT && + par->codec_id != AV_CODEC_ID_SMPTE_2038) si->nb_interleaved_streams++; } si->interleave_packet = of->interleave_packet; @@ -319,8 +322,8 @@ FF_ENABLE_DEPRECATION_WARNINGS ret = AVERROR(ENOMEM); goto fail; } - if (of->priv_class) { - *(const AVClass **)s->priv_data = of->priv_class; + if (of->p.priv_class) { + *(const AVClass **)s->priv_data = of->p.priv_class; av_opt_set_defaults(s->priv_data); if ((ret = av_opt_set_dict2(s->priv_data, &tmp, AV_OPT_SEARCH_CHILDREN)) < 0) goto fail; @@ -343,10 +346,10 @@ FF_ENABLE_DEPRECATION_WARNINGS *options = tmp; } - if (s->oformat->init) { - if ((ret = s->oformat->init(s)) < 0) { - if (s->oformat->deinit) - s->oformat->deinit(s); + if (of->init) { + if ((ret = of->init(s)) < 0) { + if (of->deinit) + of->deinit(s); return ret; } return ret == 0; @@ -420,8 +423,9 @@ static void flush_if_needed(AVFormatContext *s) static void deinit_muxer(AVFormatContext *s) { FFFormatContext *const si = ffformatcontext(s); - if (s->oformat && s->oformat->deinit && si->initialized) - s->oformat->deinit(s); + const FFOutputFormat *const of = ffofmt(s->oformat); + if (of && of->deinit && si->initialized) + of->deinit(s); si->initialized = si->streams_initialized = 0; } @@ -437,7 +441,7 @@ int avformat_init_output(AVFormatContext *s, AVDictionary **options) si->initialized = 1; si->streams_initialized = ret; - if (s->oformat->init && ret) { + if (ffofmt(s->oformat)->init && ret) { if ((ret = init_pts(s)) < 0) return ret; @@ -460,8 +464,8 @@ int avformat_write_header(AVFormatContext *s, AVDictionary **options) if (!(s->oformat->flags & AVFMT_NOFILE) && s->pb) avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_HEADER); - if (s->oformat->write_header) { - ret = s->oformat->write_header(s); + if (ffofmt(s->oformat)->write_header) { + ret = ffofmt(s->oformat)->write_header(s); if (ret >= 0 && s->pb && s->pb->error < 0) ret = s->pb->error; if (ret < 0) @@ -724,9 +728,9 @@ static int write_packet(AVFormatContext *s, AVPacket *pkt) if ((pkt->flags & AV_PKT_FLAG_UNCODED_FRAME)) { AVFrame **frame = (AVFrame **)pkt->data; av_assert0(pkt->size == sizeof(*frame)); - ret = s->oformat->write_uncoded_frame(s, pkt->stream_index, frame, 0); + ret = ffofmt(s->oformat)->write_uncoded_frame(s, pkt->stream_index, frame, 0); } else { - ret = s->oformat->write_packet(s, pkt); + ret = ffofmt(s->oformat)->write_packet(s, pkt); } if (s->pb && ret >= 0) { @@ -941,7 +945,8 @@ int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *pkt, ++stream_count; } else if (par->codec_type != AVMEDIA_TYPE_ATTACHMENT && par->codec_id != AV_CODEC_ID_VP8 && - par->codec_id != AV_CODEC_ID_VP9) { + par->codec_id != AV_CODEC_ID_VP9 && + par->codec_id != AV_CODEC_ID_SMPTE_2038) { ++noninterleaved_count; } } @@ -1078,9 +1083,9 @@ static int check_bitstream(AVFormatContext *s, FFStream *sti, AVPacket *pkt) if (!(s->flags & AVFMT_FLAG_AUTO_BSF)) return 1; - if (s->oformat->check_bitstream) { + if (ffofmt(s->oformat)->check_bitstream) { if (!sti->bitstream_checked) { - if ((ret = s->oformat->check_bitstream(s, &sti->pub, pkt)) < 0) + if ((ret = ffofmt(s->oformat)->check_bitstream(s, &sti->pub, pkt)) < 0) return ret; else if (ret == 1) sti->bitstream_checked = 1; @@ -1198,7 +1203,7 @@ int av_write_frame(AVFormatContext *s, AVPacket *in) if (!in) { if (s->oformat->flags & AVFMT_ALLOW_FLUSH) { - ret = s->oformat->write_packet(s, NULL); + ret = ffofmt(s->oformat)->write_packet(s, NULL); flush_if_needed(s); if (ret >= 0 && s->pb && s->pb->error < 0) ret = s->pb->error; @@ -1273,14 +1278,12 @@ int av_write_trailer(AVFormatContext *s) if (ret >= 0) ret = ret1; - if (s->oformat->write_trailer) { + if (ffofmt(s->oformat)->write_trailer) { if (!(s->oformat->flags & AVFMT_NOFILE) && s->pb) avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_TRAILER); - if (ret >= 0) { - ret = s->oformat->write_trailer(s); - } else { - s->oformat->write_trailer(s); - } + ret1 = ffofmt(s->oformat)->write_trailer(s); + if (ret >= 0) + ret = ret1; } deinit_muxer(s); @@ -1303,9 +1306,10 @@ int av_write_trailer(AVFormatContext *s) int av_get_output_timestamp(struct AVFormatContext *s, int stream, int64_t *dts, int64_t *wall) { - if (!s->oformat || !s->oformat->get_output_timestamp) + const FFOutputFormat *const of = ffofmt(s->oformat); + if (!of || !of->get_output_timestamp) return AVERROR(ENOSYS); - s->oformat->get_output_timestamp(s, stream, dts, wall); + of->get_output_timestamp(s, stream, dts, wall); return 0; } @@ -1395,7 +1399,7 @@ static int write_uncoded_frame_internal(AVFormatContext *s, int stream_index, AVPacket *pkt = si->parse_pkt; av_assert0(s->oformat); - if (!s->oformat->write_uncoded_frame) { + if (!ffofmt(s->oformat)->write_uncoded_frame) { av_frame_free(&frame); return AVERROR(ENOSYS); } @@ -1452,9 +1456,10 @@ int av_interleaved_write_uncoded_frame(AVFormatContext *s, int stream_index, int av_write_uncoded_frame_query(AVFormatContext *s, int stream_index) { - av_assert0(s->oformat); - if (!s->oformat->write_uncoded_frame) + const FFOutputFormat *const of = ffofmt(s->oformat); + av_assert0(of); + if (!of->write_uncoded_frame) return AVERROR(ENOSYS); - return s->oformat->write_uncoded_frame(s, stream_index, NULL, - AV_WRITE_UNCODED_FRAME_QUERY); + return of->write_uncoded_frame(s, stream_index, NULL, + AV_WRITE_UNCODED_FRAME_QUERY); } diff --git a/libavformat/mux.h b/libavformat/mux.h index 1bfcaf795fd..c2de45400c2 100644 --- a/libavformat/mux.h +++ b/libavformat/mux.h @@ -25,6 +25,119 @@ #include "libavcodec/packet.h" #include "avformat.h" +struct AVDeviceInfoList; + +typedef struct FFOutputFormat { + /** + * The public AVOutputFormat. See avformat.h for it. + */ + AVOutputFormat p; + /** + * size of private data so that it can be allocated in the wrapper + */ + int priv_data_size; + + /** + * Internal flags. See FF_FMT_FLAG_* in internal.h. + */ + int flags_internal; + + int (*write_header)(AVFormatContext *); + /** + * Write a packet. If AVFMT_ALLOW_FLUSH is set in flags, + * pkt can be NULL in order to flush data buffered in the muxer. + * When flushing, return 0 if there still is more data to flush, + * or 1 if everything was flushed and there is no more buffered + * data. + */ + int (*write_packet)(AVFormatContext *, AVPacket *pkt); + int (*write_trailer)(AVFormatContext *); + /** + * A format-specific function for interleavement. + * If unset, packets will be interleaved by dts. + * + * @param s An AVFormatContext for output. pkt will be added to + * resp. taken from its packet buffer. + * @param[in,out] pkt A packet to be interleaved if has_packet is set; + * also used to return packets. If no packet is returned + * (e.g. on error), pkt is blank on return. + * @param flush 1 if no further packets are available as input and + * all remaining packets should be output. + * @param has_packet If set, pkt contains a packet to be interleaved + * on input; otherwise pkt is blank on input. + * @return 1 if a packet was output, 0 if no packet could be output, + * < 0 if an error occurred + */ + int (*interleave_packet)(AVFormatContext *s, AVPacket *pkt, + int flush, int has_packet); + /** + * Test if the given codec can be stored in this container. + * + * @return 1 if the codec is supported, 0 if it is not. + * A negative number if unknown. + * MKTAG('A', 'P', 'I', 'C') if the codec is only supported as AV_DISPOSITION_ATTACHED_PIC + */ + int (*query_codec)(enum AVCodecID id, int std_compliance); + + void (*get_output_timestamp)(AVFormatContext *s, int stream, + int64_t *dts, int64_t *wall); + /** + * Allows sending messages from application to device. + */ + int (*control_message)(AVFormatContext *s, int type, + void *data, size_t data_size); + + /** + * Write an uncoded AVFrame. + * + * See av_write_uncoded_frame() for details. + * + * The library will free *frame afterwards, but the muxer can prevent it + * by setting the pointer to NULL. + */ + int (*write_uncoded_frame)(AVFormatContext *, int stream_index, + AVFrame **frame, unsigned flags); + /** + * Returns device list with it properties. + * @see avdevice_list_devices() for more details. + */ + int (*get_device_list)(AVFormatContext *s, struct AVDeviceInfoList *device_list); + /** + * Initialize format. May allocate data here, and set any AVFormatContext or + * AVStream parameters that need to be set before packets are sent. + * This method must not write output. + * + * Return 0 if streams were fully configured, 1 if not, negative AVERROR on failure + * + * Any allocations made here must be freed in deinit(). + */ + int (*init)(AVFormatContext *); + /** + * Deinitialize format. If present, this is called whenever the muxer is being + * destroyed, regardless of whether or not the header has been written. + * + * If a trailer is being written, this is called after write_trailer(). + * + * This is called if init() fails as well. + */ + void (*deinit)(AVFormatContext *); + /** + * Set up any necessary bitstream filtering and extract any extra data needed + * for the global header. + * + * @note pkt might have been directly forwarded by a meta-muxer; therefore + * pkt->stream_index as well as the pkt's timebase might be invalid. + * Return 0 if more packets from this stream must be checked; 1 if not. + */ + int (*check_bitstream)(AVFormatContext *s, AVStream *st, + const AVPacket *pkt); +} FFOutputFormat; + +static inline const FFOutputFormat *ffofmt(const AVOutputFormat *fmt) +{ + return (const FFOutputFormat*)fmt; +} + /** * Add packet to an AVFormatContext's packet_buffer list, determining its * interleaved position using compare() function argument. diff --git a/libavformat/mux_utils.c b/libavformat/mux_utils.c index 764c834fa2c..3e63b8039af 100644 --- a/libavformat/mux_utils.c +++ b/libavformat/mux_utils.c @@ -45,14 +45,13 @@ int avformat_query_codec(const AVOutputFormat *ofmt, enum AVCodecID codec_id, { if (ofmt) { unsigned int codec_tag; - if (ofmt->query_codec) - return ofmt->query_codec(codec_id, std_compliance); + if (ffofmt(ofmt)->query_codec) + return ffofmt(ofmt)->query_codec(codec_id, std_compliance); else if (ofmt->codec_tag) return !!av_codec_get_tag2(ofmt->codec_tag, codec_id, &codec_tag); else if (codec_id == ofmt->video_codec || codec_id == ofmt->audio_codec || - codec_id == ofmt->subtitle_codec || - codec_id == ofmt->data_codec) + codec_id == ofmt->subtitle_codec) return 1; } return AVERROR_PATCHWELCOME; diff --git a/libavformat/mxf.c b/libavformat/mxf.c index 8ef928b8fc2..a73e40e514b 100644 --- a/libavformat/mxf.c +++ b/libavformat/mxf.c @@ -78,6 +78,8 @@ const MXFCodecUL ff_mxf_codec_uls[] = { { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x02,0x03,0x02,0x01,0x00 }, 15, AV_CODEC_ID_AC3 }, { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x02,0x03,0x02,0x05,0x00 }, 15, AV_CODEC_ID_MP2 }, /* MP2 or MP3 */ //{ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x02,0x03,0x02,0x1C,0x00 }, 15, AV_CODEC_ID_DOLBY_E }, /* Dolby-E */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0D,0x04,0x02,0x02,0x02,0x04,0x03,0x00,0x00 }, 14, AV_CODEC_ID_AAC }, /* MPEG-2 AAC SMPTE 381-4 */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0D,0x04,0x02,0x02,0x02,0x04,0x04,0x00,0x00 }, 14, AV_CODEC_ID_AAC }, /* MPEG-4 AAC SMPTE 381-4 */ { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_CODEC_ID_NONE }, }; diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index fa58854fcde..34230ece980 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -1198,11 +1198,15 @@ static int mxf_read_essence_container_data(void *arg, AVIOContext *pb, int tag, static int mxf_read_index_entry_array(AVIOContext *pb, MXFIndexTableSegment *segment) { int i, length; + uint32_t nb_index_entries; if (segment->temporal_offset_entries) return AVERROR_INVALIDDATA; - segment->nb_index_entries = avio_rb32(pb); + nb_index_entries = avio_rb32(pb); + if (nb_index_entries > INT_MAX) + return AVERROR_INVALIDDATA; + segment->nb_index_entries = nb_index_entries; length = avio_rb32(pb); if(segment->nb_index_entries && length < 11) @@ -1624,6 +1628,9 @@ static const MXFCodecUL mxf_sound_essence_container_uls[] = { { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x01,0x01,0x01 }, 14, AV_CODEC_ID_PCM_S16LE, NULL, 13 }, /* D-10 Mapping 50Mbps PAL Extended Template */ { { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0xff,0x4b,0x46,0x41,0x41,0x00,0x0d,0x4d,0x4F }, 14, AV_CODEC_ID_PCM_S16LE }, /* 0001GL00.MXF.A1.mxf_opatom.mxf */ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x03,0x04,0x02,0x02,0x02,0x03,0x03,0x01,0x00 }, 14, AV_CODEC_ID_AAC }, /* MPEG-2 AAC ADTS (legacy) */ + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x0d,0x01,0x03,0x01,0x02,0x16,0x00,0x00 }, 14, AV_CODEC_ID_AAC, NULL, 14 }, /* AAC ADIF (SMPTE 381-4) */ + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x0d,0x01,0x03,0x01,0x02,0x17,0x00,0x00 }, 14, AV_CODEC_ID_AAC, NULL, 14 }, /* AAC ADTS (SMPTE 381-4) */ + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x0d,0x01,0x03,0x01,0x02,0x18,0x00,0x00 }, 14, AV_CODEC_ID_AAC, NULL, 14 }, /* AAC LATM/LOAS (SMPTE 381-4) */ { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_CODEC_ID_NONE }, }; @@ -1933,6 +1940,14 @@ static int mxf_compute_ptses_fake_index(MXFContext *mxf, MXFIndexTable *index_ta return 0; } + if (s->nb_index_entries != s->index_duration && + s->nb_index_entries != s->index_duration + 1 && /* Avid index */ + s->nb_index_entries != s->index_duration * 2 + 1) { + index_table->nb_ptses = 0; + av_log(mxf->fc, AV_LOG_ERROR, "ignoring IndexSID %d, duration does not match nb_index_entries\n", s->index_sid); + return 0; + } + index_table->nb_ptses += s->index_duration; } @@ -1987,11 +2002,11 @@ static int mxf_compute_ptses_fake_index(MXFContext *mxf, MXFIndexTable *index_ta int index_delta = 1; int n = s->nb_index_entries; - if (s->nb_index_entries == 2 * s->index_duration + 1) { + if (s->nb_index_entries == 2 * s->index_duration + 1) index_delta = 2; /* Avid index */ - /* ignore the last entry - it's the size of the essence container */ + if (s->nb_index_entries == index_delta * s->index_duration + 1) + /* ignore the last entry - it's the size of the essence container in Avid */ n--; - } for (j = 0; j < n; j += index_delta, x++) { int offset = s->temporal_offset_entries[j] / index_delta; @@ -3017,6 +3032,8 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) st->codecpar->codec_id = AV_CODEC_ID_PCM_S32BE; } else if (st->codecpar->codec_id == AV_CODEC_ID_MP2) { sti->need_parsing = AVSTREAM_PARSE_FULL; + } else if (st->codecpar->codec_id == AV_CODEC_ID_AAC) { + sti->need_parsing = AVSTREAM_PARSE_FULL; } st->codecpar->bits_per_coded_sample = av_get_bits_per_sample(st->codecpar->codec_id); @@ -3727,7 +3744,10 @@ static int mxf_read_header(AVFormatContext *s) while (!avio_feof(s->pb)) { const MXFMetadataReadTableEntry *metadata; - if (klv_read_packet(&klv, s->pb) < 0) { + ret = klv_read_packet(&klv, s->pb); + if (ret < 0 || IS_KLV_KEY(klv.key, ff_mxf_random_index_pack_key)) { + if (ret >= 0 && avio_size(s->pb) > klv.next_klv) + av_log(s, AV_LOG_WARNING, "data after the RandomIndexPack, assuming end of file\n"); /* EOF - seek to previous partition or stop */ if(mxf_parse_handle_partition_or_eof(mxf) <= 0) break; diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c index 58c551c83c3..d8252ed68f3 100644 --- a/libavformat/mxfenc.c +++ b/libavformat/mxfenc.c @@ -51,6 +51,7 @@ #include "libavcodec/golomb.h" #include "libavcodec/h264.h" #include "libavcodec/packet_internal.h" +#include "libavcodec/rangecoder.h" #include "libavcodec/startcode.h" #include "avformat.h" #include "avio_internal.h" @@ -61,8 +62,11 @@ #include "config.h" #include "version.h" -extern const AVOutputFormat ff_mxf_d10_muxer; -extern const AVOutputFormat ff_mxf_opatom_muxer; +extern const FFOutputFormat ff_mxf_d10_muxer; +extern const FFOutputFormat ff_mxf_opatom_muxer; + +#define IS_D10(s) ((s)->oformat == &ff_mxf_d10_muxer.p) +#define IS_OPATOM(s) ((s)->oformat == &ff_mxf_opatom_muxer.p) #define EDIT_UNITS_PER_BODY 250 #define KAG_SIZE 512 @@ -99,6 +103,7 @@ typedef struct MXFStreamContext { int b_picture_count; ///< maximum number of consecutive b pictures, used in mpeg-2 descriptor int low_delay; ///< low delay, used in mpeg-2 descriptor int avc_intra; + int micro_version; ///< format micro_version, used in ffv1 descriptor } MXFStreamContext; typedef struct MXFContainerEssenceEntry { @@ -127,6 +132,7 @@ enum ULIndex { INDEX_H264, INDEX_S436M, INDEX_PRORES, + INDEX_FFV1, }; static const struct { @@ -141,6 +147,7 @@ static const struct { { AV_CODEC_ID_JPEG2000, INDEX_JPEG2000 }, { AV_CODEC_ID_H264, INDEX_H264 }, { AV_CODEC_ID_PRORES, INDEX_PRORES }, + { AV_CODEC_ID_FFV1, INDEX_FFV1 }, { AV_CODEC_ID_NONE } }; @@ -148,6 +155,7 @@ static void mxf_write_wav_desc(AVFormatContext *s, AVStream *st); static void mxf_write_aes3_desc(AVFormatContext *s, AVStream *st); static void mxf_write_mpegvideo_desc(AVFormatContext *s, AVStream *st); static void mxf_write_h264_desc(AVFormatContext *s, AVStream *st); +static void mxf_write_ffv1_desc(AVFormatContext *s, AVStream *st); static void mxf_write_cdci_desc(AVFormatContext *s, AVStream *st); static void mxf_write_generic_sound_desc(AVFormatContext *s, AVStream *st); static void mxf_write_s436m_anc_desc(AVFormatContext *s, AVStream *st); @@ -205,6 +213,11 @@ static const MXFContainerEssenceEntry mxf_essence_container_uls[] = { { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01,0x15,0x01,0x17,0x00 }, { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0d,0x04,0x01,0x02,0x02,0x03,0x06,0x03,0x00 }, mxf_write_cdci_desc }, + // FFV1 + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0d,0x0d,0x01,0x03,0x01,0x02,0x23,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01,0x15,0x01,0x1d,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0d,0x04,0x01,0x02,0x02,0x03,0x09,0x00,0x00 }, + mxf_write_ffv1_desc }, { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, @@ -229,6 +242,15 @@ static const UID mxf_d10_container_uls[] = { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x06,0x01 }, // D-10 525/50 NTSC 30mb/s }; + +static const UID mxf_ffv1_codec_uls[] = { + { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0D,0x04,0x01,0x02,0x02,0x03,0x09,0x01,0x00 }, // FFV1 version 0 + { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0D,0x04,0x01,0x02,0x02,0x03,0x09,0x02,0x00 }, // FFV1 version 1 + { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0D,0x04,0x01,0x02,0x02,0x03,0x09,0x03,0x00 }, // FFV1 version 2 (was only experimental) + { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0D,0x04,0x01,0x02,0x02,0x03,0x09,0x04,0x00 }, // FFV1 version 3 + { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0D,0x04,0x01,0x02,0x02,0x03,0x09,0x05,0x00 }, // FFV1 version 4 +}; + static const uint8_t product_uid[] = { 0xAD,0xAB,0x44,0x24,0x2f,0x25,0x4d,0xc7,0x92,0xff,0x29,0xbd,0x00,0x0c,0x00,0x02}; static const uint8_t uuid_base[] = { 0xAD,0xAB,0x44,0x24,0x2f,0x25,0x4d,0xc7,0x92,0xff }; static const uint8_t umid_ul[] = { 0x06,0x0A,0x2B,0x34,0x01,0x01,0x01,0x05,0x01,0x01,0x0D,0x00,0x13 }; @@ -387,6 +409,10 @@ static const MXFLocalTagPair mxf_local_tag_batch[] = { { 0x8302, FF_MXF_MasteringDisplayWhitePointChromaticity }, { 0x8303, FF_MXF_MasteringDisplayMaximumLuminance }, { 0x8304, FF_MXF_MasteringDisplayMinimumLuminance }, + // FFV1 + { 0xDFD9, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x01,0x06,0x0C,0x06,0x00,0x00,0x00}}, /* FFV1 Micro-version */ + { 0xDFDA, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x01,0x06,0x0C,0x05,0x00,0x00,0x00}}, /* FFV1 Version */ + { 0xDFDB, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x01,0x06,0x0C,0x01,0x00,0x00,0x00}}, /* FFV1 Initialization Metadata */ }; #define MXF_NUM_TAGS FF_ARRAY_ELEMS(mxf_local_tag_batch) @@ -523,7 +549,7 @@ static void mxf_write_primer_pack(AVFormatContext *s) MXFContext *mxf = s->priv_data; AVIOContext *pb = s->pb; int local_tag_number = MXF_NUM_TAGS, i; - int will_have_avc_tags = 0, will_have_mastering_tags = 0; + int will_have_avc_tags = 0, will_have_mastering_tags = 0, will_have_ffv1_tags = 0; for (i = 0; i < s->nb_streams; i++) { MXFStreamContext *sc = s->streams[i]->priv_data; @@ -533,6 +559,9 @@ static void mxf_write_primer_pack(AVFormatContext *s) if (av_stream_get_side_data(s->streams[i], AV_PKT_DATA_MASTERING_DISPLAY_METADATA, NULL)) { will_have_mastering_tags = 1; } + if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_FFV1) { + will_have_ffv1_tags = 1; + } } if (!mxf->store_user_comments) { @@ -541,8 +570,11 @@ static void mxf_write_primer_pack(AVFormatContext *s) mxf_mark_tag_unused(mxf, 0x5003); } - if (!will_have_avc_tags) { + if (!will_have_avc_tags && !will_have_ffv1_tags) { mxf_mark_tag_unused(mxf, 0x8100); + } + + if (!will_have_avc_tags) { mxf_mark_tag_unused(mxf, 0x8200); mxf_mark_tag_unused(mxf, 0x8201); mxf_mark_tag_unused(mxf, 0x8202); @@ -555,6 +587,12 @@ static void mxf_write_primer_pack(AVFormatContext *s) mxf_mark_tag_unused(mxf, 0x8304); } + if (!will_have_ffv1_tags) { + mxf_mark_tag_unused(mxf, 0xDFD9); + mxf_mark_tag_unused(mxf, 0xDFDA); + mxf_mark_tag_unused(mxf, 0xDFDB); + } + for (i = 0; i < MXF_NUM_TAGS; i++) { if (mxf->unused_tags[i]) { local_tag_number--; @@ -667,7 +705,7 @@ static void mxf_write_preface(AVFormatContext *s) // operational pattern mxf_write_local_tag(s, 16, 0x3B09); - if (s->oformat == &ff_mxf_opatom_muxer) + if (IS_OPATOM(s)) avio_write(pb, opatom_ul, 16); else avio_write(pb, op1a_ul, 16); @@ -765,7 +803,7 @@ static void mxf_write_identification(AVFormatContext *s) AVDictionaryEntry *product_entry = av_dict_get(s->metadata, "product_name", NULL, 0); AVDictionaryEntry *version_entry = av_dict_get(s->metadata, "product_version", NULL, 0); const char *company = com_entry ? com_entry->value : "FFmpeg"; - const char *product = product_entry ? product_entry->value : s->oformat != &ff_mxf_opatom_muxer ? "OP1a Muxer" : "OPAtom Muxer"; + const char *product = product_entry ? product_entry->value : !IS_OPATOM(s) ? "OP1a Muxer" : "OPAtom Muxer"; const char *platform = s->flags & AVFMT_FLAG_BITEXACT ? "Lavf" : PLATFORM_IDENT; const char *version = version_entry ? version_entry->value : s->flags & AVFMT_FLAG_BITEXACT ? "0.0.0" : @@ -866,7 +904,7 @@ static void mxf_write_track(AVFormatContext *s, AVStream *st, MXFPackage *packag // write edit rate mxf_write_local_tag(s, 8, 0x4B01); - if (st == mxf->timecode_track && s->oformat == &ff_mxf_opatom_muxer) { + if (st == mxf->timecode_track && IS_OPATOM(s)) { avio_wb32(pb, mxf->tc.rate.num); avio_wb32(pb, mxf->tc.rate.den); } else { @@ -902,7 +940,7 @@ static void mxf_write_common_fields(AVFormatContext *s, AVStream *st) // write duration mxf_write_local_tag(s, 8, 0x0202); - if (st != mxf->timecode_track && s->oformat == &ff_mxf_opatom_muxer && st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + if (st != mxf->timecode_track && IS_OPATOM(s) && st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { avio_wb64(pb, mxf->body_offset / mxf->edit_unit_byte_count); } else { avio_wb64(pb, mxf->duration); @@ -1066,7 +1104,7 @@ static int64_t mxf_write_generic_desc(AVFormatContext *s, AVStream *st, const UI avio_wb32(pb, st->index+2); mxf_write_local_tag(s, 8, 0x3001); - if (s->oformat == &ff_mxf_d10_muxer) { + if (IS_D10(s)) { avio_wb32(pb, mxf->time_base.den); avio_wb32(pb, mxf->time_base.num); } else { @@ -1091,9 +1129,11 @@ static const UID mxf_mpegvideo_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53, static const UID mxf_wav_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x48,0x00 }; static const UID mxf_aes3_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x47,0x00 }; static const UID mxf_cdci_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x28,0x00 }; +static const UID mxf_rgba_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x29,0x00 }; static const UID mxf_generic_sound_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x42,0x00 }; static const UID mxf_avc_subdescriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x6E,0x00 }; +static const UID mxf_ffv1_subdescriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x81,0x03 }; static inline uint16_t rescale_mastering_chroma(AVRational q) { @@ -1109,8 +1149,9 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID { MXFStreamContext *sc = st->priv_data; AVIOContext *pb = s->pb; - int stored_width = 0; - int stored_height = (st->codecpar->height+15)/16*16; + int stored_width = st->codecpar->width; + int stored_height = st->codecpar->height; + int display_width; int display_height; int f1, f2; const MXFCodecUL *color_primaries_ul; @@ -1129,16 +1170,28 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID else if (st->codecpar->height == 720) stored_width = 1280; } - if (!stored_width) - stored_width = (st->codecpar->width+15)/16*16; + display_width = stored_width; + + switch (st->codecpar->codec_id) { + case AV_CODEC_ID_MPEG2VIDEO: + case AV_CODEC_ID_H264: + //Based on 16x16 macroblocks + stored_width = (stored_width+15)/16*16; + stored_height = (stored_height+15)/16*16; + break; + default: + break; + } + //Stored width mxf_write_local_tag(s, 4, 0x3203); avio_wb32(pb, stored_width); + //Stored height mxf_write_local_tag(s, 4, 0x3202); avio_wb32(pb, stored_height>>sc->interlaced); - if (s->oformat == &ff_mxf_d10_muxer) { + if (IS_D10(s)) { //Stored F2 Offset mxf_write_local_tag(s, 4, 0x3216); avio_wb32(pb, 0); @@ -1154,7 +1207,7 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID //Sampled width mxf_write_local_tag(s, 4, 0x3205); - avio_wb32(pb, stored_width); + avio_wb32(pb, display_width); //Samples height mxf_write_local_tag(s, 4, 0x3204); @@ -1168,8 +1221,9 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID mxf_write_local_tag(s, 4, 0x3207); avio_wb32(pb, 0); + //Display width mxf_write_local_tag(s, 4, 0x3209); - avio_wb32(pb, stored_width); + avio_wb32(pb, display_width); if (st->codecpar->height == 608) // PAL + VBI display_height = 576; @@ -1178,6 +1232,7 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID else display_height = st->codecpar->height; + //Display height mxf_write_local_tag(s, 4, 0x3208); avio_wb32(pb, display_height>>sc->interlaced); @@ -1195,41 +1250,43 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID avio_wb32(pb, -((st->codecpar->height - display_height)&1)); } - // component depth - mxf_write_local_tag(s, 4, 0x3301); - avio_wb32(pb, sc->component_depth); + if (key != mxf_rgba_descriptor_key) { + // component depth + mxf_write_local_tag(s, 4, 0x3301); + avio_wb32(pb, sc->component_depth); - // horizontal subsampling - mxf_write_local_tag(s, 4, 0x3302); - avio_wb32(pb, sc->h_chroma_sub_sample); + // horizontal subsampling + mxf_write_local_tag(s, 4, 0x3302); + avio_wb32(pb, sc->h_chroma_sub_sample); - // vertical subsampling - mxf_write_local_tag(s, 4, 0x3308); - avio_wb32(pb, sc->v_chroma_sub_sample); + // vertical subsampling + mxf_write_local_tag(s, 4, 0x3308); + avio_wb32(pb, sc->v_chroma_sub_sample); - // color siting - mxf_write_local_tag(s, 1, 0x3303); - avio_w8(pb, sc->color_siting); + // color siting + mxf_write_local_tag(s, 1, 0x3303); + avio_w8(pb, sc->color_siting); - // Padding Bits - mxf_write_local_tag(s, 2, 0x3307); - avio_wb16(pb, 0); + // Padding Bits + mxf_write_local_tag(s, 2, 0x3307); + avio_wb16(pb, 0); - if (st->codecpar->color_range != AVCOL_RANGE_UNSPECIFIED) { - int black = 0, - white = (1<component_depth) - 1, - color = (1<component_depth); - if (st->codecpar->color_range == AVCOL_RANGE_MPEG) { - black = 1 << (sc->component_depth - 4); - white = 235 << (sc->component_depth - 8); - color = (14 << (sc->component_depth - 4)) + 1; + if (st->codecpar->color_range != AVCOL_RANGE_UNSPECIFIED) { + int black = 0, + white = (1<component_depth) - 1, + color = (1<component_depth); + if (st->codecpar->color_range == AVCOL_RANGE_MPEG) { + black = 1 << (sc->component_depth - 4); + white = 235 << (sc->component_depth - 8); + color = (14 << (sc->component_depth - 4)) + 1; + } + mxf_write_local_tag(s, 4, 0x3304); + avio_wb32(pb, black); + mxf_write_local_tag(s, 4, 0x3305); + avio_wb32(pb, white); + mxf_write_local_tag(s, 4, 0x3306); + avio_wb32(pb, color); } - mxf_write_local_tag(s, 4, 0x3304); - avio_wb32(pb, black); - mxf_write_local_tag(s, 4, 0x3305); - avio_wb32(pb, white); - mxf_write_local_tag(s, 4, 0x3306); - avio_wb32(pb, color); } if (sc->signal_standard) { @@ -1326,6 +1383,13 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID mxf_write_uuid(pb, AVCSubDescriptor, 0); } + if (st->codecpar->codec_id == AV_CODEC_ID_FFV1) { + // write ffv1 sub descriptor ref + mxf_write_local_tag(s, 8 + 16, 0x8100); + mxf_write_refs_count(pb, 1); + mxf_write_uuid(pb, FFV1SubDescriptor, 0); + } + return pos; } @@ -1384,6 +1448,47 @@ static void mxf_write_h264_desc(AVFormatContext *s, AVStream *st) } } +static void mxf_write_ffv1_subdesc(AVFormatContext *s, AVStream *st) +{ + AVIOContext *pb = s->pb; + MXFStreamContext *sc = st->priv_data; + int64_t pos; + + avio_write(pb, mxf_ffv1_subdescriptor_key, 16); + klv_encode_ber4_length(pb, 0); + pos = avio_tell(pb); + + mxf_write_local_tag(s, 16, 0x3C0A); + mxf_write_uuid(pb, FFV1SubDescriptor, 0); + + if (st->codecpar->extradata_size) { + mxf_write_local_tag(s, st->codecpar->extradata_size, 0xDFDB); + avio_write(pb, st->codecpar->extradata, st->codecpar->extradata_size); // FFV1InitializationMetadata + } + + mxf_write_local_tag(s, 2, 0xDFDA); + avio_wb16(pb, (*sc->codec_ul)[14]); // FFV1Version + + if (st->codecpar->extradata_size) { + mxf_write_local_tag(s, 2, 0xDFD9); + avio_wb16(pb, sc->micro_version); // FFV1MicroVersion + } + + mxf_update_klv_size(s->pb, pos); +} + +static void mxf_write_ffv1_desc(AVFormatContext *s, AVStream *st) +{ + int is_rgb, pos; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(st->codecpar->format); + av_assert0(desc); + is_rgb = desc->flags & AV_PIX_FMT_FLAG_RGB; + + pos = mxf_write_cdci_common(s, st, is_rgb ? mxf_rgba_descriptor_key : mxf_cdci_descriptor_key); + mxf_update_klv_size(s->pb, pos); + mxf_write_ffv1_subdesc(s, st); +} + static void mxf_write_s436m_anc_desc(AVFormatContext *s, AVStream *st) { int64_t pos = mxf_write_generic_desc(s, st, mxf_s436m_anc_descriptor_key); @@ -1435,7 +1540,7 @@ static int64_t mxf_write_generic_sound_common(AVFormatContext *s, AVStream *st, int show_warnings = !mxf->footer_partition_offset; int64_t pos = mxf_write_generic_desc(s, st, key); - if (s->oformat == &ff_mxf_opatom_muxer) { + if (IS_OPATOM(s)) { mxf_write_local_tag(s, 8, 0x3002); avio_wb64(pb, mxf->body_offset / mxf->edit_unit_byte_count); } @@ -1449,19 +1554,19 @@ static int64_t mxf_write_generic_sound_common(AVFormatContext *s, AVStream *st, avio_wb32(pb, st->codecpar->sample_rate); avio_wb32(pb, 1); - if (s->oformat == &ff_mxf_d10_muxer) { + if (IS_D10(s)) { mxf_write_local_tag(s, 1, 0x3D04); avio_w8(pb, 0); } mxf_write_local_tag(s, 4, 0x3D07); if (mxf->channel_count == -1) { - if (show_warnings && (s->oformat == &ff_mxf_d10_muxer) && + if (show_warnings && IS_D10(s) && (st->codecpar->ch_layout.nb_channels != 4) && (st->codecpar->ch_layout.nb_channels != 8)) av_log(s, AV_LOG_WARNING, "the number of audio channels shall be 4 or 8 : the output will not comply to MXF D-10 specs, use -d10_channelcount to fix this\n"); avio_wb32(pb, st->codecpar->ch_layout.nb_channels); - } else if (s->oformat == &ff_mxf_d10_muxer) { + } else if (IS_D10(s)) { if (show_warnings && (mxf->channel_count < st->codecpar->ch_layout.nb_channels)) av_log(s, AV_LOG_WARNING, "d10_channelcount < actual number of audio channels : some channels will be discarded\n"); if (show_warnings && (mxf->channel_count != 4) && (mxf->channel_count != 8)) @@ -1961,7 +2066,7 @@ static int mxf_write_partition(AVFormatContext *s, int bodysid, avio_wb32(pb, index_byte_count ? indexsid : 0); // indexSID // BodyOffset - if (bodysid && mxf->edit_units_count && mxf->body_partitions_count && s->oformat != &ff_mxf_opatom_muxer) + if (bodysid && mxf->edit_units_count && mxf->body_partitions_count && !IS_OPATOM(s)) avio_wb64(pb, mxf->body_offset); else avio_wb64(pb, 0); @@ -1969,7 +2074,7 @@ static int mxf_write_partition(AVFormatContext *s, int bodysid, avio_wb32(pb, bodysid); // bodySID // operational pattern - if (s->oformat == &ff_mxf_opatom_muxer) + if (IS_OPATOM(s)) avio_write(pb, opatom_ul, 16); else avio_write(pb, op1a_ul, 16); @@ -2344,6 +2449,77 @@ static int mxf_parse_h264_frame(AVFormatContext *s, AVStream *st, return 1; } +static inline int get_ffv1_unsigned_symbol(RangeCoder *c, uint8_t *state) { + if(get_rac(c, state+0)) + return 0; + else{ + int i, e; + unsigned a; + e= 0; + while(get_rac(c, state+1 + FFMIN(e,9))){ //1..10 + e++; + if (e > 31) + return AVERROR_INVALIDDATA; + } + + a= 1; + for(i=e-1; i>=0; i--){ + a += a + get_rac(c, state+22 + FFMIN(i,9)); //22..31 + } + + return a; + } +} +#define FFV1_CONTEXT_SIZE 32 +static int mxf_parse_ffv1_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt) +{ + MXFContext *mxf = s->priv_data; + MXFStreamContext *sc = st->priv_data; + uint8_t state[FFV1_CONTEXT_SIZE]; + RangeCoder c; + unsigned v; + + sc->frame_size = pkt->size; + + if (mxf->header_written) + return 1; + + memset(state, 128, sizeof(state)); + if (st->codecpar->extradata) { + ff_init_range_decoder(&c, st->codecpar->extradata, st->codecpar->extradata_size); + ff_build_rac_states(&c, 0.05 * (1LL << 32), 256 - 8); + v = get_ffv1_unsigned_symbol(&c, state); + av_assert0(v >= 2); + if (v > 4) { + return 0; + } + if (v > 4) { + av_log(s, AV_LOG_ERROR, "unsupported ffv1 version %d\n", v); + return 0; + } + sc->micro_version = get_ffv1_unsigned_symbol(&c, state); + } else { + uint8_t keystate = 128; + ff_init_range_decoder(&c, pkt->data, pkt->size); + ff_build_rac_states(&c, 0.05 * (1LL << 32), 256 - 8); + get_rac(&c, &keystate); // keyframe + v = get_ffv1_unsigned_symbol(&c, state); + av_assert0(v < 2); + } + sc->codec_ul = &mxf_ffv1_codec_uls[v]; + + if (st->codecpar->field_order > AV_FIELD_PROGRESSIVE) { + sc->interlaced = 1; + sc->field_dominance = st->codecpar->field_order == (st->codecpar->field_order == AV_FIELD_TT || st->codecpar->field_order == AV_FIELD_TB) ? 1 : 2; + } + sc->aspect_ratio.num = st->codecpar->width * st->codecpar->sample_aspect_ratio.num; + sc->aspect_ratio.den = st->codecpar->height * st->codecpar->sample_aspect_ratio.den; + av_reduce(&sc->aspect_ratio.num, &sc->aspect_ratio.den, + sc->aspect_ratio.num, sc->aspect_ratio.den, INT_MAX); + + return 1; +} + static const UID mxf_mpeg2_codec_uls[] = { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x01,0x10,0x00 }, // MP-ML I-Frame { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x01,0x11,0x00 }, // MP-ML Long GOP @@ -2436,7 +2612,7 @@ static int mxf_parse_mpeg2_frame(AVFormatContext *s, AVStream *st, } } } - if (s->oformat != &ff_mxf_d10_muxer) { + if (!IS_D10(s)) { const UID *codec_ul = mxf_get_mpeg2_codec_ul(st->codecpar); if (!codec_ul) return 0; @@ -2533,7 +2709,7 @@ static int mxf_init(AVFormatContext *s) uint8_t present[FF_ARRAY_ELEMS(mxf_essence_container_uls)] = {0}; int64_t timestamp = 0; - if (s->oformat == &ff_mxf_opatom_muxer && s->nb_streams !=1) { + if (IS_OPATOM(s) && s->nb_streams != 1) { av_log(s, AV_LOG_ERROR, "there must be exactly one stream for mxf opatom\n"); return -1; } @@ -2549,7 +2725,7 @@ static int mxf_init(AVFormatContext *s) st->priv_data = sc; sc->index = -1; - if (((i == 0) ^ (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)) && s->oformat != &ff_mxf_opatom_muxer) { + if (((i == 0) ^ (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)) && !IS_OPATOM(s)) { av_log(s, AV_LOG_ERROR, "there must be exactly one video stream and it must be the first one\n"); return -1; } @@ -2593,12 +2769,12 @@ static int mxf_init(AVFormatContext *s) sc->video_bit_rate = st->codecpar->bit_rate; - if (s->oformat == &ff_mxf_d10_muxer || + if (IS_D10(s) || st->codecpar->codec_id == AV_CODEC_ID_DNXHD || st->codecpar->codec_id == AV_CODEC_ID_DVVIDEO) mxf->cbr_index = 1; - if (s->oformat == &ff_mxf_d10_muxer) { + if (IS_D10(s)) { int ntsc = mxf->time_base.den != 25; int ul_index; @@ -2636,7 +2812,7 @@ static int mxf_init(AVFormatContext *s) return -1; } avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); - if (s->oformat == &ff_mxf_d10_muxer) { + if (IS_D10(s)) { if (st->index != 1) { av_log(s, AV_LOG_ERROR, "MXF D-10 only support one audio track\n"); return -1; @@ -2648,7 +2824,7 @@ static int mxf_init(AVFormatContext *s) sc->index = INDEX_D10_AUDIO; sc->container_ul = ((MXFStreamContext*)s->streams[0]->priv_data)->container_ul; sc->frame_size = 4 + 8 * av_rescale_rnd(st->codecpar->sample_rate, mxf->time_base.num, mxf->time_base.den, AV_ROUND_UP) * 4; - } else if (s->oformat == &ff_mxf_opatom_muxer) { + } else if (IS_OPATOM(s)) { AVRational tbc = av_inv_q(mxf->audio_edit_rate); if (st->codecpar->codec_id != AV_CODEC_ID_PCM_S16LE && @@ -2707,7 +2883,7 @@ static int mxf_init(AVFormatContext *s) memcpy(sc->track_essence_element_key, mxf_essence_container_uls[sc->index].element_ul, 15); sc->track_essence_element_key[15] = present[sc->index]; - if (s->oformat == &ff_mxf_opatom_muxer && st->codecpar->codec_id == AV_CODEC_ID_DNXHD) { + if (IS_OPATOM(s) && st->codecpar->codec_id == AV_CODEC_ID_DNXHD) { // clip-wrapping requires 0x0D per ST2019-4:2009 or 0x06 per previous version ST2019-4:2008 // we choose to use 0x06 instead 0x0D to be compatible with AVID systems // and produce mxf files with the most relevant flavour for opatom @@ -2720,7 +2896,7 @@ static int mxf_init(AVFormatContext *s) present[sc->index]++; } - if (s->oformat == &ff_mxf_d10_muxer || s->oformat == &ff_mxf_opatom_muxer) { + if (IS_D10(s) || IS_OPATOM(s)) { mxf->essence_container_count = 1; } @@ -2889,7 +3065,7 @@ static void mxf_compute_edit_unit_byte_count(AVFormatContext *s) MXFContext *mxf = s->priv_data; int i; - if (s->oformat == &ff_mxf_opatom_muxer) { + if (IS_OPATOM(s)) { MXFStreamContext *sc = s->streams[0]->priv_data; mxf->edit_unit_byte_count = sc->frame_size; return; @@ -2915,7 +3091,7 @@ static int mxf_write_packet(AVFormatContext *s, AVPacket *pkt) int err; if (!mxf->header_written && pkt->stream_index != 0 && - s->oformat != &ff_mxf_opatom_muxer) { + !IS_OPATOM(s)) { av_log(s, AV_LOG_ERROR, "Received non-video packet before " "header has been written\n"); return AVERROR_INVALIDDATA; @@ -2955,6 +3131,11 @@ static int mxf_write_packet(AVFormatContext *s, AVPacket *pkt) av_log(s, AV_LOG_ERROR, "could not get h264 profile\n"); return -1; } + } else if (st->codecpar->codec_id == AV_CODEC_ID_FFV1) { + if (!mxf_parse_ffv1_frame(s, st, pkt)) { + av_log(s, AV_LOG_ERROR, "could not get ffv1 version\n"); + return -1; + } } if (mxf->cbr_index) { @@ -2967,7 +3148,7 @@ static int mxf_write_packet(AVFormatContext *s, AVPacket *pkt) mxf_compute_edit_unit_byte_count(s); } - if (s->oformat == &ff_mxf_opatom_muxer) + if (IS_OPATOM(s)) return mxf_write_opatom_packet(s, pkt, &ie); if (!mxf->header_written) { @@ -3015,8 +3196,7 @@ static int mxf_write_packet(AVFormatContext *s, AVPacket *pkt) mxf_write_klv_fill(s); avio_write(pb, sc->track_essence_element_key, 16); // write key - if (s->oformat == &ff_mxf_d10_muxer && - st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + if (IS_D10(s) && st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { mxf_write_d10_audio_packet(s, st, pkt); } else { klv_encode_ber4_length(pb, pkt->size); // write length @@ -3037,7 +3217,7 @@ static void mxf_write_random_index_pack(AVFormatContext *s) avio_write(pb, ff_mxf_random_index_pack_key, 16); klv_encode_ber_length(pb, 28 + 12LL*mxf->body_partitions_count); - if (mxf->edit_unit_byte_count && s->oformat != &ff_mxf_opatom_muxer) + if (mxf->edit_unit_byte_count && !IS_OPATOM(s)) avio_wb32(pb, 1); // BodySID of header partition else avio_wb32(pb, 0); @@ -3061,7 +3241,7 @@ static int mxf_write_footer(AVFormatContext *s) int i, err; if (!mxf->header_written || - (s->oformat == &ff_mxf_opatom_muxer && !mxf->body_partition_offset)) { + (IS_OPATOM(s) && !mxf->body_partition_offset)) { /* reason could be invalid options/not supported codec/out of memory */ return AVERROR_UNKNOWN; } @@ -3070,7 +3250,7 @@ static int mxf_write_footer(AVFormatContext *s) mxf_write_klv_fill(s); mxf->footer_partition_offset = avio_tell(pb); - if (mxf->edit_unit_byte_count && s->oformat != &ff_mxf_opatom_muxer) { // no need to repeat index + if (mxf->edit_unit_byte_count && !IS_OPATOM(s)) { // no need to repeat index if ((err = mxf_write_partition(s, 0, 0, footer_partition_key, 0)) < 0) return err; } else { @@ -3084,7 +3264,7 @@ static int mxf_write_footer(AVFormatContext *s) mxf_write_random_index_pack(s); if (s->pb->seekable & AVIO_SEEKABLE_NORMAL) { - if (s->oformat == &ff_mxf_opatom_muxer) { + if (IS_OPATOM(s)) { /* rewrite body partition to update lengths */ avio_seek(pb, mxf->body_partition_offset[0], SEEK_SET); if ((err = mxf_write_opatom_body_partition(s)) < 0) @@ -3092,7 +3272,7 @@ static int mxf_write_footer(AVFormatContext *s) } avio_seek(pb, 0, SEEK_SET); - if (mxf->edit_unit_byte_count && s->oformat != &ff_mxf_opatom_muxer) { + if (mxf->edit_unit_byte_count && !IS_OPATOM(s)) { if ((err = mxf_write_partition(s, 1, 2, header_closed_partition_key, 1)) < 0) return err; mxf_write_klv_fill(s); @@ -3260,52 +3440,52 @@ static const AVClass mxf_opatom_muxer_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVOutputFormat ff_mxf_muxer = { - .name = "mxf", - .long_name = NULL_IF_CONFIG_SMALL("MXF (Material eXchange Format)"), - .mime_type = "application/mxf", - .extensions = "mxf", +const FFOutputFormat ff_mxf_muxer = { + .p.name = "mxf", + .p.long_name = NULL_IF_CONFIG_SMALL("MXF (Material eXchange Format)"), + .p.mime_type = "application/mxf", + .p.extensions = "mxf", .priv_data_size = sizeof(MXFContext), - .audio_codec = AV_CODEC_ID_PCM_S16LE, - .video_codec = AV_CODEC_ID_MPEG2VIDEO, + .p.audio_codec = AV_CODEC_ID_PCM_S16LE, + .p.video_codec = AV_CODEC_ID_MPEG2VIDEO, .init = mxf_init, .write_packet = mxf_write_packet, .write_trailer = mxf_write_footer, .deinit = mxf_deinit, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, .interleave_packet = mxf_interleave, - .priv_class = &mxf_muxer_class, + .p.priv_class = &mxf_muxer_class, }; -const AVOutputFormat ff_mxf_d10_muxer = { - .name = "mxf_d10", - .long_name = NULL_IF_CONFIG_SMALL("MXF (Material eXchange Format) D-10 Mapping"), - .mime_type = "application/mxf", +const FFOutputFormat ff_mxf_d10_muxer = { + .p.name = "mxf_d10", + .p.long_name = NULL_IF_CONFIG_SMALL("MXF (Material eXchange Format) D-10 Mapping"), + .p.mime_type = "application/mxf", .priv_data_size = sizeof(MXFContext), - .audio_codec = AV_CODEC_ID_PCM_S16LE, - .video_codec = AV_CODEC_ID_MPEG2VIDEO, + .p.audio_codec = AV_CODEC_ID_PCM_S16LE, + .p.video_codec = AV_CODEC_ID_MPEG2VIDEO, .init = mxf_init, .write_packet = mxf_write_packet, .write_trailer = mxf_write_footer, .deinit = mxf_deinit, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, .interleave_packet = mxf_interleave, - .priv_class = &mxf_d10_muxer_class, + .p.priv_class = &mxf_d10_muxer_class, }; -const AVOutputFormat ff_mxf_opatom_muxer = { - .name = "mxf_opatom", - .long_name = NULL_IF_CONFIG_SMALL("MXF (Material eXchange Format) Operational Pattern Atom"), - .mime_type = "application/mxf", - .extensions = "mxf", +const FFOutputFormat ff_mxf_opatom_muxer = { + .p.name = "mxf_opatom", + .p.long_name = NULL_IF_CONFIG_SMALL("MXF (Material eXchange Format) Operational Pattern Atom"), + .p.mime_type = "application/mxf", + .p.extensions = "mxf", .priv_data_size = sizeof(MXFContext), - .audio_codec = AV_CODEC_ID_PCM_S16LE, - .video_codec = AV_CODEC_ID_DNXHD, + .p.audio_codec = AV_CODEC_ID_PCM_S16LE, + .p.video_codec = AV_CODEC_ID_DNXHD, .init = mxf_init, .write_packet = mxf_write_packet, .write_trailer = mxf_write_footer, .deinit = mxf_deinit, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, .interleave_packet = mxf_interleave, - .priv_class = &mxf_opatom_muxer_class, + .p.priv_class = &mxf_opatom_muxer_class, }; diff --git a/libavformat/network.c b/libavformat/network.c index 21e20b3e9a9..f752efc4114 100644 --- a/libavformat/network.c +++ b/libavformat/network.c @@ -356,7 +356,7 @@ struct ConnectionAttempt { static int start_connect_attempt(struct ConnectionAttempt *attempt, struct addrinfo **ptr, int timeout_ms, URLContext *h, - void (*customize_fd)(void *, int), void *customize_ctx) + int (*customize_fd)(void *, int, int), void *customize_ctx) { struct addrinfo *ai = *ptr; int ret; @@ -371,8 +371,14 @@ static int start_connect_attempt(struct ConnectionAttempt *attempt, ff_socket_nonblock(attempt->fd, 1); - if (customize_fd) - customize_fd(customize_ctx, attempt->fd); + if (customize_fd) { + ret = customize_fd(customize_ctx, attempt->fd, ai->ai_family); + if (ret) { + closesocket(attempt->fd); + attempt->fd = -1; + return ret; + } + } while ((ret = connect(attempt->fd, ai->ai_addr, ai->ai_addrlen))) { ret = ff_neterrno(); @@ -402,7 +408,7 @@ static int start_connect_attempt(struct ConnectionAttempt *attempt, int ff_connect_parallel(struct addrinfo *addrs, int timeout_ms_per_address, int parallel, URLContext *h, int *fd, - void (*customize_fd)(void *, int), void *customize_ctx) + int (*customize_fd)(void *, int, int), void *customize_ctx) { struct ConnectionAttempt attempts[3]; struct pollfd pfd[3]; diff --git a/libavformat/network.h b/libavformat/network.h index 71c49a73fbb..ca214087fc9 100644 --- a/libavformat/network.h +++ b/libavformat/network.h @@ -336,6 +336,6 @@ void ff_log_net_error(void *ctx, int level, const char* prefix); */ int ff_connect_parallel(struct addrinfo *addrs, int timeout_ms_per_address, int parallel, URLContext *h, int *fd, - void (*customize_fd)(void *, int), void *customize_ctx); + int (*customize_fd)(void *, int, int), void *customize_ctx); #endif /* AVFORMAT_NETWORK_H */ diff --git a/libavformat/nullenc.c b/libavformat/nullenc.c index 3deca5a7eda..60bde2a0690 100644 --- a/libavformat/nullenc.c +++ b/libavformat/nullenc.c @@ -27,12 +27,12 @@ static int null_write_packet(struct AVFormatContext *s, AVPacket *pkt) return 0; } -const AVOutputFormat ff_null_muxer = { - .name = "null", - .long_name = NULL_IF_CONFIG_SMALL("raw null video"), - .audio_codec = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE), - .video_codec = AV_CODEC_ID_WRAPPED_AVFRAME, +const FFOutputFormat ff_null_muxer = { + .p.name = "null", + .p.long_name = NULL_IF_CONFIG_SMALL("raw null video"), + .p.audio_codec = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE), + .p.video_codec = AV_CODEC_ID_WRAPPED_AVFRAME, .write_packet = null_write_packet, - .flags = AVFMT_VARIABLE_FPS | AVFMT_NOFILE | AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_VARIABLE_FPS | AVFMT_NOFILE | AVFMT_NOTIMESTAMPS, .interleave_packet = ff_interleave_packet_passthrough, }; diff --git a/libavformat/nutenc.c b/libavformat/nutenc.c index ff81ee34aa0..a5198c7ca9f 100644 --- a/libavformat/nutenc.c +++ b/libavformat/nutenc.c @@ -1253,20 +1253,20 @@ static const AVClass class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVOutputFormat ff_nut_muxer = { - .name = "nut", - .long_name = NULL_IF_CONFIG_SMALL("NUT"), - .mime_type = "video/x-nut", - .extensions = "nut", +const FFOutputFormat ff_nut_muxer = { + .p.name = "nut", + .p.long_name = NULL_IF_CONFIG_SMALL("NUT"), + .p.mime_type = "video/x-nut", + .p.extensions = "nut", .priv_data_size = sizeof(NUTContext), - .audio_codec = CONFIG_LIBVORBIS ? AV_CODEC_ID_VORBIS : + .p.audio_codec = CONFIG_LIBVORBIS ? AV_CODEC_ID_VORBIS : CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_MP2, - .video_codec = AV_CODEC_ID_MPEG4, + .p.video_codec = AV_CODEC_ID_MPEG4, .write_header = nut_write_header, .write_packet = nut_write_packet, .write_trailer = nut_write_trailer, .deinit = nut_write_deinit, - .flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS, - .codec_tag = ff_nut_codec_tags, - .priv_class = &class, + .p.flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS, + .p.codec_tag = ff_nut_codec_tags, + .p.priv_class = &class, }; diff --git a/libavformat/oggenc.c b/libavformat/oggenc.c index 458b2a9bdc4..2e582d0754c 100644 --- a/libavformat/oggenc.c +++ b/libavformat/oggenc.c @@ -33,6 +33,7 @@ #include "avformat.h" #include "avio_internal.h" #include "internal.h" +#include "mux.h" #include "version.h" #include "vorbiscomment.h" @@ -746,11 +747,11 @@ static void ogg_free(AVFormatContext *s) } #if CONFIG_OGG_MUXER -const AVOutputFormat ff_ogg_muxer = { - .name = "ogg", - .long_name = NULL_IF_CONFIG_SMALL("Ogg"), - .mime_type = "application/ogg", - .extensions = "ogg" +const FFOutputFormat ff_ogg_muxer = { + .p.name = "ogg", + .p.long_name = NULL_IF_CONFIG_SMALL("Ogg"), + .p.mime_type = "application/ogg", + .p.extensions = "ogg" #if !CONFIG_OGV_MUXER ",ogv" #endif @@ -762,90 +763,90 @@ const AVOutputFormat ff_ogg_muxer = { #endif , .priv_data_size = sizeof(OGGContext), - .audio_codec = CONFIG_LIBVORBIS_ENCODER ? + .p.audio_codec = CONFIG_LIBVORBIS_ENCODER ? AV_CODEC_ID_VORBIS : AV_CODEC_ID_FLAC, - .video_codec = AV_CODEC_ID_THEORA, + .p.video_codec = AV_CODEC_ID_THEORA, .init = ogg_init, .write_header = ogg_write_header, .write_packet = ogg_write_packet, .write_trailer = ogg_write_trailer, .deinit = ogg_free, - .flags = AVFMT_TS_NEGATIVE | AVFMT_TS_NONSTRICT | AVFMT_ALLOW_FLUSH, - .priv_class = &ogg_muxer_class, + .p.flags = AVFMT_TS_NEGATIVE | AVFMT_TS_NONSTRICT | AVFMT_ALLOW_FLUSH, + .p.priv_class = &ogg_muxer_class, }; #endif #if CONFIG_OGA_MUXER -const AVOutputFormat ff_oga_muxer = { - .name = "oga", - .long_name = NULL_IF_CONFIG_SMALL("Ogg Audio"), - .mime_type = "audio/ogg", - .extensions = "oga", +const FFOutputFormat ff_oga_muxer = { + .p.name = "oga", + .p.long_name = NULL_IF_CONFIG_SMALL("Ogg Audio"), + .p.mime_type = "audio/ogg", + .p.extensions = "oga", .priv_data_size = sizeof(OGGContext), - .audio_codec = AV_CODEC_ID_FLAC, + .p.audio_codec = AV_CODEC_ID_FLAC, .init = ogg_init, .write_header = ogg_write_header, .write_packet = ogg_write_packet, .write_trailer = ogg_write_trailer, .deinit = ogg_free, - .flags = AVFMT_TS_NEGATIVE | AVFMT_ALLOW_FLUSH, - .priv_class = &ogg_muxer_class, + .p.flags = AVFMT_TS_NEGATIVE | AVFMT_ALLOW_FLUSH, + .p.priv_class = &ogg_muxer_class, }; #endif #if CONFIG_OGV_MUXER -const AVOutputFormat ff_ogv_muxer = { - .name = "ogv", - .long_name = NULL_IF_CONFIG_SMALL("Ogg Video"), - .mime_type = "video/ogg", - .extensions = "ogv", +const FFOutputFormat ff_ogv_muxer = { + .p.name = "ogv", + .p.long_name = NULL_IF_CONFIG_SMALL("Ogg Video"), + .p.mime_type = "video/ogg", + .p.extensions = "ogv", .priv_data_size = sizeof(OGGContext), - .audio_codec = CONFIG_LIBVORBIS_ENCODER ? + .p.audio_codec = CONFIG_LIBVORBIS_ENCODER ? AV_CODEC_ID_VORBIS : AV_CODEC_ID_FLAC, - .video_codec = CONFIG_LIBTHEORA_ENCODER ? + .p.video_codec = CONFIG_LIBTHEORA_ENCODER ? AV_CODEC_ID_THEORA : AV_CODEC_ID_VP8, .init = ogg_init, .write_header = ogg_write_header, .write_packet = ogg_write_packet, .write_trailer = ogg_write_trailer, .deinit = ogg_free, - .flags = AVFMT_TS_NEGATIVE | AVFMT_TS_NONSTRICT | AVFMT_ALLOW_FLUSH, - .priv_class = &ogg_muxer_class, + .p.flags = AVFMT_TS_NEGATIVE | AVFMT_TS_NONSTRICT | AVFMT_ALLOW_FLUSH, + .p.priv_class = &ogg_muxer_class, }; #endif #if CONFIG_SPX_MUXER -const AVOutputFormat ff_spx_muxer = { - .name = "spx", - .long_name = NULL_IF_CONFIG_SMALL("Ogg Speex"), - .mime_type = "audio/ogg", - .extensions = "spx", +const FFOutputFormat ff_spx_muxer = { + .p.name = "spx", + .p.long_name = NULL_IF_CONFIG_SMALL("Ogg Speex"), + .p.mime_type = "audio/ogg", + .p.extensions = "spx", .priv_data_size = sizeof(OGGContext), - .audio_codec = AV_CODEC_ID_SPEEX, + .p.audio_codec = AV_CODEC_ID_SPEEX, .init = ogg_init, .write_header = ogg_write_header, .write_packet = ogg_write_packet, .write_trailer = ogg_write_trailer, .deinit = ogg_free, - .flags = AVFMT_TS_NEGATIVE | AVFMT_ALLOW_FLUSH, - .priv_class = &ogg_muxer_class, + .p.flags = AVFMT_TS_NEGATIVE | AVFMT_ALLOW_FLUSH, + .p.priv_class = &ogg_muxer_class, }; #endif #if CONFIG_OPUS_MUXER -const AVOutputFormat ff_opus_muxer = { - .name = "opus", - .long_name = NULL_IF_CONFIG_SMALL("Ogg Opus"), - .mime_type = "audio/ogg", - .extensions = "opus", +const FFOutputFormat ff_opus_muxer = { + .p.name = "opus", + .p.long_name = NULL_IF_CONFIG_SMALL("Ogg Opus"), + .p.mime_type = "audio/ogg", + .p.extensions = "opus", .priv_data_size = sizeof(OGGContext), - .audio_codec = AV_CODEC_ID_OPUS, + .p.audio_codec = AV_CODEC_ID_OPUS, .init = ogg_init, .write_header = ogg_write_header, .write_packet = ogg_write_packet, .write_trailer = ogg_write_trailer, .deinit = ogg_free, - .flags = AVFMT_TS_NEGATIVE | AVFMT_ALLOW_FLUSH, - .priv_class = &ogg_muxer_class, + .p.flags = AVFMT_TS_NEGATIVE | AVFMT_ALLOW_FLUSH, + .p.priv_class = &ogg_muxer_class, }; #endif diff --git a/libavformat/oggparseflac.c b/libavformat/oggparseflac.c index eef6e099277..f25ed9cc155 100644 --- a/libavformat/oggparseflac.c +++ b/libavformat/oggparseflac.c @@ -20,7 +20,7 @@ #include #include "libavcodec/avcodec.h" -#include "libavcodec/get_bits.h" +#include "libavcodec/bytestream.h" #include "libavcodec/flac.h" #include "avformat.h" #include "internal.h" @@ -34,28 +34,28 @@ flac_header (AVFormatContext * s, int idx) struct ogg *ogg = s->priv_data; struct ogg_stream *os = ogg->streams + idx; AVStream *st = s->streams[idx]; - GetBitContext gb; + GetByteContext gb; int mdt, ret; if (os->buf[os->pstart] == 0xff) return 0; - init_get_bits(&gb, os->buf + os->pstart, os->psize*8); - skip_bits1(&gb); /* metadata_last */ - mdt = get_bits(&gb, 7); + bytestream2_init(&gb, os->buf + os->pstart, os->psize); + mdt = bytestream2_get_byte(&gb) & 0x7F; if (mdt == OGG_FLAC_METADATA_TYPE_STREAMINFO) { - uint8_t *streaminfo_start = os->buf + os->pstart + 5 + 4 + 4 + 4; uint32_t samplerate; - skip_bits_long(&gb, 4*8); /* "FLAC" */ - if(get_bits(&gb, 8) != 1) /* unsupported major version */ + if (bytestream2_get_bytes_left(&gb) < 4 + 4 + 4 + 4 + FLAC_STREAMINFO_SIZE) + return AVERROR_INVALIDDATA; + bytestream2_skipu(&gb, 4); /* "FLAC" */ + if (bytestream2_get_byteu(&gb) != 1) /* unsupported major version */ return -1; - skip_bits(&gb, 8 + 16); /* minor version + header count */ - skip_bits_long(&gb, 4*8); /* "fLaC" */ + bytestream2_skipu(&gb, 1 + 2); /* minor version + header count */ + bytestream2_skipu(&gb, 4); /* "fLaC" */ /* METADATA_BLOCK_HEADER */ - if (get_bits_long(&gb, 32) != FLAC_STREAMINFO_SIZE) + if (bytestream2_get_be32u(&gb) != FLAC_STREAMINFO_SIZE) return -1; st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; @@ -64,7 +64,7 @@ flac_header (AVFormatContext * s, int idx) if ((ret = ff_alloc_extradata(st->codecpar, FLAC_STREAMINFO_SIZE)) < 0) return ret; - memcpy(st->codecpar->extradata, streaminfo_start, st->codecpar->extradata_size); + bytestream2_get_bufferu(&gb, st->codecpar->extradata, FLAC_STREAMINFO_SIZE); samplerate = AV_RB24(st->codecpar->extradata + 10) >> 4; if (!samplerate) diff --git a/libavformat/oggparsetheora.c b/libavformat/oggparsetheora.c index b9184eff7b1..85119352c3b 100644 --- a/libavformat/oggparsetheora.c +++ b/libavformat/oggparsetheora.c @@ -196,7 +196,7 @@ static int theora_packet(AVFormatContext *s, int idx) if(s->streams[idx]->start_time == AV_NOPTS_VALUE && os->lastpts != AV_NOPTS_VALUE) { s->streams[idx]->start_time = os->lastpts; if (s->streams[idx]->duration > 0) - s->streams[idx]->duration -= s->streams[idx]->start_time; + s->streams[idx]->duration = av_sat_sub64(s->streams[idx]->duration, s->streams[idx]->start_time); } } diff --git a/libavformat/oggparsevorbis.c b/libavformat/oggparsevorbis.c index 061840c2ed4..6fd12560bc9 100644 --- a/libavformat/oggparsevorbis.c +++ b/libavformat/oggparsevorbis.c @@ -311,7 +311,12 @@ static int vorbis_header(AVFormatContext *s, int idx) if (!(pkt_type & 1)) return priv->vp ? 0 : AVERROR_INVALIDDATA; - if (os->psize < 1 || pkt_type > 5) + if (pkt_type > 5) { + av_log(s, AV_LOG_VERBOSE, "Ignoring packet with unknown type %d\n", pkt_type); + return 1; + } + + if (os->psize < 1) return AVERROR_INVALIDDATA; if (priv->packet[pkt_type >> 1]) diff --git a/libavformat/omaenc.c b/libavformat/omaenc.c index 246f5195fb9..6d0b47465d1 100644 --- a/libavformat/omaenc.c +++ b/libavformat/omaenc.c @@ -24,6 +24,7 @@ #include "avio_internal.h" #include "id3v2.h" #include "internal.h" +#include "mux.h" #include "oma.h" #include "rawenc.h" @@ -91,14 +92,14 @@ static av_cold int oma_write_header(AVFormatContext *s) return 0; } -const AVOutputFormat ff_oma_muxer = { - .name = "oma", - .long_name = NULL_IF_CONFIG_SMALL("Sony OpenMG audio"), - .mime_type = "audio/x-oma", - .extensions = "oma", - .audio_codec = AV_CODEC_ID_ATRAC3, +const FFOutputFormat ff_oma_muxer = { + .p.name = "oma", + .p.long_name = NULL_IF_CONFIG_SMALL("Sony OpenMG audio"), + .p.mime_type = "audio/x-oma", + .p.extensions = "oma", + .p.audio_codec = AV_CODEC_ID_ATRAC3, .write_header = oma_write_header, .write_packet = ff_raw_write_packet, - .codec_tag = ff_oma_codec_tags_list, - .flags = AVFMT_NOTIMESTAMPS, + .p.codec_tag = ff_oma_codec_tags_list, + .p.flags = AVFMT_NOTIMESTAMPS, }; diff --git a/libavformat/options.c b/libavformat/options.c index 0079a06d9a5..e4a3aceed0a 100644 --- a/libavformat/options.c +++ b/libavformat/options.c @@ -44,7 +44,7 @@ static const char* format_to_name(void* ptr) AVFormatContext* fc = (AVFormatContext*) ptr; if(fc->iformat) return fc->iformat->name; else if(fc->oformat) return fc->oformat->name; - else return "NULL"; + else return fc->av_class->class_name; } static void *format_child_next(void *obj, void *prev) @@ -151,10 +151,12 @@ static int io_open_default(AVFormatContext *s, AVIOContext **pb, return ffio_open_whitelist(pb, url, flags, &s->interrupt_callback, options, s->protocol_whitelist, s->protocol_blacklist); } +#if FF_API_AVFORMAT_IO_CLOSE void ff_format_io_close_default(AVFormatContext *s, AVIOContext *pb) { avio_close(pb); } +#endif static int io_close2_default(AVFormatContext *s, AVIOContext *pb) { @@ -172,7 +174,11 @@ AVFormatContext *avformat_alloc_context(void) s = &si->pub; s->av_class = &av_format_context_class; s->io_open = io_open_default; +#if FF_API_AVFORMAT_IO_CLOSE +FF_DISABLE_DEPRECATION_WARNINGS s->io_close = ff_format_io_close_default; +FF_ENABLE_DEPRECATION_WARNINGS +#endif s->io_close2= io_close2_default; av_opt_set_defaults(s); @@ -257,10 +263,7 @@ AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c) return NULL; st = &sti->pub; -#if FF_API_AVSTREAM_CLASS st->av_class = &stream_class; -#endif - st->codecpar = avcodec_parameters_alloc(); if (!st->codecpar) goto fail; diff --git a/libavformat/pcmenc.c b/libavformat/pcmenc.c index 0cf497989af..7f716443081 100644 --- a/libavformat/pcmenc.c +++ b/libavformat/pcmenc.c @@ -22,18 +22,19 @@ #include "config_components.h" #include "avformat.h" +#include "mux.h" #include "rawenc.h" #define PCMDEF_0(name_, long_name_, ext, codec) #define PCMDEF_1(name_, long_name_, ext, codec) \ -const AVOutputFormat ff_pcm_ ## name_ ## _muxer = { \ - .name = #name_, \ - .long_name = NULL_IF_CONFIG_SMALL(long_name_), \ - .extensions = ext, \ - .audio_codec = codec, \ - .video_codec = AV_CODEC_ID_NONE, \ +const FFOutputFormat ff_pcm_ ## name_ ## _muxer = { \ + .p.name = #name_, \ + .p.long_name = NULL_IF_CONFIG_SMALL(long_name_), \ + .p.extensions = ext, \ + .p.audio_codec = codec, \ + .p.video_codec = AV_CODEC_ID_NONE, \ + .p.flags = AVFMT_NOTIMESTAMPS, \ .write_packet = ff_raw_write_packet, \ - .flags = AVFMT_NOTIMESTAMPS, \ }; #define PCMDEF_2(name, long_name, ext, codec, enabled) \ PCMDEF_ ## enabled(name, long_name, ext, codec) diff --git a/libavformat/pdvdec.c b/libavformat/pdvdec.c new file mode 100644 index 00000000000..9d3f386e40d --- /dev/null +++ b/libavformat/pdvdec.c @@ -0,0 +1,173 @@ +/* + * PDV demuxer + * Copyright (c) 2023 Paul B Mahol + * + * 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 "avformat.h" +#include "internal.h" + +#define PDV_MAGIC "Playdate VID\x00\x00\x00\x00" + +typedef struct PDVDemuxContext { + int current_frame; + uint8_t *frame_flags; + uint32_t *frame_offsets; +} PDVDemuxContext; + +static int pdv_probe(const AVProbeData *pd) +{ + if (strncmp(pd->buf, PDV_MAGIC, sizeof(PDV_MAGIC) - 1) == 0) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int pdv_read_header(AVFormatContext *s) +{ + PDVDemuxContext *p = s->priv_data; + AVIOContext *pb = s->pb; + AVCodecParameters *par; + AVStream *st; + uint64_t start; + uint32_t fps; + + avio_skip(pb, 16); + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + par = st->codecpar; + par->codec_type = AVMEDIA_TYPE_VIDEO; + par->codec_id = AV_CODEC_ID_PDV; + st->start_time = 0; + st->duration = + st->nb_frames = avio_rl16(pb); + avio_skip(pb, 2); + fps = avio_rl32(pb); + st->avg_frame_rate = av_d2q(av_int2float(fps), INT_MAX); + par->width = avio_rl16(pb); + par->height = avio_rl16(pb); + + avpriv_set_pts_info(st, 64, st->avg_frame_rate.den, st->avg_frame_rate.num); + + p->current_frame = 0; + p->frame_flags = av_calloc(st->nb_frames + 1, sizeof(*p->frame_flags)); + p->frame_offsets = av_calloc(st->nb_frames + 1, sizeof(*p->frame_offsets)); + + if (!p->frame_flags || !p->frame_offsets) + return AVERROR(ENOMEM); + + for (int n = 0; n <= st->nb_frames; n++) { + const uint32_t entry = avio_rl32(pb); + + p->frame_flags[n] = entry & 3; + p->frame_offsets[n] = entry >> 2; + } + + start = avio_tell(pb); + + for (int n = 0; n < st->nb_frames; n++) { + const uint64_t pos = start + p->frame_offsets[n]; + const int32_t size = p->frame_offsets[n+1] - p->frame_offsets[n]; + const int flags = p->frame_flags[n] & 1 ? AVINDEX_KEYFRAME : 0; + + if (p->frame_flags[n] == 0 || size <= 0 || + ((pb->seekable & AVIO_SEEKABLE_NORMAL) && pos + size > avio_size(pb))) + break; + av_add_index_entry(st, pos, n, size, 0, flags); + } + + return 0; +} + +static int pdv_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + PDVDemuxContext *p = s->priv_data; + AVStream *st = s->streams[0]; + FFStream *const sti = ffstream(st); + AVIOContext *pb = s->pb; + int32_t size, flags, ret; + int64_t pos; + + if (p->current_frame >= st->nb_frames) + return AVERROR_EOF; + + if (p->current_frame >= sti->nb_index_entries) + return AVERROR(EIO); + + pos = sti->index_entries[p->current_frame].pos; + flags = sti->index_entries[p->current_frame].flags; + size = sti->index_entries[p->current_frame].size; + + avio_seek(pb, pos, SEEK_SET); + if (avio_feof(pb) || ((pb->seekable & AVIO_SEEKABLE_NORMAL) && pos + size > avio_size(pb)) || size == 0) + return AVERROR_EOF; + + ret = av_get_packet(pb, pkt, size); + if (ret < 0) + return ret; + + if (flags & AVINDEX_KEYFRAME) + pkt->flags |= AV_PKT_FLAG_KEY; + pkt->stream_index = 0; + pkt->pts = p->current_frame++; + pkt->duration = 1; + + return 0; +} + +static int pdv_read_close(AVFormatContext *s) +{ + PDVDemuxContext *p = s->priv_data; + + av_freep(&p->frame_flags); + av_freep(&p->frame_offsets); + + return 0; +} + +static int pdv_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) +{ + PDVDemuxContext *p = s->priv_data; + AVStream *st = s->streams[stream_index]; + int index = av_index_search_timestamp(st, timestamp, flags); + + if (index < 0) + return -1; + + if (avio_seek(s->pb, ffstream(st)->index_entries[index].pos, SEEK_SET) < 0) + return -1; + + p->current_frame = index; + + return 0; +} + +const AVInputFormat ff_pdv_demuxer = { + .name = "pdv", + .long_name = NULL_IF_CONFIG_SMALL("PlayDate Video"), + .priv_data_size = sizeof(PDVDemuxContext), + .flags_internal = FF_FMT_INIT_CLEANUP, + .read_probe = pdv_probe, + .read_header = pdv_read_header, + .read_packet = pdv_read_packet, + .read_close = pdv_read_close, + .read_seek = pdv_read_seek, + .extensions = "pdv", +}; diff --git a/libavformat/rangecoder_dec.c b/libavformat/rangecoder_dec.c new file mode 100644 index 00000000000..7b727f656e5 --- /dev/null +++ b/libavformat/rangecoder_dec.c @@ -0,0 +1 @@ +#include "libavcodec/rangecoder.c" diff --git a/libavformat/rawdec.c b/libavformat/rawdec.c index de804366ed1..1dd7fafcf0f 100644 --- a/libavformat/rawdec.c +++ b/libavformat/rawdec.c @@ -27,8 +27,6 @@ #include "rawdec.h" #include "libavutil/opt.h" -#include "libavcodec/avcodec.h" - #define RAW_PACKET_SIZE 1024 int ff_raw_read_partial_packet(AVFormatContext *s, AVPacket *pkt) @@ -86,7 +84,7 @@ int ff_raw_video_read_header(AVFormatContext *s) st->codecpar->codec_id = s->iformat->raw_codec_id; sti->need_parsing = AVSTREAM_PARSE_FULL_RAW; - sti->avctx->framerate = s1->framerate; + st->avg_frame_rate = s1->framerate; avpriv_set_pts_info(st, 64, 1, 1200000); fail: diff --git a/libavformat/rawdec.h b/libavformat/rawdec.h index f843fe5a2e0..91d0b659afd 100644 --- a/libavformat/rawdec.h +++ b/libavformat/rawdec.h @@ -57,7 +57,7 @@ const AVInputFormat ff_ ## shortname ## _demuxer = {\ .read_header = ff_raw_video_read_header,\ .read_packet = ff_raw_read_partial_packet,\ .extensions = ext,\ - .flags = flag,\ + .flags = flag | AVFMT_NOTIMESTAMPS,\ .raw_codec_id = id,\ .priv_data_size = sizeof(FFRawVideoDemuxerContext),\ .priv_class = &ff_rawvideo_demuxer_class,\ diff --git a/libavformat/rawenc.c b/libavformat/rawenc.c index 267fce252d1..f916db13a20 100644 --- a/libavformat/rawenc.c +++ b/libavformat/rawenc.c @@ -59,16 +59,16 @@ static int force_one_stream(AVFormatContext *s) /* Note: Do not forget to add new entries to the Makefile as well. */ #if CONFIG_AC3_MUXER -const AVOutputFormat ff_ac3_muxer = { - .name = "ac3", - .long_name = NULL_IF_CONFIG_SMALL("raw AC-3"), - .mime_type = "audio/x-ac3", - .extensions = "ac3", - .audio_codec = AV_CODEC_ID_AC3, - .video_codec = AV_CODEC_ID_NONE, +const FFOutputFormat ff_ac3_muxer = { + .p.name = "ac3", + .p.long_name = NULL_IF_CONFIG_SMALL("raw AC-3"), + .p.mime_type = "audio/x-ac3", + .p.extensions = "ac3", + .p.audio_codec = AV_CODEC_ID_AC3, + .p.video_codec = AV_CODEC_ID_NONE, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif @@ -92,266 +92,266 @@ static int adx_write_trailer(AVFormatContext *s) return 0; } -const AVOutputFormat ff_adx_muxer = { - .name = "adx", - .long_name = NULL_IF_CONFIG_SMALL("CRI ADX"), - .extensions = "adx", - .audio_codec = AV_CODEC_ID_ADPCM_ADX, - .video_codec = AV_CODEC_ID_NONE, +const FFOutputFormat ff_adx_muxer = { + .p.name = "adx", + .p.long_name = NULL_IF_CONFIG_SMALL("CRI ADX"), + .p.extensions = "adx", + .p.audio_codec = AV_CODEC_ID_ADPCM_ADX, + .p.video_codec = AV_CODEC_ID_NONE, .init = force_one_stream, .write_packet = ff_raw_write_packet, .write_trailer = adx_write_trailer, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_APTX_MUXER -const AVOutputFormat ff_aptx_muxer = { - .name = "aptx", - .long_name = NULL_IF_CONFIG_SMALL("raw aptX (Audio Processing Technology for Bluetooth)"), - .extensions = "aptx", - .audio_codec = AV_CODEC_ID_APTX, - .video_codec = AV_CODEC_ID_NONE, +const FFOutputFormat ff_aptx_muxer = { + .p.name = "aptx", + .p.long_name = NULL_IF_CONFIG_SMALL("raw aptX (Audio Processing Technology for Bluetooth)"), + .p.extensions = "aptx", + .p.audio_codec = AV_CODEC_ID_APTX, + .p.video_codec = AV_CODEC_ID_NONE, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_APTX_HD_MUXER -const AVOutputFormat ff_aptx_hd_muxer = { - .name = "aptx_hd", - .long_name = NULL_IF_CONFIG_SMALL("raw aptX HD (Audio Processing Technology for Bluetooth)"), - .extensions = "aptxhd", - .audio_codec = AV_CODEC_ID_APTX_HD, - .video_codec = AV_CODEC_ID_NONE, +const FFOutputFormat ff_aptx_hd_muxer = { + .p.name = "aptx_hd", + .p.long_name = NULL_IF_CONFIG_SMALL("raw aptX HD (Audio Processing Technology for Bluetooth)"), + .p.extensions = "aptxhd", + .p.audio_codec = AV_CODEC_ID_APTX_HD, + .p.video_codec = AV_CODEC_ID_NONE, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_AVS2_MUXER -const AVOutputFormat ff_avs2_muxer = { - .name = "avs2", - .long_name = NULL_IF_CONFIG_SMALL("raw AVS2-P2/IEEE1857.4 video"), - .extensions = "avs,avs2", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_AVS2, +const FFOutputFormat ff_avs2_muxer = { + .p.name = "avs2", + .p.long_name = NULL_IF_CONFIG_SMALL("raw AVS2-P2/IEEE1857.4 video"), + .p.extensions = "avs,avs2", + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_AVS2, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_AVS3_MUXER -const AVOutputFormat ff_avs3_muxer = { - .name = "avs3", - .long_name = NULL_IF_CONFIG_SMALL("AVS3-P2/IEEE1857.10"), - .extensions = "avs3", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_AVS3, +const FFOutputFormat ff_avs3_muxer = { + .p.name = "avs3", + .p.long_name = NULL_IF_CONFIG_SMALL("AVS3-P2/IEEE1857.10"), + .p.extensions = "avs3", + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_AVS3, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_CAVSVIDEO_MUXER -const AVOutputFormat ff_cavsvideo_muxer = { - .name = "cavsvideo", - .long_name = NULL_IF_CONFIG_SMALL("raw Chinese AVS (Audio Video Standard) video"), - .extensions = "cavs", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_CAVS, +const FFOutputFormat ff_cavsvideo_muxer = { + .p.name = "cavsvideo", + .p.long_name = NULL_IF_CONFIG_SMALL("raw Chinese AVS (Audio Video Standard) video"), + .p.extensions = "cavs", + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_CAVS, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_CODEC2RAW_MUXER -const AVOutputFormat ff_codec2raw_muxer = { - .name = "codec2raw", - .long_name = NULL_IF_CONFIG_SMALL("raw codec2 muxer"), - .audio_codec = AV_CODEC_ID_CODEC2, - .video_codec = AV_CODEC_ID_NONE, +const FFOutputFormat ff_codec2raw_muxer = { + .p.name = "codec2raw", + .p.long_name = NULL_IF_CONFIG_SMALL("raw codec2 muxer"), + .p.audio_codec = AV_CODEC_ID_CODEC2, + .p.video_codec = AV_CODEC_ID_NONE, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_DATA_MUXER -const AVOutputFormat ff_data_muxer = { - .name = "data", - .long_name = NULL_IF_CONFIG_SMALL("raw data"), +const FFOutputFormat ff_data_muxer = { + .p.name = "data", + .p.long_name = NULL_IF_CONFIG_SMALL("raw data"), .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_DFPWM_MUXER -const AVOutputFormat ff_dfpwm_muxer = { - .name = "dfpwm", - .long_name = NULL_IF_CONFIG_SMALL("raw DFPWM1a"), - .extensions = "dfpwm", - .audio_codec = AV_CODEC_ID_DFPWM, - .video_codec = AV_CODEC_ID_NONE, +const FFOutputFormat ff_dfpwm_muxer = { + .p.name = "dfpwm", + .p.long_name = NULL_IF_CONFIG_SMALL("raw DFPWM1a"), + .p.extensions = "dfpwm", + .p.audio_codec = AV_CODEC_ID_DFPWM, + .p.video_codec = AV_CODEC_ID_NONE, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_DIRAC_MUXER -const AVOutputFormat ff_dirac_muxer = { - .name = "dirac", - .long_name = NULL_IF_CONFIG_SMALL("raw Dirac"), - .extensions = "drc,vc2", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_DIRAC, +const FFOutputFormat ff_dirac_muxer = { + .p.name = "dirac", + .p.long_name = NULL_IF_CONFIG_SMALL("raw Dirac"), + .p.extensions = "drc,vc2", + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_DIRAC, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_DNXHD_MUXER -const AVOutputFormat ff_dnxhd_muxer = { - .name = "dnxhd", - .long_name = NULL_IF_CONFIG_SMALL("raw DNxHD (SMPTE VC-3)"), - .extensions = "dnxhd,dnxhr", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_DNXHD, +const FFOutputFormat ff_dnxhd_muxer = { + .p.name = "dnxhd", + .p.long_name = NULL_IF_CONFIG_SMALL("raw DNxHD (SMPTE VC-3)"), + .p.extensions = "dnxhd,dnxhr", + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_DNXHD, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_DTS_MUXER -const AVOutputFormat ff_dts_muxer = { - .name = "dts", - .long_name = NULL_IF_CONFIG_SMALL("raw DTS"), - .mime_type = "audio/x-dca", - .extensions = "dts", - .audio_codec = AV_CODEC_ID_DTS, - .video_codec = AV_CODEC_ID_NONE, +const FFOutputFormat ff_dts_muxer = { + .p.name = "dts", + .p.long_name = NULL_IF_CONFIG_SMALL("raw DTS"), + .p.mime_type = "audio/x-dca", + .p.extensions = "dts", + .p.audio_codec = AV_CODEC_ID_DTS, + .p.video_codec = AV_CODEC_ID_NONE, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_EAC3_MUXER -const AVOutputFormat ff_eac3_muxer = { - .name = "eac3", - .long_name = NULL_IF_CONFIG_SMALL("raw E-AC-3"), - .mime_type = "audio/x-eac3", - .extensions = "eac3,ec3", - .audio_codec = AV_CODEC_ID_EAC3, - .video_codec = AV_CODEC_ID_NONE, +const FFOutputFormat ff_eac3_muxer = { + .p.name = "eac3", + .p.long_name = NULL_IF_CONFIG_SMALL("raw E-AC-3"), + .p.mime_type = "audio/x-eac3", + .p.extensions = "eac3,ec3", + .p.audio_codec = AV_CODEC_ID_EAC3, + .p.video_codec = AV_CODEC_ID_NONE, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_G722_MUXER -const AVOutputFormat ff_g722_muxer = { - .name = "g722", - .long_name = NULL_IF_CONFIG_SMALL("raw G.722"), - .mime_type = "audio/G722", - .extensions = "g722", - .audio_codec = AV_CODEC_ID_ADPCM_G722, - .video_codec = AV_CODEC_ID_NONE, +const FFOutputFormat ff_g722_muxer = { + .p.name = "g722", + .p.long_name = NULL_IF_CONFIG_SMALL("raw G.722"), + .p.mime_type = "audio/G722", + .p.extensions = "g722", + .p.audio_codec = AV_CODEC_ID_ADPCM_G722, + .p.video_codec = AV_CODEC_ID_NONE, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_G723_1_MUXER -const AVOutputFormat ff_g723_1_muxer = { - .name = "g723_1", - .long_name = NULL_IF_CONFIG_SMALL("raw G.723.1"), - .mime_type = "audio/g723", - .extensions = "tco,rco", - .audio_codec = AV_CODEC_ID_G723_1, - .video_codec = AV_CODEC_ID_NONE, +const FFOutputFormat ff_g723_1_muxer = { + .p.name = "g723_1", + .p.long_name = NULL_IF_CONFIG_SMALL("raw G.723.1"), + .p.mime_type = "audio/g723", + .p.extensions = "tco,rco", + .p.audio_codec = AV_CODEC_ID_G723_1, + .p.video_codec = AV_CODEC_ID_NONE, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_G726_MUXER -const AVOutputFormat ff_g726_muxer = { - .name = "g726", - .long_name = NULL_IF_CONFIG_SMALL("raw big-endian G.726 (\"left-justified\")"), - .audio_codec = AV_CODEC_ID_ADPCM_G726, - .video_codec = AV_CODEC_ID_NONE, +const FFOutputFormat ff_g726_muxer = { + .p.name = "g726", + .p.long_name = NULL_IF_CONFIG_SMALL("raw big-endian G.726 (\"left-justified\")"), + .p.audio_codec = AV_CODEC_ID_ADPCM_G726, + .p.video_codec = AV_CODEC_ID_NONE, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_G726LE_MUXER -const AVOutputFormat ff_g726le_muxer = { - .name = "g726le", - .long_name = NULL_IF_CONFIG_SMALL("raw little-endian G.726 (\"right-justified\")"), - .audio_codec = AV_CODEC_ID_ADPCM_G726LE, - .video_codec = AV_CODEC_ID_NONE, +const FFOutputFormat ff_g726le_muxer = { + .p.name = "g726le", + .p.long_name = NULL_IF_CONFIG_SMALL("raw little-endian G.726 (\"right-justified\")"), + .p.audio_codec = AV_CODEC_ID_ADPCM_G726LE, + .p.video_codec = AV_CODEC_ID_NONE, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_GSM_MUXER -const AVOutputFormat ff_gsm_muxer = { - .name = "gsm", - .long_name = NULL_IF_CONFIG_SMALL("raw GSM"), - .mime_type = "audio/x-gsm", - .extensions = "gsm", - .audio_codec = AV_CODEC_ID_GSM, - .video_codec = AV_CODEC_ID_NONE, +const FFOutputFormat ff_gsm_muxer = { + .p.name = "gsm", + .p.long_name = NULL_IF_CONFIG_SMALL("raw GSM"), + .p.mime_type = "audio/x-gsm", + .p.extensions = "gsm", + .p.audio_codec = AV_CODEC_ID_GSM, + .p.video_codec = AV_CODEC_ID_NONE, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_H261_MUXER -const AVOutputFormat ff_h261_muxer = { - .name = "h261", - .long_name = NULL_IF_CONFIG_SMALL("raw H.261"), - .mime_type = "video/x-h261", - .extensions = "h261", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_H261, +const FFOutputFormat ff_h261_muxer = { + .p.name = "h261", + .p.long_name = NULL_IF_CONFIG_SMALL("raw H.261"), + .p.mime_type = "video/x-h261", + .p.extensions = "h261", + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_H261, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_H263_MUXER -const AVOutputFormat ff_h263_muxer = { - .name = "h263", - .long_name = NULL_IF_CONFIG_SMALL("raw H.263"), - .mime_type = "video/x-h263", - .extensions = "h263", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_H263, +const FFOutputFormat ff_h263_muxer = { + .p.name = "h263", + .p.long_name = NULL_IF_CONFIG_SMALL("raw H.263"), + .p.mime_type = "video/x-h263", + .p.extensions = "h263", + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_H263, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif @@ -365,16 +365,39 @@ static int h264_check_bitstream(AVFormatContext *s, AVStream *st, return 1; } -const AVOutputFormat ff_h264_muxer = { - .name = "h264", - .long_name = NULL_IF_CONFIG_SMALL("raw H.264 video"), - .extensions = "h264,264", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_H264, +const FFOutputFormat ff_h264_muxer = { + .p.name = "h264", + .p.long_name = NULL_IF_CONFIG_SMALL("raw H.264 video"), + .p.extensions = "h264,264", + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_H264, .init = force_one_stream, .write_packet = ff_raw_write_packet, .check_bitstream = h264_check_bitstream, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, +}; +#endif + +#if CONFIG_VVC_MUXER +static int vvc_check_bitstream(AVFormatContext *s, AVStream *st, + const AVPacket *pkt) +{ + if (pkt->size >= 5 && AV_RB32(pkt->data) != 0x0000001 && + AV_RB24(pkt->data) != 0x000001) + return ff_stream_add_bitstream_filter(st, "vvc_mp4toannexb", NULL); + return 1; +} + +const FFOutputFormat ff_vvc_muxer = { + .p.name = "vvc", + .p.long_name = NULL_IF_CONFIG_SMALL("raw H.266/VVC video"), + .p.extensions = "vvc,h266,266", + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_VVC, + .init = force_one_stream, + .write_packet = ff_raw_write_packet, + .check_bitstream = vvc_check_bitstream, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif @@ -388,97 +411,110 @@ static int hevc_check_bitstream(AVFormatContext *s, AVStream *st, return 1; } -const AVOutputFormat ff_hevc_muxer = { - .name = "hevc", - .long_name = NULL_IF_CONFIG_SMALL("raw HEVC video"), - .extensions = "hevc,h265,265", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_HEVC, +const FFOutputFormat ff_hevc_muxer = { + .p.name = "hevc", + .p.long_name = NULL_IF_CONFIG_SMALL("raw HEVC video"), + .p.extensions = "hevc,h265,265", + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_HEVC, .init = force_one_stream, .write_packet = ff_raw_write_packet, .check_bitstream = hevc_check_bitstream, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, +}; +#endif + +#if CONFIG_EVC_MUXER +const FFOutputFormat ff_evc_muxer = { + .p.name = "evc", + .p.long_name = NULL_IF_CONFIG_SMALL("raw EVC video"), + .p.extensions = "evc", + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_EVC, + .init = force_one_stream, + .write_packet = ff_raw_write_packet, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_M4V_MUXER -const AVOutputFormat ff_m4v_muxer = { - .name = "m4v", - .long_name = NULL_IF_CONFIG_SMALL("raw MPEG-4 video"), - .extensions = "m4v", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_MPEG4, +const FFOutputFormat ff_m4v_muxer = { + .p.name = "m4v", + .p.long_name = NULL_IF_CONFIG_SMALL("raw MPEG-4 video"), + .p.extensions = "m4v", + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_MPEG4, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_MJPEG_MUXER -const AVOutputFormat ff_mjpeg_muxer = { - .name = "mjpeg", - .long_name = NULL_IF_CONFIG_SMALL("raw MJPEG video"), - .mime_type = "video/x-mjpeg", - .extensions = "mjpg,mjpeg", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_MJPEG, +const FFOutputFormat ff_mjpeg_muxer = { + .p.name = "mjpeg", + .p.long_name = NULL_IF_CONFIG_SMALL("raw MJPEG video"), + .p.mime_type = "video/x-mjpeg", + .p.extensions = "mjpg,mjpeg", + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_MJPEG, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_MLP_MUXER -const AVOutputFormat ff_mlp_muxer = { - .name = "mlp", - .long_name = NULL_IF_CONFIG_SMALL("raw MLP"), - .extensions = "mlp", - .audio_codec = AV_CODEC_ID_MLP, - .video_codec = AV_CODEC_ID_NONE, +const FFOutputFormat ff_mlp_muxer = { + .p.name = "mlp", + .p.long_name = NULL_IF_CONFIG_SMALL("raw MLP"), + .p.extensions = "mlp", + .p.audio_codec = AV_CODEC_ID_MLP, + .p.video_codec = AV_CODEC_ID_NONE, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_MP2_MUXER -const AVOutputFormat ff_mp2_muxer = { - .name = "mp2", - .long_name = NULL_IF_CONFIG_SMALL("MP2 (MPEG audio layer 2)"), - .mime_type = "audio/mpeg", - .extensions = "mp2,m2a,mpa", - .audio_codec = AV_CODEC_ID_MP2, - .video_codec = AV_CODEC_ID_NONE, +const FFOutputFormat ff_mp2_muxer = { + .p.name = "mp2", + .p.long_name = NULL_IF_CONFIG_SMALL("MP2 (MPEG audio layer 2)"), + .p.mime_type = "audio/mpeg", + .p.extensions = "mp2,m2a,mpa", + .p.audio_codec = AV_CODEC_ID_MP2, + .p.video_codec = AV_CODEC_ID_NONE, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_MPEG1VIDEO_MUXER -const AVOutputFormat ff_mpeg1video_muxer = { - .name = "mpeg1video", - .long_name = NULL_IF_CONFIG_SMALL("raw MPEG-1 video"), - .mime_type = "video/mpeg", - .extensions = "mpg,mpeg,m1v", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_MPEG1VIDEO, +const FFOutputFormat ff_mpeg1video_muxer = { + .p.name = "mpeg1video", + .p.long_name = NULL_IF_CONFIG_SMALL("raw MPEG-1 video"), + .p.mime_type = "video/mpeg", + .p.extensions = "mpg,mpeg,m1v", + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_MPEG1VIDEO, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_MPEG2VIDEO_MUXER -const AVOutputFormat ff_mpeg2video_muxer = { - .name = "mpeg2video", - .long_name = NULL_IF_CONFIG_SMALL("raw MPEG-2 video"), - .extensions = "m2v", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_MPEG2VIDEO, +const FFOutputFormat ff_mpeg2video_muxer = { + .p.name = "mpeg2video", + .p.long_name = NULL_IF_CONFIG_SMALL("raw MPEG-2 video"), + .p.extensions = "m2v", + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_MPEG2VIDEO, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif @@ -489,66 +525,66 @@ static int obu_check_bitstream(AVFormatContext *s, AVStream *st, return ff_stream_add_bitstream_filter(st, "av1_metadata", "td=insert"); } -const AVOutputFormat ff_obu_muxer = { - .name = "obu", - .long_name = NULL_IF_CONFIG_SMALL("AV1 low overhead OBU"), - .extensions = "obu", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_AV1, +const FFOutputFormat ff_obu_muxer = { + .p.name = "obu", + .p.long_name = NULL_IF_CONFIG_SMALL("AV1 low overhead OBU"), + .p.extensions = "obu", + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_AV1, .init = force_one_stream, .write_packet = ff_raw_write_packet, .check_bitstream = obu_check_bitstream, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_RAWVIDEO_MUXER -const AVOutputFormat ff_rawvideo_muxer = { - .name = "rawvideo", - .long_name = NULL_IF_CONFIG_SMALL("raw video"), - .extensions = "yuv,rgb", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_RAWVIDEO, +const FFOutputFormat ff_rawvideo_muxer = { + .p.name = "rawvideo", + .p.long_name = NULL_IF_CONFIG_SMALL("raw video"), + .p.extensions = "yuv,rgb", + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_RAWVIDEO, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_SBC_MUXER -const AVOutputFormat ff_sbc_muxer = { - .name = "sbc", - .long_name = NULL_IF_CONFIG_SMALL("raw SBC"), - .mime_type = "audio/x-sbc", - .extensions = "sbc,msbc", - .audio_codec = AV_CODEC_ID_SBC, +const FFOutputFormat ff_sbc_muxer = { + .p.name = "sbc", + .p.long_name = NULL_IF_CONFIG_SMALL("raw SBC"), + .p.mime_type = "audio/x-sbc", + .p.extensions = "sbc,msbc", + .p.audio_codec = AV_CODEC_ID_SBC, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_TRUEHD_MUXER -const AVOutputFormat ff_truehd_muxer = { - .name = "truehd", - .long_name = NULL_IF_CONFIG_SMALL("raw TrueHD"), - .extensions = "thd", - .audio_codec = AV_CODEC_ID_TRUEHD, - .video_codec = AV_CODEC_ID_NONE, +const FFOutputFormat ff_truehd_muxer = { + .p.name = "truehd", + .p.long_name = NULL_IF_CONFIG_SMALL("raw TrueHD"), + .p.extensions = "thd", + .p.audio_codec = AV_CODEC_ID_TRUEHD, + .p.video_codec = AV_CODEC_ID_NONE, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_VC1_MUXER -const AVOutputFormat ff_vc1_muxer = { - .name = "vc1", - .long_name = NULL_IF_CONFIG_SMALL("raw VC-1 video"), - .extensions = "vc1", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_VC1, +const FFOutputFormat ff_vc1_muxer = { + .p.name = "vc1", + .p.long_name = NULL_IF_CONFIG_SMALL("raw VC-1 video"), + .p.extensions = "vc1", + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_VC1, .init = force_one_stream, .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, + .p.flags = AVFMT_NOTIMESTAMPS, }; #endif diff --git a/libavformat/realtextdec.c b/libavformat/realtextdec.c index c281dec346d..7992a5b7fcd 100644 --- a/libavformat/realtextdec.c +++ b/libavformat/realtextdec.c @@ -80,6 +80,10 @@ static int realtext_read_header(AVFormatContext *s) const int64_t pos = ff_text_pos(&tr) - (c != 0); int n = ff_smil_extract_next_text_chunk(&tr, &buf, &c); + if (n < 0) { + res = n; + goto end; + } if (n == 0) break; @@ -103,7 +107,7 @@ static int realtext_read_header(AVFormatContext *s) /* if we just read a