mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-24 18:51:11 +00:00
1894293d63
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
197 lines
5.9 KiB
C
197 lines
5.9 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;
|
|
GstPad *sink;
|
|
|
|
if (GST_PAD_DIRECTION (new_pad) != GST_PAD_SRC)
|
|
return;
|
|
|
|
out = gst_parse_bin_from_description ("rtpvp8depay ! vp8dec ! "
|
|
"videoconvert ! queue ! xvimagesink", TRUE, NULL);
|
|
gst_bin_add (GST_BIN (pipe), out);
|
|
gst_element_sync_state_with_parent (out);
|
|
|
|
sink = out->sinkpads->data;
|
|
|
|
gst_pad_link (new_pad, sink);
|
|
}
|
|
|
|
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);
|
|
|
|
/* this is one way to tell webrtcbin that we don't want to be notified when
|
|
* this task is complete: set a NULL promise */
|
|
g_signal_emit_by_name (webrtc1, "set-remote-description", answer, NULL);
|
|
/* this is another way to tell webrtcbin that we don't want to be notified
|
|
* when this task is complete: interrupt the promise */
|
|
promise = gst_promise_new ();
|
|
g_signal_emit_by_name (webrtc2, "set-local-description", answer, NULL);
|
|
gst_promise_interrupt (promise);
|
|
gst_promise_unref (promise);
|
|
|
|
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 ("videotestsrc ! queue ! vp8enc ! rtpvp8pay ! queue ! "
|
|
"application/x-rtp,media=video,payload=96,encoding-name=VP8 ! "
|
|
"webrtcbin name=smpte videotestsrc pattern=ball ! queue ! vp8enc ! rtpvp8pay ! queue ! "
|
|
"application/x-rtp,media=video,payload=96,encoding-name=VP8 ! webrtcbin name=ball",
|
|
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;
|
|
}
|