gstreamer/tests/examples/webrtc/webrtcswap.c
Matthew Waters 1894293d63 webrtcbin: an element that handles the transport aspects of webrtc connections
SDP's are generated and consumed according to the W3C PeerConnection API
available from https://www.w3.org/TR/webrtc/

The SDP is either created initially from the connected
sink pads/attached transceivers as in the case of generating an offer or
intersected with the connected sink pads/attached transceivers as in
the case for creating an answer.  In both cases, the rtp payloaded streams
sent by the peer are exposed as separate src pads.

The implementation supports trickle ICE, RTCP muxing, reduced size RTCP.

With contributions from:
Nirbheek Chauhan <nirbheek@centricular.com>
Mathieu Duponchelle <mathieu@centricular.com>
Edward Hervey <edward@centricular.com>

https://bugzilla.gnome.org/show_bug.cgi?id=792523
2018-02-02 15:02:21 +11:00

215 lines
6.7 KiB
C

#include <gst/gst.h>
#include <gst/sdp/sdp.h>
#include <gst/webrtc/webrtc.h>
#include <string.h>
static GMainLoop *loop;
static GstElement *pipe1, *webrtc1, *webrtc2;
static GstBus *bus1;
static gboolean
_bus_watch (GstBus * bus, GstMessage * msg, GstElement * pipe)
{
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_STATE_CHANGED:
if (GST_ELEMENT (msg->src) == pipe) {
GstState old, new, pending;
gst_message_parse_state_changed (msg, &old, &new, &pending);
{
gchar *dump_name = g_strconcat ("state_changed-",
gst_element_state_get_name (old), "_",
gst_element_state_get_name (new), NULL);
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (msg->src),
GST_DEBUG_GRAPH_SHOW_ALL, dump_name);
g_free (dump_name);
}
}
break;
case GST_MESSAGE_ERROR:{
GError *err = NULL;
gchar *dbg_info = NULL;
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipe),
GST_DEBUG_GRAPH_SHOW_ALL, "error");
gst_message_parse_error (msg, &err, &dbg_info);
g_printerr ("ERROR from element %s: %s\n",
GST_OBJECT_NAME (msg->src), err->message);
g_printerr ("Debugging info: %s\n", (dbg_info) ? dbg_info : "none");
g_error_free (err);
g_free (dbg_info);
g_main_loop_quit (loop);
break;
}
case GST_MESSAGE_EOS:{
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipe),
GST_DEBUG_GRAPH_SHOW_ALL, "eos");
g_print ("EOS received\n");
g_main_loop_quit (loop);
break;
}
default:
break;
}
return TRUE;
}
static void
_webrtc_pad_added (GstElement * webrtc, GstPad * new_pad, GstElement * pipe)
{
GstElement *out = NULL;
GstPad *sink = NULL;
GstCaps *caps;
GstStructure *s;
const gchar *encoding_name;
if (GST_PAD_DIRECTION (new_pad) != GST_PAD_SRC)
return;
caps = gst_pad_get_current_caps (new_pad);
if (!caps)
caps = gst_pad_query_caps (new_pad, NULL);
GST_ERROR_OBJECT (new_pad, "caps %" GST_PTR_FORMAT, caps);
g_assert (gst_caps_is_fixed (caps));
s = gst_caps_get_structure (caps, 0);
encoding_name = gst_structure_get_string (s, "encoding-name");
if (g_strcmp0 (encoding_name, "VP8") == 0) {
out = gst_parse_bin_from_description ("rtpvp8depay ! vp8dec ! "
"videoconvert ! queue ! xvimagesink sync=false", TRUE, NULL);
} else if (g_strcmp0 (encoding_name, "OPUS") == 0) {
out = gst_parse_bin_from_description ("rtpopusdepay ! opusdec ! "
"audioconvert ! audioresample ! audiorate ! queue ! autoaudiosink",
TRUE, NULL);
} else {
g_critical ("Unknown encoding name %s", encoding_name);
g_assert_not_reached ();
}
gst_bin_add (GST_BIN (pipe), out);
gst_element_sync_state_with_parent (out);
sink = out->sinkpads->data;
gst_pad_link (new_pad, sink);
gst_caps_unref (caps);
}
static void
_on_answer_received (GstPromise * promise, gpointer user_data)
{
GstWebRTCSessionDescription *answer = NULL;
const GstStructure *reply;
gchar *desc;
g_assert (gst_promise_wait (promise) == GST_PROMISE_RESULT_REPLIED);
reply = gst_promise_get_reply (promise);
gst_structure_get (reply, "answer",
GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &answer, NULL);
gst_promise_unref (promise);
desc = gst_sdp_message_as_text (answer->sdp);
g_print ("Created answer:\n%s\n", desc);
g_free (desc);
g_signal_emit_by_name (webrtc1, "set-remote-description", answer, NULL);
g_signal_emit_by_name (webrtc2, "set-local-description", answer, NULL);
gst_webrtc_session_description_free (answer);
}
static void
_on_offer_received (GstPromise * promise, gpointer user_data)
{
GstWebRTCSessionDescription *offer = NULL;
const GstStructure *reply;
gchar *desc;
g_assert (gst_promise_wait (promise) == GST_PROMISE_RESULT_REPLIED);
reply = gst_promise_get_reply (promise);
gst_structure_get (reply, "offer",
GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &offer, NULL);
gst_promise_unref (promise);
desc = gst_sdp_message_as_text (offer->sdp);
g_print ("Created offer:\n%s\n", desc);
g_free (desc);
g_signal_emit_by_name (webrtc1, "set-local-description", offer, NULL);
g_signal_emit_by_name (webrtc2, "set-remote-description", offer, NULL);
promise = gst_promise_new_with_change_func (_on_answer_received, user_data,
NULL);
g_signal_emit_by_name (webrtc2, "create-answer", NULL, promise);
gst_webrtc_session_description_free (offer);
}
static void
_on_negotiation_needed (GstElement * element, gpointer user_data)
{
GstPromise *promise;
promise = gst_promise_new_with_change_func (_on_offer_received, user_data,
NULL);
g_signal_emit_by_name (webrtc1, "create-offer", NULL, promise);
}
static void
_on_ice_candidate (GstElement * webrtc, guint mlineindex, gchar * candidate,
GstElement * other)
{
g_signal_emit_by_name (other, "add-ice-candidate", mlineindex, candidate);
}
int
main (int argc, char *argv[])
{
gst_init (&argc, &argv);
loop = g_main_loop_new (NULL, FALSE);
pipe1 =
gst_parse_launch ("webrtcbin name=smpte webrtcbin name=ball "
"videotestsrc pattern=smpte ! queue ! vp8enc ! rtpvp8pay ! queue ! "
"application/x-rtp,media=video,payload=96,encoding-name=VP8 ! smpte.sink_0 "
"audiotestsrc ! opusenc ! rtpopuspay ! queue ! "
"application/x-rtp,media=audio,payload=97,encoding-name=OPUS ! smpte.sink_1 "
"videotestsrc pattern=ball ! queue ! vp8enc ! rtpvp8pay ! queue ! "
"application/x-rtp,media=video,payload=96,encoding-name=VP8 ! ball.sink_1 "
"audiotestsrc wave=saw ! opusenc ! rtpopuspay ! queue ! "
"application/x-rtp,media=audio,payload=97,encoding-name=OPUS ! ball.sink_0 ",
NULL);
bus1 = gst_pipeline_get_bus (GST_PIPELINE (pipe1));
gst_bus_add_watch (bus1, (GstBusFunc) _bus_watch, pipe1);
webrtc1 = gst_bin_get_by_name (GST_BIN (pipe1), "smpte");
g_signal_connect (webrtc1, "on-negotiation-needed",
G_CALLBACK (_on_negotiation_needed), NULL);
g_signal_connect (webrtc1, "pad-added", G_CALLBACK (_webrtc_pad_added),
pipe1);
webrtc2 = gst_bin_get_by_name (GST_BIN (pipe1), "ball");
g_signal_connect (webrtc2, "pad-added", G_CALLBACK (_webrtc_pad_added),
pipe1);
g_signal_connect (webrtc1, "on-ice-candidate",
G_CALLBACK (_on_ice_candidate), webrtc2);
g_signal_connect (webrtc2, "on-ice-candidate",
G_CALLBACK (_on_ice_candidate), webrtc1);
g_print ("Starting pipeline\n");
gst_element_set_state (GST_ELEMENT (pipe1), GST_STATE_PLAYING);
g_main_loop_run (loop);
gst_element_set_state (GST_ELEMENT (pipe1), GST_STATE_NULL);
g_print ("Pipeline stopped\n");
gst_object_unref (webrtc1);
gst_object_unref (webrtc2);
gst_bus_remove_watch (bus1);
gst_object_unref (bus1);
gst_object_unref (pipe1);
gst_deinit ();
return 0;
}