From 93097a178c6d0d50cbcfdf26a1a63fd571138484 Mon Sep 17 00:00:00 2001 From: Christian Pointner Date: Tue, 7 Oct 2014 23:21:34 +0200 Subject: cleanup for multiple binaries --- src/pipelines-rtp.c | 589 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 589 insertions(+) create mode 100644 src/pipelines-rtp.c (limited to 'src/pipelines-rtp.c') 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 + * + * 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 . + * + * 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 +#include +#include + +#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; +} -- cgit v1.2.3