From e4821033a325b748c1c69848554b349c76ba6f59 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Sun, 10 Jan 2016 17:02:50 +0100 Subject: Add VLC variant with drift compensation --- src/VLCInput.cpp | 34 ++++++++++++++++++++++++++ src/VLCInput.h | 44 ++++++++++++++++++++++++++++++++++ src/dabplus-enc.cpp | 69 +++++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 132 insertions(+), 15 deletions(-) diff --git a/src/VLCInput.cpp b/src/VLCInput.cpp index 7f6aae8..890de01 100644 --- a/src/VLCInput.cpp +++ b/src/VLCInput.cpp @@ -354,6 +354,40 @@ void VLCInput::write_icy_text(const std::string& filename) } } + +// ==================== VLCInputThreaded ==================== + +void VLCInputThreaded::start() +{ + if (m_fault) { + fprintf(stderr, "Cannot start VLC input. Fault detected previsouly!\n"); + } + else { + m_running = true; + m_thread = std::thread(&VLCInputThreaded::process, this); + } +} + +void VLCInputThreaded::process() +{ + uint8_t samplebuf[NUM_SAMPLES_PER_CALL * BYTES_PER_SAMPLE * m_channels]; + while (m_running) { + ssize_t n = m_read(samplebuf, NUM_SAMPLES_PER_CALL); + + if (n < 0) { + m_running = false; + m_fault = true; + break; + } + + m_queue.push(samplebuf, BYTES_PER_SAMPLE*m_channels*n); + } +} + + + + + /* VLC up to version 2.1.0 used a different callback function signature. * VLC 2.2.0 uses size_t * diff --git a/src/VLCInput.h b/src/VLCInput.h index 710a0c6..f880642 100644 --- a/src/VLCInput.h +++ b/src/VLCInput.h @@ -38,6 +38,9 @@ // 16 bits per sample is fine for now #define BYTES_PER_SAMPLE 2 +// How many samples we insert into the queue each call +#define NUM_SAMPLES_PER_CALL 10 // 10 samples @ 32kHz = 3.125ms + /* Common functionality for the direct libvlc input and the * threaded libvlc input */ @@ -143,6 +146,47 @@ class VLCInputDirect : public VLCInput }; +class VLCInputThreaded : public VLCInput +{ + public: + VLCInputThreaded(const std::string& uri, + int rate, + unsigned channels, + unsigned verbosity, + std::string& gain, + std::string& cache, + SampleQueue& queue) : + VLCInput(uri, rate, channels, verbosity, gain, cache), + m_fault(false), + m_running(false), + m_queue(queue) {} + + ~VLCInputThreaded() + { + if (m_running) { + m_running = false; + m_thread.join(); + } + } + + /* Start the libVLC thread that fills the queue */ + virtual void start(); + + bool fault_detected() { return m_fault; }; + + private: + VLCInputThreaded(const VLCInputThreaded& other) = delete; + VLCInputThreaded& operator=(const VLCInputThreaded& other) = delete; + + void process(); + + std::atomic m_fault; + std::atomic m_running; + std::thread m_thread; + SampleQueue& m_queue; + +}; + #endif // HAVE_VLC #endif diff --git a/src/dabplus-enc.cpp b/src/dabplus-enc.cpp index cab87b7..2e7f4fa 100644 --- a/src/dabplus-enc.cpp +++ b/src/dabplus-enc.cpp @@ -64,7 +64,7 @@ void usage(const char* name) { "because it would have to throw away or insert a full DAB+ superframe,\n" "which would create audible artifacts. This drift compensation can\n" "make sure that the encoding rate is correct by inserting or deleting\n" - "audio samples.\n" + "audio samples. It can be used for both ALSA and VLC inputs.\n" "\n" "When this option is enabled, you will see U and O printed in the\n" "console. These correspond to audio underruns and overruns caused\n" @@ -84,7 +84,6 @@ void usage(const char* name) { fprintf(stderr, " For the alsa input:\n" " -d, --device=alsa_device Set ALSA input device (default: \"default\").\n" - " -D, --drift-comp Enable ALSA sound card drift compensation.\n" " For the file input:\n" " -i, --input=FILENAME Input filename (default: stdin).\n" " -f, --format={ wav, raw } Set input file format (default: wav).\n" @@ -106,6 +105,8 @@ void usage(const char* name) { #else " The VLC input was disabled at compile-time\n" #endif + " Drift compensation\n" + " -D, --drift-comp Enable ALSA/VLC sound card drift compensation.\n" " Encoder parameters:\n" " -b, --bitrate={ 8, 16, ..., 192 } Output bitrate in kbps. Must be a multiple of 8.\n" " -A, --no-afterburner Disable AAC encoder quality increaser.\n" @@ -605,7 +606,8 @@ int main(int argc, char *argv[]) JackInput jack_in(jack_name, channels, sample_rate, queue); #endif #if HAVE_VLC - VLCInputDirect vlc_in(vlc_uri, sample_rate, channels, verbosity, vlc_gain, vlc_cache); + VLCInputDirect vlc_in_direct(vlc_uri, sample_rate, channels, verbosity, vlc_gain, vlc_cache); + VLCInputThreaded vlc_in_threaded(vlc_uri, sample_rate, channels, verbosity, vlc_gain, vlc_cache, queue); #endif if (infile) { @@ -624,15 +626,26 @@ int main(int argc, char *argv[]) #endif #if HAVE_VLC else if (vlc_uri != "") { - if (vlc_in.prepare() != 0) { - fprintf(stderr, "VLC preparation failed\n"); - return 1; + if (drift_compensation) { + if (vlc_in_threaded.prepare() != 0) { + fprintf(stderr, "VLC with drift compensation: preparation failed\n"); + return 1; + } + + fprintf(stderr, "Start VLC thread\n"); + vlc_in_threaded.start(); + } + else { + if (vlc_in_direct.prepare() != 0) { + fprintf(stderr, "VLC preparation failed\n"); + return 1; + } } } #endif else if (drift_compensation) { if (alsa_in_threaded.prepare() != 0) { - fprintf(stderr, "Alsa preparation failed\n"); + fprintf(stderr, "Alsa with drift compensation: preparation failed\n"); return 1; } @@ -759,18 +772,44 @@ int main(int argc, char *argv[]) } #if HAVE_VLC else if (not vlc_uri.empty()) { - read = vlc_in.read(input_buf, input_size); - if (read < 0) { - fprintf(stderr, "Detected fault in VLC input!\n"); - break; + VLCInput* vlc_in = nullptr; + + if (drift_compensation) { + vlc_in = &vlc_in_threaded; + + if (drift_compensation && vlc_in_threaded.fault_detected()) { + fprintf(stderr, "Detected fault in VLC input!\n"); + retval = 5; + break; + } + + size_t overruns; + read = queue.pop(input_buf, input_size, &overruns); // returns bytes + + if (read != input_size) { + status |= STATUS_UNDERRUN; + } + + if (overruns) { + status |= STATUS_OVERRUN; + } } - else if (read != input_size) { - fprintf(stderr, "Short VLC read !\n"); - break; + else { + vlc_in = &vlc_in_direct; + + read = vlc_in_direct.read(input_buf, input_size); + if (read < 0) { + fprintf(stderr, "Detected fault in VLC input!\n"); + break; + } + else if (read != input_size) { + fprintf(stderr, "Short VLC read !\n"); + break; + } } if (not vlc_icytext_file.empty()) { - vlc_in.write_icy_text(vlc_icytext_file); + vlc_in->write_icy_text(vlc_icytext_file); } } #endif -- cgit v1.2.3