summaryrefslogtreecommitdiff
path: root/src/pipelines-rtp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pipelines-rtp.c')
-rw-r--r--src/pipelines-rtp.c589
1 files changed, 589 insertions, 0 deletions
diff --git a/src/pipelines-rtp.c b/src/pipelines-rtp.c
new file mode 100644
index 0000000..1a04c78
--- /dev/null
+++ b/src/pipelines-rtp.c
@@ -0,0 +1,589 @@
+/*
+ * sydra
+ *
+ * sydra is a toolbox which allows you to set up multiple bidirectional
+ * Video/Audio streams from external locations.
+ * sydra has been written to be used for the Elevate Festival in Graz
+ * Austria in order to involve external locations to present themselves
+ * at the festival.
+ *
+ *
+ * Copyright (C) 2014 Christian Pointner <equinox@spreadspace.org>
+ *
+ * This file is part of sydra.
+ *
+ * sydra is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * sydra is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with sydra. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, as a special exception, the copyright holders hereby
+ * grant permission for non-GPL-compatible GStreamer plugins to be used
+ * and distributed together with GStreamer and sydra.
+ * This permission goes above and beyond the permissions granted by the
+ * GPL license sydra is covered by.
+ */
+
+#include "datatypes.h"
+
+#include <gst/gst.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "pipelines-rtp.h"
+#include "options-rtp.h"
+#include "utils.h"
+#include "log.h"
+
+struct av_elements {
+ const char* name_;
+
+ GstElement* srcsink_;
+ GstElement* tee_raw_;
+
+ const char* encdec_str_;
+ GstElement* encdec_;
+
+ GstElement* tee_enc_;
+
+ const char* payloader_str_;
+ GstElement* payloader_;
+};
+
+static GstElement* create_avsend_src(const char* desc, GstElement* pipeline)
+{
+ GstElement* src = sydra_create_bin_from_desc("AV source bin", desc, FALSE);
+ if(!src) return NULL;
+ gst_bin_add(GST_BIN(pipeline), src);
+
+ GstElement* element = gst_bin_get_by_name(GST_BIN(src), "videosrc");
+ if(!element) {
+ log_printf(ERROR, "can't find an element 'videosrc' inside source bin");
+ return NULL;
+ }
+ GstPad* pad = gst_element_get_static_pad(element, "src");
+ if(!gst_element_add_pad(src, gst_ghost_pad_new("video", pad))) {
+ log_printf(ERROR, "can't create video ghost pad for source bin");
+ return NULL;
+ }
+ gst_object_unref(GST_OBJECT(pad));
+ gst_object_unref(GST_OBJECT(element));
+
+ element = gst_bin_get_by_name(GST_BIN(src), "audiosrc");
+ if(!element) {
+ log_printf(ERROR, "can't find an element 'audiosrc' inside source bin");
+ return NULL;
+ }
+ pad = gst_element_get_static_pad(element, "src");
+ if(!gst_element_add_pad(src, gst_ghost_pad_new("audio", pad))) {
+ log_printf(ERROR, "can't create audio ghost pad for source bin");
+ return NULL;
+ }
+ gst_object_unref(GST_OBJECT(pad));
+ gst_object_unref(GST_OBJECT(element));
+
+ return src;
+}
+
+static gboolean create_avsend_elements(struct av_elements *ave, GstElement* pipeline, GstElement *rtp, uint32_t session)
+{
+ ave->tee_raw_ = sydra_create_element("tee", NULL);
+ GstElement *qr = sydra_create_element("queue", NULL);
+
+ char bin_name[32];
+ snprintf(bin_name, sizeof(bin_name), "%s encoder bin", ave->name_);
+ ave->encdec_ = sydra_create_bin_from_desc(bin_name, ave->encdec_str_, TRUE);
+ ave->tee_enc_ = sydra_create_element("tee", NULL);
+ GstElement *qe = sydra_create_element("queue", NULL);
+ ave->payloader_ = sydra_create_element(ave->payloader_str_, NULL);
+
+ if(!ave->tee_raw_ || !qr || !ave->encdec_ || !ave->tee_enc_ || !qe || !ave->payloader_) {
+ return FALSE;
+ }
+
+ log_printf(INFO, "%s path created successfully!", ave->name_);
+
+ gst_bin_add_many(GST_BIN(pipeline), ave->tee_raw_, qr, ave->encdec_, ave->tee_enc_, qe, ave->payloader_, NULL);
+ if(!sydra_link_static_static(ave->srcsink_, ave->name_, ave->tee_raw_, "sink")) return FALSE;
+ gst_element_link_many(qr, ave->encdec_, ave->tee_enc_, NULL);
+ gst_element_link(qe, ave->payloader_);
+
+ char pad_name[32];
+ snprintf(pad_name, sizeof(pad_name), "send_rtp_sink_%u", session);
+ if(!sydra_link_request_static(ave->tee_raw_, "src_%u", qr, "sink") ||
+ !sydra_link_request_static(ave->tee_enc_, "src_%u", qe, "sink") ||
+ !sydra_link_static_request(ave->payloader_, "src", rtp, pad_name)) {
+ return FALSE;
+ }
+
+ log_printf(INFO, "%s path linked successfully!", ave->name_);
+ return TRUE;
+}
+
+static GstElement* create_avrecv_sink(const char* desc, GstElement* pipeline)
+{
+ GstElement* sink = sydra_create_bin_from_desc("AV sink bin", desc, FALSE);
+ if(!sink) return NULL;
+ gst_bin_add(GST_BIN(pipeline), sink);
+
+ GstElement* element = gst_bin_get_by_name(GST_BIN(sink), "videosink");
+ if(!element) {
+ log_printf(ERROR, "can't find an element 'videosink' inside sink bin");
+ return NULL;
+ }
+ GstPad* pad = gst_element_get_static_pad(element, "sink");
+ if(!gst_element_add_pad(sink, gst_ghost_pad_new("video", pad))) {
+ log_printf(ERROR, "can't create video ghost pad for source bin");
+ return NULL;
+ }
+ gst_object_unref(GST_OBJECT(pad));
+ gst_object_unref(GST_OBJECT(element));
+
+ element = gst_bin_get_by_name(GST_BIN(sink), "audiosink");
+ if(!element) {
+ log_printf(ERROR, "can't find an element 'audiosink' inside sink bin");
+ return NULL;
+ }
+ pad = gst_element_get_static_pad(element, "sink");
+ if(!gst_element_add_pad(sink, gst_ghost_pad_new("audio", pad))) {
+ log_printf(ERROR, "can't create audio ghost pad for source bin");
+ return NULL;
+ }
+ gst_object_unref(GST_OBJECT(pad));
+ gst_object_unref(GST_OBJECT(element));
+
+ return sink;
+}
+
+static gboolean create_avrecv_elements(struct av_elements *ave, GstElement* pipeline)
+{
+ ave->tee_raw_ = sydra_create_element("tee", NULL);
+ GstElement *qr = sydra_create_element("queue", NULL);
+
+ char bin_name[32];
+ snprintf(bin_name, sizeof(bin_name), "%s decoder bin", ave->name_);
+ ave->encdec_ = sydra_create_bin_from_desc(bin_name, ave->encdec_str_, TRUE);
+ ave->tee_enc_ = sydra_create_element("tee", NULL);
+ GstElement *qe = sydra_create_element("queue", NULL);
+ ave->payloader_ = sydra_create_element(ave->payloader_str_, NULL);
+
+ if(!ave->tee_raw_ || !qr || !ave->encdec_ || !ave->tee_enc_ || !qe || !ave->payloader_) {
+ return FALSE;
+ }
+
+ log_printf(INFO, "%s path created successfully!", ave->name_);
+
+ gst_bin_add_many (GST_BIN(pipeline), ave->tee_raw_, qr, ave->encdec_, ave->tee_enc_, qe, ave->payloader_, NULL);
+ gst_element_link_many(ave->payloader_, ave->tee_enc_, qe, ave->encdec_, ave->tee_raw_, qr, NULL);
+ if(!sydra_link_static_static(qr, "src", ave->srcsink_, ave->name_)) return FALSE;
+
+ log_printf(INFO, "%s path linked successfully!", ave->name_);
+ return TRUE;
+}
+
+static gboolean create_udp_sinks(options_t* opt, GstElement* pipeline, GstElement* rtp, struct udp_sinks *sinks)
+{
+ sinks->rtp_video_.udp_ = sydra_create_element("multiudpsink", "udprtpv");
+ sinks->rtcp_video_.udp_ = sydra_create_element("multiudpsink", "udprtcpv");
+ sinks->rtp_audio_.udp_ = sydra_create_element("multiudpsink", "udprtpa");
+ sinks->rtcp_audio_.udp_ = sydra_create_element("multiudpsink", "udprtcpa");
+
+ if(!(sinks->rtp_video_.udp_) || !(sinks->rtcp_video_.udp_) || !(sinks->rtp_audio_.udp_) || !(sinks->rtcp_audio_.udp_))
+ return FALSE;
+
+ log_printf(INFO, "udp sinks created successfully!");
+
+ int rtp_port_local = opt->rtp_port_base_local_;
+ g_object_set(G_OBJECT(sinks->rtp_video_.udp_), "bind-port", rtp_port_local++, NULL);
+ g_object_set(G_OBJECT(sinks->rtcp_video_.udp_), "bind-port", rtp_port_local++, "sync", FALSE, "async", FALSE, NULL);
+ g_object_set(G_OBJECT(sinks->rtp_audio_.udp_), "bind-port", rtp_port_local++, NULL);
+ g_object_set(G_OBJECT(sinks->rtcp_audio_.udp_), "bind-port", rtp_port_local++, "sync", FALSE, "async", FALSE, NULL);
+
+ if(opt->rtp_host_) {
+ int rtp_port = opt->rtp_port_base_;
+ g_signal_emit_by_name(G_OBJECT(sinks->rtp_video_.udp_), "add", opt->rtp_host_, rtp_port++, NULL);
+ g_signal_emit_by_name(G_OBJECT(sinks->rtcp_video_.udp_), "add", opt->rtp_host_, rtp_port++, NULL);
+ g_signal_emit_by_name(G_OBJECT(sinks->rtp_audio_.udp_), "add", opt->rtp_host_, rtp_port++, NULL);
+ g_signal_emit_by_name(G_OBJECT(sinks->rtcp_audio_.udp_), "add", opt->rtp_host_, rtp_port++, NULL);
+ }
+ if(opt->rtp_addr_local_) {
+ g_object_set(G_OBJECT(sinks->rtp_video_.udp_), "bind-address", opt->rtp_addr_local_, NULL);
+ g_object_set(G_OBJECT(sinks->rtcp_video_.udp_), "bind-address", opt->rtp_addr_local_, NULL);
+ g_object_set(G_OBJECT(sinks->rtp_audio_.udp_), "bind-address", opt->rtp_addr_local_, NULL);
+ g_object_set(G_OBJECT(sinks->rtcp_audio_.udp_), "bind-address", opt->rtp_addr_local_, NULL);
+ }
+
+ log_printf(INFO, "udp sinks configured successfully!");
+
+ gst_bin_add_many(GST_BIN(pipeline), sinks->rtp_video_.udp_, sinks->rtcp_video_.udp_, sinks->rtp_audio_.udp_, sinks->rtcp_audio_.udp_, NULL);
+
+ if(!sydra_link_static_static(rtp, "send_rtp_src_0", sinks->rtp_video_.udp_, "sink") ||
+ !sydra_link_request_static(rtp, "send_rtcp_src_0", sinks->rtcp_video_.udp_, "sink") ||
+ !sydra_link_static_static(rtp, "send_rtp_src_1", sinks->rtp_audio_.udp_, "sink") ||
+ !sydra_link_request_static(rtp, "send_rtcp_src_1", sinks->rtcp_audio_.udp_, "sink"))
+ return FALSE;
+
+ log_printf(INFO, "udp sinks linked successfully!");
+
+ return TRUE;
+}
+
+struct udpsrc_tees {
+ GstElement* rtpv_;
+ GstElement* rtcpv_;
+ GstElement* rtpa_;
+ GstElement* rtcpa_;
+};
+
+static gboolean create_rtp_reflector(options_t* opt, GstElement* pipeline, struct udpsrc_tees *tees)
+{
+ GstElement* rtp_video = sydra_create_element("udpsink", "udprtpvref");
+ GstElement* qrtpv = sydra_create_element("queue", NULL);
+ GstElement* rtcp_video = sydra_create_element("udpsink", "udprtcpvref");
+ GstElement* qrtcpv = sydra_create_element("queue", NULL);
+ GstElement* rtp_audio = sydra_create_element("udpsink", "udprtparef");
+ GstElement* qrtpa = sydra_create_element("queue", NULL);
+ GstElement* rtcp_audio = sydra_create_element("udpsink", "udprtcparef");
+ GstElement* qrtcpa = sydra_create_element("queue", NULL);
+
+ if(!rtp_video || !rtcp_video || !rtp_audio || !rtcp_audio || !qrtpv || !qrtcpv || !qrtpa || !qrtcpa)
+ return FALSE;
+
+ log_printf(INFO, "udp reflector sinks created successfully!");
+
+ int rtp_port_reflector = opt->rtp_port_base_reflector_;
+ g_object_set(G_OBJECT(rtp_video), "host", opt->rtp_host_reflector_, "port", rtp_port_reflector++,
+ "sync", FALSE, "async", FALSE, NULL);
+ g_object_set(G_OBJECT(rtcp_video), "host", opt->rtp_host_reflector_, "port", rtp_port_reflector++,
+ "sync", FALSE, "async", FALSE, NULL);
+ g_object_set(G_OBJECT(rtp_audio), "host", opt->rtp_host_reflector_, "port", rtp_port_reflector++,
+ "sync", FALSE, "async", FALSE, NULL);
+ g_object_set(G_OBJECT(rtcp_audio), "host", opt->rtp_host_reflector_, "port", rtp_port_reflector++,
+ "sync", FALSE, "async", FALSE, NULL);
+
+ log_printf(INFO, "udp reflector sinks configured successfully!");
+
+ gst_bin_add_many(GST_BIN(pipeline), rtp_video, rtcp_video, rtp_audio, rtcp_audio, NULL);
+ gst_bin_add_many(GST_BIN(pipeline), qrtpv, qrtcpv, qrtpa, qrtcpa, NULL);
+
+ gst_element_link(qrtpv, rtp_video);
+ gst_element_link(qrtcpv, rtcp_video);
+ gst_element_link(qrtpa, rtp_audio);
+ gst_element_link(qrtcpa, rtcp_audio);
+
+ if(!sydra_link_request_static(tees->rtpv_, "src_%u", qrtpv, "sink") ||
+ !sydra_link_request_static(tees->rtcpv_, "src_%u", qrtcpv, "sink") ||
+ !sydra_link_request_static(tees->rtpa_, "src_%u", qrtpa, "sink") ||
+ !sydra_link_request_static(tees->rtcpa_, "src_%u", qrtcpa, "sink"))
+ return FALSE;
+
+ log_printf(INFO, "udp reflector sinks linked successfully!");
+
+ return TRUE;
+}
+
+static gboolean create_udp_sources(options_t* opt, GstElement* pipeline, GstElement* rtp, struct udp_sources *sources)
+{
+ struct udpsrc_tees tees;
+
+ sources->rtp_video_ = sydra_create_element("udpsrc", "udprtpv");
+ tees.rtpv_ = sydra_create_element("tee", "rtpvt");
+ sources->rtcp_video_ = sydra_create_element("udpsrc", "udprtcpv");
+ tees.rtcpv_ = sydra_create_element("tee", "rtcpvt");
+ sources->rtp_audio_ = sydra_create_element("udpsrc", "udprtpa");
+ tees.rtpa_ = sydra_create_element("tee", "rtpat");
+ sources->rtcp_audio_ = sydra_create_element("udpsrc", "udprtcpa");
+ tees.rtcpa_ = sydra_create_element("tee", "rtcpat");
+
+ if(!(sources->rtp_video_) || !(sources->rtcp_video_) || !(sources->rtp_audio_) || !(sources->rtcp_audio_) ||
+ !(tees.rtpv_) || !(tees.rtcpv_) || !(tees.rtpa_) || !(tees.rtcpa_))
+ return FALSE;
+
+ log_printf(INFO, "udp sources created successfully!");
+
+ GstCaps *video_caps = gst_caps_from_string(opt->video_caps_);
+ GstCaps *audio_caps = gst_caps_from_string(opt->audio_caps_);
+ if(!video_caps || !audio_caps) {
+ log_printf(ERROR, "parsing video or audio caps failed!");
+ return FALSE;
+ }
+ int rtp_port_local = opt->rtp_port_base_local_;
+ g_object_set(G_OBJECT(sources->rtp_video_), "port", rtp_port_local++, "caps", video_caps, NULL);
+ g_object_set(G_OBJECT(sources->rtcp_video_), "port", rtp_port_local++, NULL);
+ g_object_set(G_OBJECT(sources->rtp_audio_), "port", rtp_port_local++, "caps", audio_caps, NULL);
+ g_object_set(G_OBJECT(sources->rtcp_audio_), "port", rtp_port_local++, NULL);
+ gst_caps_unref(video_caps);
+ gst_caps_unref(audio_caps);
+
+ if(opt->rtp_addr_local_) {
+ g_object_set(G_OBJECT(sources->rtp_video_), "address", opt->rtp_addr_local_, NULL);
+ g_object_set(G_OBJECT(sources->rtcp_video_), "address", opt->rtp_addr_local_, NULL);
+ g_object_set(G_OBJECT(sources->rtp_audio_), "address", opt->rtp_addr_local_, NULL);
+ g_object_set(G_OBJECT(sources->rtcp_audio_), "address", opt->rtp_addr_local_, NULL);
+ }
+
+ log_printf(INFO, "udp sources configured successfully!");
+
+ gst_bin_add_many(GST_BIN(pipeline), sources->rtp_video_, sources->rtcp_video_, sources->rtp_audio_, sources->rtcp_audio_, NULL);
+ gst_bin_add_many(GST_BIN(pipeline), tees.rtpv_, tees.rtcpv_, tees.rtpa_, tees.rtcpa_, NULL);
+
+ gst_element_link(sources->rtp_video_, tees.rtpv_);
+ gst_element_link(sources->rtcp_video_, tees.rtcpv_);
+ gst_element_link(sources->rtp_audio_, tees.rtpa_);
+ gst_element_link(sources->rtcp_audio_, tees.rtcpa_);
+
+ if(!sydra_link_request_request(tees.rtpv_, "src_%u", rtp, "recv_rtp_sink_0") ||
+ !sydra_link_request_request(tees.rtcpv_, "src_%u", rtp, "recv_rtcp_sink_0") ||
+ !sydra_link_request_request(tees.rtpa_, "src_%u", rtp, "recv_rtp_sink_1") ||
+ !sydra_link_request_request(tees.rtcpa_, "src_%u", rtp, "recv_rtcp_sink_1"))
+ return FALSE;
+
+ log_printf(INFO, "udp sources linked successfully!");
+
+ if(opt->rtp_host_reflector_)
+ return create_rtp_reflector(opt, pipeline, &tees);
+
+ return TRUE;
+}
+
+static gboolean create_preview_elements(const char* preview_bin_desc, GstElement* pipeline, GstElement* tee)
+{
+ GstElement *qr = sydra_create_element("queue", NULL);
+ GstElement *preview_bin = sydra_create_bin_from_desc("preview sink bin", preview_bin_desc, TRUE);
+
+ if(!qr || !preview_bin) {
+ return FALSE;
+ }
+
+ log_printf(INFO, "preview path created successfully!");
+
+ gst_bin_add_many (GST_BIN(pipeline), qr, preview_bin, NULL);
+ gst_element_link(qr, preview_bin);
+
+ if(!sydra_link_request_static(tee, "src_%u", qr, "sink")) {
+ return FALSE;
+ }
+
+ log_printf(INFO, "preview path linked successfully!");
+ return TRUE;
+}
+
+static gboolean create_recorder_elements(options_t* opt, GstElement* pipeline, struct av_elements *ve, struct av_elements *ae)
+{
+ GstElement *qv = sydra_create_element("queue", NULL);
+ GstElement *qa = sydra_create_element("queue", NULL);
+ GstElement *mux = sydra_create_element(opt->rec_mux_, NULL);
+ GstElement *sink = sydra_create_element("filesink", NULL);
+
+ if(!qv || !qa || !mux || !sink) {
+ return FALSE;
+ }
+
+ GstElement *ev = NULL, *tv = ve->tee_enc_;
+ if(opt->video_enc_rec_) {
+ ev = sydra_create_bin_from_desc("record video encoder bin", opt->video_enc_rec_, TRUE);
+ if(!ev) return FALSE;
+ tv = ve->tee_raw_;
+ }
+ GstElement *ea = NULL, *ta = ae->tee_enc_;
+ if(opt->audio_enc_rec_) {
+ ea = sydra_create_bin_from_desc("record audio encoder bin", opt->audio_enc_rec_, TRUE);
+ if(!ea) return FALSE;
+ ta = ae->tee_raw_;
+ }
+
+ log_printf(INFO, "recorder path created successfully!");
+
+ struct timespec now;
+ clock_gettime(CLOCK_REALTIME, &now);
+ struct tm bd_time;
+ localtime_r(&(now.tv_sec), &bd_time);
+ char recfile[1024];
+ recfile[0] = 0;
+ strftime(recfile, sizeof(recfile), opt->rec_name_format_, &bd_time);
+ g_object_set(G_OBJECT(sink), "location", recfile, NULL);
+
+ gst_bin_add_many(GST_BIN(pipeline), qv, qa, mux, sink, NULL);
+ gst_element_link(mux, sink);
+ GstElement* sv = qv;
+ if(ev) {
+ gst_bin_add(GST_BIN(pipeline), ev);
+ gst_element_link(qv, ev);
+ sv = ev;
+ }
+ GstElement* sa = qa;
+ if(ev) {
+ gst_bin_add(GST_BIN(pipeline), ea);
+ gst_element_link(qa, ea);
+ sa = ea;
+ }
+
+ if(!sydra_link_request_static(tv, "src_%u", qv, "sink") ||
+ !sydra_link_static_compatible(sv, "src", mux) ||
+ !sydra_link_request_static(ta, "src_%u", qa, "sink") ||
+ !sydra_link_static_compatible(sa, "src", mux)) {
+ return FALSE;
+ }
+
+ log_printf(INFO, "recorder path linked successfully!");
+ return TRUE;
+}
+
+GstElement* create_sender_pipeline(options_t* opt, struct udp_sinks *udp)
+{
+ GstElement *pipeline = gst_pipeline_new ("sydra-rtp-sender");
+ if(!pipeline) {
+ log_printf(ERROR, "Creating pipeline failed!");
+ return NULL;
+ }
+ GstElement *rtp = sydra_create_element("rtpbin", "rtpbin");
+ if(!rtp || !gst_bin_add(GST_BIN(pipeline), rtp)) return NULL;
+ log_printf(INFO, "rtpbin created successfully!");
+
+ GstElement* src = create_avsend_src(opt->source_, pipeline);
+ if(!src) return NULL;
+ log_printf(INFO, "source bin created successfully!");
+
+ struct av_elements video = { "video", src, NULL, opt->video_enc_, NULL, NULL,
+ opt->video_payloader_, NULL };
+ struct av_elements audio = { "audio", src, NULL, opt->audio_enc_, NULL, NULL,
+ opt->audio_payloader_, NULL };
+ if(!create_avsend_elements(&video, pipeline, rtp, 0) ||
+ !create_avsend_elements(&audio, pipeline, rtp, 1) ||
+ !create_udp_sinks(opt, pipeline, rtp, udp)) {
+ return NULL;
+ }
+
+ if(opt->preview_videosink_) {
+ if(!create_preview_elements(opt->preview_videosink_, pipeline, video.tee_raw_))
+ return NULL;
+ }
+
+ if(opt->rec_mux_) {
+ if(!create_recorder_elements(opt, pipeline, &video, &audio))
+ return NULL;
+ }
+
+ log_printf(INFO, "sender pipeline created successfully!");
+ return pipeline;
+}
+
+static void rtpbin_pad_added(GstElement* rtp, GstPad* pad, gpointer user_data)
+{
+ GstElement **depays = (GstElement**)user_data;
+
+ GstPadTemplate *pad_template = gst_pad_get_pad_template(pad);
+ if(pad_template == NULL) return;
+ if(strcmp("recv_rtp_src_%u_%u_%u", GST_PAD_TEMPLATE_NAME_TEMPLATE(pad_template))) {
+ gst_object_unref(GST_OBJECT(pad_template));
+ return;
+ }
+ gst_object_unref(GST_OBJECT(pad_template));
+
+ gchar* src_pad_name = gst_element_get_name(pad);
+ log_printf(INFO, "rtpbin: new pad created %s", src_pad_name);
+
+ guint i;
+ for(i = 0; i < 2; i++) {
+ GstPad *sink_pad = gst_element_get_static_pad(depays[i], "sink");
+ if(gst_pad_is_linked(sink_pad) || !gst_pad_can_link(pad, sink_pad)) {
+ gst_object_unref(GST_OBJECT(sink_pad));
+ continue;
+ }
+ GstPadLinkReturn ret = gst_pad_link(pad, sink_pad);
+ gst_object_unref(GST_OBJECT(sink_pad));
+
+ if(GST_PAD_LINK_FAILED(ret)) {
+ gchar* src_name = gst_element_get_name(rtp);
+ gchar* sink_name = gst_element_get_name(depays[i]);
+ log_printf(ERROR, "Error linking pad '%s' of '%s' with pad '%s' of '%s'",
+ src_pad_name, src_name, "sink", sink_name);
+ g_free(src_name);
+ g_free(sink_name);
+ continue;
+ }
+ break;
+ }
+ if(!gst_pad_is_linked(pad)) {
+ log_printf(ERROR, "rtpbin: no compatible depayloader found for pad %s (or all depayloader already connected)", src_pad_name);
+ GstStructure* ms = gst_structure_new("sydra-rtp", "quit", G_TYPE_BOOLEAN, TRUE, "reason",
+ G_TYPE_STRING, "New RTP Session but all depayloader are already connected or none compatible found", NULL);
+ GstMessage* msg = gst_message_new_application(GST_OBJECT(rtp), ms);
+ if(!ms || ! msg)
+ log_printf(ERROR, "rtpbin: message creation failed!");
+ else {
+ if(!gst_element_post_message(rtp, msg))
+ log_printf(ERROR, "rtpbin: sending message to the application failed: no bus");
+ }
+ }
+ g_free(src_pad_name);
+}
+
+static void rtp_new_ssrc(GstElement *rtp, guint session, guint ssrc, gpointer user_data)
+{
+ log_printf(INFO, "rtpbin: new SSRC %u for session %u", ssrc, session);
+}
+
+static void rtp_ssrc_timeout(GstElement *rtp, guint session, guint ssrc, gpointer user_data)
+{
+ log_printf(INFO, "rtpbin: SSRC %u of session %u timed out", ssrc, session);
+ GstStructure* ms = gst_structure_new("sydra-rtp", "quit", G_TYPE_BOOLEAN, TRUE, "reason",
+ G_TYPE_STRING, "RTP session timed out", NULL);
+ GstMessage* msg = gst_message_new_application(GST_OBJECT(rtp), ms);
+ if(!ms || ! msg)
+ log_printf(ERROR, "rtpbin: message creation failed!");
+ else {
+ if(!gst_element_post_message(rtp, msg))
+ log_printf(ERROR, "rtpbin: sending message to the application failed: no bus");
+ }
+}
+
+GstElement* create_receiver_pipeline(options_t* opt, struct udp_sources *udp)
+{
+ GstElement *pipeline = gst_pipeline_new ("sydra-rtp-receiver");
+ if(!pipeline) {
+ log_printf(ERROR, "Creating pipeline failed!");
+ return NULL;
+ }
+ GstElement *rtp = sydra_create_element("rtpbin", "rtpbin");
+ if(!rtp || !gst_bin_add(GST_BIN(pipeline), rtp)) {
+ return NULL;
+ }
+ log_printf(INFO, "rtpbin created successfully!");
+
+ GstElement* sink = create_avrecv_sink(opt->sink_, pipeline);
+ if(!sink) return NULL;
+ log_printf(INFO, "sink bin created successfully!");
+
+ struct av_elements video = { "video", sink, NULL, opt->video_dec_, NULL, NULL,
+ opt->video_depayloader_, NULL };
+ struct av_elements audio = { "audio", sink, NULL, opt->audio_dec_, NULL, NULL,
+ opt->audio_depayloader_, NULL };
+ if(!create_udp_sources(opt, pipeline, rtp, udp) ||
+ !create_avrecv_elements(&video, pipeline) ||
+ !create_avrecv_elements(&audio, pipeline)) {
+ return NULL;
+ }
+ GstElement **depays = g_new(GstElement*, 2);
+ depays[0] = video.payloader_;
+ depays[1] = audio.payloader_;
+ g_signal_connect_closure(rtp, "pad-added", g_cclosure_new(G_CALLBACK(rtpbin_pad_added), depays, NULL), FALSE);
+ g_signal_connect(rtp, "on-new-ssrc", G_CALLBACK(rtp_new_ssrc), NULL);
+ g_signal_connect(rtp, "on-timeout", G_CALLBACK(rtp_ssrc_timeout), NULL);
+
+ if(opt->rec_mux_) {
+ if(!create_recorder_elements(opt, pipeline, &video, &audio))
+ return NULL;
+ }
+
+ log_printf(INFO, "receiver pipeline created successfully!");
+ return pipeline;
+}