subtree updates Jan-13-2023

meta-openembedded: d04444509a..cd13881611:
  Alex Kiernan (10):
        mdns: Upgrade 1310.140.1 -> 1790.40.31
        mdns: Set MDNS_VERSIONSTR_NODTS
        mdns: Upgrade 1790.40.31 -> 1790.60.25
        ostree: Upgrade 2022.5 -> 2022.7
        ostree: Use systemd_system_unitdir for systemd units
        ostree: Switch to fuse3 which is supported in ostree now
        ostree: Fix comments for configuration/ptest
        ostree: Handle musl's ERANGE mapping
        usbguard: Remove pegtl from DEPENDS
        usbguard: Upgrade 1.1.1 -> 1.1.2

  Alex Stewart (2):
        gvfs: stylize DEPENDS
        gvfs: obviate the ssh-client requirement for gvfs

  Alexander Kanavin (5):
        frr: add a patch to correctly check presence of python from pkg-config
        lirc: correctly use PYTHONPATH
        libportal: move to oe-core
        packagegroup-meta-python: drop python3-strict-rfc3339
        nftables: fix builds with latest setuptools

  Alexander Stein (1):
        dool: Add patch to fix rebuild

  Archana Polampalli (1):
        Nodejs - Upgrade to 16.18.1

  Bartosz Golaszewski (3):
        python3-kmod: new package
        python3-watchdogdev: new package
        packagegroup-meta-python: add missing packages

  Bruce Ashfield (1):
        zfs: update to 2.1.7

  Changqing Li (5):
        linuxptp: fix do_compile error
        keyutils: fix ptest failed since "+++ Can't Determine Endianness"
        graphviz: Do not build tcl support for native
        redis: 6.2.7 -> 6.2.8
        redis: 7.0.5 -> 7.0.7

  Chen Pei (2):
        suitesparse:fix git branch in SRC_URI
        botan: upgrade 2.19.2 -> 2.19.3

  Chen Qi (4):
        xfce4-verve-plugin: fix do_configure faiure about missing libpcre
        networkmanager: fix dhcpcd PACKAGECONFIG
        networkmanager: install config files into correct place
        networkmanager: fix /etc/resolv.conf handling

  Christian Eggers (1):
        boost-url: remove recipe

  Clément Péron (3):
        navigation: bump proj to 9.1.0 library
        proj: add a packageconfig to build as a static library
        proj: avoid leaking host path in libproj

  Devendra Tewari (1):
        android-tools: Use echo instead of bbnote

  Dmitry Baryshkov (1):
        nss: fix cross-compilation error

  Erwann Roussy (3):
        python3-schedutils: add recipe
        python3-linux-procfs: add recipe
        tuna: add recipe

  Fabio Estevam (2):
        remmina: Update to 1.4.28
        crucible: Upgrade to 2022.12.06

  Geoff Parker (1):
        python3-yappi: upgrade 1.3.6 -> 1.4.0, python 3.11 compatible

  Gerbrand De Laender (1):
        python3-aioserial: new package

  Gianfranco Costamagna (2):
        vbxguestdrivers: upgrade 7.0.2 -> 7.0.4
        boinc-client: Update boinc from 7.18.1 to 7.20.4

  Gianluigi Spagnuolo (1):
        libbpf: add native and nativesdk BBCLASSEXTEND

  Hains van den Bosch (2):
        python3-twisted: Add python3-asyncio to RDEPENDS
        python3-twisted: Add python3-typing-extensions to RDEPENDS

  He Zhe (1):
        protobuf: upgrade 3.21.5 -> 3.21.10

  Jose Quaresma (1):
        lshw: bump to 42fef565

  Kai Kang (31):
        freeradius: fix multilib systemd service start failure
        wxwidgets: 3.1.5 -> 3.2.1
        python3-attrdict3: add recipe with version 2.0.2
        python3-wxgtk4: 4.1.1 -> 4.2.0
        xfce4-settings: 4.16.3 -> 4.16.5
        python3-m2crypto: fix CVE-2020-25657 and buildpaths qa issue
        fixup! wxwidgets: 3.1.5 -> 3.2.1
        postfix: fix multilib conflict of sample-main.cf
        python3-wxgtk4: replace deprecated inspect.getargspec
        libxfce4ui: 4.16.1 -> 4.18.0
        thunar-volman: 4.16.0 -> 4.18.0
        xfce4-cpufreq-plugin: 1.2.7 -> 1.2.8
        xfce4-wavelan-plugin: 0.6.2 -> 0.6.3
        xfce4-cpugraph-plugin: 1.2.6 -> 1.2.7
        xfce4-sensors-plugin: 1.4.3 -> 1.4.4
        thunar-shares-plugin: Bump GLib minimum required to 2.26
        xfce4-dev-tools: 4.16.0 -> 4.18.0
        libxfce4util: 4.16.0 -> 4.18.0
        exo: 4.16.4 -> 4.18.0
        garcon: 4.16.1 -> 4.18.0
        xfce4-panel: 4.16.3 -> 4.18.0
        thunar: 4.16.9 -> 4.18.0
        tumbler: 4.16.0 -> 4.18.0
        xfconf: 4.16.0 -> 4.18.0
        xfce4-appfinder: 4.16.1 -> 4.18.0
        xfce4-settings: 4.16.5 -> 4.18.0
        xfce4-power-manager: 4.16.0 -> 4.18.0
        xfce4-session: 4.16.0 -> 4.18.0
        xfwm4: 4.16.1 -> 4.18.0
        xfdesktop: 4.16.0 -> 4.18.0
        xorg-lib: set XORG_EXT for recipes

  Khem Raj (91):
        gnome-text-editor: Add missing libpcre build time depenedency
        ettercap: Add missing dependency on libpcre
        xcb-util-cursor: Update to 0.1.4
        lldpd: Use github release assets for SRC_URI
        aufs-util: Fix build with large file support enabled systems
        volume-key: Inherit python3targetconfig
        proj: Enable apps when building native variant
        python3-pyproj: Export PROJ_DIR
        satyr: Inherit python3targetconfig
        rest: Re-add 0.8.1
        gfbgraph: Use rest 0.8.1
        audit: Inherit python3targetconfig
        opensaf: Check for _FILE_OFFSET_BITS instead of __TIMESIZE
        flite: Add missing deps on alsa-lib and chrpath
        python3-pystemd: Regenerate .c sources using newer cython
        libreport: Inherit python3targetconfig
        uw-imap: Disable parallelism
        gnome-calendar: Upgrade to 43.1
        gnome-photos: Upgrade to 43.0
        libgweather: Remove 40.0
        waf-samba.bbclass: point PYTHON_CONFIG to target python3-config
        amtk: Add missing dep on python3-pygments-native
        fontforge: Inherit python3targetconfig
        tepl: Add missing dep on python3-pygments-native
        alsa-oss: Remove recipe
        opencv: Check for commercial_ffmpeg as well to enable ffmpeg
        opencv: Fix build with ffmpeg 5.1+
        fwts: Upgrade to 22.11.00
        minio: Disable on mips
        sip: Add recipe for 6.7.5
        imapfilter: Upgrade to 2.7.6
        perfetto: Do not pass TUNE_CCARGS to native/host compiler
        stressapptest: Upgrade to latest tip
        mariadb: Upgrade to 10.11.1
        surf: Depend on gcr3
        fatcat: Enable 64bit off_t
        stressapptest: Fix build with largefile support and musl
        nspr: Upgrade to 4.35
        cryptsetup: Upgrade to 2.6.0
        libyui,libyui-ncurses: Upgrade to 4.2.3
        inotify-tools: Fix build on musl and lfs64
        sdbus-c++-libsystemd: Upgrade to 250.9 systemd release
        xfsprogs: Upgrade to 6.0.0
        drbd,drbd-utils: Upgrade to 9.2.1 and drbd-utils to 9.22.0
        libtraceevent: Add recipe
        libtracefs: Add recipe
        trace-cmd: Remove use of off64_t and lseek64
        xfsdump: Add -D_LARGEFILE64_SOURCE on musl
        xfstests: Add -D_LARGEFILE64_SOURCE on musl
        mariadb: Alias lseek64/open64/ftruncate64 on musl systems
        gperftools: Define off64_t on musl
        android-tools: Define lseek64 = lseek on musl
        php: Add -D_LARGEFILE64_SOURCE to cflags
        spice-gtk: Use libucontext for coroutines on musl
        wxwidgets: Fix build with musl
        wxwidgets: Fix locale on musl
        wxwidgets: Set HAVE_LARGEFILE_SUPPORT
        python3-wxgtk4: Do not use GetAssertStackTrace with USE_STACKWALKER disabled
        f2fs-tools: Upgrade to 1.15.0
        trace-cmd: Pass ldflags to compiler
        parole: Define DATADIRNAME
        abseil-cpp: Replace off64_t with off_t
        vsftpd_3.0.5.bb: Define _LARGEFILE64_SOURCE on musl
        mozjs-102: Disable mozilla stackwalk on musl
        fatresize: Fix build when 64bit time_t is enabled
        boinc-client: Fix build when using 64bit time_t
        python3-grpcio: Define -D_LARGEFILE64_SOURCE only for musl
        gnome-online-accounts: Fix build race seen on musl systems
        imagemagick: Do not set ac_cv_sys_file_offset_bits
        spdlog: Do not use LFS64 functions with musl
        mongodb: Do not use off64_t on musl
        dracut: Do not undefine _FILE_OFFSET_BITS
        libcamera: Diable 64bit time_t on glibc targets
        v4l-utils: Diable 64bit time_t on glibc targets
        opensaf: Fix the check for __fsblkcnt64_t size
        libcereal,poco: Link with -latomic on ppc32 as well
        sshpass: Use SPDX identified string for GPLv2
        nftables: Upgrade to 1.0.6
        mycroft: Check for pulseaudio in distro features
        trace-cmd: Build libs before building rest
        open-vm-tools: Fix build with 64-bit time_t
        libtraceevent: Move plugins into package of its own
        trace-cmd: Upgrade to 3.1.5
        luajit: Update to latest on v2.1 branch
        concurrencykit: Update to 0.7.0
        concurrencykit: Set correct PLAT value for riscv32
        concurrencykit: Fix build on riscv32 and riscv64
        sysbench: Enable only on architectures supporting LuaJIT
        packagegroup-meta-oe: Ensure sysbench is included in limited arches
        hwloc: Update to 2.9.0
        fluentbit: Link with libatomic on ppc32

  Lei Maohui (1):
        polkit: Fix multilib builds

  Leon Anavi (9):
        python3-watchdog: Upgrade 2.2.0 -> 2.2.1
        python3-zeroconf: Upgrade 0.39.4 -> 0.47.1
        python3-croniter: Upgrade 1.3.7 -> 1.3.8
        python3-coverage: Upgrade 7.0.1 -> 7.0.3
        python3-prompt-toolkit: Upgrade 3.0.31 -> 3.0.36
        python3-simplejson: Upgrade 3.18.0 -> 3.18.1
        python3-termcolor: Upgrade 2.1.1 -> 2.2.0
        python3-cantools: Upgrade 37.2.0 -> 38.0.0
        python3-marshmallow: Upgrade 3.18.0 -> 3.19.0

  Livin Sunny (1):
        libwebsockets: add ipv6 in PACKAGECONFIG

  Markus Volk (88):
        blueman: add RDEPEND on python3-fcntl
        hwdata: add patch to use sysroot prefix for pkgdatadir
        pipewire: upgrade 0.3.59 -> 0.3.60
        spirv-cross: upgrade; fix build
        blueman: upgrade 2.34 -> 2.35
        pipewire: upgrade 0.3.60 -> 0.3.61
        iwd: upgrade 1.30 -> 2.0
        libgdata: use gcr3
        libgweather: update 4.0.0 -> 4.2.0
        gnome-online-accounts: use gcr3
        geary: build with gcr3
        gnome-keyring: use gcr3
        evolution-data-server: update 3.44.2 -> 3.46.1
        gnome-settings-daemon: update 42.1 -> 43.0
        libnma: update 1.8.38 -> 1.10.4
        geocode-glib: build with libsoup-3.0
        gjs: update 1.72.2 -> 1.75.1
        gnome-shell: update 42.0 -> 43.1
        mutter: update 42.0 -> 43.1
        polkit: add recipe for v122
        mozjs: update 98 -> 102
        appstream-glib: update 0.7.18 -> 0.8.2
        gthumb: build with libsoup-3
        amtk: update 5.3.1 -> 5.6.1
        gedit: update 42.2 -> 43.2
        evolution-data-server: remove libgdata dependency
        tepl: update 6.0.0 -> 6.2.0
        perfetto: pass TUNE_CCARGS to use machine tune
        gnome-photos: update dependencies
        thunar-archive-plugin: update 0.4.0 -> 0.5.0
        libadwaita: remove deprecated sassc-native dependency
        gnome-shell: remove deprecated sassc-native dependency
        spice-gtk: add missing license information
        pipewire: update 0.3.61 -> 0.3.62
        gdm: update 42.0 -> 43.0
        gnome-session: update 42.0 -> 43-0
        geoclue: update to latest commit to allow to build with libsoup-3.0
        gvfs: fix polkit homedir
        editorconfig: add recipe
        tracker: update 3.4.1 -> 3.4.2
        gvfs: fix dependencies
        gnome-calculator: update 42.2 -> 43.0.1
        tracker-miners: update 3.4.1 -> 3.4.2
        gnome-photos: add missing runtime dependency on tracker-miners
        gtksourceview5: update 5.4.2 -> 5.6.1
        remmina: build with libsoup-3.0
        ostree: replace libsoup-2.4 by curl
        gnome-text-editor: update 42.2 -> 43.1
        gtk4: remove recipe
        libxmlb: allow to build native
        pipewire: update 0.3.62 -> 0.3.63
        gnome-shell-extensions: update SRC_URI and remove sassc-native dep
        grilo: update 0.3.14 -> 0.3.15
        libstemmer: move recipe to meta-oe
        xdg-desktop-portal: add recipe
        bubblewrap: import recipe from meta-security
        gnome-software: add recipe
        basu: import recipe from meta-wayland
        xdg-desktop-portal-wlr: add recipe
        appstream: add recipe
        flatpak: add recipe
        flatpak-xdg-utils: add recipe
        flatpak: add runtime dependency on flatpak-xdg-utils
        wireplumber: update 0.4.12 -> 0.4.13
        wireplumber: build with dbus support by default
        xdg-desktop-portal-gnome: add recipe
        libcloudproviders: add recipe
        evince: update 42.3 -> 43.1
        libportal: build libportal-gtk4 and vala support
        nautilus: update 42.2 -> 43.1
        gnome-desktop: update 42.0 -> 43
        file-roller: update 3.42.0 -> 43.0
        wireplumber: dont start systemd system service by default
        gnome-bluetooth: update 42.4 -> 42.5
        gnome-flashback: update 3.44.0 -> 3.46.0
        libwnck3: update 40.1 -> 43.0
        gnome-panel: update 3.44.0 -> 3.47.1
        gnome-terminal: update 3.42.2 -> 3.46.7
        dconf-editor: update 3.38.3 -> 43.0
        gnome-shell: add missing RDEPENDS
        gnome-control-center: update 42.0 -> 43.2
        gnome-shell: add runtime dependency on adwaita-icon-theme
        xdg-desktop-portal-gtk: add recipe
        thunar: add tumbler to RRECOMMENDS
        gnome:terminal add missing inherit meson
        gnome-disk-utility: update 42.0 -> 43.0
        eog: add recipe
        libdecor: import recipe

  Martin Jansa (3):
        nss: fix SRC_URI
        geoclue: fix polkit files only with modem-gps PACKAGECONFIG
        layer.conf: update LAYERSERIES_COMPAT for mickledore

  Mathieu Dubois-Briand (2):
        nss: Add missing CVE product
        nss: Whitelist CVEs related to libnssdbm

  Matthias Klein (1):
        paho-mqtt-c: upgrade 1.3.11 -> 1.3.12

  Max Krummenacher (1):
        opencv: follow changed name license_flags_accepted

  Mingli Yu (25):
        gnome-calculator: add opengl to REQUIRED_DISTRO_FEATURES
        waylandpp: add opengl to REQUIRED_DISTRO_FEATURES
        libnma: add opengl to REQUIRED_DISTRO_FEATURES
        network-manager-applet: add opengl to REQUIRED_DISTRO_FEATURES
        gssdp: check opengl is enabled or not
        gtksourceview5: add opengl to REQUIRED_DISTRO_FEATURES
        gnome-font-viewer: add opengl to REQUIRED_DISTRO_FEATURES
        libxfce4ui: check opengl DISTRO_FEATURES
        gnome-desktop: add opengl to REQUIRED_DISTRO_FEATURES
        ibus: add opengl related check
        nautilus: add opengl to REQUIRED_DISTRO_FEATURES
        gnome-bluetooth: add opengl to REQUIRED_DISTRO_FEATURES
        evince: add opengl to REQUIRED_DISTRO_FEATURES
        gnome-calendar: add opengl to REQUIRED_DISTRO_FEATURES
        xf86-video-amdgpu: add opengl to REQUIRED_DISTRO_FEATURES
        spice-gtk: add opengl to REQUIRED_DISTRO_FEATURES
        grail: add opengl to REQUIRED_DISTRO_FEATURES
        frame: add opengl to REQUIRED_DISTRO_FEATURES
        geis: add opengl to REQUIRED_DISTRO_FEATURES
        evolution-data-server: add opengl to REQUIRED_DISTRO_FEATURES
        libgweather4: add opengl to REQUIRED_DISTRO_FEATURES
        geary: add opengl to REQUIRED_DISTRO_FEATURES
        file-roller: add opengl to REQUIRED_DISTRO_FEATURES
        gnome-photos: add opengl to REQUIRED_DISTRO_FEATURES
        xdg-desktop-portal-wlr: add opengl to REQUIRED_DISTRO_FEATURES

  Naveen Saini (3):
        opencl-headers: add native and nativesdk
        tcsh: add native nativesdk BBCLASSEXTEND
        tbb: upgrade 2021.5.0 -> 2021.7.0

  Omkar Patil (1):
        ntfs-3g-ntfsprogs: Upgrade 2022.5.17 to 2022.10.3

  Ovidiu Panait (1):
        multipath-tools: upgrade 0.8.4 -> 0.9.3

  Peter Bergin (1):
        sysbench: Upgrade 0.4.12 -> 1.0.20

  Peter Kjellerstedt (4):
        chrony: Make it possible to enable editline support again
        chrony: Remove the libcap and nss PACKAGECONFIGs
        Revert "lldpd: Use github release assets for SRC_URI"
        lldpd: Correct the checksum for the tar ball to match 1.0.16

  Preeti Sachan (1):
        fluidsynth: update SRC_URI to remove non-existing 2.2.x branch

  Roger Knecht (1):
        python3-rapidjson: add recipe

  Sakib Sajal (1):
        minio: fix license information

  Samuli Piippo (1):
        protobuf: stage protoc binary to sysroot

  Tim Orling (4):
        libio-pty-perl: upgrade 1.16 -> 1.17; enable ptest
        libmozilla-ca-perl: add recipe for 20221114
        libio-socket-ssl-perl: upgrade 2.075 -> 2.076
        libtest-warnings-perl: move to oe-core

  Tomasz Żyjewski (2):
        python3-binwalk: add recipe for version 2.3.3
        python3-uefi-firmware: add recipe for version 1.9

  Wang Mingyu (190):
        byacc: upgrade 20220128 -> 20221106
        libforms: upgrade 1.2.4 -> 1.2.5pre1
        libnftnl: upgrade 1.2.3 -> 1.2.4
        mpich: upgrade 4.0.2 -> 4.0.3
        python3-u-msgpack-python: upgrade 2.7.1 -> 2.7.2
        python3-aiosignal: upgrade 1.2.0 -> 1.3.1
        python3-eth-hash: upgrade 0.5.0 -> 0.5.1
        python3-frozenlist: upgrade 1.3.1 -> 1.3.3
        python3-google-auth: upgrade 2.14.0 -> 2.14.1
        python3-greenlet: upgrade 2.0.0 -> 2.0.1
        python3-imageio: upgrade 2.22.3 -> 2.22.4
        python3-pycocotools: upgrade 2.0.5 -> 2.0.6
        babl: upgrade 0.1.96 -> 0.1.98
        ctags: upgrade 5.9.20221106.0 -> 5.9.20221113.0
        gegl: upgrade 0.4.38 -> 0.4.40
        freerdp: upgrade 2.8.1 -> 2.9.0
        glibmm-2.68: upgrade 2.72.1 -> 2.74.0
        googlebenchmark: upgrade 1.7.0 -> 1.7.1
        gnome-backgrounds: upgrade 42.0 -> 43
        nano: upgrade 6.4 -> 7.0
        networkmanager-openvpn: upgrade 1.10.0 -> 1.10.2
        python3-django: upgrade 4.1 -> 4.1.3
        python3-flask-migrate: upgrade 3.1.0 -> 4.0.0
        python3-eth-utils: upgrade 2.0.0 -> 2.1.0
        python3-eventlet: upgrade 0.33.1 -> 0.33.2
        python3-googleapis-common-protos: upgrade 1.56.4 -> 1.57.0
        python3-google-api-python-client: upgrade 2.65.0 -> 2.66.0
        python3-pymongo: upgrade 4.3.2 -> 4.3.3
        lldpd: upgrade 1.0.15 -> 1.0.16
        audit: upgrade 3.0.8 -> 3.0.9
        ccid: upgrade 1.5.0 -> 1.5.1
        colord: upgrade 1.4.5 -> 1.4.6
        ctags: upgrade 5.9.20221113.0 -> 5.9.20221120.0
        flatbuffers: upgrade 22.10.26 -> 22.11.23
        libglvnd: upgrade 1.5.0 -> 1.6.0
        gensio: upgrade 2.5.2 -> 2.6.1
        mg: upgrade 20220614 -> 20221112
        nbdkit: upgrade 1.33.2 -> 1.33.3
        xfstests: upgrade 2022.10.30 -> 2022.11.06
        pcsc-lite: upgrade 1.9.8 -> 1.9.9
        python3-matplotlib-inline: upgrade 0.1.2 -> 0.1.6
        python3-astroid: upgrade 2.12.12 -> 2.12.13
        python3-asyncinotify: upgrade 2.0.5 -> 2.0.8
        python3-charset-normalizer: upgrade 3.0.0 -> 3.0.1
        python3-dateparser: upgrade 1.1.0 -> 1.1.4
        python3-can: upgrade 4.0.0 -> 4.1.0
        python3-flask-socketio: upgrade 5.3.1 -> 5.3.2
        python3-ipython: upgrade 8.2.0 -> 8.6.0
        python3-langtable: upgrade 0.0.60 -> 0.0.61
        python3-jedi: upgrade 0.18.1 -> 0.18.2
        python3-grpcio-tools: upgrade 1.50.0 -> 1.51.0
        python3-grpcio: upgrade 1.50.0 -> 1.51.0
        python3-networkx: upgrade 2.8.7 -> 2.8.8
        python3-pyatspi: upgrade 2.38.2 -> 2.46.0
        python3-pandas: upgrade 1.5.1 -> 1.5.2
        python3-pybind11-json: upgrade 0.2.11 -> 0.2.13
        python3-pychromecast: upgrade 12.1.4 -> 13.0.1
        python3-pycodestyle: upgrade 2.9.1 -> 2.10.0
        xterm: upgrade 373 -> 377
        smarty: upgrade 4.2.1 -> 4.3.0
        spdlog: upgrade 1.10.0 -> 1.11.0
        python3-pyperf: upgrade 2.4.1 -> 2.5.0
        python3-pyflakes: upgrade 2.5.0 -> 3.0.1
        python3-pymisp: upgrade 2.4.157 -> 2.4.165.1
        capnproto: upgrade 0.10.2 -> 0.10.3
        libass: upgrade 0.16.0 -> 0.17.0
        ctags: upgrade 5.9.20221120.0 -> 5.9.20221127.0
        libio-socket-ssl-perl: upgrade 2.076 -> 2.077
        python3-grpcio-tools: upgrade 1.51.0 -> 1.51.1
        python3-asyncinotify: upgrade 2.0.8 -> 3.0.1
        python3-grpcio: upgrade 1.51.0 -> 1.51.1
        opensc: upgrade 0.22.0 -> 0.23.0
        python3-ipython: upgrade 8.6.0 -> 8.7.0
        ply: upgrade 2.2.0 -> 2.3.0
        python3-apt: upgrade 2.3.0 -> 2.5.0
        poppler: upgrade 22.11.0 -> 22.12.0
        python3-asttokens: upgrade 2.1.0 -> 2.2.0
        python3-cbor2: upgrade 5.4.3 -> 5.4.5
        python3-geomet: upgrade 0.3.0 -> 1.0.0
        python3-google-api-core: upgrade 2.10.2 -> 2.11.0
        python3-google-api-python-client: upgrade 2.66.0 -> 2.68.0
        python3-path: upgrade 16.5.0 -> 16.6.0
        python3-google-auth: upgrade 2.14.1 -> 2.15.0
        zabbix: upgrade 6.2.4 -> 6.2.5
        xmlsec1: upgrade 1.2.36 -> 1.2.37
        smcroute: upgrade 2.5.5 -> 2.5.6
        python3-protobuf: upgrade 4.21.9 -> 4.21.10
        python3-traitlets: upgrade 5.5.0 -> 5.6.0
        python3-twine: upgrade 4.0.1 -> 4.0.2
        python3-web3: upgrade 5.31.1 -> 5.31.2
        python3-ujson: upgrade 5.5.0 -> 5.6.0
        ctags: upgrade 5.9.20221127.0 -> 5.9.20221204.0
        dnsmasq: upgrade 2.87 -> 2.88
        flatbuffers: upgrade 22.11.23 -> 22.12.06
        nbdkit: upgrade 1.33.3 -> 1.33.4
        hwdata: upgrade 0.364 -> 0.365
        evolution-data-server: update 3.46.1 -> 3.46.2
        xfstests: upgrade 2022.11.06 -> 2022.11.27
        python3-protobuf: upgrade 4.21.10 -> 4.21.11
        python3-traitlets: upgrade 5.6.0 -> 5.7.0
        python3-redis: upgrade 4.3.5 -> 4.4.0
        python3-web3: upgrade 5.31.2 -> 5.31.3
        python3-asttokens: upgrade 2.2.0 -> 2.2.1
        python3-cbor2: upgrade 5.4.5 -> 5.4.6
        python3-google-api-python-client: upgrade 2.68.0 -> 2.69.0
        python3-gmpy2: upgrade 2.1.2 -> 2.1.3
        python3-multidict: upgrade 6.0.2 -> 6.0.3
        python3-watchdog: upgrade 2.1.9 -> 2.2.0
        python3-pychromecast: upgrade 13.0.1 -> 13.0.2
        python3-pymisp: upgrade 2.4.165.1 -> 2.4.166
        python3-pytest-xdist: upgrade 3.0.2 -> 3.1.0
        python3-yarl: upgrade 1.8.1 -> 1.8.2
        zabbix: upgrade 6.2.5 -> 6.2.6
        python3-yamlloader: upgrade 1.1.0 -> 1.2.2
        tio: upgrade 2.3 -> 2.4
        ctags: upgrade 5.9.20221204.0 -> 6.0.20221218.0
        dash: upgrade 0.5.11.5 -> 0.5.12
        nanopb: upgrade 0.4.6.4 -> 0.4.7
        libio-socket-ssl-perl: upgrade 2.077 -> 2.078
        libfile-slurper-perl: upgrade 0.013 -> 0.014
        protobuf: upgrade 3.21.10 -> 3.21.12
        python3-alembic: upgrade 1.8.1 -> 1.9.0
        nano: upgrade 7.0 -> 7.1
        python3-gmpy2: upgrade 2.1.3 -> 2.1.5
        python3-eth-account: upgrade 0.7.0 -> 0.8.0
        python3-google-api-python-client: upgrade 2.69.0 -> 2.70.0
        python3-protobuf: upgrade 4.21.11 -> 4.21.12
        python3-pycares: upgrade 4.2.2 -> 4.3.0
        python3-pycurl: upgrade 7.45.1 -> 7.45.2
        python3-pychromecast: upgrade 13.0.2 -> 13.0.4
        python3-pyproj: upgrade 3.4.0 -> 3.4.1
        python3-pydicti: upgrade 1.1.6 -> 1.2.0
        python3-sentry-sdk: upgrade 1.11.1 -> 1.12.0
        python3-traitlets: upgrade 5.7.0 -> 5.7.1
        tio: upgrade 2.4 -> 2.5
        python3-sqlalchemy: upgrade 1.4.44 -> 1.4.45
        xfsdump: upgrade 3.1.11 -> 3.1.12
        python3-isort: upgrade 5.10.1 -> 5.11.3
        xfstests: upgrade 2022.11.27 -> 2022.12.11
        ctags: upgrade 6.0.20221218.0 -> 6.0.20221225.0
        gst-editing-services: upgrade 1.20.4 -> 1.20.5
        logcheck: upgrade 1.3.24 -> 1.4.0
        memtester: upgrade 4.5.1 -> 4.6.0
        libmime-types-perl: upgrade 2.22 -> 2.23
        metacity: upgrade 3.46.0 -> 3.46.1
        python3-alembic: upgrade 1.9.0 -> 1.9.1
        xfstests: upgrade 2022.12.11 -> 2022.12.18
        python3-cytoolz: upgrade 0.12.0 -> 0.12.1
        python3-asgiref: upgrade 3.5.2 -> 3.6.0
        python3-autobahn: upgrade 22.7.1 -> 22.12.1
        python3-coverage: upgrade 6.5.0 -> 7.0.1
        python3-bitarray: upgrade 2.6.0 -> 2.6.1
        python3-imageio: upgrade 2.22.4 -> 2.23.0
        python3-isort: upgrade 5.11.3 -> 5.11.4
        python3-multidict: upgrade 6.0.3 -> 6.0.4
        python3-traitlets: upgrade 5.7.1 -> 5.8.0
        python3-pymisp: upgrade 2.4.166 -> 2.4.167
        python3-sentry-sdk: upgrade 1.12.0 -> 1.12.1
        python3-supervisor: upgrade 4.2.4 -> 4.2.5
        wolfssl: upgrade 5.5.3 -> 5.5.4
        remmina: upgrade 1.4.28 -> 1.4.29
        ser2net: upgrade 4.3.10 -> 4.3.11
        tesseract: upgrade 5.2.0 -> 5.3.0
        network-manager-applet: upgrade 1.26.0 -> 1.30.0
        byacc: upgrade 20221106 -> 20221229
        ctags: upgrade 6.0.20221225.0 -> 6.0.20230101.0
        flashrom: upgrade 1.2 -> 1.2.1
        fontforge: upgrade 20220308 -> 20230101
        hunspell: upgrade 1.7.1 -> 1.7.2
        libmime-types-perl: upgrade 2.23 -> 2.24
        libnet-dns-perl: upgrade 1.35 -> 1.36
        tepl: upgrade 6.2.0 -> 6.4.0
        tcpdump: upgrade 4.99.1 -> 4.99.2
        traceroute: upgrade 2.1.0 -> 2.1.1
        openwsman: upgrade 2.7.1 -> 2.7.2
        pcsc-tools: upgrade 1.6.0 -> 1.6.1
        poppler: upgrade 22.12.0 -> 23.01.0
        rsnapshot: upgrade 1.4.4 -> 1.4.5
        tree: upgrade 2.0.4 -> 2.1.0
        python3-bidict: upgrade 0.22.0 -> 0.22.1
        python3-bitarray: upgrade 2.6.1 -> 2.6.2
        python3-dateparser: upgrade 1.1.4 -> 1.1.5
        python3-lz4: upgrade 4.0.2 -> 4.3.2
        python3-mock: upgrade 4.0.3 -> 5.0.0
        python3-pillow: upgrade 9.3.0 -> 9.4.0
        python3-pydantic: upgrade 1.10.2 -> 1.10.4
        python3-pyephem: upgrade 4.1.3 -> 4.1.4
        python3-xlsxwriter: upgrade 3.0.3 -> 3.0.5
        python3-xxhash: upgrade 3.1.0 -> 3.2.0
        dnf-plugins/rpm.py: Fix grammar when RPM_PREFER_ELF_ARCH doesn't exit.

  Xiangyu Chen (1):
        lldpd: add ptest for lldpd package

  Yi Zhao (13):
        libpwquality: set correct pam plugin directory
        ostree: add runtime dependency bubblewrap for PACKAGECONFIG[selinux]
        ostree: fix selinux policy rebuild error on first deployment
        frr: upgrade 8.3.1 -> 8.4.1
        open-vm-tools: upgrade 12.1.0 -> 12.1.5
        libtdb: upgrade 1.4.3 -> 1.4.7
        libldb: upgrade 2.3.4 -> 2.6.1
        libtalloc: upgrade 2.3.3 -> 2.3.4
        libtevent: upgrade 0.10.2 -> 0.13.0
        samba upgrade 4.14.14 -> 4.17.4
        krb5: upgrade 1.17.2 -> 1.20.1
        grubby: update to latest git rev
        grubby: drop version 8.40

  Zheng Qiu (1):
        python3-inotify: add ptest

  persianpros (1):
        samba: Remove samba related PYTHONHASHSEED patches and use export function

  zhengrq.fnst@fujitsu.com (15):
        python3-pymodbus: upgrade 3.0.0 -> 3.0.2
        python3-pywbemtools: upgrade 1.0.1 -> 1.1.0
        python3-stevedore: upgrade 4.1.0 -> 4.1.1
        ser2net: upgrade 4.3.9 -> 4.3.10
        yelp-tools: upgrade 42.0 -> 42.1
        python3-python-vlc: upgrade 3.0.16120 -> 3.0.18121
        python3-sqlalchemy: upgrade 1.4.43 -> 1.4.44
        python3-zopeinterface: upgrade 5.5.1 -> 5.5.2
        python3-simplejson: upgrade 3.17.6 -> 3.18.0
        python3-pywbemtools: upgrade 1.0.1 -> 1.1.1
        python3-redis: upgrade 4.3.4 -> 4.3.5
        python3-texttable: upgrade 1.6.4 -> 1.6.7
        python3-sentry-sdk: upgrade 1.9.10 -> 1.11.1
        python3-twitter: upgrade 4.10.1 -> 4.12.1
        python3-termcolor: upgrade 2.1.0 -> 2.1.1

meta-security: 2aa48e6f4e..f991b20f56:
  Alex Kiernan (1):
        bubblewrap: Update 0.6.2 -> 0.7.0

  Armin Kuster (2):
        python3-privacyidea: update to 2.7.4
        chipsec: update to 1.9.1

  Michael Haener (1):
        tpm2-tools: update to 5.3

meta-arm: d5f132b199..5c42f084f7:
  Adam Johnston (1):
        arm/trusted-services: Fix 'no such file' when building libts

  Adrian Herrera (2):
        atp: decouple m5readfile from m5ops
        atp: move m5readfile to meta-gem5

  Adrián Herrera Arcila (5):
        atp: fix failing test_readme
        gem5: support for EXTRAS
        atp: separate recipe for gem5 models
        atp: fix machine overrides in recipes
        ci: add meta-atp to check-layers

  David Bagonyi (1):
        meta-arm-toolchain: Drop calls to datastore finalize

  Diego Sueiro (2):
        arm/classes: Introduce apply_local_src_patches bbclass
        arm/trusted-firmware-m: Fix local source patches application

  Emekcan (1):
        arm/fvp: Upgrade Corstone1000 FVP

  Emekcan Aras (6):
        arm-bsp/documentation: corstone1000: update the user guide
        arm/optee: Move optee-3.18 patches
        arm/optee: support optee 3.19
        arm-bsp/optee-os: Adds 3.19 bbappend
        arm-bsp/optee-os: N1SDP support for optee-os 3.19
        arm/qemuarm-secureboot: pin optee-os version

  Jon Mason (5):
        arm-bsp/trusted-services: rename bbappends with git version
        arm/trusted-services: limit the ts compatible machines
        arm-bsp/trusted-services: add n1sdp support
        arm/trusted-firmware-m: update to 1.6.1
        CI: define DEFAULT_TAG and CPU_REQUEST

  Khem Raj (1):
        gn: Replace lfs64 functions with original counterparts

  Mohamed Omar Asaker (5):
        arm-bsp/trusted-services: corstone1000: Use the stateless platform service calls
        arm-bsp/trusted-firmware-m: Bump TFM to v1.7
        arm-bsp/trusted-firmware-m: corstone1000: TFM 1.7
        arm-bsp/musca_b1: Edit the platform name
        arm-bsp/trusted-firmware-m: Remove TF-M 1.6 recipe

  Peter Hoyes (3):
        arm/fvp: Backport shlex.join from Python 3.8
        arm/fvpboot: Disable timing annotation by default
        arm/classes: Ensure patch files are sorted in apply_local_src_patches

  Robbie Cao (1):
        arm/fvp-base-r-aem: upgrade to version 11.20.15

  Ross Burton (17):
        CI: revert a meta-clang change which breaks pixman (thus, xserver)
        CI: add variables needed for k8s runners
        CI: add tags to all jobs
        CI: no need to install telnet
        CI: fix builds with clang
        CI: use the .setup fragment in machine-coverage
        arm/fvp-base-a-aem: upgrade to 11.20.15
        arm-bsp/edk2-firmware: allow clang builds on juno
        ci/get-binary-toolchains: rewrite, slightly
        arm-bsp/documentation: update fvp-base documentation to use runfvp
        CI: use qemuarm64 for pending-updates report job
        meta-atp: remove
        meta-gem5: remove
        arm/fvp-envelope: name the FVP tarballs for checksums
        arm/fvp-envelope: update HOMEPAGE
        arm/fvp-base-a-aem: add support for aarch64 binaries
        CI: don't pin fvp-base jobs to x86-64

poky: 44bb88cc86..0ce159991d:
  Alejandro Hernandez Samaniego (6):
        baremetal-image: Avoid overriding qemu variables from IMAGE_CLASSES
        rust: Enable building rust from stable, beta and nightly channels
        rust: Enable baremetal targets
        baremetal-helloworld: Enable x86 and x86-64 ports
        baremetal-helloworld: Move from skeleton to recipes-extended matching what rust-hello-world is doing
        oe-selftest: Add baremetal toolchain test

  Alex Kiernan (20):
        rust: Install target.json for target rustc
        rust: update 1.65.0 -> 1.66.0
        oeqa/runtime/rust: Add basic compile/run test
        libstd-rs: Merge .inc into .bb
        libstd-rs: Move source directory to library/test
        rust-llvm: Merge .inc into .bb
        rust-llvm: Update LLVM_VERSION to match embedded version
        packagegroup-rust-sdk-target: Add Rust SDK target packagegroup
        packagegroup-core-sdk: Add SDK toolchain language selection support
        rust: Merge .inc into .bb
        rust: Move musl-x86 fix for `__stack_chk_fail_local` to rust-source
        cargo: Merge .inc into .bb
        cargo: Extend DEBUG_PREFIX_MAP to cover vendor
        cargo: Include crossbeam-utils patch
        cargo: Drop exclude from world
        packagegroup-rust-sdk-target: Add cargo
        oeqa/runtime/rust: Add cargo test
        classes: image: Set empty weak default IMAGE_LINGUAS
        default-distrovars: Include "c" in IMAGE_LINGUAS for glibc
        rust: Merge all rustc-source patches into rust-source.inc

  Alex Stewart (2):
        lsof: add update-alternatives logic
        opkg: upgrade to version 0.6.1

  Alexander Kanavin (155):
        elfutils: update 0.187 -> 0.188
        rsync: update 3.2.5 -> 3.2.7
        swig: update 4.0.2 -> 4.1.0
        tcl: update 8.6.11 -> 8.6.12
        quota: update 4.06 -> 4.09
        shadow: update 4.12.3 -> 4.13
        texinfo: update 6.8 -> 7.0
        libhandy: update 1.6.3 -> 1.8.0
        xf86-input-mouse: update 1.9.3 -> 1.9.4
        flac: update 1.4.0 -> 1.4.2
        icu: update 71.1 -> 72-1
        libgpg-error: update 1.45 -> 1.46
        popt: update 1.18 -> 1.19
        vte: update 0.68.0 -> 0.70.1
        webkitgtk: update 2.36.7 -> 2.38.2
        man-db: update 2.10.2 -> 2.11.1
        gawk: update 5.1.1 -> 5.2.1
        unfs: update 0.9.22 -> 0.10.0
        qemu-helper: depend on unfs3 and pseudo directly
        runqemu: do not hardcode the ip address of the nfs server when using tap
        selftest/runqemu: reenable the nfs rootfs test
        glibc-tests: correctly pull in the actual tests when installing -ptest package
        python3: fix tests on x86 (32 bit)
        ptest-packagelists.inc: do not run valgrind ptests on 32 bit x86
        python3: use the standard shell version of python3-config
        python3targetconfig.bbclass: use PYTHONPATH to point to the target config
        bitbake: fetch2/wget.py: correctly match versioned directories
        devtool/upgrade: correctly handle recipes where S is a subdir of upstream tree
        python3-numpy: fix upstream version check
        python3-poetry-core: update 1.3.2 -> 1.4.0
        tcl: update 8.6.12 -> 8.6.13
        libnewt: update 0.52.21 -> 0.52.23
        libxdmcp: update 1.1.3 -> 1.1.4
        libxpm: update 3.5.13 -> 3.5.14
        libxrandr: update 1.5.2 -> 1.5.3
        bluez: update 5.65 -> 5.66
        libxcrypt: update PV to match SRCREV
        python3-dbusmock: update 0.28.4 -> 0.28.6
        ruby: merge .inc into .bb
        ruby: update 3.1.2 -> 3.1.3
        ghostscript: update 9.56.1 -> 10.0.0
        tzdata: update 2022d -> 2022g
        systemtap: upgrade 4.7 -> 4.8
        gnupg: upgrade 2.3.7 -> 2.3.8
        ptest-packagelists.inc: correctly assign fast and slow tests
        ovmf: update edk2-stable202208 -> edk2-stable202211
        llvm: update 15.0.4 -> 15.0.6
        tcmode-default.inc: set LLVMVERSION to a major version wildcard
        cmake: update 3.24.2 -> 3.25.1
        python3-native: further tweak to sysconfig.py to find python includes correctly
        libslirp: add recipe to continue slirp support in qemu
        qemu: update 7.1.0 -> 7.2.0
        systemd: update 251.8 -> 252.4
        dpkg: update 1.21.9 -> 1.21.13
        python3-installer: update 0.5.1 -> 0.6.0
        python3: update 3.11.0 -> 3.11.1
        weston: update 11.0.0 -> 11.0.1
        xhost: update 1.0.8 -> 1.0.9
        xinit: update 1.4.1 -> 1.4.2
        xkbcomp: update 1.4.5 -> 1.4.6
        xprop: update 1.2.5 -> 1.2.6
        xset: update 1.2.4 -> 1.2.5
        xvinfo: update 1.1.4 -> 1.1.5
        xf86-video-vesa: update 2.5.0 -> 2.6.0
        libice: update 1.0.10 -> 1.1.1
        libxcomposite: update 0.4.5 -> 0.4.6
        libxdamage: update 1.1.5 -> 1.1.6
        libxres: update 1.2.1 -> 1.2.2
        libxscrnsaver: update 1.2.3 -> 1.2.4
        libxv: update 1.0.11 -> 1.0.12
        jquery: upgrade 3.6.1 -> 3.6.2
        libmodule-build-perl: update 0.4231 -> 0.4232
        python3-chardet: upgrade 5.0.0 -> 5.1.0
        libarchive: upgrade 3.6.1 -> 3.6.2
        stress-ng: upgrade 0.15.00 -> 0.15.01
        vulkan: upgrade 1.3.231.1 -> 1.3.236.0
        Revert "python3-native: further tweak to sysconfig.py to find python includes correctly"
        conf/machine/include: add x86-64-v3 tunes (AVX, AVX2, BMI1, BMI2, F16C, FMA, LZCNT, MOVBE, XSAVE)
        go: update 1.19.3 -> 1.19.4
        vulkan-samples: update to latest revision
        boost-build-native: update 1.80.0 -> 1.81.0
        qemu: disable sporadically failing test-io-channel-command
        devtool: process local files only for the main branch
        libportal: add from meta-openembedded/meta-gnome
        libportal: convert from gtk-doc to gi-docgen
        epiphany: update 42.4 -> 43.0
        qemux86-64: build for x86-64-v3 (2013 Haswell and later) rather than Core 2 from 2006
        valgrind: disable tests that started failing after switching to x86-64-v3 target
        glib-2.0: upgrade 2.74.3 -> 2.74.4
        jquery: upgrade 3.6.2 -> 3.6.3
        nasm: update 2.15.05 -> 2.16.01
        ffmpeg: use nasm patched-in debug-prefix-map option to restore reproducibility
        gtk+3: update 3.24.35 -> 3.24.36
        libva-utils: update 2.16.0 -> 2.17.0
        xcb-util: update 0.4.0 -> 0.4.1
        gnupg: update 2.3.8 -> 2.4.0
        libksba: update 1.6.2 -> 1.6.3
        python3-pycryptodomex: upgrade 3.15.0 -> 3.16.0
        piglit: upgrade to latest revision
        python3-setuptools-scm: upgrade 7.0.5 -> 7.1.0
        python3-attrs: upgrade 22.1.0 -> 22.2.0
        webkitgtk: upgrade 2.38.2 -> 2.38.3
        linux-firmware: upgrade 20221109 -> 20221214
        harfbuzz: upgrade 5.3.1 -> 6.0.0
        python3-pytz: upgrade 2022.6 -> 2022.7
        strace: upgrade 6.0 -> 6.1
        python3-pycryptodome: upgrade 3.15.0 -> 3.16.0
        meson: upgrade 0.64.0 -> 1.0.0
        xwayland: upgrade 22.1.5 -> 22.1.7
        python3-pyrsistent: upgrade 0.19.2 -> 0.19.3
        file: upgrade 5.43 -> 5.44
        python3-subunit: upgrade 1.4.1 -> 1.4.2
        python3-zipp: upgrade 3.10.0 -> 3.11.0
        python3-cryptography: upgrade 38.0.3 -> 38.0.4
        logrotate: upgrade 3.20.1 -> 3.21.0
        python3-importlib-metadata: upgrade 5.0.0 -> 5.2.0
        python3-numpy: upgrade 1.23.4 -> 1.24.1
        xserver-xorg: upgrade 21.1.4 -> 21.1.6
        puzzles: upgrade to latest revision
        vte: upgrade 0.70.1 -> 0.70.2
        libpsl: upgrade 0.21.1 -> 0.21.2
        libtest-fatal-perl: upgrade 0.016 -> 0.017
        python3-urllib3: upgrade 1.26.12 -> 1.26.13
        python3-cryptography-vectors: upgrade 38.0.3 -> 38.0.4
        python3-setuptools: upgrade 65.5.1 -> 65.6.3
        libsdl2: upgrade 2.26.0 -> 2.26.1
        python3-gitdb: upgrade 4.0.9 -> 4.0.10
        diffoscope: upgrade 224 -> 230
        python3-mako: upgrade 1.2.3 -> 1.2.4
        python3-sphinx: upgrade 5.3.0 -> 6.0.0
        libsolv: upgrade 0.7.22 -> 0.7.23
        ruby: upgrade 3.1.3 -> 3.2.0
        python3-lxml: upgrade 4.9.1 -> 4.9.2
        python3-git: upgrade 3.1.29 -> 3.1.30
        curl: upgrade 7.86.0 -> 7.87.0
        kmscube: upgrade to latest revision
        gobject-introspection: upgrade 1.72.0 -> 1.74.0
        python3-dtschema: upgrade 2022.11 -> 2022.12
        bash: upgrade 5.2.9 -> 5.2.15
        kexec-tools: upgrade 2.0.25 -> 2.0.26
        python3-jsonschema: upgrade 4.17.0 -> 4.17.3
        python3-pycairo: upgrade 1.21.0 -> 1.23.0
        nghttp2: upgrade 1.50.0 -> 1.51.0
        python3-certifi: upgrade 2022.9.24 -> 2022.12.7
        python3-hypothesis: upgrade 6.57.1 -> 6.61.0
        libsndfile1: upgrade 1.1.0 -> 1.2.0
        repo: upgrade 2.29.9 -> 2.31
        libpcap: upgrade 1.10.1 -> 1.10.2
        python3-jsonschema: depend on rfc3339-validator in all cases
        python3-strict-rfc3339: remove the recipe
        elfutils: do not error out on deprecated declarations
        gcr3: limit version check to 3.x versions without odd-even rule
        ncurses: restore version check as it's now again working due to release of 6.4
        tiff: update 4.4.0 -> 4.5.0
        qemu: fix recent reproducibility issues

  Alexey Smirnov (1):
        classes: make TOOLCHAIN more permissive for kernel

  Anton Antonov (1):
        rust: Do not use default compiler flags defined in CC crate

  Antonin Godard (2):
        busybox: always start do_compile with orig config files
        busybox: rm temporary files if do_compile was interrupted

  Atanas Bunchev (1):
        qemu.rst: slirp port forwarding details

  Bruce Ashfield (30):
        linux-yocto-dev: bump to v6.0+
        linux-yocto/5.19: update to v5.19.16
        linux-yocto/5.15: update to v5.15.74
        linux-yocto/5.19: update to v5.19.17
        linux-yocto/5.15: update to v5.15.76
        linux-yocto/5.19: cfg: intel and vesa updates
        kern-tools: integrate ZFS speedup patch
        linux-yocto-dev: bump to v6.1
        kernel-devsrc: fix for v6.1+
        lttng-modules: fix build for v6.1+
        linux-yocto/5.19: security.cfg: remove configs which have been dropped
        linux-yocto/5.15: update to v5.15.78
        linux-yocto/5.19: fix CONFIG_CRYPTO_CCM mismatch warnings
        linux-yocto/5.15: fix CONFIG_CRYPTO_CCM mismatch warnings
        linux-yocto/5.19: fix elfutils run-backtrace-native-core ptest failure
        linux-libc-headers: add 6.x fetch location
        linux-libc-headers: bump to 6.1
        linux-yocto/5.19: fix perf build with clang
        linux-yocto/5.15: ltp and squashfs fixes
        linux-yocto: introduce v6.1 reference kernel recipes
        linux-yocto/5.15: fix perf build with clang
        linux-yocto/5.15: libbpf: Fix build warning on ref_ctr_off
        linux-yocto/5.15: update to v5.15.84
        linux-yocto/6.1: update to v6.1.1
        linux-yocto/5.15: powerpc: Fix reschedule bug in KUAP-unlocked user copy
        linux-yocto/5.19: powerpc: Fix reschedule bug in KUAP-unlocked user copy
        linux-yocto/6.1: update to v6.1.3
        linux-yocto/6.1: cfg: remove CONFIG_ARM_CRYPTO
        yocto-bsps/5.15: update to v5.15.78
        linux-yocto/5.15: update to v5.15.80

  Carlos Alberto Lopez Perez (3):
        xwayland: libxshmfence is needed when dri3 is enabled
        recipes: Enable nativesdk for gperf, unifdef, gi-docgen and its dependencies
        mesa-gl: gallium is required when enabling x11

  Changqing Li (2):
        base.bbclass: Fix way to check ccache path
        sqlite3: upgrade 3.40.0 -> 3.40.1

  Charlie Johnston (1):
        opkg: ensure opkg uses private gpg.conf when applying keys.

  Chee Yang Lee (1):
        migration-guides: add release-notes for 4.1.1

  Chen Qi (10):
        kernel.bbclass: make KERNEL_DEBUG_TIMESTAMPS work at rebuild
        resolvconf: make it work
        dhcpcd: fix to work with systemd
        bitbake: command.py: cleanup bb.cache.parse_recipe
        psplash: consider the situation of psplash not exist for systemd
        bc: extend to nativesdk
        rm_work: adjust dependency to make do_rm_work_all depend on do_rm_work
        selftest: allow '-R' and '-r' be used together
        dhcpcd: backport two patches to fix runtime error
        libseccomp: fix typo in DESCRIPTION

  Christian Eggers (1):
        boost: add url lib

  David Bagonyi (1):
        u-boot: Fix u-boot signing when building with multiple u-boot configs

  Dmitry Baryshkov (2):
        linux-firmware: upgrade 20221012 -> 20221109
        linux-firmware: add new fw file to ${PN}-qcom-adreno-a530

  Enguerrand de Ribaucourt (1):
        bitbake-layers: fix a typo

  Enrico Jörns (1):
        sstatesig: emit more helpful error message when not finding sstate manifest

  Enrico Scholz (1):
        sstate: show progress bar again

  Fabre Sébastien (1):
        u-boot: Add /boot in SYSROOT_DIRS

  Frank de Brabander (4):
        bitbake: README: Improve explanation about running the testsuite
        bitbake: bin/utils: Ensure locale en_US.UTF-8 is available on the system
        bitbake: process: log odd unlink events with bitbake.sock
        bitbake: README: add required python version for bitbake

  Harald Seiler (1):
        opkg: Set correct info_dir and status_file in opkg.conf

  Jagadeesh Krishnanjanappa (1):
        qemuboot.bbclass: make sure runqemu boots bundled initramfs kernel image

  Jan Kircher (1):
        toolchain-scripts: compatibility with unbound variable protection

  Javier Tia (1):
        poky.conf: Add Fedora 36 as supported distro

  Joe Slater (2):
        python3: Fix CVE-2022-37460
        libarchive: fix CVE-2022-36227

  Jose Quaresma (2):
        Revert "gstreamer1.0: disable flaky gstbin:test_watch_for_state_change test"
        gstreamer1.0: Fix race conditions in gstbin tests

  Joshua Watt (4):
        qemu-helper-native: Correctly pass program name as argv[0]
        bitbake: cooker: Use event to terminate parser threads
        bitbake: cooker: Start sync thread a little earlier
        bitbake: bitbake: Convert to argparse

  Kai Kang (4):
        xorg-lib-common.inc: set default value of XORG_EXT
        libx11-compose-data: 1.6.8 -> 1.8.3
        libx11: 1.8.1 -> 1.8.3
        libsm: 1.2.3 > 1.2.4

  Kasper Revsbech (1):
        bitbake: fetch2/wget: handle username/password in uri

  Khem Raj (47):
        rsync: Delete pedantic errors re-ordering patch
        pseudo: Disable LFS on 32bit arches
        libxkbcommon: Extend to build native package
        iso-codes: Extend to build native packages
        xkeyboard-config: Extend to build native package
        bluez5: enable position independent executables flag
        rpcsvc-proto: Use autoconf knob to enable largefile support
        gptfdisk: Enable largefile support functions
        libpcre2: Upgrade to 10.42
        erofs-utils: Convert from off64_t to off_t
        pseudo: Remove 64bit time_t flags
        unfs3: Define off64_t in terms of off_t on musl
        acpid: Fix largefile enabled build
        efivar: Replace off64_t with off_t
        ltp: Fix largefile support
        acl: Enable largefile support by default
        libpciaccess: Do not use 64bit functions for largefile support
        mdadm: Use _FILE_OFFSET_BITS to use largefile support
        btrfs-tools: Do not use 64bit functions for largefile support
        e2fsprogs: Do not use 64bit functions for largefile support
        libbsd: Fix build with largefile support
        gpgme: Fix with with largefile support
        virglrenderer: Replace lseek64 with lseek
        nfs-utils: Replace statfs64 with statfs
        alsa-utils: Replace off64_t with off_t
        lttng-tools: Fix build with largefile support
        strace: Add knob to enable largefile support
        numactl: Enable largefile support
        qemu: Fix build with largefile support
        systemd: Fix 252 release build on musl
        rust: Do not use open64 on musl in getrandom crate
        rust,libstd-rs: Fix build with latest musl
        rust-llvm: Fix build on latest musl
        cargo: Do not use open64 on musl anymore
        llvm: Do not use lseek64
        strace: Replace off64_t with off_t in sync_file_range.c test
        vulkan-samples: Do not use LFS64 APIs in spdlog
        pulseaudio: Do not use 64bit time_t flags
        musl: Update to latest on tip of trunk
        rust: Fix build with 64bit time_t
        stress-ng: Do not enforce gold linker
        time64.inc: Add GLIBC_64BIT_TIME_FLAGS on ppc/x86 as well
        time64: Remove leading whitespace from GLIBC_64BIT_TIME_FLAGS
        mpg123: Enable largefile support
        site/powerpc32-linux: Do not cache statvfs64 across glibc and musl
        tiff: Add packageconfig knob for webp
        site/common-musl: Set ac_cv_sys_file_offset_bits default to 64

  Lee Chee Yang (1):
        migration-guides: add release-notes for 4.0.6

  Luca Boccassi (2):
        systemd: refresh patch to remove fuzz introduced by rebase on v252
        systemd: ship pcrphase/measure tools and units in systemd-extra-utils

  Luis (1):
        rm_work.bbclass: use HOSTTOOLS 'rm' binary exclusively

  Marek Vasut (5):
        bitbake: fetch2/git: Prevent git fetcher from fetching gitlab repository metadata
        package_rpm: Fix Linux 6.1.0 perf 1.0 version mistranslation
        systemd: Make importd depend on glib-2.0 again
        bitbake: bitbake-user-manual: Document override :append, :prepend, :remove order
        bitbake: fetch2/git: Clarify the meaning of namespace

  Markus Volk (12):
        ell: upgrade 0.53 -> 0.54
        libsdl2: update 2.24.2 -> 2.26.0
        graphene: import from meta-oe
        gtk4: import recipe from meta-gnome
        gcr: rename gcr -> gcr3
        gcr: add recipe for gcr-4, needed to build with gtk4
        epiphany: use gcr3
        gtk4: add tracker-miners runtime dependency
        python3-dbusmock: allow to build native
        gtk4: update 4.8.2 -> 4.8.3
        gcr3: update 3.40.0 -> 3.41.1
        librsvg: enable vapi build

  Marta Rybczynska (2):
        efibootmgr: update compilation with musl
        cve-update-db-native: avoid incomplete updates

  Martin Jansa (4):
        libxml2: upgrade test data from 20080827 to 20130923
        nativesdk-rpm: export RPM_ETCCONFIGDIR and MAGIC in environment like RPM_CONFIGDIR
        nativesdk-rpm: don't create wrappers for WRAPPER_TOOLS
        tune-x86-64-v3.inc: set QEMU_EXTRAOPTIONS like other tune-* files

  Mathieu Dubois-Briand (1):
        dbus: Add missing CVE product name

  Michael Halstead (1):
        uninative: Upgrade to 3.8.1 to include libgcc

  Michael Opdenacker (34):
        manuals: add missing references to classes
        manuals: fix paragraphs with the "inherit" word
        ref-manual/classes.rst: remove reference to sip.bbclass
        manuals: simplify .gitignore files
        manuals: split dev-manual/common-tasks.rst
        dev-manual/sbom.rst: minor corrections
        bitbake: bitbake-user-manual: update references to Yocto Project manual
        bitbake.conf: remove SERIAL_CONSOLE variable
        bitbake: bitbake-user-manual: add reference to bitbake git repository
        ref-manual: add references to variables only documented in the BitBake manual
        manuals: add reference to yocto-docs git repository to page footer
        manuals: add missing references to variables
        manuals: add missing SPDX license header to source files
        manuals: fix double colons
        ref-manual/resources.rst: fix formating
        ref-manual: update references to release notes
        manual: improve documentation about using external toolchains
        ref-manual/images.rst: fix unnumbered list
        manuals: define proper numbered lists
        manuals: final removal of SERIAL_CONSOLE variable
        ref-manual/resources.rst: improve description of mailing lists
        ref-manual/system-requirements.rst: update buildtools instructions
        manuals: create references to buildtools
        documentation/poky.yaml.in: update minimum python version to 3.8
        manuals: prepare 4.2 migration notes
        bitbake: bitbake-user-manual: double colon fix
        bitbake: bitbake-user-manual: remove "OEBasic" signature generator
        migration-guides: fix 4.2 migration note issues
        toaster-manual: fix description of introduction video
        ref-manual/classes.rst: remove .bbclass from section titles
        manuals: simplify references to classes
        migration-1.6.rst: fix redundant reference
        ref-manual/system-requirements.rst: recommend buildtools for not supported distros
        .gitignore: ignore files generated by Toaster

  Mikko Rapeli (5):
        qemurunner.py: support setting slirp host IP address
        runqemu: limit slirp host port forwarding to localhost 127.0.0.1
        qemurunner.py: use IP address from command line
        dev-manual/runtime-testing.rst: fix oeqa runtime test path
        runqemu: add QB_SETUP_CMD and QB_CLEANUP_CMD

  Mingli Yu (8):
        tcl: correct the header location in tcl.pc
        python3: make tkinter available when enabled
        sudo: add selinux and audit PACKAGECONFIG
        iproute2: add selinux PACKAGECONFIG
        util-linux: add selinux PACKAGECONFIG
        cronie: add selinux PACKAGECONFIG
        psmisc: add selinux PACKAGECONFIG
        gcr: add opengl to REQUIRED_DISTRO_FEATURES

  Narpat Mali (2):
        ffmpeg: fix for CVE-2022-3964
        ffmpeg: fix for CVE-2022-3965

  Ola x Nilsson (4):
        kbd: Don't build tests
        glibc: Add ppoll fortify symbol for 64 bit time_t
        insane: Add QA check for 32 bit time and file offset functions
        time64.conf: Include to enable 64 bit time flags

  Ovidiu Panait (1):
        kernel.bbclass: remove empty module directories to prevent QA issues

  Patrick Williams (1):
        kernel-fitimage: reduce dependency to the cpio

  Pavel Zhukov (1):
        oeqa/rpm.py: Increase timeout and add debug output

  Peter Kjellerstedt (1):
        recipes, classes: Avoid adding extra whitespace to PACKAGESPLITFUNCS

  Peter Marko (2):
        externalsrc: fix lookup for .gitmodules
        oeqa/selftest/externalsrc: add test for srctree_hash_files

  Petr Kubizňák (1):
        harfbuzz: remove bindir only if it exists

  Petr Vorel (1):
        iputils: update to 20221126

  Polampalli, Archana (1):
        libpam: fix CVE-2022-28321

  Qiu, Zheng (3):
        valgrind: remove most hidden tests for arm64
        tiff: Security fix for CVE-2022-3970
        vim: upgrade 9.0.0820 -> 9.0.0947

  Quentin Schulz (4):
        cairo: update patch for CVE-2019-6461 with upstream solution
        cairo: fix CVE patches assigned wrong CVE number
        docs: kernel-dev: faq: update tip on how to not include kernel in image
        docs: migration-guides: migration-4.0: specify variable name change for kernel inclusion in image recipe

  Randy MacLeod (1):
        valgrind: skip the boost_thread test on arm

  Ranjitsinh Rathod (1):
        curl: Correct LICENSE from MIT-open-group to curl

  Ravula Adhitya Siddartha (2):
        linux-yocto/5.15: update genericx86* machines to v5.15.78
        linux-yocto/5.19: update genericx86* machines to v5.19.17

  Richard Purdie (97):
        bitbake: cache/cookerdata: Move recipe parsing functions from cache to databuilder
        bitbake: cache: Drop broken/unused code
        bitbake: cache: Drop unused function
        bitbake: server: Ensure cooker profiling works
        bitbake: worker/runqueue: Reduce initial data transfer in workerdata
        bitbake: cache: Drop support for not saving the cache file
        bitbake: runqueue: Add further debug for sstate reuse issues
        bitbake: runqueue: Fix race issues around hash equivalence and sstate reuse
        bitbake: data/siggen: Switch to use frozensets and optimize
        bitbake: data_smart: Add debugging for overrides stability issue
        bitbake: utils: Allow to_boolean to support int values
        base: Drop do_package base definition
        bitbake: data: Drop obsolete pydoc/path code
        bitbake: BBHandler: Remove pointless global variable declarations
        bitbake: runqueue: Improve error message for missing multiconfig
        bitbake: data_smart: Small cache reuse optimization
        bitbake.conf: Simplify CACHE setting
        oeqa/selftest/tinfoil: Add test for separate config_data with recipe_parse_file()
        qemu: Ensure libpng dependency is deterministic
        bitbake: data: Tweak code layout
        bitbake: cache/siggen: Simplify passing basehash data into the cache
        bitbake: siggen/cache: Optionally allow adding siggen hash data to the bitbake cache
        bitbake: parse: Add support for addpylib conf file directive and BB_GLOBAL_PYMODULES
        bitbake: cookerdata: Ensure layers use LAYERSERIES_COMPAT fairly
        base: Switch to use addpylib directive and BB_GLOBAL_PYMODULES
        devtool/friends: Use LAYERSERIES_CORENAMES when generating LAYERSERIES_COMPAT entries
        scripts/checklayer: Update to match bitbake changes
        yocto-check-layer: Allow OE-Core to be tested
        bitbake: main: Add timestamp to server retry messages
        bitbake: main/server: Add lockfile debugging upon server retry
        poky/poky-tiny: Drop largefile mentions
        lib/sstatesig: Drop OEBasic siggen
        bitbake: siggen: Drop non-multiconfig aware siggen support
        bitbake: build/siggen/runqueue: Drop do_setscene references
        bitbake: bitbake: Bump minimum python version requirement to 3.8
        sanity: Update minimum python version to 3.8
        bitbake: main/process: Add extra sockname debugging
        Revert "kernel-fitimage: reduce dependency to the cpio"
        bitbake: siggen: Directly store datacaches reference
        bitbake: bitbake: siggen/runqueue: Switch to using RECIPE_SIGGEN_INFO feature for signature dumping
        bitbake: siggen: Add dummy dataCaches from task context/datastore
        bitbake: build/siggen: Rework stamps functions
        bitbake: siggen: Clarify which fn is meant
        bitbake: ast/data/codeparser: Add dependencies from python module functions
        bitbake: codeparser/data: Add vardepsexclude support to module dependency code
        bitbake.conf: Add module function vardepsexclude entries
        time64: Rename to a .inc file to match the others
        bitbake: command: Add ping command
        bitbake: cache: Allow compression of the data in SiggenRecipeInfo
        bitbake: siggen: Minor code improvement
        bitbake: server/process: Add bitbake.sock race handling
        oeqa/concurrencytest: Add number of failures to summary output
        python3-poetry-core: Fix determinism issue breaking reproducibility
        bitbake: cache/siggen: Fix cache issues with signature handling
        bitbake: event: builtins fix for 'd' deletion
        bitbake: cooker: Ensure cache is cleared for partial resets
        bitbake: tinfoil: Ensure CommandExit is handled
        bitbake: cache: Drop reciever side counting for SiggenRecipeInfo
        bitbake: knotty: Avoid looping with tracebacks
        bitbake: event: Add enable/disable heartbeat code
        bitbake: cooker/cookerdata: Rework the way the datastores are reset
        bitbake: server/process: Improve exception and idle function logging
        bitbake: command: Tweak finishAsyncCommand ordering to avoid races
        bitbake: cooker: Ensure commands clean up any parser processes
        bitbake: server/process: Improve idle loop exit code
        bitbake: event: Always use threadlock
        bitbake: server/process: Add locking around idle functions accesses
        bitbake: server/process: Run idle commands in a separate idle thread
        bitbake: knotty: Ping the server/cooker periodically
        bitbake: cookerdata: Fix cache/reparsing issue
        bitbake: cookerdata: Fix previous commit to use a string, not a generator
        bitbake: command: Ensure that failure cases call finishAsyncComand
        layer.conf: Update to use mickledore as the layer series name
        layer.conf: Mark master as compatible with mickledore
        bitbake: lib/bb: Update thread/process locks to use a timeout
        package: Move fixup_perms function to bb function library
        package: Move get_conffiles/files_from_filevars functions to lib
        package: Move pkgdata handling functions to oe.packagedata
        package: Move emit_pkgdata to packagedata.py
        package: Move package functions to function library
        package: Drop unused function and obsolete comment
        package: Move mapping_rename_hook to packagedata function library
        python3-cython: Use PACKAGESPLITFUNCS instead of PACKAGEBUILDPKGD
        package: Drop support for PACKAGEBUILDPKGD function customisation
        recipes/classes: Drop prepend/append usage with PACKAGESPLITFUNCS
        bitbake: cooker: Rework the parsing results submission
        bitbake: cooker: Clean up inotify idle handler
        uninative-tarball: Add libgcc
        patchelf: Add fix submitted upstream for uninative segfaults
        bitbake: cooker/command: Drop async command handler indirection via cooker
        bitbake: process/cooker/command: Fix currentAsyncCommand locking/races
        uninative: Ensure uninative is enabled in all cases for BuildStarted event
        qemux86-64: Reduce tuning to core2-64
        bitbake: tinfoil: Don't wait for events indefinitely
        bitbake: knotty: Improve shutdown handling
        bitbake: cooker: Fix exit handling issues
        bitbake: server/process: Move heartbeat to idle thread

  Robert Andersson (1):
        go-crosssdk: avoid host contamination by GOCACHE

  Ross Burton (28):
        build-appliance-image: Update to master head revision
        lib/buildstats: fix parsing of trees with reduced_proc_pressure directories
        combo-layer: remove unused import
        combo-layer: dont use bb.utils.rename
        combo-layer: add sync-revs command
        libxml2: upgrade 2.9.14 -> 2.10.3
        libxml2: add more testing
        python3-packaging: upgrade to 22.0
        python3-hatchling: remove python3-tomli DEPENDS
        python3-cryptography: remove python3-tomli RDEPENDS
        meson: drop redundant is_debianlike() patch
        meson: always use meson subcommands
        libepoxy: remove upstreamed patch
        gtk+3: upgrade 3.24.34 -> 3.24.35
        gtk+3: port to Meson
        meson: no need to rebuild on install
        at-spi2-core: clean up x11 enabling
        at-spi2-core: disable API docs if x11 is disabled
        gtk+3: fix reproducible builds
        lsof: upgrade 4.96.4 -> 4.96.5
        pango: upgrade 1.50.11 -> 1.50.12
        python3-hatch-vcs: upgrade 0.2.0 -> 0.3.0
        python3-hatchling: upgrade 1.11.1 -> 1.12.1
        python3-pathspec: upgrade 0.10.1 -> 0.10.3
        rm_work: handle non-existant stamps directory
        oeqa/selftest/debuginfod: improve testcase
        elfutils: disable deprecation errors in all builds, not just native
        curl: don't enable debug builds

  Ryan Eatmon (1):
        go: Update reproducibility patch to fix panic errors

  Sandeep Gundlupet Raju (3):
        libdrm: Remove libdrm-kms package
        kernel-fitimage: Adjust order of dtb/dtbo files
        kernel-fitimage: Allow user to select dtb when multiple dtb exists

  Saul Wold (1):
        at: Change when files are copied

  Sergei Zhmylev (1):
        oeqa/qemurunner: implement vmdk images support

  Tim Orling (7):
        python3-hypothesis: upgrade 6.56.4 -> 6.57.1
        at-spi2-core: upgrade 2.44.1 -> 2.46.0
        mirrors.bbclass: update CPAN_MIRROR
        libtry-tiny-perl: add recipe for 0.31
        libtest-fatal-perl: add recipe for 0.016
        libtest-warnings-perl: move from meta-perl
        liburi-perl: upgrade 5.08 -> 5.17

  Trevor Woerner (1):
        local.conf.sample: update bbclass locations

  Vincent Davis Jr (1):
        mesa: enable glvnd support

  Wang Mingyu (49):
        btrfs-tools: upgrade 6.0 -> 6.0.1
        libpipeline: upgrade 1.5.6 -> 1.5.7
        btrfs-tools: upgrade 6.0.1 -> 6.0.2
        bind: upgrade 9.18.8 -> 9.18.9
        ccache: upgrade 4.7.2 -> 4.7.4
        dropbear: upgrade 2022.82 -> 2022.83
        libinput: upgrade 1.21.0 -> 1.22.0
        libxft: upgrade 2.3.6 -> 2.3.7
        mpfr: upgrade 4.1.0 -> 4.1.1
        glib-2.0: upgrade 2.74.1 -> 2.74.3
        libxcrypt-compat: upgrade 4.4.30 -> 4.4.33
        patchelf: upgrade 0.16.1 -> 0.17.0
        pciutils: upgrade 3.8.0 -> 3.9.0
        shaderc: upgrade 2022.3 -> 2022.4
        sqlite3: upgrade 3.39.4 -> 3.40.0
        stress-ng: upgrade 0.14.06 -> 0.15.00
        swig: upgrade 4.1.0 -> 4.1.1
        texinfo: upgrade 7.0 -> 7.0.1
        usbutils: upgrade 014 -> 015
        xz: upgrade 5.2.7 -> 5.2.9
        wayland-protocols: upgrade 1.28 -> 1.31
        gnu-config: upgrade to latest revision
        libfontenc: upgrade 1.1.6 -> 1.1.7
        libpcre2: upgrade 10.40 -> 10.41
        libpng: upgrade 1.6.38 -> 1.6.39
        libxau: upgrade 1.0.10 -> 1.0.11
        libxkbfile: upgrade 1.1.1 -> 1.1.2
        libxshmfence: upgrade 1.3.1 -> 1.3.2
        xrandr: upgrade 1.5.1 -> 1.5.2
        boost: upgrade 1.80.0 -> 1.81.0
        ell: upgrade 0.54 -> 0.55
        git: upgrade 2.38.1 -> 2.39.0
        help2man: upgrade 1.49.2 -> 1.49.3
        iproute2: upgrade 6.0.0 -> 6.1.0
        libmpc: upgrade 1.2.1 -> 1.3.1
        makedepend: upgrade 1.0.7 -> 1.0.8
        psmisc: upgrade 23.5 -> 23.6
        xz: upgrade 5.2.9 -> 5.4.0
        gstreamer1.0: upgrade 1.20.4 -> 1.20.5
        bind: upgrade 9.18.9 -> 9.18.10
        btrfs-tools: upgrade 6.0.2 -> 6.1
        librepo: upgrade 1.14.5 -> 1.15.1
        libsdl2: upgrade 2.26.1 -> 2.26.2
        libva-utils: upgrade 2.17.0 -> 2.17.1
        libxkbcommon: upgrade 1.4.1 -> 1.5.0
        mpfr: upgrade 4.1.1 -> 4.2.0
        dpkg: upgrade 1.21.13 -> 1.21.17
        rxvt-unicode: upgrade 9.30 -> 9.31
        virglrenderer: upgrade 0.10.3 -> 0.10.4

  Xiangyu Chen (3):
        grub: backport patches to fix CVE-2022-28736
        openssh: remove RRECOMMENDS to rng-tools for sshd package
        grub2: backport patch to fix CVE-2022-2601 CVE-2022-3775

  Yoann Congal (2):
        bitbake: Group and reorder options in bitbake help
        bitbake: main: Move --buildfile help at the end of "Execution" group

  leimaohui (1):
        libpng: Enable NEON for aarch64 to enensure consistency with arm32.

  pgowda (1):
        binutils: Add patch to fix CVE-2022-4285

  张忠山 (1):
        bitbake: data_smart: Use regex consistently for override matching

meta-raspberrypi: 93dadf336c..896566aa92:
  Carlos Alberto Lopez Perez (1):
        weston: disablepackageconfig options that fail to build with userland drivers

  Khem Raj (2):
        lirc: Drop upstreamed patch
        linux-raspberrypi.inc: Weakly assign COMPATIBLE_MACHINE

  Martin Jansa (2):
        bluez5: update patches to apply on 5.66 version
        layer.conf: update LAYERSERIES_COMPAT for mickledore

  Vincent Davis Jr (5):
        rpidistro-vlc,rpidistro-ffmpeg: update COMPATIBLE_HOST regex
        rpidistro-vlc: upgrade 3.0.12 -> 3.0.17
        rpi-default-providers: add libav and libpostproc
        rpidistro-ffmpeg: upgrade 4.3.2 -> 4.3.4
        rpidistro-ffmpeg: remove --enable-v4l2-request flag

Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
Change-Id: Ied8537beedde0f83790e6e3595057db45f408107
diff --git a/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/0001-avcodec-arm-sbcenc-avoid-callee-preserved-vfp-regist.patch b/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/0001-avcodec-arm-sbcenc-avoid-callee-preserved-vfp-regist.patch
index e9c9eb7..d9c07dd 100644
--- a/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/0001-avcodec-arm-sbcenc-avoid-callee-preserved-vfp-regist.patch
+++ b/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/0001-avcodec-arm-sbcenc-avoid-callee-preserved-vfp-regist.patch
@@ -2,6 +2,11 @@
 Date: Sun, 11 Aug 2019 16:50:56 +0100
 Subject: avcodec/arm/sbcenc: avoid callee preserved vfp registers
 
+Upstream-Status: Inappropriate
+
+RPI-Distro repo clones original ffmpeg and applies patches to enable
+raspiberry pi support.
+
 When compiling FFmpeg with GCC-9, some very random segfaults were
 observed in code which had previously called down into the SBC encoder
 NEON assembly routines. This was caused by these functions clobbering
@@ -19,8 +24,6 @@
 code more easy to follow. Since this commit only reallocates
 registers, it should have no performance impact.
 
-Upstream-status: Pending
-
 Signed-off-by: James Cowgill <jcowgill@debian.org>
 ---
  libavcodec/arm/sbcdsp_neon.S | 220 +++++++++++++++++++++----------------------
@@ -38,7 +41,7 @@
 -        vld1.16         {d8, d9}, [r2, :128]!
 +        vld1.16         {d16, d17}, [r0, :64]!
 +        vld1.16         {d20, d21}, [r2, :128]!
-
+ 
 -        vmull.s16       q0, d4, d8
 -        vld1.16         {d6,  d7}, [r0, :64]!
 -        vmull.s16       q1, d5, d9
@@ -47,7 +50,7 @@
 +        vld1.16         {d18, d19}, [r0, :64]!
 +        vmull.s16       q1, d17, d21
 +        vld1.16         {d22, d23}, [r2, :128]!
-
+ 
 -        vmlal.s16       q0, d6, d10
 -        vld1.16         {d4, d5}, [r0, :64]!
 -        vmlal.s16       q1, d7, d11
@@ -56,7 +59,7 @@
 +        vld1.16         {d16, d17}, [r0, :64]!
 +        vmlal.s16       q1, d19, d23
 +        vld1.16         {d20, d21}, [r2, :128]!
-
+ 
 -        vmlal.s16       q0, d4, d8
 -        vld1.16         {d6,  d7}, [r0, :64]!
 -        vmlal.s16       q1, d5, d9
@@ -65,7 +68,7 @@
 +        vld1.16         {d18, d19}, [r0, :64]!
 +        vmlal.s16       q1, d17, d21
 +        vld1.16         {d22, d23}, [r2, :128]!
-
+ 
 -        vmlal.s16       q0, d6, d10
 -        vld1.16         {d4, d5}, [r0, :64]!
 -        vmlal.s16       q1, d7, d11
@@ -74,23 +77,23 @@
 +        vld1.16         {d16, d17}, [r0, :64]!
 +        vmlal.s16       q1, d19, d23
 +        vld1.16         {d20, d21}, [r2, :128]!
-
+ 
 -        vmlal.s16       q0, d4, d8
 -        vmlal.s16       q1, d5, d9
 +        vmlal.s16       q0, d16, d20
 +        vmlal.s16       q1, d17, d21
-
+ 
          vpadd.s32       d0, d0, d1
          vpadd.s32       d1, d2, d3
-
+ 
          vrshrn.s32      d0, q0, SBC_PROTO_FIXED_SCALE
-
+ 
 -        vld1.16         {d2, d3, d4, d5}, [r2, :128]!
 +        vld1.16         {d16, d17, d18, d19}, [r2, :128]!
-
+ 
          vdup.i32        d1, d0[1]  /* TODO: can be eliminated */
          vdup.i32        d0, d0[0]  /* TODO: can be eliminated */
-
+ 
 -        vmull.s16       q3, d2, d0
 -        vmull.s16       q4, d3, d0
 -        vmlal.s16       q3, d4, d1
@@ -99,14 +102,14 @@
 +        vmull.s16       q11, d17, d0
 +        vmlal.s16       q10, d18, d1
 +        vmlal.s16       q11, d19, d1
-
+ 
 -        vpadd.s32       d0, d6, d7 /* TODO: can be eliminated */
 -        vpadd.s32       d1, d8, d9 /* TODO: can be eliminated */
 +        vpadd.s32       d0, d20, d21 /* TODO: can be eliminated */
 +        vpadd.s32       d1, d22, d23 /* TODO: can be eliminated */
-
+ 
          vst1.32         {d0, d1}, [r1, :128]
-
+ 
 @@ -91,57 +91,57 @@ function ff_sbc_analyze_8_neon, export=1
          /* TODO: merge even and odd cases (or even merge all four calls to this
           * function) in order to have only aligned reads from 'in' array
@@ -213,13 +216,13 @@
 +        vpadd.s32       d1, d26, d27
 +        vpadd.s32       d2, d28, d29
 +        vpadd.s32       d3, d30, d31
-
+ 
          vrshr.s32       q0, q0, SBC_PROTO_FIXED_SCALE
          vrshr.s32       q1, q1, SBC_PROTO_FIXED_SCALE
 @@ -153,38 +153,38 @@ function ff_sbc_analyze_8_neon, export=1
          vdup.i32        d1, d0[1]  /* TODO: can be eliminated */
          vdup.i32        d0, d0[0]  /* TODO: can be eliminated */
-
+ 
 -        vld1.16         {d4, d5}, [r2, :128]!
 -        vmull.s16       q6, d4, d0
 -        vld1.16         {d6, d7}, [r2, :128]!
@@ -284,5 +287,6 @@
 +        vpadd.s32       d1, d26, d27 /* TODO: can be eliminated */
 +        vpadd.s32       d2, d28, d29 /* TODO: can be eliminated */
 +        vpadd.s32       d3, d30, d31 /* TODO: can be eliminated */
-
+ 
          vst1.32         {d0, d1, d2, d3}, [r1, :128]
+ 
diff --git a/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/0002-Fix-build-on-powerpc-and-ppc64.patch b/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/0002-Fix-build-on-powerpc-and-ppc64.patch
index 4d9c1b9..f398791 100644
--- a/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/0002-Fix-build-on-powerpc-and-ppc64.patch
+++ b/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/0002-Fix-build-on-powerpc-and-ppc64.patch
@@ -2,7 +2,10 @@
 Date: Tue, 19 Jan 2021 20:35:29 +0100
 Subject: Fix build on powerpc and ppc64
 
-Upstream-status: Pending
+Upstream-Status: Inappropriate
+
+RPI-Distro repo clones original ffmpeg and applies patches to enable
+raspiberry pi support.
 
 ---
  libswscale/ppc/yuv2rgb_altivec.c | 10 ++++++++++
@@ -15,7 +18,7 @@
 @@ -283,6 +283,16 @@ static inline void cvtyuvtoRGB(SwsContext *c, vector signed short Y,
   * ------------------------------------------------------------------------------
   */
-
+ 
 +#if !HAVE_VSX
 +static inline vector unsigned char vec_xl(signed long long offset, const ubyte *addr)
 +{
diff --git a/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/0003-avcodec-pngenc-remove-monowhite-from-apng-formats.patch b/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/0003-avcodec-pngenc-remove-monowhite-from-apng-formats.patch
index 38f3fd4..11e3383 100644
--- a/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/0003-avcodec-pngenc-remove-monowhite-from-apng-formats.patch
+++ b/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/0003-avcodec-pngenc-remove-monowhite-from-apng-formats.patch
@@ -2,13 +2,15 @@
 Date: Sun, 14 Feb 2021 17:20:03 +0100
 Subject: avcodec/pngenc: remove monowhite from apng formats
 
+Upstream-Status: Inappropriate
+
+RPI-Distro repo clones original ffmpeg and applies patches to enable
+raspiberry pi support.
+
 Monowhite pixel format is not supported, and it does not make sense
 to add support for it.
 
 Fixes #7989
-
-Upstream-status: Pending
-
 ---
  libavcodec/pngenc.c | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/0004-ffmpeg-4.3.2-rpi_10.patch b/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/0004-ffmpeg-4.3.4-rpi_14.patch
similarity index 84%
rename from meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/0004-ffmpeg-4.3.2-rpi_10.patch
rename to meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/0004-ffmpeg-4.3.4-rpi_14.patch
index 6bab0d0..740ac0e 100644
--- a/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/0004-ffmpeg-4.3.2-rpi_10.patch
+++ b/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/0004-ffmpeg-4.3.4-rpi_14.patch
@@ -1,16 +1,27 @@
-Upstream-status: Pending
+Upstream-Status: Inappropriate
+
+RPI-Distro repo clones original ffmpeg and applies patches to enable
+raspiberry pi support.
 
 --- a/configure
 +++ b/configure
-@@ -274,6 +274,7 @@ External library support:
+@@ -207,6 +207,7 @@ External library support:
+   --disable-bzlib          disable bzlib [autodetect]
+   --disable-coreimage      disable Apple CoreImage framework [autodetect]
+   --enable-chromaprint     enable audio fingerprinting with chromaprint [no]
++  --disable-epoxy          disable epoxy [autodetect]
+   --enable-frei0r          enable frei0r video filtering [no]
+   --enable-gcrypt          enable gcrypt, needed for rtmp(t)e support
+                            if openssl, librtmp or gmp is not used [no]
+@@ -274,6 +275,7 @@ External library support:
    --enable-libtls          enable LibreSSL (via libtls), needed for https support
                             if openssl, gnutls or mbedtls is not used [no]
    --enable-libtwolame      enable MP2 encoding via libtwolame [no]
-+  --enable-libudev         enable libudev [no]
++  --disable-libudev        disable libudev [autodetect]
    --enable-libv4l2         enable libv4l2/v4l-utils [no]
    --enable-libvidstab      enable video stabilization using vid.stab [no]
    --enable-libvmaf         enable vmaf filter via libvmaf [no]
-@@ -336,12 +337,17 @@ External library support:
+@@ -336,12 +338,17 @@ External library support:
    --enable-libmfx          enable Intel MediaSDK (AKA Quick Sync Video) code via libmfx [no]
    --enable-libnpp          enable Nvidia Performance Primitives-based code [no]
    --enable-mmal            enable Broadcom Multi-Media Abstraction Layer (Raspberry Pi) via MMAL [no]
@@ -28,23 +39,17 @@
    --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]
-@@ -1771,6 +1777,7 @@ EXTERNAL_LIBRARY_LIST="
-     libdav1d
-     libdc1394
-     libdrm
+@@ -1699,7 +1706,9 @@ EXTERNAL_AUTODETECT_LIBRARY_LIST="
+     avfoundation
+     bzlib
+     coreimage
 +    epoxy
-     libflite
-     libfontconfig
-     libfreetype
-@@ -1807,6 +1814,7 @@ EXTERNAL_LIBRARY_LIST="
-     libtesseract
-     libtheora
-     libtwolame
+     iconv
 +    libudev
-     libv4l2
-     libvorbis
-     libvpx
-@@ -1861,7 +1869,10 @@ HWACCEL_LIBRARY_LIST="
+     libxcb
+     libxcb_shm
+     libxcb_shape
+@@ -1861,7 +1870,10 @@ HWACCEL_LIBRARY_LIST="
      mmal
      omx
      opencl
@@ -53,9 +58,9 @@
 +    rpi4_8
 +    rpi4_10
  "
-
+ 
  DOCUMENT_LIST="
-@@ -1877,12 +1888,16 @@ FEATURE_LIST="
+@@ -1877,12 +1889,16 @@ FEATURE_LIST="
      gray
      hardcoded_tables
      omx_rpi
@@ -70,17 +75,17 @@
 +    vout_drm
 +    vout_egl
  "
-
+ 
  # this list should be kept in linking order
-@@ -1923,6 +1938,7 @@ SUBSYSTEM_LIST="
+@@ -1923,6 +1939,7 @@ SUBSYSTEM_LIST="
      pixelutils
      network
      rdft
 +    rpi
  "
-
+ 
  # COMPONENT_LIST needs to come last to ensure correct dependency checking
-@@ -2405,9 +2421,11 @@ CONFIG_EXTRA="
+@@ -2405,9 +2422,11 @@ CONFIG_EXTRA="
      rangecoder
      riffdec
      riffenc
@@ -92,7 +97,7 @@
      scene_sad
      sinewin
      snappy
-@@ -2737,6 +2755,8 @@ hap_decoder_select="snappy texturedsp"
+@@ -2737,6 +2756,8 @@ hap_decoder_select="snappy texturedsp"
  hap_encoder_deps="libsnappy"
  hap_encoder_select="texturedspenc"
  hevc_decoder_select="bswapdsp cabac golomb hevcparse videodsp"
@@ -101,7 +106,7 @@
  huffyuv_decoder_select="bswapdsp huffyuvdsp llviddsp"
  huffyuv_encoder_select="bswapdsp huffman huffyuvencdsp llvidencdsp"
  hymt_decoder_select="huffyuv_decoder"
-@@ -2903,6 +2923,7 @@ d3d11va_deps="dxva_h ID3D11VideoDecoder
+@@ -2903,6 +2924,7 @@ d3d11va_deps="dxva_h ID3D11VideoDecoder
  dxva2_deps="dxva2api_h DXVA2_ConfigPictureDecode ole32 user32"
  ffnvcodec_deps_any="libdl LoadLibrary"
  nvdec_deps="ffnvcodec"
@@ -109,7 +114,7 @@
  vaapi_x11_deps="xlib"
  videotoolbox_hwaccel_deps="videotoolbox pthreads"
  videotoolbox_hwaccel_extralibs="-framework QuartzCore"
-@@ -2934,6 +2955,12 @@ hevc_dxva2_hwaccel_deps="dxva2 DXVA_PicP
+@@ -2934,6 +2956,12 @@ hevc_dxva2_hwaccel_deps="dxva2 DXVA_PicP
  hevc_dxva2_hwaccel_select="hevc_decoder"
  hevc_nvdec_hwaccel_deps="nvdec"
  hevc_nvdec_hwaccel_select="hevc_decoder"
@@ -122,16 +127,15 @@
  hevc_vaapi_hwaccel_deps="vaapi VAPictureParameterBufferHEVC"
  hevc_vaapi_hwaccel_select="hevc_decoder"
  hevc_vdpau_hwaccel_deps="vdpau VdpPictureInfoHEVC"
-@@ -3401,8 +3428,14 @@ sndio_indev_deps="sndio"
+@@ -3401,8 +3429,13 @@ sndio_indev_deps="sndio"
  sndio_outdev_deps="sndio"
  v4l2_indev_deps_any="linux_videodev2_h sys_videoio_h"
  v4l2_indev_suggest="libv4l2"
 +v4l2_outdev_deps="libdrm"
  v4l2_outdev_deps_any="linux_videodev2_h sys_videoio_h"
  v4l2_outdev_suggest="libv4l2"
-+vout_drm_outdev_deps="libdrm vout_drm"
-+vout_egl_outdev_deps="xlib"
-+vout_egl_outdev_select="epoxy"
++vout_drm_outdev_deps="libdrm"
++vout_egl_outdev_deps="xlib epoxy"
 +vout_rpi_outdev_deps="rpi"
 +vout_rpi_outdev_select="sand"
  vfwcap_indev_deps="vfw32 vfwcap_defines"
@@ -145,23 +149,20 @@
  unsharp_opencl_filter_deps="opencl"
  uspp_filter_deps="gpl avcodec"
  vaguedenoiser_filter_deps="gpl"
-@@ -6299,6 +6333,7 @@ enabled libdav1d          && require_pkg
- enabled libdavs2          && require_pkg_config libdavs2 "davs2 >= 1.6.0" davs2.h davs2_decoder_open
- enabled libdc1394         && require_pkg_config libdc1394 libdc1394-2 dc1394/dc1394.h dc1394_new
- enabled libdrm            && require_pkg_config libdrm libdrm xf86drm.h drmGetVersion
-+enabled epoxy             && require_pkg_config epoxy epoxy epoxy/egl.h epoxy_egl_version
- enabled libfdk_aac        && { check_pkg_config libfdk_aac fdk-aac "fdk-aac/aacenc_lib.h" aacEncOpen ||
-                                { require libfdk_aac fdk-aac/aacenc_lib.h aacEncOpen -lfdk-aac &&
-                                  warn "using libfdk without pkg-config"; } }
-@@ -6376,6 +6411,7 @@ enabled libtls            && require_pkg
- enabled libtwolame        && require libtwolame twolame.h twolame_init -ltwolame &&
-                              { check_lib libtwolame twolame.h twolame_encode_buffer_float32_interleaved -ltwolame ||
-                                die "ERROR: libtwolame must be installed and version must be >= 0.3.10"; }
-+enabled libudev           && require_pkg_config libudev libudev libudev.h udev_new
- enabled libv4l2           && require_pkg_config libv4l2 libv4l2 libv4l2.h v4l2_ioctl
- enabled libvidstab        && require_pkg_config libvidstab "vidstab >= 0.98" vid.stab/libvidstab.h vsMotionDetectInit
- enabled libvmaf           && require_pkg_config libvmaf "libvmaf >= 1.3.9" libvmaf.h compute_vmaf
-@@ -6430,11 +6466,12 @@ enabled mbedtls           && { check_pkg
+@@ -6102,6 +6136,12 @@ check_func_headers glob.h glob
+ enabled xlib &&
+     check_lib xlib "X11/Xlib.h X11/extensions/Xvlib.h" XvGetPortAttribute -lXv -lX11 -lXext
+ 
++enabled libudev &&
++    check_pkg_config libudev libudev libudev.h udev_new
++
++enabled epoxy &&
++    check_pkg_config epoxy epoxy epoxy/egl.h epoxy_egl_version
++
+ check_headers direct.h
+ check_headers dirent.h
+ check_headers dxgidebug.h
+@@ -6430,11 +6470,12 @@ enabled mbedtls           && { check_pkg
                                 check_lib mbedtls mbedtls/ssl.h mbedtls_ssl_init -lmbedtls -lmbedx509 -lmbedcrypto ||
                                 die "ERROR: mbedTLS not found"; }
  enabled mediacodec        && { enabled jni || die "ERROR: mediacodec requires --enable-jni"; }
@@ -176,26 +177,32 @@
                                 die "ERROR: mmal not found" &&
                                 check_func_headers interface/mmal/mmal.h "MMAL_PARAMETER_VIDEO_MAX_NUM_CALLBACKS"; }
  enabled openal            && { { for al_extralibs in "${OPENAL_LIBS}" "-lopenal" "-lOpenAL32"; do
-@@ -6475,6 +6512,10 @@ enabled rkmpp             && { require_p
+@@ -6475,8 +6516,16 @@ enabled rkmpp             && { require_p
                                 { enabled libdrm ||
                                   die "ERROR: rkmpp requires --enable-libdrm"; }
                               }
 +enabled v4l2_request      && { enabled libdrm ||
 +                               die "ERROR: v4l2-request requires --enable-libdrm"; } &&
 +                             { enabled libudev ||
-+                               die "ERROR: v4l2-request requires --enable-libudev"; }
++                               die "ERROR: v4l2-request requires libudev"; }
  enabled vapoursynth       && require_pkg_config vapoursynth "vapoursynth-script >= 42" VSScript.h vsscript_init
-
-
-@@ -6556,6 +6597,8 @@ if enabled v4l2_m2m; then
+ 
++enabled vout_drm && { enabled libdrm || die "ERROR: vout_drm requires --enable-libdrm"; }
++
++enabled vout_egl && { enabled epoxy || die "ERROR: vout_egl requires epoxy"; } &&
++                    { enabled xlib  || die "ERROR: vout_egl requires xlib"; }
+ 
+ if enabled gcrypt; then
+     GCRYPT_CONFIG="${cross_prefix}libgcrypt-config"
+@@ -6556,6 +6605,8 @@ if enabled v4l2_m2m; then
      check_cc vp9_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_VP9;"
  fi
-
+ 
 +check_func_headers "linux/media.h linux/videodev2.h" v4l2_timeval_to_ns
 +check_cc hevc_v4l2_request linux/videodev2.h "int i = V4L2_PIX_FMT_HEVC_SLICE;"
  check_headers sys/videoio.h
  test_code cc sys/videoio.h "struct v4l2_frmsizeenum vfse; vfse.discrete.width = 0;" && enable_sanitized struct_v4l2_frmivalenum_discrete
-
+ 
 --- a/fftools/ffmpeg.c
 +++ b/fftools/ffmpeg.c
 @@ -2119,8 +2119,8 @@ static int ifilter_send_frame(InputFilte
@@ -208,11 +215,11 @@
 +                       ifilter->height != av_frame_cropped_height(frame);
          break;
      }
-
+ 
 @@ -2131,6 +2131,9 @@ static int ifilter_send_frame(InputFilte
          (ifilter->hw_frames_ctx && ifilter->hw_frames_ctx->data != frame->hw_frames_ctx->data))
          need_reinit = 1;
-
+ 
 +    if (no_cvt_hw && fg->graph)
 +        need_reinit = 0;
 +
@@ -221,7 +228,7 @@
          if (ret < 0)
 @@ -2401,8 +2404,7 @@ static int decode_video(InputStream *ist
          decoded_frame->top_field_first = ist->top_field_first;
-
+ 
      ist->frames_decoded++;
 -
 -    if (ist->hwaccel_retrieve_data && decoded_frame->format == ist->hwaccel_pix_fmt) {
@@ -229,7 +236,21 @@
          err = ist->hwaccel_retrieve_data(ist->dec_ctx, decoded_frame);
          if (err < 0)
              goto fail;
-@@ -2820,6 +2822,16 @@ static enum AVPixelFormat get_format(AVC
+@@ -2600,7 +2602,12 @@ static int process_input_packet(InputStr
+         case AVMEDIA_TYPE_VIDEO:
+             ret = decode_video    (ist, repeating ? NULL : &avpkt, &got_output, &duration_pts, !pkt,
+                                    &decode_failed);
+-            if (!repeating || !pkt || got_output) {
++            // Pi: Do not inc dts if no_cvt_hw set
++            // V4L2 H264 decode has long latency and sometimes spits out a long
++            // stream of output without input. In this case incrementing DTS is wrong.
++            // There may be cases where the condition as written is correct so only
++            // "fix" in the cases which cause problems
++            if (!repeating || !pkt || (got_output && !no_cvt_hw)) {
+                 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) {
+@@ -2820,6 +2827,16 @@ static enum AVPixelFormat get_format(AVC
          } else {
              const HWAccel *hwaccel = NULL;
              int i;
@@ -246,10 +267,10 @@
              for (i = 0; hwaccels[i].name; i++) {
                  if (hwaccels[i].pix_fmt == *p) {
                      hwaccel = &hwaccels[i];
-@@ -2914,6 +2926,15 @@ static int init_input_stream(int ist_ind
+@@ -2914,6 +2931,15 @@ static int init_input_stream(int ist_ind
              return ret;
          }
-
+ 
 +#if CONFIG_HEVC_RPI_DECODER
 +        ret = -1;
 +        if (strcmp(codec->name, "hevc_rpi") == 0 &&
@@ -270,7 +291,7 @@
      HWACCEL_QSV,
 +    HWACCEL_RPI,
  };
-
+ 
  typedef struct HWAccel {
 @@ -590,6 +591,7 @@ extern int video_sync_method;
  extern float frame_drop_threshold;
@@ -283,15 +304,15 @@
 --- a/fftools/ffmpeg_filter.c
 +++ b/fftools/ffmpeg_filter.c
 @@ -1186,8 +1186,8 @@ int ifilter_parameters_from_frame(InputF
-
+ 
      ifilter->format = frame->format;
-
+ 
 -    ifilter->width               = frame->width;
 -    ifilter->height              = frame->height;
 +    ifilter->width               = av_frame_cropped_width(frame);
 +    ifilter->height              = av_frame_cropped_height(frame);
      ifilter->sample_aspect_ratio = frame->sample_aspect_ratio;
-
+ 
      ifilter->sample_rate         = frame->sample_rate;
 --- a/fftools/ffmpeg_hw.c
 +++ b/fftools/ffmpeg_hw.c
@@ -309,7 +330,7 @@
 @@ -130,6 +130,12 @@ static const char *opt_name_enc_time_bas
      }\
  }
-
+ 
 +#if CONFIG_RPI
 +static int rpi_init(AVCodecContext *avctx) {
 +    return 0;
@@ -376,7 +397,7 @@
 +					  v4l2_req_devscan.o weak_link.o
  OBJS-$(CONFIG_WMA_FREQS)               += wma_freqs.o
  OBJS-$(CONFIG_WMV2DSP)                 += wmv2dsp.o
-
+ 
 @@ -391,6 +396,14 @@ OBJS-$(CONFIG_HEVC_QSV_DECODER)        +
  OBJS-$(CONFIG_HEVC_QSV_ENCODER)        += qsvenc_hevc.o hevc_ps_enc.o       \
                                            hevc_data.o
@@ -399,7 +420,7 @@
 +OBJS-$(CONFIG_HEVC_RPI4_8_HWACCEL)        += rpivid_hevc.o
 +OBJS-$(CONFIG_HEVC_RPI4_10_HWACCEL)       += rpivid_hevc.o
 +OBJS-$(CONFIG_HEVC_V4L2REQUEST_HWACCEL)   += v4l2_request_hevc.o v4l2_req_decode_q.o\
-+                                             v4l2_req_hevc_v1.o v4l2_req_hevc_v2.o
++                                             v4l2_req_hevc_v1.o v4l2_req_hevc_v2.o v4l2_req_hevc_v3.o  v4l2_req_hevc_v4.o
  OBJS-$(CONFIG_HEVC_VAAPI_HWACCEL)         += vaapi_hevc.o h265_profile_level.o
  OBJS-$(CONFIG_HEVC_VDPAU_HWACCEL)         += vdpau_hevc.o
  OBJS-$(CONFIG_MJPEG_NVDEC_HWACCEL)        += nvdec_mjpeg.o
@@ -435,6 +456,1866 @@
 +$(SUBDIR)rpi_qpu.o: $(SUBDIR)rpi_hevc_transform8.h $(SUBDIR)rpi_hevc_transform10.h
 +$(SUBDIR)rpi_hevcdec.o $(SUBDIR)rpi_shader_template.o $(SUBDIR)rpi_qpu.o: $(SUBDIR)rpi_hevc_shader.h
 +endif
+--- a/libavcodec/aarch64/Makefile
++++ b/libavcodec/aarch64/Makefile
+@@ -44,10 +44,12 @@ NEON-OBJS-$(CONFIG_H264PRED)
+ NEON-OBJS-$(CONFIG_H264QPEL)            += aarch64/h264qpel_neon.o             \
+                                            aarch64/hpeldsp_neon.o
+ NEON-OBJS-$(CONFIG_HPELDSP)             += aarch64/hpeldsp_neon.o
+-NEON-OBJS-$(CONFIG_IDCTDSP)             += aarch64/simple_idct_neon.o
++NEON-OBJS-$(CONFIG_IDCTDSP)             += aarch64/idctdsp_neon.o              \
++                                           aarch64/simple_idct_neon.o
+ NEON-OBJS-$(CONFIG_MDCT)                += aarch64/mdct_neon.o
+ NEON-OBJS-$(CONFIG_MPEGAUDIODSP)        += aarch64/mpegaudiodsp_neon.o
+ NEON-OBJS-$(CONFIG_PIXBLOCKDSP)         += aarch64/pixblockdsp_neon.o
++NEON-OBJS-$(CONFIG_VC1DSP)              += aarch64/vc1dsp_neon.o
+ NEON-OBJS-$(CONFIG_VP8DSP)              += aarch64/vp8dsp_neon.o
+ 
+ # decoders/encoders
+--- a/libavcodec/aarch64/idctdsp_init_aarch64.c
++++ b/libavcodec/aarch64/idctdsp_init_aarch64.c
+@@ -27,19 +27,29 @@
+ #include "libavcodec/idctdsp.h"
+ #include "idct.h"
+ 
++void ff_put_pixels_clamped_neon(const int16_t *, uint8_t *, ptrdiff_t);
++void ff_put_signed_pixels_clamped_neon(const int16_t *, uint8_t *, ptrdiff_t);
++void ff_add_pixels_clamped_neon(const int16_t *, uint8_t *, ptrdiff_t);
++
+ av_cold void ff_idctdsp_init_aarch64(IDCTDSPContext *c, AVCodecContext *avctx,
+                                      unsigned high_bit_depth)
+ {
+     int cpu_flags = av_get_cpu_flags();
+ 
+-    if (have_neon(cpu_flags) && !avctx->lowres && !high_bit_depth) {
+-        if (avctx->idct_algo == FF_IDCT_AUTO ||
+-            avctx->idct_algo == FF_IDCT_SIMPLEAUTO ||
+-            avctx->idct_algo == FF_IDCT_SIMPLENEON) {
+-            c->idct_put  = ff_simple_idct_put_neon;
+-            c->idct_add  = ff_simple_idct_add_neon;
+-            c->idct      = ff_simple_idct_neon;
+-            c->perm_type = FF_IDCT_PERM_PARTTRANS;
++    if (have_neon(cpu_flags)) {
++        if (!avctx->lowres && !high_bit_depth) {
++            if (avctx->idct_algo == FF_IDCT_AUTO ||
++                avctx->idct_algo == FF_IDCT_SIMPLEAUTO ||
++                avctx->idct_algo == FF_IDCT_SIMPLENEON) {
++                c->idct_put  = ff_simple_idct_put_neon;
++                c->idct_add  = ff_simple_idct_add_neon;
++                c->idct      = ff_simple_idct_neon;
++                c->perm_type = FF_IDCT_PERM_PARTTRANS;
++            }
+         }
++
++        c->add_pixels_clamped        = ff_add_pixels_clamped_neon;
++        c->put_pixels_clamped        = ff_put_pixels_clamped_neon;
++        c->put_signed_pixels_clamped = ff_put_signed_pixels_clamped_neon;
+     }
+ }
+--- /dev/null
++++ b/libavcodec/aarch64/idctdsp_neon.S
+@@ -0,0 +1,130 @@
++/*
++ * IDCT AArch64 NEON optimisations
++ *
++ * Copyright (c) 2022 Ben Avison <bavison@riscosopen.org>
++ *
++ * 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"
++
++// Clamp 16-bit signed block coefficients to unsigned 8-bit
++// On entry:
++//   x0 -> array of 64x 16-bit coefficients
++//   x1 -> 8-bit results
++//   x2 = row stride for results, bytes
++function ff_put_pixels_clamped_neon, export=1
++        ld1             {v0.16b, v1.16b, v2.16b, v3.16b}, [x0], #64
++        ld1             {v4.16b, v5.16b, v6.16b, v7.16b}, [x0]
++        sqxtun          v0.8b, v0.8h
++        sqxtun          v1.8b, v1.8h
++        sqxtun          v2.8b, v2.8h
++        sqxtun          v3.8b, v3.8h
++        sqxtun          v4.8b, v4.8h
++        st1             {v0.8b}, [x1], x2
++        sqxtun          v0.8b, v5.8h
++        st1             {v1.8b}, [x1], x2
++        sqxtun          v1.8b, v6.8h
++        st1             {v2.8b}, [x1], x2
++        sqxtun          v2.8b, v7.8h
++        st1             {v3.8b}, [x1], x2
++        st1             {v4.8b}, [x1], x2
++        st1             {v0.8b}, [x1], x2
++        st1             {v1.8b}, [x1], x2
++        st1             {v2.8b}, [x1]
++        ret
++endfunc
++
++// Clamp 16-bit signed block coefficients to signed 8-bit (biased by 128)
++// On entry:
++//   x0 -> array of 64x 16-bit coefficients
++//   x1 -> 8-bit results
++//   x2 = row stride for results, bytes
++function ff_put_signed_pixels_clamped_neon, export=1
++        ld1             {v0.16b, v1.16b, v2.16b, v3.16b}, [x0], #64
++        movi            v4.8b, #128
++        ld1             {v16.16b, v17.16b, v18.16b, v19.16b}, [x0]
++        sqxtn           v0.8b, v0.8h
++        sqxtn           v1.8b, v1.8h
++        sqxtn           v2.8b, v2.8h
++        sqxtn           v3.8b, v3.8h
++        sqxtn           v5.8b, v16.8h
++        add             v0.8b, v0.8b, v4.8b
++        sqxtn           v6.8b, v17.8h
++        add             v1.8b, v1.8b, v4.8b
++        sqxtn           v7.8b, v18.8h
++        add             v2.8b, v2.8b, v4.8b
++        sqxtn           v16.8b, v19.8h
++        add             v3.8b, v3.8b, v4.8b
++        st1             {v0.8b}, [x1], x2
++        add             v0.8b, v5.8b, v4.8b
++        st1             {v1.8b}, [x1], x2
++        add             v1.8b, v6.8b, v4.8b
++        st1             {v2.8b}, [x1], x2
++        add             v2.8b, v7.8b, v4.8b
++        st1             {v3.8b}, [x1], x2
++        add             v3.8b, v16.8b, v4.8b
++        st1             {v0.8b}, [x1], x2
++        st1             {v1.8b}, [x1], x2
++        st1             {v2.8b}, [x1], x2
++        st1             {v3.8b}, [x1]
++        ret
++endfunc
++
++// Add 16-bit signed block coefficients to unsigned 8-bit
++// On entry:
++//   x0 -> array of 64x 16-bit coefficients
++//   x1 -> 8-bit input and results
++//   x2 = row stride for 8-bit input and results, bytes
++function ff_add_pixels_clamped_neon, export=1
++        ld1             {v0.16b, v1.16b, v2.16b, v3.16b}, [x0], #64
++        mov             x3, x1
++        ld1             {v4.8b}, [x1], x2
++        ld1             {v5.8b}, [x1], x2
++        ld1             {v6.8b}, [x1], x2
++        ld1             {v7.8b}, [x1], x2
++        ld1             {v16.16b, v17.16b, v18.16b, v19.16b}, [x0]
++        uaddw           v0.8h, v0.8h, v4.8b
++        uaddw           v1.8h, v1.8h, v5.8b
++        uaddw           v2.8h, v2.8h, v6.8b
++        ld1             {v4.8b}, [x1], x2
++        uaddw           v3.8h, v3.8h, v7.8b
++        ld1             {v5.8b}, [x1], x2
++        sqxtun          v0.8b, v0.8h
++        ld1             {v6.8b}, [x1], x2
++        sqxtun          v1.8b, v1.8h
++        ld1             {v7.8b}, [x1]
++        sqxtun          v2.8b, v2.8h
++        sqxtun          v3.8b, v3.8h
++        uaddw           v4.8h, v16.8h, v4.8b
++        st1             {v0.8b}, [x3], x2
++        uaddw           v0.8h, v17.8h, v5.8b
++        st1             {v1.8b}, [x3], x2
++        uaddw           v1.8h, v18.8h, v6.8b
++        st1             {v2.8b}, [x3], x2
++        uaddw           v2.8h, v19.8h, v7.8b
++        sqxtun          v4.8b, v4.8h
++        sqxtun          v0.8b, v0.8h
++        st1             {v3.8b}, [x3], x2
++        sqxtun          v1.8b, v1.8h
++        sqxtun          v2.8b, v2.8h
++        st1             {v4.8b}, [x3], x2
++        st1             {v0.8b}, [x3], x2
++        st1             {v1.8b}, [x3], x2
++        st1             {v2.8b}, [x3]
++        ret
++endfunc
+--- a/libavcodec/aarch64/vc1dsp_init_aarch64.c
++++ b/libavcodec/aarch64/vc1dsp_init_aarch64.c
+@@ -21,10 +21,28 @@
+ #include "libavutil/attributes.h"
+ #include "libavutil/cpu.h"
+ #include "libavutil/aarch64/cpu.h"
++#include "libavutil/intreadwrite.h"
+ #include "libavcodec/vc1dsp.h"
+ 
+ #include "config.h"
+ 
++void ff_vc1_inv_trans_8x8_neon(int16_t *block);
++void ff_vc1_inv_trans_8x4_neon(uint8_t *dest, ptrdiff_t stride, int16_t *block);
++void ff_vc1_inv_trans_4x8_neon(uint8_t *dest, ptrdiff_t stride, int16_t *block);
++void ff_vc1_inv_trans_4x4_neon(uint8_t *dest, ptrdiff_t stride, int16_t *block);
++
++void ff_vc1_inv_trans_8x8_dc_neon(uint8_t *dest, ptrdiff_t stride, int16_t *block);
++void ff_vc1_inv_trans_8x4_dc_neon(uint8_t *dest, ptrdiff_t stride, int16_t *block);
++void ff_vc1_inv_trans_4x8_dc_neon(uint8_t *dest, ptrdiff_t stride, int16_t *block);
++void ff_vc1_inv_trans_4x4_dc_neon(uint8_t *dest, ptrdiff_t stride, int16_t *block);
++
++void ff_vc1_v_loop_filter4_neon(uint8_t *src, ptrdiff_t stride, int pq);
++void ff_vc1_h_loop_filter4_neon(uint8_t *src, ptrdiff_t stride, int pq);
++void ff_vc1_v_loop_filter8_neon(uint8_t *src, ptrdiff_t stride, int pq);
++void ff_vc1_h_loop_filter8_neon(uint8_t *src, ptrdiff_t stride, int pq);
++void ff_vc1_v_loop_filter16_neon(uint8_t *src, ptrdiff_t stride, int pq);
++void ff_vc1_h_loop_filter16_neon(uint8_t *src, ptrdiff_t stride, int pq);
++
+ void ff_put_vc1_chroma_mc8_neon(uint8_t *dst, uint8_t *src, ptrdiff_t stride,
+                                 int h, int x, int y);
+ void ff_avg_vc1_chroma_mc8_neon(uint8_t *dst, uint8_t *src, ptrdiff_t stride,
+@@ -34,14 +52,90 @@ void ff_put_vc1_chroma_mc4_neon(uint8_t
+ void ff_avg_vc1_chroma_mc4_neon(uint8_t *dst, uint8_t *src, ptrdiff_t stride,
+                                 int h, int x, int y);
+ 
++int ff_vc1_unescape_buffer_helper_neon(const uint8_t *src, int size, uint8_t *dst);
++
++static int vc1_unescape_buffer_neon(const uint8_t *src, int size, uint8_t *dst)
++{
++    /* Dealing with starting and stopping, and removing escape bytes, are
++     * comparatively less time-sensitive, so are more clearly expressed using
++     * a C wrapper around the assembly inner loop. Note that we assume a
++     * little-endian machine that supports unaligned loads. */
++    int dsize = 0;
++    while (size >= 4)
++    {
++        int found = 0;
++        while (!found && (((uintptr_t) dst) & 7) && size >= 4)
++        {
++            found = (AV_RL32(src) &~ 0x03000000) == 0x00030000;
++            if (!found)
++            {
++                *dst++ = *src++;
++                --size;
++                ++dsize;
++            }
++        }
++        if (!found)
++        {
++            int skip = size - ff_vc1_unescape_buffer_helper_neon(src, size, dst);
++            dst += skip;
++            src += skip;
++            size -= skip;
++            dsize += skip;
++            while (!found && size >= 4)
++            {
++                found = (AV_RL32(src) &~ 0x03000000) == 0x00030000;
++                if (!found)
++                {
++                    *dst++ = *src++;
++                    --size;
++                    ++dsize;
++                }
++            }
++        }
++        if (found)
++        {
++            *dst++ = *src++;
++            *dst++ = *src++;
++            ++src;
++            size -= 3;
++            dsize += 2;
++        }
++    }
++    while (size > 0)
++    {
++        *dst++ = *src++;
++        --size;
++        ++dsize;
++    }
++    return dsize;
++}
++
+ av_cold void ff_vc1dsp_init_aarch64(VC1DSPContext *dsp)
+ {
+     int cpu_flags = av_get_cpu_flags();
+ 
+     if (have_neon(cpu_flags)) {
++        dsp->vc1_inv_trans_8x8 = ff_vc1_inv_trans_8x8_neon;
++        dsp->vc1_inv_trans_8x4 = ff_vc1_inv_trans_8x4_neon;
++        dsp->vc1_inv_trans_4x8 = ff_vc1_inv_trans_4x8_neon;
++        dsp->vc1_inv_trans_4x4 = ff_vc1_inv_trans_4x4_neon;
++        dsp->vc1_inv_trans_8x8_dc = ff_vc1_inv_trans_8x8_dc_neon;
++        dsp->vc1_inv_trans_8x4_dc = ff_vc1_inv_trans_8x4_dc_neon;
++        dsp->vc1_inv_trans_4x8_dc = ff_vc1_inv_trans_4x8_dc_neon;
++        dsp->vc1_inv_trans_4x4_dc = ff_vc1_inv_trans_4x4_dc_neon;
++
++        dsp->vc1_v_loop_filter4  = ff_vc1_v_loop_filter4_neon;
++        dsp->vc1_h_loop_filter4  = ff_vc1_h_loop_filter4_neon;
++        dsp->vc1_v_loop_filter8  = ff_vc1_v_loop_filter8_neon;
++        dsp->vc1_h_loop_filter8  = ff_vc1_h_loop_filter8_neon;
++        dsp->vc1_v_loop_filter16 = ff_vc1_v_loop_filter16_neon;
++        dsp->vc1_h_loop_filter16 = ff_vc1_h_loop_filter16_neon;
++
+         dsp->put_no_rnd_vc1_chroma_pixels_tab[0] = ff_put_vc1_chroma_mc8_neon;
+         dsp->avg_no_rnd_vc1_chroma_pixels_tab[0] = ff_avg_vc1_chroma_mc8_neon;
+         dsp->put_no_rnd_vc1_chroma_pixels_tab[1] = ff_put_vc1_chroma_mc4_neon;
+         dsp->avg_no_rnd_vc1_chroma_pixels_tab[1] = ff_avg_vc1_chroma_mc4_neon;
++
++        dsp->vc1_unescape_buffer = vc1_unescape_buffer_neon;
+     }
+ }
+--- /dev/null
++++ b/libavcodec/aarch64/vc1dsp_neon.S
+@@ -0,0 +1,1546 @@
++/*
++ * VC1 AArch64 NEON optimisations
++ *
++ * Copyright (c) 2022 Ben Avison <bavison@riscosopen.org>
++ *
++ * 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"
++
++// VC-1 8x8 inverse transform
++// On entry:
++//   x0 -> array of 16-bit inverse transform coefficients, in column-major order
++// On exit:
++//   array at x0 updated to hold transformed block; also now held in row-major order
++function ff_vc1_inv_trans_8x8_neon, export=1
++        ld1             {v1.16b, v2.16b}, [x0], #32
++        ld1             {v3.16b, v4.16b}, [x0], #32
++        ld1             {v5.16b, v6.16b}, [x0], #32
++        shl             v1.8h, v1.8h, #2        //         8/2 * src[0]
++        sub             x1, x0, #3*32
++        ld1             {v16.16b, v17.16b}, [x0]
++        shl             v7.8h, v2.8h, #4        //          16 * src[8]
++        shl             v18.8h, v2.8h, #2       //           4 * src[8]
++        shl             v19.8h, v4.8h, #4       //                        16 * src[24]
++        ldr             d0, .Lcoeffs_it8
++        shl             v5.8h, v5.8h, #2        //                                      8/2 * src[32]
++        shl             v20.8h, v6.8h, #4       //                                       16 * src[40]
++        shl             v21.8h, v6.8h, #2       //                                        4 * src[40]
++        shl             v22.8h, v17.8h, #4      //                                                      16 * src[56]
++        ssra            v20.8h, v19.8h, #2      //                         4 * src[24] + 16 * src[40]
++        mul             v23.8h, v3.8h, v0.h[0]  //                       6/2 * src[16]
++        sub             v19.8h, v19.8h, v21.8h  //                        16 * src[24] -  4 * src[40]
++        ssra            v7.8h, v22.8h, #2       //          16 * src[8]                               +  4 * src[56]
++        sub             v18.8h, v22.8h, v18.8h  //        -  4 * src[8]                               + 16 * src[56]
++        shl             v3.8h, v3.8h, #3        //                      16/2 * src[16]
++        mls             v20.8h, v2.8h, v0.h[2]  //        - 15 * src[8] +  4 * src[24] + 16 * src[40]
++        ssra            v1.8h, v1.8h, #1        //        12/2 * src[0]
++        ssra            v5.8h, v5.8h, #1        //                                     12/2 * src[32]
++        mla             v7.8h, v4.8h, v0.h[2]   //          16 * src[8] + 15 * src[24]                +  4 * src[56]
++        shl             v21.8h, v16.8h, #3      //                                                    16/2 * src[48]
++        mls             v19.8h, v2.8h, v0.h[1]  //        -  9 * src[8] + 16 * src[24] -  4 * src[40]
++        sub             v2.8h, v23.8h, v21.8h   // t4/2 =                6/2 * src[16]              - 16/2 * src[48]
++        mla             v18.8h, v4.8h, v0.h[1]  //        -  4 * src[8] +  9 * src[24]                + 16 * src[56]
++        add             v4.8h, v1.8h, v5.8h     // t1/2 = 12/2 * src[0]              + 12/2 * src[32]
++        sub             v1.8h, v1.8h, v5.8h     // t2/2 = 12/2 * src[0]              - 12/2 * src[32]
++        mla             v3.8h, v16.8h, v0.h[0]  // t3/2 =               16/2 * src[16]              +  6/2 * src[48]
++        mla             v7.8h, v6.8h, v0.h[1]   //  t1  =   16 * src[8] + 15 * src[24] +  9 * src[40] +  4 * src[56]
++        add             v5.8h, v1.8h, v2.8h     // t6/2 = t2/2 + t4/2
++        sub             v16.8h, v1.8h, v2.8h    // t7/2 = t2/2 - t4/2
++        mla             v20.8h, v17.8h, v0.h[1] // -t2  = - 15 * src[8] +  4 * src[24] + 16 * src[40] +  9 * src[56]
++        add             v21.8h, v1.8h, v2.8h    // t6/2 = t2/2 + t4/2
++        add             v22.8h, v4.8h, v3.8h    // t5/2 = t1/2 + t3/2
++        mls             v19.8h, v17.8h, v0.h[2] // -t3  = -  9 * src[8] + 16 * src[24] -  4 * src[40] - 15 * src[56]
++        sub             v17.8h, v4.8h, v3.8h    // t8/2 = t1/2 - t3/2
++        add             v23.8h, v4.8h, v3.8h    // t5/2 = t1/2 + t3/2
++        mls             v18.8h, v6.8h, v0.h[2]  // -t4  = -  4 * src[8] +  9 * src[24] - 15 * src[40] + 16 * src[56]
++        sub             v1.8h, v1.8h, v2.8h     // t7/2 = t2/2 - t4/2
++        sub             v2.8h, v4.8h, v3.8h     // t8/2 = t1/2 - t3/2
++        neg             v3.8h, v7.8h            // -t1
++        neg             v4.8h, v20.8h           // +t2
++        neg             v6.8h, v19.8h           // +t3
++        ssra            v22.8h, v7.8h, #1       // (t5 + t1) >> 1
++        ssra            v1.8h, v19.8h, #1       // (t7 - t3) >> 1
++        neg             v7.8h, v18.8h           // +t4
++        ssra            v5.8h, v4.8h, #1        // (t6 + t2) >> 1
++        ssra            v16.8h, v6.8h, #1       // (t7 + t3) >> 1
++        ssra            v2.8h, v18.8h, #1       // (t8 - t4) >> 1
++        ssra            v17.8h, v7.8h, #1       // (t8 + t4) >> 1
++        ssra            v21.8h, v20.8h, #1      // (t6 - t2) >> 1
++        ssra            v23.8h, v3.8h, #1       // (t5 - t1) >> 1
++        srshr           v3.8h, v22.8h, #2       // (t5 + t1 + 4) >> 3
++        srshr           v4.8h, v5.8h, #2        // (t6 + t2 + 4) >> 3
++        srshr           v5.8h, v16.8h, #2       // (t7 + t3 + 4) >> 3
++        srshr           v6.8h, v17.8h, #2       // (t8 + t4 + 4) >> 3
++        srshr           v2.8h, v2.8h, #2        // (t8 - t4 + 4) >> 3
++        srshr           v1.8h, v1.8h, #2        // (t7 - t3 + 4) >> 3
++        srshr           v7.8h, v21.8h, #2       // (t6 - t2 + 4) >> 3
++        srshr           v16.8h, v23.8h, #2      // (t5 - t1 + 4) >> 3
++        trn2            v17.8h, v3.8h, v4.8h
++        trn2            v18.8h, v5.8h, v6.8h
++        trn2            v19.8h, v2.8h, v1.8h
++        trn2            v20.8h, v7.8h, v16.8h
++        trn1            v21.4s, v17.4s, v18.4s
++        trn2            v17.4s, v17.4s, v18.4s
++        trn1            v18.4s, v19.4s, v20.4s
++        trn2            v19.4s, v19.4s, v20.4s
++        trn1            v3.8h, v3.8h, v4.8h
++        trn2            v4.2d, v21.2d, v18.2d
++        trn1            v20.2d, v17.2d, v19.2d
++        trn1            v5.8h, v5.8h, v6.8h
++        trn1            v1.8h, v2.8h, v1.8h
++        trn1            v2.8h, v7.8h, v16.8h
++        trn1            v6.2d, v21.2d, v18.2d
++        trn2            v7.2d, v17.2d, v19.2d
++        shl             v16.8h, v20.8h, #4      //                        16 * src[24]
++        shl             v17.8h, v4.8h, #4       //                                       16 * src[40]
++        trn1            v18.4s, v3.4s, v5.4s
++        trn1            v19.4s, v1.4s, v2.4s
++        shl             v21.8h, v7.8h, #4       //                                                      16 * src[56]
++        shl             v22.8h, v6.8h, #2       //           4 * src[8]
++        shl             v23.8h, v4.8h, #2       //                                        4 * src[40]
++        trn2            v3.4s, v3.4s, v5.4s
++        trn2            v1.4s, v1.4s, v2.4s
++        shl             v2.8h, v6.8h, #4        //          16 * src[8]
++        sub             v5.8h, v16.8h, v23.8h   //                        16 * src[24] -  4 * src[40]
++        ssra            v17.8h, v16.8h, #2      //                         4 * src[24] + 16 * src[40]
++        sub             v16.8h, v21.8h, v22.8h  //        -  4 * src[8]                               + 16 * src[56]
++        trn1            v22.2d, v18.2d, v19.2d
++        trn2            v18.2d, v18.2d, v19.2d
++        trn1            v19.2d, v3.2d, v1.2d
++        ssra            v2.8h, v21.8h, #2       //          16 * src[8]                               +  4 * src[56]
++        mls             v17.8h, v6.8h, v0.h[2]  //        - 15 * src[8] +  4 * src[24] + 16 * src[40]
++        shl             v21.8h, v22.8h, #2      //         8/2 * src[0]
++        shl             v18.8h, v18.8h, #2      //                                      8/2 * src[32]
++        mls             v5.8h, v6.8h, v0.h[1]   //        -  9 * src[8] + 16 * src[24] -  4 * src[40]
++        shl             v6.8h, v19.8h, #3       //                      16/2 * src[16]
++        trn2            v1.2d, v3.2d, v1.2d
++        mla             v16.8h, v20.8h, v0.h[1] //        -  4 * src[8] +  9 * src[24]                + 16 * src[56]
++        ssra            v21.8h, v21.8h, #1      //        12/2 * src[0]
++        ssra            v18.8h, v18.8h, #1      //                                     12/2 * src[32]
++        mul             v3.8h, v19.8h, v0.h[0]  //                       6/2 * src[16]
++        shl             v19.8h, v1.8h, #3       //                                                    16/2 * src[48]
++        mla             v2.8h, v20.8h, v0.h[2]  //          16 * src[8] + 15 * src[24]                +  4 * src[56]
++        add             v20.8h, v21.8h, v18.8h  // t1/2 = 12/2 * src[0]              + 12/2 * src[32]
++        mla             v6.8h, v1.8h, v0.h[0]   // t3/2 =               16/2 * src[16]              +  6/2 * src[48]
++        sub             v1.8h, v21.8h, v18.8h   // t2/2 = 12/2 * src[0]              - 12/2 * src[32]
++        sub             v3.8h, v3.8h, v19.8h    // t4/2 =                6/2 * src[16]              - 16/2 * src[48]
++        mla             v17.8h, v7.8h, v0.h[1]  // -t2  = - 15 * src[8] +  4 * src[24] + 16 * src[40] +  9 * src[56]
++        mls             v5.8h, v7.8h, v0.h[2]   // -t3  = -  9 * src[8] + 16 * src[24] -  4 * src[40] - 15 * src[56]
++        add             v7.8h, v1.8h, v3.8h     // t6/2 = t2/2 + t4/2
++        add             v18.8h, v20.8h, v6.8h   // t5/2 = t1/2 + t3/2
++        mls             v16.8h, v4.8h, v0.h[2]  // -t4  = -  4 * src[8] +  9 * src[24] - 15 * src[40] + 16 * src[56]
++        sub             v19.8h, v1.8h, v3.8h    // t7/2 = t2/2 - t4/2
++        neg             v21.8h, v17.8h          // +t2
++        mla             v2.8h, v4.8h, v0.h[1]   //  t1  =   16 * src[8] + 15 * src[24] +  9 * src[40] +  4 * src[56]
++        sub             v0.8h, v20.8h, v6.8h    // t8/2 = t1/2 - t3/2
++        neg             v4.8h, v5.8h            // +t3
++        sub             v22.8h, v1.8h, v3.8h    // t7/2 = t2/2 - t4/2
++        sub             v23.8h, v20.8h, v6.8h   // t8/2 = t1/2 - t3/2
++        neg             v24.8h, v16.8h          // +t4
++        add             v6.8h, v20.8h, v6.8h    // t5/2 = t1/2 + t3/2
++        add             v1.8h, v1.8h, v3.8h     // t6/2 = t2/2 + t4/2
++        ssra            v7.8h, v21.8h, #1       // (t6 + t2) >> 1
++        neg             v3.8h, v2.8h            // -t1
++        ssra            v18.8h, v2.8h, #1       // (t5 + t1) >> 1
++        ssra            v19.8h, v4.8h, #1       // (t7 + t3) >> 1
++        ssra            v0.8h, v24.8h, #1       // (t8 + t4) >> 1
++        srsra           v23.8h, v16.8h, #1      // (t8 - t4 + 1) >> 1
++        srsra           v22.8h, v5.8h, #1       // (t7 - t3 + 1) >> 1
++        srsra           v1.8h, v17.8h, #1       // (t6 - t2 + 1) >> 1
++        srsra           v6.8h, v3.8h, #1        // (t5 - t1 + 1) >> 1
++        srshr           v2.8h, v18.8h, #6       // (t5 + t1 + 64) >> 7
++        srshr           v3.8h, v7.8h, #6        // (t6 + t2 + 64) >> 7
++        srshr           v4.8h, v19.8h, #6       // (t7 + t3 + 64) >> 7
++        srshr           v5.8h, v0.8h, #6        // (t8 + t4 + 64) >> 7
++        srshr           v16.8h, v23.8h, #6      // (t8 - t4 + 65) >> 7
++        srshr           v17.8h, v22.8h, #6      // (t7 - t3 + 65) >> 7
++        st1             {v2.16b, v3.16b}, [x1], #32
++        srshr           v0.8h, v1.8h, #6        // (t6 - t2 + 65) >> 7
++        srshr           v1.8h, v6.8h, #6        // (t5 - t1 + 65) >> 7
++        st1             {v4.16b, v5.16b}, [x1], #32
++        st1             {v16.16b, v17.16b}, [x1], #32
++        st1             {v0.16b, v1.16b}, [x1]
++        ret
++endfunc
++
++// VC-1 8x4 inverse transform
++// On entry:
++//   x0 -> array of 8-bit samples, in row-major order
++//   x1 = row stride for 8-bit sample array
++//   x2 -> array of 16-bit inverse transform coefficients, in row-major order
++// On exit:
++//   array at x0 updated by saturated addition of (narrowed) transformed block
++function ff_vc1_inv_trans_8x4_neon, export=1
++        ld1             {v1.8b, v2.8b, v3.8b, v4.8b}, [x2], #32
++        mov             x3, x0
++        ld1             {v16.8b, v17.8b, v18.8b, v19.8b}, [x2]
++        ldr             q0, .Lcoeffs_it8        // includes 4-point coefficients in upper half of vector
++        ld1             {v5.8b}, [x0], x1
++        trn2            v6.4h, v1.4h, v3.4h
++        trn2            v7.4h, v2.4h, v4.4h
++        trn1            v1.4h, v1.4h, v3.4h
++        trn1            v2.4h, v2.4h, v4.4h
++        trn2            v3.4h, v16.4h, v18.4h
++        trn2            v4.4h, v17.4h, v19.4h
++        trn1            v16.4h, v16.4h, v18.4h
++        trn1            v17.4h, v17.4h, v19.4h
++        ld1             {v18.8b}, [x0], x1
++        trn1            v19.2s, v6.2s, v3.2s
++        trn2            v3.2s, v6.2s, v3.2s
++        trn1            v6.2s, v7.2s, v4.2s
++        trn2            v4.2s, v7.2s, v4.2s
++        trn1            v7.2s, v1.2s, v16.2s
++        trn1            v20.2s, v2.2s, v17.2s
++        shl             v21.4h, v19.4h, #4      //          16 * src[1]
++        trn2            v1.2s, v1.2s, v16.2s
++        shl             v16.4h, v3.4h, #4       //                        16 * src[3]
++        trn2            v2.2s, v2.2s, v17.2s
++        shl             v17.4h, v6.4h, #4       //                                      16 * src[5]
++        ld1             {v22.8b}, [x0], x1
++        shl             v23.4h, v4.4h, #4       //                                                    16 * src[7]
++        mul             v24.4h, v1.4h, v0.h[0]  //                       6/2 * src[2]
++        ld1             {v25.8b}, [x0]
++        shl             v26.4h, v19.4h, #2      //           4 * src[1]
++        shl             v27.4h, v6.4h, #2       //                                       4 * src[5]
++        ssra            v21.4h, v23.4h, #2      //          16 * src[1]                             +  4 * src[7]
++        ssra            v17.4h, v16.4h, #2      //                         4 * src[3] + 16 * src[5]
++        sub             v23.4h, v23.4h, v26.4h  //        -  4 * src[1]                             + 16 * src[7]
++        sub             v16.4h, v16.4h, v27.4h  //                        16 * src[3] -  4 * src[5]
++        shl             v7.4h, v7.4h, #2        //         8/2 * src[0]
++        shl             v20.4h, v20.4h, #2      //                                     8/2 * src[4]
++        mla             v21.4h, v3.4h, v0.h[2]  //          16 * src[1] + 15 * src[3]               +  4 * src[7]
++        shl             v1.4h, v1.4h, #3        //                      16/2 * src[2]
++        mls             v17.4h, v19.4h, v0.h[2] //        - 15 * src[1] +  4 * src[3] + 16 * src[5]
++        ssra            v7.4h, v7.4h, #1        //        12/2 * src[0]
++        mls             v16.4h, v19.4h, v0.h[1] //        -  9 * src[1] + 16 * src[3] -  4 * src[5]
++        ssra            v20.4h, v20.4h, #1      //                                    12/2 * src[4]
++        mla             v23.4h, v3.4h, v0.h[1]  //        -  4 * src[1] +  9 * src[3]               + 16 * src[7]
++        shl             v3.4h, v2.4h, #3        //                                                  16/2 * src[6]
++        mla             v1.4h, v2.4h, v0.h[0]   // t3/2 =               16/2 * src[2]             +  6/2 * src[6]
++        mla             v21.4h, v6.4h, v0.h[1]  //  t1  =   16 * src[1] + 15 * src[3] +  9 * src[5] +  4 * src[7]
++        mla             v17.4h, v4.4h, v0.h[1]  // -t2  = - 15 * src[1] +  4 * src[3] + 16 * src[5] +  9 * src[7]
++        sub             v2.4h, v24.4h, v3.4h    // t4/2 =                6/2 * src[2]             - 16/2 * src[6]
++        mls             v16.4h, v4.4h, v0.h[2]  // -t3  = -  9 * src[1] + 16 * src[3] -  4 * src[5] - 15 * src[7]
++        add             v3.4h, v7.4h, v20.4h    // t1/2 = 12/2 * src[0]             + 12/2 * src[4]
++        mls             v23.4h, v6.4h, v0.h[2]  // -t4  = -  4 * src[1] +  9 * src[3] - 15 * src[5] + 16 * src[7]
++        sub             v4.4h, v7.4h, v20.4h    // t2/2 = 12/2 * src[0]             - 12/2 * src[4]
++        neg             v6.4h, v21.4h           // -t1
++        add             v7.4h, v3.4h, v1.4h     // t5/2 = t1/2 + t3/2
++        sub             v19.4h, v3.4h, v1.4h    // t8/2 = t1/2 - t3/2
++        add             v20.4h, v4.4h, v2.4h    // t6/2 = t2/2 + t4/2
++        sub             v24.4h, v4.4h, v2.4h    // t7/2 = t2/2 - t4/2
++        add             v26.4h, v3.4h, v1.4h    // t5/2 = t1/2 + t3/2
++        add             v27.4h, v4.4h, v2.4h    // t6/2 = t2/2 + t4/2
++        sub             v2.4h, v4.4h, v2.4h     // t7/2 = t2/2 - t4/2
++        sub             v1.4h, v3.4h, v1.4h     // t8/2 = t1/2 - t3/2
++        neg             v3.4h, v17.4h           // +t2
++        neg             v4.4h, v16.4h           // +t3
++        neg             v28.4h, v23.4h          // +t4
++        ssra            v7.4h, v21.4h, #1       // (t5 + t1) >> 1
++        ssra            v1.4h, v23.4h, #1       // (t8 - t4) >> 1
++        ssra            v20.4h, v3.4h, #1       // (t6 + t2) >> 1
++        ssra            v24.4h, v4.4h, #1       // (t7 + t3) >> 1
++        ssra            v19.4h, v28.4h, #1      // (t8 + t4) >> 1
++        ssra            v2.4h, v16.4h, #1       // (t7 - t3) >> 1
++        ssra            v27.4h, v17.4h, #1      // (t6 - t2) >> 1
++        ssra            v26.4h, v6.4h, #1       // (t5 - t1) >> 1
++        trn1            v1.2d, v7.2d, v1.2d
++        trn1            v2.2d, v20.2d, v2.2d
++        trn1            v3.2d, v24.2d, v27.2d
++        trn1            v4.2d, v19.2d, v26.2d
++        srshr           v1.8h, v1.8h, #2        // (t5 + t1 + 4) >> 3, (t8 - t4 + 4) >> 3
++        srshr           v2.8h, v2.8h, #2        // (t6 + t2 + 4) >> 3, (t7 - t3 + 4) >> 3
++        srshr           v3.8h, v3.8h, #2        // (t7 + t3 + 4) >> 3, (t6 - t2 + 4) >> 3
++        srshr           v4.8h, v4.8h, #2        // (t8 + t4 + 4) >> 3, (t5 - t1 + 4) >> 3
++        trn2            v6.8h, v1.8h, v2.8h
++        trn1            v1.8h, v1.8h, v2.8h
++        trn2            v2.8h, v3.8h, v4.8h
++        trn1            v3.8h, v3.8h, v4.8h
++        trn2            v4.4s, v6.4s, v2.4s
++        trn1            v7.4s, v1.4s, v3.4s
++        trn2            v1.4s, v1.4s, v3.4s
++        mul             v3.8h, v4.8h, v0.h[5]   //                                                           22/2 * src[24]
++        trn1            v2.4s, v6.4s, v2.4s
++        mul             v4.8h, v4.8h, v0.h[4]   //                                                           10/2 * src[24]
++        mul             v6.8h, v7.8h, v0.h[6]   //            17 * src[0]
++        mul             v1.8h, v1.8h, v0.h[6]   //                                            17 * src[16]
++        mls             v3.8h, v2.8h, v0.h[4]   //  t4/2 =                - 10/2 * src[8]                  + 22/2 * src[24]
++        mla             v4.8h, v2.8h, v0.h[5]   //  t3/2 =                  22/2 * src[8]                  + 10/2 * src[24]
++        add             v0.8h, v6.8h, v1.8h     //   t1  =    17 * src[0]                 +   17 * src[16]
++        sub             v1.8h, v6.8h, v1.8h     //   t2  =    17 * src[0]                 -   17 * src[16]
++        neg             v2.8h, v3.8h            // -t4/2
++        neg             v6.8h, v4.8h            // -t3/2
++        ssra            v4.8h, v0.8h, #1        // (t1 + t3) >> 1
++        ssra            v2.8h, v1.8h, #1        // (t2 - t4) >> 1
++        ssra            v3.8h, v1.8h, #1        // (t2 + t4) >> 1
++        ssra            v6.8h, v0.8h, #1        // (t1 - t3) >> 1
++        srshr           v0.8h, v4.8h, #6        // (t1 + t3 + 64) >> 7
++        srshr           v1.8h, v2.8h, #6        // (t2 - t4 + 64) >> 7
++        srshr           v2.8h, v3.8h, #6        // (t2 + t4 + 64) >> 7
++        srshr           v3.8h, v6.8h, #6        // (t1 - t3 + 64) >> 7
++        uaddw           v0.8h, v0.8h, v5.8b
++        uaddw           v1.8h, v1.8h, v18.8b
++        uaddw           v2.8h, v2.8h, v22.8b
++        uaddw           v3.8h, v3.8h, v25.8b
++        sqxtun          v0.8b, v0.8h
++        sqxtun          v1.8b, v1.8h
++        sqxtun          v2.8b, v2.8h
++        sqxtun          v3.8b, v3.8h
++        st1             {v0.8b}, [x3], x1
++        st1             {v1.8b}, [x3], x1
++        st1             {v2.8b}, [x3], x1
++        st1             {v3.8b}, [x3]
++        ret
++endfunc
++
++// VC-1 4x8 inverse transform
++// On entry:
++//   x0 -> array of 8-bit samples, in row-major order
++//   x1 = row stride for 8-bit sample array
++//   x2 -> array of 16-bit inverse transform coefficients, in row-major order (row stride is 8 coefficients)
++// On exit:
++//   array at x0 updated by saturated addition of (narrowed) transformed block
++function ff_vc1_inv_trans_4x8_neon, export=1
++        mov             x3, #16
++        ldr             q0, .Lcoeffs_it8        // includes 4-point coefficients in upper half of vector
++        mov             x4, x0
++        ld1             {v1.d}[0], [x2], x3     // 00 01 02 03
++        ld1             {v2.d}[0], [x2], x3     // 10 11 12 13
++        ld1             {v3.d}[0], [x2], x3     // 20 21 22 23
++        ld1             {v4.d}[0], [x2], x3     // 30 31 32 33
++        ld1             {v1.d}[1], [x2], x3     // 40 41 42 43
++        ld1             {v2.d}[1], [x2], x3     // 50 51 52 53
++        ld1             {v3.d}[1], [x2], x3     // 60 61 62 63
++        ld1             {v4.d}[1], [x2]         // 70 71 72 73
++        ld1             {v5.s}[0], [x0], x1
++        ld1             {v6.s}[0], [x0], x1
++        ld1             {v7.s}[0], [x0], x1
++        trn2            v16.8h, v1.8h, v2.8h    // 01 11 03 13 41 51 43 53
++        trn1            v1.8h, v1.8h, v2.8h     // 00 10 02 12 40 50 42 52
++        trn2            v2.8h, v3.8h, v4.8h     // 21 31 23 33 61 71 63 73
++        trn1            v3.8h, v3.8h, v4.8h     // 20 30 22 32 60 70 62 72
++        ld1             {v4.s}[0], [x0], x1
++        trn2            v17.4s, v16.4s, v2.4s   // 03 13 23 33 43 53 63 73
++        trn1            v18.4s, v1.4s, v3.4s    // 00 10 20 30 40 50 60 70
++        trn1            v2.4s, v16.4s, v2.4s    // 01 11 21 31 41 51 61 71
++        mul             v16.8h, v17.8h, v0.h[4] //                                                          10/2 * src[3]
++        ld1             {v5.s}[1], [x0], x1
++        mul             v17.8h, v17.8h, v0.h[5] //                                                          22/2 * src[3]
++        ld1             {v6.s}[1], [x0], x1
++        trn2            v1.4s, v1.4s, v3.4s     // 02 12 22 32 42 52 62 72
++        mul             v3.8h, v18.8h, v0.h[6]  //            17 * src[0]
++        ld1             {v7.s}[1], [x0], x1
++        mul             v1.8h, v1.8h, v0.h[6]   //                                            17 * src[2]
++        ld1             {v4.s}[1], [x0]
++        mla             v16.8h, v2.8h, v0.h[5]  //  t3/2 =                  22/2 * src[1]                 + 10/2 * src[3]
++        mls             v17.8h, v2.8h, v0.h[4]  //  t4/2 =                - 10/2 * src[1]                 + 22/2 * src[3]
++        add             v2.8h, v3.8h, v1.8h     //   t1  =    17 * src[0]                 +   17 * src[2]
++        sub             v1.8h, v3.8h, v1.8h     //   t2  =    17 * src[0]                 -   17 * src[2]
++        neg             v3.8h, v16.8h           // -t3/2
++        ssra            v16.8h, v2.8h, #1       // (t1 + t3) >> 1
++        neg             v18.8h, v17.8h          // -t4/2
++        ssra            v17.8h, v1.8h, #1       // (t2 + t4) >> 1
++        ssra            v3.8h, v2.8h, #1        // (t1 - t3) >> 1
++        ssra            v18.8h, v1.8h, #1       // (t2 - t4) >> 1
++        srshr           v1.8h, v16.8h, #2       // (t1 + t3 + 64) >> 3
++        srshr           v2.8h, v17.8h, #2       // (t2 + t4 + 64) >> 3
++        srshr           v3.8h, v3.8h, #2        // (t1 - t3 + 64) >> 3
++        srshr           v16.8h, v18.8h, #2      // (t2 - t4 + 64) >> 3
++        trn2            v17.8h, v2.8h, v3.8h    // 12 13 32 33 52 53 72 73
++        trn2            v18.8h, v1.8h, v16.8h   // 10 11 30 31 50 51 70 71
++        trn1            v1.8h, v1.8h, v16.8h    // 00 01 20 21 40 41 60 61
++        trn1            v2.8h, v2.8h, v3.8h     // 02 03 22 23 42 43 62 63
++        trn1            v3.4s, v18.4s, v17.4s   // 10 11 12 13 50 51 52 53
++        trn2            v16.4s, v18.4s, v17.4s  // 30 31 32 33 70 71 72 73
++        trn1            v17.4s, v1.4s, v2.4s    // 00 01 02 03 40 41 42 43
++        mov             d18, v3.d[1]            // 50 51 52 53
++        shl             v19.4h, v3.4h, #4       //          16 * src[8]
++        mov             d20, v16.d[1]           // 70 71 72 73
++        shl             v21.4h, v16.4h, #4      //                        16 * src[24]
++        mov             d22, v17.d[1]           // 40 41 42 43
++        shl             v23.4h, v3.4h, #2       //           4 * src[8]
++        shl             v24.4h, v18.4h, #4      //                                       16 * src[40]
++        shl             v25.4h, v20.4h, #4      //                                                      16 * src[56]
++        shl             v26.4h, v18.4h, #2      //                                        4 * src[40]
++        trn2            v1.4s, v1.4s, v2.4s     // 20 21 22 23 60 61 62 63
++        ssra            v24.4h, v21.4h, #2      //                         4 * src[24] + 16 * src[40]
++        sub             v2.4h, v25.4h, v23.4h   //        -  4 * src[8]                               + 16 * src[56]
++        shl             v17.4h, v17.4h, #2      //         8/2 * src[0]
++        sub             v21.4h, v21.4h, v26.4h  //                        16 * src[24] -  4 * src[40]
++        shl             v22.4h, v22.4h, #2      //                                      8/2 * src[32]
++        mov             d23, v1.d[1]            // 60 61 62 63
++        ssra            v19.4h, v25.4h, #2      //          16 * src[8]                               +  4 * src[56]
++        mul             v25.4h, v1.4h, v0.h[0]  //                       6/2 * src[16]
++        shl             v1.4h, v1.4h, #3        //                      16/2 * src[16]
++        mls             v24.4h, v3.4h, v0.h[2]  //        - 15 * src[8] +  4 * src[24] + 16 * src[40]
++        ssra            v17.4h, v17.4h, #1      //        12/2 * src[0]
++        mls             v21.4h, v3.4h, v0.h[1]  //        -  9 * src[8] + 16 * src[24] -  4 * src[40]
++        ssra            v22.4h, v22.4h, #1      //                                     12/2 * src[32]
++        mla             v2.4h, v16.4h, v0.h[1]  //        -  4 * src[8] +  9 * src[24]                + 16 * src[56]
++        shl             v3.4h, v23.4h, #3       //                                                    16/2 * src[48]
++        mla             v19.4h, v16.4h, v0.h[2] //          16 * src[8] + 15 * src[24]                +  4 * src[56]
++        mla             v1.4h, v23.4h, v0.h[0]  // t3/2 =               16/2 * src[16]              +  6/2 * src[48]
++        mla             v24.4h, v20.4h, v0.h[1] // -t2  = - 15 * src[8] +  4 * src[24] + 16 * src[40] +  9 * src[56]
++        add             v16.4h, v17.4h, v22.4h  // t1/2 = 12/2 * src[0]              + 12/2 * src[32]
++        sub             v3.4h, v25.4h, v3.4h    // t4/2 =                6/2 * src[16]              - 16/2 * src[48]
++        sub             v17.4h, v17.4h, v22.4h  // t2/2 = 12/2 * src[0]              - 12/2 * src[32]
++        mls             v21.4h, v20.4h, v0.h[2] // -t3  = -  9 * src[8] + 16 * src[24] -  4 * src[40] - 15 * src[56]
++        mla             v19.4h, v18.4h, v0.h[1] //  t1  =   16 * src[8] + 15 * src[24] +  9 * src[40] +  4 * src[56]
++        add             v20.4h, v16.4h, v1.4h   // t5/2 = t1/2 + t3/2
++        mls             v2.4h, v18.4h, v0.h[2]  // -t4  = -  4 * src[8] +  9 * src[24] - 15 * src[40] + 16 * src[56]
++        sub             v0.4h, v16.4h, v1.4h    // t8/2 = t1/2 - t3/2
++        add             v18.4h, v17.4h, v3.4h   // t6/2 = t2/2 + t4/2
++        sub             v22.4h, v17.4h, v3.4h   // t7/2 = t2/2 - t4/2
++        neg             v23.4h, v24.4h          // +t2
++        sub             v25.4h, v17.4h, v3.4h   // t7/2 = t2/2 - t4/2
++        add             v3.4h, v17.4h, v3.4h    // t6/2 = t2/2 + t4/2
++        neg             v17.4h, v21.4h          // +t3
++        sub             v26.4h, v16.4h, v1.4h   // t8/2 = t1/2 - t3/2
++        add             v1.4h, v16.4h, v1.4h    // t5/2 = t1/2 + t3/2
++        neg             v16.4h, v19.4h          // -t1
++        neg             v27.4h, v2.4h           // +t4
++        ssra            v20.4h, v19.4h, #1      // (t5 + t1) >> 1
++        srsra           v0.4h, v2.4h, #1        // (t8 - t4 + 1) >> 1
++        ssra            v18.4h, v23.4h, #1      // (t6 + t2) >> 1
++        srsra           v22.4h, v21.4h, #1      // (t7 - t3 + 1) >> 1
++        ssra            v25.4h, v17.4h, #1      // (t7 + t3) >> 1
++        srsra           v3.4h, v24.4h, #1       // (t6 - t2 + 1) >> 1
++        ssra            v26.4h, v27.4h, #1      // (t8 + t4) >> 1
++        srsra           v1.4h, v16.4h, #1       // (t5 - t1 + 1) >> 1
++        trn1            v0.2d, v20.2d, v0.2d
++        trn1            v2.2d, v18.2d, v22.2d
++        trn1            v3.2d, v25.2d, v3.2d
++        trn1            v1.2d, v26.2d, v1.2d
++        srshr           v0.8h, v0.8h, #6        // (t5 + t1 + 64) >> 7, (t8 - t4 + 65) >> 7
++        srshr           v2.8h, v2.8h, #6        // (t6 + t2 + 64) >> 7, (t7 - t3 + 65) >> 7
++        srshr           v3.8h, v3.8h, #6        // (t7 + t3 + 64) >> 7, (t6 - t2 + 65) >> 7
++        srshr           v1.8h, v1.8h, #6        // (t8 + t4 + 64) >> 7, (t5 - t1 + 65) >> 7
++        uaddw           v0.8h, v0.8h, v5.8b
++        uaddw           v2.8h, v2.8h, v6.8b
++        uaddw           v3.8h, v3.8h, v7.8b
++        uaddw           v1.8h, v1.8h, v4.8b
++        sqxtun          v0.8b, v0.8h
++        sqxtun          v2.8b, v2.8h
++        sqxtun          v3.8b, v3.8h
++        sqxtun          v1.8b, v1.8h
++        st1             {v0.s}[0], [x4], x1
++        st1             {v2.s}[0], [x4], x1
++        st1             {v3.s}[0], [x4], x1
++        st1             {v1.s}[0], [x4], x1
++        st1             {v0.s}[1], [x4], x1
++        st1             {v2.s}[1], [x4], x1
++        st1             {v3.s}[1], [x4], x1
++        st1             {v1.s}[1], [x4]
++        ret
++endfunc
++
++// VC-1 4x4 inverse transform
++// On entry:
++//   x0 -> array of 8-bit samples, in row-major order
++//   x1 = row stride for 8-bit sample array
++//   x2 -> array of 16-bit inverse transform coefficients, in row-major order (row stride is 8 coefficients)
++// On exit:
++//   array at x0 updated by saturated addition of (narrowed) transformed block
++function ff_vc1_inv_trans_4x4_neon, export=1
++        mov             x3, #16
++        ldr             d0, .Lcoeffs_it4
++        mov             x4, x0
++        ld1             {v1.d}[0], [x2], x3     // 00 01 02 03
++        ld1             {v2.d}[0], [x2], x3     // 10 11 12 13
++        ld1             {v3.d}[0], [x2], x3     // 20 21 22 23
++        ld1             {v4.d}[0], [x2]         // 30 31 32 33
++        ld1             {v5.s}[0], [x0], x1
++        ld1             {v5.s}[1], [x0], x1
++        ld1             {v6.s}[0], [x0], x1
++        trn2            v7.4h, v1.4h, v2.4h     // 01 11 03 13
++        trn1            v1.4h, v1.4h, v2.4h     // 00 10 02 12
++        ld1             {v6.s}[1], [x0]
++        trn2            v2.4h, v3.4h, v4.4h     // 21 31 23 33
++        trn1            v3.4h, v3.4h, v4.4h     // 20 30 22 32
++        trn2            v4.2s, v7.2s, v2.2s     // 03 13 23 33
++        trn1            v16.2s, v1.2s, v3.2s    // 00 10 20 30
++        trn1            v2.2s, v7.2s, v2.2s     // 01 11 21 31
++        trn2            v1.2s, v1.2s, v3.2s     // 02 12 22 32
++        mul             v3.4h, v4.4h, v0.h[0]   //                                                          10/2 * src[3]
++        mul             v4.4h, v4.4h, v0.h[1]   //                                                          22/2 * src[3]
++        mul             v7.4h, v16.4h, v0.h[2]  //            17 * src[0]
++        mul             v1.4h, v1.4h, v0.h[2]   //                                            17 * src[2]
++        mla             v3.4h, v2.4h, v0.h[1]   //  t3/2 =                  22/2 * src[1]                 + 10/2 * src[3]
++        mls             v4.4h, v2.4h, v0.h[0]   //  t4/2 =                - 10/2 * src[1]                 + 22/2 * src[3]
++        add             v2.4h, v7.4h, v1.4h     //   t1  =    17 * src[0]                 +   17 * src[2]
++        sub             v1.4h, v7.4h, v1.4h     //   t2  =    17 * src[0]                 -   17 * src[2]
++        neg             v7.4h, v3.4h            // -t3/2
++        neg             v16.4h, v4.4h           // -t4/2
++        ssra            v3.4h, v2.4h, #1        // (t1 + t3) >> 1
++        ssra            v4.4h, v1.4h, #1        // (t2 + t4) >> 1
++        ssra            v16.4h, v1.4h, #1       // (t2 - t4) >> 1
++        ssra            v7.4h, v2.4h, #1        // (t1 - t3) >> 1
++        srshr           v1.4h, v3.4h, #2        // (t1 + t3 + 64) >> 3
++        srshr           v2.4h, v4.4h, #2        // (t2 + t4 + 64) >> 3
++        srshr           v3.4h, v16.4h, #2       // (t2 - t4 + 64) >> 3
++        srshr           v4.4h, v7.4h, #2        // (t1 - t3 + 64) >> 3
++        trn2            v7.4h, v1.4h, v3.4h     // 10 11 30 31
++        trn1            v1.4h, v1.4h, v3.4h     // 00 01 20 21
++        trn2            v3.4h, v2.4h, v4.4h     // 12 13 32 33
++        trn1            v2.4h, v2.4h, v4.4h     // 02 03 22 23
++        trn2            v4.2s, v7.2s, v3.2s     // 30 31 32 33
++        trn1            v16.2s, v1.2s, v2.2s    // 00 01 02 03
++        trn1            v3.2s, v7.2s, v3.2s     // 10 11 12 13
++        trn2            v1.2s, v1.2s, v2.2s     // 20 21 22 23
++        mul             v2.4h, v4.4h, v0.h[1]   //                                                           22/2 * src[24]
++        mul             v4.4h, v4.4h, v0.h[0]   //                                                           10/2 * src[24]
++        mul             v7.4h, v16.4h, v0.h[2]  //            17 * src[0]
++        mul             v1.4h, v1.4h, v0.h[2]   //                                            17 * src[16]
++        mls             v2.4h, v3.4h, v0.h[0]   //  t4/2 =                - 10/2 * src[8]                  + 22/2 * src[24]
++        mla             v4.4h, v3.4h, v0.h[1]   //  t3/2 =                  22/2 * src[8]                  + 10/2 * src[24]
++        add             v0.4h, v7.4h, v1.4h     //   t1  =    17 * src[0]                 +   17 * src[16]
++        sub             v1.4h, v7.4h, v1.4h     //   t2  =    17 * src[0]                 -   17 * src[16]
++        neg             v3.4h, v2.4h            // -t4/2
++        neg             v7.4h, v4.4h            // -t3/2
++        ssra            v4.4h, v0.4h, #1        // (t1 + t3) >> 1
++        ssra            v3.4h, v1.4h, #1        // (t2 - t4) >> 1
++        ssra            v2.4h, v1.4h, #1        // (t2 + t4) >> 1
++        ssra            v7.4h, v0.4h, #1        // (t1 - t3) >> 1
++        trn1            v0.2d, v4.2d, v3.2d
++        trn1            v1.2d, v2.2d, v7.2d
++        srshr           v0.8h, v0.8h, #6        // (t1 + t3 + 64) >> 7, (t2 - t4 + 64) >> 7
++        srshr           v1.8h, v1.8h, #6        // (t2 + t4 + 64) >> 7, (t1 - t3 + 64) >> 7
++        uaddw           v0.8h, v0.8h, v5.8b
++        uaddw           v1.8h, v1.8h, v6.8b
++        sqxtun          v0.8b, v0.8h
++        sqxtun          v1.8b, v1.8h
++        st1             {v0.s}[0], [x4], x1
++        st1             {v0.s}[1], [x4], x1
++        st1             {v1.s}[0], [x4], x1
++        st1             {v1.s}[1], [x4]
++        ret
++endfunc
++
++// VC-1 8x8 inverse transform, DC case
++// On entry:
++//   x0 -> array of 8-bit samples, in row-major order
++//   x1 = row stride for 8-bit sample array
++//   x2 -> 16-bit inverse transform DC coefficient
++// On exit:
++//   array at x0 updated by saturated addition of (narrowed) transformed block
++function ff_vc1_inv_trans_8x8_dc_neon, export=1
++        ldrsh           w2, [x2]
++        mov             x3, x0
++        ld1             {v0.8b}, [x0], x1
++        ld1             {v1.8b}, [x0], x1
++        ld1             {v2.8b}, [x0], x1
++        add             w2, w2, w2, lsl #1
++        ld1             {v3.8b}, [x0], x1
++        ld1             {v4.8b}, [x0], x1
++        add             w2, w2, #1
++        ld1             {v5.8b}, [x0], x1
++        asr             w2, w2, #1
++        ld1             {v6.8b}, [x0], x1
++        add             w2, w2, w2, lsl #1
++        ld1             {v7.8b}, [x0]
++        add             w0, w2, #16
++        asr             w0, w0, #5
++        dup             v16.8h, w0
++        uaddw           v0.8h, v16.8h, v0.8b
++        uaddw           v1.8h, v16.8h, v1.8b
++        uaddw           v2.8h, v16.8h, v2.8b
++        uaddw           v3.8h, v16.8h, v3.8b
++        uaddw           v4.8h, v16.8h, v4.8b
++        uaddw           v5.8h, v16.8h, v5.8b
++        sqxtun          v0.8b, v0.8h
++        uaddw           v6.8h, v16.8h, v6.8b
++        sqxtun          v1.8b, v1.8h
++        uaddw           v7.8h, v16.8h, v7.8b
++        sqxtun          v2.8b, v2.8h
++        sqxtun          v3.8b, v3.8h
++        sqxtun          v4.8b, v4.8h
++        st1             {v0.8b}, [x3], x1
++        sqxtun          v0.8b, v5.8h
++        st1             {v1.8b}, [x3], x1
++        sqxtun          v1.8b, v6.8h
++        st1             {v2.8b}, [x3], x1
++        sqxtun          v2.8b, v7.8h
++        st1             {v3.8b}, [x3], x1
++        st1             {v4.8b}, [x3], x1
++        st1             {v0.8b}, [x3], x1
++        st1             {v1.8b}, [x3], x1
++        st1             {v2.8b}, [x3]
++        ret
++endfunc
++
++// VC-1 8x4 inverse transform, DC case
++// On entry:
++//   x0 -> array of 8-bit samples, in row-major order
++//   x1 = row stride for 8-bit sample array
++//   x2 -> 16-bit inverse transform DC coefficient
++// On exit:
++//   array at x0 updated by saturated addition of (narrowed) transformed block
++function ff_vc1_inv_trans_8x4_dc_neon, export=1
++        ldrsh           w2, [x2]
++        mov             x3, x0
++        ld1             {v0.8b}, [x0], x1
++        ld1             {v1.8b}, [x0], x1
++        ld1             {v2.8b}, [x0], x1
++        add             w2, w2, w2, lsl #1
++        ld1             {v3.8b}, [x0]
++        add             w0, w2, #1
++        asr             w0, w0, #1
++        add             w0, w0, w0, lsl #4
++        add             w0, w0, #64
++        asr             w0, w0, #7
++        dup             v4.8h, w0
++        uaddw           v0.8h, v4.8h, v0.8b
++        uaddw           v1.8h, v4.8h, v1.8b
++        uaddw           v2.8h, v4.8h, v2.8b
++        uaddw           v3.8h, v4.8h, v3.8b
++        sqxtun          v0.8b, v0.8h
++        sqxtun          v1.8b, v1.8h
++        sqxtun          v2.8b, v2.8h
++        sqxtun          v3.8b, v3.8h
++        st1             {v0.8b}, [x3], x1
++        st1             {v1.8b}, [x3], x1
++        st1             {v2.8b}, [x3], x1
++        st1             {v3.8b}, [x3]
++        ret
++endfunc
++
++// VC-1 4x8 inverse transform, DC case
++// On entry:
++//   x0 -> array of 8-bit samples, in row-major order
++//   x1 = row stride for 8-bit sample array
++//   x2 -> 16-bit inverse transform DC coefficient
++// On exit:
++//   array at x0 updated by saturated addition of (narrowed) transformed block
++function ff_vc1_inv_trans_4x8_dc_neon, export=1
++        ldrsh           w2, [x2]
++        mov             x3, x0
++        ld1             {v0.s}[0], [x0], x1
++        ld1             {v1.s}[0], [x0], x1
++        ld1             {v2.s}[0], [x0], x1
++        add             w2, w2, w2, lsl #4
++        ld1             {v3.s}[0], [x0], x1
++        add             w2, w2, #4
++        asr             w2, w2, #3
++        add             w2, w2, w2, lsl #1
++        ld1             {v0.s}[1], [x0], x1
++        add             w2, w2, #16
++        asr             w2, w2, #5
++        dup             v4.8h, w2
++        ld1             {v1.s}[1], [x0], x1
++        ld1             {v2.s}[1], [x0], x1
++        ld1             {v3.s}[1], [x0]
++        uaddw           v0.8h, v4.8h, v0.8b
++        uaddw           v1.8h, v4.8h, v1.8b
++        uaddw           v2.8h, v4.8h, v2.8b
++        uaddw           v3.8h, v4.8h, v3.8b
++        sqxtun          v0.8b, v0.8h
++        sqxtun          v1.8b, v1.8h
++        sqxtun          v2.8b, v2.8h
++        sqxtun          v3.8b, v3.8h
++        st1             {v0.s}[0], [x3], x1
++        st1             {v1.s}[0], [x3], x1
++        st1             {v2.s}[0], [x3], x1
++        st1             {v3.s}[0], [x3], x1
++        st1             {v0.s}[1], [x3], x1
++        st1             {v1.s}[1], [x3], x1
++        st1             {v2.s}[1], [x3], x1
++        st1             {v3.s}[1], [x3]
++        ret
++endfunc
++
++// VC-1 4x4 inverse transform, DC case
++// On entry:
++//   x0 -> array of 8-bit samples, in row-major order
++//   x1 = row stride for 8-bit sample array
++//   x2 -> 16-bit inverse transform DC coefficient
++// On exit:
++//   array at x0 updated by saturated addition of (narrowed) transformed block
++function ff_vc1_inv_trans_4x4_dc_neon, export=1
++        ldrsh           w2, [x2]
++        mov             x3, x0
++        ld1             {v0.s}[0], [x0], x1
++        ld1             {v1.s}[0], [x0], x1
++        ld1             {v0.s}[1], [x0], x1
++        add             w2, w2, w2, lsl #4
++        ld1             {v1.s}[1], [x0]
++        add             w0, w2, #4
++        asr             w0, w0, #3
++        add             w0, w0, w0, lsl #4
++        add             w0, w0, #64
++        asr             w0, w0, #7
++        dup             v2.8h, w0
++        uaddw           v0.8h, v2.8h, v0.8b
++        uaddw           v1.8h, v2.8h, v1.8b
++        sqxtun          v0.8b, v0.8h
++        sqxtun          v1.8b, v1.8h
++        st1             {v0.s}[0], [x3], x1
++        st1             {v1.s}[0], [x3], x1
++        st1             {v0.s}[1], [x3], x1
++        st1             {v1.s}[1], [x3]
++        ret
++endfunc
++
++.align  5
++.Lcoeffs_it8:
++.quad   0x000F00090003
++.Lcoeffs_it4:
++.quad   0x0011000B0005
++.Lcoeffs:
++.quad   0x00050002
++
++// VC-1 in-loop deblocking filter for 4 pixel pairs at boundary of vertically-neighbouring blocks
++// On entry:
++//   x0 -> top-left pel of lower block
++//   x1 = row stride, bytes
++//   w2 = PQUANT bitstream parameter
++function ff_vc1_v_loop_filter4_neon, export=1
++        sub             x3, x0, w1, sxtw #2
++        ldr             d0, .Lcoeffs
++        ld1             {v1.s}[0], [x0], x1     // P5
++        ld1             {v2.s}[0], [x3], x1     // P1
++        ld1             {v3.s}[0], [x3], x1     // P2
++        ld1             {v4.s}[0], [x0], x1     // P6
++        ld1             {v5.s}[0], [x3], x1     // P3
++        ld1             {v6.s}[0], [x0], x1     // P7
++        ld1             {v7.s}[0], [x3]         // P4
++        ld1             {v16.s}[0], [x0]        // P8
++        ushll           v17.8h, v1.8b, #1       // 2*P5
++        dup             v18.8h, w2              // pq
++        ushll           v2.8h, v2.8b, #1        // 2*P1
++        uxtl            v3.8h, v3.8b            // P2
++        uxtl            v4.8h, v4.8b            // P6
++        uxtl            v19.8h, v5.8b           // P3
++        mls             v2.4h, v3.4h, v0.h[1]   // 2*P1-5*P2
++        uxtl            v3.8h, v6.8b            // P7
++        mls             v17.4h, v4.4h, v0.h[1]  // 2*P5-5*P6
++        ushll           v5.8h, v5.8b, #1        // 2*P3
++        uxtl            v6.8h, v7.8b            // P4
++        mla             v17.4h, v3.4h, v0.h[1]  // 2*P5-5*P6+5*P7
++        uxtl            v3.8h, v16.8b           // P8
++        mla             v2.4h, v19.4h, v0.h[1]  // 2*P1-5*P2+5*P3
++        uxtl            v1.8h, v1.8b            // P5
++        mls             v5.4h, v6.4h, v0.h[1]   // 2*P3-5*P4
++        mls             v17.4h, v3.4h, v0.h[0]  // 2*P5-5*P6+5*P7-2*P8
++        sub             v3.4h, v6.4h, v1.4h     // P4-P5
++        mls             v2.4h, v6.4h, v0.h[0]   // 2*P1-5*P2+5*P3-2*P4
++        mla             v5.4h, v1.4h, v0.h[1]   // 2*P3-5*P4+5*P5
++        mls             v5.4h, v4.4h, v0.h[0]   // 2*P3-5*P4+5*P5-2*P6
++        abs             v4.4h, v3.4h
++        srshr           v7.4h, v17.4h, #3
++        srshr           v2.4h, v2.4h, #3
++        sshr            v4.4h, v4.4h, #1        // clip
++        srshr           v5.4h, v5.4h, #3
++        abs             v7.4h, v7.4h            // a2
++        sshr            v3.4h, v3.4h, #8        // clip_sign
++        abs             v2.4h, v2.4h            // a1
++        cmeq            v16.4h, v4.4h, #0       // test clip == 0
++        abs             v17.4h, v5.4h           // a0
++        sshr            v5.4h, v5.4h, #8        // a0_sign
++        cmhs            v19.4h, v2.4h, v7.4h    // test a1 >= a2
++        cmhs            v18.4h, v17.4h, v18.4h  // test a0 >= pq
++        sub             v3.4h, v3.4h, v5.4h     // clip_sign - a0_sign
++        bsl             v19.8b, v7.8b, v2.8b    // a3
++        orr             v2.8b, v16.8b, v18.8b   // test clip == 0 || a0 >= pq
++        uqsub           v5.4h, v17.4h, v19.4h   // a0 >= a3 ? a0-a3 : 0  (a0 > a3 in all cases where filtering is enabled, so makes more sense to subtract this way round than the opposite and then taking the abs)
++        cmhs            v7.4h, v19.4h, v17.4h   // test a3 >= a0
++        mul             v0.4h, v5.4h, v0.h[1]   // a0 >= a3 ? 5*(a0-a3) : 0
++        orr             v5.8b, v2.8b, v7.8b     // test clip == 0 || a0 >= pq || a3 >= a0
++        mov             w0, v5.s[1]             // move to gp reg
++        ushr            v0.4h, v0.4h, #3        // a0 >= a3 ? (5*(a0-a3))>>3 : 0
++        cmhs            v5.4h, v0.4h, v4.4h
++        tbnz            w0, #0, 1f              // none of the 4 pixel pairs should be updated if this one is not filtered
++        bsl             v5.8b, v4.8b, v0.8b     // FFMIN(d, clip)
++        bic             v0.8b, v5.8b, v2.8b     // set each d to zero if it should not be filtered because clip == 0 || a0 >= pq (a3 > a0 case already zeroed by saturating sub)
++        mls             v6.4h, v0.4h, v3.4h     // invert d depending on clip_sign & a0_sign, or zero it if they match, and accumulate into P4
++        mla             v1.4h, v0.4h, v3.4h     // invert d depending on clip_sign & a0_sign, or zero it if they match, and accumulate into P5
++        sqxtun          v0.8b, v6.8h
++        sqxtun          v1.8b, v1.8h
++        st1             {v0.s}[0], [x3], x1
++        st1             {v1.s}[0], [x3]
++1:      ret
++endfunc
++
++// VC-1 in-loop deblocking filter for 4 pixel pairs at boundary of horizontally-neighbouring blocks
++// On entry:
++//   x0 -> top-left pel of right block
++//   x1 = row stride, bytes
++//   w2 = PQUANT bitstream parameter
++function ff_vc1_h_loop_filter4_neon, export=1
++        sub             x3, x0, #4              // where to start reading
++        ldr             d0, .Lcoeffs
++        ld1             {v1.8b}, [x3], x1
++        sub             x0, x0, #1              // where to start writing
++        ld1             {v2.8b}, [x3], x1
++        ld1             {v3.8b}, [x3], x1
++        ld1             {v4.8b}, [x3]
++        dup             v5.8h, w2               // pq
++        trn1            v6.8b, v1.8b, v2.8b
++        trn2            v1.8b, v1.8b, v2.8b
++        trn1            v2.8b, v3.8b, v4.8b
++        trn2            v3.8b, v3.8b, v4.8b
++        trn1            v4.4h, v6.4h, v2.4h     // P1, P5
++        trn1            v7.4h, v1.4h, v3.4h     // P2, P6
++        trn2            v2.4h, v6.4h, v2.4h     // P3, P7
++        trn2            v1.4h, v1.4h, v3.4h     // P4, P8
++        ushll           v3.8h, v4.8b, #1        // 2*P1, 2*P5
++        uxtl            v6.8h, v7.8b            // P2, P6
++        uxtl            v7.8h, v2.8b            // P3, P7
++        uxtl            v1.8h, v1.8b            // P4, P8
++        mls             v3.8h, v6.8h, v0.h[1]   // 2*P1-5*P2, 2*P5-5*P6
++        ushll           v2.8h, v2.8b, #1        // 2*P3, 2*P7
++        uxtl            v4.8h, v4.8b            // P1, P5
++        mla             v3.8h, v7.8h, v0.h[1]   // 2*P1-5*P2+5*P3, 2*P5-5*P6+5*P7
++        mov             d6, v6.d[1]             // P6
++        mls             v3.8h, v1.8h, v0.h[0]   // 2*P1-5*P2+5*P3-2*P4, 2*P5-5*P6+5*P7-2*P8
++        mov             d4, v4.d[1]             // P5
++        mls             v2.4h, v1.4h, v0.h[1]   // 2*P3-5*P4
++        mla             v2.4h, v4.4h, v0.h[1]   // 2*P3-5*P4+5*P5
++        sub             v7.4h, v1.4h, v4.4h     // P4-P5
++        mls             v2.4h, v6.4h, v0.h[0]   // 2*P3-5*P4+5*P5-2*P6
++        srshr           v3.8h, v3.8h, #3
++        abs             v6.4h, v7.4h
++        sshr            v7.4h, v7.4h, #8        // clip_sign
++        srshr           v2.4h, v2.4h, #3
++        abs             v3.8h, v3.8h            // a1, a2
++        sshr            v6.4h, v6.4h, #1        // clip
++        mov             d16, v3.d[1]            // a2
++        abs             v17.4h, v2.4h           // a0
++        cmeq            v18.4h, v6.4h, #0       // test clip == 0
++        sshr            v2.4h, v2.4h, #8        // a0_sign
++        cmhs            v19.4h, v3.4h, v16.4h   // test a1 >= a2
++        cmhs            v5.4h, v17.4h, v5.4h    // test a0 >= pq
++        sub             v2.4h, v7.4h, v2.4h     // clip_sign - a0_sign
++        bsl             v19.8b, v16.8b, v3.8b   // a3
++        orr             v3.8b, v18.8b, v5.8b    // test clip == 0 || a0 >= pq
++        uqsub           v5.4h, v17.4h, v19.4h   // a0 >= a3 ? a0-a3 : 0  (a0 > a3 in all cases where filtering is enabled, so makes more sense to subtract this way round than the opposite and then taking the abs)
++        cmhs            v7.4h, v19.4h, v17.4h   // test a3 >= a0
++        mul             v0.4h, v5.4h, v0.h[1]   // a0 >= a3 ? 5*(a0-a3) : 0
++        orr             v5.8b, v3.8b, v7.8b     // test clip == 0 || a0 >= pq || a3 >= a0
++        mov             w2, v5.s[1]             // move to gp reg
++        ushr            v0.4h, v0.4h, #3        // a0 >= a3 ? (5*(a0-a3))>>3 : 0
++        cmhs            v5.4h, v0.4h, v6.4h
++        tbnz            w2, #0, 1f              // none of the 4 pixel pairs should be updated if this one is not filtered
++        bsl             v5.8b, v6.8b, v0.8b     // FFMIN(d, clip)
++        bic             v0.8b, v5.8b, v3.8b     // set each d to zero if it should not be filtered because clip == 0 || a0 >= pq (a3 > a0 case already zeroed by saturating sub)
++        mla             v4.4h, v0.4h, v2.4h     // invert d depending on clip_sign & a0_sign, or zero it if they match, and accumulate into P5
++        mls             v1.4h, v0.4h, v2.4h     // invert d depending on clip_sign & a0_sign, or zero it if they match, and accumulate into P4
++        sqxtun          v3.8b, v4.8h
++        sqxtun          v2.8b, v1.8h
++        st2             {v2.b, v3.b}[0], [x0], x1
++        st2             {v2.b, v3.b}[1], [x0], x1
++        st2             {v2.b, v3.b}[2], [x0], x1
++        st2             {v2.b, v3.b}[3], [x0]
++1:      ret
++endfunc
++
++// VC-1 in-loop deblocking filter for 8 pixel pairs at boundary of vertically-neighbouring blocks
++// On entry:
++//   x0 -> top-left pel of lower block
++//   x1 = row stride, bytes
++//   w2 = PQUANT bitstream parameter
++function ff_vc1_v_loop_filter8_neon, export=1
++        sub             x3, x0, w1, sxtw #2
++        ldr             d0, .Lcoeffs
++        ld1             {v1.8b}, [x0], x1       // P5
++        movi            v2.2d, #0x0000ffff00000000
++        ld1             {v3.8b}, [x3], x1       // P1
++        ld1             {v4.8b}, [x3], x1       // P2
++        ld1             {v5.8b}, [x0], x1       // P6
++        ld1             {v6.8b}, [x3], x1       // P3
++        ld1             {v7.8b}, [x0], x1       // P7
++        ushll           v16.8h, v1.8b, #1       // 2*P5
++        ushll           v3.8h, v3.8b, #1        // 2*P1
++        ld1             {v17.8b}, [x3]          // P4
++        uxtl            v4.8h, v4.8b            // P2
++        ld1             {v18.8b}, [x0]          // P8
++        uxtl            v5.8h, v5.8b            // P6
++        dup             v19.8h, w2              // pq
++        uxtl            v20.8h, v6.8b           // P3
++        mls             v3.8h, v4.8h, v0.h[1]   // 2*P1-5*P2
++        uxtl            v4.8h, v7.8b            // P7
++        ushll           v6.8h, v6.8b, #1        // 2*P3
++        mls             v16.8h, v5.8h, v0.h[1]  // 2*P5-5*P6
++        uxtl            v7.8h, v17.8b           // P4
++        uxtl            v17.8h, v18.8b          // P8
++        mla             v16.8h, v4.8h, v0.h[1]  // 2*P5-5*P6+5*P7
++        uxtl            v1.8h, v1.8b            // P5
++        mla             v3.8h, v20.8h, v0.h[1]  // 2*P1-5*P2+5*P3
++        sub             v4.8h, v7.8h, v1.8h     // P4-P5
++        mls             v6.8h, v7.8h, v0.h[1]   // 2*P3-5*P4
++        mls             v16.8h, v17.8h, v0.h[0] // 2*P5-5*P6+5*P7-2*P8
++        abs             v17.8h, v4.8h
++        sshr            v4.8h, v4.8h, #8        // clip_sign
++        mls             v3.8h, v7.8h, v0.h[0]   // 2*P1-5*P2+5*P3-2*P4
++        sshr            v17.8h, v17.8h, #1      // clip
++        mla             v6.8h, v1.8h, v0.h[1]   // 2*P3-5*P4+5*P5
++        srshr           v16.8h, v16.8h, #3
++        mls             v6.8h, v5.8h, v0.h[0]   // 2*P3-5*P4+5*P5-2*P6
++        cmeq            v5.8h, v17.8h, #0       // test clip == 0
++        srshr           v3.8h, v3.8h, #3
++        abs             v16.8h, v16.8h          // a2
++        abs             v3.8h, v3.8h            // a1
++        srshr           v6.8h, v6.8h, #3
++        cmhs            v18.8h, v3.8h, v16.8h   // test a1 >= a2
++        abs             v20.8h, v6.8h           // a0
++        sshr            v6.8h, v6.8h, #8        // a0_sign
++        bsl             v18.16b, v16.16b, v3.16b // a3
++        cmhs            v3.8h, v20.8h, v19.8h   // test a0 >= pq
++        sub             v4.8h, v4.8h, v6.8h     // clip_sign - a0_sign
++        uqsub           v6.8h, v20.8h, v18.8h   // a0 >= a3 ? a0-a3 : 0  (a0 > a3 in all cases where filtering is enabled, so makes more sense to subtract this way round than the opposite and then taking the abs)
++        cmhs            v16.8h, v18.8h, v20.8h  // test a3 >= a0
++        orr             v3.16b, v5.16b, v3.16b  // test clip == 0 || a0 >= pq
++        mul             v0.8h, v6.8h, v0.h[1]   // a0 >= a3 ? 5*(a0-a3) : 0
++        orr             v5.16b, v3.16b, v16.16b // test clip == 0 || a0 >= pq || a3 >= a0
++        cmtst           v2.2d, v5.2d, v2.2d     // if 2nd of each group of is not filtered, then none of the others in the group should be either
++        mov             w0, v5.s[1]             // move to gp reg
++        ushr            v0.8h, v0.8h, #3        // a0 >= a3 ? (5*(a0-a3))>>3 : 0
++        mov             w2, v5.s[3]
++        orr             v2.16b, v3.16b, v2.16b
++        cmhs            v3.8h, v0.8h, v17.8h
++        and             w0, w0, w2
++        bsl             v3.16b, v17.16b, v0.16b // FFMIN(d, clip)
++        tbnz            w0, #0, 1f              // none of the 8 pixel pairs should be updated in this case
++        bic             v0.16b, v3.16b, v2.16b  // set each d to zero if it should not be filtered
++        mls             v7.8h, v0.8h, v4.8h     // invert d depending on clip_sign & a0_sign, or zero it if they match, and accumulate into P4
++        mla             v1.8h, v0.8h, v4.8h     // invert d depending on clip_sign & a0_sign, or zero it if they match, and accumulate into P5
++        sqxtun          v0.8b, v7.8h
++        sqxtun          v1.8b, v1.8h
++        st1             {v0.8b}, [x3], x1
++        st1             {v1.8b}, [x3]
++1:      ret
++endfunc
++
++// VC-1 in-loop deblocking filter for 8 pixel pairs at boundary of horizontally-neighbouring blocks
++// On entry:
++//   x0 -> top-left pel of right block
++//   x1 = row stride, bytes
++//   w2 = PQUANT bitstream parameter
++function ff_vc1_h_loop_filter8_neon, export=1
++        sub             x3, x0, #4              // where to start reading
++        ldr             d0, .Lcoeffs
++        ld1             {v1.8b}, [x3], x1       // P1[0], P2[0]...
++        sub             x0, x0, #1              // where to start writing
++        ld1             {v2.8b}, [x3], x1
++        add             x4, x0, x1, lsl #2
++        ld1             {v3.8b}, [x3], x1
++        ld1             {v4.8b}, [x3], x1
++        ld1             {v5.8b}, [x3], x1
++        ld1             {v6.8b}, [x3], x1
++        ld1             {v7.8b}, [x3], x1
++        trn1            v16.8b, v1.8b, v2.8b    // P1[0], P1[1], P3[0]...
++        ld1             {v17.8b}, [x3]
++        trn2            v1.8b, v1.8b, v2.8b     // P2[0], P2[1], P4[0]...
++        trn1            v2.8b, v3.8b, v4.8b     // P1[2], P1[3], P3[2]...
++        trn2            v3.8b, v3.8b, v4.8b     // P2[2], P2[3], P4[2]...
++        dup             v4.8h, w2               // pq
++        trn1            v18.8b, v5.8b, v6.8b    // P1[4], P1[5], P3[4]...
++        trn2            v5.8b, v5.8b, v6.8b     // P2[4], P2[5], P4[4]...
++        trn1            v6.4h, v16.4h, v2.4h    // P1[0], P1[1], P1[2], P1[3], P5[0]...
++        trn1            v19.4h, v1.4h, v3.4h    // P2[0], P2[1], P2[2], P2[3], P6[0]...
++        trn1            v20.8b, v7.8b, v17.8b   // P1[6], P1[7], P3[6]...
++        trn2            v7.8b, v7.8b, v17.8b    // P2[6], P2[7], P4[6]...
++        trn2            v2.4h, v16.4h, v2.4h    // P3[0], P3[1], P3[2], P3[3], P7[0]...
++        trn2            v1.4h, v1.4h, v3.4h     // P4[0], P4[1], P4[2], P4[3], P8[0]...
++        trn1            v3.4h, v18.4h, v20.4h   // P1[4], P1[5], P1[6], P1[7], P5[4]...
++        trn1            v16.4h, v5.4h, v7.4h    // P2[4], P2[5], P2[6], P2[7], P6[4]...
++        trn2            v17.4h, v18.4h, v20.4h  // P3[4], P3[5], P3[6], P3[7], P7[4]...
++        trn2            v5.4h, v5.4h, v7.4h     // P4[4], P4[5], P4[6], P4[7], P8[4]...
++        trn1            v7.2s, v6.2s, v3.2s     // P1
++        trn1            v18.2s, v19.2s, v16.2s  // P2
++        trn2            v3.2s, v6.2s, v3.2s     // P5
++        trn2            v6.2s, v19.2s, v16.2s   // P6
++        trn1            v16.2s, v2.2s, v17.2s   // P3
++        trn2            v2.2s, v2.2s, v17.2s    // P7
++        ushll           v7.8h, v7.8b, #1        // 2*P1
++        trn1            v17.2s, v1.2s, v5.2s    // P4
++        ushll           v19.8h, v3.8b, #1       // 2*P5
++        trn2            v1.2s, v1.2s, v5.2s     // P8
++        uxtl            v5.8h, v18.8b           // P2
++        uxtl            v6.8h, v6.8b            // P6
++        uxtl            v18.8h, v16.8b          // P3
++        mls             v7.8h, v5.8h, v0.h[1]   // 2*P1-5*P2
++        uxtl            v2.8h, v2.8b            // P7
++        ushll           v5.8h, v16.8b, #1       // 2*P3
++        mls             v19.8h, v6.8h, v0.h[1]  // 2*P5-5*P6
++        uxtl            v16.8h, v17.8b          // P4
++        uxtl            v1.8h, v1.8b            // P8
++        mla             v19.8h, v2.8h, v0.h[1]  // 2*P5-5*P6+5*P7
++        uxtl            v2.8h, v3.8b            // P5
++        mla             v7.8h, v18.8h, v0.h[1]  // 2*P1-5*P2+5*P3
++        sub             v3.8h, v16.8h, v2.8h    // P4-P5
++        mls             v5.8h, v16.8h, v0.h[1]  // 2*P3-5*P4
++        mls             v19.8h, v1.8h, v0.h[0]  // 2*P5-5*P6+5*P7-2*P8
++        abs             v1.8h, v3.8h
++        sshr            v3.8h, v3.8h, #8        // clip_sign
++        mls             v7.8h, v16.8h, v0.h[0]  // 2*P1-5*P2+5*P3-2*P4
++        sshr            v1.8h, v1.8h, #1        // clip
++        mla             v5.8h, v2.8h, v0.h[1]   // 2*P3-5*P4+5*P5
++        srshr           v17.8h, v19.8h, #3
++        mls             v5.8h, v6.8h, v0.h[0]   // 2*P3-5*P4+5*P5-2*P6
++        cmeq            v6.8h, v1.8h, #0        // test clip == 0
++        srshr           v7.8h, v7.8h, #3
++        abs             v17.8h, v17.8h          // a2
++        abs             v7.8h, v7.8h            // a1
++        srshr           v5.8h, v5.8h, #3
++        cmhs            v18.8h, v7.8h, v17.8h   // test a1 >= a2
++        abs             v19.8h, v5.8h           // a0
++        sshr            v5.8h, v5.8h, #8        // a0_sign
++        bsl             v18.16b, v17.16b, v7.16b // a3
++        cmhs            v4.8h, v19.8h, v4.8h    // test a0 >= pq
++        sub             v3.8h, v3.8h, v5.8h     // clip_sign - a0_sign
++        uqsub           v5.8h, v19.8h, v18.8h   // a0 >= a3 ? a0-a3 : 0  (a0 > a3 in all cases where filtering is enabled, so makes more sense to subtract this way round than the opposite and then taking the abs)
++        cmhs            v7.8h, v18.8h, v19.8h   // test a3 >= a0
++        orr             v4.16b, v6.16b, v4.16b  // test clip == 0 || a0 >= pq
++        mul             v0.8h, v5.8h, v0.h[1]   // a0 >= a3 ? 5*(a0-a3) : 0
++        orr             v5.16b, v4.16b, v7.16b  // test clip == 0 || a0 >= pq || a3 >= a0
++        mov             w2, v5.s[1]             // move to gp reg
++        ushr            v0.8h, v0.8h, #3        // a0 >= a3 ? (5*(a0-a3))>>3 : 0
++        mov             w3, v5.s[3]
++        cmhs            v5.8h, v0.8h, v1.8h
++        and             w5, w2, w3
++        bsl             v5.16b, v1.16b, v0.16b  // FFMIN(d, clip)
++        tbnz            w5, #0, 2f              // none of the 8 pixel pairs should be updated in this case
++        bic             v0.16b, v5.16b, v4.16b  // set each d to zero if it should not be filtered because clip == 0 || a0 >= pq (a3 > a0 case already zeroed by saturating sub)
++        mla             v2.8h, v0.8h, v3.8h     // invert d depending on clip_sign & a0_sign, or zero it if they match, and accumulate into P5
++        mls             v16.8h, v0.8h, v3.8h    // invert d depending on clip_sign & a0_sign, or zero it if they match, and accumulate into P4
++        sqxtun          v1.8b, v2.8h
++        sqxtun          v0.8b, v16.8h
++        tbnz            w2, #0, 1f              // none of the first 4 pixel pairs should be updated if so
++        st2             {v0.b, v1.b}[0], [x0], x1
++        st2             {v0.b, v1.b}[1], [x0], x1
++        st2             {v0.b, v1.b}[2], [x0], x1
++        st2             {v0.b, v1.b}[3], [x0]
++1:      tbnz            w3, #0, 2f              // none of the second 4 pixel pairs should be updated if so
++        st2             {v0.b, v1.b}[4], [x4], x1
++        st2             {v0.b, v1.b}[5], [x4], x1
++        st2             {v0.b, v1.b}[6], [x4], x1
++        st2             {v0.b, v1.b}[7], [x4]
++2:      ret
++endfunc
++
++// VC-1 in-loop deblocking filter for 16 pixel pairs at boundary of vertically-neighbouring blocks
++// On entry:
++//   x0 -> top-left pel of lower block
++//   x1 = row stride, bytes
++//   w2 = PQUANT bitstream parameter
++function ff_vc1_v_loop_filter16_neon, export=1
++        sub             x3, x0, w1, sxtw #2
++        ldr             d0, .Lcoeffs
++        ld1             {v1.16b}, [x0], x1      // P5
++        movi            v2.2d, #0x0000ffff00000000
++        ld1             {v3.16b}, [x3], x1      // P1
++        ld1             {v4.16b}, [x3], x1      // P2
++        ld1             {v5.16b}, [x0], x1      // P6
++        ld1             {v6.16b}, [x3], x1      // P3
++        ld1             {v7.16b}, [x0], x1      // P7
++        ushll           v16.8h, v1.8b, #1       // 2*P5[0..7]
++        ushll           v17.8h, v3.8b, #1       // 2*P1[0..7]
++        ld1             {v18.16b}, [x3]         // P4
++        uxtl            v19.8h, v4.8b           // P2[0..7]
++        ld1             {v20.16b}, [x0]         // P8
++        uxtl            v21.8h, v5.8b           // P6[0..7]
++        dup             v22.8h, w2              // pq
++        ushll2          v3.8h, v3.16b, #1       // 2*P1[8..15]
++        mls             v17.8h, v19.8h, v0.h[1] // 2*P1[0..7]-5*P2[0..7]
++        ushll2          v19.8h, v1.16b, #1      // 2*P5[8..15]
++        uxtl2           v4.8h, v4.16b           // P2[8..15]
++        mls             v16.8h, v21.8h, v0.h[1] // 2*P5[0..7]-5*P6[0..7]
++        uxtl2           v5.8h, v5.16b           // P6[8..15]
++        uxtl            v23.8h, v6.8b           // P3[0..7]
++        uxtl            v24.8h, v7.8b           // P7[0..7]
++        mls             v3.8h, v4.8h, v0.h[1]   // 2*P1[8..15]-5*P2[8..15]
++        ushll           v4.8h, v6.8b, #1        // 2*P3[0..7]
++        uxtl            v25.8h, v18.8b          // P4[0..7]
++        mls             v19.8h, v5.8h, v0.h[1]  // 2*P5[8..15]-5*P6[8..15]
++        uxtl2           v26.8h, v6.16b          // P3[8..15]
++        mla             v17.8h, v23.8h, v0.h[1] // 2*P1[0..7]-5*P2[0..7]+5*P3[0..7]
++        uxtl2           v7.8h, v7.16b           // P7[8..15]
++        ushll2          v6.8h, v6.16b, #1       // 2*P3[8..15]
++        mla             v16.8h, v24.8h, v0.h[1] // 2*P5[0..7]-5*P6[0..7]+5*P7[0..7]
++        uxtl2           v18.8h, v18.16b         // P4[8..15]
++        uxtl            v23.8h, v20.8b          // P8[0..7]
++        mls             v4.8h, v25.8h, v0.h[1]  // 2*P3[0..7]-5*P4[0..7]
++        uxtl            v24.8h, v1.8b           // P5[0..7]
++        uxtl2           v20.8h, v20.16b         // P8[8..15]
++        mla             v3.8h, v26.8h, v0.h[1]  // 2*P1[8..15]-5*P2[8..15]+5*P3[8..15]
++        uxtl2           v1.8h, v1.16b           // P5[8..15]
++        sub             v26.8h, v25.8h, v24.8h  // P4[0..7]-P5[0..7]
++        mla             v19.8h, v7.8h, v0.h[1]  // 2*P5[8..15]-5*P6[8..15]+5*P7[8..15]
++        sub             v7.8h, v18.8h, v1.8h    // P4[8..15]-P5[8..15]
++        mls             v6.8h, v18.8h, v0.h[1]  // 2*P3[8..15]-5*P4[8..15]
++        abs             v27.8h, v26.8h
++        sshr            v26.8h, v26.8h, #8      // clip_sign[0..7]
++        mls             v17.8h, v25.8h, v0.h[0] // 2*P1[0..7]-5*P2[0..7]+5*P3[0..7]-2*P4[0..7]
++        abs             v28.8h, v7.8h
++        sshr            v27.8h, v27.8h, #1      // clip[0..7]
++        mls             v16.8h, v23.8h, v0.h[0] // 2*P5[0..7]-5*P6[0..7]+5*P7[0..7]-2*P8[0..7]
++        sshr            v7.8h, v7.8h, #8        // clip_sign[8..15]
++        sshr            v23.8h, v28.8h, #1      // clip[8..15]
++        mla             v4.8h, v24.8h, v0.h[1]  // 2*P3[0..7]-5*P4[0..7]+5*P5[0..7]
++        cmeq            v28.8h, v27.8h, #0      // test clip[0..7] == 0
++        srshr           v17.8h, v17.8h, #3
++        mls             v3.8h, v18.8h, v0.h[0]  // 2*P1[8..15]-5*P2[8..15]+5*P3[8..15]-2*P4[8..15]
++        cmeq            v29.8h, v23.8h, #0      // test clip[8..15] == 0
++        srshr           v16.8h, v16.8h, #3
++        mls             v19.8h, v20.8h, v0.h[0] // 2*P5[8..15]-5*P6[8..15]+5*P7[8..15]-2*P8[8..15]
++        abs             v17.8h, v17.8h          // a1[0..7]
++        mla             v6.8h, v1.8h, v0.h[1]   // 2*P3[8..15]-5*P4[8..15]+5*P5[8..15]
++        srshr           v3.8h, v3.8h, #3
++        mls             v4.8h, v21.8h, v0.h[0]  // 2*P3[0..7]-5*P4[0..7]+5*P5[0..7]-2*P6[0..7]
++        abs             v16.8h, v16.8h          // a2[0..7]
++        srshr           v19.8h, v19.8h, #3
++        mls             v6.8h, v5.8h, v0.h[0]   // 2*P3[8..15]-5*P4[8..15]+5*P5[8..15]-2*P6[8..15]
++        cmhs            v5.8h, v17.8h, v16.8h   // test a1[0..7] >= a2[0..7]
++        abs             v3.8h, v3.8h            // a1[8..15]
++        srshr           v4.8h, v4.8h, #3
++        abs             v19.8h, v19.8h          // a2[8..15]
++        bsl             v5.16b, v16.16b, v17.16b // a3[0..7]
++        srshr           v6.8h, v6.8h, #3
++        cmhs            v16.8h, v3.8h, v19.8h   // test a1[8..15] >= a2[8.15]
++        abs             v17.8h, v4.8h           // a0[0..7]
++        sshr            v4.8h, v4.8h, #8        // a0_sign[0..7]
++        bsl             v16.16b, v19.16b, v3.16b // a3[8..15]
++        uqsub           v3.8h, v17.8h, v5.8h    // a0[0..7] >= a3[0..7] ? a0[0..7]-a3[0..7] : 0  (a0 > a3 in all cases where filtering is enabled, so makes more sense to subtract this way round than the opposite and then taking the abs)
++        abs             v19.8h, v6.8h           // a0[8..15]
++        cmhs            v20.8h, v17.8h, v22.8h  // test a0[0..7] >= pq
++        cmhs            v5.8h, v5.8h, v17.8h    // test a3[0..7] >= a0[0..7]
++        sub             v4.8h, v26.8h, v4.8h    // clip_sign[0..7] - a0_sign[0..7]
++        sshr            v6.8h, v6.8h, #8        // a0_sign[8..15]
++        mul             v3.8h, v3.8h, v0.h[1]   // a0[0..7] >= a3[0..7] ? 5*(a0[0..7]-a3[0..7]) : 0
++        uqsub           v17.8h, v19.8h, v16.8h  // a0[8..15] >= a3[8..15] ? a0[8..15]-a3[8..15] : 0  (a0 > a3 in all cases where filtering is enabled, so makes more sense to subtract this way round than the opposite and then taking the abs)
++        orr             v20.16b, v28.16b, v20.16b // test clip[0..7] == 0 || a0[0..7] >= pq
++        cmhs            v21.8h, v19.8h, v22.8h  // test a0[8..15] >= pq
++        cmhs            v16.8h, v16.8h, v19.8h  // test a3[8..15] >= a0[8..15]
++        mul             v0.8h, v17.8h, v0.h[1]  // a0[8..15] >= a3[8..15] ? 5*(a0[8..15]-a3[8..15]) : 0
++        sub             v6.8h, v7.8h, v6.8h     // clip_sign[8..15] - a0_sign[8..15]
++        orr             v5.16b, v20.16b, v5.16b // test clip[0..7] == 0 || a0[0..7] >= pq || a3[0..7] >= a0[0..7]
++        ushr            v3.8h, v3.8h, #3        // a0[0..7] >= a3[0..7] ? (5*(a0[0..7]-a3[0..7]))>>3 : 0
++        orr             v7.16b, v29.16b, v21.16b // test clip[8..15] == 0 || a0[8..15] >= pq
++        cmtst           v17.2d, v5.2d, v2.2d    // if 2nd of each group of is not filtered, then none of the others in the group should be either
++        mov             w0, v5.s[1]             // move to gp reg
++        cmhs            v19.8h, v3.8h, v27.8h
++        ushr            v0.8h, v0.8h, #3        // a0[8..15] >= a3[8..15] ? (5*(a0[8..15]-a3[8..15]))>>3 : 0
++        mov             w2, v5.s[3]
++        orr             v5.16b, v7.16b, v16.16b // test clip[8..15] == 0 || a0[8..15] >= pq || a3[8..15] >= a0[8..15]
++        orr             v16.16b, v20.16b, v17.16b
++        bsl             v19.16b, v27.16b, v3.16b // FFMIN(d[0..7], clip[0..7])
++        cmtst           v2.2d, v5.2d, v2.2d
++        cmhs            v3.8h, v0.8h, v23.8h
++        mov             w4, v5.s[1]
++        mov             w5, v5.s[3]
++        and             w0, w0, w2
++        bic             v5.16b, v19.16b, v16.16b // set each d[0..7] to zero if it should not be filtered because clip[0..7] == 0 || a0[0..7] >= pq (a3 > a0 case already zeroed by saturating sub)
++        orr             v2.16b, v7.16b, v2.16b
++        bsl             v3.16b, v23.16b, v0.16b // FFMIN(d[8..15], clip[8..15])
++        mls             v25.8h, v5.8h, v4.8h    // invert d[0..7] depending on clip_sign[0..7] & a0_sign[0..7], or zero it if they match, and accumulate into P4[0..7]
++        and             w2, w4, w5
++        bic             v0.16b, v3.16b, v2.16b  // set each d[8..15] to zero if it should not be filtered because clip[8..15] == 0 || a0[8..15] >= pq (a3 > a0 case already zeroed by saturating sub)
++        mla             v24.8h, v5.8h, v4.8h    // invert d[0..7] depending on clip_sign[0..7] & a0_sign[0..7], or zero it if they match, and accumulate into P5[0..7]
++        and             w0, w0, w2
++        mls             v18.8h, v0.8h, v6.8h    // invert d[8..15] depending on clip_sign[8..15] & a0_sign[8..15], or zero it if they match, and accumulate into P4[8..15]
++        sqxtun          v2.8b, v25.8h
++        tbnz            w0, #0, 1f              // none of the 16 pixel pairs should be updated in this case
++        mla             v1.8h, v0.8h, v6.8h     // invert d[8..15] depending on clip_sign[8..15] & a0_sign[8..15], or zero it if they match, and accumulate into P5[8..15]
++        sqxtun          v0.8b, v24.8h
++        sqxtun2         v2.16b, v18.8h
++        sqxtun2         v0.16b, v1.8h
++        st1             {v2.16b}, [x3], x1
++        st1             {v0.16b}, [x3]
++1:      ret
++endfunc
++
++// VC-1 in-loop deblocking filter for 16 pixel pairs at boundary of horizontally-neighbouring blocks
++// On entry:
++//   x0 -> top-left pel of right block
++//   x1 = row stride, bytes
++//   w2 = PQUANT bitstream parameter
++function ff_vc1_h_loop_filter16_neon, export=1
++        sub             x3, x0, #4              // where to start reading
++        ldr             d0, .Lcoeffs
++        ld1             {v1.8b}, [x3], x1       // P1[0], P2[0]...
++        sub             x0, x0, #1              // where to start writing
++        ld1             {v2.8b}, [x3], x1
++        add             x4, x0, x1, lsl #3
++        ld1             {v3.8b}, [x3], x1
++        add             x5, x0, x1, lsl #2
++        ld1             {v4.8b}, [x3], x1
++        add             x6, x4, x1, lsl #2
++        ld1             {v5.8b}, [x3], x1
++        ld1             {v6.8b}, [x3], x1
++        ld1             {v7.8b}, [x3], x1
++        trn1            v16.8b, v1.8b, v2.8b    // P1[0], P1[1], P3[0]...
++        ld1             {v17.8b}, [x3], x1
++        trn2            v1.8b, v1.8b, v2.8b     // P2[0], P2[1], P4[0]...
++        ld1             {v2.8b}, [x3], x1
++        trn1            v18.8b, v3.8b, v4.8b    // P1[2], P1[3], P3[2]...
++        ld1             {v19.8b}, [x3], x1
++        trn2            v3.8b, v3.8b, v4.8b     // P2[2], P2[3], P4[2]...
++        ld1             {v4.8b}, [x3], x1
++        trn1            v20.8b, v5.8b, v6.8b    // P1[4], P1[5], P3[4]...
++        ld1             {v21.8b}, [x3], x1
++        trn2            v5.8b, v5.8b, v6.8b     // P2[4], P2[5], P4[4]...
++        ld1             {v6.8b}, [x3], x1
++        trn1            v22.8b, v7.8b, v17.8b   // P1[6], P1[7], P3[6]...
++        ld1             {v23.8b}, [x3], x1
++        trn2            v7.8b, v7.8b, v17.8b    // P2[6], P2[7], P4[6]...
++        ld1             {v17.8b}, [x3], x1
++        trn1            v24.8b, v2.8b, v19.8b   // P1[8], P1[9], P3[8]...
++        ld1             {v25.8b}, [x3]
++        trn2            v2.8b, v2.8b, v19.8b    // P2[8], P2[9], P4[8]...
++        trn1            v19.4h, v16.4h, v18.4h  // P1[0], P1[1], P1[2], P1[3], P5[0]...
++        trn1            v26.8b, v4.8b, v21.8b   // P1[10], P1[11], P3[10]...
++        trn2            v4.8b, v4.8b, v21.8b    // P2[10], P2[11], P4[10]...
++        trn1            v21.4h, v1.4h, v3.4h    // P2[0], P2[1], P2[2], P2[3], P6[0]...
++        trn1            v27.4h, v20.4h, v22.4h  // P1[4], P1[5], P1[6], P1[7], P5[4]...
++        trn1            v28.8b, v6.8b, v23.8b   // P1[12], P1[13], P3[12]...
++        trn2            v6.8b, v6.8b, v23.8b    // P2[12], P2[13], P4[12]...
++        trn1            v23.4h, v5.4h, v7.4h    // P2[4], P2[5], P2[6], P2[7], P6[4]...
++        trn1            v29.4h, v24.4h, v26.4h  // P1[8], P1[9], P1[10], P1[11], P5[8]...
++        trn1            v30.8b, v17.8b, v25.8b  // P1[14], P1[15], P3[14]...
++        trn2            v17.8b, v17.8b, v25.8b  // P2[14], P2[15], P4[14]...
++        trn1            v25.4h, v2.4h, v4.4h    // P2[8], P2[9], P2[10], P2[11], P6[8]...
++        trn1            v31.2s, v19.2s, v27.2s  // P1[0..7]
++        trn2            v19.2s, v19.2s, v27.2s  // P5[0..7]
++        trn1            v27.2s, v21.2s, v23.2s  // P2[0..7]
++        trn2            v21.2s, v21.2s, v23.2s  // P6[0..7]
++        trn1            v23.4h, v28.4h, v30.4h  // P1[12], P1[13], P1[14], P1[15], P5[12]...
++        trn2            v16.4h, v16.4h, v18.4h  // P3[0], P3[1], P3[2], P3[3], P7[0]...
++        trn1            v18.4h, v6.4h, v17.4h   // P2[12], P2[13], P2[14], P2[15], P6[12]...
++        trn2            v20.4h, v20.4h, v22.4h  // P3[4], P3[5], P3[6], P3[7], P7[4]...
++        trn2            v22.4h, v24.4h, v26.4h  // P3[8], P3[9], P3[10], P3[11], P7[8]...
++        trn1            v24.2s, v29.2s, v23.2s  // P1[8..15]
++        trn2            v23.2s, v29.2s, v23.2s  // P5[8..15]
++        trn1            v26.2s, v25.2s, v18.2s  // P2[8..15]
++        trn2            v18.2s, v25.2s, v18.2s  // P6[8..15]
++        trn2            v25.4h, v28.4h, v30.4h  // P3[12], P3[13], P3[14], P3[15], P7[12]...
++        trn2            v1.4h, v1.4h, v3.4h     // P4[0], P4[1], P4[2], P4[3], P8[0]...
++        trn2            v3.4h, v5.4h, v7.4h     // P4[4], P4[5], P4[6], P4[7], P8[4]...
++        trn2            v2.4h, v2.4h, v4.4h     // P4[8], P4[9], P4[10], P4[11], P8[8]...
++        trn2            v4.4h, v6.4h, v17.4h    // P4[12], P4[13], P4[14], P4[15], P8[12]...
++        ushll           v5.8h, v31.8b, #1       // 2*P1[0..7]
++        ushll           v6.8h, v19.8b, #1       // 2*P5[0..7]
++        trn1            v7.2s, v16.2s, v20.2s   // P3[0..7]
++        uxtl            v17.8h, v27.8b          // P2[0..7]
++        trn2            v16.2s, v16.2s, v20.2s  // P7[0..7]
++        uxtl            v20.8h, v21.8b          // P6[0..7]
++        trn1            v21.2s, v22.2s, v25.2s  // P3[8..15]
++        ushll           v24.8h, v24.8b, #1      // 2*P1[8..15]
++        trn2            v22.2s, v22.2s, v25.2s  // P7[8..15]
++        ushll           v25.8h, v23.8b, #1      // 2*P5[8..15]
++        trn1            v27.2s, v1.2s, v3.2s    // P4[0..7]
++        uxtl            v26.8h, v26.8b          // P2[8..15]
++        mls             v5.8h, v17.8h, v0.h[1]  // 2*P1[0..7]-5*P2[0..7]
++        uxtl            v17.8h, v18.8b          // P6[8..15]
++        mls             v6.8h, v20.8h, v0.h[1]  // 2*P5[0..7]-5*P6[0..7]
++        trn1            v18.2s, v2.2s, v4.2s    // P4[8..15]
++        uxtl            v28.8h, v7.8b           // P3[0..7]
++        mls             v24.8h, v26.8h, v0.h[1] // 2*P1[8..15]-5*P2[8..15]
++        uxtl            v16.8h, v16.8b          // P7[0..7]
++        uxtl            v26.8h, v21.8b          // P3[8..15]
++        mls             v25.8h, v17.8h, v0.h[1] // 2*P5[8..15]-5*P6[8..15]
++        uxtl            v22.8h, v22.8b          // P7[8..15]
++        ushll           v7.8h, v7.8b, #1        // 2*P3[0..7]
++        uxtl            v27.8h, v27.8b          // P4[0..7]
++        trn2            v1.2s, v1.2s, v3.2s     // P8[0..7]
++        ushll           v3.8h, v21.8b, #1       // 2*P3[8..15]
++        trn2            v2.2s, v2.2s, v4.2s     // P8[8..15]
++        uxtl            v4.8h, v18.8b           // P4[8..15]
++        mla             v5.8h, v28.8h, v0.h[1]  // 2*P1[0..7]-5*P2[0..7]+5*P3[0..7]
++        uxtl            v1.8h, v1.8b            // P8[0..7]
++        mla             v6.8h, v16.8h, v0.h[1]  // 2*P5[0..7]-5*P6[0..7]+5*P7[0..7]
++        uxtl            v2.8h, v2.8b            // P8[8..15]
++        uxtl            v16.8h, v19.8b          // P5[0..7]
++        mla             v24.8h, v26.8h, v0.h[1] // 2*P1[8..15]-5*P2[8..15]+5*P3[8..15]
++        uxtl            v18.8h, v23.8b          // P5[8..15]
++        dup             v19.8h, w2              // pq
++        mla             v25.8h, v22.8h, v0.h[1] // 2*P5[8..15]-5*P6[8..15]+5*P7[8..15]
++        sub             v21.8h, v27.8h, v16.8h  // P4[0..7]-P5[0..7]
++        sub             v22.8h, v4.8h, v18.8h   // P4[8..15]-P5[8..15]
++        mls             v7.8h, v27.8h, v0.h[1]  // 2*P3[0..7]-5*P4[0..7]
++        abs             v23.8h, v21.8h
++        mls             v3.8h, v4.8h, v0.h[1]   // 2*P3[8..15]-5*P4[8..15]
++        abs             v26.8h, v22.8h
++        sshr            v21.8h, v21.8h, #8      // clip_sign[0..7]
++        mls             v5.8h, v27.8h, v0.h[0]  // 2*P1[0..7]-5*P2[0..7]+5*P3[0..7]-2*P4[0..7]
++        sshr            v23.8h, v23.8h, #1      // clip[0..7]
++        sshr            v26.8h, v26.8h, #1      // clip[8..15]
++        mls             v6.8h, v1.8h, v0.h[0]   // 2*P5[0..7]-5*P6[0..7]+5*P7[0..7]-2*P8[0..7]
++        sshr            v1.8h, v22.8h, #8       // clip_sign[8..15]
++        cmeq            v22.8h, v23.8h, #0      // test clip[0..7] == 0
++        mls             v24.8h, v4.8h, v0.h[0]  // 2*P1[8..15]-5*P2[8..15]+5*P3[8..15]-2*P4[8..15]
++        cmeq            v28.8h, v26.8h, #0      // test clip[8..15] == 0
++        srshr           v5.8h, v5.8h, #3
++        mls             v25.8h, v2.8h, v0.h[0]  // 2*P5[8..15]-5*P6[8..15]+5*P7[8..15]-2*P8[8..15]
++        srshr           v2.8h, v6.8h, #3
++        mla             v7.8h, v16.8h, v0.h[1]  // 2*P3[0..7]-5*P4[0..7]+5*P5[0..7]
++        srshr           v6.8h, v24.8h, #3
++        mla             v3.8h, v18.8h, v0.h[1]  // 2*P3[8..15]-5*P4[8..15]+5*P5[8..15]
++        abs             v5.8h, v5.8h            // a1[0..7]
++        srshr           v24.8h, v25.8h, #3
++        mls             v3.8h, v17.8h, v0.h[0]  // 2*P3[8..15]-5*P4[8..15]+5*P5[8..15]-2*P6[8..15]
++        abs             v2.8h, v2.8h            // a2[0..7]
++        abs             v6.8h, v6.8h            // a1[8..15]
++        mls             v7.8h, v20.8h, v0.h[0]  // 2*P3[0..7]-5*P4[0..7]+5*P5[0..7]-2*P6[0..7]
++        abs             v17.8h, v24.8h          // a2[8..15]
++        cmhs            v20.8h, v5.8h, v2.8h    // test a1[0..7] >= a2[0..7]
++        srshr           v3.8h, v3.8h, #3
++        cmhs            v24.8h, v6.8h, v17.8h   // test a1[8..15] >= a2[8.15]
++        srshr           v7.8h, v7.8h, #3
++        bsl             v20.16b, v2.16b, v5.16b // a3[0..7]
++        abs             v2.8h, v3.8h            // a0[8..15]
++        sshr            v3.8h, v3.8h, #8        // a0_sign[8..15]
++        bsl             v24.16b, v17.16b, v6.16b // a3[8..15]
++        abs             v5.8h, v7.8h            // a0[0..7]
++        sshr            v6.8h, v7.8h, #8        // a0_sign[0..7]
++        cmhs            v7.8h, v2.8h, v19.8h    // test a0[8..15] >= pq
++        sub             v1.8h, v1.8h, v3.8h     // clip_sign[8..15] - a0_sign[8..15]
++        uqsub           v3.8h, v2.8h, v24.8h    // a0[8..15] >= a3[8..15] ? a0[8..15]-a3[8..15] : 0  (a0 > a3 in all cases where filtering is enabled, so makes more sense to subtract this way round than the opposite and then taking the abs)
++        cmhs            v2.8h, v24.8h, v2.8h    // test a3[8..15] >= a0[8..15]
++        uqsub           v17.8h, v5.8h, v20.8h   // a0[0..7] >= a3[0..7] ? a0[0..7]-a3[0..7] : 0  (a0 > a3 in all cases where filtering is enabled, so makes more sense to subtract this way round than the opposite and then taking the abs)
++        cmhs            v19.8h, v5.8h, v19.8h   // test a0[0..7] >= pq
++        orr             v7.16b, v28.16b, v7.16b // test clip[8..15] == 0 || a0[8..15] >= pq
++        sub             v6.8h, v21.8h, v6.8h    // clip_sign[0..7] - a0_sign[0..7]
++        mul             v3.8h, v3.8h, v0.h[1]   // a0[8..15] >= a3[8..15] ? 5*(a0[8..15]-a3[8..15]) : 0
++        cmhs            v5.8h, v20.8h, v5.8h    // test a3[0..7] >= a0[0..7]
++        orr             v19.16b, v22.16b, v19.16b // test clip[0..7] == 0 || a0[0..7] >= pq
++        mul             v0.8h, v17.8h, v0.h[1]  // a0[0..7] >= a3[0..7] ? 5*(a0[0..7]-a3[0..7]) : 0
++        orr             v2.16b, v7.16b, v2.16b  // test clip[8..15] == 0 || a0[8..15] >= pq || a3[8..15] >= a0[8..15]
++        orr             v5.16b, v19.16b, v5.16b // test clip[0..7] == 0 || a0[0..7] >= pq || a3[0..7] >= a0[0..7]
++        ushr            v3.8h, v3.8h, #3        // a0[8..15] >= a3[8..15] ? (5*(a0[8..15]-a3[8..15]))>>3 : 0
++        mov             w7, v2.s[1]
++        mov             w8, v2.s[3]
++        ushr            v0.8h, v0.8h, #3        // a0[0..7] >= a3[0..7] ? (5*(a0[0..7]-a3[0..7]))>>3 : 0
++        mov             w2, v5.s[1]             // move to gp reg
++        cmhs            v2.8h, v3.8h, v26.8h
++        mov             w3, v5.s[3]
++        cmhs            v5.8h, v0.8h, v23.8h
++        bsl             v2.16b, v26.16b, v3.16b // FFMIN(d[8..15], clip[8..15])
++        and             w9, w7, w8
++        bsl             v5.16b, v23.16b, v0.16b // FFMIN(d[0..7], clip[0..7])
++        and             w10, w2, w3
++        bic             v0.16b, v2.16b, v7.16b  // set each d[8..15] to zero if it should not be filtered because clip[8..15] == 0 || a0[8..15] >= pq (a3 > a0 case already zeroed by saturating sub)
++        and             w9, w10, w9
++        bic             v2.16b, v5.16b, v19.16b // set each d[0..7] to zero if it should not be filtered because clip[0..7] == 0 || a0[0..7] >= pq (a3 > a0 case already zeroed by saturating sub)
++        mls             v4.8h, v0.8h, v1.8h     // invert d[8..15] depending on clip_sign[8..15] & a0_sign[8..15], or zero it if they match, and accumulate into P4
++        tbnz            w9, #0, 4f              // none of the 16 pixel pairs should be updated in this case
++        mls             v27.8h, v2.8h, v6.8h    // invert d[0..7] depending on clip_sign[0..7] & a0_sign[0..7], or zero it if they match, and accumulate into P4
++        mla             v16.8h, v2.8h, v6.8h    // invert d[0..7] depending on clip_sign[0..7] & a0_sign[0..7], or zero it if they match, and accumulate into P5
++        sqxtun          v2.8b, v4.8h
++        mla             v18.8h, v0.8h, v1.8h    // invert d[8..15] depending on clip_sign[8..15] & a0_sign[8..15], or zero it if they match, and accumulate into P5
++        sqxtun          v0.8b, v27.8h
++        sqxtun          v1.8b, v16.8h
++        sqxtun          v3.8b, v18.8h
++        tbnz            w2, #0, 1f
++        st2             {v0.b, v1.b}[0], [x0], x1
++        st2             {v0.b, v1.b}[1], [x0], x1
++        st2             {v0.b, v1.b}[2], [x0], x1
++        st2             {v0.b, v1.b}[3], [x0]
++1:      tbnz            w3, #0, 2f
++        st2             {v0.b, v1.b}[4], [x5], x1
++        st2             {v0.b, v1.b}[5], [x5], x1
++        st2             {v0.b, v1.b}[6], [x5], x1
++        st2             {v0.b, v1.b}[7], [x5]
++2:      tbnz            w7, #0, 3f
++        st2             {v2.b, v3.b}[0], [x4], x1
++        st2             {v2.b, v3.b}[1], [x4], x1
++        st2             {v2.b, v3.b}[2], [x4], x1
++        st2             {v2.b, v3.b}[3], [x4]
++3:      tbnz            w8, #0, 4f
++        st2             {v2.b, v3.b}[4], [x6], x1
++        st2             {v2.b, v3.b}[5], [x6], x1
++        st2             {v2.b, v3.b}[6], [x6], x1
++        st2             {v2.b, v3.b}[7], [x6]
++4:      ret
++endfunc
++
++// Copy at most the specified number of bytes from source to destination buffer,
++// stopping at a multiple of 32 bytes, none of which are the start of an escape sequence
++// On entry:
++//   x0 -> source buffer
++//   w1 = max number of bytes to copy
++//   x2 -> destination buffer, optimally 8-byte aligned
++// On exit:
++//   w0 = number of bytes not copied
++function ff_vc1_unescape_buffer_helper_neon, export=1
++        // Offset by 80 to screen out cases that are too short for us to handle,
++        // and also make it easy to test for loop termination, or to determine
++        // whether we need an odd number of half-iterations of the loop.
++        subs            w1, w1, #80
++        b.mi            90f
++
++        // Set up useful constants
++        movi            v20.4s, #3, lsl #24
++        movi            v21.4s, #3, lsl #16
++
++        tst             w1, #32
++        b.ne            1f
++
++          ld1             {v0.16b, v1.16b, v2.16b}, [x0], #48
++          ext             v25.16b, v0.16b, v1.16b, #1
++          ext             v26.16b, v0.16b, v1.16b, #2
++          ext             v27.16b, v0.16b, v1.16b, #3
++          ext             v29.16b, v1.16b, v2.16b, #1
++          ext             v30.16b, v1.16b, v2.16b, #2
++          ext             v31.16b, v1.16b, v2.16b, #3
++          bic             v24.16b, v0.16b, v20.16b
++          bic             v25.16b, v25.16b, v20.16b
++          bic             v26.16b, v26.16b, v20.16b
++          bic             v27.16b, v27.16b, v20.16b
++          bic             v28.16b, v1.16b, v20.16b
++          bic             v29.16b, v29.16b, v20.16b
++          bic             v30.16b, v30.16b, v20.16b
++          bic             v31.16b, v31.16b, v20.16b
++          eor             v24.16b, v24.16b, v21.16b
++          eor             v25.16b, v25.16b, v21.16b
++          eor             v26.16b, v26.16b, v21.16b
++          eor             v27.16b, v27.16b, v21.16b
++          eor             v28.16b, v28.16b, v21.16b
++          eor             v29.16b, v29.16b, v21.16b
++          eor             v30.16b, v30.16b, v21.16b
++          eor             v31.16b, v31.16b, v21.16b
++          cmeq            v24.4s, v24.4s, #0
++          cmeq            v25.4s, v25.4s, #0
++          cmeq            v26.4s, v26.4s, #0
++          cmeq            v27.4s, v27.4s, #0
++          add             w1, w1, #32
++          b               3f
++
++1:      ld1             {v3.16b, v4.16b, v5.16b}, [x0], #48
++        ext             v25.16b, v3.16b, v4.16b, #1
++        ext             v26.16b, v3.16b, v4.16b, #2
++        ext             v27.16b, v3.16b, v4.16b, #3
++        ext             v29.16b, v4.16b, v5.16b, #1
++        ext             v30.16b, v4.16b, v5.16b, #2
++        ext             v31.16b, v4.16b, v5.16b, #3
++        bic             v24.16b, v3.16b, v20.16b
++        bic             v25.16b, v25.16b, v20.16b
++        bic             v26.16b, v26.16b, v20.16b
++        bic             v27.16b, v27.16b, v20.16b
++        bic             v28.16b, v4.16b, v20.16b
++        bic             v29.16b, v29.16b, v20.16b
++        bic             v30.16b, v30.16b, v20.16b
++        bic             v31.16b, v31.16b, v20.16b
++        eor             v24.16b, v24.16b, v21.16b
++        eor             v25.16b, v25.16b, v21.16b
++        eor             v26.16b, v26.16b, v21.16b
++        eor             v27.16b, v27.16b, v21.16b
++        eor             v28.16b, v28.16b, v21.16b
++        eor             v29.16b, v29.16b, v21.16b
++        eor             v30.16b, v30.16b, v21.16b
++        eor             v31.16b, v31.16b, v21.16b
++        cmeq            v24.4s, v24.4s, #0
++        cmeq            v25.4s, v25.4s, #0
++        cmeq            v26.4s, v26.4s, #0
++        cmeq            v27.4s, v27.4s, #0
++        // Drop through...
++2:        mov             v0.16b, v5.16b
++          ld1             {v1.16b, v2.16b}, [x0], #32
++        cmeq            v28.4s, v28.4s, #0
++        cmeq            v29.4s, v29.4s, #0
++        cmeq            v30.4s, v30.4s, #0
++        cmeq            v31.4s, v31.4s, #0
++        orr             v24.16b, v24.16b, v25.16b
++        orr             v26.16b, v26.16b, v27.16b
++        orr             v28.16b, v28.16b, v29.16b
++        orr             v30.16b, v30.16b, v31.16b
++          ext             v25.16b, v0.16b, v1.16b, #1
++        orr             v22.16b, v24.16b, v26.16b
++          ext             v26.16b, v0.16b, v1.16b, #2
++          ext             v27.16b, v0.16b, v1.16b, #3
++          ext             v29.16b, v1.16b, v2.16b, #1
++        orr             v23.16b, v28.16b, v30.16b
++          ext             v30.16b, v1.16b, v2.16b, #2
++          ext             v31.16b, v1.16b, v2.16b, #3
++          bic             v24.16b, v0.16b, v20.16b
++          bic             v25.16b, v25.16b, v20.16b
++          bic             v26.16b, v26.16b, v20.16b
++        orr             v22.16b, v22.16b, v23.16b
++          bic             v27.16b, v27.16b, v20.16b
++          bic             v28.16b, v1.16b, v20.16b
++          bic             v29.16b, v29.16b, v20.16b
++          bic             v30.16b, v30.16b, v20.16b
++          bic             v31.16b, v31.16b, v20.16b
++        addv            s22, v22.4s
++          eor             v24.16b, v24.16b, v21.16b
++          eor             v25.16b, v25.16b, v21.16b
++          eor             v26.16b, v26.16b, v21.16b
++          eor             v27.16b, v27.16b, v21.16b
++          eor             v28.16b, v28.16b, v21.16b
++        mov             w3, v22.s[0]
++          eor             v29.16b, v29.16b, v21.16b
++          eor             v30.16b, v30.16b, v21.16b
++          eor             v31.16b, v31.16b, v21.16b
++          cmeq            v24.4s, v24.4s, #0
++          cmeq            v25.4s, v25.4s, #0
++          cmeq            v26.4s, v26.4s, #0
++          cmeq            v27.4s, v27.4s, #0
++        cbnz            w3, 90f
++        st1             {v3.16b, v4.16b}, [x2], #32
++3:          mov             v3.16b, v2.16b
++            ld1             {v4.16b, v5.16b}, [x0], #32
++          cmeq            v28.4s, v28.4s, #0
++          cmeq            v29.4s, v29.4s, #0
++          cmeq            v30.4s, v30.4s, #0
++          cmeq            v31.4s, v31.4s, #0
++          orr             v24.16b, v24.16b, v25.16b
++          orr             v26.16b, v26.16b, v27.16b
++          orr             v28.16b, v28.16b, v29.16b
++          orr             v30.16b, v30.16b, v31.16b
++            ext             v25.16b, v3.16b, v4.16b, #1
++          orr             v22.16b, v24.16b, v26.16b
++            ext             v26.16b, v3.16b, v4.16b, #2
++            ext             v27.16b, v3.16b, v4.16b, #3
++            ext             v29.16b, v4.16b, v5.16b, #1
++          orr             v23.16b, v28.16b, v30.16b
++            ext             v30.16b, v4.16b, v5.16b, #2
++            ext             v31.16b, v4.16b, v5.16b, #3
++            bic             v24.16b, v3.16b, v20.16b
++            bic             v25.16b, v25.16b, v20.16b
++            bic             v26.16b, v26.16b, v20.16b
++          orr             v22.16b, v22.16b, v23.16b
++            bic             v27.16b, v27.16b, v20.16b
++            bic             v28.16b, v4.16b, v20.16b
++            bic             v29.16b, v29.16b, v20.16b
++            bic             v30.16b, v30.16b, v20.16b
++            bic             v31.16b, v31.16b, v20.16b
++          addv            s22, v22.4s
++            eor             v24.16b, v24.16b, v21.16b
++            eor             v25.16b, v25.16b, v21.16b
++            eor             v26.16b, v26.16b, v21.16b
++            eor             v27.16b, v27.16b, v21.16b
++            eor             v28.16b, v28.16b, v21.16b
++          mov             w3, v22.s[0]
++            eor             v29.16b, v29.16b, v21.16b
++            eor             v30.16b, v30.16b, v21.16b
++            eor             v31.16b, v31.16b, v21.16b
++            cmeq            v24.4s, v24.4s, #0
++            cmeq            v25.4s, v25.4s, #0
++            cmeq            v26.4s, v26.4s, #0
++            cmeq            v27.4s, v27.4s, #0
++          cbnz            w3, 91f
++          st1             {v0.16b, v1.16b}, [x2], #32
++        subs            w1, w1, #64
++        b.pl            2b
++
++90:     add             w0, w1, #80
++        ret
++
++91:     sub             w1, w1, #32
++        b               90b
++endfunc
 --- a/libavcodec/allcodecs.c
 +++ b/libavcodec/allcodecs.c
 @@ -149,6 +149,7 @@ extern AVCodec ff_hap_decoder;
@@ -448,7 +2329,7 @@
 @@ -890,6 +891,41 @@ static enum AVCodecID remap_deprecated_c
      }
  }
-
+ 
 +static int codec_supports_format(const AVCodec * const p, const enum AVPixelFormat fmt)
 +{
 +    const enum AVPixelFormat *pf = p->pix_fmts;
@@ -528,7 +2409,7 @@
 @@ -26,83 +26,209 @@
  #include "libavutil/internal.h"
  #include "libavcodec/cabac.h"
-
+ 
 +
  #define get_cabac_inline get_cabac_inline_arm
  static av_always_inline int get_cabac_inline_arm(CABACContext *c,
@@ -622,7 +2503,7 @@
 +    );
 +    return bit;
 +}
-
+ 
 -    __asm__ volatile(
 -        "ldrb       %[bit]        , [%[state]]                  \n\t"
 -        "add        %[r_b]        , %[tables]   , %[lps_off]    \n\t"
@@ -719,7 +2600,7 @@
 +#endif
 +        "lsls       %[range] , %[low], #16          \n\t"
 +        "bne        1f                              \n\t"
-
+ 
 -    return bit & 1;
 +        "str        %[ptr]   , [%[c], %[ptr_off]]   \n\t"
 +        "rev        %[tmp]   , %[tmp]               \n\t"
@@ -803,7 +2684,7 @@
 +}
 +
  #endif /* HAVE_ARMV6T2_INLINE */
-
+ 
  #endif /* AVCODEC_ARM_CABAC_H */
 --- /dev/null
 +++ b/libavcodec/arm/rpi_hevc_cabac.h
@@ -15211,6 +17092,883 @@
 +        bx          lr
 +
 +endfunc
+--- a/libavcodec/arm/vc1dsp_init_neon.c
++++ b/libavcodec/arm/vc1dsp_init_neon.c
+@@ -19,6 +19,7 @@
+ #include <stdint.h>
+ 
+ #include "libavutil/attributes.h"
++#include "libavutil/intreadwrite.h"
+ #include "libavcodec/vc1dsp.h"
+ #include "vc1dsp.h"
+ 
+@@ -32,6 +33,13 @@ void ff_vc1_inv_trans_4x8_dc_neon(uint8_
+ void ff_vc1_inv_trans_8x4_dc_neon(uint8_t *dest, ptrdiff_t stride, int16_t *block);
+ void ff_vc1_inv_trans_4x4_dc_neon(uint8_t *dest, ptrdiff_t stride, int16_t *block);
+ 
++void ff_vc1_v_loop_filter4_neon(uint8_t *src, int stride, int pq);
++void ff_vc1_h_loop_filter4_neon(uint8_t *src, int stride, int pq);
++void ff_vc1_v_loop_filter8_neon(uint8_t *src, int stride, int pq);
++void ff_vc1_h_loop_filter8_neon(uint8_t *src, int stride, int pq);
++void ff_vc1_v_loop_filter16_neon(uint8_t *src, int stride, int pq);
++void ff_vc1_h_loop_filter16_neon(uint8_t *src, int stride, int pq);
++
+ void ff_put_pixels8x8_neon(uint8_t *block, const uint8_t *pixels,
+                            ptrdiff_t line_size, int rnd);
+ 
+@@ -77,6 +85,64 @@ void ff_put_vc1_chroma_mc4_neon(uint8_t
+ void ff_avg_vc1_chroma_mc4_neon(uint8_t *dst, uint8_t *src, ptrdiff_t stride,
+                                 int h, int x, int y);
+ 
++int ff_vc1_unescape_buffer_helper_neon(const uint8_t *src, int size, uint8_t *dst);
++
++static int vc1_unescape_buffer_neon(const uint8_t *src, int size, uint8_t *dst)
++{
++    /* Dealing with starting and stopping, and removing escape bytes, are
++     * comparatively less time-sensitive, so are more clearly expressed using
++     * a C wrapper around the assembly inner loop. Note that we assume a
++     * little-endian machine that supports unaligned loads. */
++    int dsize = 0;
++    while (size >= 4)
++    {
++        int found = 0;
++        while (!found && (((uintptr_t) dst) & 7) && size >= 4)
++        {
++            found = (AV_RL32(src) &~ 0x03000000) == 0x00030000;
++            if (!found)
++            {
++                *dst++ = *src++;
++                --size;
++                ++dsize;
++            }
++        }
++        if (!found)
++        {
++            int skip = size - ff_vc1_unescape_buffer_helper_neon(src, size, dst);
++            dst += skip;
++            src += skip;
++            size -= skip;
++            dsize += skip;
++            while (!found && size >= 4)
++            {
++                found = (AV_RL32(src) &~ 0x03000000) == 0x00030000;
++                if (!found)
++                {
++                    *dst++ = *src++;
++                    --size;
++                    ++dsize;
++                }
++            }
++        }
++        if (found)
++        {
++            *dst++ = *src++;
++            *dst++ = *src++;
++            ++src;
++            size -= 3;
++            dsize += 2;
++        }
++    }
++    while (size > 0)
++    {
++        *dst++ = *src++;
++        --size;
++        ++dsize;
++    }
++    return dsize;
++}
++
+ #define FN_ASSIGN(X, Y) \
+     dsp->put_vc1_mspel_pixels_tab[0][X+4*Y] = ff_put_vc1_mspel_mc##X##Y##_16_neon; \
+     dsp->put_vc1_mspel_pixels_tab[1][X+4*Y] = ff_put_vc1_mspel_mc##X##Y##_neon
+@@ -92,6 +158,13 @@ av_cold void ff_vc1dsp_init_neon(VC1DSPC
+     dsp->vc1_inv_trans_8x4_dc = ff_vc1_inv_trans_8x4_dc_neon;
+     dsp->vc1_inv_trans_4x4_dc = ff_vc1_inv_trans_4x4_dc_neon;
+ 
++    dsp->vc1_v_loop_filter4  = ff_vc1_v_loop_filter4_neon;
++    dsp->vc1_h_loop_filter4  = ff_vc1_h_loop_filter4_neon;
++    dsp->vc1_v_loop_filter8  = ff_vc1_v_loop_filter8_neon;
++    dsp->vc1_h_loop_filter8  = ff_vc1_h_loop_filter8_neon;
++    dsp->vc1_v_loop_filter16 = ff_vc1_v_loop_filter16_neon;
++    dsp->vc1_h_loop_filter16 = ff_vc1_h_loop_filter16_neon;
++
+     dsp->put_vc1_mspel_pixels_tab[1][ 0] = ff_put_pixels8x8_neon;
+     FN_ASSIGN(1, 0);
+     FN_ASSIGN(2, 0);
+@@ -116,4 +189,6 @@ av_cold void ff_vc1dsp_init_neon(VC1DSPC
+     dsp->avg_no_rnd_vc1_chroma_pixels_tab[0] = ff_avg_vc1_chroma_mc8_neon;
+     dsp->put_no_rnd_vc1_chroma_pixels_tab[1] = ff_put_vc1_chroma_mc4_neon;
+     dsp->avg_no_rnd_vc1_chroma_pixels_tab[1] = ff_avg_vc1_chroma_mc4_neon;
++
++    dsp->vc1_unescape_buffer = vc1_unescape_buffer_neon;
+ }
+--- a/libavcodec/arm/vc1dsp_neon.S
++++ b/libavcodec/arm/vc1dsp_neon.S
+@@ -1161,3 +1161,764 @@ function ff_vc1_inv_trans_4x4_dc_neon, e
+         vst1.32         {d1[1]},  [r0,:32]
+         bx              lr
+ endfunc
++
++@ VC-1 in-loop deblocking filter for 4 pixel pairs at boundary of vertically-neighbouring blocks
++@ On entry:
++@   r0 -> top-left pel of lower block
++@   r1 = row stride, bytes
++@   r2 = PQUANT bitstream parameter
++function ff_vc1_v_loop_filter4_neon, export=1
++        sub             r3, r0, r1, lsl #2
++        vldr            d0, .Lcoeffs
++        vld1.32         {d1[0]}, [r0], r1       @ P5
++        vld1.32         {d2[0]}, [r3], r1       @ P1
++        vld1.32         {d3[0]}, [r3], r1       @ P2
++        vld1.32         {d4[0]}, [r0], r1       @ P6
++        vld1.32         {d5[0]}, [r3], r1       @ P3
++        vld1.32         {d6[0]}, [r0], r1       @ P7
++        vld1.32         {d7[0]}, [r3]           @ P4
++        vld1.32         {d16[0]}, [r0]          @ P8
++        vshll.u8        q9, d1, #1              @ 2*P5
++        vdup.16         d17, r2                 @ pq
++        vshll.u8        q10, d2, #1             @ 2*P1
++        vmovl.u8        q11, d3                 @ P2
++        vmovl.u8        q1, d4                  @ P6
++        vmovl.u8        q12, d5                 @ P3
++        vmls.i16        d20, d22, d0[1]         @ 2*P1-5*P2
++        vmovl.u8        q11, d6                 @ P7
++        vmls.i16        d18, d2, d0[1]          @ 2*P5-5*P6
++        vshll.u8        q2, d5, #1              @ 2*P3
++        vmovl.u8        q3, d7                  @ P4
++        vmla.i16        d18, d22, d0[1]         @ 2*P5-5*P6+5*P7
++        vmovl.u8        q11, d16                @ P8
++        vmla.u16        d20, d24, d0[1]         @ 2*P1-5*P2+5*P3
++        vmovl.u8        q12, d1                 @ P5
++        vmls.u16        d4, d6, d0[1]           @ 2*P3-5*P4
++        vmls.u16        d18, d22, d0[0]         @ 2*P5-5*P6+5*P7-2*P8
++        vsub.i16        d1, d6, d24             @ P4-P5
++        vmls.i16        d20, d6, d0[0]          @ 2*P1-5*P2+5*P3-2*P4
++        vmla.i16        d4, d24, d0[1]          @ 2*P3-5*P4+5*P5
++        vmls.i16        d4, d2, d0[0]           @ 2*P3-5*P4+5*P5-2*P6
++        vabs.s16        d2, d1
++        vrshr.s16       d3, d18, #3
++        vrshr.s16       d5, d20, #3
++        vshr.s16        d2, d2, #1              @ clip
++        vrshr.s16       d4, d4, #3
++        vabs.s16        d3, d3                  @ a2
++        vshr.s16        d1, d1, #8              @ clip_sign
++        vabs.s16        d5, d5                  @ a1
++        vceq.i16        d7, d2, #0              @ test clip == 0
++        vabs.s16        d16, d4                 @ a0
++        vshr.s16        d4, d4, #8              @ a0_sign
++        vcge.s16        d18, d5, d3             @ test a1 >= a2
++        vcge.s16        d17, d16, d17           @ test a0 >= pq
++        vbsl            d18, d3, d5             @ a3
++        vsub.i16        d1, d1, d4              @ clip_sign - a0_sign
++        vorr            d3, d7, d17             @ test clip == 0 || a0 >= pq
++        vqsub.u16       d4, d16, d18            @ a0 >= a3 ? a0-a3 : 0  (a0 > a3 in all cases where filtering is enabled, so makes more sense to subtract this way round than the opposite and then taking the abs)
++        vcge.s16        d5, d18, d16            @ test a3 >= a0
++        vmul.i16        d0, d4, d0[1]           @ a0 >= a3 ? 5*(a0-a3) : 0
++        vorr            d4, d3, d5              @ test clip == 0 || a0 >= pq || a3 >= a0
++        vmov.32         r0, d4[1]               @ move to gp reg
++        vshr.u16        d0, d0, #3              @ a0 >= a3 ? (5*(a0-a3))>>3 : 0
++        vcge.s16        d4, d0, d2
++        tst             r0, #1
++        bne             1f                      @ none of the 4 pixel pairs should be updated if this one is not filtered
++        vbsl            d4, d2, d0              @ FFMIN(d, clip)
++        vbic            d0, d4, d3              @ set each d to zero if it should not be filtered because clip == 0 || a0 >= pq (a3 > a0 case already zeroed by saturating sub)
++        vmls.i16        d6, d0, d1              @ invert d depending on clip_sign & a0_sign, or zero it if they match, and accumulate into P4
++        vmla.i16        d24, d0, d1             @ invert d depending on clip_sign & a0_sign, or zero it if they match, and accumulate into P5
++        vqmovun.s16     d0, q3
++        vqmovun.s16     d1, q12
++        vst1.32         {d0[0]}, [r3], r1
++        vst1.32         {d1[0]}, [r3]
++1:      bx              lr
++endfunc
++
++@ VC-1 in-loop deblocking filter for 4 pixel pairs at boundary of horizontally-neighbouring blocks
++@ On entry:
++@   r0 -> top-left pel of right block
++@   r1 = row stride, bytes
++@   r2 = PQUANT bitstream parameter
++function ff_vc1_h_loop_filter4_neon, export=1
++        sub             r3, r0, #4              @ where to start reading
++        vldr            d0, .Lcoeffs
++        vld1.32         {d2}, [r3], r1
++        sub             r0, r0, #1              @ where to start writing
++        vld1.32         {d4}, [r3], r1
++        vld1.32         {d3}, [r3], r1
++        vld1.32         {d5}, [r3]
++        vdup.16         d1, r2                  @ pq
++        vtrn.8          q1, q2
++        vtrn.16         d2, d3                  @ P1, P5, P3, P7
++        vtrn.16         d4, d5                  @ P2, P6, P4, P8
++        vshll.u8        q3, d2, #1              @ 2*P1, 2*P5
++        vmovl.u8        q8, d4                  @ P2, P6
++        vmovl.u8        q9, d3                  @ P3, P7
++        vmovl.u8        q2, d5                  @ P4, P8
++        vmls.i16        q3, q8, d0[1]           @ 2*P1-5*P2, 2*P5-5*P6
++        vshll.u8        q10, d3, #1             @ 2*P3, 2*P7
++        vmovl.u8        q1, d2                  @ P1, P5
++        vmla.i16        q3, q9, d0[1]           @ 2*P1-5*P2+5*P3, 2*P5-5*P6+5*P7
++        vmls.i16        q3, q2, d0[0]           @ 2*P1-5*P2+5*P3-2*P4, 2*P5-5*P6+5*P7-2*P8
++        vmov            d2, d3                  @ needs to be in an even-numbered vector for when we come to narrow it later
++        vmls.i16        d20, d4, d0[1]          @ 2*P3-5*P4
++        vmla.i16        d20, d3, d0[1]          @ 2*P3-5*P4+5*P5
++        vsub.i16        d3, d4, d2              @ P4-P5
++        vmls.i16        d20, d17, d0[0]         @ 2*P3-5*P4+5*P5-2*P6
++        vrshr.s16       q3, q3, #3
++        vabs.s16        d5, d3
++        vshr.s16        d3, d3, #8              @ clip_sign
++        vrshr.s16       d16, d20, #3
++        vabs.s16        q3, q3                  @ a1, a2
++        vshr.s16        d5, d5, #1              @ clip
++        vabs.s16        d17, d16                @ a0
++        vceq.i16        d18, d5, #0             @ test clip == 0
++        vshr.s16        d16, d16, #8            @ a0_sign
++        vcge.s16        d19, d6, d7             @ test a1 >= a2
++        vcge.s16        d1, d17, d1             @ test a0 >= pq
++        vsub.i16        d16, d3, d16            @ clip_sign - a0_sign
++        vbsl            d19, d7, d6             @ a3
++        vorr            d1, d18, d1             @ test clip == 0 || a0 >= pq
++        vqsub.u16       d3, d17, d19            @ a0 >= a3 ? a0-a3 : 0  (a0 > a3 in all cases where filtering is enabled, so makes more sense to subtract this way round than the opposite and then taking the abs)
++        vcge.s16        d6, d19, d17            @ test a3 >= a0    @
++        vmul.i16        d0, d3, d0[1]           @ a0 >= a3 ? 5*(a0-a3) : 0
++        vorr            d3, d1, d6              @ test clip == 0 || a0 >= pq || a3 >= a0
++        vmov.32         r2, d3[1]               @ move to gp reg
++        vshr.u16        d0, d0, #3              @ a0 >= a3 ? (5*(a0-a3))>>3 : 0
++        vcge.s16        d3, d0, d5
++        tst             r2, #1
++        bne             1f                      @ none of the 4 pixel pairs should be updated if this one is not filtered
++        vbsl            d3, d5, d0              @ FFMIN(d, clip)
++        vbic            d0, d3, d1              @ set each d to zero if it should not be filtered because clip == 0 || a0 >= pq (a3 > a0 case already zeroed by saturating sub)
++        vmla.i16        d2, d0, d16             @ invert d depending on clip_sign & a0_sign, or zero it if they match, and accumulate into P5
++        vmls.i16        d4, d0, d16             @ invert d depending on clip_sign & a0_sign, or zero it if they match, and accumulate into P4
++        vqmovun.s16     d1, q1
++        vqmovun.s16     d0, q2
++        vst2.8          {d0[0], d1[0]}, [r0], r1
++        vst2.8          {d0[1], d1[1]}, [r0], r1
++        vst2.8          {d0[2], d1[2]}, [r0], r1
++        vst2.8          {d0[3], d1[3]}, [r0]
++1:      bx              lr
++endfunc
++
++@ VC-1 in-loop deblocking filter for 8 pixel pairs at boundary of vertically-neighbouring blocks
++@ On entry:
++@   r0 -> top-left pel of lower block
++@   r1 = row stride, bytes
++@   r2 = PQUANT bitstream parameter
++function ff_vc1_v_loop_filter8_neon, export=1
++        sub             r3, r0, r1, lsl #2
++        vldr            d0, .Lcoeffs
++        vld1.32         {d1}, [r0 :64], r1      @ P5
++        vld1.32         {d2}, [r3 :64], r1      @ P1
++        vld1.32         {d3}, [r3 :64], r1      @ P2
++        vld1.32         {d4}, [r0 :64], r1      @ P6
++        vld1.32         {d5}, [r3 :64], r1      @ P3
++        vld1.32         {d6}, [r0 :64], r1      @ P7
++        vshll.u8        q8, d1, #1              @ 2*P5
++        vshll.u8        q9, d2, #1              @ 2*P1
++        vld1.32         {d7}, [r3 :64]          @ P4
++        vmovl.u8        q1, d3                  @ P2
++        vld1.32         {d20}, [r0 :64]         @ P8
++        vmovl.u8        q11, d4                 @ P6
++        vdup.16         q12, r2                 @ pq
++        vmovl.u8        q13, d5                 @ P3
++        vmls.i16        q9, q1, d0[1]           @ 2*P1-5*P2
++        vmovl.u8        q1, d6                  @ P7
++        vshll.u8        q2, d5, #1              @ 2*P3
++        vmls.i16        q8, q11, d0[1]          @ 2*P5-5*P6
++        vmovl.u8        q3, d7                  @ P4
++        vmovl.u8        q10, d20                @ P8
++        vmla.i16        q8, q1, d0[1]           @ 2*P5-5*P6+5*P7
++        vmovl.u8        q1, d1                  @ P5
++        vmla.i16        q9, q13, d0[1]          @ 2*P1-5*P2+5*P3
++        vsub.i16        q13, q3, q1             @ P4-P5
++        vmls.i16        q2, q3, d0[1]           @ 2*P3-5*P4
++        vmls.i16        q8, q10, d0[0]          @ 2*P5-5*P6+5*P7-2*P8
++        vabs.s16        q10, q13
++        vshr.s16        q13, q13, #8            @ clip_sign
++        vmls.i16        q9, q3, d0[0]           @ 2*P1-5*P2+5*P3-2*P4
++        vshr.s16        q10, q10, #1            @ clip
++        vmla.i16        q2, q1, d0[1]           @ 2*P3-5*P4+5*P5
++        vrshr.s16       q8, q8, #3
++        vmls.i16        q2, q11, d0[0]          @ 2*P3-5*P4+5*P5-2*P6
++        vceq.i16        q11, q10, #0            @ test clip == 0
++        vrshr.s16       q9, q9, #3
++        vabs.s16        q8, q8                  @ a2
++        vabs.s16        q9, q9                  @ a1
++        vrshr.s16       q2, q2, #3
++        vcge.s16        q14, q9, q8             @ test a1 >= a2
++        vabs.s16        q15, q2                 @ a0
++        vshr.s16        q2, q2, #8              @ a0_sign
++        vbsl            q14, q8, q9             @ a3
++        vcge.s16        q8, q15, q12            @ test a0 >= pq
++        vsub.i16        q2, q13, q2             @ clip_sign - a0_sign
++        vqsub.u16       q9, q15, q14            @ a0 >= a3 ? a0-a3 : 0  (a0 > a3 in all cases where filtering is enabled, so makes more sense to subtract this way round than the opposite and then taking the abs)
++        vcge.s16        q12, q14, q15           @ test a3 >= a0
++        vorr            q8, q11, q8             @ test clip == 0 || a0 >= pq
++        vmul.i16        q0, q9, d0[1]           @ a0 >= a3 ? 5*(a0-a3) : 0
++        vorr            q9, q8, q12             @ test clip == 0 || a0 >= pq || a3 >= a0
++        vshl.i64        q11, q9, #16
++        vmov.32         r0, d18[1]              @ move to gp reg
++        vshr.u16        q0, q0, #3              @ a0 >= a3 ? (5*(a0-a3))>>3 : 0
++        vmov.32         r2, d19[1]
++        vshr.s64        q9, q11, #48
++        vcge.s16        q11, q0, q10
++        vorr            q8, q8, q9
++        and             r0, r0, r2
++        vbsl            q11, q10, q0            @ FFMIN(d, clip)
++        tst             r0, #1
++        bne             1f                      @ none of the 8 pixel pairs should be updated in this case
++        vbic            q0, q11, q8             @ set each d to zero if it should not be filtered
++        vmls.i16        q3, q0, q2              @ invert d depending on clip_sign & a0_sign, or zero it if they match, and accumulate into P4
++        vmla.i16        q1, q0, q2              @ invert d depending on clip_sign & a0_sign, or zero it if they match, and accumulate into P5
++        vqmovun.s16     d0, q3
++        vqmovun.s16     d1, q1
++        vst1.32         {d0}, [r3 :64], r1
++        vst1.32         {d1}, [r3 :64]
++1:      bx              lr
++endfunc
++
++.align  5
++.Lcoeffs:
++.quad   0x00050002
++
++@ VC-1 in-loop deblocking filter for 8 pixel pairs at boundary of horizontally-neighbouring blocks
++@ On entry:
++@   r0 -> top-left pel of right block
++@   r1 = row stride, bytes
++@   r2 = PQUANT bitstream parameter
++function ff_vc1_h_loop_filter8_neon, export=1
++        push            {lr}
++        sub             r3, r0, #4              @ where to start reading
++        vldr            d0, .Lcoeffs
++        vld1.32         {d2}, [r3], r1          @ P1[0], P2[0]...
++        sub             r0, r0, #1              @ where to start writing
++        vld1.32         {d4}, [r3], r1
++        add             r12, r0, r1, lsl #2
++        vld1.32         {d3}, [r3], r1
++        vld1.32         {d5}, [r3], r1
++        vld1.32         {d6}, [r3], r1
++        vld1.32         {d16}, [r3], r1
++        vld1.32         {d7}, [r3], r1
++        vld1.32         {d17}, [r3]
++        vtrn.8          q1, q2                  @ P1[0], P1[1], P3[0]... P1[2], P1[3], P3[2]... P2[0], P2[1], P4[0]... P2[2], P2[3], P4[2]...
++        vdup.16         q9, r2                  @ pq
++        vtrn.16         d2, d3                  @ P1[0], P1[1], P1[2], P1[3], P5[0]... P3[0], P3[1], P3[2], P3[3], P7[0]...
++        vtrn.16         d4, d5                  @ P2[0], P2[1], P2[2], P2[3], P6[0]... P4[0], P4[1], P4[2], P4[3], P8[0]...
++        vtrn.8          q3, q8                  @ P1[4], P1[5], P3[4]... P1[6], P1[7], P3[6]... P2[4], P2[5], P4[4]... P2[6], P2[7], P4[6]...
++        vtrn.16         d6, d7                  @ P1[4], P1[5], P1[6], P1[7], P5[4]... P3[4], P3[5], P3[5], P3[7], P7[4]...
++        vtrn.16         d16, d17                @ P2[4], P2[5], P2[6], P2[7], P6[4]... P4[4], P4[5], P4[6], P4[7], P8[4]...
++        vtrn.32         d2, d6                  @ P1, P5
++        vtrn.32         d4, d16                 @ P2, P6
++        vtrn.32         d3, d7                  @ P3, P7
++        vtrn.32         d5, d17                 @ P4, P8
++        vshll.u8        q10, d2, #1             @ 2*P1
++        vshll.u8        q11, d6, #1             @ 2*P5
++        vmovl.u8        q12, d4                 @ P2
++        vmovl.u8        q13, d16                @ P6
++        vmovl.u8        q14, d3                 @ P3
++        vmls.i16        q10, q12, d0[1]         @ 2*P1-5*P2
++        vmovl.u8        q12, d7                 @ P7
++        vshll.u8        q1, d3, #1              @ 2*P3
++        vmls.i16        q11, q13, d0[1]         @ 2*P5-5*P6
++        vmovl.u8        q2, d5                  @ P4
++        vmovl.u8        q8, d17                 @ P8
++        vmla.i16        q11, q12, d0[1]         @ 2*P5-5*P6+5*P7
++        vmovl.u8        q3, d6                  @ P5
++        vmla.i16        q10, q14, d0[1]         @ 2*P1-5*P2+5*P3
++        vsub.i16        q12, q2, q3             @ P4-P5
++        vmls.i16        q1, q2, d0[1]           @ 2*P3-5*P4
++        vmls.i16        q11, q8, d0[0]          @ 2*P5-5*P6+5*P7-2*P8
++        vabs.s16        q8, q12
++        vshr.s16        q12, q12, #8            @ clip_sign
++        vmls.i16        q10, q2, d0[0]          @ 2*P1-5*P2+5*P3-2*P4
++        vshr.s16        q8, q8, #1              @ clip
++        vmla.i16        q1, q3, d0[1]           @ 2*P3-5*P4+5*P5
++        vrshr.s16       q11, q11, #3
++        vmls.i16        q1, q13, d0[0]          @ 2*P3-5*P4+5*P5-2*P6
++        vceq.i16        q13, q8, #0             @ test clip == 0
++        vrshr.s16       q10, q10, #3
++        vabs.s16        q11, q11                @ a2
++        vabs.s16        q10, q10                @ a1
++        vrshr.s16       q1, q1, #3
++        vcge.s16        q14, q10, q11           @ test a1 >= a2
++        vabs.s16        q15, q1                 @ a0
++        vshr.s16        q1, q1, #8              @ a0_sign
++        vbsl            q14, q11, q10           @ a3
++        vcge.s16        q9, q15, q9             @ test a0 >= pq
++        vsub.i16        q1, q12, q1             @ clip_sign - a0_sign
++        vqsub.u16       q10, q15, q14           @ a0 >= a3 ? a0-a3 : 0  (a0 > a3 in all cases where filtering is enabled, so makes more sense to subtract this way round than the opposite and then taking the abs)
++        vcge.s16        q11, q14, q15           @ test a3 >= a0
++        vorr            q9, q13, q9             @ test clip == 0 || a0 >= pq
++        vmul.i16        q0, q10, d0[1]          @ a0 >= a3 ? 5*(a0-a3) : 0
++        vorr            q10, q9, q11            @ test clip == 0 || a0 >= pq || a3 >= a0
++        vmov.32         r2, d20[1]              @ move to gp reg
++        vshr.u16        q0, q0, #3              @ a0 >= a3 ? (5*(a0-a3))>>3 : 0
++        vmov.32         r3, d21[1]
++        vcge.s16        q10, q0, q8
++        and             r14, r2, r3
++        vbsl            q10, q8, q0             @ FFMIN(d, clip)
++        tst             r14, #1
++        bne             2f                      @ none of the 8 pixel pairs should be updated in this case
++        vbic            q0, q10, q9             @ set each d to zero if it should not be filtered because clip == 0 || a0 >= pq (a3 > a0 case already zeroed by saturating sub)
++        vmla.i16        q3, q0, q1              @ invert d depending on clip_sign & a0_sign, or zero it if they match, and accumulate into P5
++        vmls.i16        q2, q0, q1              @ invert d depending on clip_sign & a0_sign, or zero it if they match, and accumulate into P4
++        vqmovun.s16     d1, q3
++        vqmovun.s16     d0, q2
++        tst             r2, #1
++        bne             1f                      @ none of the first 4 pixel pairs should be updated if so
++        vst2.8          {d0[0], d1[0]}, [r0], r1
++        vst2.8          {d0[1], d1[1]}, [r0], r1
++        vst2.8          {d0[2], d1[2]}, [r0], r1
++        vst2.8          {d0[3], d1[3]}, [r0]
++1:      tst             r3, #1
++        bne             2f                      @ none of the second 4 pixel pairs should be updated if so
++        vst2.8          {d0[4], d1[4]}, [r12], r1
++        vst2.8          {d0[5], d1[5]}, [r12], r1
++        vst2.8          {d0[6], d1[6]}, [r12], r1
++        vst2.8          {d0[7], d1[7]}, [r12]
++2:      pop             {pc}
++endfunc
++
++@ VC-1 in-loop deblocking filter for 16 pixel pairs at boundary of vertically-neighbouring blocks
++@ On entry:
++@   r0 -> top-left pel of lower block
++@   r1 = row stride, bytes
++@   r2 = PQUANT bitstream parameter
++function ff_vc1_v_loop_filter16_neon, export=1
++        vpush           {d8-d15}
++        sub             r3, r0, r1, lsl #2
++        vldr            d0, .Lcoeffs
++        vld1.64         {q1}, [r0 :128], r1     @ P5
++        vld1.64         {q2}, [r3 :128], r1     @ P1
++        vld1.64         {q3}, [r3 :128], r1     @ P2
++        vld1.64         {q4}, [r0 :128], r1     @ P6
++        vld1.64         {q5}, [r3 :128], r1     @ P3
++        vld1.64         {q6}, [r0 :128], r1     @ P7
++        vshll.u8        q7, d2, #1              @ 2*P5[0..7]
++        vshll.u8        q8, d4, #1              @ 2*P1[0..7]
++        vld1.64         {q9}, [r3 :128]         @ P4
++        vmovl.u8        q10, d6                 @ P2[0..7]
++        vld1.64         {q11}, [r0 :128]        @ P8
++        vmovl.u8        q12, d8                 @ P6[0..7]
++        vdup.16         q13, r2                 @ pq
++        vshll.u8        q2, d5, #1              @ 2*P1[8..15]
++        vmls.i16        q8, q10, d0[1]          @ 2*P1[0..7]-5*P2[0..7]
++        vshll.u8        q10, d3, #1             @ 2*P5[8..15]
++        vmovl.u8        q3, d7                  @ P2[8..15]
++        vmls.i16        q7, q12, d0[1]          @ 2*P5[0..7]-5*P6[0..7]
++        vmovl.u8        q4, d9                  @ P6[8..15]
++        vmovl.u8        q14, d10                @ P3[0..7]
++        vmovl.u8        q15, d12                @ P7[0..7]
++        vmls.i16        q2, q3, d0[1]           @ 2*P1[8..15]-5*P2[8..15]
++        vshll.u8        q3, d10, #1             @ 2*P3[0..7]
++        vmls.i16        q10, q4, d0[1]          @ 2*P5[8..15]-5*P6[8..15]
++        vmovl.u8        q6, d13                 @ P7[8..15]
++        vmla.i16        q8, q14, d0[1]          @ 2*P1[0..7]-5*P2[0..7]+5*P3[0..7]
++        vmovl.u8        q14, d18                @ P4[0..7]
++        vmovl.u8        q9, d19                 @ P4[8..15]
++        vmla.i16        q7, q15, d0[1]          @ 2*P5[0..7]-5*P6[0..7]+5*P7[0..7]
++        vmovl.u8        q15, d11                @ P3[8..15]
++        vshll.u8        q5, d11, #1             @ 2*P3[8..15]
++        vmls.i16        q3, q14, d0[1]          @ 2*P3[0..7]-5*P4[0..7]
++        vmla.i16        q2, q15, d0[1]          @ 2*P1[8..15]-5*P2[8..15]+5*P3[8..15]
++        vmovl.u8        q15, d22                @ P8[0..7]
++        vmovl.u8        q11, d23                @ P8[8..15]
++        vmla.i16        q10, q6, d0[1]          @ 2*P5[8..15]-5*P6[8..15]+5*P7[8..15]
++        vmovl.u8        q6, d2                  @ P5[0..7]
++        vmovl.u8        q1, d3                  @ P5[8..15]
++        vmls.i16        q5, q9, d0[1]           @ 2*P3[8..15]-5*P4[8..15]
++        vmls.i16        q8, q14, d0[0]          @ 2*P1[0..7]-5*P2[0..7]+5*P3[0..7]-2*P4[0..7]
++        vmls.i16        q7, q15, d0[0]          @ 2*P5[0..7]-5*P6[0..7]+5*P7[0..7]-2*P8[0..7]
++        vsub.i16        q15, q14, q6            @ P4[0..7]-P5[0..7]
++        vmla.i16        q3, q6, d0[1]           @ 2*P3[0..7]-5*P4[0..7]+5*P5[0..7]
++        vrshr.s16       q8, q8, #3
++        vmls.i16        q2, q9, d0[0]           @ 2*P1[8..15]-5*P2[8..15]+5*P3[8..15]-2*P4[8..15]
++        vrshr.s16       q7, q7, #3
++        vmls.i16        q10, q11, d0[0]         @ 2*P5[8..15]-5*P6[8..15]+5*P7[8..15]-2*P8[8..15]
++        vabs.s16        q11, q15
++        vabs.s16        q8, q8                  @ a1[0..7]
++        vmla.i16        q5, q1, d0[1]           @ 2*P3[8..15]-5*P4[8..15]+5*P5[8..15]
++        vshr.s16        q15, q15, #8            @ clip_sign[0..7]
++        vrshr.s16       q2, q2, #3
++        vmls.i16        q3, q12, d0[0]          @ 2*P3[0..7]-5*P4[0..7]+5*P5[0..7]-2*P6[0..7]
++        vabs.s16        q7, q7                  @ a2[0..7]
++        vrshr.s16       q10, q10, #3
++        vsub.i16        q12, q9, q1             @ P4[8..15]-P5[8..15]
++        vshr.s16        q11, q11, #1            @ clip[0..7]
++        vmls.i16        q5, q4, d0[0]           @ 2*P3[8..15]-5*P4[8..15]+5*P5[8..15]-2*P6[8..15]
++        vcge.s16        q4, q8, q7              @ test a1[0..7] >= a2[0..7]
++        vabs.s16        q2, q2                  @ a1[8..15]
++        vrshr.s16       q3, q3, #3
++        vabs.s16        q10, q10                @ a2[8..15]
++        vbsl            q4, q7, q8              @ a3[0..7]
++        vabs.s16        q7, q12
++        vshr.s16        q8, q12, #8             @ clip_sign[8..15]
++        vrshr.s16       q5, q5, #3
++        vcge.s16        q12, q2, q10            @ test a1[8..15] >= a2[8.15]
++        vshr.s16        q7, q7, #1              @ clip[8..15]
++        vbsl            q12, q10, q2            @ a3[8..15]
++        vabs.s16        q2, q3                  @ a0[0..7]
++        vceq.i16        q10, q11, #0            @ test clip[0..7] == 0
++        vshr.s16        q3, q3, #8              @ a0_sign[0..7]
++        vsub.i16        q3, q15, q3             @ clip_sign[0..7] - a0_sign[0..7]
++        vcge.s16        q15, q2, q13            @ test a0[0..7] >= pq
++        vorr            q10, q10, q15           @ test clip[0..7] == 0 || a0[0..7] >= pq
++        vqsub.u16       q15, q2, q4             @ a0[0..7] >= a3[0..7] ? a0[0..7]-a3[0..7] : 0  (a0 > a3 in all cases where filtering is enabled, so makes more sense to subtract this way round than the opposite and then taking the abs)
++        vcge.s16        q2, q4, q2              @ test a3[0..7] >= a0[0..7]
++        vabs.s16        q4, q5                  @ a0[8..15]
++        vshr.s16        q5, q5, #8              @ a0_sign[8..15]
++        vmul.i16        q15, q15, d0[1]         @ a0[0..7] >= a3[0..7] ? 5*(a0[0..7]-a3[0..7]) : 0
++        vcge.s16        q13, q4, q13            @ test a0[8..15] >= pq
++        vorr            q2, q10, q2             @ test clip[0..7] == 0 || a0[0..7] >= pq || a3[0..7] >= a0[0..7]
++        vsub.i16        q5, q8, q5              @ clip_sign[8..15] - a0_sign[8..15]
++        vceq.i16        q8, q7, #0              @ test clip[8..15] == 0
++        vshr.u16        q15, q15, #3            @ a0[0..7] >= a3[0..7] ? (5*(a0[0..7]-a3[0..7]))>>3 : 0
++        vmov.32         r0, d4[1]               @ move to gp reg
++        vorr            q8, q8, q13             @ test clip[8..15] == 0 || a0[8..15] >= pq
++        vqsub.u16       q13, q4, q12            @ a0[8..15] >= a3[8..15] ? a0[8..15]-a3[8..15] : 0  (a0 > a3 in all cases where filtering is enabled, so makes more sense to subtract this way round than the opposite and then taking the abs)
++        vmov.32         r2, d5[1]
++        vcge.s16        q4, q12, q4             @ test a3[8..15] >= a0[8..15]
++        vshl.i64        q2, q2, #16
++        vcge.s16        q12, q15, q11
++        vmul.i16        q0, q13, d0[1]          @ a0[8..15] >= a3[8..15] ? 5*(a0[8..15]-a3[8..15]) : 0
++        vorr            q4, q8, q4              @ test clip[8..15] == 0 || a0[8..15] >= pq || a3[8..15] >= a0[8..15]
++        vshr.s64        q2, q2, #48
++        and             r0, r0, r2
++        vbsl            q12, q11, q15           @ FFMIN(d[0..7], clip[0..7])
++        vshl.i64        q11, q4, #16
++        vmov.32         r2, d8[1]
++        vshr.u16        q0, q0, #3              @ a0[8..15] >= a3[8..15] ? (5*(a0[8..15]-a3[8..15]))>>3 : 0
++        vorr            q2, q10, q2
++        vmov.32         r12, d9[1]
++        vshr.s64        q4, q11, #48
++        vcge.s16        q10, q0, q7
++        vbic            q2, q12, q2             @ set each d[0..7] to zero if it should not be filtered because clip[0..7] == 0 || a0[0..7] >= pq (a3 > a0 case already zeroed by saturating sub)
++        vorr            q4, q8, q4
++        and             r2, r2, r12
++        vbsl            q10, q7, q0             @ FFMIN(d[8..15], clip[8..15])
++        vmls.i16        q14, q2, q3             @ invert d[0..7] depending on clip_sign[0..7] & a0_sign[0..7], or zero it if they match, and accumulate into P4[0..7]
++        and             r0, r0, r2
++        vbic            q0, q10, q4             @ set each d[8..15] to zero if it should not be filtered because clip[8..15] == 0 || a0[8..15] >= pq (a3 > a0 case already zeroed by saturating sub)
++        tst             r0, #1
++        bne             1f                      @ none of the 16 pixel pairs should be updated in this case
++        vmla.i16        q6, q2, q3              @ invert d[0..7] depending on clip_sign[0..7] & a0_sign[0..7], or zero it if they match, and accumulate into P5[0..7]
++        vmls.i16        q9, q0, q5              @ invert d[8..15] depending on clip_sign[8..15] & a0_sign[8..15], or zero it if they match, and accumulate into P4[8..15]
++        vqmovun.s16     d4, q14
++        vmla.i16        q1, q0, q5              @ invert d[8..15] depending on clip_sign[8..15] & a0_sign[8..15], or zero it if they match, and accumulate into P5[8..15]
++        vqmovun.s16     d0, q6
++        vqmovun.s16     d5, q9
++        vqmovun.s16     d1, q1
++        vst1.64         {q2}, [r3 :128], r1
++        vst1.64         {q0}, [r3 :128]
++1:      vpop            {d8-d15}
++        bx              lr
++endfunc
++
++@ VC-1 in-loop deblocking filter for 16 pixel pairs at boundary of horizontally-neighbouring blocks
++@ On entry:
++@   r0 -> top-left pel of right block
++@   r1 = row stride, bytes
++@   r2 = PQUANT bitstream parameter
++function ff_vc1_h_loop_filter16_neon, export=1
++        push            {r4-r6,lr}
++        vpush           {d8-d15}
++        sub             r3, r0, #4              @ where to start reading
++        vldr            d0, .Lcoeffs
++        vld1.32         {d2}, [r3], r1          @ P1[0], P2[0]...
++        sub             r0, r0, #1              @ where to start writing
++        vld1.32         {d3}, [r3], r1
++        add             r4, r0, r1, lsl #2
++        vld1.32         {d10}, [r3], r1
++        vld1.32         {d11}, [r3], r1
++        vld1.32         {d16}, [r3], r1
++        vld1.32         {d4}, [r3], r1
++        vld1.32         {d8}, [r3], r1
++        vtrn.8          d2, d3                  @ P1[0], P1[1], P3[0]... P2[0], P2[1], P4[0]...
++        vld1.32         {d14}, [r3], r1
++        vld1.32         {d5}, [r3], r1
++        vtrn.8          d10, d11                @ P1[2], P1[3], P3[2]... P2[2], P2[3], P4[2]...
++        vld1.32         {d6}, [r3], r1
++        vld1.32         {d12}, [r3], r1
++        vtrn.8          d16, d4                 @ P1[4], P1[5], P3[4]... P2[4], P2[5], P4[4]...
++        vld1.32         {d13}, [r3], r1
++        vtrn.16         d2, d10                 @ P1[0], P1[1], P1[2], P1[3], P5[0]... P3[0], P3[1], P3[2], P3[3], P7[0]...
++        vld1.32         {d1}, [r3], r1
++        vtrn.8          d8, d14                 @ P1[6], P1[7], P3[6]... P2[6], P2[7], P4[6]...
++        vld1.32         {d7}, [r3], r1
++        vtrn.16         d3, d11                 @ P2[0], P2[1], P2[2], P2[3], P6[0]... P4[0], P4[1], P4[2], P4[3], P8[0]...
++        vld1.32         {d9}, [r3], r1
++        vtrn.8          d5, d6                  @ P1[8], P1[9], P3[8]... P2[8], P2[9], P4[8]...
++        vld1.32         {d15}, [r3]
++        vtrn.16         d16, d8                 @ P1[4], P1[5], P1[6], P1[7], P5[4]... P3[4], P3[5], P3[6], P3[7], P7[4]...
++        vtrn.16         d4, d14                 @ P2[4], P2[5], P2[6], P2[7], P6[4]... P4[4], P4[5], P4[6], P4[7], P8[4]...
++        vtrn.8          d12, d13                @ P1[10], P1[11], P3[10]... P2[10], P2[11], P4[10]...
++        vdup.16         q9, r2                  @ pq
++        vtrn.8          d1, d7                  @ P1[12], P1[13], P3[12]... P2[12], P2[13], P4[12]...
++        vtrn.32         d2, d16                 @ P1[0..7], P5[0..7]
++        vtrn.16         d5, d12                 @ P1[8], P1[7], P1[10], P1[11], P5[8]... P3[8], P3[9], P3[10], P3[11], P7[8]...
++        vtrn.16         d6, d13                 @ P2[8], P2[7], P2[10], P2[11], P6[8]... P4[8], P4[9], P4[10], P4[11], P8[8]...
++        vtrn.8          d9, d15                 @ P1[14], P1[15], P3[14]... P2[14], P2[15], P4[14]...
++        vtrn.32         d3, d4                  @ P2[0..7], P6[0..7]
++        vshll.u8        q10, d2, #1             @ 2*P1[0..7]
++        vtrn.32         d10, d8                 @ P3[0..7], P7[0..7]
++        vshll.u8        q11, d16, #1            @ 2*P5[0..7]
++        vtrn.32         d11, d14                @ P4[0..7], P8[0..7]
++        vtrn.16         d1, d9                  @ P1[12], P1[13], P1[14], P1[15], P5[12]... P3[12], P3[13], P3[14], P3[15], P7[12]...
++        vtrn.16         d7, d15                 @ P2[12], P2[13], P2[14], P2[15], P6[12]... P4[12], P4[13], P4[14], P4[15], P8[12]...
++        vmovl.u8        q1, d3                  @ P2[0..7]
++        vmovl.u8        q12, d4                 @ P6[0..7]
++        vtrn.32         d5, d1                  @ P1[8..15], P5[8..15]
++        vtrn.32         d6, d7                  @ P2[8..15], P6[8..15]
++        vtrn.32         d12, d9                 @ P3[8..15], P7[8..15]
++        vtrn.32         d13, d15                @ P4[8..15], P8[8..15]
++        vmls.i16        q10, q1, d0[1]          @ 2*P1[0..7]-5*P2[0..7]
++        vmovl.u8        q1, d10                 @ P3[0..7]
++        vshll.u8        q2, d5, #1              @ 2*P1[8..15]
++        vshll.u8        q13, d1, #1             @ 2*P5[8..15]
++        vmls.i16        q11, q12, d0[1]         @ 2*P5[0..7]-5*P6[0..7]
++        vmovl.u8        q14, d6                 @ P2[8..15]
++        vmovl.u8        q3, d7                  @ P6[8..15]
++        vmovl.u8        q15, d8                 @ P7[0..7]
++        vmla.i16        q10, q1, d0[1]          @ 2*P1[0..7]-5*P2[0..7]+5*P3[0..7]
++        vmovl.u8        q1, d12                 @ P3[8..15]
++        vmls.i16        q2, q14, d0[1]          @ 2*P1[8..15]-5*P2[8..15]
++        vmovl.u8        q4, d9                  @ P7[8..15]
++        vshll.u8        q14, d10, #1            @ 2*P3[0..7]
++        vmls.i16        q13, q3, d0[1]          @ 2*P5[8..15]-5*P6[8..15]
++        vmovl.u8        q5, d11                 @ P4[0..7]
++        vmla.i16        q11, q15, d0[1]         @ 2*P5[0..7]-5*P6[0..7]+5*P7[0..7]
++        vshll.u8        q15, d12, #1            @ 2*P3[8..15]
++        vmovl.u8        q6, d13                 @ P4[8..15]
++        vmla.i16        q2, q1, d0[1]           @ 2*P1[8..15]-5*P2[8..15]+5*P3[8..15]
++        vmovl.u8        q1, d14                 @ P8[0..7]
++        vmovl.u8        q7, d15                 @ P8[8..15]
++        vmla.i16        q13, q4, d0[1]          @ 2*P5[8..15]-5*P6[8..15]+5*P7[8..15]
++        vmovl.u8        q4, d16                 @ P5[0..7]
++        vmovl.u8        q8, d1                  @ P5[8..15]
++        vmls.i16        q14, q5, d0[1]          @ 2*P3[0..7]-5*P4[0..7]
++        vmls.i16        q15, q6, d0[1]          @ 2*P3[8..15]-5*P4[8..15]
++        vmls.i16        q10, q5, d0[0]          @ 2*P1[0..7]-5*P2[0..7]+5*P3[0..7]-2*P4[0..7]
++        vmls.i16        q11, q1, d0[0]          @ 2*P5[0..7]-5*P6[0..7]+5*P7[0..7]-2*P8[0..7]
++        vsub.i16        q1, q5, q4              @ P4[0..7]-P5[0..7]
++        vmls.i16        q2, q6, d0[0]           @ 2*P1[8..15]-5*P2[8..15]+5*P3[8..15]-2*P4[8..15]
++        vrshr.s16       q10, q10, #3
++        vmls.i16        q13, q7, d0[0]          @ 2*P5[8..15]-5*P6[8..15]+5*P7[8..15]-2*P8[8..15]
++        vsub.i16        q7, q6, q8              @ P4[8..15]-P5[8..15]
++        vrshr.s16       q11, q11, #3
++        vmla.s16        q14, q4, d0[1]          @ 2*P3[0..7]-5*P4[0..7]+5*P5[0..7]
++        vrshr.s16       q2, q2, #3
++        vmla.i16        q15, q8, d0[1]          @ 2*P3[8..15]-5*P4[8..15]+5*P5[8..15]
++        vabs.s16        q10, q10                @ a1[0..7]
++        vrshr.s16       q13, q13, #3
++        vmls.i16        q15, q3, d0[0]          @ 2*P3[8..15]-5*P4[8..15]+5*P5[8..15]-2*P6[8..15]
++        vabs.s16        q3, q11                 @ a2[0..7]
++        vabs.s16        q2, q2                  @ a1[8..15]
++        vmls.i16        q14, q12, d0[0]         @ 2*P3[0..7]-5*P4[0..7]+5*P5[0..7]-2*P6[0..7]
++        vabs.s16        q11, q1
++        vabs.s16        q12, q13                @ a2[8..15]
++        vcge.s16        q13, q10, q3            @ test a1[0..7] >= a2[0..7]
++        vshr.s16        q1, q1, #8              @ clip_sign[0..7]
++        vrshr.s16       q15, q15, #3
++        vshr.s16        q11, q11, #1            @ clip[0..7]
++        vrshr.s16       q14, q14, #3
++        vbsl            q13, q3, q10            @ a3[0..7]
++        vcge.s16        q3, q2, q12             @ test a1[8..15] >= a2[8.15]
++        vabs.s16        q10, q15                @ a0[8..15]
++        vshr.s16        q15, q15, #8            @ a0_sign[8..15]
++        vbsl            q3, q12, q2             @ a3[8..15]
++        vabs.s16        q2, q14                 @ a0[0..7]
++        vabs.s16        q12, q7
++        vshr.s16        q7, q7, #8              @ clip_sign[8..15]
++        vshr.s16        q14, q14, #8            @ a0_sign[0..7]
++        vshr.s16        q12, q12, #1            @ clip[8..15]
++        vsub.i16        q7, q7, q15             @ clip_sign[8..15] - a0_sign[8..15]
++        vqsub.u16       q15, q10, q3            @ a0[8..15] >= a3[8..15] ? a0[8..15]-a3[8..15] : 0  (a0 > a3 in all cases where filtering is enabled, so makes more sense to subtract this way round than the opposite and then taking the abs)
++        vcge.s16        q3, q3, q10             @ test a3[8..15] >= a0[8..15]
++        vcge.s16        q10, q10, q9            @ test a0[8..15] >= pq
++        vcge.s16        q9, q2, q9              @ test a0[0..7] >= pq
++        vsub.i16        q1, q1, q14             @ clip_sign[0..7] - a0_sign[0..7]
++        vqsub.u16       q14, q2, q13            @ a0[0..7] >= a3[0..7] ? a0[0..7]-a3[0..7] : 0  (a0 > a3 in all cases where filtering is enabled, so makes more sense to subtract this way round than the opposite and then taking the abs)
++        vcge.s16        q2, q13, q2             @ test a3[0..7] >= a0[0..7]
++        vmul.i16        q13, q15, d0[1]         @ a0[8..15] >= a3[8..15] ? 5*(a0[8..15]-a3[8..15]) : 0
++        vceq.i16        q15, q11, #0            @ test clip[0..7] == 0
++        vmul.i16        q0, q14, d0[1]          @ a0[0..7] >= a3[0..7] ? 5*(a0[0..7]-a3[0..7]) : 0
++        vorr            q9, q15, q9             @ test clip[0..7] == 0 || a0[0..7] >= pq
++        vceq.i16        q14, q12, #0            @ test clip[8..15] == 0
++        vshr.u16        q13, q13, #3            @ a0[8..15] >= a3[8..15] ? (5*(a0[8..15]-a3[8..15]))>>3 : 0
++        vorr            q2, q9, q2              @ test clip[0..7] == 0 || a0[0..7] >= pq || a3[0..7] >= a0[0..7]
++        vshr.u16        q0, q0, #3              @ a0[0..7] >= a3[0..7] ? (5*(a0[0..7]-a3[0..7]))>>3 : 0
++        vorr            q10, q14, q10           @ test clip[8..15] == 0 || a0[8..15] >= pq
++        vcge.s16        q14, q13, q12
++        vmov.32         r2, d4[1]               @ move to gp reg
++        vorr            q3, q10, q3             @ test clip[8..15] == 0 || a0[8..15] >= pq || a3[8..15] >= a0[8..15]
++        vmov.32         r3, d5[1]
++        vcge.s16        q2, q0, q11
++        vbsl            q14, q12, q13           @ FFMIN(d[8..15], clip[8..15])
++        vbsl            q2, q11, q0             @ FFMIN(d[0..7], clip[0..7])
++        vmov.32         r5, d6[1]
++        vbic            q0, q14, q10            @ set each d[8..15] to zero if it should not be filtered because clip[8..15] == 0 || a0[8..15] >= pq (a3 > a0 case already zeroed by saturating sub)
++        vmov.32         r6, d7[1]
++        and             r12, r2, r3
++        vbic            q2, q2, q9              @ set each d[0..7] to zero if it should not be filtered because clip[0..7] == 0 || a0[0..7] >= pq (a3 > a0 case already zeroed by saturating sub)
++        vmls.i16        q6, q0, q7              @ invert d[8..15] depending on clip_sign[8..15] & a0_sign[8..15], or zero it if they match, and accumulate into P4
++        vmls.i16        q5, q2, q1              @ invert d[0..7] depending on clip_sign[0..7] & a0_sign[0..7], or zero it if they match, and accumulate into P4
++        and             r14, r5, r6
++        vmla.i16        q4, q2, q1              @ invert d[0..7] depending on clip_sign[0..7] & a0_sign[0..7], or zero it if they match, and accumulate into P5
++        and             r12, r12, r14
++        vqmovun.s16     d4, q6
++        vmla.i16        q8, q0, q7              @ invert d[8..15] depending on clip_sign[8..15] & a0_sign[8..15], or zero it if they match, and accumulate into P5
++        tst             r12, #1
++        bne             4f                      @ none of the 16 pixel pairs should be updated in this case
++        vqmovun.s16     d2, q5
++        vqmovun.s16     d3, q4
++        vqmovun.s16     d5, q8
++        tst             r2, #1
++        bne             1f
++        vst2.8          {d2[0], d3[0]}, [r0], r1
++        vst2.8          {d2[1], d3[1]}, [r0], r1
++        vst2.8          {d2[2], d3[2]}, [r0], r1
++        vst2.8          {d2[3], d3[3]}, [r0]
++1:      add             r0, r4, r1, lsl #2
++        tst             r3, #1
++        bne             2f
++        vst2.8          {d2[4], d3[4]}, [r4], r1
++        vst2.8          {d2[5], d3[5]}, [r4], r1
++        vst2.8          {d2[6], d3[6]}, [r4], r1
++        vst2.8          {d2[7], d3[7]}, [r4]
++2:      add             r4, r0, r1, lsl #2
++        tst             r5, #1
++        bne             3f
++        vst2.8          {d4[0], d5[0]}, [r0], r1
++        vst2.8          {d4[1], d5[1]}, [r0], r1
++        vst2.8          {d4[2], d5[2]}, [r0], r1
++        vst2.8          {d4[3], d5[3]}, [r0]
++3:      tst             r6, #1
++        bne             4f
++        vst2.8          {d4[4], d5[4]}, [r4], r1
++        vst2.8          {d4[5], d5[5]}, [r4], r1
++        vst2.8          {d4[6], d5[6]}, [r4], r1
++        vst2.8          {d4[7], d5[7]}, [r4]
++4:      vpop            {d8-d15}
++        pop             {r4-r6,pc}
++endfunc
++
++@ Copy at most the specified number of bytes from source to destination buffer,
++@ stopping at a multiple of 16 bytes, none of which are the start of an escape sequence
++@ On entry:
++@   r0 -> source buffer
++@   r1 = max number of bytes to copy
++@   r2 -> destination buffer, optimally 8-byte aligned
++@ On exit:
++@   r0 = number of bytes not copied
++function ff_vc1_unescape_buffer_helper_neon, export=1
++        @ Offset by 48 to screen out cases that are too short for us to handle,
++        @ and also make it easy to test for loop termination, or to determine
++        @ whether we need an odd number of half-iterations of the loop.
++        subs    r1, r1, #48
++        bmi     90f
++
++        @ Set up useful constants
++        vmov.i32        q0, #0x3000000
++        vmov.i32        q1, #0x30000
++
++        tst             r1, #16
++        bne             1f
++
++          vld1.8          {q8, q9}, [r0]!
++          vbic            q12, q8, q0
++          vext.8          q13, q8, q9, #1
++          vext.8          q14, q8, q9, #2
++          vext.8          q15, q8, q9, #3
++          veor            q12, q12, q1
++          vbic            q13, q13, q0
++          vbic            q14, q14, q0
++          vbic            q15, q15, q0
++          vceq.i32        q12, q12, #0
++          veor            q13, q13, q1
++          veor            q14, q14, q1
++          veor            q15, q15, q1
++          vceq.i32        q13, q13, #0
++          vceq.i32        q14, q14, #0
++          vceq.i32        q15, q15, #0
++          add             r1, r1, #16
++          b               3f
++
++1:      vld1.8          {q10, q11}, [r0]!
++        vbic            q12, q10, q0
++        vext.8          q13, q10, q11, #1
++        vext.8          q14, q10, q11, #2
++        vext.8          q15, q10, q11, #3
++        veor            q12, q12, q1
++        vbic            q13, q13, q0
++        vbic            q14, q14, q0
++        vbic            q15, q15, q0
++        vceq.i32        q12, q12, #0
++        veor            q13, q13, q1
++        veor            q14, q14, q1
++        veor            q15, q15, q1
++        vceq.i32        q13, q13, #0
++        vceq.i32        q14, q14, #0
++        vceq.i32        q15, q15, #0
++        @ Drop through...
++2:        vmov            q8, q11
++          vld1.8          {q9}, [r0]!
++        vorr            q13, q12, q13
++        vorr            q15, q14, q15
++          vbic            q12, q8, q0
++        vorr            q3, q13, q15
++          vext.8          q13, q8, q9, #1
++          vext.8          q14, q8, q9, #2
++          vext.8          q15, q8, q9, #3
++          veor            q12, q12, q1
++        vorr            d6, d6, d7
++          vbic            q13, q13, q0
++          vbic            q14, q14, q0
++          vbic            q15, q15, q0
++          vceq.i32        q12, q12, #0
++        vmov            r3, r12, d6
++          veor            q13, q13, q1
++          veor            q14, q14, q1
++          veor            q15, q15, q1
++          vceq.i32        q13, q13, #0
++          vceq.i32        q14, q14, #0
++          vceq.i32        q15, q15, #0
++        orrs            r3, r3, r12
++        bne             90f
++        vst1.64         {q10}, [r2]!
++3:          vmov            q10, q9
++            vld1.8          {q11}, [r0]!
++          vorr            q13, q12, q13
++          vorr            q15, q14, q15
++            vbic            q12, q10, q0
++          vorr            q3, q13, q15
++            vext.8          q13, q10, q11, #1
++            vext.8          q14, q10, q11, #2
++            vext.8          q15, q10, q11, #3
++            veor            q12, q12, q1
++          vorr            d6, d6, d7
++            vbic            q13, q13, q0
++            vbic            q14, q14, q0
++            vbic            q15, q15, q0
++            vceq.i32        q12, q12, #0
++          vmov            r3, r12, d6
++            veor            q13, q13, q1
++            veor            q14, q14, q1
++            veor            q15, q15, q1
++            vceq.i32        q13, q13, #0
++            vceq.i32        q14, q14, #0
++            vceq.i32        q15, q15, #0
++          orrs            r3, r3, r12
++          bne             91f
++          vst1.64         {q8}, [r2]!
++        subs            r1, r1, #32
++        bpl             2b
++
++90:     add             r0, r1, #48
++        bx              lr
++
++91:     sub             r1, r1, #16
++        b               90b
++endfunc
 --- a/libavcodec/avcodec.h
 +++ b/libavcodec/avcodec.h
 @@ -2567,6 +2567,17 @@ typedef struct AVHWAccel {
@@ -15229,7 +17987,7 @@
 +     */
 +    void (*abort_frame)(AVCodecContext *avctx);
  } AVHWAccel;
-
+ 
  /**
 --- a/libavcodec/cabac.h
 +++ b/libavcodec/cabac.h
@@ -15253,7 +18011,7 @@
 +++ b/libavcodec/codec.h
 @@ -350,6 +350,17 @@ const AVCodec *av_codec_iterate(void **o
  AVCodec *avcodec_find_decoder(enum AVCodecID id);
-
+ 
  /**
 + * Find a registered decoder with a matching codec ID and pix_fmt.
 + * A decoder will pix_fmt set to NULL will match any fmt.
@@ -15761,12 +18519,788 @@
 +};
 +
 +#endif
+--- /dev/null
++++ b/libavcodec/hevc-ctrls-v3.h
+@@ -0,0 +1,255 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * These are the HEVC state controls for use with stateless HEVC
++ * codec drivers.
++ *
++ * It turns out that these structs are not stable yet and will undergo
++ * more changes. So keep them private until they are stable and ready to
++ * become part of the official public API.
++ */
++
++#ifndef _HEVC_CTRLS_H_
++#define _HEVC_CTRLS_H_
++
++#include <linux/videodev2.h>
++
++/* The pixel format isn't stable at the moment and will likely be renamed. */
++#define V4L2_PIX_FMT_HEVC_SLICE v4l2_fourcc('S', '2', '6', '5') /* HEVC parsed slices */
++
++#define V4L2_CID_MPEG_VIDEO_HEVC_SPS		(V4L2_CID_CODEC_BASE + 1008)
++#define V4L2_CID_MPEG_VIDEO_HEVC_PPS		(V4L2_CID_CODEC_BASE + 1009)
++#define V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS	(V4L2_CID_CODEC_BASE + 1010)
++#define V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX	(V4L2_CID_CODEC_BASE + 1011)
++#define V4L2_CID_MPEG_VIDEO_HEVC_DECODE_PARAMS	(V4L2_CID_CODEC_BASE + 1012)
++#define V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE	(V4L2_CID_CODEC_BASE + 1015)
++#define V4L2_CID_MPEG_VIDEO_HEVC_START_CODE	(V4L2_CID_CODEC_BASE + 1016)
++
++/* enum v4l2_ctrl_type type values */
++#define V4L2_CTRL_TYPE_HEVC_SPS 0x0120
++#define V4L2_CTRL_TYPE_HEVC_PPS 0x0121
++#define V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS 0x0122
++#define V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX 0x0123
++#define V4L2_CTRL_TYPE_HEVC_DECODE_PARAMS 0x0124
++
++enum v4l2_mpeg_video_hevc_decode_mode {
++	V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED,
++	V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_FRAME_BASED,
++};
++
++enum v4l2_mpeg_video_hevc_start_code {
++	V4L2_MPEG_VIDEO_HEVC_START_CODE_NONE,
++	V4L2_MPEG_VIDEO_HEVC_START_CODE_ANNEX_B,
++};
++
++#define V4L2_HEVC_SLICE_TYPE_B	0
++#define V4L2_HEVC_SLICE_TYPE_P	1
++#define V4L2_HEVC_SLICE_TYPE_I	2
++
++#define V4L2_HEVC_SPS_FLAG_SEPARATE_COLOUR_PLANE		(1ULL << 0)
++#define V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED			(1ULL << 1)
++#define V4L2_HEVC_SPS_FLAG_AMP_ENABLED				(1ULL << 2)
++#define V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET		(1ULL << 3)
++#define V4L2_HEVC_SPS_FLAG_PCM_ENABLED				(1ULL << 4)
++#define V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED		(1ULL << 5)
++#define V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT		(1ULL << 6)
++#define V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED		(1ULL << 7)
++#define V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED	(1ULL << 8)
++
++/* The controls are not stable at the moment and will likely be reworked. */
++struct v4l2_ctrl_hevc_sps {
++	/* ISO/IEC 23008-2, ITU-T Rec. H.265: Sequence parameter set */
++	__u16	pic_width_in_luma_samples;
++	__u16	pic_height_in_luma_samples;
++	__u8	bit_depth_luma_minus8;
++	__u8	bit_depth_chroma_minus8;
++	__u8	log2_max_pic_order_cnt_lsb_minus4;
++	__u8	sps_max_dec_pic_buffering_minus1;
++	__u8	sps_max_num_reorder_pics;
++	__u8	sps_max_latency_increase_plus1;
++	__u8	log2_min_luma_coding_block_size_minus3;
++	__u8	log2_diff_max_min_luma_coding_block_size;
++	__u8	log2_min_luma_transform_block_size_minus2;
++	__u8	log2_diff_max_min_luma_transform_block_size;
++	__u8	max_transform_hierarchy_depth_inter;
++	__u8	max_transform_hierarchy_depth_intra;
++	__u8	pcm_sample_bit_depth_luma_minus1;
++	__u8	pcm_sample_bit_depth_chroma_minus1;
++	__u8	log2_min_pcm_luma_coding_block_size_minus3;
++	__u8	log2_diff_max_min_pcm_luma_coding_block_size;
++	__u8	num_short_term_ref_pic_sets;
++	__u8	num_long_term_ref_pics_sps;
++	__u8	chroma_format_idc;
++	__u8	sps_max_sub_layers_minus1;
++
++	__u64	flags;
++};
++
++#define V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED	(1ULL << 0)
++#define V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT			(1ULL << 1)
++#define V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED		(1ULL << 2)
++#define V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT			(1ULL << 3)
++#define V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED		(1ULL << 4)
++#define V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED		(1ULL << 5)
++#define V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED			(1ULL << 6)
++#define V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT	(1ULL << 7)
++#define V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED			(1ULL << 8)
++#define V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED			(1ULL << 9)
++#define V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED		(1ULL << 10)
++#define V4L2_HEVC_PPS_FLAG_TILES_ENABLED			(1ULL << 11)
++#define V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED		(1ULL << 12)
++#define V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED	(1ULL << 13)
++#define V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED (1ULL << 14)
++#define V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED	(1ULL << 15)
++#define V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER	(1ULL << 16)
++#define V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT		(1ULL << 17)
++#define V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT (1ULL << 18)
++#define V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT	(1ULL << 19)
++#define V4L2_HEVC_PPS_FLAG_UNIFORM_SPACING			(1ULL << 20)
++
++struct v4l2_ctrl_hevc_pps {
++	/* ISO/IEC 23008-2, ITU-T Rec. H.265: Picture parameter set */
++	__u8	num_extra_slice_header_bits;
++	__u8	num_ref_idx_l0_default_active_minus1;
++	__u8	num_ref_idx_l1_default_active_minus1;
++	__s8	init_qp_minus26;
++	__u8	diff_cu_qp_delta_depth;
++	__s8	pps_cb_qp_offset;
++	__s8	pps_cr_qp_offset;
++	__u8	num_tile_columns_minus1;
++	__u8	num_tile_rows_minus1;
++	__u8	column_width_minus1[20];
++	__u8	row_height_minus1[22];
++	__s8	pps_beta_offset_div2;
++	__s8	pps_tc_offset_div2;
++	__u8	log2_parallel_merge_level_minus2;
++
++	__u8	padding[4];
++	__u64	flags;
++};
++
++#define V4L2_HEVC_DPB_ENTRY_LONG_TERM_REFERENCE	0x01
++
++#define V4L2_HEVC_DPB_ENTRIES_NUM_MAX		16
++
++struct v4l2_hevc_dpb_entry {
++	__u64	timestamp;
++	__u8	flags;
++	__u8	field_pic;
++	__u16	pic_order_cnt[2];
++	__u8	padding[2];
++};
++
++struct v4l2_hevc_pred_weight_table {
++	__s8	delta_luma_weight_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++	__s8	luma_offset_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++	__s8	delta_chroma_weight_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2];
++	__s8	chroma_offset_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2];
++
++	__s8	delta_luma_weight_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++	__s8	luma_offset_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++	__s8	delta_chroma_weight_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2];
++	__s8	chroma_offset_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2];
++
++	__u8	padding[6];
++
++	__u8	luma_log2_weight_denom;
++	__s8	delta_chroma_log2_weight_denom;
++};
++
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_LUMA		(1ULL << 0)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_CHROMA		(1ULL << 1)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_TEMPORAL_MVP_ENABLED	(1ULL << 2)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_MVD_L1_ZERO			(1ULL << 3)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_CABAC_INIT			(1ULL << 4)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_COLLOCATED_FROM_L0		(1ULL << 5)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_USE_INTEGER_MV		(1ULL << 6)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_DEBLOCKING_FILTER_DISABLED (1ULL << 7)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED (1ULL << 8)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_DEPENDENT_SLICE_SEGMENT	(1ULL << 9)
++
++struct v4l2_ctrl_hevc_slice_params {
++	__u32	bit_size;
++	__u32	data_bit_offset;
++
++	/* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
++	__u32	slice_segment_addr;
++	__u32	num_entry_point_offsets;
++
++	/* ISO/IEC 23008-2, ITU-T Rec. H.265: NAL unit header */
++	__u8	nal_unit_type;
++	__u8	nuh_temporal_id_plus1;
++
++	/* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
++	__u8	slice_type;
++	__u8	colour_plane_id;
++	__u16	slice_pic_order_cnt;
++	__u8	num_ref_idx_l0_active_minus1;
++	__u8	num_ref_idx_l1_active_minus1;
++	__u8	collocated_ref_idx;
++	__u8	five_minus_max_num_merge_cand;
++	__s8	slice_qp_delta;
++	__s8	slice_cb_qp_offset;
++	__s8	slice_cr_qp_offset;
++	__s8	slice_act_y_qp_offset;
++	__s8	slice_act_cb_qp_offset;
++	__s8	slice_act_cr_qp_offset;
++	__s8	slice_beta_offset_div2;
++	__s8	slice_tc_offset_div2;
++
++	/* ISO/IEC 23008-2, ITU-T Rec. H.265: Picture timing SEI message */
++	__u8	pic_struct;
++
++	/* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
++	__u8	ref_idx_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++	__u8	ref_idx_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++
++	__u8	padding[5];
++
++	__u32	entry_point_offset_minus1[256];
++
++	/* ISO/IEC 23008-2, ITU-T Rec. H.265: Weighted prediction parameter */
++	struct v4l2_hevc_pred_weight_table pred_weight_table;
++
++	__u64	flags;
++};
++
++#define V4L2_HEVC_DECODE_PARAM_FLAG_IRAP_PIC		0x1
++#define V4L2_HEVC_DECODE_PARAM_FLAG_IDR_PIC		0x2
++#define V4L2_HEVC_DECODE_PARAM_FLAG_NO_OUTPUT_OF_PRIOR  0x4
++
++struct v4l2_ctrl_hevc_decode_params {
++	__s32	pic_order_cnt_val;
++	__u8	num_active_dpb_entries;
++	struct	v4l2_hevc_dpb_entry dpb[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++	__u8	num_poc_st_curr_before;
++	__u8	num_poc_st_curr_after;
++	__u8	num_poc_lt_curr;
++	__u8	poc_st_curr_before[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++	__u8	poc_st_curr_after[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++	__u8	poc_lt_curr[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++	__u64	flags;
++};
++
++struct v4l2_ctrl_hevc_scaling_matrix {
++	__u8	scaling_list_4x4[6][16];
++	__u8	scaling_list_8x8[6][64];
++	__u8	scaling_list_16x16[6][64];
++	__u8	scaling_list_32x32[2][64];
++	__u8	scaling_list_dc_coef_16x16[6];
++	__u8	scaling_list_dc_coef_32x32[2];
++};
++
++/*  MPEG-class control IDs specific to the Hantro driver as defined by V4L2 */
++#define V4L2_CID_CODEC_HANTRO_BASE				(V4L2_CTRL_CLASS_CODEC | 0x1200)
++/*
++ * V4L2_CID_HANTRO_HEVC_SLICE_HEADER_SKIP -
++ * the number of data (in bits) to skip in the
++ * slice segment header.
++ * If non-IDR, the bits to be skipped go from syntax element "pic_output_flag"
++ * to before syntax element "slice_temporal_mvp_enabled_flag".
++ * If IDR, the skipped bits are just "pic_output_flag"
++ * (separate_colour_plane_flag is not supported).
++ */
++#define V4L2_CID_HANTRO_HEVC_SLICE_HEADER_SKIP	(V4L2_CID_CODEC_HANTRO_BASE + 0)
++
++#endif
+--- /dev/null
++++ b/libavcodec/hevc-ctrls-v4.h
+@@ -0,0 +1,515 @@
++/* SPDX-License-Identifier: ((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) */
++/*
++ *  Video for Linux Two controls header file
++ *
++ *  Copyright (C) 1999-2012 the contributors
++ *
++ *  This program is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation; either version 2 of the License, or
++ *  (at your option) any later version.
++ *
++ *  This program is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  Alternatively you can redistribute this file under the terms of the
++ *  BSD license as stated below:
++ *
++ *  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. The names of its contributors may not 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
++ *  OWNER 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.
++ *
++ *  The contents of this header was split off from videodev2.h. All control
++ *  definitions should be added to this header, which is included by
++ *  videodev2.h.
++ */
++
++#ifndef AVCODEC_HEVC_CTRLS_V4_H
++#define AVCODEC_HEVC_CTRLS_V4_H
++
++#include <linux/const.h>
++#include <linux/types.h>
++
++#define V4L2_CID_STATELESS_HEVC_SPS		(V4L2_CID_CODEC_STATELESS_BASE + 400)
++#define V4L2_CID_STATELESS_HEVC_PPS		(V4L2_CID_CODEC_STATELESS_BASE + 401)
++#define V4L2_CID_STATELESS_HEVC_SLICE_PARAMS	(V4L2_CID_CODEC_STATELESS_BASE + 402)
++#define V4L2_CID_STATELESS_HEVC_SCALING_MATRIX	(V4L2_CID_CODEC_STATELESS_BASE + 403)
++#define V4L2_CID_STATELESS_HEVC_DECODE_PARAMS	(V4L2_CID_CODEC_STATELESS_BASE + 404)
++#define V4L2_CID_STATELESS_HEVC_DECODE_MODE	(V4L2_CID_CODEC_STATELESS_BASE + 405)
++#define V4L2_CID_STATELESS_HEVC_START_CODE	(V4L2_CID_CODEC_STATELESS_BASE + 406)
++#define V4L2_CID_STATELESS_HEVC_ENTRY_POINT_OFFSETS (V4L2_CID_CODEC_STATELESS_BASE + 407)
++
++enum v4l2_stateless_hevc_decode_mode {
++	V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED,
++	V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED,
++};
++
++enum v4l2_stateless_hevc_start_code {
++	V4L2_STATELESS_HEVC_START_CODE_NONE,
++	V4L2_STATELESS_HEVC_START_CODE_ANNEX_B,
++};
++
++#define V4L2_HEVC_SLICE_TYPE_B	0
++#define V4L2_HEVC_SLICE_TYPE_P	1
++#define V4L2_HEVC_SLICE_TYPE_I	2
++
++#define V4L2_HEVC_SPS_FLAG_SEPARATE_COLOUR_PLANE		(1ULL << 0)
++#define V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED			(1ULL << 1)
++#define V4L2_HEVC_SPS_FLAG_AMP_ENABLED				(1ULL << 2)
++#define V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET		(1ULL << 3)
++#define V4L2_HEVC_SPS_FLAG_PCM_ENABLED				(1ULL << 4)
++#define V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED		(1ULL << 5)
++#define V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT		(1ULL << 6)
++#define V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED		(1ULL << 7)
++#define V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED	(1ULL << 8)
++
++/**
++ * struct v4l2_ctrl_hevc_sps - ITU-T Rec. H.265: Sequence parameter set
++ *
++ * @video_parameter_set_id: specifies the value of the
++ *			vps_video_parameter_set_id of the active VPS
++ * @seq_parameter_set_id: provides an identifier for the SPS for
++ *			  reference by other syntax elements
++ * @pic_width_in_luma_samples:	specifies the width of each decoded picture
++ *				in units of luma samples
++ * @pic_height_in_luma_samples: specifies the height of each decoded picture
++ *				in units of luma samples
++ * @bit_depth_luma_minus8: this value plus 8specifies the bit depth of the
++ *                         samples of the luma array
++ * @bit_depth_chroma_minus8: this value plus 8 specifies the bit depth of the
++ *                           samples of the chroma arrays
++ * @log2_max_pic_order_cnt_lsb_minus4: this value plus 4 specifies the value of
++ *                                     the variable MaxPicOrderCntLsb
++ * @sps_max_dec_pic_buffering_minus1: this value plus 1 specifies the maximum
++ *                                    required size of the decoded picture
++ *                                    buffer for the codec video sequence
++ * @sps_max_num_reorder_pics: indicates the maximum allowed number of pictures
++ * @sps_max_latency_increase_plus1: not equal to 0 is used to compute the
++ *				    value of SpsMaxLatencyPictures array
++ * @log2_min_luma_coding_block_size_minus3: plus 3 specifies the minimum
++ *					    luma coding block size
++ * @log2_diff_max_min_luma_coding_block_size: specifies the difference between
++ *					      the maximum and minimum luma
++ *					      coding block size
++ * @log2_min_luma_transform_block_size_minus2: plus 2 specifies the minimum luma
++ *					       transform block size
++ * @log2_diff_max_min_luma_transform_block_size: specifies the difference between
++ *						 the maximum and minimum luma
++ *						 transform block size
++ * @max_transform_hierarchy_depth_inter: specifies the maximum hierarchy
++ *					 depth for transform units of
++ *					 coding units coded in inter
++ *					 prediction mode
++ * @max_transform_hierarchy_depth_intra: specifies the maximum hierarchy
++ *					 depth for transform units of
++ *					 coding units coded in intra
++ *					 prediction mode
++ * @pcm_sample_bit_depth_luma_minus1: this value plus 1 specifies the number of
++ *                                    bits used to represent each of PCM sample
++ *                                    values of the luma component
++ * @pcm_sample_bit_depth_chroma_minus1: this value plus 1 specifies the number
++ *                                      of bits used to represent each of PCM
++ *                                      sample values of the chroma components
++ * @log2_min_pcm_luma_coding_block_size_minus3: this value plus 3 specifies the
++ *                                              minimum size of coding blocks
++ * @log2_diff_max_min_pcm_luma_coding_block_size: specifies the difference between
++ *						  the maximum and minimum size of
++ *						  coding blocks
++ * @num_short_term_ref_pic_sets: specifies the number of st_ref_pic_set()
++ *				 syntax structures included in the SPS
++ * @num_long_term_ref_pics_sps: specifies the number of candidate long-term
++ *				reference pictures that are specified in the SPS
++ * @chroma_format_idc: specifies the chroma sampling
++ * @sps_max_sub_layers_minus1: this value plus 1 specifies the maximum number
++ *                             of temporal sub-layers
++ * @reserved: padding field. Should be zeroed by applications.
++ * @flags: see V4L2_HEVC_SPS_FLAG_{}
++ */
++struct v4l2_ctrl_hevc_sps {
++	__u8	video_parameter_set_id;
++	__u8	seq_parameter_set_id;
++	__u16	pic_width_in_luma_samples;
++	__u16	pic_height_in_luma_samples;
++	__u8	bit_depth_luma_minus8;
++	__u8	bit_depth_chroma_minus8;
++	__u8	log2_max_pic_order_cnt_lsb_minus4;
++	__u8	sps_max_dec_pic_buffering_minus1;
++	__u8	sps_max_num_reorder_pics;
++	__u8	sps_max_latency_increase_plus1;
++	__u8	log2_min_luma_coding_block_size_minus3;
++	__u8	log2_diff_max_min_luma_coding_block_size;
++	__u8	log2_min_luma_transform_block_size_minus2;
++	__u8	log2_diff_max_min_luma_transform_block_size;
++	__u8	max_transform_hierarchy_depth_inter;
++	__u8	max_transform_hierarchy_depth_intra;
++	__u8	pcm_sample_bit_depth_luma_minus1;
++	__u8	pcm_sample_bit_depth_chroma_minus1;
++	__u8	log2_min_pcm_luma_coding_block_size_minus3;
++	__u8	log2_diff_max_min_pcm_luma_coding_block_size;
++	__u8	num_short_term_ref_pic_sets;
++	__u8	num_long_term_ref_pics_sps;
++	__u8	chroma_format_idc;
++	__u8	sps_max_sub_layers_minus1;
++
++	__u8	reserved[6];
++	__u64	flags;
++};
++
++#define V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED	(1ULL << 0)
++#define V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT			(1ULL << 1)
++#define V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED		(1ULL << 2)
++#define V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT			(1ULL << 3)
++#define V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED		(1ULL << 4)
++#define V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED		(1ULL << 5)
++#define V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED			(1ULL << 6)
++#define V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT	(1ULL << 7)
++#define V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED			(1ULL << 8)
++#define V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED			(1ULL << 9)
++#define V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED		(1ULL << 10)
++#define V4L2_HEVC_PPS_FLAG_TILES_ENABLED			(1ULL << 11)
++#define V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED		(1ULL << 12)
++#define V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED	(1ULL << 13)
++#define V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED (1ULL << 14)
++#define V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED	(1ULL << 15)
++#define V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER	(1ULL << 16)
++#define V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT		(1ULL << 17)
++#define V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT (1ULL << 18)
++#define V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT	(1ULL << 19)
++#define V4L2_HEVC_PPS_FLAG_UNIFORM_SPACING			(1ULL << 20)
++
++/**
++ * struct v4l2_ctrl_hevc_pps - ITU-T Rec. H.265: Picture parameter set
++ *
++ * @pic_parameter_set_id: identifies the PPS for reference by other
++ *			  syntax elements
++ * @num_extra_slice_header_bits: specifies the number of extra slice header
++ *				 bits that are present in the slice header RBSP
++ *				 for coded pictures referring to the PPS.
++ * @num_ref_idx_l0_default_active_minus1: this value plus 1 specifies the
++ *                                        inferred value of num_ref_idx_l0_active_minus1
++ * @num_ref_idx_l1_default_active_minus1: this value plus 1 specifies the
++ *                                        inferred value of num_ref_idx_l1_active_minus1
++ * @init_qp_minus26: this value plus 26 specifies the initial value of SliceQp Y for
++ *		     each slice referring to the PPS
++ * @diff_cu_qp_delta_depth: specifies the difference between the luma coding
++ *			    tree block size and the minimum luma coding block
++ *			    size of coding units that convey cu_qp_delta_abs
++ *			    and cu_qp_delta_sign_flag
++ * @pps_cb_qp_offset: specify the offsets to the luma quantization parameter Cb
++ * @pps_cr_qp_offset: specify the offsets to the luma quantization parameter Cr
++ * @num_tile_columns_minus1: this value plus 1 specifies the number of tile columns
++ *			     partitioning the picture
++ * @num_tile_rows_minus1: this value plus 1 specifies the number of tile rows partitioning
++ *			  the picture
++ * @column_width_minus1: this value plus 1 specifies the width of the each tile column in
++ *			 units of coding tree blocks
++ * @row_height_minus1: this value plus 1 specifies the height of the each tile row in
++ *		       units of coding tree blocks
++ * @pps_beta_offset_div2: specify the default deblocking parameter offsets for
++ *			  beta divided by 2
++ * @pps_tc_offset_div2: specify the default deblocking parameter offsets for tC
++ *			divided by 2
++ * @log2_parallel_merge_level_minus2: this value plus 2 specifies the value of
++ *                                    the variable Log2ParMrgLevel
++ * @reserved: padding field. Should be zeroed by applications.
++ * @flags: see V4L2_HEVC_PPS_FLAG_{}
++ */
++struct v4l2_ctrl_hevc_pps {
++	__u8	pic_parameter_set_id;
++	__u8	num_extra_slice_header_bits;
++	__u8	num_ref_idx_l0_default_active_minus1;
++	__u8	num_ref_idx_l1_default_active_minus1;
++	__s8	init_qp_minus26;
++	__u8	diff_cu_qp_delta_depth;
++	__s8	pps_cb_qp_offset;
++	__s8	pps_cr_qp_offset;
++	__u8	num_tile_columns_minus1;
++	__u8	num_tile_rows_minus1;
++	__u8	column_width_minus1[20];
++	__u8	row_height_minus1[22];
++	__s8	pps_beta_offset_div2;
++	__s8	pps_tc_offset_div2;
++	__u8	log2_parallel_merge_level_minus2;
++	__u8	reserved;
++	__u64	flags;
++};
++
++#define V4L2_HEVC_DPB_ENTRY_LONG_TERM_REFERENCE	0x01
++
++#define V4L2_HEVC_SEI_PIC_STRUCT_FRAME				0
++#define V4L2_HEVC_SEI_PIC_STRUCT_TOP_FIELD			1
++#define V4L2_HEVC_SEI_PIC_STRUCT_BOTTOM_FIELD			2
++#define V4L2_HEVC_SEI_PIC_STRUCT_TOP_BOTTOM			3
++#define V4L2_HEVC_SEI_PIC_STRUCT_BOTTOM_TOP			4
++#define V4L2_HEVC_SEI_PIC_STRUCT_TOP_BOTTOM_TOP			5
++#define V4L2_HEVC_SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM		6
++#define V4L2_HEVC_SEI_PIC_STRUCT_FRAME_DOUBLING			7
++#define V4L2_HEVC_SEI_PIC_STRUCT_FRAME_TRIPLING			8
++#define V4L2_HEVC_SEI_PIC_STRUCT_TOP_PAIRED_PREVIOUS_BOTTOM	9
++#define V4L2_HEVC_SEI_PIC_STRUCT_BOTTOM_PAIRED_PREVIOUS_TOP	10
++#define V4L2_HEVC_SEI_PIC_STRUCT_TOP_PAIRED_NEXT_BOTTOM		11
++#define V4L2_HEVC_SEI_PIC_STRUCT_BOTTOM_PAIRED_NEXT_TOP		12
++
++#define V4L2_HEVC_DPB_ENTRIES_NUM_MAX		16
++
++/**
++ * struct v4l2_hevc_dpb_entry - HEVC decoded picture buffer entry
++ *
++ * @timestamp: timestamp of the V4L2 capture buffer to use as reference.
++ * @flags: long term flag for the reference frame
++ * @field_pic: whether the reference is a field picture or a frame.
++ * @reserved: padding field. Should be zeroed by applications.
++ * @pic_order_cnt_val: the picture order count of the current picture.
++ */
++struct v4l2_hevc_dpb_entry {
++	__u64	timestamp;
++	__u8	flags;
++	__u8	field_pic;
++	__u16	reserved;
++	__s32	pic_order_cnt_val;
++};
++
++/**
++ * struct v4l2_hevc_pred_weight_table - HEVC weighted prediction parameters
++ *
++ * @delta_luma_weight_l0: the difference of the weighting factor applied
++ *			  to the luma prediction value for list 0
++ * @luma_offset_l0: the additive offset applied to the luma prediction value
++ *		    for list 0
++ * @delta_chroma_weight_l0: the difference of the weighting factor applied
++ *			    to the chroma prediction values for list 0
++ * @chroma_offset_l0: the difference of the additive offset applied to
++ *		      the chroma prediction values for list 0
++ * @delta_luma_weight_l1: the difference of the weighting factor applied
++ *			  to the luma prediction value for list 1
++ * @luma_offset_l1: the additive offset applied to the luma prediction value
++ *		    for list 1
++ * @delta_chroma_weight_l1: the difference of the weighting factor applied
++ *			    to the chroma prediction values for list 1
++ * @chroma_offset_l1: the difference of the additive offset applied to
++ *		      the chroma prediction values for list 1
++ * @luma_log2_weight_denom: the base 2 logarithm of the denominator for
++ *			    all luma weighting factors
++ * @delta_chroma_log2_weight_denom: the difference of the base 2 logarithm
++ *				    of the denominator for all chroma
++ *				    weighting factors
++ */
++struct v4l2_hevc_pred_weight_table {
++	__s8	delta_luma_weight_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++	__s8	luma_offset_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++	__s8	delta_chroma_weight_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2];
++	__s8	chroma_offset_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2];
++
++	__s8	delta_luma_weight_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++	__s8	luma_offset_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++	__s8	delta_chroma_weight_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2];
++	__s8	chroma_offset_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2];
++
++	__u8	luma_log2_weight_denom;
++	__s8	delta_chroma_log2_weight_denom;
++};
++
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_LUMA		(1ULL << 0)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_CHROMA		(1ULL << 1)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_TEMPORAL_MVP_ENABLED	(1ULL << 2)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_MVD_L1_ZERO			(1ULL << 3)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_CABAC_INIT			(1ULL << 4)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_COLLOCATED_FROM_L0		(1ULL << 5)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_USE_INTEGER_MV		(1ULL << 6)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_DEBLOCKING_FILTER_DISABLED (1ULL << 7)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED (1ULL << 8)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_DEPENDENT_SLICE_SEGMENT	(1ULL << 9)
++
++/**
++ * struct v4l2_ctrl_hevc_slice_params - HEVC slice parameters
++ *
++ * This control is a dynamically sized 1-dimensional array,
++ * V4L2_CTRL_FLAG_DYNAMIC_ARRAY flag must be set when using it.
++ *
++ * @bit_size: size (in bits) of the current slice data
++ * @data_byte_offset: offset (in bytes) to the video data in the current slice data
++ * @num_entry_point_offsets: specifies the number of entry point offset syntax
++ *			     elements in the slice header.
++ * @nal_unit_type: specifies the coding type of the slice (B, P or I)
++ * @nuh_temporal_id_plus1: minus 1 specifies a temporal identifier for the NAL unit
++ * @slice_type: see V4L2_HEVC_SLICE_TYPE_{}
++ * @colour_plane_id: specifies the colour plane associated with the current slice
++ * @slice_pic_order_cnt: specifies the picture order count
++ * @num_ref_idx_l0_active_minus1: this value plus 1 specifies the maximum
++ *                                reference index for reference picture list 0
++ *                                that may be used to decode the slice
++ * @num_ref_idx_l1_active_minus1: this value plus 1 specifies the maximum
++ *                                reference index for reference picture list 1
++ *                                that may be used to decode the slice
++ * @collocated_ref_idx: specifies the reference index of the collocated picture used
++ *			for temporal motion vector prediction
++ * @five_minus_max_num_merge_cand: specifies the maximum number of merging
++ *				   motion vector prediction candidates supported in
++ *				   the slice subtracted from 5
++ * @slice_qp_delta: specifies the initial value of QpY to be used for the coding
++ *		    blocks in the slice
++ * @slice_cb_qp_offset: specifies a difference to be added to the value of pps_cb_qp_offset
++ * @slice_cr_qp_offset: specifies a difference to be added to the value of pps_cr_qp_offset
++ * @slice_act_y_qp_offset: screen content extension parameters
++ * @slice_act_cb_qp_offset: screen content extension parameters
++ * @slice_act_cr_qp_offset: screen content extension parameters
++ * @slice_beta_offset_div2: specify the deblocking parameter offsets for beta divided by 2
++ * @slice_tc_offset_div2: specify the deblocking parameter offsets for tC divided by 2
++ * @pic_struct: indicates whether a picture should be displayed as a frame or as one or
++ *		more fields
++ * @reserved0: padding field. Should be zeroed by applications.
++ * @slice_segment_addr: specifies the address of the first coding tree block in
++ *			the slice segment
++ * @ref_idx_l0: the list of L0 reference elements as indices in the DPB
++ * @ref_idx_l1: the list of L1 reference elements as indices in the DPB
++ * @short_term_ref_pic_set_size: specifies the size of short-term reference
++ *				 pictures set included in the SPS
++ * @long_term_ref_pic_set_size: specifies the size of long-term reference
++ *				pictures set include in the SPS
++ * @pred_weight_table: the prediction weight coefficients for inter-picture
++ *		       prediction
++ * @reserved1: padding field. Should be zeroed by applications.
++ * @flags: see V4L2_HEVC_SLICE_PARAMS_FLAG_{}
++ */
++struct v4l2_ctrl_hevc_slice_params {
++	__u32	bit_size;
++	__u32	data_byte_offset;
++	__u32	num_entry_point_offsets;
++
++	/* ISO/IEC 23008-2, ITU-T Rec. H.265: NAL unit header */
++	__u8	nal_unit_type;
++	__u8	nuh_temporal_id_plus1;
++
++	/* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
++	__u8	slice_type;
++	__u8	colour_plane_id;
++	__s32	slice_pic_order_cnt;
++	__u8	num_ref_idx_l0_active_minus1;
++	__u8	num_ref_idx_l1_active_minus1;
++	__u8	collocated_ref_idx;
++	__u8	five_minus_max_num_merge_cand;
++	__s8	slice_qp_delta;
++	__s8	slice_cb_qp_offset;
++	__s8	slice_cr_qp_offset;
++	__s8	slice_act_y_qp_offset;
++	__s8	slice_act_cb_qp_offset;
++	__s8	slice_act_cr_qp_offset;
++	__s8	slice_beta_offset_div2;
++	__s8	slice_tc_offset_div2;
++
++	/* ISO/IEC 23008-2, ITU-T Rec. H.265: Picture timing SEI message */
++	__u8	pic_struct;
++
++	__u8	reserved0[3];
++	/* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
++	__u32	slice_segment_addr;
++	__u8	ref_idx_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++	__u8	ref_idx_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++	__u16	short_term_ref_pic_set_size;
++	__u16	long_term_ref_pic_set_size;
++
++	/* ISO/IEC 23008-2, ITU-T Rec. H.265: Weighted prediction parameter */
++	struct v4l2_hevc_pred_weight_table pred_weight_table;
++
++	__u8	reserved1[2];
++	__u64	flags;
++};
++
++#define V4L2_HEVC_DECODE_PARAM_FLAG_IRAP_PIC		0x1
++#define V4L2_HEVC_DECODE_PARAM_FLAG_IDR_PIC		0x2
++#define V4L2_HEVC_DECODE_PARAM_FLAG_NO_OUTPUT_OF_PRIOR  0x4
++
++/**
++ * struct v4l2_ctrl_hevc_decode_params - HEVC decode parameters
++ *
++ * @pic_order_cnt_val: picture order count
++ * @short_term_ref_pic_set_size: specifies the size of short-term reference
++ *				 pictures set included in the SPS of the first slice
++ * @long_term_ref_pic_set_size: specifies the size of long-term reference
++ *				pictures set include in the SPS of the first slice
++ * @num_active_dpb_entries: the number of entries in dpb
++ * @num_poc_st_curr_before: the number of reference pictures in the short-term
++ *			    set that come before the current frame
++ * @num_poc_st_curr_after: the number of reference pictures in the short-term
++ *			   set that come after the current frame
++ * @num_poc_lt_curr: the number of reference pictures in the long-term set
++ * @poc_st_curr_before: provides the index of the short term before references
++ *			in DPB array
++ * @poc_st_curr_after: provides the index of the short term after references
++ *		       in DPB array
++ * @poc_lt_curr: provides the index of the long term references in DPB array
++ * @reserved: padding field. Should be zeroed by applications.
++ * @dpb: the decoded picture buffer, for meta-data about reference frames
++ * @flags: see V4L2_HEVC_DECODE_PARAM_FLAG_{}
++ */
++struct v4l2_ctrl_hevc_decode_params {
++	__s32	pic_order_cnt_val;
++	__u16	short_term_ref_pic_set_size;
++	__u16	long_term_ref_pic_set_size;
++	__u8	num_active_dpb_entries;
++	__u8	num_poc_st_curr_before;
++	__u8	num_poc_st_curr_after;
++	__u8	num_poc_lt_curr;
++	__u8	poc_st_curr_before[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++	__u8	poc_st_curr_after[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++	__u8	poc_lt_curr[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++	__u8	reserved[4];
++	struct	v4l2_hevc_dpb_entry dpb[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++	__u64	flags;
++};
++
++/**
++ * struct v4l2_ctrl_hevc_scaling_matrix - HEVC scaling lists parameters
++ *
++ * @scaling_list_4x4: scaling list is used for the scaling process for
++ *		      transform coefficients. The values on each scaling
++ *		      list are expected in raster scan order
++ * @scaling_list_8x8: scaling list is used for the scaling process for
++ *		      transform coefficients. The values on each scaling
++ *		      list are expected in raster scan order
++ * @scaling_list_16x16:	scaling list is used for the scaling process for
++ *			transform coefficients. The values on each scaling
++ *			list are expected in raster scan order
++ * @scaling_list_32x32:	scaling list is used for the scaling process for
++ *			transform coefficients. The values on each scaling
++ *			list are expected in raster scan order
++ * @scaling_list_dc_coef_16x16:	scaling list is used for the scaling process
++ *				for transform coefficients. The values on each
++ *				scaling list are expected in raster scan order.
++ * @scaling_list_dc_coef_32x32:	scaling list is used for the scaling process
++ *				for transform coefficients. The values on each
++ *				scaling list are expected in raster scan order.
++ */
++struct v4l2_ctrl_hevc_scaling_matrix {
++	__u8	scaling_list_4x4[6][16];
++	__u8	scaling_list_8x8[6][64];
++	__u8	scaling_list_16x16[6][64];
++	__u8	scaling_list_32x32[2][64];
++	__u8	scaling_list_dc_coef_16x16[6];
++	__u8	scaling_list_dc_coef_32x32[2];
++};
++
++#endif
 --- a/libavcodec/hevc_parser.c
 +++ b/libavcodec/hevc_parser.c
 @@ -98,6 +98,19 @@ static int hevc_parse_slice_header(AVCod
      avctx->profile  = ps->sps->ptl.general_ptl.profile_idc;
      avctx->level    = ps->sps->ptl.general_ptl.level_idc;
-
+ 
 +    if (ps->sps->chroma_format_idc == 1) {
 +        avctx->chroma_sample_location = ps->sps->vui.chroma_loc_info_present_flag ?
 +            ps->sps->vui.chroma_sample_loc_type_top_field + 1 :
@@ -15783,12 +19317,69 @@
      if (ps->vps->vps_timing_info_present_flag) {
          num = ps->vps->vps_num_units_in_tick;
          den = ps->vps->vps_time_scale;
+--- a/libavcodec/hevc_refs.c
++++ b/libavcodec/hevc_refs.c
+@@ -96,18 +96,22 @@ static HEVCFrame *alloc_frame(HEVCContex
+         if (!frame->rpl_buf)
+             goto fail;
+ 
+-        frame->tab_mvf_buf = av_buffer_pool_get(s->tab_mvf_pool);
+-        if (!frame->tab_mvf_buf)
+-            goto fail;
+-        frame->tab_mvf = (MvField *)frame->tab_mvf_buf->data;
++        if (s->tab_mvf_pool) {
++            frame->tab_mvf_buf = av_buffer_pool_get(s->tab_mvf_pool);
++            if (!frame->tab_mvf_buf)
++                goto fail;
++            frame->tab_mvf = (MvField *)frame->tab_mvf_buf->data;
++        }
+ 
+-        frame->rpl_tab_buf = av_buffer_pool_get(s->rpl_tab_pool);
+-        if (!frame->rpl_tab_buf)
+-            goto fail;
+-        frame->rpl_tab   = (RefPicListTab **)frame->rpl_tab_buf->data;
+-        frame->ctb_count = s->ps.sps->ctb_width * s->ps.sps->ctb_height;
+-        for (j = 0; j < frame->ctb_count; j++)
+-            frame->rpl_tab[j] = (RefPicListTab *)frame->rpl_buf->data;
++        if (s->rpl_tab_pool) {
++            frame->rpl_tab_buf = av_buffer_pool_get(s->rpl_tab_pool);
++            if (!frame->rpl_tab_buf)
++                goto fail;
++            frame->rpl_tab   = (RefPicListTab **)frame->rpl_tab_buf->data;
++            frame->ctb_count = s->ps.sps->ctb_width * s->ps.sps->ctb_height;
++            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);
+@@ -276,14 +280,17 @@ static int init_slice_rpl(HEVCContext *s
+     int ctb_count    = frame->ctb_count;
+     int ctb_addr_ts  = s->ps.pps->ctb_addr_rs_to_ts[s->sh.slice_segment_addr];
+     int i;
++    RefPicListTab * const tab = (RefPicListTab *)frame->rpl_buf->data + s->slice_idx;
+ 
+     if (s->slice_idx >= frame->rpl_buf->size / sizeof(RefPicListTab))
+         return AVERROR_INVALIDDATA;
+ 
+-    for (i = ctb_addr_ts; i < ctb_count; i++)
+-        frame->rpl_tab[i] = (RefPicListTab *)frame->rpl_buf->data + s->slice_idx;
++    if (frame->rpl_tab) {
++        for (i = ctb_addr_ts; i < ctb_count; i++)
++            frame->rpl_tab[i] = tab;
++    }
+ 
+-    frame->refPicList = (RefPicList *)frame->rpl_tab[ctb_addr_ts];
++    frame->refPicList = tab->refPicList;
+ 
+     return 0;
+ }
 --- a/libavcodec/hevcdec.c
 +++ b/libavcodec/hevcdec.c
 @@ -332,6 +332,19 @@ static void export_stream_params(HEVCCon
-
+ 
      ff_set_sar(avctx, sps->vui.sar);
-
+ 
 +    if (sps->chroma_format_idc == 1) {
 +        avctx->chroma_sample_location = sps->vui.chroma_loc_info_present_flag ?
 +            sps->vui.chroma_sample_loc_type_top_field + 1 :
@@ -15816,7 +19407,7 @@
 +                     CONFIG_HEVC_RPI4_10_HWACCEL + \
                       CONFIG_HEVC_VDPAU_HWACCEL)
      enum AVPixelFormat pix_fmts[HWACCEL_MAX + 2], *fmt = pix_fmts;
-
+ 
      switch (sps->pix_fmt) {
      case AV_PIX_FMT_YUV420P:
      case AV_PIX_FMT_YUVJ420P:
@@ -15851,7 +19442,43 @@
          break;
      case AV_PIX_FMT_YUV444P:
  #if CONFIG_HEVC_VDPAU_HWACCEL
-@@ -3230,7 +3258,14 @@ static int hevc_decode_frame(AVCodecCont
+@@ -459,6 +487,16 @@ static int set_sps(HEVCContext *s, const
+     if (!sps)
+         return 0;
+ 
++    // If hwaccel then we don't need all the s/w decode helper arrays
++    if (s->avctx->hwaccel) {
++        export_stream_params(s, sps);
++
++        s->avctx->pix_fmt = pix_fmt;
++        s->ps.sps = sps;
++        s->ps.vps = (HEVCVPS*) s->ps.vps_list[s->ps.sps->vps_id]->data;
++        return 0;
++    }
++
+     ret = pic_arrays_init(s, sps);
+     if (ret < 0)
+         goto fail;
+@@ -2809,11 +2847,13 @@ static int hevc_frame_start(HEVCContext
+                            ((s->ps.sps->height >> s->ps.sps->log2_min_cb_size) + 1);
+     int ret;
+ 
+-    memset(s->horizontal_bs, 0, s->bs_width * s->bs_height);
+-    memset(s->vertical_bs,   0, s->bs_width * s->bs_height);
+-    memset(s->cbf_luma,      0, s->ps.sps->min_tb_width * s->ps.sps->min_tb_height);
+-    memset(s->is_pcm,        0, (s->ps.sps->min_pu_width + 1) * (s->ps.sps->min_pu_height + 1));
+-    memset(s->tab_slice_address, -1, pic_size_in_ctb * sizeof(*s->tab_slice_address));
++    if (s->horizontal_bs) {
++        memset(s->horizontal_bs, 0, s->bs_width * s->bs_height);
++        memset(s->vertical_bs,   0, s->bs_width * s->bs_height);
++        memset(s->cbf_luma,      0, s->ps.sps->min_tb_width * s->ps.sps->min_tb_height);
++        memset(s->is_pcm,        0, (s->ps.sps->min_pu_width + 1) * (s->ps.sps->min_pu_height + 1));
++        memset(s->tab_slice_address, -1, pic_size_in_ctb * sizeof(*s->tab_slice_address));
++    }
+ 
+     s->is_decoded        = 0;
+     s->first_nal_type    = s->nal_unit_type;
+@@ -3230,7 +3270,14 @@ static int hevc_decode_frame(AVCodecCont
      s->ref = NULL;
      ret    = decode_nal_units(s, avpkt->data, avpkt->size);
      if (ret < 0)
@@ -15863,10 +19490,38 @@
 +
          return ret;
 +    }
-
+ 
      if (avctx->hwaccel) {
          if (s->ref && (ret = avctx->hwaccel->end_frame(avctx)) < 0) {
-@@ -3585,6 +3620,15 @@ AVCodec ff_hevc_decoder = {
+@@ -3273,15 +3320,19 @@ static int hevc_ref_frame(HEVCContext *s
+     if (ret < 0)
+         return ret;
+ 
+-    dst->tab_mvf_buf = av_buffer_ref(src->tab_mvf_buf);
+-    if (!dst->tab_mvf_buf)
+-        goto fail;
+-    dst->tab_mvf = src->tab_mvf;
++    if (src->tab_mvf_buf) {
++        dst->tab_mvf_buf = av_buffer_ref(src->tab_mvf_buf);
++        if (!dst->tab_mvf_buf)
++            goto fail;
++        dst->tab_mvf = src->tab_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;
++    if (src->rpl_tab_buf) {
++        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)
+@@ -3585,6 +3636,15 @@ AVCodec ff_hevc_decoder = {
  #if CONFIG_HEVC_NVDEC_HWACCEL
                                 HWACCEL_NVDEC(hevc),
  #endif
@@ -15897,12 +19552,12 @@
 --- a/libavcodec/hwconfig.h
 +++ b/libavcodec/hwconfig.h
 @@ -24,6 +24,7 @@
-
-
+ 
+ 
  #define HWACCEL_CAP_ASYNC_SAFE      (1 << 0)
 +#define HWACCEL_CAP_MT_SAFE         (1 << 1)
-
-
+ 
+ 
  typedef struct AVCodecHWConfigInternal {
 @@ -70,6 +71,12 @@ typedef struct AVCodecHWConfigInternal {
      HW_CONFIG_HWACCEL(1, 1, 0, D3D11,        D3D11VA,      ff_ ## codec ## _d3d11va2_hwaccel)
@@ -15922,7 +19577,7 @@
 @@ -24,6 +24,9 @@
   * MMAL Video Decoder
   */
-
+ 
 +#pragma GCC diagnostic push
 +// Many many redundant decls in the header files
 +#pragma GCC diagnostic ignored "-Wredundant-decls"
@@ -15935,12 +19590,12 @@
  #include <interface/mmal/vc/mmal_vc_api.h>
 +#pragma GCC diagnostic pop
  #include <stdatomic.h>
-
+ 
  #include "avcodec.h"
 --- a/libavcodec/pthread_frame.c
 +++ b/libavcodec/pthread_frame.c
 @@ -191,7 +191,8 @@ static attribute_align_arg void *frame_w
-
+ 
          /* if the previous thread uses hwaccel then we take the lock to ensure
           * the threads don't run concurrently */
 -        if (avctx->hwaccel) {
@@ -15950,9 +19605,9 @@
              p->hwaccel_serializing = 1;
          }
 @@ -614,7 +615,9 @@ void ff_thread_finish_setup(AVCodecConte
-
+ 
      if (!(avctx->active_thread_type&FF_THREAD_FRAME)) return;
-
+ 
 -    if (avctx->hwaccel && !p->hwaccel_serializing) {
 +    if (avctx->hwaccel &&
 +        !(avctx->hwaccel->caps_internal & HWACCEL_CAP_MT_SAFE) &&
@@ -15965,7 +19620,7 @@
 @@ -293,6 +293,12 @@ const PixelFormatTag ff_raw_pix_fmt_tags
      { AV_PIX_FMT_RGB565LE,MKTAG( 3 ,  0 ,  0 ,  0 ) }, /* flipped RGB565LE */
      { AV_PIX_FMT_YUV444P, MKTAG('Y', 'V', '2', '4') }, /* YUV444P, swapped UV */
-
+ 
 +    /* RPI (Might as well define for everything) */
 +    { AV_PIX_FMT_SAND128,     MKTAG('S', 'A', 'N', 'D') },
 +    { AV_PIX_FMT_RPI4_8,      MKTAG('S', 'A', 'N', 'D') },
@@ -15974,13 +19629,13 @@
 +
      { AV_PIX_FMT_NONE, 0 },
  };
-
+ 
 --- a/libavcodec/rawenc.c
 +++ b/libavcodec/rawenc.c
 @@ -24,6 +24,7 @@
   * Raw Video Encoder
   */
-
+ 
 +#include "config.h"
  #include "avcodec.h"
  #include "raw.h"
@@ -15993,13 +19648,13 @@
 +#if CONFIG_SAND
 +#include "libavutil/rpi_sand_fns.h"
 +#endif
-
+ 
  static av_cold int raw_encode_init(AVCodecContext *avctx)
  {
 @@ -49,22 +54,114 @@ FF_ENABLE_DEPRECATION_WARNINGS
      return 0;
  }
-
+ 
 +#if CONFIG_SAND
 +static int raw_sand8_as_yuv420(AVCodecContext *avctx, AVPacket *pkt,
 +                      const AVFrame *frame)
@@ -16080,7 +19735,7 @@
 -                                       frame->width, frame->height, 1);
 +    int ret;
 +    AVFrame * frame = NULL;
-
+ 
 -    if (ret < 0)
 +#if CONFIG_SAND
 +    if (av_rpi_is_sand_frame(src_frame)) {
@@ -16104,7 +19759,7 @@
 +                                       frame->width, frame->height, 1);
 +    if (ret < 0)
 +        goto fail;
-
+ 
      if ((ret = ff_alloc_packet2(avctx, pkt, ret, ret)) < 0)
 -        return ret;
 +        goto fail;
@@ -16114,7 +19769,7 @@
                                         frame->width, frame->height, 1)) < 0)
 -        return ret;
 +        goto fail;
-
+ 
      if(avctx->codec_tag == AV_RL32("yuv2") && ret > 0 &&
         frame->format   == AV_PIX_FMT_YUYV422) {
 @@ -81,8 +178,14 @@ static int raw_encode(AVCodecContext *av
@@ -16130,7 +19785,7 @@
 +    *got_packet = 0;
 +    return ret;
  }
-
+ 
  AVCodec ff_rawvideo_encoder = {
 --- /dev/null
 +++ b/libavcodec/rpi_hevc_cabac.c
@@ -17637,7 +21292,7 @@
 +    const xy_off_t * const scan_xy_off = off_xys[scan_idx][log2_trafo_size - 2];
 +
 +    int use_vpu;
-+#if RPI_COMPRESS_COEFFS
++#if RPI_COMPRESS_COEFFS                                
 +    int num_nonzero = 0;
 +    int use_compress = 0;
 +    int *coeffs32;
@@ -17979,7 +21634,7 @@
 +          }
 +          use_compress = 0;
 +        }
-+#endif
++#endif            
 +
 +        if (nb_significant_coeff_flag != 0) {
 +            const unsigned int gt1_idx_delta = (c_idx_nz << 2) |
@@ -18055,7 +21710,7 @@
 +                        scale,
 +                        i == 0 && xy_off->coeff == 0 ? dc_scale : scale_m,
 +                        shift);
-+#if RPI_COMPRESS_COEFFS
++#if RPI_COMPRESS_COEFFS                                
 +                      if (use_compress)
 +                        coeffs32[num_nonzero++] = (res<<16) + (&blk_coeffs[xy_off->coeff] - coeffs);
 +                      else
@@ -18247,11 +21902,11 @@
 +#endif
 +
 +    if (!use_dc) {
-+#if RPI_COMPRESS_COEFFS
++#if RPI_COMPRESS_COEFFS                                
 +        if (use_compress) {
 +          coeffs32[num_nonzero] = 0;
 +        }
-+#endif
++#endif      
 +        rpi_add_residual(s, lc->jb0, log2_trafo_size, c_idx, x0, y0, coeffs);
 +    }
 +}
@@ -29840,7 +33495,7 @@
 +    unsigned int i;
 +    for (i = 0; i != 4; ++i) {
 +        cf->s[i].n = 0;
-+#if RPI_COMPRESS_COEFFS
++#if RPI_COMPRESS_COEFFS        
 +        cf->s[i].packed = 1;
 +        cf->s[i].packed_n = 0;
 +#endif
@@ -46104,76 +49759,104 @@
 @@ -21,6 +21,7 @@
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
   */
-
+ 
 +#include <drm_fourcc.h>
  #include <linux/videodev2.h>
  #include <sys/ioctl.h>
  #include <sys/mman.h>
-@@ -30,12 +31,14 @@
+@@ -29,57 +30,82 @@
+ #include <poll.h>
  #include "libavcodec/avcodec.h"
  #include "libavcodec/internal.h"
++#include "libavutil/avassert.h"
  #include "libavutil/pixdesc.h"
 +#include "libavutil/hwcontext.h"
  #include "v4l2_context.h"
  #include "v4l2_buffers.h"
  #include "v4l2_m2m.h"
 +#include "weak_link.h"
-
+ 
  #define USEC_PER_SEC 1000000
 -static AVRational v4l2_timebase = { 1, USEC_PER_SEC };
 +static const AVRational v4l2_timebase = { 1, USEC_PER_SEC };
-
- static inline V4L2m2mContext *buf_to_m2mctx(V4L2Buffer *buf)
+ 
+-static inline V4L2m2mContext *buf_to_m2mctx(V4L2Buffer *buf)
++static inline V4L2m2mContext *buf_to_m2mctx(const V4L2Buffer * const buf)
  {
-@@ -52,34 +55,44 @@ static inline AVCodecContext *logger(V4L
- static inline AVRational v4l2_get_timebase(V4L2Buffer *avbuf)
+     return V4L2_TYPE_IS_OUTPUT(buf->context->type) ?
+         container_of(buf->context, V4L2m2mContext, output) :
+         container_of(buf->context, V4L2m2mContext, capture);
+ }
+ 
+-static inline AVCodecContext *logger(V4L2Buffer *buf)
++static inline AVCodecContext *logger(const V4L2Buffer * const buf)
  {
-     V4L2m2mContext *s = buf_to_m2mctx(avbuf);
+     return buf_to_m2mctx(buf)->avctx;
+ }
+ 
+-static inline AVRational v4l2_get_timebase(V4L2Buffer *avbuf)
++static inline AVRational v4l2_get_timebase(const V4L2Buffer * const avbuf)
+ {
+-    V4L2m2mContext *s = buf_to_m2mctx(avbuf);
 -
 -    if (s->avctx->pkt_timebase.num)
 -        return s->avctx->pkt_timebase;
 -    return s->avctx->time_base;
++    const V4L2m2mContext *s = buf_to_m2mctx(avbuf);
 +    const AVRational tb = s->avctx->pkt_timebase.num ?
 +        s->avctx->pkt_timebase :
 +        s->avctx->time_base;
 +    return tb.num && tb.den ? tb : v4l2_timebase;
  }
-
+ 
 -static inline void v4l2_set_pts(V4L2Buffer *out, int64_t pts)
-+static inline void v4l2_set_pts(V4L2Buffer *out, int64_t pts, int no_rescale)
++static inline struct timeval tv_from_int(const int64_t t)
  {
 -    int64_t v4l2_pts;
--
++    return (struct timeval){
++        .tv_usec = t % USEC_PER_SEC,
++        .tv_sec  = t / USEC_PER_SEC
++    };
++}
+ 
 -    if (pts == AV_NOPTS_VALUE)
 -        pts = 0;
--
++static inline int64_t int_from_tv(const struct timeval t)
++{
++    return (int64_t)t.tv_sec * USEC_PER_SEC + t.tv_usec;
++}
+ 
++static inline void v4l2_set_pts(V4L2Buffer * const out, const int64_t pts)
++{
      /* convert pts to v4l2 timebase */
 -    v4l2_pts = av_rescale_q(pts, v4l2_get_timebase(out), v4l2_timebase);
+-    out->buf.timestamp.tv_usec = v4l2_pts % USEC_PER_SEC;
+-    out->buf.timestamp.tv_sec = v4l2_pts / USEC_PER_SEC;
 +    const int64_t v4l2_pts =
-+        no_rescale ? pts :
 +        pts == AV_NOPTS_VALUE ? 0 :
 +            av_rescale_q(pts, v4l2_get_timebase(out), v4l2_timebase);
-     out->buf.timestamp.tv_usec = v4l2_pts % USEC_PER_SEC;
-     out->buf.timestamp.tv_sec = v4l2_pts / USEC_PER_SEC;
++    out->buf.timestamp = tv_from_int(v4l2_pts);
  }
-
+ 
 -static inline int64_t v4l2_get_pts(V4L2Buffer *avbuf)
-+static inline int64_t v4l2_get_pts(V4L2Buffer *avbuf, int no_rescale)
++static inline int64_t v4l2_get_pts(const V4L2Buffer * const avbuf)
  {
 -    int64_t v4l2_pts;
 -
++    const int64_t v4l2_pts = int_from_tv(avbuf->buf.timestamp);
++    return v4l2_pts != 0 ? v4l2_pts : AV_NOPTS_VALUE;
++#if 0
      /* convert pts back to encoder timebase */
 -    v4l2_pts = (int64_t)avbuf->buf.timestamp.tv_sec * USEC_PER_SEC +
-+    const int64_t v4l2_pts = (int64_t)avbuf->buf.timestamp.tv_sec * USEC_PER_SEC +
-                         avbuf->buf.timestamp.tv_usec;
-
--    return av_rescale_q(v4l2_pts, v4l2_timebase, v4l2_get_timebase(avbuf));
+-                        avbuf->buf.timestamp.tv_usec;
 +    return
-+        no_rescale ? v4l2_pts :
++        avbuf->context->no_pts_rescale ? v4l2_pts :
 +        v4l2_pts == 0 ? AV_NOPTS_VALUE :
 +            av_rescale_q(v4l2_pts, v4l2_timebase, v4l2_get_timebase(avbuf));
++#endif
 +}
-+
+ 
+-    return av_rescale_q(v4l2_pts, v4l2_timebase, v4l2_get_timebase(avbuf));
 +static void set_buf_length(V4L2Buffer *out, unsigned int plane, uint32_t bytesused, uint32_t length)
 +{
 +    if (V4L2_TYPE_IS_MULTIPLANAR(out->buf.type)) {
@@ -46184,12 +49867,15 @@
 +        out->buf.length = length;
 +    }
  }
-
+ 
  static enum AVColorPrimaries v4l2_get_color_primaries(V4L2Buffer *buf)
-@@ -116,6 +129,105 @@ static enum AVColorPrimaries v4l2_get_co
+@@ -116,49 +142,176 @@ static enum AVColorPrimaries v4l2_get_co
      return AVCOL_PRI_UNSPECIFIED;
  }
-
+ 
+-static enum AVColorRange v4l2_get_color_range(V4L2Buffer *buf)
+-{
+-    enum v4l2_quantization qt;
 +static void v4l2_set_color(V4L2Buffer *buf,
 +                           const enum AVColorPrimaries avcp,
 +                           const enum AVColorSpace avcs,
@@ -46230,7 +49916,10 @@
 +    default:
 +        break;
 +    }
-+
+ 
+-    qt = V4L2_TYPE_IS_MULTIPLANAR(buf->buf.type) ?
+-        buf->context->format.fmt.pix_mp.quantization :
+-        buf->context->format.fmt.pix.quantization;
 +    switch (avcs) {
 +    case AVCOL_SPC_RGB:
 +        cs = V4L2_COLORSPACE_SRGB;
@@ -46260,7 +49949,10 @@
 +    default:
 +        break;
 +    }
-+
+ 
+-    switch (qt) {
+-    case V4L2_QUANTIZATION_LIM_RANGE: return AVCOL_RANGE_MPEG;
+-    case V4L2_QUANTIZATION_FULL_RANGE: return AVCOL_RANGE_JPEG;
 +    switch (xfer) {
 +    case AVCOL_TRC_BT709:
 +        xfer = V4L2_XFER_FUNC_709;
@@ -46274,10 +49966,11 @@
 +    case AVCOL_TRC_SMPTE2084:
 +        xfer = V4L2_XFER_FUNC_SMPTE2084;
 +        break;
-+    default:
-+        break;
-+    }
-+
+     default:
+         break;
+     }
+ 
+-     return AVCOL_RANGE_UNSPECIFIED;
 +    if (V4L2_TYPE_IS_MULTIPLANAR(buf->buf.type)) {
 +        buf->context->format.fmt.pix_mp.colorspace = cs;
 +        buf->context->format.fmt.pix_mp.ycbcr_enc = ycbcr;
@@ -46287,15 +49980,58 @@
 +        buf->context->format.fmt.pix.ycbcr_enc = ycbcr;
 +        buf->context->format.fmt.pix.xfer_func = xfer;
 +    }
+ }
+ 
+-static enum AVColorSpace v4l2_get_color_space(V4L2Buffer *buf)
++static inline enum v4l2_quantization
++buf_quantization(const V4L2Buffer * const buf)
+ {
+-    enum v4l2_ycbcr_encoding ycbcr;
+-    enum v4l2_colorspace cs;
++    return V4L2_TYPE_IS_MULTIPLANAR(buf->buf.type) ?
++        buf->context->format.fmt.pix_mp.quantization :
++        buf->context->format.fmt.pix.quantization;
++}
+ 
+-    cs = V4L2_TYPE_IS_MULTIPLANAR(buf->buf.type) ?
++static inline enum v4l2_colorspace
++buf_colorspace(const V4L2Buffer * const buf)
++{
++    return V4L2_TYPE_IS_MULTIPLANAR(buf->buf.type) ?
+         buf->context->format.fmt.pix_mp.colorspace :
+         buf->context->format.fmt.pix.colorspace;
++}
+ 
+-    ycbcr = V4L2_TYPE_IS_MULTIPLANAR(buf->buf.type) ?
++static inline enum v4l2_ycbcr_encoding
++buf_ycbcr_enc(const V4L2Buffer * const buf)
++{
++    return V4L2_TYPE_IS_MULTIPLANAR(buf->buf.type) ?
+         buf->context->format.fmt.pix_mp.ycbcr_enc:
+         buf->context->format.fmt.pix.ycbcr_enc;
++}
+ 
+-    switch(cs) {
+-    case V4L2_COLORSPACE_SRGB: return AVCOL_SPC_RGB;
++static enum AVColorRange v4l2_get_color_range(V4L2Buffer *buf)
++{
++    switch (buf_quantization(buf)) {
++    case V4L2_QUANTIZATION_LIM_RANGE:
++        return AVCOL_RANGE_MPEG;
++    case V4L2_QUANTIZATION_FULL_RANGE:
++        return AVCOL_RANGE_JPEG;
++    case V4L2_QUANTIZATION_DEFAULT:
++        // If YUV (which we assume for all video decode) then, from the header
++        // comments, range is limited unless CS is JPEG
++        return buf_colorspace(buf) == V4L2_COLORSPACE_JPEG ?
++            AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG;
++    default:
++        break;
++    }
++
++     return AVCOL_RANGE_UNSPECIFIED;
 +}
 +
- static enum AVColorRange v4l2_get_color_range(V4L2Buffer *buf)
- {
-     enum v4l2_quantization qt;
-@@ -134,6 +246,20 @@ static enum AVColorRange v4l2_get_color_
-      return AVCOL_RANGE_UNSPECIFIED;
- }
-
 +static void v4l2_set_color_range(V4L2Buffer *buf, const enum AVColorRange avcr)
 +{
 +    const enum v4l2_quantization q =
@@ -46310,13 +50046,51 @@
 +    }
 +}
 +
- static enum AVColorSpace v4l2_get_color_space(V4L2Buffer *buf)
++static enum AVColorSpace v4l2_get_color_space(V4L2Buffer *buf)
++{
++    switch (buf_colorspace(buf)) {
++    case V4L2_COLORSPACE_JPEG:  // JPEG -> SRGB
++    case V4L2_COLORSPACE_SRGB:
++        return AVCOL_SPC_RGB;
+     case V4L2_COLORSPACE_REC709: return AVCOL_SPC_BT709;
+     case V4L2_COLORSPACE_470_SYSTEM_M: return AVCOL_SPC_FCC;
+     case V4L2_COLORSPACE_470_SYSTEM_BG: return AVCOL_SPC_BT470BG;
+     case V4L2_COLORSPACE_SMPTE170M: return AVCOL_SPC_SMPTE170M;
+     case V4L2_COLORSPACE_SMPTE240M: return AVCOL_SPC_SMPTE240M;
+     case V4L2_COLORSPACE_BT2020:
+-        if (ycbcr == V4L2_YCBCR_ENC_BT2020_CONST_LUM)
+-            return AVCOL_SPC_BT2020_CL;
+-        else
+-             return AVCOL_SPC_BT2020_NCL;
++        return buf_ycbcr_enc(buf) == V4L2_YCBCR_ENC_BT2020_CONST_LUM ?
++            AVCOL_SPC_BT2020_CL : AVCOL_SPC_BT2020_NCL;
+     default:
+         break;
+     }
+@@ -168,17 +321,9 @@ static enum AVColorSpace v4l2_get_color_
+ 
+ static enum AVColorTransferCharacteristic v4l2_get_color_trc(V4L2Buffer *buf)
  {
-     enum v4l2_ycbcr_encoding ycbcr;
-@@ -210,73 +336,165 @@ static enum AVColorTransferCharacteristi
+-    enum v4l2_ycbcr_encoding ycbcr;
++    const enum v4l2_ycbcr_encoding ycbcr = buf_ycbcr_enc(buf);
+     enum v4l2_xfer_func xfer;
+-    enum v4l2_colorspace cs;
+-
+-    cs = V4L2_TYPE_IS_MULTIPLANAR(buf->buf.type) ?
+-        buf->context->format.fmt.pix_mp.colorspace :
+-        buf->context->format.fmt.pix.colorspace;
+-
+-    ycbcr = V4L2_TYPE_IS_MULTIPLANAR(buf->buf.type) ?
+-        buf->context->format.fmt.pix_mp.ycbcr_enc:
+-        buf->context->format.fmt.pix.ycbcr_enc;
++    const enum v4l2_colorspace cs = buf_colorspace(buf);
+ 
+     xfer = V4L2_TYPE_IS_MULTIPLANAR(buf->buf.type) ?
+         buf->context->format.fmt.pix_mp.xfer_func:
+@@ -210,73 +355,165 @@ static enum AVColorTransferCharacteristi
      return AVCOL_TRC_UNSPECIFIED;
  }
-
+ 
 -static void v4l2_free_buffer(void *opaque, uint8_t *unused)
 +static int v4l2_buf_is_interlaced(const V4L2Buffer * const buf)
  {
@@ -46327,7 +50101,7 @@
 -        atomic_fetch_sub_explicit(&s->refcount, 1, memory_order_acq_rel);
 +    return V4L2_FIELD_IS_INTERLACED(buf->buf.field);
 +}
-
+ 
 -        if (s->reinit) {
 -            if (!atomic_load(&s->refcount))
 -                sem_post(&s->refsync);
@@ -46343,7 +50117,7 @@
 +{
 +    return buf->buf.field == V4L2_FIELD_INTERLACED_TB;
 +}
-
+ 
 -        av_buffer_unref(&avbuf->context_ref);
 -    }
 +static void v4l2_set_interlace(V4L2Buffer * const buf, const int is_interlaced, const int is_tff)
@@ -46351,14 +50125,14 @@
 +    buf->buf.field = !is_interlaced ? V4L2_FIELD_NONE :
 +        is_tff ? V4L2_FIELD_INTERLACED_TB : V4L2_FIELD_INTERLACED_BT;
  }
-
+ 
 -static int v4l2_buf_increase_ref(V4L2Buffer *in)
 +static uint8_t * v4l2_get_drm_frame(V4L2Buffer *avbuf)
  {
 -    V4L2m2mContext *s = buf_to_m2mctx(in);
 +    AVDRMFrameDescriptor *drm_desc = &avbuf->drm_frame;
 +    AVDRMLayerDescriptor *layer;
-
+ 
 -    if (in->context_ref)
 -        atomic_fetch_add(&in->context_refcount, 1);
 -    else {
@@ -46368,7 +50142,7 @@
 +    /* fill the DRM frame descriptor */
 +    drm_desc->nb_objects = avbuf->num_planes;
 +    drm_desc->nb_layers = 1;
-
+ 
 -        in->context_refcount = 1;
 +    layer = &drm_desc->layers[0];
 +    layer->nb_planes = avbuf->num_planes;
@@ -46378,7 +50152,7 @@
 +        layer->planes[i].offset = 0;
 +        layer->planes[i].pitch = avbuf->plane_info[i].bytesperline;
      }
-
+ 
 -    in->status = V4L2BUF_RET_USER;
 -    atomic_fetch_add_explicit(&s->refcount, 1, memory_order_relaxed);
 +    switch (avbuf->context->av_pix_fmt) {
@@ -46386,7 +50160,7 @@
 +
 +        layer->format = DRM_FORMAT_YUYV;
 +        layer->nb_planes = 1;
-
+ 
 -    return 0;
 +        break;
 +
@@ -46435,7 +50209,7 @@
 +
 +    return (uint8_t *) drm_desc;
  }
-
+ 
 -static int v4l2_buf_to_bufref(V4L2Buffer *in, int plane, AVBufferRef **buf)
 +static void v4l2_free_bufref(void *opaque, uint8_t *data)
  {
@@ -46443,25 +50217,25 @@
 +    AVBufferRef * bufref = (AVBufferRef *)data;
 +    V4L2Buffer *avbuf = (V4L2Buffer *)bufref->data;
 +    struct V4L2Context *ctx = ff_weak_link_lock(&avbuf->context_wl);
-
+ 
 -    if (plane >= in->num_planes)
 -        return AVERROR(EINVAL);
 +    if (ctx != NULL) {
 +        // Buffer still attached to context
 +        V4L2m2mContext *s = buf_to_m2mctx(avbuf);
-
+ 
 -    /* even though most encoders return 0 in data_offset encoding vp8 does require this value */
 -    *buf = av_buffer_create((char *)in->plane_info[plane].mm_addr + in->planes[plane].data_offset,
 -                            in->plane_info[plane].length, v4l2_free_buffer, in, 0);
 -    if (!*buf)
 -        return AVERROR(ENOMEM);
 +        ff_mutex_lock(&ctx->lock);
-
+ 
 -    ret = v4l2_buf_increase_ref(in);
 -    if (ret)
 -        av_buffer_unref(buf);
-+        avbuf->status = V4L2BUF_AVAILABLE;
-
++        ff_v4l2_buffer_set_avail(avbuf);
+ 
 -    return ret;
 +        if (s->draining && V4L2_TYPE_IS_OUTPUT(ctx->type)) {
 +            av_log(logger(avbuf), AV_LOG_DEBUG, "%s: Buffer avail\n", ctx->name);
@@ -46482,8 +50256,9 @@
 +
 +    ff_weak_link_unlock(avbuf->context_wl);
 +    av_buffer_unref(&bufref);
-+}
-+
+ }
+ 
+-static int v4l2_bufref_to_buf(V4L2Buffer *out, int plane, const uint8_t* data, int size, int offset, AVBufferRef* bref)
 +static int v4l2_buffer_export_drm(V4L2Buffer* avbuf)
 +{
 +    struct v4l2_exportbuffer expbuf;
@@ -46514,20 +50289,19 @@
 +    }
 +
 +    return 0;
- }
-
--static int v4l2_bufref_to_buf(V4L2Buffer *out, int plane, const uint8_t* data, int size, int offset, AVBufferRef* bref)
++}
++
 +static int v4l2_bufref_to_buf(V4L2Buffer *out, int plane, const uint8_t* data, int size, int offset)
  {
      unsigned int bytesused, length;
 +    int rv = 0;
-
+ 
      if (plane >= out->num_planes)
          return AVERROR(EINVAL);
-@@ -284,32 +502,57 @@ static int v4l2_bufref_to_buf(V4L2Buffer
+@@ -284,32 +521,57 @@ static int v4l2_bufref_to_buf(V4L2Buffer
      length = out->plane_info[plane].length;
      bytesused = FFMIN(size+offset, length);
-
+ 
 -    memcpy((uint8_t*)out->plane_info[plane].mm_addr+offset, data, FFMIN(size, length-offset));
 -
 -    if (V4L2_TYPE_IS_MULTIPLANAR(out->buf.type)) {
@@ -46540,7 +50314,7 @@
 +        size = length - offset;
 +        rv = AVERROR(ENOMEM);
      }
-
+ 
 -    return 0;
 +    memcpy((uint8_t*)out->plane_info[plane].mm_addr+offset, data, size);
 +
@@ -46564,14 +50338,14 @@
 +    avbuf->status = V4L2BUF_RET_USER;
 +    return newbuf;
  }
-
+ 
  static int v4l2_buffer_buf_to_swframe(AVFrame *frame, V4L2Buffer *avbuf)
  {
 -    int i, ret;
 +    int i;
-
+ 
      frame->format = avbuf->context->av_pix_fmt;
-
+ 
 -    for (i = 0; i < avbuf->num_planes; i++) {
 -        ret = v4l2_buf_to_bufref(avbuf, i, &frame->buf[i]);
 -        if (ret)
@@ -46579,7 +50353,7 @@
 +    frame->buf[0] = wrap_avbuf(avbuf);
 +    if (frame->buf[0] == NULL)
 +        return AVERROR(ENOMEM);
-
++
 +    if (buf_to_m2mctx(avbuf)->output_drm) {
 +        /* 1. get references to the actual data */
 +        frame->data[0] = (uint8_t *) v4l2_get_drm_frame(avbuf);
@@ -46587,7 +50361,7 @@
 +        frame->hw_frames_ctx = av_buffer_ref(avbuf->context->frames_ref);
 +        return 0;
 +    }
-+
+ 
 +
 +    /* 1. get references to the actual data */
 +    for (i = 0; i < avbuf->num_planes; i++) {
@@ -46595,9 +50369,9 @@
          frame->linesize[i] = avbuf->plane_info[i].bytesperline;
 -        frame->data[i] = frame->buf[i]->data;
      }
-
+ 
      /* fixup special cases */
-@@ -318,17 +561,17 @@ static int v4l2_buffer_buf_to_swframe(AV
+@@ -318,17 +580,17 @@ static int v4l2_buffer_buf_to_swframe(AV
      case AV_PIX_FMT_NV21:
          if (avbuf->num_planes > 1)
              break;
@@ -46606,7 +50380,7 @@
 +        frame->linesize[1] = frame->linesize[0];
 +        frame->data[1] = frame->data[0] + frame->linesize[0] * ff_v4l2_get_format_height(&avbuf->context->format);
          break;
-
+ 
      case AV_PIX_FMT_YUV420P:
          if (avbuf->num_planes > 1)
              break;
@@ -46619,12 +50393,12 @@
 +        frame->data[1] = frame->data[0] + frame->linesize[0] * ff_v4l2_get_format_height(&avbuf->context->format);
 +        frame->data[2] = frame->data[1] + frame->linesize[1] * ff_v4l2_get_format_height(&avbuf->context->format) / 2;
          break;
-
+ 
      default:
-@@ -338,68 +581,95 @@ static int v4l2_buffer_buf_to_swframe(AV
+@@ -338,68 +600,127 @@ static int v4l2_buffer_buf_to_swframe(AV
      return 0;
  }
-
+ 
 +static void cpy_2d(uint8_t * dst, int dst_stride, const uint8_t * src, int src_stride, int w, int h)
 +{
 +    if (dst_stride == src_stride && w + 32 >= dst_stride) {
@@ -46644,6 +50418,38 @@
 +    return i != 0  && !(i == num_planes - 1 && (desc->flags & AV_PIX_FMT_FLAG_ALPHA));
 +}
 +
++static int v4l2_buffer_primeframe_to_buf(const AVFrame *frame, V4L2Buffer *out)
++{
++    const AVDRMFrameDescriptor *const src = (const AVDRMFrameDescriptor *)frame->data[0];
++
++    if (frame->format != AV_PIX_FMT_DRM_PRIME || !src)
++        return AVERROR(EINVAL);
++
++    av_assert0(out->buf.memory == V4L2_MEMORY_DMABUF);
++
++    if (V4L2_TYPE_IS_MULTIPLANAR(out->buf.type)) {
++        // Only currently cope with single buffer types
++        if (out->buf.length != 1)
++            return AVERROR_PATCHWELCOME;
++        if (src->nb_objects != 1)
++            return AVERROR(EINVAL);
++
++        out->planes[0].m.fd = src->objects[0].fd;
++    }
++    else {
++        if (src->nb_objects != 1)
++            return AVERROR(EINVAL);
++
++        out->buf.m.fd      = src->objects[0].fd;
++    }
++
++    // No need to copy src AVDescriptor and if we did then we may confuse
++    // fd close on free
++    out->ref_buf = av_buffer_ref(frame->buf[0]);
++
++    return 0;
++}
++
  static int v4l2_buffer_swframe_to_buf(const AVFrame *frame, V4L2Buffer *out)
  {
 -    int i, ret;
@@ -46756,7 +50562,7 @@
 +                av_log(NULL, AV_LOG_ERROR, "%s: Plane total %u > buffer size %zu\n", __func__, offset, out->plane_info[0].length);
 +                return -1;
 +            }
-
+ 
 -    for (i = 0; i < out->num_planes; i++) {
 -        ret = v4l2_bufref_to_buf(out, i, frame->buf[i]->data, frame->buf[i]->size, 0, frame->buf[i]);
 -        if (ret)
@@ -46770,42 +50576,60 @@
 -
      return 0;
  }
-
-@@ -411,14 +681,22 @@ static int v4l2_buffer_swframe_to_buf(co
-
- int ff_v4l2_buffer_avframe_to_buf(const AVFrame *frame, V4L2Buffer *out)
+ 
+@@ -409,16 +730,31 @@ static int v4l2_buffer_swframe_to_buf(co
+  *
+  ******************************************************************************/
+ 
+-int ff_v4l2_buffer_avframe_to_buf(const AVFrame *frame, V4L2Buffer *out)
++int ff_v4l2_buffer_avframe_to_buf(const AVFrame *frame, V4L2Buffer *out, const int64_t track_ts)
  {
 -    v4l2_set_pts(out, frame->pts);
-+    out->buf.flags = frame->key_frame ? (out->buf.flags & ~V4L2_BUF_FLAG_KEYFRAME) : (out->buf.flags | V4L2_BUF_FLAG_KEYFRAME);
+-
+-    return v4l2_buffer_swframe_to_buf(frame, out);
++    out->buf.flags = frame->key_frame ?
++        (out->buf.flags | V4L2_BUF_FLAG_KEYFRAME) :
++        (out->buf.flags & ~V4L2_BUF_FLAG_KEYFRAME);
 +    // Beware that colour info is held in format rather than the actual
 +    // v4l2 buffer struct so this may not be as useful as you might hope
 +    v4l2_set_color(out, frame->color_primaries, frame->colorspace, frame->color_trc);
 +    v4l2_set_color_range(out, frame->color_range);
 +    // PTS & interlace are buffer vars
-+    v4l2_set_pts(out, frame->pts, 0);
++    if (track_ts)
++        out->buf.timestamp = tv_from_int(track_ts);
++    else
++        v4l2_set_pts(out, frame->pts);
 +    v4l2_set_interlace(out, frame->interlaced_frame, frame->top_field_first);
-
-     return v4l2_buffer_swframe_to_buf(frame, out);
++
++    return frame->format == AV_PIX_FMT_DRM_PRIME ?
++        v4l2_buffer_primeframe_to_buf(frame, out) :
++        v4l2_buffer_swframe_to_buf(frame, out);
  }
-
--int ff_v4l2_buffer_buf_to_avframe(AVFrame *frame, V4L2Buffer *avbuf)
-+int ff_v4l2_buffer_buf_to_avframe(AVFrame *frame, V4L2Buffer *avbuf, int no_rescale_pts)
+ 
+ int ff_v4l2_buffer_buf_to_avframe(AVFrame *frame, V4L2Buffer *avbuf)
  {
      int ret;
 +    V4L2Context * const ctx = avbuf->context;
-
+ 
      av_frame_unref(frame);
-
-@@ -433,13 +711,24 @@ int ff_v4l2_buffer_buf_to_avframe(AVFram
+ 
+@@ -429,17 +765,32 @@ int ff_v4l2_buffer_buf_to_avframe(AVFram
+ 
+     /* 2. get frame information */
+     frame->key_frame = !!(avbuf->buf.flags & V4L2_BUF_FLAG_KEYFRAME);
++    frame->pict_type = frame->key_frame ? AV_PICTURE_TYPE_I :
++        (avbuf->buf.flags & V4L2_BUF_FLAG_PFRAME) != 0 ? AV_PICTURE_TYPE_P :
++        (avbuf->buf.flags & V4L2_BUF_FLAG_BFRAME) != 0 ? AV_PICTURE_TYPE_B :
++            AV_PICTURE_TYPE_NONE;
+     frame->color_primaries = v4l2_get_color_primaries(avbuf);
      frame->colorspace = v4l2_get_color_space(avbuf);
      frame->color_range = v4l2_get_color_range(avbuf);
      frame->color_trc = v4l2_get_color_trc(avbuf);
--    frame->pts = v4l2_get_pts(avbuf);
-+    frame->pts = v4l2_get_pts(avbuf, no_rescale_pts);
+     frame->pts = v4l2_get_pts(avbuf);
      frame->pkt_dts = AV_NOPTS_VALUE;
 +    frame->interlaced_frame = v4l2_buf_is_interlaced(avbuf);
 +    frame->top_field_first = v4l2_buf_is_top_first(avbuf);
-
+ 
      /* these values are updated also during re-init in v4l2_process_driver_event */
 -    frame->height = avbuf->context->height;
 -    frame->width = avbuf->context->width;
@@ -46820,18 +50644,17 @@
 +        frame->crop_right = ctx->selection.left + ctx->selection.width < frame->width ?
 +            frame->width - (ctx->selection.left + ctx->selection.width) : 0;
 +        frame->crop_bottom = ctx->selection.top + ctx->selection.height < frame->height ?
-+            frame->width - (ctx->selection.top + ctx->selection.height) : 0;
++            frame->height - (ctx->selection.top + ctx->selection.height) : 0;
 +    }
-
+ 
      /* 3. report errors upstream */
      if (avbuf->buf.flags & V4L2_BUF_FLAG_ERROR) {
-@@ -452,15 +741,16 @@ int ff_v4l2_buffer_buf_to_avframe(AVFram
-
+@@ -452,15 +803,15 @@ int ff_v4l2_buffer_buf_to_avframe(AVFram
+ 
  int ff_v4l2_buffer_buf_to_avpkt(AVPacket *pkt, V4L2Buffer *avbuf)
  {
 -    int ret;
-+    av_log(logger(avbuf), AV_LOG_INFO, "%s\n", __func__);
-
+-
      av_packet_unref(pkt);
 -    ret = v4l2_buf_to_bufref(avbuf, 0, &pkt->buf);
 -    if (ret)
@@ -46840,29 +50663,25 @@
 +    pkt->buf = wrap_avbuf(avbuf);
 +    if (pkt->buf == NULL)
 +        return AVERROR(ENOMEM);
-
+ 
      pkt->size = V4L2_TYPE_IS_MULTIPLANAR(avbuf->buf.type) ? avbuf->buf.m.planes[0].bytesused : avbuf->buf.bytesused;
 -    pkt->data = pkt->buf->data;
 +    pkt->data = (uint8_t*)avbuf->plane_info[0].mm_addr + avbuf->planes[0].data_offset;
-
++    pkt->flags = 0;
+ 
      if (avbuf->buf.flags & V4L2_BUF_FLAG_KEYFRAME)
          pkt->flags |= AV_PKT_FLAG_KEY;
-@@ -470,36 +760,89 @@ int ff_v4l2_buffer_buf_to_avpkt(AVPacket
-         pkt->flags |= AV_PKT_FLAG_CORRUPT;
-     }
-
--    pkt->dts = pkt->pts = v4l2_get_pts(avbuf);
-+    pkt->dts = pkt->pts = v4l2_get_pts(avbuf, 0);
-
+@@ -475,31 +826,91 @@ int ff_v4l2_buffer_buf_to_avpkt(AVPacket
      return 0;
  }
-
+ 
 -int ff_v4l2_buffer_avpkt_to_buf(const AVPacket *pkt, V4L2Buffer *out)
-+int ff_v4l2_buffer_avpkt_to_buf_ext(const AVPacket *pkt, V4L2Buffer *out,
-+                                    const void *extdata, size_t extlen, int no_rescale_pts)
++int ff_v4l2_buffer_avpkt_to_buf_ext(const AVPacket * const pkt, V4L2Buffer * const out,
++                                    const void *extdata, size_t extlen,
++                                    const int64_t timestamp)
  {
      int ret;
-
+ 
 -    ret = v4l2_bufref_to_buf(out, 0, pkt->data, pkt->size, 0, pkt->buf);
 -    if (ret)
 +    if (extlen) {
@@ -46874,18 +50693,23 @@
 +    ret = v4l2_bufref_to_buf(out, 0, pkt->data, pkt->size, extlen);
 +    if (ret && ret != AVERROR(ENOMEM))
          return ret;
-
+ 
 -    v4l2_set_pts(out, pkt->pts);
-+    v4l2_set_pts(out, pkt->pts, no_rescale_pts);
-
-     if (pkt->flags & AV_PKT_FLAG_KEY)
-         out->flags = V4L2_BUF_FLAG_KEYFRAME;
-
--    return 0;
++    if (timestamp)
++        out->buf.timestamp = tv_from_int(timestamp);
++    else
++        v4l2_set_pts(out, pkt->pts);
++
++    out->buf.flags = (pkt->flags & AV_PKT_FLAG_KEY) != 0 ?
++        (out->buf.flags | V4L2_BUF_FLAG_KEYFRAME) :
++        (out->buf.flags & ~V4L2_BUF_FLAG_KEYFRAME);
+ 
+-    if (pkt->flags & AV_PKT_FLAG_KEY)
+-        out->flags = V4L2_BUF_FLAG_KEYFRAME;
 +    return ret;
- }
-
--int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index)
++}
+ 
+-    return 0;
 +int ff_v4l2_buffer_avpkt_to_buf(const AVPacket *pkt, V4L2Buffer *out)
 +{
 +    return ff_v4l2_buffer_avpkt_to_buf_ext(pkt, out, NULL, 0, 0);
@@ -46908,23 +50732,27 @@
 +            close(avbuf->drm_frame.objects[i].fd);
 +    }
 +
++    av_buffer_unref(&avbuf->ref_buf);
++
 +    ff_weak_link_unref(&avbuf->context_wl);
 +
 +    av_free(avbuf);
-+}
+ }
+ 
+-int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index)
 +
-+
-+int ff_v4l2_buffer_initialize(AVBufferRef ** pbufref, int index, V4L2Context *ctx)
++int ff_v4l2_buffer_initialize(AVBufferRef ** pbufref, int index, V4L2Context *ctx, enum v4l2_memory mem)
  {
 -    V4L2Context *ctx = avbuf->context;
      int ret, i;
 +    V4L2Buffer * const avbuf = av_mallocz(sizeof(*avbuf));
 +    AVBufferRef * bufref;
-+
+ 
+-    avbuf->buf.memory = V4L2_MEMORY_MMAP;
 +    *pbufref = NULL;
 +    if (avbuf == NULL)
 +        return AVERROR(ENOMEM);
-
++
 +    bufref = av_buffer_create((uint8_t*)avbuf, sizeof(*avbuf), v4l2_buffer_buffer_free, NULL, 0);
 +    if (bufref == NULL) {
 +        av_free(avbuf);
@@ -46932,10 +50760,10 @@
 +    }
 +
 +    avbuf->context = ctx;
-     avbuf->buf.memory = V4L2_MEMORY_MMAP;
++    avbuf->buf.memory = mem;
      avbuf->buf.type = ctx->type;
      avbuf->buf.index = index;
-
+ 
 +    for (i = 0; i != FF_ARRAY_ELEMS(avbuf->drm_frame.objects); ++i) {
 +        avbuf->drm_frame.objects[i].fd = -1;
 +    }
@@ -46945,43 +50773,48 @@
      if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type)) {
          avbuf->buf.length = VIDEO_MAX_PLANES;
          avbuf->buf.m.planes = avbuf->planes;
-@@ -507,7 +850,7 @@ int ff_v4l2_buffer_initialize(V4L2Buffer
-
+@@ -507,7 +918,7 @@ int ff_v4l2_buffer_initialize(V4L2Buffer
+ 
      ret = ioctl(buf_to_m2mctx(avbuf)->fd, VIDIOC_QUERYBUF, &avbuf->buf);
      if (ret < 0)
 -        return AVERROR(errno);
 +        goto fail;
-
+ 
      if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type)) {
          avbuf->num_planes = 0;
-@@ -527,25 +870,33 @@ int ff_v4l2_buffer_initialize(V4L2Buffer
-
+@@ -520,6 +931,8 @@ int ff_v4l2_buffer_initialize(V4L2Buffer
+         avbuf->num_planes = 1;
+ 
+     for (i = 0; i < avbuf->num_planes; i++) {
++        const int want_mmap = avbuf->buf.memory == V4L2_MEMORY_MMAP &&
++            (V4L2_TYPE_IS_OUTPUT(ctx->type) || !buf_to_m2mctx(avbuf)->output_drm);
+ 
+         avbuf->plane_info[i].bytesperline = V4L2_TYPE_IS_MULTIPLANAR(ctx->type) ?
+             ctx->format.fmt.pix_mp.plane_fmt[i].bytesperline :
+@@ -527,25 +940,29 @@ int ff_v4l2_buffer_initialize(V4L2Buffer
+ 
          if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type)) {
              avbuf->plane_info[i].length = avbuf->buf.m.planes[i].length;
 -            avbuf->plane_info[i].mm_addr = mmap(NULL, avbuf->buf.m.planes[i].length,
 -                                           PROT_READ | PROT_WRITE, MAP_SHARED,
 -                                           buf_to_m2mctx(avbuf)->fd, avbuf->buf.m.planes[i].m.mem_offset);
 +
-+            if ((V4L2_TYPE_IS_OUTPUT(ctx->type) && buf_to_m2mctx(avbuf)->output_drm) ||
-+                !buf_to_m2mctx(avbuf)->output_drm) {
++            if (want_mmap)
 +                avbuf->plane_info[i].mm_addr = mmap(NULL, avbuf->buf.m.planes[i].length,
 +                                               PROT_READ | PROT_WRITE, MAP_SHARED,
 +                                               buf_to_m2mctx(avbuf)->fd, avbuf->buf.m.planes[i].m.mem_offset);
-+            }
          } else {
              avbuf->plane_info[i].length = avbuf->buf.length;
 -            avbuf->plane_info[i].mm_addr = mmap(NULL, avbuf->buf.length,
 -                                          PROT_READ | PROT_WRITE, MAP_SHARED,
 -                                          buf_to_m2mctx(avbuf)->fd, avbuf->buf.m.offset);
 +
-+            if ((V4L2_TYPE_IS_OUTPUT(ctx->type) && buf_to_m2mctx(avbuf)->output_drm) ||
-+                !buf_to_m2mctx(avbuf)->output_drm) {
++            if (want_mmap)
 +                avbuf->plane_info[i].mm_addr = mmap(NULL, avbuf->buf.length,
 +                                               PROT_READ | PROT_WRITE, MAP_SHARED,
 +                                               buf_to_m2mctx(avbuf)->fd, avbuf->buf.m.offset);
-+            }
          }
-
+ 
 -        if (avbuf->plane_info[i].mm_addr == MAP_FAILED)
 -            return AVERROR(ENOMEM);
 +        if (avbuf->plane_info[i].mm_addr == MAP_FAILED) {
@@ -46990,19 +50823,19 @@
 +            goto fail;
 +        }
      }
-
+ 
      avbuf->status = V4L2BUF_AVAILABLE;
-
+ 
 -    if (V4L2_TYPE_IS_OUTPUT(ctx->type))
 -        return 0;
 -
      if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type)) {
          avbuf->buf.m.planes = avbuf->planes;
          avbuf->buf.length   = avbuf->num_planes;
-@@ -555,7 +906,20 @@ int ff_v4l2_buffer_initialize(V4L2Buffer
+@@ -555,20 +972,51 @@ int ff_v4l2_buffer_initialize(V4L2Buffer
          avbuf->buf.length    = avbuf->planes[0].length;
      }
-
+ 
 -    return ff_v4l2_buffer_enqueue(avbuf);
 +    if (!V4L2_TYPE_IS_OUTPUT(ctx->type)) {
 +        if (buf_to_m2mctx(avbuf)->output_drm) {
@@ -47019,19 +50852,20 @@
 +    av_buffer_unref(&bufref);
 +    return ret;
  }
-
+ 
  int ff_v4l2_buffer_enqueue(V4L2Buffer* avbuf)
-@@ -564,9 +928,27 @@ int ff_v4l2_buffer_enqueue(V4L2Buffer* a
-
-     avbuf->buf.flags = avbuf->flags;
-
+ {
+     int ret;
++    int qc;
+ 
+-    avbuf->buf.flags = avbuf->flags;
 +    if (avbuf->buf.timestamp.tv_sec || avbuf->buf.timestamp.tv_usec) {
 +        av_log(logger(avbuf), AV_LOG_DEBUG, "--- %s pre VIDIOC_QBUF: index %d, ts=%ld.%06ld count=%d\n",
 +               avbuf->context->name, avbuf->buf.index,
 +               avbuf->buf.timestamp.tv_sec, avbuf->buf.timestamp.tv_usec,
 +               avbuf->context->q_count);
 +    }
-+
+ 
      ret = ioctl(buf_to_m2mctx(avbuf)->fd, VIDIOC_QBUF, &avbuf->buf);
 -    if (ret < 0)
 -        return AVERROR(errno);
@@ -47042,31 +50876,34 @@
 +               err, strerror(err));
 +        return AVERROR(err);
 +    }
+ 
++    // Lock not wanted - if called from buffer free then lock already obtained
++    qc = atomic_fetch_add(&avbuf->context->q_count, 1) + 1;
+     avbuf->status = V4L2BUF_IN_DRIVER;
++    pthread_cond_broadcast(&avbuf->context->cond);
 +
-+    ++avbuf->context->q_count;
 +    av_log(logger(avbuf), AV_LOG_DEBUG, "--- %s VIDIOC_QBUF: index %d, ts=%ld.%06ld count=%d\n",
 +           avbuf->context->name, avbuf->buf.index,
-+           avbuf->buf.timestamp.tv_sec, avbuf->buf.timestamp.tv_usec,
-+           avbuf->context->q_count);
-
-     avbuf->status = V4L2BUF_IN_DRIVER;
-
++           avbuf->buf.timestamp.tv_sec, avbuf->buf.timestamp.tv_usec, qc);
+ 
+     return 0;
+ }
 --- a/libavcodec/v4l2_buffers.h
 +++ b/libavcodec/v4l2_buffers.h
-@@ -27,25 +27,34 @@
+@@ -27,25 +27,38 @@
  #include <stdatomic.h>
  #include <linux/videodev2.h>
-
+ 
 +#include "libavutil/hwcontext_drm.h"
  #include "avcodec.h"
-
+ 
  enum V4L2Buffer_status {
      V4L2BUF_AVAILABLE,
      V4L2BUF_IN_DRIVER,
 +    V4L2BUF_IN_USE,
      V4L2BUF_RET_USER,
  };
-
+ 
  /**
   * V4L2Buffer (wrapper for v4l2_buffer management)
   */
@@ -47083,49 +50920,70 @@
 +     */
      struct V4L2Context *context;
 +    struct ff_weak_link_client *context_wl;
-
+ 
 -    /* This object is refcounted per-plane, so we need to keep track
 -     * of how many context-refs we are holding. */
 -    AVBufferRef *context_ref;
 -    atomic_uint context_refcount;
 +    /* DRM descriptor */
 +    AVDRMFrameDescriptor drm_frame;
-
++    /* For DRM_PRIME encode - need to keep a ref to the source buffer till we
++     * are done
++     */
++    AVBufferRef * ref_buf;
+ 
      /* keep track of the mmap address and mmap length */
      struct V4L2Plane_info {
-@@ -70,11 +79,12 @@ typedef struct V4L2Buffer {
-  *
-  * @param[in] frame The AVFRame to push the information to
-  * @param[in] buf The V4L2Buffer to get the information from
-+ * @param[in] no_rescale_pts If non-zero do not rescale PTS
-  *
-  * @returns 0 in case of success, AVERROR(EINVAL) if the number of planes is incorrect,
-  * AVERROR(ENOMEM) if the AVBufferRef can't be created.
-  */
--int ff_v4l2_buffer_buf_to_avframe(AVFrame *frame, V4L2Buffer *buf);
-+int ff_v4l2_buffer_buf_to_avframe(AVFrame *frame, V4L2Buffer *buf, int no_rescale_pts);
-
- /**
-  * Extracts the data from a V4L2Buffer to an AVPacket
-@@ -98,6 +108,9 @@ int ff_v4l2_buffer_buf_to_avpkt(AVPacket
+@@ -60,7 +73,6 @@ typedef struct V4L2Buffer {
+     struct v4l2_buffer buf;
+     struct v4l2_plane planes[VIDEO_MAX_PLANES];
+ 
+-    int flags;
+     enum V4L2Buffer_status status;
+ 
+ } V4L2Buffer;
+@@ -98,6 +110,10 @@ int ff_v4l2_buffer_buf_to_avpkt(AVPacket
   */
  int ff_v4l2_buffer_avpkt_to_buf(const AVPacket *pkt, V4L2Buffer *out);
-
-+int ff_v4l2_buffer_avpkt_to_buf_ext(const AVPacket *pkt, V4L2Buffer *out,
-+                                    const void *extdata, size_t extlen, int no_rescale_pts);
+ 
++int ff_v4l2_buffer_avpkt_to_buf_ext(const AVPacket * const pkt, V4L2Buffer * const out,
++                                    const void *extdata, size_t extlen,
++                                    const int64_t timestamp);
 +
  /**
   * Extracts the data from an AVFrame to a V4L2Buffer
   *
-@@ -116,7 +129,7 @@ int ff_v4l2_buffer_avframe_to_buf(const
+@@ -106,7 +122,7 @@ int ff_v4l2_buffer_avpkt_to_buf(const AV
+  *
+  * @returns 0 in case of success, a negative AVERROR code otherwise
+  */
+-int ff_v4l2_buffer_avframe_to_buf(const AVFrame *frame, V4L2Buffer *out);
++int ff_v4l2_buffer_avframe_to_buf(const AVFrame *frame, V4L2Buffer *out, const int64_t track_ts);
+ 
+ /**
+  * Initializes a V4L2Buffer
+@@ -116,7 +132,7 @@ int ff_v4l2_buffer_avframe_to_buf(const
   *
   * @returns 0 in case of success, a negative AVERROR code otherwise
   */
 -int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index);
-+int ff_v4l2_buffer_initialize(AVBufferRef **avbuf, int index, struct V4L2Context *ctx);
-
++int ff_v4l2_buffer_initialize(AVBufferRef **avbuf, int index, struct V4L2Context *ctx, enum v4l2_memory mem);
+ 
  /**
   * Enqueues a V4L2Buffer
+@@ -127,5 +143,12 @@ int ff_v4l2_buffer_initialize(V4L2Buffer
+  */
+ int ff_v4l2_buffer_enqueue(V4L2Buffer* avbuf);
+ 
++static inline void
++ff_v4l2_buffer_set_avail(V4L2Buffer* const avbuf)
++{
++    avbuf->status = V4L2BUF_AVAILABLE;
++    av_buffer_unref(&avbuf->ref_buf);
++}
++
+ 
+ #endif // AVCODEC_V4L2_BUFFERS_H
 --- a/libavcodec/v4l2_context.c
 +++ b/libavcodec/v4l2_context.c
 @@ -27,11 +27,13 @@
@@ -47139,41 +50997,233 @@
  #include "v4l2_fmt.h"
  #include "v4l2_m2m.h"
 +#include "weak_link.h"
-
+ 
  struct v4l2_format_update {
      uint32_t v4l2_fmt;
-@@ -53,16 +55,6 @@ static inline AVCodecContext *logger(V4L
-     return ctx_to_m2mctx(ctx)->avctx;
- }
-
--static inline unsigned int v4l2_get_width(struct v4l2_format *fmt)
--{
--    return V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? fmt->fmt.pix_mp.width : fmt->fmt.pix.width;
--}
--
--static inline unsigned int v4l2_get_height(struct v4l2_format *fmt)
--{
--    return V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? fmt->fmt.pix_mp.height : fmt->fmt.pix.height;
--}
--
- static AVRational v4l2_get_sar(V4L2Context *ctx)
+@@ -41,26 +43,168 @@ struct v4l2_format_update {
+     int update_avfmt;
+ };
+ 
+-static inline V4L2m2mContext *ctx_to_m2mctx(V4L2Context *ctx)
++
++static inline int64_t track_to_pts(AVCodecContext *avctx, unsigned int n)
  {
-     struct AVRational sar = { 0, 1 };
-@@ -94,8 +86,8 @@ static inline unsigned int v4l2_resoluti
+-    return V4L2_TYPE_IS_OUTPUT(ctx->type) ?
+-        container_of(ctx, V4L2m2mContext, output) :
+-        container_of(ctx, V4L2m2mContext, capture);
++    return (int64_t)n;
+ }
+ 
+-static inline AVCodecContext *logger(V4L2Context *ctx)
++static inline unsigned int pts_to_track(AVCodecContext *avctx, const int64_t pts)
+ {
+-    return ctx_to_m2mctx(ctx)->avctx;
++    return (unsigned int)pts;
+ }
+ 
+-static inline unsigned int v4l2_get_width(struct v4l2_format *fmt)
++// FFmpeg requires us to propagate a number of vars from the coded pkt into
++// the decoded frame. The only thing that tracks like that in V4L2 stateful
++// is timestamp. PTS maps to timestamp for this decode. FFmpeg makes no
++// guarantees about PTS being unique or specified for every frame so replace
++// the supplied PTS with a simple incrementing number and keep a circular
++// buffer of all the things we want preserved (including the original PTS)
++// indexed by the tracking no.
++static int64_t
++xlat_pts_pkt_in(AVCodecContext *const avctx, xlat_track_t *const x, const AVPacket *const avpkt)
+ {
+-    return V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? fmt->fmt.pix_mp.width : fmt->fmt.pix.width;
++    int64_t track_pts;
++
++    // Avoid 0
++    if (++x->track_no == 0)
++        x->track_no = 1;
++
++    track_pts = track_to_pts(avctx, x->track_no);
++
++    av_log(avctx, AV_LOG_TRACE, "In pkt PTS=%" PRId64 ", DTS=%" PRId64 ", track=%" PRId64 ", n=%u\n", avpkt->pts, avpkt->dts, track_pts, x->track_no);
++    x->track_els[x->track_no  % FF_V4L2_M2M_TRACK_SIZE] = (V4L2m2mTrackEl){
++        .discard          = 0,
++        .pending          = 1,
++        .pkt_size         = avpkt->size,
++        .pts              = avpkt->pts,
++        .dts              = avpkt->dts,
++        .reordered_opaque = avctx->reordered_opaque,
++        .pkt_pos          = avpkt->pos,
++        .pkt_duration     = avpkt->duration,
++        .track_pts        = track_pts
++    };
++    return track_pts;
+ }
+ 
+-static inline unsigned int v4l2_get_height(struct v4l2_format *fmt)
++static int64_t
++xlat_pts_frame_in(AVCodecContext *const avctx, xlat_track_t *const x, const AVFrame *const frame)
+ {
+-    return V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? fmt->fmt.pix_mp.height : fmt->fmt.pix.height;
++    int64_t track_pts;
++
++    // Avoid 0
++    if (++x->track_no == 0)
++        x->track_no = 1;
++
++    track_pts = track_to_pts(avctx, x->track_no);
++
++    av_log(avctx, AV_LOG_TRACE, "In frame PTS=%" PRId64 ", track=%" PRId64 ", n=%u\n", frame->pts, track_pts, x->track_no);
++    x->track_els[x->track_no  % FF_V4L2_M2M_TRACK_SIZE] = (V4L2m2mTrackEl){
++        .discard          = 0,
++        .pending          = 1,
++        .pkt_size         = 0,
++        .pts              = frame->pts,
++        .dts              = AV_NOPTS_VALUE,
++        .reordered_opaque = frame->reordered_opaque,
++        .pkt_pos          = frame->pkt_pos,
++        .pkt_duration     = frame->pkt_duration,
++        .track_pts        = track_pts
++    };
++    return track_pts;
++}
++
++
++// Returns -1 if we should discard the frame
++static int
++xlat_pts_frame_out(AVCodecContext *const avctx,
++             xlat_track_t * const x,
++             AVFrame *const frame)
++{
++    unsigned int n = pts_to_track(avctx, frame->pts) % FF_V4L2_M2M_TRACK_SIZE;
++    V4L2m2mTrackEl *const t = x->track_els + n;
++    if (frame->pts == AV_NOPTS_VALUE || frame->pts != t->track_pts)
++    {
++        av_log(avctx, frame->pts == AV_NOPTS_VALUE ? AV_LOG_DEBUG : AV_LOG_WARNING,
++               "Frame tracking failure: pts=%" PRId64 ", track[%d]=%" PRId64 "\n", frame->pts, n, t->track_pts);
++        frame->pts              = AV_NOPTS_VALUE;
++        frame->pkt_dts          = AV_NOPTS_VALUE;
++        frame->reordered_opaque = x->last_opaque;
++        frame->pkt_pos          = -1;
++        frame->pkt_duration     = 0;
++        frame->pkt_size         = -1;
++    }
++    else if (!t->discard)
++    {
++        frame->pts              = t->pending ? t->pts : AV_NOPTS_VALUE;
++        frame->pkt_dts          = t->dts;
++        frame->reordered_opaque = t->reordered_opaque;
++        frame->pkt_pos          = t->pkt_pos;
++        frame->pkt_duration     = t->pkt_duration;
++        frame->pkt_size         = t->pkt_size;
++
++        x->last_opaque = x->track_els[n].reordered_opaque;
++        if (frame->pts != AV_NOPTS_VALUE)
++            x->last_pts = frame->pts;
++        t->pending = 0;
++    }
++    else
++    {
++        av_log(avctx, AV_LOG_DEBUG, "Discard frame (flushed): pts=%" PRId64 ", track[%d]=%" PRId64 "\n", frame->pts, n, t->track_pts);
++        return -1;
++    }
++
++    av_log(avctx, AV_LOG_TRACE, "Out frame PTS=%" PRId64 "/%"PRId64", DTS=%" PRId64 ", track=%"PRId64", n=%d\n",
++           frame->pts, frame->best_effort_timestamp, frame->pkt_dts, t->track_pts, n);
++    return 0;
++}
++
++// Returns -1 if we should discard the frame
++static int
++xlat_pts_pkt_out(AVCodecContext *const avctx,
++             xlat_track_t * const x,
++             AVPacket *const pkt)
++{
++    unsigned int n = pts_to_track(avctx, pkt->pts) % FF_V4L2_M2M_TRACK_SIZE;
++    V4L2m2mTrackEl *const t = x->track_els + n;
++    if (pkt->pts == AV_NOPTS_VALUE || pkt->pts != t->track_pts)
++    {
++        av_log(avctx, pkt->pts == AV_NOPTS_VALUE ? AV_LOG_DEBUG : AV_LOG_WARNING,
++               "Pkt tracking failure: pts=%" PRId64 ", track[%d]=%" PRId64 "\n", pkt->pts, n, t->track_pts);
++        pkt->pts                = AV_NOPTS_VALUE;
++    }
++    else if (!t->discard)
++    {
++        pkt->pts                = t->pending ? t->pts : AV_NOPTS_VALUE;
++
++        x->last_opaque = x->track_els[n].reordered_opaque;
++        if (pkt->pts != AV_NOPTS_VALUE)
++            x->last_pts = pkt->pts;
++        t->pending = 0;
++    }
++    else
++    {
++        av_log(avctx, AV_LOG_DEBUG, "Discard packet (flushed): pts=%" PRId64 ", track[%d]=%" PRId64 "\n", pkt->pts, n, t->track_pts);
++        return -1;
++    }
++
++    // * Would like something much better than this...xlat(offset + out_count)?
++    pkt->dts = pkt->pts;
++    av_log(avctx, AV_LOG_TRACE, "Out pkt PTS=%" PRId64 ", track=%"PRId64", n=%d\n",
++           pkt->pts, t->track_pts, n);
++    return 0;
++}
++
++
++static inline V4L2m2mContext *ctx_to_m2mctx(const V4L2Context *ctx)
++{
++    return V4L2_TYPE_IS_OUTPUT(ctx->type) ?
++        container_of(ctx, V4L2m2mContext, output) :
++        container_of(ctx, V4L2m2mContext, capture);
++}
++
++static inline AVCodecContext *logger(const V4L2Context *ctx)
++{
++    return ctx_to_m2mctx(ctx)->avctx;
+ }
+ 
+ static AVRational v4l2_get_sar(V4L2Context *ctx)
+@@ -81,21 +225,29 @@ static AVRational v4l2_get_sar(V4L2Conte
+     return sar;
+ }
+ 
+-static inline unsigned int v4l2_resolution_changed(V4L2Context *ctx, struct v4l2_format *fmt2)
++static inline int ctx_buffers_alloced(const V4L2Context * const ctx)
++{
++    return ctx->bufrefs != NULL;
++}
++
++// Width/Height changed or we don't have an alloc in the first place?
++static int ctx_resolution_changed(const V4L2Context *ctx, const struct v4l2_format *fmt2)
+ {
+-    struct v4l2_format *fmt1 = &ctx->format;
+-    int ret =  V4L2_TYPE_IS_MULTIPLANAR(ctx->type) ?
+-        fmt1->fmt.pix_mp.width != fmt2->fmt.pix_mp.width ||
+-        fmt1->fmt.pix_mp.height != fmt2->fmt.pix_mp.height
+-        :
+-        fmt1->fmt.pix.width != fmt2->fmt.pix.width ||
+-        fmt1->fmt.pix.height != fmt2->fmt.pix.height;
++    const struct v4l2_format *fmt1 = &ctx->format;
++    int ret = !ctx_buffers_alloced(ctx) ||
++        (V4L2_TYPE_IS_MULTIPLANAR(ctx->type) ?
++            fmt1->fmt.pix_mp.width != fmt2->fmt.pix_mp.width ||
++            fmt1->fmt.pix_mp.height != fmt2->fmt.pix_mp.height
++            :
++            fmt1->fmt.pix.width != fmt2->fmt.pix.width ||
++            fmt1->fmt.pix.height != fmt2->fmt.pix.height);
+ 
      if (ret)
-         av_log(logger(ctx), AV_LOG_DEBUG, "%s changed (%dx%d) -> (%dx%d)\n",
+-        av_log(logger(ctx), AV_LOG_DEBUG, "%s changed (%dx%d) -> (%dx%d)\n",
++        av_log(logger(ctx), AV_LOG_DEBUG, "V4L2 %s changed: alloc=%d (%dx%d) -> (%dx%d)\n",
              ctx->name,
 -            v4l2_get_width(fmt1), v4l2_get_height(fmt1),
 -            v4l2_get_width(fmt2), v4l2_get_height(fmt2));
++            ctx_buffers_alloced(ctx),
 +            ff_v4l2_get_format_width(fmt1), ff_v4l2_get_format_height(fmt1),
 +            ff_v4l2_get_format_width(fmt2), ff_v4l2_get_format_height(fmt2));
-
+ 
      return ret;
  }
-@@ -153,58 +145,67 @@ static inline void v4l2_save_to_context(
+@@ -153,90 +305,110 @@ static inline void v4l2_save_to_context(
      }
  }
-
+ 
 -/**
 - * handle resolution change event and end of stream event
 - * returns 1 if reinit was successful, negative if it failed
@@ -47192,7 +51242,7 @@
 +        .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
 +        .target = V4L2_SEL_TGT_COMPOSE
 +    };
-
+ 
 -    ret = ioctl(s->fd, VIDIOC_DQEVENT, &evt);
 -    if (ret < 0) {
 -        av_log(logger(ctx), AV_LOG_ERROR, "%s VIDIOC_DQEVENT\n", ctx->name);
@@ -47201,7 +51251,7 @@
 +    memset(r, 0, sizeof(*r));
 +    if (ioctl(s->fd, VIDIOC_G_SELECTION, &selection))
 +        return AVERROR(errno);
-
+ 
 -    if (evt.type == V4L2_EVENT_EOS) {
 -        ctx->done = 1;
 -        return 0;
@@ -47209,49 +51259,45 @@
 +    *r = selection.r;
 +    return 0;
 +}
-
+ 
 -    if (evt.type != V4L2_EVENT_SOURCE_CHANGE)
 -        return 0;
 +static int do_source_change(V4L2m2mContext * const s)
 +{
 +    AVCodecContext *const avctx = s->avctx;
-+
+ 
+-    ret = ioctl(s->fd, VIDIOC_G_FMT, &out_fmt);
+-    if (ret) {
+-        av_log(logger(ctx), AV_LOG_ERROR, "%s VIDIOC_G_FMT\n", s->output.name);
+-        return 0;
+-    }
 +    int ret;
 +    int reinit;
-+    int full_reinit;
 +    struct v4l2_format cap_fmt = s->capture.format;
-+    struct v4l2_format out_fmt = s->output.format;
 +
-+    s->resize_pending = 0;
 +    s->capture.done = 0;
-
-     ret = ioctl(s->fd, VIDIOC_G_FMT, &out_fmt);
-     if (ret) {
--        av_log(logger(ctx), AV_LOG_ERROR, "%s VIDIOC_G_FMT\n", s->output.name);
-+        av_log(avctx, AV_LOG_ERROR, "%s VIDIOC_G_FMT failed\n", s->output.name);
-         return 0;
-     }
-
+ 
      ret = ioctl(s->fd, VIDIOC_G_FMT, &cap_fmt);
      if (ret) {
 -        av_log(logger(ctx), AV_LOG_ERROR, "%s VIDIOC_G_FMT\n", s->capture.name);
 +        av_log(avctx, AV_LOG_ERROR, "%s VIDIOC_G_FMT failed\n", s->capture.name);
          return 0;
      }
-
-     full_reinit = v4l2_resolution_changed(&s->output, &out_fmt);
-     if (full_reinit) {
+ 
+-    full_reinit = v4l2_resolution_changed(&s->output, &out_fmt);
+-    if (full_reinit) {
 -        s->output.height = v4l2_get_height(&out_fmt);
 -        s->output.width = v4l2_get_width(&out_fmt);
 -        s->output.sample_aspect_ratio = v4l2_get_sar(&s->output);
-+        s->output.height = ff_v4l2_get_format_height(&out_fmt);
-+        s->output.width = ff_v4l2_get_format_width(&out_fmt);
-     }
-+    s->output.sample_aspect_ratio = v4l2_get_sar(&s->output);
-+
+-    }
 +    get_default_selection(&s->capture, &s->capture.selection);
-
-     reinit = v4l2_resolution_changed(&s->capture, &cap_fmt);
++
++    reinit = ctx_resolution_changed(&s->capture, &cap_fmt);
++    if ((s->quirks & FF_V4L2_QUIRK_REINIT_ALWAYS) != 0)
++        reinit = 1;
+ 
+-    reinit = v4l2_resolution_changed(&s->capture, &cap_fmt);
++    s->capture.format = cap_fmt;
      if (reinit) {
 -        s->capture.height = v4l2_get_height(&cap_fmt);
 -        s->capture.width = v4l2_get_width(&cap_fmt);
@@ -47259,368 +51305,491 @@
 +        s->capture.height = ff_v4l2_get_format_height(&cap_fmt);
 +        s->capture.width = ff_v4l2_get_format_width(&cap_fmt);
      }
+ 
+-    if (full_reinit || reinit)
+-        s->reinit = 1;
+-
+-    if (full_reinit) {
+-        ret = ff_v4l2_m2m_codec_full_reinit(s);
+-        if (ret) {
+-            av_log(logger(ctx), AV_LOG_ERROR, "v4l2_m2m_codec_full_reinit\n");
+-            return AVERROR(EINVAL);
+-        }
+-        goto reinit_run;
++    // If we don't support selection (or it is bust) and we obviously have HD then kludge
++    if ((s->capture.selection.width == 0 || s->capture.selection.height == 0) &&
++        (s->capture.height == 1088 && s->capture.width == 1920)) {
++        s->capture.selection = (struct v4l2_rect){.width = 1920, .height = 1080};
+     }
+ 
 +    s->capture.sample_aspect_ratio = v4l2_get_sar(&s->capture);
 +
-+    av_log(avctx, AV_LOG_DEBUG, "Source change: SAR: %d/%d, crop %dx%d @ %d,%d\n",
++    av_log(avctx, AV_LOG_DEBUG, "Source change: SAR: %d/%d, wxh %dx%d crop %dx%d @ %d,%d, reinit=%d\n",
 +           s->capture.sample_aspect_ratio.num, s->capture.sample_aspect_ratio.den,
++           s->capture.width, s->capture.height,
 +           s->capture.selection.width, s->capture.selection.height,
-+           s->capture.selection.left, s->capture.selection.top);
-
-     if (full_reinit || reinit)
-         s->reinit = 1;
-@@ -212,34 +213,88 @@ static int v4l2_handle_event(V4L2Context
-     if (full_reinit) {
-         ret = ff_v4l2_m2m_codec_full_reinit(s);
-         if (ret) {
--            av_log(logger(ctx), AV_LOG_ERROR, "v4l2_m2m_codec_full_reinit\n");
-+            av_log(avctx, AV_LOG_ERROR, "v4l2_m2m_codec_full_reinit failed\n");
-             return AVERROR(EINVAL);
-         }
-         goto reinit_run;
-     }
-
++           s->capture.selection.left, s->capture.selection.top, reinit);
++
      if (reinit) {
 -        if (s->avctx)
+-            ret = ff_set_dimensions(s->avctx, s->capture.width, s->capture.height);
 +        if (avctx)
-             ret = ff_set_dimensions(s->avctx, s->capture.width, s->capture.height);
++            ret = ff_set_dimensions(s->avctx,
++                                    s->capture.selection.width != 0 ? s->capture.selection.width : s->capture.width,
++                                    s->capture.selection.height != 0 ? s->capture.selection.height : s->capture.height);
          if (ret < 0)
 -            av_log(logger(ctx), AV_LOG_WARNING, "update avcodec height and width\n");
 +            av_log(avctx, AV_LOG_WARNING, "update avcodec height and width failed\n");
-
+ 
          ret = ff_v4l2_m2m_codec_reinit(s);
          if (ret) {
 -            av_log(logger(ctx), AV_LOG_ERROR, "v4l2_m2m_codec_reinit\n");
 +            av_log(avctx, AV_LOG_ERROR, "v4l2_m2m_codec_reinit failed\n");
              return AVERROR(EINVAL);
          }
++
++        if (s->capture.width > ff_v4l2_get_format_width(&s->capture.format) ||
++            s->capture.height > ff_v4l2_get_format_height(&s->capture.format)) {
++            av_log(avctx, AV_LOG_ERROR, "Format post reinit too small: wanted %dx%d > got %dx%d\n",
++                   s->capture.width, s->capture.height,
++                   ff_v4l2_get_format_width(&s->capture.format), ff_v4l2_get_format_height(&s->capture.format));
++            return AVERROR(EINVAL);
++        }
++
++        // Update pixel format - should only actually do something on initial change
++        s->capture.av_pix_fmt =
++            ff_v4l2_format_v4l2_to_avfmt(ff_v4l2_get_format_pixelformat(&s->capture.format), AV_CODEC_ID_RAWVIDEO);
++        if (s->output_drm) {
++            avctx->pix_fmt = AV_PIX_FMT_DRM_PRIME;
++            avctx->sw_pix_fmt = s->capture.av_pix_fmt;
++        }
++        else
++            avctx->pix_fmt = s->capture.av_pix_fmt;
++
          goto reinit_run;
      }
-
+ 
 -    /* dummy event received */
 -    return 0;
 +    /* Buffers are OK so just stream off to ack */
-+    av_log(avctx, AV_LOG_DEBUG, "%s: Parameters only\n", __func__);
++    av_log(avctx, AV_LOG_DEBUG, "%s: Parameters only - restart decode\n", __func__);
 +
 +    ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
 +    if (ret)
 +        av_log(avctx, AV_LOG_ERROR, "capture VIDIOC_STREAMOFF failed\n");
 +    s->draining = 0;
-
+ 
      /* reinit executed */
  reinit_run:
 +    ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMON);
      return 1;
  }
-
-+static int ctx_done(V4L2Context * const ctx)
-+{
-+    int rv = 0;
-+    V4L2m2mContext * const s = ctx_to_m2mctx(ctx);
-+
-+    ctx->done = 1;
-+
-+    if (s->resize_pending && !V4L2_TYPE_IS_OUTPUT(ctx->type))
-+        rv = do_source_change(s);
-+
-+    return rv;
-+}
-+
-+/**
-+ * handle resolution change event and end of stream event
-+ * returns 1 if reinit was successful, negative if it failed
-+ * returns 0 if reinit was not executed
-+ */
-+static int v4l2_handle_event(V4L2Context *ctx)
-+{
-+    V4L2m2mContext * const s = ctx_to_m2mctx(ctx);
-+    struct v4l2_event evt = { 0 };
-+    int ret;
-+
-+    ret = ioctl(s->fd, VIDIOC_DQEVENT, &evt);
-+    if (ret < 0) {
-+        av_log(logger(ctx), AV_LOG_ERROR, "%s VIDIOC_DQEVENT\n", ctx->name);
-+        return 0;
-+    }
-+
-+    av_log(logger(ctx), AV_LOG_INFO, "Dq event %d\n", evt.type);
-+
-+    if (evt.type == V4L2_EVENT_EOS) {
-+//        ctx->done = 1;
-+        av_log(logger(ctx), AV_LOG_TRACE, "%s VIDIOC_EVENT_EOS\n", ctx->name);
-+        return 0;
-+    }
-+
-+    if (evt.type != V4L2_EVENT_SOURCE_CHANGE)
-+        return 0;
-+
-+    s->resize_pending = 1;
-+    if (!ctx->done)
-+        return 0;
-+
-+    return do_source_change(s);
-+}
-+
- static int v4l2_stop_decode(V4L2Context *ctx)
- {
-     struct v4l2_decoder_cmd cmd = {
-@@ -280,8 +335,26 @@ static int v4l2_stop_encode(V4L2Context
+ 
+@@ -280,171 +452,277 @@ static int v4l2_stop_encode(V4L2Context
      return 0;
  }
-
-+static int count_in_driver(const V4L2Context * const ctx)
+ 
+-static V4L2Buffer* v4l2_dequeue_v4l2buf(V4L2Context *ctx, int timeout)
+-{
+-    struct v4l2_plane planes[VIDEO_MAX_PLANES];
+-    struct v4l2_buffer buf = { 0 };
+-    V4L2Buffer *avbuf;
+-    struct pollfd pfd = {
+-        .events =  POLLIN | POLLRDNORM | POLLPRI | POLLOUT | POLLWRNORM, /* default blocking capture */
+-        .fd = ctx_to_m2mctx(ctx)->fd,
++// DQ a buffer
++// Amalgamates all the various ways there are of signalling EOS/Event to
++// generate a consistant EPIPE.
++//
++// Sets ctx->flag_last if next dq would produce EPIPE (i.e. stream has stopped)
++//
++// Returns:
++//  0               Success
++//  AVERROR(EPIPE)  Nothing more to read
++//  AVERROR(ENOSPC) No buffers in Q to put result in
++//  *               AVERROR(..)
++
++ static int
++dq_buf(V4L2Context * const ctx, V4L2Buffer ** const ppavbuf)
 +{
-+    int i;
-+    int n = 0;
++    V4L2m2mContext * const m = ctx_to_m2mctx(ctx);
++    AVCodecContext * const avctx = m->avctx;
++    V4L2Buffer * avbuf;
++    const int is_mp = V4L2_TYPE_IS_MULTIPLANAR(ctx->type);
 +
-+    if (!ctx->bufrefs)
-+        return -1;
++    struct v4l2_plane planes[VIDEO_MAX_PLANES] = {{0}};
 +
-+    for (i = 0; i < ctx->num_buffers; ++i) {
-+        V4L2Buffer *const avbuf = (V4L2Buffer *)ctx->bufrefs[i]->data;
-+        if (avbuf->status == V4L2BUF_IN_DRIVER)
-+            ++n;
-+    }
-+    return n;
-+}
-+
- static V4L2Buffer* v4l2_dequeue_v4l2buf(V4L2Context *ctx, int timeout)
- {
-+    V4L2m2mContext * const s = ctx_to_m2mctx(ctx);
-+    const int is_capture = !V4L2_TYPE_IS_OUTPUT(ctx->type);
-     struct v4l2_plane planes[VIDEO_MAX_PLANES];
-     struct v4l2_buffer buf = { 0 };
-     V4L2Buffer *avbuf;
-@@ -290,50 +363,84 @@ static V4L2Buffer* v4l2_dequeue_v4l2buf(
-         .fd = ctx_to_m2mctx(ctx)->fd,
++    struct v4l2_buffer buf = {
++        .type = ctx->type,
++        .memory = V4L2_MEMORY_MMAP,
      };
-     int i, ret;
-+    int no_rx_means_done = 0;
-
+-    int i, ret;
+ 
 -    if (!V4L2_TYPE_IS_OUTPUT(ctx->type) && ctx->buffers) {
-+    if (is_capture && ctx->bufrefs) {
-         for (i = 0; i < ctx->num_buffers; i++) {
+-        for (i = 0; i < ctx->num_buffers; i++) {
 -            if (ctx->buffers[i].status == V4L2BUF_IN_DRIVER)
-+            avbuf = (V4L2Buffer *)ctx->bufrefs[i]->data;
-+            if (avbuf->status == V4L2BUF_IN_DRIVER)
-                 break;
-         }
-         if (i == ctx->num_buffers)
+-                break;
+-        }
+-        if (i == ctx->num_buffers)
 -            av_log(logger(ctx), AV_LOG_WARNING, "All capture buffers returned to "
-+            av_log(logger(ctx), AV_LOG_WARNING, "All capture buffers (%d) returned to "
-                                                 "userspace. Increase num_capture_buffers "
-                                                 "to prevent device deadlock or dropped "
+-                                                "userspace. Increase num_capture_buffers "
+-                                                "to prevent device deadlock or dropped "
 -                                                "packets/frames.\n");
-+                                                "packets/frames.\n", i);
-     }
-
-+#if 0
-+    // I think this is true but pointless
-+    // we will get some other form of EOF signal
-+
-     /* if we are draining and there are no more capture buffers queued in the driver we are done */
+-    }
+-
+-    /* if we are draining and there are no more capture buffers queued in the driver we are done */
 -    if (!V4L2_TYPE_IS_OUTPUT(ctx->type) && ctx_to_m2mctx(ctx)->draining) {
-+    if (is_capture && ctx_to_m2mctx(ctx)->draining) {
-         for (i = 0; i < ctx->num_buffers; i++) {
-             /* capture buffer initialization happens during decode hence
-              * detection happens at runtime
-              */
+-        for (i = 0; i < ctx->num_buffers; i++) {
+-            /* capture buffer initialization happens during decode hence
+-             * detection happens at runtime
+-             */
 -            if (!ctx->buffers)
-+            if (!ctx->bufrefs)
-                 break;
-
+-                break;
+-
 -            if (ctx->buffers[i].status == V4L2BUF_IN_DRIVER)
-+            avbuf = (V4L2Buffer *)ctx->bufrefs[i]->data;
-+            if (avbuf->status == V4L2BUF_IN_DRIVER)
-                 goto start;
-         }
-         ctx->done = 1;
-         return NULL;
-     }
-+#endif
-
- start:
+-                goto start;
+-        }
+-        ctx->done = 1;
+-        return NULL;
+-    }
+-
+-start:
 -    if (V4L2_TYPE_IS_OUTPUT(ctx->type))
 -        pfd.events =  POLLOUT | POLLWRNORM;
 -    else {
-+    if (is_capture) {
-         /* no need to listen to requests for more input while draining */
-         if (ctx_to_m2mctx(ctx)->draining)
-             pfd.events =  POLLIN | POLLRDNORM | POLLPRI;
-+    } else {
-+        pfd.events =  POLLOUT | POLLWRNORM;
+-        /* no need to listen to requests for more input while draining */
+-        if (ctx_to_m2mctx(ctx)->draining)
+-            pfd.events =  POLLIN | POLLRDNORM | POLLPRI;
++    *ppavbuf = NULL;
++
++    if (ctx->flag_last)
++        return AVERROR(EPIPE);
++
++    if (is_mp) {
++        buf.length = VIDEO_MAX_PLANES;
++        buf.m.planes = planes;
      }
-+    no_rx_means_done = s->resize_pending && is_capture;
-
-     for (;;) {
+ 
+-    for (;;) {
 -        ret = poll(&pfd, 1, timeout);
-+        // If we have a resize pending then all buffers should be Qed
-+        // With a resize pending we should be in drain but evidence suggests
-+        // that not all decoders do this so poll to clear
-+        int t2 = no_rx_means_done ? 0 : timeout < 0 ? 3000 : timeout;
-+        const int e = pfd.events;
-+
-+        ret = poll(&pfd, 1, t2);
-+
-         if (ret > 0)
-             break;
+-        if (ret > 0)
+-            break;
 -        if (errno == EINTR)
 -            continue;
+-        return NULL;
++    while (ioctl(m->fd, VIDIOC_DQBUF, &buf) != 0) {
++        const int err = errno;
++        av_assert0(AVERROR(err) < 0);
++        if (err != EINTR) {
++            av_log(avctx, AV_LOG_DEBUG, "%s VIDIOC_DQBUF, errno (%s)\n",
++                ctx->name, av_err2str(AVERROR(err)));
 +
-+        if (ret < 0) {
-+            int err = errno;
-+            if (err == EINTR)
-+                continue;
-+            av_log(logger(ctx), AV_LOG_ERROR, "=== poll error %d (%s): events=%#x, cap buffers=%d\n",
-+                   err, strerror(err),
-+                   e, count_in_driver(ctx));
-+            return NULL;
-+        }
++            if (err == EPIPE)
++                ctx->flag_last = 1;
 +
-+        // ret == 0 (timeout)
-+        if (no_rx_means_done) {
-+            av_log(logger(ctx), AV_LOG_DEBUG, "Ctx done on timeout\n");
-+            ret = ctx_done(ctx);
-+            if (ret > 0)
-+                goto start;
++            return AVERROR(err);
 +        }
-+        if (timeout == -1)
-+            av_log(logger(ctx), AV_LOG_ERROR, "=== poll unexpected TIMEOUT: events=%#x, cap buffers=%d\n", e, count_in_driver(ctx));;
-         return NULL;
      }
-
-@@ -343,7 +450,8 @@ start:
-            no need to raise a warning */
-         if (timeout == 0) {
-             for (i = 0; i < ctx->num_buffers; i++) {
++    atomic_fetch_sub(&ctx->q_count, 1);
+ 
+-    /* 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 (timeout == 0) {
+-            for (i = 0; i < ctx->num_buffers; i++) {
 -                if (ctx->buffers[i].status != V4L2BUF_AVAILABLE)
-+                avbuf = (V4L2Buffer *)ctx->bufrefs[i]->data;
-+                if (avbuf->status != V4L2BUF_AVAILABLE)
-                     av_log(logger(ctx), AV_LOG_WARNING, "%s POLLERR\n", ctx->name);
-             }
+-                    av_log(logger(ctx), AV_LOG_WARNING, "%s POLLERR\n", ctx->name);
+-            }
++    avbuf = (V4L2Buffer *)ctx->bufrefs[buf.index]->data;
++    ff_v4l2_buffer_set_avail(avbuf);
++    avbuf->buf = buf;
++    if (is_mp) {
++        memcpy(avbuf->planes, planes, sizeof(planes));
++        avbuf->buf.m.planes = avbuf->planes;
++    }
++    // Done with any attached buffer
++    av_buffer_unref(&avbuf->ref_buf);
++
++    if (V4L2_TYPE_IS_CAPTURE(ctx->type)) {
++        // Zero length cap buffer return == EOS
++        if ((is_mp ? buf.m.planes[0].bytesused : buf.bytesused) == 0) {
++            av_log(avctx, AV_LOG_DEBUG, "Buffer empty - reQ\n");
++
++            // Must reQ so we don't leak
++            // May not matter if the next thing we do is release all the
++            // buffers but better to be tidy.
++            ff_v4l2_buffer_enqueue(avbuf);
++
++            ctx->flag_last = 1;
++            return AVERROR(EPIPE);
          }
-@@ -361,22 +469,25 @@ start:
-             ctx->done = 1;
-             return NULL;
-         }
+-        else
+-            av_log(logger(ctx), AV_LOG_WARNING, "%s POLLERR\n", ctx->name);
+ 
+-        return NULL;
++#ifdef V4L2_BUF_FLAG_LAST
++        // If flag_last set then this contains data but is the last frame
++        // so remember that but return OK
++        if ((buf.flags & V4L2_BUF_FLAG_LAST) != 0)
++            ctx->flag_last = 1;
++#endif
+     }
+ 
+-    /* 1. handle resolution changes */
+-    if (pfd.revents & POLLPRI) {
+-        ret = v4l2_handle_event(ctx);
+-        if (ret < 0) {
+-            /* if re-init failed, abort */
+-            ctx->done = 1;
+-            return NULL;
+-        }
 -        if (ret) {
 -            /* if re-init was successful drop the buffer (if there was one)
 -             * since we had to reconfigure capture (unmap all buffers)
 -             */
 -            return NULL;
--        }
-+        if (ret > 0)
-+            goto start;
-     }
-
-     /* 2. dequeue the buffer */
-     if (pfd.revents & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM)) {
-
--        if (!V4L2_TYPE_IS_OUTPUT(ctx->type)) {
-+        if (is_capture) {
-             /* there is a capture buffer ready */
-             if (pfd.revents & (POLLIN | POLLRDNORM))
-                 goto dequeue;
-
-+            // CAPTURE Q drained
-+            if (no_rx_means_done) {
-+                if (ctx_done(ctx) > 0)
-+                    goto start;
-+                return NULL;
-+            }
++    *ppavbuf = avbuf;
++    return 0;
++}
 +
-             /* the driver is ready to accept more input; instead of waiting for the capture
-              * buffer to complete we return NULL so input can proceed (we are single threaded)
-              */
-@@ -394,37 +505,58 @@ dequeue:
-             buf.m.planes = planes;
++/**
++ * handle resolution change event and end of stream event
++ * Expects to be called after the stream has stopped
++ *
++ * returns 1 if reinit was successful, negative if it failed
++ * returns 0 if reinit was not executed
++ */
++static int
++get_event(V4L2m2mContext * const m)
++{
++    AVCodecContext * const avctx = m->avctx;
++    struct v4l2_event evt = { 0 };
++
++    while (ioctl(m->fd, VIDIOC_DQEVENT, &evt) != 0) {
++        const int rv = AVERROR(errno);
++        if (rv == AVERROR(EINTR))
++            continue;
++        if (rv == AVERROR(EAGAIN)) {
++            av_log(avctx, AV_LOG_WARNING, "V4L2 failed to get expected event - assume EOS\n");
++            return AVERROR_EOF;
          }
-
++        av_log(avctx, AV_LOG_ERROR, "V4L2 VIDIOC_DQEVENT: %s\n", av_err2str(rv));
++        return rv;
+     }
+ 
+-    /* 2. dequeue the buffer */
+-    if (pfd.revents & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM)) {
++    av_log(avctx, AV_LOG_DEBUG, "Dq event %d\n", evt.type);
+ 
+-        if (!V4L2_TYPE_IS_OUTPUT(ctx->type)) {
+-            /* there is a capture buffer ready */
+-            if (pfd.revents & (POLLIN | POLLRDNORM))
+-                goto dequeue;
++    if (evt.type == V4L2_EVENT_EOS) {
++        av_log(avctx, AV_LOG_TRACE, "V4L2 VIDIOC_EVENT_EOS\n");
++        return AVERROR_EOF;
++    }
++
++    if (evt.type == V4L2_EVENT_SOURCE_CHANGE)
++        return do_source_change(m);
++
++    return 0;
++}
++
++
++// Get a buffer
++// If output then just gets the buffer in the expected way
++// If capture then runs the capture state m/c to deal with res change etc.
++// If return value == 0 then *ppavbuf != NULL
++
++static int
++get_qbuf(V4L2Context * const ctx, V4L2Buffer ** const ppavbuf, const int timeout)
++{
++    V4L2m2mContext * const m = ctx_to_m2mctx(ctx);
++    AVCodecContext * const avctx = m->avctx;
++    const int is_cap = V4L2_TYPE_IS_CAPTURE(ctx->type);
++
++    const unsigned int poll_cap = (POLLIN | POLLRDNORM);
++    const unsigned int poll_out = (POLLOUT | POLLWRNORM);
++    const unsigned int poll_event = POLLPRI;
++
++    *ppavbuf = NULL;
+ 
+-            /* the driver is ready to accept more input; instead of waiting for the capture
+-             * buffer to complete we return NULL so input can proceed (we are single threaded)
+-             */
+-            if (pfd.revents & (POLLOUT | POLLWRNORM))
+-                return NULL;
++    for (;;) {
++        struct pollfd pfd = {
++            .fd = m->fd,
++            // If capture && stream not started then assume we are waiting for the initial event
++            .events = !is_cap ? poll_out :
++                !ff_v4l2_ctx_eos(ctx) && ctx->streamon ? poll_cap :
++                    poll_event,
++        };
++        int ret;
++
++        if (ctx->done) {
++            av_log(avctx, AV_LOG_TRACE, "V4L2 %s already done\n", ctx->name);
++            return AVERROR_EOF;
+         }
+ 
+-dequeue:
+-        memset(&buf, 0, sizeof(buf));
+-        buf.memory = V4L2_MEMORY_MMAP;
+-        buf.type = ctx->type;
+-        if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type)) {
+-            memset(planes, 0, sizeof(planes));
+-            buf.length = VIDEO_MAX_PLANES;
+-            buf.m.planes = planes;
++        // If capture && timeout == -1 then also wait for rx buffer free
++        if (is_cap && timeout == -1 && m->output.streamon && !m->draining)
++            pfd.events |= poll_out;
++
++        // If nothing Qed all we will get is POLLERR - avoid that
++        if ((pfd.events == poll_out && atomic_load(&m->output.q_count) == 0) ||
++            (pfd.events == poll_cap && atomic_load(&m->capture.q_count) == 0) ||
++            (pfd.events == (poll_cap | poll_out) && atomic_load(&m->capture.q_count) == 0 && atomic_load(&m->output.q_count) == 0)) {
++            av_log(avctx, AV_LOG_TRACE, "V4L2 poll %s empty\n", ctx->name);
++            return AVERROR(ENOSPC);
+         }
+ 
 -        ret = ioctl(ctx_to_m2mctx(ctx)->fd, VIDIOC_DQBUF, &buf);
 -        if (ret) {
 -            if (errno != EAGAIN) {
 -                ctx->done = 1;
 -                if (errno != EPIPE)
-+        while ((ret = ioctl(ctx_to_m2mctx(ctx)->fd, VIDIOC_DQBUF, &buf)) == -1) {
-+            const int err = errno;
-+            if (err == EINTR)
-+                continue;
-+            if (err != EAGAIN) {
-+                // EPIPE on CAPTURE can be used instead of BUF_FLAG_LAST
-+                if (err != EPIPE || !is_capture)
-                     av_log(logger(ctx), AV_LOG_DEBUG, "%s VIDIOC_DQBUF, errno (%s)\n",
+-                    av_log(logger(ctx), AV_LOG_DEBUG, "%s VIDIOC_DQBUF, errno (%s)\n",
 -                        ctx->name, av_err2str(AVERROR(errno)));
-+                        ctx->name, av_err2str(AVERROR(err)));
-+                if (ctx_done(ctx) > 0)
-+                    goto start;
-             }
-             return NULL;
-         }
-+        --ctx->q_count;
-+        av_log(logger(ctx), AV_LOG_DEBUG, "--- %s VIDIOC_DQBUF OK: index=%d, ts=%ld.%06ld, count=%d, dq=%d\n",
-+               ctx->name, buf.index,
-+               buf.timestamp.tv_sec, buf.timestamp.tv_usec,
-+               ctx->q_count, ++ctx->dq_count);
-+
-+        avbuf = (V4L2Buffer *)ctx->bufrefs[buf.index]->data;
-+        avbuf->status = V4L2BUF_AVAILABLE;
-+        avbuf->buf = buf;
-+        if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type)) {
-+            memcpy(avbuf->planes, planes, sizeof(planes));
-+            avbuf->buf.m.planes = avbuf->planes;
++        // Timeout kludged s.t. "forever" eventually gives up & produces logging
++        // If waiting for an event when we have seen a last_frame then we expect
++        //   it to be ready already so force a short timeout
++        ret = poll(&pfd, 1,
++                   ff_v4l2_ctx_eos(ctx) ? 10 :
++                   timeout == -1 ? 3000 : timeout);
++        if (ret < 0) {
++            ret = AVERROR(errno);  // Remember errno before logging etc.
++            av_assert0(ret < 0);
 +        }
-
--        if (ctx_to_m2mctx(ctx)->draining && !V4L2_TYPE_IS_OUTPUT(ctx->type)) {
-+        if (ctx_to_m2mctx(ctx)->draining && is_capture) {
-             int bytesused = V4L2_TYPE_IS_MULTIPLANAR(buf.type) ?
-                             buf.m.planes[0].bytesused : buf.bytesused;
-             if (bytesused == 0) {
--                ctx->done = 1;
-+                av_log(logger(ctx), AV_LOG_DEBUG, "Buffer empty - reQ\n");
 +
-+                // Must reQ so we don't leak
-+                // May not matter if the next thing we do is release all the
-+                // buffers but better to be tidy.
-+                ff_v4l2_buffer_enqueue(avbuf);
++        av_log(avctx, AV_LOG_TRACE, "V4L2 poll %s ret=%d, timeout=%d, events=%#x, revents=%#x\n",
++               ctx->name, ret, timeout, pfd.events, pfd.revents);
 +
-+                if (ctx_done(ctx) > 0)
-+                    goto start;
-                 return NULL;
++        if (ret < 0) {
++            if (ret == AVERROR(EINTR))
++                continue;
++            av_log(avctx, AV_LOG_ERROR, "V4L2 %s poll error %d (%s)\n", ctx->name, AVUNERROR(ret), av_err2str(ret));
++            return ret;
++        }
++
++        if (ret == 0) {
++            if (timeout == -1)
++                av_log(avctx, AV_LOG_ERROR, "V4L2 %s poll unexpected timeout: events=%#x\n", ctx->name, pfd.events);
++            if (ff_v4l2_ctx_eos(ctx)) {
++                av_log(avctx, AV_LOG_WARNING, "V4L2 %s poll event timeout\n", ctx->name);
++                ret = get_event(m);
++                if (ret < 0) {
++                    ctx->done = 1;
++                    return ret;
++                }
              }
- #ifdef V4L2_BUF_FLAG_LAST
+-            return NULL;
++            return AVERROR(EAGAIN);
+         }
+ 
+-        if (ctx_to_m2mctx(ctx)->draining && !V4L2_TYPE_IS_OUTPUT(ctx->type)) {
+-            int bytesused = V4L2_TYPE_IS_MULTIPLANAR(buf.type) ?
+-                            buf.m.planes[0].bytesused : buf.bytesused;
+-            if (bytesused == 0) {
++        if ((pfd.revents & POLLERR) != 0) {
++            av_log(avctx, AV_LOG_WARNING, "V4L2 %s POLLERR\n", ctx->name);
++            return AVERROR_UNKNOWN;
++        }
++
++        if ((pfd.revents & poll_event) != 0) {
++            ret = get_event(m);
++            if (ret < 0) {
+                 ctx->done = 1;
+-                return NULL;
++                return ret;
+             }
+-#ifdef V4L2_BUF_FLAG_LAST
 -            if (buf.flags & V4L2_BUF_FLAG_LAST)
 -                ctx->done = 1;
-+            if (buf.flags & V4L2_BUF_FLAG_LAST) {
-+                av_log(logger(ctx), AV_LOG_TRACE, "FLAG_LAST set\n");
-+                avbuf->status = V4L2BUF_IN_USE;  // Avoid flushing this buffer
-+                ctx_done(ctx);
-+            }
- #endif
+-#endif
++            continue;
++        }
++
++        if ((pfd.revents & poll_cap) != 0) {
++            ret = dq_buf(ctx, ppavbuf);
++            if (ret == AVERROR(EPIPE))
++                continue;
++            return ret;
          }
-
+ 
 -        avbuf = &ctx->buffers[buf.index];
 -        avbuf->status = V4L2BUF_AVAILABLE;
 -        avbuf->buf = buf;
 -        if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type)) {
 -            memcpy(avbuf->planes, planes, sizeof(planes));
 -            avbuf->buf.m.planes = avbuf->planes;
--        }
-         return avbuf;
++        if ((pfd.revents & poll_out) != 0) {
++            if (is_cap)
++                return AVERROR(EAGAIN);
++            return dq_buf(ctx, ppavbuf);
+         }
+-        return avbuf;
++
++        av_log(avctx, AV_LOG_ERROR, "V4L2 poll unexpected events=%#x, revents=%#x\n", pfd.events, pfd.revents);
++        return AVERROR_UNKNOWN;
      }
-
-@@ -443,8 +575,9 @@ static V4L2Buffer* v4l2_getfree_v4l2buf(
++}
+ 
+-    return NULL;
++// Clear out flags and timestamps that should should be set by the user
++// Returns the passed avbuf
++static V4L2Buffer *
++clean_v4l2_buffer(V4L2Buffer * const avbuf)
++{
++    struct v4l2_buffer *const buf = &avbuf->buf;
++
++    buf->flags = 0;
++    buf->field = V4L2_FIELD_ANY;
++    buf->timestamp = (struct timeval){0};
++    buf->timecode = (struct v4l2_timecode){0};
++    buf->sequence = 0;
++
++    return avbuf;
+ }
+ 
+ static V4L2Buffer* v4l2_getfree_v4l2buf(V4L2Context *ctx)
+ {
+-    int timeout = 0; /* return when no more buffers to dequeue */
+     int i;
+ 
+     /* get back as many output buffers as possible */
+     if (V4L2_TYPE_IS_OUTPUT(ctx->type)) {
+-          do {
+-          } while (v4l2_dequeue_v4l2buf(ctx, timeout));
++        V4L2Buffer * avbuf;
++        do {
++            get_qbuf(ctx, &avbuf, 0);
++        } while (avbuf);
      }
-
+ 
      for (i = 0; i < ctx->num_buffers; i++) {
 -        if (ctx->buffers[i].status == V4L2BUF_AVAILABLE)
 -            return &ctx->buffers[i];
 +        V4L2Buffer * const avbuf = (V4L2Buffer *)ctx->bufrefs[i]->data;
 +        if (avbuf->status == V4L2BUF_AVAILABLE)
-+            return avbuf;
++            return clean_v4l2_buffer(avbuf);
      }
-
+ 
      return NULL;
-@@ -452,25 +585,45 @@ static V4L2Buffer* v4l2_getfree_v4l2buf(
-
+@@ -452,25 +730,45 @@ static V4L2Buffer* v4l2_getfree_v4l2buf(
+ 
  static int v4l2_release_buffers(V4L2Context* ctx)
  {
 -    struct v4l2_requestbuffers req = {
@@ -47632,12 +51801,12 @@
 +    int i;
 +    int ret = 0;
 +    const int fd = ctx_to_m2mctx(ctx)->fd;
-
+ 
 -    for (i = 0; i < ctx->num_buffers; i++) {
 -        V4L2Buffer *buffer = &ctx->buffers[i];
 +    // Orphan any buffers in the wild
 +    ff_weak_link_break(&ctx->wl_master);
-
+ 
 -        for (j = 0; j < buffer->num_planes; j++) {
 -            struct V4L2Plane_info *p = &buffer->plane_info[j];
 -            if (p->mm_addr && p->length)
@@ -47672,15 +51841,15 @@
 +                    "  2. drmIoctl(.., DRM_IOCTL_GEM_CLOSE,... )\n");
          }
      }
-+    ctx->q_count = 0;
-
++    atomic_store(&ctx->q_count, 0);
+ 
 -    return ioctl(ctx_to_m2mctx(ctx)->fd, VIDIOC_REQBUFS, &req);
 +    return ret;
  }
-
+ 
  static inline int v4l2_try_raw_format(V4L2Context* ctx, enum AVPixelFormat pixfmt)
-@@ -499,6 +652,8 @@ static inline int v4l2_try_raw_format(V4
-
+@@ -499,6 +797,8 @@ static inline int v4l2_try_raw_format(V4
+ 
  static int v4l2_get_raw_format(V4L2Context* ctx, enum AVPixelFormat *p)
  {
 +    V4L2m2mContext* s = ctx_to_m2mctx(ctx);
@@ -47688,10 +51857,10 @@
      enum AVPixelFormat pixfmt = ctx->av_pix_fmt;
      struct v4l2_fmtdesc fdesc;
      int ret;
-@@ -517,6 +672,13 @@ static int v4l2_get_raw_format(V4L2Conte
+@@ -517,6 +817,13 @@ static int v4l2_get_raw_format(V4L2Conte
          if (ret)
              return AVERROR(EINVAL);
-
+ 
 +        if (priv->pix_fmt != AV_PIX_FMT_NONE) {
 +            if (fdesc.pixelformat != ff_v4l2_format_avfmt_to_v4l2(priv->pix_fmt)) {
 +                fdesc.index++;
@@ -47702,10 +51871,10 @@
          pixfmt = ff_v4l2_format_v4l2_to_avfmt(fdesc.pixelformat, AV_CODEC_ID_RAWVIDEO);
          ret = v4l2_try_raw_format(ctx, pixfmt);
          if (ret){
-@@ -569,18 +731,77 @@ static int v4l2_get_coded_format(V4L2Con
+@@ -569,30 +876,99 @@ static int v4l2_get_coded_format(V4L2Con
    *
    *****************************************************************************/
-
+ 
 +
 +static void flush_all_buffers_status(V4L2Context* const ctx)
 +{
@@ -47717,9 +51886,9 @@
 +    for (i = 0; i < ctx->num_buffers; ++i) {
 +        struct V4L2Buffer * const buf = (struct V4L2Buffer *)ctx->bufrefs[i]->data;
 +        if (buf->status == V4L2BUF_IN_DRIVER)
-+            buf->status = V4L2BUF_AVAILABLE;
++            ff_v4l2_buffer_set_avail(buf);
 +    }
-+    ctx->q_count = 0;
++    atomic_store(&ctx->q_count, 0);
 +}
 +
 +static int stuff_all_buffers(AVCodecContext * avctx, V4L2Context* ctx)
@@ -47749,18 +51918,25 @@
  int ff_v4l2_context_set_status(V4L2Context* ctx, uint32_t cmd)
  {
      int type = ctx->type;
-     int ret;
+-    int ret;
++    int ret = 0;
 +    AVCodecContext * const avctx = logger(ctx);
-+
-+    ff_mutex_lock(&ctx->lock);
-+
-+    if (cmd == VIDIOC_STREAMON && !V4L2_TYPE_IS_OUTPUT(ctx->type))
-+        stuff_all_buffers(avctx, ctx);
-
-     ret = ioctl(ctx_to_m2mctx(ctx)->fd, cmd, &type);
+ 
+-    ret = ioctl(ctx_to_m2mctx(ctx)->fd, cmd, &type);
 -    if (ret < 0)
 -        return AVERROR(errno);
-+    if (ret < 0) {
++    // Avoid doing anything if there is nothing we can do
++    if (cmd == VIDIOC_STREAMOFF && !ctx_buffers_alloced(ctx) && !ctx->streamon)
++        return 0;
+ 
+-    ctx->streamon = (cmd == VIDIOC_STREAMON);
++    ff_mutex_lock(&ctx->lock);
+ 
+-    return 0;
++    if (cmd == VIDIOC_STREAMON && !V4L2_TYPE_IS_OUTPUT(ctx->type))
++        stuff_all_buffers(avctx, ctx);
++
++    if (ioctl(ctx_to_m2mctx(ctx)->fd, cmd, &type) < 0) {
 +        const int err = errno;
 +        av_log(avctx, AV_LOG_ERROR, "%s set status %d (%s) failed: err=%d\n", ctx->name,
 +               cmd, (cmd == VIDIOC_STREAMON) ? "ON" : "OFF", err);
@@ -47770,73 +51946,153 @@
 +    {
 +        if (cmd == VIDIOC_STREAMOFF)
 +            flush_all_buffers_status(ctx);
-
--    ctx->streamon = (cmd == VIDIOC_STREAMON);
++        else
++            ctx->first_buf = 1;
++
 +        ctx->streamon = (cmd == VIDIOC_STREAMON);
 +        av_log(avctx, AV_LOG_DEBUG, "%s set status %d (%s) OK\n", ctx->name,
 +               cmd, (cmd == VIDIOC_STREAMON) ? "ON" : "OFF");
 +    }
-
--    return 0;
++
++    // Both stream off & on effectively clear flag_last
++    ctx->flag_last = 0;
++
 +    ff_mutex_unlock(&ctx->lock);
 +
 +    return ret;
  }
-
+ 
  int ff_v4l2_context_enqueue_frame(V4L2Context* ctx, const AVFrame* frame)
-@@ -608,7 +829,8 @@ int ff_v4l2_context_enqueue_frame(V4L2Co
+ {
+-    V4L2m2mContext *s = ctx_to_m2mctx(ctx);
++    V4L2m2mContext *const s = ctx_to_m2mctx(ctx);
++    AVCodecContext *const avctx = s->avctx;
++    int64_t track_ts;
+     V4L2Buffer* avbuf;
+     int ret;
+ 
+     if (!frame) {
+         ret = v4l2_stop_encode(ctx);
+         if (ret)
+-            av_log(logger(ctx), AV_LOG_ERROR, "%s stop_encode\n", ctx->name);
++            av_log(avctx, AV_LOG_ERROR, "%s stop_encode\n", ctx->name);
+         s->draining= 1;
+         return 0;
+     }
+@@ -601,23 +977,29 @@ int ff_v4l2_context_enqueue_frame(V4L2Co
+     if (!avbuf)
+         return AVERROR(ENOMEM);
+ 
+-    ret = ff_v4l2_buffer_avframe_to_buf(frame, avbuf);
++    track_ts = xlat_pts_frame_in(avctx, &s->xlat, frame);
++
++    ret = ff_v4l2_buffer_avframe_to_buf(frame, avbuf, track_ts);
+     if (ret)
+         return ret;
+ 
      return ff_v4l2_buffer_enqueue(avbuf);
  }
-
+ 
 -int ff_v4l2_context_enqueue_packet(V4L2Context* ctx, const AVPacket* pkt)
 +int ff_v4l2_context_enqueue_packet(V4L2Context* ctx, const AVPacket* pkt,
-+                                   const void * extdata, size_t extlen, int no_rescale_pts)
++                                   const void * extdata, size_t extlen)
  {
      V4L2m2mContext *s = ctx_to_m2mctx(ctx);
++    AVCodecContext *const avctx = s->avctx;
      V4L2Buffer* avbuf;
-@@ -616,8 +838,9 @@ int ff_v4l2_context_enqueue_packet(V4L2C
-
+     int ret;
++    int64_t track_ts;
+ 
      if (!pkt->size) {
          ret = v4l2_stop_decode(ctx);
 +        // Log but otherwise ignore stop failure
          if (ret)
 -            av_log(logger(ctx), AV_LOG_ERROR, "%s stop_decode\n", ctx->name);
-+            av_log(logger(ctx), AV_LOG_ERROR, "%s stop_decode failed: err=%d\n", ctx->name, ret);
++            av_log(avctx, AV_LOG_ERROR, "%s stop_decode failed: err=%d\n", ctx->name, ret);
          s->draining = 1;
          return 0;
      }
-@@ -626,14 +849,17 @@ int ff_v4l2_context_enqueue_packet(V4L2C
+@@ -626,8 +1008,13 @@ int ff_v4l2_context_enqueue_packet(V4L2C
      if (!avbuf)
          return AVERROR(EAGAIN);
-
+ 
 -    ret = ff_v4l2_buffer_avpkt_to_buf(pkt, avbuf);
 -    if (ret)
-+    ret = ff_v4l2_buffer_avpkt_to_buf_ext(pkt, avbuf, extdata, extlen, no_rescale_pts);
++    track_ts = xlat_pts_pkt_in(avctx, &s->xlat, pkt);
++
++    ret = ff_v4l2_buffer_avpkt_to_buf_ext(pkt, avbuf, extdata, extlen, track_ts);
 +    if (ret == AVERROR(ENOMEM))
 +        av_log(logger(ctx), AV_LOG_ERROR, "Buffer overflow in %s: pkt->size=%d > buf->length=%d\n",
 +               __func__, pkt->size, avbuf->planes[0].length);
 +    else if (ret)
          return ret;
-
+ 
      return ff_v4l2_buffer_enqueue(avbuf);
- }
-
--int ff_v4l2_context_dequeue_frame(V4L2Context* ctx, AVFrame* frame, int timeout)
-+int ff_v4l2_context_dequeue_frame(V4L2Context* ctx, AVFrame* frame, int timeout, int no_rescale_pts)
+@@ -635,42 +1022,36 @@ int ff_v4l2_context_enqueue_packet(V4L2C
+ 
+ int ff_v4l2_context_dequeue_frame(V4L2Context* ctx, AVFrame* frame, int timeout)
  {
++    V4L2m2mContext *s = ctx_to_m2mctx(ctx);
++    AVCodecContext *const avctx = s->avctx;
      V4L2Buffer *avbuf;
-
-@@ -650,7 +876,7 @@ int ff_v4l2_context_dequeue_frame(V4L2Co
-         return AVERROR(EAGAIN);
-     }
-
++    int rv;
+ 
+-    /*
+-     * timeout=-1 blocks until:
+-     *  1. decoded frame available
+-     *  2. an input buffer is ready to be dequeued
+-     */
+-    avbuf = v4l2_dequeue_v4l2buf(ctx, timeout);
+-    if (!avbuf) {
+-        if (ctx->done)
+-            return AVERROR_EOF;
+-
+-        return AVERROR(EAGAIN);
+-    }
++    do {
++        if ((rv = get_qbuf(ctx, &avbuf, timeout)) != 0)
++            return rv;
++        if ((rv = ff_v4l2_buffer_buf_to_avframe(frame, avbuf)) != 0)
++            return rv;
++    } while (xlat_pts_frame_out(avctx, &s->xlat, frame) != 0);
+ 
 -    return ff_v4l2_buffer_buf_to_avframe(frame, avbuf);
-+    return ff_v4l2_buffer_buf_to_avframe(frame, avbuf, no_rescale_pts);
++   return 0;
  }
-
+ 
  int ff_v4l2_context_dequeue_packet(V4L2Context* ctx, AVPacket* pkt)
-@@ -702,78 +928,155 @@ int ff_v4l2_context_get_format(V4L2Conte
-
+ {
++    V4L2m2mContext *s = ctx_to_m2mctx(ctx);
++    AVCodecContext *const avctx = s->avctx;
+     V4L2Buffer *avbuf;
++    int rv;
+ 
+-    /*
+-     * blocks until:
+-     *  1. encoded packet available
+-     *  2. an input buffer ready to be dequeued
+-     */
+-    avbuf = v4l2_dequeue_v4l2buf(ctx, -1);
+-    if (!avbuf) {
+-        if (ctx->done)
+-            return AVERROR_EOF;
++    do {
++        if ((rv = get_qbuf(ctx, &avbuf, -1)) != 0)
++            return rv == AVERROR(ENOSPC) ? AVERROR(EAGAIN) : rv;  // Caller not currently expecting ENOSPC
++        if ((rv = ff_v4l2_buffer_buf_to_avpkt(pkt, avbuf)) != 0)
++            return rv;
++    } while (xlat_pts_pkt_out(avctx, &s->xlat, pkt) != 0);
+ 
+-        return AVERROR(EAGAIN);
+-    }
+-
+-    return ff_v4l2_buffer_buf_to_avpkt(pkt, avbuf);
++    return 0;
+ }
+ 
+ int ff_v4l2_context_get_format(V4L2Context* ctx, int probe)
+@@ -702,78 +1083,160 @@ int ff_v4l2_context_get_format(V4L2Conte
+ 
  int ff_v4l2_context_set_format(V4L2Context* ctx)
  {
 -    return ioctl(ctx_to_m2mctx(ctx)->fd, VIDIOC_S_FMT, &ctx->format);
@@ -47864,29 +52120,30 @@
 +    ret = ioctl(ctx_to_m2mctx(ctx)->fd, VIDIOC_S_FMT, &ctx->format);
 +    return ret;
  }
-
+ 
  void ff_v4l2_context_release(V4L2Context* ctx)
  {
      int ret;
-
+ 
 -    if (!ctx->buffers)
 +    if (!ctx->bufrefs)
          return;
-
+ 
      ret = v4l2_release_buffers(ctx);
      if (ret)
          av_log(logger(ctx), AV_LOG_WARNING, "V4L2 failed to unmap the %s buffers\n", ctx->name);
-
+ 
 -    av_freep(&ctx->buffers);
 +    av_freep(&ctx->bufrefs);
 +    av_buffer_unref(&ctx->frames_ref);
 +
 +    ff_mutex_destroy(&ctx->lock);
++    pthread_cond_destroy(&ctx->cond);
  }
-
+ 
 -int ff_v4l2_context_init(V4L2Context* ctx)
 +
-+static int create_buffers(V4L2Context* const ctx, const unsigned int req_buffers)
++static int create_buffers(V4L2Context* const ctx, const unsigned int req_buffers, const enum v4l2_memory mem)
  {
 -    V4L2m2mContext *s = ctx_to_m2mctx(ctx);
 +    V4L2m2mContext * const s = ctx_to_m2mctx(ctx);
@@ -47897,17 +52154,19 @@
 -        av_log(logger(ctx), AV_LOG_ERROR, "type %i not supported\n", ctx->type);
 -        return AVERROR_PATCHWELCOME;
 -    }
--
++    int ret;
++    int i;
+ 
 -    ret = ioctl(s->fd, VIDIOC_G_FMT, &ctx->format);
 -    if (ret)
 -        av_log(logger(ctx), AV_LOG_ERROR, "%s VIDIOC_G_FMT failed\n", ctx->name);
-+    int ret;
-+    int i;
-
++    av_assert0(ctx->bufrefs == NULL);
+ 
      memset(&req, 0, sizeof(req));
 -    req.count = ctx->num_buffers;
+-    req.memory = V4L2_MEMORY_MMAP;
 +    req.count = req_buffers;
-     req.memory = V4L2_MEMORY_MMAP;
++    req.memory = mem;
      req.type = ctx->type;
 -    ret = ioctl(s->fd, VIDIOC_REQBUFS, &req);
 -    if (ret < 0) {
@@ -47920,7 +52179,7 @@
 +            return ret;
 +        }
      }
-
+ 
      ctx->num_buffers = req.count;
 -    ctx->buffers = av_mallocz(ctx->num_buffers * sizeof(V4L2Buffer));
 -    if (!ctx->buffers) {
@@ -47930,7 +52189,7 @@
 -        return AVERROR(ENOMEM);
 +        goto fail_release;
      }
-
+ 
 -    for (i = 0; i < req.count; i++) {
 -        ctx->buffers[i].context = ctx;
 -        ret = ff_v4l2_buffer_initialize(&ctx->buffers[i], i);
@@ -47942,14 +52201,14 @@
 +    }
 +
 +    for (i = 0; i < ctx->num_buffers; i++) {
-+        ret = ff_v4l2_buffer_initialize(&ctx->bufrefs[i], i, ctx);
++        ret = ff_v4l2_buffer_initialize(&ctx->bufrefs[i], i, ctx, mem);
 +        if (ret) {
              av_log(logger(ctx), AV_LOG_ERROR, "%s buffer[%d] initialization (%s)\n", ctx->name, i, av_err2str(ret));
 -            goto error;
 +            goto fail_release;
          }
      }
-
+ 
      av_log(logger(ctx), AV_LOG_DEBUG, "%s: %s %02d buffers initialized: %04ux%04u, sizeimage %08u, bytesperline %08u\n", ctx->name,
          V4L2_TYPE_IS_MULTIPLANAR(ctx->type) ? av_fourcc2str(ctx->format.fmt.pix_mp.pixelformat) : av_fourcc2str(ctx->format.fmt.pix.pixelformat),
          req.count,
@@ -47959,9 +52218,9 @@
 +        ff_v4l2_get_format_height(&ctx->format),
          V4L2_TYPE_IS_MULTIPLANAR(ctx->type) ? ctx->format.fmt.pix_mp.plane_fmt[0].sizeimage : ctx->format.fmt.pix.sizeimage,
          V4L2_TYPE_IS_MULTIPLANAR(ctx->type) ? ctx->format.fmt.pix_mp.plane_fmt[0].bytesperline : ctx->format.fmt.pix.bytesperline);
-
+ 
      return 0;
-
+ 
 -error:
 +fail_release:
      v4l2_release_buffers(ctx);
@@ -47976,14 +52235,16 @@
 +
 +    // It is not valid to reinit a context without a previous release
 +    av_assert0(ctx->bufrefs == NULL);
-+
+ 
+-    av_freep(&ctx->buffers);
 +    if (!v4l2_type_supported(ctx)) {
 +        av_log(logger(ctx), AV_LOG_ERROR, "type %i not supported\n", ctx->type);
 +        return AVERROR_PATCHWELCOME;
 +    }
-
--    av_freep(&ctx->buffers);
++
 +    ff_mutex_init(&ctx->lock, NULL);
++    pthread_cond_init(&ctx->cond, NULL);
++    atomic_init(&ctx->q_count, 0);
 +
 +    if (s->output_drm) {
 +        AVHWFramesContext *hwframes;
@@ -47997,8 +52258,8 @@
 +        hwframes = (AVHWFramesContext*)ctx->frames_ref->data;
 +        hwframes->format = AV_PIX_FMT_DRM_PRIME;
 +        hwframes->sw_format = ctx->av_pix_fmt;
-+        hwframes->width = ctx->width;
-+        hwframes->height = ctx->height;
++        hwframes->width = ctx->width != 0 ? ctx->width : s->avctx->width;
++        hwframes->height = ctx->height != 0 ? ctx->height : s->avctx->height;
 +        ret = av_hwframe_ctx_init(ctx->frames_ref);
 +        if (ret < 0)
 +            goto fail_unref_hwframes;
@@ -48011,12 +52272,12 @@
 +        goto fail_unref_hwframes;
 +    }
 +
-+    ret = create_buffers(ctx, ctx->num_buffers);
++    ret = create_buffers(ctx, ctx->num_buffers, ctx->buf_mem);
 +    if (ret < 0)
 +        goto fail_unref_hwframes;
 +
 +    return 0;
-
+ 
 +fail_unref_hwframes:
 +    av_buffer_unref(&ctx->frames_ref);
 +fail_unlock:
@@ -48031,14 +52292,14 @@
  #include "libavutil/buffer.h"
 +#include "libavutil/thread.h"
  #include "v4l2_buffers.h"
-
+ 
  typedef struct V4L2Context {
 @@ -70,11 +71,18 @@ typedef struct V4L2Context {
       */
      int width, height;
      AVRational sample_aspect_ratio;
 +    struct v4l2_rect selection;
-
+ 
      /**
 -     * Indexed array of V4L2Buffers
 +     * If the default size of buffer is less than this then try to
@@ -48051,50 +52312,98 @@
 +     * Indexed array of pointers to V4L2Buffers
 +     */
 +    AVBufferRef **bufrefs;
-
+ 
      /**
       * Readonly after init.
-@@ -92,6 +100,12 @@ typedef struct V4L2Context {
+@@ -82,16 +90,38 @@ typedef struct V4L2Context {
+     int num_buffers;
+ 
+     /**
++     * Buffer memory type V4L2_MEMORY_MMAP or V4L2_MEMORY_DMABUF
++     */
++    enum v4l2_memory buf_mem;
++
++    /**
+      * Whether the stream has been started (VIDIOC_STREAMON has been sent).
+      */
+     int streamon;
+ 
++    /* 1st buffer after stream on */
++    int first_buf;
++
+     /**
+      *  Either no more buffers available or an unrecoverable error was notified
+      *  by the V4L2 kernel driver: once set the context has to be exited.
       */
      int done;
-
+ 
++    int flag_last;
++
++    /**
++     * If NZ then when Qing frame/pkt use this rather than the
++     * "real" PTS
++     */
++    uint64_t track_ts;
++
 +    AVBufferRef *frames_ref;
-+    int q_count;
-+    int dq_count;
++    atomic_int q_count;
 +    struct ff_weak_link_master *wl_master;
 +
 +    AVMutex lock;
++    pthread_cond_t cond;
  } V4L2Context;
-
+ 
  /**
-@@ -156,9 +170,12 @@ int ff_v4l2_context_dequeue_packet(V4L2C
+@@ -156,7 +186,10 @@ int ff_v4l2_context_dequeue_packet(V4L2C
   * @param[in] ctx The V4L2Context to dequeue from.
   * @param[inout] f The AVFrame to dequeue to.
   * @param[in] timeout The timeout for dequeue (-1 to block, 0 to return immediately, or milliseconds)
-+ * @param[in] no_rescale_pts (0 rescale pts, 1 use pts as
-+ *       timestamp directly)
 + *
   * @return 0 in case of success, AVERROR(EAGAIN) if no buffer was ready, another negative error in case of error.
++ *                AVERROR(ENOSPC) if no buffer availible to put
++ *                the frame in
   */
--int ff_v4l2_context_dequeue_frame(V4L2Context* ctx, AVFrame* f, int timeout);
-+int ff_v4l2_context_dequeue_frame(V4L2Context* ctx, AVFrame* f, int timeout, int no_rescale_pts);
-
- /**
-  * Enqueues a buffer to a V4L2Context from an AVPacket
-@@ -170,7 +187,7 @@ int ff_v4l2_context_dequeue_frame(V4L2Co
+ int ff_v4l2_context_dequeue_frame(V4L2Context* ctx, AVFrame* f, int timeout);
+ 
+@@ -170,7 +203,7 @@ int ff_v4l2_context_dequeue_frame(V4L2Co
   * @param[in] pkt A pointer to an AVPacket.
   * @return 0 in case of success, a negative error otherwise.
   */
 -int ff_v4l2_context_enqueue_packet(V4L2Context* ctx, const AVPacket* pkt);
-+int ff_v4l2_context_enqueue_packet(V4L2Context* ctx, const AVPacket* pkt, const void * ext_data, size_t ext_size, int no_rescale_pts);
-
++int ff_v4l2_context_enqueue_packet(V4L2Context* ctx, const AVPacket* pkt, const void * ext_data, size_t ext_size);
+ 
  /**
   * Enqueues a buffer to a V4L2Context from an AVFrame
 --- a/libavcodec/v4l2_m2m.c
 +++ b/libavcodec/v4l2_m2m.c
-@@ -215,13 +215,7 @@ int ff_v4l2_m2m_codec_reinit(V4L2m2mCont
+@@ -36,6 +36,14 @@
+ #include "v4l2_fmt.h"
+ #include "v4l2_m2m.h"
+ 
++static void
++xlat_init(xlat_track_t * const x)
++{
++    memset(x, 0, sizeof(*x));
++    x->last_pts = AV_NOPTS_VALUE;
++}
++
++
+ static inline int v4l2_splane_video(struct v4l2_capability *cap)
+ {
+     if (cap->capabilities & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT) &&
+@@ -68,7 +76,9 @@ static int v4l2_prepare_contexts(V4L2m2m
+ 
+     s->capture.done = s->output.done = 0;
+     s->capture.name = "capture";
++    s->capture.buf_mem = V4L2_MEMORY_MMAP;
+     s->output.name = "output";
++    s->output.buf_mem = s->input_drm ? V4L2_MEMORY_DMABUF : V4L2_MEMORY_MMAP;
+     atomic_init(&s->refcount, 0);
+     sem_init(&s->refsync, 0, 0);
+ 
+@@ -215,13 +225,7 @@ int ff_v4l2_m2m_codec_reinit(V4L2m2mCont
          av_log(log_ctx, AV_LOG_ERROR, "capture VIDIOC_STREAMOFF\n");
-
+ 
      /* 2. unmap the capture buffers (v4l2 and ffmpeg):
 -     *    we must wait for all references to be released before being allowed
 -     *    to queue new buffers.
@@ -48104,30 +52413,49 @@
 -        while(sem_wait(&s->refsync) == -1 && errno == EINTR);
 -
      ff_v4l2_context_release(&s->capture);
-
+ 
      /* 3. get the new capture format */
-@@ -328,7 +322,10 @@ static void v4l2_m2m_destroy_context(voi
+@@ -240,7 +244,6 @@ int ff_v4l2_m2m_codec_reinit(V4L2m2mCont
+ 
+     /* 5. complete reinit */
+     s->draining = 0;
+-    s->reinit = 0;
+ 
+     return 0;
+ }
+@@ -274,7 +277,6 @@ int ff_v4l2_m2m_codec_full_reinit(V4L2m2
+ 
+     /* start again now that we know the stream dimensions */
+     s->draining = 0;
+-    s->reinit = 0;
+ 
+     ret = ff_v4l2_context_get_format(&s->output, 0);
+     if (ret) {
+@@ -328,7 +330,13 @@ static void v4l2_m2m_destroy_context(voi
      ff_v4l2_context_release(&s->capture);
      sem_destroy(&s->refsync);
-
+ 
 -    close(s->fd);
 +    if (s->fd != -1)
 +        close(s->fd);
 +
++    av_packet_unref(&s->buf_pkt);
++    av_freep(&s->extdata_data);
++
 +    av_log(s->avctx, AV_LOG_DEBUG, "V4L2 Context destroyed\n");
-
+ 
      av_free(s);
  }
-@@ -338,17 +335,34 @@ int ff_v4l2_m2m_codec_end(V4L2m2mPriv *p
+@@ -338,17 +346,34 @@ int ff_v4l2_m2m_codec_end(V4L2m2mPriv *p
      V4L2m2mContext *s = priv->context;
      int ret;
-
+ 
 -    ret = ff_v4l2_context_set_status(&s->output, VIDIOC_STREAMOFF);
 -    if (ret)
 -        av_log(s->avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->output.name);
 +    if (!s)
 +        return 0;
-
+ 
 -    ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
 -    if (ret)
 -        av_log(s->avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->capture.name);
@@ -48145,9 +52473,9 @@
 +        if (ret)
 +            av_log(s->avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->capture.name);
 +    }
-
+ 
      ff_v4l2_context_release(&s->output);
-
+ 
 +    close(s->fd);
 +    s->fd = -1;
 +
@@ -48157,20 +52485,65 @@
 +    s->avctx = NULL;
 +    priv->context = NULL;
      av_buffer_unref(&priv->context_ref);
-
+ 
      return 0;
+@@ -392,28 +417,33 @@ int ff_v4l2_m2m_codec_init(V4L2m2mPriv *
+     return v4l2_configure_contexts(s);
+ }
+ 
+-int ff_v4l2_m2m_create_context(V4L2m2mPriv *priv, V4L2m2mContext **s)
++int ff_v4l2_m2m_create_context(V4L2m2mPriv *priv, V4L2m2mContext **pps)
+ {
+-    *s = av_mallocz(sizeof(V4L2m2mContext));
+-    if (!*s)
++    V4L2m2mContext * const s = av_mallocz(sizeof(V4L2m2mContext));
++
++    *pps = NULL;
++    if (!s)
+         return AVERROR(ENOMEM);
+ 
+-    priv->context_ref = av_buffer_create((uint8_t *) *s, sizeof(V4L2m2mContext),
++    priv->context_ref = av_buffer_create((uint8_t *)s, sizeof(*s),
+                                          &v4l2_m2m_destroy_context, NULL, 0);
+     if (!priv->context_ref) {
+-        av_freep(s);
++        av_free(s);
+         return AVERROR(ENOMEM);
+     }
+ 
+     /* assign the context */
+-    priv->context = *s;
+-    (*s)->priv = priv;
++    priv->context = s;
++    s->priv = priv;
+ 
+     /* populate it */
+-    priv->context->capture.num_buffers = priv->num_capture_buffers;
+-    priv->context->output.num_buffers  = priv->num_output_buffers;
+-    priv->context->self_ref = priv->context_ref;
+-    priv->context->fd = -1;
++    s->capture.num_buffers = priv->num_capture_buffers;
++    s->output.num_buffers  = priv->num_output_buffers;
++    s->self_ref = priv->context_ref;
++    s->fd = -1;
++
++    xlat_init(&s->xlat);
+ 
++    *pps = s;
+     return 0;
+ }
 --- a/libavcodec/v4l2_m2m.h
 +++ b/libavcodec/v4l2_m2m.h
 @@ -30,6 +30,7 @@
  #include <linux/videodev2.h>
-
+ 
  #include "libavcodec/avcodec.h"
 +#include "libavutil/pixfmt.h"
  #include "v4l2_context.h"
-
+ 
  #define container_of(ptr, type, member) ({ \
-@@ -38,7 +39,18 @@
-
+@@ -38,7 +39,37 @@
+ 
  #define V4L_M2M_DEFAULT_OPTS \
      { "num_output_buffers", "Number of buffers in the output context",\
 -        OFFSET(num_output_buffers), AV_OPT_TYPE_INT, { .i64 = 16 }, 6, INT_MAX, FLAGS }
@@ -48179,26 +52552,45 @@
 +#define FF_V4L2_M2M_TRACK_SIZE 128
 +typedef struct V4L2m2mTrackEl {
 +    int     discard;   // If we see this buffer its been flushed, so discard
++    int     pending;
 +    int     pkt_size;
 +    int64_t pts;
++    int64_t dts;
 +    int64_t reordered_opaque;
 +    int64_t pkt_pos;
 +    int64_t pkt_duration;
 +    int64_t track_pts;
 +} V4L2m2mTrackEl;
-
++
++typedef struct pts_stats_s
++{
++    void * logctx;
++    const char * name;  // For debug
++    unsigned int last_count;
++    unsigned int last_interval;
++    int64_t last_pts;
++    int64_t guess;
++} pts_stats_t;
++
++typedef struct xlat_track_s {
++    unsigned int track_no;
++    int64_t last_pts;
++    int64_t last_opaque;
++    V4L2m2mTrackEl track_els[FF_V4L2_M2M_TRACK_SIZE];
++} xlat_track_t;
+ 
  typedef struct V4L2m2mContext {
      char devname[PATH_MAX];
-@@ -53,6 +65,7 @@ typedef struct V4L2m2mContext {
+@@ -52,7 +83,6 @@ typedef struct V4L2m2mContext {
+     AVCodecContext *avctx;
      sem_t refsync;
      atomic_uint refcount;
-     int reinit;
-+    int resize_pending;
-
+-    int reinit;
+ 
      /* null frame/packet received */
      int draining;
-@@ -63,6 +76,23 @@ typedef struct V4L2m2mContext {
-
+@@ -63,6 +93,36 @@ typedef struct V4L2m2mContext {
+ 
      /* reference back to V4L2m2mPriv */
      void *priv;
 +
@@ -48207,49 +52599,72 @@
 +    /* generate DRM frames */
 +    int output_drm;
 +
++    /* input frames are drmprime */
++    int input_drm;
++
 +    /* Frame tracking */
-+    int64_t last_pkt_dts;
-+    int64_t last_opaque;
-+    unsigned int track_no;
-+    V4L2m2mTrackEl track_els[FF_V4L2_M2M_TRACK_SIZE];
++    xlat_track_t xlat;
++    int pending_hw;
++    int pending_n;
++
++    pts_stats_t pts_stat;
 +
 +    /* req pkt */
 +    int req_pkt;
 +
 +    /* Ext data sent */
 +    int extdata_sent;
++    /* Ext data sent in packet - overrides ctx */
++    uint8_t * extdata_data;
++    size_t extdata_size;
++
++#define FF_V4L2_QUIRK_REINIT_ALWAYS             1
++#define FF_V4L2_QUIRK_ENUM_FRAMESIZES_BROKEN    2
++    /* Quirks */
++    unsigned int quirks;
++
  } V4L2m2mContext;
-
+ 
  typedef struct V4L2m2mPriv {
-@@ -73,6 +103,7 @@ typedef struct V4L2m2mPriv {
-
+@@ -73,6 +133,7 @@ typedef struct V4L2m2mPriv {
+ 
      int num_output_buffers;
      int num_capture_buffers;
 +    enum AVPixelFormat pix_fmt;
  } V4L2m2mPriv;
-
+ 
  /**
-@@ -126,4 +157,16 @@ int ff_v4l2_m2m_codec_reinit(V4L2m2mCont
+@@ -126,4 +187,26 @@ int ff_v4l2_m2m_codec_reinit(V4L2m2mCont
   */
  int ff_v4l2_m2m_codec_full_reinit(V4L2m2mContext *ctx);
-
+ 
 +
-+static inline unsigned int ff_v4l2_get_format_width(struct v4l2_format *fmt)
++static inline unsigned int ff_v4l2_get_format_width(const struct v4l2_format * const fmt)
 +{
 +    return V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? fmt->fmt.pix_mp.width : fmt->fmt.pix.width;
 +}
 +
-+static inline unsigned int ff_v4l2_get_format_height(struct v4l2_format *fmt)
++static inline unsigned int ff_v4l2_get_format_height(const struct v4l2_format * const fmt)
 +{
 +    return V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? fmt->fmt.pix_mp.height : fmt->fmt.pix.height;
 +}
 +
++static inline uint32_t ff_v4l2_get_format_pixelformat(const struct v4l2_format * const fmt)
++{
++    return V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? fmt->fmt.pix_mp.pixelformat : fmt->fmt.pix.pixelformat;
++}
++
++static inline int ff_v4l2_ctx_eos(const V4L2Context * const ctx)
++{
++    return ctx->flag_last;
++}
++
 +
  #endif /* AVCODEC_V4L2_M2M_H */
 --- a/libavcodec/v4l2_m2m_dec.c
 +++ b/libavcodec/v4l2_m2m_dec.c
 @@ -23,6 +23,10 @@
-
+ 
  #include <linux/videodev2.h>
  #include <sys/ioctl.h>
 +
@@ -48259,10 +52674,10 @@
  #include "libavutil/pixfmt.h"
  #include "libavutil/pixdesc.h"
  #include "libavutil/opt.h"
-@@ -30,26 +34,51 @@
+@@ -30,75 +34,111 @@
  #include "libavcodec/decode.h"
  #include "libavcodec/internal.h"
-
+ 
 +#include "libavcodec/hwaccels.h"
 +#include "libavcodec/internal.h"
 +#include "libavcodec/hwconfig.h"
@@ -48270,7 +52685,80 @@
  #include "v4l2_context.h"
  #include "v4l2_m2m.h"
  #include "v4l2_fmt.h"
-
+ 
+-static int v4l2_try_start(AVCodecContext *avctx)
++// Pick 64 for max last count - that is >1sec at 60fps
++#define STATS_LAST_COUNT_MAX 64
++#define STATS_INTERVAL_MAX (1 << 30)
++
++#ifndef FF_API_BUFFER_SIZE_T
++#define FF_API_BUFFER_SIZE_T 1
++#endif
++
++static int64_t pts_stats_guess(const pts_stats_t * const stats)
+ {
+-    V4L2m2mContext *s = ((V4L2m2mPriv*)avctx->priv_data)->context;
+-    V4L2Context *const capture = &s->capture;
+-    V4L2Context *const output = &s->output;
+-    struct v4l2_selection selection = { 0 };
+-    int ret;
++    if (stats->last_pts == AV_NOPTS_VALUE ||
++            stats->last_interval == 0 ||
++            stats->last_count >= STATS_LAST_COUNT_MAX)
++        return AV_NOPTS_VALUE;
++    return stats->last_pts + (int64_t)(stats->last_count - 1) * (int64_t)stats->last_interval;
++}
+ 
+-    /* 1. start the output process */
+-    if (!output->streamon) {
+-        ret = ff_v4l2_context_set_status(output, VIDIOC_STREAMON);
+-        if (ret < 0) {
+-            av_log(avctx, AV_LOG_DEBUG, "VIDIOC_STREAMON on output context\n");
+-            return ret;
++static void pts_stats_add(pts_stats_t * const stats, int64_t pts)
++{
++    if (pts == AV_NOPTS_VALUE || pts == stats->last_pts) {
++        if (stats->last_count < STATS_LAST_COUNT_MAX)
++            ++stats->last_count;
++        return;
++    }
++
++    if (stats->last_pts != AV_NOPTS_VALUE) {
++        const int64_t interval = pts - stats->last_pts;
++
++        if (interval < 0 || interval >= STATS_INTERVAL_MAX ||
++            stats->last_count >= STATS_LAST_COUNT_MAX) {
++            if (stats->last_interval != 0)
++                av_log(stats->logctx, AV_LOG_DEBUG, "%s: %s: Bad interval: %" PRId64 "/%d\n",
++                       __func__, stats->name, interval, stats->last_count);
++            stats->last_interval = 0;
++        }
++        else {
++            const int64_t frame_time = interval / (int64_t)stats->last_count;
++
++            if (frame_time != stats->last_interval)
++                av_log(stats->logctx, AV_LOG_DEBUG, "%s: %s: New interval: %u->%" PRId64 "/%d=%" PRId64 "\n",
++                       __func__, stats->name, stats->last_interval, interval, stats->last_count, frame_time);
++            stats->last_interval = frame_time;
+         }
+     }
+ 
+-    if (capture->streamon)
++    stats->last_pts = pts;
++    stats->last_count = 1;
++}
++
++static void pts_stats_init(pts_stats_t * const stats, void * logctx, const char * name)
++{
++    *stats = (pts_stats_t){
++        .logctx = logctx,
++        .name = name,
++        .last_count = 1,
++        .last_interval = 0,
++        .last_pts = AV_NOPTS_VALUE
++    };
++}
++
 +static int check_output_streamon(AVCodecContext *const avctx, V4L2m2mContext *const s)
 +{
 +    int ret;
@@ -48280,81 +52768,43 @@
 +    };
 +
 +    if (s->output.streamon)
-+        return 0;
-+
-+    ret = ff_v4l2_context_set_status(&s->output, VIDIOC_STREAMON);
-+    if (ret < 0)
-+        av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMON on output context\n");
-+
-+    if (!s->capture.streamon || ret < 0)
-+        return ret;
-+
-+    ret = ioctl(s->fd, VIDIOC_DECODER_CMD, &cmd);
-+    if (ret < 0)
-+        av_log(avctx, AV_LOG_ERROR, "VIDIOC_DECODER_CMD start error: %d\n", errno);
-+    else
-+        av_log(avctx, AV_LOG_DEBUG, "VIDIOC_DECODER_CMD start OK\n");
-+
-+    return ret;
-+}
-+
- static int v4l2_try_start(AVCodecContext *avctx)
- {
-     V4L2m2mContext *s = ((V4L2m2mPriv*)avctx->priv_data)->context;
-     V4L2Context *const capture = &s->capture;
--    V4L2Context *const output = &s->output;
-     struct v4l2_selection selection = { 0 };
-     int ret;
-
-     /* 1. start the output process */
--    if (!output->streamon) {
--        ret = ff_v4l2_context_set_status(output, VIDIOC_STREAMON);
--        if (ret < 0) {
--            av_log(avctx, AV_LOG_DEBUG, "VIDIOC_STREAMON on output context\n");
--            return ret;
--        }
--    }
-+    if ((ret = check_output_streamon(avctx, s)) != 0)
-+        return ret;
-
-     if (capture->streamon)
          return 0;
-@@ -63,15 +92,29 @@ static int v4l2_try_start(AVCodecContext
+ 
+-    /* 2. get the capture format */
+-    capture->format.type = capture->type;
+-    ret = ioctl(s->fd, VIDIOC_G_FMT, &capture->format);
+-    if (ret) {
+-        av_log(avctx, AV_LOG_WARNING, "VIDIOC_G_FMT ioctl\n");
++    ret = ff_v4l2_context_set_status(&s->output, VIDIOC_STREAMON);
++    if (ret != 0) {
++        av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMON on output context: %s\n", av_err2str(ret));
+         return ret;
      }
-
-     /* 2.1 update the AVCodecContext */
+ 
+-    /* 2.1 update the AVCodecContext */
 -    avctx->pix_fmt = ff_v4l2_format_v4l2_to_avfmt(capture->format.fmt.pix_mp.pixelformat, AV_CODEC_ID_RAWVIDEO);
 -    capture->av_pix_fmt = avctx->pix_fmt;
-+    capture->av_pix_fmt =
-+        ff_v4l2_format_v4l2_to_avfmt(capture->format.fmt.pix_mp.pixelformat, AV_CODEC_ID_RAWVIDEO);
-+    if (s->output_drm) {
-+        avctx->pix_fmt = AV_PIX_FMT_DRM_PRIME;
-+        avctx->sw_pix_fmt = capture->av_pix_fmt;
-+    }
-+    else
-+        avctx->pix_fmt = capture->av_pix_fmt;
-
-     /* 3. set the crop parameters */
-+#if 1
-+    selection.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-+    selection.target = V4L2_SEL_TGT_CROP_DEFAULT;
-+    ret = ioctl(s->fd, VIDIOC_G_SELECTION, &selection);
-+    av_log(avctx, AV_LOG_INFO, "Post G selection ret=%d, err=%d %dx%d\n", ret, errno, selection.r.width, selection.r.height);
-+#else
-     selection.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-     selection.r.height = avctx->coded_height;
-     selection.r.width = avctx->coded_width;
-+    av_log(avctx, AV_LOG_INFO, "Try selection %dx%d\n", avctx->coded_width, avctx->coded_height);
-     ret = ioctl(s->fd, VIDIOC_S_SELECTION, &selection);
+-
+-    /* 3. set the crop parameters */
+-    selection.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+-    selection.r.height = avctx->coded_height;
+-    selection.r.width = avctx->coded_width;
+-    ret = ioctl(s->fd, VIDIOC_S_SELECTION, &selection);
 -    if (!ret) {
-+    av_log(avctx, AV_LOG_INFO, "Post S selection ret=%d, err=%d %dx%d\n", ret, errno, selection.r.width, selection.r.height);
-+    if (1) {
-         ret = ioctl(s->fd, VIDIOC_G_SELECTION, &selection);
-         if (ret) {
-             av_log(avctx, AV_LOG_WARNING, "VIDIOC_G_SELECTION ioctl\n");
-@@ -82,15 +125,7 @@ static int v4l2_try_start(AVCodecContext
-             capture->width  = selection.r.width;
-         }
+-        ret = ioctl(s->fd, VIDIOC_G_SELECTION, &selection);
+-        if (ret) {
+-            av_log(avctx, AV_LOG_WARNING, "VIDIOC_G_SELECTION ioctl\n");
+-        } else {
+-            av_log(avctx, AV_LOG_DEBUG, "crop output %dx%d\n", selection.r.width, selection.r.height);
+-            /* update the size of the resulting frame */
+-            capture->height = selection.r.height;
+-            capture->width  = selection.r.width;
+-        }
++    // STREAMON should do implicit START so this just for those that don't.
++    // It is optional so don't worry if it fails
++    if (ioctl(s->fd, VIDIOC_DECODER_CMD, &cmd) < 0) {
++        ret = AVERROR(errno);
++        av_log(avctx, AV_LOG_WARNING, "VIDIOC_DECODER_CMD start error: %s\n", av_err2str(ret));
      }
 -
 -    /* 4. init the capture context now that we have the capture format */
@@ -48364,131 +52814,133 @@
 -            av_log(avctx, AV_LOG_ERROR, "can't request capture buffers\n");
 -            return AVERROR(ENOMEM);
 -        }
++    else {
++        av_log(avctx, AV_LOG_TRACE, "VIDIOC_DECODER_CMD start OK\n");
+     }
++    return 0;
++}
+ 
+-    /* 5. start the capture process */
+-    ret = ff_v4l2_context_set_status(capture, VIDIOC_STREAMON);
+-    if (ret) {
+-        av_log(avctx, AV_LOG_DEBUG, "VIDIOC_STREAMON, on capture context\n");
+-        return ret;
 -    }
-+#endif
-
-     /* 5. start the capture process */
-     ret = ff_v4l2_context_set_status(capture, VIDIOC_STREAMON);
-@@ -133,52 +168,312 @@ static int v4l2_prepare_decoder(V4L2m2mC
++static int v4l2_try_start(AVCodecContext *avctx)
++{
++    V4L2m2mContext * const s = ((V4L2m2mPriv*)avctx->priv_data)->context;
++    int ret;
+ 
++    /* 1. start the output process */
++    if ((ret = check_output_streamon(avctx, s)) != 0)
++        return ret;
      return 0;
  }
-
+ 
+@@ -133,52 +173,525 @@ static int v4l2_prepare_decoder(V4L2m2mC
+     return 0;
+ }
+ 
 -static int v4l2_receive_frame(AVCodecContext *avctx, AVFrame *frame)
-+static inline int64_t track_to_pts(AVCodecContext *avctx, unsigned int n)
-+{
-+    return (int64_t)n;
-+}
-+
-+static inline unsigned int pts_to_track(AVCodecContext *avctx, const int64_t pts)
-+{
-+    return (unsigned int)pts;
-+}
-+
-+// FFmpeg requires us to propagate a number of vars from the coded pkt into
-+// the decoded frame. The only thing that tracks like that in V4L2 stateful
-+// is timestamp. PTS maps to timestamp for this decode. FFmpeg makes no
-+// guarantees about PTS being unique or specified for every frame so replace
-+// the supplied PTS with a simple incrementing number and keep a circular
-+// buffer of all the things we want preserved (including the original PTS)
-+// indexed by the tracking no.
 +static void
-+xlat_pts_in(AVCodecContext *const avctx, V4L2m2mContext *const s, AVPacket *const avpkt)
++set_best_effort_pts(AVCodecContext *const avctx,
++             pts_stats_t * const ps,
++             AVFrame *const frame)
 +{
-+    int64_t track_pts;
-+
-+    // Avoid 0
-+    if (++s->track_no == 0)
-+        s->track_no = 1;
-+
-+    track_pts = track_to_pts(avctx, s->track_no);
-+
-+    av_log(avctx, AV_LOG_TRACE, "In PTS=%" PRId64 ", DTS=%" PRId64 ", track=%" PRId64 ", n=%u\n", avpkt->pts, avpkt->dts, track_pts, s->track_no);
-+    s->last_pkt_dts = avpkt->dts;
-+    s->track_els[s->track_no  % FF_V4L2_M2M_TRACK_SIZE] = (V4L2m2mTrackEl){
-+        .discard          = 0,
-+        .pkt_size         = avpkt->size,
-+        .pts              = avpkt->pts,
-+        .reordered_opaque = avctx->reordered_opaque,
-+        .pkt_pos          = avpkt->pos,
-+        .pkt_duration     = avpkt->duration,
-+        .track_pts        = track_pts
-+    };
-+    avpkt->pts = track_pts;
-+}
-+
-+// Returns -1 if we should discard the frame
-+static int
-+xlat_pts_out(AVCodecContext *const avctx, V4L2m2mContext *const s, AVFrame *const frame)
-+{
-+    unsigned int n = pts_to_track(avctx, frame->pts) % FF_V4L2_M2M_TRACK_SIZE;
-+    const V4L2m2mTrackEl *const t = s->track_els + n;
-+    if (frame->pts == AV_NOPTS_VALUE || frame->pts != t->track_pts)
-+    {
-+        av_log(avctx, AV_LOG_INFO, "Tracking failure: pts=%" PRId64 ", track[%d]=%" PRId64 "\n", frame->pts, n, t->track_pts);
-+        frame->pts              = AV_NOPTS_VALUE;
-+        frame->pkt_dts          = s->last_pkt_dts;
-+        frame->reordered_opaque = s->last_opaque;
-+        frame->pkt_pos          = -1;
-+        frame->pkt_duration     = 0;
-+        frame->pkt_size         = -1;
-+    }
-+    else if (!t->discard)
-+    {
-+        frame->pts              = t->pts;
-+        frame->pkt_dts          = s->last_pkt_dts;
-+        frame->reordered_opaque = t->reordered_opaque;
-+        frame->pkt_pos          = t->pkt_pos;
-+        frame->pkt_duration     = t->pkt_duration;
-+        frame->pkt_size         = t->pkt_size;
-+
-+        s->last_opaque = s->track_els[n].reordered_opaque;
-+        s->track_els[n].pts = AV_NOPTS_VALUE;  // If we hit this again deny accurate knowledge of PTS
-+    }
-+    else
-+    {
-+        av_log(avctx, AV_LOG_DEBUG, "Discard frame (flushed): pts=%" PRId64 ", track[%d]=%" PRId64 "\n", frame->pts, n, t->track_pts);
-+        return -1;
-+    }
++    pts_stats_add(ps, frame->pts);
 +
 +#if FF_API_PKT_PTS
 +FF_DISABLE_DEPRECATION_WARNINGS
 +    frame->pkt_pts = frame->pts;
 +FF_ENABLE_DEPRECATION_WARNINGS
 +#endif
-+    frame->best_effort_timestamp = frame->pts;
-+    frame->pkt_dts               = frame->pts;  // We can't emulate what s/w does in a useful manner?
-+    av_log(avctx, AV_LOG_TRACE, "Out PTS=%" PRId64 ", DTS=%" PRId64 "\n", frame->pts, frame->pkt_dts);
-+    return 0;
++    frame->best_effort_timestamp = pts_stats_guess(ps);
++    // If we can't guess from just PTS - try DTS
++    if (frame->best_effort_timestamp == AV_NOPTS_VALUE)
++        frame->best_effort_timestamp = frame->pkt_dts;
++
++    // We can't emulate what s/w does in a useful manner and using the
++    // "correct" answer seems to just confuse things.
++    frame->pkt_dts               = frame->pts;
++    av_log(avctx, AV_LOG_TRACE, "Out PTS=%" PRId64 "/%"PRId64", DTS=%" PRId64 "\n",
++           frame->pts, frame->best_effort_timestamp, frame->pkt_dts);
++}
++
++static void
++xlat_flush(xlat_track_t * const x)
++{
++    unsigned int i;
++    for (i = 0; i != FF_V4L2_M2M_TRACK_SIZE; ++i) {
++        x->track_els[i].pending = 0;
++        x->track_els[i].discard = 1;
++    }
++    x->last_pts = AV_NOPTS_VALUE;
++}
++
++static int
++xlat_pending(const xlat_track_t * const x)
++{
++    unsigned int n = x->track_no % FF_V4L2_M2M_TRACK_SIZE;
++    unsigned int i;
++    int r = 0;
++    int64_t now = AV_NOPTS_VALUE;
++
++    for (i = 0; i < 32; ++i, n = (n - 1) % FF_V4L2_M2M_TRACK_SIZE) {
++        const V4L2m2mTrackEl * const t = x->track_els + n;
++
++        if (!t->pending)
++            continue;
++
++        if (now == AV_NOPTS_VALUE)
++            now = t->dts;
++
++        if (t->pts == AV_NOPTS_VALUE ||
++            ((now == AV_NOPTS_VALUE || t->pts <= now) &&
++             (x->last_pts == AV_NOPTS_VALUE || t->pts > x->last_pts)))
++            ++r;
++    }
++
++    // If we never get any ideas about PTS vs DTS allow a lot more buffer
++    if (now == AV_NOPTS_VALUE)
++        r -= 16;
++
++    return r;
 +}
 +
 +static inline int stream_started(const V4L2m2mContext * const s) {
-+    return s->capture.streamon && s->output.streamon;
++    return s->output.streamon;
 +}
 +
 +#define NQ_OK        0
 +#define NQ_Q_FULL    1
 +#define NQ_SRC_EMPTY 2
-+#define NQ_DRAINING  3
-+#define NQ_DEAD      4
++#define NQ_NONE      3
++#define NQ_DRAINING  4
++#define NQ_DEAD      5
 +
 +#define TRY_DQ(nq_status) ((nq_status) >= NQ_OK && (nq_status) <= NQ_DRAINING)
++#define RETRY_NQ(nq_status) ((nq_status) == NQ_Q_FULL || (nq_status) == NQ_NONE)
++
++// do_not_get      If true then no new packet will be got but status will
++//                  be set appropriately
 +
 +// AVERROR_EOF     Flushing an already flushed stream
 +// -ve             Error (all errors except EOF are unexpected)
 +// NQ_OK (0)       OK
 +// NQ_Q_FULL       Dst full (retry if we think V4L2 Q has space now)
 +// NQ_SRC_EMPTY    Src empty (do not retry)
++// NQ_NONE         Enqueue not attempted
 +// NQ_DRAINING     At EOS, dQ dest until EOS there too
 +// NQ_DEAD         Not running (do not retry, do not attempt capture dQ)
 +
-+static int try_enqueue_src(AVCodecContext * const avctx, V4L2m2mContext * const s)
++static int try_enqueue_src(AVCodecContext * const avctx, V4L2m2mContext * const s, const int do_not_get)
  {
 -    V4L2m2mContext *s = ((V4L2m2mPriv*)avctx->priv_data)->context;
 -    V4L2Context *const capture = &s->capture;
 -    V4L2Context *const output = &s->output;
 -    AVPacket avpkt = {0};
      int ret;
-
+ 
 -    if (s->buf_pkt.size) {
 -        avpkt = s->buf_pkt;
 -        memset(&s->buf_pkt, 0, sizeof(AVPacket));
@@ -48498,8 +52950,50 @@
 +    // If we don't already have a coded packet - get a new one
 +    // We will already have a coded pkt if the output Q was full last time we
 +    // tried to Q it
-+    if (!s->buf_pkt.size) {
-+        ret = ff_decode_get_packet(avctx, &s->buf_pkt);
++    if (!s->buf_pkt.size && !do_not_get) {
++        unsigned int i;
++
++        for (i = 0; i < 256; ++i) {
++            uint8_t * side_data;
++#if FF_API_BUFFER_SIZE_T
++            int side_size;
++#else
++            size_t side_size;
++#endif
++            ret = ff_decode_get_packet(avctx, &s->buf_pkt);
++            if (ret != 0)
++                break;
++
++            // New extradata is the only side-data we undertand
++            side_data = av_packet_get_side_data(&s->buf_pkt, AV_PKT_DATA_NEW_EXTRADATA, &side_size);
++            if (side_data) {
++                av_log(avctx, AV_LOG_DEBUG, "New extradata\n");
++                av_freep(&s->extdata_data);
++                if ((s->extdata_data = av_malloc(side_size ? side_size : 1)) == NULL) {
++                    av_log(avctx, AV_LOG_ERROR, "Failed to alloc %d bytes of extra data\n", (int)side_size);
++                    return AVERROR(ENOMEM);
++                }
++                memcpy(s->extdata_data, side_data, side_size);
++                s->extdata_size = side_size;
++                s->extdata_sent = 0;
++            }
++
++            if (s->buf_pkt.size != 0)
++                break;
++
++            if (s->buf_pkt.side_data_elems == 0) {
++                av_log(avctx, AV_LOG_WARNING, "Empty pkt from ff_decode_get_packet - treating as EOF\n");
++                ret = AVERROR_EOF;
++                break;
++            }
++
++            // Retry a side-data only pkt
++        }
++        // If i >= 256 something has gone wrong
++        if (i >= 256) {
++            av_log(avctx, AV_LOG_ERROR, "Too many side-data only packets\n");
++            return AVERROR(EIO);
++        }
 +
 +        if (ret == AVERROR(EAGAIN)) {
 +            if (!stream_started(s)) {
@@ -48523,7 +53017,7 @@
 +            if (!s->draining) {
 +                // Calling enqueue with an empty pkt starts drain
 +                av_assert0(s->buf_pkt.size == 0);
-+                ret = ff_v4l2_context_enqueue_packet(&s->output, &s->buf_pkt, NULL, 0, 1);
++                ret = ff_v4l2_context_enqueue_packet(&s->output, &s->buf_pkt, NULL, 0);
 +                if (ret) {
 +                    av_log(avctx, AV_LOG_ERROR, "Failed to start drain: ret=%d\n", ret);
 +                    return ret;
@@ -48536,22 +53030,37 @@
 +            av_log(avctx, AV_LOG_ERROR, "Failed to get coded packet: err=%d\n", ret);
              return ret;
 +        }
-+
-+        xlat_pts_in(avctx, s, &s->buf_pkt);
      }
-
+ 
 -    if (s->draining)
 -        goto dequeue;
-+    if ((ret = check_output_streamon(avctx, s)) != 0)
-+        return ret;
-
++    if (s->draining) {
++        if (s->buf_pkt.size) {
++            av_log(avctx, AV_LOG_WARNING, "Unexpected input whilst draining\n");
++            av_packet_unref(&s->buf_pkt);
++        }
++        return NQ_DRAINING;
++    }
+ 
 -    ret = ff_v4l2_context_enqueue_packet(output, &avpkt);
 -    if (ret < 0) {
 -        if (ret != AVERROR(EAGAIN))
 -           return ret;
-+    ret = ff_v4l2_context_enqueue_packet(&s->output, &s->buf_pkt,
-+                                         avctx->extradata, s->extdata_sent ? 0 : avctx->extradata_size,
-+                                         1);
++    if (!s->buf_pkt.size)
++        return NQ_NONE;
+ 
+-        s->buf_pkt = avpkt;
+-        /* no input buffers available, continue dequeing */
+-    }
++    if ((ret = check_output_streamon(avctx, s)) != 0)
++        return ret;
++
++    if (s->extdata_sent)
++        ret = ff_v4l2_context_enqueue_packet(&s->output, &s->buf_pkt, NULL, 0);
++    else if (s->extdata_data)
++        ret = ff_v4l2_context_enqueue_packet(&s->output, &s->buf_pkt, s->extdata_data, s->extdata_size);
++    else
++        ret = ff_v4l2_context_enqueue_packet(&s->output, &s->buf_pkt, avctx->extradata, avctx->extradata_size);
 +
 +    if (ret == AVERROR(EAGAIN)) {
 +        // Out of input buffers - keep packet
@@ -48561,19 +53070,19 @@
 +        // In all other cases we are done with this packet
 +        av_packet_unref(&s->buf_pkt);
 +        s->extdata_sent = 1;
-
--        s->buf_pkt = avpkt;
--        /* no input buffers available, continue dequeing */
-+        if (ret) {
+ 
+-    if (avpkt.size) {
+-        ret = v4l2_try_start(avctx);
+         if (ret) {
+-            av_packet_unref(&avpkt);
 +            av_log(avctx, AV_LOG_ERROR, "Packet enqueue failure: err=%d\n", ret);
 +            return ret;
 +        }
-     }
-
--    if (avpkt.size) {
--        ret = v4l2_try_start(avctx);
--        if (ret) {
--            av_packet_unref(&avpkt);
++    }
+ 
+-            /* cant recover */
+-            if (ret == AVERROR(ENOMEM))
+-                return ret;
 +    // Start if we haven't
 +    {
 +        const int ret2 = v4l2_try_start(avctx);
@@ -48582,62 +53091,139 @@
 +            ret = (ret2 == AVERROR(ENOMEM)) ? ret2 : NQ_DEAD;
 +        }
 +    }
-
--            /* cant recover */
--            if (ret == AVERROR(ENOMEM))
--                return ret;
++
 +    return ret;
 +}
-
++
++static int qbuf_wait(AVCodecContext * const avctx, V4L2Context * const ctx)
++{
++    int rv = 0;
+ 
 -            return 0;
++    ff_mutex_lock(&ctx->lock);
++
++    while (atomic_load(&ctx->q_count) == 0 && ctx->streamon) {
++        if (pthread_cond_wait(&ctx->cond, &ctx->lock) != 0) {
++            rv = AVERROR(errno);
++            av_log(avctx, AV_LOG_ERROR, "Cond wait failure: %s\n", av_err2str(rv));
++            break;
+         }
+     }
+ 
+-dequeue:
+-    if (!s->buf_pkt.size)
+-        av_packet_unref(&avpkt);
+-    return ff_v4l2_context_dequeue_frame(capture, frame, -1);
++    ff_mutex_unlock(&ctx->lock);
++    return rv;
++}
++
++// Number of frames over what xlat_pending returns that we keep *16
++// This is a min value - if it appears to be too small the threshold should
++// adjust dynamically.
++#define PENDING_HW_MIN      (3 * 16)
++// Offset to use when setting dynamically
++// Set to %16 == 15 to avoid the threshold changing immediately as we relax
++#define PENDING_HW_OFFSET   (PENDING_HW_MIN - 1)
++// Number of consecutive times we've failed to get a frame when we prefer it
++// before we increase the prefer threshold (5ms * N = max expected decode
++// time)
++#define PENDING_N_THRESHOLD 6
++
 +static int v4l2_receive_frame(AVCodecContext *avctx, AVFrame *frame)
 +{
 +    V4L2m2mContext *const s = ((V4L2m2mPriv*)avctx->priv_data)->context;
-+    int src_rv;
++    int src_rv = NQ_OK;
 +    int dst_rv = 1;  // Non-zero (done), non-negative (error) number
++    unsigned int i = 0;
 +
 +    do {
-+        src_rv = try_enqueue_src(avctx, s);
++        const int pending = xlat_pending(&s->xlat);
++        const int prefer_dq = (pending > s->pending_hw / 16);
++        const int last_src_rv = src_rv;
 +
-+        // If we got a frame last time and we have nothing to enqueue then
-+        // return now. rv will be AVERROR(EAGAIN) indicating that we want more input
++        // Enqueue another pkt for decode if
++        // (a) We don't have a lot of stuff in the buffer already OR
++        // (b) ... we (think we) do but we've failed to get a frame already OR
++        // (c) We've dequeued a lot of frames without asking for input
++        src_rv = try_enqueue_src(avctx, s, !(!prefer_dq || i != 0 || s->req_pkt > 2));
++
++        // If we got a frame last time or we've already tried to get a frame and
++        // we have nothing to enqueue then return now. rv will be AVERROR(EAGAIN)
++        // indicating that we want more input.
 +        // This should mean that once decode starts we enter a stable state where
 +        // we alternately ask for input and produce output
-+        if (s->req_pkt && src_rv == NQ_SRC_EMPTY)
++        if ((i != 0 || s->req_pkt) && src_rv == NQ_SRC_EMPTY)
 +            break;
 +
-+        if (src_rv == NQ_Q_FULL && dst_rv == AVERROR(EAGAIN)) {
-+            av_log(avctx, AV_LOG_WARNING, "Poll says src Q has space but enqueue fail");
-+            src_rv = NQ_SRC_EMPTY;  // If we can't enqueue pretend that there is nothing to enqueue
++        if (src_rv == NQ_Q_FULL && last_src_rv == NQ_Q_FULL) {
++            av_log(avctx, AV_LOG_WARNING, "Poll thinks src Q has space; none found\n");
++            break;
 +        }
 +
 +        // Try to get a new frame if
 +        // (a) we haven't already got one AND
 +        // (b) enqueue returned a status indicating that decode should be attempted
 +        if (dst_rv != 0 && TRY_DQ(src_rv)) {
-+            do {
-+                // Dequeue frame will unref any previous contents of frame
-+                // if it returns success so we don't need an explicit unref
-+                // when discarding
-+                // This returns AVERROR(EAGAIN) if there isn't a frame ready yet
-+                // but there is room in the input Q
-+                dst_rv = ff_v4l2_context_dequeue_frame(&s->capture, frame, -1, 1);
++            // Pick a timeout depending on state
++            const int t =
++                src_rv == NQ_DRAINING ? 300 :
++                prefer_dq ? 5 :
++                src_rv == NQ_Q_FULL ? -1 : 0;
 +
-+                if (dst_rv == AVERROR_EOF && (s->draining || s->capture.done))
-+                    av_log(avctx, AV_LOG_DEBUG, "Dequeue EOF: draining=%d, cap.done=%d\n",
-+                           s->draining, s->capture.done);
-+                else if (dst_rv && dst_rv != AVERROR(EAGAIN))
-+                    av_log(avctx, AV_LOG_ERROR, "Packet dequeue failure: draining=%d, cap.done=%d, err=%d\n",
-+                           s->draining, s->capture.done, dst_rv);
++            // Dequeue frame will unref any previous contents of frame
++            // if it returns success so we don't need an explicit unref
++            // when discarding
++            // This returns AVERROR(EAGAIN) on timeout or if
++            // there is room in the input Q and timeout == -1
++            dst_rv = ff_v4l2_context_dequeue_frame(&s->capture, frame, t);
 +
-+                // Go again if we got a frame that we need to discard
-+            } while (dst_rv == 0 && xlat_pts_out(avctx, s, frame));
++            // Failure due to no buffer in Q?
++            if (dst_rv == AVERROR(ENOSPC)) {
++                // Wait & retry
++                if ((dst_rv = qbuf_wait(avctx, &s->capture)) == 0) {
++                    dst_rv = ff_v4l2_context_dequeue_frame(&s->capture, frame, t);
++                }
++            }
++
++            // Adjust dynamic pending threshold
++            if (dst_rv == 0) {
++                if (--s->pending_hw < PENDING_HW_MIN)
++                    s->pending_hw = PENDING_HW_MIN;
++                s->pending_n = 0;
++
++                set_best_effort_pts(avctx, &s->pts_stat, frame);
++            }
++            else if (dst_rv == AVERROR(EAGAIN)) {
++                if (prefer_dq && ++s->pending_n > PENDING_N_THRESHOLD) {
++                    s->pending_hw = pending * 16 + PENDING_HW_OFFSET;
++                    s->pending_n = 0;
++                }
++            }
++
++            if (dst_rv == AVERROR(EAGAIN) && src_rv == NQ_DRAINING) {
++                av_log(avctx, AV_LOG_WARNING, "Timeout in drain - assume EOF");
++                dst_rv = AVERROR_EOF;
++                s->capture.done = 1;
++            }
++            else if (dst_rv == AVERROR_EOF && (s->draining || s->capture.done))
++                av_log(avctx, AV_LOG_DEBUG, "Dequeue EOF: draining=%d, cap.done=%d\n",
++                       s->draining, s->capture.done);
++            else if (dst_rv && dst_rv != AVERROR(EAGAIN))
++                av_log(avctx, AV_LOG_ERROR, "Packet dequeue failure: draining=%d, cap.done=%d, err=%d\n",
++                       s->draining, s->capture.done, dst_rv);
++        }
++
++        ++i;
++        if (i >= 256) {
++            av_log(avctx, AV_LOG_ERROR, "Unexpectedly large retry count: %d\n", i);
++            src_rv = AVERROR(EIO);
 +        }
 +
 +        // Continue trying to enqueue packets if either
 +        // (a) we succeeded last time OR
-+        // (b) enqueue failed due to input Q full AND there is now room
-+    } while (src_rv == NQ_OK || (src_rv == NQ_Q_FULL && dst_rv == AVERROR(EAGAIN)) );
++        // (b) we didn't ret a frame and we can retry the input
++    } while (src_rv == NQ_OK || (dst_rv == AVERROR(EAGAIN) && RETRY_NQ(src_rv)));
 +
 +    // Ensure that the frame contains nothing if we aren't returning a frame
 +    // (might happen when discarding)
@@ -48645,7 +53231,7 @@
 +        av_frame_unref(frame);
 +
 +    // If we got a frame this time ask for a pkt next time
-+    s->req_pkt = (dst_rv == 0);
++    s->req_pkt = (dst_rv == 0) ? s->req_pkt + 1 : 0;
 +
 +#if 0
 +    if (dst_rv == 0)
@@ -48655,8 +53241,8 @@
 +            av_log(avctx, AV_LOG_ERROR, "Streamoff and die?\n");
 +            ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
 +            return -1;
-         }
-     }
++        }
++    }
 +#endif
 +
 +    return dst_rv == 0 ? 0 :
@@ -48687,18 +53273,113 @@
 +}
 +#endif
 +
++static int
++check_size(AVCodecContext * const avctx, V4L2m2mContext * const s)
++{
++    unsigned int i;
++    const uint32_t fcc = ff_v4l2_get_format_pixelformat(&s->capture.format);
++    const uint32_t w = avctx->coded_width;
++    const uint32_t h = avctx->coded_height;
++
++    if (w == 0 || h == 0 || fcc == 0) {
++        av_log(avctx, AV_LOG_TRACE, "%s: Size %dx%d or fcc %s empty\n", __func__, w, h, av_fourcc2str(fcc));
++        return 0;
++    }
++    if ((s->quirks & FF_V4L2_QUIRK_ENUM_FRAMESIZES_BROKEN) != 0) {
++        av_log(avctx, AV_LOG_TRACE, "%s: Skipped (quirk): Size %dx%d, fcc %s\n", __func__, w, h, av_fourcc2str(fcc));
++        return 0;
++    }
++
++    for (i = 0;; ++i) {
++        struct v4l2_frmsizeenum fs = {
++            .index = i,
++            .pixel_format = fcc,
++        };
++
++        while (ioctl(s->fd, VIDIOC_ENUM_FRAMESIZES, &fs) != 0) {
++            const int err = AVERROR(errno);
++            if (err == AVERROR(EINTR))
++                continue;
++            if (i == 0 && err == AVERROR(ENOTTY)) {
++                av_log(avctx, AV_LOG_DEBUG, "Framesize enum not supported\n");
++                return 0;
++            }
++            if (err != AVERROR(EINVAL)) {
++                av_log(avctx, AV_LOG_ERROR, "Failed to enum framesizes: %s", av_err2str(err));
++                return err;
++            }
++            av_log(avctx, AV_LOG_WARNING, "Failed to find Size=%dx%d, fmt=%s in %u frame size enums\n",
++                   w, h, av_fourcc2str(fcc), i);
++            return err;
++        }
++
++        switch (fs.type) {
++            case V4L2_FRMSIZE_TYPE_DISCRETE:
++                av_log(avctx, AV_LOG_TRACE, "%s[%d]: Discrete: %dx%d\n", __func__, i,
++                       fs.discrete.width,fs.discrete.height);
++                if (w == fs.discrete.width && h == fs.discrete.height)
++                    return 0;
++                break;
++            case V4L2_FRMSIZE_TYPE_STEPWISE:
++                av_log(avctx, AV_LOG_TRACE, "%s[%d]: Stepwise: Min: %dx%d Max: %dx%d, Step: %dx%d\n", __func__, i,
++                       fs.stepwise.min_width, fs.stepwise.min_height,
++                       fs.stepwise.max_width, fs.stepwise.max_height,
++                       fs.stepwise.step_width,fs.stepwise.step_height);
++                if (w >= fs.stepwise.min_width && w <= fs.stepwise.max_width &&
++                    h >= fs.stepwise.min_height && h <= fs.stepwise.max_height &&
++                    (w - fs.stepwise.min_width) % fs.stepwise.step_width == 0 &&
++                    (h - fs.stepwise.min_height) % fs.stepwise.step_height == 0)
++                    return 0;
++                break;
++            case V4L2_FRMSIZE_TYPE_CONTINUOUS:
++                av_log(avctx, AV_LOG_TRACE, "%s[%d]: Continuous: Min: %dx%d Max: %dx%d, Step: %dx%d\n", __func__, i,
++                       fs.stepwise.min_width, fs.stepwise.min_height,
++                       fs.stepwise.max_width, fs.stepwise.max_height,
++                       fs.stepwise.step_width,fs.stepwise.step_height);
++                if (w >= fs.stepwise.min_width && w <= fs.stepwise.max_width &&
++                    h >= fs.stepwise.min_height && h <= fs.stepwise.max_height)
++                    return 0;
++                break;
++            default:
++                av_log(avctx, AV_LOG_ERROR, "Unexpected framesize enum: %d", fs.type);
++                return AVERROR(EINVAL);
++        }
++    }
++}
++
++static int
++get_quirks(AVCodecContext * const avctx, V4L2m2mContext * const s)
++{
++    struct v4l2_capability cap;
++
++    memset(&cap, 0, sizeof(cap));
++    while (ioctl(s->fd, VIDIOC_QUERYCAP, &cap) != 0) {
++        int err = errno;
++        if (err == EINTR)
++            continue;
++        av_log(avctx, AV_LOG_ERROR, "V4L2: Failed to get capabilities: %s\n", strerror(err));
++        return AVERROR(err);
++    }
++
++    // Could be made table driven if we have a few more but right now there
++    // seems no point
++
++    // Meson (amlogic) always gives a resolution changed event after output
++    // streamon and userspace must (re)allocate capture buffers and streamon
++    // capture to clear the event even if the capture buffers were the right
++    // size in the first place.
++    if (strcmp(cap.driver, "meson-vdec") == 0)
++        s->quirks |= FF_V4L2_QUIRK_REINIT_ALWAYS | FF_V4L2_QUIRK_ENUM_FRAMESIZES_BROKEN;
++
++    av_log(avctx, AV_LOG_DEBUG, "Driver '%s': Quirks=%#x\n", cap.driver, s->quirks);
++    return 0;
++}
++
++// This heuristic is for H264 but use for everything
 +static uint32_t max_coded_size(const AVCodecContext * const avctx)
 +{
 +    uint32_t wxh = avctx->coded_width * avctx->coded_height;
 +    uint32_t size;
-
--dequeue:
--    if (!s->buf_pkt.size)
--        av_packet_unref(&avpkt);
--    return ff_v4l2_context_dequeue_frame(capture, frame, -1);
-+    // Currently the only thing we try to set our own limits for is H264
-+    if (avctx->codec_id != AV_CODEC_ID_H264)
-+        return 0;
 +
 +    size = wxh * 3 / 2;
 +    // H.264 Annex A table A-1 gives minCR which is either 2 or 4
@@ -48711,27 +53392,53 @@
 +    // with small WxH
 +    return size + (1 << 16);
  }
-
+ 
  static av_cold int v4l2_decode_init(AVCodecContext *avctx)
-@@ -186,8 +481,12 @@ static av_cold int v4l2_decode_init(AVCo
+@@ -186,12 +699,29 @@ static av_cold int v4l2_decode_init(AVCo
      V4L2Context *capture, *output;
      V4L2m2mContext *s;
      V4L2m2mPriv *priv = avctx->priv_data;
 +    int gf_pix_fmt;
      int ret;
-
+ 
 +    av_log(avctx, AV_LOG_TRACE, "<<< %s\n", __func__);
 +
++    if (avctx->codec_id == AV_CODEC_ID_H264) {
++        if (avctx->ticks_per_frame == 1) {
++            if(avctx->time_base.den < INT_MAX/2) {
++                avctx->time_base.den *= 2;
++            } else
++                avctx->time_base.num /= 2;
++        }
++        avctx->ticks_per_frame = 2;
++    }
++
 +    av_log(avctx, AV_LOG_INFO, "level=%d\n", avctx->level);
      ret = ff_v4l2_m2m_create_context(priv, &s);
      if (ret < 0)
          return ret;
-@@ -204,17 +503,43 @@ static av_cold int v4l2_decode_init(AVCo
-
+ 
++    pts_stats_init(&s->pts_stat, avctx, "decoder");
++    s->pending_hw = PENDING_HW_MIN;
++
+     capture = &s->capture;
+     output = &s->output;
+ 
+@@ -199,34 +729,127 @@ static av_cold int v4l2_decode_init(AVCo
+      * by the v4l2 driver; this event will trigger a full pipeline reconfig and
+      * the proper values will be retrieved from the kernel driver.
+      */
+-    output->height = capture->height = avctx->coded_height;
+-    output->width = capture->width = avctx->coded_width;
++//    output->height = capture->height = avctx->coded_height;
++//    output->width = capture->width = avctx->coded_width;
++    output->height = capture->height = 0;
++    output->width = capture->width = 0;
+ 
      output->av_codec_id = avctx->codec_id;
      output->av_pix_fmt  = AV_PIX_FMT_NONE;
 +    output->min_buf_size = max_coded_size(avctx);
-
+ 
      capture->av_codec_id = AV_CODEC_ID_RAWVIDEO;
      capture->av_pix_fmt = avctx->pix_fmt;
 +    capture->min_buf_size = 0;
@@ -48743,15 +53450,21 @@
 +     *       check the v4l2_get_drm_frame function.
 +     */
 +
++    avctx->sw_pix_fmt = avctx->pix_fmt;
 +    gf_pix_fmt = ff_get_format(avctx, avctx->codec->pix_fmts);
-+    av_log(avctx, AV_LOG_DEBUG, "avctx requested=%d (%s); get_format requested=%d (%s)\n",
-+           avctx->pix_fmt, av_get_pix_fmt_name(avctx->pix_fmt), gf_pix_fmt, av_get_pix_fmt_name(gf_pix_fmt));
++    av_log(avctx, AV_LOG_DEBUG, "avctx requested=%d (%s) %dx%d; get_format requested=%d (%s)\n",
++           avctx->pix_fmt, av_get_pix_fmt_name(avctx->pix_fmt),
++           avctx->coded_width, avctx->coded_height,
++           gf_pix_fmt, av_get_pix_fmt_name(gf_pix_fmt));
 +
-+    s->output_drm = 0;
 +    if (gf_pix_fmt == AV_PIX_FMT_DRM_PRIME || avctx->pix_fmt == AV_PIX_FMT_DRM_PRIME) {
 +        avctx->pix_fmt = AV_PIX_FMT_DRM_PRIME;
 +        s->output_drm = 1;
 +    }
++    else {
++        capture->av_pix_fmt = gf_pix_fmt;
++        s->output_drm = 0;
++    }
 +
 +    s->device_ref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_DRM);
 +    if (!s->device_ref) {
@@ -48762,7 +53475,7 @@
 +    ret = av_hwdevice_ctx_init(s->device_ref);
 +    if (ret < 0)
 +        return ret;
-
+ 
      s->avctx = avctx;
      ret = ff_v4l2_m2m_codec_init(priv);
      if (ret) {
@@ -48772,15 +53485,24 @@
 -
          return ret;
      }
-
-@@ -223,10 +548,53 @@ static av_cold int v4l2_decode_init(AVCo
-
+ 
+-    return v4l2_prepare_decoder(s);
++    if ((ret = v4l2_prepare_decoder(s)) < 0)
++        return ret;
++
++    if ((ret = get_quirks(avctx, s)) != 0)
++        return ret;
++
++    if ((ret = check_size(avctx, s)) != 0)
++        return ret;
++
++    return 0;
+ }
+ 
  static av_cold int v4l2_decode_close(AVCodecContext *avctx)
  {
 -    V4L2m2mPriv *priv = avctx->priv_data;
 -    V4L2m2mContext *s = priv->context;
--    av_packet_unref(&s->buf_pkt);
--    return ff_v4l2_m2m_codec_end(priv);
 +    int rv;
 +    av_log(avctx, AV_LOG_TRACE, "<<< %s\n", __func__);
 +    rv = ff_v4l2_m2m_codec_end(avctx->priv_data);
@@ -48802,7 +53524,6 @@
 +    V4L2m2mContext * const s = priv->context;
 +    V4L2Context * const output = &s->output;
 +    V4L2Context * const capture = &s->capture;
-+    int ret, i;
 +
 +    av_log(avctx, AV_LOG_TRACE, "<<< %s: streamon=%d\n", __func__, output->streamon);
 +
@@ -48810,14 +53531,23 @@
 +    // states like EOS processing so don't try to optimize out (having got it
 +    // wrong once)
 +
-+    ret = ff_v4l2_context_set_status(output, VIDIOC_STREAMOFF);
-+    if (ret < 0)
-+        av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s error: %d\n", output->name, ret);
++    ff_v4l2_context_set_status(output, VIDIOC_STREAMOFF);
++
++    // Clear any buffered input packet
+     av_packet_unref(&s->buf_pkt);
+-    return ff_v4l2_m2m_codec_end(priv);
++
++    // Clear a pending EOS
++    if (ff_v4l2_ctx_eos(capture)) {
++        // Arguably we could delay this but this is easy and doesn't require
++        // thought or extra vars
++        ff_v4l2_context_set_status(capture, VIDIOC_STREAMOFF);
++        ff_v4l2_context_set_status(capture, VIDIOC_STREAMON);
++    }
 +
 +    // V4L2 makes no guarantees about whether decoded frames are flushed or not
 +    // so mark all frames we are tracking to be discarded if they appear
-+    for (i = 0; i != FF_V4L2_M2M_TRACK_SIZE; ++i)
-+        s->track_els[i].discard = 1;
++    xlat_flush(&s->xlat);
 +
 +    // resend extradata
 +    s->extdata_sent = 0;
@@ -48829,9 +53559,9 @@
 +    // Stream on will occur when we actually submit a new frame
 +    av_log(avctx, AV_LOG_TRACE, ">>> %s\n", __func__);
  }
-
+ 
  #define OFFSET(x) offsetof(V4L2m2mPriv, x)
-@@ -235,10 +603,16 @@ static av_cold int v4l2_decode_close(AVC
+@@ -235,10 +858,16 @@ static av_cold int v4l2_decode_close(AVC
  static const AVOption options[] = {
      V4L_M2M_DEFAULT_OPTS,
      { "num_capture_buffers", "Number of buffers in the capture context",
@@ -48840,7 +53570,7 @@
 +    { "pixel_format", "Pixel format to be used by the decoder", OFFSET(pix_fmt), AV_OPT_TYPE_PIXEL_FMT, {.i64 = AV_PIX_FMT_NONE}, AV_PIX_FMT_NONE, AV_PIX_FMT_NB, FLAGS },
      { NULL},
  };
-
+ 
 +static const AVCodecHWConfigInternal *v4l2_m2m_hw_configs[] = {
 +    HW_CONFIG_INTERNAL(DRM_PRIME),
 +    NULL
@@ -48849,7 +53579,7 @@
  #define M2MDEC_CLASS(NAME) \
      static const AVClass v4l2_m2m_ ## NAME ## _dec_class = { \
          .class_name = #NAME "_v4l2m2m_decoder", \
-@@ -259,9 +633,15 @@ static const AVOption options[] = {
+@@ -259,9 +888,15 @@ static const AVOption options[] = {
          .init           = v4l2_decode_init, \
          .receive_frame  = v4l2_receive_frame, \
          .close          = v4l2_decode_close, \
@@ -48865,7 +53595,366 @@
 +        .hw_configs     = v4l2_m2m_hw_configs, \
          .wrapper_name   = "v4l2m2m", \
      }
-
+ 
+--- a/libavcodec/v4l2_m2m_enc.c
++++ b/libavcodec/v4l2_m2m_enc.c
+@@ -24,6 +24,8 @@
+ #include <linux/videodev2.h>
+ #include <sys/ioctl.h>
+ #include <search.h>
++#include <drm_fourcc.h>
++
+ #include "libavcodec/avcodec.h"
+ #include "libavcodec/internal.h"
+ #include "libavutil/pixdesc.h"
+@@ -37,6 +39,34 @@
+ #define MPEG_CID(x) V4L2_CID_MPEG_VIDEO_##x
+ #define MPEG_VIDEO(x) V4L2_MPEG_VIDEO_##x
+ 
++// P030 should be defined in drm_fourcc.h and hopefully will be sometime
++// in the future but until then...
++#ifndef DRM_FORMAT_P030
++#define DRM_FORMAT_P030 fourcc_code('P', '0', '3', '0')
++#endif
++
++#ifndef DRM_FORMAT_NV15
++#define DRM_FORMAT_NV15 fourcc_code('N', 'V', '1', '5')
++#endif
++
++#ifndef DRM_FORMAT_NV20
++#define DRM_FORMAT_NV20 fourcc_code('N', 'V', '2', '0')
++#endif
++
++#ifndef V4L2_CID_CODEC_BASE
++#define V4L2_CID_CODEC_BASE V4L2_CID_MPEG_BASE
++#endif
++
++// V4L2_PIX_FMT_NV12_10_COL128 and V4L2_PIX_FMT_NV12_COL128 should be defined
++// in videodev2.h hopefully will be sometime in the future but until then...
++#ifndef V4L2_PIX_FMT_NV12_10_COL128
++#define V4L2_PIX_FMT_NV12_10_COL128 v4l2_fourcc('N', 'C', '3', '0')
++#endif
++
++#ifndef V4L2_PIX_FMT_NV12_COL128
++#define V4L2_PIX_FMT_NV12_COL128 v4l2_fourcc('N', 'C', '1', '2') /* 12  Y/CbCr 4:2:0 128 pixel wide column */
++#endif
++
+ static inline void v4l2_set_timeperframe(V4L2m2mContext *s, unsigned int num, unsigned int den)
+ {
+     struct v4l2_streamparm parm = { 0 };
+@@ -147,15 +177,14 @@ static inline int v4l2_mpeg4_profile_fro
+ static int v4l2_check_b_frame_support(V4L2m2mContext *s)
+ {
+     if (s->avctx->max_b_frames)
+-        av_log(s->avctx, AV_LOG_WARNING, "Encoder does not support b-frames yet\n");
++        av_log(s->avctx, AV_LOG_WARNING, "Encoder does not support %d b-frames yet\n", s->avctx->max_b_frames);
+ 
+-    v4l2_set_ext_ctrl(s, MPEG_CID(B_FRAMES), 0, "number of B-frames", 0);
++    v4l2_set_ext_ctrl(s, MPEG_CID(B_FRAMES), s->avctx->max_b_frames, "number of B-frames", 1);
+     v4l2_get_ext_ctrl(s, MPEG_CID(B_FRAMES), &s->avctx->max_b_frames, "number of B-frames", 0);
+     if (s->avctx->max_b_frames == 0)
+         return 0;
+ 
+     avpriv_report_missing_feature(s->avctx, "DTS/PTS calculation for V4L2 encoding");
+-
+     return AVERROR_PATCHWELCOME;
+ }
+ 
+@@ -270,13 +299,184 @@ static int v4l2_prepare_encoder(V4L2m2mC
+     return 0;
+ }
+ 
++static int avdrm_to_v4l2(struct v4l2_format * const format, const AVFrame * const frame)
++{
++    const AVDRMFrameDescriptor *const src = (const AVDRMFrameDescriptor *)frame->data[0];
++
++    const uint32_t drm_fmt = src->layers[0].format;
++    // Treat INVALID as LINEAR
++    const uint64_t mod = src->objects[0].format_modifier == DRM_FORMAT_MOD_INVALID ?
++        DRM_FORMAT_MOD_LINEAR : src->objects[0].format_modifier;
++    uint32_t pix_fmt = 0;
++    uint32_t w = 0;
++    uint32_t h = 0;
++    uint32_t bpl = src->layers[0].planes[0].pitch;
++
++    // We really don't expect multiple layers
++    // All formats that we currently cope with are single object
++
++    if (src->nb_layers != 1 || src->nb_objects != 1)
++        return AVERROR(EINVAL);
++
++    switch (drm_fmt) {
++        case DRM_FORMAT_YUV420:
++            if (mod == DRM_FORMAT_MOD_LINEAR) {
++                if (src->layers[0].nb_planes != 3)
++                    break;
++                pix_fmt = V4L2_PIX_FMT_YUV420;
++                h = src->layers[0].planes[1].offset / bpl;
++                w = bpl;
++            }
++            break;
++
++        case DRM_FORMAT_NV12:
++            if (mod == DRM_FORMAT_MOD_LINEAR) {
++                if (src->layers[0].nb_planes != 2)
++                    break;
++                pix_fmt = V4L2_PIX_FMT_NV12;
++                h = src->layers[0].planes[1].offset / bpl;
++                w = bpl;
++            }
++            else if (fourcc_mod_broadcom_mod(mod) == DRM_FORMAT_MOD_BROADCOM_SAND128) {
++                if (src->layers[0].nb_planes != 2)
++                    break;
++                pix_fmt = V4L2_PIX_FMT_NV12_COL128;
++                w = bpl;
++                h = src->layers[0].planes[1].offset / 128;
++                bpl = fourcc_mod_broadcom_param(mod);
++            }
++            break;
++
++        case DRM_FORMAT_P030:
++            if (fourcc_mod_broadcom_mod(mod) == DRM_FORMAT_MOD_BROADCOM_SAND128) {
++                if (src->layers[0].nb_planes != 2)
++                    break;
++                pix_fmt =  V4L2_PIX_FMT_NV12_10_COL128;
++                w = bpl / 2;  // Matching lie to how we construct this
++                h = src->layers[0].planes[1].offset / 128;
++                bpl = fourcc_mod_broadcom_param(mod);
++            }
++            break;
++
++        default:
++            break;
++    }
++
++    if (!pix_fmt)
++        return AVERROR(EINVAL);
++
++    if (V4L2_TYPE_IS_MULTIPLANAR(format->type)) {
++        struct v4l2_pix_format_mplane *const pix = &format->fmt.pix_mp;
++
++        pix->width = w;
++        pix->height = h;
++        pix->pixelformat = pix_fmt;
++        pix->plane_fmt[0].bytesperline = bpl;
++        pix->num_planes = 1;
++    }
++    else {
++        struct v4l2_pix_format *const pix = &format->fmt.pix;
++
++        pix->width = w;
++        pix->height = h;
++        pix->pixelformat = pix_fmt;
++        pix->bytesperline = bpl;
++    }
++
++    return 0;
++}
++
++// Do we have similar enough formats to be usable?
++static int fmt_eq(const struct v4l2_format * const a, const struct v4l2_format * const b)
++{
++    if (a->type != b->type)
++        return 0;
++
++    if (V4L2_TYPE_IS_MULTIPLANAR(a->type)) {
++        const struct v4l2_pix_format_mplane *const pa = &a->fmt.pix_mp;
++        const struct v4l2_pix_format_mplane *const pb = &b->fmt.pix_mp;
++        unsigned int i;
++        if (pa->pixelformat != pb->pixelformat ||
++            pa->num_planes != pb->num_planes)
++            return 0;
++        for (i = 0; i != pa->num_planes; ++i) {
++            if (pa->plane_fmt[i].bytesperline != pb->plane_fmt[i].bytesperline)
++                return 0;
++        }
++    }
++    else {
++        const struct v4l2_pix_format *const pa = &a->fmt.pix;
++        const struct v4l2_pix_format *const pb = &b->fmt.pix;
++        if (pa->pixelformat != pb->pixelformat ||
++            pa->bytesperline != pb->bytesperline)
++            return 0;
++    }
++    return 1;
++}
++
++
+ static int v4l2_send_frame(AVCodecContext *avctx, const AVFrame *frame)
+ {
+     V4L2m2mContext *s = ((V4L2m2mPriv*)avctx->priv_data)->context;
+     V4L2Context *const output = &s->output;
+ 
++    // Signal EOF if needed
++    if (!frame) {
++        return ff_v4l2_context_enqueue_frame(output, frame);
++    }
++
++    if (s->input_drm && !output->streamon) {
++        int rv;
++        struct v4l2_format req_format = {.type = output->format.type};
++
++        // Set format when we first get a buffer
++        if ((rv = avdrm_to_v4l2(&req_format, frame)) != 0) {
++            av_log(avctx, AV_LOG_ERROR, "Failed to get V4L2 format from DRM_PRIME frame\n");
++            return rv;
++        }
++
++        ff_v4l2_context_release(output);
++
++        output->format = req_format;
++
++        if ((rv = ff_v4l2_context_set_format(output)) != 0) {
++            av_log(avctx, AV_LOG_ERROR, "Failed to set V4L2 format\n");
++            return rv;
++        }
++
++        if (!fmt_eq(&req_format, &output->format)) {
++            av_log(avctx, AV_LOG_ERROR, "Format mismatch after setup\n");
++            return AVERROR(EINVAL);
++        }
++
++        output->selection.top = frame->crop_top;
++        output->selection.left = frame->crop_left;
++        output->selection.width = av_frame_cropped_width(frame);
++        output->selection.height = av_frame_cropped_height(frame);
++
++        if ((rv = ff_v4l2_context_init(output)) != 0) {
++            av_log(avctx, AV_LOG_ERROR, "Failed to (re)init context\n");
++            return rv;
++        }
++
++        {
++            struct v4l2_selection selection = {
++                .type = V4L2_BUF_TYPE_VIDEO_OUTPUT,
++                .target = V4L2_SEL_TGT_CROP,
++                .r = output->selection
++            };
++            if (ioctl(s->fd, VIDIOC_S_SELECTION, &selection) != 0) {
++                av_log(avctx, AV_LOG_WARNING, "S_SELECTION (CROP) %dx%d @ %d,%d failed: %s\n",
++                       selection.r.width, selection.r.height, selection.r.left, selection.r.top,
++                       av_err2str(AVERROR(errno)));
++            }
++            av_log(avctx, AV_LOG_TRACE, "S_SELECTION (CROP) %dx%d @ %d,%d OK\n",
++                   selection.r.width, selection.r.height, selection.r.left, selection.r.top);
++        }
++    }
++
+ #ifdef V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME
+-    if (frame && frame->pict_type == AV_PICTURE_TYPE_I)
++    if (frame->pict_type == AV_PICTURE_TYPE_I)
+         v4l2_set_ext_ctrl(s, MPEG_CID(FORCE_KEY_FRAME), 0, "force key frame", 1);
+ #endif
+ 
+@@ -310,7 +510,70 @@ static int v4l2_receive_packet(AVCodecCo
+     }
+ 
+ dequeue:
+-    return ff_v4l2_context_dequeue_packet(capture, avpkt);
++    if ((ret = ff_v4l2_context_dequeue_packet(capture, avpkt)) != 0)
++        return ret;
++
++    if (capture->first_buf == 1) {
++        uint8_t * data;
++        const int len = avpkt->size;
++
++        // 1st buffer after streamon should be SPS/PPS
++        capture->first_buf = 2;
++
++        // Clear both possible stores so there is no chance of confusion
++        av_freep(&s->extdata_data);
++        s->extdata_size = 0;
++        av_freep(&avctx->extradata);
++        avctx->extradata_size = 0;
++
++        if ((data = av_malloc(len + AV_INPUT_BUFFER_PADDING_SIZE)) != NULL)
++            memcpy(data, avpkt->data, len);
++
++        av_packet_unref(avpkt);
++
++        if (data == NULL)
++            return AVERROR(ENOMEM);
++
++        // We need to copy the header, but keep local if not global
++        if ((avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) != 0) {
++            avctx->extradata = data;
++            avctx->extradata_size = len;
++        }
++        else {
++            s->extdata_data = data;
++            s->extdata_size = len;
++        }
++
++        if ((ret = ff_v4l2_context_dequeue_packet(capture, avpkt)) != 0)
++            return ret;
++    }
++
++    // First frame must be key so mark as such even if encoder forgot
++    if (capture->first_buf == 2)
++        avpkt->flags |= AV_PKT_FLAG_KEY;
++
++    // Add SPS/PPS to the start of every key frame if non-global headers
++    if ((avpkt->flags & AV_PKT_FLAG_KEY) != 0 && s->extdata_size != 0) {
++        const size_t newlen = s->extdata_size + avpkt->size;
++        AVBufferRef * const buf = av_buffer_alloc(newlen + AV_INPUT_BUFFER_PADDING_SIZE);
++
++        if (buf == NULL) {
++            av_packet_unref(avpkt);
++            return AVERROR(ENOMEM);
++        }
++
++        memcpy(buf->data, s->extdata_data, s->extdata_size);
++        memcpy(buf->data + s->extdata_size, avpkt->data, avpkt->size);
++
++        av_buffer_unref(&avpkt->buf);
++        avpkt->buf = buf;
++        avpkt->data = buf->data;
++        avpkt->size = newlen;
++    }
++
++//    av_log(avctx, AV_LOG_INFO, "%s: PTS out=%"PRId64", size=%d, ret=%d\n", __func__, avpkt->pts, avpkt->size, ret);
++    capture->first_buf = 0;
++    return 0;
+ }
+ 
+ static av_cold int v4l2_encode_init(AVCodecContext *avctx)
+@@ -322,6 +585,8 @@ static av_cold int v4l2_encode_init(AVCo
+     uint32_t v4l2_fmt_output;
+     int ret;
+ 
++    av_log(avctx, AV_LOG_INFO, " <<< %s: fmt=%d/%d\n", __func__, avctx->pix_fmt, avctx->sw_pix_fmt);
++
+     ret = ff_v4l2_m2m_create_context(priv, &s);
+     if (ret < 0)
+         return ret;
+@@ -329,13 +594,17 @@ static av_cold int v4l2_encode_init(AVCo
+     capture = &s->capture;
+     output  = &s->output;
+ 
++    s->input_drm = (avctx->pix_fmt == AV_PIX_FMT_DRM_PRIME);
++
+     /* common settings output/capture */
+     output->height = capture->height = avctx->height;
+     output->width = capture->width = avctx->width;
+ 
+     /* output context */
+     output->av_codec_id = AV_CODEC_ID_RAWVIDEO;
+-    output->av_pix_fmt = avctx->pix_fmt;
++    output->av_pix_fmt = !s->input_drm ? avctx->pix_fmt :
++            avctx->sw_pix_fmt != AV_PIX_FMT_NONE ? avctx->sw_pix_fmt :
++            AV_PIX_FMT_YUV420P;
+ 
+     /* capture context */
+     capture->av_codec_id = avctx->codec_id;
+@@ -354,7 +623,7 @@ static av_cold int v4l2_encode_init(AVCo
+         v4l2_fmt_output = output->format.fmt.pix.pixelformat;
+ 
+     pix_fmt_output = ff_v4l2_format_v4l2_to_avfmt(v4l2_fmt_output, AV_CODEC_ID_RAWVIDEO);
+-    if (pix_fmt_output != avctx->pix_fmt) {
++    if (!s->input_drm && pix_fmt_output != avctx->pix_fmt) {
+         const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt_output);
+         av_log(avctx, AV_LOG_ERROR, "Encoder requires %s pixel format.\n", desc->name);
+         return AVERROR(EINVAL);
 --- /dev/null
 +++ b/libavcodec/v4l2_req_decode_q.c
 @@ -0,0 +1,84 @@
@@ -49780,16 +54869,26 @@
 +#include "v4l2_req_hevc_vx.c"
 +
 --- /dev/null
++++ b/libavcodec/v4l2_req_hevc_v3.c
+@@ -0,0 +1,3 @@
++#define HEVC_CTRLS_VERSION 3
++#include "v4l2_req_hevc_vx.c"
++
+--- /dev/null
++++ b/libavcodec/v4l2_req_hevc_v4.c
+@@ -0,0 +1,3 @@
++#define HEVC_CTRLS_VERSION 4
++#include "v4l2_req_hevc_vx.c"
++
+--- /dev/null
 +++ b/libavcodec/v4l2_req_hevc_vx.c
-@@ -0,0 +1,1188 @@
+@@ -0,0 +1,1365 @@
 +// File included by v4l2_req_hevc_v* - not compiled on its own
 +
 +#include "decode.h"
 +#include "hevcdec.h"
 +#include "hwconfig.h"
 +
-+#include "v4l2_request_hevc.h"
-+
 +#if HEVC_CTRLS_VERSION == 1
 +#include "hevc-ctrls-v1.h"
 +
@@ -49798,10 +54897,39 @@
 +
 +#elif HEVC_CTRLS_VERSION == 2
 +#include "hevc-ctrls-v2.h"
++#elif HEVC_CTRLS_VERSION == 3
++#include "hevc-ctrls-v3.h"
++#elif HEVC_CTRLS_VERSION == 4
++#include <linux/v4l2-controls.h>
++#if !defined(V4L2_CID_STATELESS_HEVC_SPS)
++#include "hevc-ctrls-v4.h"
++#endif
 +#else
 +#error Unknown HEVC_CTRLS_VERSION
 +#endif
 +
++#ifndef V4L2_CID_STATELESS_HEVC_SPS
++#define V4L2_CID_STATELESS_HEVC_SPS                     V4L2_CID_MPEG_VIDEO_HEVC_SPS
++#define V4L2_CID_STATELESS_HEVC_PPS                     V4L2_CID_MPEG_VIDEO_HEVC_PPS
++#define V4L2_CID_STATELESS_HEVC_SLICE_PARAMS            V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS
++#define V4L2_CID_STATELESS_HEVC_SCALING_MATRIX          V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX
++#define V4L2_CID_STATELESS_HEVC_DECODE_PARAMS           V4L2_CID_MPEG_VIDEO_HEVC_DECODE_PARAMS
++#define V4L2_CID_STATELESS_HEVC_DECODE_MODE             V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE
++#define V4L2_CID_STATELESS_HEVC_START_CODE              V4L2_CID_MPEG_VIDEO_HEVC_START_CODE
++
++#define V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED     V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED
++#define V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED     V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_FRAME_BASED
++#define V4L2_STATELESS_HEVC_START_CODE_NONE             V4L2_MPEG_VIDEO_HEVC_START_CODE_NONE
++#define V4L2_STATELESS_HEVC_START_CODE_ANNEX_B          V4L2_MPEG_VIDEO_HEVC_START_CODE_ANNEX_B
++#endif
++
++// Should be in videodev2 but we might not have a good enough one
++#ifndef V4L2_PIX_FMT_HEVC_SLICE
++#define V4L2_PIX_FMT_HEVC_SLICE v4l2_fourcc('S', '2', '6', '5') /* HEVC parsed slices */
++#endif
++
++#include "v4l2_request_hevc.h"
++
 +#include "libavutil/hwcontext_drm.h"
 +
 +#include <semaphore.h>
@@ -49837,11 +54965,16 @@
 +    struct v4l2_ctrl_hevc_slice_params * slice_params;
 +    struct slice_info * slices;
 +
++    size_t num_offsets;
++    size_t alloced_offsets;
++    uint32_t *offsets;
++
 +} V4L2MediaReqDescriptor;
 +
 +struct slice_info {
 +    const uint8_t * ptr;
 +    size_t len; // bytes
++    size_t n_offsets;
 +};
 +
 +// Handy container for accumulating controls before setting
@@ -49929,6 +55062,7 @@
 +    }
 +}
 +
++#if HEVC_CTRLS_VERSION <= 2
 +static int find_frame_rps_type(const HEVCContext *h, uint64_t timestamp)
 +{
 +    const HEVCFrame *frame;
@@ -49954,6 +55088,7 @@
 +
 +    return 0;
 +}
++#endif
 +
 +static unsigned int
 +get_ref_pic_index(const HEVCContext *h, const HEVCFrame *frame,
@@ -49998,7 +55133,7 @@
 +    if (rd->num_slices >= rd->alloced_slices) {
 +        struct v4l2_ctrl_hevc_slice_params * p2;
 +        struct slice_info * s2;
-+        size_t n2 = rd->num_slices == 0 ? 8 : rd->num_slices * 2;
++        size_t n2 = rd->alloced_slices == 0 ? 8 : rd->alloced_slices * 2;
 +
 +        p2 = av_realloc_array(rd->slice_params, n2, sizeof(*p2));
 +        if (p2 == NULL)
@@ -50016,6 +55151,23 @@
 +    return 0;
 +}
 +
++static int offsets_add(V4L2MediaReqDescriptor *const rd, const size_t n, const unsigned * const offsets)
++{
++    if (rd->num_offsets + n > rd->alloced_offsets) {
++        size_t n2 = rd->alloced_slices == 0 ? 128 : rd->alloced_slices * 2;
++        void * p2;
++        while (rd->num_offsets + n > n2)
++            n2 *= 2;
++        if ((p2 = av_realloc_array(rd->offsets, n2, sizeof(*rd->offsets))) == NULL)
++            return AVERROR(ENOMEM);
++        rd->offsets = p2;
++        rd->alloced_offsets = n2;
++    }
++    for (size_t i = 0; i != n; ++i)
++        rd->offsets[rd->num_offsets++] = offsets[i] - 1;
++    return 0;
++}
++
 +static unsigned int
 +fill_dpb_entries(const HEVCContext * const h, struct v4l2_hevc_dpb_entry * const entries)
 +{
@@ -50029,12 +55181,21 @@
 +            struct v4l2_hevc_dpb_entry * const entry = entries + n++;
 +
 +            entry->timestamp = frame_capture_dpb(frame->frame);
++#if HEVC_CTRLS_VERSION <= 2
 +            entry->rps = find_frame_rps_type(h, entry->timestamp);
++#else
++            entry->flags = (frame->flags & HEVC_FRAME_FLAG_LONG_REF) == 0 ? 0 :
++                V4L2_HEVC_DPB_ENTRY_LONG_TERM_REFERENCE;
++#endif
 +            entry->field_pic = frame->frame->interlaced_frame;
 +
++#if HEVC_CTRLS_VERSION <= 3
 +            /* TODO: Interleaved: Get the POC for each field. */
 +            entry->pic_order_cnt[0] = frame->poc;
 +            entry->pic_order_cnt[1] = frame->poc;
++#else
++            entry->pic_order_cnt_val = frame->poc;
++#endif
 +        }
 +    }
 +    return n;
@@ -50060,8 +55221,11 @@
 +
 +    *slice_params = (struct v4l2_ctrl_hevc_slice_params) {
 +        .bit_size = bit_size,
++#if HEVC_CTRLS_VERSION <= 3
 +        .data_bit_offset = bit_offset,
-+
++#else
++        .data_byte_offset = bit_offset / 8 + 1,
++#endif
 +        /* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
 +        .slice_segment_addr = sh->slice_segment_addr,
 +
@@ -50144,6 +55308,7 @@
 +    fill_pred_table(h, &slice_params->pred_weight_table);
 +
 +    slice_params->num_entry_point_offsets = sh->num_entry_point_offsets;
++#if HEVC_CTRLS_VERSION <= 3
 +    if (slice_params->num_entry_point_offsets > 256) {
 +        slice_params->num_entry_point_offsets = 256;
 +        av_log(NULL, AV_LOG_ERROR, "%s: Currently only 256 entry points are supported, but slice has %d entry points.\n", __func__, sh->num_entry_point_offsets);
@@ -50151,6 +55316,7 @@
 +
 +    for (i = 0; i < slice_params->num_entry_point_offsets; i++)
 +        slice_params->entry_point_offset_minus1[i] = sh->entry_point_offset[i] - 1;
++#endif
 +}
 +
 +#if HEVC_CTRLS_VERSION >= 2
@@ -50526,51 +55692,66 @@
 +#if HEVC_CTRLS_VERSION >= 2
 +    struct v4l2_ctrl_hevc_decode_params * const dec,
 +#endif
-+    struct v4l2_ctrl_hevc_slice_params * const slices,
-+    const unsigned int slice_no,
-+    const unsigned int slice_count)
++    struct v4l2_ctrl_hevc_slice_params * const slices, const unsigned int slice_count,
++    void * const offsets, const size_t offset_count)
 +{
 +    int rv;
++#if HEVC_CTRLS_VERSION >= 2
++    unsigned int n = 3;
++#else
++    unsigned int n = 2;
++#endif
 +
-+    struct v4l2_ext_control control[] = {
++    struct v4l2_ext_control control[6] = {
 +        {
-+            .id = V4L2_CID_MPEG_VIDEO_HEVC_SPS,
++            .id = V4L2_CID_STATELESS_HEVC_SPS,
 +            .ptr = &controls->sps,
 +            .size = sizeof(controls->sps),
 +        },
 +        {
-+            .id = V4L2_CID_MPEG_VIDEO_HEVC_PPS,
++            .id = V4L2_CID_STATELESS_HEVC_PPS,
 +            .ptr = &controls->pps,
 +            .size = sizeof(controls->pps),
 +        },
 +#if HEVC_CTRLS_VERSION >= 2
 +        {
-+            .id = V4L2_CID_MPEG_VIDEO_HEVC_DECODE_PARAMS,
++            .id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS,
 +            .ptr = dec,
 +            .size = sizeof(*dec),
 +        },
 +#endif
-+        {
-+            .id = V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS,
-+            .ptr = slices + slice_no,
-+            .size = sizeof(*slices) * slice_count,
-+        },
-+        // Optional
-+        {
-+            .id = V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX,
-+            .ptr = &controls->scaling_matrix,
-+            .size = sizeof(controls->scaling_matrix),
-+        },
 +    };
 +
-+    rv = mediabufs_ctl_set_ext_ctrls(ctx->mbufs, mreq, control,
-+            controls->has_scaling ?
-+                FF_ARRAY_ELEMS(control) :
-+                FF_ARRAY_ELEMS(control) - 1);
++    if (slices)
++        control[n++] = (struct v4l2_ext_control) {
++            .id = V4L2_CID_STATELESS_HEVC_SLICE_PARAMS,
++            .ptr = slices,
++            .size = sizeof(*slices) * slice_count,
++        };
++
++    if (controls->has_scaling)
++        control[n++] = (struct v4l2_ext_control) {
++            .id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX,
++            .ptr = &controls->scaling_matrix,
++            .size = sizeof(controls->scaling_matrix),
++        };
++
++#if HEVC_CTRLS_VERSION >= 4
++    if (offsets)
++        control[n++] = (struct v4l2_ext_control) {
++            .id = V4L2_CID_STATELESS_HEVC_ENTRY_POINT_OFFSETS,
++            .ptr = offsets,
++            .size = sizeof(((struct V4L2MediaReqDescriptor *)0)->offsets[0]) * offset_count,
++        };
++#endif
++
++    rv = mediabufs_ctl_set_ext_ctrls(ctx->mbufs, mreq, control, n);
 +
 +    return rv;
 +}
 +
++// This only works because we started out from a single coded frame buffer
++// that will remain intact until after end_frame
 +static int v4l2_request_hevc_decode_slice(AVCodecContext *avctx, const uint8_t *buffer, uint32_t size)
 +{
 +    const HEVCContext * const h = avctx->priv_data;
@@ -50579,18 +55760,45 @@
 +    int bcount = get_bits_count(&h->HEVClc->gb);
 +    uint32_t boff = (ptr_from_index(buffer, bcount/8 + 1) - (buffer + bcount/8 + 1)) * 8 + bcount;
 +
++    const unsigned int n = rd->num_slices;
++    const unsigned int block_start = (n / ctx->max_slices) * ctx->max_slices;
++
 +    int rv;
 +    struct slice_info * si;
 +
++    // This looks dodgy but we know that FFmpeg has parsed this from a buffer
++    // that contains the entire frame including the start code
++    if (ctx->start_code == V4L2_STATELESS_HEVC_START_CODE_ANNEX_B) {
++        buffer -= 3;
++        size += 3;
++        boff += 24;
++        if (buffer[0] != 0 || buffer[1] != 0 || buffer[2] != 1) {
++            av_log(avctx, AV_LOG_ERROR, "Start code requested but missing %02x:%02x:%02x\n",
++                   buffer[0], buffer[1], buffer[2]);
++        }
++    }
++
++    if (ctx->decode_mode == V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED) {
++        if (rd->slices == NULL) {
++            if ((rd->slices = av_mallocz(sizeof(*rd->slices))) == NULL)
++                return AVERROR(ENOMEM);
++            rd->slices->ptr = buffer;
++            rd->num_slices = 1;
++        }
++        rd->slices->len = buffer - rd->slices->ptr + size;
++        return 0;
++    }
++
 +    if ((rv = slice_add(rd)) != 0)
 +        return rv;
 +
-+    si = rd->slices + rd->num_slices - 1;
++    si = rd->slices + n;
 +    si->ptr = buffer;
 +    si->len = size;
++    si->n_offsets = rd->num_offsets;
 +
-+    if (ctx->multi_slice && rd->num_slices > 1) {
-+        struct slice_info *const si0 = rd->slices;
++    if (n != block_start) {
++        struct slice_info *const si0 = rd->slices + block_start;
 +        const size_t offset = (buffer - si0->ptr);
 +        boff += offset * 8;
 +        size += offset;
@@ -50598,12 +55806,15 @@
 +    }
 +
 +#if HEVC_CTRLS_VERSION >= 2
-+    if (rd->num_slices == 1)
++    if (n == 0)
 +        fill_decode_params(h, &rd->dec);
-+    fill_slice_params(h, &rd->dec, rd->slice_params + rd->num_slices - 1, size * 8, boff);
++    fill_slice_params(h, &rd->dec, rd->slice_params + n, size * 8, boff);
 +#else
-+    fill_slice_params(h, rd->slice_params + rd->num_slices - 1, size * 8, boff);
++    fill_slice_params(h, rd->slice_params + n, size * 8, boff);
 +#endif
++    if (ctx->max_offsets != 0 &&
++        (rv = offsets_add(rd, h->sh.num_entry_point_offsets, h->sh.entry_point_offset)) != 0)
++        return rv;
 +
 +    return 0;
 +}
@@ -50629,10 +55840,13 @@
 +{
 +    V4L2RequestContextHEVC * const ctx = avctx->internal->hwaccel_priv_data;
 +
++    const int is_last = (j == rd->num_slices);
 +    struct slice_info *const si = rd->slices + i;
 +    struct media_request * req = NULL;
 +    struct qent_src * src = NULL;
 +    MediaBufsStatus stat;
++    void * offsets = rd->offsets + rd->slices[i].n_offsets;
++    size_t n_offsets = (is_last ? rd->num_offsets : rd->slices[j].n_offsets) - rd->slices[i].n_offsets;
 +
 +    if ((req = media_request_get(ctx->mpool)) == NULL) {
 +        av_log(avctx, AV_LOG_ERROR, "%s: Failed to alloc media request\n", __func__);
@@ -50644,8 +55858,8 @@
 +#if HEVC_CTRLS_VERSION >= 2
 +                     &rd->dec,
 +#endif
-+                     rd->slice_params,
-+                     i, j - i)) {
++                     rd->slice_params + i, j - i,
++                     offsets, n_offsets)) {
 +        av_log(avctx, AV_LOG_ERROR, "%s: Failed to set req ctls\n", __func__);
 +        goto fail1;
 +    }
@@ -50665,13 +55879,9 @@
 +        goto fail2;
 +    }
 +
-+#warning ANNEX_B start code
-+//        if (ctx->start_code == V4L2_MPEG_VIDEO_HEVC_START_CODE_ANNEX_B) {
-+//        }
-+
 +    stat = mediabufs_start_request(ctx->mbufs, &req, &src,
 +                                   i == 0 ? rd->qe_dst : NULL,
-+                                   j == rd->num_slices);
++                                   is_last);
 +
 +    if (stat != MEDIABUFS_STATUS_SUCCESS) {
 +        av_log(avctx, AV_LOG_ERROR, "%s: Failed to start request\n", __func__);
@@ -50736,18 +55946,11 @@
 +    }
 +
 +    // Send as slices
-+    if (ctx->multi_slice)
-+    {
-+        if ((rv = send_slice(avctx, rd, &rc, 0, rd->num_slices)) != 0)
++    for (i = 0; i < rd->num_slices; i += ctx->max_slices) {
++        const unsigned int e = FFMIN(rd->num_slices, i + ctx->max_slices);
++        if ((rv = send_slice(avctx, rd, &rc, i, e)) != 0)
 +            goto fail;
 +    }
-+    else
-+    {
-+        for (i = 0; i != rd->num_slices; ++i) {
-+            if ((rv = send_slice(avctx, rd, &rc, i, i + 1)) != 0)
-+                goto fail;
-+        }
-+    }
 +
 +    // Set the drm_prime desriptor
 +    drm_from_format(&rd->drm, mediabufs_dst_fmt(ctx->mbufs));
@@ -50762,6 +55965,12 @@
 +    return rv;
 +}
 +
++static inline int
++ctrl_valid(const struct v4l2_query_ext_ctrl * const c, const int64_t v)
++{
++    return v >= c->minimum && v <= c->maximum;
++}
++
 +// Initial check & init
 +static int
 +probe(AVCodecContext * const avctx, V4L2RequestContextHEVC * const ctx)
@@ -50773,17 +55982,19 @@
 +
 +    // Check for var slice array
 +    struct v4l2_query_ext_ctrl qc[] = {
-+        { .id = V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS },
-+        { .id = V4L2_CID_MPEG_VIDEO_HEVC_SPS },
-+        { .id = V4L2_CID_MPEG_VIDEO_HEVC_PPS },
-+        { .id = V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX },
++        { .id = V4L2_CID_STATELESS_HEVC_SLICE_PARAMS },
++        { .id = V4L2_CID_STATELESS_HEVC_DECODE_MODE, },
++        { .id = V4L2_CID_STATELESS_HEVC_SPS },
++        { .id = V4L2_CID_STATELESS_HEVC_PPS },
++        { .id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX },
 +#if HEVC_CTRLS_VERSION >= 2
-+        { .id = V4L2_CID_MPEG_VIDEO_HEVC_DECODE_PARAMS },
++        { .id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS },
 +#endif
 +    };
 +    // Order & size must match!
 +    static const size_t ctrl_sizes[] = {
 +        sizeof(struct v4l2_ctrl_hevc_slice_params),
++        sizeof(int32_t),
 +        sizeof(struct v4l2_ctrl_hevc_sps),
 +        sizeof(struct v4l2_ctrl_hevc_pps),
 +        sizeof(struct v4l2_ctrl_hevc_scaling_matrix),
@@ -50793,26 +56004,44 @@
 +    };
 +    const unsigned int noof_ctrls = FF_ARRAY_ELEMS(qc);
 +
-+    if (mediabufs_ctl_query_ext_ctrls(ctx->mbufs, qc, noof_ctrls)) {
-+        av_log(avctx, AV_LOG_DEBUG, "Probed V%d control missing\n", HEVC_CTRLS_VERSION);
++#if HEVC_CTRLS_VERSION == 2
++    if (mediabufs_ctl_driver_version(ctx->mbufs) >= MEDIABUFS_DRIVER_VERSION(5, 18, 0))
 +        return AVERROR(EINVAL);
-+    }
-+    for (i = 0; i != noof_ctrls; ++i) {
-+        if (ctrl_sizes[i] != qc[i].elem_size) {
-+            av_log(avctx, AV_LOG_DEBUG, "Probed V%d control %d size mismatch %u != %u\n",
-+                   HEVC_CTRLS_VERSION, i, ctrl_sizes[i], qc[i].elem_size);
++#elif HEVC_CTRLS_VERSION == 3
++    if (mediabufs_ctl_driver_version(ctx->mbufs) < MEDIABUFS_DRIVER_VERSION(5, 18, 0))
++        return AVERROR(EINVAL);
++#endif
++
++    mediabufs_ctl_query_ext_ctrls(ctx->mbufs, qc, noof_ctrls);
++    i = 0;
++#if HEVC_CTRLS_VERSION >= 4
++    // Skip slice check if no slice mode
++    if (qc[1].type != 0 && !ctrl_valid(qc + 1, V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED))
++        i = 1;
++#else
++    // Fail frame mode silently for anything prior to V4
++    if (qc[1].type == 0 || !ctrl_valid(qc + 1, V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED))
++        return AVERROR(EINVAL);
++#endif
++    for (; i != noof_ctrls; ++i) {
++        if (qc[i].type == 0) {
++            av_log(avctx, AV_LOG_DEBUG, "Probed V%d control %#x missing\n", HEVC_CTRLS_VERSION, qc[i].id);
++            return AVERROR(EINVAL);
++        }
++        if (ctrl_sizes[i] != (size_t)qc[i].elem_size) {
++            av_log(avctx, AV_LOG_DEBUG, "Probed V%d control %d size mismatch %zu != %zu\n",
++                   HEVC_CTRLS_VERSION, i, ctrl_sizes[i], (size_t)qc[i].elem_size);
 +            return AVERROR(EINVAL);
 +        }
 +    }
 +
 +    fill_sps(&ctrl_sps, sps);
 +
-+    if (mediabufs_set_ext_ctrl(ctx->mbufs, NULL, V4L2_CID_MPEG_VIDEO_HEVC_SPS, &ctrl_sps, sizeof(ctrl_sps))) {
++    if (mediabufs_set_ext_ctrl(ctx->mbufs, NULL, V4L2_CID_STATELESS_HEVC_SPS, &ctrl_sps, sizeof(ctrl_sps))) {
 +        av_log(avctx, AV_LOG_ERROR, "Failed to set initial SPS\n");
 +        return AVERROR(EINVAL);
 +    }
 +
-+    ctx->multi_slice = (qc[0].flags & V4L2_CTRL_FLAG_DYNAMIC_ARRAY) != 0;
 +    return 0;
 +}
 +
@@ -50823,38 +56052,63 @@
 +    int ret;
 +
 +    struct v4l2_query_ext_ctrl querys[] = {
-+        { .id = V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE, },
-+        { .id = V4L2_CID_MPEG_VIDEO_HEVC_START_CODE, },
-+        { .id = V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS, },
++        { .id = V4L2_CID_STATELESS_HEVC_DECODE_MODE, },
++        { .id = V4L2_CID_STATELESS_HEVC_START_CODE, },
++        { .id = V4L2_CID_STATELESS_HEVC_SLICE_PARAMS, },
++#if HEVC_CTRLS_VERSION >= 4
++        { .id = V4L2_CID_STATELESS_HEVC_ENTRY_POINT_OFFSETS, },
++#endif
 +    };
 +
 +    struct v4l2_ext_control ctrls[] = {
-+        { .id = V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE, },
-+        { .id = V4L2_CID_MPEG_VIDEO_HEVC_START_CODE, },
++        { .id = V4L2_CID_STATELESS_HEVC_DECODE_MODE, },
++        { .id = V4L2_CID_STATELESS_HEVC_START_CODE, },
 +    };
 +
 +    mediabufs_ctl_query_ext_ctrls(ctx->mbufs, querys, FF_ARRAY_ELEMS(querys));
 +
-+    ctx->decode_mode = querys[0].default_value;
++    ctx->max_slices = (!(querys[2].flags & V4L2_CTRL_FLAG_DYNAMIC_ARRAY) ||
++                       querys[2].nr_of_dims != 1 || querys[2].dims[0] == 0) ?
++        1 : querys[2].dims[0];
++    av_log(avctx, AV_LOG_DEBUG, "%s: Max slices %d\n", __func__, ctx->max_slices);
 +
-+    if (ctx->decode_mode != V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED &&
-+        ctx->decode_mode != V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_FRAME_BASED) {
-+        av_log(avctx, AV_LOG_ERROR, "%s: unsupported decode mode, %d\n", __func__, ctx->decode_mode);
++#if HEVC_CTRLS_VERSION >= 4
++    ctx->max_offsets = (querys[3].type == 0 || querys[3].nr_of_dims != 1) ?
++        0 : querys[3].dims[0];
++    av_log(avctx, AV_LOG_DEBUG, "%s: Entry point offsets %d\n", __func__, ctx->max_offsets);
++#else
++    ctx->max_offsets = 0;
++#endif
++
++    if (querys[0].default_value == V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED ||
++        querys[0].default_value == V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED)
++        ctx->decode_mode = querys[0].default_value;
++    else if (ctrl_valid(querys + 0, V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED))
++        ctx->decode_mode = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED;
++    else if (ctrl_valid(querys + 0, V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED))
++        ctx->decode_mode = V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED;
++    else {
++        av_log(avctx, AV_LOG_ERROR, "%s: unsupported decode mode\n", __func__);
 +        return AVERROR(EINVAL);
 +    }
 +
-+    ctx->start_code = querys[1].default_value;
-+    if (ctx->start_code != V4L2_MPEG_VIDEO_HEVC_START_CODE_NONE &&
-+        ctx->start_code != V4L2_MPEG_VIDEO_HEVC_START_CODE_ANNEX_B) {
-+        av_log(avctx, AV_LOG_ERROR, "%s: unsupported start code, %d\n", __func__, ctx->start_code);
++    if (querys[1].default_value == V4L2_STATELESS_HEVC_START_CODE_NONE ||
++        querys[1].default_value == V4L2_STATELESS_HEVC_START_CODE_ANNEX_B)
++        ctx->start_code = querys[1].default_value;
++    else if (ctrl_valid(querys + 1, V4L2_STATELESS_HEVC_START_CODE_ANNEX_B))
++        ctx->start_code = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B;
++    else if (ctrl_valid(querys + 1, V4L2_STATELESS_HEVC_START_CODE_NONE))
++        ctx->start_code = V4L2_STATELESS_HEVC_START_CODE_NONE;
++    else {
++        av_log(avctx, AV_LOG_ERROR, "%s: unsupported start code\n", __func__);
 +        return AVERROR(EINVAL);
 +    }
 +
-+    ctx->max_slices = querys[2].elems;
-+    if (ctx->max_slices > MAX_SLICES) {
-+        av_log(avctx, AV_LOG_ERROR, "%s: unsupported max slices, %d\n", __func__, ctx->max_slices);
-+        return AVERROR(EINVAL);
-+    }
++    // If we are in slice mode & START_CODE_NONE supported then pick that
++    // as it doesn't require the slightly dodgy look backwards in our raw buffer
++    if (ctx->decode_mode == V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED &&
++        ctrl_valid(querys + 1, V4L2_STATELESS_HEVC_START_CODE_NONE))
++        ctx->start_code = V4L2_STATELESS_HEVC_START_CODE_NONE;
 +
 +    ctrls[0].value = ctx->decode_mode;
 +    ctrls[1].value = ctx->start_code;
@@ -50878,6 +56132,7 @@
 +
 +    av_freep(&rd->slices);
 +    av_freep(&rd->slice_params);
++    av_freep(&rd->offsets);
 +
 +    av_free(rd);
 +}
@@ -50904,6 +56159,7 @@
 +    return ref;
 +}
 +
++#if 0
 +static void v4l2_req_pool_free(void *opaque)
 +{
 +    av_log(NULL, AV_LOG_DEBUG, "%s: opaque=%p\n", __func__, opaque);
@@ -50915,6 +56171,7 @@
 +
 +    av_buffer_pool_uninit(&hwfc->pool);
 +}
++#endif
 +
 +static int frame_params(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx)
 +{
@@ -50931,7 +56188,7 @@
 +        hwfc->width = vfmt->fmt.pix.width;
 +        hwfc->height = vfmt->fmt.pix.height;
 +    }
-+
++#if 0
 +    hwfc->pool = av_buffer_pool_init2(sizeof(V4L2MediaReqDescriptor), avctx, v4l2_req_frame_alloc, v4l2_req_pool_free);
 +    if (!hwfc->pool)
 +        return AVERROR(ENOMEM);
@@ -50950,12 +56207,32 @@
 +    default:
 +        hwfc->initial_pool_size += 2;
 +    }
-+
++#endif
 +    av_log(avctx, AV_LOG_DEBUG, "%s: avctx=%p ctx=%p hw_frames_ctx=%p hwfc=%p pool=%p width=%d height=%d initial_pool_size=%d\n", __func__, avctx, ctx, hw_frames_ctx, hwfc, hwfc->pool, hwfc->width, hwfc->height, hwfc->initial_pool_size);
 +
 +    return 0;
 +}
 +
++static int alloc_frame(AVCodecContext * avctx, AVFrame *frame)
++{
++    int rv;
++
++    frame->buf[0] = v4l2_req_frame_alloc(avctx, sizeof(V4L2MediaReqDescriptor));
++    if (!frame->buf[0])
++        return AVERROR(ENOMEM);
++
++    frame->data[0] = frame->buf[0]->data;
++
++    frame->hw_frames_ctx = av_buffer_ref(avctx->hw_frames_ctx);
++
++    if ((rv = ff_attach_decode_data(frame)) != 0) {
++        av_log(avctx, AV_LOG_ERROR, "Failed to attach decode data to frame\n");
++        av_frame_unref(frame);
++        return rv;
++    }
++
++    return 0;
++}
 +
 +const v4l2_req_decode_fns V(ff_v4l2_req_hevc) = {
 +    .src_pix_fmt_v4l2 = V4L2_PIX_FMT_HEVC_SLICE,
@@ -50968,11 +56245,12 @@
 +    .end_frame      = v4l2_request_hevc_end_frame,
 +    .abort_frame    = v4l2_request_hevc_abort_frame,
 +    .frame_params   = frame_params,
++    .alloc_frame    = alloc_frame,
 +};
 +
 --- /dev/null
 +++ b/libavcodec/v4l2_req_media.c
-@@ -0,0 +1,1569 @@
+@@ -0,0 +1,1601 @@
 +/*
 + * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
 + *
@@ -51388,7 +56666,7 @@
 +    free(be_dst);
 +}
 +
-+static struct qent_dst * qe_dst_new(void)
++static struct qent_dst* qe_dst_new(struct ff_weak_link_master * const wl)
 +{
 +    struct qent_dst *const be_dst = malloc(sizeof(*be_dst));
 +    if (!be_dst)
@@ -51396,7 +56674,8 @@
 +    *be_dst = (struct qent_dst){
 +        .base = QENT_BASE_INITIALIZER,
 +        .lock = PTHREAD_MUTEX_INITIALIZER,
-+        .cond = PTHREAD_COND_INITIALIZER
++        .cond = PTHREAD_COND_INITIALIZER,
++        .mbc_wl = ff_weak_link_ref(wl)
 +    };
 +    return be_dst;
 +}
@@ -51568,6 +56847,7 @@
 +    int vfd;
 +    bool stream_on;
 +    bool polling;
++    bool dst_fixed;             // Dst Q is fixed size
 +    pthread_mutex_t lock;
 +    struct buf_pool * src;
 +    struct buf_pool * dst;
@@ -51577,6 +56857,7 @@
 +
 +    struct v4l2_format src_fmt;
 +    struct v4l2_format dst_fmt;
++    struct v4l2_capability capability;
 +};
 +
 +static int qe_v4l2_queue(struct qent_base *const be,
@@ -51747,13 +57028,13 @@
 +{
 +    if (!be->dh[0] || len > dmabuf_size(be->dh[0])) {
 +        size_t newsize = round_up_size(len);
-+        request_log("%s: Overrun %d > %d; trying %d\n", __func__, len, dmabuf_size(be->dh[0]), newsize);
++        request_log("%s: Overrun %zd > %zd; trying %zd\n", __func__, len, dmabuf_size(be->dh[0]), newsize);
 +        if (!dbsc) {
 +            request_log("%s: No dmbabuf_ctrl for realloc\n", __func__);
 +            return -ENOMEM;
 +        }
 +        if ((be->dh[0] = dmabuf_realloc(dbsc, be->dh[0], newsize)) == NULL) {
-+            request_log("%s: Realloc %d failed\n", __func__, newsize);
++            request_log("%s: Realloc %zd failed\n", __func__, newsize);
 +            return -ENOMEM;
 +        }
 +    }
@@ -52069,10 +57350,13 @@
 +    return MEDIABUFS_STATUS_SUCCESS;
 +}
 +
-+static int create_dst_buf(struct mediabufs_ctl *const mbc)
++// Returns noof buffers created, -ve for error
++static int create_dst_bufs(struct mediabufs_ctl *const mbc, unsigned int n, struct qent_dst * const qes[])
 +{
++    unsigned int i;
++
 +    struct v4l2_create_buffers cbuf = {
-+        .count = 1,
++        .count = n,
 +        .memory = V4L2_MEMORY_DMABUF,
 +        .format = mbc->dst_fmt,
 +    };
@@ -52084,7 +57368,14 @@
 +            return -err;
 +        }
 +    }
-+    return cbuf.index;
++
++    if (cbuf.count != n)
++        request_warn(mbc->dc, "%s: Created %d of %d V4L2 buffers requested\n", __func__, cbuf.count, n);
++
++    for (i = 0; i != cbuf.count; ++i)
++        qes[i]->base.index = cbuf.index + i;
++
++    return cbuf.count;
 +}
 +
 +struct qent_dst* mediabufs_dst_qent_alloc(struct mediabufs_ctl *const mbc, struct dmabufs_ctl *const dbsc)
@@ -52092,27 +57383,29 @@
 +    struct qent_dst * be_dst;
 +
 +    if (mbc == NULL) {
-+        be_dst = qe_dst_new();
++        be_dst = qe_dst_new(NULL);
 +        if (be_dst)
 +            be_dst->base.status = QENT_IMPORT;
 +        return be_dst;
 +    }
 +
-+    be_dst = base_to_dst(queue_tryget_free(mbc->dst));
-+    if (!be_dst) {
-+        int index;
-+
-+        be_dst = qe_dst_new();
++    if (mbc->dst_fixed) {
++        be_dst = base_to_dst(queue_get_free(mbc->dst));
 +        if (!be_dst)
 +            return NULL;
++    }
++    else {
++        be_dst = base_to_dst(queue_tryget_free(mbc->dst));
++        if (!be_dst) {
++            be_dst = qe_dst_new(mbc->this_wlm);
++            if (!be_dst)
++                return NULL;
 +
-+        if ((be_dst->mbc_wl = ff_weak_link_ref(mbc->this_wlm)) == NULL ||
-+            (index = create_dst_buf(mbc)) < 0) {
-+            qe_dst_free(be_dst);
-+            return NULL;
++            if (create_dst_bufs(mbc, 1, &be_dst) != 1) {
++                qe_dst_free(be_dst);
++                return NULL;
++            }
 +        }
-+
-+        be_dst->base.index = (uint32_t)index;
 +    }
 +
 +    if (qe_alloc_from_fmt(&be_dst->base, dbsc, &mbc->dst_fmt)) {
@@ -52166,29 +57459,42 @@
 +    return status;
 +}
 +
-+MediaBufsStatus mediabufs_dst_slots_create(struct mediabufs_ctl *const mbc, unsigned int n)
++// ** This is a mess if we get partial alloc but without any way to remove
++//    individual V4L2 Q members we are somewhat stuffed
++MediaBufsStatus mediabufs_dst_slots_create(struct mediabufs_ctl *const mbc, const unsigned int n, const bool fixed)
 +{
-+    // **** request buffers
 +    unsigned int i;
++    int a = 0;
++    unsigned int qc;
++    struct qent_dst * qes[32];
 +
-+    for (i = 0; i != n; ++i)
++    if (n > 32)
++        return MEDIABUFS_ERROR_ALLOCATION_FAILED;
++
++    // Create qents first as it is hard to get rid of the V4L2 buffers on error
++    for (qc = 0; qc != n; ++qc)
 +    {
-+        int index;
-+        struct qent_dst * const be_dst = qe_dst_new();
-+        if (!be_dst)
-+            return MEDIABUFS_ERROR_OPERATION_FAILED;
-+
-+        index = create_dst_buf(mbc);
-+        if (index < 0) {
-+            qe_dst_free(be_dst);
-+            return MEDIABUFS_ERROR_OPERATION_FAILED;
-+        }
-+
-+        // Add index to free chain
-+        be_dst->base.index = (uint32_t)index;
-+        queue_put_free(mbc->dst, &be_dst->base);
++        if ((qes[qc] = qe_dst_new(mbc->this_wlm)) == NULL)
++            goto fail;
 +    }
++
++    if ((a = create_dst_bufs(mbc, n, qes)) < 0)
++        goto fail;
++
++    for (i = 0; i != a; ++i)
++        queue_put_free(mbc->dst, &qes[i]->base);
++
++    if (a != n)
++        goto fail;
++
++    mbc->dst_fixed = fixed;
 +    return MEDIABUFS_STATUS_SUCCESS;
++
++fail:
++    for (i = (a < 0 ? 0 : a); i != qc; ++i)
++        qe_dst_free(qes[i]);
++
++    return MEDIABUFS_ERROR_ALLOCATION_FAILED;
 +}
 +
 +struct qent_src *mediabufs_src_qent_get(struct mediabufs_ctl *const mbc)
@@ -52446,20 +57752,24 @@
 +    mediabufs_ctl_delete(mbc);
 +}
 +
++unsigned int mediabufs_ctl_driver_version(struct mediabufs_ctl *const mbc)
++{
++    return mbc->capability.version;
++}
++
 +static int set_capabilities(struct mediabufs_ctl *const mbc)
 +{
-+    struct v4l2_capability capability = { 0 };
 +    uint32_t caps;
 +
-+    if (ioctl(mbc->vfd, VIDIOC_QUERYCAP, &capability)) {
++    if (ioctl(mbc->vfd, VIDIOC_QUERYCAP, &mbc->capability)) {
 +        int err = errno;
 +        request_err(mbc->dc, "Failed to get capabilities: %s\n", strerror(err));
 +        return -err;
 +    }
 +
-+    caps = (capability.capabilities & V4L2_CAP_DEVICE_CAPS) != 0 ?
-+            capability.device_caps :
-+            capability.capabilities;
++    caps = (mbc->capability.capabilities & V4L2_CAP_DEVICE_CAPS) != 0 ?
++            mbc->capability.device_caps :
++            mbc->capability.capabilities;
 +
 +    if ((caps & V4L2_CAP_VIDEO_M2M_MPLANE) != 0) {
 +        mbc->src_fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
@@ -52544,7 +57854,7 @@
 +
 --- /dev/null
 +++ b/libavcodec/v4l2_req_media.h
-@@ -0,0 +1,148 @@
+@@ -0,0 +1,154 @@
 +/*
 +e.h
 +*
@@ -52646,11 +57956,14 @@
 +                struct qent_dst *const dst_be,
 +                const bool is_final);
 +// Get / alloc a dst buffer & associate with a slot
-+// * BEWARE * Currently has no alloc limit
++// If the dst pool is empty then behaviour depends on the fixed flag passed to
++// dst_slots_create.  Default is !fixed = unlimited alloc
 +struct qent_dst* mediabufs_dst_qent_alloc(struct mediabufs_ctl *const mbc,
 +                           struct dmabufs_ctl *const dbsc);
 +// Create dst slots without alloc
-+MediaBufsStatus mediabufs_dst_slots_create(struct mediabufs_ctl *const mbc, unsigned int n);
++// If fixed true then qent_alloc will only get slots from this pool and will
++// block until a qent has been unrefed
++MediaBufsStatus mediabufs_dst_slots_create(struct mediabufs_ctl *const mbc, const unsigned int n, const bool fixed);
 +
 +MediaBufsStatus mediabufs_stream_on(struct mediabufs_ctl *const mbc);
 +MediaBufsStatus mediabufs_stream_off(struct mediabufs_ctl *const mbc);
@@ -52686,6 +57999,9 @@
 +                  struct dmabufs_ctl * const dbsc,
 +                  unsigned int n);
 +
++#define MEDIABUFS_DRIVER_VERSION(a, b, c) (((a) << 16) | ((b) << 8) | (c))
++unsigned int mediabufs_ctl_driver_version(struct mediabufs_ctl *const mbc);
++
 +struct mediabufs_ctl * mediabufs_ctl_new(void * const dc,
 +                     const char *vpath, struct pollqueue *const pq);
 +void mediabufs_ctl_unref(struct mediabufs_ctl **const pmbc);
@@ -52695,7 +58011,7 @@
 +#endif
 --- /dev/null
 +++ b/libavcodec/v4l2_req_pollqueue.c
-@@ -0,0 +1,363 @@
+@@ -0,0 +1,361 @@
 +#include <errno.h>
 +#include <limits.h>
 +#include <poll.h>
@@ -52885,19 +58201,19 @@
 +        unsigned int i;
 +        unsigned int n = 0;
 +        struct polltask *pt;
++        struct polltask *pt_next;
 +        uint64_t now = pollqueue_now(0);
 +        int timeout = -1;
 +        int rv;
 +
-+        for (pt = pq->head; pt; pt = pt->next) {
++        for (pt = pq->head; pt; pt = pt_next) {
 +            int64_t t;
 +
++            pt_next = pt->next;
++
 +            if (pt->state == POLLTASK_Q_KILL) {
-+                struct polltask * const prev = pt->prev;
 +                pollqueue_rem_task(pq, pt);
 +                sem_post(&pt->kill_sem);
-+                if ((pt = prev) == NULL)
-+                    break;
 +                continue;
 +            }
 +
@@ -52936,8 +58252,8 @@
 +         * infinite looping
 +        */
 +        pq->no_prod = true;
-+        for (i = 0, pt = pq->head; i < n; ++i) {
-+            struct polltask *const pt_next = pt->next;
++        for (i = 0, pt = pq->head; i < n; ++i, pt = pt_next) {
++            pt_next = pt->next;
 +
 +            /* Pending? */
 +            if (a[i].revents ||
@@ -52961,8 +58277,6 @@
 +                if (pt->state == POLLTASK_RUN_KILL)
 +                    sem_post(&pt->kill_sem);
 +            }
-+
-+            pt = pt_next;
 +        }
 +        pq->no_prod = false;
 +
@@ -53082,12 +58396,13 @@
 +#endif /* POLLQUEUE_H_ */
 --- /dev/null
 +++ b/libavcodec/v4l2_req_utils.h
-@@ -0,0 +1,21 @@
+@@ -0,0 +1,22 @@
 +#include "libavutil/log.h"
 +
 +#define request_log(...) av_log(NULL, AV_LOG_INFO, __VA_ARGS__)
 +
 +#define request_err(_ctx, ...) av_log(_ctx, AV_LOG_ERROR, __VA_ARGS__)
++#define request_warn(_ctx, ...) av_log(_ctx, AV_LOG_WARNING, __VA_ARGS__)
 +#define request_info(_ctx, ...) av_log(_ctx, AV_LOG_INFO, __VA_ARGS__)
 +#define request_debug(_ctx, ...) av_log(_ctx, AV_LOG_DEBUG, __VA_ARGS__)
 +
@@ -53106,7 +58421,7 @@
 +
 --- /dev/null
 +++ b/libavcodec/v4l2_request_hevc.c
-@@ -0,0 +1,280 @@
+@@ -0,0 +1,315 @@
 +/*
 + * This file is part of FFmpeg.
 + *
@@ -53194,6 +58509,13 @@
 +    return ctx->fns->frame_params(avctx, hw_frames_ctx);
 +}
 +
++static int v4l2_req_hevc_alloc_frame(AVCodecContext * avctx, AVFrame *frame)
++{
++    V4L2RequestContextHEVC * const ctx = avctx->internal->hwaccel_priv_data;
++    return ctx->fns->alloc_frame(avctx, frame);
++}
++
++
 +static int v4l2_request_hevc_uninit(AVCodecContext *avctx)
 +{
 +    V4L2RequestContextHEVC * const ctx = avctx->internal->hwaccel_priv_data;
@@ -53248,6 +58570,17 @@
 +
 +    av_log(avctx, AV_LOG_DEBUG, "<<< %s\n", __func__);
 +
++    // Give up immediately if this is something that we have no code to deal with
++    if (h->ps.sps->chroma_format_idc != 1) {
++        av_log(avctx, AV_LOG_WARNING, "chroma_format_idc(%d) != 1: Not implemented\n", h->ps.sps->chroma_format_idc);
++        return AVERROR_PATCHWELCOME;
++    }
++    if (!(h->ps.sps->bit_depth == 10 || h->ps.sps->bit_depth == 8) ||
++        h->ps.sps->bit_depth != h->ps.sps->bit_depth_chroma) {
++        av_log(avctx, AV_LOG_WARNING, "Bit depth Y:%d C:%d: Not implemented\n", h->ps.sps->bit_depth, h->ps.sps->bit_depth_chroma);
++        return AVERROR_PATCHWELCOME;
++    }
++
 +    if ((ret = devscan_build(avctx, &ctx->devscan)) != 0) {
 +        av_log(avctx, AV_LOG_WARNING, "Failed to find any V4L2 devices\n");
 +        return (AVERROR(-ret));
@@ -53300,7 +58633,15 @@
 +        goto fail4;
 +    }
 +
-+    if (V2(ff_v4l2_req_hevc, 2).probe(avctx, ctx) == 0) {
++    if (V2(ff_v4l2_req_hevc, 4).probe(avctx, ctx) == 0) {
++        av_log(avctx, AV_LOG_DEBUG, "HEVC API version 4 probed successfully\n");
++        ctx->fns = &V2(ff_v4l2_req_hevc, 4);
++    }
++    else if (V2(ff_v4l2_req_hevc, 3).probe(avctx, ctx) == 0) {
++        av_log(avctx, AV_LOG_DEBUG, "HEVC API version 3 probed successfully\n");
++        ctx->fns = &V2(ff_v4l2_req_hevc, 3);
++    }
++    else if (V2(ff_v4l2_req_hevc, 2).probe(avctx, ctx) == 0) {
 +        av_log(avctx, AV_LOG_DEBUG, "HEVC API version 2 probed successfully\n");
 +        ctx->fns = &V2(ff_v4l2_req_hevc, 2);
 +    }
@@ -53325,9 +58666,18 @@
 +        goto fail4;
 +    }
 +
-+    if (mediabufs_dst_slots_create(ctx->mbufs, 1)) {
-+        av_log(avctx, AV_LOG_ERROR, "Failed to create destination slots\n");
-+        goto fail4;
++    {
++        unsigned int dst_slots = sps->temporal_layer[sps->max_sub_layers - 1].max_dec_pic_buffering +
++            avctx->thread_count + (avctx->extra_hw_frames > 0 ? avctx->extra_hw_frames : 6);
++        av_log(avctx, AV_LOG_DEBUG, "Slots=%d: Reordering=%d, threads=%d, hw+=%d\n", dst_slots,
++               sps->temporal_layer[sps->max_sub_layers - 1].max_dec_pic_buffering,
++               avctx->thread_count, avctx->extra_hw_frames);
++
++        // extra_hw_frames is -1 if unset
++        if (mediabufs_dst_slots_create(ctx->mbufs, dst_slots, (avctx->extra_hw_frames > 0))) {
++            av_log(avctx, AV_LOG_ERROR, "Failed to create destination slots\n");
++            goto fail4;
++        }
 +    }
 +
 +    if (mediabufs_stream_on(ctx->mbufs)) {
@@ -53376,7 +58726,7 @@
 +    .type           = AVMEDIA_TYPE_VIDEO,
 +    .id             = AV_CODEC_ID_HEVC,
 +    .pix_fmt        = AV_PIX_FMT_DRM_PRIME,
-+//    .alloc_frame    = v4l2_request_hevc_alloc_frame,
++    .alloc_frame    = v4l2_req_hevc_alloc_frame,
 +    .start_frame    = v4l2_req_hevc_start_frame,
 +    .decode_slice   = v4l2_req_hevc_decode_slice,
 +    .end_frame      = v4l2_req_hevc_end_frame,
@@ -53389,7 +58739,7 @@
 +};
 --- /dev/null
 +++ b/libavcodec/v4l2_request_hevc.h
-@@ -0,0 +1,100 @@
+@@ -0,0 +1,101 @@
 +#ifndef AVCODEC_V4L2_REQUEST_HEVC_H
 +#define AVCODEC_V4L2_REQUEST_HEVC_H
 +
@@ -53437,8 +58787,6 @@
 +#define V4L2_CTRL_FLAG_DYNAMIC_ARRAY	0x0800
 +#endif
 +
-+#define MAX_SLICES 128
-+
 +#define VCAT(name, version) name##_v##version
 +#define V2(n,v) VCAT(n, v)
 +#define V(n) V2(n, HEVC_CTRLS_VERSION)
@@ -53455,10 +58803,10 @@
 +
 +    unsigned int timestamp;  // ?? maybe uint64_t
 +
-+    int multi_slice;
 +    int decode_mode;
 +    int start_code;
-+    int max_slices;
++    unsigned int max_slices;    // 0 => not wanted (frame mode)
++    unsigned int max_offsets;   // 0 => not wanted
 +
 +    req_decode_q decode_q;
 +
@@ -53483,16 +58831,121 @@
 +    int (*end_frame)(AVCodecContext *avctx);
 +    void (*abort_frame)(AVCodecContext *avctx);
 +    int (*frame_params)(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx);
++    int (*alloc_frame)(AVCodecContext * avctx, AVFrame *frame);
 +} v4l2_req_decode_fns;
 +
 +
 +extern const v4l2_req_decode_fns V2(ff_v4l2_req_hevc, 1);
 +extern const v4l2_req_decode_fns V2(ff_v4l2_req_hevc, 2);
++extern const v4l2_req_decode_fns V2(ff_v4l2_req_hevc, 3);
++extern const v4l2_req_decode_fns V2(ff_v4l2_req_hevc, 4);
 +
 +#endif
+--- a/libavcodec/vc1dec.c
++++ b/libavcodec/vc1dec.c
+@@ -486,7 +486,7 @@ static av_cold int vc1_decode_init(AVCod
+             size = next - start - 4;
+             if (size <= 0)
+                 continue;
+-            buf2_size = vc1_unescape_buffer(start + 4, size, buf2);
++            buf2_size = v->vc1dsp.vc1_unescape_buffer(start + 4, size, buf2);
+             init_get_bits(&gb, buf2, buf2_size * 8);
+             switch (AV_RB32(start)) {
+             case VC1_CODE_SEQHDR:
+@@ -689,7 +689,7 @@ static int vc1_decode_frame(AVCodecConte
+                 case VC1_CODE_FRAME:
+                     if (avctx->hwaccel)
+                         buf_start = start;
+-                    buf_size2 = vc1_unescape_buffer(start + 4, size, buf2);
++                    buf_size2 = v->vc1dsp.vc1_unescape_buffer(start + 4, size, buf2);
+                     break;
+                 case VC1_CODE_FIELD: {
+                     int buf_size3;
+@@ -706,8 +706,8 @@ static int vc1_decode_frame(AVCodecConte
+                         ret = AVERROR(ENOMEM);
+                         goto err;
+                     }
+-                    buf_size3 = vc1_unescape_buffer(start + 4, size,
+-                                                    slices[n_slices].buf);
++                    buf_size3 = v->vc1dsp.vc1_unescape_buffer(start + 4, size,
++                                                              slices[n_slices].buf);
+                     init_get_bits(&slices[n_slices].gb, slices[n_slices].buf,
+                                   buf_size3 << 3);
+                     slices[n_slices].mby_start = avctx->coded_height + 31 >> 5;
+@@ -718,7 +718,7 @@ static int vc1_decode_frame(AVCodecConte
+                     break;
+                 }
+                 case VC1_CODE_ENTRYPOINT: /* it should be before frame data */
+-                    buf_size2 = vc1_unescape_buffer(start + 4, size, buf2);
++                    buf_size2 = v->vc1dsp.vc1_unescape_buffer(start + 4, size, buf2);
+                     init_get_bits(&s->gb, buf2, buf_size2 * 8);
+                     ff_vc1_decode_entry_point(avctx, v, &s->gb);
+                     break;
+@@ -735,8 +735,8 @@ static int vc1_decode_frame(AVCodecConte
+                         ret = AVERROR(ENOMEM);
+                         goto err;
+                     }
+-                    buf_size3 = vc1_unescape_buffer(start + 4, size,
+-                                                    slices[n_slices].buf);
++                    buf_size3 = v->vc1dsp.vc1_unescape_buffer(start + 4, size,
++                                                              slices[n_slices].buf);
+                     init_get_bits(&slices[n_slices].gb, slices[n_slices].buf,
+                                   buf_size3 << 3);
+                     slices[n_slices].mby_start = get_bits(&slices[n_slices].gb, 9);
+@@ -770,7 +770,7 @@ static int vc1_decode_frame(AVCodecConte
+                     ret = AVERROR(ENOMEM);
+                     goto err;
+                 }
+-                buf_size3 = vc1_unescape_buffer(divider + 4, buf + buf_size - divider - 4, slices[n_slices].buf);
++                buf_size3 = v->vc1dsp.vc1_unescape_buffer(divider + 4, buf + buf_size - divider - 4, slices[n_slices].buf);
+                 init_get_bits(&slices[n_slices].gb, slices[n_slices].buf,
+                               buf_size3 << 3);
+                 slices[n_slices].mby_start = s->mb_height + 1 >> 1;
+@@ -779,9 +779,9 @@ static int vc1_decode_frame(AVCodecConte
+                 n_slices1 = n_slices - 1;
+                 n_slices++;
+             }
+-            buf_size2 = vc1_unescape_buffer(buf, divider - buf, buf2);
++            buf_size2 = v->vc1dsp.vc1_unescape_buffer(buf, divider - buf, buf2);
+         } else {
+-            buf_size2 = vc1_unescape_buffer(buf, buf_size, buf2);
++            buf_size2 = v->vc1dsp.vc1_unescape_buffer(buf, buf_size, buf2);
+         }
+         init_get_bits(&s->gb, buf2, buf_size2*8);
+     } else
+--- a/libavcodec/vc1dsp.c
++++ b/libavcodec/vc1dsp.c
+@@ -32,6 +32,7 @@
+ #include "rnd_avg.h"
+ #include "vc1dsp.h"
+ #include "startcode.h"
++#include "vc1_common.h"
+ 
+ /* Apply overlap transform to horizontal edge */
+ static void vc1_v_overlap_c(uint8_t *src, int stride)
+@@ -1028,6 +1029,7 @@ av_cold void ff_vc1dsp_init(VC1DSPContex
+ #endif /* CONFIG_WMV3IMAGE_DECODER || CONFIG_VC1IMAGE_DECODER */
+ 
+     dsp->startcode_find_candidate = ff_startcode_find_candidate_c;
++    dsp->vc1_unescape_buffer      = vc1_unescape_buffer;
+ 
+     if (ARCH_AARCH64)
+         ff_vc1dsp_init_aarch64(dsp);
+--- a/libavcodec/vc1dsp.h
++++ b/libavcodec/vc1dsp.h
+@@ -80,6 +80,9 @@ typedef struct VC1DSPContext {
+      * one or more further zero bytes and a one byte.
+      */
+     int (*startcode_find_candidate)(const uint8_t *buf, int size);
++
++    /* Copy a buffer, removing startcode emulation escape bytes as we go */
++    int (*vc1_unescape_buffer)(const uint8_t *src, int size, uint8_t *dst);
+ } VC1DSPContext;
+ 
+ void ff_vc1dsp_init(VC1DSPContext* c);
 --- /dev/null
 +++ b/libavcodec/weak_link.c
-@@ -0,0 +1,100 @@
+@@ -0,0 +1,102 @@
 +#include <stdlib.h>
 +#include <pthread.h>
 +#include <stdatomic.h>
@@ -53549,6 +59002,8 @@
 +
 +struct ff_weak_link_client* ff_weak_link_ref(struct ff_weak_link_master * w)
 +{
++    if (!w)
++        return NULL;
 +    atomic_fetch_add(&w->ref_count, 1);
 +    return (struct ff_weak_link_client*)w;
 +}
@@ -53630,7 +59085,7 @@
 +OBJS-$(CONFIG_VOUT_RPI_OUTDEV)           += rpi_vout.o
  OBJS-$(CONFIG_XCBGRAB_INDEV)             += xcbgrab.o
  OBJS-$(CONFIG_XV_OUTDEV)                 += xv.o
-
+ 
 --- a/libavdevice/alldevices.c
 +++ b/libavdevice/alldevices.c
 @@ -52,6 +52,9 @@ extern AVOutputFormat ff_sndio_muxer;
@@ -53642,7 +59097,7 @@
 +extern AVOutputFormat ff_vout_rpi_muxer;
  extern AVInputFormat  ff_xcbgrab_demuxer;
  extern AVOutputFormat ff_xv_muxer;
-
+ 
 --- /dev/null
 +++ b/libavdevice/drm_vout.c
 @@ -0,0 +1,643 @@
@@ -53856,7 +59311,7 @@
 +
 +        while (drmWaitVBlank(de->drm_fd, &vbl)) {
 +            if (errno != EINTR) {
-+                av_log(s, AV_LOG_WARNING, "drmWaitVBlank failed: %s\n", ERRSTR);
++//                av_log(s, AV_LOG_WARNING, "drmWaitVBlank failed: %s\n", ERRSTR);
 +                break;
 +            }
 +        }
@@ -54291,7 +59746,7 @@
 +
 --- /dev/null
 +++ b/libavdevice/egl_vout.c
-@@ -0,0 +1,825 @@
+@@ -0,0 +1,816 @@
 +/*
 + * Copyright (c) 2020 John Cox for Raspberry Pi Trading
 + *
@@ -54334,16 +59789,8 @@
 +#include <stdatomic.h>
 +#include <unistd.h>
 +
-+#include "drm_fourcc.h"
-+#include <drm.h>
-+#include <drm_mode.h>
-+#include <xf86drm.h>
-+#include <xf86drmMode.h>
 +#include <X11/Xlib.h>
 +#include <X11/Xutil.h>
-+#include <X11/Xlib-xcb.h>
-+#include <xcb/xcb.h>
-+#include <xcb/dri3.h>
 +
 +#include "libavutil/rpi_sand_fns.h"
 +
@@ -54555,8 +60002,7 @@
 +   XMapWindow(dpy, win);
 +
 +   {
-+      EGLSurface surf = eglCreateWindowSurface(egl_dpy, config,
-+                                               (void *)(uintptr_t)win, NULL);
++      EGLSurface surf = eglCreateWindowSurface(egl_dpy, config, (EGLNativeWindowType)win, NULL);
 +      if (!surf) {
 +         av_log(s, AV_LOG_ERROR, "Error: eglCreateWindowSurface failed\n");
 +         return -1;
@@ -55656,7 +61102,15 @@
 +};
 --- a/libavfilter/Makefile
 +++ b/libavfilter/Makefile
-@@ -434,6 +434,7 @@ OBJS-$(CONFIG_TRANSPOSE_OPENCL_FILTER)
+@@ -218,6 +218,7 @@ OBJS-$(CONFIG_DEFLATE_FILTER)
+ OBJS-$(CONFIG_DEFLICKER_FILTER)              += vf_deflicker.o
+ OBJS-$(CONFIG_DEINTERLACE_QSV_FILTER)        += vf_deinterlace_qsv.o
+ OBJS-$(CONFIG_DEINTERLACE_VAAPI_FILTER)      += vf_deinterlace_vaapi.o vaapi_vpp.o
++OBJS-$(CONFIG_DEINTERLACE_V4L2M2M_FILTER)    += vf_deinterlace_v4l2m2m.o
+ OBJS-$(CONFIG_DEJUDDER_FILTER)               += vf_dejudder.o
+ OBJS-$(CONFIG_DELOGO_FILTER)                 += vf_delogo.o
+ OBJS-$(CONFIG_DENOISE_VAAPI_FILTER)          += vf_misc_vaapi.o vaapi_vpp.o
+@@ -434,6 +435,7 @@ OBJS-$(CONFIG_TRANSPOSE_OPENCL_FILTER)
  OBJS-$(CONFIG_TRANSPOSE_VAAPI_FILTER)        += vf_transpose_vaapi.o vaapi_vpp.o
  OBJS-$(CONFIG_TRIM_FILTER)                   += trim.o
  OBJS-$(CONFIG_UNPREMULTIPLY_FILTER)          += vf_premultiply.o framesync.o
@@ -55666,7 +61120,15 @@
                                                  opencl/unsharp.o
 --- a/libavfilter/allfilters.c
 +++ b/libavfilter/allfilters.c
-@@ -414,6 +414,7 @@ extern AVFilter ff_vf_transpose_opencl;
+@@ -204,6 +204,7 @@ extern AVFilter ff_vf_dedot;
+ extern AVFilter ff_vf_deflate;
+ extern AVFilter ff_vf_deflicker;
+ extern AVFilter ff_vf_deinterlace_qsv;
++extern AVFilter ff_vf_deinterlace_v4l2m2m;
+ extern AVFilter ff_vf_deinterlace_vaapi;
+ extern AVFilter ff_vf_dejudder;
+ extern AVFilter ff_vf_delogo;
+@@ -414,6 +415,7 @@ extern AVFilter ff_vf_transpose_opencl;
  extern AVFilter ff_vf_transpose_vaapi;
  extern AVFilter ff_vf_trim;
  extern AVFilter ff_vf_unpremultiply;
@@ -55683,13 +61145,13 @@
 +#if CONFIG_UNSAND_FILTER
 +#include "libavutil/rpi_sand_fns.h"
 +#endif
-
+ 
  #define FF_INTERNAL_FIELDS 1
  #include "framequeue.h"
 @@ -427,6 +430,19 @@ static int can_merge_formats(AVFilterFor
      }
  }
-
+ 
 +#if CONFIG_UNSAND_FILTER
 +static int has_sand_format(const AVFilterFormats * const ff)
 +{
@@ -55711,13 +61173,13 @@
              AVFilterLink *link = filter->inputs[j];
              int convert_needed = 0;
 +            unsigned int extra_convert_tried = 0;
-
+ 
              if (!link)
                  continue;
 @@ -514,11 +531,14 @@ static int query_formats(AVFilterGraph *
              )
  #undef MERGE_DISPATCH
-
+ 
 -            if (convert_needed) {
 +            while (convert_needed) {
                  AVFilterContext *convert;
@@ -55727,7 +61189,7 @@
 +                int can_retry = 0;
 +
 +                convert_needed = 0;
-
+ 
                  if (graph->disable_auto_convert) {
                      av_log(log_ctx, AV_LOG_ERROR,
 @@ -531,19 +551,45 @@ static int query_formats(AVFilterGraph *
@@ -55760,7 +61222,7 @@
 +                                                                inst_name, "", NULL,
 +                                                                graph)) < 0)
 +                            return ret;
-
+ 
 -                    if ((ret = avfilter_graph_create_filter(&convert, filter,
 -                                                            inst_name, graph->scale_sws_opts, NULL,
 -                                                            graph)) < 0)
@@ -55813,7 +61275,7 @@
 --- a/libavfilter/buffersrc.c
 +++ b/libavfilter/buffersrc.c
 @@ -210,7 +210,7 @@ static int av_buffersrc_add_frame_intern
-
+ 
          switch (ctx->outputs[0]->type) {
          case AVMEDIA_TYPE_VIDEO:
 -            CHECK_VIDEO_PARAM_CHANGE(ctx, s, frame->width, frame->height,
@@ -55822,6 +61284,1345 @@
              break;
          case AVMEDIA_TYPE_AUDIO:
 --- /dev/null
++++ b/libavfilter/vf_deinterlace_v4l2m2m.c
+@@ -0,0 +1,1336 @@
++/*
++ * 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 - V4L2 M2M
++ */
++
++#include <drm_fourcc.h>
++
++#include <linux/videodev2.h>
++
++#include <dirent.h>
++#include <fcntl.h>
++#include <poll.h>
++#include <stdatomic.h>
++#include <stdio.h>
++#include <string.h>
++#include <sys/ioctl.h>
++#include <sys/mman.h>
++#include <unistd.h>
++
++#include "libavutil/avassert.h"
++#include "libavutil/avstring.h"
++#include "libavutil/common.h"
++#include "libavutil/hwcontext.h"
++#include "libavutil/hwcontext_drm.h"
++#include "libavutil/internal.h"
++#include "libavutil/mathematics.h"
++#include "libavutil/opt.h"
++#include "libavutil/pixdesc.h"
++#include "libavutil/time.h"
++
++#define FF_INTERNAL_FIELDS 1
++#include "framequeue.h"
++#include "filters.h"
++#include "avfilter.h"
++#include "formats.h"
++#include "internal.h"
++#include "video.h"
++
++typedef struct V4L2Queue V4L2Queue;
++typedef struct DeintV4L2M2MContextShared DeintV4L2M2MContextShared;
++
++typedef struct V4L2PlaneInfo {
++    int bytesperline;
++    size_t length;
++} V4L2PlaneInfo;
++
++typedef struct V4L2Buffer {
++    int enqueued;
++    int reenqueue;
++    int fd;
++    struct v4l2_buffer buffer;
++    AVFrame frame;
++    struct v4l2_plane planes[VIDEO_MAX_PLANES];
++    int num_planes;
++    V4L2PlaneInfo plane_info[VIDEO_MAX_PLANES];
++    AVDRMFrameDescriptor drm_frame;
++    V4L2Queue *q;
++} V4L2Buffer;
++
++typedef struct V4L2Queue {
++    struct v4l2_format format;
++    int num_buffers;
++    V4L2Buffer *buffers;
++    DeintV4L2M2MContextShared *ctx;
++} V4L2Queue;
++
++typedef struct pts_stats_s
++{
++    void * logctx;
++    const char * name;  // For debug
++    unsigned int last_count;
++    unsigned int last_interval;
++    int64_t last_pts;
++} pts_stats_t;
++
++#define PTS_TRACK_SIZE 32
++typedef struct pts_track_el_s
++{
++    uint32_t n;
++    unsigned int interval;
++    AVFrame * props;
++} pts_track_el_t;
++
++typedef struct pts_track_s
++{
++    uint32_t n;
++    uint32_t last_n;
++    int got_2;
++    void * logctx;
++    pts_stats_t stats;
++    pts_track_el_t a[PTS_TRACK_SIZE];
++} pts_track_t;
++
++typedef struct DeintV4L2M2MContextShared {
++    void * logctx;  // For logging - will be NULL when done
++
++    int fd;
++    int done;
++    int width;
++    int height;
++    int orig_width;
++    int orig_height;
++    atomic_uint refcount;
++
++    AVBufferRef *hw_frames_ctx;
++
++    unsigned int field_order;
++
++    pts_track_t track;
++
++    V4L2Queue output;
++    V4L2Queue capture;
++} DeintV4L2M2MContextShared;
++
++typedef struct DeintV4L2M2MContext {
++    const AVClass *class;
++
++    DeintV4L2M2MContextShared *shared;
++} DeintV4L2M2MContext;
++
++static unsigned int pts_stats_interval(const pts_stats_t * const stats)
++{
++    return stats->last_interval;
++}
++
++// Pick 64 for max last count - that is >1sec at 60fps
++#define STATS_LAST_COUNT_MAX 64
++#define STATS_INTERVAL_MAX (1 << 30)
++static void pts_stats_add(pts_stats_t * const stats, int64_t pts)
++{
++    if (pts == AV_NOPTS_VALUE || pts == stats->last_pts) {
++        if (stats->last_count < STATS_LAST_COUNT_MAX)
++            ++stats->last_count;
++        return;
++    }
++
++    if (stats->last_pts != AV_NOPTS_VALUE) {
++        const int64_t interval = pts - stats->last_pts;
++
++        if (interval < 0 || interval >= STATS_INTERVAL_MAX ||
++            stats->last_count >= STATS_LAST_COUNT_MAX) {
++            if (stats->last_interval != 0)
++                av_log(stats->logctx, AV_LOG_DEBUG, "%s: %s: Bad interval: %" PRId64 "/%d\n",
++                       __func__, stats->name, interval, stats->last_count);
++            stats->last_interval = 0;
++        }
++        else {
++            const int64_t frame_time = interval / (int64_t)stats->last_count;
++
++            if (frame_time != stats->last_interval)
++                av_log(stats->logctx, AV_LOG_DEBUG, "%s: %s: New interval: %u->%" PRId64 "/%d=%" PRId64 "\n",
++                       __func__, stats->name, stats->last_interval, interval, stats->last_count, frame_time);
++            stats->last_interval = frame_time;
++        }
++    }
++
++    stats->last_pts = pts;
++    stats->last_count = 1;
++}
++
++static void pts_stats_init(pts_stats_t * const stats, void * logctx, const char * name)
++{
++    *stats = (pts_stats_t){
++        .logctx = logctx,
++        .name = name,
++        .last_count = 1,
++        .last_interval = 0,
++        .last_pts = AV_NOPTS_VALUE
++    };
++}
++
++static inline uint32_t pts_track_next_n(pts_track_t * const trk)
++{
++    if (++trk->n == 0)
++        trk->n = 1;
++    return trk->n;
++}
++
++static int pts_track_get_frame(pts_track_t * const trk, const struct timeval tv, AVFrame * const dst)
++{
++    uint32_t n = (uint32_t)(tv.tv_usec / 2 + tv.tv_sec * 500000);
++    pts_track_el_t * t;
++
++    // As a first guess assume that n==0 means last frame
++    if (n == 0) {
++        n = trk->last_n;
++        if (n == 0)
++            goto fail;
++    }
++
++    t = trk->a + (n & (PTS_TRACK_SIZE - 1));
++
++    if (t->n != n) {
++        av_log(trk->logctx, AV_LOG_ERROR, "%s: track failure: got %u, expected %u\n", __func__, n, trk->n);
++        goto fail;
++    }
++
++    // 1st frame is simple - just believe it
++    if (n != trk->last_n) {
++        trk->last_n = n;
++        trk->got_2 = 0;
++        return av_frame_copy_props(dst, t->props);
++    }
++
++    // Only believe in a single interpolated frame
++    if (trk->got_2)
++        goto fail;
++    trk->got_2 = 1;
++
++    av_frame_copy_props(dst, t->props);
++
++
++    // If we can't guess - don't
++    if (t->interval == 0) {
++        dst->best_effort_timestamp = AV_NOPTS_VALUE;
++        dst->pts = AV_NOPTS_VALUE;
++        dst->pkt_dts = AV_NOPTS_VALUE;
++    }
++    else {
++        if (dst->best_effort_timestamp != AV_NOPTS_VALUE)
++            dst->best_effort_timestamp += t->interval / 2;
++        if (dst->pts != AV_NOPTS_VALUE)
++            dst->pts += t->interval / 2;
++        if (dst->pkt_dts != AV_NOPTS_VALUE)
++            dst->pkt_dts += t->interval / 2;
++    }
++
++    return 0;
++
++fail:
++    trk->last_n = 0;
++    trk->got_2 = 0;
++    dst->pts = AV_NOPTS_VALUE;
++    dst->pkt_dts = AV_NOPTS_VALUE;
++    return 0;
++}
++
++static struct timeval pts_track_add_frame(pts_track_t * const trk, const AVFrame * const src)
++{
++    const uint32_t n = pts_track_next_n(trk);
++    pts_track_el_t * const t = trk->a + (n & (PTS_TRACK_SIZE - 1));
++
++    pts_stats_add(&trk->stats, src->pts);
++
++    t->n = n;
++    t->interval = pts_stats_interval(&trk->stats); // guess that next interval is the same as the last
++    av_frame_unref(t->props);
++    av_frame_copy_props(t->props, src);
++
++    // We now know what the previous interval was, rather than having to guess,
++    // so set it.  There is a better than decent chance that this is before
++    // we use it.
++    if (t->interval != 0) {
++        pts_track_el_t * const prev_t = trk->a + ((n - 1) & (PTS_TRACK_SIZE - 1));
++        prev_t->interval = t->interval;
++    }
++
++    // In case deinterlace interpolates frames use every other usec
++    return (struct timeval){.tv_sec = n / 500000, .tv_usec = (n % 500000) * 2};
++}
++
++static void pts_track_uninit(pts_track_t * const trk)
++{
++    unsigned int i;
++    for (i = 0; i != PTS_TRACK_SIZE; ++i) {
++        trk->a[i].n = 0;
++        av_frame_free(&trk->a[i].props);
++    }
++}
++
++static int pts_track_init(pts_track_t * const trk, void *logctx)
++{
++    unsigned int i;
++    trk->n = 1;
++    pts_stats_init(&trk->stats, logctx, "track");
++    for (i = 0; i != PTS_TRACK_SIZE; ++i) {
++        trk->a[i].n = 0;
++        if ((trk->a[i].props = av_frame_alloc()) == NULL) {
++            pts_track_uninit(trk);
++            return AVERROR(ENOMEM);
++        }
++    }
++    return 0;
++}
++
++static int deint_v4l2m2m_prepare_context(DeintV4L2M2MContextShared *ctx)
++{
++    struct v4l2_capability cap;
++    int ret;
++
++    memset(&cap, 0, sizeof(cap));
++    ret = ioctl(ctx->fd, VIDIOC_QUERYCAP, &cap);
++    if (ret < 0)
++        return ret;
++
++    if (!(cap.capabilities & V4L2_CAP_STREAMING))
++        return AVERROR(EINVAL);
++
++    if (cap.capabilities & V4L2_CAP_VIDEO_M2M) {
++        ctx->capture.format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++        ctx->output.format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
++
++        return 0;
++    }
++
++    if (cap.capabilities & V4L2_CAP_VIDEO_M2M_MPLANE) {
++        ctx->capture.format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
++        ctx->output.format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
++
++        return 0;
++    }
++
++    return AVERROR(EINVAL);
++}
++
++static int deint_v4l2m2m_try_format(V4L2Queue *queue)
++{
++    struct v4l2_format *fmt        = &queue->format;
++    DeintV4L2M2MContextShared *ctx = queue->ctx;
++    int ret, field;
++
++    ret = ioctl(ctx->fd, VIDIOC_G_FMT, fmt);
++    if (ret)
++        av_log(ctx->logctx, AV_LOG_ERROR, "VIDIOC_G_FMT failed: %d\n", ret);
++
++    if (V4L2_TYPE_IS_OUTPUT(fmt->type))
++        field = V4L2_FIELD_INTERLACED_TB;
++    else
++        field = V4L2_FIELD_NONE;
++
++    if (V4L2_TYPE_IS_MULTIPLANAR(fmt->type)) {
++        fmt->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420;
++        fmt->fmt.pix_mp.field = field;
++        fmt->fmt.pix_mp.width = ctx->width;
++        fmt->fmt.pix_mp.height = ctx->height;
++    } else {
++        fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
++        fmt->fmt.pix.field = field;
++        fmt->fmt.pix.width = ctx->width;
++        fmt->fmt.pix.height = ctx->height;
++    }
++
++    av_log(ctx->logctx, AV_LOG_DEBUG, "%s: Trying format for type %d, wxh: %dx%d, fmt: %08x, size %u bpl %u pre\n", __func__,
++		 fmt->type, fmt->fmt.pix_mp.width, fmt->fmt.pix_mp.height,
++		 fmt->fmt.pix_mp.pixelformat,
++		 fmt->fmt.pix_mp.plane_fmt[0].sizeimage, fmt->fmt.pix_mp.plane_fmt[0].bytesperline);
++
++    ret = ioctl(ctx->fd, VIDIOC_TRY_FMT, fmt);
++    if (ret)
++        return AVERROR(EINVAL);
++
++    av_log(ctx->logctx, AV_LOG_DEBUG, "%s: Trying format for type %d, wxh: %dx%d, fmt: %08x, size %u bpl %u post\n", __func__,
++		 fmt->type, fmt->fmt.pix_mp.width, fmt->fmt.pix_mp.height,
++		 fmt->fmt.pix_mp.pixelformat,
++		 fmt->fmt.pix_mp.plane_fmt[0].sizeimage, fmt->fmt.pix_mp.plane_fmt[0].bytesperline);
++
++    if (V4L2_TYPE_IS_MULTIPLANAR(fmt->type)) {
++        if ((fmt->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_YUV420 &&
++             fmt->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_NV12) ||
++            fmt->fmt.pix_mp.field != field) {
++            av_log(ctx->logctx, AV_LOG_DEBUG, "format not supported for type %d\n", fmt->type);
++
++            return AVERROR(EINVAL);
++        }
++    } else {
++        if ((fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_YUV420 &&
++             fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_NV12) ||
++            fmt->fmt.pix.field != field) {
++            av_log(ctx->logctx, AV_LOG_DEBUG, "format not supported for type %d\n", fmt->type);
++
++            return AVERROR(EINVAL);
++        }
++    }
++
++    return 0;
++}
++
++static int deint_v4l2m2m_set_format(V4L2Queue *queue, uint32_t pixelformat, uint32_t field, int width, int height, int pitch, int ysize)
++{
++    struct v4l2_format *fmt        = &queue->format;
++    DeintV4L2M2MContextShared *ctx = queue->ctx;
++    int ret;
++
++    struct v4l2_selection sel = {
++        .type = fmt->type,
++        .target = V4L2_TYPE_IS_OUTPUT(fmt->type) ? V4L2_SEL_TGT_CROP_BOUNDS : V4L2_SEL_TGT_COMPOSE_BOUNDS,
++    };
++
++    // This works for most single object 4:2:0 types
++    if (V4L2_TYPE_IS_MULTIPLANAR(fmt->type)) {
++        fmt->fmt.pix_mp.pixelformat = pixelformat;
++        fmt->fmt.pix_mp.field = field;
++        fmt->fmt.pix_mp.width = width;
++        fmt->fmt.pix_mp.height = ysize / pitch;
++        fmt->fmt.pix_mp.plane_fmt[0].bytesperline = pitch;
++        fmt->fmt.pix_mp.plane_fmt[0].sizeimage = ysize + (ysize >> 1);
++    } else {
++        fmt->fmt.pix.pixelformat = pixelformat;
++        fmt->fmt.pix.field = field;
++        fmt->fmt.pix.width = width;
++        fmt->fmt.pix.height = height;
++        fmt->fmt.pix.sizeimage = 0;
++        fmt->fmt.pix.bytesperline = 0;
++    }
++
++    ret = ioctl(ctx->fd, VIDIOC_S_FMT, fmt);
++    if (ret) {
++        ret = AVERROR(errno);
++        av_log(ctx->logctx, AV_LOG_ERROR, "VIDIOC_S_FMT failed: %d\n", ret);
++        return ret;
++    }
++
++    if (pixelformat != fmt->fmt.pix.pixelformat) {
++        av_log(ctx->logctx, AV_LOG_ERROR, "Format not supported: %s; S_FMT returned %s\n", av_fourcc2str(pixelformat), av_fourcc2str(fmt->fmt.pix.pixelformat));
++        return AVERROR(EINVAL);
++    }
++
++    ret = ioctl(ctx->fd, VIDIOC_G_SELECTION, &sel);
++    if (ret) {
++        ret = AVERROR(errno);
++        av_log(ctx->logctx, AV_LOG_WARNING, "VIDIOC_G_SELECTION failed: %d\n", ret);
++    }
++
++    sel.r.width = width;
++    sel.r.height = height;
++    sel.r.left = 0;
++    sel.r.top = 0;
++    sel.target = V4L2_TYPE_IS_OUTPUT(fmt->type) ? V4L2_SEL_TGT_CROP : V4L2_SEL_TGT_COMPOSE,
++    sel.flags = V4L2_SEL_FLAG_LE;
++
++    ret = ioctl(ctx->fd, VIDIOC_S_SELECTION, &sel);
++    if (ret) {
++        ret = AVERROR(errno);
++        av_log(ctx->logctx, AV_LOG_WARNING, "VIDIOC_S_SELECTION failed: %d\n", ret);
++    }
++
++    return 0;
++}
++
++static int deint_v4l2m2m_probe_device(DeintV4L2M2MContextShared *ctx, char *node)
++{
++    int ret;
++
++    ctx->fd = open(node, O_RDWR | O_NONBLOCK, 0);
++    if (ctx->fd < 0)
++        return AVERROR(errno);
++
++    ret = deint_v4l2m2m_prepare_context(ctx);
++    if (ret)
++        goto fail;
++
++    ret = deint_v4l2m2m_try_format(&ctx->capture);
++    if (ret)
++        goto fail;
++
++    ret = deint_v4l2m2m_try_format(&ctx->output);
++    if (ret)
++        goto fail;
++
++    return 0;
++
++fail:
++    close(ctx->fd);
++    ctx->fd = -1;
++
++    return ret;
++}
++
++static int deint_v4l2m2m_find_device(DeintV4L2M2MContextShared *ctx)
++{
++    int ret = AVERROR(EINVAL);
++    struct dirent *entry;
++    char node[PATH_MAX];
++    DIR *dirp;
++
++    dirp = opendir("/dev");
++    if (!dirp)
++        return AVERROR(errno);
++
++    for (entry = readdir(dirp); entry; entry = readdir(dirp)) {
++
++        if (strncmp(entry->d_name, "video", 5))
++            continue;
++
++        snprintf(node, sizeof(node), "/dev/%s", entry->d_name);
++        av_log(ctx->logctx, AV_LOG_DEBUG, "probing device %s\n", node);
++        ret = deint_v4l2m2m_probe_device(ctx, node);
++        if (!ret)
++            break;
++    }
++
++    closedir(dirp);
++
++    if (ret) {
++        av_log(ctx->logctx, AV_LOG_ERROR, "Could not find a valid device\n");
++        ctx->fd = -1;
++
++        return ret;
++    }
++
++    av_log(ctx->logctx, AV_LOG_INFO, "Using device %s\n", node);
++
++    return 0;
++}
++
++static int deint_v4l2m2m_enqueue_buffer(V4L2Buffer *buf)
++{
++    int ret;
++
++    ret = ioctl(buf->q->ctx->fd, VIDIOC_QBUF, &buf->buffer);
++    if (ret < 0)
++        return AVERROR(errno);
++
++    buf->enqueued = 1;
++
++    return 0;
++}
++
++static int v4l2_buffer_export_drm(V4L2Buffer* avbuf, const uint32_t pixelformat)
++{
++    struct v4l2_exportbuffer expbuf;
++    int i, ret;
++    uint64_t mod = DRM_FORMAT_MOD_LINEAR;
++    uint32_t fmt = 0;
++
++    switch (pixelformat) {
++    case V4L2_PIX_FMT_NV12:
++        fmt = DRM_FORMAT_NV12;
++        break;
++    case V4L2_PIX_FMT_YUV420:
++        fmt = DRM_FORMAT_YUV420;
++        break;
++    default:
++        return AVERROR(EINVAL);
++    }
++
++    avbuf->drm_frame.layers[0].format = fmt;
++
++    for (i = 0; i < avbuf->num_planes; i++) {
++        memset(&expbuf, 0, sizeof(expbuf));
++
++        expbuf.index = avbuf->buffer.index;
++        expbuf.type = avbuf->buffer.type;
++        expbuf.plane = i;
++
++        ret = ioctl(avbuf->q->ctx->fd, VIDIOC_EXPBUF, &expbuf);
++        if (ret < 0)
++            return AVERROR(errno);
++
++        avbuf->fd = expbuf.fd;
++
++        if (V4L2_TYPE_IS_MULTIPLANAR(avbuf->buffer.type)) {
++            /* drm frame */
++            avbuf->drm_frame.objects[i].size = avbuf->buffer.m.planes[i].length;
++            avbuf->drm_frame.objects[i].fd = expbuf.fd;
++            avbuf->drm_frame.objects[i].format_modifier = mod;
++        } else {
++            /* drm frame */
++            avbuf->drm_frame.objects[0].size = avbuf->buffer.length;
++            avbuf->drm_frame.objects[0].fd = expbuf.fd;
++            avbuf->drm_frame.objects[0].format_modifier = mod;
++        }
++    }
++
++    return 0;
++}
++
++static int deint_v4l2m2m_allocate_buffers(V4L2Queue *queue)
++{
++    struct v4l2_format *fmt = &queue->format;
++    DeintV4L2M2MContextShared *ctx = queue->ctx;
++    struct v4l2_requestbuffers req;
++    int ret, i, j, multiplanar;
++    uint32_t memory;
++
++    memory = V4L2_TYPE_IS_OUTPUT(fmt->type) ?
++        V4L2_MEMORY_DMABUF : V4L2_MEMORY_MMAP;
++
++    multiplanar = V4L2_TYPE_IS_MULTIPLANAR(fmt->type);
++
++    memset(&req, 0, sizeof(req));
++    req.count = queue->num_buffers;
++    req.memory = memory;
++    req.type = fmt->type;
++
++    ret = ioctl(ctx->fd, VIDIOC_REQBUFS, &req);
++    if (ret < 0) {
++        av_log(ctx->logctx, AV_LOG_ERROR, "VIDIOC_REQBUFS failed: %s\n", strerror(errno));
++
++        return AVERROR(errno);
++    }
++
++    queue->num_buffers = req.count;
++    queue->buffers = av_mallocz(queue->num_buffers * sizeof(V4L2Buffer));
++    if (!queue->buffers) {
++        av_log(ctx->logctx, AV_LOG_ERROR, "malloc enomem\n");
++
++        return AVERROR(ENOMEM);
++    }
++
++    for (i = 0; i < queue->num_buffers; i++) {
++        V4L2Buffer *buf = &queue->buffers[i];
++
++        buf->enqueued = 0;
++        buf->fd = -1;
++        buf->q = queue;
++
++        buf->buffer.type = fmt->type;
++        buf->buffer.memory = memory;
++        buf->buffer.index = i;
++
++        if (multiplanar) {
++            buf->buffer.length = VIDEO_MAX_PLANES;
++            buf->buffer.m.planes = buf->planes;
++        }
++
++        ret = ioctl(ctx->fd, VIDIOC_QUERYBUF, &buf->buffer);
++        if (ret < 0) {
++            ret = AVERROR(errno);
++
++            goto fail;
++        }
++
++        if (multiplanar)
++            buf->num_planes = buf->buffer.length;
++        else
++            buf->num_planes = 1;
++
++        for (j = 0; j < buf->num_planes; j++) {
++            V4L2PlaneInfo *info = &buf->plane_info[j];
++
++            if (multiplanar) {
++                info->bytesperline = fmt->fmt.pix_mp.plane_fmt[j].bytesperline;
++                info->length = buf->buffer.m.planes[j].length;
++            } else {
++                info->bytesperline = fmt->fmt.pix.bytesperline;
++                info->length = buf->buffer.length;
++            }
++        }
++
++        if (!V4L2_TYPE_IS_OUTPUT(fmt->type)) {
++            ret = deint_v4l2m2m_enqueue_buffer(buf);
++            if (ret)
++                goto fail;
++
++            ret = v4l2_buffer_export_drm(buf, multiplanar ? fmt->fmt.pix_mp.pixelformat : fmt->fmt.pix.pixelformat);
++            if (ret)
++                goto fail;
++        }
++    }
++
++    return 0;
++
++fail:
++    for (i = 0; i < queue->num_buffers; i++)
++        if (queue->buffers[i].fd >= 0)
++            close(queue->buffers[i].fd);
++    av_free(queue->buffers);
++    queue->buffers = NULL;
++
++    return ret;
++}
++
++static int deint_v4l2m2m_streamon(V4L2Queue *queue)
++{
++    DeintV4L2M2MContextShared * const ctx = queue->ctx;
++    int type = queue->format.type;
++    int ret;
++
++    ret = ioctl(ctx->fd, VIDIOC_STREAMON, &type);
++    av_log(ctx->logctx, AV_LOG_DEBUG, "%s: type:%d ret:%d errno:%d\n", __func__, type, ret, AVERROR(errno));
++    if (ret < 0)
++        return AVERROR(errno);
++
++    return 0;
++}
++
++static int deint_v4l2m2m_streamoff(V4L2Queue *queue)
++{
++    DeintV4L2M2MContextShared * const ctx = queue->ctx;
++    int type = queue->format.type;
++    int ret;
++
++    ret = ioctl(ctx->fd, VIDIOC_STREAMOFF, &type);
++    av_log(ctx->logctx, AV_LOG_DEBUG, "%s: type:%d ret:%d errno:%d\n", __func__, type, ret, AVERROR(errno));
++    if (ret < 0)
++        return AVERROR(errno);
++
++    return 0;
++}
++
++// timeout in ms
++static V4L2Buffer* deint_v4l2m2m_dequeue_buffer(V4L2Queue *queue, int timeout)
++{
++    struct v4l2_plane planes[VIDEO_MAX_PLANES];
++    DeintV4L2M2MContextShared *ctx = queue->ctx;
++    struct v4l2_buffer buf = { 0 };
++    V4L2Buffer* avbuf = NULL;
++    struct pollfd pfd;
++    short events;
++    int ret;
++
++    if (V4L2_TYPE_IS_OUTPUT(queue->format.type))
++        events =  POLLOUT | POLLWRNORM;
++    else
++        events = POLLIN | POLLRDNORM;
++
++    pfd.events = events;
++    pfd.fd = ctx->fd;
++
++    for (;;) {
++        ret = poll(&pfd, 1, timeout);
++        if (ret > 0)
++            break;
++        if (errno == EINTR)
++            continue;
++        return NULL;
++    }
++
++    if (pfd.revents & POLLERR)
++        return NULL;
++
++    if (pfd.revents & events) {
++        memset(&buf, 0, sizeof(buf));
++        buf.memory = V4L2_MEMORY_MMAP;
++        buf.type = queue->format.type;
++        if (V4L2_TYPE_IS_MULTIPLANAR(queue->format.type)) {
++            memset(planes, 0, sizeof(planes));
++            buf.length = VIDEO_MAX_PLANES;
++            buf.m.planes = planes;
++        }
++
++        ret = ioctl(ctx->fd, VIDIOC_DQBUF, &buf);
++        if (ret) {
++            if (errno != EAGAIN)
++                av_log(ctx->logctx, AV_LOG_DEBUG, "VIDIOC_DQBUF, errno (%s)\n",
++                       av_err2str(AVERROR(errno)));
++            return NULL;
++        }
++
++        avbuf = &queue->buffers[buf.index];
++        avbuf->enqueued = 0;
++        avbuf->buffer = buf;
++        if (V4L2_TYPE_IS_MULTIPLANAR(queue->format.type)) {
++            memcpy(avbuf->planes, planes, sizeof(planes));
++            avbuf->buffer.m.planes = avbuf->planes;
++        }
++        return avbuf;
++    }
++
++    return NULL;
++}
++
++static V4L2Buffer *deint_v4l2m2m_find_free_buf(V4L2Queue *queue)
++{
++    int i;
++    V4L2Buffer *buf = NULL;
++
++    for (i = 0; i < queue->num_buffers; i++)
++        if (!queue->buffers[i].enqueued) {
++            buf = &queue->buffers[i];
++            break;
++        }
++    return buf;
++}
++
++static void deint_v4l2m2m_unref_queued(V4L2Queue *queue)
++{
++    int i;
++    V4L2Buffer *buf = NULL;
++
++    if (!queue || !queue->buffers)
++        return;
++    for (i = 0; i < queue->num_buffers; i++) {
++        buf = &queue->buffers[i];
++        if (queue->buffers[i].enqueued)
++            av_frame_unref(&buf->frame);
++    }
++}
++
++static void recycle_q(V4L2Queue * const queue)
++{
++    V4L2Buffer* avbuf;
++    while (avbuf = deint_v4l2m2m_dequeue_buffer(queue, 0), avbuf) {
++        av_frame_unref(&avbuf->frame);
++    }
++}
++
++static int count_enqueued(V4L2Queue *queue)
++{
++    int i;
++    int n = 0;
++
++    if (queue->buffers == NULL)
++        return 0;
++
++    for (i = 0; i < queue->num_buffers; i++)
++        if (queue->buffers[i].enqueued)
++            ++n;
++    return n;
++}
++
++static int deint_v4l2m2m_enqueue_frame(V4L2Queue * const queue, AVFrame * const frame)
++{
++    DeintV4L2M2MContextShared *const ctx = queue->ctx;
++    AVDRMFrameDescriptor *drm_desc = (AVDRMFrameDescriptor *)frame->data[0];
++    V4L2Buffer *buf;
++    int i;
++
++    if (V4L2_TYPE_IS_OUTPUT(queue->format.type))
++        recycle_q(queue);
++
++    buf = deint_v4l2m2m_find_free_buf(queue);
++    if (!buf) {
++        av_log(ctx->logctx, AV_LOG_ERROR, "%s: error %d finding free buf\n", __func__, 0);
++        return AVERROR(EAGAIN);
++    }
++    if (V4L2_TYPE_IS_MULTIPLANAR(buf->buffer.type))
++        for (i = 0; i < drm_desc->nb_objects; i++)
++            buf->buffer.m.planes[i].m.fd = drm_desc->objects[i].fd;
++    else
++        buf->buffer.m.fd = drm_desc->objects[0].fd;
++
++    buf->buffer.field = !frame->interlaced_frame ? V4L2_FIELD_NONE :
++        frame->top_field_first ? V4L2_FIELD_INTERLACED_TB :
++            V4L2_FIELD_INTERLACED_BT;
++
++    if (ctx->field_order != buf->buffer.field) {
++        av_log(ctx->logctx, AV_LOG_DEBUG, "%s: Field changed: %d->%d\n", __func__, ctx->field_order, buf->buffer.field);
++        ctx->field_order = buf->buffer.field;
++    }
++
++    buf->buffer.timestamp = pts_track_add_frame(&ctx->track, frame);
++
++    buf->drm_frame.objects[0].fd = drm_desc->objects[0].fd;
++
++    av_frame_move_ref(&buf->frame, frame);
++
++    return deint_v4l2m2m_enqueue_buffer(buf);
++}
++
++static void deint_v4l2m2m_destroy_context(DeintV4L2M2MContextShared *ctx)
++{
++    if (atomic_fetch_sub(&ctx->refcount, 1) == 1) {
++        V4L2Queue *capture = &ctx->capture;
++        V4L2Queue *output  = &ctx->output;
++        int i;
++
++        av_log(NULL, AV_LOG_DEBUG, "%s - destroying context\n", __func__);
++
++        if (ctx->fd >= 0) {
++            deint_v4l2m2m_streamoff(capture);
++            deint_v4l2m2m_streamoff(output);
++        }
++
++        if (capture->buffers)
++            for (i = 0; i < capture->num_buffers; i++) {
++                capture->buffers[i].q = NULL;
++                if (capture->buffers[i].fd >= 0)
++                    close(capture->buffers[i].fd);
++            }
++
++        deint_v4l2m2m_unref_queued(output);
++
++        av_buffer_unref(&ctx->hw_frames_ctx);
++
++        if (capture->buffers)
++            av_free(capture->buffers);
++
++        if (output->buffers)
++            av_free(output->buffers);
++
++        if (ctx->fd >= 0) {
++            close(ctx->fd);
++            ctx->fd = -1;
++        }
++
++        av_free(ctx);
++    }
++}
++
++static void v4l2_free_buffer(void *opaque, uint8_t *unused)
++{
++    V4L2Buffer *buf                = opaque;
++    DeintV4L2M2MContextShared *ctx = buf->q->ctx;
++
++    if (!ctx->done)
++        deint_v4l2m2m_enqueue_buffer(buf);
++
++    deint_v4l2m2m_destroy_context(ctx);
++}
++
++static uint8_t * v4l2_get_drm_frame(V4L2Buffer *avbuf, int height)
++{
++    AVDRMFrameDescriptor *drm_desc = &avbuf->drm_frame;
++    AVDRMLayerDescriptor *layer;
++
++    /* fill the DRM frame descriptor */
++    drm_desc->nb_objects = avbuf->num_planes;
++    drm_desc->nb_layers = 1;
++
++    layer = &drm_desc->layers[0];
++    layer->nb_planes = avbuf->num_planes;
++
++    for (int i = 0; i < avbuf->num_planes; i++) {
++        layer->planes[i].object_index = i;
++        layer->planes[i].offset = 0;
++        layer->planes[i].pitch = avbuf->plane_info[i].bytesperline;
++    }
++
++    switch (layer->format) {
++    case DRM_FORMAT_YUYV:
++        layer->nb_planes = 1;
++        break;
++
++    case DRM_FORMAT_NV12:
++    case DRM_FORMAT_NV21:
++        if (avbuf->num_planes > 1)
++            break;
++
++        layer->nb_planes = 2;
++
++        layer->planes[1].object_index = 0;
++        layer->planes[1].offset = avbuf->plane_info[0].bytesperline *
++            height;
++        layer->planes[1].pitch = avbuf->plane_info[0].bytesperline;
++        break;
++
++    case DRM_FORMAT_YUV420:
++        if (avbuf->num_planes > 1)
++            break;
++
++        layer->nb_planes = 3;
++
++        layer->planes[1].object_index = 0;
++        layer->planes[1].offset = avbuf->plane_info[0].bytesperline *
++            height;
++        layer->planes[1].pitch = avbuf->plane_info[0].bytesperline >> 1;
++
++        layer->planes[2].object_index = 0;
++        layer->planes[2].offset = layer->planes[1].offset +
++            ((avbuf->plane_info[0].bytesperline *
++              height) >> 2);
++        layer->planes[2].pitch = avbuf->plane_info[0].bytesperline >> 1;
++        break;
++
++    default:
++        drm_desc->nb_layers = 0;
++        break;
++    }
++
++    return (uint8_t *) drm_desc;
++}
++
++// timeout in ms
++static int deint_v4l2m2m_dequeue_frame(V4L2Queue *queue, AVFrame* frame, int timeout)
++{
++    DeintV4L2M2MContextShared *ctx = queue->ctx;
++    V4L2Buffer* avbuf;
++
++    av_log(ctx->logctx, AV_LOG_TRACE, "<<< %s\n", __func__);
++
++    avbuf = deint_v4l2m2m_dequeue_buffer(queue, timeout);
++    if (!avbuf) {
++        av_log(ctx->logctx, AV_LOG_DEBUG, "%s: No buffer to dequeue (timeout=%d)\n", __func__, timeout);
++        return AVERROR(EAGAIN);
++    }
++
++    // Fill in PTS and anciliary info from src frame
++    // we will want to overwrite some fields as only the pts/dts
++    // fields are updated with new timing in this fn
++    pts_track_get_frame(&ctx->track, avbuf->buffer.timestamp, frame);
++
++    frame->buf[0] = av_buffer_create((uint8_t *) &avbuf->drm_frame,
++                            sizeof(avbuf->drm_frame), v4l2_free_buffer,
++                            avbuf, AV_BUFFER_FLAG_READONLY);
++    if (!frame->buf[0]) {
++        av_log(ctx->logctx, AV_LOG_ERROR, "%s: error %d creating buffer\n", __func__, 0);
++        return AVERROR(ENOMEM);
++    }
++
++    atomic_fetch_add(&ctx->refcount, 1);
++
++    frame->data[0] = (uint8_t *)v4l2_get_drm_frame(avbuf, ctx->orig_height);
++    frame->format = AV_PIX_FMT_DRM_PRIME;
++    if (ctx->hw_frames_ctx)
++        frame->hw_frames_ctx = av_buffer_ref(ctx->hw_frames_ctx);
++    frame->height = ctx->height;
++    frame->width = ctx->width;
++
++    // Not interlaced now
++    frame->interlaced_frame = 0;
++    frame->top_field_first = 0;
++    // Pkt duration halved
++    frame->pkt_duration /= 2;
++
++    if (avbuf->buffer.flags & V4L2_BUF_FLAG_ERROR) {
++        av_log(ctx->logctx, AV_LOG_ERROR, "driver decode error\n");
++        frame->decode_error_flags |= FF_DECODE_ERROR_INVALID_BITSTREAM;
++    }
++
++    av_log(ctx->logctx, AV_LOG_TRACE, ">>> %s: PTS=%"PRId64"\n", __func__, frame->pts);
++    return 0;
++}
++
++static int deint_v4l2m2m_config_props(AVFilterLink *outlink)
++{
++    AVFilterLink *inlink           = outlink->src->inputs[0];
++    AVFilterContext *avctx         = outlink->src;
++    DeintV4L2M2MContext *priv      = avctx->priv;
++    DeintV4L2M2MContextShared *ctx = priv->shared;
++    int ret;
++
++    ctx->height = avctx->inputs[0]->h;
++    ctx->width = avctx->inputs[0]->w;
++
++    av_log(priv, AV_LOG_DEBUG, "%s: %dx%d\n", __func__, ctx->width, ctx->height);
++
++    outlink->time_base           = inlink->time_base;
++    outlink->w                   = inlink->w;
++    outlink->h                   = inlink->h;
++    outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
++    outlink->format              = inlink->format;
++    outlink->frame_rate = (AVRational) {1, 0};  // Deny knowledge of frame rate
++
++    ret = deint_v4l2m2m_find_device(ctx);
++    if (ret)
++        return ret;
++
++    if (inlink->hw_frames_ctx) {
++        ctx->hw_frames_ctx = av_buffer_ref(inlink->hw_frames_ctx);
++        if (!ctx->hw_frames_ctx)
++            return AVERROR(ENOMEM);
++    }
++    return 0;
++}
++
++static int deint_v4l2m2m_query_formats(AVFilterContext *avctx)
++{
++    static const enum AVPixelFormat pixel_formats[] = {
++        AV_PIX_FMT_DRM_PRIME,
++        AV_PIX_FMT_YUV420P,
++        AV_PIX_FMT_NONE,
++    };
++
++    return ff_set_common_formats(avctx, ff_make_format_list(pixel_formats));
++}
++
++static uint32_t desc_pixelformat(const AVDRMFrameDescriptor * const drm_desc)
++{
++    const int is_linear = (drm_desc->objects[0].format_modifier == DRM_FORMAT_MOD_LINEAR ||
++            drm_desc->objects[0].format_modifier == DRM_FORMAT_MOD_INVALID);
++
++    switch (drm_desc->layers[0].format) {
++    case DRM_FORMAT_YUV420:
++        if (is_linear)
++            return drm_desc->nb_objects == 1 ? V4L2_PIX_FMT_YUV420 : 0;
++        break;
++    case DRM_FORMAT_NV12:
++        if (is_linear)
++            return drm_desc->nb_objects == 1 ? V4L2_PIX_FMT_NV12 : 0;
++        break;
++    default:
++        break;
++    }
++    return 0;
++}
++
++static int deint_v4l2m2m_filter_frame(AVFilterLink *link, AVFrame *in)
++{
++    AVFilterContext *avctx         = link->dst;
++    DeintV4L2M2MContext *priv      = avctx->priv;
++    DeintV4L2M2MContextShared *ctx = priv->shared;
++    V4L2Queue *capture             = &ctx->capture;
++    V4L2Queue *output              = &ctx->output;
++    int ret;
++
++    av_log(priv, AV_LOG_DEBUG, "<<< %s: input pts: %"PRId64" (%"PRId64") field :%d interlaced: %d aspect:%d/%d\n",
++          __func__, in->pts, AV_NOPTS_VALUE, in->top_field_first, in->interlaced_frame, in->sample_aspect_ratio.num, in->sample_aspect_ratio.den);
++    av_log(priv, AV_LOG_DEBUG, "--- %s: in status in %d/ot %d; out status in %d/out %d\n", __func__,
++           avctx->inputs[0]->status_in, avctx->inputs[0]->status_out, avctx->outputs[0]->status_in, avctx->outputs[0]->status_out);
++
++    if (ctx->field_order == V4L2_FIELD_ANY) {
++        const AVDRMFrameDescriptor * const drm_desc = (AVDRMFrameDescriptor *)in->data[0];
++        const uint32_t pixelformat = desc_pixelformat(drm_desc);
++
++        if (pixelformat == 0) {
++            av_log(avctx, AV_LOG_ERROR, "Unsupported DRM format %s in %d objects, modifier %#" PRIx64 "\n",
++                   av_fourcc2str(drm_desc->layers[0].format),
++                   drm_desc->nb_objects, drm_desc->objects[0].format_modifier);
++            return AVERROR(EINVAL);
++        }
++
++        ctx->orig_width = drm_desc->layers[0].planes[0].pitch;
++        ctx->orig_height = drm_desc->layers[0].planes[1].offset / ctx->orig_width;
++
++        av_log(priv, AV_LOG_DEBUG, "%s: %dx%d (%td,%td)\n", __func__, ctx->width, ctx->height,
++           drm_desc->layers[0].planes[0].pitch, drm_desc->layers[0].planes[1].offset);
++
++        ret = deint_v4l2m2m_set_format(output, pixelformat, ctx->field_order, ctx->width, ctx->height, ctx->orig_width, drm_desc->layers[0].planes[1].offset);
++        if (ret)
++            return ret;
++
++        ret = deint_v4l2m2m_set_format(capture, pixelformat, V4L2_FIELD_NONE, ctx->width, ctx->height, ctx->orig_width, drm_desc->layers[0].planes[1].offset);
++        if (ret)
++            return ret;
++
++        ret = deint_v4l2m2m_allocate_buffers(capture);
++        if (ret)
++            return ret;
++
++        ret = deint_v4l2m2m_streamon(capture);
++        if (ret)
++            return ret;
++
++        ret = deint_v4l2m2m_allocate_buffers(output);
++        if (ret)
++            return ret;
++
++        ret = deint_v4l2m2m_streamon(output);
++        if (ret)
++            return ret;
++
++        if (in->top_field_first)
++            ctx->field_order = V4L2_FIELD_INTERLACED_TB;
++        else
++            ctx->field_order = V4L2_FIELD_INTERLACED_BT;
++
++    }
++
++    ret = deint_v4l2m2m_enqueue_frame(output, in);
++
++    av_log(priv, AV_LOG_TRACE, ">>> %s: %s\n", __func__, av_err2str(ret));
++    return ret;
++}
++
++static int deint_v4l2m2m_activate(AVFilterContext *avctx)
++{
++    DeintV4L2M2MContext * const priv = avctx->priv;
++    DeintV4L2M2MContextShared *const s = priv->shared;
++    AVFilterLink * const outlink = avctx->outputs[0];
++    AVFilterLink * const inlink = avctx->inputs[0];
++    int n = 0;
++    int cn = 99;
++    int instatus = 0;
++    int64_t inpts = 0;
++    int did_something = 0;
++
++    av_log(priv, AV_LOG_TRACE, "<<< %s\n", __func__);
++
++    FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, avctx);
++
++    ff_inlink_acknowledge_status(inlink, &instatus, &inpts);
++
++    if (!ff_outlink_frame_wanted(outlink)) {
++        av_log(priv, AV_LOG_TRACE, "%s: Not wanted out\n", __func__);
++    }
++    else if (s->field_order != V4L2_FIELD_ANY)  // Can't DQ if no setup!
++    {
++        AVFrame * frame = av_frame_alloc();
++        int rv;
++
++again:
++        recycle_q(&s->output);
++        n = count_enqueued(&s->output);
++
++        if (frame == NULL) {
++            av_log(priv, AV_LOG_ERROR, "%s: error allocating frame\n", __func__);
++            return AVERROR(ENOMEM);
++        }
++
++        rv = deint_v4l2m2m_dequeue_frame(&s->capture, frame, n > 4 ? 300 : 0);
++        if (rv != 0) {
++            av_frame_free(&frame);
++            if (rv != AVERROR(EAGAIN)) {
++                av_log(priv, AV_LOG_ERROR, ">>> %s: DQ fail: %s\n", __func__, av_err2str(rv));
++                return rv;
++            }
++        }
++        else {
++            frame->interlaced_frame = 0;
++            // frame is always consumed by filter_frame - even on error despite
++            // a somewhat confusing comment in the header
++            rv = ff_filter_frame(outlink, frame);
++
++            if (instatus != 0) {
++                av_log(priv, AV_LOG_TRACE, "%s: eof loop\n", __func__);
++                goto again;
++            }
++
++            av_log(priv, AV_LOG_TRACE, "%s: Filtered: %s\n", __func__, av_err2str(rv));
++            did_something = 1;
++        }
++
++        cn = count_enqueued(&s->capture);
++    }
++
++    if (instatus != 0) {
++        ff_outlink_set_status(outlink, instatus, inpts);
++        av_log(priv, AV_LOG_TRACE, ">>> %s: Status done: %s\n", __func__, av_err2str(instatus));
++        return 0;
++    }
++
++    recycle_q(&s->output);
++    n = count_enqueued(&s->output);
++
++    while (n < 6) {
++        AVFrame * frame;
++        int rv;
++
++        if ((rv = ff_inlink_consume_frame(inlink, &frame)) < 0) {
++            av_log(priv, AV_LOG_ERROR, "%s: consume in failed: %s\n", __func__, av_err2str(rv));
++            return rv;
++        }
++
++        if (frame == NULL) {
++            av_log(priv, AV_LOG_TRACE, "%s: No frame\n", __func__);
++            break;
++        }
++
++        rv = deint_v4l2m2m_filter_frame(inlink, frame);
++        av_frame_free(&frame);
++
++        if (rv != 0)
++            return rv;
++
++        av_log(priv, AV_LOG_TRACE, "%s: Q frame\n", __func__);
++        ++n;
++    }
++
++    if (n < 6) {
++        ff_inlink_request_frame(inlink);
++        did_something = 1;
++        av_log(priv, AV_LOG_TRACE, "%s: req frame\n", __func__);
++    }
++
++    if (n > 4 && ff_outlink_frame_wanted(outlink)) {
++        ff_filter_set_ready(avctx, 1);
++        did_something = 1;
++        av_log(priv, AV_LOG_TRACE, "%s: ready\n", __func__);
++    }
++
++    av_log(priv, AV_LOG_TRACE, ">>> %s: OK (n=%d, cn=%d)\n", __func__, n, cn);
++    return did_something ? 0 : FFERROR_NOT_READY;
++}
++
++static av_cold int deint_v4l2m2m_init(AVFilterContext *avctx)
++{
++    DeintV4L2M2MContext * const priv = avctx->priv;
++    DeintV4L2M2MContextShared * const ctx = av_mallocz(sizeof(DeintV4L2M2MContextShared));
++
++    if (!ctx) {
++        av_log(priv, AV_LOG_ERROR, "%s: error %d allocating context\n", __func__, 0);
++        return AVERROR(ENOMEM);
++    }
++    priv->shared = ctx;
++    ctx->logctx = priv;
++    ctx->fd = -1;
++    ctx->output.ctx = ctx;
++    ctx->output.num_buffers = 8;
++    ctx->capture.ctx = ctx;
++    ctx->capture.num_buffers = 12;
++    ctx->done = 0;
++    ctx->field_order = V4L2_FIELD_ANY;
++
++    pts_track_init(&ctx->track, priv);
++
++    atomic_init(&ctx->refcount, 1);
++
++    return 0;
++}
++
++static void deint_v4l2m2m_uninit(AVFilterContext *avctx)
++{
++    DeintV4L2M2MContext *priv = avctx->priv;
++    DeintV4L2M2MContextShared *ctx = priv->shared;
++
++    ctx->done = 1;
++    ctx->logctx = NULL;  // Log to NULL works, log to missing crashes
++    pts_track_uninit(&ctx->track);
++    deint_v4l2m2m_destroy_context(ctx);
++}
++
++static const AVOption deinterlace_v4l2m2m_options[] = {
++    { NULL },
++};
++
++AVFILTER_DEFINE_CLASS(deinterlace_v4l2m2m);
++
++static const AVFilterPad deint_v4l2m2m_inputs[] = {
++    {
++        .name         = "default",
++        .type         = AVMEDIA_TYPE_VIDEO,
++    },
++    { NULL }
++};
++
++static const AVFilterPad deint_v4l2m2m_outputs[] = {
++    {
++        .name          = "default",
++        .type          = AVMEDIA_TYPE_VIDEO,
++        .config_props  = deint_v4l2m2m_config_props,
++    },
++    { NULL }
++};
++
++AVFilter ff_vf_deinterlace_v4l2m2m = {
++    .name           = "deinterlace_v4l2m2m",
++    .description    = NULL_IF_CONFIG_SMALL("V4L2 M2M deinterlacer"),
++    .priv_size      = sizeof(DeintV4L2M2MContext),
++    .init           = &deint_v4l2m2m_init,
++    .uninit         = &deint_v4l2m2m_uninit,
++    .query_formats  = &deint_v4l2m2m_query_formats,
++    .inputs         = deint_v4l2m2m_inputs,
++    .outputs        = deint_v4l2m2m_outputs,
++    .priv_class     = &deinterlace_v4l2m2m_class,
++    .activate       = deint_v4l2m2m_activate,
++};
+--- /dev/null
 +++ b/libavfilter/vf_unsand.c
 @@ -0,0 +1,234 @@
 +/*
@@ -56063,7 +62864,7 @@
 @@ -3051,6 +3051,40 @@ static int has_codec_parameters(AVStream
      return 1;
  }
-
+ 
 +#if CONFIG_HEVC_RPI_DECODER && CONFIG_HEVC_DECODER
 +// This should be quite general purpose but avoid possible conflicts
 +// by limiting usage to cases wehere we know it works.
@@ -56165,10 +62966,10 @@
            sha512.h                                                      \
 @@ -86,6 +87,7 @@ HEADERS = adler32.h
            tx.h                                                          \
-
+ 
  HEADERS-$(CONFIG_LZO)                   += lzo.h
 +HEADERS-$(CONFIG-RPI)                   += rpi_sand_fn_pw.h
-
+ 
  ARCH_HEADERS = bswap.h                                                  \
                 intmath.h                                                \
 @@ -180,6 +182,7 @@ OBJS-$(CONFIG_LZO)
@@ -56184,14 +62985,14 @@
 @@ -1,4 +1,6 @@
  OBJS += aarch64/cpu.o                                                 \
          aarch64/float_dsp_init.o                                      \
-
+ 
 -NEON-OBJS += aarch64/float_dsp_neon.o
 +NEON-OBJS += aarch64/float_dsp_neon.o                                 \
 +             aarch64/rpi_sand_neon.o                                  \
 +
 --- /dev/null
 +++ b/libavutil/aarch64/rpi_sand_neon.S
-@@ -0,0 +1,676 @@
+@@ -0,0 +1,781 @@
 +/*
 +Copyright (c) 2021 Michael Eiler
 +
@@ -56252,7 +63053,7 @@
 +
 +    // this is the value we have to add to the src pointer after reading a complete block
 +    // it will move the address to the start of the next block
-+    // w10 = stride2 * stride1 - stride1
++    // w10 = stride2 * stride1 - stride1 
 +    mov w10, w4
 +    lsl w10, w10, #7
 +    sub w10, w10, #128
@@ -56279,7 +63080,7 @@
 +    // copy 128 bytes (a full block) into the vector registers v0-v7 and increase the src address by 128
 +    // fortunately these aren't callee saved ones, meaning we don't need to backup them
 +    ld1 { v0.16b,  v1.16b,  v2.16b,  v3.16b}, [x13], #64
-+    ld1 { v4.16b,  v5.16b,  v6.16b,  v7.16b}, [x13], #64
++    ld1 { v4.16b,  v5.16b,  v6.16b,  v7.16b}, [x13], #64 
 +
 +    // write these registers back to the destination vector and increase the dst address by 128
 +    st1 { v0.16b,  v1.16b,  v2.16b,  v3.16b }, [x0], #64
@@ -56310,13 +63111,13 @@
 +    add w5, w5, #1
 +    b incomplete_block_loop_y8
 +incomplete_block_loop_end_y8:
-+
-+
-+    // increase the row offset by 128 (stride1)
++    
++   
++    // increase the row offset by 128 (stride1) 
 +    add w11, w11, #128
 +    // increment the row counter
 +    add w12, w12, #1
-+
++    
 +    // process the next row if we haven't finished yet
 +    cmp w15, w12
 +    bgt row_loop
@@ -56380,7 +63181,7 @@
 +    beq no_main_c8
 +
 +block_loop_c8:
-+    // load the full block -> 128 bytes, the block contains 64 interleaved U and V values
++    // load the full block -> 128 bytes, the block contains 64 interleaved U and V values 
 +    ld2 { v0.16b,  v1.16b }, [x13], #32
 +    ld2 { v2.16b,  v3.16b }, [x13], #32
 +    ld2 { v4.16b,  v5.16b }, [x13], #32
@@ -56403,14 +63204,14 @@
 +    // increment row counter and move src to the beginning of the next block
 +    add w14, w14, #1
 +    add x13, x13, x10
-+
++    
 +    // jump to block_loop_c8 iff the block count is smaller than the number of full blocks
 +    cmp w8, w14
 +    bgt block_loop_c8
 +
 +no_main_c8:
 +    // handle incomplete block at the end of every row
-+    eor w5, w5, w5 // point counter, this might be
++    eor w5, w5, w5 // point counter, this might be 
 +incomplete_block_loop_c8:
 +    cmp w5, w9
 +    bge incomplete_block_loop_end_c8
@@ -56442,228 +63243,6 @@
 +    ret
 +endfunc
 +
-+//void ff_rpi_sand30_lines_to_planar_y16(
-+//  uint8_t * dest,             // [x0]
-+//  unsigned int dst_stride,    // [w1] -> assumed to be equal to _w
-+//  const uint8_t * src,        // [x2]
-+//  unsigned int src_stride1,   // [w3] -> 128
-+//  unsigned int src_stride2,   // [w4]
-+//  unsigned int _x,            // [w5]
-+//  unsigned int y,             // [w6]
-+//  unsigned int _w,            // [w7]
-+//  unsigned int h);            // [sp, #0]
-+
-+function ff_rpi_sand30_lines_to_planar_y16, export=1
-+    stp x19, x20, [sp, #-48]!
-+    stp x21, x22, [sp, #16]
-+    stp x23, x24, [sp, #32]
-+
-+    // w6 = argument h
-+    ldr w6, [sp, #48]
-+
-+    // slice_inc = ((stride2 - 1) * stride1)
-+    mov w5, w4
-+    sub w5, w5, #1
-+    lsl w5, w5, #7
-+
-+    // total number of bytes per row = (width / 3) * 4
-+    mov w8, w7
-+    mov w9, #3
-+    udiv w8, w8, w9
-+    lsl w8, w8, #2
-+
-+    // number of full 128 byte blocks to be processed
-+    mov w9, #96
-+    udiv w9, w7, w9 // = (width * 4) / (3*128) = width/96
-+
-+    // w10 = number of full integers to process (4 bytes)
-+    // w11 = remaning zero to two 10bit values still to copy over
-+    mov w12, #96
-+    mul w12, w9, w12
-+    sub w12, w7, w12  // width - blocks*96 = remaining points per row
-+    mov w11, #3
-+    udiv w10, w12, w11 // full integers to process = w12 / 3
-+    mul w11, w10, w11  // #integers *3
-+    sub w11, w12, w11  // remaining 0-2 points = remaining points - integers*3
-+
-+    // increase w9 by one if w10+w11 is not zero, and decrease the row count by one
-+    // this is to efficiently copy incomplete blocks at the end of the rows
-+    // the last row is handled explicitly to avoid writing out of bounds
-+    add w22, w10, w11
-+    cmp w22, #0
-+    cset w22, ne // 1 iff w10+w11 not zero, 0 otherwise
-+    add w9, w9, w22
-+    sub w6, w6, #1
-+
-+    // store the number of bytes in w20 which we copy too much for every row
-+    // when the width of the frame is not a multiple of 96 (128bytes storing 96 10bit values)
-+    mov w20, #96*2
-+    mul w20, w20, w9
-+    sub w20, w1, w20
-+
-+    mov w23, #0 // flag to check whether the last line had already been processed
-+
-+    // bitmask to clear the uppper 6bits of the result values
-+    mov x19, #0x03ff03ff03ff03ff
-+    dup v22.2d, x19
-+
-+    // row counter = 0
-+    eor w12, w12, w12
-+row_loop_y16:
-+    cmp w12, w6               // jump to row_loop_y16_fin if we processed all rows
-+    bge row_loop_y16_fin
-+
-+    mov x13, x2               // row src
-+    eor w14, w14, w14         // full block counter
-+block_loop_y16:
-+    cmp w14, w9
-+    bge block_loop_y16_fin
-+
-+    // load 64 bytes
-+    ld1 { v0.4s,  v1.4s, v2.4s, v3.4s }, [x13], #64
-+
-+    // process v0 and v1
-+    xtn v16.4h, v0.4s
-+    ushr v0.4s, v0.4s, #10
-+    xtn v17.4h, v0.4s
-+    ushr v0.4s, v0.4s, #10
-+    xtn v18.4h, v0.4s
-+
-+    xtn2 v16.8h, v1.4s
-+    and v16.16b, v16.16b, v22.16b
-+    ushr v1.4s, v1.4s, #10
-+    xtn2 v17.8h, v1.4s
-+    and v17.16b, v17.16b, v22.16b
-+    ushr v1.4s, v1.4s, #10
-+    xtn2 v18.8h, v1.4s
-+    and v18.16b, v18.16b, v22.16b
-+
-+    st3 { v16.8h, v17.8h, v18.8h }, [x0], #48
-+
-+    // process v2 and v3
-+    xtn v23.4h, v2.4s
-+    ushr v2.4s, v2.4s, #10
-+    xtn v24.4h, v2.4s
-+    ushr v2.4s, v2.4s, #10
-+    xtn v25.4h, v2.4s
-+
-+    xtn2 v23.8h, v3.4s
-+    and v23.16b, v23.16b, v22.16b
-+    ushr v3.4s, v3.4s, #10
-+    xtn2 v24.8h, v3.4s
-+    and v24.16b, v24.16b, v22.16b
-+    ushr v3.4s, v3.4s, #10
-+    xtn2 v25.8h, v3.4s
-+    and v25.16b, v25.16b, v22.16b
-+
-+    st3 { v23.8h, v24.8h, v25.8h }, [x0], #48
-+
-+    // load the second half of the block -> 64 bytes into registers v4-v7
-+    ld1 { v4.4s,  v5.4s,  v6.4s,  v7.4s }, [x13], #64
-+
-+    // process v4 and v5
-+    xtn v16.4h, v4.4s
-+    ushr v4.4s, v4.4s, #10
-+    xtn v17.4h, v4.4s
-+    ushr v4.4s, v4.4s, #10
-+    xtn v18.4h, v4.4s
-+
-+    xtn2 v16.8h, v5.4s
-+    and v16.16b, v16.16b, v22.16b
-+    ushr v5.4s, v5.4s, #10
-+    xtn2 v17.8h, v5.4s
-+    and v17.16b, v17.16b, v22.16b
-+    ushr v5.4s, v5.4s, #10
-+    xtn2 v18.8h, v5.4s
-+    and v18.16b, v18.16b, v22.16b
-+
-+    st3 { v16.8h, v17.8h, v18.8h }, [x0], #48
-+
-+    // v6 and v7
-+    xtn v23.4h, v6.4s
-+    ushr v6.4s, v6.4s, #10
-+    xtn v24.4h, v6.4s
-+    ushr v6.4s, v6.4s, #10
-+    xtn v25.4h, v6.4s
-+
-+    xtn2 v23.8h, v7.4s
-+    and v23.16b, v23.16b, v22.16b
-+    ushr v7.4s, v7.4s, #10
-+    xtn2 v24.8h, v7.4s
-+    and v24.16b, v24.16b, v22.16b
-+    ushr v7.4s, v7.4s, #10
-+    xtn2 v25.8h, v7.4s
-+    and v25.16b, v25.16b, v22.16b
-+
-+    st3 { v23.8h, v24.8h, v25.8h }, [x0], #48
-+
-+    add x13, x13, x5          // row src += slice_inc
-+    add w14, w14, #1
-+    b block_loop_y16
-+block_loop_y16_fin:
-+
-+
-+
-+
-+    add x2, x2, #128          // src += stride1 (start of the next row)
-+    add x0, x0, w20, sxtw     // subtract the bytes we copied too much from dst
-+    add w12, w12, #1
-+    b row_loop_y16
-+row_loop_y16_fin:
-+
-+    // check whether we have incomplete blocks at the end of every row
-+    // in that case decrease row block count by one
-+    // change height back to it's original value (meaning increase it by 1)
-+    // and jump back to another iteration of row_loop_y16
-+
-+    cmp w23, #1
-+    beq row_loop_y16_fin2 // don't continue here if we already processed the last row
-+    add w6, w6, #1    // increase height to the original value
-+    sub w9, w9, w22   // block count - 1 or 0, depending on the remaining bytes count
-+    mov w23, #1
-+    b row_loop_y16
-+row_loop_y16_fin2:
-+
-+    sub x0, x0, w20, sxtw // with the last row we didn't actually move the dst ptr to far ahead, therefore readd the diference
-+
-+    // now we've got to handle the last block in the last row
-+    eor w12, w12, w12 // w12 = 0 = counter
-+integer_loop_y16:
-+    cmp w12, w10
-+    bge integer_loop_y16_fin
-+    ldr w14, [x13], #4
-+    and w15, w14, #0x3ff
-+    strh w15, [x0], #2
-+    lsr w14, w14, #10
-+    and w15, w14, #0x3ff
-+    strh w15, [x0], #2
-+    lsr w14, w14, #10
-+    and w15, w14, #0x3ff
-+    strh w15, [x0], #2
-+    add w12, w12, #1
-+    b integer_loop_y16
-+integer_loop_y16_fin:
-+
-+final_values_y16:
-+    // remaining point count = w11
-+    ldr w14, [x13], #4
-+    cmp w11, #0
-+    beq final_values_y16_fin
-+    and w15, w14, #0x3ff
-+    strh w15, [x0], #2
-+    cmp w11, #1
-+    beq final_values_y16_fin
-+    lsr w14, w14, #10
-+    and w15, w14, #0x3ff
-+    strh w15, [x0], #2
-+final_values_y16_fin:
-+
-+    ldp x23, x24, [sp, #32]
-+    ldp x21, x22, [sp, #16]
-+    ldp x19, x20, [sp], #48
-+    ret
-+endfunc
-+
 +//void ff_rpi_sand30_lines_to_planar_c16(
 +//  uint8_t * dst_u,            // [x0]
 +//  unsigned int dst_stride_u,  // [w1] == _w*2
@@ -56671,7 +63250,7 @@
 +//  unsigned int dst_stride_v,  // [w3] == _w*2
 +//  const uint8_t * src,        // [x4]
 +//  unsigned int stride1,       // [w5] == 128
-+//  unsigned int stride2,       // [w6]
++//  unsigned int stride2,       // [w6] 
 +//  unsigned int _x,            // [w7] == 0
 +//  unsigned int y,             // [sp, #0] == 0
 +//  unsigned int _w,            // [sp, #8] -> w3
@@ -56694,7 +63273,7 @@
 +    and v5.16b, v5.16b, v16.16b
 +    and v6.16b, v6.16b, v16.16b
 +    st3 { v4.8h, v5.8h, v6.8h }, [sp], #48
-+
++    
 +    xtn v4.4h, v2.4s
 +    ushr v2.4s, v2.4s, #10
 +    xtn v5.4h, v2.4s
@@ -56841,7 +63420,7 @@
 +    ldr w22, [x4], #4
 +    str w22, [x0], #2
 +    lsr w22, w22, #16
-+    str w22, [x2], #2
++    str w22, [x2], #2 
 +
 +    add w20, w20, #1
 +    b rem_pix_c16_loop
@@ -56868,9 +63447,336 @@
 +//  unsigned int _w,
 +//  unsigned int h);
 +
++// void ff_rpi_sand30_lines_to_planar_y8(
++//   uint8_t * dest,            : x0
++//   unsigned int dst_stride,   : w1
++//   const uint8_t * src,       : x2
++//   unsigned int src_stride1,  : w3, always 128
++//   unsigned int src_stride2,  : w4
++//   unsigned int _x,           : w5
++//   unsigned int y,            : w6
++//   unsigned int _w,           : w7
++//   unsigned int h);           : [sp, #0]
++//
++// Assumes that we are starting on a stripe boundary and that overreading
++// within the stripe is OK. However it does respect the dest size for wri
++
++function ff_rpi_sand30_lines_to_planar_y16, export=1
++                lsl             w4,  w4,  #7
++                sub             w4,  w4,  #64
++                sub             w1,  w1,  w7, lsl #1
++                uxtw            x6,  w6
++                add             x8,  x2,  x6, lsl #7
++                ldr             w6,  [sp, #0]
++
++10:
++                mov             x2,  x8
++                mov             w5,  w7
++1:
++                ld1             {v0.4s, v1.4s, v2.4s, v3.4s}, [x2], #64
++                ld1             {v4.4s, v5.4s, v6.4s, v7.4s}, [x2], x4
++
++                subs            w5,  w5,  #96
++
++                // v0, v1
++
++                shrn            v18.4h,  v0.4s,   #14
++                xtn             v16.4h,  v0.4s
++                shrn            v17.4h,  v0.4s,   #10
++
++                shrn2           v18.8h,  v1.4s,   #14
++                xtn2            v16.8h,  v1.4s
++                shrn2           v17.8h,  v1.4s,   #10
++
++                ushr            v18.8h,  v18.8h,  #6
++                bic             v16.8h,  #0xfc,   lsl #8
++                bic             v17.8h,  #0xfc,   lsl #8
++
++                // v2, v3
++
++                shrn            v21.4h,  v2.4s,   #14
++                xtn             v19.4h,  v2.4s
++                shrn            v20.4h,  v2.4s,   #10
++
++                shrn2           v21.8h,  v3.4s,   #14
++                xtn2            v19.8h,  v3.4s
++                shrn2           v20.8h,  v3.4s,   #10
++
++                ushr            v21.8h,  v21.8h,  #6
++                bic             v19.8h,  #0xfc,   lsl #8
++                bic             v20.8h,  #0xfc,   lsl #8
++
++                // v4, v5
++
++                shrn            v24.4h,  v4.4s,   #14
++                xtn             v22.4h,  v4.4s
++                shrn            v23.4h,  v4.4s,   #10
++
++                shrn2           v24.8h,  v5.4s,   #14
++                xtn2            v22.8h,  v5.4s
++                shrn2           v23.8h,  v5.4s,   #10
++
++                ushr            v24.8h,  v24.8h,  #6
++                bic             v22.8h,  #0xfc,   lsl #8
++                bic             v23.8h,  #0xfc,   lsl #8
++
++                // v6, v7
++
++                shrn            v27.4h,  v6.4s,   #14
++                xtn             v25.4h,  v6.4s
++                shrn            v26.4h,  v6.4s,   #10
++
++                shrn2           v27.8h,  v7.4s,   #14
++                xtn2            v25.8h,  v7.4s
++                shrn2           v26.8h,  v7.4s,   #10
++
++                ushr            v27.8h,  v27.8h,  #6
++                bic             v25.8h,  #0xfc,   lsl #8
++                bic             v26.8h,  #0xfc,   lsl #8
++
++                blt             2f
++
++                st3             {v16.8h, v17.8h, v18.8h}, [x0], #48
++                st3             {v19.8h, v20.8h, v21.8h}, [x0], #48
++                st3             {v22.8h, v23.8h, v24.8h}, [x0], #48
++                st3             {v25.8h, v26.8h, v27.8h}, [x0], #48
++
++                bne             1b
++
++11:
++                subs            w6,  w6,  #1
++                add             x0,  x0,  w1,  uxtw
++                add             x8,  x8,  #128
++                bne             10b
++
++                ret
++
++// Partial final write
++2:
++                cmp             w5,  #48-96
++                blt             1f
++                st3             {v16.8h, v17.8h, v18.8h}, [x0], #48
++                st3             {v19.8h, v20.8h, v21.8h}, [x0], #48
++                beq             11b
++                mov             v16.16b, v22.16b
++                mov             v17.16b, v23.16b
++                sub             w5,  w5,  #48
++                mov             v18.16b, v24.16b
++                mov             v19.16b, v25.16b
++                mov             v20.16b, v26.16b
++                mov             v21.16b, v27.16b
++1:
++                cmp             w5,  #24-96
++                blt             1f
++                st3             {v16.8h, v17.8h, v18.8h}, [x0], #48
++                beq             11b
++                mov             v16.16b, v19.16b
++                mov             v17.16b, v20.16b
++                sub             w5,  w5,  #24
++                mov             v18.16b, v21.16b
++1:
++                cmp             w5,  #12-96
++                blt             1f
++                st3             {v16.4h, v17.4h, v18.4h}, [x0], #24
++                beq             11b
++                mov             v16.2d[0], v16.2d[1]
++                sub             w5,  w5,  #12
++                mov             v17.2d[0], v17.2d[1]
++                mov             v18.2d[0], v18.2d[1]
++1:
++                cmp             w5,  #6-96
++                blt             1f
++                st3             {v16.h, v17.h, v18.h}[0], [x0], #6
++                st3             {v16.h, v17.h, v18.h}[1], [x0], #6
++                beq             11b
++                mov             v16.2s[0], v16.2s[1]
++                sub             w5,  w5,  #6
++                mov             v17.2s[0], v17.2s[1]
++                mov             v18.2s[0], v18.2s[1]
++1:
++                cmp             w5,  #3-96
++                blt             1f
++                st3             {v16.h, v17.h, v18.h}[0], [x0], #6
++                beq             11b
++                mov             v16.4h[0], v16.4h[1]
++                sub             w5,  w5,  #3
++                mov             v17.4h[0], v17.4h[1]
++1:
++                cmp             w5,  #2-96
++                blt             1f
++                st2             {v16.h, v17.h}[0], [x0], #4
++                b               11b
++1:
++                st1             {v16.h}[0], [x0], #2
++                b               11b
++
++endfunc
++
++// void ff_rpi_sand30_lines_to_planar_y8(
++//   uint8_t * dest,            : x0
++//   unsigned int dst_stride,   : w1
++//   const uint8_t * src,       : x2
++//   unsigned int src_stride1,  : w3, always 128
++//   unsigned int src_stride2,  : w4
++//   unsigned int _x,           : w5
++//   unsigned int y,            : w6
++//   unsigned int _w,           : w7
++//   unsigned int h);           : [sp, #0]
++//
++// Assumes that we are starting on a stripe boundary and that overreading
++// within the stripe is OK. However it does respect the dest size for wri
++
++function ff_rpi_sand30_lines_to_planar_y8, export=1
++                lsl             w4,  w4,  #7
++                sub             w4,  w4,  #64
++                sub             w1,  w1,  w7
++                uxtw            x6,  w6
++                add             x8,  x2,  x6, lsl #7
++                ldr             w6,  [sp, #0]
++
++10:
++                mov             x2,  x8
++                mov             w5,  w7
++1:
++                ld1             {v0.4s, v1.4s, v2.4s, v3.4s}, [x2], #64
++                ld1             {v4.4s, v5.4s, v6.4s, v7.4s}, [x2], x4
++
++                subs            w5,  w5,  #96
++
++                // v0, v1
++
++                shrn            v18.4h,  v0.4s,   #16
++                xtn             v16.4h,  v0.4s
++                shrn            v17.4h,  v0.4s,   #12
++
++                shrn2           v18.8h,  v1.4s,   #16
++                xtn2            v16.8h,  v1.4s
++                shrn2           v17.8h,  v1.4s,   #12
++
++                shrn            v18.8b,  v18.8h,  #6
++                shrn            v16.8b,  v16.8h,  #2
++                xtn             v17.8b,  v17.8h
++
++                // v2, v3
++
++                shrn            v21.4h,  v2.4s,   #16
++                xtn             v19.4h,  v2.4s
++                shrn            v20.4h,  v2.4s,   #12
++
++                shrn2           v21.8h,  v3.4s,   #16
++                xtn2            v19.8h,  v3.4s
++                shrn2           v20.8h,  v3.4s,   #12
++
++                shrn2           v18.16b, v21.8h,  #6
++                shrn2           v16.16b, v19.8h,  #2
++                xtn2            v17.16b, v20.8h
++
++                // v4, v5
++
++                shrn            v24.4h,  v4.4s,   #16
++                xtn             v22.4h,  v4.4s
++                shrn            v23.4h,  v4.4s,   #12
++
++                shrn2           v24.8h,  v5.4s,   #16
++                xtn2            v22.8h,  v5.4s
++                shrn2           v23.8h,  v5.4s,   #12
++
++                shrn            v21.8b,  v24.8h,  #6
++                shrn            v19.8b,  v22.8h,  #2
++                xtn             v20.8b,  v23.8h
++
++                // v6, v7
++
++                shrn            v27.4h,  v6.4s,   #16
++                xtn             v25.4h,  v6.4s
++                shrn            v26.4h,  v6.4s,   #12
++
++                shrn2           v27.8h,  v7.4s,   #16
++                xtn2            v25.8h,  v7.4s
++                shrn2           v26.8h,  v7.4s,   #12
++
++                shrn2           v21.16b, v27.8h,  #6
++                shrn2           v19.16b, v25.8h,  #2
++                xtn2            v20.16b, v26.8h
++
++                blt             2f
++
++                st3             {v16.16b, v17.16b, v18.16b}, [x0], #48
++                st3             {v19.16b, v20.16b, v21.16b}, [x0], #48
++
++                bne             1b
++
++11:
++                subs            w6,  w6,  #1
++                add             x0,  x0,  w1,  uxtw
++                add             x8,  x8,  #128
++                bne             10b
++
++                ret
++
++// Partial final write
++2:
++                cmp             w5,  #48-96
++                blt             1f
++                st3             {v16.16b, v17.16b, v18.16b}, [x0], #48
++                beq             11b
++                mov             v16.16b, v22.16b
++                mov             v17.16b, v23.16b
++                sub             w5,  w5,  #48
++                mov             v18.16b, v24.16b
++1:
++                cmp             w5,  #24-96
++                blt             1f
++                st3             {v16.8b, v17.8b, v18.8b}, [x0], #24
++                beq             11b
++                mov             v16.2d[0], v16.2d[1]
++                sub             w5,  w5,  #24
++                mov             v17.2d[0], v17.2d[1]
++                mov             v18.2d[0], v18.2d[1]
++1:
++                cmp             w5,  #12-96
++                blt             1f
++                st3             {v16.b, v17.b, v18.b}[0], [x0], #3
++                st3             {v16.b, v17.b, v18.b}[1], [x0], #3
++                st3             {v16.b, v17.b, v18.b}[2], [x0], #3
++                st3             {v16.b, v17.b, v18.b}[3], [x0], #3
++                beq             11b
++                mov             v16.2s[0], v16.2s[1]
++                sub             w5,  w5,  #12
++                mov             v17.2s[0], v17.2s[1]
++                mov             v18.2s[0], v18.2s[1]
++1:
++                cmp             w5,  #6-96
++                blt             1f
++                st3             {v16.b, v17.b, v18.b}[0], [x0], #3
++                st3             {v16.b, v17.b, v18.b}[1], [x0], #3
++                beq             11b
++                mov             v16.4h[0], v16.4h[1]
++                sub             w5,  w5,  #6
++                mov             v17.4h[0], v17.4h[1]
++                mov             v18.4h[0], v18.4h[1]
++1:
++                cmp             w5,  #3-96
++                blt             1f
++                st3             {v16.b, v17.b, v18.b}[0], [x0], #3
++                beq             11b
++                mov             v16.8b[0], v16.8b[1]
++                sub             w5,  w5,  #3
++                mov             v17.8b[0], v17.8b[1]
++1:
++                cmp             w5,  #2-96
++                blt             1f
++                st2             {v16.b, v17.b}[0], [x0], #2
++                b               11b
++1:
++                st1             {v16.b}[0], [x0], #1
++                b               11b
++
++endfunc
++
 --- /dev/null
 +++ b/libavutil/aarch64/rpi_sand_neon.h
-@@ -0,0 +1,55 @@
+@@ -0,0 +1,59 @@
 +/*
 +Copyright (c) 2021 Michael Eiler
 +
@@ -56922,6 +63828,10 @@
 +  uint8_t * dst_v, unsigned int dst_stride_v, const uint8_t * src, unsigned int stride1,
 +  unsigned int stride2, unsigned int _x, unsigned int y, unsigned int _w, unsigned int h);
 +
++void ff_rpi_sand30_lines_to_planar_y8(uint8_t * dest, unsigned int dst_stride,
++  const uint8_t * src, unsigned int src_stride1, unsigned int src_stride2,
++  unsigned int _x, unsigned int y, unsigned int _w, unsigned int h);
++
 +#ifdef __cplusplus
 +}
 +#endif
@@ -56929,13 +63839,13 @@
 --- a/libavutil/arm/Makefile
 +++ b/libavutil/arm/Makefile
 @@ -6,3 +6,4 @@ VFP-OBJS += arm/float_dsp_init_vfp.o
-
+ 
  NEON-OBJS += arm/float_dsp_init_neon.o                                  \
               arm/float_dsp_neon.o                                       \
 +             arm/rpi_sand_neon.o                                        \
 --- /dev/null
 +++ b/libavutil/arm/rpi_sand_neon.S
-@@ -0,0 +1,768 @@
+@@ -0,0 +1,925 @@
 +/*
 +Copyright (c) 2018 Raspberry Pi (Trading) Ltd.
 +All rights reserved.
@@ -57298,7 +64208,6 @@
 +                ldr             r6,  [sp, #36]
 +                ldr             r7,  [sp, #32]  @ y
 +                mov             r12, #48
-+                vmov.u16        q15, #0x3ff
 +                sub             r3,  #1
 +                lsl             r3,  #7
 +                sub             r1,  r1,  r6,  lsl #1
@@ -57314,37 +64223,33 @@
 +                vldm            r2!, {q10-q13}
 +                add             lr,  #64
 +
-+                vshr.u32        q14, q10, #20    @ Cannot vshrn.u32 #20!
++                vshrn.u32       d4 , q10, #14    @ Cannot vshrn.u32 #20!
 +                ands            lr,  #127
 +                vshrn.u32       d2,  q10, #10
 +                vmovn.u32       d0,  q10
-+                vmovn.u32       d4,  q14
 +
-+                vshr.u32        q14, q11, #20
++                vshrn.u32       d5,  q11, #14
 +                it              eq
 +                addeq           r2,  r3
 +                vshrn.u32       d3,  q11, #10
 +                vmovn.u32       d1,  q11
-+                vmovn.u32       d5,  q14
 +
 +                subs            r5,  #48
-+                vand            q0,  q15
-+                vand            q1,  q15
-+                vand            q2,  q15
++                vshr.u16        q2,  #6
++                vbic.u16        q0,  #0xfc00
++                vbic.u16        q1,  #0xfc00
 +
-+                vshr.u32        q14, q12, #20
++                vshrn.u32       d20, q12, #14
 +                vshrn.u32       d18, q12, #10
 +                vmovn.u32       d16, q12
-+                vmovn.u32       d20, q14
 +
-+                vshr.u32        q14, q13, #20
++                vshrn.u32       d21, q13, #14
 +                vshrn.u32       d19, q13, #10
 +                vmovn.u32       d17, q13
-+                vmovn.u32       d21, q14
 +
-+                vand            q8,  q15
-+                vand            q9,  q15
-+                vand            q10, q15
++                vshr.u16        q10, #6
++                vbic.u16        q8,  #0xfc00
++                vbic.u16        q9 , #0xfc00
 +                blt             2f
 +
 +                vst3.16         {d0,  d2,  d4},  [r0], r12
@@ -57437,7 +64342,6 @@
 +                ldr             r7,  [sp, #48]
 +                ldr             r9,  [sp, #52]
 +                mov             r12, #48
-+                vmov.u16        q15, #0x3ff
 +                sub             r8,  #1
 +                lsl             r8,  #7
 +                add             r5,  r5,  r7,  lsl #7
@@ -57453,48 +64357,44 @@
 +                add             lr,  #64
 +
 +                @ N.B. unpack [0,1,2] -> (reg order) 1, 0, 2
-+                vshr.u32        q14, q0,  #20
-+                vshrn.u32       d16, q0,  #10
++                vshrn.u32       d20, q0,  #14
 +                vmovn.u32       d18, q0
++                vshrn.u32       d0,  q0,  #10
 +                ands            lr,  #127
-+                vmovn.u32       d20, q14
 +
-+                vshr.u32        q14, q1,  #20
-+                vshrn.u32       d17, q1,  #10
++                vshrn.u32       d21, q1,  #14
 +                vmovn.u32       d19, q1
-+                vmovn.u32       d21, q14
++                vshrn.u32       d1,  q1,  #10
 +
-+                vshr.u32        q14, q2,  #20
 +                vshrn.u32       d22, q2,  #10
-+                vmovn.u32       d24, q2
-+                vmovn.u32       d26, q14
++                vmovn.u32       d2,  q2
++                vshrn.u32       d4,  q2,  #14
 +
-+                vshr.u32        q14, q3,  #20
-+                vshrn.u32       d23, q3,  #10
-+                vmovn.u32       d25, q3
 +                add             r10, r0,  #24
-+                vmovn.u32       d27, q14
++                vshrn.u32       d23, q3,  #10
++                vmovn.u32       d3,  q3
++                vshrn.u32       d5,  q3,  #14
 +
 +                it              eq
 +                addeq           r4,  r8
-+                vuzp.16         q8,  q11
-+                vuzp.16         q9,  q12
-+                vuzp.16         q10, q13
++                vuzp.16         q0,  q11
++                vuzp.16         q9,  q1
++                vuzp.16         q10, q2
 +
-+                @ q8   V0, V3,.. -> q0
++                @ q0   V0, V3,..
 +                @ q9   U0, U3...
 +                @ q10  U1, U4...
 +                @ q11  U2, U5,..
-+                @ q12  V1, V4,.. -> q1
-+                @ q13  V2, V5,.. -> q2
++                @ q1   V1, V4,
++                @ q2   V2, V5,..
 +
 +                subs            r6,  #24
-+                vand            q11, q15
-+                vand            q9,  q15
-+                vand            q10, q15
-+                vand            q0,  q8,  q15
-+                vand            q1,  q12, q15
-+                vand            q2,  q13, q15
++                vbic.u16        q11, #0xfc00
++                vbic.u16        q9,  #0xfc00
++                vshr.u16        q10, #6
++                vshr.u16        q2,  #6
++                vbic.u16        q0,  #0xfc00
++                vbic.u16        q1,  #0xfc00
 +
 +                blt             2f
 +
@@ -57703,10 +64603,177 @@
 +endfunc
 +
 +
++@ void ff_rpi_sand30_lines_to_planar_y8(
++@   uint8_t * dest,             // [r0]
++@   unsigned int dst_stride,    // [r1]
++@   const uint8_t * src,        // [r2]
++@   unsigned int src_stride1,   // [r3]      Ignored - assumed 128
++@   unsigned int src_stride2,   // [sp, #0]  -> r3
++@   unsigned int _x,            // [sp, #4]  Ignored - 0
++@   unsigned int y,             // [sp, #8]  (r7 in prefix)
++@   unsigned int _w,            // [sp, #12] -> r6 (cur r5)
++@   unsigned int h);            // [sp, #16] -> r7
++@
++@ Assumes that we are starting on a stripe boundary and that overreading
++@ within the stripe is OK. However it does respect the dest size for wri
++
++function ff_rpi_sand30_lines_to_planar_y8, export=1
++                push            {r4-r8, lr}     @ +24
++                ldr             r3,  [sp, #24]
++                ldr             r6,  [sp, #36]
++                ldr             r7,  [sp, #32]  @ y
++                mov             r12, #48
++                lsl             r3,  #7
++                sub             r1,  r1,  r6
++                add             r8,  r2,  r7,  lsl #7
++                ldr             r7,  [sp, #40]
++
++10:
++                mov             r2,  r8
++                add             r4,  r0,  #24
++                mov             r5,  r6
++1:
++                vldm            r2,  {q8-q15}
++
++                subs            r5,  #96
++
++                vmovn.u32       d0,  q8
++                vshrn.u32       d2,  q8,  #12
++                vshrn.u32       d4,  q8,  #16    @ Cannot vshrn.u32 #20!
++
++                add             r2,  r3
++
++                vmovn.u32       d1,  q9
++                vshrn.u32       d3,  q9,  #12
++                vshrn.u32       d5,  q9,  #16
++
++                pld             [r2, #0]
++
++                vshrn.u16       d0,  q0,  #2
++                vmovn.u16       d1,  q1
++                vshrn.u16       d2,  q2,  #6
++
++                vmovn.u32       d16, q10
++                vshrn.u32       d18, q10, #12
++                vshrn.u32       d20, q10, #16
++
++                vmovn.u32       d17, q11
++                vshrn.u32       d19, q11, #12
++                vshrn.u32       d21, q11, #16
++
++                pld             [r2, #64]
++
++                vshrn.u16       d4,  q8,  #2
++                vmovn.u16       d5,  q9
++                vshrn.u16       d6,  q10, #6
++
++                vmovn.u32       d16, q12
++                vshrn.u32       d18, q12, #12
++                vshrn.u32       d20, q12, #16
++
++                vmovn.u32       d17, q13
++                vshrn.u32       d19, q13, #12
++                vshrn.u32       d21, q13, #16
++
++                vshrn.u16       d16, q8,  #2
++                vmovn.u16       d17, q9
++                vshrn.u16       d18, q10, #6
++
++                vmovn.u32       d20, q14
++                vshrn.u32       d22, q14, #12
++                vshrn.u32       d24, q14, #16
++
++                vmovn.u32       d21, q15
++                vshrn.u32       d23, q15, #12
++                vshrn.u32       d25, q15, #16
++
++                vshrn.u16       d20, q10, #2
++                vmovn.u16       d21, q11
++                vshrn.u16       d22, q12, #6
++
++                blt             2f
++
++                vst3.8          {d0,  d1,  d2},  [r0], r12
++                vst3.8          {d4,  d5,  d6},  [r4], r12
++                vst3.8          {d16, d17, d18}, [r0], r12
++                vst3.8          {d20, d21, d22}, [r4], r12
++
++                bne             1b
++
++11:
++                subs            r7,  #1
++                add             r0,  r1
++                add             r8,  #128
++                bne             10b
++
++                pop             {r4-r8, pc}
++
++@ Partial final write
++2:
++                cmp             r5,  #48-96
++                blt             1f
++                vst3.8          {d0,  d1,  d2},  [r0], r12
++                vst3.8          {d4,  d5,  d6},  [r4], r12
++                beq             11b
++                vmov            q0,  q8
++                vmov            q2,  q10
++                sub             r5,  #48
++                vmov            d2,  d18
++                vmov            d6,  d22
++1:
++                cmp             r5,  #24-96
++                blt             1f
++                vst3.8          {d0,  d1,  d2},  [r0]!
++                beq             11b
++                vmov            q0,  q2
++                sub             r5,  #24
++                vmov            d2,  d6
++1:
++                cmp             r5,  #12-96
++                blt             1f
++                vst3.8          {d0[0], d1[0], d2[0]}, [r0]!
++                vst3.8          {d0[1], d1[1], d2[1]}, [r0]!
++                vst3.8          {d0[2], d1[2], d2[2]}, [r0]!
++                vst3.8          {d0[3], d1[3], d2[3]}, [r0]!
++                beq             11b
++                vmov            s0,  s1
++                sub             r5,  #12
++                vmov            s2,  s3
++                vmov            s4,  s5
++1:
++                cmp             r5,  #6-96
++                blt             1f
++                vst3.8          {d0[0], d1[0], d2[0]}, [r0]!
++                vst3.8          {d0[1], d1[1], d2[1]}, [r0]!
++                add             r0,  #12
++                beq             11b
++                vshr.u32        d0,  #16
++                sub             r5,  #6
++                vshr.u32        d1,  #16
++                vshr.u32        d2,  #16
++1:
++                cmp             r5, #3-96
++                blt             1f
++                vst3.8          {d0[0], d1[0], d2[0]}, [r0]!
++                beq             11b
++                sub             r5, #3
++                vshr.u32        d0, #8
++                vshr.u32        d1, #8
++1:
++                cmp             r5, #2-96
++                blt             1f
++                vst2.8          {d0[0], d1[0]}, [r0]!
++                b               11b
++1:
++                vst1.8          {d0[0]}, [r0]!
++                b               11b
++
++endfunc
++
 +
 --- /dev/null
 +++ b/libavutil/arm/rpi_sand_neon.h
-@@ -0,0 +1,99 @@
+@@ -0,0 +1,110 @@
 +/*
 +Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
 +All rights reserved.
@@ -57804,6 +64871,17 @@
 +  unsigned int _w,            // [sp, #12] -> r6 (cur r5)
 +  unsigned int h);            // [sp, #16] -> r7
 +
++void ff_rpi_sand30_lines_to_planar_y8(
++  uint8_t * dest,             // [r0]
++  unsigned int dst_stride,    // [r1]
++  const uint8_t * src,        // [r2]
++  unsigned int src_stride1,   // [r3]      Ignored - assumed 128
++  unsigned int src_stride2,   // [sp, #0]  -> r3
++  unsigned int _x,            // [sp, #4]  Ignored - 0
++  unsigned int y,             // [sp, #8]  (r7 in prefix)
++  unsigned int _w,            // [sp, #12] -> r6 (cur r5)
++  unsigned int h);            // [sp, #16] -> r7
++
 +#endif // AVUTIL_ARM_SAND_NEON_H
 +
 --- a/libavutil/frame.c
@@ -57811,7 +64889,7 @@
 @@ -16,6 +16,8 @@
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
   */
-
+ 
 +#include "config.h"
 +
  #include "channel_layout.h"
@@ -57824,13 +64902,13 @@
 +#if CONFIG_SAND
 +#include "rpi_sand_fns.h"
 +#endif
-
+ 
  #if FF_API_FRAME_GET_SET
  MAKE_ACCESSORS(AVFrame, frame, int64_t, best_effort_timestamp)
 @@ -902,6 +907,12 @@ int av_frame_apply_cropping(AVFrame *fra
          (frame->crop_top + frame->crop_bottom) >= frame->height)
          return AVERROR(ERANGE);
-
+ 
 +#if CONFIG_SAND
 +    // Sand cannot be cropped - do not try
 +    if (av_rpi_is_sand_format(frame->format))
@@ -57845,7 +64923,7 @@
 @@ -968,6 +968,16 @@ int av_frame_apply_cropping(AVFrame *fra
   */
  const char *av_frame_side_data_name(enum AVFrameSideDataType type);
-
+ 
 +
 +static inline int av_frame_cropped_width(const AVFrame * const frame)
 +{
@@ -57866,11 +64944,11 @@
  #include <sys/mman.h>
  #include <unistd.h>
 +#include <sys/ioctl.h>
-
+ 
  #include <drm.h>
 +#include <libdrm/drm_fourcc.h>
  #include <xf86drm.h>
-
+ 
  #include "avassert.h"
 @@ -28,6 +30,11 @@
  #include "hwcontext_drm.h"
@@ -57881,13 +64959,13 @@
 +#include <linux/mman.h>
 +#include <linux/dma-buf.h>
 +#include <linux/dma-heap.h>
-
-
+ 
+ 
  static void drm_device_free(AVHWDeviceContext *hwdev)
 @@ -43,6 +50,11 @@ static int drm_device_create(AVHWDeviceC
      AVDRMDeviceContext *hwctx = hwdev->hwctx;
      drmVersionPtr version;
-
+ 
 +    if (device == NULL) {
 +      hwctx->fd = -1;
 +      return 0;
@@ -57905,7 +64983,7 @@
      size_t length[AV_DRM_MAX_PLANES];
 +    int fds[AV_DRM_MAX_PLANES];
  } DRMMapping;
-
+ 
 +static int dmasync(const int fd, const unsigned int flags)
 +{
 +    struct dma_buf_sync sync = {
@@ -57926,19 +65004,19 @@
  {
      DRMMapping *map = hwmap->priv;
      int i;
-
+ 
 -    for (i = 0; i < map->nb_regions; i++)
 +    for (i = 0; i < map->nb_regions; i++) {
          munmap(map->address[i], map->length[i]);
 +        dmasync(map->fds[i], DMA_BUF_SYNC_END | map->dmaflags);
 +    }
-
+ 
      av_free(map);
  }
 @@ -114,15 +145,28 @@ static int drm_map_frame(AVHWFramesConte
      if (!map)
          return AVERROR(ENOMEM);
-
+ 
 +    for (i = 0; i < AV_DRM_MAX_PLANES; i++)
 +        map->fds[i] = -1;
 +
@@ -57956,7 +65034,7 @@
 +
 +    if (dst->format == AV_PIX_FMT_NONE)
 +        dst->format = hwfc->sw_format;
-
+ 
      av_assert0(desc->nb_objects <= AV_DRM_MAX_PLANES);
      for (i = 0; i < desc->nb_objects; i++) {
 -        addr = mmap(NULL, desc->objects[i].size, mmap_prot, MAP_SHARED,
@@ -57968,7 +65046,7 @@
          if (addr == MAP_FAILED) {
              err = AVERROR(errno);
 @@ -151,6 +195,23 @@ static int drm_map_frame(AVHWFramesConte
-
+ 
      dst->width  = src->width;
      dst->height = src->height;
 +    dst->crop_top    = src->crop_top;
@@ -57988,12 +65066,12 @@
 +        // *** Are we sure src->height is actually what we want ???
 +    }
 +#endif
-
+ 
      err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src,
                                  &drm_unmap_frame, map);
 @@ -160,7 +221,9 @@ static int drm_map_frame(AVHWFramesConte
      return 0;
-
+ 
  fail:
 -    for (i = 0; i < desc->nb_objects; i++) {
 +    for (i = 0; i < AV_DRM_MAX_PLANES; i++) {
@@ -58002,13 +65080,23 @@
          if (map->address[i])
              munmap(map->address[i], map->length[i]);
      }
-@@ -178,7 +241,15 @@ static int drm_transfer_get_formats(AVHW
-     if (!pix_fmts)
+@@ -172,16 +235,29 @@ static int drm_transfer_get_formats(AVHW
+                                     enum AVHWFrameTransferDirection dir,
+                                     enum AVPixelFormat **formats)
+ {
+-    enum AVPixelFormat *pix_fmts;
++    enum AVPixelFormat *p;
+ 
+-    pix_fmts = av_malloc_array(2, sizeof(*pix_fmts));
+-    if (!pix_fmts)
++    p = *formats = av_malloc_array(3, sizeof(*p));
++    if (!p)
          return AVERROR(ENOMEM);
-
+ 
 -    pix_fmts[0] = ctx->sw_format;
+-    pix_fmts[1] = AV_PIX_FMT_NONE;
 +    // **** Offer native sand too ????
-+    pix_fmts[0] =
++    *p++ =
 +#if CONFIG_SAND
 +        ctx->sw_format == AV_PIX_FMT_RPI4_8 || ctx->sw_format == AV_PIX_FMT_SAND128 ?
 +            AV_PIX_FMT_YUV420P :
@@ -58016,21 +65104,30 @@
 +            AV_PIX_FMT_YUV420P10LE :
 +#endif
 +            ctx->sw_format;
-     pix_fmts[1] = AV_PIX_FMT_NONE;
-
-     *formats = pix_fmts;
-@@ -197,18 +268,80 @@ static int drm_transfer_data_from(AVHWFr
++
++#if CONFIG_SAND
++    if (ctx->sw_format == AV_PIX_FMT_RPI4_10 ||
++        ctx->sw_format == AV_PIX_FMT_RPI4_8 || ctx->sw_format == AV_PIX_FMT_SAND128)
++        *p++ = AV_PIX_FMT_NV12;
++#endif
+ 
+-    *formats = pix_fmts;
++    *p = AV_PIX_FMT_NONE;
+     return 0;
+ }
+ 
+@@ -197,18 +273,63 @@ static int drm_transfer_data_from(AVHWFr
      map = av_frame_alloc();
      if (!map)
          return AVERROR(ENOMEM);
 -    map->format = dst->format;
-
+ 
 +    // Map to default
 +    map->format = AV_PIX_FMT_NONE;
      err = drm_map_frame(hwfc, map, src, AV_HWFRAME_MAP_READ);
      if (err)
          goto fail;
-
+ 
 -    map->width  = dst->width;
 -    map->height = dst->height;
 +#if 0
@@ -58054,29 +65151,12 @@
 +        const unsigned int w = FFMIN(dst->width, map->width);
 +        const unsigned int h = FFMIN(dst->height, map->height);
 +
-+        if (map->format == AV_PIX_FMT_RPI4_8 && dst->format == AV_PIX_FMT_YUV420P) {
-+            av_rpi_sand_to_planar_y8(dst->data[0], dst->linesize[0],
-+                                     map->data[0],
-+                                     128, stride2,
-+                                     0, 0, w, h);
-+            av_rpi_sand_to_planar_c8(dst->data[1], dst->linesize[1],
-+                                     dst->data[2], dst->linesize[2],
-+                                     map->data[1],
-+                                     128, stride2,
-+                                     0, 0, w / 2, h / 2);
-+        }
-+        else if (map->format == AV_PIX_FMT_RPI4_10 && dst->format == AV_PIX_FMT_YUV420P10LE) {
-+            av_rpi_sand30_to_planar_y16(dst->data[0], dst->linesize[0],
-+                                     map->data[0],
-+                                     128, stride2,
-+                                     0, 0, w, h);
-+            av_rpi_sand30_to_planar_c16(dst->data[1], dst->linesize[1],
-+                                     dst->data[2], dst->linesize[2],
-+                                     map->data[1],
-+                                     128, stride2,
-+                                     0, 0, w / 2, h / 2);
-+        }
-+        else
++        map->crop_top = 0;
++        map->crop_bottom = 0;
++        map->crop_left = 0;
++        map->crop_right = 0;
++
++        if (av_rpi_sand_to_planar_frame(dst, map) != 0)
 +        {
 +            av_log(hwfc, AV_LOG_ERROR, "%s: Incompatible output pixfmt for sand\n", __func__);
 +            err = AVERROR(EINVAL);
@@ -58094,30 +65174,30 @@
 +        map->height = dst->height;
 +        err = av_frame_copy(dst, map);
 +    }
-
+ 
 -    err = av_frame_copy(dst, map);
      if (err)
 +    {
 +        av_log(hwfc, AV_LOG_ERROR, "%s: Copy fail\n", __func__);
          goto fail;
 +    }
-
+ 
      err = 0;
  fail:
-@@ -223,7 +356,10 @@ static int drm_transfer_data_to(AVHWFram
+@@ -223,7 +344,10 @@ static int drm_transfer_data_to(AVHWFram
      int err;
-
+ 
      if (src->width > hwfc->width || src->height > hwfc->height)
 +    {
 +        av_log(hwfc, AV_LOG_ERROR, "%s: H/w mismatch: %d/%d, %d/%d\n", __func__, dst->width, hwfc->width, dst->height, hwfc->height);
          return AVERROR(EINVAL);
 +    }
-
+ 
      map = av_frame_alloc();
      if (!map)
 --- a/libavutil/pixdesc.c
 +++ b/libavutil/pixdesc.c
-@@ -2371,6 +2371,38 @@ static const AVPixFmtDescriptor av_pix_f
+@@ -2371,6 +2371,50 @@ static const AVPixFmtDescriptor av_pix_f
          .name = "vulkan",
          .flags = AV_PIX_FMT_FLAG_HWACCEL,
      },
@@ -58140,17 +65220,29 @@
 +        .log2_chroma_h = 1,
 +        .comp = {
 +            { 0, 2, 0, 0, 10, 0, 9, 1 },        /* Y */
-+            { 1, 4, 0, 0, 10, 1, 9, 1 },        /* U */
-+            { 1, 4, 1, 0, 10, 1, 9, 2 },        /* V */
++            { 1, 4, 0, 0, 10, 3, 9, 1 },        /* U */
++            { 1, 4, 2, 0, 10, 3, 9, 3 },        /* V */
++        },
++        .flags = 0,
++    },
++    [AV_PIX_FMT_SAND64_16] = {
++        .name = "sand64_16",
++        .nb_components = 3,
++        .log2_chroma_w = 1,
++        .log2_chroma_h = 1,
++        .comp = {
++            { 0, 2, 0, 0, 16, 0, 15, 1 },        /* Y */
++            { 1, 4, 0, 0, 16, 3, 15, 1 },        /* U */
++            { 1, 4, 2, 0, 16, 3, 15, 3 },        /* V */
 +        },
 +        .flags = 0,
 +    },
 +    [AV_PIX_FMT_RPI4_8] = {
-+        .name = "rpi",
++        .name = "rpi4_8",
 +        .flags = AV_PIX_FMT_FLAG_HWACCEL,
 +    },
 +    [AV_PIX_FMT_RPI4_10] = {
-+        .name = "rpi",
++        .name = "rpi4_10",
 +        .flags = AV_PIX_FMT_FLAG_HWACCEL,
 +    },
  };
@@ -58159,7 +65251,7 @@
 --- a/libavutil/pixfmt.h
 +++ b/libavutil/pixfmt.h
 @@ -357,6 +357,12 @@ enum AVPixelFormat {
-
+ 
      AV_PIX_FMT_Y210BE,    ///< packed YUV 4:2:2 like YUYV422, 20bpp, data in the high bits, big-endian
      AV_PIX_FMT_Y210LE,    ///< packed YUV 4:2:2 like YUYV422, 20bpp, data in the high bits, little-endian
 +// RPI - not on ifdef so can be got at by calling progs
@@ -58168,7 +65260,7 @@
 +    AV_PIX_FMT_SAND64_16,  ///< 4:2:0 16-bit  64x*Y stripe, 32x*UV stripe, then next x stripe, mysterious padding
 +    AV_PIX_FMT_RPI4_8,
 +    AV_PIX_FMT_RPI4_10,
-
+ 
      AV_PIX_FMT_NB         ///< number of pixel formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions
  };
 --- /dev/null
@@ -58403,7 +65495,7 @@
 +
 --- /dev/null
 +++ b/libavutil/rpi_sand_fns.c
-@@ -0,0 +1,356 @@
+@@ -0,0 +1,445 @@
 +/*
 +Copyright (c) 2018 Raspberry Pi (Trading) Ltd.
 +All rights reserved.
@@ -58635,6 +65727,75 @@
 +    }
 +}
 +
++// Fetches a single patch - offscreen fixup not done here
++// w <= stride1
++// single lose bottom 2 bits truncation
++// _x & _w in pixels, strides in bytes
++void av_rpi_sand30_to_planar_y8(uint8_t * dst, const unsigned int dst_stride,
++                             const uint8_t * src,
++                             unsigned int stride1, unsigned int stride2,
++                             unsigned int _x, unsigned int y,
++                             unsigned int _w, unsigned int h)
++{
++    const unsigned int x0 = (_x / 3) * 4; // Byte offset of the word
++    const unsigned int xskip0 = _x - (x0 >> 2) * 3;
++    const unsigned int x1 = ((_x + _w) / 3) * 4;
++    const unsigned int xrem1 = _x + _w - (x1 >> 2) * 3;
++    const unsigned int mask = stride1 - 1;
++    const uint8_t * p0 = src + (x0 & mask) + y * stride1 + (x0 & ~mask) * stride2;
++    const unsigned int slice_inc = ((stride2 - 1) * stride1) >> 2;  // RHS of a stripe to LHS of next in words
++
++#if HAVE_SAND_ASM
++    if (_x == 0) {
++        ff_rpi_sand30_lines_to_planar_y8(dst, dst_stride, src, stride1, stride2, _x, y, _w, h);
++        return;
++    }
++#endif
++
++    if (x0 == x1) {
++        // *******************
++        // Partial single word xfer
++        return;
++    }
++
++    for (unsigned int i = 0; i != h; ++i, dst += dst_stride, p0 += stride1)
++    {
++        unsigned int x = x0;
++        const uint32_t * p = (const uint32_t *)p0;
++        uint8_t * d = dst;
++
++        if (xskip0 != 0) {
++            const uint32_t p3 = *p++;
++
++            if (xskip0 == 1)
++                *d++ = (p3 >> 12) & 0xff;
++            *d++ = (p3 >> 22) & 0xff;
++
++            if (((x += 4) & mask) == 0)
++                p += slice_inc;
++        }
++
++        while (x != x1) {
++            const uint32_t p3 = *p++;
++            *d++ = (p3 >> 2) & 0xff;
++            *d++ = (p3 >> 12) & 0xff;
++            *d++ = (p3 >> 22) & 0xff;
++
++            if (((x += 4) & mask) == 0)
++                p += slice_inc;
++        }
++
++        if (xrem1 != 0) {
++            const uint32_t p3 = *p;
++
++            *d++ = (p3 >> 2) & 0xff;
++            if (xrem1 == 2)
++                *d++ = (p3 >> 12) & 0xff;
++        }
++    }
++}
++
++
 +
 +// w/h in pixels
 +void av_rpi_sand16_to_sand8(uint8_t * dst, const unsigned int dst_stride1, const unsigned int dst_stride2,
@@ -58716,6 +65877,16 @@
 +                                             av_rpi_sand_frame_stride1(src), av_rpi_sand_frame_stride2(src),
 +                                             x/2, y/2,  w/2, h/2);
 +                    break;
++                case AV_PIX_FMT_NV12:
++                    av_rpi_sand_to_planar_y8(dst->data[0], dst->linesize[0],
++                                             src->data[0],
++                                             av_rpi_sand_frame_stride1(src), av_rpi_sand_frame_stride2(src),
++                                             x, y, w, h);
++                    av_rpi_sand_to_planar_y8(dst->data[1], dst->linesize[1],
++                                             src->data[1],
++                                             av_rpi_sand_frame_stride1(src), av_rpi_sand_frame_stride2(src),
++                                             x/2, y/2, w, h/2);
++                    break;
 +                default:
 +                    return -1;
 +            }
@@ -58750,6 +65921,16 @@
 +                                             av_rpi_sand_frame_stride1(src), av_rpi_sand_frame_stride2(src),
 +                                             x/2, y/2, w/2, h/2);
 +                    break;
++                case AV_PIX_FMT_NV12:
++                    av_rpi_sand30_to_planar_y8(dst->data[0], dst->linesize[0],
++                                             src->data[0],
++                                             av_rpi_sand_frame_stride1(src), av_rpi_sand_frame_stride2(src),
++                                             x, y, w, h);
++                    av_rpi_sand30_to_planar_y8(dst->data[1], dst->linesize[1],
++                                             src->data[1],
++                                             av_rpi_sand_frame_stride1(src), av_rpi_sand_frame_stride2(src),
++                                             x/2, y/2, w, h/2);
++                    break;
 +                default:
 +                    return -1;
 +            }
@@ -58762,7 +65943,7 @@
 +}
 --- /dev/null
 +++ b/libavutil/rpi_sand_fns.h
-@@ -0,0 +1,183 @@
+@@ -0,0 +1,188 @@
 +/*
 +Copyright (c) 2018 Raspberry Pi (Trading) Ltd.
 +All rights reserved.
@@ -58850,6 +66031,11 @@
 +                             unsigned int _x, unsigned int y,
 +                             unsigned int _w, unsigned int h);
 +
++void av_rpi_sand30_to_planar_y8(uint8_t * dst, const unsigned int dst_stride,
++                             const uint8_t * src,
++                             unsigned int stride1, unsigned int stride2,
++                             unsigned int _x, unsigned int y,
++                             unsigned int _w, unsigned int h);
 +
 +// w/h in pixels
 +void av_rpi_sand16_to_sand8(uint8_t * dst, const unsigned int dst_stride1, const unsigned int dst_stride2,
@@ -58948,34 +66134,64 @@
 +
 --- /dev/null
 +++ b/pi-util/BUILD.txt
-@@ -0,0 +1,29 @@
+@@ -0,0 +1,59 @@
 +Building Pi FFmpeg
 +==================
 +
-+Configuration:
-+=============
++Current only building on a Pi is supported.
++This builds ffmpeg the way I've tested it
 +
-+These instructions work for cross compiles from Ubuntu 16.04 & Ubuntu
-+18.04. I would expect most other linux environments to work but I haven't
-+tried them.
++Get all dependencies - the current package dependencies are good enough
 +
-+pi-util/conf_pi2.sh
++$ sudo apt-get build-dep ffmpeg
 +
-+contains suitable options to build the code for Pi2/3.  It expects to find
-+git clones of
++Configure using the pi-util/conf_native.sh script
++-------------------------------------------------
 +
-+https://github.com/raspberrypi/tools
-+https://github.com/raspberrypi/firmware
++This sets the normal release options and creates an ouutput dir to build into
++The directory name will depend on system and options but will be under out/
 +
-+in the parent of the FFmpeg directory.  I recommend using --depth 1 to avoid a
-+lot of history you don't want.
++There are a few choices here
++ --mmal  build including the legacy mmal-based decoders and zero-copy code
++         this requires appropriate libraries which currently will exist for
++         armv7 but not arm64
++ --noshared
++         Build a static image rather than a shared library one.  Static is
++         easier for testing as there is no need to worry about library
++         paths being confused and therefore running the wrong code,  Shared
++         is what is needed, in most cases, when building for use by other
++         programs.
 +
-+If you have a copy of qasm.py in ../local/bin then the .qasm sources will be
-+rebuilt.  Otherwise the prebuilt .c & .h files will be used.
-+Likewise ../local/bin/vasmvidcore_std will enable VPU code rebuild
++So for a static build
++---------------------
 +
-+pi-util/conf_p1.sh should configure for Pi1.  Beware that as of this time
-+H265 QPU acceleration is broken on Pi1 and so it is disabled.
++$ pi-util/conf_native.sh --noshared
++
++$ make -j8 -C out/<wherever the script said it was building to>
++
++You can now run ffmpeg directly from where it was built
++
++For a shared build
++------------------
++
++$ pi-util/conf_native.sh
++
++You will normally want an install target if shared. Note that the script has
++set this up to be generated in out/<builddir>/install, you don't have to worry
++about overwriting your system libs.
++
++$ make -j8 -C out/<builddir> install
++
++You can now set LD_LIBRARY_PATH appropriately and run ffmpeg from where it was
++built or install the image on the system - you have to be careful to get rid
++of all other ffmpeg libs or confusion may result.  There is a little script
++that wipes all other versions - obviously use with care!
++
++$ sudo pi-util/clean_usr_libs.sh
++
++Then simply copying from the install to /usr works
++
++$ sudo cp -r out/<builddir>/install/* /usr
 +
 +
 --- /dev/null
@@ -59137,29 +66353,32 @@
 +

 --- /dev/null
 +++ b/pi-util/clean_usr_libs.sh
-@@ -0,0 +1,23 @@
+@@ -0,0 +1,26 @@
 +set -e
 +U=/usr/lib/arm-linux-gnueabihf
 +rm -f $U/libavcodec.*
 +rm -f $U/libavdevice.*
 +rm -f $U/libavfilter.*
 +rm -f $U/libavformat.*
-+rm -f $U/libavresample.*
 +rm -f $U/libavutil.*
++rm -f $U/libswresample.*
++rm -f $U/libswscale.*
 +U=/usr/lib/arm-linux-gnueabihf/neon/vfp
 +rm -f $U/libavcodec.*
 +rm -f $U/libavdevice.*
 +rm -f $U/libavfilter.*
 +rm -f $U/libavformat.*
-+rm -f $U/libavresample.*
 +rm -f $U/libavutil.*
++rm -f $U/libswresample.*
++rm -f $U/libswscale.*
 +U=/usr/lib/aarch64-linux-gnu
 +rm -f $U/libavcodec.*
 +rm -f $U/libavdevice.*
 +rm -f $U/libavfilter.*
 +rm -f $U/libavformat.*
-+rm -f $U/libavresample.*
 +rm -f $U/libavutil.*
++rm -f $U/libswresample.*
++rm -f $U/libswscale.*
 +
 --- /dev/null
 +++ b/pi-util/conf_arm64_native.sh
@@ -59706,57 +66925,90 @@
 +1,WPP_F_ericsson_MAIN_2,WPP_F_ericsson_MAIN_2.bit,WPP_F_ericsson_MAIN_2_yuv.md5

 --- /dev/null
 +++ b/pi-util/conf_native.sh
-@@ -0,0 +1,78 @@
+@@ -0,0 +1,106 @@
 +echo "Configure for native build"
 +
 +FFSRC=`pwd`
-+MC=`uname -m`
++MC=`dpkg --print-architecture`
++BUILDBASE=$FFSRC/out
 +
 +#RPI_KEEPS="-save-temps=obj"
 +RPI_KEEPS=""
 +
-+if [ "$MC" == "aarch64" ]; then
++NOSHARED=
++MMAL=
++
++while [ "$1" != "" ] ; do
++    case $1 in
++	--noshared)
++	    NOSHARED=1
++	    ;;
++	--mmal)
++	    MMAL=1
++	    ;;
++	*)
++	    echo "Usage $0: [--noshared] [--mmal]"
++	    exit 1
++	    ;;
++    esac
++    shift
++done
++
++
++MCOPTS=
++RPI_INCLUDES=
++RPI_LIBDIRS=
++RPI_DEFINES=
++RPI_EXTRALIBS=
++
++if [ "$MC" == "arm64" ]; then
 +  echo "M/C aarch64"
 +  A=aarch64-linux-gnu
 +  B=arm64
-+  MCOPTS=
-+  RPI_INCLUDES=
-+  RPI_LIBDIRS=
-+  RPI_DEFINES=
-+  RPI_EXTRALIBS=
-+  RPIOPTS="--disable-mmal --enable-sand"
-+else
++elif [ "$MC" == "armhf" ]; then
 +  echo "M/C armv7"
 +  A=arm-linux-gnueabihf
 +  B=armv7
 +  MCOPTS="--arch=armv6t2 --cpu=cortex-a7"
++  RPI_DEFINES=-mfpu=neon-vfpv4
++else
++  echo Unexpected architecture $MC
++  exit 1
++fi
++
++if [ $MMAL ]; then
 +  RPI_OPT_VC=/opt/vc
 +  RPI_INCLUDES="-I$RPI_OPT_VC/include -I$RPI_OPT_VC/include/interface/vcos/pthreads -I$RPI_OPT_VC/include/interface/vmcs_host/linux"
 +  RPI_LIBDIRS="-L$RPI_OPT_VC/lib"
-+  RPI_DEFINES="-D__VCCOREVER__=0x4000000 -mfpu=neon-vfpv4"
++  RPI_DEFINES="$RPI_DEFINES -D__VCCOREVER__=0x4000000"
 +  RPI_EXTRALIBS="-Wl,--start-group -lbcm_host -lmmal -lmmal_util -lmmal_core -lvcos -lvcsm -lvchostif -lvchiq_arm -Wl,--end-group"
 +  RPIOPTS="--enable-mmal --enable-rpi"
++else
++  RPIOPTS="--disable-mmal --enable-sand"
 +fi
++
 +C=`lsb_release -sc`
 +V=`cat RELEASE`
 +
 +SHARED_LIBS="--enable-shared"
-+if [ "$1" == "--noshared" ]; then
++if [ $NOSHARED ]; then
 +  SHARED_LIBS="--disable-shared"
-+  OUT=out/$B-$C-$V-static-rel
++  OUT=$BUILDBASE/$B-$C-$V-static-rel
 +  echo Static libs
 +else
 +  echo Shared libs
-+  OUT=out/$B-$C-$V-shared-rel
++  OUT=$BUILDBASE/$B-$C-$V-shared-rel
 +fi
 +
-+USR_PREFIX=$FFSRC/$OUT/install
++USR_PREFIX=$OUT/install
 +LIB_PREFIX=$USR_PREFIX/lib/$A
 +INC_PREFIX=$USR_PREFIX/include/$A
 +
 +echo Destination directory: $OUT
-+mkdir -p $FFSRC/$OUT
-+cd $FFSRC/$OUT
++mkdir -p $OUT
++# Nothing under here need worry git - including this .gitignore!
++echo "**" > $BUILDBASE/.gitignore
++cd $OUT
 +
 +$FFSRC/configure \
 + --prefix=$USR_PREFIX\
@@ -59767,10 +67019,8 @@
 + --disable-thumb\
 + --enable-v4l2-request\
 + --enable-libdrm\
-+ --enable-epoxy\
-+ --enable-libudev\
-+ --enable-vout-drm\
 + --enable-vout-egl\
++ --enable-vout-drm\
 + $SHARED_LIBS\
 + $RPIOPTS\
 + --extra-cflags="-ggdb $RPI_KEEPS $RPI_DEFINES $RPI_INCLUDES"\
@@ -59779,118 +67029,13 @@
 + --extra-libs="$RPI_EXTRALIBS"\
 + --extra-version="rpi"
 +
-+# --enable-decoder=hevc_rpi\
-+# --enable-extra-warnings\
-+# --arch=armv71\
-+
-+# gcc option for getting asm listing
-+# -Wa,-ahls
---- /dev/null
-+++ b/pi-util/conf_pi1.sh
-@@ -0,0 +1,39 @@
-+echo "Configure for Pi1"
-+
-+RPI_TOOLROOT=`pwd`/../tools/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf
-+RPI_OPT_VC=`pwd`/../firmware/hardfp/opt/vc
-+
-+RPI_INCLUDES="-I$RPI_OPT_VC/include -I$RPI_OPT_VC/include/interface/vcos/pthreads -I$RPI_OPT_VC/include/interface/vmcs_host/linux"
-+RPI_LIBDIRS="-L$RPI_TOOLROOT/lib -L$RPI_OPT_VC/lib"
-+#RPI_KEEPS="-save-temps=obj"
-+RPI_KEEPS=""
-+
-+SHARED_LIBS="--enable-shared"
-+if [ "$1" == "--noshared" ]; then
-+  SHARED_LIBS="--disable-shared"
-+  echo Static libs
-+else
-+  echo Shared libs
-+fi
-+
-+./configure --enable-cross-compile\
-+ --cpu=arm1176jzf-s\
-+ --arch=arm\
-+ --disable-neon\
-+ --target-os=linux\
-+ --disable-stripping\
-+ --enable-mmal\
-+ $SHARED_LIBS\
-+ --extra-cflags="-g $RPI_KEEPS $RPI_INCLUDES"\
-+ --extra-cxxflags="$RPI_INCLUDES"\
-+ --extra-ldflags="$RPI_LIBDIRS -Wl,-rpath=/opt/vc/lib,-rpath-link=$RPI_OPT_VC/lib,-rpath=/lib,-rpath=/usr/lib,-rpath-link=$RPI_TOOLROOT/lib,-rpath-link=$RPI_TOOLROOT/lib"\
-+ --extra-libs="-Wl,--start-group -lbcm_host -lmmal -lmmal_util -lmmal_core -lvcos -lvcsm -lvchostif -lvchiq_arm"\
-+ --cross-prefix=$RPI_TOOLROOT/bin/arm-linux-gnueabihf-
-+
-+
-+# --enable-extra-warnings\
-+# --arch=armv71\
-+# --enable-shared\
-+
-+# gcc option for getting asm listing
-+# -Wa,-ahls
---- /dev/null
-+++ b/pi-util/conf_pi2.sh
-@@ -0,0 +1,57 @@
-+echo "Configure for Pi2/3"
-+
-+FFSRC=`pwd`
-+
-+RPI_TOOLROOT=$FFSRC/../tools/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf
-+RPI_OPT_VC=$FFSRC/../firmware/hardfp/opt/vc
-+
-+RPI_INCLUDES="-I$RPI_OPT_VC/include -I$RPI_OPT_VC/include/interface/vcos/pthreads -I$RPI_OPT_VC/include/interface/vmcs_host/linux"
-+RPI_LIBDIRS="-L$RPI_TOOLROOT/lib -L$RPI_OPT_VC/lib"
-+RPI_DEFINES="-D__VCCOREVER__=0x4000000 -mfpu=neon-vfpv4"
-+#RPI_KEEPS="-save-temps=obj"
-+RPI_KEEPS=""
-+
-+SHARED_LIBS="--enable-shared"
-+if [ "$1" == "--noshared" ]; then
-+  SHARED_LIBS="--disable-shared"
-+  OUT=out/x-armv7-static-rel
-+  echo Static libs
-+else
-+  echo Shared libs
-+  OUT=out/x-armv7-shared-rel
-+fi
-+
-+USR_PREFIX=$FFSRC/$OUT/install
-+LIB_PREFIX=$USR_PREFIX/lib/arm-linux-gnueabihf
-+INC_PREFIX=$USR_PREFIX/include/arm-linux-gnueabihf
-+
-+mkdir -p $FFSRC/$OUT
-+cd $FFSRC/$OUT
-+
-+$FFSRC/configure --enable-cross-compile\
-+ --prefix=$USR_PREFIX\
-+ --libdir=$LIB_PREFIX\
-+ --incdir=$INC_PREFIX\
-+ --arch=armv6t2\
-+ --cpu=cortex-a7\
-+ --target-os=linux\
-+ --disable-stripping\
-+ --disable-thumb\
-+ --enable-mmal\
-+ --enable-rpi\
-+ $SHARED_LIBS\
-+ --extra-cflags="-ggdb $RPI_KEEPS $RPI_DEFINES $RPI_INCLUDES"\
-+ --extra-cxxflags="$RPI_DEFINES $RPI_INCLUDES"\
-+ --extra-ldflags="$RPI_LIBDIRS -Wl,-rpath=/opt/vc/lib,-rpath-link=$RPI_OPT_VC/lib,-rpath=/lib,-rpath=/usr/lib,-rpath-link=$RPI_TOOLROOT/lib,-rpath-link=$RPI_TOOLROOT/lib"\
-+ --extra-libs="-Wl,--start-group -lbcm_host -lmmal -lmmal_util -lmmal_core -lvcos -lvcsm -lvchostif -lvchiq_arm"\
-+ --cross-prefix=$RPI_TOOLROOT/bin/arm-linux-gnueabihf-
-+
-+# --enable-shared\
-+
-+# --enable-decoder=hevc_rpi\
-+# --enable-extra-warnings\
-+# --arch=armv71\
-+# --enable-shared\
 +
 +# gcc option for getting asm listing
 +# -Wa,-ahls
 --- /dev/null
 +++ b/pi-util/ffconf.py
 @@ -0,0 +1,215 @@
-+#!/usr/bin/env python
++#!/usr/bin/env python3
 +
 +import string
 +import os
@@ -59967,16 +67112,16 @@
 +        pass
 +
 +    if  m1 and m2 and m1.group() == m2.group():
-+        print >> flog, "Match: " + m1.group()
++        print("Match: " + m1.group(), file=flog)
 +        rv = 0
 +    elif not m1:
-+        print >> flog, "****** Cannot find m1"
++        print("****** Cannot find m1", file=flog)
 +        rv = 3
 +    elif not m2:
-+        print >> flog, "****** Cannot find m2"
++        print("****** Cannot find m2", file=flog)
 +        rv = 2
 +    else:
-+        print >> flog, "****** Mismatch: " + m1.group() + " != " + m2.group()
++        print("****** Mismatch: " + m1.group() + " != " + m2.group(), file=flog)
 +        rv = 1
 +    flog.close()
 +    return rv
@@ -60022,7 +67167,7 @@
 +        exp_test = int(a[0])
 +        if (exp_test and runtest(a[1], tests)):
 +            name = a[1]
-+            print "==== ", name,
++            print ("==== ", name, end="")
 +            sys.stdout.flush()
 +
 +            rv = testone(os.path.join(test_root, name), name, a[2], a[3], a[4], dectype=dectype, vcodec=vcodec, ffmpeg_exec=ffmpeg_exec)
@@ -60033,31 +67178,31 @@
 +
 +            if (rv == 0):
 +                if exp_test == 2:
-+                    print ": * OK *"
++                    print(": * OK *")
 +                    unx_success.append(name)
 +                else:
-+                    print ": ok"
++                    print(": ok")
 +            elif exp_test == 2 and rv == 1:
-+                print ": fail"
++                print(": fail")
 +            elif exp_test == 3 and rv == 2:
 +                # Call an expected "crash" an abort
-+                print ": abort"
++                print(": abort")
 +            else:
 +                unx_failures.append(name)
 +                if rv == 1:
-+                    print ": * FAIL *"
++                    print(": * FAIL *")
 +                elif (rv == 2) :
-+                    print ": * CRASH *"
++                    print(": * CRASH *")
 +                elif (rv == 3) :
-+                    print ": * MD5 MISSING *"
++                    print(": * MD5 MISSING *")
 +                else :
-+                    print ": * BANG *"
++                    print(": * BANG *")
 +
 +    if unx_failures or unx_success:
-+        print "Unexpected Failures:", unx_failures
-+        print "Unexpected Success: ", unx_success
++        print("Unexpected Failures:", unx_failures)
++        print("Unexpected Success: ", unx_success)
 +    else:
-+        print "All tests normal:", successes, "ok,", failures, "failed"
++        print("All tests normal:", successes, "ok,", failures, "failed")
 +
 +
 +class ConfCSVDialect(csv.Dialect):
@@ -60567,3 +67712,630 @@
 +
 +    do_logparse(args.logfile)
 +
+--- a/tests/checkasm/Makefile
++++ b/tests/checkasm/Makefile
+@@ -9,8 +9,10 @@ AVCODECOBJS-$(CONFIG_G722DSP)
+ AVCODECOBJS-$(CONFIG_H264DSP)           += h264dsp.o
+ AVCODECOBJS-$(CONFIG_H264PRED)          += h264pred.o
+ AVCODECOBJS-$(CONFIG_H264QPEL)          += h264qpel.o
++AVCODECOBJS-$(CONFIG_IDCTDSP)           += idctdsp.o
+ AVCODECOBJS-$(CONFIG_LLVIDDSP)          += llviddsp.o
+ AVCODECOBJS-$(CONFIG_LLVIDENCDSP)       += llviddspenc.o
++AVCODECOBJS-$(CONFIG_VC1DSP)            += vc1dsp.o
+ AVCODECOBJS-$(CONFIG_VP8DSP)            += vp8dsp.o
+ AVCODECOBJS-$(CONFIG_VIDEODSP)          += videodsp.o
+ 
+--- a/tests/checkasm/checkasm.c
++++ b/tests/checkasm/checkasm.c
+@@ -121,6 +121,9 @@ static const struct {
+     #if CONFIG_HUFFYUV_DECODER
+         { "huffyuvdsp", checkasm_check_huffyuvdsp },
+     #endif
++    #if CONFIG_IDCTDSP
++        { "idctdsp", checkasm_check_idctdsp },
++    #endif
+     #if CONFIG_JPEG2000_DECODER
+         { "jpeg2000dsp", checkasm_check_jpeg2000dsp },
+     #endif
+@@ -145,6 +148,9 @@ static const struct {
+     #if CONFIG_V210_ENCODER
+         { "v210enc", checkasm_check_v210enc },
+     #endif
++    #if CONFIG_VC1DSP
++        { "vc1dsp", checkasm_check_vc1dsp },
++    #endif
+     #if CONFIG_VP8DSP
+         { "vp8dsp", checkasm_check_vp8dsp },
+     #endif
+--- a/tests/checkasm/checkasm.h
++++ b/tests/checkasm/checkasm.h
+@@ -60,6 +60,7 @@ void checkasm_check_hevc_add_res(void);
+ void checkasm_check_hevc_idct(void);
+ void checkasm_check_hevc_sao(void);
+ void checkasm_check_huffyuvdsp(void);
++void checkasm_check_idctdsp(void);
+ void checkasm_check_jpeg2000dsp(void);
+ void checkasm_check_llviddsp(void);
+ void checkasm_check_llviddspenc(void);
+@@ -73,6 +74,7 @@ void checkasm_check_sw_scale(void);
+ void checkasm_check_utvideodsp(void);
+ void checkasm_check_v210dec(void);
+ void checkasm_check_v210enc(void);
++void checkasm_check_vc1dsp(void);
+ void checkasm_check_vf_eq(void);
+ void checkasm_check_vf_gblur(void);
+ void checkasm_check_vf_hflip(void);
+--- /dev/null
++++ b/tests/checkasm/idctdsp.c
+@@ -0,0 +1,98 @@
++/*
++ * Copyright (c) 2022 Ben Avison
++ *
++ * This file is part of FFmpeg.
++ *
++ * FFmpeg is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 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 General Public License for more details.
++ *
++ * You should have received a copy of the GNU 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 <string.h>
++
++#include "checkasm.h"
++
++#include "libavcodec/idctdsp.h"
++
++#include "libavutil/common.h"
++#include "libavutil/internal.h"
++#include "libavutil/intreadwrite.h"
++#include "libavutil/mem_internal.h"
++
++#define IDCTDSP_TEST(func) { #func, offsetof(IDCTDSPContext, func) },
++
++typedef struct {
++    const char *name;
++    size_t offset;
++} test;
++
++#define RANDOMIZE_BUFFER16(name, size)          \
++    do {                                        \
++        int i;                                  \
++        for (i = 0; i < size; ++i) {            \
++            uint16_t r = rnd() % 0x201 - 0x100; \
++            AV_WN16A(name##0 + i, r);           \
++            AV_WN16A(name##1 + i, r);           \
++        }                                       \
++    } while (0)
++
++#define RANDOMIZE_BUFFER8(name, size)         \
++    do {                                      \
++        int i;                                \
++        for (i = 0; i < size; ++i) {          \
++            uint8_t r = rnd();                \
++            name##0[i] = r;                   \
++            name##1[i] = r;                   \
++        }                                     \
++    } while (0)
++
++static void check_add_put_clamped(void)
++{
++    /* Source buffers are only as big as needed, since any over-read won't affect results */
++    LOCAL_ALIGNED_16(int16_t, src0, [64]);
++    LOCAL_ALIGNED_16(int16_t, src1, [64]);
++    /* Destination buffers have borders of one row above/below and 8 columns left/right to catch overflows */
++    LOCAL_ALIGNED_8(uint8_t, dst0, [10 * 24]);
++    LOCAL_ALIGNED_8(uint8_t, dst1, [10 * 24]);
++
++    AVCodecContext avctx = { 0 };
++    IDCTDSPContext h;
++
++    const test tests[] = {
++        IDCTDSP_TEST(add_pixels_clamped)
++        IDCTDSP_TEST(put_pixels_clamped)
++        IDCTDSP_TEST(put_signed_pixels_clamped)
++    };
++
++    ff_idctdsp_init(&h, &avctx);
++
++    for (size_t t = 0; t < FF_ARRAY_ELEMS(tests); ++t) {
++        void (*func)(const int16_t *, uint8_t * ptrdiff_t) = *(void **)((intptr_t) &h + tests[t].offset);
++        if (check_func(func, "idctdsp.%s", tests[t].name)) {
++            declare_func_emms(AV_CPU_FLAG_MMX, void, const int16_t *, uint8_t *, ptrdiff_t);
++            RANDOMIZE_BUFFER16(src, 64);
++            RANDOMIZE_BUFFER8(dst, 10 * 24);
++            call_ref(src0, dst0 + 24 + 8, 24);
++            call_new(src1, dst1 + 24 + 8, 24);
++            if (memcmp(dst0, dst1, 10 * 24))
++                fail();
++            bench_new(src1, dst1 + 24 + 8, 24);
++        }
++    }
++}
++
++void checkasm_check_idctdsp(void)
++{
++    check_add_put_clamped();
++    report("idctdsp");
++}
+--- /dev/null
++++ b/tests/checkasm/vc1dsp.c
+@@ -0,0 +1,452 @@
++/*
++ * Copyright (c) 2022 Ben Avison
++ *
++ * This file is part of FFmpeg.
++ *
++ * FFmpeg is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 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 General Public License for more details.
++ *
++ * You should have received a copy of the GNU 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 <string.h>
++
++#include "checkasm.h"
++
++#include "libavcodec/vc1dsp.h"
++
++#include "libavutil/common.h"
++#include "libavutil/internal.h"
++#include "libavutil/intreadwrite.h"
++#include "libavutil/mem_internal.h"
++
++#define VC1DSP_TEST(func) { #func, offsetof(VC1DSPContext, func) },
++#define VC1DSP_SIZED_TEST(func, width, height) { #func, offsetof(VC1DSPContext, func), width, height },
++
++typedef struct {
++    const char *name;
++    size_t offset;
++    int width;
++    int height;
++} test;
++
++typedef struct matrix {
++    size_t width;
++    size_t height;
++    float d[];
++} matrix;
++
++static const matrix T8 = { 8, 8, {
++        12,  12,  12,  12,  12,  12,  12,  12,
++        16,  15,   9,   4,  -4,  -9, -15, -16,
++        16,   6,  -6, -16, -16,  -6,   6,  16,
++        15,  -4, -16,  -9,   9,  16,   4, -15,
++        12, -12, -12,  12,  12, -12, -12,  12,
++         9, -16,   4,  15, -15,  -4,  16,  -9,
++         6, -16,  16,  -6,  -6,  16, -16,   6,
++         4,  -9,  15, -16,  16, -15,   9,  -4
++} };
++
++static const matrix T4 = { 4, 4, {
++        17,  17,  17,  17,
++        22,  10, -10, -22,
++        17, -17, -17,  17,
++        10, -22,  22, -10
++} };
++
++static const matrix T8t = { 8, 8, {
++        12,  16,  16,  15,  12,   9,   6,   4,
++        12,  15,   6,  -4, -12, -16, -16,  -9,
++        12,   9,  -6, -16, -12,   4,  16,  15,
++        12,   4, -16,  -9,  12,  15,  -6, -16,
++        12,  -4, -16,   9,  12, -15,  -6,  16,
++        12,  -9,  -6,  16, -12,  -4,  16, -15,
++        12, -15,   6,   4, -12,  16, -16,   9,
++        12, -16,  16, -15,  12,  -9,   6,  -4
++} };
++
++static const matrix T4t = { 4, 4, {
++        17,  22,  17,  10,
++        17,  10, -17, -22,
++        17, -10, -17,  22,
++        17, -22,  17, -10
++} };
++
++static matrix *new_matrix(size_t width, size_t height)
++{
++    matrix *out = av_mallocz(sizeof (matrix) + height * width * sizeof (float));
++    if (out == NULL) {
++        fprintf(stderr, "Memory allocation failure\n");
++        exit(EXIT_FAILURE);
++    }
++    out->width = width;
++    out->height = height;
++    return out;
++}
++
++static matrix *multiply(const matrix *a, const matrix *b)
++{
++    matrix *out;
++    if (a->width != b->height) {
++        fprintf(stderr, "Incompatible multiplication\n");
++        exit(EXIT_FAILURE);
++    }
++    out = new_matrix(b->width, a->height);
++    for (int j = 0; j < out->height; ++j)
++        for (int i = 0; i < out->width; ++i) {
++            float sum = 0;
++            for (int k = 0; k < a->width; ++k)
++                sum += a->d[j * a->width + k] * b->d[k * b->width + i];
++            out->d[j * out->width + i] = sum;
++        }
++    return out;
++}
++
++static void normalise(matrix *a)
++{
++    for (int j = 0; j < a->height; ++j)
++        for (int i = 0; i < a->width; ++i) {
++            float *p = a->d + j * a->width + i;
++            *p *= 64;
++            if (a->height == 4)
++                *p /= (const unsigned[]) { 289, 292, 289, 292 } [j];
++            else
++                *p /= (const unsigned[]) { 288, 289, 292, 289, 288, 289, 292, 289 } [j];
++            if (a->width == 4)
++                *p /= (const unsigned[]) { 289, 292, 289, 292 } [i];
++            else
++                *p /= (const unsigned[]) { 288, 289, 292, 289, 288, 289, 292, 289 } [i];
++        }
++}
++
++static void divide_and_round_nearest(matrix *a, float by)
++{
++    for (int j = 0; j < a->height; ++j)
++        for (int i = 0; i < a->width; ++i) {
++            float *p = a->d + j * a->width + i;
++            *p = rintf(*p / by);
++        }
++}
++
++static void tweak(matrix *a)
++{
++    for (int j = 4; j < a->height; ++j)
++        for (int i = 0; i < a->width; ++i) {
++            float *p = a->d + j * a->width + i;
++            *p += 1;
++        }
++}
++
++/* The VC-1 spec places restrictions on the values permitted at three
++ * different stages:
++ * - D: the input coefficients in frequency domain
++ * - E: the intermediate coefficients, inverse-transformed only horizontally
++ * - R: the fully inverse-transformed coefficients
++ *
++ * To fully cater for the ranges specified requires various intermediate
++ * values to be held to 17-bit precision; yet these conditions do not appear
++ * to be utilised in real-world streams. At least some assembly
++ * implementations have chosen to restrict these values to 16-bit precision,
++ * to accelerate the decoding of real-world streams at the cost of strict
++ * adherence to the spec. To avoid our test marking these as failures,
++ * reduce our random inputs.
++ */
++#define ATTENUATION 4
++
++static matrix *generate_inverse_quantized_transform_coefficients(size_t width, size_t height)
++{
++    matrix *raw, *tmp, *D, *E, *R;
++    raw = new_matrix(width, height);
++    for (int i = 0; i < width * height; ++i)
++        raw->d[i] = (int) (rnd() % (1024/ATTENUATION)) - 512/ATTENUATION;
++    tmp = multiply(height == 8 ? &T8 : &T4, raw);
++    D = multiply(tmp, width == 8 ? &T8t : &T4t);
++    normalise(D);
++    divide_and_round_nearest(D, 1);
++    for (int i = 0; i < width * height; ++i) {
++        if (D->d[i] < -2048/ATTENUATION || D->d[i] > 2048/ATTENUATION-1) {
++            /* Rare, so simply try again */
++            av_free(raw);
++            av_free(tmp);
++            av_free(D);
++            return generate_inverse_quantized_transform_coefficients(width, height);
++        }
++    }
++    E = multiply(D, width == 8 ? &T8 : &T4);
++    divide_and_round_nearest(E, 8);
++    for (int i = 0; i < width * height; ++i)
++        if (E->d[i] < -4096/ATTENUATION || E->d[i] > 4096/ATTENUATION-1) {
++            /* Rare, so simply try again */
++            av_free(raw);
++            av_free(tmp);
++            av_free(D);
++            av_free(E);
++            return generate_inverse_quantized_transform_coefficients(width, height);
++        }
++    R = multiply(height == 8 ? &T8t : &T4t, E);
++    tweak(R);
++    divide_and_round_nearest(R, 128);
++    for (int i = 0; i < width * height; ++i)
++        if (R->d[i] < -512/ATTENUATION || R->d[i] > 512/ATTENUATION-1) {
++            /* Rare, so simply try again */
++            av_free(raw);
++            av_free(tmp);
++            av_free(D);
++            av_free(E);
++            av_free(R);
++            return generate_inverse_quantized_transform_coefficients(width, height);
++        }
++    av_free(raw);
++    av_free(tmp);
++    av_free(E);
++    av_free(R);
++    return D;
++}
++
++#define RANDOMIZE_BUFFER16(name, size)        \
++    do {                                      \
++        int i;                                \
++        for (i = 0; i < size; ++i) {          \
++            uint16_t r = rnd();               \
++            AV_WN16A(name##0 + i, r);         \
++            AV_WN16A(name##1 + i, r);         \
++        }                                     \
++    } while (0)
++
++#define RANDOMIZE_BUFFER8(name, size)         \
++    do {                                      \
++        int i;                                \
++        for (i = 0; i < size; ++i) {          \
++            uint8_t r = rnd();                \
++            name##0[i] = r;                   \
++            name##1[i] = r;                   \
++        }                                     \
++    } while (0)
++
++#define RANDOMIZE_BUFFER8_MID_WEIGHTED(name, size)  \
++    do {                                            \
++        uint8_t *p##0 = name##0, *p##1 = name##1;   \
++        int i = (size);                             \
++        while (i-- > 0) {                           \
++            int x = 0x80 | (rnd() & 0x7F);          \
++            x >>= rnd() % 9;                        \
++            if (rnd() & 1)                          \
++                x = -x;                             \
++            *p##1++ = *p##0++ = 0x80 + x;           \
++        }                                           \
++    } while (0)
++
++static void check_inv_trans_inplace(void)
++{
++    /* Inverse transform input coefficients are stored in a 16-bit buffer
++     * with row stride of 8 coefficients irrespective of transform size.
++     * vc1_inv_trans_8x8 differs from the others in two ways: coefficients
++     * are stored in column-major order, and the outputs are written back
++     * to the input buffer, so we oversize it slightly to catch overruns. */
++    LOCAL_ALIGNED_16(int16_t, inv_trans_in0, [10 * 8]);
++    LOCAL_ALIGNED_16(int16_t, inv_trans_in1, [10 * 8]);
++
++    VC1DSPContext h;
++
++    ff_vc1dsp_init(&h);
++
++    if (check_func(h.vc1_inv_trans_8x8, "vc1dsp.vc1_inv_trans_8x8")) {
++        matrix *coeffs;
++        declare_func_emms(AV_CPU_FLAG_MMX, void, int16_t *);
++        RANDOMIZE_BUFFER16(inv_trans_in, 10 * 8);
++        coeffs = generate_inverse_quantized_transform_coefficients(8, 8);
++        for (int j = 0; j < 8; ++j)
++            for (int i = 0; i < 8; ++i) {
++                int idx = 8 + i * 8 + j;
++                inv_trans_in1[idx] = inv_trans_in0[idx] = coeffs->d[j * 8 + i];
++            }
++        call_ref(inv_trans_in0 + 8);
++        call_new(inv_trans_in1 + 8);
++        if (memcmp(inv_trans_in0,  inv_trans_in1,  10 * 8 * sizeof (int16_t)))
++            fail();
++        bench_new(inv_trans_in1 + 8);
++        av_free(coeffs);
++    }
++}
++
++static void check_inv_trans_adding(void)
++{
++    /* Inverse transform input coefficients are stored in a 16-bit buffer
++     * with row stride of 8 coefficients irrespective of transform size. */
++    LOCAL_ALIGNED_16(int16_t, inv_trans_in0, [8 * 8]);
++    LOCAL_ALIGNED_16(int16_t, inv_trans_in1, [8 * 8]);
++
++    /* For all but vc1_inv_trans_8x8, the inverse transform is narrowed and
++     * added with saturation to an array of unsigned 8-bit values. Oversize
++     * this by 8 samples left and right and one row above and below. */
++    LOCAL_ALIGNED_8(uint8_t, inv_trans_out0, [10 * 24]);
++    LOCAL_ALIGNED_8(uint8_t, inv_trans_out1, [10 * 24]);
++
++    VC1DSPContext h;
++
++    const test tests[] = {
++        VC1DSP_SIZED_TEST(vc1_inv_trans_8x4, 8, 4)
++        VC1DSP_SIZED_TEST(vc1_inv_trans_4x8, 4, 8)
++        VC1DSP_SIZED_TEST(vc1_inv_trans_4x4, 4, 4)
++        VC1DSP_SIZED_TEST(vc1_inv_trans_8x8_dc, 8, 8)
++        VC1DSP_SIZED_TEST(vc1_inv_trans_8x4_dc, 8, 4)
++        VC1DSP_SIZED_TEST(vc1_inv_trans_4x8_dc, 4, 8)
++        VC1DSP_SIZED_TEST(vc1_inv_trans_4x4_dc, 4, 4)
++    };
++
++    ff_vc1dsp_init(&h);
++
++    for (size_t t = 0; t < FF_ARRAY_ELEMS(tests); ++t) {
++        void (*func)(uint8_t *, ptrdiff_t, int16_t *) = *(void **)((intptr_t) &h + tests[t].offset);
++        if (check_func(func, "vc1dsp.%s", tests[t].name)) {
++            matrix *coeffs;
++            declare_func_emms(AV_CPU_FLAG_MMX, void, uint8_t *, ptrdiff_t, int16_t *);
++            RANDOMIZE_BUFFER16(inv_trans_in, 8 * 8);
++            RANDOMIZE_BUFFER8(inv_trans_out, 10 * 24);
++            coeffs = generate_inverse_quantized_transform_coefficients(tests[t].width, tests[t].height);
++            for (int j = 0; j < tests[t].height; ++j)
++                for (int i = 0; i < tests[t].width; ++i) {
++                    int idx = j * 8 + i;
++                    inv_trans_in1[idx] = inv_trans_in0[idx] = coeffs->d[j * tests[t].width + i];
++                }
++            call_ref(inv_trans_out0 + 24 + 8, 24, inv_trans_in0);
++            call_new(inv_trans_out1 + 24 + 8, 24, inv_trans_in1);
++            if (memcmp(inv_trans_out0, inv_trans_out1, 10 * 24))
++                fail();
++            bench_new(inv_trans_out1 + 24 + 8, 24, inv_trans_in1 + 8);
++            av_free(coeffs);
++        }
++    }
++}
++
++static void check_loop_filter(void)
++{
++    /* Deblocking filter buffers are big enough to hold a 16x16 block,
++     * plus 16 columns left and 4 rows above to hold filter inputs
++     * (depending on whether v or h neighbouring block edge, oversized
++     * horizontally to maintain 16-byte alignment) plus 16 columns and
++     * 4 rows below to catch write overflows */
++    LOCAL_ALIGNED_16(uint8_t, filter_buf0, [24 * 48]);
++    LOCAL_ALIGNED_16(uint8_t, filter_buf1, [24 * 48]);
++
++    VC1DSPContext h;
++
++    const test tests[] = {
++        VC1DSP_TEST(vc1_v_loop_filter4)
++        VC1DSP_TEST(vc1_h_loop_filter4)
++        VC1DSP_TEST(vc1_v_loop_filter8)
++        VC1DSP_TEST(vc1_h_loop_filter8)
++        VC1DSP_TEST(vc1_v_loop_filter16)
++        VC1DSP_TEST(vc1_h_loop_filter16)
++    };
++
++    ff_vc1dsp_init(&h);
++
++    for (size_t t = 0; t < FF_ARRAY_ELEMS(tests); ++t) {
++        void (*func)(uint8_t *, ptrdiff_t, int) = *(void **)((intptr_t) &h + tests[t].offset);
++        declare_func_emms(AV_CPU_FLAG_MMX, void, uint8_t *, ptrdiff_t, int);
++        if (check_func(func, "vc1dsp.%s", tests[t].name)) {
++            for (int count = 1000; count > 0; --count) {
++                int pq = rnd() % 31 + 1;
++                RANDOMIZE_BUFFER8_MID_WEIGHTED(filter_buf, 24 * 48);
++                call_ref(filter_buf0 + 4 * 48 + 16, 48, pq);
++                call_new(filter_buf1 + 4 * 48 + 16, 48, pq);
++                if (memcmp(filter_buf0, filter_buf1, 24 * 48))
++                    fail();
++            }
++        }
++        for (int j = 0; j < 24; ++j)
++            for (int i = 0; i < 48; ++i)
++                filter_buf1[j * 48 + i] = 0x60 + 0x40 * (i >= 16 && j >= 4);
++        if (check_func(func, "vc1dsp.%s_bestcase", tests[t].name))
++            bench_new(filter_buf1 + 4 * 48 + 16, 48, 1);
++        if (check_func(func, "vc1dsp.%s_worstcase", tests[t].name))
++            bench_new(filter_buf1 + 4 * 48 + 16, 48, 31);
++    }
++}
++
++#define TEST_UNESCAPE                                                                               \
++    do {                                                                                            \
++        for (int count = 100; count > 0; --count) {                                                 \
++            escaped_offset = rnd() & 7;                                                             \
++            unescaped_offset = rnd() & 7;                                                           \
++            escaped_len = (1u << (rnd() % 8) + 3) - (rnd() & 7);                                    \
++            RANDOMIZE_BUFFER8(unescaped, UNESCAPE_BUF_SIZE);                                        \
++            len0 = call_ref(escaped0 + escaped_offset, escaped_len, unescaped0 + unescaped_offset); \
++            len1 = call_new(escaped1 + escaped_offset, escaped_len, unescaped1 + unescaped_offset); \
++            if (len0 != len1 || memcmp(unescaped0, unescaped1, UNESCAPE_BUF_SIZE))                  \
++                fail();                                                                             \
++        }                                                                                           \
++    } while (0)
++
++static void check_unescape(void)
++{
++    /* This appears to be a typical length of buffer in use */
++#define LOG2_UNESCAPE_BUF_SIZE 17
++#define UNESCAPE_BUF_SIZE (1u<<LOG2_UNESCAPE_BUF_SIZE)
++    LOCAL_ALIGNED_8(uint8_t, escaped0, [UNESCAPE_BUF_SIZE]);
++    LOCAL_ALIGNED_8(uint8_t, escaped1, [UNESCAPE_BUF_SIZE]);
++    LOCAL_ALIGNED_8(uint8_t, unescaped0, [UNESCAPE_BUF_SIZE]);
++    LOCAL_ALIGNED_8(uint8_t, unescaped1, [UNESCAPE_BUF_SIZE]);
++
++    VC1DSPContext h;
++
++    ff_vc1dsp_init(&h);
++
++    if (check_func(h.vc1_unescape_buffer, "vc1dsp.vc1_unescape_buffer")) {
++        int len0, len1, escaped_offset, unescaped_offset, escaped_len;
++        declare_func_emms(AV_CPU_FLAG_MMX, int, const uint8_t *, int, uint8_t *);
++
++        /* Test data which consists of escapes sequences packed as tightly as possible */
++        for (int x = 0; x < UNESCAPE_BUF_SIZE; ++x)
++            escaped1[x] = escaped0[x] = 3 * (x % 3 == 0);
++        TEST_UNESCAPE;
++
++        /* Test random data */
++        RANDOMIZE_BUFFER8(escaped, UNESCAPE_BUF_SIZE);
++        TEST_UNESCAPE;
++
++        /* Test data with escape sequences at random intervals */
++        for (int x = 0; x <= UNESCAPE_BUF_SIZE - 4;) {
++            int gap, gap_msb;
++            escaped1[x+0] = escaped0[x+0] = 0;
++            escaped1[x+1] = escaped0[x+1] = 0;
++            escaped1[x+2] = escaped0[x+2] = 3;
++            escaped1[x+3] = escaped0[x+3] = rnd() & 3;
++            gap_msb = 2u << (rnd() % 8);
++            gap = (rnd() &~ -gap_msb) | gap_msb;
++            x += gap;
++        }
++        TEST_UNESCAPE;
++
++        /* Test data which is known to contain no escape sequences */
++        memset(escaped0, 0xFF, UNESCAPE_BUF_SIZE);
++        memset(escaped1, 0xFF, UNESCAPE_BUF_SIZE);
++        TEST_UNESCAPE;
++
++        /* Benchmark the no-escape-sequences case */
++        bench_new(escaped1, UNESCAPE_BUF_SIZE, unescaped1);
++    }
++}
++
++void checkasm_check_vc1dsp(void)
++{
++    check_inv_trans_inplace();
++    check_inv_trans_adding();
++    report("inv_trans");
++
++    check_loop_filter();
++    report("loop_filter");
++
++    check_unescape();
++    report("unescape_buffer");
++}
+--- a/tests/fate/checkasm.mak
++++ b/tests/fate/checkasm.mak
+@@ -16,6 +16,7 @@ FATE_CHECKASM = fate-checkasm-aacpsdsp
+                 fate-checkasm-hevc_add_res                              \
+                 fate-checkasm-hevc_idct                                 \
+                 fate-checkasm-hevc_sao                                  \
++                fate-checkasm-idctdsp                                   \
+                 fate-checkasm-jpeg2000dsp                               \
+                 fate-checkasm-llviddsp                                  \
+                 fate-checkasm-llviddspenc                               \
+@@ -27,6 +28,7 @@ FATE_CHECKASM = fate-checkasm-aacpsdsp
+                 fate-checkasm-sw_scale                                  \
+                 fate-checkasm-v210dec                                   \
+                 fate-checkasm-v210enc                                   \
++                fate-checkasm-vc1dsp                                    \
+                 fate-checkasm-vf_blend                                  \
+                 fate-checkasm-vf_colorspace                             \
+                 fate-checkasm-vf_eq                                     \
diff --git a/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/0005-fix_flags.diff b/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/0005-fix-flags.diff
similarity index 89%
rename from meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/0005-fix_flags.diff
rename to meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/0005-fix-flags.diff
index 1eb1074..ab6f139 100644
--- a/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/0005-fix_flags.diff
+++ b/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/0005-fix-flags.diff
@@ -1,8 +1,11 @@
-Upstream-status: Pending
+Upstream-Status: Inappropriate
+
+RPI-Distro repo clones original ffmpeg and applies patches to enable
+raspiberry pi support.
 
 --- a/configure
 +++ b/configure
-@@ -6467,11 +6467,9 @@ enabled mbedtls           && { check_pkg
+@@ -6471,11 +6471,9 @@ enabled mbedtls           && { check_pkg
                                 die "ERROR: mbedTLS not found"; }
  enabled mediacodec        && { enabled jni || die "ERROR: mediacodec requires --enable-jni"; }
  ( enabled rpi ||
diff --git a/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/2001-configure-setup-for-OE-core-usage.patch b/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/2001-configure-setup-for-OE-core-usage.patch
new file mode 100644
index 0000000..f153827
--- /dev/null
+++ b/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/2001-configure-setup-for-OE-core-usage.patch
@@ -0,0 +1,82 @@
+From 01e738a8f1414acd0102e432bbc15b4e603fd956 Mon Sep 17 00:00:00 2001
+From: Vincent Davis Jr <vince@underview.tech>
+Date: Thu, 8 Dec 2022 10:34:20 -0600
+Subject: [PATCH] configure: setup for OE-core usage
+
+Upstream-Status: Inappropriate
+
+RPI-Distro repo clones original ffmpeg and applies patches to enable
+raspiberry pi support.
+
+Add global CFLAGS and LDFLAGS. So, that when
+./configure runs test it's able to locate proper
+headers and libs in a cross-compile environment.
+
+Add new check to opengl. None of the above headers
+exists and we also should be using GLESv2.
+
+Update where compiler finds OMX_Core.h
+
+Only check that sdl2 version greater than 2.0.1
+
+Signed-off-by: Vincent Davis Jr <vince@underview.tech>
+---
+ configure | 16 +++++++++-------
+ 1 file changed, 9 insertions(+), 7 deletions(-)
+
+diff --git a/configure b/configure
+index 723b81f1..0c7f2654 100755
+--- a/configure
++++ b/configure
+@@ -5746,6 +5746,9 @@ enable_weak_pic() {
+ }
+ 
+ enabled pic && enable_weak_pic
++# Set CFLAGS and LDFLAGS globally
++add_cflags -I${sysroot}/usr/include/ -I${sysroot}/usr/include/IL -I${sysroot}/usr/include/drm
++add_ldflags -L${sysroot}/usr/lib/
+ 
+ test_cc <<EOF || die "Symbol mangling check failed."
+ int ff_extern;
+@@ -6471,8 +6474,7 @@ enabled mbedtls           && { check_pkg_config mbedtls mbedtls mbedtls/x509_crt
+                                die "ERROR: mbedTLS not found"; }
+ enabled mediacodec        && { enabled jni || die "ERROR: mediacodec requires --enable-jni"; }
+ ( enabled rpi ||
+-  enabled mmal )          && { { add_cflags -isystem/opt/vc/include/ -isystem/opt/vc/include/interface/vmcs_host/linux -isystem/opt/vc/include/interface/vcos/pthreads -fgnu89-inline &&
+-                               add_ldflags -L/opt/vc/lib/ &&
++  enabled mmal )          && { { add_cflags -I${sysroot}/usr/include/interface/vmcs_host/linux -I${sysroot}/usr/include/interface/vcos/pthreads -fgnu89-inline &&
+                                check_lib mmal interface/mmal/mmal.h mmal_port_connect -lmmal_core -lmmal_util -lmmal_vc_client -lbcm_host -lvcsm -lvchostif -lvchiq_arm -lvcos; } ||
+                                die "ERROR: mmal not found" &&
+                                check_func_headers interface/mmal/mmal.h "MMAL_PARAMETER_VIDEO_MAX_NUM_CALLBACKS"; }
+@@ -6492,15 +6494,15 @@ enabled opengl            && { check_lib opengl GL/glx.h glXGetProcAddress "-lGL
+                                check_lib opengl windows.h wglGetProcAddress "-lopengl32 -lgdi32" ||
+                                check_lib opengl OpenGL/gl3.h glGetError "-Wl,-framework,OpenGL" ||
+                                check_lib opengl ES2/gl.h glGetError "-isysroot=${sysroot} -Wl,-framework,OpenGLES" ||
++                               check_lib opengl GLES2/gl2.h glGetError "-lGLESv2" ||
+                                die "ERROR: opengl not found."
+                              }
+-enabled omx_rpi           && { test_code cc OMX_Core.h OMX_IndexConfigBrcmVideoRequestIFrame ||
++enabled omx_rpi           && { test_code cc IL/OMX_Core.h OMX_IndexConfigBrcmVideoRequestIFrame ||
+                                { ! enabled cross_compile &&
+-                                 add_cflags -isystem/opt/vc/include/IL &&
+-                                 test_code cc OMX_Core.h OMX_IndexConfigBrcmVideoRequestIFrame; } ||
++                                 test_code cc IL/OMX_Core.h OMX_IndexConfigBrcmVideoRequestIFrame; } ||
+                                die "ERROR: OpenMAX IL headers from raspberrypi/firmware not found"; } &&
+                              enable omx
+-enabled omx               && require_headers OMX_Core.h
++enabled omx               && require_headers IL/OMX_Core.h
+ enabled openssl           && { check_pkg_config openssl openssl openssl/ssl.h OPENSSL_init_ssl ||
+                                check_pkg_config openssl openssl openssl/ssl.h SSL_library_init ||
+                                check_lib openssl openssl/ssl.h OPENSSL_init_ssl -lssl -lcrypto ||
+@@ -6540,7 +6542,7 @@ fi
+ 
+ if enabled sdl2; then
+     SDL2_CONFIG="${cross_prefix}sdl2-config"
+-    test_pkg_config sdl2 "sdl2 >= 2.0.1 sdl2 < 2.1.0" SDL_events.h SDL_PollEvent
++    test_pkg_config sdl2 "sdl2 >= 2.0.1" SDL_events.h SDL_PollEvent
+     if disabled sdl2 && "${SDL2_CONFIG}" --version > /dev/null 2>&1; then
+         sdl2_cflags=$("${SDL2_CONFIG}" --cflags)
+         sdl2_extralibs=$("${SDL2_CONFIG}" --libs)
+-- 
+2.38.1
+
diff --git a/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/2002-libavdevice-opengl_enc-update-dynamic-function-loader.patch b/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/2002-libavdevice-opengl_enc-update-dynamic-function-loader.patch
new file mode 100644
index 0000000..43a9191
--- /dev/null
+++ b/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/2002-libavdevice-opengl_enc-update-dynamic-function-loader.patch
@@ -0,0 +1,111 @@
+From be426ad76c3e486f1364dd292cf8e1c633c80e91 Mon Sep 17 00:00:00 2001
+From: Vincent Davis Jr <vince@underview.tech>
+Date: Thu, 8 Dec 2022 10:39:47 -0600
+Subject: [PATCH] libavdevice: opengl_enc.c update dynamic function loader
+
+Upstream-Status: Inappropriate
+
+RPI-Distro repo clones original ffmpeg and applies patches to enable
+raspiberry pi support.
+
+For meta-raspberrypi ffmpeg builds, when opengl
+is enabled do_compile will fail. Reasion is that
+glGetProcAddress is undefined in either GLES2/gl2.h
+or GLES2/gl2ext.h.
+
+define SelectedGetProcAddress to SDL_GL_GetProcAddress
+if sdl2 is included. If not included, define function
+pointers at compile time versus runtime.
+
+Signed-off-by: Vincent Davis Jr <vince@underview.tech>
+---
+ libavdevice/opengl_enc.c | 44 ++++++++++++++++++++++++++++++++++++----
+ 1 file changed, 40 insertions(+), 4 deletions(-)
+
+diff --git a/libavdevice/opengl_enc.c b/libavdevice/opengl_enc.c
+index 2bdb8da7..eabc1bf8 100644
+--- a/libavdevice/opengl_enc.c
++++ b/libavdevice/opengl_enc.c
+@@ -37,12 +37,13 @@
+ #include <OpenGL/gl3.h>
+ #elif HAVE_ES2_GL_H
+ #include <ES2/gl.h>
+-#else
+-#include <GL/gl.h>
+-#include <GL/glext.h>
+ #endif
+ #if HAVE_GLXGETPROCADDRESS
+ #include <GL/glx.h>
++#else
++#define GL_GLEXT_PROTOTYPES
++#include <GLES2/gl2.h>
++#include <GLES2/gl2ext.h>
+ #endif
+ 
+ #if CONFIG_SDL2
+@@ -493,8 +494,14 @@ static int av_cold opengl_load_procedures(OpenGLContext *opengl)
+ 
+ #if HAVE_GLXGETPROCADDRESS
+ #define SelectedGetProcAddress glXGetProcAddress
++#define CAN_DYNAMIC_LOAD 1
+ #elif HAVE_WGLGETPROCADDRESS
+ #define SelectedGetProcAddress wglGetProcAddress
++#elif CONFIG_SDL2
++#define SelectedGetProcAddress SDL_GL_GetProcAddress
++#define CAN_DYNAMIC_LOAD 1
++#else
++#define CAN_DYNAMIC_LOAD 0
+ #endif
+ 
+ #define LOAD_OPENGL_FUN(name, type) \
+@@ -504,7 +511,8 @@ static int av_cold opengl_load_procedures(OpenGLContext *opengl)
+         return AVERROR(ENOSYS); \
+     }
+ 
+-#if CONFIG_SDL2
++#if CAN_DYNAMIC_LOAD
++#if CONFIG_SDL2 
+     if (!opengl->no_window)
+         return opengl_sdl_load_procedures(opengl);
+ #endif
+@@ -534,9 +542,37 @@ static int av_cold opengl_load_procedures(OpenGLContext *opengl)
+     LOAD_OPENGL_FUN(glGetShaderInfoLog, FF_PFNGLGETSHADERINFOLOGPROC)
+     LOAD_OPENGL_FUN(glEnableVertexAttribArray, FF_PFNGLENABLEVERTEXATTRIBARRAYPROC)
+     LOAD_OPENGL_FUN(glVertexAttribPointer, FF_PFNGLVERTEXATTRIBPOINTERPROC)
++#else
++    procs->glActiveTexture = glActiveTexture;
++    procs->glGenBuffers = glGenBuffers;
++    procs->glDeleteBuffers = glDeleteBuffers;
++    procs->glBufferData = glBufferData;
++    procs->glBindBuffer = glBindBuffer;
++    procs->glGetAttribLocation = glGetAttribLocation;
++    procs->glGetUniformLocation = glGetUniformLocation;
++    procs->glUniform1f = glUniform1f;
++    procs->glUniform1i = glUniform1i;
++    procs->glUniformMatrix4fv = glUniformMatrix4fv;
++    procs->glCreateProgram = glCreateProgram;
++    procs->glDeleteProgram = glDeleteProgram;
++    procs->glUseProgram = glUseProgram;
++    procs->glLinkProgram = glLinkProgram;
++    procs->glGetProgramiv = glGetProgramiv;
++    procs->glGetProgramInfoLog = glGetProgramInfoLog;
++    procs->glAttachShader = glAttachShader;
++    procs->glCreateShader = glCreateShader;
++    procs->glDeleteShader = glDeleteShader;
++    procs->glCompileShader = glCompileShader;
++    procs->glShaderSource = glShaderSource;
++    procs->glGetShaderiv = glGetShaderiv;
++    procs->glGetShaderInfoLog = glGetShaderInfoLog;
++    procs->glEnableVertexAttribArray = glEnableVertexAttribArray;
++    procs->glVertexAttribPointer = (FF_PFNGLVERTEXATTRIBPOINTERPROC) glVertexAttribPointer;
++#endif
+ 
+     return 0;
+ 
++#undef CAN_DYNAMIC_LOAD
+ #undef SelectedGetProcAddress
+ #undef LOAD_OPENGL_FUN
+ }
+-- 
+2.38.1
+
diff --git a/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/2003-libavcodec-fix-v4l2_req_devscan.patch b/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/2003-libavcodec-fix-v4l2_req_devscan.patch
new file mode 100644
index 0000000..2232c48
--- /dev/null
+++ b/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/2003-libavcodec-fix-v4l2_req_devscan.patch
@@ -0,0 +1,45 @@
+From 62c2f041890a6e20770350721a0a2138d0b38634 Mon Sep 17 00:00:00 2001
+From: Vincent Davis Jr <vince@underview.tech>
+Date: Sat, 3 Dec 2022 23:35:51 -0600
+Subject: [PATCH] libavcodec: fix v4l2_req_devscan.h
+
+Upstream-Status: Inappropriate
+
+RPI-Distro repo clones original ffmpeg and applies patches to enable
+raspiberry pi support.
+
+Fixes minor differences between v4l2_req_devscan.c
+and v4l2_req_devscan.h after all patches have been
+applied.
+
+Signed-off-by: Vincent Davis Jr <vince@underview.tech>
+---
+ libavcodec/v4l2_req_devscan.h | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/libavcodec/v4l2_req_devscan.h b/libavcodec/v4l2_req_devscan.h
+index 0baef365..cd9c49ac 100644
+--- a/libavcodec/v4l2_req_devscan.h
++++ b/libavcodec/v4l2_req_devscan.h
+@@ -1,6 +1,8 @@
+ #ifndef _DEVSCAN_H_
+ #define _DEVSCAN_H_
+ 
++#include <stdint.h>
++
+ struct devscan;
+ struct decdev;
+ enum v4l2_buf_type;
+@@ -13,7 +15,8 @@ const char *decdev_video_path(const struct decdev *const dev);
+ enum v4l2_buf_type decdev_src_type(const struct decdev *const dev);
+ uint32_t decdev_src_pixelformat(const struct decdev *const dev);
+ 
+-const struct decdev *devscan_find(struct devscan *const scan, const uint32_t src_fmt_v4l2);
++const struct decdev *devscan_find(struct devscan *const scan,
++                                  const uint32_t src_fmt_v4l2);
+ 
+ int devscan_build(void * const dc, struct devscan **pscan);
+ void devscan_delete(struct devscan **const pScan);
+-- 
+2.38.1
+
diff --git a/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/2004-libavcodec-omx-replace-opt-vc-path-with-usr-lib.patch b/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/2004-libavcodec-omx-replace-opt-vc-path-with-usr-lib.patch
new file mode 100644
index 0000000..02c07de
--- /dev/null
+++ b/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/files/2004-libavcodec-omx-replace-opt-vc-path-with-usr-lib.patch
@@ -0,0 +1,35 @@
+From 0dfb56e12fa709794525cda1471091f6699905d5 Mon Sep 17 00:00:00 2001
+From: Vincent Davis Jr <vince@underview.tech>
+Date: Thu, 8 Dec 2022 10:49:03 -0600
+Subject: [PATCH] libavcodec: omx replace /opt/vc path with /usr/lib
+
+Upstream-Status: Inappropriate
+
+RPI-Distro repo clones original ffmpeg and applies patches to enable
+raspiberry pi support.
+
+Configures omx.c for OE usages as libbcm_host.so
+and libopenmaxil.so are located in a different
+location.
+
+Signed-off-by: Vincent Davis Jr <vince@underview.tech>
+---
+ libavcodec/omx.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/libavcodec/omx.c b/libavcodec/omx.c
+index 0a6a3083..8c6e9193 100644
+--- a/libavcodec/omx.c
++++ b/libavcodec/omx.c
+@@ -141,7 +141,7 @@ static av_cold OMXContext *omx_init(void *logctx, const char *libname, const cha
+ {
+     static const char * const libnames[] = {
+ #if CONFIG_OMX_RPI
+-        "/opt/vc/lib/libopenmaxil.so", "/opt/vc/lib/libbcm_host.so",
++        "/usr/lib/libopenmaxil.so", "/usr/lib/libbcm_host.so",
+ #else
+         "libOMX_Core.so", NULL,
+         "libOmxCore.so", NULL,
+-- 
+2.38.1
+
diff --git a/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/rpidistro-ffmpeg_4.3.2.bb b/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/rpidistro-ffmpeg_4.3.4.bb
similarity index 88%
rename from meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/rpidistro-ffmpeg_4.3.2.bb
rename to meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/rpidistro-ffmpeg_4.3.4.bb
index bf8d4b8..30e7c57 100644
--- a/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/rpidistro-ffmpeg_4.3.2.bb
+++ b/meta-raspberrypi/recipes-multimedia/rpidistro-ffmpeg/rpidistro-ffmpeg_4.3.4.bb
@@ -33,23 +33,27 @@
 DEPENDS = "nasm-native"
 
 inherit autotools pkgconfig
-PACKAGECONFIG ??= "avdevice avfilter avcodec avformat swresample swscale postproc avresample \
-                   opengl udev sdl2 ffplay alsa bzlib lzma pic pthreads shared theora zlib \
-                   libvorbis x264 gpl sand rpi vout-drm vout-egl \
-                   ${@bb.utils.contains('MACHINE_FEATURES', 'vc4graphics', '', 'mmal', d)} \
+PACKAGECONFIG ??= "avdevice avfilter avcodec avformat swresample swscale postproc avresample ffplay \
+                   v4l2 drm udev alsa bzlib lzma pic pthreads shared theora zlib libvorbis x264 gpl \
+                   ${@bb.utils.contains('MACHINE_FEATURES', 'vc4graphics', '', 'mmal rpi sand vout-drm', d)} \
                    ${@bb.utils.contains('AVAILTUNES', 'mips32r2', 'mips32r2', '', d)} \
-                   ${@bb.utils.contains('DISTRO_FEATURES', 'x11', 'xv xcb', '', d)}"
+                   ${@bb.utils.contains('DISTRO_FEATURES', 'opengl', 'opengl', '', d)} \
+                   ${@bb.utils.contains('DISTRO_FEATURES', 'x11', 'xv xcb vout-egl epoxy', '', d)}"
 
 SRC_URI = "\
     git://git@github.com/RPi-Distro/ffmpeg;protocol=https;branch=pios/bullseye \
     file://0001-avcodec-arm-sbcenc-avoid-callee-preserved-vfp-regist.patch \
     file://0002-Fix-build-on-powerpc-and-ppc64.patch \
     file://0003-avcodec-pngenc-remove-monowhite-from-apng-formats.patch \
-    file://0004-ffmpeg-4.3.2-rpi_10.patch \
-    file://0005-fix_flags.diff \
-"
+    file://0004-ffmpeg-4.3.4-rpi_14.patch \
+    file://0005-fix-flags.diff \
+    file://2001-configure-setup-for-OE-core-usage.patch \
+    file://2002-libavdevice-opengl_enc-update-dynamic-function-loader.patch \
+    file://2003-libavcodec-fix-v4l2_req_devscan.patch \
+    file://2004-libavcodec-omx-replace-opt-vc-path-with-usr-lib.patch \
+    "
 
-SRCREV = "ea72093f350f38edcd39c480b331c3219c377642"
+SRCREV = "246e1a55a0eca931537d8706acd8b133c07beb05"
 
 S = "${WORKDIR}/git"
 
@@ -70,7 +74,7 @@
 PACKAGECONFIG[bzlib] = "--enable-bzlib,--disable-bzlib,bzip2"
 PACKAGECONFIG[fdk-aac] = "--enable-libfdk-aac --enable-nonfree,--disable-libfdk-aac,fdk-aac"
 PACKAGECONFIG[gpl] = "--enable-gpl,--disable-gpl"
-PACKAGECONFIG[opengl] = "--enable-opengl,--disable-opengl,virtual/libgl"
+PACKAGECONFIG[opengl] = "--enable-opengl,--disable-opengl,virtual/libgles2"
 PACKAGECONFIG[gsm] = "--enable-libgsm,--disable-libgsm,libgsm"
 PACKAGECONFIG[jack] = "--enable-indev=jack,--disable-indev=jack,jack"
 PACKAGECONFIG[libvorbis] = "--enable-libvorbis,--disable-libvorbis,libvorbis"
@@ -90,9 +94,11 @@
 PACKAGECONFIG[xcb] = "--enable-libxcb,--disable-libxcb,libxcb"
 PACKAGECONFIG[xv] = "--enable-outdev=xv,--disable-outdev=xv,libxv"
 PACKAGECONFIG[zlib] = "--enable-zlib,--disable-zlib,zlib"
-#PACKAGECONFIG[snappy] = "--enable-libsnappy,--enable-libsnappy,snappy"
+PACKAGECONFIG[snappy] = "--enable-libsnappy,--disable-libsnappy,snappy"
 PACKAGECONFIG[udev] = "--enable-libudev,--disable-libudev,udev"
-PACKAGECONFIG[v4l2] = "--enable-libv4l2 --enable-v4l2-request --enable-libdrm,,v4l-utils"
+PACKAGECONFIG[drm] = "--enable-libdrm,--disable-libdrm,libdrm"
+PACKAGECONFIG[epoxy] = "--enable-epoxy,--disable-epoxy,libepoxy"
+PACKAGECONFIG[v4l2] = "--enable-libv4l2 --enable-v4l2-m2m,,v4l-utils"
 PACKAGECONFIG[mmal] = "--enable-omx --enable-omx-rpi --enable-mmal,,userland"
 PACKAGECONFIG[sand] = "--enable-sand,,"
 PACKAGECONFIG[rpi] = "--enable-rpi,,"
@@ -138,11 +144,6 @@
 "
 EXTRA_OECONF:append:linux-gnux32 = " --disable-asm"
 
-# Directly specify the include directories the contain headers for
-#   libdrm
-#   openmaxil
-TARGET_CFLAGS:append = " -I${STAGING_INCDIR}/IL -I${STAGING_INCDIR}/drm"
-
 # gold crashes on x86, another solution is to --disable-asm but thats more hacky
 # ld.gold: internal error in relocate_section, at ../../gold/i386.cc:3684
 LDFLAGS:append:x86 = "${@bb.utils.contains('DISTRO_FEATURES', 'ld-is-gold', ' -fuse-ld=bfd ', '', d)}"
@@ -190,5 +191,5 @@
 
 # Only enable it for rpi class of machines
 COMPATIBLE_HOST = "null"
-COMPATIBLE_HOST:rpi = "'(.*)'"
+COMPATIBLE_HOST:rpi = "(.*)"