Tech7 min read

FFmpeg 9.0 defaults tls_verify to 1 and decodes animated WebP, tested vs 8.1.2

IkesanContents

The FFmpeg project released major version 9.0 “Lei” on August 4, 2026.
The release branch was cut from master on June 26, 2026, and this is the first major release in roughly four months since 8.1 (March 16, 2026). The n8.1...n9.0 comparison on GitHub shows 2,205 commits touching 1,794 files.

FFmpeg is developed on the ffmpeg-devel mailing list and embedded in the video and audio pipelines behind browsers, smartphone apps, and streaming servers, so even developers who never type the command are affected through it as a dependency.

All seven libraries bumped their major versions

FFmpeg consists of several internal libraries such as libavcodec (codec handling) and libavformat (containers and protocols), and their version numbers go up at major-release time.
In 9.0, all seven libraries got a bump.

LibraryAs of 8.09.0Role
libavutil60.8.10061.1.100Common utilities
libavcodec62.11.10063.1.100Encoding and decoding
libavformat62.3.10063.1.100Containers and protocols
libavdevice-63.1.100I/O devices
libavfilter-12.1.100Filter graphs
libswscale-10.1.100Colorspace conversion and scaling
libswresample-7.1.100Audio resampling

A major version bump breaks the ABI, so applications that dynamically link FFmpeg need a rebuild.

TLS certificate verification is now on by default

9.0 changes the default value of the tls_verify option in the TLS protocol.
This option applies to protocols that ride on TLS, such as https and rtmps, and its default used to be 0 (do not verify).
In 9.0, triggered by libavformat reaching major version 63, the default flipped to 1 (verify the certificate presented by the peer).

Timo Rothenpieler implemented the change for FFmpeg 8.0 in August 2025, using the FF_API_NO_DEFAULT_TLS_VERIFY macro so that the old behavior stayed in place below major version 63 and the new default kicked in from 63 onward.
The 8.0 release notes announced that it would be enabled starting with the next major version, and 9.0 applied it exactly as announced.

Self-signed certificates not registered in the trust store, expired certificates, and environments missing the required CA certificates will now fail to connect on TLS backends that support verification.
Passing -tls_verify 0 turns verification off. Passing the certificate via -ca_file lets you connect with verification still enabled.

The Changelog explicitly lists two more removals.

ChangeDetails
CELT decoder removedDrops decode support for the standalone celt codec. This is separate from the CELT layer inside Opus, so Opus decoding is unaffected
Old NVENC options removedDrops deprecated NVENC options and support for NVIDIA SDK versions below 11.1

swscale was redesigned

libswscale, which handles colorspace conversion and scaling, got its internals overhauled.
On top of the existing CPU paths with x86 SIMD and AArch64 NEON, it now also runs on the GPU through Vulkan SPIR-V shaders.

Animated WebP decoding goes native

FFmpeg had a decoder for still WebP images, but no demuxing or decoding for animated WebP.
It did not handle animation chunks like ANIM and ANMF, and the ticket reported failures with image data not found.
After ticket #4907 was opened in October 2015 and several rounds of patch proposals, 9.0 finally adds a dedicated decoder and demuxer.

Test environment

I verified the TLS and animated WebP changes locally.
9.0 has not landed in Homebrew yet (the stable formula is still 8.1.2), so I built both 8.1.2 and 9.0 from the official source archives for a like-for-like comparison.

ItemDetails
MachineM4 Mac mini (10 cores)
OSmacOS 26.5.2
CompilerApple clang 17.0.0
TLS backendOpenSSL 3 (Homebrew’s openssl@3)
configure--enable-openssl --disable-doc --disable-debug --disable-ffplay
Otherslibwebp 1.6.0 (img2webp), Python 3.14.4

With almost no external libraries in this minimal configuration, make -j10 finished in the 40-second range for each version.

How 8.1.2 and 9.0 handle self-signed HTTPS

First, -h protocol=tls confirms the changed default.

# 8.1.2
-tls_verify        <boolean>    ED......... Verify the peer certificate (default false)
# 9.0
-tls_verify        <boolean>    ED......... Verify the peer certificate (default true)

For the connection test, I created a self-signed certificate with localhost in the SAN and served a test video from a local HTTPS server made by wrapping Python’s http.server in TLS.

openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem \
  -days 30 -nodes -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost"

The serving side tripped me up first. Python’s http.server does not support Range requests, so a regular mp4 with the moov atom at the end fails at the stream-probing stage before TLS even matters.
I generated the test video with -movflags +faststart.

Here is what ffmpeg -i https://localhost:8443/test.mp4 -f null - did across five patterns.

VersionOptionsResult
8.1.2noneSuccess (75 frames decoded)
9.0noneFailure (certificate verification error)
9.0-tls_verify 0Success
9.0-ca_file cert.pemSuccess
8.1.2-tls_verify 1 -ca_file cert.pemSuccess

Running 9.0 with no options produced this error and the input never opened.

[tls @ 0xa8ec14080] error:0A000086:SSL routines::certificate verify failed
[in#0 @ 0xa8ec0c000] Error opening input: Input/output error
Error opening input file https://localhost:8443/test.mp4.

Since verification is on by default in 9.0, passing just -ca_file cert.pem without -tls_verify 1 was enough for the connection to go through.
The certificate carried a SAN, so host-name verification passed as well.
8.1.2 also connected with verification when I passed the options explicitly, so the only difference between the two was the default.

Decoding animated WebP on 8.1.2 vs 9.0

I built a 10-frame animated WebP at 100 ms per frame (2,476 bytes) with img2webp and fed the same file to both versions.

8.1.2 tries to read it as the still-image webp_pipe, fails to probe the stream, and decoding stops at 0 frames.

[webp_pipe @ 0xa7d42c000] Could not find codec parameters for stream 0 (Video: webp, none): unspecified size
(snip)
frame=    0 fps=0.0 q=0.0 Lsize=       0KiB time=N/A bitrate=N/A speed=N/A
Conversion failed!

The message differs from the image data not found in ticket #4907, but the outcome is the same: no decode.

9.0 reads it with the new webp_anim demuxer and decoded all 10 frames.

Input #0, webp_anim, from 'anim.webp':
  Duration: N/A, start: 0.000000, bitrate: N/A
  Stream #0:0: Video: webp_anim, argb, 320x240, 10 fps, 10 tbr, 1k tbn

Converting to mp4 also worked and produced a 10-frame mpeg4 video without extra flags.

AdditionDetails
v360_vulkan filterRuns 360-degree/panoramic projection conversion on the GPU via Vulkan
Vulkan hardware acceleration for APVEncodes and decodes Samsung’s APV codec on the GPU
ONNX Runtime DNN backendLets the dnn_processing filter pick CUDA, DirectML, or VitisAI execution providers in addition to CPU
transpose_cuda filterRotates and transposes frames on NVIDIA GPUs
AMD AMFAdds frc_amf frame-rate conversion, vpp_amf with wider HDR support, and hardware memory mapping

Other codec and feature additions

  • VideoToolbox hardware acceleration for ProRes RAW (for Apple hardware)
  • dovi_split bitstream filter that separates multi-layer HEVC in Dolby Vision Profile 7
  • SMPTE 2094-50 metadata reading and passthrough (HDR-related metadata)
  • LCEVC track muxing in the MP4 muxer
  • HE-AAC 960 decoding (for DAB+ digital radio)
  • A video encoder and muxer for the Playdate console

Following FFmpeg 8.0’s OpenAI Whisper speech-recognition filter, Vulkan-based AV1 encoding, and Vulkan hardware acceleration for VP9, 9.0 keeps widening GPU support with Vulkan at the center.

I have not checked the 9.0 packages from Homebrew, apt, or static-build distributions (BtbN/FFmpeg-Builds and the like) yet, but they build from the same source, so the same default change should arrive there too.

Sources: