vlc Documentation

VLC media player - plays everything, runs anywhere. Code here:

20,069 stars C C 25 files ยท ~52,687 tokens #c#framework#gplv2#lgpl#multimedia#open-source#vlc
RAW Doc

Technical Documentation: videolan/vlc

โ„น๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference

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 and
is developed and supported by a community of volunteers.

The VideoLAN project was started at the university ร‰cole Centrale Paris 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.

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

Nota Bene: The Android app and
the iOS app 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),
Swift (iOS), and C# (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.

Contributions

Contributions are now done through Merge Requests on our GitLab repository.

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

Some useful links that might help you:

Source Code sitemap

text
/* 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,
)

text
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]);

text
// 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();

text
// 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(),
};

text
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...]
Use fixed value to seed the PRNG
Options:
--affinity= Run the process on CPU
--bench -b Benchmark the tested functions
--csv, --tsv, --json, Choose output format for benchmarks
--html
--function= -f Test only the functions matching
--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[=] Repeat tests N times, on successive seeds
--test= -t Test only
--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) (recommended) or msys2.
You may also build inside a Docker on Linux
or on Windows.

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:
text
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:
text
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

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

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
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:

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.

text
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:

text
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,
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.

Old clock system

The old clock from VLC was mainly an "input clock", based on the input
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
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:

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`)

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

-- #videolan
%

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

-- #videolan
%
xav - AZRTYUUUUOP"'(-รˆ
oups je devrais pas nettoyer mon clavier par ici :)

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

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

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

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

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

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

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

-- #videolan
%
you really shouldn't have touched a computer when you were a little boy
it came to me

-- #videolan
%
hehe : Open failed !: Success

-- #videolan
%
vlc rulez
it dont work
what doesn't work ?
vlc rulez

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

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

-- #videolan
%
-!- Meuuh [[email protected]] has joined #videolan
Meuuh!
Meuuh.

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

-- #videolan
%
c'est bon, Jรฉrรดme m'a envoyรฉ le truc sans virus cette fois

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

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

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

-- #videolan
%
$grabeuh
heheheuhrahaaheuwaheuhehrabeugraaaahawhahrahaa !!
hey THAT was a nice grabeuh

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

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

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

-- #videolan
%
fils de p_aout

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

-- #videolan
%
-+- Ourumov [[email protected]] has joined #videolan
hey anybody here know videolan ?
-*- SignOff: Ourumov (Client Quit)

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

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

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

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

-- #videolan
%
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.
yeah
Meuuh: not sure if you sound more like an mpeg guru or a blues
artist
like a cow, maybe ?
or something

-- #videolan
%
a52old & wi11y?

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

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

-- #videolan
%
$grut Dnumgis
Dnumgis: tiรฅr ditt og nรธdhavn klikk ?
gally: og hva skulle det bety?
gally: what was that supposed to mean?

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

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

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

-- #videolan
%
I just listened through an entire ogg/vorbis file with vlc with no
audible problem whatsoever
but I don't get the time of the stream
gibalou: why don't I get the time?
because time does not exist
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
Dnumgis: a few things are left to be implemented, like seeking,
stream length display, etc...
or you can listen to gibalou's dull explanation

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

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

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

-- #videolan
%
damn
why didn't you use LGPL?
why don't you use GPL?

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

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

-- #videolan
%
jlj: what did you do?
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
%
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
that's illegal
<_broomy-> yep everything is illegal
<_broomy-> i bet u do illegal shit aswell

-- #videolan
%
tiens, j'ai une idee pour les gens qui se plaignent que le site web
est pas traduit en grec antique
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
%
KKI: I was merely joking, ignore me :)
it is generally a good idea to ignore sam

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

-- #videolan, du code de pro
%
someone is not releasing a p_vout object correctly
it's not me
i was not there this afternoon

  • Meuuh looks in his pockets

    -- #videolan
    %
    seriously though, out of curiosity: why are people working on
    VideoLAN? It can't be to keep us BeOS people happy ;-)

world domination
didn't you get the memo ?

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

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

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

-- #videolan
%
sounds perfectly normal
ah
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] ([email protected]) has joined channel
#videolan
<[MaDog]> c'รจ qualcuno ?????????
read the topic
<[MaDog]> :)
<[MaDog]> xcuse me
*** Signoff: [MaDog] (Client Quit)

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

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

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

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

-- #videolan
%
pour votre punition, รงa sera un bootstrap chacun :P
c'est reparti pour 20 min
c'est reparti pour 3 x 20 min
c'est reparti pour une modif de Makefile.am d'ici 20 min

-- #videolan
%
any girls here with a webcam

-- #videolan
%
16K ChangeLog-1999
96K ChangeLog-2000
168K ChangeLog-2001
304K ChangeLog-2002
488K ChangeLog-2003
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
%
and what's the news ?
Mac OS 10.2 and 10.3 support hopefully
Win98 maybe
haven't had any chance to test it ?
I need to send a new version to a tester
hire him meuuh :P
E-bola : we already did :p
ahhh
lol

-- #videolan
%
*** Bond470 (n=[email protected]) has joined channel #videolan
olas
;)
dionoea ?
lisboa
e tu?

-- #videolan
%
lol, fen is a la(i)mer
fen [n=[email protected]]

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

-- #videolan
%
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 ?
zorglub: la meilleure doc c'est le code de mplayer/xine
c'est bien ce que je craignais :)
j'ai pas regardรฉ xine ceci dit
c'est plus agrรฉable ร  lire que mplayer
mouahaha
non rien.
ah bah si quand mรชme
tu veux dire que les variables ont un nom qui reflรจte ce qu'elles
font ?
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
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=[email protected]] has joined #videolan
hi
how to activate hotkeys?
-!- Bond631 [n=[email protected]] has quit [Client Quit]
I think Bond631 found the hotkey for quit

-- #videolan
%
-!- tumu [n=[email protected]] has quit ["~"]

  • Dnumgis notes that tumu must be one of very few with dsl in their sauna

    -- #videolan
    %

thedj: gibalou quits officially, or has he just generally
disappeared?
he's probably getting married or something even worse :(

-- #videolan
%
kee-un: you need to slap zorglub
kee-un: oups no i'm telling a lie
xtophe: too late, I slapped him
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: --
    %

sam : could you delete FK_ from the access list ?
sure, why?
he's now known as feepk
ah, ok
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
    %

xtophe: look at it this way:
solve some bounties involving vlc and earn some extra money
use this money and your natural charm to get a girlfriend
make sure Meuuh sees it
Meuuh would become horribly jealous and try to repeat your trick
with great vigor and solve all remaining bugs with vlc
and the (vlc)world becomes a better place
Dnumgis_: do you have a girlfriend ?
j-b: not at the moment, no
Dnumgis_: so why don't you try this technick ?
j-b: I don't live close enough to Meuuh
thats some good norwegian reasoning
-- #videolan
%
of course when using sout-ts-es-id-pid you can't have any stream with
an id < 16
so use fake-id=68 or whatever
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
%
hi, how can i adjust the brightness?
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
%
what is atsc ?
like dvb but in america
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
%
si, la 0.9 est utilisable
รงa dรฉpend pour quoi
pour faire croire ร  un naรฏf qu'un dvd est pรฉtรฉ par
exemple
"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
%
ivoire: ?
Quovodis: sorry
my parents push me out of the computer :(
-- #videolan
%
VLC development is NOT about being a sed god.
-- courmisch
%
no one reads my documentation
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
%
guys..thats what I get when I try to run vlc as root
vlc -I dummy
thanks for calling me dummy
makes me feel warm and fuzzy
n/p
sorry, this is already in the fortunes
still won't start
and now I see dummy is one of the options
:)
I really thought you were calling me dummy..sorry
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
%
(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

%
Who's Barack Obama on the etherpad?
etix: the president of the USA
etix: You really should have heard of him
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"
    (Create an annotated tag, you can skip -a if you used -m)
    git push origin

  • 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
  • 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/?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
  • 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

text
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.

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 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:

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)

text
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:

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
      1. 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() 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()
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 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:

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. 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 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 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, 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() 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()
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:

text
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:

text
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:

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

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 (and sometimes on more)
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
(or later) 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 (or later).

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 (and sometimes on more)
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 (or later) 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 (or later).


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/.

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:

text
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 [email protected].

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:

text
* 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);

text
// 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
// ...

text
// 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];

text
// 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
// ...
}

text
// 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,
// ...
};

text
// 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();

text
// 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

text
// 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());

text
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

@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);

text
// 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
}

text
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);

text
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];

text
// 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]);

text
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]);

text
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]);

text
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);

text
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");

text
// 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;

text
#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)
{
// ...

text
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);

text
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]);

text
// 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 ---