1 #include <gst/gst.h>
2 #include <gst/sdp/sdp.h>
3 #include <gst/webrtc/webrtc.h>
4
5 #include <string.h>
6
7 static GMainLoop *loop;
8 static GstElement *pipe1, *webrtc1, *webrtc2;
9 static GstBus *bus1;
10
11 static gboolean
_bus_watch(GstBus * bus,GstMessage * msg,GstElement * pipe)12 _bus_watch (GstBus * bus, GstMessage * msg, GstElement * pipe)
13 {
14 switch (GST_MESSAGE_TYPE (msg)) {
15 case GST_MESSAGE_STATE_CHANGED:
16 if (GST_ELEMENT (msg->src) == pipe) {
17 GstState old, new, pending;
18
19 gst_message_parse_state_changed (msg, &old, &new, &pending);
20
21 {
22 gchar *dump_name = g_strconcat ("state_changed-",
23 gst_element_state_get_name (old), "_",
24 gst_element_state_get_name (new), NULL);
25 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (msg->src),
26 GST_DEBUG_GRAPH_SHOW_ALL, dump_name);
27 g_free (dump_name);
28 }
29 }
30 break;
31 case GST_MESSAGE_ERROR:{
32 GError *err = NULL;
33 gchar *dbg_info = NULL;
34
35 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipe),
36 GST_DEBUG_GRAPH_SHOW_ALL, "error");
37
38 gst_message_parse_error (msg, &err, &dbg_info);
39 g_printerr ("ERROR from element %s: %s\n",
40 GST_OBJECT_NAME (msg->src), err->message);
41 g_printerr ("Debugging info: %s\n", (dbg_info) ? dbg_info : "none");
42 g_error_free (err);
43 g_free (dbg_info);
44 g_main_loop_quit (loop);
45 break;
46 }
47 case GST_MESSAGE_EOS:{
48 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipe),
49 GST_DEBUG_GRAPH_SHOW_ALL, "eos");
50 g_print ("EOS received\n");
51 g_main_loop_quit (loop);
52 break;
53 }
54 default:
55 break;
56 }
57
58 return TRUE;
59 }
60
61 static void
_webrtc_pad_added(GstElement * webrtc,GstPad * new_pad,GstElement * pipe)62 _webrtc_pad_added (GstElement * webrtc, GstPad * new_pad, GstElement * pipe)
63 {
64 GstElement *out;
65 GstPad *sink;
66
67 if (GST_PAD_DIRECTION (new_pad) != GST_PAD_SRC)
68 return;
69
70 out = gst_parse_bin_from_description ("rtpvp8depay ! vp8dec ! "
71 "videoconvert ! queue ! xvimagesink", TRUE, NULL);
72 gst_bin_add (GST_BIN (pipe), out);
73 gst_element_sync_state_with_parent (out);
74
75 sink = out->sinkpads->data;
76
77 gst_pad_link (new_pad, sink);
78 }
79
80 static void
_on_answer_received(GstPromise * promise,gpointer user_data)81 _on_answer_received (GstPromise * promise, gpointer user_data)
82 {
83 GstWebRTCSessionDescription *answer = NULL;
84 const GstStructure *reply;
85 gchar *desc;
86
87 g_assert (gst_promise_wait (promise) == GST_PROMISE_RESULT_REPLIED);
88 reply = gst_promise_get_reply (promise);
89 gst_structure_get (reply, "answer",
90 GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &answer, NULL);
91 gst_promise_unref (promise);
92 desc = gst_sdp_message_as_text (answer->sdp);
93 g_print ("Created answer:\n%s\n", desc);
94 g_free (desc);
95
96 g_signal_emit_by_name (webrtc1, "set-remote-description", answer, NULL);
97 g_signal_emit_by_name (webrtc2, "set-local-description", answer, NULL);
98
99 gst_webrtc_session_description_free (answer);
100 }
101
102 static void
_on_offer_received(GstPromise * promise,gpointer user_data)103 _on_offer_received (GstPromise * promise, gpointer user_data)
104 {
105 GstWebRTCSessionDescription *offer = NULL;
106 const GstStructure *reply;
107 gchar *desc;
108
109 g_assert (gst_promise_wait (promise) == GST_PROMISE_RESULT_REPLIED);
110 reply = gst_promise_get_reply (promise);
111 gst_structure_get (reply, "offer",
112 GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &offer, NULL);
113 gst_promise_unref (promise);
114 desc = gst_sdp_message_as_text (offer->sdp);
115 g_print ("Created offer:\n%s\n", desc);
116 g_free (desc);
117
118 g_signal_emit_by_name (webrtc1, "set-local-description", offer, NULL);
119 g_signal_emit_by_name (webrtc2, "set-remote-description", offer, NULL);
120
121 promise = gst_promise_new_with_change_func (_on_answer_received, user_data,
122 NULL);
123 g_signal_emit_by_name (webrtc2, "create-answer", NULL, promise);
124
125 gst_webrtc_session_description_free (offer);
126 }
127
128 static void
_on_negotiation_needed(GstElement * element,gpointer user_data)129 _on_negotiation_needed (GstElement * element, gpointer user_data)
130 {
131 GstPromise *promise;
132
133 promise = gst_promise_new_with_change_func (_on_offer_received, user_data,
134 NULL);
135 g_signal_emit_by_name (webrtc1, "create-offer", NULL, promise);
136 }
137
138 static void
_on_ice_candidate(GstElement * webrtc,guint mlineindex,gchar * candidate,GstElement * other)139 _on_ice_candidate (GstElement * webrtc, guint mlineindex, gchar * candidate,
140 GstElement * other)
141 {
142 g_signal_emit_by_name (other, "add-ice-candidate", mlineindex, candidate);
143 }
144
145 static void
_on_new_transceiver(GstElement * webrtc,GstWebRTCRTPTransceiver * trans)146 _on_new_transceiver (GstElement * webrtc, GstWebRTCRTPTransceiver * trans)
147 {
148 /* If we expected more than one transceiver, we would take a look at
149 * trans->mline, and compare it with webrtcbin's local description */
150 g_object_set (trans, "fec-type", GST_WEBRTC_FEC_TYPE_ULP_RED, NULL);
151 }
152
153 static void
add_fec_to_offer(GstElement * webrtc)154 add_fec_to_offer (GstElement * webrtc)
155 {
156 GstWebRTCRTPTransceiver *trans;
157 GArray *transceivers;
158
159 /* A transceiver has already been created when a sink pad was
160 * requested on the sending webrtcbin */
161
162 g_signal_emit_by_name (webrtc, "get-transceivers", &transceivers);
163
164 trans = g_array_index (transceivers, GstWebRTCRTPTransceiver *, 0);
165
166 g_object_set (trans, "fec-type", GST_WEBRTC_FEC_TYPE_ULP_RED,
167 "fec-percentage", 100, NULL);
168
169 g_array_unref (transceivers);
170 }
171
172 int
main(int argc,char * argv[])173 main (int argc, char *argv[])
174 {
175 gst_init (&argc, &argv);
176
177 loop = g_main_loop_new (NULL, FALSE);
178 pipe1 =
179 gst_parse_launch
180 ("videotestsrc pattern=ball ! video/x-raw ! queue ! vp8enc ! rtpvp8pay ! queue ! "
181 "application/x-rtp,media=video,payload=96,encoding-name=VP8 ! "
182 "webrtcbin name=send webrtcbin name=recv", NULL);
183 bus1 = gst_pipeline_get_bus (GST_PIPELINE (pipe1));
184 gst_bus_add_watch (bus1, (GstBusFunc) _bus_watch, pipe1);
185
186 webrtc1 = gst_bin_get_by_name (GST_BIN (pipe1), "send");
187 g_signal_connect (webrtc1, "on-negotiation-needed",
188 G_CALLBACK (_on_negotiation_needed), NULL);
189 add_fec_to_offer (webrtc1);
190
191 webrtc2 = gst_bin_get_by_name (GST_BIN (pipe1), "recv");
192 g_signal_connect (webrtc2, "pad-added", G_CALLBACK (_webrtc_pad_added),
193 pipe1);
194 g_signal_connect (webrtc1, "on-ice-candidate",
195 G_CALLBACK (_on_ice_candidate), webrtc2);
196 g_signal_connect (webrtc2, "on-ice-candidate",
197 G_CALLBACK (_on_ice_candidate), webrtc1);
198 g_signal_connect (webrtc2, "on-new-transceiver",
199 G_CALLBACK (_on_new_transceiver), NULL);
200
201 g_print ("Starting pipeline\n");
202 gst_element_set_state (GST_ELEMENT (pipe1), GST_STATE_PLAYING);
203
204 g_main_loop_run (loop);
205
206 gst_element_set_state (GST_ELEMENT (pipe1), GST_STATE_NULL);
207 g_print ("Pipeline stopped\n");
208
209 gst_object_unref (webrtc1);
210 gst_object_unref (webrtc2);
211 gst_bus_remove_watch (bus1);
212 gst_object_unref (bus1);
213 gst_object_unref (pipe1);
214
215 gst_deinit ();
216
217 return 0;
218 }
219