diff options
author | Matthias P. Braendli <matthias.braendli@mpb.li> | 2015-03-11 21:15:38 +0100 |
---|---|---|
committer | Matthias P. Braendli <matthias.braendli@mpb.li> | 2015-03-11 21:15:38 +0100 |
commit | 58378f49c75b7c6e184c499082328761b468da68 (patch) | |
tree | df16e70225672ea19fb1df01f2af40aaae67dfaa | |
parent | 599d0fc5892725e471772e567555bf075ad2ae86 (diff) | |
download | ODR-AudioEnc-58378f49c75b7c6e184c499082328761b468da68.tar.gz ODR-AudioEnc-58378f49c75b7c6e184c499082328761b468da68.tar.bz2 ODR-AudioEnc-58378f49c75b7c6e184c499082328761b468da68.zip |
Add libvlc input
Be careful about sample rate conversion, VLC only wants to use
the ugly resampler on some machines.
-rw-r--r-- | Makefile.am | 5 | ||||
-rw-r--r-- | configure.ac | 13 | ||||
-rw-r--r-- | src/VLCInput.cpp | 164 | ||||
-rw-r--r-- | src/VLCInput.h | 107 | ||||
-rw-r--r-- | src/dabplus-enc.cpp | 60 |
5 files changed, 346 insertions, 3 deletions
diff --git a/Makefile.am b/Makefile.am index 8d8f54e..e1449d3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -52,7 +52,8 @@ endif dabplus_enc_LDFLAGS = -no-install dabplus_enc_LDADD = libfdk-aac.la -lfec -lzmq -lasound \ - -lrt -lboost_thread $(dabplus_enc_LDADD_JACK) + -lrt -lboost_thread $(dabplus_enc_LDADD_JACK) \ + $(LIBVLC_LIBS) dabplus_enc_CPPFLAGS = $(AM_CPPFLAGS) $(GITVERSION_FLAGS) -ggdb dabplus_enc_SOURCES = src/dabplus-enc.cpp \ src/FileInput.cpp \ @@ -61,6 +62,8 @@ dabplus_enc_SOURCES = src/dabplus-enc.cpp \ src/AlsaInput.h \ src/JackInput.cpp \ src/JackInput.h \ + src/VLCInput.cpp \ + src/VLCInput.h \ src/SampleQueue.h \ src/encryption.c \ src/encryption.h \ diff --git a/configure.ac b/configure.ac index 1220cfe..d0b4371 100644 --- a/configure.ac +++ b/configure.ac @@ -28,12 +28,24 @@ AM_PATH_ALSA(1.0.25) AC_ARG_ENABLE([jack], AS_HELP_STRING([--enable-jack], [Enable JACK input])) +AC_ARG_ENABLE([vlc], + AS_HELP_STRING([--enable-vlc], [Enable libvlc input])) + AS_IF([test "x$enable_jack" = "xyes"], AC_CHECK_LIB(jack, jack_client_open, [], [AC_MSG_ERROR([JACK is required])])) AS_IF([test "x$enable_jack" = "xyes"], AC_DEFINE(HAVE_JACK, [1], [Define if JACK input is enabled])) +AS_IF([test "x$enable_vlc" = "xyes"], [ + PKG_CHECK_MODULES([LIBVLC], [libvlc]) + AC_SUBST([LIBVLC_CFLAGS]) + AC_SUBST([LIBVLC_LIBS]) ] ) + +AS_IF([test "x$enable_vlc" = "xyes"], + AC_DEFINE(HAVE_VLC, [1], [Define if VLC input is enabled])) + + # Link against jack AM_CONDITIONAL([HAVE_JACK], [ test "x$enable_jack" = "xyes" ]) @@ -49,6 +61,7 @@ else AC_MSG_ERROR(MagickWand not found) fi + dnl soname version to use dnl goes by ‘current[:revision[:age]]’ with the soname ending up as dnl current.age.revision 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 <cstdio> +#include <string> + +#include "VLCInput.h" + +#include "config.h" + +#if HAVE_VLC + +#include <sys/time.h> +#include <boost/date_time/posix_time/posix_time.hpp> + + +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 <cstdio> +#include <string> +#include <vector> +#include <deque> + +#include <vlc/vlc.h> +#include <boost/thread/thread.hpp> + +#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<uint8_t> m_current_buf; + + boost::mutex m_queue_mutex; + std::deque<uint8_t> 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" @@ -92,6 +94,14 @@ void usage(const char* name) { #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" " -A, --no-afterburner Disable AAC encoder quality increaser.\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; @@ -397,6 +413,17 @@ int main(int argc, char *argv[]) } 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': usage(argv[0]); @@ -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) { @@ -551,6 +587,14 @@ 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; + } + } +#endif else if (drift_compensation) { if (alsa_in_threaded.prepare() != 0) { fprintf(stderr, "Alsa preparation failed\n"); @@ -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"); |