From 58378f49c75b7c6e184c499082328761b468da68 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Wed, 11 Mar 2015 21:15:38 +0100 Subject: Add libvlc input Be careful about sample rate conversion, VLC only wants to use the ugly resampler on some machines. --- src/VLCInput.cpp | 164 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/VLCInput.h | 107 ++++++++++++++++++++++++++++++++++ src/dabplus-enc.cpp | 60 ++++++++++++++++++- 3 files changed, 329 insertions(+), 2 deletions(-) create mode 100644 src/VLCInput.cpp create mode 100644 src/VLCInput.h (limited to 'src') diff --git a/src/VLCInput.cpp b/src/VLCInput.cpp new file mode 100644 index 0000000..555b1e1 --- /dev/null +++ b/src/VLCInput.cpp @@ -0,0 +1,164 @@ +/* ------------------------------------------------------------------ + * Copyright (C) 2015 Matthias P. Braendli + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * ------------------------------------------------------------------- + */ + +#include +#include + +#include "VLCInput.h" + +#include "config.h" + +#if HAVE_VLC + +#include +#include + + +using namespace std; + +// VLC Audio prerender callback +void prepareRender( + void* p_audio_data, + uint8_t** pp_pcm_buffer, + size_t size) +{ + VLCInput* in = (VLCInput*)p_audio_data; + + in->preRender(pp_pcm_buffer, size); +} + + +// Audio postrender callback +void handleStream( + void* p_audio_data, + uint8_t* p_pcm_buffer, + unsigned int channels, + unsigned int rate, + unsigned int nb_samples, + unsigned int bits_per_sample, + size_t size, + int64_t pts) +{ + VLCInput* in = (VLCInput*)p_audio_data; + + assert(channels == 2); + assert(rate == in->getRate()); + assert(bits_per_sample == 8*BYTES_PER_SAMPLE); + + in->postRender(p_pcm_buffer, size); +} + +int VLCInput::prepare() +{ + int err; + fprintf(stderr, "Initialising VLC...\n"); + + // VLC options + char smem_options[512]; + snprintf(smem_options, sizeof(smem_options), + "#transcode{acodec=s16l,samplerate=%d}:" + // We are using transcode because smem only support raw audio and + // video formats + "smem{" + "audio-postrender-callback=%lld," + "audio-prerender-callback=%lld," + "audio-data=%lld" + "}", + m_rate, + (long long int)(intptr_t)(void*)&handleStream, + (long long int)(intptr_t)(void*)&prepareRender, + (long long int)(intptr_t)this); + + char verb_options[512]; + snprintf(verb_options, sizeof(verb_options), + "--verbose=%d", m_verbosity); + + const char * const vlc_args[] = { + verb_options, + "--sout", smem_options // Stream to memory + }; + + // Launch VLC + m_vlc = libvlc_new(sizeof(vlc_args) / sizeof(vlc_args[0]), vlc_args); + + // Load the media + libvlc_media_t *m; + m = libvlc_media_new_location(m_vlc, m_uri.c_str()); + m_mp = libvlc_media_player_new_from_media(m); + libvlc_media_release(m); + + // Start playing + libvlc_media_player_play(m_mp); + + fprintf(stderr, "VLC launched.\n"); + return 0; +} + +void VLCInput::preRender(uint8_t** pp_pcm_buffer, size_t size) +{ + boost::mutex::scoped_lock lock(m_queue_mutex); + + m_current_buf.resize(size); + *pp_pcm_buffer = &m_current_buf[0]; +} + +void VLCInput::postRender(uint8_t* p_pcm_buffer, size_t size) +{ + boost::mutex::scoped_lock lock(m_queue_mutex); + + assert(m_current_buf.size() == size); + + size_t queue_size = m_queue.size(); + m_queue.resize(m_queue.size() + size); + std::copy(m_current_buf.begin(), m_current_buf.end(), + m_queue.begin() + queue_size); +} + +ssize_t VLCInput::m_read(uint8_t* buf, size_t length) +{ + ssize_t err = 0; + for (;;) { + boost::mutex::scoped_lock lock(m_queue_mutex); + + if (m_queue.size() >= length) { + for (size_t i = 0; i < length; i++) { + buf[i] = m_queue[i]; + } + m_queue.erase(m_queue.begin(), m_queue.begin() + length); + + return length; + } + + lock.unlock(); + boost::this_thread::sleep(boost::posix_time::milliseconds(1)); + } + return err; +} + +ssize_t VLCInput::read(uint8_t* buf, size_t length) +{ + int bytes_per_frame = m_channels * BYTES_PER_SAMPLE; + assert(length % bytes_per_frame == 0); + + ssize_t read = m_read(buf, length); + + return read; +} + +#endif // HAVE_VLC + diff --git a/src/VLCInput.h b/src/VLCInput.h new file mode 100644 index 0000000..13b0695 --- /dev/null +++ b/src/VLCInput.h @@ -0,0 +1,107 @@ +/* ------------------------------------------------------------------ + * Copyright (C) 2015 Matthias P. Braendli + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * ------------------------------------------------------------------- + */ + +#ifndef __VLC_INPUT_H_ +#define __VLC_INPUT_H_ + +#include "config.h" + +#if HAVE_VLC + +#include +#include +#include +#include + +#include +#include + +#include "SampleQueue.h" + +// 16 bits per sample is fine for now +#define BYTES_PER_SAMPLE 2 + +class VLCInput +{ + public: + VLCInput(const std::string& uri, + int rate, + unsigned verbosity) : + m_uri(uri), + m_verbosity(verbosity), + m_channels(2), + m_rate(rate), + m_vlc(NULL) { } + + ~VLCInput() { + if (m_mp) { + /* Stop playing */ + libvlc_media_player_stop(m_mp); + + /* Free the media_player */ + libvlc_media_player_release(m_mp); + } + + if (m_vlc) { + libvlc_release(m_vlc); + m_vlc = NULL; + } + } + + /* Prepare the audio input */ + int prepare(); + + ssize_t read(uint8_t* buf, size_t length); + + + // Callbacks for VLC + void preRender( + uint8_t** pp_pcm_buffer, + size_t size); + + void postRender( + uint8_t* p_pcm_buffer, + size_t size); + + int getRate() { return m_rate; } + + protected: + ssize_t m_read(uint8_t* buf, size_t length); + + std::vector m_current_buf; + + boost::mutex m_queue_mutex; + std::deque m_queue; + + std::string m_uri; + unsigned m_verbosity; + unsigned m_channels; + int m_rate; + + // VLC pointers + libvlc_instance_t *m_vlc; + libvlc_media_player_t *m_mp; + + private: + VLCInput(const VLCInput& other) {} +}; + +#endif // HAVE_VLC + +#endif + diff --git a/src/dabplus-enc.cpp b/src/dabplus-enc.cpp index 0862690..3dc09fe 100644 --- a/src/dabplus-enc.cpp +++ b/src/dabplus-enc.cpp @@ -21,6 +21,7 @@ #include "AlsaInput.h" #include "FileInput.h" #include "JackInput.h" +#include "VLCInput.h" #include "SampleQueue.h" #include "zmq.hpp" @@ -54,6 +55,7 @@ void usage(const char* name) { "based on fdk-aac-dabplus that can read from" "JACK, ALSA or a file source\n" "and encode to a ZeroMQ output for ODR-DabMux.\n" + "(Experimental!)It can also use libvlc as an input.\n" "\n" "The -D option enables experimental sound card clock drift compensation.\n" "A consumer sound card has a clock that is always a bit imprecise, and\n" @@ -91,6 +93,14 @@ void usage(const char* name) { " -j, --jack=name Enable JACK input, and define our name\n" #else " The JACK input was disabled at compile-time\n" +#endif + " For the VLC input:\n" +#if HAVE_JACK + " -v, --vlc-uri=uri Enable VLC input and use the URI given as source\n" + " -V Increase the VLC verbosity by one (can be given \n" + " multiple times)\n" +#else + " The JACK input was disabled at compile-time\n" #endif " Encoder parameters:\n" " -b, --bitrate={ 8, 16, ..., 192 } Output bitrate in kbps. Must be a multiple of 8.\n" @@ -235,6 +245,10 @@ int main(int argc, char *argv[]) const char *infile = NULL; int raw_input = 0; + // For the VLC input + std::string vlc_uri = ""; + unsigned verbosity = 0; + // For the file output FILE *out_fh = NULL; @@ -278,6 +292,7 @@ int main(int argc, char *argv[]) {"bitrate", required_argument, 0, 'b'}, {"channels", required_argument, 0, 'c'}, {"device", required_argument, 0, 'd'}, + {"vlc-uri", required_argument, 0, 'v'}, {"format", required_argument, 0, 'f'}, {"input", required_argument, 0, 'i'}, {"jack", required_argument, 0, 'j'}, @@ -292,6 +307,7 @@ int main(int argc, char *argv[]) {"drift-comp", no_argument, 0, 'D'}, {"help", no_argument, 0, 'h'}, {"level", no_argument, 0, 'l'}, + {"verbosity", no_argument, 0, 'V'}, {"aaclc", no_argument, 0, 0 }, {"sbr", no_argument, 0, 1 }, {"ps", no_argument, 0, 2 }, @@ -319,7 +335,7 @@ int main(int argc, char *argv[]) int index; while(ch != -1) { - ch = getopt_long(argc, argv, "aAhDlb:c:f:i:j:k:o:r:d:p:P:s:", longopts, &index); + ch = getopt_long(argc, argv, "aAhDlb:c:f:i:j:k:o:r:d:p:P:s:v:", longopts, &index); switch (ch) { case 0: // AAC-LC aot = AOT_DABPLUS_AAC_LC; @@ -396,6 +412,17 @@ int main(int argc, char *argv[]) return 1; } + break; + case 'v': +#ifndef HAVE_VLC + fprintf(stderr, "VLC input not enabled at compile time!\n"); + return 1; +#else + vlc_uri = optarg; + break; +#endif + case 'V': + verbosity++; break; case '?': case 'h': @@ -404,7 +431,13 @@ int main(int argc, char *argv[]) } } - if (alsa_device && infile && jack_name) { + int num_inputs = 0; + if (alsa_device) num_inputs++; + if (infile) num_inputs++; + if (jack_name) num_inputs++; + if (vlc_uri != "") num_inputs++; + + if (num_inputs > 1) { fprintf(stderr, "You must define only one possible input, not several!\n"); return 1; } @@ -536,6 +569,9 @@ int main(int argc, char *argv[]) #if HAVE_JACK JackInput jack_in(jack_name, channels, sample_rate, queue); #endif +#if HAVE_VLC + VLCInput vlc_in(vlc_uri, sample_rate, verbosity); +#endif if (infile) { if (file_in.prepare() != 0) { @@ -550,6 +586,14 @@ int main(int argc, char *argv[]) return 1; } } +#endif +#if HAVE_VLC + else if (vlc_uri != "") { + if (vlc_in.prepare() != 0) { + fprintf(stderr, "VLC preparation failed\n"); + return 1; + } + } #endif else if (drift_compensation) { if (alsa_in_threaded.prepare() != 0) { @@ -676,6 +720,18 @@ int main(int argc, char *argv[]) } } } +#if HAVE_VLC + else if (vlc_uri != "") { + read = vlc_in.read(input_buf, input_size); + if (read < 0) { + break; + } + else if (read != input_size) { + fprintf(stderr, "Short VLC read !\n"); + break; + } + } +#endif else if (drift_compensation || jack_name) { if (drift_compensation && alsa_in_threaded.fault_detected()) { fprintf(stderr, "Detected fault in alsa input!\n"); -- cgit v1.2.3