diff options
-rw-r--r-- | README.md | 9 | ||||
-rw-r--r-- | src/GSTInput.cpp | 72 | ||||
-rw-r--r-- | src/GSTInput.h | 6 | ||||
-rw-r--r-- | src/odr-audioenc.cpp | 15 |
4 files changed, 76 insertions, 26 deletions
@@ -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/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; |