rtpbin: receive bundle support
[platform/upstream/gst-plugins-good.git] / tests / examples / rtp / client-rtpbundle.c
1 /* GStreamer
2  * Copyright (C) 2016 Igalia S.L
3  *   @author Philippe Normand <philn@igalia.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #include <gst/gst.h>
22
23 /*
24  * RTP bundle receiver
25  *
26  * In this example we initially create one RTP session but the incoming RTP
27  * and RTCP streams actually bundle 2 different media type, one audio stream
28  * and one video stream. We are notified of the discovery of the streams by
29  * the on-bundled-ssrc rtpbin signal. In the handler we decide to assign the
30  * first SSRC to the (existing) audio session and the second SSRC to a new
31  * session (id: 1).
32  *
33  *             .-------.      .----------.        .-----------.    .-------.    .-------------.
34  *  RTP        |udpsrc |      | rtpbin   |        | pcmadepay |    |alawdec|    |autoaudiosink|
35  *  port=5001  |      src->recv_rtp_0 recv_rtp_0->sink       src->sink    src->sink           |
36  *             '-------'      |          |        '-----------'    '-------'    '-------------'
37  *                            |          |
38  *                            |          |     .-------.
39  *                            |          |     |udpsink|  RTCP
40  *                            |  send_rtcp_0->sink     | port=5003
41  *             .-------.      |          |     '-------' sync=false
42  *  RTCP       |udpsrc |      |          |               async=false
43  *  port=5002  |     src->recv_rtcp_0    |
44  *             '-------'      |          |
45  *                            |          |
46  *                            |          |        .---------.    .-------------.
47  *                            |          |        |vrawdepay|    |autovideosink|
48  *                            |       recv_rtp_1->sink     src->sink           |
49  *                            |          |        '---------'    '-------------'
50  *                            |          |
51  *                            |          |     .-------.
52  *                            |          |     |udpsink|  RTCP
53  *                            |  send_rtcp_1->sink     | port=5004
54  *                            |          |     '-------' sync=false
55  *                            |          |               async=false
56  *                            |          |
57  *                            '----------'
58  *
59  */
60
61 static gboolean
62 plug_video_rtcp_sender (gpointer user_data)
63 {
64   gint send_video_rtcp_port = 5004;
65   GstElement *rtpbin = GST_ELEMENT_CAST (user_data);
66   GstElement *send_video_rtcp_udpsink;
67   GstElement *pipeline =
68       GST_ELEMENT_CAST (gst_object_get_parent (GST_OBJECT (rtpbin)));
69
70   send_video_rtcp_udpsink = gst_element_factory_make ("udpsink", NULL);
71   g_object_set (send_video_rtcp_udpsink, "host", "127.0.0.1", NULL);
72   g_object_set (send_video_rtcp_udpsink, "port", send_video_rtcp_port, NULL);
73   g_object_set (send_video_rtcp_udpsink, "sync", FALSE, NULL);
74   g_object_set (send_video_rtcp_udpsink, "async", FALSE, NULL);
75   gst_bin_add (GST_BIN (pipeline), send_video_rtcp_udpsink);
76   gst_element_link_pads (rtpbin, "send_rtcp_src_1", send_video_rtcp_udpsink,
77       "sink");
78   gst_element_sync_state_with_parent (send_video_rtcp_udpsink);
79
80   gst_object_unref (pipeline);
81   gst_object_unref (rtpbin);
82   return G_SOURCE_REMOVE;
83 }
84
85 static void
86 on_rtpbinreceive_pad_added (GstElement * rtpbin, GstPad * new_pad,
87     gpointer data)
88 {
89   GstElement *pipeline = GST_ELEMENT (data);
90   gchar *pad_name = gst_pad_get_name (new_pad);
91
92   if (g_str_has_prefix (pad_name, "recv_rtp_src_")) {
93     GstCaps *caps = gst_pad_get_current_caps (new_pad);
94     GstStructure *s = gst_caps_get_structure (caps, 0);
95     const gchar *media_type = gst_structure_get_string (s, "media");
96     gchar *depayloader_name = g_strdup_printf ("%s_rtpdepayloader", media_type);
97     GstElement *rtpdepayloader =
98         gst_bin_get_by_name (GST_BIN (pipeline), depayloader_name);
99     GstPad *sinkpad;
100
101     g_free (depayloader_name);
102
103     sinkpad = gst_element_get_static_pad (rtpdepayloader, "sink");
104     gst_pad_link (new_pad, sinkpad);
105     gst_object_unref (sinkpad);
106     gst_object_unref (rtpdepayloader);
107
108     gst_caps_unref (caps);
109
110     if (g_str_has_prefix (pad_name, "recv_rtp_src_1")) {
111       g_timeout_add (0, plug_video_rtcp_sender, gst_object_ref (rtpbin));
112     }
113   }
114   g_free (pad_name);
115 }
116
117 static guint
118 on_bundled_ssrc (GstElement * rtpbin, guint ssrc, gpointer user_data)
119 {
120   static gboolean create_session = FALSE;
121   guint session_id = 0;
122
123   if (create_session) {
124     session_id = 1;
125   } else {
126     create_session = TRUE;
127     /* use existing session 0, a new session will be created for the next discovered bundled SSRC */
128   }
129   return session_id;
130 }
131
132 static GstCaps *
133 on_request_pt_map (GstElement * rtpbin, guint session_id, guint pt,
134     gpointer user_data)
135 {
136   GstCaps *caps = NULL;
137   if (pt == 96) {
138     caps =
139         gst_caps_from_string
140         ("application/x-rtp,media=(string)audio,encoding-name=(string)PCMA,clock-rate=(int)8000");
141   } else if (pt == 100) {
142     caps =
143         gst_caps_from_string
144         ("application/x-rtp,media=(string)video,encoding-name=(string)RAW,clock-rate=(int)90000,sampling=(string)\"YCbCr-4:2:0\",depth=(string)8,width=(string)320,height=(string)240");
145   }
146   return caps;
147 }
148
149 static GstElement *
150 create_pipeline (void)
151 {
152   GstElement *pipeline, *rtpbin, *recv_rtp_udpsrc, *recv_rtcp_udpsrc,
153       *audio_rtpdepayloader, *audio_decoder, *audio_sink, *video_rtpdepayloader,
154       *video_sink, *send_audio_rtcp_udpsink;
155   GstCaps *rtpcaps;
156   gint rtp_udp_port = 5001;
157   gint rtcp_udp_port = 5002;
158   gint send_audio_rtcp_port = 5003;
159
160   pipeline = gst_pipeline_new (NULL);
161
162   rtpbin = gst_element_factory_make ("rtpbin", NULL);
163   g_object_set (rtpbin, "latency", 200, NULL);
164
165   g_signal_connect (rtpbin, "on-bundled-ssrc",
166       G_CALLBACK (on_bundled_ssrc), NULL);
167   g_signal_connect (rtpbin, "request-pt-map",
168       G_CALLBACK (on_request_pt_map), NULL);
169
170   g_signal_connect (rtpbin, "pad-added",
171       G_CALLBACK (on_rtpbinreceive_pad_added), pipeline);
172
173   gst_bin_add (GST_BIN (pipeline), rtpbin);
174
175   recv_rtp_udpsrc = gst_element_factory_make ("udpsrc", NULL);
176   g_object_set (recv_rtp_udpsrc, "port", rtp_udp_port, NULL);
177   rtpcaps = gst_caps_from_string ("application/x-rtp");
178   g_object_set (recv_rtp_udpsrc, "caps", rtpcaps, NULL);
179   gst_caps_unref (rtpcaps);
180
181   recv_rtcp_udpsrc = gst_element_factory_make ("udpsrc", NULL);
182   g_object_set (recv_rtcp_udpsrc, "port", rtcp_udp_port, NULL);
183
184   audio_rtpdepayloader =
185       gst_element_factory_make ("rtppcmadepay", "audio_rtpdepayloader");
186   audio_decoder = gst_element_factory_make ("alawdec", NULL);
187   audio_sink = gst_element_factory_make ("autoaudiosink", NULL);
188
189   video_rtpdepayloader =
190       gst_element_factory_make ("rtpvrawdepay", "video_rtpdepayloader");
191   video_sink = gst_element_factory_make ("autovideosink", NULL);
192
193   gst_bin_add_many (GST_BIN (pipeline), recv_rtp_udpsrc, recv_rtcp_udpsrc,
194       audio_rtpdepayloader, audio_decoder, audio_sink, video_rtpdepayloader,
195       video_sink, NULL);
196
197   gst_element_link_pads (audio_rtpdepayloader, "src", audio_decoder, "sink");
198   gst_element_link (audio_decoder, audio_sink);
199
200   gst_element_link_pads (video_rtpdepayloader, "src", video_sink, "sink");
201
202   /* request a single receiving RTP session. */
203   gst_element_link_pads (recv_rtcp_udpsrc, "src", rtpbin, "recv_rtcp_sink_0");
204   gst_element_link_pads (recv_rtp_udpsrc, "src", rtpbin, "recv_rtp_sink_0");
205
206   send_audio_rtcp_udpsink = gst_element_factory_make ("udpsink", NULL);
207   g_object_set (send_audio_rtcp_udpsink, "host", "127.0.0.1", NULL);
208   g_object_set (send_audio_rtcp_udpsink, "port", send_audio_rtcp_port, NULL);
209   g_object_set (send_audio_rtcp_udpsink, "sync", FALSE, NULL);
210   g_object_set (send_audio_rtcp_udpsink, "async", FALSE, NULL);
211   gst_bin_add (GST_BIN (pipeline), send_audio_rtcp_udpsink);
212   gst_element_link_pads (rtpbin, "send_rtcp_src_0", send_audio_rtcp_udpsink,
213       "sink");
214
215   return pipeline;
216 }
217
218 /*
219  * Used to generate informative messages during pipeline startup
220  */
221 static void
222 cb_state (GstBus * bus, GstMessage * message, gpointer data)
223 {
224   GstObject *pipe = GST_OBJECT (data);
225   GstState old, new, pending;
226   gst_message_parse_state_changed (message, &old, &new, &pending);
227   if (message->src == pipe) {
228     g_print ("Pipeline %s changed state from %s to %s\n",
229         GST_OBJECT_NAME (message->src),
230         gst_element_state_get_name (old), gst_element_state_get_name (new));
231     if (old == GST_STATE_PAUSED && new == GST_STATE_PLAYING)
232       GST_DEBUG_BIN_TO_DOT_FILE (GST_BIN (pipe), GST_DEBUG_GRAPH_SHOW_ALL,
233           GST_OBJECT_NAME (message->src));
234   }
235 }
236
237 int
238 main (int argc, char **argv)
239 {
240   GstElement *pipe;
241   GstBus *bus;
242   GMainLoop *loop;
243
244   gst_init (&argc, &argv);
245
246   loop = g_main_loop_new (NULL, FALSE);
247
248   pipe = create_pipeline ();
249   bus = gst_element_get_bus (pipe);
250   g_signal_connect (bus, "message::state-changed", G_CALLBACK (cb_state), pipe);
251   gst_bus_add_signal_watch (bus);
252   gst_object_unref (bus);
253
254   g_print ("starting server pipeline\n");
255   gst_element_set_state (pipe, GST_STATE_PLAYING);
256
257   g_main_loop_run (loop);
258
259   g_print ("stopping server pipeline\n");
260   gst_element_set_state (pipe, GST_STATE_NULL);
261
262   gst_object_unref (pipe);
263   g_main_loop_unref (loop);
264
265   return 0;
266 }