Context

Our C++ application needs to play audio files. There are libraries that handle this - they simply need to be included, initialized and used. miniaudio is easy to integrate, but has file format limitations. libvlc can support almost any format, but bundling it with the application impacts size and performance. Dynamic linking addresses this issue, but creates a requirement for external dependencies. As a compromise, the application may try to use libvlc, but fall back to miniaudio.

Miniaudio

Miniaudio is an audio playback library for C and C++; it is made up of a single source file without external dependencies.

To use the library, download the header file miniaudio.h.

To test the library by playing a file, create ma-test.cpp:

#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"
#include <iostream>

int main(){
	// Miniaudio variables
	ma_engine engine;
	ma_result result;
	ma_sound sound;

	// Initialize engine
	result = ma_engine_init(NULL, &engine);

	if (result == MA_SUCCESS){
		// Load file
		result = ma_sound_init_from_file(&engine, "file.mp3", 0, NULL, NULL, &sound);
		if (result == MA_SUCCESS){
			// Start playback
			ma_sound_start(&sound);
			std::cout << "Playing file.\n";
			// Wait until completion
			while(!ma_sound_at_end(&sound)){
				ma_sleep(10);
			}
			ma_sound_uninit(&sound);
			std::cout << "Playback completed.\n";
		} else {
			std::cout << "Failed to load file.\n";
		}
	} else {
		std::cout << "Failed to initialize file.\n";
	}
}

Compile and run:

g++ ma-test.cpp -o ma-test.o
./ma-test.o

Ensure the header file miniaudio.h and audio file file.mp3 are in the same folder as the above source code file.

The high level API has built in support for WAV, MP3 and FLAC - other formats require more complex configuration with the low level API. 1 If support for an extensive list of audio formats is required, libvlc may be better suited.

libvlc

libvlc is a C library that is the core engine of the VLC media player. VideoLAN provides official C++ bindings (libvlcpp) that acts as a wrapper for libvlc. The bindings consist of header files; only the vlcpp folder - containing several .hpp files - is required from the repository: https://code.videolan.org/videolan/libvlcpp.

VLC and libvlc are required dependencies. Some sources of VLC may not expose libvlc as a library. Refer to the VLC installation guides for your particular system.

Once libvlc is accessible and the vlcpp folder has been copied to the current folder, the engine can be tested; create vlc-test.cpp:

#include "vlcpp/vlc.hpp"
#include <unistd.h>

int main(){
    
    // VLC variables
    VLC::Instance m_instance;
    VLC::MediaPlayer m_player;
    VLC::Media m_media;

    // Initialize with video disabled
    static const char* const vlc_args[] {"--no-video", "--vout=dummy"};
    m_instance = VLC::Instance(int(sizeof(vlc_args)/sizeof(vlc_args[0])), vlc_args);

    m_player = VLC::MediaPlayer(m_instance);
    m_media = VLC::Media(m_instance, "file.mp3", VLC::Media::FromPath);
    m_player.setMedia(m_media);

    // Start playback
    if (m_player) {
        m_player.play();
        // Wait for playback to start, then finish
        while (!m_player.isPlaying()){ usleep(10); }
        while (m_player.isPlaying()) { usleep(10); }
    }
    
    return 0;

}

libvlc must be linked when compiling:

g++ -lvlc vlc-test.cpp -o vlc-test.o
./vlc-test.o

If you need to package the application as an AppImage, you must exclude libvlc when building. VLC initialization unexpectedly fails with partial vlc installations (missing some plugins) and including the full installation would be quite inefficient - it is better to dynamically link.

Fallback

It would be beneficial if the application could retain some functionality even if libvlc is unavailable. When using libvlc by #include <vlcpp/vlc.hpp>, the application does not start without libvlc.

Using preprocessor directives only works for compilation. The developer machine can be required to have vlc installed, but this doesn’t solve the runtime issue.

Another option is to load the library at runtime using dlopen or LoadLibrary, but since libvlcpp is being used as a binding, this doesn’t seem easily possible.

Currently, fallback is not supported. Two build version may be offered - one using miniaudio and the other libvlc. An integrated version would be prefered.