summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2015-03-11 21:15:38 +0100
committerMatthias P. Braendli <matthias.braendli@mpb.li>2015-03-11 21:15:38 +0100
commit58378f49c75b7c6e184c499082328761b468da68 (patch)
treedf16e70225672ea19fb1df01f2af40aaae67dfaa
parent599d0fc5892725e471772e567555bf075ad2ae86 (diff)
downloadfdk-aac-58378f49c75b7c6e184c499082328761b468da68.tar.gz
fdk-aac-58378f49c75b7c6e184c499082328761b468da68.tar.bz2
fdk-aac-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.am5
-rw-r--r--configure.ac13
-rw-r--r--src/VLCInput.cpp164
-rw-r--r--src/VLCInput.h107
-rw-r--r--src/dabplus-enc.cpp60
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");