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
Links
Some useful links that might help you:
Source Code sitemap
/* Detailed source-code truncated for AI context efficiency. */Test Checkasm Ext Docs Getting Started (`test/checkasm/ext/docs/getting-started.md`)
@page getting_started Getting Started
This guide will walk you through installing checkasm and writing your first test.
@tableofcontents
@section installation Installation
You can either load checkasm as a library (e.g. via pkg-config), or include it directly in your project's build system.
@subsection meson_submodules Meson using wrap files (recommended)
First, create subprojects/checkasm.wrap:
@code{.ini}
[wrap-git]
url = https://code.videolan.org/videolan/checkasm.git
revision = release # or a specific tag/release
directory = checkasm
@endcode
Then integrate it into your build system:
@code{.meson}
This first attempts loading checkasm as an external dependency using the
appropriate platform-specific method (e.g. pkg-config on POSIX systems),
and falls back to using the bundled version inside `subprojects/checkasm`
otherwise.
checkasm_dependency = dependency('checkasm',
# Extracts the checkasm_dep variable from the checkasm subproject.
fallback: ['checkasm', 'checkasm_dep'],
required: false
)
Alternatively, you can directly force use of the bundled version:
checkasm_dependency = subproject('checkasm').get_variable('checkasm_dep')
if checkasm_dependency.found()
checkasm = executable('checkasm',
checkasm_sources,
dependencies: checkasm_dependency,
)
test('checkasm', checkasm, suite: 'checkasm')
benchmark('checkasm', checkasm, suite: 'checkasm', args: '--bench')endif
@endcode
@subsection meson_wrap Meson using submodules (alternative)
As an alternative, you may use git submodules to include checkasm as a subproject.
This may be preferred in some environments where the build system cannot access
the internet during configuration time, or if you're already using submodules
in your project.
@code{.bash}
git submodule init
git submodule add -b release https://code.videolan.org/videolan/checkasm subprojects/checkasm
or checkout a specific tag/release
@endcode
Then declare the dependency in your meson.build as usual. (See the previous section)
@subsection manual_installation Manual Installation
You can also build and install checkasm manually:
@code{.bash}
git clone https://github.com/videolan/checkasm.git && cd checkasm
meson setup builddir -Dprefix=$PREFIX # (set optional build prefix)
meson compile -C builddir
meson install -C builddir
@endcode
This is discouraged in favor of using Meson subprojects or distribution packages,
but may be useful inside containerized environments, CI systems or custom
build roots.
@section quick_start Quick Start Example
Let's create a simple test for a vector addition function that operates on buffers.
@subsection quick_start1 1. Prerequisites
Let's assume you have a reference implementation and an optimized version,
alongside a way of detecting CPU features and choosing the implementation
based on that:
@code{.h}
// my_dsp.h
#include <stdint.h>
enum {
CPU_FLAG_AVX = 1 << 0,
};
unsigned detect_cpu_flags(void);
typedef void (*add8_func_t)(uint16_t *dst, const uint8_t *src1,
const uint8_t *src2, size_t len);
add8_func_t get_add8_func(unsigned cpu_flags);
@endcode
@code{.c}
// my_dsp.c
#include "my_cpu.h"
// Reference implementation (pure C)
static void add8_c(uint16_t *dst, const uint8_t *src1,
const uint8_t *src2, size_t len)
{
for (size_t i = 0; i < len; i++)
dst[i] = src1[i] + src2[i];
}
// Optimized implementation (pretend this is assembly)
static void add8_avx(uint16_t *dst, const uint8_t *src1,
const uint8_t *src2, size_t len)
{
// Assembly optimized version would go here
add8_c(dst, src1, src2, len);
}
add8_func_t get_add8_func(unsigned cpu_flags)
{
if (cpu_flags & CPU_FLAG_AVX)
return add8_avx;
return add8_c;
}
@endcode
@subsection quick_start2 2. Write the Test
Create your test file:
@code{.c}
// check_dsp.c
#include <checkasm/checkasm.h>
#include <checkasm/test.h>
#include "my_dsp.h"
#define WIDTH 1024
static void test_add8(const CheckasmCpu cpu)
{
// Declare aligned buffers for testing
CHECKASM_ALIGN(uint8_t src1[WIDTH]);
CHECKASM_ALIGN(uint8_t src2[WIDTH]);
CHECKASM_ALIGN(uint16_t dst_c[WIDTH]);
CHECKASM_ALIGN(uint16_t dst_a[WIDTH]);
// Declare the function signature
checkasm_declare(void, uint16_t *, const uint8_t *, const uint8_t *, size_t);
if (checkasm_check_func(get_add8_func(cpu), "add_8")) {
// Initialize source buffers with quasi-random test vectors
INITIALIZE_BUF(src1);
INITIALIZE_BUF(src2);
// Test with various buffer sizes
for (int w = 1; w <= WIDTH; w <<= 1) {
// Clear destination buffers before each test
CLEAR_BUF(dst_c);
CLEAR_BUF(dst_a);
// Call reference and optimized implementations
checkasm_call_ref(dst_c, src1, src2, w);
checkasm_call_new(dst_a, src1, src2, w);
// Compare results - checkasm_check1d will report any mismatches
checkasm_check1d(uint16_t, dst_c, dst_a, w, "sum");
}
// Benchmark the optimized version on the largest buffer size
checkasm_bench_new(checkasm_alternate(dst_c, dst_a), src1, src2, WIDTH);
}}
static void check_dsp(void)
{
const CheckasmCpu cpu = checkasm_get_cpu_flags();
// Test all related functions and report as a single function group
test_add8(cpu);
// test_add16(cpu);
// ...
checkasm_report("add");
// Check more function groups
// ...}
// Test registry
static const CheckasmTest tests[] = {
{ "dsp", check_dsp },
{0} // array terminator
};
// CPU flag registry
static const CheckasmCpuInfo cpu_flags[] = {
{ "AVX", "avx", CPU_FLAG_AVX },
{0} // array terminator
};
int main(int argc, const char *argv[]) {
CheckasmConfig config = {
.tests = tests,
.cpu_flags = cpu_flags,
.cpu = detect_cpu_flags(),
};
return checkasm_main(&config, argc, argv);}
@endcode
@subsection quick_start3 3. Build and Run
@code{.bash}
Compile (example using gcc directly)
gcc -o check_dsp my_dsp.c check_dsp.c $(pkg-config --cflags --libs checkasm)
or use `meson compile` if using Meson
Run all tests
./check_dsp
@endcode
@section options Command-Line Options
checkasm provides several useful command-line options:
@code{.bash}
List all available functions
./checkasm --list-functions
Run specific functions (supports wildcards)
./checkasm --function=add_*_8bpc
Run benchmarks
./checkasm --bench
Run specified test with higher benchmark duration (here: 10 ms)
./checkasm --test=pixel --bench --duration=10000
Enable verbose output
./checkasm --verbose
@endcode
The --help output shows all available options:
@code{.txt}
Usage: checkasm [options...]
Options:
--affinity=
--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[=
--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:
sudo apt-get update -qq
sudo apt-get install -qqy \
git wget bzip2 file libwine-dev unzip libtool libtool-bin libltdl-dev pkg-config ant \
build-essential automake texinfo yasm p7zip-full autopoint \
gettext cmake zip wine nsis g++-mingw-w64-i686 curl gperf flex bison \
libcurl4-gnutls-dev python3 python3-setuptools python3-mako python3-requests python3-venv \
gcc make procps ca-certificates \
openjdk-11-jdk-headless nasm jq gnupg \
meson autoconf- On msys2 you should install similar packages:
pacman -Syu
pacman -S --needed git wget bzip2 file unzip libtool pkg-config \
automake autoconf texinfo yasm p7zip \
gettext cmake zip curl gperf flex bison \
python3 python3-setuptools python3-mako \
gcc make ca-certificates nasm gnupg patch help2man \
python3 mesonToolchains
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, anducrt for Universal Windows Platform (UWP) builds.
- On Linux:
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 /optEvery 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:
echo 'export PATH=/opt/llvm-mingw-20220906-msvcrt-ubuntu-18.04-x86_64/bin:$PATH' > toolchain.shYou will need to call this file when you start your build session:
source toolchain.sh- On msys2, use the mingw64 (blue) environment (ie not msys (purple) or mingw32 (grey) environments):
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 /optEvery 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:
echo 'export PATH=/opt/llvm-mingw-20220906-msvcrt-x86_64/bin:$PATH' > toolchain.shYou will need to call this file when you start your build session:
source toolchain.shOn 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
- msvcrt:
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:
sudo apt-get install -qqy \
gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 mingw-w64-tools- On msys2:
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:20240212151604image.
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:
git config --global core.autocrlf false
git clone https://code.videolan.org/videolan/vlc.gitThis will download the VLC source code into the vlc folder, in your current directory.
Building VLC
Fully build in a separate folder build:
mkdir build
cd build
../vlc/extras/package/win32/build.sh -a x86_64This 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:
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 -pBuilding 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:
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 -plibvlc
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.
mkdir build
cd build
../vlc/extras/package/win32/build.sh -a x86_64 -zOther 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.
JOBS=8 MESON_BUILD="-j 1" ../vlc/extras/package/win32/build.sh -a x86_64Config 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 :
../vlc/configure -hYou can pass all these options to build.sh using the CONFIGFLAGS environment
variable.
For example you can enable the address sanitizer with:
CONFIGFLAGS="--with-sanitizer=address" ../vlc/extras/package/win32/build.sh -a x86_64Contrib 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:
../vlc/contribs/bootstrap -hYou can pass all these options to build.sh using the CONTRIBFLAGS environment
variable.
For example you can disable bluray building with:
CONTRIBFLAGS="--disable-bluray" ../vlc/extras/package/win32/build.sh -a x86_64Debugging
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.
../vlc/extras/package/win32/build.sh -a x86_64 -dThis 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:
../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:
CFLAGS="-fdebug-prefix-map='/mnt/c/'='c:/'" CXXFLAGS="-fdebug-prefix-map='/mnt/c/'='c:/'" ../vlc/extras/package/win32/build.sh -a x86_64 -dVLC 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:
cd win64; make package-win-commonYou will end up with a vlc-4.0.0-dev folder with the vlc EXE's and DLL's in the
proper place. You can debug from there. But this way of building is much slower than
just building the files that have been modified.
A faster way to debug is to just build with build.sh or even just calling make
in the win64 folder. In that case you need to setup your debugging environment to
pick libvlccore.dll, libvlc.dll and plugins that you built. libvlccore.dll is
found in win64/src/.libs and libvlc.dll is found in win64/lib/.libs. You need to
add these paths to your PATH.
PATH="c:\path\to\sources\vlc\win64\lib\.libs;c:\path\to\sources\vlc\win64\src\.libs;C:\Windows\System32;C:\Windows;C:\Windows\System32\downlevel"And when you run vlc.exe, you also need to set the VLC_PLUGIN_PATH environment variable
to the folder with all the plugins you built:
VLC_PLUGIN_PATH=c:\path\to\sources\vlc\win64\modulesFuture
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:
struct my_task
{
/* custom data */
int num;
char *str;
/* embedded runnable */
struct vlc_runnable runnable;
};
void Run(void *userdata)
{
// ...
}and submit it as follow:
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.
VLC_API void
vlc_executor_Submit(vlc_executor_t *executor, struct vlc_runnable *runnable);
VLC_API bool
vlc_executor_Cancel(vlc_executor_t *executor, struct vlc_runnable *runnable);Design discussions
The design of this API is the result of many discussions, especially on the
mailing-list:
- https://mailman.videolan.org/pipermail/vlc-devel/2020-August/136696.html
- https://mailman.videolan.org/pipermail/vlc-devel/2020-September/136944.html
- https://mailman.videolan.org/pipermail/vlc-devel/2020-September/137188.html
This documentation aims to explain the rationale for each decision.
Runnable allocation
The struct vlc_runnable must be provided by the client, and the executor uses
only this instance to represent its task (it does not allocate per-task private
data).
Firstly, note that this allows vlc_executor_Submit() to return void, so no
error handling is necessary. It's minor, but convenient.
An attractive alternative would be to return a new allocated runnable on
submission, which could be used for cancellation:
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 avlc_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:
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) */
}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:
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 pointvlc_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:
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:
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 ofvlc_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`)
--vout' to playlist <Meuuh> intf: added dummy' to playlist
-- #videolan
%
-- #videolan
%
-- #videolan
%
sam : {#][^#{~]@[ ;P
sam : pouette
:)
-- #videolan
%
-- #videolan
%
-- #videolan
%
-+- djk sets topic on #videolan to: "VideoLAN - 100% skin free DVD!"
%
-- #livid
%
-- #videolan
%
-- #videolan
%
-- #videolan
%
you really shouldn't have touched a computer when you were a little boy
-- #videolan
%
-- #videolan
%
-- #videolan
%
-- #videolan
%
[...]
-- #videolan
%
-!- Meuuh [[email protected]] has joined #videolan
-- #videolan
%
distort:transform:distort:transform:distort :(
-- #videolan
%
-- #videolan
%
the MPlayer CVS :)
-- #videolan
%
-- #videolan
%
-- #videolan
%
-- #videolan
%
-- #videolan
%
-- #videolan
%
-- #videolan
%
-- #videolan
%
-- #videolan
%
-+- Ourumov [[email protected]] has joined #videolan
-*- SignOff: Ourumov (Client Quit)
-- #videolan
%
-- #videolan
%
-- #videolan
%
broken.
-- #videolan
%
-- #videolan
%
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.
artist
-- #videolan
%
-- #videolan
%
-- #videolan
%
-- #videolan
%
-- #videolan
%
-- #videolan
%
-- #videolan
%
no... using windows
-- #videolan
%
audible problem whatsoever
priori that time is like a mathematical line, rendering time as a form
of conscious experience
stream length display, etc...
-- #videolan
%
-- #videolan
%
ooooh
that's a pretty good idea
-- #videolan
%
dance with me
than she
-- #videolan
%
-- #videolan
%
-- #videolan
%
-- #videolan
%
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
<_broomy-> yep everything is illegal
<_broomy-> i bet u do illegal shit aswell
-- #videolan
%
est pas traduit en grec antique
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
%
-- #videolan
%
video_ts.vob */
-- #videolan, du code de pro
%
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 ;-)
-- #videolan
%
<ka|i> sam : a cause de la greve ou tu te reveilles ?
-- #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...
-- #c00lz
%
-- #videolan
%
-- #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 ?????????
<[MaDog]> :)
<[MaDog]> xcuse me
*** Signoff: [MaDog] (Client Quit)
-- #videolan
%
i hope so. haven't heard a thing about a nuke explosion in Paris
-- #videolan
%
i don't trust ffmpeg's make process ;)
-- #videolan
%
-- #videolan
%
-- #videolan
%
-- #videolan
%
-- #videolan
%
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
%
-- #videolan
%
*** Bond470 (n=[email protected]) has joined channel #videolan
olas
;)
lisboa
e tu?
-- #videolan
%
lol, fen is a la(i)mer
fen [n=[email protected]]
-- #videolan
%
-- #videolan
%
real ou fen a fait le demux directement en lisant des dumps / le
demuxer de mplayer ?
tu veux dire que les variables ont un nom qui reflรจte ce qu'elles
font ?
lisibilitรฉ car cela dรฉtourne l'attention du vrai code, constituรฉ des
mots-clรฉs du langage et des divers opรฉrateurs
-- #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
%
disappeared?
-- #videolan
%
-- #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: --
%
-- #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
%
with great vigor and solve all remaining bugs with vlc
-- #videolan
%
an id < 16
-- #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
%
-- #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
%
-- #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
%
exemple
%
< funman> {1} Active Tickets (490 matches)
< funman> i disconnect before 500 is reached
-!- FoX_ [i=FoX@nat/ecp/x-1a0545f92a4d3c50] has quit [Remote closed the connection]
-!- zorglub [i=zorglub@nat/ecp/x-61892e92340285ff] has quit [Remote closed the connection]
-!- bigben [i=bigben@nat/ecp/x-1e86f79de0c1cdc9] has quit [Remote closed the connection]
-!- ipkiss [i=ipkiss@nat/ecp/x-31e3605240c16dc6] has quit [Remote closed the connection]
-!- Dimar [i=Dimar@nat/ecp/x-d24991bac9f169c9] has quit [Remote closed the connection]
< whyz> were those your fakenicks funman?
< funman> i hit the wrong button sorry
< whyz> :O
-- #videolan
%
< Mr_Mirsal> ./bootstrap
< StFS> trying that now
< StFS> damn this takes long. :-/
< thresh> good things always take long time
-- #videolan
%
< Mr_Mirsal> Oh, btw, I request a fortunes.txt update...
< thresh> Mr_Mirsal: commit !
< Mr_Mirsal> thresh: Eh ! I can't.
< thresh> and i don't want to be a developer who only develops fortunes! :)
-- #videolan
%
Quovodis: sorry
my parents push me out of the computer :(
-- #videolan
%
VLC development is NOT about being a sed god.
-- courmisch
%
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
%
vlc -I dummy
n/p
:)
-- #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
%
-- #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
%
-- #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.
- Check that the version number everywhere it's needed
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 originTarballs (Use a clean tree !!!)
- Edit POTFILES.in to include generated Qt UI files (doesn't work out of tree)
- Use 'make distcheck' to make sure all files are exported correctly
- copy the tar.xz file on ganesh.videolan.org
- generate SHA, MD5 hashes and OpenPGP signature of these files :
for file in vlc*; do
for sum in md5 sha1 sha256; do
${sum}sum --binary $file > $file.$sum
done
gpg --rfc4880 --no-textmode --armor --detach-sign
-u "VideoLAN Release Signing Key 2026" --digest-algo SHA512
--output "$file.asc" -- "$file"
done
- Contribs
- Put a copy of the libraries or git snapshot in ftp://ftp.videolan.org/pub/contrib/
- Put the relevant win32 and Mac OS contrib package
- Update s.v.o/vlc and www.v.o/vlc/download-sources.html
- Win32 Packages
- configure using the extras/package/win32/configure.sh script
- copy the packages files on ganesh.videolan.org
- copy the vlc-2.0.4 and symbols-2.0.4 to balthazar for stacktraces
- Mac OS X packages
- configure using the extras/package/macosx/configure.sh script
- use extras/package/macosx/codesign.sh with options and key to sign it.
- copy the package to ganesh.videolan.org
- Sign all the files and move the signed files on /opt/ftp/pub/videolan/vlc/X.X.X
- At first block access to this directory by http or ftp. Only authorize rsync.
This is to avoid leak as some journalists and forums seem to watch the ftp. - Update the "last" symlink
- Update the website
- edit the /index.php /vlc/*.php, include/os-specific.php files
- create a /vlc/release/X.X.X page
- add a news item to news.msg
- update the front page OS dependent screenshots if the interface changed.
- update the md5
- "Check for updates" functionality
- Edit the appropriate status file under update.videolan.org/vlc/:
status-win-x86, status-win-x64 or status-win-arm64 for Windows. - Sign with the same release key as the advertised installer, then verify:
file=status-win-x64
gpg --rfc4880 --no-textmode --armor --detach-sign
-u "VideoLAN Release Signing Key 2026" --digest-algo SHA512
--output "$file.asc" -- "$file"
- Mirrors
- Forwarn the mirror admins (optional for any minor release)
- Go to https://get.videolan.org/?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 www.freecode.com (let jb or sam do it, if you lack the account)
- go to https://freecode.com/projects/vlc/
- chose "Submit a release" in the right menu, in "Administration"
- Do the steps and wait (the update can take several hours)
Announce on Twitter, Facebook and G+
Unfortunately, those are more followed than the all other mediums combinedAnnounce 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
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.HTTP interface
You can launch it as a common interface
Default port is 8080.
Access it from http://127.0.0.1:8080/vlm.html
II - VLM Elements
A media is composed with a list of inputs (the videos and audios you
want to stream), an output (where you want to stream them), and some
options. It is very close to a TV program, or channel: it is launched,
stopped, paused by the administrator, may be repeated several times etc.III - Command line syntax:
Note: the only type of element in this version is media.
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.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.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:
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.libvlc_time_tis now microseconds everywhere. LibVLC 3 mixed
milliseconds and microseconds in the public API. In 4.0 a single typed
unitlibvlc_time_t(int64_t) is used throughout the API for time units.New
libvlc_parser_tAPI: Parsing and thumbnail generation is moved fromlibvlc_media_tto 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_tlibvlc_event_t/libvlc_event_type_tlibvlc_callback_tlibvlc_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_tlibvlc_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_Bufferingstate value (inlibvlc_state_t): use the media
player'son_buffering_changedcallback.libvlc_media_get_parsed_status()and thelibvlc_media_parsed_status_t
enum: use the newlibvlc_media_is_parsed(), which returns abool.
The detailed outcome of a parse request is now reported only throughlibvlc_parser_cbs.on_parsedas alibvlc_parser_status_t(ยง6).libvlc_media_discoverer_media_list(): Use thelibvlc_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:
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:
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.
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 everylibvlc_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 ofseek,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:
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. Calllibvlc_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 thelibvlc_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 alibvlc_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_usthrottles how oftenon_updatefires - it does not limit how often you may interpolate:clibvlc_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)
withlibvlc_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() andvlc_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`
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`
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:
libvlc_media_t *m = libvlc_media_new_callbacks(open_cb, read_cb, seek_cb,
close_cb, opaque);LibVLC 4:
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 withlibvlc_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:
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, andlibvlc_MediaListPlayerPlayed / libvlc_MediaListPlayerStopped byon_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
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
/* 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 parsedlibvlc_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
/* 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, orNULLto cancel everything. Returns the number of requests cancelled.
Cancelled parse tasks fireon_parsedwithlibvlc_parser_status_cancelled. Cancelled thumbnail generation
fireon_endedwithpictureas 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 insideon_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:
Replace the event system.
- Remove every
libvlc_event_attach()/detach()call and the
associated dispatcher functions. - For each object, build a
*_cbsstruct withversionset to the
latest version number (currently0) and translate your handlers
using the table in ยง4.1. - Delete any
libvlc_*_event_manager()lookup.
- Remove every
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).
Rename destructors / retainers.
libvlc_media_discoverer_release->_destroy.libvlc_renderer_discoverer_release->_destroy.libvlc_media_track_hold->_retain.libvlc_renderer_item_hold->_retain.
Switch times to microseconds. Audit every call site involving time unit.
Convert(ms)literals to(ms * 1000), rename locals, update database
schemas if needed.Port parsing / thumbnailing. Replace
libvlc_media_parse_request()/libvlc_media_thumbnail_request_by_*()
calls with a sharedlibvlc_parser_tper libvlc instance; create the task
withlibvlc_parser_task_new_parse()/libvlc_parser_task_new_thumbnail(),
start it withlibvlc_parser_submit(), and removelibvlc_media_parse_stop().Update the dialog struct. Add
.version = 0(the latest
version). Move your error handler tolibvlc_dialog_set_error_callback().Review async control flow. Anywhere your code assumed
libvlc_media_player_stop()/set_pause()/set_media()returned
after the state change, introduce a wait onon_state_changed/on_media_changed/on_media_stopping.Drop removed enum values.
libvlc_Bufferinglibvlc_media_parsed_status_tenum (replacelibvlc_media_get_parsed_status()withlibvlc_media_is_parsed()).- Old
libvlc_media_parse_flag_tflags (parse_local,parse_network,parse_forced).
Add the
u.prefix on named unions. For every anonymous-union access
listed in ยง3.1, insertu.:track->video->track->u.video,out->dxgi_format->out->u.dxgi_format, etc. The compiler flags each one.Recompile with
-Werror. The majority of remaining breakage will be
trivial adaptations.
8. Reference: sample code
Ported examples are available under doc/libvlc/ and exercise the new
callback-based APIs:
player.cโ media player + time watcherparser.cโ parser queuethumbnailer.cโ thumbnailing through the parsermedia_discoverer.cโ discoverer callbacksrenderer_discoverer.cโ renderer callbacks
Follow-up reading: the in-header doxygen documents every callback field and its
version field.
Doc Libvlc CMakeLists (`doc/libvlc/CMakeLists.txt`)
CMAKE_MINIMUM_REQUIRED(VERSION 3.10)
configure with your own path
-DLIBVLC_SDK_INC:STRING=S:/sources/build/win64/win64/vlc-4.0.0-dev/sdk/include
-DLIBVLC_SDK_LIB:STRING=S:/sources/build/win64/win64/vlc-4.0.0-dev/sdk/lib
or set them in your VSCode settings
{
"cmake.configureSettings": {
"LIBVLC_SDK_INC": "S:/sources/vlc/include",
"LIBVLC_SDK_LIB": "S:/sources/build/win64/win64/lib/.libs"
}
}
set("LIBVLC_SDK_INC" "" CACHE PATH "libvlc include folder, containing the vlc/ includes")
set("LIBVLC_SDK_LIB" "" CACHE PATH "libvlc library folder, containing the libvlc libraries")
project("libvlc samples")
define the libvlc external build
add_library(libvlc SHARED IMPORTED GLOBAL)
target_include_directories(libvlc INTERFACE ${LIBVLC_SDK_INC})
if (MSVC)
set_target_properties(libvlc PROPERTIES IMPORTED_IMPLIB "${LIBVLC_SDK_LIB}/libvlc.lib")
else ()
set_target_properties(libvlc PROPERTIES IMPORTED_IMPLIB "${LIBVLC_SDK_LIB}/libvlc.dll.a")
endif ()
if(WIN32)
add_executable(d3d9_player WIN32 d3d9_player.c)
target_link_libraries(d3d9_player libvlc d3d9)
add_executable(d3d11_player WIN32 d3d11_player.cpp)
target_compile_definitions(d3d11_player PRIVATE _WIN32_WINNT=0x0601)
target_link_libraries(d3d11_player libvlc d3d11 d3dcompiler uuid)
add_executable(win_player WIN32 win_player.c)
target_link_libraries(win_player libvlc)endif()
Doc Skins Events Howto (`doc/skins/events-howto.txt`)
Before reading this document, you should first take a look at skins-howto.txt
to understand the general functioning of VLC skins.
What is an event ?
Events are the dynamic part of the skins. It means that beyond visual aspect,
the interface must react with the user actions. An event describes a simple
interaction, in fact one simple action such as playing a file, hiding a
window...
So when designing a skin you will have to specify what those interactions are.
For this you will use simple actions that are described in event tags and you
would be able to add them and associate them to controls.
How to create an event ?
An event describes a simple action as seen above.
All attributes are explained in the 'skins-howto.txt' file except the 'event'
attribute which is a bit special.
In the 'event' attribute you will enter a simple script with the following
syntax :
"EVENT(parameter1,parameter2,...)"
The number of parameters depends on EVENT.
All this is case sensitive.
Don't add spaces.
EVENT is the action to execute, it can be one of the following:
VLC_NOTHING:
Action : none, it executes nothing so don't use it !
Parameters: none.VLC_SHOW:
Action : Open all windows of the interface with a fading effect if
selected.
Parameters: none.VLC_HIDE:
Action : Close all windows of the interface with a fading effect if
selected.
Parameters:- First 1 is an EVENT to execute when all windows have been closed.
VLC_QUIT:
Action : Quit the interface
Parameters: none.VLC_OPEN:
Action : Open an "open file dialog box" to open a file to play.
Parameters: none.VLC_LOAD_SKIN:
Action : Open an "open file dialog box" to change the current skin.
Parameters: none.VLC_ON_TOP:
Action : Toggle the "Always on top" status
Parameters: none.VLC_LOG_SHOW:
Not supported yetVLC_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".
- magnet: allows to select the range of action (in pixels) of magnetism
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.
- id: this is the name of the bitmap that will be used with controls
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".
- id: this is the name of the event that will be used with controls.
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".
- id: this is the name of the font that will be used with controls.
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 is nearly total transaprency and should be avoided. 255 is total
opacity.
Default is "255". You should use high values.
- 1 is nearly total transaprency and should be avoided. 255 is total
- 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".
- id: this is the name of the window (it will be only used for events
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".
- x: try and guess.
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".
- x: is it really necessary to explain ?
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).
- up: identifiant of a Bitmap. Used for drawing the up state of the
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).
- type: two 'types' of sliders are predefined: 'time' for a slider
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.
- width: width of the list in pixels. This is the whole width for file
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 theconfigureshell script from theconfigure.acfile - Automake
Generates the Makefiles from theMakefile.amfiles - 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 theoption() 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_depsuffix, those found withfind_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:
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 thehost_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'sfind_library function. It basically does a linker check for the
specified library name and returns a dependency object for it. It is roughtly equivalent toAC_CHECK_LIB in autoconf.
Options
To add an option for the dependency, add the respective option entry in meson_options.txt:
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:
# 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:
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
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!
endifit 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:
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:
meson test -C build-meson test_src_input_thumbnailwhere 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 themodule_depends variable. If every modules should be used to run the test,
you can use the special value vlc_plugins_targets.keys(), but adding more
module dependencies than necessary will drastically increase the compile time
when running a specific test in cases like automated git bisect.
Compiling with contribs
The meson build system also supports using dependencies and binaries provided
by the contrib system.
Once the contrib has been prepared, a meson machine file will be generated
in the installation prefix. For native linux x86_64, it will typically be
found at the following location:
vlc/contrib/x86_64-pc-linux-gnu/share/meson/native/contrib.iniWhen using prebuilt contribs, make prebuilt regenerates this file for the
local tool paths. Run make tools before configuring VLC if the required
build tools are not available on the system.
Then you can setup meson with this file using the following command:
meson setup build-meson \
--native-file contrib/x86_64-pc-linux-gnu/share/meson/native/contrib.iniWhen cross-compiling, both the crossfile and the contrib machine file
can be supplied at the same time:
meson setup build-meson --cross-file win32.crossfile \
--cross-file contrib/x86_64-w64-mingw32/share/meson/cross/contrib.iniDoc 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 --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 avlc_*.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.
[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.
# 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 relevantmoduledir folder from modules/ folder using Cargo, ending with -rs
and without version control creation given that VLC sources are already
versioned:
cd modules/moduledir && cargo new --lib --vcs none mynewmodule-rsFor instance, a new video filter would be created with:
cd modules/video_filter && cargo new --lib --vcs none mynewfilter-rsThen, you can modify the Cargo.toml file to include the different crates
the module will be using from the workspace.
[package]
name = "mynewmodule-rs"
edition = "2021"
version = "0.1.0"
[dependencies]
vlcrs-messages.workspace = trueThe module will need to be listed into modules/Cargo.toml members:
[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:
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
endifFor meson, you will use the vlc_rust_modules dictionary from the
parent meson.build file:
vlc_rust_modules += {
'name' : 'mynewmodule_rs',
'sources' : files('mynewmodule-rs/src/lib.rs'),
'cargo_toml' : files('mynewmodule-rs/Cargo.toml'),
}Creating a new plugin requires the definition of the plugin manifest to
expose the different modules from there. This is done through themodule!{} macro:
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:
* Stream number (1 byte). Only bits 0-3 are used as there can only
be up to 8 subtitles.
* Packet number(1 byte). The first packet starts at 0; the last
packet of a subtitle has the 7th bit set. A subtitle can be
broken up into several packets when it is multiplexed into the
MPEG stream.
* Subtitle image number (2 bytes).
* Length of the rest of the packet (described in the next section).================ SUBTITLE PACKETS
The information below comes immediately after the packet header in the
same packet.
- Packet length (2 bytes)
The assembled subtitle, with stripped sequence numbers
- Option byte (1 byte)
This is normally set to 0x2e; bit 3 indicates that a field with the
display-time of the subtitle is present. What the other bits do is not
known.
- Time to display the subtitle (4 bytes)
This in 1/90000'th of a second. This is only present if bit 3 above is
set. This value specifies how long the subtitle will be shown, not
when. When the subtitle is to be shown is specified in the PTS in the
PES header.
X position (2 bytes)
Y position (2 bytes)
Width (2 bytes)
This value must be even.
- Height (2 bytes)
This value must be even.
Unknown (1 byte)
4 Color Palette Entries (16 bytes)
A pallette entry has the following format:
- Y (1 byte)
- U (1 byte)
- V (1 byte)
- Transparency (1 byte). A 0 value means totally transparent and
255 totally opaque.
Command (1 byte)
if cmd>>6 == 1 then a shift command and (cmd>>4)&3 is the
direction. 0=top, 1=left, 2 = right, 3 = bottom
- Odd-field data offset (2 bytes)
Image data is stored interlaced with the even lines first. This offset
specifies where the first odd-line image data starts.
IMAGE DATA
Then subtitle image data follows the above control information. Image
lines are interlaced. That is line 0 is comes first, then line 2, then
line 4. After all of the even lines, comes line 1, then line 3,
etc.. The subtitle aspect ratio is not to be scaled. It is independent
of the aspect ratio of the underlying image which it is to be
overlayed on top of.
The image is encoded using two bits per pixel that select a palette
entry except that value 0 starts a limited run-length encoding for
color 0. When 0 is seen, the next two bits encode one less than
the number of pixels, so we can encode run lengths from 1 to 4. These
get filled with the color in palette entry 0.
The encoding of each line is padded to a whole number of bytes. The
first field is padded to an even byte length and the complete subtitle
is padded to a 4-byte multiple that always include one zero byte at
the end.
================ SUBTITLING TOOLS
Philips SVCD tools can create SVCD subtitles, but it only works under
NT4. It is not that easy to find anymore. (dvd.da.ru, doom9.net or
similar perhaps?). It can create good subtitled SVCDs. Subtitles are
created in a text editor. Another tool is called submux. Last version
I tried doesn't do renumber presentation time stamps properly, but
many media players (now) can tolerate this.
The subtitles you get from tools like "I-author" are not SVCD
subtitles, they are CVD subtitles. They look similar to DVD subtitles,
but the run-length encoding (RLE) and command format are different.
Test Checkasm Ext Docs Benchmarking (`test/checkasm/ext/docs/benchmarking.md`)
@page benchmarking Benchmarking
This guide explains how to use checkasm's benchmarking capabilities to accurately measure
and compare the performance of optimized assembly implementations against reference code.
@tableofcontents
@section bench_basic Basics
@subsection bench_workflow Benchmark Workflow
Benchmarking in checkasm follows the same structure as correctness testing, with
an additional call to checkasm_bench_new():
@code{.c}
BUF_RECT(uint8_t, src, 64, 64);
BUF_RECT(uint8_t, dst_c, 64, 64);
BUF_RECT(uint8_t, dst_a, 64, 64);
checkasm_declare(void, uint8_t *dst, ptrdiff_t dst_stride,
const uint8_t *src, ptrdiff_t src_stride,
int w, int h);
if (checkasm_check_func(dsp->filter, "filter_64x64")) {
INITIALIZE_BUF_RECT(src);
CLEAR_BUF_RECT(dst_c);
CLEAR_BUF_RECT(dst_a);
// Correctness testing
checkasm_call_ref(dst_c, dst_c_stride, src, src_stride, 64, 64);
checkasm_call_new(dst_a, dst_a_stride, src, src_stride, 64, 64);
checkasm_check_rect_padded(dst_c, dst_c_stride,
dst_a, dst_a_stride, 64, 64, "dst");
// Benchmarking
checkasm_bench_new(checkasm_alternate(dst_a, dst_c), dst_a_stride,
src, src_stride, 64, 64);}
checkasm_report("filter");
@endcode
@subsection bench_cli Running Benchmarks
Enable benchmarking with the --bench flag:
@code{.bash}
Quick benchmark (uses default duration)
./checkasm --bench
Longer benchmark for more accurate results (10ms per function)
./checkasm --bench --duration=10000
Benchmark specific functions only
./checkasm --bench --function='filter_*'
Verbose output showing all timing measurements
./checkasm --bench --verbose
@endcode
The --duration parameter controls how long (in microseconds) each function is
benchmarked. Longer durations provide more accurate results but take more time.
The default is typically sufficient for most cases.
@subsection bench_export Exporting Results
checkasm can export benchmark results in multiple formats:
@code{.bash}
CSV format (suitable for spreadsheets)
./checkasm --bench --csv > results.csv # without column headers
./checkasm --bench --csv --verbose > results.csv # with column headers
JSON format (for programmatic analysis, includes all data)
./checkasm --bench --json > results.json
HTML format (interactive visualizations)
./checkasm --bench --html > results.html
@endcode
The JSON output format includes all measurement data and detailed statistical
parameters, including kernel density estimates, regression parameters, and confidence
intervals. The HTML output displays this same data in the form of interactive charts.
@section bench_methodology Statistical Methodology
@subsection bench_lognormal Log-Normal Distribution Modeling
checkasm models execution time as a log-normal distribution, which is well-suited
for performance measurements because:
- Execution time is always positive
- Performance variations tend to be multiplicative (e.g. power states) rather than additive
- 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:
- The benchmarking loop itself
- The function call mechanism
- 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
@endcodeDisable 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
niceto prioritize the benchmark process:
@code{.bash}
nice -n -20 ./checkasm --bench
@endcode
Thermal Throttling:
- Ensure adequate cooling to prevent thermal throttling
- Allow sufficient cool-down time between benchmark runs
- Monitor CPU temperature during long benchmark sessions
@subsection bp_alignment Cache Alignment
checkasm automatically warms up caches before taking measurements, but you
should be aware of cache effects:
- L1/L2 Cache: Most optimized functions should fit in L2 cache
- Data Working Set: Buffers allocated with BUF_RECT() are properly aligned
and sized to avoid cache conflicts. Failure to do so may lead to performance
hits from unaligned memory accesses or cache thrashing.
For functions with large working sets that don't fit in cache, benchmark results
may reflect cache miss behavior, which is often realistic for real-world usage.
@subsection bp_alternating Buffer Alternation
Use checkasm_alternate() when benchmarking to prevent cache pollution:
@code{.c}
// Good: alternates between dst_a and dst_c to prevent cache hits from
// previous iterations
checkasm_bench_new(checkasm_alternate(dst_a, dst_c), dst_a_stride,
src, src_stride, w, h);
// Acceptable: always writes to dst_a
checkasm_bench_new(dst_a, dst_a_stride, src, src_stride, w, h);
@endcode
Alternating buffers ensures that benchmarks are not stalled by previous
access to the same data buffer from the prior loop iteration.
@subsection bp_realistic Realistic Test Data
Use realistic input data for benchmarks:
@code{.c}
// For general data processing: use INITIALIZE_BUF() which includes common edge cases
INITIALIZE_BUF_RECT(src);
// For specific patterns: use domain-appropriate data
checkasm_randomize_normf(audio_buf, len); // Audio: normal distribution
// For worst-case analysis: test pathological inputs
generate_worst_case_pattern(buf, len);
@endcode
The input data can significantly affect performance due to:
- Data-dependent branches in the implementation
- SIMD instruction efficiency varying with data patterns
- Cache behavior depending on data values
If your function's performance varies significantly with the input data or
configuration, consider looping over all such configurations and running
checkasm_bench_new() for each, to measure an overall average.
@subsection bp_configurations Choosing Configurations
When benchmarking functions that support multiple sizes or configurations:
Option 1: Benchmark all configurations
@code{.c}
for (int w = 4; w <= 128; w <<= 1) {
if (checkasm_check_func(dsp->filter, "filter_w%d", w)) {
for (int h = 4; h <= 128; h <<= 1) {
// Test for correctness
// ...
// Benchmark each configuration
checkasm_bench_new(dst, dst_stride, src, src_stride, w, h);
}
// Reports geometric mean of all configurations
}}
@endcode
Option 2: Benchmark representative sizes separately
@code{.c}
const int sizes[][2] = { {16, 16}, {16, 32}, {64, 16}, {64, 32} };
for (int i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++) {
int w = sizes[i][0], h = sizes[i][1];
// Separate check_func call = separate benchmark report
if (checkasm_check_func(dsp->filter, "filter_%dx%d", w, h)) {
// Test for correctness
// ...
checkasm_bench_new(dst, dst_stride, src, src_stride, w, h);
}}
@endcode
Option 3: Benchmark only the limiting case
@code{.c}
for (int w = 4; w <= 128; w <<= 1) {
if (checkasm_check_func(dsp->filter, "filter_w%d", w)) {
for (int h = 4; h <= 128; h <<= 1) {
// Test all for correctness
// ...
}
// Benchmark only the largest size
checkasm_bench_new(dst, dst_stride, src, src_stride, w, 128);
}}
@endcode
Choose based on your needs:
- Option 1: General performance across all sizes
- Option 2: Specific performance for important sizes
- Option 3: Best-case or worst-case performance
@section bench_interpreting Interpreting Results
@subsection interp_output Understanding Output
checkasm's benchmark output provides several pieces of information. Pass--verbose to see all timing measurements.
@code{.plaintext}
Benchmark results:
name cycles +/- stddev time (nanoseconds) (vs ref)
nop: 2.4 +/- 0.0 1.2 ns +/- 0.1
filter_c: 64.9 +/- 75.4 33.8 ns +/- 40.8
filter_sse2: 46.9 +/- 13.3 24.4 ns +/- 7.8 ( 1.22x)
filter_avx2: 20.6 +/- 1.2 10.7 ns +/- 1.4 ( 3.15x)
@endcode
Columns:
- name: Function name (with ISA suffix for optimized versions)
- cycles: Estimated CPU cycles per call (mean ยฑ standard deviation)
- time: Estimated nanoseconds per call (mean ยฑ standard deviation)
- (vs ref): Speedup relative to the reference (C) implementation
What to look for:
- Lower cycle counts indicate better performance
- Standard deviation shows measurement reliability
- Speedup factors show optimization effectiveness
@subsection interp_variance High Variance
High standard deviation (large ยฑ values) can indicate:
System noise: Background processes, interrupts, frequency scaling
- Solution: Follow best practices in @ref bp_system_state
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
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:
Baseline measurements: Save benchmark results for your codebase:
@code{.bash}
./checkasm --bench --json > baseline.json
@endcodeAfter changes: Run benchmarks again:
@code{.bash}
./checkasm --bench --json > current.json
@endcodeCompare: 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)
@endcodex86/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:
Use fixed random seed:
@code{.bash}
./checkasm --bench 12345 # Use seed 12345
@endcode
This ensures the same test data patterns across runs.Document system state:
- CPU model and frequency settings
- Compiler version and flags
- Operating system and kernel version
- checkasm version/commit
Multiple runs:
Run benchmarks multiple times and verify consistency:
@code{.bash}
for i in {1..5}; do
./checkasm --bench --function='filter_*' | tee run_$i.txt
done
@endcode
Test Checkasm Ext Docs Integration (`test/checkasm/ext/docs/integration.md`)
@page integration Integration Guide
This guide covers how to integrate checkasm into existing or new projects.
@tableofcontents
@section config_options Configuration Options
Checkasm uses optional C11 features inside public header files. For safety, these are only enabled
conservatively based on the C11 standard version signaled by the compiler. Sometimes, these checks
could be relaxed, such as when the target project is explicitly compiled with -std=c99 or older,
but using a modern compiler that would still understand and accept C11 features. In this case,
these feature checks may be checked by the user and defined before including checkasm.h. See
@ref config for a list of such options.
@section cpu_flags CPU Flags
CPU flags represent instruction set extensions and features that your optimized implementations
depend on (e.g., SSE2, AVX2, NEON). You must define an array of these flags, so checkasm can
systematically test each implementation variant.
@note checkasm does not provide CPU detection or runtime dispatch functionality on its own.
It is a pure testing framework, and as such, should not be used as a runtime dependency of your
project. This means that your project must implement its own CPU feature detection and dispatch
mechanisms for production use. checkasm plugs into these existing mechanisms during testing.
@subsection defining_cpu_flags Defining CPU Flags
Assuming you have a set of CPU flags defined in your project, e.g.,
@code{.h}
// my_cpu.h
enum {
CPU_FLAG_SSE2 = 1 << 0,
CPU_FLAG_SSSE3 = 1 << 1,
CPU_FLAG_SSE41 = 1 << 2,
CPU_FLAG_AVX2 = 1 << 3,
CPU_FLAG_AVX512 = 1 << 4,
// ...
};
typedef uint64_t MyCpuFlags;
MyCpuFlags detect_cpu_flags(void);
@endcode
Then create a CheckasmCpuInfo array describing each flag, terminated by {0}:
@code{.c}
// checkasm.c
static const CheckasmCpuInfo cpu_flags[] = {
{ "SSE2", "sse2", CPU_FLAG_SSE2 },
{ "SSSE3", "ssse3", CPU_FLAG_SSSE3 },
{ "SSE4.1", "sse41", CPU_FLAG_SSE41 },
{ "AVX2", "avx2", CPU_FLAG_AVX2 },
{ "AVX512", "avx512", CPU_FLAG_AVX512 },
{0} // array terminator
};
// This ordering means:
// - SSE2 functions are tested with just CPU_FLAG_SSE2
// - SSSE3 functions are tested with CPU_FLAG_SSE2 | CPU_FLAG_SSSE3
// - SSE4.1 functions are tested with CPU_FLAG_SSE2 | CPU_FLAG_SSSE3 | CPU_FLAG_SSE41
// - And so on...
@endcode
Each entry contains:
- name: Human-readable name displayed in output (e.g., "SSE4.1")
- suffix: Short suffix used in function names and filtering (e.g., "sse41")
- flag: The bitfield value from your CPU flag enum
@note Flags are tested in the order defined in the array. Each test inherits
flags from all previous entries, allowing checkasm to test progressively more
advanced instruction sets.
Register the CPU flags with checkasm via the CheckasmConfig structure:
@code{.c}
// checkasm.c
static const CheckasmCpuInfo cpu_flags[] = {
// ...
{0}
};
int main(int argc, const char *argv[])
{
CheckasmConfig config = {
.cpu_flags = cpu_flags,
// ...
};
// Set initial CPU flags using your own runtime detection function
config.cpu = detect_cpu_flags();
return checkasm_main(&config, argc, argv);}
@endcode
@subsection extra_cpu_flags Extra CPU Flags
You can include additional flags in CheckasmConfig.cpu that aren't in CheckasmConfig.cpu_flags.
These are transparently passed through to checkasm_get_cpu_flags() and can be used for
modifier flags like CPU_FLAG_FAST_* that don't require separate testing, but should
instead always be assumed to be available when matching function implementations.
@section selecting_functions Selecting Functions
There are two common strategies for selecting the correct function implementation during tests,
depending on how your project structures its dispatch mechanism:
@note Choose the strategy that matches your project's existing architecture. If you are
developing a new library, we recommend the first approach.
@subsection mask_callback Strategy 1: Mask Callback
If your project has a mask_cpu_flags (or cpu_flags_override) function that
updates an internal static bitmask used internally by dispatch table getters, e.g.:
@code{.c}
// my_cpu.c
static unsigned cpu_flags = 0;
static unsigned cpu_flags_mask = -1;
unsigned get_cpu_flags(void)
{
return cpu_flags & cpu_flags_mask;
}
void mask_cpu_flags(unsigned flags)
{
cpu_flags_mask = flags;
}
@endcode
@code{.c}
// my_foo_dsp.c
void foo_dsp_init(foo_dsp *dsp)
{
const unsigned cpu_flags = get_cpu_flags();
// Initialize with C implementations
dsp->add = add_c;
dsp->sub = sub_c;
// Override with optimized versions based on cpu_flags
if (cpu_flags & CPU_FLAG_SSE2) {
dsp->add = add_sse2;
}
if (cpu_flags & CPU_FLAG_AVX2) {
dsp->add = add_avx2;
dsp->sub = sub_avx2;
}
// ...}
@endcode
Then, in your checkasm main file, you can set that directly as a callback in CheckasmConfig.set_cpu_flags:
@code{.c}
// You may need a wrapper to fix the function signature
static void set_cpu_flags(CheckasmCpu cpu)
{
mask_cpu_flags((unsigned) cpu);
}
CheckasmConfig config = {
// ...
.set_cpu_flags = set_cpu_flags,
};
@endcode
With this approach, checkasm will automatically call your set_cpu_flags() function whenever
it changes the active CPU feature set during testing.
@note This will always be a subset of the initially detected CPU flags provided in CheckasmConfig.cpu,
so there is no meaningful distinction between a mask_cpu_flags (that masks out real CPU
flags) and a cpu_flags_override (that overrides them wholesale). Both can be used
as a callback.
@subsection direct_getters Strategy 2: Direct Getters
If your project uses dispatch functions that directly accept a CPU mask parameter (e.g.,void foo_dsp_init(foo_dsp *dsp, unsigned cpu_flags)), you can call them within
each test using checkasm_get_cpu_flags():
@code{.c}
// my_foo_dsp.c
void foo_dsp_init(foo_dsp *dsp, unsigned cpu_flags)
{
// Initialize *dsp based on the provided CPU flags
}
@endcode
Then, in your checkasm test files:
@code{.c}
// check_foo_dsp.c
void check_foo_dsp(void)
{
foo_dsp dsp;
foo_dsp_init(&dsp, checkasm_get_cpu_flags()); // Get current test flags
// Now test dsp.add, dsp.sub, etc.
// ...}
@endcode
The same applies if your project uses individual function getters likeadd_func_t get_add_func(unsigned cpu_flags) instead of dispatch tables / dsp structs.
@section organizing_tests Organizing Multiple Tests
For larger projects, organize tests by module:
@code{.c}
// Test module declarations
void checkasm_check_mc(void);
void checkasm_check_pixel(void);
void checkasm_check_filmgrain(void);
// ...
static const CheckasmTest tests[] = {
{ "mc", checkasm_check_mc },
{ "pixel", checkasm_check_pixel },
{ "filmgrain", checkasm_check_filmgrain },
// ...
{0}
};
@endcode
Then implement each test in separate files:
@code{.c}
// check_mc.c
#include <checkasm/test.h>
#include "mc_dsp.h"
static void test_mc_func1(const mc_dsp *dsp);
static void test_mc_func2(const mc_dsp *dsp);
static void test_mc_func3(const mc_dsp *dsp);
// ...
void checkasm_check_mc(void)
{
mc_dsp dsp;
mc_dsp_init(&dsp, checkasm_get_cpu_flags());
test_func1(&dsp);
test_func2(&dsp);
test_func3(&dsp);
checkasm_report("group1");
test_func4(&dsp);
test_func5(&dsp);
test_func6(&dsp);
checkasm_report("group2");
// ...}
@endcode
@section integration_next_steps Next Steps
Now that you know how to integrate checkasm with your project's architecture,
dive deeper into best practices and advanced patterns for writing comprehensive
tests.
Next: @ref writing_tests
Test Checkasm Ext Docs Introduction (`test/checkasm/ext/docs/introduction.md`)
@mainpage Introduction
checkasm is a robust, portable testing and benchmarking framework specifically designed for
validating optimized assembly implementations against reference C code. Originally developed for the
x264 encoder project, checkasm has grown considerably while in use by FFmpeg and dav1d,
and now provides comprehensive correctness verification, crash detection,
and accurate performance measurements across multiple architectures.
This project in particular stems from an effort to combine and improve upon multiple diverging
checkasm forks in the wild (x264, FFmpeg, dav1d, etc.). It is a direct descendent of the dav1d
variant, with all relevant enhancements from the FFmpeg fork merged in. It has since evolved
into a fully fledged cross-platform standalone library and benchmarking framework.
@tableofcontents
@section features Key Features
@subsection features_correctness Correctness Verification
- Automated comparison between reference (C) and optimized (assembly) implementations, including
support for fuzzing implementations over multiple seeds and data patterns. - Detection of ABI violations such as register misuse, illegal opcodes,
or missing state cleanup. - Extensive data handling utilities for defining, initializing, and comparing
input/output buffers of various types, sizes and dimensionalities. - Crash resilience including signal handling and stack smashing detection.
@subsection features_benchmarking Performance Benchmarking
- Accurate cycle counting using platform-specific high-resolution timers.
- Statistical analysis with robust outlier rejection, logarithmic regression and confidence intervals.
- Comparative measurements showing speedup of optimized code including comprehensive HTML reports.
- Minimal overhead to ensure realistic performance data, and high throughput.
@subsection features_platform Platform Support
The following architectures are explicitly supported for asm verification, with
the listed detection mechanisms for typical asm mistakes:
- x86, x86-64: registers, stack, AVX2, MMX/FPU state
- ARM, ARM64 (aarch64): registers, stack, VFP state
- RISC-V: registers, stack, vector state
- LoongArch (32, 64): registers
- PowerPC (64le): none
In addition, hardware timers are available for benchmarking purposes on all of
the listed platforms (except RISCV-V), with fall-backs to generic OS-specific APIs.
@section links Project Links
@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:
- Allocate (aligned) buffers for test data
- Declare the function signature with checkasm_declare()
- Check if the function should be tested with checkasm_check_func()
- Initialize test inputs and clear output buffers
- Call both reference (checkasm_call_ref()) and new (checkasm_call_new()) implementations
- Compare results with checkasm_check2d() or similar
- Benchmark the new implementation with checkasm_bench_new()
- Report results with checkasm_report() (optional)
@section naming_conventions API Naming Conventions
checkasm supports two API naming styles:
@subsection modern_api Modern API (Recommended)
The modern API uses the checkasm_ prefix for all functions:
@code{.c}
checkasm_declare(void, uint8_t *dst, const uint8_t *src, int len);
checkasm_check_func(dsp->func, "func_name")
checkasm_call_ref(dst_c, src, len);
checkasm_call_new(dst_a, src, len);
checkasm_check1d(uint8_t, dst_c, dst_a, len, "dst");
checkasm_bench_new(dst_a, src, len);
checkasm_report("func_name");
checkasm_fail()
checkasm_alternate(buf0, buf1)
// ...
@endcode
@subsection legacy_api Legacy/Short API
For convenience and backwards compatibility, shorter aliases are available:
@code{.c}
declare_func(void, uint8_t *dst, const uint8_t *src, int len);
check_func(dsp->func, "func_name")
call_ref(dst_c, src, len);
call_new(dst_a, src, len);
// checkasm_check1d() has no short alias
bench_new(dst_a, src, len);
report("func_name");
fail()
alternate(buf0, buf1)
@endcode
@note Both naming styles are fully supported and can be mixed within the same
test file. However, for consistency and readability in documentation and new
code, we recommend using the modern checkasm_ prefixed names.
@section best_practices Best Practices
The remainder of this guide focuses on best practices, common patterns, and advanced testing scenarios.
@subsection bp_buffer_allocation Buffer Allocation
Always use properly aligned buffers for testing:
@code{.c}
// For simple arrays
CHECKASM_ALIGN(uint8_t buf[1024]);
// For 2D buffers with automatic padding and stride calculation
// - Defines dst as a pointer to a 64x32 area, and dst_stride (in bytes)
BUF_RECT(uint8_t, dst, 64, 32);
@endcode
Important: Apply CHECKASM_ALIGN() to each buffer individually:
@code{.c}
// Correct
CHECKASM_ALIGN(uint8_t buf1[32]);
CHECKASM_ALIGN(uint8_t buf2[32]);
// Wrong - only buf1 will be aligned
CHECKASM_ALIGN(uint8_t buf1[32], buf2[32]);
@endcode
@subsection bp_buffer_init Buffer Initialization
checkasm provides several buffer initialization functions:
@code{.c}
// Random data (uniformly distributed)
RANDOMIZE_BUF(buf);
checkasm_randomize(buf, sizeof(buf)); // Equivalent to the above macro
checkasm_randomize_mask8 (buf8, width, 0x0F); // Constrain value range
checkasm_randomize_mask16(buf16, width, 1023); // Random 10-bit values
// Pathological test patterns (mix of edge cases and random bytes)
INITIALIZE_BUF(buf);
checkasm_init(buf, sizeof(buf)); // Equivalent to the above macro
checkasm_init_mask16(buf16, width, (1 << 12) - 1); // Constrain to 12-bit range
// Clear to a constant value
CLEAR_BUF(buf);
checkasm_clear(buf, sizeof(buf)); // Equivalent to the above macro
checkasm_clear16(buf16, width, 0x1234);
@endcode
For 2D buffers created with BUF_RECT():
@code{.c}
BUF_RECT(uint8_t, src, 64, 32);
INITIALIZE_BUF_RECT(src); // Initialize with pathological test bytes
RANDOMIZE_BUF_RECT(tmp); // Fill with random data
CLEAR_BUF_RECT(dst); // Clear to constant data (currently 0xAA)
@endcode
See @ref memory for a full list of buffer initialization functions.
@subsection bp_test_loops Testing Multiple Configurations
Test functions across multiple configurations (e.g. block sizes) to ensure
comprehensive coverage:
@code{.c}
BUF_RECT(uint8_t, src, 128, 128);
BUF_RECT(uint8_t, dst_c, 128, 128);
BUF_RECT(uint8_t, dst_a, 128, 128);
checkasm_declare(void, uint8_t *dst, ptrdiff_t dst_stride,
const uint8_t *src, ptrdiff_t src_stride,
int w, int h);
// Test various power-of-two block sizes, from 4x4 to 128x128
for (int h = 4; h <= 128; h <<= 1) {
for (int w = 4; w <= 128; w <<= 1) {
if (checkasm_check_func(get_func(w), "func_%dx%d", w, h)) {
// Initialize test data - doing this inside the loop picks a
// different data pattern for each test
INITIALIZE_BUF(src);
CLEAR_BUF(dst_c);
CLEAR_BUF(dst_a);
// Test this configuration
checkasm_call_ref(dst_c, dst_c_stride, src, src_stride, w, h);
checkasm_call_new(dst_a, dst_a_stride, src, src_stride, w, h);
checkasm_check_rect_padded(dst_c, dst_c_stride,
dst_a, dst_a_stride, w, h, "dst");
// Benchmark this configuration
checkasm_bench_new(checkasm_alternate(dst_a, dst_c), dst_a_stride,
src, src_stride, w, h);
}
}}
checkasm_report("func");
@endcode
@subsection bp_random_params Using Random Parameters
Use checkasm_rand() and related functions to generate diverse test inputs:
@code{.c}
// Random integers
int byte = checkasm_rand() & 0xFF;
uint32_t flags = checkasm_rand_uint32();
// Random floats
double d = checkasm_randf(); // [0.0, 1.0)
float f = (float) checkasm_randf() * 100.0f; // [0.0, 100.0)
// Normal distribution
double normal = checkasm_rand_norm(); // mean=0, stddev=1
checkasm_randomize_normf(buf, len); // Fill buffer with N(0,1)
@endcode
See @ref rng for a full list of random number generation functions.
@subsection bp_reporting Organizing Reports
Group related functions and use checkasm_report() strategically:
@code{.c}
static void check_add_functions(const DSPContext *dsp)
{
// Test multiple related functions
for (int bpc = 8; bpc <= 12; bpc += 2) {
if (checkasm_check_func(get_add8(bpc), "add8_%dbpc", bpc)) {
// Test add8
}
if (checkasm_check_func(get_add16(bpc), "add16_%dbpc", bpc)) {
// Test add16
}
}
// Report once for the whole group
checkasm_report("add");}
@endcode
For very simple tests with only a few functions, for which there is no
logical grouping, or for miscellaneous functions, this can be left out.
Any functions without a checkasm_report() call after them will be implicitly
reported under the name of the test itself. Note that this only makes sense
if such functions are the last functions being tested in a given test, since
any later checkasm_report() call would otherwise include all prior functions.
@section common_patterns Common Test Patterns
@subsection pattern_2d 2D Buffer Processing
For functions operating on 2D buffers with stride:
@code{.c}
static void check_filter(const DSPContext *dsp)
{
// Define padded 64x64 buffers
BUF_RECT(uint8_t, src, 64, 64);
BUF_RECT(uint8_t, dst_c, 64, 64);
BUF_RECT(uint8_t, dst_a, 64, 64);
checkasm_declare(void, uint8_t *dst, ptrdiff_t dst_stride,
const uint8_t *src, ptrdiff_t src_stride,
int w, int h);
for (int w = 4; w <= 64; w <<= 1) {
if (checkasm_check_func(dsp->filter, "filter_w%d", w)) {
// Check multiple heights
for (int h = 4; h <= 64; h <<= 1) {
INITIALIZE_BUF_RECT(src);
CLEAR_BUF_RECT(dst_c);
CLEAR_BUF_RECT(dst_a);
checkasm_call_ref(dst_c, dst_c_stride, src, src_stride, w, h);
checkasm_call_new(dst_a, dst_a_stride, src, src_stride, w, h);
// Check buffers including padding
checkasm_check_rect_padded(dst_c, dst_c_stride,
dst_a, dst_a_stride,
w, h, "dst");
}
// Benchmark only the full height configuration
checkasm_bench_new(checkasm_alternate(dst_a, dst_c), dst_a_stride,
src, src_stride, w, 64);
}
}
checkasm_report("filter");}
@endcode
@subsection pattern_state State-Based Functions
For functions that modify internal state or have side effects:
@code{.c}
static void check_decoder(const DecoderContext *dec)
{
DecoderState state_c, state_a;
uint8_t bitstream[128];
// Initialize bitstream with random data
RANDOMIZE_BUF(bitstream);
checkasm_declare(int, DecoderState *state, const uint8_t *data, int len);
if (checkasm_check_func(dec->decode, "decode")) {
// Initialize both states identically
init_decoder_state(&state_c, bitstream, 128);
init_decoder_state(&state_a, bitstream, 128);
// Decode and compare
int result_c = checkasm_call_ref(&state_c, bitstream, 128);
int result_a = checkasm_call_new(&state_a, bitstream, 128);
// Compare return values
if (result_c != result_a) {
if (checkasm_fail()) {
fprintf(stderr, "return value mismatch: %d vs %d\n",
result_c, result_a);
}
}
// Compare final states (optional)
//
// The validity of this check depends on whether or not `DecoderState`
// has padding bytes or non-deterministic internal state.
checkasm_check1d(uint8_t, &state_c, &state_a, sizeof(DecoderState),
"decoder state");
checkasm_bench_new(&state_a, bitstream, 128);
}
checkasm_report("decode");}
@endcode
@subsection pattern_multi_output Multiple Outputs
For functions that produce multiple output values:
@code{.c}
static void check_stats(const DSPContext *dsp)
{
CHECKASM_ALIGN(uint8_t buf[64 * 64]);
checkasm_declare(int, const uint8_t *buf, int len,
unsigned *variance, unsigned *sum);
if (checkasm_check_func(dsp->compute_stats, "compute_stats")) {
unsigned var_c, var_a, sum_c, sum_a;
INITIALIZE_BUF(buf);
int result_c = checkasm_call_ref(buf, 64*64, &var_c, &sum_c);
int result_a = checkasm_call_new(buf, 64*64, &var_a, &sum_a);
// Compare all outputs
if (result_c != result_a || var_c != var_a || sum_c != sum_a) {
if (checkasm_fail()) {
fprintf(stderr, "result: %d vs %d, var: %u vs %u, sum: %u vs %u\n",
result_c, result_a, var_c, var_a, sum_c, sum_a);
}
}
checkasm_bench_new(buf, 64*64, &var_a, &sum_a);
}
checkasm_report("compute_stats");}
@endcode
@subsection pattern_custom_input Custom Input Generation
For functions requiring specific test patterns:
@code{.c}
// Generate worst-case inputs to stress the implementation
static void generate_worst_case(uint16_t *buf, int len, int bitdepth_max)
{
// Create reverse sorted sequence of input values
for (int i = 0; i < len; i++)
buf[i] = (len - 1 - i) & bitdepth_max;
}
static void check_transform(const DSPContext *dsp)
{
#define WIDTH 64
CHECKASM_ALIGN(int16_t src [WIDTH]);
CHECKASM_ALIGN(int16_t dst_c[WIDTH]);
CHECKASM_ALIGN(int16_t dst_a[WIDTH]);
checkasm_declare(void, int16_t *dst, const int16_t *src, int width);
if (checkasm_check_func(dsp->transform, "transform")) {
// Test with both random and worst-case inputs
for (int pattern = 0; pattern < 2; pattern++) {
if (pattern == 0) {
INITIALIZE_BUF(src); // Random input
} else {
generate_worst_case(src, WIDTH, 32767); // Worst case pattern
}
CLEAR_BUF(dst_c);
CLEAR_BUF(dst_a);
checkasm_call_ref(dst_c, src, WIDTH);
checkasm_call_new(dst_a, src, WIDTH);
checkasm_check1d(int16_t, dst_c, dst_a, WIDTH, "dst");
}
checkasm_bench_new(checkasm_alternate(dst_a, dst_c), src, WIDTH);
}
checkasm_report("transform");}
@endcode
@section advanced_topics Advanced Topics
@subsection adv_float Floating-Point Comparison
For functions producing floating-point results, use tolerance-based comparison:
@code{.c}
static void check_float_func(const DSPContext *dsp)
{
#define WIDTH 128
CHECKASM_ALIGN(float src [WIDTH]);
CHECKASM_ALIGN(float dst_c[WIDTH]);
CHECKASM_ALIGN(float dst_a[WIDTH]);
checkasm_declare(void, float *dst, const float *src, int len);
if (checkasm_check_func(dsp->process_float, "process_float")) {
checkasm_randomize_normf(src, WIDTH);
checkasm_call_ref(dst_c, src, WIDTH);
checkasm_call_new(dst_a, src, WIDTH);
// Compare with ULP (Units in Last Place) tolerance
// Note: cannot use checkasm_check1d() here
const int max_ulp = 1;
checkasm_check2d(float_ulp, dst_c, 0, dst_a, 0, WIDTH, 1, "dst", max_ulp);
// Or use absolute epsilon tolerance
// if (!checkasm_float_near_abs_eps_array(dst_c, dst_a, 1e-6f, WIDTH)) {
// checkasm_fail();
// }
checkasm_bench_new(checkasm_alternate(dst_a, dst_c), src, WIDTH);
}
checkasm_report("process_float");}
@endcode
@subsection adv_padding Padding and Over-Write Detection
Detect when functions write beyond their intended boundaries:
@code{.c}
static void check_bounds(const DSPContext *dsp)
{
BUF_RECT(uint8_t, dst_c, 64, 64);
BUF_RECT(uint8_t, dst_a, 64, 64);
checkasm_declare(void, uint8_t *dst, ptrdiff_t stride, int w, int h);
if (checkasm_check_func(dsp->fill, "fill")) {
const int w = 64, h = 64;
CLEAR_BUF_RECT(dst_c);
CLEAR_BUF_RECT(dst_a);
checkasm_call_ref(dst_c, dst_c_stride, w, h);
checkasm_call_new(dst_a, dst_a_stride, w, h);
// Standard check (no padding)
checkasm_check2d(uint8_t, dst_c, dst_c_stride,
dst_a, dst_a_stride, w, h, "dst");
// Check with padding detection (detects writes outside wรh)
checkasm_check_rect_padded(dst_c, dst_c_stride,
dst_a, dst_a_stride, w, h, "dst");
// Allow over-write up to 16-element alignment on right edge
checkasm_check_rect_padded_align(dst_c, dst_c_stride,
dst_a, dst_a_stride,
w, h, "dst", 16, 1);
checkasm_bench_new(checkasm_alternate(dst_a, dst_c), dst_a_stride, w, h);
}
checkasm_report("fill");}
@endcode
@subsection bench_multiple Benchmarking Multiple Configurations
For functions that can be benchmarked at multiple configurations:
@code{.c}
if (checkasm_check_func(dsp->filter, "filter_w%d", w)) {
for (int h = 4; h <= 64; h <<= 1) {
// Test all heights for correctness
checkasm_call_ref(dst_c, dst_c_stride, src, src_stride, w, h);
checkasm_call_new(dst_a, dst_a_stride, src, src_stride, w, h);
checkasm_check2d(uint8_t, dst_c, dst_c_stride,
dst_a, dst_a_stride, w, h, "dst");
// Benchmark each configuration
checkasm_bench_new(checkasm_alternate(dst_a, dst_c), dst_a_stride,
src, src_stride, w, h);
}
// The framework will report the geometric mean of all benchmark runs for
// the same checkasm_check_func() call}
checkasm_report("filter_w%d", w);
@endcode
Alternatively, you could call checkasm_check_func() on each configuration
to get a separate benchmark report for each size, or call checkasm_bench_new()
only on the largest input size to test the limiting behavior.
@subsection adv_bitdepth Multi-Bitdepth Testing
For codecs supporting multiple bit depths:
@code{.c}
static void check_pixfunc(void)
{
DSPContext dsp;
#define WIDTH 64
CHECKASM_ALIGN(uint16_t src [WIDTH]);
CHECKASM_ALIGN(uint16_t dst_c[WIDTH]);
CHECKASM_ALIGN(uint16_t dst_a[WIDTH]);
checkasm_declare(void, uint16_t *dst, const uint16_t *src, int len);
for (int bpc = 10; bpc <= 12; bpc += 2) {
const int bitdepth_max = (1 << bpc) - 1;
dsp_context_init(&dsp, checkasm_get_cpu_flags(), bpc);
if (checkasm_check_func(dsp->process, "process_%dbpc", bpc)) {
// Randomize within valid bit depth range
checkasm_randomize_mask16(src, WIDTH, bitdepth_max);
checkasm_call_ref(dst_c, src, WIDTH);
checkasm_call_new(dst_a, src, WIDTH);
checkasm_check1d(uint16_t, dst_c, dst_a, WIDTH, "dst");
checkasm_bench_new(checkasm_alternate(dst_a, dst_c), src, WIDTH);
}
}
checkasm_report("process");}
@endcode
Alternatively, you may prefer to compile the test file itself multiple times,
using preprocessor definitions like -DBITDEPTH=10 etc.
@subsection adv_failure Custom Failure Reporting
Provide detailed diagnostics when tests fail:
@code{.c}
static void check_complex(const DSPContext *dsp)
{
// ...
if (checkasm_check_func(dsp->complex, "complex")) {
for (int param = 0; param < 16; param++) {
int result_c = checkasm_call_ref(param);
int result_a = checkasm_call_new(param);
// Check return value
if (result_c != result_a) {
if (checkasm_fail()) {
// This branch is only executed if verbose error diagnostics
// are requested by the user
fprintf(stderr, "return mismatch for param=%d: %d vs %d\n",
param, result_c, result_a);
}
}
}
}
checkasm_report("complex");}
@endcode
@subsection adv_indirect Calling Functions Through Wrappers
When the function being tested must be called indirectly through a wrapper,
you may use checkasm_call() and checkasm_call_checked() to invoke an arbitrary
helper function.
In this case, the declared function type must be the type of the wrapper, not
the inner function passed to checkasm_check_func(). You may then access the
untyped reference/tested function pointers via @ref checkasm_key_ref and
@ref checkasm_key_new "":
@code{.c}
typedef int (my_func)(int);
// Wrapper that invokes the actual function
static int sum_upto_n(my_func *func, int count)
{
int sum = 0;
for (int i = 0; i < count; i++)
sum += func(i);
return sum;
}
static void check_wrapper(void)
{
// Declare the signature of the wrapper, not the inner function
checkasm_declare(int, my_func *, int);
if (checkasm_check_func(get_my_func(), "my_wrapped_func")) {
const int count = checkasm_rand() % 100;
// Cast checkasm_key_new to the appropriate type and pass to wrapper
const my_func *my_ref = (my_func *) checkasm_key_ref;
const my_func *my_new = (my_func *) checkasm_key_new;
int sum_c = checkasm_call(sum_upto_n, my_ref, count);
int sum_a = checkasm_call_checked(sum_upto_n, my_new, count);
if (sum_c != sum_a)
checkasm_fail();
}}
@endcode
@note When using a pattern like this, the value passed to checkasm_check_func()
may not even need to be a function pointer. It could be an arbitrary pointer
or pointer-sized integer, such as a configuration struct or index into a dispatch
table, so long as it uniquely identifies the underlying implementation being
tested.
@subsection adv_mmx MMX Functions (x86)
MMX functions on x86 often omit the emms instruction before returning, expecting
the caller to execute it manually after a loop. The emms instruction is necessary
to clear MMX state before any floating-point code can execute, but it can be very
slow, so optimized loops that call into MMX kernels usually defer it to the loop
end to minimize overhead.
Use checkasm_declare_emms() for such (non-ABI-compliant) functions:
@code{.c}
static void check_sad_mmx(const DSPContext *dsp)
{
#define SIZE 16
CHECKASM_ALIGN(uint8_t src [SIZE * SIZE]);
CHECKASM_ALIGN(uint8_t ref [SIZE * SIZE]);
// Declaring with CPU_FLAG_MMX enables an automatic emms after calling
// this function whenever CPU_FLAG_MMX is active
checkasm_declare_emms(CPU_FLAG_MMX, int, const uint8_t *src,
const uint8_t *ref, ptrdiff_t stride);
if (checkasm_check_func(dsp->sad_16x16, "sad_16x16")) {
INITIALIZE_BUF(src);
INITIALIZE_BUF(ref);
// checkasm will automatically call emms after checkasm_call_new()
int result_c = checkasm_call_ref(src, ref, SIZE);
int result_a = checkasm_call_new(src, ref, SIZE);
if (result_c != result_a) {
if (checkasm_fail()) {
fprintf(stderr, "sad mismatch: %d vs %d\n", result_c, result_a);
}
}
// checkasm will also call emms after benchmarking iterations
checkasm_bench_new(src, ref, SIZE);
}
checkasm_report("sad");}
@endcode
The first parameter is a CPU flag mask (e.g., CPU_FLAG_MMX | CPU_FLAG_MMXEXT).
When any of the specified CPU flags are active, checkasm will call emms after
each checkasm_call_new() and benchmark run. On non-x86 platforms, checkasm_declare_emms()
is equivalent to checkasm_declare().
@note Modern SIMD instruction sets (SSE and later) do not use MMX registers and
therefore don't require emms. Only use checkasm_declare_emms() for legacy
MMX-only code or MMXEXT functions that explicitly use MMX registers.
@section tips_tricks Tips and Tricks
@subsection tips_deterministic Deterministic Testing
checkasm uses a seeded PRNG for reproducible tests. To test with a specific seed:
@code{.bash}
./checkasm 12345 # Use seed 12345
@endcode
Failed tests will print the seed used, allowing you to reproduce failures:
@code{.txt}
checkasm: using random seed 987654321
...
sad_16x16: FAILED (ref:1234 new:1235)
@endcode
@subsection tips_selective Selective Testing
Test specific functions or groups:
@code{.bash}
Test only functions matching pattern
./checkasm --function='add_*'
Test only a specific test module
./checkasm --test=math
Combine both
./checkasm --test=dsp --function='blend_*'
@endcode
@subsection tips_verbose Verbose Output
Enable verbose mode for detailed failure information:
@code{.bash}
./checkasm --verbose
@endcode
This shows hexdumps of differing buffer regions automatically, when using
the built-in checkasm_check*() series of buffer comparison helpers.
@subsection tips_bench Benchmarking Tips
Run benchmarks with appropriate duration:
@code{.bash}
Quick benchmark (default)
./checkasm --bench
Longer benchmark for more accurate results (10ms per function)
./checkasm --bench --duration=10000
Export results in different formats
./checkasm --bench --csv > results.csv
./checkasm --bench --json > results.json
./checkasm --bench --html > results.html
@endcode
@subsection tips_helpers Helper Macros
Create helper macros to reduce repetition in your tests:
@code{.c}
#define TEST_FILTER(name, w, h)
if (checkasm_check_func(dsp->name, #name "%dx%d", w, h)) {
test_filter##name(dsp, w, h);
checkasm_bench_new(dst, dst_stride, src, src_stride, w, h);
}
// Usage:
TEST_FILTER(blur, 16, 16);
TEST_FILTER(blur, 32, 32);
TEST_FILTER(sharpen, 16, 16);
@endcode
@section tests_next_steps Next Steps
Now that you've mastered writing tests, learn how to accurately measure and
compare the performance of your optimized implementations using checkasm's
benchmarking capabilities.
Next: @ref benchmarking
--- METRICS ---
- Files Extracted: 25
- Estimated Token Budget: ~52611 tokens
- Canonical Reference: https://codewiki.google/github.com/videolan/vlc