• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = NULL;
65   GstPad *sink = NULL;
66   GstCaps *caps;
67   GstStructure *s;
68   const gchar *encoding_name;
69 
70   if (GST_PAD_DIRECTION (new_pad) != GST_PAD_SRC)
71     return;
72 
73   caps = gst_pad_get_current_caps (new_pad);
74   if (!caps)
75     caps = gst_pad_query_caps (new_pad, NULL);
76   GST_ERROR_OBJECT (new_pad, "caps %" GST_PTR_FORMAT, caps);
77   g_assert (gst_caps_is_fixed (caps));
78   s = gst_caps_get_structure (caps, 0);
79   encoding_name = gst_structure_get_string (s, "encoding-name");
80   if (g_strcmp0 (encoding_name, "VP8") == 0) {
81     out = gst_parse_bin_from_description ("rtpvp8depay ! vp8dec ! "
82         "videoconvert ! queue ! xvimagesink sync=false", TRUE, NULL);
83   } else if (g_strcmp0 (encoding_name, "OPUS") == 0) {
84     out = gst_parse_bin_from_description ("rtpopusdepay ! opusdec ! "
85         "audioconvert ! audioresample ! audiorate ! queue ! autoaudiosink",
86         TRUE, NULL);
87   } else {
88     g_critical ("Unknown encoding name %s", encoding_name);
89     g_assert_not_reached ();
90   }
91   gst_bin_add (GST_BIN (pipe), out);
92   gst_element_sync_state_with_parent (out);
93   sink = out->sinkpads->data;
94 
95   gst_pad_link (new_pad, sink);
96 
97   gst_caps_unref (caps);
98 }
99 
100 static void
_on_answer_received(GstPromise * promise,gpointer user_data)101 _on_answer_received (GstPromise * promise, gpointer user_data)
102 {
103   GstWebRTCSessionDescription *answer = NULL;
104   const GstStructure *reply;
105   gchar *desc;
106 
107   g_assert (gst_promise_wait (promise) == GST_PROMISE_RESULT_REPLIED);
108   reply = gst_promise_get_reply (promise);
109   gst_structure_get (reply, "answer",
110       GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &answer, NULL);
111   gst_promise_unref (promise);
112   desc = gst_sdp_message_as_text (answer->sdp);
113   g_print ("Created answer:\n%s\n", desc);
114   g_free (desc);
115 
116   g_signal_emit_by_name (webrtc1, "set-remote-description", answer, NULL);
117   g_signal_emit_by_name (webrtc2, "set-local-description", answer, NULL);
118 
119   gst_webrtc_session_description_free (answer);
120 }
121 
122 static void
_on_offer_received(GstPromise * promise,gpointer user_data)123 _on_offer_received (GstPromise * promise, gpointer user_data)
124 {
125   GstWebRTCSessionDescription *offer = NULL;
126   const GstStructure *reply;
127   gchar *desc;
128 
129   g_assert (gst_promise_wait (promise) == GST_PROMISE_RESULT_REPLIED);
130   reply = gst_promise_get_reply (promise);
131   gst_structure_get (reply, "offer",
132       GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &offer, NULL);
133   gst_promise_unref (promise);
134   desc = gst_sdp_message_as_text (offer->sdp);
135   g_print ("Created offer:\n%s\n", desc);
136   g_free (desc);
137 
138   g_signal_emit_by_name (webrtc1, "set-local-description", offer, NULL);
139   g_signal_emit_by_name (webrtc2, "set-remote-description", offer, NULL);
140 
141   promise = gst_promise_new_with_change_func (_on_answer_received, user_data,
142       NULL);
143   g_signal_emit_by_name (webrtc2, "create-answer", NULL, promise);
144 
145   gst_webrtc_session_description_free (offer);
146 }
147 
148 static void
_on_negotiation_needed(GstElement * element,gpointer user_data)149 _on_negotiation_needed (GstElement * element, gpointer user_data)
150 {
151   GstPromise *promise;
152 
153   promise = gst_promise_new_with_change_func (_on_offer_received, user_data,
154       NULL);
155   g_signal_emit_by_name (webrtc1, "create-offer", NULL, promise);
156 }
157 
158 static void
_on_ice_candidate(GstElement * webrtc,guint mlineindex,gchar * candidate,GstElement * other)159 _on_ice_candidate (GstElement * webrtc, guint mlineindex, gchar * candidate,
160     GstElement * other)
161 {
162   g_signal_emit_by_name (other, "add-ice-candidate", mlineindex, candidate);
163 }
164 
165 int
main(int argc,char * argv[])166 main (int argc, char *argv[])
167 {
168   gst_init (&argc, &argv);
169 
170   loop = g_main_loop_new (NULL, FALSE);
171   pipe1 =
172       gst_parse_launch ("webrtcbin name=smpte webrtcbin name=ball "
173       "videotestsrc pattern=smpte ! queue ! vp8enc ! rtpvp8pay ! queue ! "
174       "application/x-rtp,media=video,payload=96,encoding-name=VP8 ! smpte.sink_0 "
175       "audiotestsrc ! opusenc ! rtpopuspay ! queue ! "
176       "application/x-rtp,media=audio,payload=97,encoding-name=OPUS ! smpte.sink_1 "
177       "videotestsrc pattern=ball ! queue ! vp8enc ! rtpvp8pay ! queue ! "
178       "application/x-rtp,media=video,payload=96,encoding-name=VP8 ! ball.sink_1 "
179       "audiotestsrc wave=saw ! opusenc ! rtpopuspay ! queue ! "
180       "application/x-rtp,media=audio,payload=97,encoding-name=OPUS ! ball.sink_0 ",
181       NULL);
182   bus1 = gst_pipeline_get_bus (GST_PIPELINE (pipe1));
183   gst_bus_add_watch (bus1, (GstBusFunc) _bus_watch, pipe1);
184 
185   webrtc1 = gst_bin_get_by_name (GST_BIN (pipe1), "smpte");
186   g_signal_connect (webrtc1, "on-negotiation-needed",
187       G_CALLBACK (_on_negotiation_needed), NULL);
188   g_signal_connect (webrtc1, "pad-added", G_CALLBACK (_webrtc_pad_added),
189       pipe1);
190   webrtc2 = gst_bin_get_by_name (GST_BIN (pipe1), "ball");
191   g_signal_connect (webrtc2, "pad-added", G_CALLBACK (_webrtc_pad_added),
192       pipe1);
193   g_signal_connect (webrtc1, "on-ice-candidate",
194       G_CALLBACK (_on_ice_candidate), webrtc2);
195   g_signal_connect (webrtc2, "on-ice-candidate",
196       G_CALLBACK (_on_ice_candidate), webrtc1);
197 
198   g_print ("Starting pipeline\n");
199   gst_element_set_state (GST_ELEMENT (pipe1), GST_STATE_PLAYING);
200 
201   g_main_loop_run (loop);
202 
203   gst_element_set_state (GST_ELEMENT (pipe1), GST_STATE_NULL);
204   g_print ("Pipeline stopped\n");
205 
206   gst_object_unref (webrtc1);
207   gst_object_unref (webrtc2);
208   gst_bus_remove_watch (bus1);
209   gst_object_unref (bus1);
210   gst_object_unref (pipe1);
211 
212   gst_deinit ();
213 
214   return 0;
215 }
216