summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md9
-rw-r--r--TODO.md16
-rw-r--r--src/GSTInput.cpp72
-rw-r--r--src/GSTInput.h6
-rw-r--r--src/odr-audioenc.cpp15
5 files changed, 76 insertions, 42 deletions
diff --git a/README.md b/README.md
index 0449898..d103b41 100644
--- a/README.md
+++ b/README.md
@@ -200,6 +200,15 @@ libVLC.
If the webstream bitrate is slightly wrong (bad clock at the source), you can
enable drift compensation with `-D`.
+## Scenario *Custom GStreamer pipeline*
+
+The `--gst-pipeline` option lets you run custom pipelines, using the same
+syntax as `gst-launch`, which can be necessary for sources that you cannot specify through a URI through the `-G` option.
+For example, you may use udpsrc to receive an RTP stream:
+
+ odr-audioenc --gst-pipeline 'udpsrc port=5004 caps=application/x-rtp,media=(string)audio,payload=(int)10,clock-rate=44100 ! rtpL16depay ! audioconvert ! audioresample' \
+ -e $DST -l -b $BITRATE
+
## Scenario *JACK input*
JACK input: Instead of `-i (file input)` or `-d (ALSA input)`, use `-j *name*`, where *name* specifies the JACK
name for the encoder:
diff --git a/TODO.md b/TODO.md
index ae5d35b..a7c8cae 100644
--- a/TODO.md
+++ b/TODO.md
@@ -4,22 +4,6 @@ to some degree.
Unless written, no activity has been started on the topics.
-Write ICY Text from ogg streams using libvlc
---------------------------------------------
-
-Apparently there is something wrong with this combination.
-
-
-Sample rate conversion
-----------------------
-
-It's impossible to encode from a JACK, ALSA or file source that does not carry
-audio at the desired output sample rate.
-
-Implementing libsamplerate or libsoxr integration would enable this, but maybe it'd
-be better to converge towards using GStreamer.
-
-
Drift compenstation statistics
------------------------------
diff --git a/src/GSTInput.cpp b/src/GSTInput.cpp
index 5c63147..6c437d7 100644
--- a/src/GSTInput.cpp
+++ b/src/GSTInput.cpp
@@ -41,10 +41,12 @@ GSTData::GSTData(SampleQueue<uint8_t>& samplequeue) :
{ }
GSTInput::GSTInput(const std::string& uri,
+ const std::string& pipeline,
int rate,
unsigned channels,
SampleQueue<uint8_t>& queue) :
m_uri(uri),
+ m_pipeline(pipeline),
m_channels(channels),
m_rate(rate),
m_gst_data(queue)
@@ -110,25 +112,34 @@ void GSTInput::prepare()
{
gst_init(nullptr, nullptr);
- m_gst_data.uridecodebin = gst_element_factory_make("uridecodebin", "uridecodebin");
- assert(m_gst_data.uridecodebin != nullptr);
- g_object_set(m_gst_data.uridecodebin, "uri", m_uri.c_str(), nullptr);
- g_signal_connect(m_gst_data.uridecodebin, "pad-added", G_CALLBACK(cb_newpad), &m_gst_data);
+ if (not m_uri.empty()) {
+ m_gst_data.uridecodebin = gst_element_factory_make("uridecodebin", "uridecodebin");
+ assert(m_gst_data.uridecodebin != nullptr);
+ g_object_set(m_gst_data.uridecodebin, "uri", m_uri.c_str(), nullptr);
+ g_signal_connect(m_gst_data.uridecodebin, "pad-added", G_CALLBACK(cb_newpad), &m_gst_data);
- m_gst_data.audio_convert = gst_element_factory_make("audioconvert", "audio_convert");
- assert(m_gst_data.audio_convert != nullptr);
+ m_gst_data.audio_convert = gst_element_factory_make("audioconvert", "audio_convert");
+ assert(m_gst_data.audio_convert != nullptr);
- m_gst_data.audio_resample = gst_element_factory_make("audioresample", "audio_resample");
- assert(m_gst_data.audio_resample != nullptr);
- g_object_set(m_gst_data.audio_resample,
+ m_gst_data.audio_resample = gst_element_factory_make("audioresample", "audio_resample");
+ assert(m_gst_data.audio_resample != nullptr);
+ g_object_set(m_gst_data.audio_resample,
#if (GST_VERSION_MAJOR == 1 && GST_VERSION_MINOR >= 10) || GST_VERSION_MAJOR > 1
- "sinc-filter-mode", GST_AUDIO_RESAMPLER_FILTER_MODE_FULL,
+ "sinc-filter-mode", GST_AUDIO_RESAMPLER_FILTER_MODE_FULL,
#else
#warning "GStreamer version is too old to set GST_AUDIO_RESAMPLER_FILTER_MODE_FULL" GST_VERSION_MAJOR
#endif
- "quality", 6, // between 0 and 10, 10 being best
- /* default audio-resampler-method: GST_AUDIO_RESAMPLER_METHOD_KAISER */
- NULL);
+ "quality", 6, // between 0 and 10, 10 being best
+ /* default audio-resampler-method: GST_AUDIO_RESAMPLER_METHOD_KAISER */
+ NULL);
+
+ }
+ else if (not m_pipeline.empty()) {
+ m_gst_data.custom_bin = gst_parse_bin_from_description(m_pipeline.c_str(), true, nullptr);
+ if (m_gst_data.custom_bin == nullptr) {
+ throw runtime_error("Could not instantiate pipeline");
+ }
+ }
m_gst_data.caps_filter = gst_element_factory_make("capsfilter", "caps_filter");
assert(m_gst_data.caps_filter != nullptr);
@@ -149,19 +160,34 @@ void GSTInput::prepare()
g_signal_connect(m_gst_data.app_sink, "new-sample", G_CALLBACK(new_sample), &m_gst_data);
gst_caps_unref(audio_caps);
- gst_bin_add_many(GST_BIN(m_gst_data.pipeline),
- m_gst_data.uridecodebin,
- m_gst_data.audio_convert,
- m_gst_data.audio_resample,
- m_gst_data.caps_filter,
- m_gst_data.app_sink, NULL);
-
- if (gst_element_link_many(
+ if (not m_uri.empty()) {
+ gst_bin_add_many(GST_BIN(m_gst_data.pipeline),
+ m_gst_data.uridecodebin,
m_gst_data.audio_convert,
m_gst_data.audio_resample,
m_gst_data.caps_filter,
- m_gst_data.app_sink, NULL) != true) {
- throw runtime_error("Could not link GST elements");
+ m_gst_data.app_sink, NULL);
+
+ if (gst_element_link_many(
+ m_gst_data.audio_convert,
+ m_gst_data.audio_resample,
+ m_gst_data.caps_filter,
+ m_gst_data.app_sink, NULL) != true) {
+ throw runtime_error("Could not link GST elements");
+ }
+ }
+ else if (not m_pipeline.empty()) {
+ gst_bin_add_many(GST_BIN(m_gst_data.pipeline),
+ m_gst_data.custom_bin,
+ m_gst_data.caps_filter,
+ m_gst_data.app_sink, NULL);
+
+ if (gst_element_link_many(
+ m_gst_data.custom_bin,
+ m_gst_data.caps_filter,
+ m_gst_data.app_sink, NULL) != true) {
+ throw runtime_error("Could not link GST elements");
+ }
}
m_gst_data.bus = gst_element_get_bus(m_gst_data.pipeline);
diff --git a/src/GSTInput.h b/src/GSTInput.h
index 4bfae34..3c2971d 100644
--- a/src/GSTInput.h
+++ b/src/GSTInput.h
@@ -43,6 +43,7 @@
struct GSTData {
GSTData(SampleQueue<uint8_t>& samplequeue);
+ // When using URL and uridecodebin
GstElement *pipeline = nullptr;
GstElement *uridecodebin = nullptr;
GstElement *audio_convert = nullptr;
@@ -50,6 +51,9 @@ struct GSTData {
GstElement *caps_filter = nullptr;
GstElement *app_sink = nullptr;
+ // When using pipeline
+ GstElement *custom_bin = nullptr;
+
GstBus *bus = nullptr;
SampleQueue<uint8_t>& samplequeue;
@@ -59,6 +63,7 @@ class GSTInput : public InputInterface
{
public:
GSTInput(const std::string& uri,
+ const std::string& pipeline,
int rate,
unsigned channels,
SampleQueue<uint8_t>& queue);
@@ -78,6 +83,7 @@ class GSTInput : public InputInterface
virtual bool fault_detected(void) const override { return m_fault; };
private:
std::string m_uri;
+ std::string m_pipeline;
unsigned m_channels;
int m_rate;
diff --git a/src/odr-audioenc.cpp b/src/odr-audioenc.cpp
index e3708c1..21abb91 100644
--- a/src/odr-audioenc.cpp
+++ b/src/odr-audioenc.cpp
@@ -162,6 +162,9 @@ static void usage(const char* name)
" For the GStreamer input:\n"
#if HAVE_GST
" -G, --gst-uri=uri Enable GStreamer input and use the URI given as source\n"
+ " --gst-pipeline=pipeline Specify a GStreamer pipeline that receives your source.\n"
+ " The last pipeline element is connected to a caps filter that specifies\n"
+ " the audio format and sample rate.\n"
#else
" The GStreamer input was disabled at compile-time\n"
#endif
@@ -427,6 +430,7 @@ public:
// For the GST input
string gst_uri;
+ string gst_pipeline;
string jack_name;
@@ -517,6 +521,7 @@ int AudioEnc::run()
#endif
#if HAVE_GST
if (not gst_uri.empty()) num_inputs++;
+ if (not gst_pipeline.empty()) num_inputs++;
#endif
if (num_inputs == 0) {
@@ -987,7 +992,7 @@ int AudioEnc::run()
}
#endif
#if HAVE_GST
- else if (not gst_uri.empty()) {
+ else if ((not gst_uri.empty()) or (not gst_pipeline.empty())) {
GSTInput *gst_input = (GSTInput*)(input.get());
text = gst_input->get_icy_text();
}
@@ -1342,8 +1347,8 @@ shared_ptr<InputInterface> AudioEnc::initialise_input()
}
#endif
#if HAVE_GST
- else if (not gst_uri.empty()) {
- input = make_shared<GSTInput>(gst_uri, sample_rate, channels, queue);
+ else if ((not gst_uri.empty()) or (not gst_pipeline.empty())) {
+ input = make_shared<GSTInput>(gst_uri, gst_pipeline, sample_rate, channels, queue);
}
#endif
#if HAVE_ALSA
@@ -1381,6 +1386,7 @@ int main(int argc, char *argv[])
{"decode", required_argument, 0, 6 },
{"format", required_argument, 0, 'f'},
{"gst-uri", required_argument, 0, 'G'},
+ {"gst-pipeline", required_argument, 0, 11 },
{"identifier", required_argument, 0, 7 },
{"input", required_argument, 0, 'i'},
{"jack", required_argument, 0, 'j'},
@@ -1543,6 +1549,9 @@ int main(int argc, char *argv[])
case 'G':
audio_enc.gst_uri = optarg;
break;
+ case 11: // --gst-pipeline
+ audio_enc.gst_pipeline = optarg;
+ break;
#endif
case 'i':
audio_enc.infile = optarg;