# Technical Documentation: videolan/vlc

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/videolan/vlc)

### Overview (README.md)

# VLC media player

**VLC** is a libre and open source **media player** and **multimedia engine**,
focused on **playing everything**, and **running everywhere**.

**VLC** can play most multimedia files, discs, streams, devices and is also able to
convert, encode, **stream** and manipulate streams into numerous formats.

VLC is used by many over the world, on numerous platforms, for very different use cases.

The **engine of VLC** can be embedded into 3rd party applications, and is called *libVLC*.

**VLC** is part of the [VideoLAN project](https://videolan.org) and
is developed and supported by a community of volunteers.

The VideoLAN project was started at the university [École Centrale Paris](https://www.centralesupelec.fr/) who
relicensed VLC under the GPLv2 license in February 2001. Since then, VLC has
been downloaded **billions** of times.

## License

**VLC** is released under the GPLv2 *(or later)* license.
*On some platforms, it is de facto GPLv3, because of the licenses of dependencies*.

**libVLC**, the engine is released under the LGPLv2 *(or later)* license. \
This allows embedding the engine in 3rd party applications, while letting them to be licensed under other licenses.

# Platforms

VLC is available for the following platforms:
- [Windows] *(from 7 and later, including UWP platforms and all versions of Windows 10)*
- [macOS] *(10.10 and later)*
- [GNU/Linux] and affiliated
- [BSD] and affiliated
- [Android] *(4.2 and later)*, including Android TV and Android Auto
- [iOS] *(9 and later)*, including AppleTV and iPadOS
- Haiku, OS/2 and a few others.

[Windows]: https://www.videolan.org/vlc/download-windows.html
[macOS]: https://www.videolan.org/vlc/download-macosx.html
[GNU/Linux]: https://www.videolan.org/vlc/#download
[BSD]: https://www.videolan.org/vlc/download-freebsd.html
[Android]: https://www.videolan.org/vlc/download-android.html
[iOS]: https://www.videolan.org/vlc/download-ios.html

Not all platforms receive the same amount of care, due to our limited resources.

**Nota Bene**: The [Android app](https://code.videolan.org/videolan/vlc-android/) and
the [iOS app](https://code.videolan.org/videolan/vlc-ios/) are located in different repositories
than the main one.

# Contributing & Community

**VLC** is maintained by a community of people, and VideoLAN is not paying any of them.\
The community is composed of developers, helpers, maintainers, designers and writers that want
this open source project to thrive.

The main development of VLC is done in the C language, but this repository also contains
plenty of C++, Obj-C, asm and Rust.

Other repositories linked to vlc are done in languages including Kotlin/Java [(Android)](https://code.videolan.org/videolan/vlc-android/),
Swift [(iOS)](https://code.videolan.org/videolan/vlc-ios/), and C# [(libVLCSharp)](https://code.videolan.org/videolan/libvlcsharp/).

We need help with the following tasks:
- Coding
- Packaging for Windows, macOS and Linux distributions
- Technical writing for the documentation
- Design
- Support
- Community management and communication.

Please contribute :)

We are on IRC. You can find us on the **#videolan** channel on *[Libera.chat]*.

[Libera.chat]: https://libera.chat

## Contributions

Contributions are now done through Merge Requests on our [GitLab repository](https://code.videolan.org/videolan/vlc/).

CI and discussions should be resolved before a Merge Request can be merged.

# libVLC

**libVLC** is an embeddable engine for 3rd party applications and frameworks.

It runs on the same platforms as VLC *(and sometimes on more)* and can provide playback,
streaming and conversion of multimedia files and streams.


**libVLC** has numerous bindings for other languages, such as C++, Python and C#.

# Support

## Links

Some useful links that might help you:

- [VLC web site](https://www.videolan.org/vlc/)
- [Support](https://www.videolan.org/support/)
- [Forums](https://forum.videolan.org/)
- [Wiki](https://wiki.videolan.org/)
- [Developer's Corner](https://wiki.videolan.org/Developers_Corner)
- [VLC hacking guide](https://wiki.videolan.org/Hacker_Guide)
- [Bugtracker](https://code.videolan.org/videolan/vlc/-/issues)
- [VideoLAN web site](https://www.videolan.org/)

## Source Code sitemap
```
/* Detailed source-code truncated for AI context efficiency. */
```

---

### Test Checkasm Ext Docs Getting Started (`test/checkasm/ext/docs/getting-started.md`)

@page getting_started Getting Started

This guide will walk you through installing checkasm and writing your first test.

@tableofcontents

@section installation Installation

You can either load checkasm as a library (e.g. via `pkg-config`), or include it directly in your project's build system.

@subsection meson_submodules Meson using wrap files (recommended)

First, create `subprojects/checkasm.wrap`:

@code{.ini}
[wrap-git]
url = https://code.videolan.org/videolan/checkasm.git
revision = release # or a specific tag/release
directory = checkasm
@endcode

Then integrate it into your build system:

@code{.meson}
# This first attempts loading checkasm as an external dependency using the
# appropriate platform-specific method (e.g. pkg-config on POSIX systems),
# and falls back to using the bundled version inside `subprojects/checkasm`
# otherwise.
checkasm_dependency = dependency('checkasm',
    # Extracts the `checkasm_dep` variable from the `checkasm` subproject.
    fallback: ['checkasm', 'checkasm_dep'],
    required: false
)

# Alternatively, you can directly force use of the bundled version:
# checkasm_dependency = subproject('checkasm').get_variable('checkasm_dep')

if checkasm_dependency.found()
    checkasm = executable('checkasm',
        checkasm_sources,
        dependencies: checkasm_dependency,
    )

    test('checkasm', checkasm, suite: 'checkasm')
    benchmark('checkasm', checkasm, suite: 'checkasm', args: '--bench')
endif
@endcode

@subsection meson_wrap Meson using submodules (alternative)

As an alternative, you may use git submodules to include checkasm as a subproject.
This may be preferred in some environments where the build system cannot access
the internet during configuration time, or if you're already using submodules
in your project.

@code{.bash}
git submodule init
git submodule add -b release https://code.videolan.org/videolan/checkasm subprojects/checkasm
# or checkout a specific tag/release
@endcode

Then declare the dependency in your `meson.build` as usual. (See the previous section)

@subsection manual_installation Manual Installation

You can also build and install checkasm manually:

@code{.bash}
git clone https://github.com/videolan/checkasm.git && cd checkasm
meson setup builddir -Dprefix=$PREFIX # (set optional build prefix)
meson compile -C builddir
meson install -C builddir
@endcode

This is discouraged in favor of using Meson subprojects or distribution packages,
but may be useful inside containerized environments, CI systems or custom
build roots.

@section quick_start Quick Start Example

Let's create a simple test for a vector addition function that operates on buffers.

@subsection quick_start1 1. Prerequisites

Let's assume you have a reference implementation and an optimized version,
alongside a way of detecting CPU features and choosing the implementation
based on that:

@code{.h}
// my_dsp.h
#include <stdint.h>

enum {
    CPU_FLAG_AVX = 1 << 0,
};

unsigned detect_cpu_flags(void);

typedef void (*add8_func_t)(uint16_t *dst, const uint8_t *src1,
                            const uint8_t *src2, size_t len);

add8_func_t get_add8_func(unsigned cpu_flags);
@endcode

@code{.c}
// my_dsp.c

#include "my_cpu.h"

// Reference implementation (pure C)
static void add8_c(uint16_t *dst, const uint8_t *src1,
                   const uint8_t *src2, size_t len)
{
    for (size_t i = 0; i < len; i++)
        dst[i] = src1[i] + src2[i];
}

// Optimized implementation (pretend this is assembly)
static void add8_avx(uint16_t *dst, const uint8_t *src1,
                     const uint8_t *src2, size_t len)
{
    // Assembly optimized version would go here
    add8_c(dst, src1, src2, len);
}

add8_func_t get_add8_func(unsigned cpu_flags)
{
    if (cpu_flags & CPU_FLAG_AVX)
        return add8_avx;
    return add8_c;
}
@endcode

@subsection quick_start2 2. Write the Test

Create your test file:

@code{.c}
// check_dsp.c

#include <checkasm/checkasm.h>
#include <checkasm/test.h>

#include "my_dsp.h"

#define WIDTH 1024

static void test_add8(const CheckasmCpu cpu)
{
    // Declare aligned buffers for testing
    CHECKASM_ALIGN(uint8_t src1[WIDTH]);
    CHECKASM_ALIGN(uint8_t src2[WIDTH]);
    CHECKASM_ALIGN(uint16_t dst_c[WIDTH]);
    CHECKASM_ALIGN(uint16_t dst_a[WIDTH]);

    // Declare the function signature
    checkasm_declare(void, uint16_t *, const uint8_t *, const uint8_t *, size_t);

    if (checkasm_check_func(get_add8_func(cpu), "add_8")) {
        // Initialize source buffers with quasi-random test vectors
        INITIALIZE_BUF(src1);
        INITIALIZE_BUF(src2);

        // Test with various buffer sizes
        for (int w = 1; w <= WIDTH; w <<= 1) {
            // Clear destination buffers before each test
            CLEAR_BUF(dst_c);
            CLEAR_BUF(dst_a);

            // Call reference and optimized implementations
            checkasm_call_ref(dst_c, src1, src2, w);
            checkasm_call_new(dst_a, src1, src2, w);

            // Compare results - checkasm_check1d will report any mismatches
            checkasm_check1d(uint16_t, dst_c, dst_a, w, "sum");
        }

        // Benchmark the optimized version on the largest buffer size
        checkasm_bench_new(checkasm_alternate(dst_c, dst_a), src1, src2, WIDTH);
    }
}

static void check_dsp(void)
{
    const CheckasmCpu cpu = checkasm_get_cpu_flags();

    // Test all related functions and report as a single function group
    test_add8(cpu);
    // test_add16(cpu);
    // ...
    checkasm_report("add");

    // Check more function groups
    // ...
}

// Test registry
static const CheckasmTest tests[] = {
    { "dsp", check_dsp },
    {0} // array terminator
};

// CPU flag registry
static const CheckasmCpuInfo cpu_flags[] = {
    { "AVX", "avx", CPU_FLAG_AVX },
    {0} // array terminator
};

int main(int argc, const char *argv[]) {
    CheckasmConfig config = {
        .tests     = tests,
        .cpu_flags = cpu_flags,
        .cpu       = detect_cpu_flags(),
    };

    return checkasm_main(&config, argc, argv);
}
@endcode

@subsection quick_start3 3. Build and Run

@code{.bash}
# Compile (example using gcc directly)
gcc -o check_dsp my_dsp.c check_dsp.c $(pkg-config --cflags --libs checkasm)
# or use `meson compile` if using Meson

# Run all tests
./check_dsp
@endcode

@section options Command-Line Options

checkasm provides several useful command-line options:

@code{.bash}
# List all available functions
./checkasm --list-functions

# Run specific functions (supports wildcards)
./checkasm --function=add_*_8bpc

# Run benchmarks
./checkasm --bench

# Run specified test with higher benchmark duration (here: 10 ms)
./checkasm --test=pixel --bench --duration=10000

# Enable verbose output
./checkasm --verbose
@endcode

The `--help` output shows all available options:

@code{.txt}
Usage: checkasm [options...] <random seed>
    <random seed>              Use fixed value to seed the PRNG
Options:
    --affinity=<cpu>           Run the process on CPU <cpu>
    --bench -b                 Benchmark the tested functions
    --csv, --tsv, --json,      Choose output format for benchmarks
    --html
    --function=<pattern> -f    Test only the functions matching <pattern>
    --help -h                  Print this usage info
    --list-cpu-flags           List available cpu flags
    --list-functions           List available functions
    --list-tests               List available tests
    --duration=<μs>            Benchmark duration (per function) in μs
    --repeat[=<N>]             Repeat tests N times, on successive seeds
    --test=<pattern> -t        Test only <pattern>
    --verbose -v               Print verbose timing info and failure data
@endcode

---

@section getting_started_next_steps Next Steps

Now that you've set up checkasm and written your first test, learn how to
integrate it properly with your project's CPU detection and dispatch mechanisms.

**Next:** @ref integration

---

### Doc BUILD Win32 (`doc/BUILD-win32.md`)

# Compile VLC for Windows

There are multiple ways to compile VLC for Windows. All of them involve `gcc` or `llvm` with `mingw-w64`.
Compilation with MSVC or clang-cl is currently not supported.

# UNIX Shell

You will also need a UNIX shell to run the build process. On Windows you can either
use [Windows Subsystem for Linux (WSL)](https://learn.microsoft.com/en-us/windows/wsl/) (recommended) or [msys2](https://www.msys2.org/).
You may also build inside a Docker on [Linux](https://docs.docker.com/desktop/install/linux-install/)
or on [Windows](https://docs.docker.com/desktop/install/windows-install/).

The choice of which is best depends on your goals. In terms of build times, Docker is the fastest
then WSL and msys2 is the slowest.

Unless you use a Docker image, you will need to install multiple development packages that VLC
needs to build itself and its contribs.

* On Ubuntu/Debian (or WSL), they are the same tools installed in our [Docker Images](https://code.videolan.org/videolan/docker-images/-/blob/master/vlc-debian-win64/Dockerfile#L27):
```
sudo apt-get update -qq
sudo apt-get install -qqy \
    git wget bzip2 file libwine-dev unzip libtool libtool-bin libltdl-dev pkg-config ant \
    build-essential automake texinfo yasm p7zip-full autopoint \
    gettext cmake zip wine nsis g++-mingw-w64-i686 curl gperf flex bison \
    libcurl4-gnutls-dev python3 python3-setuptools python3-mako python3-requests python3-venv \
    gcc make procps ca-certificates \
    openjdk-11-jdk-headless nasm jq gnupg \
    meson autoconf
```

* On msys2 you should install similar packages:
```
pacman -Syu
pacman -S --needed git wget bzip2 file unzip libtool pkg-config \
    automake autoconf texinfo yasm p7zip \
    gettext cmake zip curl gperf flex bison \
    python3 python3-setuptools python3-mako \
    gcc make ca-certificates nasm gnupg patch help2man \
    python3 meson
```


# Toolchains

There are 2 toolchains supported to build VLC for Windows:
* mingw-w64 with gcc
* mingw-w64 with llvm

The `gcc` is the most common one as it comes as packages in Linux distributions and msys2.
The problem with the gcc toolchain is that if you need to debug your build with `gdb`.
It is very slow when you use breakpoints because for each of the 200+ DLLs loaded
during the VLC launch, it looks for your breakpoints.

The 'llvm' toolchain solves this issue by producing .pdb files. You then debug your
code with the Windows Debugger, even within Visual Studio.

## Install mingw-w64 LLVM

The 'llvm' toolchain is normally not found in Linux or msys2. You will need to install it yourself.
You can download prebuilt packages from https://github.com/mstorsjo/llvm-mingw. And then add the
place where you decompressed it, followed by `/bin`, in your `PATH`.

You also have a choice between `ucrt` and `msvcrt`. `ucrt` is the one you should use if you don't care about
support on Windows versions older than Windows 10. If you need to support older Windows versions you
should go with the `msvcrt` version. The official VLC builds use `msvcrt` for desktop builds, and
`ucrt` for Universal Windows Platform (UWP) builds.

* On Linux:
```sh
wget https://github.com/mstorsjo/llvm-mingw/releases/download/20220906/llvm-mingw-20220906-msvcrt-ubuntu-18.04-x86_64.tar.xz
tar xvf llvm-mingw-20220906-msvcrt-ubuntu-18.04-x86_64.tar.xz -C /opt
```
Every time you build VLC, you will need to have the toolchain in your PATH.
We recommend creating a script file to do this, so that you can call
it when you start your build session:

```sh
echo 'export PATH=/opt/llvm-mingw-20220906-msvcrt-ubuntu-18.04-x86_64/bin:$PATH' > toolchain.sh
```

You will need to call this file when you start your build session:

```sh
source toolchain.sh
```

* On msys2, **use the mingw64 (blue) environment** (ie not msys (purple) or mingw32 (grey) environments):
```sh
wget https://github.com/mstorsjo/llvm-mingw/releases/download/20220906/llvm-mingw-20220906-msvcrt-x86_64.zip
unzip llvm-mingw-20220906-msvcrt-x86_64.zip -d /opt
```

Every time you build VLC, you will need to have the toolchain in your PATH.
We recommend creating a script file to do this, so that you can call
it when you start your build session:

```sh
echo 'export PATH=/opt/llvm-mingw-20220906-msvcrt-x86_64/bin:$PATH' > toolchain.sh
```

You will need to call this file when you start your build session:

```sh
source toolchain.sh
```

* On Docker, you can use these 2 images:

  * msvcrt: `registry.videolan.org/vlc-debian-llvm-msvcrt:20240212151604`
  * ucrt:   `registry.videolan.org/vlc-debian-llvm-ucrt:20240212151604`

You can find the latest Docker images we use in [extras/ci/gitlab-ci.yml](/extras/ci/gitlab-ci.yml)

## Install mingw-w64 gcc

This is the default build environment of VLC when cross-compiling. This is also
the environment used to produce the official builds that everyone uses. The mingw-w64
toolchain is usually found in all Linux distributions and in msys2.
You can install it this way:

* On Linux:
```sh
sudo apt-get install -qqy \
        gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 mingw-w64-tools
```

* On msys2:
```sh
pacman -S mingw-w64-x86_64-toolchain
> :: Repository mingw64
   1) mingw-w64-x86_64-binutils  2) mingw-w64-x86_64-crt-git  3) mingw-w64-x86_64-gcc  4) mingw-w64-x86_64-gcc-ada  5) mingw-w64-x86_64-gcc-fortran  6) mingw-w64-x86_64-gcc-libgfortran  7) mingw-w64-x86_64-gcc-libs  8) mingw-w64-x86_64-gcc-objc
   9) mingw-w64-x86_64-gdb  10) mingw-w64-x86_64-gdb-multiarch  11) mingw-w64-x86_64-headers-git  12) mingw-w64-x86_64-libgccjit  13) mingw-w64-x86_64-libmangle-git  14) mingw-w64-x86_64-libwinpthread-git  15) mingw-w64-x86_64-make  16) mingw-w64-x86_64-pkgconf
   17) mingw-w64-x86_64-tools-git  18) mingw-w64-x86_64-winpthreads-git  19) mingw-w64-x86_64-winstorecompat-git
> Enter a selection (default=all):

Type enter to select "all"
```


* On Docker, you can use the `registry.videolan.org/vlc-debian-win64-posix:20240212151604` image.

You can find the latest Docker images we use in [extras/ci/gitlab-ci.yml](/extras/ci/gitlab-ci.yml)


# Getting VLC

This is the easy part. In your UNIX Shell type:

```sh
git config --global core.autocrlf false
git clone https://code.videolan.org/videolan/vlc.git
```

This will download the VLC source code into the `vlc` folder, in your current directory.

# Building VLC

Fully build in a separate folder `build`:

```sh
mkdir build
cd build
../vlc/extras/package/win32/build.sh -a x86_64
```

This will build
* VLC "extra tools" in case you are missing some in your environment.
* VLC "contribs", a hundred different libraries that VLC uses like FFmpeg, Qt, etc.
* VLC "core" (libvlccore.dll) the heart of VLC.
* VLC "modules" which contains all the possible extensions to add functionality to VLC.
* libvlc.dll a DLL to use VLC from external code with a stable API.

## Prebuilt contribs

You may not want to spend one or two hours building all the contribs if you're never going to
want to touch any of them. In that case you may want to reuse prebuilt binaries and save some time.
To build with prebuilt contribs you need to have a **matching compiler** and especially
a **matching C++ compiler**. So the ones available may not match your environment. In that case you
still need to build the contribs yourself.

The choice of compiler may depend on how you plan to debug your code. See the [Debugging
section](#debugging) below for more information.

## Building with gcc (11) prebuilt contribs

If your mingw-w64 compiler/toolchain is gcc 11 you can use these commands to build VLC
and reuse prebuilt contribs:

```sh
mkdir build
cd build
export VLC_CONTRIB_SHA="$(cd ../vlc; extras/ci/get-contrib-sha.sh win32)"
export VLC_PREBUILT_CONTRIBS_URL="https://artifacts.videolan.org/vlc/win64/vlc-contrib-x86_64-w64-mingw32-${VLC_CONTRIB_SHA}.tar.bz2"
../vlc/extras/package/win32/build.sh -a x86_64 -p
```

## Building with LLVM (13) prebuilt contribs

If your mingw-w64 compiler/toolchain is LLVM 13 you can use these commands to build VLC
and reuse prebuilt contribs. The name of the prebuilt tarball is the same, but the folder is different:

```sh
mkdir build
cd build
export VLC_CONTRIB_SHA="$(cd ../vlc; extras/ci/get-contrib-sha.sh win32)"
export VLC_PREBUILT_CONTRIBS_URL="https://artifacts.videolan.org/vlc/win64-llvm/vlc-contrib-x86_64-w64-mingw32-$VLC_CONTRIB_SHA.tar.bz2"
time ../vlc/extras/package/win32/build.sh -a x86_64 -p
```

## libvlc

You may be interested in just building libvlc without the desktop app. In that case
you can add `-z` to you build.sh call. It will save you some building time, especially
if you build contribs. And it will also avoid creating the Qt gui plugins you will never use.

```sh
mkdir build
cd build
../vlc/extras/package/win32/build.sh -a x86_64 -z
```

# Other options

## CPU Control

You may want to reduce the load on your CPU, and thus your memory, when building.
You can control the number of maximum threads used during compilation with the `JOBS`
environment variable.

Due to a missing feature in ninja, contribs built from Meson will use all your CPU's
regardless of the `JOBS` value. You can reduce the problem by allocating a single
CPU thread to each Meson contrib with the `MESON_BUILD` environment variable.

Here is an example limit the build to 8 threads and 1 per Meson/ninja contrib.

```sh
JOBS=8 MESON_BUILD="-j 1" ../vlc/extras/package/win32/build.sh -a x86_64
```

## Config Flags

When building VLC manually, it's possible to enable/disable certain features of
VLC or select/unselect some compilation configurations.
You can find the list of options when running :

```sh
../vlc/configure -h
```

You can pass all these options to `build.sh` using the `CONFIGFLAGS` environment
variable.
For example you can enable the address sanitizer with:

```sh
CONFIGFLAGS="--with-sanitizer=address" ../vlc/extras/package/win32/build.sh -a x86_64
```

## Contrib Flags

When building VLC manually, it's possible to select/unselect certain contribs or
enable/disable some features.
You can find the list of options when running:

```sh
../vlc/contribs/bootstrap -h
```

You can pass all these options to `build.sh` using the `CONTRIBFLAGS` environment
variable.
For example you can disable bluray building with:

```sh
CONTRIBFLAGS="--disable-bluray" ../vlc/extras/package/win32/build.sh -a x86_64
```

# Debugging

Most of the time, if you need to build VLC yourself, you will also need to debug it.
The way to debug VLC on Windows depends on the toolchain you are using. When building
with gcc you need to use `gdb`. When building with LLVM, you can use `gdb` or
the `Windows Debugger`.

The latter is much faster than gdb which is very slow to pick breakpoints in the
hundred of VLC DLL's.

## Debugging with Windows Debugger

By default VLC is built with debug symbols. But if you want to use the **`Windows Debugger`
with LLVM**, you need to build with `.pdb` files. You can do that by adding `-d` to
the `build.sh` call.

```sh
../vlc/extras/package/win32/build.sh -a x86_64 -d
```

This will create PDB files, but since we are building in a UNIX environment, the
paths written in the PDB are UNIX files. The debugger will fail to find the proper
source files when you are debugging. There are different ways to fix this. Both
involve mapping the UNIX paths to paths in your Windows machine. That also means
if you want to debug properly the code you are editing, it needs to be visible
in Windows. It's always the case in msys2. It can be the case with WSL also it's
faster in a WSL1 environment than in WSL2. In Docker it's also possible by
mapping a Windows folder to make it accessible in your Docker. In the end your
VLC source code **should be on a partition/drive that Windows can see**,
and thus the Windows Debugger.

You can set the UNIX to Windows mapping when enabling PDB's in `build.sh`. You need
to set the Windows path where the VLC sources are this way:

```sh
../vlc/extras/package/win32/build.sh -a x86_64 -D "c:/path/to/sources/vlc"
```

You can also set it in your environment `CFLAGS` and `CXXFLAGS` which you can also add
in your `toolchain.sh` described [above](#install-mingw-w64-llvm):

```sh
CFLAGS="-fdebug-prefix-map='/mnt/c/'='c:/'" CXXFLAGS="-fdebug-prefix-map='/mnt/c/'='c:/'" ../vlc/extras/package/win32/build.sh -a x86_64 -d
```

## VLC desktop debug

The VLC you download has all plugins structured in different folders, all in a `"plugins"` folder.
But when you build VLC this is not the case. VLC cannot be used that way. It needs to be
either *installed* with the following command:

```sh
cd win64; make package-win-common
```

You will end up with a `vlc-4.0.0-dev` folder with the vlc EXE's and DLL's in the
proper place. You can debug from there. But this way of building is much slower than
just building the files that have been modified.

A faster way to debug is to just build with `build.sh` or even just calling make
in the `win64` folder. In that case you need to setup your debugging environment to
pick `libvlccore.dll`, `libvlc.dll` and plugins that you built. `libvlccore.dll` is
found in `win64/src/.libs` and `libvlc.dll` is found in `win64/lib/.libs`. You need to
add these paths to your `PATH`.

```
PATH="c:\path\to\sources\vlc\win64\lib\.libs;c:\path\to\sources\vlc\win64\src\.libs;C:\Windows\System32;C:\Windows;C:\Windows\System32\downlevel"
```

And when you run vlc.exe, you also need to set the `VLC_PLUGIN_PATH` environment variable
to the folder with all the plugins you built:

```
VLC_PLUGIN_PATH=c:\path\to\sources\vlc\win64\modules
```

# Future

In the future it might be possible to build VLC with Meson. With a native Windows toolchain it might
be possible to have proper Windows paths when compiling, which makes it even easier to
develop (click on the file path on compilation error). It will also avoid mapping the
PDB paths of UNIX paths to Windows paths.

---

### Doc Browsing (`doc/browsing.txt`)

= Directory-like browsing modules

== Access modules

Directory-like browsing is done in access modules providing a pf_readdir
callback. The pf_readdir callback has a specified prototype and must
match a specific expected behavior:

=== pf_readdir prototype

int (*pf_readdir)( stream_t *p_access, input_item_node_t *p_node );

* p_access: This is a pointer to the stream_t object that you are
  calling pf_readdir on. It CANNOT be NULL.
* p_node: A pointer on an input_item_node_t that you must provide and be
  responsible for. In particular, you have the responsibility to free it
  in case of error. Upon successful completion of this function, the
  node SHOULD contain all the items present in the directory-like object
  that the access was created for (psz_location field). It CANNOT be
  NULL.

=== pf_readdir return values and behavior

A call to pf_readdir has 3 possible results:

* The call was successful and the node has been filled with all the
  input_item_t possible depending on system state and module options. In this
  case, pf_readdir MUST return VLC_SUCCESS and info.b_eof MUST be set to true.
  This callback must NOT be called again.
* An unrecoverable error has occurred and no input_item_t was added to the node.
  The callback returns a VLC_ENOENT error code, and sets info.b_eof to true.
  This error SHOULD be propagated by the calling code (stream/demux/...)
  This callback must NOT be called again.
* A recoverable error has occurred. The callback MUST return an error code
  that is not VLC_SUCCESS or VLC_ENOENT (e.g. VLC_EGENERIC, VLC_ENOMEM, ...).
  Some input_item_t objects might have been added to the node; they are
  owned by the node which is owned by the access. This callback CAN be
  called again.

---

### Doc Clock (`doc/clock.md`)

# Clock Architecture

## Introduction

This document presents the new clock architecture for **VLC**, starting from
VLC 4.0.

The clock is the element that manages the synchronisation of all [ES][ES],
notably audio and video (+subtitles) synchronization.

While it can seem simple, at first glance, this part is not trivial, because
one must take care of numerous clocks in parallel, and they can be out-of-sync:
for example, your audio clock and your system clock are not necessarily in-sync.
This is the same issue between your streamer's clock and your player's clock.
And most clocks drift.

[PCR]: #f  "Program Clock Reference"
[PTS]: #f  "Presentation TimeStamp"
[DTS]: #f  "Decoding TimeStamp"
[ES]: #f "Elementary streams, aka Tracks"

### Old clock system

The old clock from VLC was mainly an "*input clock*", based on the [input
PCR][PCR] (from the file), inherited from the time where VLC was mostly a
**MPEG-2/TS** player on the network. This is the correct clock for streaming,
and notably when your input format carries a [PCR][] or something similar.

This old clock is quite nice, but has quite a few shortcomings, notably it
requires resampling of your audio output, even for local files or simple audio
files.

It also rebases all the timings on the main input [PCR][], and it loses all the
original [PTS][] (because it was adding the current computer date). This is
notably an issue for transcoding (where it loses the original timestamps), for
pausing (we need to keep rewriting the timestamps) and for frame-accuracy
(because you know accurately only the input timings).

It also depends too much on having a valid input, which are very rare,
unfortunately. And it does not work well with very large delays.

Finally, the UI seekbar advances only when the [PCR][] is updated, which makes
big jumps in the seekbar, and is not smooth for the end-user. This notably
happens with large-audio samples and is related to the file format.

## New clock system

The idea of the new clock system is to have multiple pluggable clocks, one of
which being the master clock, that could be selected depending on the
situation.

For example, you could have an **audio master clock** *(local files)*, an
**input PCR master clock** *(streaming)*, a **video master clock** *(V-Sync)*
or a future **external clock** *(SDI, netsync...)*.  In the *audio master
clock* mode, VLC would not resample the audio anymore.

As previously, there is one clock per input-program.  This **main clock** is
therefore mostly at the es_out level and manages mostly the [PTS][] of all the
Elementary Streams.

### Different clocks: main, slave and master

Every output *(audio, video, stream)* has a clock, managed in the core. One of
those clock is master, the other are slaves.

The main clock is the part managing the selection of the clocks and it will
derive the main timings from the system clock *(the monotonic clock)* and
will provide those timings to the rest of VLC, including outputs, modules and
interfaces.

It is currently an affine function based on the system clock, where the affine
coefficients are the moving average of the coefficient computed from the master
clock (In the future, it could be a different function).

The main clock holds a reference to all the output clocks, whether they are the
master or one of the slaves. Please refer to the ***src/clock/clock.c*** for
details about those structures

The master clock de facto defines the slope of the affine function.

The main clock will rebase the timestamps according to the master clock.  The
slaves ask the main clock, what is the system time corresponding to their
[PTS][].

If you want to see it differently, the master clock is a setter and the slave
clocks are the getters.

### Core outputs

The audio will be the master clock, in the nominal case.

## Delays

One important feature is the delaying/hastening of [elementary streams][ES]
with regards to other ES, also known as "Track Synchronization".

It's very hard to hasten ES, because most hardware decoders will not like that,
and because often your decoder is already fully loaded (taking a lot of CPU).

Instead, we delay all the other [ES][] that are not in advance, by (sort of)
**pausing** them. That means not displaying any new image for video outputs, or
playing silence for audio outputs.

However, if we are in the case where the master output is the one that is in
advance, pausing this output will break the main clock, and it will
artificially drift.

The main clock needs therefore to be reset when you find the synchro again, aka
when the output is "un-paused".

---

### Doc Executor (`doc/executor.md`)

# Executor

## Overview

The executor API allows to submit _runnables_ to be executed from background
threads.

The runnable instances are allocated and freed by the caller. It is the
responsibility of the caller to guarantee that the runnable is valid until
the task is cancelled or completed.

The caller is expected to (but not forced to) embed the runnable into its
custom task structure:

```c
struct my_task
{
    /* custom data */
    int num;
    char *str;

    /* embedded runnable */
    struct vlc_runnable runnable;
};

void Run(void *userdata)
{
    // ...
}
```

and submit it as follow:

```c
    struct my_task *task = ...;

    task->runnable.run = Run;
    task->runnable.userdata = task; /* userdata passed to Run() */

    vlc_executor_Submit(executor, &task->runnable);
```

Since the task is allocated by the caller, vlc_executor_Submit() may not fail
(it returns void).

The cancellation of a submitted runnable may be requested. It succeeds only if
the runnable has not started yet.

```c
VLC_API void
vlc_executor_Submit(vlc_executor_t *executor, struct vlc_runnable *runnable);

VLC_API bool
vlc_executor_Cancel(vlc_executor_t *executor, struct vlc_runnable *runnable);
```


## Design discussions

The design of this API is the result of many discussions, especially on the
mailing-list:
 - <https://mailman.videolan.org/pipermail/vlc-devel/2020-August/136696.html>
 - <https://mailman.videolan.org/pipermail/vlc-devel/2020-September/136944.html>
 - <https://mailman.videolan.org/pipermail/vlc-devel/2020-September/137188.html>

This documentation aims to explain the rationale for each decision.


### Runnable allocation

The `struct vlc_runnable` must be provided by the client, and the executor uses
only this instance to represent its task (it does not allocate per-task private
data).

Firstly, note that this allows `vlc_executor_Submit()` to return `void`, so no
error handling is necessary. It's minor, but convenient.

An attractive alternative would be to return a new allocated runnable on
submission, which could be used for cancellation:

```c
struct vlc_runnable *
vlc_executor_Submit(vlc_executor_t *, void (*run)(void *), void *userdata);

void vlc_executor_Cancel(vlc_executor_t *, struct vlc_runnable *);
```

But this is inherently racy: a naive implementation could return a pointer to a
`vlc_runnable` already freed. Indeed, `vlc_executor_Submit()` must post the
runnable to some pending queue, and in theory it may be executed by a background
thread even before the function returns.

For example, if the executor frees the runnable as soon as its execution is
complete, this simple code is incorrect:

```c
struct my_task
{
    char *some_custom_data;
    struct vlc_runnable *runnable; /* keep a pointer to cancel it */
};

void Run(void *userdata)
{
    struct my_task *task = userdata;
    /* (task->runnable may be uninitialized here) */
}
```

```c
    struct my_task *task = ...;
    task->runnable = vlc_executor_Submit(executor, Run, task);
    /* task->runnable may be already freed here */
    vlc_executor_Cancel(executor, task->runnable); /* boom, use-after-free */
```

To avoid use-after-free, runnable ownership must be shared. This makes
the API more complex:

```c
struct vlc_runnable_t *runnable = vlc_executor_Submit(executor, Run, task);
vlc_runnable_Release(runnable); /* don't need the runnable anymore */
```

However, this would still be insufficient: the task resources must typically be
released at the end of the `run()` callback, but at this point
`vlc_executor_Submit()` may not have returned yet, so the runnable instance
would not be known by the user.

Since in practice, the client needs its own task structure (to store the
parameters, state and result of the execution), so it must allocate it and pass
it to the executor (as userdata) anyway. Therefore, it's simpler if the task
already embeds the runnable.

Note that it is not mandatory to embed the runnable into the client task
structure (it's just convenient). The runnable could be anywhere, for example, on
the stack (provided that it lives long enough).

This design also has disadvantages. In particular, it forces the runnable queue
to be implemented as an intrusive list. As a consequence, the public `struct
vlc_runnable` must include an executor-private field (`struct vlc_list node`),
and the same runnable may not be queued twice (it is not possible to put the
same item twice in the same intrusive list).

Therefore, a client must always explicitly create a new runnable for each
execution. In practice, it should not be a problem, since it must often
create a new custom task for the execution state anyway.

In addition, further extensions of the executor API are constrained by the fact
that it can not allocate and store per-task data (other than the intrusive
list node).


### Cancellation

It is important to be able to cancel and interrupt a running task.

However, in C, the interruption mechanism is very specific to the concrete task
implementation, so it can not be provided by the executor: it has to be
provided by the user. And since the user is also at the origin of the
cancellation request (which could lead to the interruption), the executor just
lets the user handle the interruption manually.

Since it does not handle interruptions, it could not provide a generic timeout
mechanism either (see below). The user has to handle timeouts manually.

It just provides a way to cancel a queued task that has not started yet:

```c
VLC_API bool
vlc_executor_Cancel(vlc_executor_t *executor, struct vlc_runnable *runnable);
```

The runnable instance is created and destroyed by the user, so there is no
possible use-after-free race condition.

Since the runnable is queued via an intrusive list, it can be removed in O(1).

The tricky part is that it must indicate to the user if the runnable has
actually been dequeued or not (i.e., if it has already been taken by a thread to
be run). Indeed, the user must be able to know if the `run()` callback will be
executed or not, in order to release the task resources correctly in all cases,
without race conditions. This adds some complexity for the user.

For the implementation, the problem is that even if we can remove an item from an
intrusive list in O(1), there is a priori no way to know immediately if the item
was in a specific list or not. To circumvent this problem, when an item is
dequeued, the executor resets the list nodes to `NULL`. This allows to store 1
bit of information indicating whether the item has been dequeued or not.

Note that the use-after-free race condition on cancel could alternatively be
solved by passing an optional user-provided `void *id`:

```c
struct vlc_runnable
{
    void (*run)(void *userdata)
    void *userdata;
    struct vlc_list node;

    /* add a user-provided id (may be NULL) used for cancellation */
    void *id;
}

void vlc_executor_Submit(vlc_executor_t *executor, struct vlc_runnable *);

void vlc_executor_Cancel(vlc_executor_t *executor, void *id);
```

This would solve the problem because the user owns the `id`, so it is guaranteed
to exist on cancel. This is also practical, because the user could just pass a
pointer to its custom task structure.

An additional advantage is that a higher-level API (like the preparser) could
directly benefit from the cancellation API, without tracking its submitted tasks
to find the matching runnable.

But it would only make sense if the user was not already forced to track its
submitted tasks, to be able to cancel them on deletion. Indeed, since the
executor must report for each task if it has been dequeued or not, it is not
possible to provide a function to cancel all tasks at once. Therefore, the user
has to keep track of submitted tasks, which is inconvenient.


### Timeout

A timeout implementation is often very specific to the concrete task
implementation (for example, calling `vlc_cond_timedwait()` instead of
`vlc_cond_wait()`), so it makes sense to let the user implement it.

However, we could see the timeout as a cancellation where the deadline is known
in advance. Since the user must already implement the interruption on its own,
it could have been used to provide a timeout mechanism "for free" (for the
user).

We decided against it, for several reasons:
 - In theory, the user may want to do something different on timeout and
   cancellation (it may for example consider that one is an error and the other
   is not);
 - This would require more callbacks and complicate the executor API;
 - If it's necessary, the user could just use a separate component (timer) to
   trigger the interruption.


## Conclusion

The resulting executor is a "minimal" API, simple and general: it just handles
execution.

As a drawback, it does not help with interruption, cancellation, or timeout, so
the user has more work to do on its own. For example, it must always track its
submitted tasks, and implement boilerplate to correctly handle cancellation and
interruption on deletion.

But I can't think of an executor API design in C with all the desirable
properties. Any design I considered sucked one way or another. :/

---

### Doc Fortunes (`doc/fortunes.txt`)

<Meuuh> intf: added `--vout' to playlist
<Meuuh> intf: added `dummy' to playlist

  -- #videolan
%

<colin> p_bit_stream->buffer |=((u32) *(p_bit_stream->byte_stream.p_byte++))
<colin>     << (24 - p_bit_stream->i_available);
<colin> psychopathes

  -- #videolan
%
<Stef> xav - AZRTYUUUUOP"'(-È
<Stef> oups je devrais pas nettoyer mon clavier par ici :)

  -- #videolan
%
<bbpig> sam : {#][^#{~]@[ ;P
<sam> bbp ?
<bbpig> sam : pouette
<sam> ah
<bbpig> :)
<sam> ok, gotta sleep now
<sam> it was very nice having this interesting discussion with you

  -- #videolan
%
<NovaKANE> spudec error: index mismatch (0x0331 != 0x0000)
<NovaKANE> spudec error: unknown command 0x7d
<sam> NovaKANE: yeah if you could cut a slice of VOB it would be pretty helpful
<jlj> hey, don't you plan on making fair use of your DVDs
<jlj> I'll give my good friend Jack a call

  -- #videolan
%
<NovaKANE> vlc is using about 3% cpu with no audio/video
<sam> 3% ? the interface isn't bloated enough then :)

  -- #videolan
%
-+- djk sets topic on #videolan to: "VideoLAN - 100% skin free DVD!"
%
<vektor> so, i think it's time for a mass exodus to libdvdcss.
<taaz> ack!  hold on a minute
<vektor> what?
<vektor> did you feel a great disturbance in the force?

  -- #livid
%
<stef> héhé, mais j'ai acheté une voiture :)
<Meuuh> ouaaah oh, quoi ?
<stef> une petite 206 toute noire
<reno> ce qu'il dit pas c'est que c'est une s16 ;)
<Meuuh> ah 16 bits signés ?

  -- #videolan
%
<Meuuh> je l'ai pas testé, hein, faut pas déconner non plus
<Meuuh> c le boulot des users de tester

  -- #videolan
%
<Xag> j ai du retard ds le son quand je stream du dvd.
<Meuuh> sur ton iPAQ ?
<Xag> nan laptop
<Xag> et le CPU n est pas a 100%
<Xag> et j utilise le dsp
<Meuuh> ah c'est dommage
<Meuuh> fais un test de grossesse, on sait jamais

  -- #videolan
%
<bozo> you really shouldn't have touched a computer when you were a little boy
<stef> _it_ came to me

  -- #videolan
%
<henrisson> hehe : Open failed !: Success

  -- #videolan
%
<lool> vlc rulez
<liquidacid> it dont work
<lool> what doesn't work ?
<liquidacid> vlc rulez

  -- #videolan
%
<ebarra> What time is it (for you)?
<lool> 6:16 am
<ebarra> Are you a teacher or an student?
<lool> I'm a student
<lool> teacher are in bed right now
<lool> what are you doing in life ?
<ebarra> I am a teacher of Computer Science...

  -- #videolan
%
<fuckall> and you, do you have some movies in svcd format?
  [...]
<walken> je lui ai envoye un cones.svcd
<walken> c'etait un lien vers /dev/zero

  -- #videolan
%
-!- Meuuh [www-data@brahma.via.ecp.fr] has joined #videolan
<hh> Meuuh!
<Meuuh> Meuuh.

  -- #videolan
%
<gibalou> --filter distort:transform:distort rocks :)
<gibalou> hey, I don't have enough processing power for --filter 
          distort:transform:distort:transform:distort :(

  -- #videolan
%
<alexis> c'est bon, Jérôme m'a envoyé le truc sans virus cette fois

  -- #videolan
%
<sam> anyway, why should we care? as soon as we release libvlc, it'll be in
      the MPlayer CVS :)

  -- #videolan
%
<Meuuh> I read a Jean-Claude Vandamme interview yesterday and it works well
<Meuuh> except for the sound
<Kev> Do you really want to have the sound for Jean-Claude Van Damme?

  -- #videolan
%
<JediMaster> I'm using the gtk+ interface in kde3
<lit> silly
<lit> heheh
<lit> i use the gnome interface in gnome
<lit> CALL ME CRAZY

  -- #videolan
%
<vax> $grabeuh
<gally> heheheuhrahaaheuwaheuhehrabeugraaaahawhahrahaa !!
<vax> hey THAT was a nice grabeuh

  -- #videolan
%
<gibalou> hi vax, how's work ?
<vax> beuhh .. another question ? :P
<gibalou> heuh, how's you girlfriend ? ;-)

  -- #videolan
%
<Meuuh> BTW, there is "Jeanne et le garçon formidable" on M6
<Meuuh> this movie is so great
<Meuuh> jlj you debug the http plug-in ? :p
<jlj> well, you're so busy with Virginie Ledoyen, so I had to do it :)

  -- #videolan
%
<Meuuh> then we need to update download.html
<Meuuh> what do we still have ?
<sam> the mail, freshmeat, the videolan.org news
<sam> cvs tag
<sam> regression tests
<sam> bwaahahahahahAHAHAHA
<Meuuh> mouhahaha

  -- #videolan
%
<Meuuh> fils de p_aout

  -- #videolan
%
<ali> by the way i've never learnt the way to make figlets lool ...
<lool> /exec -o figlet -f small 'ali is so stupid !'
<sam> I'm not absolutely positive it was a good idea to teach her
<sam> it's like handing a rocket launcher to a 6yr old ...

  -- #videolan
%
-+- Ourumov [www-data@brahma.via.ecp.fr] has joined #videolan
<Ourumov> hey anybody here know videolan ?
-*- SignOff: Ourumov (Client Quit)

  -- #videolan
%
<zBen_home> mais truc[tres_grand] sur la pile ca me parait idiot :)
<tooney> zBen_home - ou mais c'est truc[tout_petit] donc ca va
<zBen_home> ah vi alors ca va
<zBen_home> parceque meuh donnait un example avec un entier assez costaud
<tooney> c'est parce qu'il etait pas content

  -- #videolan
%
<ArkanumJ> meuuh: How's the 0.5 coming along? 
<Meuuh> ArkanumJ : about 5 seconds
<ArkanumJ> meuuh: huh? its going to be released soon?
<Meuuh> ArkanumJ : no, it works for five seconds, before crashing

  -- #videolan
%
<ArkanumJ> BTW, the CVS version runs well on MacOS X, but the sound is a bit
           broken. 
<Dnumgis> ArkanumJ: sound is a bit broken everywhere I think
<Dnumgis> ArkanumJ: except the places where it is completly broken

  -- #videolan
%
<Dnumgis> what does vout_CreatePicture do?
<sam> asks for a picture to the video output thread
<Dnumgis> so it just returns the next picture to be displayed?
<sam> not necessarily the next
<sam> it's your duty to set its display date, afterwards
<Meuuh> I'd even say, it is your destiny, Luke, to set its display date

  -- #videolan
%
<hh> When the first picture of the coded frame is a I-field picture, then the
     second picture of the frame shall be either an I-field picture or a
     P-field picture. If the second picture is a P-field picture then certain
     restrictions apply, see 7.6.3.5.
<Meuuh> yeah
<Dnumgis> Meuuh: not sure if you sound more like an mpeg guru or a blues
          artist
<Meuuh> like a cow, maybe ?
<Dnumgis> or something

  -- #videolan
%
<deepmind> a52old & wi11y?

  -- #videolan
%
<sam> bah oui tu peux faire de la doc sur n'importe quoi hein
<Meuuh> je fais bien une doc développeur vlc
<Meuuh> comme si y'avait des développeurs vlc à part sam et moi !

  -- #videolan
%
<deepmind> pis comme ca je me tiens au courant de où va vlc :)
<Meuuh> dans le mur ?
<Meuuh> y'a sam qui rajoute du charbon, moi je m'agrippe au frein

  -- #videolan
%
<tooney> $grut Dnumgis 
<gally> Dnumgis: tiår ditt og nødhavn klikk ?
<Dnumgis> gally: og hva skulle det bety?
<Dnumgis> gally: what was that supposed to mean?

  -- #videolan
%
<sam> $grut
<gally> vekt yrer gøten øke utgjøre kan 
<Dnumgis> sam: you're insane
<Dnumgis> someone have too much spare time
<sam> definitely
<Dnumgis> maybe they should do something useful instead
<gally> grunt grunt snort grumpf grumpf gra grunt uiigrumpf uiihiiii snort !!

  -- #videolan
%
<Dnumgis> I just burned an amb duron trying to compile vlc :(
<Meuuh> is it my fault ?
<Dnumgis> no
<Dnumgis> unless you wrote blocks.c :)
<Meuuh> actually, I did...

  -- #videolan
%
<Meuuh> pleasehelp : are you trying with a dolby trailer ?
<pleasehelp> no... using windows

  -- #videolan
%
<Dnumgis> I just listened through an entire ogg/vorbis file with vlc with no
          audible problem whatsoever
<Dnumgis> but I don't get the time of the stream
<Dnumgis> gibalou: why don't I get the time?
<sam> because time does not exist
<sam> as Kant stated, our mind structures our perceptions so that we know a 
      priori that time is like a mathematical line, rendering time as a form 
      of conscious experience
<gibalou> Dnumgis: a few things are left to be implemented, like seeking, 
          stream length display, etc...
<sam> or you can listen to gibalou's dull explanation

  -- #videolan
%
<alexis> Meuuh: rahh, p'in, t'as raison
<alexis> je suis une merde...
<Meuuh> axiome de videolan : j'ai *toujours* raison

  -- #videolan
%
<phun> ooooh
<phun> that's a pretty good idea
<Meuuh> yeah, that's mine :)

  -- #videolan
%
<Meuuh> at the salsa lesson tonight, a girl told me that she was very happy to  
        dance with me
<Meuuh> do you think it means something ?
<Dnumgis> Probably that she finally found someone that was worse at dancing 
          than she

  -- #videolan
%
<hal_3k> damn
<hal_3k> why didn't you use LGPL?
<sam> why don't you use GPL?

  -- #videolan
%
<sam> dudes, you'll hate me again: bootstrap will now need an argument :)
<Dnumgis> sam: hate you already
<gibalou> sam: don't worry sam, we already hated you ;)

  -- #videolan
%
<JMunakra> nah you have zip with password protection
<jlj> that's not classified as DRM
<JMunakra> where does DRM start?
<Meuuh> I don't know, but I know where it ends : at my doorstep

  -- #videolan
%
<hal_3k> jlj: what did you do?
<jlj> I was like watching a few of my DVDs using unauthorized software under
      the evil GNU/Linux operating system, and then my computer was, like,
      beep beep beep beep beep beep beep, and then, like, all my computers
      were confiscated, and I was like, nnnn? ... It's kind of ... a bummer.

  -- #videolan
%
<bbp> et de toute façon ca sert a rien de coder sur vlc, ils changent tout a
      chaque fois entre le moment ou on commence a écrire sa première ligne
      de code et celui où on écrit la deuxième

  -- #c00lz
%
<_broomy-> then ill download the OS
<jlj> that's illegal
<_broomy-> yep everything is illegal
<_broomy-> i bet u do illegal shit aswell

  -- #videolan
%
<walken> tiens, j'ai une idee pour les gens qui se plaignent que le site web
         est pas traduit en grec antique
<walken> a chacun qui se plaint, tu lui proposes de le faire. s'il demande
         encore pourquoi videolan ne le fait pas, tu lui files l'email du
         precedent en disant c'est parce qu'il ne l'a pas fait :)

  -- #videolan
%
<sam> KKI: I was merely joking, ignore me :)
<Meuuh> it is generally a good idea to ignore sam

  -- #videolan
%
<sam> p_input->stream.pp_areas ça commence à 0 ou à 1 ?
<sam> aaaah /* We start from 1 here since the default area 0 is reserved for
      video_ts.vob */
<Meuuh> si c pas du code de porc ça mon bon monsieur
<sam> mais alors l'area 0 elle sert à rien ou quoi ? :)
<jobi> ben pour le vcd à rien du tout, si je me souviens bien
<fenrir_> à ben voila ma piste qui disparaissait pour les cda...

  -- #videolan, du code de pro
%
<Dnumgis> someone is not releasing a p_vout object correctly
<tooney> it's not me
<tooney> i was not there this afternoon
* Meuuh looks in his pockets

  -- #videolan
%
<BeKavlie> seriously though, out of curiosity: why are people working on
           VideoLAN?  It can't be to keep us BeOS people happy  ;-)
<jlj> world domination
<jlj> didn't you get the memo ?

  -- #videolan
%
<sam> kali: j'ai raté mon train ...
<sam> ben je voulais partir en belgique ce matin
<ka|i> sam : a cause de la greve ou tu te reveilles ?
<sam> mais ç'aurait été une mauvaise idée ...
<sam> vu qu'en fait c'est la semaine prochaine le fosdem !

  -- #videolan
%
<bbpig> tiens au fait y a des mauvais signal handlers dans le vlc ;P
<Ipkiss> je crois qu'il n'y a pas de bons ou de mauvais signal handlers
<bbpig> Ipkiss: ah ?
<Ipkiss> si je devais résumer le vlc aujourd'hui...
<tooney> c'est un effort commun de gens qui aiment, qui dansent l'opensource
<tooney> qui chantent l'opensource

  -- #c00lz
%
<Meuuh> do you actually have soundsticks ?
<xtoph> want to see an image to believe it ?
<Meuuh> then you can debug the soundsticks support

  -- #videolan
%
<Dnumgis> sounds perfectly normal
<tooney> ah
<Dnumgis> very wrong, but normal in vlc

  -- #videolan
%
*** Meuuh has changed the topic on channel #videolan to "NO, we do not know
    how to access Italian pay TVs, so don't ask"
*** [MaDog] (MaDog@host41-156.pool80180.interbusiness.it) has joined channel
    #videolan
<[MaDog]> c'è qualcuno ?????????
<Meuuh> read the topic
<[MaDog]> :)
<[MaDog]> xcuse me
*** Signoff: [MaDog] (Client Quit)

  -- #videolan
%
<Xtophe> is there any centralien alive ?
<thedj> i hope so. haven't heard a thing about a nuke explosion in Paris

  -- #videolan
%
<thedj> i don't trust ffmpeg's make process ;)
<gibalou> their make mess you mean ?

  -- #videolan
%
<alexis> cdemarco: you can try with "-I dummy"
<cdemarco> You mean I should call my computer names!?

  -- #videolan
%
<JediMaster> sh-2.03# reboot
<JediMaster> Segmentation fault
<JediMaster> that's not good

  -- #videolan
%
<sam> pour votre punition, ça sera un bootstrap chacun :P
<titer> c'est reparti pour 20 min
<gibalou> c'est reparti pour 3 x 20 min
<sam> c'est reparti pour une modif de Makefile.am d'ici 20 min

  -- #videolan
%
<maleki> any girls here with a webcam

  -- #videolan
%
<sam> 16K     ChangeLog-1999
<sam> 96K     ChangeLog-2000
<sam> 168K    ChangeLog-2001
<sam> 304K    ChangeLog-2002
<sam> 488K    ChangeLog-2003
<zorglub> conclusion: les dévs pipotent de plus en plus dans les messages de
          commit

  -- #videolan
%
-!- kixx_ changed the topic of #videolan to: VLC 0.7.0 :
          http://www.videolan.org/ | Fenrir ta cotiz !

  -- #videolan
%

< zorglub> vlc file1.mpg :start-time=300 :stop-time=420 --sout 
                    file/ps:file2.mpg
< boggle> sgagers: use zorglubs command, it will copy the needed 
                   timeslice to file2.mpeg
< sgagers> boggle: zorglubs is an utility of ffmpeg?

  -- #videolan
%
<Meuuh> and what's the news ?
<nto> Mac OS 10.2 and 10.3 support hopefully
<nto> Win98 maybe
<Meuuh> haven't had any chance to test it ?
<nto> I need to send a new version to a tester
<E-bola> hire him meuuh :P
<Meuuh> E-bola : we already did :p
<E-bola> ahhh
<E-bola> lol

  -- #videolan
%
*** Bond470 (n=3e30a4ee@krishna.via.ecp.fr) has joined channel #videolan
<Bond470> olas
<Bond470> ;)
<Meuuh> dionoea ?
<Bond470> lisboa
<Bond470> e tu?

  -- #videolan
%
<thedj> lol, fen is a la(i)mer
<thedj> fen [n=laimar@217.167.236.61]

  -- #videolan
%
<E-bola> what is all the fortune.txt stuff?
<E-bola> some internal french joke?
<E-bola> lol
<E-bola> ohh chan quotes
<E-bola> wtf im in it lol

  -- #videolan
%
<zorglub> gibalou: y'a un doc quelque part qui explique un peu plus le format
          real ou fen a fait le demux directement en lisant des dumps / le
          demuxer de mplayer ?
<gibalou> zorglub: la meilleure doc c'est le code de mplayer/xine
<zorglub> c'est bien ce que je craignais :)
<zorglub> j'ai pas regardé xine ceci dit
<zorglub> c'est plus agréable à lire que mplayer
<sam> mouahaha
<sam> non rien.
<zorglub> ah bah si quand même
<bigben> tu veux dire que les variables ont un nom qui reflète ce qu'elles
         font ?
<sam> avoir des noms de variables qu'on peut prononcer nuit fortement à la
      lisibilité car cela détourne l'attention du vrai code, constitué des
      mots-clés du langage et des divers opérateurs
<sam> j'appelle mes variables Illl, lIll, lIlI, IllI, IlII, etc.
  -- #videolan
%
20:47:12 < glspi> can you get rid of the wxwidgets interface and only show the 
                  video somehow?
20:47:17 < zorglub> -I dummy
20:50:10 < glspi> -I didn't work btw, in windows at least
20:52:28 < glspi> i don't see it in interface/general either! ahhh :(
20:58:46 < glspi> ok i thought you were calling ME dummy, not saying use -I 
                  dummy :p
  -- #videolan
%
-!- Bond631 [n=548d9042@krishna.via.ecp.fr] has joined #videolan
<Bond631> hi
<Bond631> how to activate hotkeys?
-!- Bond631 [n=548d9042@krishna.via.ecp.fr] has quit [Client Quit]
<Trax``> I think Bond631 found the hotkey for quit

  -- #videolan
%
-!- tumu [n=tumu@GYKMDCCCXXIX.dsl.saunalahti.fi] has quit ["~"]
* Dnumgis notes that tumu must be one of very few with dsl in their sauna

  -- #videolan
%
<Dnumgis> thedj: gibalou quits officially, or has he just generally
          disappeared?
<sam> he's probably getting married or something even worse :(

  -- #videolan
%
<xtophe> kee-un: you need to slap zorglub
<xtophe> kee-un: oups no i'm telling a lie
<kee-un> xtophe: too late, I slapped him
<kee-un> xtophe: I'm sure he deserved it for something else anyway

  -- #videolan
%
VLC is the greatest thing since sliced bread!

 -- http://forum.videolan.org/
%
* E-bola is bouncing up and down while enjoying: HTTP/1.1 200 OK
* E-bola is bouncing up and down while enjoying: Content-type: text/html; charset=UTF-8
* E-bola is bouncing up and down while enjoying: Cache-Control: no-cache
* E-bola is bouncing up and down while enjoying: Content-Length: 25
* E-bola is bouncing up and down while enjoying: --
%
<Meuuh> sam : could you delete FK_ from the access list ?
<sam> sure, why?
<Meuuh> he's now known as feepk
<sam> ah, ok
<sam> I thought he was banned for sending a patch to mplayer
  -- #videolan
%
* kinema loves VideoLAN.
< kinema> and it's developers
< funman> what is your sex ? ( i may be interested )
  -- #videolan
%
< thedj> mmm.
< thedj> (*&$%$!**%!)%#@!*O*%#@!
< thedj> this pisses me of
< dionoea> is that perl ? :)
  -- #videolan
%
<Dnumgis_> xtophe: look at it this way:
<Dnumgis_> solve some bounties involving vlc and earn some extra money
<Dnumgis_> use this money and your natural charm to get a girlfriend
<Dnumgis_> make sure Meuuh sees it
<Dnumgis_> Meuuh would become horribly jealous and try to repeat your trick
           with great vigor and solve all remaining bugs with vlc
<Dnumgis_> and the (vlc)world becomes a better place
<j-b> Dnumgis_: do you have a girlfriend ?
<Dnumgis_> j-b: not at the moment, no
<j-b> Dnumgis_: so why don't you try this technick ?
<Dnumgis_> j-b: I don't live close enough to Meuuh 
<E-bola> thats some good norwegian reasoning
  -- #videolan
%
<Meuuh> of course when using sout-ts-es-id-pid you can't have any stream with
        an id < 16
<Meuuh> so use fake-id=68 or whatever
<Dnumgis> it's illegal to use fake id
  -- #videolan
%
< j-b> my mother is desperate because she thinks I am gay, because I haven't presented my gfs...
< thedj> j-b: i know the feeling
< LordRPI> my mother gets upset if I tell her I'm at a bar without a girl
< thedj> haha
< thedj> LordRPI: she fears you will turn into a lonely drunk 
< thedj> roaming the city at night looking for a place to stay :D
< tumu> lonely drunk is good
  -- #videolan, same night, 4:38 am
 %
<olsen_> hi, how can i adjust the brightness?
<Dnumgis> olsen_: usually there is a nob on your monitor
  -- #videolan
%
< Jastr> is there a way to see the rtp stream before it hits darwin?
< Dnumgis> Jastr: the rtp stream is a ninja assasin, you never see it
                 before it hits
  -- #videolan
%
<zigovr3> what is atsc ?
<xtophe> like dvb but in america
<xtophe> with packet measured in inches instead of mm
  -- #videolan
%
< Bond493> Can anyone refer me to some documents which describe the VLC player internal? I'd 
           like to learn how to write a media player espcially on embedded platform.
< Dnumgis> Bond493: we keep this information secret in order to keep ahead of the competition
  -- #videolan
%
 < pdherbemont> :p j'aimerai bien être un peu plus polard parfois *sigh*
  -- #videolan
%
< DiDjCodt> dionoea: /leave xxx ?
< dionoea> nah. Btw, i have a localised irssi. Using /qui j-b works the same was as /who j-b
< dionoea> really awesome
-!- thresh [n=popa3d@unaffiliated/thresh] has quit ["j-b"]
  -- #videolan
%
< dionoea> w00t! police in the UK are using VLC for forsenic analisys
< dionoea> I'll forward the email later
< dionoea> Police Crime Bureau - Computer Crime Team UK
< xtophe> ah i should remember to wear my vlc tshirt next time i commit a crime
  -- #videolan
%
<spaceman_spiff> si, la 0.9 est utilisable
<spaceman_spiff> ça dépend pour quoi
<spaceman_spiff> pour faire croire à un naïf qu'un dvd est pété par
                 exemple
<spaceman_spiff> "regarde il fait planter mon vlc"
%
< funman> {1} Active Tickets (490 matches)
< funman> i disconnect before 500 is reached
-!- FoX_ [i=FoX@nat/ecp/x-1a0545f92a4d3c50] has quit [Remote closed the connection]
-!- zorglub [i=zorglub@nat/ecp/x-61892e92340285ff] has quit [Remote closed the connection]
-!- bigben [i=bigben@nat/ecp/x-1e86f79de0c1cdc9] has quit [Remote closed the connection]
-!- ipkiss [i=ipkiss@nat/ecp/x-31e3605240c16dc6] has quit [Remote closed the connection]
-!- Dimar [i=Dimar@nat/ecp/x-d24991bac9f169c9] has quit [Remote closed the connection]
< whyz> were those your fakenicks funman?
< funman> i hit the wrong button sorry
< whyz> :O
  -- #videolan
%
< Mr_Mirsal> ./bootstrap
< StFS> trying that now
< StFS> damn this takes long. :-/
< thresh> good things always take long time
  -- #videolan
%
< Mr_Mirsal> Oh, btw, I request a fortunes.txt update...
< thresh> Mr_Mirsal: commit !
< Mr_Mirsal> thresh: Eh ! I can't.
< thresh> and i don't want to be a developer who only develops fortunes! :)
  -- #videolan
%
<Quovodis> ivoire: ?
<ivoire> Quovodis: sorry
<ivoire> my parents push me out of the computer :(
  -- #videolan
%
VLC development is NOT about being a sed god.
  -- courmisch
%
<Meuuh> no one reads my documentation
<Dnumgis> Meuuh: they see your name on it and naturally assume it must be out
          dated
  -- #videolan
%
< alesan> ok my wife is yelling I have to go to bed with her
< alesan> uff
< alesan> see you tomorrow
< alesan> :(
-!- alesan [n=alesan@***.telecomitalia.it] has quit
< dionoea> poor guy ...
  -- #videolan
%
< d_anny> do u guys know how to remove cygwin
< j-b> d_anny: suicide
< d_anny> j-b: suicide , a command in cygwin ?
  -- #videolan
%
<jkernsjr> guys..thats what I get when I try to run vlc as root
<thresh> vlc -I dummy
<jkernsjr> thanks for calling me dummy
<jkernsjr> makes me feel warm and fuzzy
<thresh> n/p
<Meuuh> sorry, this is already in the fortunes
<jkernsjr> still won't start
<jkernsjr> and now I see dummy is one of the options
<thresh> :)
<jkernsjr> I really thought you were calling me dummy..sorry
<Meuuh> ha, NOW you can go into the fortunes
  -- #videolan
%
< Daught> wait cygwin does not even have qt4...
< Daught> if I have to disable more stuff I will end up with windows media player
%
< thedj> lloyd: i'd use CSA, simple and no nonsense.
< lloyd> i like simple and no nonsense.. like us brits.. not overly dramatic or noisy..
< j-b> isn't Amy WineHouse brit?
 -- #videolan
%
< funman> some program for fun
< funman> some program for profit
< funman> i program for beers
< funman> gtg
 -- #videolan
%
< xand_> guys how can i force ffplay to use avcodec ?
 -- #videolan
%
< nefrir> btw, anyone *really* opposed to temporary (1 month at most) breakage of video splitter (wall, clone, panoramix) and some vout filters (ie some mode of deinterlacing, opencv, autocrop) in master ?
< j-b> NO
< j-b> nefrir: seriously, if people care, they can branch
< Dark_Shikari> isn't this what local branches are for?
< j-b> Dark_Shikari: indeed. But you could consider master as nefrir 's local branch :)
< Dark_Shikari> lol
 -- #videolan
%
< pk> vlc should be recorded in guiness book of world records for command line options
 -- #videolan
%
<xxcv> (wrong == right is still wrong right ?)
 -- #videolan, Raffarin-attitude
%
< sam> is there still a way to force the video decoder used by VLC? --codec no longer exists
< mru> there's always butterflies
< j-b> sam: --codec avcodec,none still works and exists
< linkfanel> maybe sam is one branch ahead of us
< j-b> very likely
< xxcv> vlc-1.3 ?
< ILEoo> vlc 2.0
< ILEoo> like web 2.0
< mru> someone is rewriting vlc in javascript?

< sam> duh!
< sam> j-b: you're right, I don't know what I smoked
< mru> must've been good though, care to share it?
< ILEoo> sam: hope you didn't smoke it all allready?
< j-b> sam: I hope you could tell me what you smoked
< sam> as a matter of fact, I know exactly what I smoked
< sam> but it's not that good
< linkfanel> you remember? i guess you didn't smoke enough of it
< ILEoo> you smoked some capacitors from your motherboard? they're full of vitamins
 -- #videolan, one _morning_
%
< mru> documentation only attracts users too stupid to rtfs
 -- #videolan, speaking of documentation
%
"As already stated several times, libfluidsynth leaks memory like a Fukushima
reactor leaks ionizing radiations."
 -- courmisch, on forum.videolan.org
%
< courmisch> aww, I had a genius idea, and then I figured out it did not work
 -- #videolan, maybe an evil plan?!?
%
< linkfanel> what am i supposed to mean?
< mru> that is a very good question
 -- #videolan, sunday philosophy
%
< jesuitx> consider unblocking safari. mac users are people too.
 -- #videolan, it's a conspiracy!
%
< edwardw> ENOSAMPLES
< j-b> edwardw: https://people.xiph.org/~tterribe/opus/multichannel/test-1-mono.opus
< j-b> ENOEXCUSE
< edwardw> Excuse e = new Excuse();
< edwardw> e.useOn(j-b);
< mru> argument-oriented programming?
< mru> where functions are called demands, variables claims, and errors excuses
< edwardw> j-b: "centred"
< funman> try { edward.fixit(); }
< funman> catch { jb.fixit(); }
< mru> !jb
< Hector4212> Fix it!
< edwardw> finally { funman.runAway() }
< TypX> finally { featureit(); }
 -- #videolan
%
< courmisch> OSS needs an open device to enumerate devices.
< courmisch> Because whenever something does not make sense, OSS does it.
< courmisch> For further informations, refer to the chicken-and-egg problem.
< funman> you never had to do with the french administration did you? :P
< courmisch> no but the Finnish immigration process:
< courmisch> work contract needs tax card
< courmisch> tax card need residence registration
< courmisch> residence registration needs work contract
 -- #videolan, or why OSS is worse than Government administrations
%
< j-b> funman patch rejected; /me is afraid
< TypX> well funman isn't really a good coder anyway
 * j-b slaps TypX
< TypX> oh yes
< TypX> again
 * funman craps TypX
< TypX> I don't hate you
< funman> that might be the most beautiful thing i was ever told <3
 -- #videolan, BDSM and Litote
%
<etix> Who's Barack Obama on the etherpad?
<TypX> etix: the president of the USA
<No_se> etix: You really should have heard of him
<etix> Yeah of course
 -- #videolan, unexpected attendee to technical discussions at VDD13
%
< tmatth> cowboys and endianess
< tmatth> flac over telegraph
 -- #videolan, remembering good old times
%
<+courmisch> elenril: there are three types of OSes: Windows, those
 with pthreads, and the useless ones
 -- IRC, trol^Wdebating on mobile OSes
%
 -!- courmisch is now known as EvilCourmisch
< cone-129> vlc.git Rémi Denis-Courmont NEWS configure.ac
 modules/logger/Makefile.am modules/logger/journal.c:
 journal: add native logger module for the SystemD Journal
< EvilCourmisch> muahahahahahahahahahahahahahahahahahahahahaha
 -- IRC, we're loosing Rémi!
%
< courmisch> lo
< j-b> courmisch: SanDiego?
< courmisch> j-b: uh?
< j-b> uh!
 * j-b lost context
 * evilril gives j-b an MpegEncContext
< courmisch> this happens when you omit the frame pointer
< Sean_McG> lol
 -- IRC, MpegEncContext is evil
%
< VLC> http://www.videolan.org/developers/libdvdcss.txt
< VLC> i want to know if this is only for reading content, or also for ripping it
< VLC> does VLC provide any kind of protection against unauthorized ripping?
< VLC> or is this within the scope of the end user?
<@j-b> end user
< courmisch> VLC: what's the difference between ripping and reading?
< TypX> courmisch: Jack the Reader was way less dangerous than Jack the Ripper
 -- #videolan, badum ching
%
< wm4> poor guy can't afford proper pants
< j-b> yeah, poor guy
< hanna> wm4: almost as bad as not being able to afford a proper HDR monitor :p
< wm4> won't j-b buy hanna some pants???
 -- #videolan, about HDR
%
< ePirat> Daemon404, https://www.stonepages.com/prehistorama.html
< Daemon404> :D
< ePirat> Wow QuickTime 7 still works on macOS 10.14 and can play these :D
< Daemon404> yeah
< Daemon404> next os x + qt wont
< thresh> bribe Kostya to RE it?
< Daemon404> not hard to RE, and the api spec is xbox hueg
< Daemon404> just nobody cares
< durandal_1707> what is left to RE?
< Daemon404> durandal_1707, tons of stuff in quicktime ;)
< Daemon404> all the effects 'codecs', qtvr, qtma, etc
< durandal_1707> i mean useful codecs, like behere?? something
< Daemon404> 'useful' is in the eye of the beholder
< durandal_1707> BeHereiVideo.qtx --> RE that one
< Daemon404> never heard of it
< durandal_1707> now you heard
< koda> it's a video codec, of course you can't hear it
-courmisch:#videolan- badum ching
 -- #videolan, about "hearing" video codecs from the 90s.
%

---

### Doc Release Howto (`doc/release-howto.txt`)

Release HOWTO
-------------

[ Note: this howto is probably useful to core vlc developers only ]

 * Tests: compilation, regression tests ...

 * Changes on the git repository
   - Check that the version number everywhere it's needed
     · configure.ac
     · NEWS
   - Update important things and add them to the NEWS file
   - Close the milestone on Trac https://trac.videolan.org/vlc
   - Update OpenPGP key embedded in include/vlc_pgpkey.h if needed
   - Increase the libvlccore and libvlc revisions in src/Makefile.am
      _if_and_only_if_ the ABI number has not been increased instead, in
      which case the revision goes back to zero.

 * Commit
 * "Tag" the release
    git tag -a -m "VLC Release VERSION" <VERSION>
    (Create an annotated tag, you can skip -a if you used -m)
    git push origin <VERSION>

 * Tarballs (Use a clean tree !!!)
  - Edit POTFILES.in to include generated Qt UI files (doesn't work out of tree)
  - Use 'make distcheck' to make sure all files are exported correctly
  - copy the tar.xz file on ganesh.videolan.org
  - generate SHA, MD5 hashes and OpenPGP signature of these files :
        for file in vlc*; do
            for sum in md5 sha1 sha256; do
                ${sum}sum --binary $file > $file.$sum
            done
            gpg --rfc4880 --no-textmode --armor --detach-sign \
                -u "VideoLAN Release Signing Key 2026" --digest-algo SHA512 \
                --output "$file.asc" -- "$file"
        done

 * Contribs
  - Put a copy of the libraries or git snapshot in ftp://ftp.videolan.org/pub/contrib/
  - Put the relevant win32 and Mac OS contrib package
  - Update s.v.o/vlc and www.v.o/vlc/download-sources.html

 * Win32 Packages
  - configure using the extras/package/win32/configure.sh script
  - copy the packages files on ganesh.videolan.org
  - copy the vlc-2.0.4 and symbols-2.0.4 to balthazar for stacktraces

 * Mac OS X packages
  - configure using the extras/package/macosx/configure.sh script
  - use extras/package/macosx/codesign.sh with options and key to sign it.
  - copy the package to ganesh.videolan.org

 * Sign all the files and move the signed files on /opt/ftp/pub/videolan/vlc/X.X.X
  - At first block access to this directory by http or ftp. Only authorize rsync.
    This is to avoid leak as some journalists and forums seem to watch the ftp.
  - Update the "last" symlink

 * Update the website
  - edit the /index.php /vlc/*.php, include/os-specific.php files
  - create a /vlc/release/X.X.X page
  - add a news item to news.msg
  - update the front page OS dependent screenshots if the interface changed.
  - update the md5

 * "Check for updates" functionality
  - Edit the appropriate status file under update.videolan.org/vlc/:
    status-win-x86, status-win-x64 or status-win-arm64 for Windows.
  - Sign with the same release key as the advertised installer, then verify:
        file=status-win-x64
        gpg --rfc4880 --no-textmode --armor --detach-sign \
            -u "VideoLAN Release Signing Key 2026" --digest-algo SHA512 \
            --output "$file.asc" -- "$file"

 * Mirrors
  - Forwarn the mirror admins (optional for any minor release)
  - Go to https://get.videolan.org/<path-to-the-file>?mirrorlist
  - Ensures there is enough mirrors able to serve the file in each region
    or just wait few more hours before doing the release.
    Suggested number of mirrors:
        - > 70% for a minor release
        - > 80% for a major release
        - > 80% for any auto-update
  - Keep an eye on https://get.videolan.org/?mirrorstats during the release

 * Update the wiki
  - edit http://wiki.videolan.org/Template:VLC:latest_version

 * Announce on www.freecode.com (let jb or sam do it, if you lack the account)
  - go to https://freecode.com/projects/vlc/
  - chose "Submit a release" in the right menu, in "Administration"
  - Do the steps and wait (the update can take several hours)

 * Announce on Twitter, Facebook and G+
  Unfortunately, those are more followed than the all other mediums combined

 * Announce email and forum topic
  - Write something along the lines of previous releases
  - Post in videolan-announce (moderated), vlc et vlc-devel
    You are advised to add the author list for this version using
    'git shortlog -s PREVIOUS_VERSION..VERSION'
    And to add git diff --stat PREVIOUS_VERSION..VERSION if it's relevant.
    You can play a bit with git to show the piece of information that may be
    interesting.
  - Add a sticky thread in the "General VLC media player discussion" forum

---

### Doc Strings (`doc/strings.txt`)

This documentation is outdated. Please refer to the appropriate Wiki page :
http://wiki.videolan.org/index.php/VideolanStringGuidelines

---

### Doc Translations (`doc/translations.txt`)

This file is outdated.
Please refer to https://www.videolan.org/developers/i18n/

---

### Doc Vlm (`doc/vlm.txt`)

VLM: VideoLAN (Media) Manager

VLM is a little media manager originally designed to launch multiple
streams with only one VLC.

I - Interfaces
 1) Telnet interface
    You can launch it as a common interface.
    Default port is 4212, default password is "admin". These are common
    options of the interface, so you can modify them as you wish.
    Once you are connected on telnet interface, just type a command
    line.

 2) HTTP interface
    You can launch it as a common interface
    Default port is 8080.
    Access it from http://127.0.0.1:8080/vlm.html

II - VLM Elements

    A media is composed with a list of inputs (the videos and audios you
    want to stream), an output (where you want to stream them), and some
    options. It is very close to a TV program, or channel: it is launched,
    stopped, paused by the administrator, may be repeated several times etc.

III - Command line syntax:
Note: the only type of element in this version is media.
 1) Command lines:
    help
        Displays an exhaustive commmand lines list.

    new (name) broadcast|schedule [properties]
        Creates a new element. You must specify if a broadcast media, or
        a schedule (there is no default type).
        "media" and "schedule" are reserved names, and element names
        must be unique.
        Properties are optional: you can use the "setup" command to set
        it.
    setup (name) (properties)
        Set the property of the (name) element.
        See Properties section for more information.
    show [(name)|media|schedule]
        Displays current element states and configurations.
        "show (name)" displays all information about the (name) element.
        "show media" displays a summary of medias states.
        "show schedule" displays a summary of schedules states.
        "show" is a the same as "show media show schedule".
    del (name)|all|media|schedule
        Destroys an element, or a group of elements. If the element is
        at a non-stop state, it stops it (media or schedule).
        "del (name)" destroys the (name) element.
        "del all" destroys all elements.
        "del media" destroys all medias.
    control (name) [instance_name] (command)
        Changes the state of the (instance_name) instance of (name) media.
        If (instance_name) is not specified, the control command affects the
        default instance.
        See Commands section for more information.
    save (config_file)
        Saves all media configurations in the (config_file)
        configuration file. the "save" command overwrites the file if it
        already exists. States (playing, paused, stop) are not saved.
        See Configuration File section for more information.
    load (config_file)
        Loads the (config_file) configuration file.
        See Configuration File section for more information.

 2) Properties:
 Note: except the "append" property, all property can be followed by
 another one, recursively.For example:
 "setup pouet input file://arf.avi output udp:127.0.0.1 enabled loop"
 is a valid command line.
    Media Properties Syntax:
        input (input_name)
            Adds (input_name) at the end of the media input list.
        inputdel (input_name)|all
            Deletes (input_name) or all items from the media input list.
        inputdeln (input_number)
            Deletes the item #(input_number) from the media input list.
        output (output_name)
            Defines the output of the media.
            The syntax is equivalent to the ":sout=..." option , but you
            do not have to put the ":sout=" string.
        option (option_name)[=value]
            Adds the (option_name) to the media option list.
            The syntax is equivalent to the ":(option)=..." option , but you
            do not have to put the ":" string.
            Options are global: they are applied to all inputs of the
            media.
        enabled|disabled
            Enable or Disable the media.
            If a media is disabled, it can not be streamed, paused.
        loop|unloop
            Used for broadcast only.
            If a media with "loop" option receives the "play" command
            and finally finishes to play the last input of the list, it
            will automatically restart to play the input list.

 3) Control Commands Syntax:
    play
        Starts a broadcast media. The media begins to launch the first
        item of the input list, then launches the next one, and so on
        (like a playlist).
    pause
        Puts the broadcast media in paused status.
    stop
        Stops the broadcast media.
    seek (percentage)
        Seeks in the current playing item of input list.

IV - The configuration file
  A VLM configuration file is very simple: it is only a list of command
  lines: one line corresponds to one command line.
  If you want to create a configuration file, just edit a text file and
  type a list of VLM command lines. But beware of recursive calls: you
  can put a "load (file)" command in a configuration file, so for
  example, if you put a "load pouet" in a pouet file, and you launch the
  "load pouet" command, then pouet will be loaded endlessly, and VLC
  will crash (at least).
  The load command converts the medias configurations into command lines,
  and writes them into a file.
  Any line where the first non white space character is a '#' is considered
  as a comment.

---

### Doc Libvlc CHANGES V3 To V4 (`doc/libvlc/CHANGES-v3-to-v4.md`)

# LibVLC 3 to 4 Migration Guide

This guide documents the API-breaking changes introduced in LibVLC 4.0 and
helps applications written against LibVLC 3 port to LibVLC 4. It covers the
removed, renamed and reworked APIs, new functionality, and the behavioral
differences to be aware of. A migration checklist is provided at the end.

---

## 1. Biggest structural changes

Three changes pervade the whole API and are likely to touch every non-trivial
application:

1. **The `libvlc_event_manager_t` / `libvlc_event_attach()` event system is
   gone.** Every LibVLC object (media player, media discoverer, renderer
   discoverer, media list player, parser, media) now takes an explicit
   versioned callback struct at creation time.

2. **`libvlc_time_t` is now microseconds everywhere.** LibVLC 3 mixed
   milliseconds and microseconds in the public API. In 4.0 a single typed
   unit `libvlc_time_t` (`int64_t`) is used throughout the API for time units. 

3. **New `libvlc_parser_t` API:** Parsing and thumbnail generation is moved from
   `libvlc_media_t` to its own separate API. One parser object owns its own thread pool
   and can serve many concurrent parse and thumbnail requests. See §6 for more details.

---

## 2. Removed APIs

### 2.1 Global event system

Everything in `<vlc/libvlc_events.h>` is gone. The header has been deleted.

Removed symbols:

- `libvlc_event_manager_t`
- `libvlc_event_t` / `libvlc_event_type_t`
- `libvlc_callback_t`
- `libvlc_event_attach()`
- `libvlc_event_detach()`
- `libvlc_media_event_manager()`
- `libvlc_media_list_event_manager()`
- `libvlc_media_player_event_manager()` (and equivalents on every other
  object)
- All enum values (`libvlc_MediaPlayerPlaying`, `libvlc_MediaParsedChanged`,
  `libvlc_MediaListItemAdded`, `libvlc_MediaListEndReached`,
  `libvlc_MediaPlayerMediaChanged`, etc.)

**Migration:** use the object-specific callback struct (`*_cbs`) passed at
creation. See §4 for per-object mappings.

### 2.2 Media parsing / thumbnailing on `libvlc_media_t`

Removed from `<vlc/libvlc_media.h>`:

- `libvlc_media_parse_request()`
- `libvlc_media_parse_stop()`
- `libvlc_media_thumbnail_request_t`
- `libvlc_media_thumbnail_request_by_time()`
- `libvlc_media_thumbnail_request_by_pos()`
- `libvlc_media_thumbnail_request_destroy()`

The old `libvlc_media_parse_flag_t` enum values `libvlc_media_parse_local`,
`libvlc_media_parse_network` and `libvlc_media_parse_forced` are gone; the
new flag set is redefined at [`libvlc_parser.h`](../../include/vlc/libvlc_parser.h).

**Migration:** use the new `libvlc_parser_t` object in
`<vlc/libvlc_parser.h>` (§6). It handles both parsing and thumbnailing, can
manage multiple concurrent requests, and exposes a single cancel/timeout
surface.

### 2.3 Other removals

- `libvlc_Buffering` state value (in `libvlc_state_t`): use the media
  player's `on_buffering_changed` callback.
- `libvlc_media_get_parsed_status()` and the `libvlc_media_parsed_status_t`
  enum: use the new `libvlc_media_is_parsed()`, which returns a `bool`.
  The detailed outcome of a parse request is now reported only through
  `libvlc_parser_cbs.on_parsed` as a `libvlc_parser_status_t` (§6).
- `libvlc_media_discoverer_media_list()`: Use the `libvlc_media_discoverer_cbs`
  callbacks instead.
- `libvlc_media_list_player_set_media_player()`: the player must be the one
  supplied at construction time and cannot be swapped anymore.

---

## 3. Renamed APIs

| LibVLC 3 | LibVLC 4 |
|---|---|
| `libvlc_media_player_stop()` | `libvlc_media_player_stop_async()` |
| `libvlc_media_discoverer_release()` | `libvlc_media_discoverer_destroy()` |
| `libvlc_renderer_discoverer_release()` | `libvlc_renderer_discoverer_destroy()` |
| `libvlc_media_track_hold()` | `libvlc_media_track_retain()` |
| `libvlc_renderer_item_hold()` | `libvlc_renderer_item_retain()` |

The `release -> destroy` rename marks objects that are *not* reference-counted,
they have a single owner and one call frees them. The `hold -> retain`
rename aligns with the rest of the library (`libvlc_media_retain`,
`libvlc_picture_retain`, …).

### 3.1 Anonymous unions are now named `u`

Every public struct that used to embed an *anonymous* union now wraps it in a
named member `u`. The members themselves keep their names; only an extra `u.`
appears on the access path. This removes the reliance on the (non-standard in
C++, compiler-extension) anonymous-union behaviour and makes the discriminated
unions explicit.

The main motivation is auto-generated bindings. Tools that generate bindings
for other languages from the C headers cannot reliably name or address the
fields of an anonymous union, since those fields have no enclosing member to
qualify them. Giving the union an explicit name `u` gives every field a stable,
addressable path, so the bindings can be generated mechanically.

Affected structs and their fields:

| Struct (header) | Fields now under `.u` |
|---|---|
| `libvlc_media_track_t` (`libvlc_media_track.h`) | `audio`, `video`, `subtitle` |
| `libvlc_video_setup_device_info_t` (`libvlc_media_player.h`) | `d3d11`, `d3d9` |
| `libvlc_video_output_cfg_t` (`libvlc_media_player.h`) | `dxgi_format`, `d3d9_format`, `opengl_format`, `p_surface`, `anw` |

LibVLC 3:
```c
switch (track->i_type) {
case libvlc_track_video:
    w = track->video->i_width;   /* anonymous union */
    break;
case libvlc_track_audio:
    rate = track->audio->i_rate;
    break;
}
```

LibVLC 4:
```c
switch (track->i_type) {
case libvlc_track_video:
    w = track->u.video->i_width; /* named union member 'u' */
    break;
case libvlc_track_audio:
    rate = track->u.audio->i_rate;
    break;
}
```

The same `.u.` prefix applies inside the D3D11/D3D9/OpenGL video-callback
setup, e.g. `out->d3d11.device_context` becomes `out->u.d3d11.device_context`
and `out->dxgi_format` becomes `out->u.dxgi_format`.

---

## 4. Callback structs: the new event model

Every object that used to rely on an event manager now takes a versioned
`*_cbs` struct and an opaque pointer at construction. Every struct starts
with a `uint32_t version` field. Set it to the latest version number
available in the headers you compiled against — currently `0` for every
struct. Each field is documented with the version it became available in
(for example, "available since version 0"). The version you set tells LibVLC
exactly which fields you populated, so LibVLC can safely call every callback
you set. Setting a version lower than the fields you actually filled in
causes LibVLC to ignore those fields, silently dropping callbacks you
expected to fire. When you rebuild against newer headers that add fields and
you want to use them, raise this number to the new latest version.

**Forward ABI compatibility:** Future LibVLC releases may append new fields
to a `*_cbs` struct and raise the latest version number. Because structs are always passed
by pointer and fields are strictly append-only, the byte offsets of all
existing fields stay fixed. An old application's baked-in version number
tells LibVLC exactly how far into the struct it may read; everything beyond
that boundary is treated as NULL. A newer `libvlc.so` therefore runs old,
unmodified binaries correctly.

```c
static const struct libvlc_media_player_cbs cbs = {
    .version = 0,
    .on_state_changed    = my_on_state,
    .on_position_changed = my_on_position,
    /* remaining fields may be NULL if the callback is marked Optional */
};
libvlc_media_player_t *mp =
    libvlc_media_player_new(inst, &cbs, my_opaque);
```

Unused callbacks may be left NULL or zero-initialized when the documentation
marks them *Optional*. Callbacks marked *Mandatory* must be set.

**Lifetime and const-correctness:** LibVLC stores the pointer you pass; it
does not copy the struct. The callback object must therefore:

- outlive the LibVLC object it was registered with (the player,
  discoverer, etc.) and
- not be modified after it has been submitted.

Declare every `*_cbs` struct as `static const` when all of its fields are
compile-time constants.

### 4.1 Media player — `libvlc_media_player_cbs`

Defined in `<vlc/libvlc_media_player.h>`. Replaces every
`libvlc_MediaPlayer*` event. Full list:

| Callback | Replaces (v3 event) |
|---|---|
| `on_media_changed` | `libvlc_MediaPlayerMediaChanged` |
| `on_media_stopping` | `libvlc_MediaPlayerStopping` (now receives `libvlc_stopping_reason_t`) |
| `on_state_changed` | `libvlc_MediaPlayer{Opening,Playing,Paused,Stopped,…}` |
| `on_buffering_changed` | `libvlc_MediaPlayerBuffering` |
| `on_capabilities_changed` | `libvlc_MediaPlayerSeekableChanged`, `PausableChanged` |
| `on_position_changed` | `libvlc_MediaPlayerPositionChanged` / `TimeChanged` (receives both time in us and position) |
| `on_length_changed` | `libvlc_MediaPlayerLengthChanged` |
| `on_track_list_changed`, `on_track_selection_changed` | track-related events |
| `on_program_list_changed`, `on_program_selection_changed` | program-related events |
| `on_titles_changed`, `on_title_selection_changed` | title events |
| `on_chapter_selection_changed` | now delivers `libvlc_title_description_t *` and `libvlc_chapter_description_t *` directly |
| `on_recording_changed` | `libvlc_MediaPlayerRecordChanged` |
| `on_screenshot_taken` | `libvlc_MediaPlayerSnapshotTaken` |
| `on_media_parsed` | `libvlc_MediaParsedChanged` (delivered through the player) |
| `on_media_meta_changed` | `libvlc_MediaMetaChanged` |
| `on_media_subitems_changed` | `libvlc_MediaSubItemAdded` / `SubItemTreeAdded` |
| `on_media_attachments_added` | **new** — delivers `libvlc_picture_list_t *` attachments |
| `on_vout_changed` | `libvlc_MediaPlayerVout` |
| `on_cork_changed` | `libvlc_MediaPlayerCorked` / `Uncorked` |
| `on_audio_volume_changed`, `on_audio_mute_changed`, `on_audio_device_changed` | matching audio events |

New enums that appear in the callbacks:

- `libvlc_capability_t` — bitfield of `seek`, `pause`, `change_rate`, `rewind`.
- `libvlc_list_action_t` — `added`, `removed`, `updated`.
- `libvlc_stopping_reason_t` — `error`, `eos`, `user`.

### 4.2 Media player time watcher — `libvlc_media_player_watch_time_cbs`

For a simple "the time/position changed" notification, `on_position_changed`
(§4.1) is enough. When you need an **accurate**, output-synced playback time -
to drive a seek bar and a clock label - use `libvlc_media_player_watch_time()`
instead. It takes a callback struct:

```c
int libvlc_media_player_watch_time(libvlc_media_player_t *mp,
                                   libvlc_time_t min_period_us,
                                   const struct libvlc_media_player_watch_time_cbs *cbs,
                                   void *cbs_opaque);
```

with members `on_update` (mandatory), `on_paused`, `on_seek`. Call
`libvlc_media_player_unwatch_time()` to stop. Only one watcher can be
registered at a time.

**Why it exists.** In LibVLC 3 a time display was driven by the
`libvlc_MediaPlayerTimeChanged` / `PositionChanged` events, which reported the
*input* (demux) time. That time was only refreshed about every 250ms and
lagged the actual audio/video output by the output buffer size (300ms to 2s),
so a clock could be visibly wrong and out of sync with what was heard or seen.
The watcher instead reports points coming from the *output* itself - each
displayed video frame or written audio sample.

**How it is used.** Each `on_update` delivers a
`libvlc_media_player_time_point_t` carrying a `position`, a `rate`, a `ts_us`,
a `length_us` and a `libvlc_clock()`-based `system_date_us`. Because each point
embeds a system date and a rate, it is a self-contained value: `on_update` only
has to copy it to the UI thread once - that copy is the single point of
synchronization. From then on the UI mainloop can re-interpolate that *same*
point whenever it repaints. Interpolation reads only the copied point, takes no
lock and is a handful of arithmetic operations, so it is essentially free to
call as often as needed. Source updates can be sparse and irregular
(5ms, 1s, 10s…); interpolation fills the gaps:

- **Interpolate the time/position to "now"** with
  `libvlc_media_player_time_point_interpolate()`, driving a smooth seek bar at
  any frequency from the UI mainloop. `min_period_us` throttles how often
  `on_update` fires - it does not limit how often you may interpolate:

  ```c
  libvlc_time_t now = libvlc_clock(), ts_us;
  double pos;
  if (libvlc_media_player_time_point_interpolate(&point, now, &ts_us, &pos) == 0)
      ui_set_time(ts_us, pos);
  ```

- **Compute the system date of the next interval** (e.g. the next whole second)
  with `libvlc_media_player_time_point_get_next_date()`, to refresh a time
  label exactly on the second instead of polling for it.

`on_paused` tells the watcher to stop its interpolation timer; `on_seek`
reports the target while a seek is in flight (and `NULL` once it settles), so
the UI can snap to it instead of interpolating across the jump.

See [`player.c`](./player.c) for a complete example. This libvlc API wraps the
core `vlc_player_AddTimer()` (with `vlc_player_timer_point_Interpolate()` and
`vlc_player_timer_point_GetNextIntervalDate()`), used the same way by the Qt
interface in `modules/gui/qt/player/player_controller.cpp`.

### 4.3 Media discoverer — `libvlc_media_discoverer_cbs`

```c
libvlc_media_discoverer_t *
libvlc_media_discoverer_new(libvlc_instance_t *inst, const char *name,
                            const struct libvlc_media_discoverer_cbs *cbs,
                            void *cbs_opaque);
```

Callbacks: `on_media_added(opaque, parent, media)`,
`on_media_removed(opaque, media)`. The `parent` argument replaces the
removed `libvlc_media_discoverer_media_list()` function for parent tracking.

### 4.4 Renderer discoverer — `libvlc_renderer_discoverer_cbs`

```c
libvlc_renderer_discoverer_t *
libvlc_renderer_discoverer_new(libvlc_instance_t *inst, const char *name,
                               const struct libvlc_renderer_discoverer_cbs *cbs,
                               void *cbs_opaque);
```

Callbacks: `on_item_added` (mandatory), `on_item_removed` (optional).

### 4.5 Media — `libvlc_media_open_cbs`

`libvlc_media_new_callbacks()` no longer takes four function pointers – it
takes a struct:

LibVLC 3:
```c
libvlc_media_t *m = libvlc_media_new_callbacks(open_cb, read_cb, seek_cb,
                                               close_cb, opaque);
```

LibVLC 4:
```c
static const struct libvlc_media_open_cbs cbs = {
    .version = 0,
    .open    = my_open,   /* optional */
    .read    = my_read,   /* mandatory */
    .seek    = my_seek,   /* optional */
    .close   = my_close,  /* optional */
};
libvlc_media_t *m = libvlc_media_new_callbacks(&cbs, opaque);
```

### 4.6 Dialogs — `libvlc_dialog_cbs`

`libvlc_dialog_cbs` gained a `uint32_t version` field (initialise with
the latest version number, currently `0`). The error-display callback is no longer
part of the struct; register it separately with
`libvlc_dialog_set_error_callback()`.

### 4.7 Media list player — uses `libvlc_media_player_cbs`

`libvlc_media_list_player_new()` now takes the media-player callback struct
so that state / media changes can be observed without any list-player
events:

```c
libvlc_media_list_player_t *
libvlc_media_list_player_new(libvlc_instance_t *inst,
                             const struct libvlc_media_player_cbs *cbs,
                             void *cbs_opaque);
```

`libvlc_MediaListPlayerNextItemSet` is replaced by `on_media_changed`, and
`libvlc_MediaListPlayerPlayed` / `libvlc_MediaListPlayerStopped` by
`on_state_changed`.

### 4.8 Parser — `libvlc_parser_cbs` / `libvlc_thumbnailer_cbs`

See §6.

---

## 5. Microsecond unit

Functions that in LibVLC 3 used milliseconds now accept/return microseconds
through `libvlc_time_t`. A single typed time unit has been used throughout
the API. If you serialize or exchange times with other components (databases,
schedulers), review every boundary.

---

## 6. The new `libvlc_parser_t` API

Header: `<vlc/libvlc_parser.h>`

One parser object owns its own thread pool and can serve many concurrent
parse and thumbnail requests. Each request is identified with an opaque task
handle.

### 6.1 Lifetime

```c
struct libvlc_parser_cfg cfg = {
    .version                 = 0,
    .max_parser_threads      = 0,   /* 0 = default (1) */
    .max_thumbnailer_threads = 0,
    .timeout                 = 0,   /* in us, 0 = no timeout */
};
libvlc_parser_t *p = libvlc_parser_new(inst, &cfg);
/* ... */
libvlc_parser_destroy(p);
```

**NOTE:** `libvlc_parser_destroy` cancels all pending and running tasks, reports
it via their corresponding on_parsed/on_ended callback and blocks until all worker
threads are joined.

### 6.2 Parse a media

```c
/* cbs must outlive the returned task handle (i.e., until on_parsed is
   called on that particular task handle) */
static const struct libvlc_parser_cbs cbs = {
    .version              = 0,
    .on_parsed            = my_on_parsed,            /* mandatory */
    .on_attachments_added = my_on_attachments_added, /* optional */
};
libvlc_parser_request_t req = {
    .version     = 0,
    .media       = media,
    .parse_flags = libvlc_media_parse | libvlc_media_fetch_local,
};
libvlc_parser_task *task =
    libvlc_parser_task_new_parse(p, &req, &cbs, opaque);
libvlc_parser_submit(p, task);
/* later, when done: */
libvlc_parser_task_release(task);
```

`libvlc_parser_t` now allows re-queueing of an already parsed
`libvlc_media_t`. In LibVLC 3, a media that was already parsed was rejected.
In that case, the media will be parsed again because its metadata might have changed.

### 6.3 Generate a thumbnail

```c
/* tcbs must outlive the returned task handle (i.e., until on_ended is
   called on that particular task handle) */
static const struct libvlc_thumbnailer_cbs tcbs = {
    .version  = 0,
    .on_ended = my_on_thumbnail_ended, /* mandatory */
};
libvlc_thumbnailer_request_t treq = {
    .version = 0,
    .media   = media,
    .width   = 320,
    .height  = 0,      /* derived from aspect ratio */
    .crop    = false,
    .type    = libvlc_picture_Argb,
    .seek    = {
        .type  = libvlc_thumbnailer_seek_time,
        .value = { .time = 10 * 1000 * 1000, /* 10s, in us */ },
        .speed = libvlc_media_thumbnail_seek_fast,
    },
    .hw_dec  = false,
};
libvlc_parser_task *task =
    libvlc_parser_task_new_thumbnail(p, &treq, &tcbs, opaque);
libvlc_parser_submit(p, task);
/* later, when done: */
libvlc_parser_task_release(task);
```

### 6.4 Cancelling / introspection

- `libvlc_parser_cancel_request(p, task)` – cancel a specific request, or
  `NULL` to cancel everything. Returns the number of requests cancelled.
  Cancelled parse tasks fire `on_parsed` with
  `libvlc_parser_status_cancelled`. Cancelled thumbnail generation
  fire `on_ended` with `picture` as NULL.
- `libvlc_parser_task_get_media(task)` – borrowed pointer; do not release.
- `libvlc_parser_task_release(task)` – mandatory, releases the task handle
  (safe to call from inside `on_parsed` / `on_ended`). It does not
  cancel an in-flight request.
- `libvlc_parser_destroy(p)` – cancels all pending and running tasks, reports it
  via their corresponding on_parsed/on_ended callback and blocks until all
  worker threads are joined.

---

## 7. Migration checklist

Work through these in order; each step compiles more of your tree:

1. **Replace the event system.**
   - Remove every `libvlc_event_attach()` / `detach()` call and the
     associated dispatcher functions.
   - For each object, build a `*_cbs` struct with `version` set to the
     latest version number (currently `0`) and translate your handlers
     using the table in §4.1.
   - Delete any `libvlc_*_event_manager()` lookup.

2. **Update object construction.**
   - `libvlc_media_player_new(inst)` ->
     `libvlc_media_player_new(inst, &cbs, opaque)`.
   - Same for `libvlc_media_player_new_from_media()`,
     `libvlc_media_list_player_new()`,
     `libvlc_media_discoverer_new()`,
     `libvlc_renderer_discoverer_new()`.
   - `libvlc_media_new_callbacks(open, read, seek, close, op)` ->
     `libvlc_media_new_callbacks(&cbs_struct, op)`.

3. **Rename destructors / retainers.**
   - `libvlc_media_discoverer_release` -> `_destroy`.
   - `libvlc_renderer_discoverer_release` -> `_destroy`.
   - `libvlc_media_track_hold` -> `_retain`.
   - `libvlc_renderer_item_hold` -> `_retain`.

4. **Switch times to microseconds.** Audit every call site involving time unit.
   Convert `(ms)` literals to `(ms * 1000)`, rename locals, update database
   schemas if needed.

5. **Port parsing / thumbnailing.** Replace
   `libvlc_media_parse_request()` / `libvlc_media_thumbnail_request_by_*()`
   calls with a shared `libvlc_parser_t` per libvlc instance; create the task
   with `libvlc_parser_task_new_parse()` / `libvlc_parser_task_new_thumbnail()`,
   start it with `libvlc_parser_submit()`, and remove
   `libvlc_media_parse_stop()`.

6. **Update the dialog struct.** Add `.version = 0` (the latest
   version). Move your error handler to
   `libvlc_dialog_set_error_callback()`.

7. **Review async control flow.** Anywhere your code assumed
   `libvlc_media_player_stop()` / `set_pause()` / `set_media()` returned
   after the state change, introduce a wait on `on_state_changed` /
   `on_media_changed` / `on_media_stopping`.

8. **Drop removed enum values.**
   - `libvlc_Buffering`
   - `libvlc_media_parsed_status_t` enum (replace
     `libvlc_media_get_parsed_status()` with `libvlc_media_is_parsed()`).
   - Old `libvlc_media_parse_flag_t` flags (`parse_local`, `parse_network`,
     `parse_forced`).

9. **Add the `u.` prefix on named unions.** For every anonymous-union access
   listed in §3.1, insert `u.`: `track->video` -> `track->u.video`,
   `out->dxgi_format` -> `out->u.dxgi_format`, etc. The compiler flags each one.

10. **Recompile with `-Werror`.** The majority of remaining breakage will be
    trivial adaptations.

---

## 8. Reference: sample code

Ported examples are available under `doc/libvlc/` and exercise the new
callback-based APIs:

- [`player.c`](./player.c) – media player + time watcher
- [`parser.c`](./parser.c) – parser queue
- [`thumbnailer.c`](./thumbnailer.c) – thumbnailing through the parser
- [`media_discoverer.c`](./media_discoverer.c) – discoverer callbacks
- [`renderer_discoverer.c`](./renderer_discoverer.c) – renderer callbacks

Follow-up reading: the in-header doxygen documents every callback field and its
version field.

---

### Doc Libvlc CMakeLists (`doc/libvlc/CMakeLists.txt`)

CMAKE_MINIMUM_REQUIRED(VERSION 3.10)

# configure with your own path
#  -DLIBVLC_SDK_INC:STRING=S:/sources/build/win64/win64/vlc-4.0.0-dev/sdk/include
#  -DLIBVLC_SDK_LIB:STRING=S:/sources/build/win64/win64/vlc-4.0.0-dev/sdk/lib
#
# or set them in your VSCode settings
# {
#     "cmake.configureSettings": {
#         "LIBVLC_SDK_INC": "S:/sources/vlc/include",
#         "LIBVLC_SDK_LIB": "S:/sources/build/win64/win64/lib/.libs"
#     }
# }

set("LIBVLC_SDK_INC" "" CACHE PATH "libvlc include folder, containing the vlc/ includes")
set("LIBVLC_SDK_LIB" "" CACHE PATH "libvlc library folder, containing the libvlc libraries")

project("libvlc samples")

# define the libvlc external build
add_library(libvlc SHARED IMPORTED GLOBAL)
target_include_directories(libvlc INTERFACE ${LIBVLC_SDK_INC})
if (MSVC)
    set_target_properties(libvlc PROPERTIES IMPORTED_IMPLIB "${LIBVLC_SDK_LIB}/libvlc.lib")
else ()
    set_target_properties(libvlc PROPERTIES IMPORTED_IMPLIB "${LIBVLC_SDK_LIB}/libvlc.dll.a")
endif ()

if(WIN32)

    add_executable(d3d9_player WIN32 d3d9_player.c)
    target_link_libraries(d3d9_player libvlc d3d9)

    add_executable(d3d11_player WIN32 d3d11_player.cpp)
    target_compile_definitions(d3d11_player PRIVATE _WIN32_WINNT=0x0601)
    target_link_libraries(d3d11_player libvlc d3d11 d3dcompiler uuid)

    add_executable(win_player WIN32 win_player.c)
    target_link_libraries(win_player libvlc)

endif()

---

### Doc Skins Events Howto (`doc/skins/events-howto.txt`)

Before reading this document, you should first take a look at skins-howto.txt
to understand the general functioning of VLC skins.

What is an event ?
==================

Events are the dynamic part of the skins. It means that beyond visual aspect,
the interface must react with the user actions. An event describes a simple
interaction, in fact one simple action such as playing a file, hiding a
window...
So when designing a skin you will have to specify what those interactions are.
For this you will use simple actions that are described in event tags and you
would be able to add them and associate them to controls.

How to create an event ?
========================

An event describes a simple action as seen above.
All attributes are explained in the 'skins-howto.txt' file except the 'event'
attribute which is a bit special.
In the 'event' attribute you will enter a simple script with the following
syntax :
  "EVENT(parameter1,parameter2,...)"

The number of parameters depends on EVENT.
All this is case sensitive.
Don't add spaces.
EVENT is the action to execute, it can be one of the following:

 - VLC_NOTHING:
   Action    : none, it executes nothing so don't use it !
   Parameters: none.

 - VLC_SHOW:
   Action    : Open all windows of the interface with a fading effect if
               selected.
   Parameters: none.

 - VLC_HIDE:
   Action    : Close all windows of the interface with a fading effect if
               selected.
   Parameters:
     - First 1 is an EVENT to execute when all windows have been closed.

 - VLC_QUIT:
   Action    : Quit the interface
   Parameters: none.

 - VLC_OPEN:
   Action    : Open an "open file dialog box" to open a file to play.
   Parameters: none.

 - VLC_LOAD_SKIN:
   Action    : Open an "open file dialog box" to change the current skin.
   Parameters: none.

 - VLC_ON_TOP:
   Action    : Toggle the "Always on top" status
   Parameters: none.

 - VLC_LOG_SHOW:
   Not supported yet

 - VLC_LOG_CLEAR:
   Not supported yet.

 - VLC_INTF_REFRESH:
   Action    : Force refreshing of the interface.
   Parameters: none.

 - VLC_CHANGE_TRAY:
   Action    : if VLC is not visible in system tray, show it, else hide it.
   Parameters: none.

 - VLC_CHANGE_TASKBAR:
   Action    : if VLC is not visible in taskbar, show it, else hide it.
   Parameters: none.

 - VLC_FULLSCREEN:
   Action    : switch current playlist item to fullscreen mode.
   Parameters: none.

 - VLC_PLAY:
   Action    : play the stream.
   Parameters: none.

 - VLC_STOP:
   Action    : stop the stream.
   Parameters: none.

 - VLC_PAUSE:
   Action    : pause the stream.
   Parameters: none.

 - VLC_NEXT:
   Action    : go to the next file in the playlist.
   Parameters: none.

 - VLC_PREV:
   Action    : go to the previous file in the playlist.
   Parameters: none.

 - VLC_SLOWER:
   Action    : play the stream slower.
   Parameters: none.

 - VLC_FASTER:
   Action    : play the stream faster.
   Parameters: none.

 - VLC_STREAMPOS:
   Not supported yet.

 - VLC_VOLUME_CHANGE:
   Action    : change sound volume.
   Parameters:
     1: - VLC_VOLUME_MUTE: switch to mute mode.
        - VLC_VOLUME_UP: raise sounds volume.
        - VLC_VOLUME_DOWN:
        - VLC_VOLUME_SET: set sound volume to second parameter
     2: if first parameter is VLC_VOLUME_SET only, an integer between 0 and 100.

 - VLC_PLAYLIST_ADD_FILE:
   Action    : Open an "open file dialog box" to add files to playlist.
   Parameters: none.

 - VLC_WINDOW_MOVE:
   Action    : initiate manual window movement.
   Parameters: only one which must match the ID of a window. It should be
               used with image controls.

 - VLC_WINDOW_OPEN:
   Action    : open a window with a fading effect if selected.
   Parameters:
     1: ID of the window to open.
     2: Describe what to do. Nothing is opening. 'TRUE' is the same. 'FALSE' is
        closing window. 'CHANGE' is switching between these two states.

 - VLC_WINDOW_CLOSE:
   Action    : close a window with a fading effect if selected.
   Parameters:
     1: ID of the window to close.
     2: Describe what to do. Nothing is closing. 'TRUE' is the same. 'FALSE' is
        opening window. 'CHANGE' is switching between these two states.

 - CTRL_SET_SLIDER:
   Not supported yet.

 - CTRL_SET_TEXT:
   Not supported yet.

 - CTRL_ID_VISIBLE:
   Action    : hide/show a control.
   Parameters:
     1: ID of the control to hide/show.
     2: Describe what to do. Nothing is showing control. 'TRUE' is the same.
        'FALSE' is hiding control. 'CHANGE' is switching between these two
        states.

 - CTRL_ID_ENABLED:
   Not supported yet.

 - CTRL_ID_MOVE:
   Action    : moves a control.
   Parameters:
     1: ID of the control to move.
     2: horizontal offset of movement.
     3: vertical offset of movement.

 - PLAYLIST_ID_DEL:
   Action    : remove items from playlist.
   Parameters:
     1: ID of the playlist.


What to do with events ?
=======================

When creating your event, you must assign an ID to each of them.
Now you have to associate events with controls.
Some attributes of some controls are supposed to be filled with those IDs. That
is to say that when the action corresponding to the attribute will be done,
the event associated will be executed. The best example is assigning an event
to the 'onclick' attribute of a button control. The event will be executed when
clicking on the button.
You can execute several events. To do this you just have to separate them with
semicolon.
Example:
  <ButtonControl [...] onclick="event1;event2;event3"/>


Do I have to create every event for each skin ?
===============================================

No, a set of predefined events are present. Here they are with their ID and
shortcut.

  ID           Shortcut     Description

  tray                      Hide or show in the system tray.
  taskbar      CTRL+B       Hide or show in the taskbar.
  play         X            Play.
  pause        C            Pause.
  stop         V            Stop.
  next         B            Next file.
  prev         Z            Previous file.
  slow                      Play slower.
  fast                      Play faster.
  fullscreen   F            Switch to fullscreen mode.
  mute                      Mute the sound.
  volume_up
  volume_down
  quit         CTRL+C       Quit VLC.
  open         CTRL+O       Open a file.
  add_file     CTRL+A       Add a file.
  load_skin    CTRL+S       Change skin.
  on_top       CTRL+T       Toggle the "Always on top" status
  show_prefs                Show the preferences dialog box.
  show_info                 Show the FileInfo dialog box.
  show_log                  Show the Messages dialog box.
  hide_log                  Hide the Messages dialog box.

---

### Doc Skins Skins Howto (`doc/skins/skins-howto.txt`)

Note: This document is short and highly incomplete. If you want to write a new,
decent one, it will be more than welcome!



Basic principles
================

A skin (or theme, the two words have almost the same meaning) for VLC is made
of many BMP files (Windows Bitmap format) containing all the images needed, and
of an XML file describing how these images should be displayed, what happens
when the user clicks on a button, etc.

Those of you who have already done skins for other softwares shouldn't have too
many difficulties to understand how all this works.



Bezier curves
=============

One cool thing with VLC sliders is that they are not necessarily rectilinear,
but they can follow any Bezier curve. So if you want to have a slider moving
on a half-circle, or even doing a loop, you can !

This is not the place to explain how these curves work, the only thing to know
is that a Bezier curve can be caracterised by a set of points. Once you have
them (thanks to the graphical utility presented at the end of this file, for
example), you just need to enter the list of abscissas in the 'abs' attribute
and the list of ordinates (in the same order...) in the 'ord' attribute. The
separator is the coma. Example: abs="2,45,88" ord="50,120,50"

Bezier curves are used for the SliderControl and the PlaylistControl tags.



The bitmaps
===========

Basically, you need one bitmap file by state of control. For example, with a
image control you'll need 1 image, with a button 2 images (or 3 if you provide
the disabled state). A slider will also need 2, one for the static part and
another for the mobile part. Of course, the same bitmap file can be used for
many controls, provided you want to display the same image!

The bitmap format doesn't allow a transparent color, but in the XML file you
can specify a color that will be considered as transparent wherever it appears
in the bitmap file.



The XML file
============

XML is a markup language, like HTML. It won't be explained here any further,
please use Google if you don't know what is XML. You'll see, it's rather easy
to understand.

The XML file used for the VLC skins follows a predefined DTD. You can find this
DTD in VLC CVS, and its reading is strongly advised, since it contains most of
the default values used for the parameters. A skin that doesn't follow the DTD
with which VLC was compiled won't be loaded by VLC (and it might even crash
it...).

For a better comprehension of what follows, you should have a look at the DTD
(in modules/gui/skin/parser/dkin.dtd) and/or at an example of valid xml skin
(such as modules/gui/skin/skins/default/theme.xml).

OK, let's go for an enumeration of the different tags and theor attributes :

 - Theme: The main tag.
   Attributes:
     - magnet: allows to select the range of action (in pixels) of magnetism
       with border of screen : that is to say when the distance between the
       border of the screen and a window is less than this value, the window
       will stick to the border. 0 means no magnetism.
       Default is "9".

 - ThemeInfo: You can enter here some information about you (but this
   information is currently unused by VLC...)
   Attributes :
     - name: not yet supported.
     - author: not yet supported.
     - email: not yet supported.
     - webpage: not yet supported.

 - Bitmap: Associates a bitmap file with a name (=identifiant) that will be
   used by the various controls. Obviously, you need one Bitmap tag for each
   bitmap file you have.
   Attributes:
     - id: this is the name of the bitmap that will be used with controls
       (2 bitmaps shouldn't have the same name).
     - alphacolor: this is the transparency color of the bitmap. It must be
       indicated with the following format: "#RRGGBB" (where RR stands for the
       hexadecimal value of the red component, GG for the green one, and BB for
       the blue one).
     - file: this attribute is used to indicate the path and name of the bitmap
       file used. This path can be absolute (but you should avoid it as often as
       possible), or relative to the path of the xml file.

 - Event: An action that will be associated to a control later.
   Attributes :
     - id: this is the name of the event that will be used with controls.
       (2 events shouldn't have the same name).
     - event: see events-howto.txt
     - key: this is the shortcut key associated with the event. This means that
       the event will be executed when hitting the correspounding key. It must
       be indicated with the following format : "MOD+L" where "MOD" is the
       modifier key ("CTRL" or "ALT") and "L" is the letter in uppercase
       ("MOD+" is optionnal).
       Default is "none".

 - Font: Declares a font to be used in a TextControl or PlaylistControl.
   Attributes :
      - id: this is the name of the font that will be used with controls.
        Default is "default". (2 fonts shouldn't have the same name).
      - font: this is the name of the font
        Default is "arial".
      - size: this is the size of the font in points (pt).
        Default is "12".
      - color: this is the color of the font with the following format,
        "#RRGGBB" (see Bitmap).
        Default is "#000000" (black).
      - weight: this is the weight of the font. It must be between 0 and 1000
        Default is "400" (normal weight). Fewer is thinner...
      - italic: sets if the font must be in italic format.
        Default is "false".
      - underline: sets if the font must be underlined.
        Default is "false".

 - Window: A window that will appear on screen.
      - id: this is the name of the window (it will be only used for events
        but it is important: 2 windows shouldn't have the same name).
      - visible: sets if the window should appear or not at the launch of VLC.
        Default is "true".
      - x: sets the left position of the window.
        Default is "0".
      - y: sets the top position of the window.
        Default is "0".
      - fadetime: sets the time in milliseconds of the hide/show fading
        transition.
        Default is "500".
      - alpha: sets the transparency of the window. It must be between 1 and
        255. 1 is nearly total transaprency and should be avoided. 255 is total
        opacity.
        Default is "255". You should use high values.
      - movealpha: sets the transparency of the window when the window is
        moving. Same range as alpha.
        Default is "255".
      - dragdrop: sets if drag and drop of media files is allowed in this
        window.
        Default is "true".
      - playondrop: sets if a drop file is played directly (true) or only
        enqueud (false). This has no effect if dragdrop is set to "false".
        Default is "true".

 - ControlGroup: Adds an offset to the elements it contains. A ControlGroup is
   only supposed to ease the job of the skin designer, who can adjust the
   position of a group of controls without modifying all the coordinates, but
   you can ignore it if you want (only one ControlGroup is necessary, just
   inside Window). ControlGroup tags can be nested.
   Attributes :
     - x: try and guess.
       Default is "0".
     - y: what do you think ?
       Default is "0".

 - Anchor: Creates a "magnetic point" in the current Window. If an anchor of
   another Window enters in the range of action of this anchor, the 2 anchors
   will automatically be on the same place, and the windows are "sticked". Each
   anchor has a priority ('priority' attribute), and the anchor with the
   highest priority is the winner, which means that when moving its Window all
   the other anchored Windows will move too. To break the effect of 2 linked
   anchors, you need to move the Window whose anchor has the lower priority.
   Attributes :
     - x: is it really necessary to explain ?
       Default is "0".
     - y: ...
       Default is "0".
     - priority: priority of anchor (see the previous description).
       No default, must de defined!
     - range: Range of action of the anchor in pixels.
       Default is "10".

 - ImageControl, ButtonControl, CheckBoxControl, TextControl, SliderControl,
   PlaylistControl: The various visual controls that constitute a Window. They
   share some properties: 'x' and 'y' for the position, 'visible' for the
   initial state, 'id' for identifying them (only needed if you want to create
   an event acting on this particular control) and 'help' for a short
   description of the control that could be displayed in a special TextControl
   (see below). All the controls can also accept events, and you have the
   possibility to associate many events to a control at once, separing them
   with semicolons.

 - ImageControl: Creates a simple image. Useful for backgrounds.
     - image: this attribute must be set to an identifiant of a Bitmap tag.
     - onclick: the 'event' attribute can be used to associate an event to the
       image (the event is triggered by a click on the image).
       Typical use: an Event made with 'WINDOW_MOVE(window)' (where 'window' is
       the id of a Window) can be associated to an ImageControl of this
       Window. Hence the Window can be moved via the image...

 - ButtonControl: Creates a button.
     - up: identifiant of a Bitmap. Used for drawing the up state of the
       button.
     - down: identifiant of a Bitmap. Used for drawing the down state of the
       button.
     - disabled : identifiant of a Bitmap. Used for drawing the disabled state
       of the button.
     - onclick: event executed when clicking on the button.
       Default is "none".
     - onmouseover: event executed when the mouse cursor enters the button.
       Default is "none".
     - onmouseout: event executed when the mouse cursor leaves the button.
       Default is "none".
     - tooltiptext : used to display a tooltip.
       Default is "none" (no tooltip).

 - CheckBoxControl: Creates a checkbox, i.e. a button with 2 states
   (checked/unchecked). So you need 6 images for a full-featured checkbox: each
   state has a basic image, an image corresponding to a click not yet released,
   and an image for the disabled control. If you supply only the basic images,
   the other ones will be identical.
   Attributes:
     - img1: identifiant of a Bitmap. Used for drawing control in state 1.
     - clickimg1: identifiants of a bitmap. Used for drawing control when
       clicking on it in state 1.
       Default is the value of 'img1' attribute.
     - img2: identifiant of a Bitmap. Used for drawing control in state 2.
     - clickimg2: identifiant of a Bitmap. Used for drawing control when
       clicking on it in state 2.
       Default is the value of 'img2' attribute.
     - disabled1: identifiant of a Bitmap. Used for drawing control in state 1
       when disabling.
       Default is the value of 'img1' attribute.
     - disabled2: identifiant of a Bitmap. Used for drawing control in state 1
       when disabling.
       Default is the value of 'img2' attribute.
     - onclick1: event executed when clicking on the control in state 1.
       Default is "none".
     - onclick2: event executed when clicking on the control in state 2.
       Default is "none".
     - onmouseover1: event executed when the mouse cursor enters the button in
       state 1.
       Default is "none".
     - onmouseout1: event executed when the mouse cursor leaves the button in
       state 1.
       Default is "none".
     - onmouseover2: event executed when the mouse cursor enters the button in
       state 2.
       Default is "none".
     - onmouseout2: event executed when the mouse cursor leaves the button in
       state 2.
       Default is "none".

 - TextControl: Creates a text.
   Attributes:
     - font: the font to use, which must be one of the Font identifiants.
     - text: the text to display.
     - align: either 'left' or 'center' or 'right'.
       Default is "left".
     - scroll: if set to 'true', the text will scroll if it does not fit into
       the 'scrollspace'.
       Default is "true".
     - scrollspace: size in pixel between two occurrences of the text when
       scrolling.
       Default is "20".
     - display: this value is a bit special, it allows to have a text
       auto-updated by VLC. Possible values are 'time', 'left_time',
       'total_time', 'file_name' (for the current file name with its path),
       'title' (for the file name without the path) and 'help' (for a help
       about the controls that defined their 'help' attribute).
       You can specify several types by separating them with semicolons. To
       switch between then, just double click on the text.
       Useful to switch between 'time' and 'left_time'.
     - width: Width of the text in pixels. If set to "0", the width is
       automatically calculated to fit with the current text.
       Default is "0".

 - SliderControl: Creates a slider. The 'abs' and 'ord' attributes are needed
   for the Bezier curve that the slider will follow. 'up' and 'down' are the
   images of the slider. The 'tooltiptext' attribute works, and the slider will
   automatically append the percentage of the position.
   Attributes:
     - type: two 'types' of sliders are predefined: 'time' for a slider
       allowing to seek in the stream, and 'volume' for a volume slider.
       Default is "time".
     - up: identifiant of a Bitmap.
     - down: identifiant of a Bitmap.
     - abs: see SliderControl description and bezier curve description.
     - ord: see SliderControl description and bezier curve description.
     - tooltiptext: used to display a tooltip.
       Default is "none" (no tooltip).

 - PlaylistControl: Creates a playlist. This tag must contain a SliderControl
   tag (to allow scrolling in the playlist). If the playlist contains entries
   wider than the list width, an automatic tooltip will appear with
   the full name of the entry. The other attributes are rather easy to
   understand...
   Attributes:
     - width: width of the list in pixels. This is the whole width for file
       name, number of files in the playlist and info text.
       Default is "200".
     - infowidth: width of the info text in pixels.
       Default is "50".
     - font: the font to use, which must be one of the Font identifiants.
     - playfont: the font to use for current playing file, which must be one of
       the Font identifiants.
       Default is "none".
     - selcolor: color in "#RRGGBB" format of the selected files.
       Default is "#0000FF" (blue).
     - abs: see PlaylistControl description and bezier curve description.
     - ord: see PlaylistControl description and bezier curve description.



Compression
===========

Once your skin is finished, instead of keeping many bitmap files and the XML
file, you can compress them in a .tar.gz archive (Winzip and UmtimateZip can do
it perfectly, for example). Before doing so, don't forget to rename your XML
file into "theme.xml", or VLC won't be able to read it... Then rename your
compressed file with the .vlt extension and... that's all! VLC can load
directly skins with the .vlt extension.



Tools and advice
================

 - To generate easily Bezier curves, you can use the curve-maker. Basically,
   you add and remove points at will, and you can move them to see how the
   curve evolves. When you have reached the perfect curve, you just have to
   copy-paste the list of abscissas and ordinates into the 'abs' and 'ord'
   attributes of your SliderControl or PlaylistControl. The curve-maker also
   allows to load a bitmap, this could be useful if you want to follow a
   specific pattern of a slider, for example.

 - When you are creating your skin, you may want to see the VLC messages where
   some errors are logged. For this, open a dos window, go to the directory
   where VLC is installed, and type "vlc -I skin -v --extraintf logger". This
   should open VLC and a log window (what's more, the logs should be saved in a
   file called vlc-log.txt). The interesting lines are those with "skin
   interface"...

 - For the Bitmap tags, don't use absolute paths but relative paths (they are
   relative to the XML file directory), so that your skin can be reused by
   anybody without a particular file structure.

 - To fully use the possibilities given, you should look at how other skins are
   made, it's often very useful.


Good luck!

---

### Doc Standalone Buildsystem (`doc/standalone/buildsystem.md`)

# Buildsystem documentation {#buildsystem}

## Autotools

VLC uses the Autotools build system tools, that is composed of various tools:

- m4  
  Used for macro expansions in the configure.ac file.
- Autoconf  
  Generates the `configure` shell script from the `configure.ac` file
- Automake  
  Generates the Makefiles from the `Makefile.am` files
- libtool  
  Wrapper tool to abstract linker differences across different platforms


## Meson

In addition to the "traditional" autotools build system, recently there has
been an ongoing effort to migrate the VLC build system to meson. Currently
the meson buildsystem is in an **experimental state** and should not be used for
release builds of VLC!

However we encourage developers to try out the meson buildsystem and report
any issues with it or help add missing module builds to meson.

### Organization

All meson build definitions can be found in `meson.build` files. All
options are defined in the top-level directory `meson_options.txt` file,
which has the same syntax as the meson build files but only accepts the
`option()` function.


### Code style and conventions

- Indentation must be used with 4 spaces, no tabs should be used.
- Keyword arguments (named arguments) should have the `:` right next
  to them followed by a space:
  ```meson
  # Right
  add_project_arguments('-D_EXAMPLE', language: ['c', 'cpp', 'objc'])

  # Wrong
  add_project_arguments('-D_EXAMPLE', language : ['c', 'cpp', 'objc'])
  ```
- For dictionaries, key and value are separated by `:` and spaces should be
  used before and after:
  ```meson
  # Right
  { 'key' : 'value' }

  # Wrong
  { 'key': 'value' }
  ```
- Strings in meson must always use 'single' quotes, never use "double" quotes.
- Do not use arrays where not necessary, especially for the `files()` function:
  ```meson
  # Right
  files('hello.c', 'world.c')

  # Wrong
  files(['hello.c', 'world.c'])
  ```
- Dependencies found with the `dependency()` function **must** use a variable named after the
  library followed by the `_dep` suffix, those found with `find_library()` must use the `_lib`
  suffix:
  ```meson
  # Right
  foundation_dep = dependency('Foundation')
  iconv_lib = cc.find_library('iconv')

  # Wrong
  foundation_lib = dependency('Foundation')
  iconv_dep = cc.find_library('iconv')
  ```

### Adding a module

VLC modules are meson dicts added to a special array named `vlc_modules`. To add a new module, simply
append to that variable:

```meson
vlc_modules += {
    'name' : 'file_logger',
    'sources' : files('file.c')
}
```

@warning 	Make sure to not accidentally overwrite the `vlc_modules` variable by using
		 	an `=` instead of `+=` when appending the dictionary.

Currently the modules dict accepts the following keys:

@param 	name
		The name of the VLC plugin (used for the `MODULE_STRING` define). **Required**

@param 	sources
		The source files for the module, use [`files()`][mref_files] to specify them. **Required**

@param  enabled
        A boolean indicating whether the module should be built or not.

@param  dependencies
		The dependencies needed by the module. Only list external dependencies
		here, not libraries that are built as part of the VLC build, for these
		use `link_with` instead.

@param 	include_directories
		Additional include directories that should be used when compiling the module.
		These should be specified using the [`include_directories()`][mref_include_directories]
		function.

@param  c_args
		Additional flags to pass to the C compiler.

@param 	cpp_args
		Additional flags to pass to the C++ compiler.

@param 	objc_args
		Additional flags to pass to the Objective-C compiler.

@param 	nasm_args
		Additional flags to pass to the NASM compiler.

@param  link_args
		Additional flags to pass to the dynamic linker. Do _not_ use this to specify
		additional libraries to link with, use the `dependencies` instead.

@param 	link_language
		Force the linker to be for the specified language. This is not needed in most
		cases but can be useful for example for a C plugin that depends on a C++ library
		therefore needing the C++ standard library linked.

### Checking the host system
It is frequently necessary to check the host we are building for. To do that, use the
`host_system` variable. For possible values, refer to the [reference table][mref_hostsys] in
the documentation.

For now, `host_system` is equivalent to `host_machine.system()`, however this might not
always be the case, hence the special variable for that.

### Finding dependencies
As most VLC modules likely require an external dependency too, let's have a look how to find a
dependency and add an option for it.

To find dependencies, Meson has essentially two options:

- [`compiler.find_library()`][mref_compiler_find_library]
- [`dependency()`][mref_dependency]

Generally the latter should be preferred in most cases. It primarily uses `pkg-config` to find
the dependency, however even for very common dependencies that do not provide a `.pc` file, it
can have handling to find it by other means. For a full list of the special cases, consult the
[documentation][mdoc_dpendency_custom]. It is roughly equivalent to `PKG_CHECK_MODULES` in autoconf.

However there might be a case where you need to find a library that has no `.pc` file, no custom
`*-config` tool or handling by meson. In these cases you can use the compiler object's
[`find_library`][mref_compiler_find_library] function. It basically does a linker check for the
specified library name and returns a dependency object for it. It is roughtly equivalent to
`AC_CHECK_LIB` in autoconf.

#### Options

To add an option for the dependency, add the respective option entry in `meson_options.txt`:

```meson
option('dav1d',
    type : 'feature',
    value : 'auto',
    description : 'libdav1d AV1 decoder support')
```

This adds a new [feature option][mdoc_feature] with the name `dav1d`. A feature option is a special tri-state
option that can be `enabled`, `disabled` or `auto`.

Now we can just look up the option and use that for the `required` argument of the `dependency()` function:

```meson
# dav1d AV1 decoder
dav1d_dep = dependency('dav1d', version: '>= 0.5.0', required: get_option('dav1d'))
vlc_modules += {
    'name' : 'dav1d',
    'sources' : files('dav1d.c'),
    'dependencies' : [dav1d_dep],
    'enabled' : dav1d_dep.found(),
}
```

As the option defaults to `auto`, meson will look it up, and if its is found, add the module.
If the option is manually set to `disabled`, meson will never look it up and just always return a not-found
dependency object. If it's set to `enabled`, meson will error out if it's not found.

The feature option object provides a few more [useful functions][mref_feature], for more complex
cases of conditional dependencies. For example suppose we want an option to be disabled in some
cases when it is set to `auto`:

```meson
vlc_modules += {
    'name' : 'xcb',
    'sources' : files('xcb.c'),
    'enabled' : get_option('x11') \
        .disable_auto_if(host_system in ['darwin', 'windows']) \
        .allowed(),
}
```

This will disable the `x11` option if it is set to auto, when on `darwin` or `windows`.

\note
Options, like most objects in meson, are immutable. So if you were to instead write
```meson
x11_opt = get_option('x11')
x11_opt.disable_auto_if(host_system in ['darwin', 'windows']) # Wrong, don't do this!
if (x11_opt.allowed())
	# This is not disabled on Darwin or Windows!
endif
```
it would not do what you might expect, as `disable_auto_if` returns a new option and does not mutate the
existing one. The returned option object is never assigned to any variable, so it is lost.

### Adding new tests

VLC tests are also meson dictionaries added to the special array named `vlc_tests`.
This mechanism allows a test to reference modules that should be available when
running the test.

To add a new test, simply append to that variable:

```meson
vlc_tests += {
    'name' : 'test_src_input_thumbnail',
    'sources' : files('input/thumbnail.c'),
    'suite' : ['src', 'test_src'],
    'link_with' : [libvlc, libvlccore],
    'module_depends' : ['demux_mock', 'rawvideo']
}
```

@warning    Make sure to not accidentally overwrite the `vlc_tests` variable
            by using an `=` instead of `+=` when appending the dictionary.

Currently the modules dictionary accepts the following keys:

@param  name
        The name of the new VLC test, which will map to the executable name. **Required**

@param  sources
        The source files for the new test, use [`files()`][mref_files] to specify them. **Required**

@param  suite
        The meson test suites to which this test should be available.

@param  dependencies
		The dependencies needed by the test. Only list external dependencies
		here, not libraries that are built as part of the VLC build, for these
		use `link_with` instead.

@param  link_with
		The dependencies needed by the test that are part of the VLC build.
        For external dependencies, use `dependencies` instead.

@param  include_directories
		Additional include directories that should be used when compiling the test.
		These should be specified using the [`include_directories()`][mref_include_directories]
		function.

@param  module_depends
        The list of module names this test depends upon.

@param  moc_headers
        A list of header files that should be transformed by Qt's moc
        source translater built for the test.

@param  c_args
	    Additional flags to pass to the C compiler.

@param  cpp_args
		Additional flags to pass to the C++ compiler.

@param  objc_args
		Additional flags to pass to the Objective-C compiler.

To run a specific test, you can directly use:

```bash
meson test -C build-meson test_src_input_thumbnail
```

where `test_src_input_thumbnail` can be replaced by the `name` of the test.
Make sure that a module really depends upon the module it's using through the
`module_depends` variable. If every modules should be used to run the test,
you can use the special value `vlc_plugins_targets.keys()`, but adding more
module dependencies than necessary will drastically increase the compile time
when running a specific test in cases like automated `git bisect`.

#### Compiling with contribs

The meson build system also supports using dependencies and binaries provided
by the contrib system. 

Once the contrib has been prepared, a meson machine file will be generated
in the installation prefix. For native linux x86_64, it will typically be
found at the following location:

    vlc/contrib/x86_64-pc-linux-gnu/share/meson/native/contrib.ini

When using prebuilt contribs, `make prebuilt` regenerates this file for the
local tool paths. Run `make tools` before configuring VLC if the required
build tools are not available on the system.

Then you can setup meson with this file using the following command:

    meson setup build-meson \
        --native-file contrib/x86_64-pc-linux-gnu/share/meson/native/contrib.ini 

When cross-compiling, both the crossfile and the contrib machine file
can be supplied at the same time:

    meson setup build-meson --cross-file win32.crossfile \
        --cross-file contrib/x86_64-w64-mingw32/share/meson/cross/contrib.ini 

[mref_files]: https://mesonbuild.com/Reference-manual_functions.html#files
[mref_include_directories]: https://mesonbuild.com/Reference-manual_functions.html#include_directories
[mref_compiler_find_library]: https://mesonbuild.com/Reference-manual_returned_compiler.html#compilerfind_library
[mref_dependency]: https://mesonbuild.com/Reference-manual_functions.html#dependency
[mdoc_dpendency_custom]: https://mesonbuild.com/Dependencies.html#dependencies-with-custom-lookup-functionality
[mdoc_feature]: https://mesonbuild.com/Build-options.html#features
[mref_feature]: https://mesonbuild.com/Reference-manual_returned_feature.html#feature-option-object-feature
[mref_hostsys]: https://mesonbuild.com/Reference-tables.html#operating-system-names

---

### Doc Standalone Libvlc (`doc/standalone/libvlc.md`)

# LibVLC Documentation {#libvlc}

## Introduction

[libVLC](@ref libvlc) is an embeddable engine for
3rd party applications and frameworks.

It runs on the same platforms as VLC <em>(and sometimes on more)</em>
and can provide playback, streaming and conversion of multimedia
files and streams.

[libVLC](@ref libvlc) has numerous bindings for
other languages, such as C++, Python, java, Objective-C and C#.

The generated documentation can be browsed from [here](@ref libvlc).

## License

[libVLC](@ref libvlc) is released under the LGPLv2
<em>(or later)</em> license. This allows embedding the engine in 3rd
party applications, while letting them to be licensed under other
licenses.

However, note that some plugins are under more restrictive licenses,
such as GPLv3 <em>(or later)</em>.

## Example

\snippet{lineno} example.c minimal example

---

### Doc Standalone Mainpage (`doc/standalone/mainpage.md`)

\mainpage
# LibVLC and libvlccore documentation

## Introduction

[libVLC](@ref libvlc) is an embeddable engine for
3rd party applications and frameworks.

It runs on the same platforms as VLC <em>(and sometimes on more)</em>
and can provide playback, streaming and conversion of multimedia
files and streams.

[libVLC](@ref libvlc) has numerous bindings for
other languages, such as C++, Python, java, Objective-C and C#.

##  License

[libVLC](@ref libvlc) and **libvlccore** are released
under the LGPLv2 <em>(or later)</em> license. This allows embedding the
engine in 3rd party applications, while letting them to be licensed
under other licenses.

However, note that some plugins are under more restrictive licenses,
such as GPLv3 <em>(or later)</em>.

---

### Doc Standalone Rust (`doc/standalone/rust.md`)

# Rust bindings for vlccore #{rust_bindings}

_libvlccore_ provide [Rust] bindings so as to interface with the core
implementation in C and implement VLC plugins in [Rust] as a crate.

The bindings are located in the `src/rust/` folder for everything which
is mapping headers from `include/vlc_*.h`. There's no module-specific
crate for now, but those would be located in `modules/`.

[Rust]: https://www.rust-lang.org/

## Creating a new core crate

New core crate needs to be placed into the `src/rust/` directory. They
usually match with another C `vlc_*.h` header. For instance `vlc_tick.h`
will match with the `src/rust/vlcrs-tick` crate.

Cargo manifest for the workspace at `src/rust/Cargo.toml` needs to be
updated to account for the new crate.

```diff
diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml
index b1a9555a23..72ca86951d 100644
--- a/src/rust/Cargo.toml
+++ b/src/rust/Cargo.toml
@@ -1,5 +1,6 @@
 [workspace]
 members = [
+    "vlcrs-newcrate",
     "vlcrs-messages",
     "vlcrs-sys-generator"
 ]
```

A core crate might depend on another core crate, in particular when a
`vlc_*.h` header is depending on another header or uses forward
declarations.

In that case, the Cargo.toml from this crate should directly reference
the other crate using a path locator.

```toml
[package]
name = "vlcrs-messages"
edition = "2021"
version.workspace = true
license.workspace = true

[dependencies]
vlcrs-utils = { path = "../vlcrs-utils" }
```

Finally, if the new crate is meant to be available from modules, it
should also be added to the `modules/Cargo.toml` workspace file using a
path locator, inside the workspace dependencies.

```toml
# modules/Cargo.toml
[workspace]
resolver = "2"
members = ["/*-rs"]

[workspace.dependencies]
vlcrs-tick = { path = "../src/rust/vlcrs-tick" }
vlcrs-messages = { path = "../src/rust/vlcrs-messages" }
# ...
```

## Module creation

To create a new module, create a new crate inside the relevant
`moduledir` folder from `modules/` folder using Cargo, ending with `-rs`
and without version control creation given that VLC sources are already
versioned:

```bash
cd modules/moduledir && cargo new --lib --vcs none mynewmodule-rs
```

For instance, a new video filter would be created with:

```bash
cd modules/video_filter && cargo new --lib --vcs none mynewfilter-rs
```

Then, you can modify the Cargo.toml file to include the different crates
the module will be using from the workspace.

```toml
[package]
name = "mynewmodule-rs"
edition = "2021"
version = "0.1.0"

[dependencies]
vlcrs-messages.workspace = true
```

The module will need to be listed into `modules/Cargo.toml` members:

```toml
[workspace]
# ...
members = [
    # ...
    "moduledir/mynewmodule-rs"
]
```

Then you can add the plugin to the buildsystems. For automake, check the
parent Makefile.am or create a new one depending on how crowded it is,
and add:

```make
libmynewmodule_rs.la:
	@$(LIBTOOL_CARGO) $(srcdir)/moduledir/mynewmodule-rs/ $@

libmynewmodule_rs_plugin_la_SOURCES = \
	mynewmodule-rs/Cargo.toml \
	mynewmodule-rs/src/lib.rs 
libmynewmodule_rs_plugin_la_LIBADD = libmynewmodule_rs.la

if HAVE_RUST
moduledir_PLUGINS += libmynewmodule_rs_plugin.la
# Example: video_filter_PLUGINS += libmynewfilter_rs_plugin.la
endif
```

For meson, you will use the `vlc_rust_modules` dictionary from the
parent meson.build file:

```
vlc_rust_modules += {
    'name' : 'mynewmodule_rs',
    'sources' : files('mynewmodule-rs/src/lib.rs'),
    'cargo_toml' : files('mynewmodule-rs/Cargo.toml'),
}
```

Creating a new plugin requires the definition of the plugin manifest to
expose the different modules from there. This is done through the
`module!{}` macro:

```rust
use vlcrs_plugin::module;
use vlcrs_video_filter::FilterModule;

pub struct MyNewModule {};

impl FilterModule for MyNewModule {
    fn open<'a> (filter: Filter<'a>) -> Result<()> {
        todo!();
    }
}

module! {
    type: MyNewModule,
    capability: "video filter" @ 0,
    category: SUBCAT_VIDEO_FILTER,
    description: "A new module",
    shortname: "mynewmodule",
    shortcuts: ["mynewmodule_filter"],
}
```

---

### Doc Subtitles Cvd Subtitles (`doc/subtitles/cvd-subtitles.txt`)

The following information is culled from information from 
Julio Sanchez Fernandez (http://subhandler.sourceforge.net)
by Rocky Bernstein. 

We do not have information on the subtitle format used on CVD's except
the submux sample code and a couple of samples of dubious
origin. Thus, the information below is result of reading some code
whose correctness is not known and some experimentation.

Please, if you have any corrections or additions to make, send
them to rocky@panix.com.

CVD subtitles are different in several ways from SVCD OGT subtitles
(see see corresponding info on that.)

The image data comes first and meta data is at the end.  So that the
metadata can be found easily, the subtitle packet starts with two
bytes (everything is big-endian again) that gives the total size of
the subtitle data and the offset to the metadata - i.e. size of the
image data plus the four bytes at the beginning.

As with OGT subtitles, Data for single screen subtitle may come in
several non-contiguous packets of a stream.  From the scant data on
the format, the only way known to detect the first packet in a
subtitle.  The first packet seems to have a valid PTS while later
packets for the same image don't.

The image data comes interlaced and is run-length encoded (RLE). Each
field is a four-bit nibbles that is further subdivided in a two-bit
repeat count and a two-bit color number - up to three pixels can be
described in four bits.  What a 0 repeat count means not known.  It
might be used for RLE extension.  There is a special case of a 0
repeat count that has been inferred: when the full nibble is zero, the
rest of the line is filled with the color value in the next nibble.
It is unknown what happens if the color value is greater than three.
The rest seems to use a 4-entries palette.  It is possible that the
fill-line general case above is not as described and the zero repeat
count means fill line.  But since this hasn't been ever seen, the
preceding is only conjecture.

Here is information given at the start of a subtitle:

SPU size        2 bytes
metadata offset 2 bytes

Although metadata information does come in a fixed field order, every
metadata field consists of a tag byte followed by parameters. In all
cases seen, the size including the tag byte is exactly four bytes.

code    Meaning 
----    -------
0x0c 	Unknown

0x04 	24-bit subtitle duration in 1/90000ths of a second

0x17	upper left x, y position, each a 10-bit value, encoded:
           x = ((p[1]&0x0f)<<6) + (p[2]>>2)
           y = ((p[2]&0x03)<<8) + p[3];

0x1f	lower right x, y position, each a 10-bit (0-1023) value,
        encoded as above 

0x24    3 bytes primary palette 0 - 1 byte for each of y, u, and v 
0x25    3 bytes primary palette 1 - 1 byte for each of y, u, and v
0x26    3 bytes primary palette 2 - 1 byte for each of y, u, and v
0x27    3 bytes primary palette 3 - 1 byte for each of y, u, and v

0x2c    3 bytes highlight palette 0 - 1 byte for each of y, u, and v 
0x2d    3 bytes highlight palette 1 - 1 byte for each of y, u, and v
0x2e    3 bytes highlight palette 2 - 1 byte for each of y, u, and v
0x2f    3 bytes highlight palette 3 - 1 byte for each of y, u, and v

0x37    3 bytes transparency for primary palette - 1 byte for each 
	of y, u  and v

0x3f    3 bytes transparency for highlight palette - 1 byte for each 
	of y, u  and v

0x47    Offset to start of even rows of interlaced image.
0x4f    Offset to start of odd rows of interlaced image.

---

### Doc Subtitles Svcd Ogt Subtitles (`doc/subtitles/svcd-ogt-subtitles.txt`)

The following information is culled from information from Julio
Sanchez Fernandez [1] by Rocky Bernstein.

SVCD subtitles (Philips Overlay Graphics Text or OGT) are transported
in an MPEG private stream with stream id 0x70. AC3 subtitle stream
numbers start at 0,x80 and go to 0,x87. (CVD subtitle stream numbers
start at stream ID 0 and go to 3.)

All numbers are in big-endian and the image is interlaced with a
resolution of 720x480 for NTSC streams or 720x576 for PAL streams.

Data packets start with a 4-byte header:

    * Stream number (1 byte). Only bits 0-3 are used as there can only
      be up to 8 subtitles.

    * Packet number(1 byte). The first packet starts at 0; the last
      packet of a subtitle has the 7th bit set. A subtitle can be
      broken up into several packets when it is multiplexed into the
      MPEG stream.

    * Subtitle image number (2 bytes).

    * Length of the rest of the packet (described in the next section). 

================
SUBTITLE PACKETS
================

The information below comes immediately after the packet header in the
same packet.

* Packet length (2 bytes) 

The assembled subtitle, with stripped sequence numbers

* Option byte (1 byte) 

This is normally set to 0x2e; bit 3 indicates that a field with the
display-time of the subtitle is present. What the other bits do is not
known.

* Time to display the subtitle (4 bytes) 

This in 1/90000'th of a second. This is only present if bit 3 above is
set. This value specifies how long the subtitle will be shown, not
when. When the subtitle is to be shown is specified in the PTS in the
PES header.

* X position (2 bytes) 

* Y position (2 bytes) 

* Width (2 bytes)

This value must be even.

* Height (2 bytes)

This value must be even.

* Unknown (1 byte) 

* 4 Color Palette Entries (16 bytes)

* A pallette entry has the following format:

    * Y (1 byte)
    * U (1 byte)
    * V (1 byte)
    * Transparency (1 byte). A 0 value means totally transparent and
      255 totally opaque.

* Command (1 byte) 

if cmd>>6 == 1 then a shift command and (cmd>>4)&3 is the
direction. 0=top, 1=left, 2 = right, 3 = bottom

* Odd-field data offset (2 bytes) 

Image data is stored interlaced with the even lines first. This offset
specifies where the first odd-line image data starts.

IMAGE DATA

Then subtitle image data follows the above control information. Image
lines are interlaced. That is line 0 is comes first, then line 2, then
line 4. After all of the even lines, comes line 1, then line 3,
etc.. The subtitle aspect ratio is not to be scaled. It is independent
of the aspect ratio of the underlying image which it is to be
overlayed on top of.

The image is encoded using two bits per pixel that select a palette
entry except that value 0 starts a limited run-length encoding for
color 0. When 0 is seen, the next two bits encode one less than
the number of pixels, so we can encode run lengths from 1 to 4. These
get filled with the color in palette entry 0.

The encoding of each line is padded to a whole number of bytes. The
first field is padded to an even byte length and the complete subtitle
is padded to a 4-byte multiple that always include one zero byte at
the end.

================
SUBTITLING TOOLS
================

Philips SVCD tools can create SVCD subtitles, but it only works under
NT4. It is not that easy to find anymore. (dvd.da.ru, doom9.net or
similar perhaps?). It can create good subtitled SVCDs. Subtitles are
created in a text editor. Another tool is called submux. Last version
I tried doesn't do renumber presentation time stamps properly, but
many media players (now) can tolerate this.

The subtitles you get from tools like "I-author" are not SVCD
subtitles, they are CVD subtitles. They look similar to DVD subtitles,
but the run-length encoding (RLE) and command format are different.

---

### Test Checkasm Ext Docs Benchmarking (`test/checkasm/ext/docs/benchmarking.md`)

@page benchmarking Benchmarking

This guide explains how to use checkasm's benchmarking capabilities to accurately measure
and compare the performance of optimized assembly implementations against reference code.

@tableofcontents

@section bench_basic Basics

@subsection bench_workflow Benchmark Workflow

Benchmarking in checkasm follows the same structure as correctness testing, with
an additional call to checkasm_bench_new():

@code{.c}
BUF_RECT(uint8_t, src,   64, 64);
BUF_RECT(uint8_t, dst_c, 64, 64);
BUF_RECT(uint8_t, dst_a, 64, 64);

checkasm_declare(void, uint8_t *dst, ptrdiff_t dst_stride,
                       const uint8_t *src, ptrdiff_t src_stride,
                       int w, int h);

if (checkasm_check_func(dsp->filter, "filter_64x64")) {
    INITIALIZE_BUF_RECT(src);
    CLEAR_BUF_RECT(dst_c);
    CLEAR_BUF_RECT(dst_a);

    // Correctness testing
    checkasm_call_ref(dst_c, dst_c_stride, src, src_stride, 64, 64);
    checkasm_call_new(dst_a, dst_a_stride, src, src_stride, 64, 64);
    checkasm_check_rect_padded(dst_c, dst_c_stride,
                               dst_a, dst_a_stride, 64, 64, "dst");

    // Benchmarking
    checkasm_bench_new(checkasm_alternate(dst_a, dst_c), dst_a_stride,
                       src, src_stride, 64, 64);
}

checkasm_report("filter");
@endcode

@subsection bench_cli Running Benchmarks

Enable benchmarking with the `--bench` flag:

@code{.bash}
# Quick benchmark (uses default duration)
./checkasm --bench

# Longer benchmark for more accurate results (10ms per function)
./checkasm --bench --duration=10000

# Benchmark specific functions only
./checkasm --bench --function='filter_*'

# Verbose output showing all timing measurements
./checkasm --bench --verbose
@endcode

The `--duration` parameter controls how long (in microseconds) each function is
benchmarked. Longer durations provide more accurate results but take more time.
The default is typically sufficient for most cases.

@subsection bench_export Exporting Results

checkasm can export benchmark results in multiple formats:

@code{.bash}
# CSV format (suitable for spreadsheets)
./checkasm --bench --csv > results.csv           # without column headers
./checkasm --bench --csv --verbose > results.csv # with column headers

# JSON format (for programmatic analysis, includes all data)
./checkasm --bench --json > results.json

# HTML format (interactive visualizations)
./checkasm --bench --html > results.html
@endcode

The JSON output format includes all measurement data and detailed statistical
parameters, including kernel density estimates, regression parameters, and confidence
intervals. The HTML output displays this same data in the form of interactive charts.

@section bench_methodology Statistical Methodology

@subsection bench_lognormal Log-Normal Distribution Modeling

checkasm models execution time as a log-normal distribution, which is well-suited
for performance measurements because:

1. Execution time is always positive
2. Performance variations tend to be multiplicative (e.g. power states) rather than additive
3. Outliers (e.g., from cache misses or interrupts) naturally fall into the long tail

The statistical estimator tracks two parameters:
- **Log mean** (μ): the logarithm of the median execution time
- **Log variance** (σ²): the variance of log(execution time)

From these, checkasm computes:
- **Mode**: most likely execution time = exp(μ - σ²)
- **Median**: middle execution time = exp(μ)
- **Mean**: average execution time = exp(μ + σ²/2)
- **Standard deviation**: sqrt(exp(2μ + σ²) × (exp(σ²) - 1))
- **Upper/Lower 95% confidence intervals**: exp(μ ± 1.96 × σ)

@subsection bench_regression Linear Regression

checkasm performs linear regression in log-space on the relationship between
iteration count and total execution time:

@code{.plaintext}
log(per_call_time) = log(total_time) - log(iterations)
@endcode

This approach:
- Automatically handles the multiplicative nature of timing variations
- Provides robust outlier rejection through regression residuals
- Separates per-call time from measurement overhead
- Computes confidence intervals for the estimates

@subsection bench_geometric Geometric Mean for Multiple Runs

When checkasm_bench_new() is called multiple times for the same function
(e.g., testing different block sizes), the final reported value is the
**geometric mean** of all measurements:

@code{.plaintext}
geometric_mean = (x₁ × x₂ × ... × xₙ)^(1/n)
@endcode

The geometric mean is appropriate for performance measurements because it:
- Is not skewed by outliers as heavily as arithmetic mean
- Properly handles ratios and speedups across multiple orders of magnitude
- Provides a representative "typical" performance across configurations

@subsection bench_overhead Overhead Correction

checkasm measures and subtracts the overhead of:
1. The benchmarking loop itself
2. The function call mechanism
3. The timer read operation

This is done by measuring a no-op function and subtracting its measured time
from all benchmark results. The no-op overhead is reported at startup:

@code{.plaintext}
 - No-op overhead: 2.41 +/- 0.093 cycles per call (provisional)
@endcode

For accuracy, this is re-estimated periodically throughout the benchmarking
process to account for any drift. The final value is reported again at the end
if `--verbose` is enabled.

@section bench_best_practices Best Practices

@subsection bp_system_state System State

Despite all of the statistical techniques employed by checkasm to combat
short- and medium-term noise, there is an unavoidable dependence on long-term
changes in system state. For reliable benchmarking, consider:

**Power Management:**
- Disable CPU frequency scaling if possible:
  @code{.bash}
  # Linux: set CPU governor to performance mode
  sudo cpupower frequency-set --governor performance

  # Or for all CPUs
  for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
      echo performance | sudo tee $cpu
  done
  @endcode

- Disable turbo boost for consistent results:
  @code{.bash}
  # Intel
  echo 1 | sudo tee /sys/devices/system/cpu/intel_pstate/no_turbo

  # AMD
  echo 0 | sudo tee /sys/devices/system/cpu/cpufreq/boost
  @endcode

**System Load:**
- Close unnecessary applications
- Avoid running benchmarks on heavily loaded systems
- Consider using `nice` to prioritize the benchmark process:
  @code{.bash}
  nice -n -20 ./checkasm --bench
  @endcode

**Thermal Throttling:**
- Ensure adequate cooling to prevent thermal throttling
- Allow sufficient cool-down time between benchmark runs
- Monitor CPU temperature during long benchmark sessions

@subsection bp_alignment Cache Alignment

checkasm automatically warms up caches before taking measurements, but you
should be aware of cache effects:

- **L1/L2 Cache**: Most optimized functions should fit in L2 cache
- **Data Working Set**: Buffers allocated with BUF_RECT() are properly aligned
  and sized to avoid cache conflicts. Failure to do so may lead to performance
  hits from unaligned memory accesses or cache thrashing.

For functions with large working sets that don't fit in cache, benchmark results
may reflect cache miss behavior, which is often realistic for real-world usage.

@subsection bp_alternating Buffer Alternation

Use checkasm_alternate() when benchmarking to prevent cache pollution:

@code{.c}
// Good: alternates between dst_a and dst_c to prevent cache hits from
// previous iterations
checkasm_bench_new(checkasm_alternate(dst_a, dst_c), dst_a_stride,
                   src, src_stride, w, h);

// Acceptable: always writes to dst_a
checkasm_bench_new(dst_a, dst_a_stride, src, src_stride, w, h);
@endcode

Alternating buffers ensures that benchmarks are not stalled by previous
access to the same data buffer from the prior loop iteration.

@subsection bp_realistic Realistic Test Data

Use realistic input data for benchmarks:

@code{.c}
// For general data processing: use INITIALIZE_BUF() which includes common edge cases
INITIALIZE_BUF_RECT(src);

// For specific patterns: use domain-appropriate data
checkasm_randomize_normf(audio_buf, len);  // Audio: normal distribution

// For worst-case analysis: test pathological inputs
generate_worst_case_pattern(buf, len);
@endcode

The input data can significantly affect performance due to:
- Data-dependent branches in the implementation
- SIMD instruction efficiency varying with data patterns
- Cache behavior depending on data values

If your function's performance varies significantly with the input data or
configuration, consider looping over all such configurations and running
checkasm_bench_new() for each, to measure an overall average.

@subsection bp_configurations Choosing Configurations

When benchmarking functions that support multiple sizes or configurations:

**Option 1: Benchmark all configurations**
@code{.c}
for (int w = 4; w <= 128; w <<= 1) {
    if (checkasm_check_func(dsp->filter, "filter_w%d", w)) {
        for (int h = 4; h <= 128; h <<= 1) {
            // Test for correctness
            // ...

            // Benchmark each configuration
            checkasm_bench_new(dst, dst_stride, src, src_stride, w, h);
        }
        // Reports geometric mean of all configurations
    }
}
@endcode

**Option 2: Benchmark representative sizes separately**
@code{.c}
const int sizes[][2] = { {16, 16}, {16, 32}, {64, 16}, {64, 32} };

for (int i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++) {
    int w = sizes[i][0], h = sizes[i][1];

    // Separate check_func call = separate benchmark report
    if (checkasm_check_func(dsp->filter, "filter_%dx%d", w, h)) {
        // Test for correctness
        // ...

        checkasm_bench_new(dst, dst_stride, src, src_stride, w, h);
    }
}
@endcode

**Option 3: Benchmark only the limiting case**
@code{.c}
for (int w = 4; w <= 128; w <<= 1) {
    if (checkasm_check_func(dsp->filter, "filter_w%d", w)) {
        for (int h = 4; h <= 128; h <<= 1) {
            // Test all for correctness
            // ...
        }

        // Benchmark only the largest size
        checkasm_bench_new(dst, dst_stride, src, src_stride, w, 128);
    }
}
@endcode

Choose based on your needs:
- Option 1: General performance across all sizes
- Option 2: Specific performance for important sizes
- Option 3: Best-case or worst-case performance

@section bench_interpreting Interpreting Results

@subsection interp_output Understanding Output

checkasm's benchmark output provides several pieces of information. Pass
`--verbose` to see all timing measurements.

@code{.plaintext}
Benchmark results:
  name                         cycles +/- stddev         time (nanoseconds) (vs ref)
  nop:                            2.4 +/- 0.0             1.2 ns +/- 0.1
  filter_c:                      64.9 +/- 75.4           33.8 ns +/- 40.8
  filter_sse2:                   46.9 +/- 13.3           24.4 ns +/- 7.8    ( 1.22x)
  filter_avx2:                   20.6 +/- 1.2            10.7 ns +/- 1.4    ( 3.15x)
@endcode

**Columns:**
- **name**: Function name (with ISA suffix for optimized versions)
- **cycles**: Estimated CPU cycles per call (mean ± standard deviation)
- **time**: Estimated nanoseconds per call (mean ± standard deviation)
- **(vs ref)**: Speedup relative to the reference (C) implementation

**What to look for:**
- Lower cycle counts indicate better performance
- Standard deviation shows measurement reliability
- Speedup factors show optimization effectiveness

@subsection interp_variance High Variance

High standard deviation (large ± values) can indicate:

1. **System noise**: Background processes, interrupts, frequency scaling
   - Solution: Follow best practices in @ref bp_system_state

2. **Data-dependent performance**: Function runs faster/slower on different inputs
   - This may be legitimate behavior (e.g., early exit conditions)
   - Consider whether benchmark input is representative

3. **Cache effects**: Function doesn't fit in cache or has cache conflicts
   - May be realistic for large working sets
   - Ensure buffers are properly aligned

The benchmark summary reports average timing error:
@code{.plaintext}
 - average timing error: 17.371% across 15 benchmarks (maximum 65.404%)
@endcode

High maximum error typically indicates at least one very noisy measurement.

@subsection interp_comparison Comparing Implementations

When comparing optimized implementations:

**Absolute speedup:**
@code{.plaintext}
filter_avx2: 20.6 cycles    ( 3.15x)
@endcode
This implementation is 3.15× faster than the C reference.

**Relative comparison:**
@code{.plaintext}
filter_sse2: 46.9 cycles    ( 1.22x)
filter_avx2: 20.6 cycles    ( 3.15x)
@endcode
AVX2 is 46.9 / 20.6 = 2.28× faster than SSE2.

@subsection interp_regression Regression Detection

Use benchmark results to detect performance regressions:

1. **Baseline measurements**: Save benchmark results for your codebase:
   @code{.bash}
   ./checkasm --bench --json > baseline.json
   @endcode

2. **After changes**: Run benchmarks again:
   @code{.bash}
   ./checkasm --bench --json > current.json
   @endcode

3. **Compare**: Look for functions that got slower
   - Small variations (< 5%) are typically noise
   - Changes > 10% warrant investigation
   - Changes > 20% are likely real regressions or improvements

@section bench_advanced Advanced Topics

@subsection adv_microbench Microbenchmarking Pitfalls

Be aware of common microbenchmarking issues:

- **Dead Code Elimination:** If the optimized function's results aren't used, the compiler might optimize
  it away, especially when compiling with link time optimization. This would
  usually be seen as unrealistically low cycle counts.
- **Constant Folding:** Always use INITIALIZE_BUF() or RANDOMIZE_BUF() to ensure inputs aren't
  compile-time constants that could be folded away.
- **Branch Prediction:** Running the same code path repeatedly (as benchmarks do) leads to perfect
  branch prediction, which may not reflect real-world performance that involve
  mixed function calls (e.g. varying block sizes). This is generally acceptable
  since you're comparing implementations under the same conditions, but may hide
  performance gains from e.g. branchless implementations.
- **Memory Hierarchy:** Benchmarks often measure L1/L2 cache performance, not DRAM performance.
  For functions with large working sets, real-world performance may be lower
  than benchmarks suggest, which puts a bound on the realistically achievable
  speedup from SIMD optimizations.

@subsection adv_platform Platform Considerations

@subsubsection adv_timer Timer Resolution
checkasm reports timer resolution at startup:
@code{.plaintext}
 - Timing source: x86 (rdtsc)
 - Timing resolution: 0.5976 +/- 0.057 ns/cycle (1644 +/- 156.8 MHz) (provisional)
@endcode

- x86/x86_64: rdtsc (cycle counter) - very high resolution
- ARM/AArch64: pmccntr (cycle counter) - high resolution
- LoongArch: rdtime (tick counter) - high resolution
- PowerPC 64le: mfspr (tick counter) - medium resolution
- Other/Fallback: OS-provided timers - lower resolution

Lower resolution timers may require longer `--duration` for accurate results.

@subsubsection adv_freq_scaling Frequency Scaling
The timer resolution includes clock frequency estimation. If CPU frequency
scaling is enabled, this estimate may be inaccurate. However, this affects
only the conversion to nanoseconds, not cycle counts. For most accurate results,
disable frequency scaling, or compare only raw cycle counts (for platforms with
access to high-resolution cycle counters).

@subsubsection adv_cross_platform Cross-Platform Comparison
Comparing cycle counts across different CPUs is meaningful when:
- Both CPUs are from the same architecture family
- Both run at similar clock speeds
- You account for microarchitectural differences

For cross-platform comparison, use relative speedup (optimized vs C) rather
than absolute cycle counts.

@subsection adv_html HTML Report Overview

The HTML report provides detailed statistical visualizations:

@subsubsection adv_kde_regression Kernel Density Estimate (left chart)
- Shows the probability distribution of execution times
- Peak indicates most likely execution time (mode)
- Wider distribution = higher variance
- Derived from log-normal distribution fit

@subsubsection adv_raw_measurements Raw Measurements (right chart)
- X-axis: iteration count (how many times function was called in one measurement)
- Y-axis: total time for all iterations in one measurement
- Line: linear regression fit
- Shaded area: 95% confidence interval
- Points far from line: potential outliers

@subsubsection adv_metrics Metrics Table
- **Adjusted cycles/time**: After overhead subtraction (use this for comparisons)
- **Raw cycles/time**: Before overhead subtraction (may be more reliable for sub-10-cycle functions)

@section bench_tips Tips and Tricks

@subsection tips_reproducible Reproducible Benchmarks

For reproducible results:

1. **Use fixed random seed**:
   @code{.bash}
   ./checkasm --bench 12345  # Use seed 12345
   @endcode
   This ensures the same test data patterns across runs.

2. **Document system state**:
   - CPU model and frequency settings
   - Compiler version and flags
   - Operating system and kernel version
   - checkasm version/commit

3. **Multiple runs**:
   Run benchmarks multiple times and verify consistency:
   @code{.bash}
   for i in {1..5}; do
       ./checkasm --bench --function='filter_*' | tee run_$i.txt
   done
   @endcode

---

### Test Checkasm Ext Docs Integration (`test/checkasm/ext/docs/integration.md`)

@page integration Integration Guide

This guide covers how to integrate checkasm into existing or new projects.

@tableofcontents

@section config_options Configuration Options

Checkasm uses optional C11 features inside public header files. For safety, these are only enabled
conservatively based on the C11 standard version signaled by the compiler. Sometimes, these checks
could be relaxed, such as when the target project is explicitly compiled with `-std=c99` or older,
but using a modern compiler that would still understand and accept C11 features. In this case,
these feature checks may be checked by the user and defined before including checkasm.h. See
@ref config for a list of such options.

@section cpu_flags CPU Flags

CPU flags represent instruction set extensions and features that your optimized implementations
depend on (e.g., SSE2, AVX2, NEON). You must define an array of these flags, so checkasm can
systematically test each implementation variant.

@note checkasm does not provide CPU detection or runtime dispatch functionality on its own.
It is a pure testing framework, and as such, should not be used as a runtime dependency of your
project. This means that your project must implement its own CPU feature detection and dispatch
mechanisms for production use. checkasm plugs into these existing mechanisms during testing.

@subsection defining_cpu_flags Defining CPU Flags

Assuming you have a set of CPU flags defined in your project, e.g.,

@code{.h}
// my_cpu.h

enum {
    CPU_FLAG_SSE2    = 1 << 0,
    CPU_FLAG_SSSE3   = 1 << 1,
    CPU_FLAG_SSE41   = 1 << 2,
    CPU_FLAG_AVX2    = 1 << 3,
    CPU_FLAG_AVX512  = 1 << 4,
    // ...
};

typedef uint64_t MyCpuFlags;
MyCpuFlags detect_cpu_flags(void);
@endcode

Then create a CheckasmCpuInfo array describing each flag, terminated by `{0}`:

@code{.c}
// checkasm.c

static const CheckasmCpuInfo cpu_flags[] = {
    { "SSE2",   "sse2",   CPU_FLAG_SSE2   },
    { "SSSE3",  "ssse3",  CPU_FLAG_SSSE3  },
    { "SSE4.1", "sse41",  CPU_FLAG_SSE41  },
    { "AVX2",   "avx2",   CPU_FLAG_AVX2   },
    { "AVX512", "avx512", CPU_FLAG_AVX512 },
    {0} // array terminator
};

// This ordering means:
// - SSE2 functions are tested with just CPU_FLAG_SSE2
// - SSSE3 functions are tested with CPU_FLAG_SSE2 | CPU_FLAG_SSSE3
// - SSE4.1 functions are tested with CPU_FLAG_SSE2 | CPU_FLAG_SSSE3 | CPU_FLAG_SSE41
// - And so on...
@endcode

Each entry contains:
- **name**: Human-readable name displayed in output (e.g., "SSE4.1")
- **suffix**: Short suffix used in function names and filtering (e.g., "sse41")
- **flag**: The bitfield value from your CPU flag enum

@note Flags are tested in the order defined in the array. Each test inherits
flags from all previous entries, allowing checkasm to test progressively more
advanced instruction sets.

Register the CPU flags with checkasm via the CheckasmConfig structure:

@code{.c}
// checkasm.c

static const CheckasmCpuInfo cpu_flags[] = {
    // ...
    {0}
};

int main(int argc, const char *argv[])
{
    CheckasmConfig config = {
        .cpu_flags = cpu_flags,
        // ...
    };

    // Set initial CPU flags using your own runtime detection function
    config.cpu = detect_cpu_flags();

    return checkasm_main(&config, argc, argv);
}
@endcode

@subsection extra_cpu_flags Extra CPU Flags

You can include additional flags in CheckasmConfig.cpu that aren't in CheckasmConfig.cpu_flags.
These are transparently passed through to checkasm_get_cpu_flags() and can be used for
modifier flags like `CPU_FLAG_FAST_*` that don't require separate testing, but should
instead always be assumed to be available when matching function implementations.

@section selecting_functions Selecting Functions

There are two common strategies for selecting the correct function implementation during tests,
depending on how your project structures its dispatch mechanism:

@note Choose the strategy that matches your project's existing architecture. If you are
developing a new library, we recommend the first approach.

@subsection mask_callback Strategy 1: Mask Callback

If your project has a `mask_cpu_flags` (or `cpu_flags_override`) function that
updates an internal static bitmask used internally by dispatch table getters, e.g.:

@code{.c}
// my_cpu.c
static unsigned cpu_flags      = 0;
static unsigned cpu_flags_mask = -1;

unsigned get_cpu_flags(void)
{
    return cpu_flags & cpu_flags_mask;
}

void mask_cpu_flags(unsigned flags)
{
    cpu_flags_mask = flags;
}
@endcode

@code{.c}
// my_foo_dsp.c
void foo_dsp_init(foo_dsp *dsp)
{
    const unsigned cpu_flags = get_cpu_flags();

    // Initialize with C implementations
    dsp->add = add_c;
    dsp->sub = sub_c;

    // Override with optimized versions based on cpu_flags
    if (cpu_flags & CPU_FLAG_SSE2) {
        dsp->add = add_sse2;
    }

    if (cpu_flags & CPU_FLAG_AVX2) {
        dsp->add = add_avx2;
        dsp->sub = sub_avx2;
    }

    // ...
}
@endcode

Then, in your checkasm main file, you can set that directly as a callback in CheckasmConfig.set_cpu_flags:

@code{.c}
// You may need a wrapper to fix the function signature
static void set_cpu_flags(CheckasmCpu cpu)
{
    mask_cpu_flags((unsigned) cpu);
}

CheckasmConfig config = {
    // ...
    .set_cpu_flags = set_cpu_flags,
};
@endcode

With this approach, checkasm will automatically call your `set_cpu_flags()` function whenever
it changes the active CPU feature set during testing.

@note This will always be a subset of the initially detected CPU flags provided in CheckasmConfig.cpu,
so there is no meaningful distinction between a `mask_cpu_flags` (that masks out real CPU
flags) and a `cpu_flags_override` (that overrides them wholesale). Both can be used
as a callback.

@subsection direct_getters Strategy 2: Direct Getters

If your project uses dispatch functions that directly accept a CPU mask parameter (e.g.,
`void foo_dsp_init(foo_dsp *dsp, unsigned cpu_flags)`), you can call them within
each test using checkasm_get_cpu_flags():

@code{.c}
// my_foo_dsp.c
void foo_dsp_init(foo_dsp *dsp, unsigned cpu_flags)
{
    // Initialize *dsp based on the provided CPU flags
}
@endcode

Then, in your checkasm test files:

@code{.c}
// check_foo_dsp.c
void check_foo_dsp(void)
{
    foo_dsp dsp;
    foo_dsp_init(&dsp, checkasm_get_cpu_flags());  // Get current test flags

    // Now test dsp.add, dsp.sub, etc.
    // ...
}
@endcode

The same applies if your project uses individual function getters like
`add_func_t get_add_func(unsigned cpu_flags)` instead of dispatch tables / dsp structs.

@section organizing_tests Organizing Multiple Tests

For larger projects, organize tests by module:

@code{.c}
// Test module declarations
void checkasm_check_mc(void);
void checkasm_check_pixel(void);
void checkasm_check_filmgrain(void);
// ...

static const CheckasmTest tests[] = {
    { "mc",        checkasm_check_mc },
    { "pixel",     checkasm_check_pixel },
    { "filmgrain", checkasm_check_filmgrain },
    // ...
    {0}
};
@endcode

Then implement each test in separate files:

@code{.c}
// check_mc.c
#include <checkasm/test.h>
#include "mc_dsp.h"

static void test_mc_func1(const mc_dsp *dsp);
static void test_mc_func2(const mc_dsp *dsp);
static void test_mc_func3(const mc_dsp *dsp);
// ...

void checkasm_check_mc(void)
{
    mc_dsp dsp;
    mc_dsp_init(&dsp, checkasm_get_cpu_flags());

    test_func1(&dsp);
    test_func2(&dsp);
    test_func3(&dsp);
    checkasm_report("group1");

    test_func4(&dsp);
    test_func5(&dsp);
    test_func6(&dsp);
    checkasm_report("group2");

    // ...
}
@endcode

---

@section integration_next_steps Next Steps

Now that you know how to integrate checkasm with your project's architecture,
dive deeper into best practices and advanced patterns for writing comprehensive
tests.

**Next:** @ref writing_tests

---

### Test Checkasm Ext Docs Introduction (`test/checkasm/ext/docs/introduction.md`)

@mainpage Introduction

**checkasm** is a robust, portable testing and benchmarking framework specifically designed for
validating optimized assembly implementations against reference C code. Originally developed for the
x264 encoder project, checkasm has grown considerably while in use by FFmpeg and dav1d,
and now provides comprehensive correctness verification, crash detection,
and accurate performance measurements across multiple architectures.

This project in particular stems from an effort to combine and improve upon multiple diverging
checkasm forks in the wild (x264, FFmpeg, dav1d, etc.). It is a direct descendent of the dav1d
variant, with all relevant enhancements from the FFmpeg fork merged in. It has since evolved
into a fully fledged cross-platform standalone library and benchmarking framework.

@tableofcontents

@section features Key Features

@subsection features_correctness Correctness Verification
- **Automated comparison** between reference (C) and optimized (assembly) implementations, including
  support for fuzzing implementations over multiple seeds and data patterns.
- **Detection of ABI violations** such as register misuse, illegal opcodes,
  or missing state cleanup.
- **Extensive data handling utilities** for defining, initializing, and comparing
  input/output buffers of various types, sizes and dimensionalities.
- **Crash resilience** including signal handling and stack smashing detection.

@subsection features_benchmarking Performance Benchmarking
- **Accurate cycle counting** using platform-specific high-resolution timers.
- **Statistical analysis** with robust outlier rejection, logarithmic regression and confidence intervals.
- **Comparative measurements** showing speedup of optimized code including comprehensive HTML reports.
- **Minimal overhead** to ensure realistic performance data, and high throughput.

@subsection features_platform Platform Support

The following architectures are explicitly supported for asm verification, with
the listed detection mechanisms for typical asm mistakes:

- **x86, x86-64**: registers, stack, AVX2, MMX/FPU state
- **ARM, ARM64 (aarch64)**: registers, stack, VFP state
- **RISC-V**: registers, stack, vector state
- **LoongArch (32, 64)**: registers
- **PowerPC (64le)**: *none*

In addition, hardware timers are available for benchmarking purposes on all of
the listed platforms (except RISCV-V), with fall-backs to generic OS-specific APIs.

@section links Project Links

- [**GitLab Repository**](https://code.videolan.org/videolan/checkasm)
- [**Issue Tracker**](https://code.videolan.org/videolan/checkasm/issues)
- [VideoLAN Homepage](https://www.videolan.org/)

@subsection intro_next_steps Next Steps

Continue learning about checkasm through the following guides:

- @ref getting_started for a quick start guide
- @ref integration for a guide on how to integrate checkasm into your project
- @ref writing_tests for an in-depth explanation of writing tests
- @ref benchmarking for tips and tricks on benchmarking with checkasm

Or explore the full API reference:

- @ref checkasm.h -- @copybrief checkasm.h
- @ref test.h -- @copybrief test.h
- @ref utils.h -- @copybrief utils.h

@section example_outputs Example Outputs

@subsection example_benchmark Benchmark Output

This is what the output looks like, when using `--verbose` mode to print all
timing data (on a pretty noisy/busy system):

@code{.plaintext}
checkasm:
 - CPU: Intel(R) Core(TM) Ultra 7 258V (000B06D1)
 - Timing source: x86 (rdtsc)
 - Timing resolution: 0.5976 +/- 0.057 ns/cycle (1644 +/- 156.8 MHz) (provisional)
 - No-op overhead: 2.41 +/- 0.093 cycles per call (provisional)
 - Bench duration: 10000 µs per function (18221161 cycles)
 - Random seed: 3203178780
C:
 - msac.decode_symbol [OK]
 - msac.decode_bool   [OK]
 - msac.decode_hi_tok [OK]
SSE2:
 - msac.decode_symbol [OK]
 - msac.decode_bool   [OK]
 - msac.decode_hi_tok [OK]
AVX2:
 - msac.decode_symbol [OK]
checkasm: all 15 tests passed
Benchmark results:
  name                               cycles +/- stddev         time (nanoseconds) (vs ref)
  nop:                                  2.4 +/- 0.0             1.2 ns +/- 0.1
  msac_decode_bool_c:                   6.3 +/- 0.2             3.3 ns +/- 0.4
  msac_decode_bool_sse2:                5.7 +/- 0.2             3.0 ns +/- 0.4    ( 1.10x)
  msac_decode_bool_adapt_c:             5.9 +/- 0.6             3.1 ns +/- 0.5
  msac_decode_bool_adapt_sse2:          6.3 +/- 0.2             3.3 ns +/- 0.4    ( 0.94x)
  msac_decode_bool_equi_c:              5.1 +/- 0.2             2.6 ns +/- 0.3
  msac_decode_bool_equi_sse2:           2.8 +/- 0.1             1.4 ns +/- 0.2    ( 1.84x)
  msac_decode_hi_tok_c:                64.9 +/- 75.4           33.8 ns +/- 40.8
  msac_decode_hi_tok_sse2:             46.9 +/- 13.3           24.4 ns +/- 7.8    ( 1.22x)
  msac_decode_symbol_adapt4_c:         12.6 +/- 0.7             6.5 ns +/- 0.9
  msac_decode_symbol_adapt4_sse2:      12.3 +/- 0.8             6.4 ns +/- 0.9    ( 1.02x)
  msac_decode_symbol_adapt8_c:         20.5 +/- 2.3            10.7 ns +/- 1.8
  msac_decode_symbol_adapt8_sse2:      12.1 +/- 0.8             6.3 ns +/- 0.8    ( 1.68x)
  msac_decode_symbol_adapt16_c:        33.6 +/- 1.2            17.5 ns +/- 2.1
  msac_decode_symbol_adapt16_sse2:     13.8 +/- 0.6             7.2 ns +/- 0.9    ( 2.42x)
  msac_decode_symbol_adapt16_avx2:     20.6 +/- 1.2            10.7 ns +/- 1.4    ( 1.62x)
 - average timing error: 17.371% across 15 benchmarks (maximum 65.404%)
@endcode

@subsection example_errors Error Reporting

This is what various failures could look like:

@code{.plaintext}
checkasm:
 - CPU: AMD Ryzen 9 9950X3D 16-Core Processor (00B40F40)
 - Random seed: 3689286425
x86:
 - x86.copy                [OK]
FAILURE: sigill_x86 (illegal instruction)
 - x86.sigill              [FAILED]
FAILURE: corrupt_stack_x86 (stack corruption)
 - x86.corrupt_stack       [FAILED]
FAILURE: clobber_r9_x86 (failed to preserve register: r9)
FAILURE: clobber_r10_x86 (failed to preserve register: r10)
FAILURE: clobber_r11_x86 (failed to preserve register: r11)
FAILURE: clobber_r12_x86 (failed to preserve register: r12)
FAILURE: clobber_r13_x86 (failed to preserve register: r13)
FAILURE: clobber_r14_x86 (failed to preserve register: r14)
 - x86.clobber             [FAILED]
FAILURE: underwrite_64_x86 (../tests/generic.c:56)
dst data (64x1):
  0:  45 b3 dc aa 68 90 a4 5d cf d9 d9 9b d0 34     45 b3 dc aa 68 90 a4 5d cf d9 d9 9b d0 34    ..............
      1b 88 37 0f 14 f1 77 f4 94 bf 53 a3 21 3f     1b 88 37 0f 14 f1 77 f4 94 bf 53 a3 21 3f    ..............
      b5 64 77 fc 75 6b a9 2a 38 8a c7 e8 f9 1d     b5 64 77 fc 75 6b a9 2a 38 8a c7 e8 f9 1d    ..............
      b4 80 d6 f4 34 1d c6 2b fd a8 e5 83 51 bb     b4 80 d6 f4 34 1d c6 2b fd a8 e5 83 51 bb    ..............
      72 f6 e5 c1 47 80 63 f2                       72 f6 e5 c1 aa aa aa aa                      ....xxxx
 - generic.underwrite      [FAILED]
@endcode

@section license License

checkasm is licensed under the **2-clause BSD license**. See the LICENSE file in the repository for full details.

---

### Test Checkasm Ext Docs Writing Tests (`test/checkasm/ext/docs/writing-tests.md`)

@page writing_tests Writing Tests

This guide covers best practices and common patterns for writing checkasm tests.

@tableofcontents

@section test_structure Basic Test Structure

Before diving into advanced patterns and best practices, familiarize yourself
with the basic test structure by reading the @ref quick_start.

The typical test workflow is:
1. Allocate (aligned) buffers for test data
2. Declare the function signature with checkasm_declare()
3. Check if the function should be tested with checkasm_check_func()
4. Initialize test inputs and clear output buffers
5. Call both reference (checkasm_call_ref()) and new (checkasm_call_new()) implementations
6. Compare results with checkasm_check2d() or similar
7. Benchmark the new implementation with checkasm_bench_new()
8. Report results with checkasm_report() (optional)

@section naming_conventions API Naming Conventions

checkasm supports two API naming styles:

@subsection modern_api Modern API (Recommended)

The modern API uses the `checkasm_` prefix for all functions:

@code{.c}
checkasm_declare(void, uint8_t *dst, const uint8_t *src, int len);
checkasm_check_func(dsp->func, "func_name")
checkasm_call_ref(dst_c, src, len);
checkasm_call_new(dst_a, src, len);
checkasm_check1d(uint8_t, dst_c, dst_a, len, "dst");
checkasm_bench_new(dst_a, src, len);
checkasm_report("func_name");
checkasm_fail()
checkasm_alternate(buf0, buf1)
// ...
@endcode

@subsection legacy_api Legacy/Short API

For convenience and backwards compatibility, shorter aliases are available:

@code{.c}
declare_func(void, uint8_t *dst, const uint8_t *src, int len);
check_func(dsp->func, "func_name")
call_ref(dst_c, src, len);
call_new(dst_a, src, len);
// checkasm_check1d() has no short alias
bench_new(dst_a, src, len);
report("func_name");
fail()
alternate(buf0, buf1)
@endcode

@note Both naming styles are fully supported and can be mixed within the same
test file. However, for consistency and readability in documentation and new
code, we recommend using the modern `checkasm_` prefixed names.

@section best_practices Best Practices

The remainder of this guide focuses on best practices, common patterns, and advanced testing scenarios.

@subsection bp_buffer_allocation Buffer Allocation

Always use properly aligned buffers for testing:

@code{.c}
// For simple arrays
CHECKASM_ALIGN(uint8_t buf[1024]);

// For 2D buffers with automatic padding and stride calculation
// - Defines `dst` as a pointer to a 64x32 area, and `dst_stride` (in bytes)
BUF_RECT(uint8_t, dst, 64, 32);
@endcode

**Important:** Apply CHECKASM_ALIGN() to each buffer individually:

@code{.c}
// Correct
CHECKASM_ALIGN(uint8_t buf1[32]);
CHECKASM_ALIGN(uint8_t buf2[32]);

// Wrong - only buf1 will be aligned
CHECKASM_ALIGN(uint8_t buf1[32], buf2[32]);
@endcode

@subsection bp_buffer_init Buffer Initialization

checkasm provides several buffer initialization functions:

@code{.c}
// Random data (uniformly distributed)
RANDOMIZE_BUF(buf);
checkasm_randomize(buf, sizeof(buf)); // Equivalent to the above macro
checkasm_randomize_mask8 (buf8,  width, 0x0F); // Constrain value range
checkasm_randomize_mask16(buf16, width, 1023); // Random 10-bit values

// Pathological test patterns (mix of edge cases and random bytes)
INITIALIZE_BUF(buf);
checkasm_init(buf, sizeof(buf)); // Equivalent to the above macro
checkasm_init_mask16(buf16, width, (1 << 12) - 1); // Constrain to 12-bit range

// Clear to a constant value
CLEAR_BUF(buf);
checkasm_clear(buf, sizeof(buf)); // Equivalent to the above macro
checkasm_clear16(buf16, width, 0x1234);
@endcode

For 2D buffers created with BUF_RECT():

@code{.c}
BUF_RECT(uint8_t, src, 64, 32);
INITIALIZE_BUF_RECT(src);  // Initialize with pathological test bytes
RANDOMIZE_BUF_RECT(tmp);   // Fill with random data
CLEAR_BUF_RECT(dst);       // Clear to constant data (currently 0xAA)
@endcode

See @ref memory for a full list of buffer initialization functions.

@subsection bp_test_loops Testing Multiple Configurations

Test functions across multiple configurations (e.g. block sizes) to ensure
comprehensive coverage:

@code{.c}
BUF_RECT(uint8_t, src,   128, 128);
BUF_RECT(uint8_t, dst_c, 128, 128);
BUF_RECT(uint8_t, dst_a, 128, 128);

checkasm_declare(void, uint8_t *dst, ptrdiff_t dst_stride,
                       const uint8_t *src, ptrdiff_t src_stride,
                       int w, int h);

// Test various power-of-two block sizes, from 4x4 to 128x128
for (int h = 4; h <= 128; h <<= 1) {
    for (int w = 4; w <= 128; w <<= 1) {
        if (checkasm_check_func(get_func(w), "func_%dx%d", w, h)) {
            // Initialize test data - doing this inside the loop picks a
            // different data pattern for each test
            INITIALIZE_BUF(src);
            CLEAR_BUF(dst_c);
            CLEAR_BUF(dst_a);

            // Test this configuration
            checkasm_call_ref(dst_c, dst_c_stride, src, src_stride, w, h);
            checkasm_call_new(dst_a, dst_a_stride, src, src_stride, w, h);

            checkasm_check_rect_padded(dst_c, dst_c_stride,
                                       dst_a, dst_a_stride, w, h, "dst");

            // Benchmark this configuration
            checkasm_bench_new(checkasm_alternate(dst_a, dst_c), dst_a_stride,
                               src, src_stride, w, h);
        }
    }
}

checkasm_report("func");
@endcode

@subsection bp_random_params Using Random Parameters

Use checkasm_rand() and related functions to generate diverse test inputs:

@code{.c}
// Random integers
int byte = checkasm_rand() & 0xFF;
uint32_t flags = checkasm_rand_uint32();

// Random floats
double d = checkasm_randf();  // [0.0, 1.0)
float f = (float) checkasm_randf() * 100.0f;  // [0.0, 100.0)

// Normal distribution
double normal = checkasm_rand_norm();  // mean=0, stddev=1
checkasm_randomize_normf(buf, len);    // Fill buffer with N(0,1)
@endcode

See @ref rng for a full list of random number generation functions.

@subsection bp_reporting Organizing Reports

Group related functions and use checkasm_report() strategically:

@code{.c}
static void check_add_functions(const DSPContext *dsp)
{
    // Test multiple related functions
    for (int bpc = 8; bpc <= 12; bpc += 2) {
        if (checkasm_check_func(get_add8(bpc), "add8_%dbpc", bpc)) {
            // Test add8
        }

        if (checkasm_check_func(get_add16(bpc), "add16_%dbpc", bpc)) {
            // Test add16
        }
    }

    // Report once for the whole group
    checkasm_report("add");
}
@endcode

For very simple tests with only a few functions, for which there is no
logical grouping, or for miscellaneous functions, this can be left out.
Any functions without a checkasm_report() call after them will be implicitly
reported under the name of the test itself. Note that this only makes sense
if such functions are the last functions being tested in a given test, since
any later checkasm_report() call would otherwise include all prior functions.

@section common_patterns Common Test Patterns

@subsection pattern_2d 2D Buffer Processing

For functions operating on 2D buffers with stride:

@code{.c}
static void check_filter(const DSPContext *dsp)
{
    // Define padded 64x64 buffers
    BUF_RECT(uint8_t, src,   64, 64);
    BUF_RECT(uint8_t, dst_c, 64, 64);
    BUF_RECT(uint8_t, dst_a, 64, 64);

    checkasm_declare(void, uint8_t *dst, ptrdiff_t dst_stride,
                     const uint8_t *src, ptrdiff_t src_stride,
                     int w, int h);

    for (int w = 4; w <= 64; w <<= 1) {
        if (checkasm_check_func(dsp->filter, "filter_w%d", w)) {
            // Check multiple heights
            for (int h = 4; h <= 64; h <<= 1) {
                INITIALIZE_BUF_RECT(src);
                CLEAR_BUF_RECT(dst_c);
                CLEAR_BUF_RECT(dst_a);

                checkasm_call_ref(dst_c, dst_c_stride, src, src_stride, w, h);
                checkasm_call_new(dst_a, dst_a_stride, src, src_stride, w, h);

                // Check buffers including padding
                checkasm_check_rect_padded(dst_c, dst_c_stride,
                                           dst_a, dst_a_stride,
                                           w, h, "dst");
            }

            // Benchmark only the full height configuration
            checkasm_bench_new(checkasm_alternate(dst_a, dst_c), dst_a_stride,
                               src, src_stride, w, 64);
        }
    }

    checkasm_report("filter");
}
@endcode

@subsection pattern_state State-Based Functions

For functions that modify internal state or have side effects:

@code{.c}
static void check_decoder(const DecoderContext *dec)
{
    DecoderState state_c, state_a;
    uint8_t bitstream[128];

    // Initialize bitstream with random data
    RANDOMIZE_BUF(bitstream);

    checkasm_declare(int, DecoderState *state, const uint8_t *data, int len);

    if (checkasm_check_func(dec->decode, "decode")) {
        // Initialize both states identically
        init_decoder_state(&state_c, bitstream, 128);
        init_decoder_state(&state_a, bitstream, 128);

        // Decode and compare
        int result_c = checkasm_call_ref(&state_c, bitstream, 128);
        int result_a = checkasm_call_new(&state_a, bitstream, 128);

        // Compare return values
        if (result_c != result_a) {
            if (checkasm_fail()) {
                fprintf(stderr, "return value mismatch: %d vs %d\n",
                        result_c, result_a);
            }
        }

        // Compare final states (optional)
        //
        // The validity of this check depends on whether or not `DecoderState`
        // has padding bytes or non-deterministic internal state.
        checkasm_check1d(uint8_t, &state_c, &state_a, sizeof(DecoderState),
                         "decoder state");

        checkasm_bench_new(&state_a, bitstream, 128);
    }

    checkasm_report("decode");
}
@endcode

@subsection pattern_multi_output Multiple Outputs

For functions that produce multiple output values:

@code{.c}
static void check_stats(const DSPContext *dsp)
{
    CHECKASM_ALIGN(uint8_t buf[64 * 64]);

    checkasm_declare(int, const uint8_t *buf, int len,
                     unsigned *variance, unsigned *sum);

    if (checkasm_check_func(dsp->compute_stats, "compute_stats")) {
        unsigned var_c, var_a, sum_c, sum_a;

        INITIALIZE_BUF(buf);

        int result_c = checkasm_call_ref(buf, 64*64, &var_c, &sum_c);
        int result_a = checkasm_call_new(buf, 64*64, &var_a, &sum_a);

        // Compare all outputs
        if (result_c != result_a || var_c != var_a || sum_c != sum_a) {
            if (checkasm_fail()) {
                fprintf(stderr, "result: %d vs %d, var: %u vs %u, sum: %u vs %u\n",
                        result_c, result_a, var_c, var_a, sum_c, sum_a);
            }
        }

        checkasm_bench_new(buf, 64*64, &var_a, &sum_a);
    }

    checkasm_report("compute_stats");
}
@endcode

@subsection pattern_custom_input Custom Input Generation

For functions requiring specific test patterns:

@code{.c}
// Generate worst-case inputs to stress the implementation
static void generate_worst_case(uint16_t *buf, int len, int bitdepth_max)
{
    // Create reverse sorted sequence of input values
    for (int i = 0; i < len; i++)
        buf[i] = (len - 1 - i) & bitdepth_max;
}

static void check_transform(const DSPContext *dsp)
{
    #define WIDTH 64
    CHECKASM_ALIGN(int16_t src  [WIDTH]);
    CHECKASM_ALIGN(int16_t dst_c[WIDTH]);
    CHECKASM_ALIGN(int16_t dst_a[WIDTH]);

    checkasm_declare(void, int16_t *dst, const int16_t *src, int width);

    if (checkasm_check_func(dsp->transform, "transform")) {
        // Test with both random and worst-case inputs
        for (int pattern = 0; pattern < 2; pattern++) {
            if (pattern == 0) {
                INITIALIZE_BUF(src); // Random input
            } else {
                generate_worst_case(src, WIDTH, 32767); // Worst case pattern
            }

            CLEAR_BUF(dst_c);
            CLEAR_BUF(dst_a);

            checkasm_call_ref(dst_c, src, WIDTH);
            checkasm_call_new(dst_a, src, WIDTH);

            checkasm_check1d(int16_t, dst_c, dst_a, WIDTH, "dst");
        }

        checkasm_bench_new(checkasm_alternate(dst_a, dst_c), src, WIDTH);
    }

    checkasm_report("transform");
}
@endcode

@section advanced_topics Advanced Topics

@subsection adv_float Floating-Point Comparison

For functions producing floating-point results, use tolerance-based comparison:

@code{.c}
static void check_float_func(const DSPContext *dsp)
{
    #define WIDTH 128
    CHECKASM_ALIGN(float src  [WIDTH]);
    CHECKASM_ALIGN(float dst_c[WIDTH]);
    CHECKASM_ALIGN(float dst_a[WIDTH]);

    checkasm_declare(void, float *dst, const float *src, int len);

    if (checkasm_check_func(dsp->process_float, "process_float")) {
        checkasm_randomize_normf(src, WIDTH);

        checkasm_call_ref(dst_c, src, WIDTH);
        checkasm_call_new(dst_a, src, WIDTH);

        // Compare with ULP (Units in Last Place) tolerance
        //   Note: cannot use checkasm_check1d() here
        const int max_ulp = 1;
        checkasm_check2d(float_ulp, dst_c, 0, dst_a, 0, WIDTH, 1, "dst", max_ulp);

        // Or use absolute epsilon tolerance
        // if (!checkasm_float_near_abs_eps_array(dst_c, dst_a, 1e-6f, WIDTH)) {
        //     checkasm_fail();
        // }

        checkasm_bench_new(checkasm_alternate(dst_a, dst_c), src, WIDTH);
    }

    checkasm_report("process_float");
}
@endcode

@subsection adv_padding Padding and Over-Write Detection

Detect when functions write beyond their intended boundaries:

@code{.c}
static void check_bounds(const DSPContext *dsp)
{
    BUF_RECT(uint8_t, dst_c, 64, 64);
    BUF_RECT(uint8_t, dst_a, 64, 64);

    checkasm_declare(void, uint8_t *dst, ptrdiff_t stride, int w, int h);

    if (checkasm_check_func(dsp->fill, "fill")) {
        const int w = 64, h = 64;

        CLEAR_BUF_RECT(dst_c);
        CLEAR_BUF_RECT(dst_a);

        checkasm_call_ref(dst_c, dst_c_stride, w, h);
        checkasm_call_new(dst_a, dst_a_stride, w, h);

        // Standard check (no padding)
        checkasm_check2d(uint8_t, dst_c, dst_c_stride,
                                  dst_a, dst_a_stride, w, h, "dst");

        // Check with padding detection (detects writes outside w×h)
        checkasm_check_rect_padded(dst_c, dst_c_stride,
                                   dst_a, dst_a_stride, w, h, "dst");

        // Allow over-write up to 16-element alignment on right edge
        checkasm_check_rect_padded_align(dst_c, dst_c_stride,
                                         dst_a, dst_a_stride,
                                         w, h, "dst", 16, 1);

        checkasm_bench_new(checkasm_alternate(dst_a, dst_c), dst_a_stride, w, h);
    }

    checkasm_report("fill");
}
@endcode

@subsection bench_multiple Benchmarking Multiple Configurations

For functions that can be benchmarked at multiple configurations:

@code{.c}
if (checkasm_check_func(dsp->filter, "filter_w%d", w)) {
    for (int h = 4; h <= 64; h <<= 1) {
        // Test all heights for correctness
        checkasm_call_ref(dst_c, dst_c_stride, src, src_stride, w, h);
        checkasm_call_new(dst_a, dst_a_stride, src, src_stride, w, h);
        checkasm_check2d(uint8_t, dst_c, dst_c_stride,
                                  dst_a, dst_a_stride, w, h, "dst");

        // Benchmark each configuration
        checkasm_bench_new(checkasm_alternate(dst_a, dst_c), dst_a_stride,
                           src, src_stride, w, h);
    }

    // The framework will report the geometric mean of all benchmark runs for
    // the same checkasm_check_func() call
}

checkasm_report("filter_w%d", w);
@endcode

Alternatively, you could call checkasm_check_func() on each configuration
to get a separate benchmark report for each size, or call checkasm_bench_new()
only on the largest input size to test the limiting behavior.

@subsection adv_bitdepth Multi-Bitdepth Testing

For codecs supporting multiple bit depths:

@code{.c}
static void check_pixfunc(void)
{
    DSPContext dsp;

    #define WIDTH 64
    CHECKASM_ALIGN(uint16_t src  [WIDTH]);
    CHECKASM_ALIGN(uint16_t dst_c[WIDTH]);
    CHECKASM_ALIGN(uint16_t dst_a[WIDTH]);

    checkasm_declare(void, uint16_t *dst, const uint16_t *src, int len);

    for (int bpc = 10; bpc <= 12; bpc += 2) {
        const int bitdepth_max = (1 << bpc) - 1;
        dsp_context_init(&dsp, checkasm_get_cpu_flags(), bpc);

        if (checkasm_check_func(dsp->process, "process_%dbpc", bpc)) {
            // Randomize within valid bit depth range
            checkasm_randomize_mask16(src, WIDTH, bitdepth_max);

            checkasm_call_ref(dst_c, src, WIDTH);
            checkasm_call_new(dst_a, src, WIDTH);
            checkasm_check1d(uint16_t, dst_c, dst_a, WIDTH, "dst");
            checkasm_bench_new(checkasm_alternate(dst_a, dst_c), src, WIDTH);
        }
    }

    checkasm_report("process");
}
@endcode

Alternatively, you may prefer to compile the test file itself multiple times,
using preprocessor definitions like `-DBITDEPTH=10` etc.

@subsection adv_failure Custom Failure Reporting

Provide detailed diagnostics when tests fail:

@code{.c}
static void check_complex(const DSPContext *dsp)
{
    // ...

    if (checkasm_check_func(dsp->complex, "complex")) {
        for (int param = 0; param < 16; param++) {
            int result_c = checkasm_call_ref(param);
            int result_a = checkasm_call_new(param);

            // Check return value
            if (result_c != result_a) {
                if (checkasm_fail()) {
                    // This branch is only executed if verbose error diagnostics
                    // are requested by the user
                    fprintf(stderr, "return mismatch for param=%d: %d vs %d\n",
                            param, result_c, result_a);
                }
            }
        }
    }

    checkasm_report("complex");
}
@endcode

@subsection adv_indirect Calling Functions Through Wrappers

When the function being tested must be called indirectly through a wrapper,
you may use checkasm_call() and checkasm_call_checked() to invoke an arbitrary
helper function.

In this case, the declared function type must be the type of the wrapper, not
the inner function passed to checkasm_check_func(). You may then access the
untyped reference/tested function pointers via @ref checkasm_key_ref and
@ref checkasm_key_new "":

@code{.c}
typedef int (my_func)(int);

// Wrapper that invokes the actual function
static int sum_upto_n(my_func *func, int count)
{
    int sum = 0;
    for (int i = 0; i < count; i++)
      sum += func(i);
    return sum;
}

static void check_wrapper(void)
{
    // Declare the signature of the wrapper, not the inner function
    checkasm_declare(int, my_func *, int);

    if (checkasm_check_func(get_my_func(), "my_wrapped_func")) {
        const int count = checkasm_rand() % 100;

        // Cast checkasm_key_new to the appropriate type and pass to wrapper
        const my_func *my_ref = (my_func *) checkasm_key_ref;
        const my_func *my_new = (my_func *) checkasm_key_new;
        int sum_c = checkasm_call(sum_upto_n, my_ref, count);
        int sum_a = checkasm_call_checked(sum_upto_n, my_new, count);

        if (sum_c != sum_a)
            checkasm_fail();
    }
}
@endcode

@note When using a pattern like this, the value passed to checkasm_check_func()
may not even need to be a function pointer. It could be an arbitrary pointer
or pointer-sized integer, such as a configuration struct or index into a dispatch
table, so long as it uniquely identifies the underlying implementation being
tested.

@subsection adv_mmx MMX Functions (x86)

MMX functions on x86 often omit the `emms` instruction before returning, expecting
the caller to execute it manually after a loop. The `emms` instruction is necessary
to clear MMX state before any floating-point code can execute, but it can be very
slow, so optimized loops that call into MMX kernels usually defer it to the loop
end to minimize overhead.

Use checkasm_declare_emms() for such (non-ABI-compliant) functions:

@code{.c}
static void check_sad_mmx(const DSPContext *dsp)
{
    #define SIZE 16
    CHECKASM_ALIGN(uint8_t src [SIZE * SIZE]);
    CHECKASM_ALIGN(uint8_t ref [SIZE * SIZE]);

    // Declaring with CPU_FLAG_MMX enables an automatic emms after calling
    // this function whenever CPU_FLAG_MMX is active
    checkasm_declare_emms(CPU_FLAG_MMX, int, const uint8_t *src,
                          const uint8_t *ref, ptrdiff_t stride);

    if (checkasm_check_func(dsp->sad_16x16, "sad_16x16")) {
        INITIALIZE_BUF(src);
        INITIALIZE_BUF(ref);

        // checkasm will automatically call emms after checkasm_call_new()
        int result_c = checkasm_call_ref(src, ref, SIZE);
        int result_a = checkasm_call_new(src, ref, SIZE);

        if (result_c != result_a) {
            if (checkasm_fail()) {
                fprintf(stderr, "sad mismatch: %d vs %d\n", result_c, result_a);
            }
        }

        // checkasm will also call emms after benchmarking iterations
        checkasm_bench_new(src, ref, SIZE);
    }

    checkasm_report("sad");
}
@endcode

The first parameter is a CPU flag mask (e.g., `CPU_FLAG_MMX | CPU_FLAG_MMXEXT`).
When any of the specified CPU flags are active, checkasm will call `emms` after
each checkasm_call_new() and benchmark run. On non-x86 platforms, checkasm_declare_emms()
is equivalent to checkasm_declare().

@note Modern SIMD instruction sets (SSE and later) do not use MMX registers and
      therefore don't require `emms`. Only use checkasm_declare_emms() for legacy
      MMX-only code or MMXEXT functions that explicitly use MMX registers.

@section tips_tricks Tips and Tricks

@subsection tips_deterministic Deterministic Testing

checkasm uses a seeded PRNG for reproducible tests. To test with a specific seed:

@code{.bash}
./checkasm 12345  # Use seed 12345
@endcode

Failed tests will print the seed used, allowing you to reproduce failures:

@code{.txt}
checkasm: using random seed 987654321
...
sad_16x16: FAILED (ref:1234 new:1235)
@endcode

@subsection tips_selective Selective Testing

Test specific functions or groups:

@code{.bash}
# Test only functions matching pattern
./checkasm --function='add_*'

# Test only a specific test module
./checkasm --test=math

# Combine both
./checkasm --test=dsp --function='blend_*'
@endcode

@subsection tips_verbose Verbose Output

Enable verbose mode for detailed failure information:

@code{.bash}
./checkasm --verbose
@endcode

This shows hexdumps of differing buffer regions automatically, when using
the built-in checkasm_check*() series of buffer comparison helpers.

@subsection tips_bench Benchmarking Tips

Run benchmarks with appropriate duration:

@code{.bash}
# Quick benchmark (default)
./checkasm --bench

# Longer benchmark for more accurate results (10ms per function)
./checkasm --bench --duration=10000

# Export results in different formats
./checkasm --bench --csv  > results.csv
./checkasm --bench --json > results.json
./checkasm --bench --html > results.html
@endcode

@subsection tips_helpers Helper Macros

Create helper macros to reduce repetition in your tests:

@code{.c}
#define TEST_FILTER(name, w, h)                                       \
    if (checkasm_check_func(dsp->name, #name "_%dx%d", w, h)) {       \
        test_filter_##name(dsp, w, h);                                \
        checkasm_bench_new(dst, dst_stride, src, src_stride, w, h);   \
    }

// Usage:
TEST_FILTER(blur,    16, 16);
TEST_FILTER(blur,    32, 32);
TEST_FILTER(sharpen, 16, 16);
@endcode

---

@section tests_next_steps Next Steps

Now that you've mastered writing tests, learn how to accurately measure and
compare the performance of your optimized implementations using checkasm's
benchmarking capabilities.

**Next:** @ref benchmarking

---

--- METRICS ---
- Files Extracted: 25
- Estimated Token Budget: ~52611 tokens
- Canonical Reference: https://codewiki.google/github.com/videolan/vlc
