From 8d782e44602adf42f0f221a315aad3a1ff885612 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sat, 21 Oct 2017 19:43:01 +0530 Subject: [PATCH 001/130] Initial commit --- webrtc/.gitignore | 52 +++++++++++++++++++++++++++++++++++++++++++++++ webrtc/LICENSE | 25 +++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 webrtc/.gitignore create mode 100644 webrtc/LICENSE diff --git a/webrtc/.gitignore b/webrtc/.gitignore new file mode 100644 index 0000000000..c6127b38c1 --- /dev/null +++ b/webrtc/.gitignore @@ -0,0 +1,52 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf diff --git a/webrtc/LICENSE b/webrtc/LICENSE new file mode 100644 index 0000000000..bd5093440e --- /dev/null +++ b/webrtc/LICENSE @@ -0,0 +1,25 @@ +BSD 2-Clause License + +Copyright (c) 2017, Centricular +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From 663ad7ba98237801f2c6d4b7080f45ccf33af171 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sat, 21 Oct 2017 19:56:52 +0530 Subject: [PATCH 002/130] Add a simple python3 webrtc signalling server + client for testing + protocol documentation --- webrtc/signalling/Protocol.md | 80 ++++++++++++ webrtc/signalling/README.md | 26 ++++ webrtc/signalling/client.py | 85 +++++++++++++ webrtc/signalling/generate_cert.sh | 3 + webrtc/signalling/simple-server.py | 187 +++++++++++++++++++++++++++++ 5 files changed, 381 insertions(+) create mode 100644 webrtc/signalling/Protocol.md create mode 100644 webrtc/signalling/README.md create mode 100755 webrtc/signalling/client.py create mode 100755 webrtc/signalling/generate_cert.sh create mode 100755 webrtc/signalling/simple-server.py diff --git a/webrtc/signalling/Protocol.md b/webrtc/signalling/Protocol.md new file mode 100644 index 0000000000..1af892eee3 --- /dev/null +++ b/webrtc/signalling/Protocol.md @@ -0,0 +1,80 @@ +# Terminology + +### Client + +A GStreamer-based application + +### Browser + +A JS application that runs in the browser and uses built-in browser webrtc APIs + +### Peer + +Any webrtc-using application that can participate in a call + +### Signalling server + +Basic websockets server implemented in Python that manages the peers list and shovels data between peers + +# Overview + +This is a basic protocol for doing 1-1 audio+video calls between a gstreamer app and a JS app in a browser. + +# Peer registration and calling + +Peers must register with the signalling server before a call can be initiated. The server connection should stay open as long as the peer is available or in a call. + +This protocol builds upon https://github.com/shanet/WebRTC-Example/ + +* Connect to the websocket server +* Send `HELLO ` where `` is a string which will uniquely identify this peer +* Receive `HELLO` +* Any other message starting with `ERROR` is an error. + +* To connect to a peer, send `SESSION ` where `` identifies the peer to connect to, and receive `SESSION_OK` +* All further messages will be forwarded to the peer +* The call negotiation with the peer can be started by sending JSON encoded SDP and ICE + +* Closure of the server connection means the call has ended; either because the other peer ended it or went away +* To end the call, disconnect from the server. You may reconnect again whenever you wish. + +# Negotiation + +Once a call has been setup with the signalling server, the peers must negotiate SDP and ICE candidates with each other. + +The calling side must create an SDP offer and send it to the peer as a JSON object: + +```json +{ + "sdp": { + "sdp": "o=- [....]", + "type": "offer" + } +} +``` + +The callee must then reply with an answer: + +```json +{ + "sdp": { + "sdp": "o=- [....]", + "type": "answer" + } +} +``` + +ICE candidates must be exchanged similarly by exchanging JSON objects: + + +```json +{ + "ice": { + "candidate": ..., + "sdpMLineIndex": ..., + ... + } +} +``` + +Note that the structure of these is the same as that specified by the WebRTC spec. diff --git a/webrtc/signalling/README.md b/webrtc/signalling/README.md new file mode 100644 index 0000000000..898178208a --- /dev/null +++ b/webrtc/signalling/README.md @@ -0,0 +1,26 @@ +## Overview + +Read Protocol.md + +## Dependencies + +* Python 3 +* pip3 install --user websockets + +## Example usage + +In three separate tabs, run consecutively: + +```console +$ ./generate_certs.sh +$ ./simple-server.py +``` + +```console +$ ./client.py +Our uid is 'ws-test-client-8f63b9' +``` + +```console +$ ./client.py --call ws-test-client-8f63b9 +``` diff --git a/webrtc/signalling/client.py b/webrtc/signalling/client.py new file mode 100755 index 0000000000..0fa8227d78 --- /dev/null +++ b/webrtc/signalling/client.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +# +# Test client for simple 1-1 call signalling server +# +# Copyright (C) 2017 Centricular Ltd. +# +# Author: Nirbheek Chauhan +# + +import sys +import ssl +import json +import uuid +import asyncio +import websockets +import argparse + +parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) +parser.add_argument('--url', default='wss://localhost:8443', help='URL to connect to') +parser.add_argument('--call', default=None, help='uid of peer to call') + +options = parser.parse_args(sys.argv[1:]) + +SERVER_ADDR = options.url +CALLEE_ID = options.call +PEER_ID = 'ws-test-client-' + str(uuid.uuid4())[:6] + +sslctx = False +if SERVER_ADDR.startswith(('wss://', 'https://')): + sslctx = ssl.create_default_context() + # FIXME + sslctx.check_hostname = False + sslctx.verify_mode = ssl.CERT_NONE + +def reply_sdp_ice(msg): + # Here we'd parse the incoming JSON message for ICE and SDP candidates + print("Got: " + msg) + reply = json.dumps({'sdp': 'reply sdp'}) + print("Sent: " + reply) + return reply + +def send_sdp_ice(): + reply = json.dumps({'sdp': 'initial sdp'}) + print("Sent: " + reply) + return reply + +async def hello(): + async with websockets.connect(SERVER_ADDR, ssl=sslctx) as ws: + await ws.send('HELLO ' + PEER_ID) + assert(await ws.recv() == 'HELLO') + + # Initiate call if requested + if CALLEE_ID: + await ws.send('CALL {}'.format(CALLEE_ID)) + + # Receive messages + while True: + msg = await ws.recv() + if msg.startswith('ERROR'): + # On error, we bring down the webrtc pipeline, etc + print('{!r}, exiting'.format(msg)) + return + if CALLEE_ID: + if msg == 'CALL_OK': + await ws.send(send_sdp_ice()) + # Return so we don't have an infinite loop + return + else: + print('Unknown reply: {!r}, exiting'.format(msg)) + return + else: + await ws.send(reply_sdp_ice(msg)) + # Return so we don't have an infinite loop + return + +print('Our uid is {!r}'.format(PEER_ID)) + +try: + asyncio.get_event_loop().run_until_complete(hello()) +except websockets.exceptions.InvalidHandshake: + print('Invalid handshake: are you sure this is a websockets server?\n') + raise +except ssl.SSLError: + print('SSL Error: are you sure the server is using TLS?\n') + raise diff --git a/webrtc/signalling/generate_cert.sh b/webrtc/signalling/generate_cert.sh new file mode 100755 index 0000000000..68a4b96f3c --- /dev/null +++ b/webrtc/signalling/generate_cert.sh @@ -0,0 +1,3 @@ +#! /bin/bash + +openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes diff --git a/webrtc/signalling/simple-server.py b/webrtc/signalling/simple-server.py new file mode 100755 index 0000000000..34af863f06 --- /dev/null +++ b/webrtc/signalling/simple-server.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python3 +# +# Example 1-1 call signalling server +# +# Copyright (C) 2017 Centricular Ltd. +# +# Author: Nirbheek Chauhan +# + +import os +import sys +import ssl +import logging +import asyncio +import websockets +import argparse + +from concurrent.futures._base import TimeoutError + +parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) +parser.add_argument('--addr', default='0.0.0.0', help='Address to listen on') +parser.add_argument('--port', default=8443, type=int, help='Port to listen on') +parser.add_argument('--keepalive-timeout', dest='keepalive_timeout', default=30, type=int, help='Timeout for keepalive (in seconds)') +parser.add_argument('--cert-path', default=os.path.dirname(__file__)) + +options = parser.parse_args(sys.argv[1:]) + +ADDR_PORT = (options.addr, options.port) +KEEPALIVE_TIMEOUT = options.keepalive_timeout + +# Format: {uid: (Peer WebSocketServerProtocol, remote_address)} +peers = dict() +# Format: {caller_uid: callee_uid, +# callee_uid: caller_uid} +# Bidirectional mapping between the two peers +sessions = dict() + +############### Helper functions ############### + +async def recv_msg_ping(ws, raddr): + ''' + Wait for a message forever, and send a regular ping to prevent bad routers + from closing the connection. + ''' + msg = None + while msg is None: + try: + msg = await asyncio.wait_for(ws.recv(), KEEPALIVE_TIMEOUT) + except TimeoutError: + print('Sending keepalive ping to {!r} in recv'.format(raddr)) + await ws.ping() + return msg + +async def disconnect(ws, peer_id): + ''' + Remove @peer_id from the list of sessions and close our connection to it. + This informs the peer that the session and all calls have ended, and it + must reconnect. + ''' + global sessions + if peer_id in sessions: + del sessions[peer_id] + # Close connection + if ws and ws.open: + # Don't care about errors + asyncio.ensure_future(ws.close(reason='hangup')) + +async def remove_peer(uid): + if uid in sessions: + other_id = sessions[uid] + del sessions[uid] + print("Cleaned up {} session".format(uid)) + if other_id in sessions: + del sessions[other_id] + print("Also cleaned up {} session".format(other_id)) + # If there was a session with this peer, also + # close the connection to reset its state. + if other_id in peers: + print("Closing connection to {}".format(other_id)) + wso, oaddr = peers[other_id] + del peers[other_id] + await wso.close() + if uid in peers: + ws, raddr = peers[uid] + del peers[uid] + await ws.close() + print("Disconnected from peer {!r} at {!r}".format(uid, raddr)) + +############### Handler functions ############### + +async def connection_handler(ws, peer_id): + global peers, sessions + raddr = ws.remote_address + peers[peer_id] = (ws, raddr) + print("Registered peer {!r} at {!r}".format(peer_id, raddr)) + while True: + # Receive command, wait forever if necessary + msg = await recv_msg_ping(ws, raddr) + if msg.startswith('SESSION'): + print("{!r} command {!r}".format(peer_id, msg)) + _, callee_id = msg.split(maxsplit=1) + if callee_id not in peers: + await ws.send('ERROR peer {!r} not found'.format(callee_id)) + continue + if callee_id in sessions: + await ws.send('ERROR peer {!r} busy'.format(callee_id)) + continue + await ws.send('SESSION_OK') + wsc = peers[callee_id][0] + print("Session from {!r} ({!r}) to {!r} ({!r})".format(peer_id, raddr, callee_id, + wsc.remote_address)) + # Register call + sessions[peer_id] = callee_id + sessions[callee_id] = peer_id + # We're in a session, route message to connected peer + elif peer_id in sessions: + other_id = sessions[peer_id] + wso, oaddr = peers[other_id] + print("{} -> {}: {}".format(peer_id, other_id, msg)) + await wso.send(msg) + +async def hello_peer(ws): + ''' + Exchange hello, register peer + ''' + raddr = ws.remote_address + hello = await ws.recv() + hello, uid = hello.split(maxsplit=1) + if hello != 'HELLO': + await ws.close(code=1002, reason='invalid protocol') + raise Exception("Invalid hello from {!r}".format(raddr)) + if not uid or uid in peers: + await ws.close(code=1002, reason='invalid peer uid') + raise Exception("Invalid uid {!r} from {!r}".format(uid, raddr)) + # Send back a HELLO + await ws.send('HELLO') + return uid + +async def handler(ws, path): + ''' + All incoming messages are handled here. @path is unused. + ''' + raddr = ws.remote_address + print("Connected to {!r}".format(raddr)) + peer_id = await hello_peer(ws) + try: + await connection_handler(ws, peer_id) + except websockets.ConnectionClosed: + print("Connection to peer {!r} closed, exiting handler".format(raddr)) + finally: + await remove_peer(peer_id) + +# Create an SSL context to be used by the websocket server +certpath = options.cert_path +print('Using TLS with keys in {!r}'.format(certpath)) +if 'letsencrypt' in certpath: + chain_pem = os.path.join(certpath, 'fullchain.pem') + key_pem = os.path.join(certpath, 'privkey.pem') +else: + chain_pem = os.path.join(certpath, 'cert.pem') + key_pem = os.path.join(certpath, 'key.pem') + +sslctx = ssl.create_default_context() +try: + sslctx.load_cert_chain(chain_pem, keyfile=key_pem) +except FileNotFoundError: + print("Certificates not found, did you run generate_cert.sh?") + sys.exit(1) +# FIXME +sslctx.check_hostname = False +sslctx.verify_mode = ssl.CERT_NONE + +print("Listening on https://{}:{}".format(*ADDR_PORT)) +# Websocket server +wsd = websockets.serve(handler, *ADDR_PORT, ssl=sslctx, + # Maximum number of messages that websockets will pop + # off the asyncio and OS buffers per connection. See: + # https://websockets.readthedocs.io/en/stable/api.html#websockets.protocol.WebSocketCommonProtocol + max_queue=16) + +logger = logging.getLogger('websockets.server') + +logger.setLevel(logging.ERROR) +logger.addHandler(logging.StreamHandler()) + +asyncio.get_event_loop().run_until_complete(wsd) +asyncio.get_event_loop().run_forever() From e9b0656bad4ddc053d5349dc3a419f5bd626855f Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sat, 21 Oct 2017 19:57:29 +0530 Subject: [PATCH 003/130] Add sendrecv implementation in js and gst webrtc JS code runs on the browser and uses the browser's webrtc implementation. C code uses gstreamer's webrtc implementation, for which you need the following repositories: https://github.com/ystreet/gstreamer/tree/promise https://github.com/ystreet/gst-plugins-bad/tree/webrtc You can build these with either Autotools gst-uninstalled: https://arunraghavan.net/2014/07/quick-start-guide-to-gst-uninstalled-1-x/ Or with Meson gst-build: https://cgit.freedesktop.org/gstreamer/gst-build/ --- webrtc/.gitignore | 11 +- webrtc/README.md | 31 ++ webrtc/sendrecv/gst/webrtc-sendrecv.c | 596 ++++++++++++++++++++++++++ webrtc/sendrecv/js/index.html | 26 ++ webrtc/sendrecv/js/webrtc.js | 203 +++++++++ 5 files changed, 859 insertions(+), 8 deletions(-) create mode 100644 webrtc/README.md create mode 100644 webrtc/sendrecv/gst/webrtc-sendrecv.c create mode 100644 webrtc/sendrecv/js/index.html create mode 100644 webrtc/sendrecv/js/webrtc.js diff --git a/webrtc/.gitignore b/webrtc/.gitignore index c6127b38c1..bab2990ef5 100644 --- a/webrtc/.gitignore +++ b/webrtc/.gitignore @@ -42,11 +42,6 @@ *.idb *.pdb -# Kernel Module Compile Results -*.mod* -*.cmd -.tmp_versions/ -modules.order -Module.symvers -Mkfile.old -dkms.conf +# Our stuff +*.pem +webrtc-sendrecv diff --git a/webrtc/README.md b/webrtc/README.md new file mode 100644 index 0000000000..3c3ae09ef3 --- /dev/null +++ b/webrtc/README.md @@ -0,0 +1,31 @@ +## GStreamer WebRTC demos + +All demos use the same signalling server in the `signalling/` directory + +You will need the following repositories till the GStreamer WebRTC implementation is merged upstream: + +https://github.com/ystreet/gstreamer/tree/promise + +https://github.com/ystreet/gst-plugins-bad/tree/webrtc + +You can build these with either Autotools gst-uninstalled: + +https://arunraghavan.net/2014/07/quick-start-guide-to-gst-uninstalled-1-x/ + +Or with Meson gst-build: + +https://cgit.freedesktop.org/gstreamer/gst-build/ + +### sendrecv: Send and receive audio and video + +* Serve the `js/` directory on the root of your website, or open https://webrtc.nirbheek.in + - The JS code assumes the signalling server is on port 8443 of the same server serving the HTML +* Build and run the sources in the `gst/` directory on your machine + +```console +$ gcc webrtc-sendrecv.c $(pkg-config --cflags --libs gstreamer-webrtc-1.0 gstreamer-sdp-1.0 libsoup-2.4 json-glib-1.0) -o webrtc-sendrecv +``` + +* Open the website in a browser and ensure that the status is "Registered with server, waiting for call", and note the `id` too. +* Run `webrtc-sendrecv --peer-id=ID` with the `id` from the browser. You will see state changes and an SDP exchange. +* You will see a bouncing ball + hear red noise in the browser, and your browser's webcam + mic in the gst app diff --git a/webrtc/sendrecv/gst/webrtc-sendrecv.c b/webrtc/sendrecv/gst/webrtc-sendrecv.c new file mode 100644 index 0000000000..8c25f97cfb --- /dev/null +++ b/webrtc/sendrecv/gst/webrtc-sendrecv.c @@ -0,0 +1,596 @@ +/* + * Demo gstreamer app for negotiating and streaming a sendrecv webrtc stream + * with a browser JS app. + * + * gcc webrtc-sendrecv.c $(pkg-config --cflags --libs gstreamer-webrtc-1.0 gstreamer-sdp-1.0 libsoup-2.4 json-glib-1.0) -o webrtc-sendrecv + * + * Author: Nirbheek Chauhan + */ +#include +#include +#include + +/* For signalling */ +#include +#include + +#include + +enum AppState { + APP_STATE_UNKNOWN = 0, + APP_STATE_ERROR = 1, /* generic error */ + SERVER_CONNECTING = 1000, + SERVER_CONNECTION_ERROR, + SERVER_CONNECTED, /* Ready to register */ + SERVER_REGISTERING = 2000, + SERVER_REGISTRATION_ERROR, + SERVER_REGISTERED, /* Ready to call a peer */ + SERVER_CLOSED, /* server connection closed by us or the server */ + PEER_CONNECTING = 3000, + PEER_CONNECTION_ERROR, + PEER_CONNECTED, + PEER_CALL_NEGOTIATING = 4000, + PEER_CALL_STARTED, + PEER_CALL_STOPPING, + PEER_CALL_STOPPED, + PEER_CALL_ERROR, +}; + +static GMainLoop *loop; +static GstElement *pipe1, *webrtc1; + +static SoupWebsocketConnection *ws_conn = NULL; +static enum AppState app_state = 0; +static const gchar *peer_id = NULL; +static const gchar *server_url = "wss://webrtc.nirbheek.in:8443"; + +static GOptionEntry entries[] = +{ + { "peer-id", 0, 0, G_OPTION_ARG_STRING, &peer_id, "String ID of the peer to connect to", "ID" }, + { "server", 0, 0, G_OPTION_ARG_STRING, &server_url, "Signalling server to connect to", "URL" }, +}; + +static gboolean +cleanup_and_quit_loop (const gchar * msg, enum AppState state) +{ + if (msg) + g_printerr ("%s\n", msg); + if (state > 0) + app_state = state; + + if (ws_conn) { + if (soup_websocket_connection_get_state (ws_conn) == + SOUP_WEBSOCKET_STATE_OPEN) + /* This will call us again */ + soup_websocket_connection_close (ws_conn, 1000, ""); + else + g_object_unref (ws_conn); + } + + if (loop) { + g_main_loop_quit (loop); + loop = NULL; + } + + /* To allow usage as a GSourceFunc */ + return G_SOURCE_REMOVE; +} + +static gchar* +get_string_from_json_object (JsonObject * object) +{ + JsonNode *root; + JsonGenerator *generator; + gchar *text; + + /* Make it the root node */ + root = json_node_init_object (json_node_alloc (), object); + generator = json_generator_new (); + json_generator_set_root (generator, root); + text = json_generator_to_data (generator, NULL); + + /* Release everything */ + g_object_unref (generator); + json_node_free (root); + return text; +} + +static void +handle_media_stream (GstPad * pad, GstElement * pipe, const char * convert_name, + const char * sink_name) +{ + GstPad *qpad; + GstElement *q, *conv, *sink; + GstPadLinkReturn ret; + + q = gst_element_factory_make ("queue", NULL); + g_assert (q); + conv = gst_element_factory_make (convert_name, NULL); + g_assert (conv); + sink = gst_element_factory_make (sink_name, NULL); + g_assert (sink); + gst_bin_add_many (GST_BIN (pipe), q, conv, sink, NULL); + gst_element_sync_state_with_parent (q); + gst_element_sync_state_with_parent (conv); + gst_element_sync_state_with_parent (sink); + gst_element_link_many (q, conv, sink, NULL); + + qpad = gst_element_get_static_pad (q, "sink"); + + ret = gst_pad_link (pad, qpad); + g_assert (ret == GST_PAD_LINK_OK); +} + +static void +on_incoming_decodebin_stream (GstElement * decodebin, GstPad * pad, + GstElement * pipe) +{ + GstCaps *caps; + const gchar *name; + + if (!gst_pad_has_current_caps (pad)) { + g_printerr ("Pad '%s' has no caps, can't do anything, ignoring\n", + GST_PAD_NAME (pad)); + return; + } + + caps = gst_pad_get_current_caps (pad); + name = gst_structure_get_name (gst_caps_get_structure (caps, 0)); + + if (g_str_has_prefix (name, "video")) { + handle_media_stream (pad, pipe, "videoconvert", "autovideosink"); + } else if (g_str_has_prefix (name, "audio")) { + handle_media_stream (pad, pipe, "audioconvert", "autoaudiosink"); + } else { + g_printerr ("Unknown pad %s, ignoring", GST_PAD_NAME (pad)); + } +} + +static void +on_incoming_stream (GstElement * webrtc, GstPad * pad, GstElement * pipe) +{ + GstElement *decodebin; + + if (GST_PAD_DIRECTION (pad) != GST_PAD_SRC) + return; + + decodebin = gst_element_factory_make ("decodebin", NULL); + g_signal_connect (decodebin, "pad-added", + G_CALLBACK (on_incoming_decodebin_stream), pipe); + gst_bin_add (GST_BIN (pipe), decodebin); + gst_element_sync_state_with_parent (decodebin); + gst_element_link (webrtc, decodebin); +} + +static void +send_ice_candidate_message (GstElement * webrtc G_GNUC_UNUSED, guint mlineindex, + gchar * candidate, gpointer user_data G_GNUC_UNUSED) +{ + gchar *text; + JsonObject *ice, *msg; + + if (app_state < PEER_CALL_NEGOTIATING) { + cleanup_and_quit_loop ("Can't send ICE, not in call", APP_STATE_ERROR); + return; + } + + ice = json_object_new (); + json_object_set_string_member (ice, "candidate", candidate); + json_object_set_int_member (ice, "sdpMLineIndex", mlineindex); + msg = json_object_new (); + json_object_set_object_member (msg, "ice", ice); + text = get_string_from_json_object (msg); + json_object_unref (msg); + + soup_websocket_connection_send_text (ws_conn, text); + g_free (text); +} + +static void +send_sdp_offer (GstWebRTCSessionDescription * offer) +{ + gchar *text; + JsonObject *msg, *sdp; + + if (app_state < PEER_CALL_NEGOTIATING) { + cleanup_and_quit_loop ("Can't send offer, not in call", APP_STATE_ERROR); + return; + } + + text = gst_sdp_message_as_text (offer->sdp); + g_print ("Sending offer:\n%s\n", text); + + sdp = json_object_new (); + json_object_set_string_member (sdp, "type", "offer"); + json_object_set_string_member (sdp, "sdp", text); + g_free (text); + + msg = json_object_new (); + json_object_set_object_member (msg, "sdp", sdp); + text = get_string_from_json_object (msg); + json_object_unref (msg); + + soup_websocket_connection_send_text (ws_conn, text); + g_free (text); +} + +/* Offer created by our pipeline, to be sent to the peer */ +static void +on_offer_received (GstPromise * promise, gpointer user_data) +{ + GstWebRTCSessionDescription *offer = NULL; + gchar *desc; + + g_assert (app_state == PEER_CALL_NEGOTIATING); + + g_assert (promise->result == GST_PROMISE_RESULT_REPLIED); + gst_structure_get (promise->promise, "offer", + GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &offer, NULL); + gst_promise_unref (promise); + + promise = gst_promise_new (); + g_signal_emit_by_name (webrtc1, "set-local-description", offer, promise); + gst_promise_interrupt (promise); + gst_promise_unref (promise); + + /* Send offer to peer */ + send_sdp_offer (offer); + gst_webrtc_session_description_free (offer); +} + +static void +on_negotiation_needed (GstElement * element, gpointer user_data) +{ + GstPromise *promise = gst_promise_new (); + + app_state = PEER_CALL_NEGOTIATING; + g_signal_emit_by_name (webrtc1, "create-offer", NULL, promise); + gst_promise_set_change_callback (promise, on_offer_received, user_data, + NULL); +} + +#define RTP_CAPS_OPUS "application/x-rtp,media=audio,encoding-name=OPUS,payload=" +#define RTP_CAPS_VP8 "application/x-rtp,media=video,encoding-name=VP8,payload=" + +static gboolean +start_pipeline (void) +{ + GstStateChangeReturn ret; + GError *error = NULL; + + pipe1 = + gst_parse_launch ("webrtcbin name=sendrecv " + "videotestsrc pattern=ball ! queue ! vp8enc deadline=1 ! rtpvp8pay ! " + "queue ! " RTP_CAPS_VP8 "96 ! sendrecv. " + "audiotestsrc wave=red-noise ! queue ! opusenc ! rtpopuspay ! " + "queue ! " RTP_CAPS_OPUS "97 ! sendrecv. ", + &error); + + if (error) { + g_printerr ("Failed to parse launch: %s\n", error->message); + g_error_free (error); + goto err; + } + + webrtc1 = gst_bin_get_by_name (GST_BIN (pipe1), "sendrecv"); + g_assert (webrtc1 != NULL); + + /* This is the gstwebrtc entry point where we create the offer and so on. It + * will be called when the pipeline goes to PLAYING. */ + g_signal_connect (webrtc1, "on-negotiation-needed", + G_CALLBACK (on_negotiation_needed), NULL); + /* We need to transmit this ICE candidate to the browser via the websockets + * signalling server. Incoming ice candidates from the browser need to be + * added by us too, see on_server_message() */ + g_signal_connect (webrtc1, "on-ice-candidate", + G_CALLBACK (send_ice_candidate_message), NULL); + /* Incoming streams will be exposed via this signal */ + g_signal_connect (webrtc1, "pad-added", G_CALLBACK (on_incoming_stream), + pipe1); + /* Lifetime is the same as the pipeline itself */ + gst_object_unref (webrtc1); + + g_print ("Starting pipeline\n"); + ret = gst_element_set_state (GST_ELEMENT (pipe1), GST_STATE_PLAYING); + if (ret == GST_STATE_CHANGE_FAILURE) + goto err; + + return TRUE; + +err: + if (pipe1) + g_clear_object (&pipe1); + if (webrtc1) + webrtc1 = NULL; + return FALSE; +} + +static gboolean +setup_call (void) +{ + gchar *msg; + + if (soup_websocket_connection_get_state (ws_conn) != + SOUP_WEBSOCKET_STATE_OPEN) + return FALSE; + + if (!peer_id) + return FALSE; + + g_print ("Setting up signalling server call with %s\n", peer_id); + app_state = PEER_CONNECTING; + msg = g_strdup_printf ("SESSION %s", peer_id); + soup_websocket_connection_send_text (ws_conn, msg); + g_free (msg); + return TRUE; +} + +static gboolean +register_with_server (void) +{ + gchar *hello; + gint32 our_id; + + if (soup_websocket_connection_get_state (ws_conn) != + SOUP_WEBSOCKET_STATE_OPEN) + return FALSE; + + our_id = g_random_int_range (10, 10000); + g_print ("Registering id %i with server\n", our_id); + app_state = SERVER_REGISTERING; + + /* Register with the server with a random integer id. Reply will be received + * by on_server_message() */ + hello = g_strdup_printf ("HELLO %i", our_id); + soup_websocket_connection_send_text (ws_conn, hello); + g_free (hello); + + return TRUE; +} + +static void +on_server_closed (SoupWebsocketConnection * conn G_GNUC_UNUSED, + gpointer user_data G_GNUC_UNUSED) +{ + app_state = SERVER_CLOSED; + cleanup_and_quit_loop ("Server connection closed", 0); +} + +/* One mega message handler for our asynchronous calling mechanism */ +static void +on_server_message (SoupWebsocketConnection * conn, SoupWebsocketDataType type, + GBytes * message, gpointer user_data) +{ + gsize size; + gchar *text, *data; + + switch (type) { + case SOUP_WEBSOCKET_DATA_BINARY: + g_printerr ("Received unknown binary message, ignoring\n"); + g_bytes_unref (message); + return; + case SOUP_WEBSOCKET_DATA_TEXT: + data = g_bytes_unref_to_data (message, &size); + /* Convert to NULL-terminated string */ + text = g_strndup (data, size); + g_free (data); + break; + default: + g_assert_not_reached (); + } + + /* Server has accepted our registration, we are ready to send commands */ + if (g_strcmp0 (text, "HELLO") == 0) { + if (app_state != SERVER_REGISTERING) { + cleanup_and_quit_loop ("ERROR: Received HELLO when not registering", + APP_STATE_ERROR); + goto out; + } + app_state = SERVER_REGISTERED; + g_print ("Registered with server\n"); + /* Ask signalling server to connect us with a specific peer */ + if (!setup_call ()) { + cleanup_and_quit_loop ("ERROR: Failed to setup call", PEER_CALL_ERROR); + goto out; + } + /* Call has been setup by the server, now we can start negotiation */ + } else if (g_strcmp0 (text, "SESSION_OK") == 0) { + if (app_state != PEER_CONNECTING) { + cleanup_and_quit_loop ("ERROR: Received SESSION_OK when not calling", + PEER_CONNECTION_ERROR); + goto out; + } + + app_state = PEER_CONNECTED; + /* Start negotiation (exchange SDP and ICE candidates) */ + if (!start_pipeline ()) + cleanup_and_quit_loop ("ERROR: failed to start pipeline", + PEER_CALL_ERROR); + /* Handle errors */ + } else if (g_str_has_prefix (text, "ERROR")) { + switch (app_state) { + case SERVER_CONNECTING: + app_state = SERVER_CONNECTION_ERROR; + break; + case SERVER_REGISTERING: + app_state = SERVER_REGISTRATION_ERROR; + break; + case PEER_CONNECTING: + app_state = PEER_CONNECTION_ERROR; + break; + case PEER_CONNECTED: + case PEER_CALL_NEGOTIATING: + app_state = PEER_CALL_ERROR; + default: + app_state = APP_STATE_ERROR; + } + cleanup_and_quit_loop (text, 0); + /* Look for JSON messages containing SDP and ICE candidates */ + } else { + JsonNode *root; + JsonObject *object; + JsonParser *parser = json_parser_new (); + if (!json_parser_load_from_data (parser, text, -1, NULL)) { + g_printerr ("Unknown message '%s', ignoring", text); + g_object_unref (parser); + goto out; + } + + root = json_parser_get_root (parser); + if (!JSON_NODE_HOLDS_OBJECT (root)) { + g_printerr ("Unknown json message '%s', ignoring", text); + g_object_unref (parser); + goto out; + } + + object = json_node_get_object (root); + /* Check type of JSON message */ + if (json_object_has_member (object, "sdp")) { + int ret; + const gchar *text; + GstSDPMessage *sdp; + GstWebRTCSessionDescription *answer; + + g_assert (app_state == PEER_CALL_NEGOTIATING); + + g_assert (json_object_has_member (object, "type")); + /* In this example, we always create the offer and receive one answer. + * See tests/examples/webrtcbidirectional.c in gst-plugins-bad for how to + * handle offers from peers and reply with answers using webrtcbin. */ + g_assert_cmpstr (json_object_get_string_member (object, "type"), ==, + "answer"); + + text = json_object_get_string_member (object, "sdp"); + + g_print ("Received answer:\n%s\n", text); + + ret = gst_sdp_message_new (&sdp); + g_assert (ret == GST_SDP_OK); + + ret = gst_sdp_message_parse_buffer (text, strlen (text), sdp); + g_assert (ret == GST_SDP_OK); + + answer = gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_ANSWER, + sdp); + g_assert (answer); + + /* Set remote description on our pipeline */ + { + GstPromise *promise = gst_promise_new (); + g_signal_emit_by_name (webrtc1, "set-remote-description", answer, + promise); + gst_promise_interrupt (promise); + gst_promise_unref (promise); + } + + app_state = PEER_CALL_STARTED; + } else if (json_object_has_member (object, "ice")) { + JsonObject *ice; + const gchar *candidate; + gint sdpmlineindex; + + ice = json_object_get_object_member (object, "ice"); + candidate = json_object_get_string_member (ice, "candidate"); + sdpmlineindex = json_object_get_int_member (ice, "sdpMLineIndex"); + + /* Add ice candidate sent by remote peer */ + g_signal_emit_by_name (webrtc1, "add-ice-candidate", sdpmlineindex, + candidate); + } else { + g_printerr ("Ignoring unknown JSON message:\n%s\n", text); + } + g_object_unref (parser); + } + +out: + g_free (text); +} + +static void +on_server_connected (SoupSession * session, GAsyncResult * res, + SoupMessage *msg) +{ + GError *error = NULL; + + ws_conn = soup_session_websocket_connect_finish (session, res, &error); + if (error) { + cleanup_and_quit_loop (error->message, SERVER_CONNECTION_ERROR); + g_error_free (error); + return; + } + + g_assert (ws_conn != NULL); + + app_state = SERVER_CONNECTED; + g_print ("Connected to signalling server\n"); + + g_signal_connect (ws_conn, "closed", G_CALLBACK (on_server_closed), NULL); + g_signal_connect (ws_conn, "message", G_CALLBACK (on_server_message), NULL); + + /* Register with the server so it knows about us and can accept commands */ + register_with_server (); +} + +/* + * Connect to the signalling server. This is the entrypoint for everything else. + */ +static void +connect_to_websocket_server_async (void) +{ + SoupLogger *logger; + SoupMessage *message; + SoupSession *session; + const char *https_aliases[] = {"wss", NULL}; + + session = soup_session_new_with_options (SOUP_SESSION_SSL_STRICT, TRUE, + SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE, + //SOUP_SESSION_SSL_CA_FILE, "/etc/ssl/certs/ca-bundle.crt", + SOUP_SESSION_HTTPS_ALIASES, https_aliases, NULL); + + logger = soup_logger_new (SOUP_LOGGER_LOG_BODY, -1); + soup_session_add_feature (session, SOUP_SESSION_FEATURE (logger)); + g_object_unref (logger); + + message = soup_message_new (SOUP_METHOD_GET, server_url); + + g_print ("Connecting to server...\n"); + + /* Once connected, we will register */ + soup_session_websocket_connect_async (session, message, NULL, NULL, NULL, + (GAsyncReadyCallback) on_server_connected, message); + app_state = SERVER_CONNECTING; +} + +int +main (int argc, char *argv[]) +{ + SoupSession *session; + GOptionContext *context; + GError *error = NULL; + + context = g_option_context_new ("- gstreamer webrtc sendrecv demo"); + g_option_context_add_main_entries (context, entries, NULL); + g_option_context_add_group (context, gst_init_get_option_group ()); + if (!g_option_context_parse (context, &argc, &argv, &error)) { + g_printerr ("Error initializing: %s\n", error->message); + return -1; + } + + if (!peer_id) { + g_printerr ("--peer-id is a required argument\n"); + return -1; + } + + loop = g_main_loop_new (NULL, FALSE); + + connect_to_websocket_server_async (); + + g_main_loop_run (loop); + + gst_element_set_state (GST_ELEMENT (pipe1), GST_STATE_NULL); + g_print ("Pipeline stopped\n"); + + gst_object_unref (pipe1); + + return 0; +} diff --git a/webrtc/sendrecv/js/index.html b/webrtc/sendrecv/js/index.html new file mode 100644 index 0000000000..0a0b7fec8a --- /dev/null +++ b/webrtc/sendrecv/js/index.html @@ -0,0 +1,26 @@ + + + + + + + + + + +
+
Status: unknown
+
Our id is unknown
+ + diff --git a/webrtc/sendrecv/js/webrtc.js b/webrtc/sendrecv/js/webrtc.js new file mode 100644 index 0000000000..31116c017a --- /dev/null +++ b/webrtc/sendrecv/js/webrtc.js @@ -0,0 +1,203 @@ +/* vim: set sts=4 sw=4 et : + * + * Demo Javascript app for negotiating and streaming a sendrecv webrtc stream + * with a GStreamer app. Runs only in passive mode, i.e., responds to offers + * with answers, exchanges ICE candidates, and streams. + * + * Author: Nirbheek Chauhan + */ + +var connect_attempts = 0; + +var peer_connection = null; +var rtc_configuration = {iceServers: [{urls: "stun:stun.services.mozilla.com"}, + {urls: "stun:stun.l.google.com:19302"}]}; +var ws_conn; +var local_stream; +var peer_id; + +function getOurId() { + return Math.floor(Math.random() * (9000 - 10) + 10).toString(); +} + +function resetState() { + // This will call onServerClose() + ws_conn.close(); +} + +function handleIncomingError(error) { + setStatus("ERROR: " + error); + resetState(); +} + +function getVideoElement() { + return document.getElementById("stream"); +} + +function setStatus(text) { + console.log(text); + document.getElementById("status").textContent = text; +} + +function resetVideoElement() { + var videoElement = getVideoElement(); + videoElement.pause(); + videoElement.src = ""; + videoElement.load(); +} + +// SDP offer received from peer, set remote description and create an answer +function onIncomingSDP(sdp) { + console.log("Incoming SDP is "+ JSON.stringify(sdp)); + peer_connection.setRemoteDescription(sdp).then(() => { + setStatus("Remote SDP set"); + if (sdp.type != "offer") + return; + setStatus("Got SDP offer, creating answer"); + peer_connection.createAnswer().then(onLocalDescription).catch(setStatus); + }).catch(setStatus); +} + +// Local description was set, send it to peer +function onLocalDescription(desc) { + console.log("Got local description: " + JSON.stringify(desc)); + peer_connection.setLocalDescription(desc).then(function() { + setStatus("Sending SDP answer"); + ws_conn.send(JSON.stringify(peer_connection.localDescription)); + }); +} + +// ICE candidate received from peer, add it to the peer connection +function onIncomingICE(ice) { + console.log("Incoming ICE: " + JSON.stringify(ice)); + var candidate = new RTCIceCandidate(ice); + peer_connection.addIceCandidate(candidate).catch(setStatus); +} + +function onServerMessage(event) { + console.log("Received " + event.data); + switch (event.data) { + case "HELLO": + setStatus("Registered with server, waiting for call"); + return; + default: + if (event.data.startsWith("ERROR")) { + handleIncomingError(event.data); + return; + } + // Handle incoming JSON SDP and ICE messages + try { + msg = JSON.parse(event.data); + } catch (e) { + if (e instanceof SyntaxError) { + handleIncomingError("Error parsing incoming JSON: " + event.data); + } else { + handleIncomingError("Unknown error parsing response: " + event.data); + } + return; + } + + // Incoming JSON signals the beginning of a call + if (peer_connection == null) + createCall(msg); + + if (msg.sdp != null) { + onIncomingSDP(msg.sdp); + } else if (msg.ice != null) { + onIncomingICE(msg.ice); + } else { + handleIncomingError("Unknown incoming JSON: " + msg); + } + } +} + +function onServerClose(event) { + resetVideoElement(); + + if (peer_connection != null) { + peer_connection.close(); + peer_connection = null; + } + + // Reset after a second + window.setTimeout(websocketServerConnect, 1000); +} + +function onServerError(event) { + setStatus("Unable to connect to server, did you add an exception for the certificate?") + // Retry after 3 seconds + window.setTimeout(websocketServerConnect, 3000); +} + +function websocketServerConnect() { + connect_attempts++; + if (connect_attempts > 3) { + setStatus("Too many connection attempts, aborting. Refresh page to try again"); + return; + } + peer_id = getOurId(); + setStatus("Connecting to server"); + ws_conn = new WebSocket('wss://' + window.location.hostname + ':8443'); + /* When connected, immediately register with the server */ + ws_conn.addEventListener('open', (event) => { + document.getElementById("peer-id").textContent = peer_id; + ws_conn.send('HELLO ' + peer_id); + setStatus("Registering with server"); + }); + ws_conn.addEventListener('error', onServerError); + ws_conn.addEventListener('message', onServerMessage); + ws_conn.addEventListener('close', onServerClose); + + var constraints = {video: true, audio: true}; + + // Add local stream + if (navigator.mediaDevices.getUserMedia) { + navigator.mediaDevices.getUserMedia(constraints) + .then((stream) => { local_stream = stream }) + .catch(errorUserMediaHandler); + } else { + errorUserMediaHandler(); + } +} + +function onRemoteStreamAdded(event) { + videoTracks = event.stream.getVideoTracks(); + audioTracks = event.stream.getAudioTracks(); + + if (videoTracks.length > 0) { + console.log('Incoming stream: ' + videoTracks.length + ' video tracks and ' + audioTracks.length + ' audio tracks'); + getVideoElement().srcObject = event.stream; + } else { + handleIncomingError('Stream with unknown tracks added, resetting'); + } +} + +function errorUserMediaHandler() { + setStatus("Browser doesn't support getUserMedia!"); +} + +function createCall(msg) { + // Reset connection attempts because we connected successfully + connect_attempts = 0; + + peer_connection = new RTCPeerConnection(rtc_configuration); + peer_connection.onaddstream = onRemoteStreamAdded; + /* Send our video/audio to the other peer */ + peer_connection.addStream(local_stream); + + if (!msg.sdp) { + console.log("WARNING: First message wasn't an SDP message!?"); + } + + peer_connection.onicecandidate = (event) => { + // We have a candidate, send it to the remote party with the + // same uuid + if (event.candidate == null) { + console.log("ICE Candidate was null, done"); + return; + } + ws_conn.send(JSON.stringify({'ice': event.candidate})); + }; + + setStatus("Created peer connection for call, waiting for SDP"); +} From c2961305e3cdb96387d0d5086bcf2b321b1d1a33 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sat, 28 Oct 2017 19:00:03 +0530 Subject: [PATCH 004/130] signalling/client.py: Rename to session-client.py Also fix CALL -> SESSION naming --- webrtc/signalling/{client.py => session-client.py} | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) rename webrtc/signalling/{client.py => session-client.py} (88%) diff --git a/webrtc/signalling/client.py b/webrtc/signalling/session-client.py similarity index 88% rename from webrtc/signalling/client.py rename to webrtc/signalling/session-client.py index 0fa8227d78..d7d0f3ed64 100755 --- a/webrtc/signalling/client.py +++ b/webrtc/signalling/session-client.py @@ -51,27 +51,29 @@ async def hello(): # Initiate call if requested if CALLEE_ID: - await ws.send('CALL {}'.format(CALLEE_ID)) + await ws.send('SESSION {}'.format(CALLEE_ID)) # Receive messages + sent_sdp = False while True: msg = await ws.recv() if msg.startswith('ERROR'): # On error, we bring down the webrtc pipeline, etc print('{!r}, exiting'.format(msg)) return + if sent_sdp: + print('Got reply sdp: ' + msg) + return # Done if CALLEE_ID: - if msg == 'CALL_OK': + if msg == 'SESSION_OK': await ws.send(send_sdp_ice()) - # Return so we don't have an infinite loop - return + sent_sdp = True else: print('Unknown reply: {!r}, exiting'.format(msg)) return else: await ws.send(reply_sdp_ice(msg)) - # Return so we don't have an infinite loop - return + return # Done print('Our uid is {!r}'.format(PEER_ID)) From 2db85c41ccee6ce3772d1e70684660f3c00eda47 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sat, 28 Oct 2017 19:02:56 +0530 Subject: [PATCH 005/130] Protocol.md: Fix headings --- webrtc/signalling/Protocol.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/webrtc/signalling/Protocol.md b/webrtc/signalling/Protocol.md index 1af892eee3..18ef5fa430 100644 --- a/webrtc/signalling/Protocol.md +++ b/webrtc/signalling/Protocol.md @@ -1,26 +1,26 @@ # Terminology -### Client +## Client A GStreamer-based application -### Browser +## Browser A JS application that runs in the browser and uses built-in browser webrtc APIs -### Peer +## Peer Any webrtc-using application that can participate in a call -### Signalling server +## Signalling server Basic websockets server implemented in Python that manages the peers list and shovels data between peers -# Overview +### Overview This is a basic protocol for doing 1-1 audio+video calls between a gstreamer app and a JS app in a browser. -# Peer registration and calling +### Peer registration and calling Peers must register with the signalling server before a call can be initiated. The server connection should stay open as long as the peer is available or in a call. @@ -38,7 +38,7 @@ This protocol builds upon https://github.com/shanet/WebRTC-Example/ * Closure of the server connection means the call has ended; either because the other peer ended it or went away * To end the call, disconnect from the server. You may reconnect again whenever you wish. -# Negotiation +### Negotiation Once a call has been setup with the signalling server, the peers must negotiate SDP and ICE candidates with each other. From d687ff3d9130cc297482b34a8f578f6627e65458 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sat, 28 Oct 2017 19:00:42 +0530 Subject: [PATCH 006/130] simple-server: Add support for multi-party rooms Also add a new room-client.py to test the protocol which is documented in Protocol.md --- webrtc/signalling/Protocol.md | 20 ++++- webrtc/signalling/room-client.py | 107 +++++++++++++++++++++++ webrtc/signalling/simple-server.py | 135 ++++++++++++++++++++++++----- 3 files changed, 237 insertions(+), 25 deletions(-) create mode 100755 webrtc/signalling/room-client.py diff --git a/webrtc/signalling/Protocol.md b/webrtc/signalling/Protocol.md index 18ef5fa430..0aad2b8fb5 100644 --- a/webrtc/signalling/Protocol.md +++ b/webrtc/signalling/Protocol.md @@ -20,7 +20,7 @@ Basic websockets server implemented in Python that manages the peers list and sh This is a basic protocol for doing 1-1 audio+video calls between a gstreamer app and a JS app in a browser. -### Peer registration and calling +### Peer registration Peers must register with the signalling server before a call can be initiated. The server connection should stay open as long as the peer is available or in a call. @@ -31,13 +31,29 @@ This protocol builds upon https://github.com/shanet/WebRTC-Example/ * Receive `HELLO` * Any other message starting with `ERROR` is an error. -* To connect to a peer, send `SESSION ` where `` identifies the peer to connect to, and receive `SESSION_OK` +### 1-1 calls with a 'session' + +* To connect to a single peer, send `SESSION ` where `` identifies the peer to connect to, and receive `SESSION_OK` * All further messages will be forwarded to the peer * The call negotiation with the peer can be started by sending JSON encoded SDP and ICE * Closure of the server connection means the call has ended; either because the other peer ended it or went away * To end the call, disconnect from the server. You may reconnect again whenever you wish. +### Multi-party calls with a 'room' + +* To create a multi-party call, you must first register (or join) a room. Send `ROOM ` where `` is a unique room name +* Receive `ROOM_OK ` from the server if this is a new room, or `ROOM_OK ...` where `` are unique identifiers for the peers already in the room +* To send messages to a specific peer within the room for call negotiation (or any other purpose, use `ROOM_PEER_MSG ` +* When a new peer joins the room, you will receive a `ROOM_PEER_JOINED ` message + - For the purposes of convention and to avoid overwhelming newly-joined peers, offers must only be sent by the newly-joined peer +* When a peer leaves the room, you will receive a `ROOM_PEER_LEFT ` message + - You should stop sending/receiving media from/to this peer +* To get a list of all peers currently in the room, send `ROOM_PEER_LIST` and receive `ROOM_PEER_LIST ...` + - This list will never contain your own `` + - In theory you should never need to use this since you are guaranteed to receive JOINED and LEFT messages for all peers in a room +* You may stay connected to a room for as long as you like + ### Negotiation Once a call has been setup with the signalling server, the peers must negotiate SDP and ICE candidates with each other. diff --git a/webrtc/signalling/room-client.py b/webrtc/signalling/room-client.py new file mode 100755 index 0000000000..e82a3f242c --- /dev/null +++ b/webrtc/signalling/room-client.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +# +# Test client for simple room-based multi-peer p2p calling +# +# Copyright (C) 2017 Centricular Ltd. +# +# Author: Nirbheek Chauhan +# + +import sys +import ssl +import json +import uuid +import asyncio +import websockets +import argparse + +parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) +parser.add_argument('--url', default='wss://localhost:8443', help='URL to connect to') +parser.add_argument('--room', default=None, help='the room to join') + +options = parser.parse_args(sys.argv[1:]) + +SERVER_ADDR = options.url +PEER_ID = 'ws-test-client-' + str(uuid.uuid4())[:6] +ROOM_ID = options.room +if ROOM_ID is None: + print('--room argument is required') + sys.exit(1) + +sslctx = False +if SERVER_ADDR.startswith(('wss://', 'https://')): + sslctx = ssl.create_default_context() + # FIXME + sslctx.check_hostname = False + sslctx.verify_mode = ssl.CERT_NONE + +def get_answer_sdp(offer, peer_id): + # Here we'd parse the incoming JSON message for ICE and SDP candidates + print("Got: " + offer) + sdp = json.dumps({'sdp': 'reply sdp'}) + answer = 'ROOM_PEER_MSG {} {}'.format(peer_id, sdp) + print("Sent: " + answer) + return answer + +def get_offer_sdp(peer_id): + sdp = json.dumps({'sdp': 'initial sdp'}) + offer = 'ROOM_PEER_MSG {} {}'.format(peer_id, sdp) + print("Sent: " + offer) + return offer + +async def hello(): + async with websockets.connect(SERVER_ADDR, ssl=sslctx) as ws: + await ws.send('HELLO ' + PEER_ID) + assert(await ws.recv() == 'HELLO') + + await ws.send('ROOM {}'.format(ROOM_ID)) + + sent_offers = set() + # Receive messages + while True: + msg = await ws.recv() + if msg.startswith('ERROR'): + # On error, we bring down the webrtc pipeline, etc + print('{!r}, exiting'.format(msg)) + return + if msg.startswith('ROOM_OK'): + print('Got ROOM_OK for room {!r}'.format(ROOM_ID)) + _, *room_peers = msg.split() + for peer_id in room_peers: + print('Sending offer to {!r}'.format(peer_id)) + # Create a peer connection for each peer and start + # exchanging SDP and ICE candidates + await ws.send(get_offer_sdp(peer_id)) + sent_offers.add(peer_id) + continue + elif msg.startswith('ROOM_PEER'): + if msg.startswith('ROOM_PEER_JOINED'): + _, peer_id = msg.split(maxsplit=1) + print('Peer {!r} joined the room'.format(peer_id)) + # Peer will send us an offer + continue + if msg.startswith('ROOM_PEER_LEFT'): + _, peer_id = msg.split(maxsplit=1) + print('Peer {!r} left the room'.format(peer_id)) + continue + elif msg.startswith('ROOM_PEER_MSG'): + _, peer_id, msg = msg.split(maxsplit=2) + if peer_id in sent_offers: + print('Got answer from {!r}: {}'.format(peer_id, msg)) + continue + print('Got offer from {!r}, replying'.format(peer_id)) + await ws.send(get_answer_sdp(msg, peer_id)) + continue + print('Unknown msg: {!r}, exiting'.format(msg)) + return + +print('Our uid is {!r}'.format(PEER_ID)) + +try: + asyncio.get_event_loop().run_until_complete(hello()) +except websockets.exceptions.InvalidHandshake: + print('Invalid handshake: are you sure this is a websockets server?\n') + raise +except ssl.SSLError: + print('SSL Error: are you sure the server is using TLS?\n') + raise diff --git a/webrtc/signalling/simple-server.py b/webrtc/signalling/simple-server.py index 34af863f06..9714213c47 100755 --- a/webrtc/signalling/simple-server.py +++ b/webrtc/signalling/simple-server.py @@ -28,12 +28,19 @@ options = parser.parse_args(sys.argv[1:]) ADDR_PORT = (options.addr, options.port) KEEPALIVE_TIMEOUT = options.keepalive_timeout -# Format: {uid: (Peer WebSocketServerProtocol, remote_address)} +############### Global data ############### + +# Format: {uid: (Peer WebSocketServerProtocol, +# remote_address, +# <'session'|room_id|None>)} peers = dict() # Format: {caller_uid: callee_uid, # callee_uid: caller_uid} # Bidirectional mapping between the two peers sessions = dict() +# Format: {room_id: {peer1_id, peer2_id, peer3_id, ...}} +# Room dict with a set of peers in each room +rooms = dict() ############### Helper functions ############### @@ -65,7 +72,7 @@ async def disconnect(ws, peer_id): # Don't care about errors asyncio.ensure_future(ws.close(reason='hangup')) -async def remove_peer(uid): +async def cleanup_session(uid): if uid in sessions: other_id = sessions[uid] del sessions[uid] @@ -77,47 +84,129 @@ async def remove_peer(uid): # close the connection to reset its state. if other_id in peers: print("Closing connection to {}".format(other_id)) - wso, oaddr = peers[other_id] + wso, oaddr, _ = peers[other_id] del peers[other_id] await wso.close() + +async def cleanup_room(uid, room_id): + room_peers = rooms[room_id] + if uid not in room_peers: + return + room_peers.remove(uid) + for pid in room_peers: + wsp, paddr, _ = peers[pid] + msg = 'ROOM_PEER_LEFT {}'.format(uid) + print('room {}: {} -> {}: {}'.format(room_id, uid, pid, msg)) + await wsp.send(msg) + +async def remove_peer(uid): + await cleanup_session(uid) if uid in peers: - ws, raddr = peers[uid] + ws, raddr, status = peers[uid] + if status and status != 'session': + await cleanup_room(uid, status) del peers[uid] await ws.close() print("Disconnected from peer {!r} at {!r}".format(uid, raddr)) ############### Handler functions ############### -async def connection_handler(ws, peer_id): - global peers, sessions +async def connection_handler(ws, uid): + global peers, sessions, rooms raddr = ws.remote_address - peers[peer_id] = (ws, raddr) - print("Registered peer {!r} at {!r}".format(peer_id, raddr)) + peer_status = None + peers[uid] = [ws, raddr, peer_status] + print("Registered peer {!r} at {!r}".format(uid, raddr)) while True: # Receive command, wait forever if necessary msg = await recv_msg_ping(ws, raddr) - if msg.startswith('SESSION'): - print("{!r} command {!r}".format(peer_id, msg)) + # Update current status + peer_status = peers[uid][2] + # We are in a session or a room, messages must be relayed + if peer_status is not None: + # We're in a session, route message to connected peer + if peer_status == 'session': + other_id = sessions[uid] + wso, oaddr, status = peers[other_id] + assert(status == 'session') + print("{} -> {}: {}".format(uid, other_id, msg)) + await wso.send(msg) + # We're in a room, accept room-specific commands + elif peer_status: + # ROOM_PEER_MSG peer_id MSG + if msg.startswith('ROOM_PEER_MSG'): + _, other_id, msg = msg.split(maxsplit=2) + if other_id not in peers: + await ws.send('ERROR peer {!r} not found' + ''.format(other_id)) + continue + wso, oaddr, status = peers[other_id] + if status != room_id: + await ws.send('ERROR peer {!r} is not in the room' + ''.format(other_id)) + continue + msg = 'ROOM_PEER_MSG {} {}'.format(uid, msg) + print('room {}: {} -> {}: {}'.format(room_id, uid, other_id, msg)) + await wso.send(msg) + elif msg == 'ROOM_PEER_LIST': + room_id = peers[peer_id][2] + room_peers = ' '.join([pid for pid in rooms[room_id] if pid != peer_id]) + msg = 'ROOM_PEER_LIST {}'.format(room_peers) + print('room {}: -> {}: {}'.format(room_id, uid, msg)) + await ws.send(msg) + else: + await ws.send('ERROR invalid msg, already in room') + continue + else: + raise AssertionError('Unknown peer status {!r}'.format(peer_status)) + # Requested a session with a specific peer + elif msg.startswith('SESSION'): + print("{!r} command {!r}".format(uid, msg)) _, callee_id = msg.split(maxsplit=1) if callee_id not in peers: await ws.send('ERROR peer {!r} not found'.format(callee_id)) continue - if callee_id in sessions: + if peer_status is not None: await ws.send('ERROR peer {!r} busy'.format(callee_id)) continue await ws.send('SESSION_OK') wsc = peers[callee_id][0] - print("Session from {!r} ({!r}) to {!r} ({!r})".format(peer_id, raddr, callee_id, - wsc.remote_address)) - # Register call - sessions[peer_id] = callee_id - sessions[callee_id] = peer_id - # We're in a session, route message to connected peer - elif peer_id in sessions: - other_id = sessions[peer_id] - wso, oaddr = peers[other_id] - print("{} -> {}: {}".format(peer_id, other_id, msg)) - await wso.send(msg) + print('Session from {!r} ({!r}) to {!r} ({!r})' + ''.format(uid, raddr, callee_id, wsc.remote_address)) + # Register session + peers[uid][2] = peer_status = 'session' + sessions[uid] = callee_id + peers[callee_id][2] = 'session' + sessions[callee_id] = uid + # Requested joining or creation of a room + elif msg.startswith('ROOM'): + print('{!r} command {!r}'.format(uid, msg)) + _, room_id = msg.split(maxsplit=1) + # Room name cannot be 'session', empty, or contain whitespace + if room_id == 'session' or room_id.split() != [room_id]: + await ws.send('ERROR invalid room id {!r}'.format(room_id)) + continue + if room_id in rooms: + if uid in rooms[room_id]: + raise AssertionError('How did we accept a ROOM command ' + 'despite already being in a room?') + else: + # Create room if required + rooms[room_id] = set() + room_peers = ' '.join([pid for pid in rooms[room_id]]) + await ws.send('ROOM_OK {}'.format(room_peers)) + # Enter room + peers[uid][2] = peer_status = room_id + rooms[room_id].add(uid) + for pid in rooms[room_id]: + if pid == uid: + continue + wsp, paddr, _ = peers[pid] + msg = 'ROOM_PEER_JOINED {}'.format(uid) + print('room {}: {} -> {}: {}'.format(room_id, uid, pid, msg)) + await wsp.send(msg) + else: + print('Ignoring unknown message {!r} from {!r}'.format(msg, uid)) async def hello_peer(ws): ''' @@ -129,7 +218,7 @@ async def hello_peer(ws): if hello != 'HELLO': await ws.close(code=1002, reason='invalid protocol') raise Exception("Invalid hello from {!r}".format(raddr)) - if not uid or uid in peers: + if not uid or uid in peers or uid.split() != [uid]: # no whitespace await ws.close(code=1002, reason='invalid peer uid') raise Exception("Invalid uid {!r} from {!r}".format(uid, raddr)) # Send back a HELLO From 96e4f39fd887c189f8b804f4e5b04552890c57be Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sun, 29 Oct 2017 04:08:45 +0530 Subject: [PATCH 007/130] Update Protocol.md Fix indentation typos --- webrtc/signalling/Protocol.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webrtc/signalling/Protocol.md b/webrtc/signalling/Protocol.md index 0aad2b8fb5..2772d5500a 100644 --- a/webrtc/signalling/Protocol.md +++ b/webrtc/signalling/Protocol.md @@ -48,10 +48,10 @@ This protocol builds upon https://github.com/shanet/WebRTC-Example/ * When a new peer joins the room, you will receive a `ROOM_PEER_JOINED ` message - For the purposes of convention and to avoid overwhelming newly-joined peers, offers must only be sent by the newly-joined peer * When a peer leaves the room, you will receive a `ROOM_PEER_LEFT ` message - - You should stop sending/receiving media from/to this peer + - You should stop sending/receiving media from/to this peer * To get a list of all peers currently in the room, send `ROOM_PEER_LIST` and receive `ROOM_PEER_LIST ...` - - This list will never contain your own `` - - In theory you should never need to use this since you are guaranteed to receive JOINED and LEFT messages for all peers in a room + - This list will never contain your own `` + - In theory you should never need to use this since you are guaranteed to receive JOINED and LEFT messages for all peers in a room * You may stay connected to a room for as long as you like ### Negotiation From 569aff43f9e80f95eb6ce6e7de190b9664775b00 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Mon, 30 Oct 2017 09:12:06 +0530 Subject: [PATCH 008/130] sendrecv: Rename function for greater clarity --- webrtc/sendrecv/gst/webrtc-sendrecv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webrtc/sendrecv/gst/webrtc-sendrecv.c b/webrtc/sendrecv/gst/webrtc-sendrecv.c index 8c25f97cfb..3ee0e44789 100644 --- a/webrtc/sendrecv/gst/webrtc-sendrecv.c +++ b/webrtc/sendrecv/gst/webrtc-sendrecv.c @@ -216,7 +216,7 @@ send_sdp_offer (GstWebRTCSessionDescription * offer) /* Offer created by our pipeline, to be sent to the peer */ static void -on_offer_received (GstPromise * promise, gpointer user_data) +on_offer_created (GstPromise * promise, gpointer user_data) { GstWebRTCSessionDescription *offer = NULL; gchar *desc; @@ -245,7 +245,7 @@ on_negotiation_needed (GstElement * element, gpointer user_data) app_state = PEER_CALL_NEGOTIATING; g_signal_emit_by_name (webrtc1, "create-offer", NULL, promise); - gst_promise_set_change_callback (promise, on_offer_received, user_data, + gst_promise_set_change_callback (promise, on_offer_created, user_data, NULL); } From 9b1a0e538992444b292f8bbca38bbe4a7da4c7ab Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Mon, 30 Oct 2017 09:09:36 +0530 Subject: [PATCH 009/130] WIP: Add a new multiparty sendrecv gstreamer demo You can join a room and an audio-only call will be started with all peers in that room. Currently uses audiotestsrc only. BUG: With >2 peers in a call, if a peer leaves, the pipeline stops outputting data from the remaining peers to the (audio) sink. TODO: JS code to allow a browser to join the call TODO: Cleanup pipeline when a peer leaves TODO: Add ICE servers to allow calls over the Internet TODO: Perhaps setup a TURN server as well --- webrtc/multiparty-sendrecv/gst/.gitignore | 1 + .../gst/mp-webrtc-sendrecv.c | 889 ++++++++++++++++++ 2 files changed, 890 insertions(+) create mode 100644 webrtc/multiparty-sendrecv/gst/.gitignore create mode 100644 webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c diff --git a/webrtc/multiparty-sendrecv/gst/.gitignore b/webrtc/multiparty-sendrecv/gst/.gitignore new file mode 100644 index 0000000000..e1b42bed77 --- /dev/null +++ b/webrtc/multiparty-sendrecv/gst/.gitignore @@ -0,0 +1 @@ +mp-webrtc-sendrecv diff --git a/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c b/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c new file mode 100644 index 0000000000..ed25f117c4 --- /dev/null +++ b/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c @@ -0,0 +1,889 @@ +/* + * Demo gstreamer app for negotiating and streaming a sendrecv audio-only webrtc + * stream to all the peers in a multiparty room. + * + * gcc mp-webrtc-sendrecv.c $(pkg-config --cflags --libs gstreamer-webrtc-1.0 gstreamer-sdp-1.0 libsoup-2.4 json-glib-1.0) -o mp-webrtc-sendrecv + * + * Author: Nirbheek Chauhan + */ +#include +#include +#include + +/* For signalling */ +#include +#include + +#include + +enum AppState { + APP_STATE_UNKNOWN = 0, + APP_STATE_ERROR = 1, /* generic error */ + SERVER_CONNECTING = 1000, + SERVER_CONNECTION_ERROR, + SERVER_CONNECTED, /* Ready to register */ + SERVER_REGISTERING = 2000, + SERVER_REGISTRATION_ERROR, + SERVER_REGISTERED, /* Ready to call a peer */ + SERVER_CLOSED, /* server connection closed by us or the server */ + ROOM_JOINING = 3000, + ROOM_JOIN_ERROR, + ROOM_JOINED, + ROOM_CALL_NEGOTIATING = 4000, /* negotiating with some or all peers */ + ROOM_CALL_OFFERING, /* when we're the one sending the offer */ + ROOM_CALL_ANSWERING, /* when we're the one answering an offer */ + ROOM_CALL_STARTED, /* in a call with some or all peers */ + ROOM_CALL_STOPPING, + ROOM_CALL_STOPPED, + ROOM_CALL_ERROR, +}; + +static GMainLoop *loop; +static GstElement *pipeline; +static GList *peers; + +static SoupWebsocketConnection *ws_conn = NULL; +static enum AppState app_state = 0; +static const gchar *default_server_url = "wss://webrtc.nirbheek.in:8443"; +static gchar *server_url = NULL; +static gchar *local_id = NULL; +static gchar *room_id = NULL; + +static GOptionEntry entries[] = +{ + { "name", 0, 0, G_OPTION_ARG_STRING, &local_id, "Name we will send to the server", "ID" }, + { "room-id", 0, 0, G_OPTION_ARG_STRING, &room_id, "Room name to join or create", "ID" }, + { "server", 0, 0, G_OPTION_ARG_STRING, &server_url, "Signalling server to connect to", "URL" }, +}; + +static gint +compare_str_glist (gconstpointer a, gconstpointer b) +{ + return g_strcmp0 (a, b); +} + +static const gchar * +find_peer_from_list (const gchar * peer_id) +{ + return (g_list_find_custom (peers, peer_id, compare_str_glist))->data; +} + +static gboolean +cleanup_and_quit_loop (const gchar * msg, enum AppState state) +{ + if (msg) + g_printerr ("%s\n", msg); + if (state > 0) + app_state = state; + + if (ws_conn) { + if (soup_websocket_connection_get_state (ws_conn) == + SOUP_WEBSOCKET_STATE_OPEN) + /* This will call us again */ + soup_websocket_connection_close (ws_conn, 1000, ""); + else + g_object_unref (ws_conn); + } + + if (loop) { + g_main_loop_quit (loop); + loop = NULL; + } + + /* To allow usage as a GSourceFunc */ + return G_SOURCE_REMOVE; +} + +static gchar* +get_string_from_json_object (JsonObject * object) +{ + JsonNode *root; + JsonGenerator *generator; + gchar *text; + + /* Make it the root node */ + root = json_node_init_object (json_node_alloc (), object); + generator = json_generator_new (); + json_generator_set_root (generator, root); + text = json_generator_to_data (generator, NULL); + + /* Release everything */ + g_object_unref (generator); + json_node_free (root); + return text; +} + +static void +handle_media_stream (GstPad * pad, GstElement * pipe, const char * convert_name, + const char * sink_name) +{ + GstPad *qpad; + GstElement *q, *conv, *sink; + GstPadLinkReturn ret; + + q = gst_element_factory_make ("queue", NULL); + g_assert_nonnull (q); + conv = gst_element_factory_make (convert_name, NULL); + g_assert_nonnull (conv); + sink = gst_element_factory_make (sink_name, NULL); + g_assert_nonnull (sink); + gst_bin_add_many (GST_BIN (pipe), q, conv, sink, NULL); + gst_element_sync_state_with_parent (q); + gst_element_sync_state_with_parent (conv); + gst_element_sync_state_with_parent (sink); + gst_element_link_many (q, conv, sink, NULL); + + qpad = gst_element_get_static_pad (q, "sink"); + + ret = gst_pad_link (pad, qpad); + g_assert_cmpint (ret, ==, GST_PAD_LINK_OK); +} + +static void +on_incoming_decodebin_stream (GstElement * decodebin, GstPad * pad, + GstElement * pipe) +{ + GstCaps *caps; + const gchar *name; + + if (!gst_pad_has_current_caps (pad)) { + g_printerr ("Pad '%s' has no caps, can't do anything, ignoring\n", + GST_PAD_NAME (pad)); + return; + } + + caps = gst_pad_get_current_caps (pad); + name = gst_structure_get_name (gst_caps_get_structure (caps, 0)); + + if (g_str_has_prefix (name, "video")) { + handle_media_stream (pad, pipe, "videoconvert", "autovideosink"); + } else if (g_str_has_prefix (name, "audio")) { + handle_media_stream (pad, pipe, "audioconvert", "autoaudiosink"); + } else { + g_printerr ("Unknown pad %s, ignoring", GST_PAD_NAME (pad)); + } +} + +static void +on_incoming_stream (GstElement * webrtc, GstPad * pad, GstElement * pipe) +{ + GstElement *decodebin; + + if (GST_PAD_DIRECTION (pad) != GST_PAD_SRC) + return; + + decodebin = gst_element_factory_make ("decodebin", NULL); + g_signal_connect (decodebin, "pad-added", + G_CALLBACK (on_incoming_decodebin_stream), pipe); + gst_bin_add (GST_BIN (pipe), decodebin); + gst_element_sync_state_with_parent (decodebin); + gst_element_link (webrtc, decodebin); +} + +static void +send_room_peer_msg (const gchar * text, const gchar * peer_id) +{ + gchar *msg; + + msg = g_strdup_printf ("ROOM_PEER_MSG %s %s", peer_id, text); + soup_websocket_connection_send_text (ws_conn, msg); + g_free (msg); +} + +static void +send_ice_candidate_message (GstElement * webrtc G_GNUC_UNUSED, guint mlineindex, + gchar * candidate, const gchar * peer_id) +{ + gchar *text; + JsonObject *ice, *msg; + + if (app_state < ROOM_CALL_OFFERING) { + cleanup_and_quit_loop ("Can't send ICE, not in call", APP_STATE_ERROR); + return; + } + + ice = json_object_new (); + json_object_set_string_member (ice, "candidate", candidate); + json_object_set_int_member (ice, "sdpMLineIndex", mlineindex); + msg = json_object_new (); + json_object_set_object_member (msg, "ice", ice); + text = get_string_from_json_object (msg); + json_object_unref (msg); + + send_room_peer_msg (text, peer_id); + g_free (text); +} + +static void +send_room_peer_sdp (GstWebRTCSessionDescription * desc, const gchar * peer_id) +{ + JsonObject *msg, *sdp; + gchar *text, *sdptype, *sdptext; + + g_assert_cmpint (app_state, <, ROOM_CALL_OFFERING); + + if (desc->type == GST_WEBRTC_SDP_TYPE_OFFER) + sdptype = "offer"; + else if (desc->type == GST_WEBRTC_SDP_TYPE_ANSWER) + sdptype = "answer"; + else + g_assert_not_reached (); + + text = gst_sdp_message_as_text (desc->sdp); + g_print ("Sending sdp %s to %s:\n%s\n", sdptype, peer_id, text); + + sdp = json_object_new (); + json_object_set_string_member (sdp, "type", sdptype); + json_object_set_string_member (sdp, "sdp", text); + g_free (text); + + msg = json_object_new (); + json_object_set_object_member (msg, "sdp", sdp); + sdptext = get_string_from_json_object (msg); + json_object_unref (msg); + + send_room_peer_msg (sdptext, peer_id); + g_free (sdptext); +} + +/* Offer created by our pipeline, to be sent to the peer */ +static void +on_offer_created (GstPromise * promise, const gchar * peer_id) +{ + GstElement *webrtc; + GstWebRTCSessionDescription *offer; + + g_assert_cmpint (app_state, ==, ROOM_CALL_OFFERING); + + g_assert_cmpint (promise->result, ==, GST_PROMISE_RESULT_REPLIED); + gst_structure_get (promise->promise, "offer", + GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &offer, NULL); + gst_promise_unref (promise); + + promise = gst_promise_new (); + webrtc = gst_bin_get_by_name (GST_BIN (pipeline), peer_id); + g_assert_nonnull (webrtc); + g_signal_emit_by_name (webrtc, "set-local-description", offer, promise); + gst_promise_interrupt (promise); + gst_promise_unref (promise); + + /* Send offer to peer */ + send_room_peer_sdp (offer, peer_id); + gst_webrtc_session_description_free (offer); +} + +static void +on_negotiation_needed (GstElement * webrtc, const gchar * peer_id) +{ + GstPromise *promise = gst_promise_new (); + + app_state = ROOM_CALL_OFFERING; + g_signal_emit_by_name (webrtc, "create-offer", NULL, promise); + gst_promise_set_change_callback (promise, + (GstPromiseChangeFunc) on_offer_created, (gpointer) peer_id, NULL); +} + +static void +add_webrtcbin_to_pipeline (const gchar * peer_id, gboolean offer) +{ + int ret; + GstElement *tee, *webrtc; + GstPad *srcpad, *sinkpad; + + webrtc = gst_element_factory_make ("webrtcbin", peer_id); + g_assert_nonnull (webrtc); + + tee = gst_bin_get_by_name (GST_BIN (pipeline), "audiotee"); + g_assert_nonnull (tee); + srcpad = gst_element_get_request_pad (tee, "src_%u"); + gst_object_unref (tee); + sinkpad = gst_element_get_request_pad (webrtc, "sink_%u"); + + /* Add the bin to the pipeline and connect it */ + gst_bin_add (GST_BIN (pipeline), webrtc); + + ret = gst_pad_link (srcpad, sinkpad); + g_assert_cmpint (ret, ==, GST_PAD_LINK_OK); + gst_object_unref (srcpad); + gst_object_unref (sinkpad); + + /* This is the gstwebrtc entry point where we create the offer and so on. It + * will be called when the pipeline goes to PLAYING. + * XXX: We must connect this after webrtcbin has been linked to a source via + * get_request_pad() otherwise webrtcbin will create an SDP offer with no + * media lines in it. */ + if (offer) + g_signal_connect (webrtc, "on-negotiation-needed", + G_CALLBACK (on_negotiation_needed), (gpointer) peer_id); + + /* We need to transmit this ICE candidate to the browser via the websockets + * signalling server. Incoming ice candidates from the browser need to be + * added by us too, see on_server_message() */ + g_signal_connect (webrtc, "on-ice-candidate", + G_CALLBACK (send_ice_candidate_message), (gpointer) peer_id); + /* Incoming streams will be exposed via this signal */ + g_signal_connect (webrtc, "pad-added", G_CALLBACK (on_incoming_stream), + pipeline); + + /* Set to bin to PLAYING */ + ret = gst_element_sync_state_with_parent (webrtc); + gst_object_unref (webrtc); + g_assert_true (ret); +} + +static void +call_peer (const gchar * peer_id) +{ + add_webrtcbin_to_pipeline (peer_id, TRUE); +} + +static void +incoming_call_from_peer (const gchar * peer_id) +{ + add_webrtcbin_to_pipeline (peer_id, FALSE); +} + +#define STR(x) #x +#define RTP_CAPS_OPUS(x) "application/x-rtp,media=audio,encoding-name=OPUS,payload=" STR(x) +#define RTP_CAPS_VP8(x) "application/x-rtp,media=video,encoding-name=VP8,payload=" STR(x) + +static gboolean +start_pipeline (void) +{ + GstStateChangeReturn ret; + GError *error = NULL; + + /* NOTE: webrtcbin currently does not support dynamic addition/removal of + * streams, so we use a separate webrtcbin for each peer, but all of them are + * inside the same pipeline. We start by connecting it to a fakesink so that + * we can preroll early. */ + pipeline = gst_parse_launch ("tee name=audiotee ! queue ! fakesink " + "audiotestsrc is-live=true wave=red-noise ! queue ! opusenc ! rtpopuspay ! " + "queue ! " RTP_CAPS_OPUS(96) " ! audiotee. ", + &error); + + if (error) { + g_printerr ("Failed to parse launch: %s\n", error->message); + g_error_free (error); + goto err; + } + + g_print ("Starting pipeline, not transmitting yet\n"); + ret = gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING); + if (ret == GST_STATE_CHANGE_FAILURE) + goto err; + + return TRUE; + +err: + if (pipeline) + g_clear_object (&pipeline); + return FALSE; +} + +static gboolean +join_room_on_server (void) +{ + gchar *msg; + + if (soup_websocket_connection_get_state (ws_conn) != + SOUP_WEBSOCKET_STATE_OPEN) + return FALSE; + + if (!room_id) + return FALSE; + + g_print ("Joining room %s\n", room_id); + app_state = ROOM_JOINING; + msg = g_strdup_printf ("ROOM %s", room_id); + soup_websocket_connection_send_text (ws_conn, msg); + g_free (msg); + return TRUE; +} + +static gboolean +register_with_server (void) +{ + gchar *hello; + + if (soup_websocket_connection_get_state (ws_conn) != + SOUP_WEBSOCKET_STATE_OPEN) + return FALSE; + + g_print ("Registering id %s with server\n", local_id); + app_state = SERVER_REGISTERING; + + /* Register with the server with a random integer id. Reply will be received + * by on_server_message() */ + hello = g_strdup_printf ("HELLO %s", local_id); + soup_websocket_connection_send_text (ws_conn, hello); + g_free (hello); + + return TRUE; +} + +static void +on_server_closed (SoupWebsocketConnection * conn G_GNUC_UNUSED, + gpointer user_data G_GNUC_UNUSED) +{ + app_state = SERVER_CLOSED; + cleanup_and_quit_loop ("Server connection closed", 0); +} + +static gboolean +do_registration (void) +{ + if (app_state != SERVER_REGISTERING) { + cleanup_and_quit_loop ("ERROR: Received HELLO when not registering", + APP_STATE_ERROR); + return FALSE; + } + app_state = SERVER_REGISTERED; + g_print ("Registered with server\n"); + /* Ask signalling server that we want to join a room */ + if (!join_room_on_server ()) { + cleanup_and_quit_loop ("ERROR: Failed to join room", ROOM_CALL_ERROR); + return FALSE; + } + return TRUE; +} + +/* + * When we join a room, we are responsible for calling by starting negotiation + * with each peer in it by sending an SDP offer and ICE candidates. + */ +static void +do_join_room (const gchar * text) +{ + gint ii, len; + gchar **peer_ids; + + if (app_state != ROOM_JOINING) { + cleanup_and_quit_loop ("ERROR: Received ROOM_OK when not calling", + ROOM_JOIN_ERROR); + return; + } + + app_state = ROOM_JOINED; + /* Start recording, but not transmitting */ + if (!start_pipeline ()) { + cleanup_and_quit_loop ("ERROR: Failed to start pipeline", + ROOM_CALL_ERROR); + return; + } + + peer_ids = g_strsplit (text, " ", -1); + g_assert_cmpstr (peer_ids[0], ==, "ROOM_OK"); + len = g_strv_length (peer_ids); + /* There are peers in the room already. We need to start negotiation + * (exchange SDP and ICE candidates) and transmission of media. */ + if (len > 1 && strlen (peer_ids[1]) > 0) { + g_print ("Found %i peers already in room\n", len - 1); + app_state = ROOM_CALL_OFFERING; + for (ii = 1; ii < len; ii++) { + gchar *peer_id = g_strdup (peer_ids[ii]); + g_print ("Negotiating with peer %s\n", peer_id); + /* This might fail asynchronously */ + call_peer (peer_id); + peers = g_list_prepend (peers, peer_id); + } + } + + g_strfreev (peer_ids); + return; +} + +static void +handle_error_message (const gchar * msg) +{ + switch (app_state) { + case SERVER_CONNECTING: + app_state = SERVER_CONNECTION_ERROR; + break; + case SERVER_REGISTERING: + app_state = SERVER_REGISTRATION_ERROR; + break; + case ROOM_JOINING: + app_state = ROOM_JOIN_ERROR; + break; + case ROOM_JOINED: + case ROOM_CALL_NEGOTIATING: + case ROOM_CALL_OFFERING: + case ROOM_CALL_ANSWERING: + app_state = ROOM_CALL_ERROR; + break; + case ROOM_CALL_STARTED: + case ROOM_CALL_STOPPING: + case ROOM_CALL_STOPPED: + app_state = ROOM_CALL_ERROR; + break; + default: + app_state = APP_STATE_ERROR; + } + cleanup_and_quit_loop (msg, 0); +} + +static void +on_answer_created (GstPromise * promise, const gchar * peer_id) +{ + GstElement *webrtc; + GstWebRTCSessionDescription *answer; + + g_assert_cmpint (app_state, ==, ROOM_CALL_ANSWERING); + + g_assert_cmpint (promise->result, ==, GST_PROMISE_RESULT_REPLIED); + gst_structure_get (promise->promise, "answer", + GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &answer, NULL); + gst_promise_unref (promise); + + promise = gst_promise_new (); + webrtc = gst_bin_get_by_name (GST_BIN (pipeline), peer_id); + g_assert_nonnull (webrtc); + g_signal_emit_by_name (webrtc, "set-local-description", answer, promise); + gst_promise_interrupt (promise); + gst_promise_unref (promise); + + /* Send offer to peer */ + send_room_peer_sdp (answer, peer_id); + gst_webrtc_session_description_free (answer); + + app_state = ROOM_CALL_STARTED; +} + +static void +handle_sdp_offer (const gchar * peer_id, const gchar * text) +{ + int ret; + GstPromise *promise; + GstElement *webrtc; + GstSDPMessage *sdp; + GstWebRTCSessionDescription *offer; + + g_assert_cmpint (app_state, ==, ROOM_CALL_ANSWERING); + + g_print ("Received offer:\n%s\n", text); + + ret = gst_sdp_message_new (&sdp); + g_assert_cmpint (ret, ==, GST_SDP_OK); + + ret = gst_sdp_message_parse_buffer (text, strlen (text), sdp); + g_assert_cmpint (ret, ==, GST_SDP_OK); + + offer = gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_OFFER, sdp); + g_assert_nonnull (offer); + + /* Set remote description on our pipeline */ + promise = gst_promise_new (); + webrtc = gst_bin_get_by_name (GST_BIN (pipeline), peer_id); + g_assert_nonnull (webrtc); + g_signal_emit_by_name (webrtc, "set-remote-description", offer, promise); + /* We don't want to be notified when the action is done */ + gst_promise_interrupt (promise); + gst_promise_unref (promise); + + /* Create an answer that we will send back to the peer */ + promise = gst_promise_new (); + gst_promise_set_change_callback (promise, + (GstPromiseChangeFunc) on_answer_created, (gpointer) peer_id, NULL); + g_signal_emit_by_name (webrtc, "create-answer", NULL, promise); + + gst_webrtc_session_description_free (offer); + gst_object_unref (webrtc); +} + +static void +handle_sdp_answer (const gchar * peer_id, const gchar * text) +{ + int ret; + GstPromise *promise; + GstElement *webrtc; + GstSDPMessage *sdp; + GstWebRTCSessionDescription *answer; + + g_assert_cmpint (app_state, >=, ROOM_CALL_OFFERING); + + g_print ("Received answer:\n%s\n", text); + + ret = gst_sdp_message_new (&sdp); + g_assert_cmpint (ret, ==, GST_SDP_OK); + + ret = gst_sdp_message_parse_buffer (text, strlen (text), sdp); + g_assert_cmpint (ret, ==, GST_SDP_OK); + + answer = gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_ANSWER, sdp); + g_assert_nonnull (answer); + + /* Set remote description on our pipeline */ + promise = gst_promise_new (); + webrtc = gst_bin_get_by_name (GST_BIN (pipeline), peer_id); + g_assert_nonnull (webrtc); + g_signal_emit_by_name (webrtc, "set-remote-description", answer, promise); + gst_object_unref (webrtc); + /* We don't want to be notified when the action is done */ + gst_promise_interrupt (promise); + gst_promise_unref (promise); +} + +static gboolean +handle_peer_message (const gchar * peer_id, const gchar * msg) +{ + JsonNode *root; + JsonObject *object, *child; + JsonParser *parser = json_parser_new (); + if (!json_parser_load_from_data (parser, msg, -1, NULL)) { + g_printerr ("Unknown message '%s' from '%s', ignoring", msg, peer_id); + g_object_unref (parser); + return FALSE; + } + + root = json_parser_get_root (parser); + if (!JSON_NODE_HOLDS_OBJECT (root)) { + g_printerr ("Unknown json message '%s' from '%s', ignoring", msg, peer_id); + g_object_unref (parser); + return FALSE; + } + + g_print ("Message from peer %s: %s\n", peer_id, msg); + + object = json_node_get_object (root); + /* Check type of JSON message */ + if (json_object_has_member (object, "sdp")) { + int ret; + GstSDPMessage *sdp; + const gchar *text, *sdp_type; + GstWebRTCSessionDescription *answer; + + g_assert_cmpint (app_state, >=, ROOM_JOINED); + + child = json_object_get_object_member (object, "sdp"); + + if (!json_object_has_member (child, "type")) { + cleanup_and_quit_loop ("ERROR: received SDP without 'type'", + ROOM_CALL_ERROR); + return FALSE; + } + + sdp_type = json_object_get_string_member (child, "type"); + text = json_object_get_string_member (child, "sdp"); + + if (g_strcmp0 (sdp_type, "offer") == 0) { + app_state = ROOM_CALL_ANSWERING; + incoming_call_from_peer (peer_id); + handle_sdp_offer (peer_id, text); + } else if (g_strcmp0 (sdp_type, "answer") == 0) { + g_assert_cmpint (app_state, >=, ROOM_CALL_OFFERING); + handle_sdp_answer (peer_id, text); + app_state = ROOM_CALL_STARTED; + } else { + cleanup_and_quit_loop ("ERROR: invalid sdp_type", ROOM_CALL_ERROR); + return FALSE; + } + } else if (json_object_has_member (object, "ice")) { + GstElement *webrtc; + const gchar *candidate; + gint sdpmlineindex; + + child = json_object_get_object_member (object, "ice"); + candidate = json_object_get_string_member (child, "candidate"); + sdpmlineindex = json_object_get_int_member (child, "sdpMLineIndex"); + + /* Add ice candidate sent by remote peer */ + webrtc = gst_bin_get_by_name (GST_BIN (pipeline), peer_id); + g_assert_nonnull (webrtc); + g_signal_emit_by_name (webrtc, "add-ice-candidate", sdpmlineindex, + candidate); + gst_object_unref (webrtc); + } else { + g_printerr ("Ignoring unknown JSON message:\n%s\n", msg); + } + g_object_unref (parser); + return TRUE; +} + +/* One mega message handler for our asynchronous calling mechanism */ +static void +on_server_message (SoupWebsocketConnection * conn, SoupWebsocketDataType type, + GBytes * message, gpointer user_data) +{ + gsize size; + gchar *text, *data; + + switch (type) { + case SOUP_WEBSOCKET_DATA_BINARY: + g_printerr ("Received unknown binary message, ignoring\n"); + g_bytes_unref (message); + return; + case SOUP_WEBSOCKET_DATA_TEXT: + data = g_bytes_unref_to_data (message, &size); + /* Convert to NULL-terminated string */ + text = g_strndup (data, size); + g_free (data); + break; + default: + g_assert_not_reached (); + } + + /* Server has accepted our registration, we are ready to send commands */ + if (g_strcmp0 (text, "HELLO") == 0) { + /* May fail asynchronously */ + do_registration (); + /* Room-related message */ + } else if (g_str_has_prefix (text, "ROOM_")) { + /* Room joined, now we can start negotiation */ + if (g_str_has_prefix (text, "ROOM_OK ")) { + /* May fail asynchronously */ + do_join_room (text); + } else if (g_str_has_prefix (text, "ROOM_PEER")) { + gchar **splitm = NULL; + const gchar *peer_id; + /* SDP and ICE, usually */ + if (g_str_has_prefix (text, "ROOM_PEER_MSG")) { + splitm = g_strsplit (text, " ", 3); + peer_id = find_peer_from_list (splitm[1]); + g_assert_nonnull (peer_id); + /* Could be an offer or an answer, or ICE, or an arbitrary message */ + handle_peer_message (peer_id, splitm[2]); + } else if (g_str_has_prefix (text, "ROOM_PEER_JOINED")) { + splitm = g_strsplit (text, " ", 2); + peers = g_list_prepend (peers, g_strdup (splitm[1])); + peer_id = find_peer_from_list (splitm[1]); + g_assert_nonnull (peer_id); + g_print ("Peer %s has joined the room\n", peer_id); + } else if (g_str_has_prefix (text, "ROOM_PEER_LEFT")) { + splitm = g_strsplit (text, " ", 2); + peer_id = find_peer_from_list (splitm[1]); + g_assert_nonnull (peer_id); + peers = g_list_remove (peers, peer_id); + g_print ("Peer %s has left the room\n", peer_id); + g_free ((gchar*) peer_id); + /* TODO: cleanup pipeline */ + } else { + g_printerr ("WARNING: Ignoring unknown message %s\n", text); + } + g_strfreev (splitm); + } else { + goto err; + } + /* Handle errors */ + } else if (g_str_has_prefix (text, "ERROR")) { + handle_error_message (text); + } else { + goto err; + } + +out: + g_free (text); + return; + +err: + { + gchar *err_s = g_strdup_printf ("ERROR: unknown message %s", text); + cleanup_and_quit_loop (err_s, 0); + g_free (err_s); + goto out; + } +} + +static void +on_server_connected (SoupSession * session, GAsyncResult * res, + SoupMessage *msg) +{ + GError *error = NULL; + + ws_conn = soup_session_websocket_connect_finish (session, res, &error); + if (error) { + cleanup_and_quit_loop (error->message, SERVER_CONNECTION_ERROR); + g_error_free (error); + return; + } + + g_assert_nonnull (ws_conn); + + app_state = SERVER_CONNECTED; + g_print ("Connected to signalling server\n"); + + g_signal_connect (ws_conn, "closed", G_CALLBACK (on_server_closed), NULL); + g_signal_connect (ws_conn, "message", G_CALLBACK (on_server_message), NULL); + + /* Register with the server so it knows about us and can accept commands + * responses from the server will be handled in on_server_message() above */ + register_with_server (); +} + +/* + * Connect to the signalling server. This is the entrypoint for everything else. + */ +static void +connect_to_websocket_server_async (void) +{ + SoupLogger *logger; + SoupMessage *message; + SoupSession *session; + const char *https_aliases[] = {"wss", NULL}; + + session = soup_session_new_with_options (SOUP_SESSION_SSL_STRICT, TRUE, + SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE, + //SOUP_SESSION_SSL_CA_FILE, "/etc/ssl/certs/ca-bundle.crt", + SOUP_SESSION_HTTPS_ALIASES, https_aliases, NULL); + + logger = soup_logger_new (SOUP_LOGGER_LOG_BODY, -1); + soup_session_add_feature (session, SOUP_SESSION_FEATURE (logger)); + g_object_unref (logger); + + message = soup_message_new (SOUP_METHOD_GET, server_url); + + g_print ("Connecting to server...\n"); + + /* Once connected, we will register */ + soup_session_websocket_connect_async (session, message, NULL, NULL, NULL, + (GAsyncReadyCallback) on_server_connected, message); + app_state = SERVER_CONNECTING; +} + +int +main (int argc, char *argv[]) +{ + SoupSession *session; + GOptionContext *context; + GError *error = NULL; + + context = g_option_context_new ("- gstreamer webrtc sendrecv demo"); + g_option_context_add_main_entries (context, entries, NULL); + g_option_context_add_group (context, gst_init_get_option_group ()); + if (!g_option_context_parse (context, &argc, &argv, &error)) { + g_printerr ("Error initializing: %s\n", error->message); + return -1; + } + + if (!room_id) { + g_printerr ("--room-id is a required argument\n"); + return -1; + } + + if (!local_id) + local_id = g_strdup_printf ("%s-%i", g_get_user_name (), + g_random_int_range (10, 10000)); + /* Sanitize by removing whitespace, modifies string in-place */ + g_strdelimit (local_id, " \t\n\r", '-'); + + g_print ("Our local id is %s\n", local_id); + + if (!server_url) + server_url = g_strdup (default_server_url); + + loop = g_main_loop_new (NULL, FALSE); + + connect_to_websocket_server_async (); + + g_main_loop_run (loop); + + gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL); + g_print ("Pipeline stopped\n"); + + gst_object_unref (pipeline); + g_free (server_url); + g_free (local_id); + g_free (room_id); + + return 0; +} From 0c5e7999526cbdcfc2ccc82da4f637bc4dd37040 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Mon, 30 Oct 2017 13:24:21 +0530 Subject: [PATCH 010/130] multiparty sendrecv: Add a queue before the audio sink Missed this, fixes the bug where removing a peer causes the pipeline to get stuck. However, when peers leave, there is still a chance that the pipeline will get stuck. --- .../gst/mp-webrtc-sendrecv.c | 79 +++++++++++++++---- 1 file changed, 64 insertions(+), 15 deletions(-) diff --git a/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c b/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c index ed25f117c4..184c02954e 100644 --- a/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c +++ b/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c @@ -220,7 +220,7 @@ send_room_peer_sdp (GstWebRTCSessionDescription * desc, const gchar * peer_id) JsonObject *msg, *sdp; gchar *text, *sdptype, *sdptext; - g_assert_cmpint (app_state, <, ROOM_CALL_OFFERING); + g_assert_cmpint (app_state, >=, ROOM_CALL_OFFERING); if (desc->type == GST_WEBRTC_SDP_TYPE_OFFER) sdptype = "offer"; @@ -284,24 +284,71 @@ on_negotiation_needed (GstElement * webrtc, const gchar * peer_id) } static void -add_webrtcbin_to_pipeline (const gchar * peer_id, gboolean offer) +remove_peer_from_pipeline (const gchar * peer_id) +{ + gchar *qname; + GstPad *srcpad, *sinkpad; + GstElement *webrtc, *q, *tee; + + webrtc = gst_bin_get_by_name (GST_BIN (pipeline), peer_id); + if (!webrtc) + return; + + gst_bin_remove (GST_BIN (pipeline), webrtc); + gst_object_unref (webrtc); + + qname = g_strdup_printf ("queue-%s", peer_id); + q = gst_bin_get_by_name (GST_BIN (pipeline), qname); + g_free (qname); + + sinkpad = gst_element_get_static_pad (q, "sink"); + g_assert_nonnull (sinkpad); + srcpad = gst_pad_get_peer (sinkpad); + g_assert_nonnull (srcpad); + gst_object_unref (sinkpad); + + gst_bin_remove (GST_BIN (pipeline), q); + gst_object_unref (q); + + tee = gst_bin_get_by_name (GST_BIN (pipeline), "audiotee"); + g_assert_nonnull (tee); + gst_element_release_request_pad (tee, srcpad); + gst_object_unref (srcpad); + gst_object_unref (tee); +} + +static void +add_peer_to_pipeline (const gchar * peer_id, gboolean offer) { int ret; - GstElement *tee, *webrtc; + gchar *tmp; + GstElement *tee, *webrtc, *q; GstPad *srcpad, *sinkpad; + GError *error = NULL; + tmp = g_strdup_printf ("queue-%s", peer_id); + q = gst_element_factory_make ("queue", tmp); + g_free (tmp); webrtc = gst_element_factory_make ("webrtcbin", peer_id); - g_assert_nonnull (webrtc); + + gst_bin_add_many (GST_BIN (pipeline), q, webrtc, NULL); + + srcpad = gst_element_get_static_pad (q, "src"); + g_assert_nonnull (srcpad); + sinkpad = gst_element_get_request_pad (webrtc, "sink_%u"); + g_assert_nonnull (sinkpad); + ret = gst_pad_link (srcpad, sinkpad); + g_assert_cmpint (ret, ==, GST_PAD_LINK_OK); + gst_object_unref (srcpad); + gst_object_unref (sinkpad); tee = gst_bin_get_by_name (GST_BIN (pipeline), "audiotee"); g_assert_nonnull (tee); srcpad = gst_element_get_request_pad (tee, "src_%u"); + g_assert_nonnull (srcpad); gst_object_unref (tee); - sinkpad = gst_element_get_request_pad (webrtc, "sink_%u"); - - /* Add the bin to the pipeline and connect it */ - gst_bin_add (GST_BIN (pipeline), webrtc); - + sinkpad = gst_element_get_static_pad (q, "sink"); + g_assert_nonnull (sinkpad); ret = gst_pad_link (srcpad, sinkpad); g_assert_cmpint (ret, ==, GST_PAD_LINK_OK); gst_object_unref (srcpad); @@ -310,8 +357,8 @@ add_webrtcbin_to_pipeline (const gchar * peer_id, gboolean offer) /* This is the gstwebrtc entry point where we create the offer and so on. It * will be called when the pipeline goes to PLAYING. * XXX: We must connect this after webrtcbin has been linked to a source via - * get_request_pad() otherwise webrtcbin will create an SDP offer with no - * media lines in it. */ + * get_request_pad() and before we go from NULL->READY otherwise webrtcbin + * will create an SDP offer with no media lines in it. */ if (offer) g_signal_connect (webrtc, "on-negotiation-needed", G_CALLBACK (on_negotiation_needed), (gpointer) peer_id); @@ -325,22 +372,23 @@ add_webrtcbin_to_pipeline (const gchar * peer_id, gboolean offer) g_signal_connect (webrtc, "pad-added", G_CALLBACK (on_incoming_stream), pipeline); - /* Set to bin to PLAYING */ + /* Set to pipeline branch to PLAYING */ + ret = gst_element_sync_state_with_parent (q); + g_assert_true (ret); ret = gst_element_sync_state_with_parent (webrtc); - gst_object_unref (webrtc); g_assert_true (ret); } static void call_peer (const gchar * peer_id) { - add_webrtcbin_to_pipeline (peer_id, TRUE); + add_peer_to_pipeline (peer_id, TRUE); } static void incoming_call_from_peer (const gchar * peer_id) { - add_webrtcbin_to_pipeline (peer_id, FALSE); + add_peer_to_pipeline (peer_id, FALSE); } #define STR(x) #x @@ -755,6 +803,7 @@ on_server_message (SoupWebsocketConnection * conn, SoupWebsocketDataType type, g_assert_nonnull (peer_id); peers = g_list_remove (peers, peer_id); g_print ("Peer %s has left the room\n", peer_id); + remove_peer_from_pipeline (peer_id); g_free ((gchar*) peer_id); /* TODO: cleanup pipeline */ } else { From e5c5767298c0b1881267016bee9464865528ac9d Mon Sep 17 00:00:00 2001 From: Mathieu Duponchelle Date: Wed, 22 Nov 2017 13:15:48 +0100 Subject: [PATCH 011/130] Update to new promise API --- .../gst/mp-webrtc-sendrecv.c | 21 +++++++++++-------- webrtc/sendrecv/gst/webrtc-sendrecv.c | 11 +++++----- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c b/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c index 184c02954e..5a257c6d63 100644 --- a/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c +++ b/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c @@ -252,11 +252,13 @@ on_offer_created (GstPromise * promise, const gchar * peer_id) { GstElement *webrtc; GstWebRTCSessionDescription *offer; + const GstStructure *reply; g_assert_cmpint (app_state, ==, ROOM_CALL_OFFERING); - g_assert_cmpint (promise->result, ==, GST_PROMISE_RESULT_REPLIED); - gst_structure_get (promise->promise, "offer", + g_assert_cmpint (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); @@ -275,12 +277,12 @@ on_offer_created (GstPromise * promise, const gchar * peer_id) static void on_negotiation_needed (GstElement * webrtc, const gchar * peer_id) { - GstPromise *promise = gst_promise_new (); + GstPromise *promise; app_state = ROOM_CALL_OFFERING; - g_signal_emit_by_name (webrtc, "create-offer", NULL, promise); - gst_promise_set_change_callback (promise, + promise = gst_promise_new_with_change_func ( (GstPromiseChangeFunc) on_offer_created, (gpointer) peer_id, NULL); + g_signal_emit_by_name (webrtc, "create-offer", NULL, promise); } static void @@ -576,11 +578,13 @@ on_answer_created (GstPromise * promise, const gchar * peer_id) { GstElement *webrtc; GstWebRTCSessionDescription *answer; + const GstStructure *reply; g_assert_cmpint (app_state, ==, ROOM_CALL_ANSWERING); - g_assert_cmpint (promise->result, ==, GST_PROMISE_RESULT_REPLIED); - gst_structure_get (promise->promise, "answer", + g_assert_cmpint (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); @@ -630,8 +634,7 @@ handle_sdp_offer (const gchar * peer_id, const gchar * text) gst_promise_unref (promise); /* Create an answer that we will send back to the peer */ - promise = gst_promise_new (); - gst_promise_set_change_callback (promise, + promise = gst_promise_new_with_change_func ( (GstPromiseChangeFunc) on_answer_created, (gpointer) peer_id, NULL); g_signal_emit_by_name (webrtc, "create-answer", NULL, promise); diff --git a/webrtc/sendrecv/gst/webrtc-sendrecv.c b/webrtc/sendrecv/gst/webrtc-sendrecv.c index 3ee0e44789..bab104702e 100644 --- a/webrtc/sendrecv/gst/webrtc-sendrecv.c +++ b/webrtc/sendrecv/gst/webrtc-sendrecv.c @@ -219,12 +219,14 @@ static void on_offer_created (GstPromise * promise, gpointer user_data) { GstWebRTCSessionDescription *offer = NULL; + const GstStructure *reply; gchar *desc; g_assert (app_state == PEER_CALL_NEGOTIATING); - g_assert (promise->result == GST_PROMISE_RESULT_REPLIED); - gst_structure_get (promise->promise, "offer", + 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); @@ -241,12 +243,11 @@ on_offer_created (GstPromise * promise, gpointer user_data) static void on_negotiation_needed (GstElement * element, gpointer user_data) { - GstPromise *promise = gst_promise_new (); + GstPromise *promise; app_state = PEER_CALL_NEGOTIATING; + promise = gst_promise_new_with_change_func (on_offer_created, user_data, NULL);; g_signal_emit_by_name (webrtc1, "create-offer", NULL, promise); - gst_promise_set_change_callback (promise, on_offer_created, user_data, - NULL); } #define RTP_CAPS_OPUS "application/x-rtp,media=audio,encoding-name=OPUS,payload=" From e4e83a648bd405e14bb87f2fdc26ae816e258892 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Thu, 23 Nov 2017 00:21:36 +1100 Subject: [PATCH 012/130] server/js: also allow running on localhost --- webrtc/sendrecv/js/webrtc.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/webrtc/sendrecv/js/webrtc.js b/webrtc/sendrecv/js/webrtc.js index 31116c017a..b95df54dcd 100644 --- a/webrtc/sendrecv/js/webrtc.js +++ b/webrtc/sendrecv/js/webrtc.js @@ -137,7 +137,15 @@ function websocketServerConnect() { } peer_id = getOurId(); setStatus("Connecting to server"); - ws_conn = new WebSocket('wss://' + window.location.hostname + ':8443'); + loc = null; + if (window.location.protocol.startsWith ("file")) { + loc = "127.0.0.1"; + } else if (window.location.protocol.startsWith ("http")) { + loc = window.location.hostname; + } else { + throw new Error ("Don't know how to connect to the signalling server with uri" + window.location); + } + ws_conn = new WebSocket('wss://' + loc + ':8443'); /* When connected, immediately register with the server */ ws_conn.addEventListener('open', (event) => { document.getElementById("peer-id").textContent = peer_id; From 97cf7634208bbc87c491b299f5636ead7e965804 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Tue, 12 Dec 2017 21:40:09 +0530 Subject: [PATCH 013/130] sendrecv: Add a Google STUN server to the configuration Without this, the example will only work on link-local and localhost networks. --- webrtc/sendrecv/gst/webrtc-sendrecv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webrtc/sendrecv/gst/webrtc-sendrecv.c b/webrtc/sendrecv/gst/webrtc-sendrecv.c index bab104702e..c823f651c9 100644 --- a/webrtc/sendrecv/gst/webrtc-sendrecv.c +++ b/webrtc/sendrecv/gst/webrtc-sendrecv.c @@ -250,6 +250,7 @@ on_negotiation_needed (GstElement * element, gpointer user_data) g_signal_emit_by_name (webrtc1, "create-offer", NULL, promise); } +#define STUN_SERVER " stun-server=stun://stun.l.google.com:19302 " #define RTP_CAPS_OPUS "application/x-rtp,media=audio,encoding-name=OPUS,payload=" #define RTP_CAPS_VP8 "application/x-rtp,media=video,encoding-name=VP8,payload=" @@ -260,7 +261,7 @@ start_pipeline (void) GError *error = NULL; pipe1 = - gst_parse_launch ("webrtcbin name=sendrecv " + gst_parse_launch ("webrtcbin name=sendrecv " STUN_SERVER "videotestsrc pattern=ball ! queue ! vp8enc deadline=1 ! rtpvp8pay ! " "queue ! " RTP_CAPS_VP8 "96 ! sendrecv. " "audiotestsrc wave=red-noise ! queue ! opusenc ! rtpopuspay ! " From 43a27385c397d2bc6b6bf7a49fd774fa3ee200a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Fri, 2 Feb 2018 08:23:30 +0000 Subject: [PATCH 014/130] Update README Point to upstream repos now that it's been merged --- webrtc/README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/webrtc/README.md b/webrtc/README.md index 3c3ae09ef3..af1bc23912 100644 --- a/webrtc/README.md +++ b/webrtc/README.md @@ -2,11 +2,13 @@ All demos use the same signalling server in the `signalling/` directory -You will need the following repositories till the GStreamer WebRTC implementation is merged upstream: +The GStreamer WebRTC implementation has now been merged upstream, so all +you need is the latest GStreamer git master, as of 2 February 2018 or later. -https://github.com/ystreet/gstreamer/tree/promise - -https://github.com/ystreet/gst-plugins-bad/tree/webrtc +http://cgit.freedesktop.org/gstreamer/gstreamer +http://cgit.freedesktop.org/gstreamer/gst-plugins-base +http://cgit.freedesktop.org/gstreamer/gst-plugins-good +http://cgit.freedesktop.org/gstreamer/gst-plugins-bad You can build these with either Autotools gst-uninstalled: From 72c10e82436da3292530bfae1b3f8bc13af69c6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Fri, 2 Feb 2018 08:39:04 +0000 Subject: [PATCH 015/130] webrtc-sendrecv: define GST_USE_UNSTABLE_API to avoid compiler warnings --- webrtc/sendrecv/gst/webrtc-sendrecv.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/webrtc/sendrecv/gst/webrtc-sendrecv.c b/webrtc/sendrecv/gst/webrtc-sendrecv.c index c823f651c9..1de11c23ae 100644 --- a/webrtc/sendrecv/gst/webrtc-sendrecv.c +++ b/webrtc/sendrecv/gst/webrtc-sendrecv.c @@ -8,6 +8,8 @@ */ #include #include + +#define GST_USE_UNSTABLE_API #include /* For signalling */ From 2e5204ae3b30b7938d5821cf25ddfc50fa45f747 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Fri, 2 Feb 2018 08:41:21 +0000 Subject: [PATCH 016/130] README: fix formatting --- webrtc/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/webrtc/README.md b/webrtc/README.md index af1bc23912..2bbaa6be4f 100644 --- a/webrtc/README.md +++ b/webrtc/README.md @@ -3,12 +3,12 @@ All demos use the same signalling server in the `signalling/` directory The GStreamer WebRTC implementation has now been merged upstream, so all -you need is the latest GStreamer git master, as of 2 February 2018 or later. +you need is the latest GStreamer git master, as of 2 February 2018 or later: -http://cgit.freedesktop.org/gstreamer/gstreamer -http://cgit.freedesktop.org/gstreamer/gst-plugins-base -http://cgit.freedesktop.org/gstreamer/gst-plugins-good -http://cgit.freedesktop.org/gstreamer/gst-plugins-bad + - http://cgit.freedesktop.org/gstreamer/gstreamer + - http://cgit.freedesktop.org/gstreamer/gst-plugins-base + - http://cgit.freedesktop.org/gstreamer/gst-plugins-good + - http://cgit.freedesktop.org/gstreamer/gst-plugins-bad You can build these with either Autotools gst-uninstalled: From 492d13a7c97ecb8dd4107680b40f9e4ddfff256e Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sat, 17 Feb 2018 08:10:59 +0530 Subject: [PATCH 017/130] README: link to blog post, document multiparty example Also add TODO stubs for MCU and SFU --- webrtc/README.md | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/webrtc/README.md b/webrtc/README.md index 2bbaa6be4f..5083518324 100644 --- a/webrtc/README.md +++ b/webrtc/README.md @@ -1,7 +1,9 @@ -## GStreamer WebRTC demos +# GStreamer WebRTC demos All demos use the same signalling server in the `signalling/` directory +## Build + The GStreamer WebRTC implementation has now been merged upstream, so all you need is the latest GStreamer git master, as of 2 February 2018 or later: @@ -18,11 +20,23 @@ Or with Meson gst-build: https://cgit.freedesktop.org/gstreamer/gst-build/ +You may need to install the following packages using your package manager: + +json-glib, libsoup, libnice, libnice-gstreamer1 (the gstreamer plugin for libnice) + +## Documentation + +Currently, the best way to understand the API is to read the examples. This post breaking down the API should help with that: + +http://blog.nirbheek.in/2018/02/gstreamer-webrtc.html + +## Examples + ### sendrecv: Send and receive audio and video * Serve the `js/` directory on the root of your website, or open https://webrtc.nirbheek.in - The JS code assumes the signalling server is on port 8443 of the same server serving the HTML -* Build and run the sources in the `gst/` directory on your machine +* Build the sources in the `gst/` directory on your machine ```console $ gcc webrtc-sendrecv.c $(pkg-config --cflags --libs gstreamer-webrtc-1.0 gstreamer-sdp-1.0 libsoup-2.4 json-glib-1.0) -o webrtc-sendrecv @@ -31,3 +45,29 @@ $ gcc webrtc-sendrecv.c $(pkg-config --cflags --libs gstreamer-webrtc-1.0 gstrea * Open the website in a browser and ensure that the status is "Registered with server, waiting for call", and note the `id` too. * Run `webrtc-sendrecv --peer-id=ID` with the `id` from the browser. You will see state changes and an SDP exchange. * You will see a bouncing ball + hear red noise in the browser, and your browser's webcam + mic in the gst app + +TODO: Port to Python and Rust. + +### multiparty-sendrecv: Multiparty audio conference with N peers + +* Build the sources in the `gst/` directory on your machine + +```console +$ gcc mp-webrtc-sendrecv.c $(pkg-config --cflags --libs gstreamer-webrtc-1.0 gstreamer-sdp-1.0 libsoup-2.4 json-glib-1.0) -o mp-webrtc-sendrecv +``` + +* Run `mp-webrtc-sendrecv --room-id=ID` with `ID` as a room name. The peer will connect to the signalling server and setup a conference room. +* Run this as many times as you like, each will spawn a peer that sends red noise and outputs the red noise it receives from other peers. + - To change what a peer sends, find the `audiotestsrc` element in the source and change the `wave` property. + - You can, of course, also replace `audiotestsrc` itself with `autoaudiosrc` (any platform) or `pulsesink` (on linux). +* TODO: implement JS to do the same, derived from the JS for the `sendrecv` example. + +### TODO: Selective Forwarding Unit (SFU) example + +* Server routes media between peers +* Participant sends 1 stream, receives n-1 streams + +### TODO: Multipoint Control Unit (MCU) example + +* Server mixes media from all participants +* Participant sends 1 stream, receives 1 stream From fa2adc717bb66d9f08ddc34c157a7b83d040e1d4 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Thu, 8 Mar 2018 20:10:55 +0530 Subject: [PATCH 018/130] Fix crash on Windows by delimiting option entries with NULL Also use more verbose forms of g_assert which print values on failure --- .../gst/mp-webrtc-sendrecv.c | 1 + webrtc/sendrecv/gst/webrtc-sendrecv.c | 29 ++++++++++--------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c b/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c index 5a257c6d63..00bcae433a 100644 --- a/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c +++ b/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c @@ -54,6 +54,7 @@ static GOptionEntry entries[] = { "name", 0, 0, G_OPTION_ARG_STRING, &local_id, "Name we will send to the server", "ID" }, { "room-id", 0, 0, G_OPTION_ARG_STRING, &room_id, "Room name to join or create", "ID" }, { "server", 0, 0, G_OPTION_ARG_STRING, &server_url, "Signalling server to connect to", "URL" }, + { NULL } }; static gint diff --git a/webrtc/sendrecv/gst/webrtc-sendrecv.c b/webrtc/sendrecv/gst/webrtc-sendrecv.c index 1de11c23ae..28f9660fba 100644 --- a/webrtc/sendrecv/gst/webrtc-sendrecv.c +++ b/webrtc/sendrecv/gst/webrtc-sendrecv.c @@ -50,6 +50,7 @@ static GOptionEntry entries[] = { { "peer-id", 0, 0, G_OPTION_ARG_STRING, &peer_id, "String ID of the peer to connect to", "ID" }, { "server", 0, 0, G_OPTION_ARG_STRING, &server_url, "Signalling server to connect to", "URL" }, + { NULL }, }; static gboolean @@ -105,12 +106,14 @@ handle_media_stream (GstPad * pad, GstElement * pipe, const char * convert_name, GstElement *q, *conv, *sink; GstPadLinkReturn ret; + g_print ("Trying to handle stream with %s ! %s", convert_name, sink_name); + q = gst_element_factory_make ("queue", NULL); - g_assert (q); + g_assert_nonnull (q); conv = gst_element_factory_make (convert_name, NULL); - g_assert (conv); + g_assert_nonnull (conv); sink = gst_element_factory_make (sink_name, NULL); - g_assert (sink); + g_assert_nonnull (sink); gst_bin_add_many (GST_BIN (pipe), q, conv, sink, NULL); gst_element_sync_state_with_parent (q); gst_element_sync_state_with_parent (conv); @@ -120,7 +123,7 @@ handle_media_stream (GstPad * pad, GstElement * pipe, const char * convert_name, qpad = gst_element_get_static_pad (q, "sink"); ret = gst_pad_link (pad, qpad); - g_assert (ret == GST_PAD_LINK_OK); + g_assert_cmphex (ret, ==, GST_PAD_LINK_OK); } static void @@ -224,9 +227,9 @@ on_offer_created (GstPromise * promise, gpointer user_data) const GstStructure *reply; gchar *desc; - g_assert (app_state == PEER_CALL_NEGOTIATING); + g_assert_cmphex (app_state, ==, PEER_CALL_NEGOTIATING); - g_assert (gst_promise_wait(promise) == GST_PROMISE_RESULT_REPLIED); + g_assert_cmphex (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); @@ -277,7 +280,7 @@ start_pipeline (void) } webrtc1 = gst_bin_get_by_name (GST_BIN (pipe1), "sendrecv"); - g_assert (webrtc1 != NULL); + g_assert_nonnull (webrtc1); /* This is the gstwebrtc entry point where we create the offer and so on. It * will be called when the pipeline goes to PLAYING. */ @@ -455,9 +458,9 @@ on_server_message (SoupWebsocketConnection * conn, SoupWebsocketDataType type, GstSDPMessage *sdp; GstWebRTCSessionDescription *answer; - g_assert (app_state == PEER_CALL_NEGOTIATING); + g_assert_cmphex (app_state, ==, PEER_CALL_NEGOTIATING); - g_assert (json_object_has_member (object, "type")); + g_assert_true (json_object_has_member (object, "type")); /* In this example, we always create the offer and receive one answer. * See tests/examples/webrtcbidirectional.c in gst-plugins-bad for how to * handle offers from peers and reply with answers using webrtcbin. */ @@ -469,14 +472,14 @@ on_server_message (SoupWebsocketConnection * conn, SoupWebsocketDataType type, g_print ("Received answer:\n%s\n", text); ret = gst_sdp_message_new (&sdp); - g_assert (ret == GST_SDP_OK); + g_assert_cmphex (ret, ==, GST_SDP_OK); ret = gst_sdp_message_parse_buffer (text, strlen (text), sdp); - g_assert (ret == GST_SDP_OK); + g_assert_cmphex (ret, ==, GST_SDP_OK); answer = gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_ANSWER, sdp); - g_assert (answer); + g_assert_nonnull (answer); /* Set remote description on our pipeline */ { @@ -523,7 +526,7 @@ on_server_connected (SoupSession * session, GAsyncResult * res, return; } - g_assert (ws_conn != NULL); + g_assert_nonnull (ws_conn); app_state = SERVER_CONNECTED; g_print ("Connected to signalling server\n"); From 55e86469d9aab4adf24dee86e3e4c06b623bcea1 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sat, 10 Mar 2018 01:54:48 +0530 Subject: [PATCH 019/130] Check for all necessary plugins at startup People seem to be having problems ensuring that they have all the right plugins built, so make it a bit easier for them. --- .../gst/mp-webrtc-sendrecv.c | 27 +++++++++++++++++++ webrtc/sendrecv/gst/webrtc-sendrecv.c | 27 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c b/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c index 00bcae433a..4a99eb8f3c 100644 --- a/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c +++ b/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c @@ -893,6 +893,30 @@ connect_to_websocket_server_async (void) app_state = SERVER_CONNECTING; } +static gboolean +check_plugins (void) +{ + int i; + gboolean ret; + GstPlugin *plugin; + GstRegistry *registry; + const gchar *needed[] = { "opus", "nice", "webrtc", "dtls", "srtp" + "rtpmanager", "audiotestsrc", NULL}; + + registry = gst_registry_get (); + ret = TRUE; + for (i = 0; i < g_strv_length ((gchar **) needed); i++) { + plugin = gst_registry_find_plugin (registry, needed[i]); + if (!plugin) { + g_print ("Required gstreamer plugin '%s' not found\n", needed[i]); + ret = FALSE; + continue; + } + gst_object_unref (plugin); + } + return ret; +} + int main (int argc, char *argv[]) { @@ -908,6 +932,9 @@ main (int argc, char *argv[]) return -1; } + if (!check_plugins ()) + return -1; + if (!room_id) { g_printerr ("--room-id is a required argument\n"); return -1; diff --git a/webrtc/sendrecv/gst/webrtc-sendrecv.c b/webrtc/sendrecv/gst/webrtc-sendrecv.c index 28f9660fba..fcad886ad4 100644 --- a/webrtc/sendrecv/gst/webrtc-sendrecv.c +++ b/webrtc/sendrecv/gst/webrtc-sendrecv.c @@ -568,6 +568,30 @@ connect_to_websocket_server_async (void) app_state = SERVER_CONNECTING; } +static gboolean +check_plugins (void) +{ + int i; + gboolean ret; + GstPlugin *plugin; + GstRegistry *registry; + const gchar *needed[] = { "opus", "vpx", "nice", "webrtc", "dtls", "srtp", + "rtpmanager", "videotestsrc", "audiotestsrc", NULL}; + + registry = gst_registry_get (); + ret = TRUE; + for (i = 0; i < g_strv_length ((gchar **) needed); i++) { + plugin = gst_registry_find_plugin (registry, needed[i]); + if (!plugin) { + g_print ("Required gstreamer plugin '%s' not found\n", needed[i]); + ret = FALSE; + continue; + } + gst_object_unref (plugin); + } + return ret; +} + int main (int argc, char *argv[]) { @@ -583,6 +607,9 @@ main (int argc, char *argv[]) return -1; } + if (!check_plugins ()) + return -1; + if (!peer_id) { g_printerr ("--peer-id is a required argument\n"); return -1; From 429f4bb65f37819f12f313cb001adff0c3298027 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sat, 10 Mar 2018 13:21:34 +0530 Subject: [PATCH 020/130] README.md: Document the binaries and Cerbero Also mention where to file bug reports about the plugin itself. --- webrtc/README.md | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/webrtc/README.md b/webrtc/README.md index 5083518324..5e8ac6769e 100644 --- a/webrtc/README.md +++ b/webrtc/README.md @@ -2,15 +2,29 @@ All demos use the same signalling server in the `signalling/` directory -## Build +## Downloading GStreamer -The GStreamer WebRTC implementation has now been merged upstream, so all -you need is the latest GStreamer git master, as of 2 February 2018 or later: +The GStreamer WebRTC implementation has now been merged upstream, and is in the GStreamer 1.14 release. Binaries can be found here: + +https://gstreamer.freedesktop.org/download/ + +## Building GStreamer from source + +If you don't want to use the binaries provided by GStreamer or on your Linux distro, you can build GStreamer from source. + +The easiest way to build the webrtc plugin and all the plugins it needs, is to [use Cerbero](https://gstreamer.freedesktop.org/documentation/installing/building-from-source-using-cerbero.html). These instructions should work out of the box for all platforms, including cross-compiling for iOS and Android. + +One thing to note is that it's written in Python 2, so you may need to replace all instances of `./cerbero-uninstalled` (or `cerbero`) with `python2 cerbero-uninstalled` or whatever Python 2 is called on your platform. + +## Building GStreamer manually from source + +For hacking on the webrtc plugin, you may want to build manually using the git repositories: - http://cgit.freedesktop.org/gstreamer/gstreamer - http://cgit.freedesktop.org/gstreamer/gst-plugins-base - http://cgit.freedesktop.org/gstreamer/gst-plugins-good - http://cgit.freedesktop.org/gstreamer/gst-plugins-bad + - http://cgit.freedesktop.org/libnice/libnice You can build these with either Autotools gst-uninstalled: @@ -24,6 +38,12 @@ You may need to install the following packages using your package manager: json-glib, libsoup, libnice, libnice-gstreamer1 (the gstreamer plugin for libnice) +## Filing bugs + +Please only file bugs about the demos here. Bugs about GStreamer's WebRTC implementation should be filed on the [GStreamer bugzilla](https://bugzilla.gnome.org/enter_bug.cgi?product=GStreamer&component=gst-plugins-bad). + +You can also find us on IRC by joining #gstreamer @ FreeNode. + ## Documentation Currently, the best way to understand the API is to read the examples. This post breaking down the API should help with that: From 2b82525bb0a4afa2a9a82bffdd62f55a0c39f2c5 Mon Sep 17 00:00:00 2001 From: Sebastian Kilb Date: Wed, 21 Mar 2018 01:56:49 +0100 Subject: [PATCH 021/130] Fix audio/video linking error on windows Closes https://github.com/centricular/gstwebrtc-demos/issues/5 --- webrtc/sendrecv/gst/webrtc-sendrecv.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/webrtc/sendrecv/gst/webrtc-sendrecv.c b/webrtc/sendrecv/gst/webrtc-sendrecv.c index fcad886ad4..268c898f0a 100644 --- a/webrtc/sendrecv/gst/webrtc-sendrecv.c +++ b/webrtc/sendrecv/gst/webrtc-sendrecv.c @@ -103,7 +103,7 @@ handle_media_stream (GstPad * pad, GstElement * pipe, const char * convert_name, const char * sink_name) { GstPad *qpad; - GstElement *q, *conv, *sink; + GstElement *q, *conv, *resample, *sink; GstPadLinkReturn ret; g_print ("Trying to handle stream with %s ! %s", convert_name, sink_name); @@ -114,11 +114,23 @@ handle_media_stream (GstPad * pad, GstElement * pipe, const char * convert_name, g_assert_nonnull (conv); sink = gst_element_factory_make (sink_name, NULL); g_assert_nonnull (sink); - gst_bin_add_many (GST_BIN (pipe), q, conv, sink, NULL); - gst_element_sync_state_with_parent (q); - gst_element_sync_state_with_parent (conv); - gst_element_sync_state_with_parent (sink); - gst_element_link_many (q, conv, sink, NULL); + + if (g_strcmp0 (convert_name, "audioconvert") == 0) { + resample = gst_element_factory_make ("audioresample", NULL); + g_assert_nonnull (resample); + gst_bin_add_many (GST_BIN (pipe), q, conv, resample, sink, NULL); + gst_element_sync_state_with_parent (q); + gst_element_sync_state_with_parent (conv); + gst_element_sync_state_with_parent (resample); + gst_element_sync_state_with_parent (sink); + gst_element_link_many (q, conv, resample, sink, NULL); + } else { + gst_bin_add_many (GST_BIN (pipe), q, conv, sink, NULL); + gst_element_sync_state_with_parent (q); + gst_element_sync_state_with_parent (conv); + gst_element_sync_state_with_parent (sink); + gst_element_link_many (q, conv, sink, NULL); + } qpad = gst_element_get_static_pad (q, "sink"); From 20cf2503eee220e058a1b11e7a969dc9e85e3618 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Fri, 23 Mar 2018 11:36:40 +0530 Subject: [PATCH 022/130] sendrecv: Fix SDP message format The format is {'sdp': {'sdp': , 'type': }} The multiparty-sendrecv demo already uses this format. --- webrtc/sendrecv/gst/webrtc-sendrecv.c | 26 ++++++++++++++++---------- webrtc/sendrecv/js/webrtc.js | 3 ++- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/webrtc/sendrecv/gst/webrtc-sendrecv.c b/webrtc/sendrecv/gst/webrtc-sendrecv.c index 268c898f0a..ed0c8452cd 100644 --- a/webrtc/sendrecv/gst/webrtc-sendrecv.c +++ b/webrtc/sendrecv/gst/webrtc-sendrecv.c @@ -447,7 +447,7 @@ on_server_message (SoupWebsocketConnection * conn, SoupWebsocketDataType type, /* Look for JSON messages containing SDP and ICE candidates */ } else { JsonNode *root; - JsonObject *object; + JsonObject *object, *child; JsonParser *parser = json_parser_new (); if (!json_parser_load_from_data (parser, text, -1, NULL)) { g_printerr ("Unknown message '%s', ignoring", text); @@ -466,20 +466,27 @@ on_server_message (SoupWebsocketConnection * conn, SoupWebsocketDataType type, /* Check type of JSON message */ if (json_object_has_member (object, "sdp")) { int ret; - const gchar *text; GstSDPMessage *sdp; + const gchar *text, *sdptype; GstWebRTCSessionDescription *answer; g_assert_cmphex (app_state, ==, PEER_CALL_NEGOTIATING); - g_assert_true (json_object_has_member (object, "type")); + child = json_object_get_object_member (object, "sdp"); + + if (!json_object_has_member (child, "type")) { + cleanup_and_quit_loop ("ERROR: received SDP without 'type'", + PEER_CALL_ERROR); + goto out; + } + + sdptype = json_object_get_string_member (child, "type"); /* In this example, we always create the offer and receive one answer. * See tests/examples/webrtcbidirectional.c in gst-plugins-bad for how to * handle offers from peers and reply with answers using webrtcbin. */ - g_assert_cmpstr (json_object_get_string_member (object, "type"), ==, - "answer"); + g_assert_cmpstr (sdptype, ==, "answer"); - text = json_object_get_string_member (object, "sdp"); + text = json_object_get_string_member (child, "sdp"); g_print ("Received answer:\n%s\n", text); @@ -504,13 +511,12 @@ on_server_message (SoupWebsocketConnection * conn, SoupWebsocketDataType type, app_state = PEER_CALL_STARTED; } else if (json_object_has_member (object, "ice")) { - JsonObject *ice; const gchar *candidate; gint sdpmlineindex; - ice = json_object_get_object_member (object, "ice"); - candidate = json_object_get_string_member (ice, "candidate"); - sdpmlineindex = json_object_get_int_member (ice, "sdpMLineIndex"); + child = json_object_get_object_member (object, "ice"); + candidate = json_object_get_string_member (child, "candidate"); + sdpmlineindex = json_object_get_int_member (child, "sdpMLineIndex"); /* Add ice candidate sent by remote peer */ g_signal_emit_by_name (webrtc1, "add-ice-candidate", sdpmlineindex, diff --git a/webrtc/sendrecv/js/webrtc.js b/webrtc/sendrecv/js/webrtc.js index b95df54dcd..bca5d7c798 100644 --- a/webrtc/sendrecv/js/webrtc.js +++ b/webrtc/sendrecv/js/webrtc.js @@ -63,7 +63,8 @@ function onLocalDescription(desc) { console.log("Got local description: " + JSON.stringify(desc)); peer_connection.setLocalDescription(desc).then(function() { setStatus("Sending SDP answer"); - ws_conn.send(JSON.stringify(peer_connection.localDescription)); + sdp = {'sdp': peer_connection.localDescription} + ws_conn.send(JSON.stringify(sdp)); }); } From 2d2bc0fe0e20bdaaf95fb42fed76d873696954c5 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Fri, 23 Mar 2018 12:05:09 +0530 Subject: [PATCH 023/130] Fix compiler warnings in all C demos --- webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c | 10 +++------- webrtc/sendrecv/gst/webrtc-sendrecv.c | 4 +--- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c b/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c index 4a99eb8f3c..b0ab698bc2 100644 --- a/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c +++ b/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c @@ -8,6 +8,7 @@ */ #include #include +#define GST_USE_UNSTABLE_API #include /* For signalling */ @@ -327,7 +328,6 @@ add_peer_to_pipeline (const gchar * peer_id, gboolean offer) gchar *tmp; GstElement *tee, *webrtc, *q; GstPad *srcpad, *sinkpad; - GError *error = NULL; tmp = g_strdup_printf ("queue-%s", peer_id); q = gst_element_factory_make ("queue", tmp); @@ -619,7 +619,7 @@ handle_sdp_offer (const gchar * peer_id, const gchar * text) ret = gst_sdp_message_new (&sdp); g_assert_cmpint (ret, ==, GST_SDP_OK); - ret = gst_sdp_message_parse_buffer (text, strlen (text), sdp); + ret = gst_sdp_message_parse_buffer ((guint8 *) text, strlen (text), sdp); g_assert_cmpint (ret, ==, GST_SDP_OK); offer = gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_OFFER, sdp); @@ -659,7 +659,7 @@ handle_sdp_answer (const gchar * peer_id, const gchar * text) ret = gst_sdp_message_new (&sdp); g_assert_cmpint (ret, ==, GST_SDP_OK); - ret = gst_sdp_message_parse_buffer (text, strlen (text), sdp); + ret = gst_sdp_message_parse_buffer ((guint8 *) text, strlen (text), sdp); g_assert_cmpint (ret, ==, GST_SDP_OK); answer = gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_ANSWER, sdp); @@ -700,10 +700,7 @@ handle_peer_message (const gchar * peer_id, const gchar * msg) object = json_node_get_object (root); /* Check type of JSON message */ if (json_object_has_member (object, "sdp")) { - int ret; - GstSDPMessage *sdp; const gchar *text, *sdp_type; - GstWebRTCSessionDescription *answer; g_assert_cmpint (app_state, >=, ROOM_JOINED); @@ -920,7 +917,6 @@ check_plugins (void) int main (int argc, char *argv[]) { - SoupSession *session; GOptionContext *context; GError *error = NULL; diff --git a/webrtc/sendrecv/gst/webrtc-sendrecv.c b/webrtc/sendrecv/gst/webrtc-sendrecv.c index ed0c8452cd..f48e27a2d1 100644 --- a/webrtc/sendrecv/gst/webrtc-sendrecv.c +++ b/webrtc/sendrecv/gst/webrtc-sendrecv.c @@ -237,7 +237,6 @@ on_offer_created (GstPromise * promise, gpointer user_data) { GstWebRTCSessionDescription *offer = NULL; const GstStructure *reply; - gchar *desc; g_assert_cmphex (app_state, ==, PEER_CALL_NEGOTIATING); @@ -493,7 +492,7 @@ on_server_message (SoupWebsocketConnection * conn, SoupWebsocketDataType type, ret = gst_sdp_message_new (&sdp); g_assert_cmphex (ret, ==, GST_SDP_OK); - ret = gst_sdp_message_parse_buffer (text, strlen (text), sdp); + ret = gst_sdp_message_parse_buffer ((guint8 *) text, strlen (text), sdp); g_assert_cmphex (ret, ==, GST_SDP_OK); answer = gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_ANSWER, @@ -613,7 +612,6 @@ check_plugins (void) int main (int argc, char *argv[]) { - SoupSession *session; GOptionContext *context; GError *error = NULL; From 0e1be2a63f90e8bc73024dc333edcbd3eb4cb1e1 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Fri, 23 Mar 2018 12:10:26 +0530 Subject: [PATCH 024/130] Add Makefiles for all C demos --- webrtc/multiparty-sendrecv/gst/Makefile | 6 ++++++ webrtc/sendrecv/gst/Makefile | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 webrtc/multiparty-sendrecv/gst/Makefile create mode 100644 webrtc/sendrecv/gst/Makefile diff --git a/webrtc/multiparty-sendrecv/gst/Makefile b/webrtc/multiparty-sendrecv/gst/Makefile new file mode 100644 index 0000000000..ee1fef51e4 --- /dev/null +++ b/webrtc/multiparty-sendrecv/gst/Makefile @@ -0,0 +1,6 @@ +CC := gcc +LIBS := $(shell pkg-config --libs --cflags gstreamer-webrtc-1.0 gstreamer-sdp-1.0 libsoup-2.4 json-glib-1.0) +CFLAGS := -O0 -ggdb -Wall -fno-omit-frame-pointer + +mp-webrtc-sendrecv: mp-webrtc-sendrecv.c + "$(CC)" $(CFLAGS) $^ $(LIBS) -o $@ diff --git a/webrtc/sendrecv/gst/Makefile b/webrtc/sendrecv/gst/Makefile new file mode 100644 index 0000000000..9bce613729 --- /dev/null +++ b/webrtc/sendrecv/gst/Makefile @@ -0,0 +1,6 @@ +CC := gcc +LIBS := $(shell pkg-config --libs --cflags gstreamer-webrtc-1.0 gstreamer-sdp-1.0 libsoup-2.4 json-glib-1.0) +CFLAGS := -O0 -ggdb -Wall -fno-omit-frame-pointer + +webrtc-sendrecv: webrtc-sendrecv.c + "$(CC)" $(CFLAGS) $^ $(LIBS) -o $@ From 82314cabbb10fc338adf860e0276d067a76f2d92 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sat, 31 Mar 2018 10:27:05 +0530 Subject: [PATCH 025/130] Don't use strict ssl certificate checking for localhost When using localhost signalling servers, we don't want to use strict ssl because it's probably using a self-signed certificate and there's no need to do certificate checking over localhost anyway. --- webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c | 13 ++++++++++++- webrtc/sendrecv/gst/webrtc-sendrecv.c | 13 ++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c b/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c index b0ab698bc2..413c83298d 100644 --- a/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c +++ b/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c @@ -49,6 +49,7 @@ static const gchar *default_server_url = "wss://webrtc.nirbheek.in:8443"; static gchar *server_url = NULL; static gchar *local_id = NULL; static gchar *room_id = NULL; +static gboolean strict_ssl = TRUE; static GOptionEntry entries[] = { @@ -871,7 +872,7 @@ connect_to_websocket_server_async (void) SoupSession *session; const char *https_aliases[] = {"wss", NULL}; - session = soup_session_new_with_options (SOUP_SESSION_SSL_STRICT, TRUE, + session = soup_session_new_with_options (SOUP_SESSION_SSL_STRICT, strict_ssl, SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE, //SOUP_SESSION_SSL_CA_FILE, "/etc/ssl/certs/ca-bundle.crt", SOUP_SESSION_HTTPS_ALIASES, https_aliases, NULL); @@ -947,6 +948,16 @@ main (int argc, char *argv[]) if (!server_url) server_url = g_strdup (default_server_url); + /* Don't use strict ssl when running a localhost server, because + * it's probably a test server with a self-signed certificate */ + { + GstUri *uri = gst_uri_from_string (server_url); + if (g_strcmp0 ("localhost", gst_uri_get_host (uri)) == 0 || + g_strcmp0 ("127.0.0.1", gst_uri_get_host (uri)) == 0) + strict_ssl = FALSE; + gst_uri_unref (uri); + } + loop = g_main_loop_new (NULL, FALSE); connect_to_websocket_server_async (); diff --git a/webrtc/sendrecv/gst/webrtc-sendrecv.c b/webrtc/sendrecv/gst/webrtc-sendrecv.c index f48e27a2d1..607732e9d9 100644 --- a/webrtc/sendrecv/gst/webrtc-sendrecv.c +++ b/webrtc/sendrecv/gst/webrtc-sendrecv.c @@ -45,6 +45,7 @@ static SoupWebsocketConnection *ws_conn = NULL; static enum AppState app_state = 0; static const gchar *peer_id = NULL; static const gchar *server_url = "wss://webrtc.nirbheek.in:8443"; +static gboolean strict_ssl = TRUE; static GOptionEntry entries[] = { @@ -566,7 +567,7 @@ connect_to_websocket_server_async (void) SoupSession *session; const char *https_aliases[] = {"wss", NULL}; - session = soup_session_new_with_options (SOUP_SESSION_SSL_STRICT, TRUE, + session = soup_session_new_with_options (SOUP_SESSION_SSL_STRICT, strict_ssl, SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE, //SOUP_SESSION_SSL_CA_FILE, "/etc/ssl/certs/ca-bundle.crt", SOUP_SESSION_HTTPS_ALIASES, https_aliases, NULL); @@ -631,6 +632,16 @@ main (int argc, char *argv[]) return -1; } + /* Don't use strict ssl when running a localhost server, because + * it's probably a test server with a self-signed certificate */ + { + GstUri *uri = gst_uri_from_string (server_url); + if (g_strcmp0 ("localhost", gst_uri_get_host (uri)) == 0 || + g_strcmp0 ("127.0.0.1", gst_uri_get_host (uri)) == 0) + strict_ssl = FALSE; + gst_uri_unref (uri); + } + loop = g_main_loop_new (NULL, FALSE); connect_to_websocket_server_async (); From 563826deaf63a156f41bfb0e7dc3fd4a0476d02a Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sat, 31 Mar 2018 10:28:51 +0530 Subject: [PATCH 026/130] sendrecv: Don't set pipeline state if it's NULL Avoids ugly CRITICAL warnings when erroring out. --- webrtc/sendrecv/gst/webrtc-sendrecv.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/webrtc/sendrecv/gst/webrtc-sendrecv.c b/webrtc/sendrecv/gst/webrtc-sendrecv.c index 607732e9d9..6ccefaa1f5 100644 --- a/webrtc/sendrecv/gst/webrtc-sendrecv.c +++ b/webrtc/sendrecv/gst/webrtc-sendrecv.c @@ -647,11 +647,13 @@ main (int argc, char *argv[]) connect_to_websocket_server_async (); g_main_loop_run (loop); + g_main_loop_unref (loop); - gst_element_set_state (GST_ELEMENT (pipe1), GST_STATE_NULL); - g_print ("Pipeline stopped\n"); - - gst_object_unref (pipe1); + if (pipe1) { + gst_element_set_state (GST_ELEMENT (pipe1), GST_STATE_NULL); + g_print ("Pipeline stopped\n"); + gst_object_unref (pipe1); + } return 0; } From bd6deaca46e48a0a7726031f474d1f71e8bf6723 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sat, 31 Mar 2018 13:52:02 +0530 Subject: [PATCH 027/130] sendrecv/js: Call getUserMedia on incoming call Instead of registering it on page load. This will allow us to add an option for users to override the default constraints later. This is also generally nicer because the browser won't open the webcam immediately when you load the page and keep recording from it. --- webrtc/sendrecv/js/webrtc.js | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/webrtc/sendrecv/js/webrtc.js b/webrtc/sendrecv/js/webrtc.js index bca5d7c798..865fcac6d2 100644 --- a/webrtc/sendrecv/js/webrtc.js +++ b/webrtc/sendrecv/js/webrtc.js @@ -13,6 +13,7 @@ var peer_connection = null; var rtc_configuration = {iceServers: [{urls: "stun:stun.services.mozilla.com"}, {urls: "stun:stun.l.google.com:19302"}]}; var ws_conn; +// Promise for local stream after constraints are approved by the user var local_stream; var peer_id; @@ -54,7 +55,10 @@ function onIncomingSDP(sdp) { if (sdp.type != "offer") return; setStatus("Got SDP offer, creating answer"); - peer_connection.createAnswer().then(onLocalDescription).catch(setStatus); + local_stream.then((stream) => { + peer_connection.createAnswer() + .then(onLocalDescription).catch(setStatus); + }).catch(errorUserMediaHandler); }).catch(setStatus); } @@ -130,6 +134,17 @@ function onServerError(event) { window.setTimeout(websocketServerConnect, 3000); } +function getLocalStream() { + var constraints = {video: true, audio: true}; + + // Add local stream + if (navigator.mediaDevices.getUserMedia) { + return navigator.mediaDevices.getUserMedia(constraints); + } else { + errorUserMediaHandler(); + } +} + function websocketServerConnect() { connect_attempts++; if (connect_attempts > 3) { @@ -156,17 +171,6 @@ function websocketServerConnect() { ws_conn.addEventListener('error', onServerError); ws_conn.addEventListener('message', onServerMessage); ws_conn.addEventListener('close', onServerClose); - - var constraints = {video: true, audio: true}; - - // Add local stream - if (navigator.mediaDevices.getUserMedia) { - navigator.mediaDevices.getUserMedia(constraints) - .then((stream) => { local_stream = stream }) - .catch(errorUserMediaHandler); - } else { - errorUserMediaHandler(); - } } function onRemoteStreamAdded(event) { @@ -192,7 +196,10 @@ function createCall(msg) { peer_connection = new RTCPeerConnection(rtc_configuration); peer_connection.onaddstream = onRemoteStreamAdded; /* Send our video/audio to the other peer */ - peer_connection.addStream(local_stream); + local_stream = getLocalStream().then((stream) => { + console.log('Adding local stream'); + peer_connection.addStream(stream); + }).catch(errorUserMediaHandler); if (!msg.sdp) { console.log("WARNING: First message wasn't an SDP message!?"); From 3eabe5cb0b6bd99cba3563444b2fa1fb14f6cb9e Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sat, 31 Mar 2018 22:24:15 +0530 Subject: [PATCH 028/130] sendrecv/js: Make error statuses more prominent Colour errors in red, and ensure that later status updates don't overwrite existing error statuses. --- webrtc/sendrecv/js/index.html | 3 +++ webrtc/sendrecv/js/webrtc.js | 35 ++++++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/webrtc/sendrecv/js/index.html b/webrtc/sendrecv/js/index.html index 0a0b7fec8a..1744715265 100644 --- a/webrtc/sendrecv/js/index.html +++ b/webrtc/sendrecv/js/index.html @@ -11,6 +11,9 @@ --> + \n \ + \n \ + \n \ + \n \ + \n \ +
\n \ + \n \ +
\n \ + \n \ + \n \ +"; + + + + +ReceiverEntry * +create_receiver_entry (SoupWebsocketConnection * connection) +{ + GError *error; + ReceiverEntry *receiver_entry; + GstWebRTCRTPTransceiver *trans; + GArray *transceivers; + + receiver_entry = g_slice_alloc0 (sizeof (ReceiverEntry)); + receiver_entry->connection = connection; + + g_object_ref (G_OBJECT (connection)); + + g_signal_connect (G_OBJECT (connection), "message", + G_CALLBACK (soup_websocket_message_cb), (gpointer) receiver_entry); + + error = NULL; + receiver_entry->pipeline = gst_parse_launch ("webrtcbin name=webrtcbin stun-server=stun://" STUN_SERVER " " + "v4l2src ! videorate ! video/x-raw,width=640,height=360,framerate=15/1 ! videoconvert ! queue max-size-buffers=1 ! x264enc bitrate=600 speed-preset=ultrafast tune=zerolatency key-int-max=15 ! video/x-h264,profile=constrained-baseline ! queue max-size-time=100000000 ! h264parse ! " + "rtph264pay config-interval=-1 name=payloader ! " + "application/x-rtp,media=video,encoding-name=H264,payload=" + RTP_PAYLOAD_TYPE " ! webrtcbin. ", &error); + if (error != NULL) { + g_error ("Could not create WebRTC pipeline: %s\n", error->message); + g_error_free (error); + goto cleanup; + } + + receiver_entry->webrtcbin = + gst_bin_get_by_name (GST_BIN (receiver_entry->pipeline), "webrtcbin"); + g_assert (receiver_entry->webrtcbin != NULL); + + g_signal_emit_by_name (receiver_entry->webrtcbin, "get-transceivers", &transceivers); + g_assert (transceivers != NULL && transceivers->len > 0); + trans = g_array_index (transceivers, GstWebRTCRTPTransceiver *, 0); + trans->direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY; + + g_signal_connect (receiver_entry->webrtcbin, "on-negotiation-needed", + G_CALLBACK (on_negotiation_needed_cb), (gpointer) receiver_entry); + + g_signal_connect (receiver_entry->webrtcbin, "on-ice-candidate", + G_CALLBACK (on_ice_candidate_cb), (gpointer) receiver_entry); + + gst_element_set_state (receiver_entry->pipeline, GST_STATE_PLAYING); + + return receiver_entry; + +cleanup: + destroy_receiver_entry ((gpointer) receiver_entry); + return NULL; +} + +void +destroy_receiver_entry (gpointer receiver_entry_ptr) +{ + ReceiverEntry *receiver_entry = (ReceiverEntry *) receiver_entry_ptr; + + g_assert (receiver_entry != NULL); + + if (receiver_entry->pipeline != NULL) { + gst_element_set_state (GST_ELEMENT (receiver_entry->pipeline), + GST_STATE_NULL); + + gst_object_unref (GST_OBJECT (receiver_entry->webrtcbin)); + gst_object_unref (GST_OBJECT (receiver_entry->pipeline)); + } + + if (receiver_entry->connection != NULL) + g_object_unref (G_OBJECT (receiver_entry->connection)); + + g_slice_free1 (sizeof (ReceiverEntry), receiver_entry); +} + + +void +on_offer_created_cb (GstPromise * promise, gpointer user_data) +{ + gchar *sdp_string; + gchar *json_string; + JsonObject *sdp_json; + JsonObject *sdp_data_json; + GstStructure const *reply; + GstPromise *local_desc_promise; + GstWebRTCSessionDescription *offer = NULL; + ReceiverEntry *receiver_entry = (ReceiverEntry *) user_data; + + reply = gst_promise_get_reply (promise); + gst_structure_get (reply, "offer", GST_TYPE_WEBRTC_SESSION_DESCRIPTION, + &offer, NULL); + gst_promise_unref (promise); + + local_desc_promise = gst_promise_new (); + g_signal_emit_by_name (receiver_entry->webrtcbin, "set-local-description", + offer, local_desc_promise); + gst_promise_interrupt (local_desc_promise); + gst_promise_unref (local_desc_promise); + + sdp_string = gst_sdp_message_as_text (offer->sdp); + g_print ("Negotiation offer created:\n%s\n", sdp_string); + + sdp_json = json_object_new (); + json_object_set_string_member (sdp_json, "type", "sdp"); + + sdp_data_json = json_object_new (); + json_object_set_string_member (sdp_data_json, "type", "offer"); + json_object_set_string_member (sdp_data_json, "sdp", sdp_string); + json_object_set_object_member (sdp_json, "data", sdp_data_json); + + json_string = get_string_from_json_object (sdp_json); + json_object_unref (sdp_json); + + soup_websocket_connection_send_text (receiver_entry->connection, json_string); + g_free (json_string); + g_free (sdp_string); + + gst_webrtc_session_description_free (offer); +} + + +void +on_negotiation_needed_cb (GstElement * webrtcbin, gpointer user_data) +{ + GstPromise *promise; + ReceiverEntry *receiver_entry = (ReceiverEntry *) user_data; + + g_print ("Creating negotiation offer\n"); + + promise = gst_promise_new_with_change_func (on_offer_created_cb, + (gpointer) receiver_entry, NULL); + g_signal_emit_by_name (G_OBJECT (webrtcbin), "create-offer", NULL, promise); +} + + +void +on_ice_candidate_cb (G_GNUC_UNUSED GstElement * webrtcbin, guint mline_index, + gchar * candidate, gpointer user_data) +{ + JsonObject *ice_json; + JsonObject *ice_data_json; + gchar *json_string; + ReceiverEntry *receiver_entry = (ReceiverEntry *) user_data; + + ice_json = json_object_new (); + json_object_set_string_member (ice_json, "type", "ice"); + + ice_data_json = json_object_new (); + json_object_set_int_member (ice_data_json, "sdpMLineIndex", mline_index); + json_object_set_string_member (ice_data_json, "candidate", candidate); + json_object_set_object_member (ice_json, "data", ice_data_json); + + json_string = get_string_from_json_object (ice_json); + json_object_unref (ice_json); + + soup_websocket_connection_send_text (receiver_entry->connection, json_string); + g_free (json_string); +} + + +void +soup_websocket_message_cb (G_GNUC_UNUSED SoupWebsocketConnection * connection, + SoupWebsocketDataType data_type, GBytes * message, gpointer user_data) +{ + gsize size; + gchar *data; + gchar *data_string; + const gchar *type_string; + JsonNode *root_json; + JsonObject *root_json_object; + JsonObject *data_json_object; + JsonParser *json_parser = NULL; + ReceiverEntry *receiver_entry = (ReceiverEntry *) user_data; + + switch (data_type) { + case SOUP_WEBSOCKET_DATA_BINARY: + g_error ("Received unknown binary message, ignoring\n"); + g_bytes_unref (message); + return; + + case SOUP_WEBSOCKET_DATA_TEXT: + data = g_bytes_unref_to_data (message, &size); + /* Convert to NULL-terminated string */ + data_string = g_strndup (data, size); + g_free (data); + break; + + default: + g_assert_not_reached (); + } + + json_parser = json_parser_new (); + if (!json_parser_load_from_data (json_parser, data_string, -1, NULL)) + goto unknown_message; + + root_json = json_parser_get_root (json_parser); + if (!JSON_NODE_HOLDS_OBJECT (root_json)) + goto unknown_message; + + root_json_object = json_node_get_object (root_json); + + if (!json_object_has_member (root_json_object, "type")) { + g_error ("Received message without type field\n"); + goto cleanup; + } + type_string = json_object_get_string_member (root_json_object, "type"); + + if (!json_object_has_member (root_json_object, "data")) { + g_error ("Received message without data field\n"); + goto cleanup; + } + data_json_object = json_object_get_object_member (root_json_object, "data"); + + if (g_strcmp0 (type_string, "sdp") == 0) { + const gchar *sdp_type_string; + const gchar *sdp_string; + GstPromise *promise; + GstSDPMessage *sdp; + GstWebRTCSessionDescription *answer; + int ret; + + if (!json_object_has_member (data_json_object, "type")) { + g_error ("Received SDP message without type field\n"); + goto cleanup; + } + sdp_type_string = json_object_get_string_member (data_json_object, "type"); + + if (g_strcmp0 (sdp_type_string, "answer") != 0) { + g_error ("Expected SDP message type \"answer\", got \"%s\"\n", + sdp_type_string); + goto cleanup; + } + + if (!json_object_has_member (data_json_object, "sdp")) { + g_error ("Received SDP message without SDP string\n"); + goto cleanup; + } + sdp_string = json_object_get_string_member (data_json_object, "sdp"); + + g_print ("Received SDP:\n%s\n", sdp_string); + + ret = gst_sdp_message_new (&sdp); + g_assert_cmphex (ret, ==, GST_SDP_OK); + + ret = + gst_sdp_message_parse_buffer ((guint8 *) sdp_string, + strlen (sdp_string), sdp); + if (ret != GST_SDP_OK) { + g_error ("Could not parse SDP string\n"); + goto cleanup; + } + + answer = gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_ANSWER, + sdp); + g_assert_nonnull (answer); + + promise = gst_promise_new (); + g_signal_emit_by_name (receiver_entry->webrtcbin, "set-remote-description", + answer, promise); + gst_promise_interrupt (promise); + gst_promise_unref (promise); + gst_webrtc_session_description_free (answer); + } else if (g_strcmp0 (type_string, "ice") == 0) { + guint mline_index; + const gchar *candidate_string; + + if (!json_object_has_member (data_json_object, "sdpMLineIndex")) { + g_error ("Received ICE message without mline index\n"); + goto cleanup; + } + mline_index = + json_object_get_int_member (data_json_object, "sdpMLineIndex"); + + if (!json_object_has_member (data_json_object, "candidate")) { + g_error ("Received ICE message without ICE candidate string\n"); + goto cleanup; + } + candidate_string = json_object_get_string_member (data_json_object, + "candidate"); + + g_print ("Received ICE candidate with mline index %u; candidate: %s\n", + mline_index, candidate_string); + + g_signal_emit_by_name (receiver_entry->webrtcbin, "add-ice-candidate", + mline_index, candidate_string); + } else + goto unknown_message; + +cleanup: + if (json_parser != NULL) + g_object_unref (G_OBJECT (json_parser)); + g_free (data_string); + return; + +unknown_message: + g_error ("Unknown message \"%s\", ignoring", data_string); + goto cleanup; +} + + +void +soup_websocket_closed_cb (SoupWebsocketConnection * connection, + gpointer user_data) +{ + GHashTable *receiver_entry_table = (GHashTable *) user_data; + g_hash_table_remove (receiver_entry_table, connection); + g_print ("Closed websocket connection %p\n", (gpointer) connection); +} + + +void +soup_http_handler (G_GNUC_UNUSED SoupServer * soup_server, + SoupMessage * message, const char *path, G_GNUC_UNUSED GHashTable * query, + G_GNUC_UNUSED SoupClientContext * client_context, + G_GNUC_UNUSED gpointer user_data) +{ + SoupBuffer *soup_buffer; + + if ((g_strcmp0 (path, "/") != 0) && (g_strcmp0 (path, "/index.html") != 0)) { + soup_message_set_status (message, SOUP_STATUS_NOT_FOUND); + return; + } + + soup_buffer = + soup_buffer_new (SOUP_MEMORY_STATIC, html_source, strlen (html_source)); + + soup_message_headers_set_content_type (message->response_headers, "text/html", + NULL); + soup_message_body_append_buffer (message->response_body, soup_buffer); + soup_buffer_free (soup_buffer); + + soup_message_set_status (message, SOUP_STATUS_OK); +} + + +void +soup_websocket_handler (G_GNUC_UNUSED SoupServer * server, + SoupWebsocketConnection * connection, G_GNUC_UNUSED const char *path, + G_GNUC_UNUSED SoupClientContext * client_context, gpointer user_data) +{ + ReceiverEntry *receiver_entry; + GHashTable *receiver_entry_table = (GHashTable *) user_data; + + g_print ("Processing new websocket connection %p", (gpointer) connection); + + g_signal_connect (G_OBJECT (connection), "closed", + G_CALLBACK (soup_websocket_closed_cb), (gpointer) receiver_entry_table); + + receiver_entry = create_receiver_entry (connection); + g_hash_table_replace (receiver_entry_table, connection, receiver_entry); +} + + +static gchar * +get_string_from_json_object (JsonObject * object) +{ + JsonNode *root; + JsonGenerator *generator; + gchar *text; + + /* Make it the root node */ + root = json_node_init_object (json_node_alloc (), object); + generator = json_generator_new (); + json_generator_set_root (generator, root); + text = json_generator_to_data (generator, NULL); + + /* Release everything */ + g_object_unref (generator); + json_node_free (root); + return text; +} + + +gboolean +exit_sighandler (gpointer user_data) +{ + g_print ("Caught signal, stopping mainloop\n"); + GMainLoop *mainloop = (GMainLoop *) user_data; + g_main_loop_quit (mainloop); + return TRUE; +} + + +int +main (int argc, char *argv[]) +{ + GMainLoop *mainloop; + SoupServer *soup_server; + GHashTable *receiver_entry_table; + + setlocale (LC_ALL, ""); + gst_init (&argc, &argv); + + receiver_entry_table = + g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, + destroy_receiver_entry); + + mainloop = g_main_loop_new (NULL, FALSE); + g_assert (mainloop != NULL); + + g_unix_signal_add (SIGINT, exit_sighandler, mainloop); + g_unix_signal_add (SIGTERM, exit_sighandler, mainloop); + + soup_server = + soup_server_new (SOUP_SERVER_SERVER_HEADER, "webrtc-soup-server", NULL); + soup_server_add_handler (soup_server, "/", soup_http_handler, NULL, NULL); + soup_server_add_websocket_handler (soup_server, "/ws", NULL, NULL, + soup_websocket_handler, (gpointer) receiver_entry_table, NULL); + soup_server_listen_all (soup_server, SOUP_HTTP_PORT, + (SoupServerListenOptions) 0, NULL); + + g_print ("WebRTC page link: http://127.0.0.1:%d/\n", (gint) SOUP_HTTP_PORT); + + g_main_loop_run (mainloop); + + g_object_unref (G_OBJECT (soup_server)); + g_hash_table_destroy (receiver_entry_table); + g_main_loop_unref (mainloop); + + gst_deinit (); + + return 0; +} From 3cabee61c79c4f8321cf718770fff5e06a0b7c91 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Wed, 15 Jan 2020 10:47:27 +1100 Subject: [PATCH 097/130] Add python Janus videoroom streaming example. Added with permission and copyright @tobiasfriden and @saket424 on github. See https://github.com/centricular/gstwebrtc-demos/issues/66 --- webrtc/janus/janusvideoroom.py | 443 +++++++++++++++++++++++++++++++++ 1 file changed, 443 insertions(+) create mode 100644 webrtc/janus/janusvideoroom.py diff --git a/webrtc/janus/janusvideoroom.py b/webrtc/janus/janusvideoroom.py new file mode 100644 index 0000000000..80ee47fef5 --- /dev/null +++ b/webrtc/janus/janusvideoroom.py @@ -0,0 +1,443 @@ +# Janus Videoroom example +# Copyright @tobiasfriden and @saket424 on github +# See https://github.com/centricular/gstwebrtc-demos/issues/66 +# Copyright Jan Schmidt 2020 +import random +import ssl +import websockets +import asyncio +import os +import sys +import json +import argparse +import string +from websockets.exceptions import ConnectionClosed + +import attr + +@attr.s +class JanusEvent: + sender = attr.ib(validator=attr.validators.instance_of(int)) + +@attr.s +class PluginData(JanusEvent): + plugin = attr.ib(validator=attr.validators.instance_of(str)) + data = attr.ib() + jsep = attr.ib() + +@attr.s +class WebrtcUp(JanusEvent): + pass + +@attr.s +class Media(JanusEvent): + receiving = attr.ib(validator=attr.validators.instance_of(bool)) + kind = attr.ib(validator=attr.validators.in_(["audio", "video"])) + + @kind.validator + def validate_kind(self, attribute, kind): + if kind not in ["video", "audio"]: + raise ValueError("kind must equal video or audio") + +@attr.s +class SlowLink(JanusEvent): + uplink = attr.ib(validator=attr.validators.instance_of(bool)) + lost = attr.ib(validator=attr.validators.instance_of(int)) + +@attr.s +class HangUp(JanusEvent): + reason = attr.ib(validator=attr.validators.instance_of(str)) + +@attr.s(cmp=False) +class Ack: + transaction = attr.ib(validator=attr.validators.instance_of(str)) + +@attr.s +class Jsep: + sdp = attr.ib() + type = attr.ib(validator=attr.validators.in_(["offer", "pranswer", "answer", "rollback"])) + + +import gi +gi.require_version('Gst', '1.0') +from gi.repository import Gst +gi.require_version('GstWebRTC', '1.0') +from gi.repository import GstWebRTC +gi.require_version('GstSdp', '1.0') +from gi.repository import GstSdp + +DO_VP8 = True + +if DO_VP8: + ( encoder, payloader, rtp_encoding) = ( "vp8enc target-bitrate=500000", "rtpvp8pay", "VP8" ) +else: + ( encoder, payloader, rtp_encoding) = ( "x264enc", "rtph264pay", "H264" ) + +PIPELINE_DESC = ''' + webrtcbin name=sendrecv stun-server=stun://stun.l.google.com:19302 + videotestsrc pattern=ball ! video/x-raw,width=320,height=240 ! videoconvert ! queue ! + {} ! {} ! queue ! application/x-rtp,media=video,encoding-name={},payload=96 ! sendrecv. +'''.format(encoder, payloader, rtp_encoding) + +def transaction_id(): + return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) + +@attr.s +class JanusGateway: + server = attr.ib(validator=attr.validators.instance_of(str)) + #secure = attr.ib(default=True) + _messages = attr.ib(factory=set) + conn = None + + async def connect(self): + sslCon=None + if self.server.startswith("wss"): + sslCon=ssl.SSLContext() + self.conn = await websockets.connect(self.server, subprotocols=['janus-protocol'], ssl=sslCon) + transaction = transaction_id() + await self.conn.send(json.dumps({ + "janus": "create", + "transaction": transaction + })) + resp = await self.conn.recv() + print (resp) + parsed = json.loads(resp) + assert parsed["janus"] == "success", "Failed creating session" + assert parsed["transaction"] == transaction, "Incorrect transaction" + self.session = parsed["data"]["id"] + + async def close(self): + if self.conn: + await self.conn.close() + + async def attach(self, plugin): + assert hasattr(self, "session"), "Must connect before attaching to plugin" + transaction = transaction_id() + await self.conn.send(json.dumps({ + "janus": "attach", + "session_id": self.session, + "plugin": plugin, + "transaction": transaction + })) + resp = await self.conn.recv() + parsed = json.loads(resp) + assert parsed["janus"] == "success", "Failed attaching to {}".format(plugin) + assert parsed["transaction"] == transaction, "Incorrect transaction" + self.handle = parsed["data"]["id"] + + async def sendtrickle(self, candidate): + assert hasattr(self, "session"), "Must connect before sending messages" + assert hasattr(self, "handle"), "Must attach before sending messages" + + transaction = transaction_id() + janus_message = { + "janus": "trickle", + "session_id": self.session, + "handle_id": self.handle, + "transaction": transaction, + "candidate": candidate + } + + await self.conn.send(json.dumps(janus_message)) + + #while True: + # resp = await self._recv_and_parse() + # if isinstance(resp, PluginData): + # return resp + # else: + # self._messages.add(resp) +# + async def sendmessage(self, body, jsep=None): + assert hasattr(self, "session"), "Must connect before sending messages" + assert hasattr(self, "handle"), "Must attach before sending messages" + + transaction = transaction_id() + janus_message = { + "janus": "message", + "session_id": self.session, + "handle_id": self.handle, + "transaction": transaction, + "body": body + } + if jsep is not None: + janus_message["jsep"] = jsep + + await self.conn.send(json.dumps(janus_message)) + + #while True: + # resp = await self._recv_and_parse() + # if isinstance(resp, PluginData): + # if jsep is not None: + # await client.handle_sdp(resp.jsep) + # return resp + # else: + # self._messages.add(resp) + + async def keepalive(self): + assert hasattr(self, "session"), "Must connect before sending messages" + assert hasattr(self, "handle"), "Must attach before sending messages" + + while True: + try: + await asyncio.sleep(10) + transaction = transaction_id() + await self.conn.send(json.dumps({ + "janus": "keepalive", + "session_id": self.session, + "handle_id": self.handle, + "transaction": transaction + })) + except KeyboardInterrupt: + return + + async def recv(self): + if len(self._messages) > 0: + return self._messages.pop() + else: + return await self._recv_and_parse() + + async def _recv_and_parse(self): + raw = json.loads(await self.conn.recv()) + janus = raw["janus"] + + if janus == "event": + return PluginData( + sender=raw["sender"], + plugin=raw["plugindata"]["plugin"], + data=raw["plugindata"]["data"], + jsep=raw["jsep"] if "jsep" in raw else None + ) + elif janus == "webrtcup": + return WebrtcUp( + sender=raw["sender"] + ) + elif janus == "media": + return Media( + sender=raw["sender"], + receiving=raw["receiving"], + kind=raw["type"] + ) + elif janus == "slowlink": + return SlowLink( + sender=raw["sender"], + uplink=raw["uplink"], + lost=raw["lost"] + ) + elif janus == "hangup": + return HangUp( + sender=raw["sender"], + reason=raw["reason"] + ) + elif janus == "ack": + return Ack( + transaction=raw["transaction"] + ) + else: + return raw + +class WebRTCClient: + def __init__(self, id_, peer_id, server, signaling): + self.id_ = id_ + self.conn = None + self.pipe = None + self.webrtc = None + self.peer_id = peer_id + self.server = server or 'wss://127.0.0.1:8989' + self.signaling = signaling + self.request = None + self.offermsg = None + + def send_sdp_offer(self, offer): + text = offer.sdp.as_text() + print ('Sending offer:\n%s' % text) + # configure media + media = {'audio': True, 'video': True} + request = {'request': 'publish'} + request.update(media) + self.request = request + self.offermsg = { 'sdp': text, 'trickle': True, 'type': 'offer' } + print (self.offermsg) + loop = asyncio.new_event_loop() + loop.run_until_complete(self.signaling.sendmessage(self.request, self.offermsg)) + + def on_offer_created(self, promise, _, __): + promise.wait() + reply = promise.get_reply() + offer = reply.get_value('offer') + promise = Gst.Promise.new() + self.webrtc.emit('set-local-description', offer, promise) + promise.interrupt() + self.send_sdp_offer(offer) + + def on_negotiation_needed(self, element): + promise = Gst.Promise.new_with_change_func(self.on_offer_created, element, None) + element.emit('create-offer', None, promise) + + def send_ice_candidate_message(self, _, mlineindex, candidate): + icemsg = {'candidate': candidate, 'sdpMLineIndex': mlineindex} + print ("Sending ICE", icemsg) + loop = asyncio.new_event_loop() + loop.run_until_complete(self.signaling.sendtrickle(icemsg)) + + def on_incoming_decodebin_stream(self, _, pad): + if not pad.has_current_caps(): + print (pad, 'has no caps, ignoring') + return + + caps = pad.get_current_caps() + name = caps.to_string() + if name.startswith('video'): + q = Gst.ElementFactory.make('queue') + conv = Gst.ElementFactory.make('videoconvert') + sink = Gst.ElementFactory.make('autovideosink') + self.pipe.add(q) + self.pipe.add(conv) + self.pipe.add(sink) + self.pipe.sync_children_states() + pad.link(q.get_static_pad('sink')) + q.link(conv) + conv.link(sink) + elif name.startswith('audio'): + q = Gst.ElementFactory.make('queue') + conv = Gst.ElementFactory.make('audioconvert') + resample = Gst.ElementFactory.make('audioresample') + sink = Gst.ElementFactory.make('autoaudiosink') + self.pipe.add(q) + self.pipe.add(conv) + self.pipe.add(resample) + self.pipe.add(sink) + self.pipe.sync_children_states() + pad.link(q.get_static_pad('sink')) + q.link(conv) + conv.link(resample) + resample.link(sink) + + def on_incoming_stream(self, _, pad): + if pad.direction != Gst.PadDirection.SRC: + return + + decodebin = Gst.ElementFactory.make('decodebin') + decodebin.connect('pad-added', self.on_incoming_decodebin_stream) + self.pipe.add(decodebin) + decodebin.sync_state_with_parent() + self.webrtc.link(decodebin) + + def start_pipeline(self): + self.pipe = Gst.parse_launch(PIPELINE_DESC) + self.webrtc = self.pipe.get_by_name('sendrecv') + self.webrtc.connect('on-negotiation-needed', self.on_negotiation_needed) + self.webrtc.connect('on-ice-candidate', self.send_ice_candidate_message) + self.webrtc.connect('pad-added', self.on_incoming_stream) + self.pipe.set_state(Gst.State.PLAYING) + + def extract_ice_from_sdp(self, sdp): + mlineindex = -1 + for line in sdp.splitlines(): + if line.startswith("a=candidate"): + candidate = line[2:] + if mlineindex < 0: + print("Received ice candidate in SDP before any m= line") + continue + print ('Received remote ice-candidate mlineindex {}: {}'.format(mlineindex, candidate)) + self.webrtc.emit('add-ice-candidate', mlineindex, candidate) + elif line.startswith("m="): + mlineindex += 1 + + async def handle_sdp(self, msg): + print (msg) + if 'sdp' in msg: + sdp = msg['sdp'] + assert(msg['type'] == 'answer') + print ('Received answer:\n%s' % sdp) + res, sdpmsg = GstSdp.SDPMessage.new() + GstSdp.sdp_message_parse_buffer(bytes(sdp.encode()), sdpmsg) + + answer = GstWebRTC.WebRTCSessionDescription.new(GstWebRTC.WebRTCSDPType.ANSWER, sdpmsg) + promise = Gst.Promise.new() + self.webrtc.emit('set-remote-description', answer, promise) + promise.interrupt() + + # Extract ICE candidates from the SDP to work around a GStreamer + # limitation in (at least) 1.16.2 and below + self.extract_ice_from_sdp (sdp) + + elif 'ice' in msg: + ice = msg['ice'] + candidate = ice['candidate'] + sdpmlineindex = ice['sdpMLineIndex'] + self.webrtc.emit('add-ice-candidate', sdpmlineindex, candidate) + + async def loop(self, signaling): + await signaling.connect() + await signaling.attach("janus.plugin.videoroom") + + loop = asyncio.get_event_loop() + loop.create_task(signaling.keepalive()) + #asyncio.create_task(self.keepalive()) + + joinmessage = { "request": "join", "ptype": "publisher", "room": 1234, "display": self.peer_id } + await signaling.sendmessage(joinmessage) + + assert signaling.conn + self.start_pipeline() + + while True: + try: + msg = await signaling.recv() + if isinstance(msg, PluginData): + if msg.jsep is not None: + await self.handle_sdp(msg.jsep) + elif isinstance(msg, Media): + print (msg) + elif isinstance(msg, WebrtcUp): + print (msg) + elif isinstance(msg, SlowLink): + print (msg) + elif isinstance(msg, HangUp): + print (msg) + elif not isinstance(msg, Ack): + if 'candidate' in msg: + ice = msg['candidate'] + print (ice) + if 'candidate' in ice: + candidate = ice['candidate'] + sdpmlineindex = ice['sdpMLineIndex'] + self.webrtc.emit('add-ice-candidate', sdpmlineindex, candidate) + print(msg) + except (KeyboardInterrupt, ConnectionClosed): + return + + return 0 + + +def check_plugins(): + needed = ["opus", "vpx", "nice", "webrtc", "dtls", "srtp", "rtp", + "rtpmanager", "videotestsrc", "audiotestsrc"] + missing = list(filter(lambda p: Gst.Registry.get().find_plugin(p) is None, needed)) + if len(missing): + print('Missing gstreamer plugins:', missing) + return False + return True + + +if __name__=='__main__': + Gst.init(None) + if not check_plugins(): + sys.exit(1) + parser = argparse.ArgumentParser() + parser.add_argument('label', help='videoroom label') + parser.add_argument('--server', help='Signalling server to connect to, eg "wss://127.0.0.1:8989"') + args = parser.parse_args() + our_id = random.randrange(10, 10000) + signaling = JanusGateway(args.server) + c = WebRTCClient(our_id, args.label, args.server, signaling) + loop = asyncio.get_event_loop() + try: + loop.run_until_complete( + c.loop(signaling) + ) + except KeyboardInterrupt: + pass + finally: + print("Interrupted, cleaning up") + loop.run_until_complete(signaling.close()) From 42c6eac7f1b3227c9a3a91851840fd7270bce4c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 23 Jan 2020 08:35:25 +0200 Subject: [PATCH 098/130] Update dependencies of Rust examples and simplify slightly --- .../multiparty-sendrecv/gst-rust/Cargo.lock | 250 +++++++++--------- .../multiparty-sendrecv/gst-rust/src/main.rs | 10 +- webrtc/sendrecv/gst-rust/Cargo.lock | 137 +++++----- webrtc/sendrecv/gst-rust/src/main.rs | 10 +- 4 files changed, 208 insertions(+), 199 deletions(-) diff --git a/webrtc/multiparty-sendrecv/gst-rust/Cargo.lock b/webrtc/multiparty-sendrecv/gst-rust/Cargo.lock index 053bacdf35..fb1183fdec 100644 --- a/webrtc/multiparty-sendrecv/gst-rust/Cargo.lock +++ b/webrtc/multiparty-sendrecv/gst-rust/Cargo.lock @@ -7,7 +7,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "async-native-tls" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "async-std 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -20,7 +20,7 @@ name = "async-std" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "async-task 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "async-task 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -29,34 +29,35 @@ dependencies = [ "futures-timer 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "kv-log-macro 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project-lite 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "async-task" -version = "1.1.1" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "async-tungstenite" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "async-native-tls 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "async-native-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "async-std 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "tungstenite 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -65,6 +66,11 @@ name = "autocfg" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "base64" version = "0.11.0" @@ -123,7 +129,7 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.48" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -289,9 +295,9 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -320,7 +326,7 @@ dependencies = [ "futures-macro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -337,17 +343,17 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "glib" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -383,20 +389,22 @@ dependencies = [ [[package]] name = "gstreamer" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "glib 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "muldiv 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-rational 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -405,10 +413,10 @@ name = "gstreamer-sdp" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "glib 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gstreamer 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer-sdp-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -441,10 +449,10 @@ name = "gstreamer-webrtc" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "glib 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gstreamer 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer-sdp 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer-webrtc-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -502,7 +510,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -568,7 +576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "memchr" -version = "2.2.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -652,34 +660,34 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.41" +version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-rational" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-traits" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num_cpus" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -688,7 +696,7 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -720,7 +728,7 @@ version = "0.9.53" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -741,9 +749,9 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -753,25 +761,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "pin-project" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "pin-project-internal 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project-internal 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pin-project-internal" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pin-project-lite" -version = "0.1.1" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -791,25 +799,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "proc-macro-error" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro-error-attr 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-error-attr 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "rustversion 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "proc-macro-error-attr" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "rustversion 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", "syn-mid 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -818,9 +826,9 @@ name = "proc-macro-hack" version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -830,7 +838,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "proc-macro2" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -841,15 +849,15 @@ name = "quote" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -870,7 +878,7 @@ name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -907,9 +915,9 @@ name = "rustversion" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -973,14 +981,14 @@ name = "serde_derive" version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "1.0.44" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -990,7 +998,7 @@ dependencies = [ [[package]] name = "sha-1" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1011,31 +1019,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "structopt" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt-derive 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt-derive 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "structopt-derive" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-error 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-error 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "syn" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1045,9 +1054,9 @@ name = "syn-mid" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1057,7 +1066,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1084,9 +1093,9 @@ name = "thiserror-impl" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1101,9 +1110,9 @@ dependencies = [ "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "input_buffer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "utf-8 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1122,7 +1131,7 @@ dependencies = [ [[package]] name = "unicode-normalization" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1145,7 +1154,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "url" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1165,7 +1174,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "wasi" -version = "0.7.0" +version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1174,17 +1183,17 @@ version = "0.1.0" dependencies = [ "anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", "async-std 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "async-tungstenite 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "async-tungstenite 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gstreamer 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer-sdp 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer-webrtc 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1227,11 +1236,12 @@ dependencies = [ [metadata] "checksum anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" -"checksum async-native-tls 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9f073e25c972058b3a98e74bfbd2297ec0a4b2ed7b9c3f7b1e66d976601b98a8" +"checksum async-native-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d40a615e861c981117e15c28c577daf9918cabd2e2d588a5e06811ae79c9da1a" "checksum async-std 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0bf6039b315300e057d198b9d3ab92ee029e31c759b7f1afae538145e6f18a3e" -"checksum async-task 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d22dc86693d375d2733b536fd8914bea0fa93adf4b1e6bcbd9c7c500cb62d920" -"checksum async-tungstenite 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d35b7b512425d228d0366885b417ae3884930673ac1265764f1a8389dd2b00c4" +"checksum async-task 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a9f534e76ca33eaa82bc8da5adb1b9e94a16f6fa217b78e9b400094dbbf844f9" +"checksum async-tungstenite 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37250bc739b253cf2d861047a7bcaad1d46d1d027070b44dbeab9168b40cf407" "checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" "checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" @@ -1240,7 +1250,7 @@ dependencies = [ "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" "checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" "checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" -"checksum cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)" = "f52a465a666ca3d838ebbf08b241383421412fe7ebb463527bba275526d89f76" +"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" "checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" @@ -1267,11 +1277,11 @@ dependencies = [ "checksum futures-timer 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a1de7508b218029b0f01662ed8f61b1c964b3ae99d6f25462d0f55a595109df6" "checksum futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c0d66274fb76985d3c62c886d1da7ac4c0903a8c9f754e8fe0f35a6a6cc39e76" "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" -"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407" -"checksum glib 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "121c502fc6895e62d2ce084e677d3289ccbdd7f56edd4ac9a5ab8bd95d4a8670" +"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +"checksum glib 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27bafffe3fc615449d5a87705f93f6fe4fcf749662b9d08cc9d5451f6c1b0f21" "checksum glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "95856f3802f446c05feffa5e24859fe6a183a7cb849c8449afc35c86b1e316e2" "checksum gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31d1a804f62034eccf370006ccaef3708a71c31d561fee88564abe71177553d9" -"checksum gstreamer 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08d9ea04f6e746e90d979eaf5b55a9125fd159e58959f203a2f3fbc4b2a93b77" +"checksum gstreamer 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bbd828ab7aa022faec4248f3cf06891f6e02e7ca9271a5c0e5253eaf83b5259f" "checksum gstreamer-sdp 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "674df58b85cb077a357c581c29796fbeb5aa36e8362269807a11f938e5c7b973" "checksum gstreamer-sdp-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "99e88ac4f9f20323ef3409dddcea3bbf58364ff8eea10b14da5303bfcb23347a" "checksum gstreamer-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1d18da01b97d0ab5896acd5151e4c155acefd0e6c03c3dd24dd133ba054053db" @@ -1291,7 +1301,7 @@ dependencies = [ "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" -"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" +"checksum memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223" "checksum memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9" "checksum mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)" = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" "checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" @@ -1299,11 +1309,11 @@ dependencies = [ "checksum muldiv 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0419348c027fa7be448d2ae7ea0e4e04c2334c31dc4e74ab29f00a2a7ca69204" "checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e" "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" -"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" -"checksum num-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2885278d5fe2adc2f75ced642d52d879bffaceb5a2e0b1d4309ffdfb239b454" -"checksum num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4" -"checksum num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76dac5ed2a876980778b8b85f75a71b6cbf0db0b1232ee12f826bccb00d09d72" -"checksum once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "891f486f630e5c5a4916c7e16c4b24a53e78c860b646e9f8e005e4f16847bfed" +"checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" +"checksum num-rational 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "da4dc79f9e6c81bef96148c8f6b8e72ad4541caa4a24373e900a36da07de03a3" +"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" +"checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" +"checksum once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" "checksum openssl 0.10.26 (registry+https://github.com/rust-lang/crates.io-index)" = "3a3cc5799d98e1088141b8e01ff760112bbd9f19d850c124500566ca6901a585" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" @@ -1311,19 +1321,19 @@ dependencies = [ "checksum paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "423a519e1c6e828f1e73b720f9d9ed2fa643dce8a7737fb43235ce0b41eeaa49" "checksum paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" -"checksum pin-project 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "94b90146c7216e4cb534069fb91366de4ea0ea353105ee45ed297e2d1619e469" -"checksum pin-project-internal 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "44ca92f893f0656d3cba8158dd0f2b99b94de256a4a54e870bd6922fcc6c8355" -"checksum pin-project-lite 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f0af6cbca0e6e3ce8692ee19fb8d734b641899e07b68eb73e9bbbd32f1703991" +"checksum pin-project 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "75fca1c4ff21f60ca2d37b80d72b63dab823a9d19d3cda3a81d18bc03f0ba8c5" +"checksum pin-project-internal 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6544cd4e4ecace61075a6ec78074beeef98d58aa9a3d07d053d993b2946a90d6" +"checksum pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" "checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" "checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" "checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" -"checksum proc-macro-error 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "53c98547ceaea14eeb26fcadf51dc70d01a2479a7839170eae133721105e4428" -"checksum proc-macro-error-attr 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c2bf5d493cf5d3e296beccfd61794e445e830dfc8070a9c248ad3ee071392c6c" +"checksum proc-macro-error 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1b79a464461615532fcc8a6ed8296fa66cc12350c18460ab3f4594a6cee0fcb6" +"checksum proc-macro-error-attr 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "23832e5eae6bac56bbac190500eef1aaede63776b5cd131eaa4ee7fe120cd892" "checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" "checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" -"checksum proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0319972dcae462681daf4da1adeeaa066e3ebd29c69be96c6abb1259d2ee2bcc" +"checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" -"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" +"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" "checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" "checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" "checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" @@ -1340,13 +1350,13 @@ dependencies = [ "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" "checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" -"checksum serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)" = "48c575e0cc52bdd09b47f330f646cf59afc586e9c4e3ccd6fc1f625b8ea1dad7" -"checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68" +"checksum serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "eab8f15f15d6c41a154c1b128a22f2dfabe350ef53c40953d84e36155c91192b" +"checksum sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "checksum smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44e59e0c9fa00817912ae6e4e6e3c4fe04455e75699d06eedc7d85917ed8e8f4" -"checksum structopt 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "884ae79d6aad1e738f4a70dff314203fd498490a63ebc4d03ea83323c40b7b72" -"checksum structopt-derive 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0a97f829a34a0a9d5b353a881025a23b8c9fd09d46be6045df6b22920dbd7a93" -"checksum syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1e4ff033220a41d1a57d8125eab57bf5263783dfdcc18688b1dacc6ce9651ef8" +"checksum structopt 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "df136b42d76b1fbea72e2ab3057343977b04b4a2e00836c3c7c0673829572713" +"checksum structopt-derive 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd50a87d2f7b8958055f3e73a963d78feaccca3836767a9069844e34b5b03c0a" +"checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" "checksum syn-mid 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd3937748a7eccff61ba5b90af1a20dbf610858923a9192ea0ecb0cb77db1d0" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" @@ -1355,14 +1365,14 @@ dependencies = [ "checksum tungstenite 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8a0c2bd5aeb7dcd2bb32e472c8872759308495e5eccc942e929a513cd8d36110" "checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -"checksum unicode-normalization 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b561e267b2326bb4cebfc0ef9e68355c7abe6c6f522aeac2f5bf95d56c59bdcf" +"checksum unicode-normalization 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" "checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" "checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" -"checksum url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61" +"checksum url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" "checksum utf-8 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" "checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" -"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" +"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/webrtc/multiparty-sendrecv/gst-rust/src/main.rs b/webrtc/multiparty-sendrecv/gst-rust/src/main.rs index 4f25d26566..dfe80326a4 100644 --- a/webrtc/multiparty-sendrecv/gst-rust/src/main.rs +++ b/webrtc/multiparty-sendrecv/gst-rust/src/main.rs @@ -191,15 +191,9 @@ impl App { .get_by_name("audio-mixer") .expect("can't find audio-mixer"); + // Create a stream for handling the GStreamer message asynchronously let bus = pipeline.get_bus().unwrap(); - - // Send our bus messages via a futures channel to be handled asynchronously - let (send_gst_msg_tx, send_gst_msg_rx) = mpsc::unbounded::(); - let send_gst_msg_tx = Mutex::new(send_gst_msg_tx); - bus.set_sync_handler(move |_, msg| { - let _ = send_gst_msg_tx.lock().unwrap().unbounded_send(msg.clone()); - gst::BusSyncReply::Drop - }); + let send_gst_msg_rx = gst::BusStream::new(&bus); // Channel for outgoing WebSocket messages from other threads let (send_ws_msg_tx, send_ws_msg_rx) = mpsc::unbounded::(); diff --git a/webrtc/sendrecv/gst-rust/Cargo.lock b/webrtc/sendrecv/gst-rust/Cargo.lock index 927c73c8c3..2eefc9ea3b 100644 --- a/webrtc/sendrecv/gst-rust/Cargo.lock +++ b/webrtc/sendrecv/gst-rust/Cargo.lock @@ -8,9 +8,9 @@ checksum = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" [[package]] name = "async-native-tls" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f073e25c972058b3a98e74bfbd2297ec0a4b2ed7b9c3f7b1e66d976601b98a8" +checksum = "d40a615e861c981117e15c28c577daf9918cabd2e2d588a5e06811ae79c9da1a" dependencies = [ "async-std", "native-tls", @@ -44,18 +44,19 @@ dependencies = [ [[package]] name = "async-task" -version = "1.1.1" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22dc86693d375d2733b536fd8914bea0fa93adf4b1e6bcbd9c7c500cb62d920" +checksum = "a9f534e76ca33eaa82bc8da5adb1b9e94a16f6fa217b78e9b400094dbbf844f9" dependencies = [ - "crossbeam-utils", + "libc", + "winapi 0.3.8", ] [[package]] name = "async-tungstenite" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d35b7b512425d228d0366885b417ae3884930673ac1265764f1a8389dd2b00c4" +checksum = "37250bc739b253cf2d861047a7bcaad1d46d1d027070b44dbeab9168b40cf407" dependencies = [ "async-native-tls", "async-std", @@ -71,6 +72,12 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + [[package]] name = "base64" version = "0.11.0" @@ -137,9 +144,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.48" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52a465a666ca3d838ebbf08b241383421412fe7ebb463527bba275526d89f76" +checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" [[package]] name = "cfg-if" @@ -199,7 +206,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac" dependencies = [ - "autocfg", + "autocfg 0.1.7", "cfg-if", "crossbeam-utils", "lazy_static", @@ -213,7 +220,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" dependencies = [ - "autocfg", + "autocfg 0.1.7", "cfg-if", "lazy_static", ] @@ -378,9 +385,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" dependencies = [ "cfg-if", "libc", @@ -389,9 +396,9 @@ dependencies = [ [[package]] name = "glib" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c502fc6895e62d2ce084e677d3289ccbdd7f56edd4ac9a5ab8bd95d4a8670" +checksum = "27bafffe3fc615449d5a87705f93f6fe4fcf749662b9d08cc9d5451f6c1b0f21" dependencies = [ "bitflags", "futures-channel", @@ -428,13 +435,15 @@ dependencies = [ [[package]] name = "gstreamer" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d9ea04f6e746e90d979eaf5b55a9125fd159e58959f203a2f3fbc4b2a93b77" +checksum = "bbd828ab7aa022faec4248f3cf06891f6e02e7ca9271a5c0e5253eaf83b5259f" dependencies = [ "bitflags", "cfg-if", + "futures-channel", "futures-core", + "futures-util", "glib", "glib-sys", "gobject-sys", @@ -633,9 +642,9 @@ checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" [[package]] name = "memchr" -version = "2.2.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" +checksum = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223" [[package]] name = "memoffset" @@ -725,39 +734,39 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.41" +version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" +checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" dependencies = [ - "autocfg", + "autocfg 1.0.0", "num-traits", ] [[package]] name = "num-rational" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2885278d5fe2adc2f75ced642d52d879bffaceb5a2e0b1d4309ffdfb239b454" +checksum = "da4dc79f9e6c81bef96148c8f6b8e72ad4541caa4a24373e900a36da07de03a3" dependencies = [ - "autocfg", + "autocfg 1.0.0", "num-integer", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4" +checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" dependencies = [ - "autocfg", + "autocfg 1.0.0", ] [[package]] name = "num_cpus" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76dac5ed2a876980778b8b85f75a71b6cbf0db0b1232ee12f826bccb00d09d72" +checksum = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" dependencies = [ "hermit-abi", "libc", @@ -765,9 +774,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891f486f630e5c5a4916c7e16c4b24a53e78c860b646e9f8e005e4f16847bfed" +checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" [[package]] name = "opaque-debug" @@ -801,7 +810,7 @@ version = "0.9.53" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "465d16ae7fc0e313318f7de5cecf57b2fbe7511fd213978b457e1c96ff46736f" dependencies = [ - "autocfg", + "autocfg 0.1.7", "cc", "libc", "pkg-config", @@ -838,18 +847,18 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pin-project" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b90146c7216e4cb534069fb91366de4ea0ea353105ee45ed297e2d1619e469" +checksum = "75fca1c4ff21f60ca2d37b80d72b63dab823a9d19d3cda3a81d18bc03f0ba8c5" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44ca92f893f0656d3cba8158dd0f2b99b94de256a4a54e870bd6922fcc6c8355" +checksum = "6544cd4e4ecace61075a6ec78074beeef98d58aa9a3d07d053d993b2946a90d6" dependencies = [ "proc-macro2", "quote", @@ -858,9 +867,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.1.1" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0af6cbca0e6e3ce8692ee19fb8d734b641899e07b68eb73e9bbbd32f1703991" +checksum = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" [[package]] name = "pin-utils" @@ -882,9 +891,9 @@ checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" [[package]] name = "proc-macro-error" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53c98547ceaea14eeb26fcadf51dc70d01a2479a7839170eae133721105e4428" +checksum = "1b79a464461615532fcc8a6ed8296fa66cc12350c18460ab3f4594a6cee0fcb6" dependencies = [ "proc-macro-error-attr", "proc-macro2", @@ -895,9 +904,9 @@ dependencies = [ [[package]] name = "proc-macro-error-attr" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2bf5d493cf5d3e296beccfd61794e445e830dfc8070a9c248ad3ee071392c6c" +checksum = "23832e5eae6bac56bbac190500eef1aaede63776b5cd131eaa4ee7fe120cd892" dependencies = [ "proc-macro2", "quote", @@ -925,9 +934,9 @@ checksum = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" [[package]] name = "proc-macro2" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0319972dcae462681daf4da1adeeaa066e3ebd29c69be96c6abb1259d2ee2bcc" +checksum = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" dependencies = [ "unicode-xid", ] @@ -943,9 +952,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ "getrandom", "libc", @@ -1094,9 +1103,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.44" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c575e0cc52bdd09b47f330f646cf59afc586e9c4e3ccd6fc1f625b8ea1dad7" +checksum = "eab8f15f15d6c41a154c1b128a22f2dfabe350ef53c40953d84e36155c91192b" dependencies = [ "itoa", "ryu", @@ -1105,9 +1114,9 @@ dependencies = [ [[package]] name = "sha-1" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" dependencies = [ "block-buffer", "digest", @@ -1129,19 +1138,20 @@ checksum = "44e59e0c9fa00817912ae6e4e6e3c4fe04455e75699d06eedc7d85917ed8e8f4" [[package]] name = "structopt" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884ae79d6aad1e738f4a70dff314203fd498490a63ebc4d03ea83323c40b7b72" +checksum = "df136b42d76b1fbea72e2ab3057343977b04b4a2e00836c3c7c0673829572713" dependencies = [ "clap", + "lazy_static", "structopt-derive", ] [[package]] name = "structopt-derive" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a97f829a34a0a9d5b353a881025a23b8c9fd09d46be6045df6b22920dbd7a93" +checksum = "fd50a87d2f7b8958055f3e73a963d78feaccca3836767a9069844e34b5b03c0a" dependencies = [ "heck", "proc-macro-error", @@ -1152,9 +1162,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e4ff033220a41d1a57d8125eab57bf5263783dfdcc18688b1dacc6ce9651ef8" +checksum = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" dependencies = [ "proc-macro2", "quote", @@ -1251,9 +1261,9 @@ dependencies = [ [[package]] name = "unicode-normalization" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b561e267b2326bb4cebfc0ef9e68355c7abe6c6f522aeac2f5bf95d56c59bdcf" +checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" dependencies = [ "smallvec", ] @@ -1278,9 +1288,9 @@ checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" [[package]] name = "url" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61" +checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" dependencies = [ "idna", "matches", @@ -1301,9 +1311,9 @@ checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" [[package]] name = "wasi" -version = "0.7.0" +version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "webrtc-app" @@ -1313,6 +1323,7 @@ dependencies = [ "async-std", "async-tungstenite", "futures", + "glib", "gstreamer", "gstreamer-sdp", "gstreamer-webrtc", diff --git a/webrtc/sendrecv/gst-rust/src/main.rs b/webrtc/sendrecv/gst-rust/src/main.rs index 2072def02c..c35cce48ff 100644 --- a/webrtc/sendrecv/gst-rust/src/main.rs +++ b/webrtc/sendrecv/gst-rust/src/main.rs @@ -133,15 +133,9 @@ impl App { webrtcbin.set_property_from_str("stun-server", STUN_SERVER); webrtcbin.set_property_from_str("bundle-policy", "max-bundle"); + // Create a stream for handling the GStreamer message asynchronously let bus = pipeline.get_bus().unwrap(); - - // Send our bus messages via a futures channel to be handled asynchronously - let (send_gst_msg_tx, send_gst_msg_rx) = mpsc::unbounded::(); - let send_gst_msg_tx = Mutex::new(send_gst_msg_tx); - bus.set_sync_handler(move |_, msg| { - let _ = send_gst_msg_tx.lock().unwrap().unbounded_send(msg.clone()); - gst::BusSyncReply::Drop - }); + let send_gst_msg_rx = gst::BusStream::new(&bus); // Channel for outgoing WebSocket messages from other threads let (send_ws_msg_tx, send_ws_msg_rx) = mpsc::unbounded::(); From d8e76871328a9d6b128ed66806898408abc58fb9 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Tue, 28 Jan 2020 00:03:39 +1100 Subject: [PATCH 099/130] janus: Add options near the top Add some script configuration options to choose between VP8 and H.264 near the top, to modify the video input source, and to enable/disable RTX support --- webrtc/janus/janusvideoroom.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/webrtc/janus/janusvideoroom.py b/webrtc/janus/janusvideoroom.py index 80ee47fef5..c1b2cd5a26 100644 --- a/webrtc/janus/janusvideoroom.py +++ b/webrtc/janus/janusvideoroom.py @@ -15,6 +15,15 @@ from websockets.exceptions import ConnectionClosed import attr +# Set to False to send H.264 +DO_VP8 = True +# Set to False to disable RTX (lost packet retransmission) +DO_RTX = True +# Choose the video source: +VIDEO_SRC="videotestsrc pattern=ball" +# VIDEO_SRC="v4l2src" + + @attr.s class JanusEvent: sender = attr.ib(validator=attr.validators.instance_of(int)) @@ -66,8 +75,6 @@ from gi.repository import GstWebRTC gi.require_version('GstSdp', '1.0') from gi.repository import GstSdp -DO_VP8 = True - if DO_VP8: ( encoder, payloader, rtp_encoding) = ( "vp8enc target-bitrate=500000", "rtpvp8pay", "VP8" ) else: @@ -75,9 +82,9 @@ else: PIPELINE_DESC = ''' webrtcbin name=sendrecv stun-server=stun://stun.l.google.com:19302 - videotestsrc pattern=ball ! video/x-raw,width=320,height=240 ! videoconvert ! queue ! + {} ! video/x-raw,width=640,height=480 ! videoconvert ! queue ! {} ! {} ! queue ! application/x-rtp,media=video,encoding-name={},payload=96 ! sendrecv. -'''.format(encoder, payloader, rtp_encoding) +'''.format(VIDEO_SRC, encoder, payloader, rtp_encoding) def transaction_id(): return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) @@ -328,6 +335,10 @@ class WebRTCClient: self.webrtc.connect('on-negotiation-needed', self.on_negotiation_needed) self.webrtc.connect('on-ice-candidate', self.send_ice_candidate_message) self.webrtc.connect('pad-added', self.on_incoming_stream) + + trans = self.webrtc.emit('get-transceiver', 0) + if DO_RTX: + trans.set_property ('do-nack', True) self.pipe.set_state(Gst.State.PLAYING) def extract_ice_from_sdp(self, sdp): From 1f1233064f759c4d839311be6c4e8efe80684749 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Tue, 28 Jan 2020 00:04:27 +1100 Subject: [PATCH 100/130] janus: Add picture-id-mode=2 to VP8 payloading This writes an extended header and Picture-ID into each RTP packet which makes Janus able to detect which frames are keyframes and to request replacement keyframes. --- webrtc/janus/janusvideoroom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webrtc/janus/janusvideoroom.py b/webrtc/janus/janusvideoroom.py index c1b2cd5a26..4372a6361f 100644 --- a/webrtc/janus/janusvideoroom.py +++ b/webrtc/janus/janusvideoroom.py @@ -76,7 +76,7 @@ gi.require_version('GstSdp', '1.0') from gi.repository import GstSdp if DO_VP8: - ( encoder, payloader, rtp_encoding) = ( "vp8enc target-bitrate=500000", "rtpvp8pay", "VP8" ) + ( encoder, payloader, rtp_encoding) = ( "vp8enc target-bitrate=100000 overshoot=25 undershoot=100 deadline=33000 keyframe-max-dist=1", "rtpvp8pay picture-id-mode=2", "VP8" ) else: ( encoder, payloader, rtp_encoding) = ( "x264enc", "rtph264pay", "H264" ) From 699b830213996be7d7d2362477802e02557d0f24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sat, 1 Feb 2020 15:21:08 +0200 Subject: [PATCH 101/130] Update Rust examples to async-tungstenite 0.4 --- .../multiparty-sendrecv/gst-rust/Cargo.lock | 147 +++++++++--------- .../multiparty-sendrecv/gst-rust/Cargo.toml | 3 +- .../multiparty-sendrecv/gst-rust/src/main.rs | 3 +- webrtc/sendrecv/gst-rust/Cargo.lock | 92 +++++------ webrtc/sendrecv/gst-rust/Cargo.toml | 3 +- webrtc/sendrecv/gst-rust/src/main.rs | 3 +- 6 files changed, 118 insertions(+), 133 deletions(-) diff --git a/webrtc/multiparty-sendrecv/gst-rust/Cargo.lock b/webrtc/multiparty-sendrecv/gst-rust/Cargo.lock index fb1183fdec..7fce37ce1c 100644 --- a/webrtc/multiparty-sendrecv/gst-rust/Cargo.lock +++ b/webrtc/multiparty-sendrecv/gst-rust/Cargo.lock @@ -12,7 +12,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "async-std 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -20,7 +20,7 @@ name = "async-std" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "async-task 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "async-task 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -41,7 +41,7 @@ dependencies = [ [[package]] name = "async-task" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", @@ -50,15 +50,15 @@ dependencies = [ [[package]] name = "async-tungstenite" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "async-native-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "async-std 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tungstenite 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tungstenite 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -112,12 +112,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bytes" -version = "0.4.12" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "c2-chacha" @@ -389,7 +385,7 @@ dependencies = [ [[package]] name = "gstreamer" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -416,7 +412,7 @@ dependencies = [ "glib 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)", + "gstreamer 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer-sdp-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -452,7 +448,7 @@ dependencies = [ "glib 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)", + "gstreamer 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer-sdp 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer-webrtc-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -490,12 +486,12 @@ dependencies = [ [[package]] name = "http" -version = "0.1.21" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -515,10 +511,10 @@ dependencies = [ [[package]] name = "input_buffer" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -531,7 +527,7 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -639,9 +635,9 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl 0.10.26 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.27 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.53 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.54 (registry+https://github.com/rust-lang/crates.io-index)", "schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -706,7 +702,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl" -version = "0.10.26" +version = "0.10.27" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -714,7 +710,7 @@ dependencies = [ "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.53 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.54 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -724,10 +720,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl-sys" -version = "0.9.53" +version = "0.9.54" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", @@ -761,15 +757,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "pin-project" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "pin-project-internal 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project-internal 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pin-project-internal" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -799,26 +795,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "proc-macro-error" -version = "0.4.5" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro-error-attr 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-error-attr 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustversion 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustversion 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "proc-macro-error-attr" -version = "0.4.5" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustversion 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustversion 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", - "syn-mid 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syn-mid 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -912,7 +908,7 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -991,7 +987,7 @@ name = "serde_json" version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1014,26 +1010,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "smallvec" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "structopt" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt-derive 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt-derive 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "structopt-derive" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-error 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-error 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1051,7 +1047,7 @@ dependencies = [ [[package]] name = "syn-mid" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1082,15 +1078,15 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "thiserror-impl 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror-impl 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "thiserror-impl" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1100,15 +1096,15 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.9.2" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "input_buffer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "input_buffer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1134,7 +1130,7 @@ name = "unicode-normalization" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1183,17 +1179,16 @@ version = "0.1.0" dependencies = [ "anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", "async-std 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "async-tungstenite 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "async-tungstenite 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)", + "gstreamer 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer-sdp 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer-webrtc 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1238,8 +1233,8 @@ dependencies = [ "checksum anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" "checksum async-native-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d40a615e861c981117e15c28c577daf9918cabd2e2d588a5e06811ae79c9da1a" "checksum async-std 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0bf6039b315300e057d198b9d3ab92ee029e31c759b7f1afae538145e6f18a3e" -"checksum async-task 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a9f534e76ca33eaa82bc8da5adb1b9e94a16f6fa217b78e9b400094dbbf844f9" -"checksum async-tungstenite 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37250bc739b253cf2d861047a7bcaad1d46d1d027070b44dbeab9168b40cf407" +"checksum async-task 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f20c6fda19d0fc02406779587ca4f9a4171cd32e4a5bda0bd016f0a1334c8d4a" +"checksum async-tungstenite 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1f1a7fca4f1ccd1fe926bec21a4ba6259d3d91f04d67a9dd76bd549366be5bef" "checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" "checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" "checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" @@ -1248,7 +1243,7 @@ dependencies = [ "checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" "checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" -"checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +"checksum bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" "checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" "checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" @@ -1281,7 +1276,7 @@ dependencies = [ "checksum glib 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27bafffe3fc615449d5a87705f93f6fe4fcf749662b9d08cc9d5451f6c1b0f21" "checksum glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "95856f3802f446c05feffa5e24859fe6a183a7cb849c8449afc35c86b1e316e2" "checksum gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31d1a804f62034eccf370006ccaef3708a71c31d561fee88564abe71177553d9" -"checksum gstreamer 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bbd828ab7aa022faec4248f3cf06891f6e02e7ca9271a5c0e5253eaf83b5259f" +"checksum gstreamer 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f439859b5f3310518adc0aea572ac89ddd94f8ba3af4626d6156733671599629" "checksum gstreamer-sdp 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "674df58b85cb077a357c581c29796fbeb5aa36e8362269807a11f938e5c7b973" "checksum gstreamer-sdp-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "99e88ac4f9f20323ef3409dddcea3bbf58364ff8eea10b14da5303bfcb23347a" "checksum gstreamer-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1d18da01b97d0ab5896acd5151e4c155acefd0e6c03c3dd24dd133ba054053db" @@ -1289,12 +1284,12 @@ dependencies = [ "checksum gstreamer-webrtc-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f392bd821b42efecfc21016c8ef20da188b45a45bbb5ddf81758704f93aae615" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eff2656d88f158ce120947499e971d743c05dbcbed62e5bd2f38f1698bbc3772" -"checksum http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" +"checksum http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b708cc7f06493459026f53b9a61a7a121a5d1ec6238dee58ea4941132b30156b" "checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" "checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" -"checksum input_buffer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8e1b822cc844905551931d6f81608ed5f50a79c1078a4e2b4d42dbc7c1eedfbf" +"checksum input_buffer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "19a8a95243d5a0398cae618ec29477c6e3cb631152be5c19481f80bc71559754" "checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" -"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" +"checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum kv-log-macro 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c54d9f465d530a752e6ebdc217e081a7a614b48cb200f6f0aee21ba6bc9aabb" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" @@ -1315,20 +1310,20 @@ dependencies = [ "checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" "checksum once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -"checksum openssl 0.10.26 (registry+https://github.com/rust-lang/crates.io-index)" = "3a3cc5799d98e1088141b8e01ff760112bbd9f19d850c124500566ca6901a585" +"checksum openssl 0.10.27 (registry+https://github.com/rust-lang/crates.io-index)" = "e176a45fedd4c990e26580847a525e39e16ec32ac78957dbf62ded31b3abfd6f" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" -"checksum openssl-sys 0.9.53 (registry+https://github.com/rust-lang/crates.io-index)" = "465d16ae7fc0e313318f7de5cecf57b2fbe7511fd213978b457e1c96ff46736f" +"checksum openssl-sys 0.9.54 (registry+https://github.com/rust-lang/crates.io-index)" = "1024c0a59774200a555087a6da3f253a9095a5f344e353b212ac4c8b8e450986" "checksum paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "423a519e1c6e828f1e73b720f9d9ed2fa643dce8a7737fb43235ce0b41eeaa49" "checksum paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" -"checksum pin-project 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "75fca1c4ff21f60ca2d37b80d72b63dab823a9d19d3cda3a81d18bc03f0ba8c5" -"checksum pin-project-internal 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6544cd4e4ecace61075a6ec78074beeef98d58aa9a3d07d053d993b2946a90d6" +"checksum pin-project 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7804a463a8d9572f13453c516a5faea534a2403d7ced2f0c7e100eeff072772c" +"checksum pin-project-internal 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "385322a45f2ecf3410c68d2a549a4a2685e8051d0f278e39743ff4e451cb9b3f" "checksum pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" "checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" "checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" "checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" -"checksum proc-macro-error 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1b79a464461615532fcc8a6ed8296fa66cc12350c18460ab3f4594a6cee0fcb6" -"checksum proc-macro-error-attr 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "23832e5eae6bac56bbac190500eef1aaede63776b5cd131eaa4ee7fe120cd892" +"checksum proc-macro-error 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "875077759af22fa20b610ad4471d8155b321c89c3f2785526c9839b099be4e0a" +"checksum proc-macro-error-attr 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c5717d9fa2664351a01ed73ba5ef6df09c01a521cb42cb65a061432a826f3c7a" "checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" "checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" "checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" @@ -1340,7 +1335,7 @@ dependencies = [ "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum rustversion 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a0538bd897e17257b0128d2fd95c2ed6df939374073a36166051a79e2eb7986" +"checksum rustversion 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b3bba175698996010c4f6dce5e7f173b6eb781fce25d2cfc45e27091ce0b79f6" "checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" "checksum schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "87f550b06b6cba9c8b8be3ee73f391990116bf527450d2556e9b9ce263b9a021" "checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" @@ -1353,16 +1348,16 @@ dependencies = [ "checksum serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "eab8f15f15d6c41a154c1b128a22f2dfabe350ef53c40953d84e36155c91192b" "checksum sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -"checksum smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44e59e0c9fa00817912ae6e4e6e3c4fe04455e75699d06eedc7d85917ed8e8f4" -"checksum structopt 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "df136b42d76b1fbea72e2ab3057343977b04b4a2e00836c3c7c0673829572713" -"checksum structopt-derive 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd50a87d2f7b8958055f3e73a963d78feaccca3836767a9069844e34b5b03c0a" +"checksum smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" +"checksum structopt 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "a1bcbed7d48956fcbb5d80c6b95aedb553513de0a1b451ea92679d999c010e98" +"checksum structopt-derive 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "095064aa1f5b94d14e635d0a5684cf140c43ae40a0fd990708d38f5d669e5f64" "checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" -"checksum syn-mid 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd3937748a7eccff61ba5b90af1a20dbf610858923a9192ea0ecb0cb77db1d0" +"checksum syn-mid 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -"checksum thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6f357d1814b33bc2dc221243f8424104bfe72dbe911d5b71b3816a2dff1c977e" -"checksum thiserror-impl 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2e25d25307eb8436894f727aba8f65d07adf02e5b35a13cebed48bd282bfef" -"checksum tungstenite 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8a0c2bd5aeb7dcd2bb32e472c8872759308495e5eccc942e929a513cd8d36110" +"checksum thiserror 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "205684fd018ca14432b12cce6ea3d46763311a571c3d294e71ba3f01adcf1aad" +"checksum thiserror-impl 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "57e4d2e50ca050ed44fb58309bdce3efa79948f84f9993ad1978de5eebdce5a7" +"checksum tungstenite 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08f33f14c64af9dfc6e50e6812261178d6bd434c0e501f87c7683b8aaa9eb8d3" "checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" "checksum unicode-normalization 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" diff --git a/webrtc/multiparty-sendrecv/gst-rust/Cargo.toml b/webrtc/multiparty-sendrecv/gst-rust/Cargo.toml index ec353310fa..94e6533f6c 100644 --- a/webrtc/multiparty-sendrecv/gst-rust/Cargo.toml +++ b/webrtc/multiparty-sendrecv/gst-rust/Cargo.toml @@ -9,9 +9,8 @@ futures = "0.3" async-std = "1" structopt = { version = "0.3", default-features = false } anyhow = "1" -url = "2" rand = "0.7" -async-tungstenite = { version = "0.3", features = ["async-std-runtime", "async-native-tls"] } +async-tungstenite = { version = "0.4", features = ["async-std-runtime", "async-native-tls"] } gst = { package = "gstreamer", version = "0.15", features = ["v1_14"] } gst-webrtc = { package = "gstreamer-webrtc", version = "0.15" } gst-sdp = { package = "gstreamer-sdp", version = "0.15", features = ["v1_14"] } diff --git a/webrtc/multiparty-sendrecv/gst-rust/src/main.rs b/webrtc/multiparty-sendrecv/gst-rust/src/main.rs index dfe80326a4..995b4bd361 100644 --- a/webrtc/multiparty-sendrecv/gst-rust/src/main.rs +++ b/webrtc/multiparty-sendrecv/gst-rust/src/main.rs @@ -1010,8 +1010,7 @@ async fn async_main() -> Result<(), anyhow::Error> { let args = Args::from_args(); // Connect to the given server - let url = url::Url::parse(&args.server)?; - let (mut ws, _) = async_tungstenite::async_std::connect_async(url).await?; + let (mut ws, _) = async_tungstenite::async_std::connect_async(&args.server).await?; println!("connected"); diff --git a/webrtc/sendrecv/gst-rust/Cargo.lock b/webrtc/sendrecv/gst-rust/Cargo.lock index 2eefc9ea3b..cebc8e6043 100644 --- a/webrtc/sendrecv/gst-rust/Cargo.lock +++ b/webrtc/sendrecv/gst-rust/Cargo.lock @@ -44,9 +44,9 @@ dependencies = [ [[package]] name = "async-task" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9f534e76ca33eaa82bc8da5adb1b9e94a16f6fa217b78e9b400094dbbf844f9" +checksum = "f20c6fda19d0fc02406779587ca4f9a4171cd32e4a5bda0bd016f0a1334c8d4a" dependencies = [ "libc", "winapi 0.3.8", @@ -54,9 +54,9 @@ dependencies = [ [[package]] name = "async-tungstenite" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37250bc739b253cf2d861047a7bcaad1d46d1d027070b44dbeab9168b40cf407" +checksum = "1f1a7fca4f1ccd1fe926bec21a4ba6259d3d91f04d67a9dd76bd549366be5bef" dependencies = [ "async-native-tls", "async-std", @@ -125,13 +125,9 @@ checksum = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" [[package]] name = "bytes" -version = "0.4.12" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" -dependencies = [ - "byteorder", - "iovec", -] +checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" [[package]] name = "c2-chacha" @@ -435,9 +431,9 @@ dependencies = [ [[package]] name = "gstreamer" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd828ab7aa022faec4248f3cf06891f6e02e7ca9271a5c0e5253eaf83b5259f" +checksum = "f439859b5f3310518adc0aea572ac89ddd94f8ba3af4626d6156733671599629" dependencies = [ "bitflags", "cfg-if", @@ -544,9 +540,9 @@ dependencies = [ [[package]] name = "http" -version = "0.1.21" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" +checksum = "b708cc7f06493459026f53b9a61a7a121a5d1ec6238dee58ea4941132b30156b" dependencies = [ "bytes", "fnv", @@ -572,9 +568,9 @@ dependencies = [ [[package]] name = "input_buffer" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1b822cc844905551931d6f81608ed5f50a79c1078a4e2b4d42dbc7c1eedfbf" +checksum = "19a8a95243d5a0398cae618ec29477c6e3cb631152be5c19481f80bc71559754" dependencies = [ "bytes", ] @@ -590,9 +586,9 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" +checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" [[package]] name = "kernel32-sys" @@ -786,9 +782,9 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "openssl" -version = "0.10.26" +version = "0.10.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3cc5799d98e1088141b8e01ff760112bbd9f19d850c124500566ca6901a585" +checksum = "e176a45fedd4c990e26580847a525e39e16ec32ac78957dbf62ded31b3abfd6f" dependencies = [ "bitflags", "cfg-if", @@ -806,11 +802,11 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" [[package]] name = "openssl-sys" -version = "0.9.53" +version = "0.9.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465d16ae7fc0e313318f7de5cecf57b2fbe7511fd213978b457e1c96ff46736f" +checksum = "1024c0a59774200a555087a6da3f253a9095a5f344e353b212ac4c8b8e450986" dependencies = [ - "autocfg 0.1.7", + "autocfg 1.0.0", "cc", "libc", "pkg-config", @@ -847,18 +843,18 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pin-project" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75fca1c4ff21f60ca2d37b80d72b63dab823a9d19d3cda3a81d18bc03f0ba8c5" +checksum = "7804a463a8d9572f13453c516a5faea534a2403d7ced2f0c7e100eeff072772c" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6544cd4e4ecace61075a6ec78074beeef98d58aa9a3d07d053d993b2946a90d6" +checksum = "385322a45f2ecf3410c68d2a549a4a2685e8051d0f278e39743ff4e451cb9b3f" dependencies = [ "proc-macro2", "quote", @@ -891,9 +887,9 @@ checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" [[package]] name = "proc-macro-error" -version = "0.4.5" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b79a464461615532fcc8a6ed8296fa66cc12350c18460ab3f4594a6cee0fcb6" +checksum = "875077759af22fa20b610ad4471d8155b321c89c3f2785526c9839b099be4e0a" dependencies = [ "proc-macro-error-attr", "proc-macro2", @@ -904,9 +900,9 @@ dependencies = [ [[package]] name = "proc-macro-error-attr" -version = "0.4.5" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23832e5eae6bac56bbac190500eef1aaede63776b5cd131eaa4ee7fe120cd892" +checksum = "c5717d9fa2664351a01ed73ba5ef6df09c01a521cb42cb65a061432a826f3c7a" dependencies = [ "proc-macro2", "quote", @@ -1017,9 +1013,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a0538bd897e17257b0128d2fd95c2ed6df939374073a36166051a79e2eb7986" +checksum = "b3bba175698996010c4f6dce5e7f173b6eb781fce25d2cfc45e27091ce0b79f6" dependencies = [ "proc-macro2", "quote", @@ -1132,15 +1128,15 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] name = "smallvec" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44e59e0c9fa00817912ae6e4e6e3c4fe04455e75699d06eedc7d85917ed8e8f4" +checksum = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" [[package]] name = "structopt" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df136b42d76b1fbea72e2ab3057343977b04b4a2e00836c3c7c0673829572713" +checksum = "a1bcbed7d48956fcbb5d80c6b95aedb553513de0a1b451ea92679d999c010e98" dependencies = [ "clap", "lazy_static", @@ -1149,9 +1145,9 @@ dependencies = [ [[package]] name = "structopt-derive" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd50a87d2f7b8958055f3e73a963d78feaccca3836767a9069844e34b5b03c0a" +checksum = "095064aa1f5b94d14e635d0a5684cf140c43ae40a0fd990708d38f5d669e5f64" dependencies = [ "heck", "proc-macro-error", @@ -1173,9 +1169,9 @@ dependencies = [ [[package]] name = "syn-mid" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fd3937748a7eccff61ba5b90af1a20dbf610858923a9192ea0ecb0cb77db1d0" +checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" dependencies = [ "proc-macro2", "quote", @@ -1207,18 +1203,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f357d1814b33bc2dc221243f8424104bfe72dbe911d5b71b3816a2dff1c977e" +checksum = "205684fd018ca14432b12cce6ea3d46763311a571c3d294e71ba3f01adcf1aad" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2e25d25307eb8436894f727aba8f65d07adf02e5b35a13cebed48bd282bfef" +checksum = "57e4d2e50ca050ed44fb58309bdce3efa79948f84f9993ad1978de5eebdce5a7" dependencies = [ "proc-macro2", "quote", @@ -1227,9 +1223,9 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.9.2" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0c2bd5aeb7dcd2bb32e472c8872759308495e5eccc942e929a513cd8d36110" +checksum = "08f33f14c64af9dfc6e50e6812261178d6bd434c0e501f87c7683b8aaa9eb8d3" dependencies = [ "base64", "byteorder", @@ -1323,7 +1319,6 @@ dependencies = [ "async-std", "async-tungstenite", "futures", - "glib", "gstreamer", "gstreamer-sdp", "gstreamer-webrtc", @@ -1332,7 +1327,6 @@ dependencies = [ "serde_derive", "serde_json", "structopt", - "url", ] [[package]] diff --git a/webrtc/sendrecv/gst-rust/Cargo.toml b/webrtc/sendrecv/gst-rust/Cargo.toml index ec353310fa..94e6533f6c 100644 --- a/webrtc/sendrecv/gst-rust/Cargo.toml +++ b/webrtc/sendrecv/gst-rust/Cargo.toml @@ -9,9 +9,8 @@ futures = "0.3" async-std = "1" structopt = { version = "0.3", default-features = false } anyhow = "1" -url = "2" rand = "0.7" -async-tungstenite = { version = "0.3", features = ["async-std-runtime", "async-native-tls"] } +async-tungstenite = { version = "0.4", features = ["async-std-runtime", "async-native-tls"] } gst = { package = "gstreamer", version = "0.15", features = ["v1_14"] } gst-webrtc = { package = "gstreamer-webrtc", version = "0.15" } gst-sdp = { package = "gstreamer-sdp", version = "0.15", features = ["v1_14"] } diff --git a/webrtc/sendrecv/gst-rust/src/main.rs b/webrtc/sendrecv/gst-rust/src/main.rs index c35cce48ff..48f9c38674 100644 --- a/webrtc/sendrecv/gst-rust/src/main.rs +++ b/webrtc/sendrecv/gst-rust/src/main.rs @@ -639,8 +639,7 @@ async fn async_main() -> Result<(), anyhow::Error> { let args = Args::from_args(); // Connect to the given server - let url = url::Url::parse(&args.server)?; - let (mut ws, _) = async_tungstenite::async_std::connect_async(url).await?; + let (mut ws, _) = async_tungstenite::async_std::connect_async(&args.server).await?; println!("connected"); From d2236266dc62d6912bcecb9195129c1a1ce4bb5d Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Thu, 30 Jan 2020 14:46:05 +1100 Subject: [PATCH 102/130] Android: Update build for android example --- webrtc/android/app/build.gradle | 12 ++-- webrtc/android/gradlew | 100 ++++++++++++++++++-------------- webrtc/android/gradlew.bat | 14 ++--- 3 files changed, 67 insertions(+), 59 deletions(-) diff --git a/webrtc/android/app/build.gradle b/webrtc/android/app/build.gradle index e7def0cd53..9092448743 100644 --- a/webrtc/android/app/build.gradle +++ b/webrtc/android/app/build.gradle @@ -52,13 +52,15 @@ android { } afterEvaluate { - compileDebugJavaWithJavac.dependsOn 'externalNativeBuildDebug' - compileReleaseJavaWithJavac.dependsOn 'externalNativeBuildRelease' + if (project.hasProperty('compileDebugJavaWithJavac')) + compileDebugJavaWithJavac.dependsOn 'externalNativeBuildDebug' + if (project.hasProperty('compileReleaseJavaWithJavac')) + compileReleaseJavaWithJavac.dependsOn 'externalNativeBuildRelease' } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - testCompile 'junit:junit:4.12' - compile 'com.android.support:appcompat-v7:23.1.1' + api fileTree(dir: 'libs', include: ['*.jar']) + testImplementation 'junit:junit:4.12' + implementation 'com.android.support:appcompat-v7:23.1.1' implementation 'com.android.support.constraint:constraint-layout:1.0.2' } diff --git a/webrtc/android/gradlew b/webrtc/android/gradlew index 9d82f78915..cccdd3d517 100755 --- a/webrtc/android/gradlew +++ b/webrtc/android/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -6,42 +6,6 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "$*" -} - -die ( ) { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; -esac - # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" @@ -60,6 +24,46 @@ cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" cd "$SAVED" >/dev/null +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -85,7 +89,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -150,11 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/webrtc/android/gradlew.bat b/webrtc/android/gradlew.bat index aec99730b4..e95643d6a2 100644 --- a/webrtc/android/gradlew.bat +++ b/webrtc/android/gradlew.bat @@ -8,14 +8,14 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,10 +46,9 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +59,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line From c8e79c9671a627e12a299adcf2beb19d28c71344 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Fri, 21 Feb 2020 14:01:58 +1100 Subject: [PATCH 103/130] webrtc-sendrecv.py: Add a stun server Fixes https://github.com/centricular/gstwebrtc-demos/issues/160 --- webrtc/sendrecv/gst/webrtc-sendrecv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webrtc/sendrecv/gst/webrtc-sendrecv.py b/webrtc/sendrecv/gst/webrtc-sendrecv.py index 88870b12ca..b19bc3fa6f 100644 --- a/webrtc/sendrecv/gst/webrtc-sendrecv.py +++ b/webrtc/sendrecv/gst/webrtc-sendrecv.py @@ -16,7 +16,7 @@ gi.require_version('GstSdp', '1.0') from gi.repository import GstSdp PIPELINE_DESC = ''' -webrtcbin name=sendrecv bundle-policy=max-bundle +webrtcbin name=sendrecv bundle-policy=max-bundle stun-server=stun://stun.l.google.com:19302 videotestsrc is-live=true pattern=ball ! videoconvert ! queue ! vp8enc deadline=1 ! rtpvp8pay ! queue ! application/x-rtp,media=video,encoding-name=VP8,payload=97 ! sendrecv. audiotestsrc is-live=true wave=red-noise ! audioconvert ! audioresample ! queue ! opusenc ! rtpopuspay ! From 5bf67feae8f4755862e571674ce0c8cf78d9b45b Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Thu, 5 Mar 2020 03:03:17 +1100 Subject: [PATCH 104/130] sendrecv: Add a switch for remote-offerer Add a switch to the command line utility that makes it request the initial offer from the peer instead of generating it. Modify the webrtc.js example to support a new REQUEST_OFFER message, and generate the offer when receiving it. --- webrtc/sendrecv/gst/webrtc-sendrecv.c | 160 ++++++++++++++++++++------ webrtc/sendrecv/js/webrtc.js | 60 ++++++---- 2 files changed, 165 insertions(+), 55 deletions(-) diff --git a/webrtc/sendrecv/gst/webrtc-sendrecv.c b/webrtc/sendrecv/gst/webrtc-sendrecv.c index c2bb55c722..39c4ef9f9a 100644 --- a/webrtc/sendrecv/gst/webrtc-sendrecv.c +++ b/webrtc/sendrecv/gst/webrtc-sendrecv.c @@ -47,12 +47,14 @@ static enum AppState app_state = 0; static const gchar *peer_id = NULL; static const gchar *server_url = "wss://webrtc.nirbheek.in:8443"; static gboolean disable_ssl = FALSE; +static gboolean remote_is_offerer = FALSE; static GOptionEntry entries[] = { { "peer-id", 0, 0, G_OPTION_ARG_STRING, &peer_id, "String ID of the peer to connect to", "ID" }, { "server", 0, 0, G_OPTION_ARG_STRING, &server_url, "Signalling server to connect to", "URL" }, { "disable-ssl", 0, 0, G_OPTION_ARG_NONE, &disable_ssl, "Disable ssl", NULL }, + { "remote-offerer", 0, 0, G_OPTION_ARG_NONE, &remote_is_offerer, "Request that the peer generate the offer and we'll answer", NULL }, { NULL }, }; @@ -213,21 +215,31 @@ send_ice_candidate_message (GstElement * webrtc G_GNUC_UNUSED, guint mlineindex, } static void -send_sdp_offer (GstWebRTCSessionDescription * offer) +send_sdp_to_peer (GstWebRTCSessionDescription *desc) { gchar *text; JsonObject *msg, *sdp; if (app_state < PEER_CALL_NEGOTIATING) { - cleanup_and_quit_loop ("Can't send offer, not in call", APP_STATE_ERROR); + cleanup_and_quit_loop ("Can't send SDP to peer, not in call", APP_STATE_ERROR); return; } - text = gst_sdp_message_as_text (offer->sdp); - g_print ("Sending offer:\n%s\n", text); - + text = gst_sdp_message_as_text (desc->sdp); sdp = json_object_new (); - json_object_set_string_member (sdp, "type", "offer"); + + if (desc->type == GST_WEBRTC_SDP_TYPE_OFFER) { + g_print ("Sending offer:\n%s\n", text); + json_object_set_string_member (sdp, "type", "offer"); + } + else if (desc->type == GST_WEBRTC_SDP_TYPE_ANSWER) { + g_print ("Sending answer:\n%s\n", text); + json_object_set_string_member (sdp, "type", "answer"); + } + else { + g_assert_not_reached (); + } + json_object_set_string_member (sdp, "sdp", text); g_free (text); @@ -261,18 +273,24 @@ on_offer_created (GstPromise * promise, gpointer user_data) gst_promise_unref (promise); /* Send offer to peer */ - send_sdp_offer (offer); + send_sdp_to_peer (offer); gst_webrtc_session_description_free (offer); } static void on_negotiation_needed (GstElement * element, gpointer user_data) { - GstPromise *promise; - app_state = PEER_CALL_NEGOTIATING; - promise = gst_promise_new_with_change_func (on_offer_created, user_data, NULL);; - g_signal_emit_by_name (webrtc1, "create-offer", NULL, promise); + + if (remote_is_offerer) { + gchar *msg = g_strdup_printf ("OFFER_REQUEST"); + soup_websocket_connection_send_text (ws_conn, msg); + g_free (msg); + } else { + GstPromise *promise; + promise = gst_promise_new_with_change_func (on_offer_created, user_data, NULL);; + g_signal_emit_by_name (webrtc1, "create-offer", NULL, promise); + } } #define STUN_SERVER " stun-server=stun://stun.l.google.com:19302 " @@ -327,6 +345,29 @@ on_data_channel (GstElement * webrtc, GObject * data_channel, gpointer user_data receive_channel = data_channel; } +static void +on_ice_gathering_state_notify (GstElement * webrtcbin, GParamSpec * pspec, + gpointer user_data) +{ + GstWebRTCICEGatheringState ice_gather_state; + const gchar *new_state = "unknown"; + + g_object_get (webrtcbin, "ice-gathering-state", &ice_gather_state, + NULL); + switch (ice_gather_state) { + case GST_WEBRTC_ICE_GATHERING_STATE_NEW: + new_state = "new"; + break; + case GST_WEBRTC_ICE_GATHERING_STATE_GATHERING: + new_state = "gathering"; + break; + case GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE: + new_state = "complete"; + break; + } + g_print ("ICE gathering state changed to %s\n", new_state); +} + static gboolean start_pipeline (void) { @@ -359,6 +400,8 @@ start_pipeline (void) * added by us too, see on_server_message() */ g_signal_connect (webrtc1, "on-ice-candidate", G_CALLBACK (send_ice_candidate_message), NULL); + g_signal_connect (webrtc1, "notify::ice-gathering-state", + G_CALLBACK (on_ice_gathering_state_notify), NULL); gst_element_set_state (pipe1, GST_STATE_READY); @@ -445,6 +488,55 @@ on_server_closed (SoupWebsocketConnection * conn G_GNUC_UNUSED, cleanup_and_quit_loop ("Server connection closed", 0); } +/* Answer created by our pipeline, to be sent to the peer */ +static void +on_answer_created (GstPromise * promise, gpointer user_data) +{ + GstWebRTCSessionDescription *answer = NULL; + const GstStructure *reply; + + g_assert_cmphex (app_state, ==, PEER_CALL_NEGOTIATING); + + g_assert_cmphex (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); + + promise = gst_promise_new (); + g_signal_emit_by_name (webrtc1, "set-local-description", answer, promise); + gst_promise_interrupt (promise); + gst_promise_unref (promise); + + /* Send answer to peer */ + send_sdp_to_peer (answer); + gst_webrtc_session_description_free (answer); +} + +static void +on_offer_received (GstSDPMessage *sdp) +{ + GstWebRTCSessionDescription *offer = NULL; + GstPromise *promise; + + offer = gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_OFFER, sdp); + g_assert_nonnull (offer); + + /* Set remote description on our pipeline */ + { + promise = gst_promise_new (); + g_signal_emit_by_name (webrtc1, "set-remote-description", offer, + promise); + gst_promise_interrupt (promise); + gst_promise_unref (promise); + } + gst_webrtc_session_description_free (offer); + + promise = gst_promise_new_with_change_func (on_answer_created, NULL, + NULL); + g_signal_emit_by_name (webrtc1, "create-answer", NULL, promise); +} + /* One mega message handler for our asynchronous calling mechanism */ static void on_server_message (SoupWebsocketConnection * conn, SoupWebsocketDataType type, @@ -550,35 +642,39 @@ on_server_message (SoupWebsocketConnection * conn, SoupWebsocketDataType type, } sdptype = json_object_get_string_member (child, "type"); - /* In this example, we always create the offer and receive one answer. - * See tests/examples/webrtcbidirectional.c in gst-plugins-bad for how to - * handle offers from peers and reply with answers using webrtcbin. */ - g_assert_cmpstr (sdptype, ==, "answer"); - + /* In this example, we create the offer and receive one answer by default, + * but it's possible to comment out the offer creation and wait for an offer + * instead, so we handle either here. + * + * See tests/examples/webrtcbidirectional.c in gst-plugins-bad for another + * example how to handle offers from peers and reply with answers using webrtcbin. */ text = json_object_get_string_member (child, "sdp"); - - g_print ("Received answer:\n%s\n", text); - ret = gst_sdp_message_new (&sdp); g_assert_cmphex (ret, ==, GST_SDP_OK); - ret = gst_sdp_message_parse_buffer ((guint8 *) text, strlen (text), sdp); g_assert_cmphex (ret, ==, GST_SDP_OK); - answer = gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_ANSWER, - sdp); - g_assert_nonnull (answer); - - /* Set remote description on our pipeline */ - { - GstPromise *promise = gst_promise_new (); - g_signal_emit_by_name (webrtc1, "set-remote-description", answer, - promise); - gst_promise_interrupt (promise); - gst_promise_unref (promise); + if (g_str_equal (sdptype, "answer")) { + g_print ("Received answer:\n%s\n", text); + answer = gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_ANSWER, + sdp); + g_assert_nonnull (answer); + + /* Set remote description on our pipeline */ + { + GstPromise *promise = gst_promise_new (); + g_signal_emit_by_name (webrtc1, "set-remote-description", answer, + promise); + gst_promise_interrupt (promise); + gst_promise_unref (promise); + } + app_state = PEER_CALL_STARTED; + } + else { + g_print ("Received offer:\n%s\n", text); + on_offer_received (sdp); } - app_state = PEER_CALL_STARTED; } else if (json_object_has_member (object, "ice")) { const gchar *candidate; gint sdpmlineindex; diff --git a/webrtc/sendrecv/js/webrtc.js b/webrtc/sendrecv/js/webrtc.js index 124c10d7cf..471085eeff 100644 --- a/webrtc/sendrecv/js/webrtc.js +++ b/webrtc/sendrecv/js/webrtc.js @@ -93,12 +93,16 @@ function onIncomingSDP(sdp) { function onLocalDescription(desc) { console.log("Got local description: " + JSON.stringify(desc)); peer_connection.setLocalDescription(desc).then(function() { - setStatus("Sending SDP answer"); + setStatus("Sending SDP " + desc.type); sdp = {'sdp': peer_connection.localDescription} ws_conn.send(JSON.stringify(sdp)); }); } +function generateOffer() { + peer_connection.createOffer().then(onLocalDescription).catch(setError); +} + // ICE candidate received from peer, add it to the peer connection function onIncomingICE(ice) { var candidate = new RTCIceCandidate(ice); @@ -116,29 +120,36 @@ function onServerMessage(event) { handleIncomingError(event.data); return; } - // Handle incoming JSON SDP and ICE messages - try { - msg = JSON.parse(event.data); - } catch (e) { - if (e instanceof SyntaxError) { - handleIncomingError("Error parsing incoming JSON: " + event.data); - } else { - handleIncomingError("Unknown error parsing response: " + event.data); + if (event.data.startsWith("OFFER_REQUEST")) { + // The peer wants us to set up and then send an offer + if (!peer_connection) + createCall(null).then (generateOffer); + } + else { + // Handle incoming JSON SDP and ICE messages + try { + msg = JSON.parse(event.data); + } catch (e) { + if (e instanceof SyntaxError) { + handleIncomingError("Error parsing incoming JSON: " + event.data); + } else { + handleIncomingError("Unknown error parsing response: " + event.data); + } + return; } - return; - } - // Incoming JSON signals the beginning of a call - if (!peer_connection) - createCall(msg); + // Incoming JSON signals the beginning of a call + if (!peer_connection) + createCall(msg); - if (msg.sdp != null) { - onIncomingSDP(msg.sdp); - } else if (msg.ice != null) { - onIncomingICE(msg.ice); - } else { - handleIncomingError("Unknown incoming JSON: " + msg); - } + if (msg.sdp != null) { + onIncomingSDP(msg.sdp); + } else if (msg.ice != null) { + onIncomingICE(msg.ice); + } else { + handleIncomingError("Unknown incoming JSON: " + msg); + } + } } } @@ -286,7 +297,7 @@ function createCall(msg) { return stream; }).catch(setError); - if (!msg.sdp) { + if (msg != null && !msg.sdp) { console.log("WARNING: First message wasn't an SDP message!?"); } @@ -300,5 +311,8 @@ function createCall(msg) { ws_conn.send(JSON.stringify({'ice': event.candidate})); }; - setStatus("Created peer connection for call, waiting for SDP"); + if (msg != null) + setStatus("Created peer connection for call, waiting for SDP"); + + return local_stream_promise; } From 65db695212f9cd09fafb37e2ac19b5a42137faa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 24 Mar 2020 12:57:17 +0200 Subject: [PATCH 105/130] Set TURN server in Rust sendrecv example too Previously it was only in the multiparty example. --- webrtc/sendrecv/gst-rust/src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/webrtc/sendrecv/gst-rust/src/main.rs b/webrtc/sendrecv/gst-rust/src/main.rs index 48f9c38674..92e71b29b6 100644 --- a/webrtc/sendrecv/gst-rust/src/main.rs +++ b/webrtc/sendrecv/gst-rust/src/main.rs @@ -24,6 +24,7 @@ use serde_derive::{Deserialize, Serialize}; use anyhow::{anyhow, bail, Context}; const STUN_SERVER: &str = "stun://stun.l.google.com:19302"; +const TURN_SERVER: &str = "turn://foo:bar@webrtc.nirbheek.in:3478"; // upgrade weak reference or return #[macro_export] @@ -131,6 +132,7 @@ impl App { // Set some properties on webrtcbin webrtcbin.set_property_from_str("stun-server", STUN_SERVER); + webrtcbin.set_property_from_str("turn-server", TURN_SERVER); webrtcbin.set_property_from_str("bundle-policy", "max-bundle"); // Create a stream for handling the GStreamer message asynchronously From 804c0c2f5ead8264414e2a9b0cb46dc42124e56d Mon Sep 17 00:00:00 2001 From: Costa Shulyupin Date: Tue, 14 Apr 2020 13:49:41 +0300 Subject: [PATCH 106/130] gst-indent --- .../gst/mp-webrtc-sendrecv.c | 58 ++++++++++--------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c b/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c index d1d3561bdb..6e66fbd162 100644 --- a/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c +++ b/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c @@ -17,23 +17,24 @@ #include -enum AppState { +enum AppState +{ APP_STATE_UNKNOWN = 0, - APP_STATE_ERROR = 1, /* generic error */ + APP_STATE_ERROR = 1, /* generic error */ SERVER_CONNECTING = 1000, SERVER_CONNECTION_ERROR, - SERVER_CONNECTED, /* Ready to register */ + SERVER_CONNECTED, /* Ready to register */ SERVER_REGISTERING = 2000, SERVER_REGISTRATION_ERROR, - SERVER_REGISTERED, /* Ready to call a peer */ - SERVER_CLOSED, /* server connection closed by us or the server */ + SERVER_REGISTERED, /* Ready to call a peer */ + SERVER_CLOSED, /* server connection closed by us or the server */ ROOM_JOINING = 3000, ROOM_JOIN_ERROR, ROOM_JOINED, ROOM_CALL_NEGOTIATING = 4000, /* negotiating with some or all peers */ - ROOM_CALL_OFFERING, /* when we're the one sending the offer */ - ROOM_CALL_ANSWERING, /* when we're the one answering an offer */ - ROOM_CALL_STARTED, /* in a call with some or all peers */ + ROOM_CALL_OFFERING, /* when we're the one sending the offer */ + ROOM_CALL_ANSWERING, /* when we're the one answering an offer */ + ROOM_CALL_STARTED, /* in a call with some or all peers */ ROOM_CALL_STOPPING, ROOM_CALL_STOPPED, ROOM_CALL_ERROR, @@ -51,12 +52,14 @@ static gchar *local_id = NULL; static gchar *room_id = NULL; static gboolean strict_ssl = TRUE; -static GOptionEntry entries[] = -{ - { "name", 0, 0, G_OPTION_ARG_STRING, &local_id, "Name we will send to the server", "ID" }, - { "room-id", 0, 0, G_OPTION_ARG_STRING, &room_id, "Room name to join or create", "ID" }, - { "server", 0, 0, G_OPTION_ARG_STRING, &server_url, "Signalling server to connect to", "URL" }, - { NULL } +static GOptionEntry entries[] = { + {"name", 0, 0, G_OPTION_ARG_STRING, &local_id, + "Name we will send to the server", "ID"}, + {"room-id", 0, 0, G_OPTION_ARG_STRING, &room_id, + "Room name to join or create", "ID"}, + {"server", 0, 0, G_OPTION_ARG_STRING, &server_url, + "Signalling server to connect to", "URL"}, + {NULL} }; static gint @@ -97,7 +100,7 @@ cleanup_and_quit_loop (const gchar * msg, enum AppState state) return G_SOURCE_REMOVE; } -static gchar* +static gchar * get_string_from_json_object (JsonObject * object) { JsonNode *root; @@ -117,8 +120,8 @@ get_string_from_json_object (JsonObject * object) } static void -handle_media_stream (GstPad * pad, GstElement * pipe, const char * convert_name, - const char * sink_name) +handle_media_stream (GstPad * pad, GstElement * pipe, const char *convert_name, + const char *sink_name) { GstPad *qpad; GstElement *q, *conv, *sink; @@ -415,8 +418,7 @@ start_pipeline (void) * we can preroll early. */ pipeline = gst_parse_launch ("tee name=audiotee ! queue ! fakesink " "audiotestsrc is-live=true wave=red-noise ! queue ! opusenc ! rtpopuspay ! " - "queue ! " RTP_CAPS_OPUS(96) " ! audiotee. ", - &error); + "queue ! " RTP_CAPS_OPUS (96) " ! audiotee. ", &error); if (error) { g_printerr ("Failed to parse launch: %s\n", error->message); @@ -525,8 +527,7 @@ do_join_room (const gchar * text) g_print ("Room joined\n"); /* Start recording, but not transmitting */ if (!start_pipeline ()) { - cleanup_and_quit_loop ("ERROR: Failed to start pipeline", - ROOM_CALL_ERROR); + cleanup_and_quit_loop ("ERROR: Failed to start pipeline", ROOM_CALL_ERROR); return; } @@ -767,7 +768,7 @@ on_server_message (SoupWebsocketConnection * conn, SoupWebsocketDataType type, case SOUP_WEBSOCKET_DATA_BINARY: g_printerr ("Received unknown binary message, ignoring\n"); return; - case SOUP_WEBSOCKET_DATA_TEXT: { + case SOUP_WEBSOCKET_DATA_TEXT:{ gsize size; const gchar *data = g_bytes_get_data (message, &size); /* Convert to NULL-terminated string */ @@ -782,7 +783,7 @@ on_server_message (SoupWebsocketConnection * conn, SoupWebsocketDataType type, if (g_strcmp0 (text, "HELLO") == 0) { /* May fail asynchronously */ do_registration (); - /* Room-related message */ + /* Room-related message */ } else if (g_str_has_prefix (text, "ROOM_")) { /* Room joined, now we can start negotiation */ if (g_str_has_prefix (text, "ROOM_OK ")) { @@ -811,7 +812,7 @@ on_server_message (SoupWebsocketConnection * conn, SoupWebsocketDataType type, peers = g_list_remove (peers, peer_id); g_print ("Peer %s has left the room\n", peer_id); remove_peer_from_pipeline (peer_id); - g_free ((gchar*) peer_id); + g_free ((gchar *) peer_id); /* TODO: cleanup pipeline */ } else { g_printerr ("WARNING: Ignoring unknown message %s\n", text); @@ -820,7 +821,7 @@ on_server_message (SoupWebsocketConnection * conn, SoupWebsocketDataType type, } else { goto err; } - /* Handle errors */ + /* Handle errors */ } else if (g_str_has_prefix (text, "ERROR")) { handle_error_message (text); } else { @@ -842,7 +843,7 @@ err: static void on_server_connected (SoupSession * session, GAsyncResult * res, - SoupMessage *msg) + SoupMessage * msg) { GError *error = NULL; @@ -875,7 +876,7 @@ connect_to_websocket_server_async (void) SoupLogger *logger; SoupMessage *message; SoupSession *session; - const char *https_aliases[] = {"wss", NULL}; + const char *https_aliases[] = { "wss", NULL }; session = soup_session_new_with_options (SOUP_SESSION_SSL_STRICT, strict_ssl, SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE, @@ -904,7 +905,8 @@ check_plugins (void) GstPlugin *plugin; GstRegistry *registry; const gchar *needed[] = { "opus", "nice", "webrtc", "dtls", "srtp", - "rtpmanager", "audiotestsrc", NULL}; + "rtpmanager", "audiotestsrc", NULL + }; registry = gst_registry_get (); ret = TRUE; From ca96b6de860349aa7eba26bd55a59d01664a8025 Mon Sep 17 00:00:00 2001 From: Costa Shulyupin Date: Tue, 14 Apr 2020 13:49:48 +0300 Subject: [PATCH 107/130] gst-indent --- webrtc/sendonly/webrtc-unidirectional-h264.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/webrtc/sendonly/webrtc-unidirectional-h264.c b/webrtc/sendonly/webrtc-unidirectional-h264.c index e71ff39afb..e6f7a59ae0 100644 --- a/webrtc/sendonly/webrtc-unidirectional-h264.c +++ b/webrtc/sendonly/webrtc-unidirectional-h264.c @@ -186,7 +186,9 @@ create_receiver_entry (SoupWebsocketConnection * connection) G_CALLBACK (soup_websocket_message_cb), (gpointer) receiver_entry); error = NULL; - receiver_entry->pipeline = gst_parse_launch ("webrtcbin name=webrtcbin stun-server=stun://" STUN_SERVER " " + receiver_entry->pipeline = + gst_parse_launch ("webrtcbin name=webrtcbin stun-server=stun://" + STUN_SERVER " " "v4l2src ! videorate ! video/x-raw,width=640,height=360,framerate=15/1 ! videoconvert ! queue max-size-buffers=1 ! x264enc bitrate=600 speed-preset=ultrafast tune=zerolatency key-int-max=15 ! video/x-h264,profile=constrained-baseline ! queue max-size-time=100000000 ! h264parse ! " "rtph264pay config-interval=-1 name=payloader ! " "application/x-rtp,media=video,encoding-name=H264,payload=" @@ -201,7 +203,8 @@ create_receiver_entry (SoupWebsocketConnection * connection) gst_bin_get_by_name (GST_BIN (receiver_entry->pipeline), "webrtcbin"); g_assert (receiver_entry->webrtcbin != NULL); - g_signal_emit_by_name (receiver_entry->webrtcbin, "get-transceivers", &transceivers); + g_signal_emit_by_name (receiver_entry->webrtcbin, "get-transceivers", + &transceivers); g_assert (transceivers != NULL && transceivers->len > 0); trans = g_array_index (transceivers, GstWebRTCRTPTransceiver *, 0); trans->direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY; From 2557eab9d579e3de04a00b2c0b91266fee47aace Mon Sep 17 00:00:00 2001 From: Costa Shulyupin Date: Tue, 14 Apr 2020 13:49:55 +0300 Subject: [PATCH 108/130] gst-indent --- webrtc/sendrecv/gst/webrtc-sendrecv.c | 110 +++++++++++++------------- 1 file changed, 56 insertions(+), 54 deletions(-) diff --git a/webrtc/sendrecv/gst/webrtc-sendrecv.c b/webrtc/sendrecv/gst/webrtc-sendrecv.c index 39c4ef9f9a..584863d0c4 100644 --- a/webrtc/sendrecv/gst/webrtc-sendrecv.c +++ b/webrtc/sendrecv/gst/webrtc-sendrecv.c @@ -18,16 +18,17 @@ #include -enum AppState { +enum AppState +{ APP_STATE_UNKNOWN = 0, - APP_STATE_ERROR = 1, /* generic error */ + APP_STATE_ERROR = 1, /* generic error */ SERVER_CONNECTING = 1000, SERVER_CONNECTION_ERROR, - SERVER_CONNECTED, /* Ready to register */ + SERVER_CONNECTED, /* Ready to register */ SERVER_REGISTERING = 2000, SERVER_REGISTRATION_ERROR, - SERVER_REGISTERED, /* Ready to call a peer */ - SERVER_CLOSED, /* server connection closed by us or the server */ + SERVER_REGISTERED, /* Ready to call a peer */ + SERVER_CLOSED, /* server connection closed by us or the server */ PEER_CONNECTING = 3000, PEER_CONNECTION_ERROR, PEER_CONNECTED, @@ -49,13 +50,15 @@ static const gchar *server_url = "wss://webrtc.nirbheek.in:8443"; static gboolean disable_ssl = FALSE; static gboolean remote_is_offerer = FALSE; -static GOptionEntry entries[] = -{ - { "peer-id", 0, 0, G_OPTION_ARG_STRING, &peer_id, "String ID of the peer to connect to", "ID" }, - { "server", 0, 0, G_OPTION_ARG_STRING, &server_url, "Signalling server to connect to", "URL" }, - { "disable-ssl", 0, 0, G_OPTION_ARG_NONE, &disable_ssl, "Disable ssl", NULL }, - { "remote-offerer", 0, 0, G_OPTION_ARG_NONE, &remote_is_offerer, "Request that the peer generate the offer and we'll answer", NULL }, - { NULL }, +static GOptionEntry entries[] = { + {"peer-id", 0, 0, G_OPTION_ARG_STRING, &peer_id, + "String ID of the peer to connect to", "ID"}, + {"server", 0, 0, G_OPTION_ARG_STRING, &server_url, + "Signalling server to connect to", "URL"}, + {"disable-ssl", 0, 0, G_OPTION_ARG_NONE, &disable_ssl, "Disable ssl", NULL}, + {"remote-offerer", 0, 0, G_OPTION_ARG_NONE, &remote_is_offerer, + "Request that the peer generate the offer and we'll answer", NULL}, + {NULL}, }; static gboolean @@ -84,7 +87,7 @@ cleanup_and_quit_loop (const gchar * msg, enum AppState state) return G_SOURCE_REMOVE; } -static gchar* +static gchar * get_string_from_json_object (JsonObject * object) { JsonNode *root; @@ -104,8 +107,8 @@ get_string_from_json_object (JsonObject * object) } static void -handle_media_stream (GstPad * pad, GstElement * pipe, const char * convert_name, - const char * sink_name) +handle_media_stream (GstPad * pad, GstElement * pipe, const char *convert_name, + const char *sink_name) { GstPad *qpad; GstElement *q, *conv, *resample, *sink; @@ -215,13 +218,14 @@ send_ice_candidate_message (GstElement * webrtc G_GNUC_UNUSED, guint mlineindex, } static void -send_sdp_to_peer (GstWebRTCSessionDescription *desc) +send_sdp_to_peer (GstWebRTCSessionDescription * desc) { gchar *text; JsonObject *msg, *sdp; if (app_state < PEER_CALL_NEGOTIATING) { - cleanup_and_quit_loop ("Can't send SDP to peer, not in call", APP_STATE_ERROR); + cleanup_and_quit_loop ("Can't send SDP to peer, not in call", + APP_STATE_ERROR); return; } @@ -231,12 +235,10 @@ send_sdp_to_peer (GstWebRTCSessionDescription *desc) if (desc->type == GST_WEBRTC_SDP_TYPE_OFFER) { g_print ("Sending offer:\n%s\n", text); json_object_set_string_member (sdp, "type", "offer"); - } - else if (desc->type == GST_WEBRTC_SDP_TYPE_ANSWER) { + } else if (desc->type == GST_WEBRTC_SDP_TYPE_ANSWER) { g_print ("Sending answer:\n%s\n", text); json_object_set_string_member (sdp, "type", "answer"); - } - else { + } else { g_assert_not_reached (); } @@ -261,7 +263,7 @@ on_offer_created (GstPromise * promise, gpointer user_data) g_assert_cmphex (app_state, ==, PEER_CALL_NEGOTIATING); - g_assert_cmphex (gst_promise_wait(promise), ==, GST_PROMISE_RESULT_REPLIED); + g_assert_cmphex (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); @@ -288,7 +290,8 @@ on_negotiation_needed (GstElement * element, gpointer user_data) g_free (msg); } else { GstPromise *promise; - promise = gst_promise_new_with_change_func (on_offer_created, user_data, NULL);; + promise = + gst_promise_new_with_change_func (on_offer_created, user_data, NULL);; g_signal_emit_by_name (webrtc1, "create-offer", NULL, promise); } } @@ -306,7 +309,7 @@ data_channel_on_error (GObject * dc, gpointer user_data) static void data_channel_on_open (GObject * dc, gpointer user_data) { - GBytes *bytes = g_bytes_new ("data", strlen("data")); + GBytes *bytes = g_bytes_new ("data", strlen ("data")); g_print ("data channel opened\n"); g_signal_emit_by_name (dc, "send-string", "Hi! from GStreamer"); g_signal_emit_by_name (dc, "send-data", bytes); @@ -320,7 +323,7 @@ data_channel_on_close (GObject * dc, gpointer user_data) } static void -data_channel_on_message_string (GObject * dc, gchar *str, gpointer user_data) +data_channel_on_message_string (GObject * dc, gchar * str, gpointer user_data) { g_print ("Received data channel message: %s\n", str); } @@ -328,18 +331,19 @@ data_channel_on_message_string (GObject * dc, gchar *str, gpointer user_data) static void connect_data_channel_signals (GObject * data_channel) { - g_signal_connect (data_channel, "on-error", G_CALLBACK (data_channel_on_error), - NULL); + g_signal_connect (data_channel, "on-error", + G_CALLBACK (data_channel_on_error), NULL); g_signal_connect (data_channel, "on-open", G_CALLBACK (data_channel_on_open), NULL); - g_signal_connect (data_channel, "on-close", G_CALLBACK (data_channel_on_close), - NULL); - g_signal_connect (data_channel, "on-message-string", G_CALLBACK (data_channel_on_message_string), - NULL); + g_signal_connect (data_channel, "on-close", + G_CALLBACK (data_channel_on_close), NULL); + g_signal_connect (data_channel, "on-message-string", + G_CALLBACK (data_channel_on_message_string), NULL); } static void -on_data_channel (GstElement * webrtc, GObject * data_channel, gpointer user_data) +on_data_channel (GstElement * webrtc, GObject * data_channel, + gpointer user_data) { connect_data_channel_signals (data_channel); receive_channel = data_channel; @@ -352,8 +356,7 @@ on_ice_gathering_state_notify (GstElement * webrtcbin, GParamSpec * pspec, GstWebRTCICEGatheringState ice_gather_state; const gchar *new_state = "unknown"; - g_object_get (webrtcbin, "ice-gathering-state", &ice_gather_state, - NULL); + g_object_get (webrtcbin, "ice-gathering-state", &ice_gather_state, NULL); switch (ice_gather_state) { case GST_WEBRTC_ICE_GATHERING_STATE_NEW: new_state = "new"; @@ -375,12 +378,12 @@ start_pipeline (void) GError *error = NULL; pipe1 = - gst_parse_launch ("webrtcbin bundle-policy=max-bundle name=sendrecv " STUN_SERVER + gst_parse_launch ("webrtcbin bundle-policy=max-bundle name=sendrecv " + STUN_SERVER "videotestsrc is-live=true pattern=ball ! videoconvert ! queue ! vp8enc deadline=1 ! rtpvp8pay ! " "queue ! " RTP_CAPS_VP8 "96 ! sendrecv. " "audiotestsrc is-live=true wave=red-noise ! audioconvert ! audioresample ! queue ! opusenc ! rtpopuspay ! " - "queue ! " RTP_CAPS_OPUS "97 ! sendrecv. ", - &error); + "queue ! " RTP_CAPS_OPUS "97 ! sendrecv. ", &error); if (error) { g_printerr ("Failed to parse launch: %s\n", error->message); @@ -497,7 +500,7 @@ on_answer_created (GstPromise * promise, gpointer user_data) g_assert_cmphex (app_state, ==, PEER_CALL_NEGOTIATING); - g_assert_cmphex (gst_promise_wait(promise), ==, GST_PROMISE_RESULT_REPLIED); + g_assert_cmphex (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); @@ -514,7 +517,7 @@ on_answer_created (GstPromise * promise, gpointer user_data) } static void -on_offer_received (GstSDPMessage *sdp) +on_offer_received (GstSDPMessage * sdp) { GstWebRTCSessionDescription *offer = NULL; GstPromise *promise; @@ -525,15 +528,13 @@ on_offer_received (GstSDPMessage *sdp) /* Set remote description on our pipeline */ { promise = gst_promise_new (); - g_signal_emit_by_name (webrtc1, "set-remote-description", offer, - promise); + g_signal_emit_by_name (webrtc1, "set-remote-description", offer, promise); gst_promise_interrupt (promise); gst_promise_unref (promise); } gst_webrtc_session_description_free (offer); - promise = gst_promise_new_with_change_func (on_answer_created, NULL, - NULL); + promise = gst_promise_new_with_change_func (on_answer_created, NULL, NULL); g_signal_emit_by_name (webrtc1, "create-answer", NULL, promise); } @@ -548,7 +549,7 @@ on_server_message (SoupWebsocketConnection * conn, SoupWebsocketDataType type, case SOUP_WEBSOCKET_DATA_BINARY: g_printerr ("Received unknown binary message, ignoring\n"); return; - case SOUP_WEBSOCKET_DATA_TEXT: { + case SOUP_WEBSOCKET_DATA_TEXT:{ gsize size; const gchar *data = g_bytes_get_data (message, &size); /* Convert to NULL-terminated string */ @@ -573,7 +574,7 @@ on_server_message (SoupWebsocketConnection * conn, SoupWebsocketDataType type, cleanup_and_quit_loop ("ERROR: Failed to setup call", PEER_CALL_ERROR); goto out; } - /* Call has been setup by the server, now we can start negotiation */ + /* Call has been setup by the server, now we can start negotiation */ } else if (g_strcmp0 (text, "SESSION_OK") == 0) { if (app_state != PEER_CONNECTING) { cleanup_and_quit_loop ("ERROR: Received SESSION_OK when not calling", @@ -586,7 +587,7 @@ on_server_message (SoupWebsocketConnection * conn, SoupWebsocketDataType type, if (!start_pipeline ()) cleanup_and_quit_loop ("ERROR: failed to start pipeline", PEER_CALL_ERROR); - /* Handle errors */ + /* Handle errors */ } else if (g_str_has_prefix (text, "ERROR")) { switch (app_state) { case SERVER_CONNECTING: @@ -605,7 +606,7 @@ on_server_message (SoupWebsocketConnection * conn, SoupWebsocketDataType type, app_state = APP_STATE_ERROR; } cleanup_and_quit_loop (text, 0); - /* Look for JSON messages containing SDP and ICE candidates */ + /* Look for JSON messages containing SDP and ICE candidates */ } else { JsonNode *root; JsonObject *object, *child; @@ -659,7 +660,7 @@ on_server_message (SoupWebsocketConnection * conn, SoupWebsocketDataType type, answer = gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_ANSWER, sdp); g_assert_nonnull (answer); - + /* Set remote description on our pipeline */ { GstPromise *promise = gst_promise_new (); @@ -669,8 +670,7 @@ on_server_message (SoupWebsocketConnection * conn, SoupWebsocketDataType type, gst_promise_unref (promise); } app_state = PEER_CALL_STARTED; - } - else { + } else { g_print ("Received offer:\n%s\n", text); on_offer_received (sdp); } @@ -698,7 +698,7 @@ out: static void on_server_connected (SoupSession * session, GAsyncResult * res, - SoupMessage *msg) + SoupMessage * msg) { GError *error = NULL; @@ -730,9 +730,10 @@ connect_to_websocket_server_async (void) SoupLogger *logger; SoupMessage *message; SoupSession *session; - const char *https_aliases[] = {"wss", NULL}; + const char *https_aliases[] = { "wss", NULL }; - session = soup_session_new_with_options (SOUP_SESSION_SSL_STRICT, !disable_ssl, + session = + soup_session_new_with_options (SOUP_SESSION_SSL_STRICT, !disable_ssl, SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE, //SOUP_SESSION_SSL_CA_FILE, "/etc/ssl/certs/ca-bundle.crt", SOUP_SESSION_HTTPS_ALIASES, https_aliases, NULL); @@ -759,7 +760,8 @@ check_plugins (void) GstPlugin *plugin; GstRegistry *registry; const gchar *needed[] = { "opus", "vpx", "nice", "webrtc", "dtls", "srtp", - "rtpmanager", "videotestsrc", "audiotestsrc", NULL}; + "rtpmanager", "videotestsrc", "audiotestsrc", NULL + }; registry = gst_registry_get (); ret = TRUE; From 133a1593ee219b60173b367a42a5d814eef419c5 Mon Sep 17 00:00:00 2001 From: Costa Shulyupin Date: Tue, 14 Apr 2020 20:13:56 +0300 Subject: [PATCH 109/130] android, sendrecv: add missing break in switch case statements --- webrtc/android/app/src/main/jni/webrtc.c | 1 + webrtc/sendrecv/gst/webrtc-sendrecv.c | 1 + 2 files changed, 2 insertions(+) diff --git a/webrtc/android/app/src/main/jni/webrtc.c b/webrtc/android/app/src/main/jni/webrtc.c index badd485cb9..bfcd8b24b2 100644 --- a/webrtc/android/app/src/main/jni/webrtc.c +++ b/webrtc/android/app/src/main/jni/webrtc.c @@ -498,6 +498,7 @@ on_server_message (SoupWebsocketConnection * conn, SoupWebsocketDataType type, case PEER_CONNECTED: case PEER_CALL_NEGOTIATING: webrtc->app_state = PEER_CALL_ERROR; + break; default: webrtc->app_state = APP_STATE_ERROR; } diff --git a/webrtc/sendrecv/gst/webrtc-sendrecv.c b/webrtc/sendrecv/gst/webrtc-sendrecv.c index 584863d0c4..f2fef123cc 100644 --- a/webrtc/sendrecv/gst/webrtc-sendrecv.c +++ b/webrtc/sendrecv/gst/webrtc-sendrecv.c @@ -602,6 +602,7 @@ on_server_message (SoupWebsocketConnection * conn, SoupWebsocketDataType type, case PEER_CONNECTED: case PEER_CALL_NEGOTIATING: app_state = PEER_CALL_ERROR; + break; default: app_state = APP_STATE_ERROR; } From 8c4345da7d86686974e37b0cecd753fb6e20f3ac Mon Sep 17 00:00:00 2001 From: Costa Shulyupin Date: Tue, 14 Apr 2020 20:13:37 +0300 Subject: [PATCH 110/130] android, mp-webrtc-sendrecv, sendonly: cleanup webrtc-unidirectional-h264.c: removed empty lines android: removed unused var --- webrtc/android/app/src/main/jni/webrtc.c | 1 - webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c | 3 +-- webrtc/sendonly/webrtc-unidirectional-h264.c | 12 ------------ 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/webrtc/android/app/src/main/jni/webrtc.c b/webrtc/android/app/src/main/jni/webrtc.c index bfcd8b24b2..6bfd16a556 100644 --- a/webrtc/android/app/src/main/jni/webrtc.c +++ b/webrtc/android/app/src/main/jni/webrtc.c @@ -280,7 +280,6 @@ on_offer_created (GstPromise * promise, WebRTC * webrtc) { GstWebRTCSessionDescription *offer = NULL; const GstStructure *reply; - gchar *desc; g_assert (webrtc->app_state == PEER_CALL_NEGOTIATING); diff --git a/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c b/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c index 6e66fbd162..1c5b0ecc23 100644 --- a/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c +++ b/webrtc/multiparty-sendrecv/gst/mp-webrtc-sendrecv.c @@ -404,7 +404,6 @@ incoming_call_from_peer (const gchar * peer_id) #define STR(x) #x #define RTP_CAPS_OPUS(x) "application/x-rtp,media=audio,encoding-name=OPUS,payload=" STR(x) -#define RTP_CAPS_VP8(x) "application/x-rtp,media=video,encoding-name=VP8,payload=" STR(x) static gboolean start_pipeline (void) @@ -902,7 +901,6 @@ check_plugins (void) { int i; gboolean ret; - GstPlugin *plugin; GstRegistry *registry; const gchar *needed[] = { "opus", "nice", "webrtc", "dtls", "srtp", "rtpmanager", "audiotestsrc", NULL @@ -911,6 +909,7 @@ check_plugins (void) registry = gst_registry_get (); ret = TRUE; for (i = 0; i < g_strv_length ((gchar **) needed); i++) { + GstPlugin *plugin; plugin = gst_registry_find_plugin (registry, needed[i]); if (!plugin) { g_print ("Required gstreamer plugin '%s' not found\n", needed[i]); diff --git a/webrtc/sendonly/webrtc-unidirectional-h264.c b/webrtc/sendonly/webrtc-unidirectional-h264.c index e6f7a59ae0..bb9ca8e44b 100644 --- a/webrtc/sendonly/webrtc-unidirectional-h264.c +++ b/webrtc/sendonly/webrtc-unidirectional-h264.c @@ -11,14 +11,10 @@ #include #include - - #define RTP_PAYLOAD_TYPE "96" #define SOUP_HTTP_PORT 57778 #define STUN_SERVER "stun.l.google.com:19302" - - typedef struct _ReceiverEntry ReceiverEntry; ReceiverEntry *create_receiver_entry (SoupWebsocketConnection * connection); @@ -48,9 +44,6 @@ static gchar *get_string_from_json_object (JsonObject * object); gboolean exit_sighandler (gpointer user_data); - - - struct _ReceiverEntry { SoupWebsocketConnection *connection; @@ -59,8 +52,6 @@ struct _ReceiverEntry GstElement *webrtcbin; }; - - const gchar *html_source = " \n \ \n \ \n \ @@ -166,9 +157,6 @@ const gchar *html_source = " \n \ \n \ "; - - - ReceiverEntry * create_receiver_entry (SoupWebsocketConnection * connection) { From 56a03add7873298399985c72c45daece18154855 Mon Sep 17 00:00:00 2001 From: Costa Shulyupin Date: Wed, 15 Apr 2020 11:08:40 +0300 Subject: [PATCH 111/130] html: charset Avoid warning: The character encoding of the HTML document was not declared. The document will render with garbled text in some browser configurations if the document contains characters from outside the US-ASCII range. The character encoding of the page must be declared in the document or in the transfer protocol. --- webrtc/sendrecv/js/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/webrtc/sendrecv/js/index.html b/webrtc/sendrecv/js/index.html index cdd3151a7c..8325070e8a 100644 --- a/webrtc/sendrecv/js/index.html +++ b/webrtc/sendrecv/js/index.html @@ -11,6 +11,7 @@ --> + From 37cf0dffb54be3a7ba319fb17a49aa265c5e92b5 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Thu, 12 Sep 2019 19:15:49 +1000 Subject: [PATCH 112/130] add __pycache__ to .gitignore --- webrtc/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/webrtc/.gitignore b/webrtc/.gitignore index 2747cceb90..99215a08a7 100644 --- a/webrtc/.gitignore +++ b/webrtc/.gitignore @@ -52,3 +52,4 @@ out/ # Our stuff *.pem webrtc-sendrecv +__pycache__ From bc821a85d4b418d2c4ff670f76e203fc0bfc3290 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Mon, 10 Sep 2018 18:08:15 +1000 Subject: [PATCH 113/130] tests: first pass at some basic browser tests --- webrtc/meson.build | 11 + webrtc/sendrecv/gst/meson.build | 4 + webrtc/sendrecv/gst/tests/basic.py | 144 +++++++++ webrtc/sendrecv/gst/tests/meson.build | 15 + ...{webrtc-sendrecv.py => webrtc_sendrecv.py} | 28 +- webrtc/signalling/generate_cert.sh | 11 +- webrtc/signalling/meson.build | 8 + webrtc/signalling/simple-server.py | 286 ----------------- webrtc/signalling/simple_server.py | 291 ++++++++++++++++++ 9 files changed, 505 insertions(+), 293 deletions(-) create mode 100644 webrtc/sendrecv/gst/tests/basic.py create mode 100644 webrtc/sendrecv/gst/tests/meson.build rename webrtc/sendrecv/gst/{webrtc-sendrecv.py => webrtc_sendrecv.py} (90%) create mode 100644 webrtc/signalling/meson.build delete mode 100755 webrtc/signalling/simple-server.py create mode 100755 webrtc/signalling/simple_server.py diff --git a/webrtc/meson.build b/webrtc/meson.build index 482b82498a..3ee35f7bd9 100644 --- a/webrtc/meson.build +++ b/webrtc/meson.build @@ -1,5 +1,6 @@ project('gstwebrtc-demo', 'c', meson_version : '>= 0.48', + license: 'BSD-2-Clause', default_options : [ 'warning_level=1', 'buildtype=debug' ]) @@ -24,5 +25,15 @@ libsoup_dep = dependency('libsoup-2.4', version : '>=2.48', json_glib_dep = dependency('json-glib-1.0', fallback : ['json-glib', 'json_glib_dep']) + +py3_mod = import('python3') +py3 = py3_mod.find_python() + +py3_version = py3_mod.language_version() +if py3_version.version_compare('< 3.6') + error('Could not find a sufficient python version required: 3.6, found {}'.format(py3_version)) +endif + subdir('multiparty-sendrecv') +subdir('signalling') subdir('sendrecv') diff --git a/webrtc/sendrecv/gst/meson.build b/webrtc/sendrecv/gst/meson.build index 7150e42b0c..85950add4a 100644 --- a/webrtc/sendrecv/gst/meson.build +++ b/webrtc/sendrecv/gst/meson.build @@ -1,3 +1,7 @@ executable('webrtc-sendrecv', 'webrtc-sendrecv.c', dependencies : [gst_dep, gstsdp_dep, gstwebrtc_dep, libsoup_dep, json_glib_dep ]) + +webrtc_py = files('webrtc_sendrecv.py') + +subdir('tests') diff --git a/webrtc/sendrecv/gst/tests/basic.py b/webrtc/sendrecv/gst/tests/basic.py new file mode 100644 index 0000000000..84ce81de76 --- /dev/null +++ b/webrtc/sendrecv/gst/tests/basic.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 + +import os +import unittest +from selenium import webdriver +from selenium.webdriver.support.wait import WebDriverWait +from selenium.webdriver.firefox.firefox_profile import FirefoxProfile +from selenium.webdriver.chrome.options import Options as COptions +import webrtc_sendrecv as webrtc +import simple_server as sserver +import asyncio +import threading +import signal + +import gi +gi.require_version('Gst', '1.0') +from gi.repository import Gst + +thread = None +stop = None +server = None + +class AsyncIOThread(threading.Thread): + def __init__ (self, loop): + threading.Thread.__init__(self) + self.loop = loop + + def run(self): + asyncio.set_event_loop(self.loop) + self.loop.run_forever() + self.loop.close() + print ("closed loop") + + def stop_thread(self): + self.loop.call_soon_threadsafe(self.loop.stop) + +async def run_until(server, stop_token): + async with server: + await stop_token + print ("run_until done") + +def setUpModule(): + global thread, server + Gst.init(None) + cacerts_path = os.environ.get('TEST_CA_CERT_PATH') + loop = asyncio.new_event_loop() + + thread = AsyncIOThread(loop) + thread.start() + server = sserver.WebRTCSimpleServer('127.0.0.1', 8443, 20, False, cacerts_path) + def f(): + global stop + stop = asyncio.ensure_future(server.run()) + loop.call_soon_threadsafe(f) + +def tearDownModule(): + global thread, stop + stop.cancel() + thread.stop_thread() + thread.join() + print("thread joined") + +def valid_int(n): + if isinstance(n, int): + return True + if isinstance(n, str): + try: + num = int(n) + return True + except: + return False + return False + +def create_firefox_driver(): + capabilities = webdriver.DesiredCapabilities().FIREFOX.copy() + capabilities['acceptSslCerts'] = True + capabilities['acceptInsecureCerts'] = True + profile = FirefoxProfile() + profile.set_preference ('media.navigator.streams.fake', True) + profile.set_preference ('media.navigator.permission.disabled', True) + + return webdriver.Firefox(firefox_profile=profile, capabilities=capabilities) + +def create_chrome_driver(): + capabilities = webdriver.DesiredCapabilities().CHROME.copy() + capabilities['acceptSslCerts'] = True + capabilities['acceptInsecureCerts'] = True + copts = COptions() + copts.add_argument('--allow-file-access-from-files') + copts.add_argument('--use-fake-ui-for-media-stream') + copts.add_argument('--use-fake-device-for-media-stream') + copts.add_argument('--enable-blink-features=RTCUnifiedPlanByDefault') + + return webdriver.Chrome(options=copts, desired_capabilities=capabilities) + +class ServerConnectionTestCase(unittest.TestCase): + def setUp(self): + self.browser = create_firefox_driver() +# self.browser = create_chrome_driver() + self.addCleanup(self.browser.quit) + self.html_source = os.environ.get('TEST_HTML_SOURCE') + self.assertIsNot(self.html_source, None) + self.assertNotEqual(self.html_source, '') + self.html_source = 'file://' + self.html_source + '/index.html' + + def get_peer_id(self): + self.browser.get(self.html_source) + peer_id = WebDriverWait(self.browser, 5).until( + lambda x: x.find_element_by_id('peer-id'), + message='Peer-id element was never seen' + ) + WebDriverWait (self.browser, 5).until( + lambda x: valid_int(peer_id.text), + message='Peer-id never became a number' + ) + return int(peer_id.text) + + def testPeerID(self): + self.get_peer_id() + + def testPerformCall(self): + loop = asyncio.new_event_loop() + thread = AsyncIOThread(loop) + thread.start() + peer_id = self.get_peer_id() + client = webrtc.WebRTCClient(peer_id + 1, peer_id, 'wss://127.0.0.1:8443') + + async def do_things(): + await client.connect() + async def stop_after(client, delay): + await asyncio.sleep(delay) + await client.stop() + future = asyncio.ensure_future (stop_after (client, 5)) + res = await client.loop() + thread.stop_thread() + return res + + res = asyncio.run_coroutine_threadsafe(do_things(), loop).result() + thread.join() + print ("client thread joined") + self.assertEqual(res, 0) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/webrtc/sendrecv/gst/tests/meson.build b/webrtc/sendrecv/gst/tests/meson.build new file mode 100644 index 0000000000..dfca6ebae7 --- /dev/null +++ b/webrtc/sendrecv/gst/tests/meson.build @@ -0,0 +1,15 @@ +tests = [ + ['basic', 'basic.py'], +] + +test_deps = [certs] + +foreach elem : tests + test(elem.get(0), + py3, + depends: test_deps, + args : files(elem.get(1)), + env : ['PYTHONPATH=' + join_paths(meson.source_root(), 'sendrecv', 'gst') + ':' + join_paths(meson.source_root(), 'signalling'), + 'TEST_HTML_SOURCE=' + join_paths(meson.source_root(), 'sendrecv', 'js'), + 'TEST_CA_CERT_PATH=' + join_paths(meson.build_root(), 'signalling')]) +endforeach diff --git a/webrtc/sendrecv/gst/webrtc-sendrecv.py b/webrtc/sendrecv/gst/webrtc_sendrecv.py similarity index 90% rename from webrtc/sendrecv/gst/webrtc-sendrecv.py rename to webrtc/sendrecv/gst/webrtc_sendrecv.py index b19bc3fa6f..b101e8cab9 100644 --- a/webrtc/sendrecv/gst/webrtc-sendrecv.py +++ b/webrtc/sendrecv/gst/webrtc_sendrecv.py @@ -23,6 +23,8 @@ webrtcbin name=sendrecv bundle-policy=max-bundle stun-server=stun://stun.l.googl queue ! application/x-rtp,media=audio,encoding-name=OPUS,payload=96 ! sendrecv. ''' +from websockets.version import version as wsv + class WebRTCClient: def __init__(self, id_, peer_id, server): self.id_ = id_ @@ -32,10 +34,11 @@ class WebRTCClient: self.peer_id = peer_id self.server = server or 'wss://webrtc.nirbheek.in:8443' + async def connect(self): sslctx = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH) self.conn = await websockets.connect(self.server, ssl=sslctx) - await self.conn.send('HELLO %d' % our_id) + await self.conn.send('HELLO %d' % self.id_) async def setup_call(self): await self.conn.send('SESSION {}'.format(self.peer_id)) @@ -46,6 +49,7 @@ class WebRTCClient: msg = json.dumps({'sdp': {'type': 'offer', 'sdp': text}}) loop = asyncio.new_event_loop() loop.run_until_complete(self.conn.send(msg)) + loop.close() def on_offer_created(self, promise, _, __): promise.wait() @@ -64,6 +68,7 @@ class WebRTCClient: icemsg = json.dumps({'ice': {'candidate': candidate, 'sdpMLineIndex': mlineindex}}) loop = asyncio.new_event_loop() loop.run_until_complete(self.conn.send(icemsg)) + loop.close() def on_incoming_decodebin_stream(self, _, pad): if not pad.has_current_caps(): @@ -113,7 +118,7 @@ class WebRTCClient: self.webrtc.connect('pad-added', self.on_incoming_stream) self.pipe.set_state(Gst.State.PLAYING) - async def handle_sdp(self, message): + def handle_sdp(self, message): assert (self.webrtc) msg = json.loads(message) if 'sdp' in msg: @@ -133,6 +138,11 @@ class WebRTCClient: sdpmlineindex = ice['sdpMLineIndex'] self.webrtc.emit('add-ice-candidate', sdpmlineindex, candidate) + def close_pipeline(self): + self.pipe.set_state(Gst.State.NULL) + self.pipe = None + self.webrtc = None + async def loop(self): assert self.conn async for message in self.conn: @@ -142,11 +152,18 @@ class WebRTCClient: self.start_pipeline() elif message.startswith('ERROR'): print (message) + self.close_pipeline() return 1 else: - await self.handle_sdp(message) + self.handle_sdp(message) + self.close_pipeline() return 0 + async def stop(self): + if self.conn: + await self.conn.close() + self.conn = None + def check_plugins(): needed = ["opus", "vpx", "nice", "webrtc", "dtls", "srtp", "rtp", @@ -168,6 +185,7 @@ if __name__=='__main__': args = parser.parse_args() our_id = random.randrange(10, 10000) c = WebRTCClient(our_id, args.peerid, args.server) - asyncio.get_event_loop().run_until_complete(c.connect()) - res = asyncio.get_event_loop().run_until_complete(c.loop()) + loop = asyncio.get_event_loop() + loop.run_until_complete(c.connect()) + res = loop.run_until_complete(c.loop()) sys.exit(res) diff --git a/webrtc/signalling/generate_cert.sh b/webrtc/signalling/generate_cert.sh index 68a4b96f3c..7f4084fffa 100755 --- a/webrtc/signalling/generate_cert.sh +++ b/webrtc/signalling/generate_cert.sh @@ -1,3 +1,10 @@ -#! /bin/bash +#! /bin/sh -openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes +BASE_DIR=$(dirname $0) + +OUTDIR="" +if [ $# -eq 1 ]; then + OUTDIR=$1/ +fi + +openssl req -x509 -newkey rsa:4096 -keyout ${OUTDIR}key.pem -out ${OUTDIR}cert.pem -days 365 -nodes -subj "/CN=example.com" diff --git a/webrtc/signalling/meson.build b/webrtc/signalling/meson.build new file mode 100644 index 0000000000..43a53bfe73 --- /dev/null +++ b/webrtc/signalling/meson.build @@ -0,0 +1,8 @@ +generate_certs = find_program('generate_cert.sh') +certs = custom_target( + 'generate-certs', + command: [generate_certs, '@OUTDIR@'], + output : ['key.pem', 'cert.pem'] +) + +simple_server = files('simple_server.py') diff --git a/webrtc/signalling/simple-server.py b/webrtc/signalling/simple-server.py deleted file mode 100755 index b337eae2bc..0000000000 --- a/webrtc/signalling/simple-server.py +++ /dev/null @@ -1,286 +0,0 @@ -#!/usr/bin/env python3 -# -# Example 1-1 call signalling server -# -# Copyright (C) 2017 Centricular Ltd. -# -# Author: Nirbheek Chauhan -# - -import os -import sys -import ssl -import logging -import asyncio -import websockets -import argparse -import http - -from concurrent.futures._base import TimeoutError - -parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) -# See: host, port in https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.create_server -parser.add_argument('--addr', default='', help='Address to listen on (default: all interfaces, both ipv4 and ipv6)') -parser.add_argument('--port', default=8443, type=int, help='Port to listen on') -parser.add_argument('--keepalive-timeout', dest='keepalive_timeout', default=30, type=int, help='Timeout for keepalive (in seconds)') -parser.add_argument('--cert-path', default=os.path.dirname(__file__)) -parser.add_argument('--disable-ssl', default=False, help='Disable ssl', action='store_true') -parser.add_argument('--health', default='/health', help='Health check route') - -options = parser.parse_args(sys.argv[1:]) - -ADDR_PORT = (options.addr, options.port) -KEEPALIVE_TIMEOUT = options.keepalive_timeout - -############### Global data ############### - -# Format: {uid: (Peer WebSocketServerProtocol, -# remote_address, -# <'session'|room_id|None>)} -peers = dict() -# Format: {caller_uid: callee_uid, -# callee_uid: caller_uid} -# Bidirectional mapping between the two peers -sessions = dict() -# Format: {room_id: {peer1_id, peer2_id, peer3_id, ...}} -# Room dict with a set of peers in each room -rooms = dict() - -############### Helper functions ############### - -async def health_check(path, request_headers): - if path == options.health: - return http.HTTPStatus.OK, [], b"OK\n" - -async def recv_msg_ping(ws, raddr): - ''' - Wait for a message forever, and send a regular ping to prevent bad routers - from closing the connection. - ''' - msg = None - while msg is None: - try: - msg = await asyncio.wait_for(ws.recv(), KEEPALIVE_TIMEOUT) - except TimeoutError: - print('Sending keepalive ping to {!r} in recv'.format(raddr)) - await ws.ping() - return msg - -async def disconnect(ws, peer_id): - ''' - Remove @peer_id from the list of sessions and close our connection to it. - This informs the peer that the session and all calls have ended, and it - must reconnect. - ''' - global sessions - if peer_id in sessions: - del sessions[peer_id] - # Close connection - if ws and ws.open: - # Don't care about errors - asyncio.ensure_future(ws.close(reason='hangup')) - -async def cleanup_session(uid): - if uid in sessions: - other_id = sessions[uid] - del sessions[uid] - print("Cleaned up {} session".format(uid)) - if other_id in sessions: - del sessions[other_id] - print("Also cleaned up {} session".format(other_id)) - # If there was a session with this peer, also - # close the connection to reset its state. - if other_id in peers: - print("Closing connection to {}".format(other_id)) - wso, oaddr, _ = peers[other_id] - del peers[other_id] - await wso.close() - -async def cleanup_room(uid, room_id): - room_peers = rooms[room_id] - if uid not in room_peers: - return - room_peers.remove(uid) - for pid in room_peers: - wsp, paddr, _ = peers[pid] - msg = 'ROOM_PEER_LEFT {}'.format(uid) - print('room {}: {} -> {}: {}'.format(room_id, uid, pid, msg)) - await wsp.send(msg) - -async def remove_peer(uid): - await cleanup_session(uid) - if uid in peers: - ws, raddr, status = peers[uid] - if status and status != 'session': - await cleanup_room(uid, status) - del peers[uid] - await ws.close() - print("Disconnected from peer {!r} at {!r}".format(uid, raddr)) - -############### Handler functions ############### - -async def connection_handler(ws, uid): - global peers, sessions, rooms - raddr = ws.remote_address - peer_status = None - peers[uid] = [ws, raddr, peer_status] - print("Registered peer {!r} at {!r}".format(uid, raddr)) - while True: - # Receive command, wait forever if necessary - msg = await recv_msg_ping(ws, raddr) - # Update current status - peer_status = peers[uid][2] - # We are in a session or a room, messages must be relayed - if peer_status is not None: - # We're in a session, route message to connected peer - if peer_status == 'session': - other_id = sessions[uid] - wso, oaddr, status = peers[other_id] - assert(status == 'session') - print("{} -> {}: {}".format(uid, other_id, msg)) - await wso.send(msg) - # We're in a room, accept room-specific commands - elif peer_status: - # ROOM_PEER_MSG peer_id MSG - if msg.startswith('ROOM_PEER_MSG'): - _, other_id, msg = msg.split(maxsplit=2) - if other_id not in peers: - await ws.send('ERROR peer {!r} not found' - ''.format(other_id)) - continue - wso, oaddr, status = peers[other_id] - if status != room_id: - await ws.send('ERROR peer {!r} is not in the room' - ''.format(other_id)) - continue - msg = 'ROOM_PEER_MSG {} {}'.format(uid, msg) - print('room {}: {} -> {}: {}'.format(room_id, uid, other_id, msg)) - await wso.send(msg) - elif msg == 'ROOM_PEER_LIST': - room_id = peers[peer_id][2] - room_peers = ' '.join([pid for pid in rooms[room_id] if pid != peer_id]) - msg = 'ROOM_PEER_LIST {}'.format(room_peers) - print('room {}: -> {}: {}'.format(room_id, uid, msg)) - await ws.send(msg) - else: - await ws.send('ERROR invalid msg, already in room') - continue - else: - raise AssertionError('Unknown peer status {!r}'.format(peer_status)) - # Requested a session with a specific peer - elif msg.startswith('SESSION'): - print("{!r} command {!r}".format(uid, msg)) - _, callee_id = msg.split(maxsplit=1) - if callee_id not in peers: - await ws.send('ERROR peer {!r} not found'.format(callee_id)) - continue - if peer_status is not None: - await ws.send('ERROR peer {!r} busy'.format(callee_id)) - continue - await ws.send('SESSION_OK') - wsc = peers[callee_id][0] - print('Session from {!r} ({!r}) to {!r} ({!r})' - ''.format(uid, raddr, callee_id, wsc.remote_address)) - # Register session - peers[uid][2] = peer_status = 'session' - sessions[uid] = callee_id - peers[callee_id][2] = 'session' - sessions[callee_id] = uid - # Requested joining or creation of a room - elif msg.startswith('ROOM'): - print('{!r} command {!r}'.format(uid, msg)) - _, room_id = msg.split(maxsplit=1) - # Room name cannot be 'session', empty, or contain whitespace - if room_id == 'session' or room_id.split() != [room_id]: - await ws.send('ERROR invalid room id {!r}'.format(room_id)) - continue - if room_id in rooms: - if uid in rooms[room_id]: - raise AssertionError('How did we accept a ROOM command ' - 'despite already being in a room?') - else: - # Create room if required - rooms[room_id] = set() - room_peers = ' '.join([pid for pid in rooms[room_id]]) - await ws.send('ROOM_OK {}'.format(room_peers)) - # Enter room - peers[uid][2] = peer_status = room_id - rooms[room_id].add(uid) - for pid in rooms[room_id]: - if pid == uid: - continue - wsp, paddr, _ = peers[pid] - msg = 'ROOM_PEER_JOINED {}'.format(uid) - print('room {}: {} -> {}: {}'.format(room_id, uid, pid, msg)) - await wsp.send(msg) - else: - print('Ignoring unknown message {!r} from {!r}'.format(msg, uid)) - -async def hello_peer(ws): - ''' - Exchange hello, register peer - ''' - raddr = ws.remote_address - hello = await ws.recv() - hello, uid = hello.split(maxsplit=1) - if hello != 'HELLO': - await ws.close(code=1002, reason='invalid protocol') - raise Exception("Invalid hello from {!r}".format(raddr)) - if not uid or uid in peers or uid.split() != [uid]: # no whitespace - await ws.close(code=1002, reason='invalid peer uid') - raise Exception("Invalid uid {!r} from {!r}".format(uid, raddr)) - # Send back a HELLO - await ws.send('HELLO') - return uid - -async def handler(ws, path): - ''' - All incoming messages are handled here. @path is unused. - ''' - raddr = ws.remote_address - print("Connected to {!r}".format(raddr)) - peer_id = await hello_peer(ws) - try: - await connection_handler(ws, peer_id) - except websockets.ConnectionClosed: - print("Connection to peer {!r} closed, exiting handler".format(raddr)) - finally: - await remove_peer(peer_id) - -sslctx = None -if not options.disable_ssl: - # Create an SSL context to be used by the websocket server - certpath = options.cert_path - print('Using TLS with keys in {!r}'.format(certpath)) - if 'letsencrypt' in certpath: - chain_pem = os.path.join(certpath, 'fullchain.pem') - key_pem = os.path.join(certpath, 'privkey.pem') - else: - chain_pem = os.path.join(certpath, 'cert.pem') - key_pem = os.path.join(certpath, 'key.pem') - - sslctx = ssl.create_default_context() - try: - sslctx.load_cert_chain(chain_pem, keyfile=key_pem) - except FileNotFoundError: - print("Certificates not found, did you run generate_cert.sh?") - sys.exit(1) - # FIXME - sslctx.check_hostname = False - sslctx.verify_mode = ssl.CERT_NONE - -print("Listening on https://{}:{}".format(*ADDR_PORT)) -# Websocket server -wsd = websockets.serve(handler, *ADDR_PORT, ssl=sslctx, process_request=health_check, - # Maximum number of messages that websockets will pop - # off the asyncio and OS buffers per connection. See: - # https://websockets.readthedocs.io/en/stable/api.html#websockets.protocol.WebSocketCommonProtocol - max_queue=16) - -logger = logging.getLogger('websockets.server') - -logger.setLevel(logging.ERROR) -logger.addHandler(logging.StreamHandler()) - -asyncio.get_event_loop().run_until_complete(wsd) -asyncio.get_event_loop().run_forever() diff --git a/webrtc/signalling/simple_server.py b/webrtc/signalling/simple_server.py new file mode 100755 index 0000000000..ead3034a36 --- /dev/null +++ b/webrtc/signalling/simple_server.py @@ -0,0 +1,291 @@ +#!/usr/bin/env python3 +# +# Example 1-1 call signalling server +# +# Copyright (C) 2017 Centricular Ltd. +# +# Author: Nirbheek Chauhan +# + +import os +import sys +import ssl +import logging +import asyncio +import websockets +import argparse +import http + +from concurrent.futures._base import TimeoutError + +class WebRTCSimpleServer(object): + + def __init__(self, addr, port, keepalive_timeout, disable_ssl, certpath, health_path=None): + ############### Global data ############### + + # Format: {uid: (Peer WebSocketServerProtocol, + # remote_address, + # <'session'|room_id|None>)} + self.peers = dict() + # Format: {caller_uid: callee_uid, + # callee_uid: caller_uid} + # Bidirectional mapping between the two peers + self.sessions = dict() + # Format: {room_id: {peer1_id, peer2_id, peer3_id, ...}} + # Room dict with a set of peers in each room + self.rooms = dict() + + self.keepalive_timeout = keepalive_timeout + self.addr = addr + self.port = port + self.disable_ssl = disable_ssl + self.certpath = certpath + self.health_path = health_path + + ############### Helper functions ############### + + async def health_check(self, path, request_headers): + if path == self.health_part: + return http.HTTPStatus.OK, [], b"OK\n" + return None + + async def recv_msg_ping(self, ws, raddr): + ''' + Wait for a message forever, and send a regular ping to prevent bad routers + from closing the connection. + ''' + msg = None + while msg is None: + try: + msg = await asyncio.wait_for(ws.recv(), self.keepalive_timeout) + except TimeoutError: + print('Sending keepalive ping to {!r} in recv'.format(raddr)) + await ws.ping() + return msg + + async def cleanup_session(self, uid): + if uid in self.sessions: + other_id = self.sessions[uid] + del self.sessions[uid] + print("Cleaned up {} session".format(uid)) + if other_id in self.sessions: + del self.sessions[other_id] + print("Also cleaned up {} session".format(other_id)) + # If there was a session with this peer, also + # close the connection to reset its state. + if other_id in self.peers: + print("Closing connection to {}".format(other_id)) + wso, oaddr, _ = self.peers[other_id] + del self.peers[other_id] + await wso.close() + + async def cleanup_room(self, uid, room_id): + room_peers = self.rooms[room_id] + if uid not in room_peers: + return + room_peers.remove(uid) + for pid in room_peers: + wsp, paddr, _ = self.peers[pid] + msg = 'ROOM_PEER_LEFT {}'.format(uid) + print('room {}: {} -> {}: {}'.format(room_id, uid, pid, msg)) + await wsp.send(msg) + + async def remove_peer(self, uid): + await self.cleanup_session(uid) + if uid in self.peers: + ws, raddr, status = self.peers[uid] + if status and status != 'session': + await self.cleanup_room(uid, status) + del self.peers[uid] + await ws.close() + print("Disconnected from peer {!r} at {!r}".format(uid, raddr)) + + ############### Handler functions ############### + + + async def connection_handler(self, ws, uid): + raddr = ws.remote_address + peer_status = None + self.peers[uid] = [ws, raddr, peer_status] + print("Registered peer {!r} at {!r}".format(uid, raddr)) + while True: + # Receive command, wait forever if necessary + msg = await self.recv_msg_ping(ws, raddr) + # Update current status + peer_status = self.peers[uid][2] + # We are in a session or a room, messages must be relayed + if peer_status is not None: + # We're in a session, route message to connected peer + if peer_status == 'session': + other_id = self.sessions[uid] + wso, oaddr, status = self.peers[other_id] + assert(status == 'session') + print("{} -> {}: {}".format(uid, other_id, msg)) + await wso.send(msg) + # We're in a room, accept room-specific commands + elif peer_status: + # ROOM_PEER_MSG peer_id MSG + if msg.startswith('ROOM_PEER_MSG'): + _, other_id, msg = msg.split(maxsplit=2) + if other_id not in self.peers: + await ws.send('ERROR peer {!r} not found' + ''.format(other_id)) + continue + wso, oaddr, status = self.peers[other_id] + if status != room_id: + await ws.send('ERROR peer {!r} is not in the room' + ''.format(other_id)) + continue + msg = 'ROOM_PEER_MSG {} {}'.format(uid, msg) + print('room {}: {} -> {}: {}'.format(room_id, uid, other_id, msg)) + await wso.send(msg) + elif msg == 'ROOM_PEER_LIST': + room_id = self.peers[peer_id][2] + room_peers = ' '.join([pid for pid in self.rooms[room_id] if pid != peer_id]) + msg = 'ROOM_PEER_LIST {}'.format(room_peers) + print('room {}: -> {}: {}'.format(room_id, uid, msg)) + await ws.send(msg) + else: + await ws.send('ERROR invalid msg, already in room') + continue + else: + raise AssertionError('Unknown peer status {!r}'.format(peer_status)) + # Requested a session with a specific peer + elif msg.startswith('SESSION'): + print("{!r} command {!r}".format(uid, msg)) + _, callee_id = msg.split(maxsplit=1) + if callee_id not in self.peers: + await ws.send('ERROR peer {!r} not found'.format(callee_id)) + continue + if peer_status is not None: + await ws.send('ERROR peer {!r} busy'.format(callee_id)) + continue + await ws.send('SESSION_OK') + wsc = self.peers[callee_id][0] + print('Session from {!r} ({!r}) to {!r} ({!r})' + ''.format(uid, raddr, callee_id, wsc.remote_address)) + # Register session + self.peers[uid][2] = peer_status = 'session' + self.sessions[uid] = callee_id + self.peers[callee_id][2] = 'session' + self.sessions[callee_id] = uid + # Requested joining or creation of a room + elif msg.startswith('ROOM'): + print('{!r} command {!r}'.format(uid, msg)) + _, room_id = msg.split(maxsplit=1) + # Room name cannot be 'session', empty, or contain whitespace + if room_id == 'session' or room_id.split() != [room_id]: + await ws.send('ERROR invalid room id {!r}'.format(room_id)) + continue + if room_id in self.rooms: + if uid in self.rooms[room_id]: + raise AssertionError('How did we accept a ROOM command ' + 'despite already being in a room?') + else: + # Create room if required + self.rooms[room_id] = set() + room_peers = ' '.join([pid for pid in self.rooms[room_id]]) + await ws.send('ROOM_OK {}'.format(room_peers)) + # Enter room + self.peers[uid][2] = peer_status = room_id + self.rooms[room_id].add(uid) + for pid in self.rooms[room_id]: + if pid == uid: + continue + wsp, paddr, _ = self.peers[pid] + msg = 'ROOM_PEER_JOINED {}'.format(uid) + print('room {}: {} -> {}: {}'.format(room_id, uid, pid, msg)) + await wsp.send(msg) + else: + print('Ignoring unknown message {!r} from {!r}'.format(msg, uid)) + + async def hello_peer(self, ws): + ''' + Exchange hello, register peer + ''' + raddr = ws.remote_address + hello = await ws.recv() + hello, uid = hello.split(maxsplit=1) + if hello != 'HELLO': + await ws.close(code=1002, reason='invalid protocol') + raise Exception("Invalid hello from {!r}".format(raddr)) + if not uid or uid in self.peers or uid.split() != [uid]: # no whitespace + await ws.close(code=1002, reason='invalid peer uid') + raise Exception("Invalid uid {!r} from {!r}".format(uid, raddr)) + # Send back a HELLO + await ws.send('HELLO') + return uid + + def run(self): + sslctx = None + if not self.disable_ssl: + # Create an SSL context to be used by the websocket server + print('Using TLS with keys in {!r}'.format(self.certpath)) + if 'letsencrypt' in self.certpath: + chain_pem = os.path.join(self.certpath, 'fullchain.pem') + key_pem = os.path.join(self.certpath, 'privkey.pem') + else: + chain_pem = os.path.join(self.certpath, 'cert.pem') + key_pem = os.path.join(self.certpath, 'key.pem') + + sslctx = ssl.create_default_context() + try: + sslctx.load_cert_chain(chain_pem, keyfile=key_pem) + except FileNotFoundError: + print("Certificates not found, did you run generate_cert.sh?") + sys.exit(1) + # FIXME + sslctx.check_hostname = False + sslctx.verify_mode = ssl.CERT_NONE + + async def handler(ws, path): + ''' + All incoming messages are handled here. @path is unused. + ''' + raddr = ws.remote_address + print("Connected to {!r}".format(raddr)) + peer_id = await self.hello_peer(ws) + try: + await self.connection_handler(ws, peer_id) + except websockets.ConnectionClosed: + print("Connection to peer {!r} closed, exiting handler".format(raddr)) + finally: + await self.remove_peer(peer_id) + + print("Listening on https://{}:{}".format(self.addr, self.port)) + # Websocket server + wsd = websockets.serve(handler, self.addr, self.port, ssl=sslctx, process_request=self.health_check if self.health_path else None, + # Maximum number of messages that websockets will pop + # off the asyncio and OS buffers per connection. See: + # https://websockets.readthedocs.io/en/stable/api.html#websockets.protocol.WebSocketCommonProtocol + max_queue=16) + + logger = logging.getLogger('websockets.server') + + logger.setLevel(logging.ERROR) + logger.addHandler(logging.StreamHandler()) + + return wsd + +def main(): + parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) + # See: host, port in https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.create_server + parser.add_argument('--addr', default='', help='Address to listen on (default: all interfaces, both ipv4 and ipv6)') + parser.add_argument('--port', default=8443, type=int, help='Port to listen on') + parser.add_argument('--keepalive-timeout', dest='keepalive_timeout', default=30, type=int, help='Timeout for keepalive (in seconds)') + parser.add_argument('--cert-path', default=os.path.dirname(__file__)) + parser.add_argument('--disable-ssl', default=False, help='Disable ssl', action='store_true') + parser.add_argument('--health', default='/health', help='Health check route') + + options = parser.parse_args(sys.argv[1:]) + + loop = asyncio.get_event_loop() + + r = WebRTCSimpleServer(options.addr, options.port, options.keepalive_timeout, options.disable_ssl, options.cert_path) + + loop.run_until_complete (r.run()) + loop.run_forever () + print ("Goodbye!") + +if __name__ == "__main__": + main() From c3f629340d3ba6ad2b4b1db863a76b4a8c98ff61 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Mon, 17 Dec 2018 22:34:10 +1100 Subject: [PATCH 114/130] check: first pass at a couple of validate tests --- webrtc/{sendrecv/gst/tests => check}/basic.py | 0 .../{sendrecv/gst/tests => check}/meson.build | 0 webrtc/check/validate/README.md | 17 + webrtc/check/validate/actions.py | 77 +++++ webrtc/check/validate/apps/__init__.py | 0 webrtc/check/validate/apps/gstwebrtc.py | 187 ++++++++++ webrtc/check/validate/browser.py | 79 +++++ webrtc/check/validate/client.py | 212 ++++++++++++ webrtc/check/validate/enums.py | 36 ++ webrtc/check/validate/observer.py | 43 +++ .../validate/scenarios/offer_answer.scenario | 3 + .../scenarios/vp8_send_stream.scenario | 5 + webrtc/check/validate/signalling.py | 161 +++++++++ webrtc/check/validate/testsuites/__init__.py | 0 webrtc/check/validate/testsuites/webrtc.py | 36 ++ webrtc/check/validate/web/single_stream.html | 35 ++ webrtc/check/validate/web/webrtc.js | 320 ++++++++++++++++++ webrtc/check/validate/webrtc_validate.py | 203 +++++++++++ webrtc/meson.build | 2 + webrtc/sendrecv/gst/meson.build | 2 - 20 files changed, 1416 insertions(+), 2 deletions(-) rename webrtc/{sendrecv/gst/tests => check}/basic.py (100%) rename webrtc/{sendrecv/gst/tests => check}/meson.build (100%) create mode 100644 webrtc/check/validate/README.md create mode 100644 webrtc/check/validate/actions.py create mode 100644 webrtc/check/validate/apps/__init__.py create mode 100644 webrtc/check/validate/apps/gstwebrtc.py create mode 100644 webrtc/check/validate/browser.py create mode 100644 webrtc/check/validate/client.py create mode 100644 webrtc/check/validate/enums.py create mode 100644 webrtc/check/validate/observer.py create mode 100644 webrtc/check/validate/scenarios/offer_answer.scenario create mode 100644 webrtc/check/validate/scenarios/vp8_send_stream.scenario create mode 100644 webrtc/check/validate/signalling.py create mode 100644 webrtc/check/validate/testsuites/__init__.py create mode 100644 webrtc/check/validate/testsuites/webrtc.py create mode 100644 webrtc/check/validate/web/single_stream.html create mode 100644 webrtc/check/validate/web/webrtc.js create mode 100644 webrtc/check/validate/webrtc_validate.py diff --git a/webrtc/sendrecv/gst/tests/basic.py b/webrtc/check/basic.py similarity index 100% rename from webrtc/sendrecv/gst/tests/basic.py rename to webrtc/check/basic.py diff --git a/webrtc/sendrecv/gst/tests/meson.build b/webrtc/check/meson.build similarity index 100% rename from webrtc/sendrecv/gst/tests/meson.build rename to webrtc/check/meson.build diff --git a/webrtc/check/validate/README.md b/webrtc/check/validate/README.md new file mode 100644 index 0000000000..ea071532cc --- /dev/null +++ b/webrtc/check/validate/README.md @@ -0,0 +1,17 @@ +# What is this? + +The entire contents of this folder perform testing of GStreamer's webrtc +implementation against browser implementations using the selenium webdriver +testing framework. + +# Dependencies: + +- gst-validate: https://gitlab.freedesktop.org/gstreamer/gst-devtools/tree/master/validate +- gst-python: https://gitlab.freedesktop.org/gstreamer/gst-python/ +- selenium: https://www.seleniumhq.org/projects/webdriver/ +- selenium python bindings +- chrome and firefox with webdriver support + +# Run the tests + +`GST_VALIDATE_APPS_DIR=/path/to/gstwebrtc-demos/check/validate/apps/ GST_VALIDATE_SCENARIOS_PATH=/path/to/gstwebrtc-demos/check/validate/scenarios/ gst-validate-launcher --testsuites-dir /path/to/gstwebrtc-demos/check/validate/testsuites/ webrtc` diff --git a/webrtc/check/validate/actions.py b/webrtc/check/validate/actions.py new file mode 100644 index 0000000000..6ced2cfa7c --- /dev/null +++ b/webrtc/check/validate/actions.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2018, Matthew Waters +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. + +import gi +gi.require_version("GstValidate", "1.0") +from gi.repository import GstValidate + +from observer import Signal + +class ActionObserver(object): + def __init__(self): + def _action_continue(val): + return val not in [GstValidate.ActionReturn.ERROR, GstValidate.ActionReturn.ERROR_REPORTED] + def _action_accum(previous, val): + # we want to always keep any errors propagated + if val in [GstValidate.ActionReturn.ERROR, GstValidate.ActionReturn.ERROR_REPORTED]: + return val + if previous in [GstValidate.ActionReturn.ERROR, GstValidate.ActionReturn.ERROR_REPORTED]: + return previous + + # we want to always prefer async returns + if previous in [GstValidate.ActionReturn.ASYNC, GstValidate.ActionReturn.INTERLACED]: + return previous + if val in [GstValidate.ActionReturn.ASYNC, GstValidate.ActionReturn.INTERLACED]: + return val + + return val + + self.create_offer = Signal(_action_continue, _action_accum) + self.wait_for_negotiation_state = Signal(_action_continue, _action_accum) + self.add_stream = Signal(_action_continue, _action_accum) + self.wait_for_remote_state = Signal(_action_continue, _action_accum) + + def _create_offer(self, scenario, action): + print("action create-offer") + return self.create_offer.fire() + def _wait_for_negotiation_state(self, scenario, action): + state = action.structure["state"] + print("action wait-for-negotiation-state", state) + return self.wait_for_negotiation_state.fire(state) + def _add_stream(self, scenario, action): + pipeline = action.structure["pipeline"] + print("action add-stream", pipeline) + return self.add_stream.fire(pipeline) + +def register_action_types(observer): + if not isinstance(observer, ActionObserver): + raise TypeError + + GstValidate.register_action_type("create-offer", "webrtc", + observer._create_offer, None, + "Instruct a create-offer to commence", + GstValidate.ActionTypeFlags.NONE) + GstValidate.register_action_type("wait-for-negotiation-state", "webrtc", + observer._wait_for_negotiation_state, None, + "Wait for a specific negotiation state to be reached", + GstValidate.ActionTypeFlags.NONE) + GstValidate.register_action_type("add-stream", "webrtc", + observer._add_stream, None, + "Add a stream to the webrtcbin", + GstValidate.ActionTypeFlags.NONE) diff --git a/webrtc/check/validate/apps/__init__.py b/webrtc/check/validate/apps/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/webrtc/check/validate/apps/gstwebrtc.py b/webrtc/check/validate/apps/gstwebrtc.py new file mode 100644 index 0000000000..693ac4aea6 --- /dev/null +++ b/webrtc/check/validate/apps/gstwebrtc.py @@ -0,0 +1,187 @@ +#*- vi:si:et:sw=4:sts=4:ts=4:syntax=python +# +# Copyright (c) 2018 Matthew Waters +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. + +import inspect +import os +import sys +import shutil + +import tempfile + +from launcher.baseclasses import TestsManager, TestsGenerator, GstValidateTest, ScenarioManager +from launcher.utils import DEFAULT_TIMEOUT + +DEFAULT_BROWSERS = ['firefox', 'chrome'] +DEFAULT_SCENARIOS = [ + "offer_answer", + "vp8_send_stream" + ] + +BROWSER_SCENARIO_BLACKLISTS = { + 'firefox' : [ + 'offer_answer', # fails to accept an SDP without any media sections + ], + 'chrome' : [ + ], +} + +class MutableInt(object): + def __init__(self, value): + self.value = value + +class GstWebRTCTest(GstValidateTest): + __used_ports = set() + __last_id = MutableInt(10) + + @classmethod + def __get_open_port(cls): + while True: + # hackish trick from + # http://stackoverflow.com/questions/2838244/get-open-tcp-port-in-python?answertab=votes#tab-top + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.bind(("", 0)) + port = s.getsockname()[1] + if port not in cls.__used_ports: + cls.__used_ports.add(port) + s.close() + return port + + s.close() + + @classmethod + def __get_available_peer_id(cls): + peerid = cls.__last_id.value + cls.__last_id.value += 2 + return peerid + + def __init__(self, classname, tests_manager, scenario, browser, timeout=DEFAULT_TIMEOUT): + super().__init__("python3", + classname, + tests_manager.options, + tests_manager.reporter, + timeout=timeout, + scenario=scenario) + self.webrtc_server = None + filename = inspect.getframeinfo (inspect.currentframe ()).filename + self.current_file_path = os.path.dirname (os.path.abspath (filename)) + self.certdir = None + self.browser = browser + + def launch_server(self): + if self.options.redirect_logs == 'stdout': + self.webrtcserver_logs = sys.stdout + elif self.options.redirect_logs == 'stderr': + self.webrtcserver_logs = sys.stderr + else: + self.webrtcserver_logs = open(self.logfile + '_webrtcserver.log', 'w+') + self.extra_logfiles.add(self.webrtcserver_logs.name) + + generate_certs_location = os.path.join(self.current_file_path, "..", "..", "..", "signalling", "generate_cert.sh") + self.certdir = tempfile.mkdtemp() + command = [generate_certs_location, self.certdir] + + server_env = os.environ.copy() + + subprocess.run(command, + stderr=self.webrtcserver_logs, + stdout=self.webrtcserver_logs, + env=server_env) + + self.server_port = self.__get_open_port() + + server_location = os.path.join(self.current_file_path, "..", "..", "..", "signalling", "simple_server.py") + command = [server_location, "--cert-path", self.certdir, "--addr", "127.0.0.1", "--port", str(self.server_port)] + + self.webrtc_server = subprocess.Popen(command, + stderr=self.webrtcserver_logs, + stdout=self.webrtcserver_logs, + env=server_env) + while True: + s = socket.socket() + try: + s.connect((("127.0.0.1", self.server_port))) + break + except ConnectionRefusedError: + time.sleep(0.1) + continue + finally: + s.close() + + return ' '.join(command) + + def build_arguments(self): + gst_id = self.__get_available_peer_id() + web_id = gst_id + 1 + + self.add_arguments(os.path.join(self.current_file_path, '..', 'webrtc_validate.py')) + self.add_arguments('--server') + self.add_arguments("wss://127.0.0.1:%s" % (self.server_port,)) + self.add_arguments('--browser') + self.add_arguments(self.browser) + self.add_arguments("--html-source") + html_page = os.path.join(self.current_file_path, '..', 'web', 'single_stream.html') + html_params = '?server=127.0.0.1&port=' + str(self.server_port) + '&id=' + str(web_id) + self.add_arguments("file://" + html_page + html_params) + self.add_arguments('--peer-id') + self.add_arguments(str(web_id)) + self.add_arguments(str(gst_id)) + + def close_logfile(self): + super().close_logfile() + if not self.options.redirect_logs: + self.webrtcserver_logs.close() + + def process_update(self): + res = super().process_update() + if res: + kill_subprocess(self, self.webrtc_server, DEFAULT_TIMEOUT) + self.__used_ports.remove(self.server_port) + if self.certdir: + shutil.rmtree(self.certdir, ignore_errors=True) + self.certdir + + return res + +class GstWebRTCTestsManager(TestsManager): + scenarios_manager = ScenarioManager() + name = "webrtc" + + def __init__(self): + super(GstWebRTCTestsManager, self).__init__() + self.loading_testsuite = self.name + + def webrtc_server_address(self): + return "wss://127.0.0.1:8443" + + def populate_testsuite(self): + self.add_scenarios (DEFAULT_SCENARIOS) + + scenarios = [(scenario_name, self.scenarios_manager.get_scenario(scenario_name)) + for scenario_name in self.get_scenarios()] + + for name, scenario in scenarios: + if not scenario: + self.warning("Could not find scenario %s" % name) + continue + for browser in DEFAULT_BROWSERS: + if name in BROWSER_SCENARIO_BLACKLISTS[browser]: + self.warning('Skipping broken test', name, 'for browser', browser) + continue + classname = browser + '_' + name + self.add_test(GstWebRTCTest(classname, self, scenario, browser)) diff --git a/webrtc/check/validate/browser.py b/webrtc/check/validate/browser.py new file mode 100644 index 0000000000..536a0319c4 --- /dev/null +++ b/webrtc/check/validate/browser.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2018, Matthew Waters +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. + +from selenium import webdriver +from selenium.webdriver.support.wait import WebDriverWait +from selenium.webdriver.firefox.firefox_profile import FirefoxProfile +from selenium.webdriver.chrome.options import Options as COptions + +def create_firefox_driver(): + capabilities = webdriver.DesiredCapabilities().FIREFOX.copy() + capabilities['acceptSslCerts'] = True + profile = FirefoxProfile() + profile.set_preference ('media.navigator.streams.fake', True) + profile.set_preference ('media.navigator.permission.disabled', True) + + return webdriver.Firefox(firefox_profile=profile, capabilities=capabilities) + +def create_chrome_driver(): + capabilities = webdriver.DesiredCapabilities().CHROME.copy() + capabilities['acceptSslCerts'] = True + copts = COptions() + copts.add_argument('--allow-file-access-from-files') + copts.add_argument('--use-fake-ui-for-media-stream') + copts.add_argument('--use-fake-device-for-media-stream') + copts.add_argument('--enable-blink-features=RTCUnifiedPlanByDefault') + + return webdriver.Chrome(options=copts, desired_capabilities=capabilities) + +def create_driver(name): + if name == 'firefox': + return create_firefox_driver() + elif name == 'chrome': + return create_chrome_driver() + else: + raise ValueError("Unknown browser name " + name) + +def valid_int(n): + if isinstance(n, int): + return True + if isinstance(n, str): + try: + num = int(n) + return True + except: + return False + return False + +class Browser(object): + def __init__(self, driver, html_source): + self.driver = driver + self.html_source = html_source + + def get_peer_id(self): + self.driver.get(self.html_source) + peer_id = WebDriverWait(self.driver, 10).until( + lambda x: x.find_element_by_id('peer-id'), + message='Peer-id element was never seen' + ) + WebDriverWait (self.driver, 10).until( + lambda x: valid_int(peer_id.text), + message='Peer-id never became a number' + ) + return int(peer_id.text) diff --git a/webrtc/check/validate/client.py b/webrtc/check/validate/client.py new file mode 100644 index 0000000000..24c8bcd7bc --- /dev/null +++ b/webrtc/check/validate/client.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2018, Matthew Waters +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. + +import threading +import copy + +from observer import Signal +from enums import NegotiationState + +import gi +gi.require_version("Gst", "1.0") +from gi.repository import Gst +gi.require_version("GstWebRTC", "1.0") +from gi.repository import GstWebRTC +gi.require_version("GstSdp", "1.0") +from gi.repository import GstSdp +gi.require_version("GstValidate", "1.0") +from gi.repository import GstValidate + +class WebRTCBinObserver(object): + def __init__(self, element): + self.state = NegotiationState.NEW + self.state_cond = threading.Condition() + self.element = element + self.signal_handlers = [] + self.signal_handlers.append(element.connect("on-negotiation-needed", self._on_negotiation_needed)) + self.signal_handlers.append(element.connect("on-ice-candidate", self._on_ice_candidate)) + self.signal_handlers.append(element.connect("pad-added", self._on_pad_added)) + self.signal_handlers.append(element.connect("on-new-transceiver", self._on_new_transceiver)) + self.signal_handlers.append(element.connect("on-data-channel", self._on_data_channel)) + self.on_offer_created = Signal() + self.on_answer_created = Signal() + self.on_negotiation_needed = Signal() + self.on_ice_candidate = Signal() + self.on_pad_added = Signal() + self.on_new_transceiver = Signal() + self.on_data_channel = Signal() + self.on_local_description_set = Signal() + self.on_remote_description_set = Signal() + + def _on_negotiation_needed(self, element): + self.on_negotiation_needed.fire() + + def _on_ice_candidate(self, element, mline, candidate): + self.on_ice_candidate.fire(mline, candidate) + + def _on_pad_added(self, element, pad): + self.on_pad_added.fire(pad) + + def _on_local_description_set(self, promise, desc): + self._update_negotiation_from_description_state(desc) + self.on_local_description_set.fire(desc) + + def _on_remote_description_set(self, promise, desc): + self._update_negotiation_from_description_state(desc) + self.on_remote_description_set.fire(desc) + + def _on_new_transceiver(self, element, transceiver): + self.on_new_transceiver.fire(transceiver) + + def _on_data_channel(self, element): + self.on_data_channel.fire(desc) + + def _update_negotiation_state(self, new_state): + with self.state_cond: + old_state = self.state + self.state = new_state + self.state_cond.notify_all() + print ("observer updated state to", new_state) + + def wait_for_negotiation_states(self, states): + ret = None + with self.state_cond: + while self.state not in states: + self.state_cond.wait() + print ("observer waited for", states) + ret = self.state + return ret + + def _update_negotiation_from_description_state(self, desc): + new_state = None + if desc.type == GstWebRTC.WebRTCSDPType.OFFER: + new_state = NegotiationState.OFFER_SET + elif desc.type == GstWebRTC.WebRTCSDPType.ANSWER: + new_state = NegotiationState.ANSWER_SET + assert new_state is not None + self._update_negotiation_state(new_state) + + def _deepcopy_session_description(self, desc): + # XXX: passing 'offer' to both a promise and an action signal without + # a deepcopy will segfault... + new_sdp = GstSdp.SDPMessage.new()[1] + GstSdp.sdp_message_parse_buffer(bytes(desc.sdp.as_text().encode()), new_sdp) + return GstWebRTC.WebRTCSessionDescription.new(desc.type, new_sdp) + + def _on_offer_created(self, promise, element): + self._update_negotiation_state(NegotiationState.OFFER_CREATED) + reply = promise.get_reply() + offer = reply['offer'] + + new_offer = self._deepcopy_session_description(offer) + promise = Gst.Promise.new_with_change_func(self._on_local_description_set, new_offer) + + new_offer = self._deepcopy_session_description(offer) + self.element.emit('set-local-description', new_offer, promise) + + self.on_offer_created.fire(offer) + + def _on_answer_created(self, promise, element): + self._update_negotiation_state(NegotiationState.ANSWER_CREATED) + reply = promise.get_reply() + offer = reply['answer'] + + new_offer = self._deepcopy_session_description(offer) + promise = Gst.Promise.new_with_change_func(self._on_local_description_set, new_offer) + + new_offer = self._deepcopy_session_description(offer) + self.element.emit('set-local-description', new_offer, promise) + + self.on_answer_created.fire(offer) + + def create_offer(self, options=None): + promise = Gst.Promise.new_with_change_func(self._on_offer_created, self.element) + self.element.emit('create-offer', options, promise) + + def create_answer(self, options=None): + promise = Gst.Promise.new_with_change_func(self._on_answer_created, self.element) + self.element.emit('create-answer', options, promise) + + def set_remote_description(self, desc): + promise = Gst.Promise.new_with_change_func(self._on_remote_description_set, desc) + self.element.emit('set-remote-description', desc, promise) + + def add_ice_candidate(self, mline, candidate): + self.element.emit('add-ice-candidate', mline, candidate) + +class WebRTCStream(object): + def __init__(self): + self.bin = None + + def set_description(self, desc): + assert self.bin is None + self.bin = Gst.parse_bin_from_description(desc, True) + + def add_and_link(self, parent, link): + assert self.bin is not None + self.bin.set_locked_state(True) + parent.add(self.bin) + src = self.bin.get_static_pad("src") + sink = self.bin.get_static_pad("sink") + assert src is None or sink is None + if src: + self.bin.link(link) + if sink: + link.link(self.bin) + self.bin.set_locked_state(False) + self.bin.sync_state_with_parent() + + def add_and_link_to(self, parent, link, pad): + assert self.bin is not None + self.bin.set_locked_state(True) + parent.add(self.bin) + src = self.bin.get_static_pad("src") + sink = self.bin.get_static_pad("sink") + assert src is None or sink is None + if pad.get_direction() == Gst.PadDirection.SRC: + assert sink is not None + pad.link(sink) + if pad.get_direction() == Gst.PadDirection.SINK: + assert src is not None + src.link(pad) + self.bin.set_locked_state(False) + self.bin.sync_state_with_parent() + +class WebRTCClient(WebRTCBinObserver): + def __init__(self): + self.pipeline = Gst.Pipeline(None) + self.webrtcbin = Gst.ElementFactory.make("webrtcbin") + super().__init__(self.webrtcbin) + self.pipeline.add(self.webrtcbin) + self._streams = [] + + def stop(self): + self.pipeline.set_state (Gst.State.NULL) + + def add_stream(self, desc): + stream = WebRTCStream() + stream.set_description(desc) + stream.add_and_link (self.pipeline, self.webrtcbin) + self._streams.append(stream) + + def add_stream_with_pad(self, desc, pad): + stream = WebRTCStream() + stream.set_description(desc) + stream.add_and_link_to (self.pipeline, self.webrtcbin, pad) + self._streams.append(stream) diff --git a/webrtc/check/validate/enums.py b/webrtc/check/validate/enums.py new file mode 100644 index 0000000000..14bc31a49d --- /dev/null +++ b/webrtc/check/validate/enums.py @@ -0,0 +1,36 @@ +# Copyright (c) 2018, Matthew Waters +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. + +class SignallingState(object): + NEW = "new" # no connection has been made + OPEN = "open" # websocket connection is open + ERROR = "error" # and error was thrown. overrides all others + HELLO = "hello" # hello was sent and received + SESSION = "session" # session setup was sent and received + +class NegotiationState(object): + NEW = "new" + ERROR = "error" + NEGOTIATION_NEEDED = "negotiation-needed" + OFFER_CREATED = "offer-created" + ANSWER_CREATED = "answer-created" + OFFER_SET = "offer-set" + ANSWER_SET = "answer-set" + +class RemoteState(object): + ERROR = "error" + REMOTE_STREAM_RECEIVED = "remote-stream-received" diff --git a/webrtc/check/validate/observer.py b/webrtc/check/validate/observer.py new file mode 100644 index 0000000000..b4f6be512e --- /dev/null +++ b/webrtc/check/validate/observer.py @@ -0,0 +1,43 @@ +# Copyright (c) 2018, Matthew Waters +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. + +class Signal(object): + def __init__(self, cont_func=None, accum_func=None): + self._handlers = [] + if not cont_func: + # by default continue when None/no return value is provided or + # True is returned + cont_func = lambda x: x is None or x + self.cont_func = cont_func + # default to accumulating truths + if not accum_func: + accum_func = lambda prev, v: prev and v + self.accum_func = accum_func + + def connect(self, handler): + self._handlers.append(handler) + + def disconnect(self, handler): + self._handlers.remove(handler) + + def fire(self, *args): + ret = None + for handler in self._handlers: + ret = self.accum_func(ret, handler(*args)) + if not self.cont_func(ret): + break + return ret diff --git a/webrtc/check/validate/scenarios/offer_answer.scenario b/webrtc/check/validate/scenarios/offer_answer.scenario new file mode 100644 index 0000000000..559f65fad7 --- /dev/null +++ b/webrtc/check/validate/scenarios/offer_answer.scenario @@ -0,0 +1,3 @@ +description, summary="Produce an offer" +create-offer; +wait-for-negotiation-state, state="answer-set" diff --git a/webrtc/check/validate/scenarios/vp8_send_stream.scenario b/webrtc/check/validate/scenarios/vp8_send_stream.scenario new file mode 100644 index 0000000000..2ec15a5391 --- /dev/null +++ b/webrtc/check/validate/scenarios/vp8_send_stream.scenario @@ -0,0 +1,5 @@ +description, summary="Send a VP8 stream", handles-state=true +add-stream, pipeline="videotestsrc is-live=1 ! vp8enc ! rtpvp8pay ! queue" +set-state, state="playing"; +create-offer; +wait-for-negotiation-state, state="answer-set" diff --git a/webrtc/check/validate/signalling.py b/webrtc/check/validate/signalling.py new file mode 100644 index 0000000000..2a7d9388b8 --- /dev/null +++ b/webrtc/check/validate/signalling.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2018, Matthew Waters +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. + +import websockets +import asyncio +import ssl +import os +import sys +import threading +import json + +from observer import Signal +from enums import SignallingState, RemoteState + +class AsyncIOThread(threading.Thread): + def __init__ (self, loop): + threading.Thread.__init__(self) + self.loop = loop + + def run(self): + asyncio.set_event_loop(self.loop) + self.loop.run_forever() + + def stop_thread(self): + self.loop.call_soon_threadsafe(self.loop.stop) + +class SignallingClientThread(object): + def __init__(self, server): + self.server = server + + self.wss_connected = Signal() + self.message = Signal() + + self._init_async() + + def _init_async(self): + self.conn = None + self._loop = asyncio.new_event_loop() + + self._thread = AsyncIOThread(self._loop) + self._thread.start() + + self._loop.call_soon_threadsafe(lambda: asyncio.ensure_future(self._a_loop())) + + async def _a_connect(self): + assert not self.conn + sslctx = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH) + self.conn = await websockets.connect(self.server, ssl=sslctx) + + async def _a_loop(self): + await self._a_connect() + self.wss_connected.fire() + assert self.conn + async for message in self.conn: + self.message.fire(message) + + def send(self, data): + async def _a_send(): + await self.conn.send(data) + self._loop.call_soon_threadsafe(lambda: asyncio.ensure_future(_a_send())) + + def stop(self): + cond = threading.Condition() + + # asyncio, why you so complicated to stop ? + tasks = asyncio.all_tasks(self._loop) + async def _a_stop(): + if self.conn: + await self.conn.close() + self.conn = None + + to_wait = [t for t in tasks if not t.done()] + if to_wait: + done, pending = await asyncio.wait(to_wait) + with cond: + cond.notify() + + self._loop.call_soon_threadsafe(lambda: asyncio.ensure_future(_a_stop())) + with cond: + cond.wait() + self._thread.stop_thread() + self._thread.join() + +class WebRTCSignallingClient(SignallingClientThread): + def __init__(self, server, id_): + super().__init__(server) + + self.wss_connected.connect(self._on_connection) + self.message.connect(self._on_message) + self.state = SignallingState.NEW + self._state_cond = threading.Condition() + + self.id = id_ + self._peerid = None + + # override that base class + self.connected = Signal() + self.session_created = Signal() + self.error = Signal() + self.have_json = Signal() + + def wait_for_states(self, states): + ret = None + with self._state_cond: + while self.state not in states: + self._state_cond.wait() + ret = self.state + return ret + + def _update_state(self, state): + with self._state_cond: + if self.state is not SignallingState.ERROR: + self.state = state + self._state_cond.notify_all() + + def hello(self): + self.send('HELLO ' + str(self.id)) + self.wait_for_states([SignallingState.HELLO]) + print("signalling-client sent HELLO") + + def create_session(self, peerid): + self._peerid = peerid + self.send('SESSION {}'.format(self._peerid)) + self.wait_for_states([SignallingState.SESSION, SignallingState.ERROR]) + print("signalling-client sent SESSION") + + def _on_connection(self): + self._update_state (SignallingState.OPEN) + + def _on_message(self, message): + print("signalling-client received", message) + if message == 'HELLO': + self._update_state (SignallingState.HELLO) + self.connected.fire() + elif message == 'SESSION_OK': + self._update_state (SignallingState.SESSION) + self.session_created.fire() + elif message.startswith('ERROR'): + self._update_state (SignallingState.ERROR) + self.error.fire(message) + else: + msg = json.loads(message) + self.have_json.fire(msg) + return False + diff --git a/webrtc/check/validate/testsuites/__init__.py b/webrtc/check/validate/testsuites/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/webrtc/check/validate/testsuites/webrtc.py b/webrtc/check/validate/testsuites/webrtc.py new file mode 100644 index 0000000000..c078e5d314 --- /dev/null +++ b/webrtc/check/validate/testsuites/webrtc.py @@ -0,0 +1,36 @@ +#*- vi:si:et:sw=4:sts=4:ts=4:syntax=python +# +# Copyright (c) 2018 Matthew Waters +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. + +""" +The GstValidate webrtc streams testsuite +""" + +import os + +TEST_MANAGER = "webrtc" + +BLACKLIST = [ + ] + +def setup_tests(test_manager, options): + print("Setting up webrtc tests") +# test_manager.set_default_blacklist(BLACKLIST) + + return True + diff --git a/webrtc/check/validate/web/single_stream.html b/webrtc/check/validate/web/single_stream.html new file mode 100644 index 0000000000..9a95fb7491 --- /dev/null +++ b/webrtc/check/validate/web/single_stream.html @@ -0,0 +1,35 @@ + + + + + + + + + + + +
+
Status: unknown
+
+
Our id is unknown
+
+
+
getUserMedia constraints being used:
+
+
+ + diff --git a/webrtc/check/validate/web/webrtc.js b/webrtc/check/validate/web/webrtc.js new file mode 100644 index 0000000000..eb7e00f5e0 --- /dev/null +++ b/webrtc/check/validate/web/webrtc.js @@ -0,0 +1,320 @@ +/* vim: set sts=4 sw=4 et : + * + * Demo Javascript app for negotiating and streaming a sendrecv webrtc stream + * with a GStreamer app. Runs only in passive mode, i.e., responds to offers + * with answers, exchanges ICE candidates, and streams. + * + * Author: Nirbheek Chauhan + */ + +// Set this to override the automatic detection in websocketServerConnect() +var ws_server; +var ws_port; +// Set this to use a specific peer id instead of a random one +var default_peer_id; +// Override with your own STUN servers if you want +var rtc_configuration = {iceServers: [{urls: "stun:stun.services.mozilla.com"}, + {urls: "stun:stun.l.google.com:19302"}]}; +// The default constraints that will be attempted. Can be overriden by the user. +var default_constraints = {video: true, audio: true}; + +var connect_attempts = 0; +var peer_connection; +var send_channel; +var ws_conn; +// Promise for local stream after constraints are approved by the user +var local_stream_promise; + +function getOurId() { + return Math.floor(Math.random() * (9000 - 10) + 10).toString(); +} + +function resetState() { + // This will call onServerClose() + ws_conn.close(); +} + +function handleIncomingError(error) { + setError("ERROR: " + error); + resetState(); +} + +function getVideoElement() { + return document.getElementById("stream"); +} + +function setStatus(text) { + console.log(text); + var span = document.getElementById("status") + // Don't set the status if it already contains an error + if (!span.classList.contains('error')) + span.textContent = text; +} + +function setError(text) { + console.error(text); + var span = document.getElementById("status") + span.textContent = text; + span.classList.add('error'); + ws_conn.send(JSON.stringify({'STATE': 'error'})) +} + +function resetVideo() { + // Release the webcam and mic + if (local_stream_promise) + local_stream_promise.then(stream => { + if (stream) { + stream.getTracks().forEach(function (track) { track.stop(); }); + } + }); + + // Reset the video element and stop showing the last received frame + var videoElement = getVideoElement(); + videoElement.pause(); + videoElement.src = ""; + videoElement.load(); +} + +// SDP offer received from peer, set remote description and create an answer +function onIncomingSDP(sdp) { + peer_connection.setRemoteDescription(sdp).then(() => { + setStatus("Remote SDP set"); + if (sdp.type != "offer") + return; + setStatus("Got SDP offer"); + local_stream_promise.then((stream) => { + setStatus("Got local stream, creating answer"); + peer_connection.createAnswer() + .then(onLocalDescription).catch(setError); + }).catch(setError); + }).catch(setError); +} + +// Local description was set, send it to peer +function onLocalDescription(desc) { + console.log("Got local description: " + JSON.stringify(desc)); + peer_connection.setLocalDescription(desc).then(function() { + setStatus("Sending SDP answer"); + sdp = {'sdp': peer_connection.localDescription} + ws_conn.send(JSON.stringify(sdp)); + }); +} + +// ICE candidate received from peer, add it to the peer connection +function onIncomingICE(ice) { + var candidate = new RTCIceCandidate(ice); + peer_connection.addIceCandidate(candidate).catch(setError); +} + +function onServerMessage(event) { + console.log("Received " + event.data); + switch (event.data) { + case "HELLO": + setStatus("Registered with server, waiting for call"); + return; + default: + if (event.data.startsWith("ERROR")) { + handleIncomingError(event.data); + return; + } + // Handle incoming JSON SDP and ICE messages + try { + msg = JSON.parse(event.data); + } catch (e) { + if (e instanceof SyntaxError) { + handleIncomingError("Error parsing incoming JSON: " + event.data); + } else { + handleIncomingError("Unknown error parsing response: " + event.data); + } + return; + } + + // Incoming JSON signals the beginning of a call + if (!peer_connection) + createCall(msg); + + if (msg.sdp != null) { + onIncomingSDP(msg.sdp); + } else if (msg.ice != null) { + onIncomingICE(msg.ice); + } else { + handleIncomingError("Unknown incoming JSON: " + msg); + } + } +} + +function onServerClose(event) { + setStatus('Disconnected from server'); + resetVideo(); + + if (peer_connection) { + peer_connection.close(); + peer_connection = null; + } + + // Reset after a second + window.setTimeout(websocketServerConnect, 1000); +} + +function onServerError(event) { + setError("Unable to connect to server, did you add an exception for the certificate?") + // Retry after 3 seconds + window.setTimeout(websocketServerConnect, 3000); +} + +function getLocalStream() { + var constraints; + var textarea = document.getElementById('constraints'); + try { + constraints = JSON.parse(textarea.value); + } catch (e) { + console.error(e); + setError('ERROR parsing constraints: ' + e.message + ', using default constraints'); + constraints = default_constraints; + } + console.log(JSON.stringify(constraints)); + + // Add local stream + if (navigator.mediaDevices.getUserMedia) { + return navigator.mediaDevices.getUserMedia(constraints); + } else { + errorUserMediaHandler(); + } +} + +function websocketServerConnect() { + connect_attempts++; + if (connect_attempts > 3) { + setError("Too many connection attempts, aborting. Refresh page to try again"); + return; + } + // Clear errors in the status span + var span = document.getElementById("status"); + span.classList.remove('error'); + span.textContent = ''; + // Populate constraints + var textarea = document.getElementById('constraints'); + if (textarea.value == '') + textarea.value = JSON.stringify(default_constraints); + // Fetch the peer id to use + + var url = new URL(window.location.href); + + peer_id = url.searchParams.get("id"); + peer_id = peer_id || default_peer_id || getOurId(); + + ws_port = ws_port || url.searchParams.get("port"); + ws_port = ws_port || '8443'; + + ws_server = ws_server || url.searchParams.get("server"); + if (window.location.protocol.startsWith ("file")) { + ws_server = ws_server || "127.0.0.1"; + } else if (window.location.protocol.startsWith ("http")) { + ws_server = ws_server || window.location.hostname; + } else { + throw new Error ("Don't know how to connect to the signalling server with uri" + window.location); + } + + var ws_url = 'wss://' + ws_server + ':' + ws_port + setStatus("Connecting to server " + ws_url); + ws_conn = new WebSocket(ws_url); + /* When connected, immediately register with the server */ + ws_conn.addEventListener('open', (event) => { + document.getElementById("peer-id").textContent = peer_id; + ws_conn.send('HELLO ' + peer_id); + setStatus("Registering with server"); + }); + ws_conn.addEventListener('error', onServerError); + ws_conn.addEventListener('message', onServerMessage); + ws_conn.addEventListener('close', onServerClose); +} + +function onRemoteStreamAdded(event) { + videoTracks = event.stream.getVideoTracks(); + audioTracks = event.stream.getAudioTracks(); + + if (videoTracks.length > 0) { + console.log('Incoming stream: ' + videoTracks.length + ' video tracks and ' + audioTracks.length + ' audio tracks'); + getVideoElement().srcObject = event.stream; + ws_conn.send(JSON.stringify({'STATE': 'remote-stream-received'})) + } else { + handleIncomingError('Stream with unknown tracks added, resetting'); + } +} + +function errorUserMediaHandler() { + setError("Browser doesn't support getUserMedia!"); +} + +const handleDataChannelOpen = (event) =>{ + console.log("dataChannel.OnOpen", event); +}; + +const handleDataChannelMessageReceived = (event) =>{ + console.log("dataChannel.OnMessage:", event, event.data.type); + + setStatus("Received data channel message"); + if (typeof event.data === 'string' || event.data instanceof String) { + console.log('Incoming string message: ' + event.data); + textarea = document.getElementById("text") + textarea.value = textarea.value + '\n' + event.data + } else { + console.log('Incoming data message'); + } + send_channel.send("Hi! (from browser)"); +}; + +const handleDataChannelError = (error) =>{ + console.log("dataChannel.OnError:", error); +}; + +const handleDataChannelClose = (event) =>{ + console.log("dataChannel.OnClose", event); +}; + +function onDataChannel(event) { + setStatus("Data channel created"); + let receiveChannel = event.channel; + receiveChannel.onopen = handleDataChannelOpen; + receiveChannel.onmessage = handleDataChannelMessageReceived; + receiveChannel.onerror = handleDataChannelError; + receiveChannel.onclose = handleDataChannelClose; +} + +function createCall(msg) { + // Reset connection attempts because we connected successfully + connect_attempts = 0; + + console.log('Creating RTCPeerConnection'); + + peer_connection = new RTCPeerConnection(rtc_configuration); + send_channel = peer_connection.createDataChannel('label', null); + send_channel.onopen = handleDataChannelOpen; + send_channel.onmessage = handleDataChannelMessageReceived; + send_channel.onerror = handleDataChannelError; + send_channel.onclose = handleDataChannelClose; + peer_connection.ondatachannel = onDataChannel; + peer_connection.onaddstream = onRemoteStreamAdded; + /* Send our video/audio to the other peer */ + local_stream_promise = getLocalStream().then((stream) => { + console.log('Adding local stream'); + peer_connection.addStream(stream); + return stream; + }).catch(setError); + + if (!msg.sdp) { + console.log("WARNING: First message wasn't an SDP message!?"); + } + + peer_connection.onicecandidate = (event) => { + // We have a candidate, send it to the remote party with the + // same uuid + if (event.candidate == null) { + console.log("ICE Candidate was null, done"); + return; + } + ws_conn.send(JSON.stringify({'ice': event.candidate})); + }; + + setStatus("Created peer connection for call, waiting for SDP"); +} diff --git a/webrtc/check/validate/webrtc_validate.py b/webrtc/check/validate/webrtc_validate.py new file mode 100644 index 0000000000..6215a0a2e2 --- /dev/null +++ b/webrtc/check/validate/webrtc_validate.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2018, Matthew Waters +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. + +import os +import sys +import argparse +import json + +from signalling import WebRTCSignallingClient +from actions import register_action_types, ActionObserver +from client import WebRTCClient +from browser import Browser, create_driver +from enums import SignallingState, NegotiationState, RemoteState + +import gi +gi.require_version("GLib", "2.0") +from gi.repository import GLib +gi.require_version("Gst", "1.0") +from gi.repository import Gst +gi.require_version("GstWebRTC", "1.0") +from gi.repository import GstWebRTC +gi.require_version("GstSdp", "1.0") +from gi.repository import GstSdp +gi.require_version("GstValidate", "1.0") +from gi.repository import GstValidate + +class WebRTCApplication(object): + def __init__(self, server, id_, peerid, scenario_name, browser_name, html_source): + self.server = server + self.peerid = peerid + self.html_source = html_source + self.id = id_ + self.scenario_name = scenario_name + self.browser_name = browser_name + + def _init_validate(self, scenario_file): + self.runner = GstValidate.Runner.new() + self.monitor = GstValidate.Monitor.factory_create( + self.client.pipeline, self.runner, None) + self._scenario = GstValidate.Scenario.factory_create( + self.runner, self.client.pipeline, self.scenario_name) + self._scenario.connect("done", self._on_scenario_done) + self._scenario.props.execute_on_idle = True + if not self._scenario.props.handles_states: + self.client.pipeline.set_state(Gst.State.PLAYING) + + def _on_scenario_done(self, scenario): + self.quit() + + def _connect_actions(self): + def create_offer(): + self.client.create_offer(None) + return GstValidate.ActionReturn.OK + self.actions.create_offer.connect(create_offer) + + def wait_for_negotiation_state(state): + states = [state, NegotiationState.ERROR] + state = self.client.wait_for_negotiation_states(states) + return GstValidate.ActionReturn.OK if state != RemoteState.ERROR else GstValidate.ActionReturn.ERROR + self.actions.wait_for_negotiation_state.connect(wait_for_negotiation_state) + + def add_stream(pipeline): + self.client.add_stream(pipeline) + return GstValidate.ActionReturn.OK + self.actions.add_stream.connect(add_stream) + + def _connect_client_observer(self): + def on_offer_created(offer): + text = offer.sdp.as_text() + msg = json.dumps({'sdp': {'type': 'offer', 'sdp': text}}) + self.signalling.send(msg) + self.client.on_offer_created.connect(on_offer_created) + + def on_ice_candidate(mline, candidate): + msg = json.dumps({'ice': {'sdpMLineIndex': str(mline), 'candidate' : candidate}}) + self.signalling.send(msg) + self.client.on_ice_candidate.connect(on_ice_candidate) + + def on_pad_added(pad): + if pad.get_direction() != Gst.PadDirection.SRC: + return + self.client.add_stream_with_pad('fakesink', pad) + self.client.on_pad_added.connect(on_pad_added) + + def _connect_signalling_observer(self): + def have_json(msg): + if 'sdp' in msg: + sdp = msg['sdp'] + res, sdpmsg = GstSdp.SDPMessage.new() + GstSdp.sdp_message_parse_buffer(bytes(sdp['sdp'].encode()), sdpmsg) + sdptype = GstWebRTC.WebRTCSDPType.ANSWER if sdp['type'] == 'answer' else GstWebRTC.WebRTCSDPType.OFFER + desc = GstWebRTC.WebRTCSessionDescription.new(sdptype, sdpmsg) + self.client.set_remote_description(desc) + elif 'ice' in msg: + ice = msg['ice'] + candidate = ice['candidate'] + sdpmlineindex = ice['sdpMLineIndex'] + self.client.add_ice_candidate(sdpmlineindex, candidate) + self.signalling.have_json.connect(have_json) + + def error(msg): + # errors are unexpected + GLib.idle_add(self.quit) + GLib.idle_add(sys.exit, -20) + self.signalling.error.connect(error) + + def _init(self): + self.main_loop = GLib.MainLoop() + + self.client = WebRTCClient() + self._connect_client_observer() + + self.actions = ActionObserver() + register_action_types(self.actions) + self._connect_actions() + + self.signalling = WebRTCSignallingClient(self.server, self.id) + self._connect_signalling_observer() + self.signalling.wait_for_states([SignallingState.OPEN]) + self.signalling.hello() + + self.browser = Browser(create_driver(self.browser_name), self.html_source) + + browser_id = self.browser.get_peer_id () + assert browser_id == self.peerid + + self.signalling.create_session(self.peerid) + + self._init_validate(self.scenario_name) + print("app initialized") + + def quit(self): + # Stop signalling first so asyncio doesn't keep us alive on weird failures + self.signalling.stop() + self.browser.driver.quit() + self.client.stop() + self.main_loop.quit() + + def run(self): + try: + self._init() + self.main_loop.run() + except: + self.quit() + raise + +def parse_options(): + parser = argparse.ArgumentParser() + parser.add_argument('id', help='ID of this client', type=int) + parser.add_argument('--peer-id', help='ID of the peer to connect to', type=int) + parser.add_argument('--server', help='Signalling server to connect to, eg "wss://127.0.0.1:8443"') + parser.add_argument('--html-source', help='HTML page to open in the browser', default=None) + parser.add_argument('--scenario', help='Scenario file to execute', default=None) + parser.add_argument('--browser', help='Browser name to use', default=None) + return parser.parse_args() + +def init(): + Gst.init(None) + GstValidate.init() + + args = parse_options() + if not args.scenario: + args.scenario = os.environ.get('GST_VALIDATE_SCENARIO', None) + # if we have both manual scenario creation and env, then the scenario + # is executed twice... + if 'GST_VALIDATE_SCENARIO' in os.environ: + del os.environ['GST_VALIDATE_SCENARIO'] + if not args.scenario: + raise ValueError("No scenario file provided") + if not args.server: + raise ValueError("No server location provided") + if not args.peer_id: + raise ValueError("No peer id provided") + if not args.html_source: + raise ValueError("No HTML page provided") + if not args.browser: + raise ValueError("No Browser provided") + + return args + +def run(): + args = init() + w = WebRTCApplication (args.server, args.id, args.peer_id, args.scenario, args.browser, args.html_source) + return w.run() + +if __name__ == "__main__": + sys.exit(run()) diff --git a/webrtc/meson.build b/webrtc/meson.build index 3ee35f7bd9..db32d48174 100644 --- a/webrtc/meson.build +++ b/webrtc/meson.build @@ -37,3 +37,5 @@ endif subdir('multiparty-sendrecv') subdir('signalling') subdir('sendrecv') + +subdir('check') diff --git a/webrtc/sendrecv/gst/meson.build b/webrtc/sendrecv/gst/meson.build index 85950add4a..5c9509c7e1 100644 --- a/webrtc/sendrecv/gst/meson.build +++ b/webrtc/sendrecv/gst/meson.build @@ -3,5 +3,3 @@ executable('webrtc-sendrecv', dependencies : [gst_dep, gstsdp_dep, gstwebrtc_dep, libsoup_dep, json_glib_dep ]) webrtc_py = files('webrtc_sendrecv.py') - -subdir('tests') From 615813ef93f6a7b977280bd55d6ffd17c25fcbd2 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Wed, 12 Feb 2020 21:56:34 +1100 Subject: [PATCH 115/130] check/validate: a few more tests and improvements Tests a matrix of options: - local/remote negotiation initiator - 'most' bundle-policy combinations (some combinations will never work) - firefox or chrome browser Across 4 test scenarios: - simple negotiation with default browser streams (or none if gstreamer initiates) - sending a vp8 stream - opening a data channel - sending a message over the data channel for a total of 112 tests! --- webrtc/check/validate/actions.py | 95 ++++++--- webrtc/check/validate/apps/gstwebrtc.py | 156 +++++++++++--- webrtc/check/validate/browser.py | 29 ++- webrtc/check/validate/client.py | 123 +++++++---- webrtc/check/validate/enums.py | 63 ++++-- webrtc/check/validate/observer.py | 128 +++++++++++- .../bundle_policy.scenario | 1 + .../bundle_policy.scenario | 1 + .../bundle_policy.scenario | 1 + .../bundle_policy.scenario | 1 + .../bundle_policy.scenario | 1 + .../bundle_policy.scenario | 1 + .../bundle_policy.scenario | 1 + .../bundle_policy.scenario | 1 + .../bundle_policy.scenario | 1 + .../bundle_policy.scenario | 1 + .../bundle_policy.scenario | 1 + .../bundle_policy.scenario | 1 + .../negotiation_initiator.scenario | 1 + .../validate/scenarios/offer_answer.scenario | 18 +- .../scenarios/open_data_channel.scenario | 23 ++ .../negotiation_initiator.scenario | 1 + .../send_data_channel_string.scenario | 21 ++ .../scenarios/vp8_send_stream.scenario | 14 +- webrtc/check/validate/signalling.py | 155 +++++++++++--- webrtc/check/validate/testsuites/webrtc.py | 7 +- webrtc/check/validate/web/single_stream.html | 4 - webrtc/check/validate/web/webrtc.js | 197 ++++++++++++------ webrtc/check/validate/webrtc_validate.py | 141 ++++++++++--- 29 files changed, 926 insertions(+), 262 deletions(-) create mode 100644 webrtc/check/validate/scenarios/bundle_local_balanced_remote_balanced/bundle_policy.scenario create mode 100644 webrtc/check/validate/scenarios/bundle_local_balanced_remote_max_bundle/bundle_policy.scenario create mode 100644 webrtc/check/validate/scenarios/bundle_local_balanced_remote_max_compat/bundle_policy.scenario create mode 100644 webrtc/check/validate/scenarios/bundle_local_max_bundle_remote_balanced/bundle_policy.scenario create mode 100644 webrtc/check/validate/scenarios/bundle_local_max_bundle_remote_max_bundle/bundle_policy.scenario create mode 100644 webrtc/check/validate/scenarios/bundle_local_max_bundle_remote_max_compat/bundle_policy.scenario create mode 100644 webrtc/check/validate/scenarios/bundle_local_max_compat_remote_balanced/bundle_policy.scenario create mode 100644 webrtc/check/validate/scenarios/bundle_local_max_compat_remote_max_bundle/bundle_policy.scenario create mode 100644 webrtc/check/validate/scenarios/bundle_local_max_compat_remote_max_compat/bundle_policy.scenario create mode 100644 webrtc/check/validate/scenarios/bundle_local_none_remote_balanced/bundle_policy.scenario create mode 100644 webrtc/check/validate/scenarios/bundle_local_none_remote_max_bundle/bundle_policy.scenario create mode 100644 webrtc/check/validate/scenarios/bundle_local_none_remote_max_compat/bundle_policy.scenario create mode 100644 webrtc/check/validate/scenarios/local_initiates_negotiation/negotiation_initiator.scenario create mode 100644 webrtc/check/validate/scenarios/open_data_channel.scenario create mode 100644 webrtc/check/validate/scenarios/remote_initiates_negotiation/negotiation_initiator.scenario create mode 100644 webrtc/check/validate/scenarios/send_data_channel_string.scenario diff --git a/webrtc/check/validate/actions.py b/webrtc/check/validate/actions.py index 6ced2cfa7c..98a3cb837f 100644 --- a/webrtc/check/validate/actions.py +++ b/webrtc/check/validate/actions.py @@ -1,6 +1,4 @@ -#!/usr/bin/env python3 -# -# Copyright (c) 2018, Matthew Waters +# Copyright (c) 2020, Matthew Waters # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -22,6 +20,11 @@ gi.require_version("GstValidate", "1.0") from gi.repository import GstValidate from observer import Signal +from enums import Actions + +import logging + +l = logging.getLogger(__name__) class ActionObserver(object): def __init__(self): @@ -42,36 +45,62 @@ class ActionObserver(object): return val - self.create_offer = Signal(_action_continue, _action_accum) - self.wait_for_negotiation_state = Signal(_action_continue, _action_accum) - self.add_stream = Signal(_action_continue, _action_accum) - self.wait_for_remote_state = Signal(_action_continue, _action_accum) + self.action = Signal(_action_continue, _action_accum) - def _create_offer(self, scenario, action): - print("action create-offer") - return self.create_offer.fire() - def _wait_for_negotiation_state(self, scenario, action): - state = action.structure["state"] - print("action wait-for-negotiation-state", state) - return self.wait_for_negotiation_state.fire(state) - def _add_stream(self, scenario, action): - pipeline = action.structure["pipeline"] - print("action add-stream", pipeline) - return self.add_stream.fire(pipeline) + def _action(self, scenario, action): + l.debug('executing action: ' + str(action.structure)) + return self.action.fire (Actions(action.structure.get_name()), action) -def register_action_types(observer): - if not isinstance(observer, ActionObserver): - raise TypeError - GstValidate.register_action_type("create-offer", "webrtc", - observer._create_offer, None, - "Instruct a create-offer to commence", - GstValidate.ActionTypeFlags.NONE) - GstValidate.register_action_type("wait-for-negotiation-state", "webrtc", - observer._wait_for_negotiation_state, None, - "Wait for a specific negotiation state to be reached", - GstValidate.ActionTypeFlags.NONE) - GstValidate.register_action_type("add-stream", "webrtc", - observer._add_stream, None, - "Add a stream to the webrtcbin", - GstValidate.ActionTypeFlags.NONE) + def register_action_types(observer): + if not isinstance(observer, ActionObserver): + raise TypeError + + GstValidate.register_action_type(Actions.CREATE_OFFER.value, + "webrtc", observer._action, None, + "Instruct a create-offer to commence", + GstValidate.ActionTypeFlags.NONE) + GstValidate.register_action_type(Actions.CREATE_ANSWER.value, + "webrtc", observer._action, None, + "Create answer", + GstValidate.ActionTypeFlags.NONE) + GstValidate.register_action_type(Actions.WAIT_FOR_NEGOTIATION_STATE.value, + "webrtc", observer._action, None, + "Wait for a specific negotiation state to be reached", + GstValidate.ActionTypeFlags.NONE) + GstValidate.register_action_type(Actions.ADD_STREAM.value, + "webrtc", observer._action, None, + "Add a stream to the webrtcbin", + GstValidate.ActionTypeFlags.NONE) + GstValidate.register_action_type(Actions.ADD_DATA_CHANNEL.value, + "webrtc", observer._action, None, + "Add a data channel to the webrtcbin", + GstValidate.ActionTypeFlags.NONE) + GstValidate.register_action_type(Actions.SEND_DATA_CHANNEL_STRING.value, + "webrtc", observer._action, None, + "Send a message using a data channel", + GstValidate.ActionTypeFlags.NONE) + GstValidate.register_action_type(Actions.WAIT_FOR_DATA_CHANNEL_STATE.value, + "webrtc", observer._action, None, + "Wait for data channel to reach state", + GstValidate.ActionTypeFlags.NONE) + GstValidate.register_action_type(Actions.CLOSE_DATA_CHANNEL.value, + "webrtc", observer._action, None, + "Close a data channel", + GstValidate.ActionTypeFlags.NONE) + GstValidate.register_action_type(Actions.WAIT_FOR_DATA_CHANNEL.value, + "webrtc", observer._action, None, + "Wait for a data channel to appear", + GstValidate.ActionTypeFlags.NONE) + GstValidate.register_action_type(Actions.WAIT_FOR_DATA_CHANNEL_STRING.value, + "webrtc", observer._action, None, + "Wait for a data channel to receive a message", + GstValidate.ActionTypeFlags.NONE) + GstValidate.register_action_type(Actions.WAIT_FOR_NEGOTIATION_NEEDED.value, + "webrtc", observer._action, None, + "Wait for a the on-negotiation-needed signal to fire", + GstValidate.ActionTypeFlags.NONE) + GstValidate.register_action_type(Actions.SET_WEBRTC_OPTIONS.value, + "webrtc", observer._action, None, + "Set some webrtc options", + GstValidate.ActionTypeFlags.NONE) diff --git a/webrtc/check/validate/apps/gstwebrtc.py b/webrtc/check/validate/apps/gstwebrtc.py index 693ac4aea6..27d4088bb9 100644 --- a/webrtc/check/validate/apps/gstwebrtc.py +++ b/webrtc/check/validate/apps/gstwebrtc.py @@ -1,6 +1,4 @@ -#*- vi:si:et:sw=4:sts=4:ts=4:syntax=python -# -# Copyright (c) 2018 Matthew Waters +# Copyright (c) 2020, Matthew Waters # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -21,26 +19,82 @@ import inspect import os import sys import shutil +import itertools import tempfile -from launcher.baseclasses import TestsManager, TestsGenerator, GstValidateTest, ScenarioManager +from launcher.baseclasses import TestsManager, GstValidateTest, ScenarioManager from launcher.utils import DEFAULT_TIMEOUT DEFAULT_BROWSERS = ['firefox', 'chrome'] -DEFAULT_SCENARIOS = [ - "offer_answer", - "vp8_send_stream" - ] -BROWSER_SCENARIO_BLACKLISTS = { - 'firefox' : [ - 'offer_answer', # fails to accept an SDP without any media sections - ], - 'chrome' : [ - ], +# list of scenarios. These are the names of the actual scenario files stored +# on disk. +DEFAULT_SCENARIOS = [ + "offer_answer", + "vp8_send_stream", + "open_data_channel", + "send_data_channel_string", + ] + +# various configuration changes that are included from other scenarios. +# key is the name of the override used in the name of the test +# value is the subdirectory where the override is placed +# changes some things about the test like: +# - who initiates the negotiation +# - bundle settings +SCENARIO_OVERRIDES = { + # name : directory + + # who starts the negotiation + 'local' : 'local_initiates_negotiation', + 'remote' : 'remote_initiates_negotiation', + + # bundle-policy configuration + # XXX: webrtcbin's bundle-policy=none is not part of the spec + 'none_compat' : 'bundle_local_none_remote_max_compat', + 'none_balanced' : 'bundle_local_none_remote_balanced', + 'none_bundle' : 'bundle_local_none_remote_max_bundle', + 'compat_compat' : 'bundle_local_max_compat_remote_max_compat', + 'compat_balanced' : 'bundle_local_max_compat_remote_balanced', + 'compat_bundle' : 'bundle_local_max_compat_remote_max_bundle', + 'balanced_compat' : 'bundle_local_balanced_remote_max_compat', + 'balanced_balanced' : 'bundle_local_balanced_remote_balanced', + 'balanced_bundle' : 'bundle_local_balanced_remote_bundle', + 'bundle_compat' : 'bundle_local_max_bundle_remote_max_compat', + 'bundle_balanced' : 'bundle_local_max_bundle_remote_balanced', + 'bundle_bundle' : 'bundle_local_max_bundle_remote_max_bundle', } +bundle_options = ['compat', 'balanced', 'bundle'] + +# Given an override, these are the choices to choose from. Each choice is a +# separate test +OVERRIDE_CHOICES = { + 'initiator' : ['local', 'remote'], + 'bundle' : ['_'.join(opt) for opt in itertools.product(['none'] + bundle_options, bundle_options)], +} + +# Which scenarios support which override. All the overrides will be chosen +SCENARIO_OVERRIDES_SUPPORTED = { + "offer_answer" : ['initiator', 'bundle'], + "vp8_send_stream" : ['initiator', 'bundle'], + "open_data_channel" : ['initiator', 'bundle'], + "send_data_channel_string" : ['initiator', 'bundle'], +} + +# Things that don't work for some reason or another. +DEFAULT_BLACKLIST = [ + (r"webrtc\.firefox\.local\..*offer_answer", + "Firefox doesn't like a SDP without any media"), + (r"webrtc.*remote.*vp8_send_stream", + "We can't match payload types with a remote offer and a sending stream"), + (r"webrtc.*\.balanced_.*", + "webrtcbin doesn't implement bundle-policy=balanced"), + (r"webrtc.*\.none_bundle.*", + "Browsers want a BUNDLE group if in max-bundle mode"), +] + class MutableInt(object): def __init__(self, value): self.value = value @@ -66,11 +120,12 @@ class GstWebRTCTest(GstValidateTest): @classmethod def __get_available_peer_id(cls): + # each connection uses two peer ids peerid = cls.__last_id.value cls.__last_id.value += 2 return peerid - def __init__(self, classname, tests_manager, scenario, browser, timeout=DEFAULT_TIMEOUT): + def __init__(self, classname, tests_manager, scenario, browser, scenario_override_includes=None, timeout=DEFAULT_TIMEOUT): super().__init__("python3", classname, tests_manager.options, @@ -82,6 +137,7 @@ class GstWebRTCTest(GstValidateTest): self.current_file_path = os.path.dirname (os.path.abspath (filename)) self.certdir = None self.browser = browser + self.scenario_override_includes = scenario_override_includes def launch_server(self): if self.options.redirect_logs == 'stdout': @@ -138,6 +194,8 @@ class GstWebRTCTest(GstValidateTest): html_page = os.path.join(self.current_file_path, '..', 'web', 'single_stream.html') html_params = '?server=127.0.0.1&port=' + str(self.server_port) + '&id=' + str(web_id) self.add_arguments("file://" + html_page + html_params) + self.add_arguments("--name") + self.add_arguments(self.classname) self.add_arguments('--peer-id') self.add_arguments(str(web_id)) self.add_arguments(str(gst_id)) @@ -154,10 +212,27 @@ class GstWebRTCTest(GstValidateTest): self.__used_ports.remove(self.server_port) if self.certdir: shutil.rmtree(self.certdir, ignore_errors=True) - self.certdir return res + def get_subproc_env(self): + env = super().get_subproc_env() + if not self.scenario_override_includes: + return env + + # this feels gross... + paths = env.get('GST_VALIDATE_SCENARIOS_PATH', '').split(os.pathsep) + new_paths = [] + for p in paths: + new_paths.append(p) + for override_path in self.scenario_override_includes: + new_p = os.path.join(p, override_path) + if os.path.exists (new_p): + new_paths.append(new_p) + env['GST_VALIDATE_SCENARIOS_PATH'] = os.pathsep.join(new_paths) + + return env + class GstWebRTCTestsManager(TestsManager): scenarios_manager = ScenarioManager() name = "webrtc" @@ -165,23 +240,50 @@ class GstWebRTCTestsManager(TestsManager): def __init__(self): super(GstWebRTCTestsManager, self).__init__() self.loading_testsuite = self.name + self._scenarios = [] - def webrtc_server_address(self): - return "wss://127.0.0.1:8443" + def add_scenarios(self, scenarios): + if isinstance(scenarios, list): + self._scenarios.extend(scenarios) + else: + self._scenarios.append(scenarios) + + self._scenarios = list(set(self._scenarios)) + + def set_scenarios(self, scenarios): + self._scenarios = [] + self.add_scenarios(scenarios) + + def get_scenarios(self): + return self._scenarios def populate_testsuite(self): self.add_scenarios (DEFAULT_SCENARIOS) + self.set_default_blacklist(DEFAULT_BLACKLIST) + + def list_tests(self): + if self.tests: + return self.tests scenarios = [(scenario_name, self.scenarios_manager.get_scenario(scenario_name)) for scenario_name in self.get_scenarios()] - for name, scenario in scenarios: - if not scenario: - self.warning("Could not find scenario %s" % name) - continue - for browser in DEFAULT_BROWSERS: - if name in BROWSER_SCENARIO_BLACKLISTS[browser]: - self.warning('Skipping broken test', name, 'for browser', browser) + for browser in DEFAULT_BROWSERS: + for name, scenario in scenarios: + if not scenario: + self.warning("Could not find scenario %s" % name) continue - classname = browser + '_' + name - self.add_test(GstWebRTCTest(classname, self, scenario, browser)) + if not SCENARIO_OVERRIDES_SUPPORTED[name]: + # no override choices supported + classname = browser + '.' + name + print ("adding", classname) + self.add_test(GstWebRTCTest(classname, self, scenario, browser)) + else: + for overrides in itertools.product(*[OVERRIDE_CHOICES[c] for c in SCENARIO_OVERRIDES_SUPPORTED[name]]): + oname = '.'.join (overrides) + opaths = [SCENARIO_OVERRIDES[p] for p in overrides] + classname = browser + '.' + oname + '.' + name + print ("adding", classname) + self.add_test(GstWebRTCTest(classname, self, scenario, browser, opaths)) + + return self.tests diff --git a/webrtc/check/validate/browser.py b/webrtc/check/validate/browser.py index 536a0319c4..7db9b0b60b 100644 --- a/webrtc/check/validate/browser.py +++ b/webrtc/check/validate/browser.py @@ -1,6 +1,4 @@ -#!/usr/bin/env python3 -# -# Copyright (c) 2018, Matthew Waters +# Copyright (c) 2020, Matthew Waters # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -17,11 +15,14 @@ # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, # Boston, MA 02110-1301, USA. +import logging from selenium import webdriver from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.firefox.firefox_profile import FirefoxProfile from selenium.webdriver.chrome.options import Options as COptions +l = logging.getLogger(__name__) + def create_firefox_driver(): capabilities = webdriver.DesiredCapabilities().FIREFOX.copy() capabilities['acceptSslCerts'] = True @@ -39,6 +40,9 @@ def create_chrome_driver(): copts.add_argument('--use-fake-ui-for-media-stream') copts.add_argument('--use-fake-device-for-media-stream') copts.add_argument('--enable-blink-features=RTCUnifiedPlanByDefault') + # XXX: until libnice can deal with mdns candidates + local_state = {"enabled_labs_experiments" : ["enable-webrtc-hide-local-ips-with-mdns@2"] } + copts.add_experimental_option("localState", {"browser" : local_state}) return webdriver.Chrome(options=copts, desired_capabilities=capabilities) @@ -48,7 +52,7 @@ def create_driver(name): elif name == 'chrome': return create_chrome_driver() else: - raise ValueError("Unknown browser name " + name) + raise ValueError("Unknown browser name \'" + name + "\'") def valid_int(n): if isinstance(n, int): @@ -62,18 +66,23 @@ def valid_int(n): return False class Browser(object): - def __init__(self, driver, html_source): + """ + A browser as connected through selenium. + """ + def __init__(self, driver): + l.info('Using driver \'' + driver.name + '\' with capabilities ' + str(driver.capabilities)) self.driver = driver - self.html_source = html_source + + def open(self, html_source): + self.driver.get(html_source) def get_peer_id(self): - self.driver.get(self.html_source) - peer_id = WebDriverWait(self.driver, 10).until( + peer_id = WebDriverWait(self.driver, 5).until( lambda x: x.find_element_by_id('peer-id'), message='Peer-id element was never seen' ) - WebDriverWait (self.driver, 10).until( + WebDriverWait (self.driver, 5).until( lambda x: valid_int(peer_id.text), message='Peer-id never became a number' ) - return int(peer_id.text) + return int(peer_id.text) diff --git a/webrtc/check/validate/client.py b/webrtc/check/validate/client.py index 24c8bcd7bc..4a19982f14 100644 --- a/webrtc/check/validate/client.py +++ b/webrtc/check/validate/client.py @@ -1,6 +1,4 @@ -#!/usr/bin/env python3 -# -# Copyright (c) 2018, Matthew Waters +# Copyright (c) 2020, Matthew Waters # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -20,8 +18,8 @@ import threading import copy -from observer import Signal -from enums import NegotiationState +from observer import Signal, WebRTCObserver, DataChannelObserver, StateObserver +from enums import NegotiationState, DataChannelState import gi gi.require_version("Gst", "1.0") @@ -33,10 +31,13 @@ from gi.repository import GstSdp gi.require_version("GstValidate", "1.0") from gi.repository import GstValidate -class WebRTCBinObserver(object): + +class WebRTCBinObserver(WebRTCObserver): + """ + Observe a webrtcbin element. + """ def __init__(self, element): - self.state = NegotiationState.NEW - self.state_cond = threading.Condition() + WebRTCObserver.__init__(self) self.element = element self.signal_handlers = [] self.signal_handlers.append(element.connect("on-negotiation-needed", self._on_negotiation_needed)) @@ -44,17 +45,16 @@ class WebRTCBinObserver(object): self.signal_handlers.append(element.connect("pad-added", self._on_pad_added)) self.signal_handlers.append(element.connect("on-new-transceiver", self._on_new_transceiver)) self.signal_handlers.append(element.connect("on-data-channel", self._on_data_channel)) - self.on_offer_created = Signal() - self.on_answer_created = Signal() + self.negotiation_needed = 0 + self._negotiation_needed_observer = StateObserver(self, "negotiation_needed", threading.Condition()) self.on_negotiation_needed = Signal() self.on_ice_candidate = Signal() self.on_pad_added = Signal() self.on_new_transceiver = Signal() - self.on_data_channel = Signal() - self.on_local_description_set = Signal() - self.on_remote_description_set = Signal() def _on_negotiation_needed(self, element): + self.negotiation_needed += 1 + self._negotiation_needed_observer.update(self.negotiation_needed) self.on_negotiation_needed.fire() def _on_ice_candidate(self, element, mline, candidate): @@ -63,35 +63,19 @@ class WebRTCBinObserver(object): def _on_pad_added(self, element, pad): self.on_pad_added.fire(pad) - def _on_local_description_set(self, promise, desc): - self._update_negotiation_from_description_state(desc) - self.on_local_description_set.fire(desc) - - def _on_remote_description_set(self, promise, desc): - self._update_negotiation_from_description_state(desc) - self.on_remote_description_set.fire(desc) + def _on_description_set(self, promise, desc): + new_state = self._update_negotiation_from_description_state(desc) + if new_state == NegotiationState.OFFER_SET: + self.on_offer_set.fire (desc) + elif new_state == NegotiationState.ANSWER_SET: + self.on_answer_set.fire (desc) def _on_new_transceiver(self, element, transceiver): self.on_new_transceiver.fire(transceiver) - def _on_data_channel(self, element): - self.on_data_channel.fire(desc) - - def _update_negotiation_state(self, new_state): - with self.state_cond: - old_state = self.state - self.state = new_state - self.state_cond.notify_all() - print ("observer updated state to", new_state) - - def wait_for_negotiation_states(self, states): - ret = None - with self.state_cond: - while self.state not in states: - self.state_cond.wait() - print ("observer waited for", states) - ret = self.state - return ret + def _on_data_channel(self, element, channel): + observer = WebRTCBinDataChannelObserver(channel, channel.props.label, 'remote') + self.add_channel(observer) def _update_negotiation_from_description_state(self, desc): new_state = None @@ -101,6 +85,7 @@ class WebRTCBinObserver(object): new_state = NegotiationState.ANSWER_SET assert new_state is not None self._update_negotiation_state(new_state) + return new_state def _deepcopy_session_description(self, desc): # XXX: passing 'offer' to both a promise and an action signal without @@ -115,11 +100,10 @@ class WebRTCBinObserver(object): offer = reply['offer'] new_offer = self._deepcopy_session_description(offer) - promise = Gst.Promise.new_with_change_func(self._on_local_description_set, new_offer) + promise = Gst.Promise.new_with_change_func(self._on_description_set, new_offer) new_offer = self._deepcopy_session_description(offer) self.element.emit('set-local-description', new_offer, promise) - self.on_offer_created.fire(offer) def _on_answer_created(self, promise, element): @@ -128,11 +112,10 @@ class WebRTCBinObserver(object): offer = reply['answer'] new_offer = self._deepcopy_session_description(offer) - promise = Gst.Promise.new_with_change_func(self._on_local_description_set, new_offer) + promise = Gst.Promise.new_with_change_func(self._on_description_set, new_offer) new_offer = self._deepcopy_session_description(offer) self.element.emit('set-local-description', new_offer, promise) - self.on_answer_created.fire(offer) def create_offer(self, options=None): @@ -144,13 +127,24 @@ class WebRTCBinObserver(object): self.element.emit('create-answer', options, promise) def set_remote_description(self, desc): - promise = Gst.Promise.new_with_change_func(self._on_remote_description_set, desc) + promise = Gst.Promise.new_with_change_func(self._on_description_set, desc) self.element.emit('set-remote-description', desc, promise) def add_ice_candidate(self, mline, candidate): self.element.emit('add-ice-candidate', mline, candidate) + def add_data_channel(self, ident): + channel = self.element.emit('create-data-channel', ident, None) + observer = WebRTCBinDataChannelObserver(channel, ident, 'local') + self.add_channel(observer) + + def wait_for_negotiation_needed(self, generation): + self._negotiation_needed_observer.wait_for ((generation,)) + class WebRTCStream(object): + """ + An stream attached to a webrtcbin element + """ def __init__(self): self.bin = None @@ -189,6 +183,10 @@ class WebRTCStream(object): self.bin.sync_state_with_parent() class WebRTCClient(WebRTCBinObserver): + """ + Client for performing webrtc operations. Controls the pipeline that + contains a webrtcbin element. + """ def __init__(self): self.pipeline = Gst.Pipeline(None) self.webrtcbin = Gst.ElementFactory.make("webrtcbin") @@ -210,3 +208,42 @@ class WebRTCClient(WebRTCBinObserver): stream.set_description(desc) stream.add_and_link_to (self.pipeline, self.webrtcbin, pad) self._streams.append(stream) + + def set_options (self, opts): + if opts.has_field("local-bundle-policy"): + self.webrtcbin.props.bundle_policy = opts["local-bundle-policy"] + + +class WebRTCBinDataChannelObserver(DataChannelObserver): + """ + Data channel observer for a webrtcbin data channel. + """ + def __init__(self, target, ident, location): + super().__init__(ident, location) + self.target = target + self.signal_handlers = [] + self.signal_handlers.append(target.connect("on-open", self._on_open)) + self.signal_handlers.append(target.connect("on-close", self._on_close)) + self.signal_handlers.append(target.connect("on-error", self._on_error)) + self.signal_handlers.append(target.connect("on-message-data", self._on_message_data)) + self.signal_handlers.append(target.connect("on-message-string", self._on_message_string)) + self.signal_handlers.append(target.connect("on-buffered-amount-low", self._on_buffered_amount_low)) + + def _on_open(self, channel): + self._update_state (DataChannelState.OPEN) + def _on_close(self, channel): + self._update_state (DataChannelState.CLOSED) + def _on_error(self, channel): + self._update_state (DataChannelState.ERROR) + def _on_message_data(self, channel, data): + self.data.append(msg) + def _on_message_string(self, channel, msg): + self.got_message (msg) + def _on_buffered_amount_low(self, channel): + pass + + def close(self): + self.target.emit('close') + + def send_string (self, msg): + self.target.emit('send-string', msg) diff --git a/webrtc/check/validate/enums.py b/webrtc/check/validate/enums.py index 14bc31a49d..a23d2c9129 100644 --- a/webrtc/check/validate/enums.py +++ b/webrtc/check/validate/enums.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018, Matthew Waters +# Copyright (c) 2020, Matthew Waters # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -15,22 +15,57 @@ # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, # Boston, MA 02110-1301, USA. -class SignallingState(object): +from enum import Enum, unique + +@unique +class SignallingState(Enum): + """ + State of the signalling protocol. + """ NEW = "new" # no connection has been made + ERROR = "error" # an error was thrown. overrides all others OPEN = "open" # websocket connection is open - ERROR = "error" # and error was thrown. overrides all others HELLO = "hello" # hello was sent and received SESSION = "session" # session setup was sent and received -class NegotiationState(object): - NEW = "new" - ERROR = "error" - NEGOTIATION_NEEDED = "negotiation-needed" - OFFER_CREATED = "offer-created" - ANSWER_CREATED = "answer-created" - OFFER_SET = "offer-set" - ANSWER_SET = "answer-set" +@unique +class NegotiationState(Enum): + """ + State of the webrtc negotiation. Both peers have separate states and are + tracked separately. + """ + NEW = "new" # No negotiation has been performed + ERROR = "error" # an error occured + OFFER_CREATED = "offer-created" # offer was created + ANSWER_CREATED = "answer-created" # answer was created + OFFER_SET = "offer-set" # offer has been set + ANSWER_SET = "answer-set" # answer has been set -class RemoteState(object): - ERROR = "error" - REMOTE_STREAM_RECEIVED = "remote-stream-received" +@unique +class DataChannelState(Enum): + """ + State of a data channel. Each data channel is tracked individually + """ + NEW = "new" # data channel created but not connected + OPEN = "open" # data channel is open, data can flow + CLOSED = "closed" # data channel is closed, sending data will fail + ERROR = "error" # data channel encountered an error + +@unique +class Actions(Enum): + """ + Action names that we implement. Each name is the structure name for each + action as stored in the scenario file. + """ + CREATE_OFFER = "create-offer" # create an offer and send it to the peer + CREATE_ANSWER = "create-answer" # create an answer and send it to the peer + WAIT_FOR_NEGOTIATION_STATE = "wait-for-negotiation-state" # wait for the @NegotiationState to reach a certain value + ADD_STREAM = "add-stream" # add a stream to send to the peer. local only + ADD_DATA_CHANNEL = "add-data-channel" # add a stream to send to the peer. local only + WAIT_FOR_DATA_CHANNEL = "wait-for-data-channel" # wait for a data channel to appear + WAIT_FOR_DATA_CHANNEL_STATE = "wait-for-data-channel-state" # wait for a data channel to have a certain state + SEND_DATA_CHANNEL_STRING = "send-data-channel-string" # send a string over the data channel + WAIT_FOR_DATA_CHANNEL_STRING = "wait-for-data-channel-string" # wait for a string on the data channel + CLOSE_DATA_CHANNEL = "close-data-channel" # close a data channel + WAIT_FOR_NEGOTIATION_NEEDED = "wait-for-negotiation-needed" # wait for negotiation needed to fire + SET_WEBRTC_OPTIONS = "set-webrtc-options" # set some options diff --git a/webrtc/check/validate/observer.py b/webrtc/check/validate/observer.py index b4f6be512e..d60785865f 100644 --- a/webrtc/check/validate/observer.py +++ b/webrtc/check/validate/observer.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018, Matthew Waters +# Copyright (c) 2020, Matthew Waters # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -15,7 +15,17 @@ # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, # Boston, MA 02110-1301, USA. +import logging +import threading + +from enums import NegotiationState, DataChannelState + +l = logging.getLogger(__name__) + class Signal(object): + """ + A class for callback-based signal handlers. + """ def __init__(self, cont_func=None, accum_func=None): self._handlers = [] if not cont_func: @@ -41,3 +51,119 @@ class Signal(object): if not self.cont_func(ret): break return ret + + +class StateObserver(object): + """ + Observe some state. Allows waiting for specific states to occur and + notifying waiters of updated values. Will hold previous states to ensure + @update cannot change the state before @wait_for can look at the state. + """ + def __init__(self, target, attr_name, cond): + self.target = target + self.attr_name = attr_name + self.cond = cond + # track previous states of the value so that the notification still + # occurs even if the field has moved on to another state + self.previous_states = [] + + def wait_for(self, states): + ret = None + with self.cond: + state = getattr (self.target, self.attr_name) + l.debug (str(self.target) + " \'" + self.attr_name + + "\' waiting for " + str(states)) + while True: + l.debug(str(self.target) + " \'" + self.attr_name + + "\' previous states: " + str(self.previous_states)) + for i, s in enumerate (self.previous_states): + if s in states: + l.debug(str(self.target) + " \'" + self.attr_name + + "\' " + str(s) + " found at position " + + str(i) + " of " + str(self.previous_states)) + self.previous_states = self.previous_states[i:] + return s + self.cond.wait() + + def update (self, new_state): + with self.cond: + self.previous_states += [new_state] + setattr(self.target, self.attr_name, new_state) + self.cond.notify_all() + l.debug (str(self.target) + " updated \'" + self.attr_name + "\' to " + str(new_state)) + + +class WebRTCObserver(object): + """ + Base webrtc observer class. Stores a lot of duplicated functionality + between the local and remove peer implementations. + """ + def __init__(self): + self.state = NegotiationState.NEW + self._state_observer = StateObserver(self, "state", threading.Condition()) + self.on_offer_created = Signal() + self.on_answer_created = Signal() + self.on_offer_set = Signal() + self.on_answer_set = Signal() + self.on_data_channel = Signal() + self.data_channels = [] + self._xxxxxxxdata_channel_ids = None + self._data_channels_observer = StateObserver(self, "_xxxxxxxdata_channel_ids", threading.Condition()) + + def _update_negotiation_state(self, new_state): + self._state_observer.update (new_state) + + def wait_for_negotiation_states(self, states): + return self._state_observer.wait_for (states) + + def find_channel (self, ident): + for c in self.data_channels: + if c.ident == ident: + return c + + def add_channel (self, channel): + l.debug('adding channel ' + str (channel) + ' with name ' + str(channel.ident)) + self.data_channels.append (channel) + self._data_channels_observer.update (channel.ident) + self.on_data_channel.fire(channel) + + def wait_for_data_channel(self, ident): + return self._data_channels_observer.wait_for (ident) + + def create_offer(self, options): + raise NotImplementedError() + + def add_data_channel(self, ident): + raise NotImplementedError() + + +class DataChannelObserver(object): + """ + Base webrtc data channelobserver class. Stores a lot of duplicated + functionality between the local and remove peer implementations. + """ + def __init__(self, ident, location): + self.state = DataChannelState.NEW + self._state_observer = StateObserver(self, "state", threading.Condition()) + self.ident = ident + self.location = location + self.message = None + self._message_observer = StateObserver(self, "message", threading.Condition()) + + def _update_state(self, new_state): + self._state_observer.update (new_state) + + def wait_for_states(self, states): + return self._state_observer.wait_for (states) + + def wait_for_message (self, msg): + return self._message_observer.wait_for (msg) + + def got_message(self, msg): + self._message_observer.update (msg) + + def close (self): + raise NotImplementedError() + + def send_string (self, msg): + raise NotImplementedError() diff --git a/webrtc/check/validate/scenarios/bundle_local_balanced_remote_balanced/bundle_policy.scenario b/webrtc/check/validate/scenarios/bundle_local_balanced_remote_balanced/bundle_policy.scenario new file mode 100644 index 0000000000..b3220c4b66 --- /dev/null +++ b/webrtc/check/validate/scenarios/bundle_local_balanced_remote_balanced/bundle_policy.scenario @@ -0,0 +1 @@ +set-vars, local_bundle_policy=balanced, remote_bundle_policy=balanced diff --git a/webrtc/check/validate/scenarios/bundle_local_balanced_remote_max_bundle/bundle_policy.scenario b/webrtc/check/validate/scenarios/bundle_local_balanced_remote_max_bundle/bundle_policy.scenario new file mode 100644 index 0000000000..fd296161ca --- /dev/null +++ b/webrtc/check/validate/scenarios/bundle_local_balanced_remote_max_bundle/bundle_policy.scenario @@ -0,0 +1 @@ +set-vars, local_bundle_policy=balanced, remote_bundle_policy=max-bundle diff --git a/webrtc/check/validate/scenarios/bundle_local_balanced_remote_max_compat/bundle_policy.scenario b/webrtc/check/validate/scenarios/bundle_local_balanced_remote_max_compat/bundle_policy.scenario new file mode 100644 index 0000000000..d770994549 --- /dev/null +++ b/webrtc/check/validate/scenarios/bundle_local_balanced_remote_max_compat/bundle_policy.scenario @@ -0,0 +1 @@ +set-vars, local_bundle_policy=balanced, remote_bundle_policy=max-compat diff --git a/webrtc/check/validate/scenarios/bundle_local_max_bundle_remote_balanced/bundle_policy.scenario b/webrtc/check/validate/scenarios/bundle_local_max_bundle_remote_balanced/bundle_policy.scenario new file mode 100644 index 0000000000..34c2946e13 --- /dev/null +++ b/webrtc/check/validate/scenarios/bundle_local_max_bundle_remote_balanced/bundle_policy.scenario @@ -0,0 +1 @@ +set-vars, local_bundle_policy=max-bundle, remote_bundle_policy=balanced diff --git a/webrtc/check/validate/scenarios/bundle_local_max_bundle_remote_max_bundle/bundle_policy.scenario b/webrtc/check/validate/scenarios/bundle_local_max_bundle_remote_max_bundle/bundle_policy.scenario new file mode 100644 index 0000000000..6504adf559 --- /dev/null +++ b/webrtc/check/validate/scenarios/bundle_local_max_bundle_remote_max_bundle/bundle_policy.scenario @@ -0,0 +1 @@ +set-vars, local_bundle_policy=max-bundle, remote_bundle_policy=max-bundle diff --git a/webrtc/check/validate/scenarios/bundle_local_max_bundle_remote_max_compat/bundle_policy.scenario b/webrtc/check/validate/scenarios/bundle_local_max_bundle_remote_max_compat/bundle_policy.scenario new file mode 100644 index 0000000000..cbe82f40ec --- /dev/null +++ b/webrtc/check/validate/scenarios/bundle_local_max_bundle_remote_max_compat/bundle_policy.scenario @@ -0,0 +1 @@ +set-vars, local_bundle_policy=max-bundle, remote_bundle_policy=max-compat diff --git a/webrtc/check/validate/scenarios/bundle_local_max_compat_remote_balanced/bundle_policy.scenario b/webrtc/check/validate/scenarios/bundle_local_max_compat_remote_balanced/bundle_policy.scenario new file mode 100644 index 0000000000..30bd7d16a4 --- /dev/null +++ b/webrtc/check/validate/scenarios/bundle_local_max_compat_remote_balanced/bundle_policy.scenario @@ -0,0 +1 @@ +set-vars, local_bundle_policy=max-compat, remote_bundle_policy=balanced diff --git a/webrtc/check/validate/scenarios/bundle_local_max_compat_remote_max_bundle/bundle_policy.scenario b/webrtc/check/validate/scenarios/bundle_local_max_compat_remote_max_bundle/bundle_policy.scenario new file mode 100644 index 0000000000..2b564b9711 --- /dev/null +++ b/webrtc/check/validate/scenarios/bundle_local_max_compat_remote_max_bundle/bundle_policy.scenario @@ -0,0 +1 @@ +set-vars, local_bundle_policy=max-compat, remote_bundle_policy=max-bundle diff --git a/webrtc/check/validate/scenarios/bundle_local_max_compat_remote_max_compat/bundle_policy.scenario b/webrtc/check/validate/scenarios/bundle_local_max_compat_remote_max_compat/bundle_policy.scenario new file mode 100644 index 0000000000..34508fab8d --- /dev/null +++ b/webrtc/check/validate/scenarios/bundle_local_max_compat_remote_max_compat/bundle_policy.scenario @@ -0,0 +1 @@ +set-vars, local_bundle_policy=max-compat, remote_bundle_policy=max-compat diff --git a/webrtc/check/validate/scenarios/bundle_local_none_remote_balanced/bundle_policy.scenario b/webrtc/check/validate/scenarios/bundle_local_none_remote_balanced/bundle_policy.scenario new file mode 100644 index 0000000000..57d208ae5d --- /dev/null +++ b/webrtc/check/validate/scenarios/bundle_local_none_remote_balanced/bundle_policy.scenario @@ -0,0 +1 @@ +set-vars, local_bundle_policy=none, remote_bundle_policy=balanced diff --git a/webrtc/check/validate/scenarios/bundle_local_none_remote_max_bundle/bundle_policy.scenario b/webrtc/check/validate/scenarios/bundle_local_none_remote_max_bundle/bundle_policy.scenario new file mode 100644 index 0000000000..adee196bf4 --- /dev/null +++ b/webrtc/check/validate/scenarios/bundle_local_none_remote_max_bundle/bundle_policy.scenario @@ -0,0 +1 @@ +set-vars, local_bundle_policy=none, remote_bundle_policy=max-bundle diff --git a/webrtc/check/validate/scenarios/bundle_local_none_remote_max_compat/bundle_policy.scenario b/webrtc/check/validate/scenarios/bundle_local_none_remote_max_compat/bundle_policy.scenario new file mode 100644 index 0000000000..370881547d --- /dev/null +++ b/webrtc/check/validate/scenarios/bundle_local_none_remote_max_compat/bundle_policy.scenario @@ -0,0 +1 @@ +set-vars, local_bundle_policy=none, remote_bundle_policy=max-compat diff --git a/webrtc/check/validate/scenarios/local_initiates_negotiation/negotiation_initiator.scenario b/webrtc/check/validate/scenarios/local_initiates_negotiation/negotiation_initiator.scenario new file mode 100644 index 0000000000..022d2357fd --- /dev/null +++ b/webrtc/check/validate/scenarios/local_initiates_negotiation/negotiation_initiator.scenario @@ -0,0 +1 @@ +set-vars, negotiation_initiator=local, negotiation_responder=remote diff --git a/webrtc/check/validate/scenarios/offer_answer.scenario b/webrtc/check/validate/scenarios/offer_answer.scenario index 559f65fad7..4e797bd2e8 100644 --- a/webrtc/check/validate/scenarios/offer_answer.scenario +++ b/webrtc/check/validate/scenarios/offer_answer.scenario @@ -1,3 +1,15 @@ -description, summary="Produce an offer" -create-offer; -wait-for-negotiation-state, state="answer-set" +description, summary="Produce an offer and negotiate it with the peer" +include,location=negotiation_initiator.scenario +include,location=bundle_policy.scenario + +set-webrtc-options, local-bundle-policy="$(local_bundle_policy)", remote-bundle-policy="$(remote_bundle_policy)" + +create-offer, which="$(negotiation_initiator)"; +# all of these waits are technically unnecessary and only the last is needed +wait-for-negotiation-state, which="$(negotiation_initiator)", state="offer-created" +wait-for-negotiation-state, which="$(negotiation_initiator)", state="offer-set" +wait-for-negotiation-state, which="$(negotiation_responder)", state="offer-set" +create-answer, which="$(negotiation_responder)"; +wait-for-negotiation-state, which="$(negotiation_responder)", state="answer-created" +wait-for-negotiation-state, which="$(negotiation_responder)", state="answer-set" +wait-for-negotiation-state, which="$(negotiation_initiator)", state="answer-set" diff --git a/webrtc/check/validate/scenarios/open_data_channel.scenario b/webrtc/check/validate/scenarios/open_data_channel.scenario new file mode 100644 index 0000000000..8c1bc391b1 --- /dev/null +++ b/webrtc/check/validate/scenarios/open_data_channel.scenario @@ -0,0 +1,23 @@ +description, summary="Open a data channel" +include,location=negotiation_initiator.scenario +include,location=bundle_policy.scenario + +set-webrtc-options, local-bundle-policy="$(local_bundle_policy)", remote-bundle-policy="$(remote_bundle_policy)" + +# add the channel on the initiator so that datachannel is added to the sdp +add-data-channel, which="$(negotiation_initiator)", id="gstreamer"; + +# negotiate +create-offer, which="$(negotiation_initiator)"; +wait-for-negotiation-state, which="$(negotiation_responder)", state="offer-set" +create-answer, which="$(negotiation_responder)"; +wait-for-negotiation-state, which="$(negotiation_initiator)", state="answer-set" + +# ensure data channel is created +wait-for-data-channel, which="$(negotiation_responder)", id="gstreamer"; +wait-for-data-channel, which="$(negotiation_initiator)", id="gstreamer"; +wait-for-data-channel-state, which="$(negotiation_initiator)", id="gstreamer", state="open"; + +# only the browser closing works at the moment +close-data-channel, which="remote", id="gstreamer" +wait-for-data-channel-state, which="local", id="gstreamer", state="closed"; diff --git a/webrtc/check/validate/scenarios/remote_initiates_negotiation/negotiation_initiator.scenario b/webrtc/check/validate/scenarios/remote_initiates_negotiation/negotiation_initiator.scenario new file mode 100644 index 0000000000..07ba3bbdfe --- /dev/null +++ b/webrtc/check/validate/scenarios/remote_initiates_negotiation/negotiation_initiator.scenario @@ -0,0 +1 @@ +set-vars, negotiation_initiator=remote, negotiation_responder=local diff --git a/webrtc/check/validate/scenarios/send_data_channel_string.scenario b/webrtc/check/validate/scenarios/send_data_channel_string.scenario new file mode 100644 index 0000000000..5d247baee1 --- /dev/null +++ b/webrtc/check/validate/scenarios/send_data_channel_string.scenario @@ -0,0 +1,21 @@ +description, summary="Send data over a data channel" +include,location=negotiation_initiator.scenario +include,location=bundle_policy.scenario + +set-webrtc-options, local-bundle-policy="$(local_bundle_policy)", remote-bundle-policy="$(remote_bundle_policy)" + +add-data-channel, which="$(negotiation_initiator)", id="gstreamer"; + +create-offer, which="$(negotiation_initiator)"; +wait-for-negotiation-state, which="$(negotiation_responder)", state="offer-set" +create-answer, which="$(negotiation_responder)"; +wait-for-negotiation-state, which="$(negotiation_initiator)", state="answer-set" + +# wait for the data channel to appear +wait-for-data-channel, which="$(negotiation_initiator)", id="gstreamer"; +wait-for-data-channel, which="$(negotiation_responder)", id="gstreamer"; +wait-for-data-channel-state, which="$(negotiation_initiator)", id="gstreamer", state="open"; + +# send something +send-data-channel-string, which="local", id="gstreamer", msg="some data"; +wait-for-data-channel-string, which="remote", id="gstreamer", msg="some data"; diff --git a/webrtc/check/validate/scenarios/vp8_send_stream.scenario b/webrtc/check/validate/scenarios/vp8_send_stream.scenario index 2ec15a5391..308148886f 100644 --- a/webrtc/check/validate/scenarios/vp8_send_stream.scenario +++ b/webrtc/check/validate/scenarios/vp8_send_stream.scenario @@ -1,5 +1,15 @@ description, summary="Send a VP8 stream", handles-state=true +include,location=negotiation_initiator.scenario +include,location=bundle_policy.scenario + +set-webrtc-options, local-bundle-policy="$(local_bundle_policy)", remote-bundle-policy="$(remote_bundle_policy)" + add-stream, pipeline="videotestsrc is-live=1 ! vp8enc ! rtpvp8pay ! queue" set-state, state="playing"; -create-offer; -wait-for-negotiation-state, state="answer-set" +wait-for-negotiation-needed, generation=1; + +# negotiate +create-offer, which="$(negotiation_initiator)"; +wait-for-negotiation-state, which="$(negotiation_responder)", state="offer-set" +create-answer, which="$(negotiation_responder)"; +wait-for-negotiation-state, which="$(negotiation_initiator)", state="answer-set" diff --git a/webrtc/check/validate/signalling.py b/webrtc/check/validate/signalling.py index 2a7d9388b8..2e6d6748cc 100644 --- a/webrtc/check/validate/signalling.py +++ b/webrtc/check/validate/signalling.py @@ -1,6 +1,4 @@ -#!/usr/bin/env python3 -# -# Copyright (c) 2018, Matthew Waters +# Copyright (c) 2020, Matthew Waters # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -24,11 +22,17 @@ import os import sys import threading import json +import logging -from observer import Signal -from enums import SignallingState, RemoteState +from observer import Signal, StateObserver, WebRTCObserver, DataChannelObserver +from enums import SignallingState, NegotiationState, DataChannelState + +l = logging.getLogger(__name__) class AsyncIOThread(threading.Thread): + """ + Run an asyncio loop in another thread. + """ def __init__ (self, loop): threading.Thread.__init__(self) self.loop = loop @@ -40,16 +44,24 @@ class AsyncIOThread(threading.Thread): def stop_thread(self): self.loop.call_soon_threadsafe(self.loop.stop) + class SignallingClientThread(object): + """ + Connect to a signalling server + """ def __init__(self, server): + # server string to connect to. Passed directly to websockets.connect() self.server = server + # fired after we have connected to the signalling server self.wss_connected = Signal() + # fired every time we receive a message from the signalling server self.message = Signal() self._init_async() def _init_async(self): + self._running = False self.conn = None self._loop = asyncio.new_event_loop() @@ -59,23 +71,31 @@ class SignallingClientThread(object): self._loop.call_soon_threadsafe(lambda: asyncio.ensure_future(self._a_loop())) async def _a_connect(self): + # connect to the signalling server assert not self.conn sslctx = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH) self.conn = await websockets.connect(self.server, ssl=sslctx) async def _a_loop(self): + self._running = True + l.info('loop started') await self._a_connect() self.wss_connected.fire() assert self.conn async for message in self.conn: self.message.fire(message) + l.info('loop exited') def send(self, data): + # send some information to the peer async def _a_send(): await self.conn.send(data) self._loop.call_soon_threadsafe(lambda: asyncio.ensure_future(_a_send())) def stop(self): + if self._running == False: + return + cond = threading.Condition() # asyncio, why you so complicated to stop ? @@ -87,64 +107,70 @@ class SignallingClientThread(object): to_wait = [t for t in tasks if not t.done()] if to_wait: + l.info('waiting for ' + str(to_wait)) done, pending = await asyncio.wait(to_wait) with cond: + l.error('notifying cond') cond.notify() + self._running = False - self._loop.call_soon_threadsafe(lambda: asyncio.ensure_future(_a_stop())) with cond: - cond.wait() + self._loop.call_soon_threadsafe(lambda: asyncio.ensure_future(_a_stop())) + l.error('cond waiting') + cond.wait() + l.error('cond waited') self._thread.stop_thread() self._thread.join() + l.error('thread joined') + class WebRTCSignallingClient(SignallingClientThread): + """ + Signalling client implementation. Deals wit session management over the + signalling protocol. Sends and receives from a peer. + """ def __init__(self, server, id_): super().__init__(server) self.wss_connected.connect(self._on_connection) self.message.connect(self._on_message) self.state = SignallingState.NEW - self._state_cond = threading.Condition() + self._state_observer = StateObserver(self, "state", threading.Condition()) self.id = id_ self._peerid = None - # override that base class + # fired when the hello has been received self.connected = Signal() + # fired when the signalling server responds that the session creation is ok self.session_created = Signal() + # fired on an error self.error = Signal() + # fired when the peer receives some json data self.have_json = Signal() - def wait_for_states(self, states): - ret = None - with self._state_cond: - while self.state not in states: - self._state_cond.wait() - ret = self.state - return ret + def _update_state(self, new_state): + self._state_observer.update (new_state) - def _update_state(self, state): - with self._state_cond: - if self.state is not SignallingState.ERROR: - self.state = state - self._state_cond.notify_all() + def wait_for_states(self, states): + return self._state_observer.wait_for (states) def hello(self): self.send('HELLO ' + str(self.id)) + l.info("sent HELLO") self.wait_for_states([SignallingState.HELLO]) - print("signalling-client sent HELLO") def create_session(self, peerid): self._peerid = peerid self.send('SESSION {}'.format(self._peerid)) - self.wait_for_states([SignallingState.SESSION, SignallingState.ERROR]) - print("signalling-client sent SESSION") + l.info("sent SESSION") + self.wait_for_states([SignallingState.SESSION]) def _on_connection(self): self._update_state (SignallingState.OPEN) def _on_message(self, message): - print("signalling-client received", message) + l.debug("received: " + message) if message == 'HELLO': self._update_state (SignallingState.HELLO) self.connected.fire() @@ -159,3 +185,82 @@ class WebRTCSignallingClient(SignallingClientThread): self.have_json.fire(msg) return False + +class RemoteWebRTCObserver(WebRTCObserver): + """ + Use information sent over the signalling channel to construct the current + state of a remote peer. Allow performing actions by sending requests over + the signalling channel. + """ + def __init__(self, signalling): + super().__init__() + self.signalling = signalling + + def on_json(msg): + if 'STATE' in msg: + state = NegotiationState (msg['STATE']) + self._update_negotiation_state(state) + if state == NegotiationState.OFFER_CREATED: + self.on_offer_created.fire(msg['description']) + elif state == NegotiationState.ANSWER_CREATED: + self.on_answer_created.fire(msg['description']) + elif state == NegotiationState.OFFER_SET: + self.on_offer_set.fire (msg['description']) + elif state == NegotiationState.ANSWER_SET: + self.on_answer_set.fire (msg['description']) + elif 'DATA-NEW' in msg: + new = msg['DATA-NEW'] + observer = RemoteDataChannelObserver(new['id'], new['location'], self) + self.add_channel (observer) + elif 'DATA-STATE' in msg: + ident = msg['id'] + channel = self.find_channel(ident) + channel._update_state (DataChannelState(msg['DATA-STATE'])) + elif 'DATA-MSG' in msg: + ident = msg['id'] + channel = self.find_channel(ident) + channel.got_message(msg['DATA-MSG']) + self.signalling.have_json.connect (on_json) + + def add_data_channel (self, ident): + msg = json.dumps({'DATA_CREATE': {'id': ident}}) + self.signalling.send (msg) + + def create_offer (self): + msg = json.dumps({'CREATE_OFFER': ""}) + self.signalling.send (msg) + + def create_answer (self): + msg = json.dumps({'CREATE_ANSWER': ""}) + self.signalling.send (msg) + + def set_title (self, title): + # entirely for debugging purposes + msg = json.dumps({'SET_TITLE': title}) + self.signalling.send (msg) + + def set_options (self, opts): + options = {} + if opts.has_field("remote-bundle-policy"): + options["bundlePolicy"] = opts["remote-bundle-policy"] + msg = json.dumps({'OPTIONS' : options}) + self.signalling.send (msg) + + +class RemoteDataChannelObserver(DataChannelObserver): + """ + Use information sent over the signalling channel to construct the current + state of a remote peer's data channel. Allow performing actions by sending + requests over the signalling channel. + """ + def __init__(self, ident, location, webrtc): + super().__init__(ident, location) + self.webrtc = webrtc + + def send_string(self, msg): + msg = json.dumps({'DATA_SEND_MSG': {'msg' : msg, 'id': self.ident}}) + self.webrtc.signalling.send (msg) + + def close (self): + msg = json.dumps({'DATA_CLOSE': {'id': self.ident}}) + self.webrtc.signalling.send (msg) diff --git a/webrtc/check/validate/testsuites/webrtc.py b/webrtc/check/validate/testsuites/webrtc.py index c078e5d314..3101098202 100644 --- a/webrtc/check/validate/testsuites/webrtc.py +++ b/webrtc/check/validate/testsuites/webrtc.py @@ -1,6 +1,4 @@ -#*- vi:si:et:sw=4:sts=4:ts=4:syntax=python -# -# Copyright (c) 2018 Matthew Waters +# Copyright (c) 2020 Matthew Waters # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -25,12 +23,9 @@ import os TEST_MANAGER = "webrtc" -BLACKLIST = [ - ] def setup_tests(test_manager, options): print("Setting up webrtc tests") -# test_manager.set_default_blacklist(BLACKLIST) return True diff --git a/webrtc/check/validate/web/single_stream.html b/webrtc/check/validate/web/single_stream.html index 9a95fb7491..218b200ed0 100644 --- a/webrtc/check/validate/web/single_stream.html +++ b/webrtc/check/validate/web/single_stream.html @@ -27,9 +27,5 @@
Our id is unknown

-
-
getUserMedia constraints being used:
-
-
diff --git a/webrtc/check/validate/web/webrtc.js b/webrtc/check/validate/web/webrtc.js index eb7e00f5e0..2a0d5358d1 100644 --- a/webrtc/check/validate/web/webrtc.js +++ b/webrtc/check/validate/web/webrtc.js @@ -14,13 +14,12 @@ var ws_port; var default_peer_id; // Override with your own STUN servers if you want var rtc_configuration = {iceServers: [{urls: "stun:stun.services.mozilla.com"}, - {urls: "stun:stun.l.google.com:19302"}]}; -// The default constraints that will be attempted. Can be overriden by the user. -var default_constraints = {video: true, audio: true}; + {urls: "stun:stun.l.google.com:19302"},]}; +var default_constraints = {video: true, audio: false}; var connect_attempts = 0; var peer_connection; -var send_channel; +var channels = [] var ws_conn; // Promise for local stream after constraints are approved by the user var local_stream_promise; @@ -56,7 +55,7 @@ function setError(text) { var span = document.getElementById("status") span.textContent = text; span.classList.add('error'); - ws_conn.send(JSON.stringify({'STATE': 'error'})) + ws_conn.send(JSON.stringify({'STATE': 'error', 'msg' : text})) } function resetVideo() { @@ -75,27 +74,40 @@ function resetVideo() { videoElement.load(); } +function updateRemoteStateFromSetSDPJson(sdp) { + if (sdp.type == "offer") + ws_conn.send(JSON.stringify({'STATE': 'offer-set', 'description' : sdp})) + else if (sdp.type == "answer") + ws_conn.send(JSON.stringify({'STATE': 'answer-set', 'description' : sdp})) + else + throw new Error ("Unknown SDP type!"); +} + +function updateRemoteStateFromGeneratedSDPJson(sdp) { + if (sdp.type == "offer") + ws_conn.send(JSON.stringify({'STATE': 'offer-created', 'description' : sdp})) + else if (sdp.type == "answer") + ws_conn.send(JSON.stringify({'STATE': 'answer-created', 'description' : sdp})) + else + throw new Error ("Unknown SDP type!"); +} + // SDP offer received from peer, set remote description and create an answer function onIncomingSDP(sdp) { peer_connection.setRemoteDescription(sdp).then(() => { - setStatus("Remote SDP set"); - if (sdp.type != "offer") - return; - setStatus("Got SDP offer"); - local_stream_promise.then((stream) => { - setStatus("Got local stream, creating answer"); - peer_connection.createAnswer() - .then(onLocalDescription).catch(setError); - }).catch(setError); + updateRemoteStateFromSetSDPJson(sdp) + setStatus("Set remote SDP", sdp.type); }).catch(setError); } // Local description was set, send it to peer function onLocalDescription(desc) { + updateRemoteStateFromGeneratedSDPJson(desc) console.log("Got local description: " + JSON.stringify(desc)); peer_connection.setLocalDescription(desc).then(function() { - setStatus("Sending SDP answer"); - sdp = {'sdp': peer_connection.localDescription} + updateRemoteStateFromSetSDPJson(desc) + sdp = {'sdp': desc} + setStatus("Sending SDP", sdp.type); ws_conn.send(JSON.stringify(sdp)); }); } @@ -103,9 +115,33 @@ function onLocalDescription(desc) { // ICE candidate received from peer, add it to the peer connection function onIncomingICE(ice) { var candidate = new RTCIceCandidate(ice); + console.log("adding candidate", candidate) peer_connection.addIceCandidate(candidate).catch(setError); } +function createOffer(offer) { + local_stream_promise.then((stream) => { + setStatus("Got local stream, creating offer"); + peer_connection.createOffer() + .then(onLocalDescription).catch(setError); + }).catch(setError) +} + +function createAnswer(offer) { + local_stream_promise.then((stream) => { + setStatus("Got local stream, creating answer"); + peer_connection.createAnswer() + .then(onLocalDescription).catch(setError); + }).catch(setError) +} + +function handleOptions(options) { + console.log ('received options', options); + if (options.bundlePolicy != null) { + rtc_configuration['bundlePolicy'] = options.bundlePolicy; + } +} + function onServerMessage(event) { console.log("Received " + event.data); switch (event.data) { @@ -129,14 +165,33 @@ function onServerMessage(event) { return; } + if (msg.SET_TITLE != null) { + // some debugging for tests that hang around + document.title = msg['SET_TITLE'] + return; + } else if (msg.OPTIONS != null) { + handleOptions(msg.OPTIONS); + return; + } + // Incoming JSON signals the beginning of a call if (!peer_connection) - createCall(msg); + createCall(); if (msg.sdp != null) { onIncomingSDP(msg.sdp); } else if (msg.ice != null) { onIncomingICE(msg.ice); + } else if (msg.CREATE_OFFER != null) { + createOffer(msg.CREATE_OFFER) + } else if (msg.CREATE_ANSWER != null) { + createAnswer(msg.CREATE_ANSWER) + } else if (msg.DATA_CREATE != null) { + addDataChannel(msg.DATA_CREATE.id) + } else if (msg.DATA_CLOSE != null) { + closeDataChannel(msg.DATA_CLOSE.id) + } else if (msg.DATA_SEND_MSG != null) { + sendDataChannelMessage(msg.DATA_SEND_MSG) } else { handleIncomingError("Unknown incoming JSON: " + msg); } @@ -151,6 +206,7 @@ function onServerClose(event) { peer_connection.close(); peer_connection = null; } + channels = [] // Reset after a second window.setTimeout(websocketServerConnect, 1000); @@ -164,14 +220,7 @@ function onServerError(event) { function getLocalStream() { var constraints; - var textarea = document.getElementById('constraints'); - try { - constraints = JSON.parse(textarea.value); - } catch (e) { - console.error(e); - setError('ERROR parsing constraints: ' + e.message + ', using default constraints'); - constraints = default_constraints; - } + constraints = default_constraints; console.log(JSON.stringify(constraints)); // Add local stream @@ -192,12 +241,7 @@ function websocketServerConnect() { var span = document.getElementById("status"); span.classList.remove('error'); span.textContent = ''; - // Populate constraints - var textarea = document.getElementById('constraints'); - if (textarea.value == '') - textarea.value = JSON.stringify(default_constraints); // Fetch the peer id to use - var url = new URL(window.location.href); peer_id = url.searchParams.get("id"); @@ -236,7 +280,6 @@ function onRemoteStreamAdded(event) { if (videoTracks.length > 0) { console.log('Incoming stream: ' + videoTracks.length + ' video tracks and ' + audioTracks.length + ' audio tracks'); getVideoElement().srcObject = event.stream; - ws_conn.send(JSON.stringify({'STATE': 'remote-stream-received'})) } else { handleIncomingError('Stream with unknown tracks added, resetting'); } @@ -246,53 +289,80 @@ function errorUserMediaHandler() { setError("Browser doesn't support getUserMedia!"); } -const handleDataChannelOpen = (event) =>{ - console.log("dataChannel.OnOpen", event); -}; - const handleDataChannelMessageReceived = (event) =>{ console.log("dataChannel.OnMessage:", event, event.data.type); - setStatus("Received data channel message"); - if (typeof event.data === 'string' || event.data instanceof String) { - console.log('Incoming string message: ' + event.data); - textarea = document.getElementById("text") - textarea.value = textarea.value + '\n' + event.data - } else { - console.log('Incoming data message'); - } - send_channel.send("Hi! (from browser)"); + ws_conn.send(JSON.stringify({'DATA-MSG' : event.data, 'id' : event.target.label})); +}; + +const handleDataChannelOpen = (event) =>{ + console.log("dataChannel.OnOpen", event); + ws_conn.send(JSON.stringify({'DATA-STATE' : 'open', 'id' : event.target.label})); }; const handleDataChannelError = (error) =>{ console.log("dataChannel.OnError:", error); + ws_conn.send(JSON.stringify({'DATA-STATE' : error, 'id' : event.target.label})); }; const handleDataChannelClose = (event) =>{ console.log("dataChannel.OnClose", event); + ws_conn.send(JSON.stringify({'DATA-STATE' : 'closed', 'id' : event.target.label})); }; function onDataChannel(event) { setStatus("Data channel created"); - let receiveChannel = event.channel; - receiveChannel.onopen = handleDataChannelOpen; - receiveChannel.onmessage = handleDataChannelMessageReceived; - receiveChannel.onerror = handleDataChannelError; - receiveChannel.onclose = handleDataChannelClose; + let channel = event.channel; + console.log('adding remote data channel with label', channel.label) + ws_conn.send(JSON.stringify({'DATA-NEW' : {'id' : channel.label, 'location' : 'remote'}})); + channel.onopen = handleDataChannelOpen; + channel.onmessage = handleDataChannelMessageReceived; + channel.onerror = handleDataChannelError; + channel.onclose = handleDataChannelClose; + channels.push(channel) } -function createCall(msg) { +function addDataChannel(label) { + channel = peer_connection.createDataChannel(label, null); + console.log('adding local data channel with label', label) + ws_conn.send(JSON.stringify({'DATA-NEW' : {'id' : label, 'location' : 'local'}})); + channel.onopen = handleDataChannelOpen; + channel.onmessage = handleDataChannelMessageReceived; + channel.onerror = handleDataChannelError; + channel.onclose = handleDataChannelClose; + channels.push(channel) +} + +function find_channel(label) { + console.log('find', label, 'in', channels) + for (var c in channels) { + if (channels[c].label === label) { + console.log('found', label, c) + return channels[c]; + } + } + return null; +} + +function closeDataChannel(label) { + channel = find_channel (label) + console.log('closing data channel with label', label) + channel.close() +} + +function sendDataChannelMessage(msg) { + channel = find_channel (msg.id) + console.log('sending on data channel', msg.id, 'message', msg.msg) + channel.send(msg.msg) +} + +function createCall() { // Reset connection attempts because we connected successfully connect_attempts = 0; - console.log('Creating RTCPeerConnection'); + console.log('Creating RTCPeerConnection with configuration', rtc_configuration); peer_connection = new RTCPeerConnection(rtc_configuration); - send_channel = peer_connection.createDataChannel('label', null); - send_channel.onopen = handleDataChannelOpen; - send_channel.onmessage = handleDataChannelMessageReceived; - send_channel.onerror = handleDataChannelError; - send_channel.onclose = handleDataChannelClose; peer_connection.ondatachannel = onDataChannel; peer_connection.onaddstream = onRemoteStreamAdded; /* Send our video/audio to the other peer */ @@ -302,18 +372,15 @@ function createCall(msg) { return stream; }).catch(setError); - if (!msg.sdp) { - console.log("WARNING: First message wasn't an SDP message!?"); - } - peer_connection.onicecandidate = (event) => { - // We have a candidate, send it to the remote party with the - // same uuid - if (event.candidate == null) { + // We have a candidate, send it to the remote party with the + // same uuid + if (event.candidate == null) { console.log("ICE Candidate was null, done"); return; - } - ws_conn.send(JSON.stringify({'ice': event.candidate})); + } + console.log("generated ICE Candidate", event.candidate); + ws_conn.send(JSON.stringify({'ice': event.candidate})); }; setStatus("Created peer connection for call, waiting for SDP"); diff --git a/webrtc/check/validate/webrtc_validate.py b/webrtc/check/validate/webrtc_validate.py index 6215a0a2e2..67f3aebf4e 100644 --- a/webrtc/check/validate/webrtc_validate.py +++ b/webrtc/check/validate/webrtc_validate.py @@ -21,12 +21,13 @@ import os import sys import argparse import json +import logging -from signalling import WebRTCSignallingClient -from actions import register_action_types, ActionObserver +from signalling import WebRTCSignallingClient, RemoteWebRTCObserver +from actions import ActionObserver from client import WebRTCClient from browser import Browser, create_driver -from enums import SignallingState, NegotiationState, RemoteState +from enums import SignallingState, NegotiationState, DataChannelState, Actions import gi gi.require_version("GLib", "2.0") @@ -40,14 +41,20 @@ from gi.repository import GstSdp gi.require_version("GstValidate", "1.0") from gi.repository import GstValidate +FORMAT = '%(asctime)-23s %(levelname)-7s %(thread)d %(name)-24s\t%(funcName)-24s %(message)s' +LEVEL = os.environ.get("LOGLEVEL", "DEBUG") +logging.basicConfig(level=LEVEL, format=FORMAT) +l = logging.getLogger(__name__) + class WebRTCApplication(object): - def __init__(self, server, id_, peerid, scenario_name, browser_name, html_source): + def __init__(self, server, id_, peerid, scenario_name, browser_name, html_source, test_name=None): self.server = server self.peerid = peerid self.html_source = html_source self.id = id_ self.scenario_name = scenario_name self.browser_name = browser_name + self.test_name = test_name def _init_validate(self, scenario_file): self.runner = GstValidate.Runner.new() @@ -61,24 +68,79 @@ class WebRTCApplication(object): self.client.pipeline.set_state(Gst.State.PLAYING) def _on_scenario_done(self, scenario): - self.quit() + l.error ('scenario done') + GLib.idle_add(self.quit) - def _connect_actions(self): - def create_offer(): - self.client.create_offer(None) - return GstValidate.ActionReturn.OK - self.actions.create_offer.connect(create_offer) + def _connect_actions(self, actions): + def on_action(atype, action): + """ + From a validate action, perform the action as required + """ + if atype == Actions.CREATE_OFFER: + assert action.structure["which"] in ("local", "remote") + c = self.client if action.structure["which"] == "local" else self.remote_client + c.create_offer() + return GstValidate.ActionReturn.OK + elif atype == Actions.CREATE_ANSWER: + assert action.structure["which"] in ("local", "remote") + c = self.client if action.structure["which"] == "local" else self.remote_client + c.create_answer() + return GstValidate.ActionReturn.OK + elif atype == Actions.WAIT_FOR_NEGOTIATION_STATE: + states = [NegotiationState(action.structure["state"]), NegotiationState.ERROR] + assert action.structure["which"] in ("local", "remote") + c = self.client if action.structure["which"] == "local" else self.remote_client + state = c.wait_for_negotiation_states(states) + return GstValidate.ActionReturn.OK if state != NegotiationState.ERROR else GstValidate.ActionReturn.ERROR + elif atype == Actions.ADD_STREAM: + self.client.add_stream(action.structure["pipeline"]) + return GstValidate.ActionReturn.OK + elif atype == Actions.ADD_DATA_CHANNEL: + assert action.structure["which"] in ("local", "remote") + c = self.client if action.structure["which"] == "local" else self.remote_client + c.add_data_channel(action.structure["id"]) + return GstValidate.ActionReturn.OK + elif atype == Actions.SEND_DATA_CHANNEL_STRING: + assert action.structure["which"] in ("local", "remote") + c = self.client if action.structure["which"] == "local" else self.remote_client + channel = c.find_channel (action.structure["id"]) + channel.send_string (action.structure["msg"]) + return GstValidate.ActionReturn.OK + elif atype == Actions.WAIT_FOR_DATA_CHANNEL_STATE: + assert action.structure["which"] in ("local", "remote") + c = self.client if action.structure["which"] == "local" else self.remote_client + states = [DataChannelState(action.structure["state"]), DataChannelState.ERROR] + channel = c.find_channel (action.structure["id"]) + state = channel.wait_for_states(states) + return GstValidate.ActionReturn.OK if state != DataChannelState.ERROR else GstValidate.ActionReturn.ERROR + elif atype == Actions.CLOSE_DATA_CHANNEL: + assert action.structure["which"] in ("local", "remote") + c = self.client if action.structure["which"] == "local" else self.remote_client + channel = c.find_channel (action.structure["id"]) + channel.close() + return GstValidate.ActionReturn.OK + elif atype == Actions.WAIT_FOR_DATA_CHANNEL: + assert action.structure["which"] in ("local", "remote") + c = self.client if action.structure["which"] == "local" else self.remote_client + state = c.wait_for_data_channel(action.structure["id"]) + return GstValidate.ActionReturn.OK + elif atype == Actions.WAIT_FOR_DATA_CHANNEL_STRING: + assert action.structure["which"] in ("local", "remote") + c = self.client if action.structure["which"] == "local" else self.remote_client + channel = c.find_channel (action.structure["id"]) + channel.wait_for_message(action.structure["msg"]) + return GstValidate.ActionReturn.OK + elif atype == Actions.WAIT_FOR_NEGOTIATION_NEEDED: + self.client.wait_for_negotiation_needed(action.structure["generation"]) + return GstValidate.ActionReturn.OK + elif atype == Actions.SET_WEBRTC_OPTIONS: + self.client.set_options (action.structure) + self.remote_client.set_options (action.structure) + return GstValidate.ActionReturn.OK + else: + assert "Not reached" == "" - def wait_for_negotiation_state(state): - states = [state, NegotiationState.ERROR] - state = self.client.wait_for_negotiation_states(states) - return GstValidate.ActionReturn.OK if state != RemoteState.ERROR else GstValidate.ActionReturn.ERROR - self.actions.wait_for_negotiation_state.connect(wait_for_negotiation_state) - - def add_stream(pipeline): - self.client.add_stream(pipeline) - return GstValidate.ActionReturn.OK - self.actions.add_stream.connect(add_stream) + actions.action.connect (on_action) def _connect_client_observer(self): def on_offer_created(offer): @@ -87,6 +149,12 @@ class WebRTCApplication(object): self.signalling.send(msg) self.client.on_offer_created.connect(on_offer_created) + def on_answer_created(answer): + text = answer.sdp.as_text() + msg = json.dumps({'sdp': {'type': 'answer', 'sdp': text}}) + self.signalling.send(msg) + self.client.on_answer_created.connect(on_answer_created) + def on_ice_candidate(mline, candidate): msg = json.dumps({'ice': {'sdpMLineIndex': str(mline), 'candidate' : candidate}}) self.signalling.send(msg) @@ -116,6 +184,7 @@ class WebRTCApplication(object): def error(msg): # errors are unexpected + l.error ('Unexpected error: ' + msg) GLib.idle_add(self.quit) GLib.idle_add(sys.exit, -20) self.signalling.error.connect(error) @@ -126,37 +195,50 @@ class WebRTCApplication(object): self.client = WebRTCClient() self._connect_client_observer() - self.actions = ActionObserver() - register_action_types(self.actions) - self._connect_actions() - self.signalling = WebRTCSignallingClient(self.server, self.id) + self.remote_client = RemoteWebRTCObserver (self.signalling) self._connect_signalling_observer() + + actions = ActionObserver() + actions.register_action_types() + self._connect_actions(actions) + + # wait for the signalling server to start up before creating the browser self.signalling.wait_for_states([SignallingState.OPEN]) self.signalling.hello() - self.browser = Browser(create_driver(self.browser_name), self.html_source) + self.browser = Browser(create_driver(self.browser_name)) + self.browser.open(self.html_source) browser_id = self.browser.get_peer_id () assert browser_id == self.peerid self.signalling.create_session(self.peerid) + test_name = self.test_name if self.test_name else self.scenario_name + self.remote_client.set_title (test_name) self._init_validate(self.scenario_name) - print("app initialized") def quit(self): # Stop signalling first so asyncio doesn't keep us alive on weird failures + l.info('quiting') self.signalling.stop() - self.browser.driver.quit() - self.client.stop() + l.info('signalling stopped') self.main_loop.quit() + l.info('main loop stopped') + self.client.stop() + l.info('client stopped') + self.browser.driver.quit() + l.info('browser exitted') def run(self): try: self._init() + l.info("app initialized") self.main_loop.run() + l.info("loop exited") except: + l.exception("Fatal error") self.quit() raise @@ -168,6 +250,7 @@ def parse_options(): parser.add_argument('--html-source', help='HTML page to open in the browser', default=None) parser.add_argument('--scenario', help='Scenario file to execute', default=None) parser.add_argument('--browser', help='Browser name to use', default=None) + parser.add_argument('--name', help='Name of the test', default=None) return parser.parse_args() def init(): @@ -196,7 +279,7 @@ def init(): def run(): args = init() - w = WebRTCApplication (args.server, args.id, args.peer_id, args.scenario, args.browser, args.html_source) + w = WebRTCApplication (args.server, args.id, args.peer_id, args.scenario, args.browser, args.html_source, test_name=args.name) return w.run() if __name__ == "__main__": From 3a86a37c03c9701061abe551ac1d9fb2e38dd610 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Fri, 1 May 2020 18:52:33 +1000 Subject: [PATCH 116/130] sendrecv: wait until the offer is set before creating answer Pragmatically, an answer cannot be created until the offer is created as the answer creation needs information from the offer. Practically, due to implementation details, the answer was always queued after the set of the offer and so the call flow did not matter. The current code also hid a bug in webrtcbin where ice candidates would be generated before the answer had been created which is against the JSEP specification. Change to the correct call flow for exemplary effect. --- webrtc/sendrecv/gst/webrtc-sendrecv.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/webrtc/sendrecv/gst/webrtc-sendrecv.c b/webrtc/sendrecv/gst/webrtc-sendrecv.c index f2fef123cc..956b92b95a 100644 --- a/webrtc/sendrecv/gst/webrtc-sendrecv.c +++ b/webrtc/sendrecv/gst/webrtc-sendrecv.c @@ -517,7 +517,15 @@ on_answer_created (GstPromise * promise, gpointer user_data) } static void -on_offer_received (GstSDPMessage * sdp) +on_offer_set (GstPromise * promise, gpointer user_data) +{ + gst_promise_unref (promise); + promise = gst_promise_new_with_change_func (on_answer_created, NULL, NULL); + g_signal_emit_by_name (webrtc1, "create-answer", NULL, promise); +} + +static void +on_offer_received (GstSDPMessage *sdp) { GstWebRTCSessionDescription *offer = NULL; GstPromise *promise; @@ -527,15 +535,11 @@ on_offer_received (GstSDPMessage * sdp) /* Set remote description on our pipeline */ { - promise = gst_promise_new (); - g_signal_emit_by_name (webrtc1, "set-remote-description", offer, promise); - gst_promise_interrupt (promise); - gst_promise_unref (promise); + promise = gst_promise_new_with_change_func (on_offer_set, NULL, NULL); + g_signal_emit_by_name (webrtc1, "set-remote-description", offer, + promise); } gst_webrtc_session_description_free (offer); - - promise = gst_promise_new_with_change_func (on_answer_created, NULL, NULL); - g_signal_emit_by_name (webrtc1, "create-answer", NULL, promise); } /* One mega message handler for our asynchronous calling mechanism */ From 7445fc492835a07fb719e6861f3b0502dce8aed6 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Fri, 1 May 2020 18:58:30 +1000 Subject: [PATCH 117/130] signalling/server: python 3.8 asyncio has it's own TimeoutError --- webrtc/signalling/simple_server.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/webrtc/signalling/simple_server.py b/webrtc/signalling/simple_server.py index ead3034a36..7da614bb05 100755 --- a/webrtc/signalling/simple_server.py +++ b/webrtc/signalling/simple_server.py @@ -15,8 +15,7 @@ import asyncio import websockets import argparse import http - -from concurrent.futures._base import TimeoutError +import concurrent class WebRTCSimpleServer(object): @@ -58,7 +57,7 @@ class WebRTCSimpleServer(object): while msg is None: try: msg = await asyncio.wait_for(ws.recv(), self.keepalive_timeout) - except TimeoutError: + except (asyncio.exceptions.TimeoutError, concurrent.futures._base.TimeoutError): print('Sending keepalive ping to {!r} in recv'.format(raddr)) await ws.ping() return msg From 8da83759861c018e9d6abd79b7a1eb6c55090231 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Thu, 19 Mar 2020 16:28:19 +1100 Subject: [PATCH 118/130] sendonly: Fix transceivers leak. Make sure to unref the transceivers array after use. --- webrtc/sendonly/webrtc-unidirectional-h264.c | 1 + 1 file changed, 1 insertion(+) diff --git a/webrtc/sendonly/webrtc-unidirectional-h264.c b/webrtc/sendonly/webrtc-unidirectional-h264.c index bb9ca8e44b..4887c7eed2 100644 --- a/webrtc/sendonly/webrtc-unidirectional-h264.c +++ b/webrtc/sendonly/webrtc-unidirectional-h264.c @@ -196,6 +196,7 @@ create_receiver_entry (SoupWebsocketConnection * connection) g_assert (transceivers != NULL && transceivers->len > 0); trans = g_array_index (transceivers, GstWebRTCRTPTransceiver *, 0); trans->direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY; + g_array_unref (transceivers); g_signal_connect (receiver_entry->webrtcbin, "on-negotiation-needed", G_CALLBACK (on_negotiation_needed_cb), (gpointer) receiver_entry); From 255fef3896221170c29c9820b869b9e7b24175bd Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Sat, 9 May 2020 19:09:26 +1000 Subject: [PATCH 119/130] webrtc-recvonly-h264: Add a recvonly standalone example. This example sets up a recvonly H.264 transceiver and receives H.264 from a peer, while sending bi-directional Opus audio. --- webrtc/sendonly/Makefile | 5 + webrtc/sendonly/webrtc-recvonly-h264.c | 702 +++++++++++++++++++++++++ 2 files changed, 707 insertions(+) create mode 100644 webrtc/sendonly/webrtc-recvonly-h264.c diff --git a/webrtc/sendonly/Makefile b/webrtc/sendonly/Makefile index ad588062d8..e153bc0379 100644 --- a/webrtc/sendonly/Makefile +++ b/webrtc/sendonly/Makefile @@ -2,5 +2,10 @@ CC := gcc LIBS := $(shell pkg-config --libs --cflags gstreamer-webrtc-1.0 gstreamer-sdp-1.0 libsoup-2.4 json-glib-1.0) CFLAGS := -O0 -ggdb -Wall -fno-omit-frame-pointer +all: webrtc-unidirectional-h264 webrtc-recvonly-h264 + webrtc-unidirectional-h264: webrtc-unidirectional-h264.c "$(CC)" $(CFLAGS) $^ $(LIBS) -o $@ + +webrtc-recvonly-h264: webrtc-recvonly-h264.c + "$(CC)" $(CFLAGS) $^ $(LIBS) -o $@ diff --git a/webrtc/sendonly/webrtc-recvonly-h264.c b/webrtc/sendonly/webrtc-recvonly-h264.c new file mode 100644 index 0000000000..9ba7fe4926 --- /dev/null +++ b/webrtc/sendonly/webrtc-recvonly-h264.c @@ -0,0 +1,702 @@ +#include +#include +#include +#include +#include + +#define GST_USE_UNSTABLE_API +#include + +#include +#include +#include + +/* This example is a standalone app which serves a web page + * and configures webrtcbin to receive an H.264 video feed, and to + * send+recv an Opus audio stream */ + +#define RTP_PAYLOAD_TYPE "96" +#define RTP_CAPS_OPUS "application/x-rtp,media=audio,encoding-name=OPUS,payload=" + +#define SOUP_HTTP_PORT 57778 +#define STUN_SERVER "stun.l.google.com:19302" + + + +typedef struct _ReceiverEntry ReceiverEntry; + +ReceiverEntry *create_receiver_entry (SoupWebsocketConnection * connection); +void destroy_receiver_entry (gpointer receiver_entry_ptr); + +GstPadProbeReturn payloader_caps_event_probe_cb (GstPad * pad, + GstPadProbeInfo * info, gpointer user_data); + +void on_offer_created_cb (GstPromise * promise, gpointer user_data); +void on_negotiation_needed_cb (GstElement * webrtcbin, gpointer user_data); +void on_ice_candidate_cb (GstElement * webrtcbin, guint mline_index, + gchar * candidate, gpointer user_data); + +void soup_websocket_message_cb (SoupWebsocketConnection * connection, + SoupWebsocketDataType data_type, GBytes * message, gpointer user_data); +void soup_websocket_closed_cb (SoupWebsocketConnection * connection, + gpointer user_data); + +void soup_http_handler (SoupServer * soup_server, SoupMessage * message, + const char *path, GHashTable * query, SoupClientContext * client_context, + gpointer user_data); +void soup_websocket_handler (G_GNUC_UNUSED SoupServer * server, + SoupWebsocketConnection * connection, const char *path, + SoupClientContext * client_context, gpointer user_data); + +static gchar *get_string_from_json_object (JsonObject * object); + +gboolean exit_sighandler (gpointer user_data); + + + + +struct _ReceiverEntry +{ + SoupWebsocketConnection *connection; + + GstElement *pipeline; + GstElement *webrtcbin; +}; + + + +const gchar *html_source = " \n \ + \n \ + \n \ + \n \ + \n \ + \n \ + \n \ + \n \ +
\n \ + \n \ +
\n \ + \n \ + \n \ +"; + +static void +handle_media_stream (GstPad * pad, GstElement * pipe, const char * convert_name, + const char * sink_name) +{ + GstPad *qpad; + GstElement *q, *conv, *resample, *sink; + GstPadLinkReturn ret; + + g_print ("Trying to handle stream with %s ! %s", convert_name, sink_name); + + q = gst_element_factory_make ("queue", NULL); + g_assert_nonnull (q); + conv = gst_element_factory_make (convert_name, NULL); + g_assert_nonnull (conv); + sink = gst_element_factory_make (sink_name, NULL); + g_assert_nonnull (sink); + + if (g_strcmp0 (convert_name, "audioconvert") == 0) { + /* Might also need to resample, so add it just in case. + * Will be a no-op if it's not required. */ + resample = gst_element_factory_make ("audioresample", NULL); + g_assert_nonnull (resample); + gst_bin_add_many (GST_BIN (pipe), q, conv, resample, sink, NULL); + gst_element_sync_state_with_parent (q); + gst_element_sync_state_with_parent (conv); + gst_element_sync_state_with_parent (resample); + gst_element_sync_state_with_parent (sink); + gst_element_link_many (q, conv, resample, sink, NULL); + } else { + gst_bin_add_many (GST_BIN (pipe), q, conv, sink, NULL); + gst_element_sync_state_with_parent (q); + gst_element_sync_state_with_parent (conv); + gst_element_sync_state_with_parent (sink); + gst_element_link_many (q, conv, sink, NULL); + } + + qpad = gst_element_get_static_pad (q, "sink"); + + ret = gst_pad_link (pad, qpad); + g_assert_cmphex (ret, ==, GST_PAD_LINK_OK); +} + +static void +on_incoming_decodebin_stream (GstElement * decodebin, GstPad * pad, + GstElement * pipe) +{ + GstCaps *caps; + const gchar *name; + + if (!gst_pad_has_current_caps (pad)) { + g_printerr ("Pad '%s' has no caps, can't do anything, ignoring\n", + GST_PAD_NAME (pad)); + return; + } + + caps = gst_pad_get_current_caps (pad); + name = gst_structure_get_name (gst_caps_get_structure (caps, 0)); + + if (g_str_has_prefix (name, "video")) { + handle_media_stream (pad, pipe, "videoconvert", "autovideosink"); + } else if (g_str_has_prefix (name, "audio")) { + handle_media_stream (pad, pipe, "audioconvert", "autoaudiosink"); + } else { + g_printerr ("Unknown pad %s, ignoring", GST_PAD_NAME (pad)); + } +} + +static void +on_incoming_stream (GstElement * webrtc, GstPad * pad, ReceiverEntry *receiver_entry) +{ + GstElement *decodebin; + GstPad *sinkpad; + + if (GST_PAD_DIRECTION (pad) != GST_PAD_SRC) + return; + + decodebin = gst_element_factory_make ("decodebin", NULL); + g_signal_connect (decodebin, "pad-added", + G_CALLBACK (on_incoming_decodebin_stream), receiver_entry->pipeline); + gst_bin_add (GST_BIN (receiver_entry->pipeline), decodebin); + gst_element_sync_state_with_parent (decodebin); + + sinkpad = gst_element_get_static_pad (decodebin, "sink"); + gst_pad_link (pad, sinkpad); + gst_object_unref (sinkpad); +} + + +ReceiverEntry * +create_receiver_entry (SoupWebsocketConnection * connection) +{ + GError *error; + ReceiverEntry *receiver_entry; + GstCaps *video_caps; + GstWebRTCRTPTransceiver *trans = NULL; + + receiver_entry = g_slice_alloc0 (sizeof (ReceiverEntry)); + receiver_entry->connection = connection; + + g_object_ref (G_OBJECT (connection)); + + g_signal_connect (G_OBJECT (connection), "message", + G_CALLBACK (soup_websocket_message_cb), (gpointer) receiver_entry); + + error = NULL; + receiver_entry->pipeline = gst_parse_launch ("webrtcbin name=webrtcbin stun-server=stun://" STUN_SERVER " " + "audiotestsrc is-live=true wave=red-noise ! audioconvert ! audioresample ! queue ! opusenc ! rtpopuspay ! " + "queue ! " RTP_CAPS_OPUS "97 ! webrtcbin. " + , &error); + if (error != NULL) { + g_error ("Could not create WebRTC pipeline: %s\n", error->message); + g_error_free (error); + goto cleanup; + } + + receiver_entry->webrtcbin = + gst_bin_get_by_name (GST_BIN (receiver_entry->pipeline), "webrtcbin"); + g_assert (receiver_entry->webrtcbin != NULL); + + /* Incoming streams will be exposed via this signal */ + g_signal_connect (receiver_entry->webrtcbin, "pad-added", G_CALLBACK (on_incoming_stream), + receiver_entry); + +#if 0 + GstElement *rtpbin = gst_bin_get_by_name (GST_BIN (receiver_entry->webrtcbin), "rtpbin"); + g_object_set (rtpbin, "latency", 40, NULL); + gst_object_unref (rtpbin); +#endif + + // Create a 2nd transceiver for the receive only video stream + video_caps = gst_caps_from_string ("application/x-rtp,media=video,encoding-name=H264,payload=" RTP_PAYLOAD_TYPE ",clock-rate=90000,packetization-mode=(string)1, profile-level-id=(string)42c016"); + g_signal_emit_by_name (receiver_entry->webrtcbin, "add-transceiver", GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY, video_caps, NULL, &trans); + gst_caps_unref (video_caps); + gst_object_unref (trans); + + g_signal_connect (receiver_entry->webrtcbin, "on-negotiation-needed", + G_CALLBACK (on_negotiation_needed_cb), (gpointer) receiver_entry); + + g_signal_connect (receiver_entry->webrtcbin, "on-ice-candidate", + G_CALLBACK (on_ice_candidate_cb), (gpointer) receiver_entry); + + gst_element_set_state (receiver_entry->pipeline, GST_STATE_PLAYING); + + return receiver_entry; + +cleanup: + destroy_receiver_entry ((gpointer) receiver_entry); + return NULL; +} + +void +destroy_receiver_entry (gpointer receiver_entry_ptr) +{ + ReceiverEntry *receiver_entry = (ReceiverEntry *) receiver_entry_ptr; + + g_assert (receiver_entry != NULL); + + if (receiver_entry->pipeline != NULL) { + gst_element_set_state (GST_ELEMENT (receiver_entry->pipeline), + GST_STATE_NULL); + + gst_object_unref (GST_OBJECT (receiver_entry->webrtcbin)); + gst_object_unref (GST_OBJECT (receiver_entry->pipeline)); + } + + if (receiver_entry->connection != NULL) + g_object_unref (G_OBJECT (receiver_entry->connection)); + + g_slice_free1 (sizeof (ReceiverEntry), receiver_entry); +} + + +void +on_offer_created_cb (GstPromise * promise, gpointer user_data) +{ + gchar *sdp_string; + gchar *json_string; + JsonObject *sdp_json; + JsonObject *sdp_data_json; + GstStructure const *reply; + GstPromise *local_desc_promise; + GstWebRTCSessionDescription *offer = NULL; + ReceiverEntry *receiver_entry = (ReceiverEntry *) user_data; + + reply = gst_promise_get_reply (promise); + gst_structure_get (reply, "offer", GST_TYPE_WEBRTC_SESSION_DESCRIPTION, + &offer, NULL); + gst_promise_unref (promise); + + local_desc_promise = gst_promise_new (); + g_signal_emit_by_name (receiver_entry->webrtcbin, "set-local-description", + offer, local_desc_promise); + gst_promise_interrupt (local_desc_promise); + gst_promise_unref (local_desc_promise); + + sdp_string = gst_sdp_message_as_text (offer->sdp); + g_print ("Negotiation offer created:\n%s\n", sdp_string); + + sdp_json = json_object_new (); + json_object_set_string_member (sdp_json, "type", "sdp"); + + sdp_data_json = json_object_new (); + json_object_set_string_member (sdp_data_json, "type", "offer"); + json_object_set_string_member (sdp_data_json, "sdp", sdp_string); + json_object_set_object_member (sdp_json, "data", sdp_data_json); + + json_string = get_string_from_json_object (sdp_json); + json_object_unref (sdp_json); + + soup_websocket_connection_send_text (receiver_entry->connection, json_string); + g_free (json_string); + g_free (sdp_string); + + gst_webrtc_session_description_free (offer); +} + + +void +on_negotiation_needed_cb (GstElement * webrtcbin, gpointer user_data) +{ + GstPromise *promise; + ReceiverEntry *receiver_entry = (ReceiverEntry *) user_data; + + g_print ("Creating negotiation offer\n"); + + promise = gst_promise_new_with_change_func (on_offer_created_cb, + (gpointer) receiver_entry, NULL); + g_signal_emit_by_name (G_OBJECT (webrtcbin), "create-offer", NULL, promise); +} + + +void +on_ice_candidate_cb (G_GNUC_UNUSED GstElement * webrtcbin, guint mline_index, + gchar * candidate, gpointer user_data) +{ + JsonObject *ice_json; + JsonObject *ice_data_json; + gchar *json_string; + ReceiverEntry *receiver_entry = (ReceiverEntry *) user_data; + + ice_json = json_object_new (); + json_object_set_string_member (ice_json, "type", "ice"); + + ice_data_json = json_object_new (); + json_object_set_int_member (ice_data_json, "sdpMLineIndex", mline_index); + json_object_set_string_member (ice_data_json, "candidate", candidate); + json_object_set_object_member (ice_json, "data", ice_data_json); + + json_string = get_string_from_json_object (ice_json); + json_object_unref (ice_json); + + soup_websocket_connection_send_text (receiver_entry->connection, json_string); + g_free (json_string); +} + + +void +soup_websocket_message_cb (G_GNUC_UNUSED SoupWebsocketConnection * connection, + SoupWebsocketDataType data_type, GBytes * message, gpointer user_data) +{ + gsize size; + gchar *data; + gchar *data_string; + const gchar *type_string; + JsonNode *root_json; + JsonObject *root_json_object; + JsonObject *data_json_object; + JsonParser *json_parser = NULL; + ReceiverEntry *receiver_entry = (ReceiverEntry *) user_data; + + switch (data_type) { + case SOUP_WEBSOCKET_DATA_BINARY: + g_error ("Received unknown binary message, ignoring\n"); + g_bytes_unref (message); + return; + + case SOUP_WEBSOCKET_DATA_TEXT: + data = g_bytes_unref_to_data (message, &size); + /* Convert to NULL-terminated string */ + data_string = g_strndup (data, size); + g_free (data); + break; + + default: + g_assert_not_reached (); + } + + json_parser = json_parser_new (); + if (!json_parser_load_from_data (json_parser, data_string, -1, NULL)) + goto unknown_message; + + root_json = json_parser_get_root (json_parser); + if (!JSON_NODE_HOLDS_OBJECT (root_json)) + goto unknown_message; + + root_json_object = json_node_get_object (root_json); + + if (!json_object_has_member (root_json_object, "type")) { + g_error ("Received message without type field\n"); + goto cleanup; + } + type_string = json_object_get_string_member (root_json_object, "type"); + + if (!json_object_has_member (root_json_object, "data")) { + g_error ("Received message without data field\n"); + goto cleanup; + } + data_json_object = json_object_get_object_member (root_json_object, "data"); + + if (g_strcmp0 (type_string, "sdp") == 0) { + const gchar *sdp_type_string; + const gchar *sdp_string; + GstPromise *promise; + GstSDPMessage *sdp; + GstWebRTCSessionDescription *answer; + int ret; + + if (!json_object_has_member (data_json_object, "type")) { + g_error ("Received SDP message without type field\n"); + goto cleanup; + } + sdp_type_string = json_object_get_string_member (data_json_object, "type"); + + if (g_strcmp0 (sdp_type_string, "answer") != 0) { + g_error ("Expected SDP message type \"answer\", got \"%s\"\n", + sdp_type_string); + goto cleanup; + } + + if (!json_object_has_member (data_json_object, "sdp")) { + g_error ("Received SDP message without SDP string\n"); + goto cleanup; + } + sdp_string = json_object_get_string_member (data_json_object, "sdp"); + + g_print ("Received SDP:\n%s\n", sdp_string); + + ret = gst_sdp_message_new (&sdp); + g_assert_cmphex (ret, ==, GST_SDP_OK); + + ret = + gst_sdp_message_parse_buffer ((guint8 *) sdp_string, + strlen (sdp_string), sdp); + if (ret != GST_SDP_OK) { + g_error ("Could not parse SDP string\n"); + goto cleanup; + } + + answer = gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_ANSWER, + sdp); + g_assert_nonnull (answer); + + promise = gst_promise_new (); + g_signal_emit_by_name (receiver_entry->webrtcbin, "set-remote-description", + answer, promise); + gst_promise_interrupt (promise); + gst_promise_unref (promise); + gst_webrtc_session_description_free (answer); + } else if (g_strcmp0 (type_string, "ice") == 0) { + guint mline_index; + const gchar *candidate_string; + + if (!json_object_has_member (data_json_object, "sdpMLineIndex")) { + g_error ("Received ICE message without mline index\n"); + goto cleanup; + } + mline_index = + json_object_get_int_member (data_json_object, "sdpMLineIndex"); + + if (!json_object_has_member (data_json_object, "candidate")) { + g_error ("Received ICE message without ICE candidate string\n"); + goto cleanup; + } + candidate_string = json_object_get_string_member (data_json_object, + "candidate"); + + g_print ("Received ICE candidate with mline index %u; candidate: %s\n", + mline_index, candidate_string); + + g_signal_emit_by_name (receiver_entry->webrtcbin, "add-ice-candidate", + mline_index, candidate_string); + } else + goto unknown_message; + +cleanup: + if (json_parser != NULL) + g_object_unref (G_OBJECT (json_parser)); + g_free (data_string); + return; + +unknown_message: + g_error ("Unknown message \"%s\", ignoring", data_string); + goto cleanup; +} + + +void +soup_websocket_closed_cb (SoupWebsocketConnection * connection, + gpointer user_data) +{ + GHashTable *receiver_entry_table = (GHashTable *) user_data; + g_hash_table_remove (receiver_entry_table, connection); + g_print ("Closed websocket connection %p\n", (gpointer) connection); +} + + +void +soup_http_handler (G_GNUC_UNUSED SoupServer * soup_server, + SoupMessage * message, const char *path, G_GNUC_UNUSED GHashTable * query, + G_GNUC_UNUSED SoupClientContext * client_context, + G_GNUC_UNUSED gpointer user_data) +{ + SoupBuffer *soup_buffer; + + if ((g_strcmp0 (path, "/") != 0) && (g_strcmp0 (path, "/index.html") != 0)) { + soup_message_set_status (message, SOUP_STATUS_NOT_FOUND); + return; + } + + soup_buffer = + soup_buffer_new (SOUP_MEMORY_STATIC, html_source, strlen (html_source)); + + soup_message_headers_set_content_type (message->response_headers, "text/html", + NULL); + soup_message_body_append_buffer (message->response_body, soup_buffer); + soup_buffer_free (soup_buffer); + + soup_message_set_status (message, SOUP_STATUS_OK); +} + + +void +soup_websocket_handler (G_GNUC_UNUSED SoupServer * server, + SoupWebsocketConnection * connection, G_GNUC_UNUSED const char *path, + G_GNUC_UNUSED SoupClientContext * client_context, gpointer user_data) +{ + ReceiverEntry *receiver_entry; + GHashTable *receiver_entry_table = (GHashTable *) user_data; + + g_print ("Processing new websocket connection %p", (gpointer) connection); + + g_signal_connect (G_OBJECT (connection), "closed", + G_CALLBACK (soup_websocket_closed_cb), (gpointer) receiver_entry_table); + + receiver_entry = create_receiver_entry (connection); + g_hash_table_replace (receiver_entry_table, connection, receiver_entry); +} + + +static gchar * +get_string_from_json_object (JsonObject * object) +{ + JsonNode *root; + JsonGenerator *generator; + gchar *text; + + /* Make it the root node */ + root = json_node_init_object (json_node_alloc (), object); + generator = json_generator_new (); + json_generator_set_root (generator, root); + text = json_generator_to_data (generator, NULL); + + /* Release everything */ + g_object_unref (generator); + json_node_free (root); + return text; +} + + +gboolean +exit_sighandler (gpointer user_data) +{ + g_print ("Caught signal, stopping mainloop\n"); + GMainLoop *mainloop = (GMainLoop *) user_data; + g_main_loop_quit (mainloop); + return TRUE; +} + + +int +main (int argc, char *argv[]) +{ + GMainLoop *mainloop; + SoupServer *soup_server; + GHashTable *receiver_entry_table; + + setlocale (LC_ALL, ""); + gst_init (&argc, &argv); + + receiver_entry_table = + g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, + destroy_receiver_entry); + + mainloop = g_main_loop_new (NULL, FALSE); + g_assert (mainloop != NULL); + + g_unix_signal_add (SIGINT, exit_sighandler, mainloop); + g_unix_signal_add (SIGTERM, exit_sighandler, mainloop); + + soup_server = + soup_server_new (SOUP_SERVER_SERVER_HEADER, "webrtc-soup-server", NULL); + soup_server_add_handler (soup_server, "/", soup_http_handler, NULL, NULL); + soup_server_add_websocket_handler (soup_server, "/ws", NULL, NULL, + soup_websocket_handler, (gpointer) receiver_entry_table, NULL); + soup_server_listen_all (soup_server, SOUP_HTTP_PORT, + (SoupServerListenOptions) 0, NULL); + + g_print ("WebRTC page link: http://127.0.0.1:%d/\n", (gint) SOUP_HTTP_PORT); + + g_main_loop_run (mainloop); + + g_object_unref (G_OBJECT (soup_server)); + g_hash_table_destroy (receiver_entry_table); + g_main_loop_unref (mainloop); + + gst_deinit (); + + return 0; +} From c0f303eacfa172277fbaad242d4064416b8322a2 Mon Sep 17 00:00:00 2001 From: Philippe Normand Date: Thu, 14 May 2020 11:04:37 +0100 Subject: [PATCH 120/130] janus: Remove unused parameters and refactor --- webrtc/janus/janusvideoroom.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/webrtc/janus/janusvideoroom.py b/webrtc/janus/janusvideoroom.py index 4372a6361f..37a6824434 100644 --- a/webrtc/janus/janusvideoroom.py +++ b/webrtc/janus/janusvideoroom.py @@ -243,14 +243,12 @@ class JanusGateway: return raw class WebRTCClient: - def __init__(self, id_, peer_id, server, signaling): - self.id_ = id_ + def __init__(self, peer_id, server): self.conn = None self.pipe = None self.webrtc = None self.peer_id = peer_id - self.server = server or 'wss://127.0.0.1:8989' - self.signaling = signaling + self.signaling = JanusGateway(server) self.request = None self.offermsg = None @@ -378,7 +376,8 @@ class WebRTCClient: sdpmlineindex = ice['sdpMLineIndex'] self.webrtc.emit('add-ice-candidate', sdpmlineindex, candidate) - async def loop(self, signaling): + async def loop(self): + signaling = self.signaling await signaling.connect() await signaling.attach("janus.plugin.videoroom") @@ -420,6 +419,8 @@ class WebRTCClient: return 0 + async def close(self): + return await self.signaling.close() def check_plugins(): needed = ["opus", "vpx", "nice", "webrtc", "dtls", "srtp", "rtp", @@ -439,16 +440,14 @@ if __name__=='__main__': parser.add_argument('label', help='videoroom label') parser.add_argument('--server', help='Signalling server to connect to, eg "wss://127.0.0.1:8989"') args = parser.parse_args() - our_id = random.randrange(10, 10000) - signaling = JanusGateway(args.server) - c = WebRTCClient(our_id, args.label, args.server, signaling) + c = WebRTCClient(args.label, args.server) loop = asyncio.get_event_loop() try: loop.run_until_complete( - c.loop(signaling) + c.loop() ) except KeyboardInterrupt: pass finally: print("Interrupted, cleaning up") - loop.run_until_complete(signaling.close()) + loop.run_until_complete(c.close()) From 180e1ce24c7742b983f0f662ab2d0421988272e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 22 May 2020 22:45:35 +0300 Subject: [PATCH 121/130] Update dependencies of Rust demos --- .../multiparty-sendrecv/gst-rust/Cargo.lock | 712 +++++++++--------- .../multiparty-sendrecv/gst-rust/Cargo.toml | 2 +- webrtc/sendrecv/gst-rust/Cargo.lock | 394 +++++----- webrtc/sendrecv/gst-rust/Cargo.toml | 2 +- 4 files changed, 523 insertions(+), 587 deletions(-) diff --git a/webrtc/multiparty-sendrecv/gst-rust/Cargo.lock b/webrtc/multiparty-sendrecv/gst-rust/Cargo.lock index 7fce37ce1c..4d0e83c5f9 100644 --- a/webrtc/multiparty-sendrecv/gst-rust/Cargo.lock +++ b/webrtc/multiparty-sendrecv/gst-rust/Cargo.lock @@ -2,70 +2,67 @@ # It is not intended for manual editing. [[package]] name = "anyhow" -version = "1.0.26" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "async-native-tls" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "async-std 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "async-std 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", + "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "async-std" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "async-task 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "async-task 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-channel 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures-timer 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "kv-log-macro 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kv-log-macro 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-uds 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", + "once_cell 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project-lite 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "async-task" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "async-tungstenite" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "async-native-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "async-std 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "async-native-tls 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "async-std 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tungstenite 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tungstenite 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "autocfg" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "autocfg" version = "1.0.0" @@ -88,7 +85,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -107,7 +104,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "byteorder" -version = "1.3.2" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -115,17 +112,9 @@ name = "bytes" version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "c2-chacha" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "cc" -version = "1.0.50" +version = "1.0.54" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -135,7 +124,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "clap" -version = "2.33.0" +version = "2.33.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -145,54 +134,57 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "core-foundation-sys" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "crossbeam-channel" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-deque" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-epoch" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-utils" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -212,7 +204,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "fnv" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -244,67 +236,70 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "futures" -version = "0.3.1" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures-channel 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-executor 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-executor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "futures-channel" -version = "0.3.1" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "futures-core" -version = "0.3.1" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "futures-executor" -version = "0.3.1" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "futures-io" -version = "0.3.1" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "futures-macro" -version = "0.3.1" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "futures-sink" -version = "0.3.1" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "futures-task" -version = "0.3.1" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "once_cell 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "futures-timer" @@ -313,19 +308,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "futures-util" -version = "0.3.1" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures-channel 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-macro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-macro 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-nested 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -334,7 +330,7 @@ name = "generic-array" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -343,25 +339,25 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "glib" -version = "0.9.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-channel 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-executor 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-executor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -369,7 +365,7 @@ name = "glib-sys" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -379,40 +375,40 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gstreamer" -version = "0.15.2" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-channel 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "glib 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "muldiv 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-rational 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-rational 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "paste 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gstreamer-sdp" -version = "0.15.0" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "glib 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gstreamer 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer-sdp-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -425,7 +421,7 @@ dependencies = [ "glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -436,23 +432,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gstreamer-webrtc" -version = "0.15.0" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "glib 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer-sdp 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gstreamer 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)", + "gstreamer-sdp 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer-webrtc-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -464,7 +460,7 @@ dependencies = [ "gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer-sdp-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -478,19 +474,19 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.6" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "http" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -522,7 +518,7 @@ name = "iovec" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -541,7 +537,7 @@ dependencies = [ [[package]] name = "kv-log-macro" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -554,7 +550,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.66" +version = "0.2.70" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -570,22 +566,27 @@ name = "matches" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "memchr" -version = "2.3.0" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "memoffset" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "mio" -version = "0.6.21" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -593,22 +594,22 @@ dependencies = [ "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "mio-uds" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -617,7 +618,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -629,28 +630,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "native-tls" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl 0.10.27 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.29 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.54 (registry+https://github.com/rust-lang/crates.io-index)", - "schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.56 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "net2" -version = "0.2.33" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -665,7 +666,7 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -683,16 +684,16 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "once_cell" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -702,15 +703,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl" -version = "0.10.27" +version = "0.10.29" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.54 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.56 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -720,34 +721,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl-sys" -version = "0.9.54" +version = "0.9.56" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.54 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "paste" -version = "0.1.6" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "paste-impl 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "paste-impl" -version = "0.1.6" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -757,30 +758,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "pin-project" -version = "0.4.8" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "pin-project-internal 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project-internal 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pin-project-internal" -version = "0.4.8" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pin-project-lite" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "pin-utils" -version = "0.1.0-alpha.4" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -790,51 +791,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "ppv-lite86" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "proc-macro-error" -version = "0.4.8" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro-error-attr 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustversion 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-error-attr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "proc-macro-error-attr" -version = "0.4.8" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustversion 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", "syn-mid 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "proc-macro-hack" -version = "0.5.11" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "proc-macro-nested" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "proc-macro2" -version = "1.0.8" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -842,10 +838,10 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.2" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -854,18 +850,18 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand_chacha" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ppv-lite86 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -898,32 +894,14 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rustversion" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "ryu" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "schannel" -version = "0.1.16" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -932,64 +910,53 @@ dependencies = [ [[package]] name = "scopeguard" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "security-framework" -version = "0.3.4" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "security-framework-sys" -version = "0.3.3" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "serde" -version = "1.0.104" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde_derive" -version = "1.0.104" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "1.0.45" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1010,38 +977,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "smallvec" -version = "1.2.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "structopt" -version = "0.3.9" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt-derive 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt-derive 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "structopt-derive" -version = "0.4.2" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-error 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-error 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "syn" -version = "1.0.14" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1050,9 +1017,9 @@ name = "syn-mid" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1061,7 +1028,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1078,31 +1045,31 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.10" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "thiserror-impl 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror-impl 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "thiserror-impl" -version = "1.0.10" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tungstenite" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "input_buffer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1114,7 +1081,7 @@ dependencies = [ [[package]] name = "typenum" -version = "1.11.2" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1130,7 +1097,7 @@ name = "unicode-normalization" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1168,6 +1135,11 @@ name = "vcpkg" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "version_check" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -1177,18 +1149,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "webrtc-app" version = "0.1.0" dependencies = [ - "anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "async-std 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "async-tungstenite 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer-sdp 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer-webrtc 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", + "anyhow 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "async-std 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "async-tungstenite 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "gstreamer 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)", + "gstreamer-sdp 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)", + "gstreamer-webrtc 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1230,135 +1202,130 @@ dependencies = [ ] [metadata] -"checksum anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" -"checksum async-native-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d40a615e861c981117e15c28c577daf9918cabd2e2d588a5e06811ae79c9da1a" -"checksum async-std 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0bf6039b315300e057d198b9d3ab92ee029e31c759b7f1afae538145e6f18a3e" -"checksum async-task 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f20c6fda19d0fc02406779587ca4f9a4171cd32e4a5bda0bd016f0a1334c8d4a" -"checksum async-tungstenite 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1f1a7fca4f1ccd1fe926bec21a4ba6259d3d91f04d67a9dd76bd549366be5bef" -"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +"checksum anyhow 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" +"checksum async-native-tls 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9e9e7a929bd34c68a82d58a4de7f86fffdaf97fb2af850162a7bb19dd7269b33" +"checksum async-std 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "538ecb01eb64eecd772087e5b6f7540cbc917f047727339a472dafed2185b267" +"checksum async-task 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0ac2c016b079e771204030951c366db398864f5026f84a44dafb0ff20f02085d" +"checksum async-tungstenite 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "182617e5bbfe7001b6f2317883506f239c77313171620a04cc11292704d3e171" "checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" "checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" "checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" "checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" -"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" +"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" "checksum bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" -"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" -"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" +"checksum cc 1.0.54 (registry+https://github.com/rust-lang/crates.io-index)" = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" -"checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" -"checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" -"checksum crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "acec9a3b0b3559f15aee4f90746c4e5e293b701c0f7d3925d24e01645267b68c" -"checksum crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca" -"checksum crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac" -"checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" +"checksum clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" +"checksum core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" +"checksum core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" +"checksum crossbeam-channel 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061" +"checksum crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" +"checksum crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" +"checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" -"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +"checksum fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b6f16056ecbb57525ff698bb955162d0cd03bee84e6241c27ff75c08d8ca5987" -"checksum futures-channel 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fcae98ca17d102fd8a3603727b9259fcf7fa4239b603d2142926189bc8999b86" -"checksum futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "79564c427afefab1dfb3298535b21eda083ef7935b4f0ecbfcb121f0aec10866" -"checksum futures-executor 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1e274736563f686a837a0568b478bdabfeaec2dca794b5649b04e2fe1627c231" -"checksum futures-io 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e676577d229e70952ab25f3945795ba5b16d63ca794ca9d2c860e5595d20b5ff" -"checksum futures-macro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "52e7c56c15537adb4f76d0b7a76ad131cb4d2f4f32d3b0bcabcbe1c7c5e87764" -"checksum futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "171be33efae63c2d59e6dbba34186fe0d6394fb378069a76dfd80fdcffd43c16" -"checksum futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0bae52d6b29cf440e298856fec3965ee6fa71b06aa7495178615953fd669e5f9" +"checksum futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613" +"checksum futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" +"checksum futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" +"checksum futures-executor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314" +"checksum futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" +"checksum futures-macro 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" +"checksum futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" +"checksum futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" "checksum futures-timer 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a1de7508b218029b0f01662ed8f61b1c964b3ae99d6f25462d0f55a595109df6" -"checksum futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c0d66274fb76985d3c62c886d1da7ac4c0903a8c9f754e8fe0f35a6a6cc39e76" +"checksum futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" "checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" -"checksum glib 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27bafffe3fc615449d5a87705f93f6fe4fcf749662b9d08cc9d5451f6c1b0f21" +"checksum glib 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "40fb573a09841b6386ddf15fd4bc6655b4f5b106ca962f57ecaecde32a0061c0" "checksum glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "95856f3802f446c05feffa5e24859fe6a183a7cb849c8449afc35c86b1e316e2" "checksum gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31d1a804f62034eccf370006ccaef3708a71c31d561fee88564abe71177553d9" -"checksum gstreamer 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f439859b5f3310518adc0aea572ac89ddd94f8ba3af4626d6156733671599629" -"checksum gstreamer-sdp 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "674df58b85cb077a357c581c29796fbeb5aa36e8362269807a11f938e5c7b973" +"checksum gstreamer 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)" = "5918f609a4309c98347fc58b7259eac99fe5c69adcdd23cf617d967d7d4da1d1" +"checksum gstreamer-sdp 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1b9f2fcf072e733c9ecd29ce40df432148304995827b481a16e3a2adf57a6dfa" "checksum gstreamer-sdp-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "99e88ac4f9f20323ef3409dddcea3bbf58364ff8eea10b14da5303bfcb23347a" "checksum gstreamer-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1d18da01b97d0ab5896acd5151e4c155acefd0e6c03c3dd24dd133ba054053db" -"checksum gstreamer-webrtc 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e64235af90676896b01f3cbe50808fefaf498057ca288ce4a31a89de81464e04" +"checksum gstreamer-webrtc 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1f433d1294266fb1d65e1dc2d4de365f7f4caf23cb72db3a3bd6904eeec88cf1" "checksum gstreamer-webrtc-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f392bd821b42efecfc21016c8ef20da188b45a45bbb5ddf81758704f93aae615" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" -"checksum hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eff2656d88f158ce120947499e971d743c05dbcbed62e5bd2f38f1698bbc3772" -"checksum http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b708cc7f06493459026f53b9a61a7a121a5d1ec6238dee58ea4941132b30156b" +"checksum hermit-abi 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" +"checksum http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9" "checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" "checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" "checksum input_buffer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "19a8a95243d5a0398cae618ec29477c6e3cb631152be5c19481f80bc71559754" "checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" "checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum kv-log-macro 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c54d9f465d530a752e6ebdc217e081a7a614b48cb200f6f0aee21ba6bc9aabb" +"checksum kv-log-macro 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2a2d3beed37e5483887d81eb39de6de03a8346531410e1306ca48a9a89bd3a51" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +"checksum libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)" = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" -"checksum memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223" -"checksum memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9" -"checksum mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)" = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" -"checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" +"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" +"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +"checksum memoffset 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8" +"checksum mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)" = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" +"checksum mio-uds 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" "checksum muldiv 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0419348c027fa7be448d2ae7ea0e4e04c2334c31dc4e74ab29f00a2a7ca69204" -"checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e" -"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" +"checksum native-tls 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d" +"checksum net2 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)" = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" "checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" -"checksum num-rational 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "da4dc79f9e6c81bef96148c8f6b8e72ad4541caa4a24373e900a36da07de03a3" +"checksum num-rational 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" "checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" -"checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" -"checksum once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" +"checksum num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +"checksum once_cell 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -"checksum openssl 0.10.27 (registry+https://github.com/rust-lang/crates.io-index)" = "e176a45fedd4c990e26580847a525e39e16ec32ac78957dbf62ded31b3abfd6f" +"checksum openssl 0.10.29 (registry+https://github.com/rust-lang/crates.io-index)" = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" -"checksum openssl-sys 0.9.54 (registry+https://github.com/rust-lang/crates.io-index)" = "1024c0a59774200a555087a6da3f253a9095a5f344e353b212ac4c8b8e450986" -"checksum paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "423a519e1c6e828f1e73b720f9d9ed2fa643dce8a7737fb43235ce0b41eeaa49" -"checksum paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5" +"checksum openssl-sys 0.9.56 (registry+https://github.com/rust-lang/crates.io-index)" = "f02309a7f127000ed50594f0b50ecc69e7c654e16d41b4e8156d1b3df8e0b52e" +"checksum paste 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "0a229b1c58c692edcaa5b9b0948084f130f55d2dcc15b02fcc5340b2b4521476" +"checksum paste-impl 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "2e0bf239e447e67ff6d16a8bb5e4d4bd2343acf5066061c0e8e06ac5ba8ca68c" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" -"checksum pin-project 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7804a463a8d9572f13453c516a5faea534a2403d7ced2f0c7e100eeff072772c" -"checksum pin-project-internal 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "385322a45f2ecf3410c68d2a549a4a2685e8051d0f278e39743ff4e451cb9b3f" -"checksum pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" -"checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" +"checksum pin-project 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)" = "edc93aeee735e60ecb40cf740eb319ff23eab1c5748abfdb5c180e4ce49f7791" +"checksum pin-project-internal 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)" = "e58db2081ba5b4c93bd6be09c40fd36cb9193a8336c384f3b40012e531aa7e40" +"checksum pin-project-lite 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f7505eeebd78492e0f6108f7171c4948dbb120ee8119d9d77d0afa5469bef67f" +"checksum pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" "checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" -"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" -"checksum proc-macro-error 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "875077759af22fa20b610ad4471d8155b321c89c3f2785526c9839b099be4e0a" -"checksum proc-macro-error-attr 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c5717d9fa2664351a01ed73ba5ef6df09c01a521cb42cb65a061432a826f3c7a" -"checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" -"checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" -"checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" -"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum ppv-lite86 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" +"checksum proc-macro-error 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98e9e4b82e0ef281812565ea4751049f1bdcdfccda7d3f459f2e138a40c08678" +"checksum proc-macro-error-attr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4f5444ead4e9935abd7f27dc51f7e852a0569ac888096d5ec2499470794e2e53" +"checksum proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)" = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63" +"checksum proc-macro-nested 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694" +"checksum proc-macro2 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "70a50b9351bfa8d65a7d93ce712dc63d2fd15ddbf2c36990fc7cac344859c04f" +"checksum quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" "checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" +"checksum rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" "checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" "checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" -"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum rustversion 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b3bba175698996010c4f6dce5e7f173b6eb781fce25d2cfc45e27091ce0b79f6" -"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" -"checksum schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "87f550b06b6cba9c8b8be3ee73f391990116bf527450d2556e9b9ce263b9a021" -"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" -"checksum security-framework 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8ef2429d7cefe5fd28bd1d2ed41c944547d4ff84776f5935b456da44593a16df" -"checksum security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e31493fc37615debb8c5090a7aeb4a9730bc61e77ab10b9af59f1a202284f895" -"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" -"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" -"checksum serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "eab8f15f15d6c41a154c1b128a22f2dfabe350ef53c40953d84e36155c91192b" +"checksum ryu 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" +"checksum schannel 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)" = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +"checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +"checksum security-framework 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535" +"checksum security-framework-sys 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "17bf11d99252f512695eb468de5516e5cf75455521e69dfe343f3b74e4748405" +"checksum serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)" = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c" +"checksum serde_derive 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)" = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984" +"checksum serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)" = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2" "checksum sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -"checksum smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" -"checksum structopt 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "a1bcbed7d48956fcbb5d80c6b95aedb553513de0a1b451ea92679d999c010e98" -"checksum structopt-derive 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "095064aa1f5b94d14e635d0a5684cf140c43ae40a0fd990708d38f5d669e5f64" -"checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" +"checksum smallvec 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" +"checksum structopt 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "863246aaf5ddd0d6928dfeb1a9ca65f505599e4e1b399935ef7e75107516b4ef" +"checksum structopt-derive 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "d239ca4b13aee7a2142e6795cbd69e457665ff8037aed33b3effdc430d2f927a" +"checksum syn 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)" = "95b5f192649e48a5302a13f2feb224df883b98933222369e4b3b0fe2a5447269" "checksum syn-mid 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -"checksum thiserror 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "205684fd018ca14432b12cce6ea3d46763311a571c3d294e71ba3f01adcf1aad" -"checksum thiserror-impl 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "57e4d2e50ca050ed44fb58309bdce3efa79948f84f9993ad1978de5eebdce5a7" -"checksum tungstenite 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08f33f14c64af9dfc6e50e6812261178d6bd434c0e501f87c7683b8aaa9eb8d3" -"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" +"checksum thiserror 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "b13f926965ad00595dd129fa12823b04bbf866e9085ab0a5f2b05b850fbfc344" +"checksum thiserror-impl 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "893582086c2f98cde18f906265a65b5030a074b1046c674ae898be6519a7f479" +"checksum tungstenite 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cfea31758bf674f990918962e8e5f07071a3161bd7c4138ed23e416e1ac4264e" +"checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" "checksum unicode-normalization 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" "checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" @@ -1367,6 +1334,7 @@ dependencies = [ "checksum url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" "checksum utf-8 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" "checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" +"checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" "checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" diff --git a/webrtc/multiparty-sendrecv/gst-rust/Cargo.toml b/webrtc/multiparty-sendrecv/gst-rust/Cargo.toml index 94e6533f6c..ed9812ed71 100644 --- a/webrtc/multiparty-sendrecv/gst-rust/Cargo.toml +++ b/webrtc/multiparty-sendrecv/gst-rust/Cargo.toml @@ -10,7 +10,7 @@ async-std = "1" structopt = { version = "0.3", default-features = false } anyhow = "1" rand = "0.7" -async-tungstenite = { version = "0.4", features = ["async-std-runtime", "async-native-tls"] } +async-tungstenite = { version = "0.5", features = ["async-std-runtime", "async-native-tls"] } gst = { package = "gstreamer", version = "0.15", features = ["v1_14"] } gst-webrtc = { package = "gstreamer-webrtc", version = "0.15" } gst-sdp = { package = "gstreamer-sdp", version = "0.15", features = ["v1_14"] } diff --git a/webrtc/sendrecv/gst-rust/Cargo.lock b/webrtc/sendrecv/gst-rust/Cargo.lock index cebc8e6043..cf2cbd7396 100644 --- a/webrtc/sendrecv/gst-rust/Cargo.lock +++ b/webrtc/sendrecv/gst-rust/Cargo.lock @@ -2,26 +2,27 @@ # It is not intended for manual editing. [[package]] name = "anyhow" -version = "1.0.26" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" +checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" [[package]] name = "async-native-tls" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40a615e861c981117e15c28c577daf9918cabd2e2d588a5e06811ae79c9da1a" +checksum = "9e9e7a929bd34c68a82d58a4de7f86fffdaf97fb2af850162a7bb19dd7269b33" dependencies = [ "async-std", "native-tls", "thiserror", + "url", ] [[package]] name = "async-std" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf6039b315300e057d198b9d3ab92ee029e31c759b7f1afae538145e6f18a3e" +checksum = "538ecb01eb64eecd772087e5b6f7540cbc917f047727339a472dafed2185b267" dependencies = [ "async-task", "crossbeam-channel", @@ -44,9 +45,9 @@ dependencies = [ [[package]] name = "async-task" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f20c6fda19d0fc02406779587ca4f9a4171cd32e4a5bda0bd016f0a1334c8d4a" +checksum = "0ac2c016b079e771204030951c366db398864f5026f84a44dafb0ff20f02085d" dependencies = [ "libc", "winapi 0.3.8", @@ -54,24 +55,19 @@ dependencies = [ [[package]] name = "async-tungstenite" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f1a7fca4f1ccd1fe926bec21a4ba6259d3d91f04d67a9dd76bd549366be5bef" +checksum = "182617e5bbfe7001b6f2317883506f239c77313171620a04cc11292704d3e171" dependencies = [ "async-native-tls", "async-std", - "futures", + "futures-io", + "futures-util", "log", "pin-project", "tungstenite", ] -[[package]] -name = "autocfg" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" - [[package]] name = "autocfg" version = "1.0.0" @@ -119,9 +115,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "byteorder" -version = "1.3.2" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "bytes" @@ -129,20 +125,11 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" -[[package]] -name = "c2-chacha" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" -dependencies = [ - "ppv-lite86", -] - [[package]] name = "cc" -version = "1.0.50" +version = "1.0.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" +checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311" [[package]] name = "cfg-if" @@ -152,9 +139,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "clap" -version = "2.33.0" +version = "2.33.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" dependencies = [ "bitflags", "textwrap", @@ -163,9 +150,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" +checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" dependencies = [ "core-foundation-sys", "libc", @@ -173,50 +160,53 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" +checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" [[package]] name = "crossbeam-channel" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acec9a3b0b3559f15aee4f90746c4e5e293b701c0f7d3925d24e01645267b68c" +checksum = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061" dependencies = [ "crossbeam-utils", + "maybe-uninit", ] [[package]] name = "crossbeam-deque" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca" +checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" dependencies = [ "crossbeam-epoch", "crossbeam-utils", + "maybe-uninit", ] [[package]] name = "crossbeam-epoch" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac" +checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" dependencies = [ - "autocfg 0.1.7", + "autocfg", "cfg-if", "crossbeam-utils", "lazy_static", + "maybe-uninit", "memoffset", "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ - "autocfg 0.1.7", + "autocfg", "cfg-if", "lazy_static", ] @@ -238,9 +228,9 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "fnv" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foreign-types" @@ -275,9 +265,9 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "futures" -version = "0.3.1" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6f16056ecbb57525ff698bb955162d0cd03bee84e6241c27ff75c08d8ca5987" +checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613" dependencies = [ "futures-channel", "futures-core", @@ -290,9 +280,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.1" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcae98ca17d102fd8a3603727b9259fcf7fa4239b603d2142926189bc8999b86" +checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" dependencies = [ "futures-core", "futures-sink", @@ -300,15 +290,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.1" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79564c427afefab1dfb3298535b21eda083ef7935b4f0ecbfcb121f0aec10866" +checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" [[package]] name = "futures-executor" -version = "0.3.1" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e274736563f686a837a0568b478bdabfeaec2dca794b5649b04e2fe1627c231" +checksum = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314" dependencies = [ "futures-core", "futures-task", @@ -317,15 +307,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.1" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e676577d229e70952ab25f3945795ba5b16d63ca794ca9d2c860e5595d20b5ff" +checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" [[package]] name = "futures-macro" -version = "0.3.1" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e7c56c15537adb4f76d0b7a76ad131cb4d2f4f32d3b0bcabcbe1c7c5e87764" +checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" dependencies = [ "proc-macro-hack", "proc-macro2", @@ -335,15 +325,18 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.1" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171be33efae63c2d59e6dbba34186fe0d6394fb378069a76dfd80fdcffd43c16" +checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" [[package]] name = "futures-task" -version = "0.3.1" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bae52d6b29cf440e298856fec3965ee6fa71b06aa7495178615953fd669e5f9" +checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" +dependencies = [ + "once_cell", +] [[package]] name = "futures-timer" @@ -353,9 +346,9 @@ checksum = "a1de7508b218029b0f01662ed8f61b1c964b3ae99d6f25462d0f55a595109df6" [[package]] name = "futures-util" -version = "0.3.1" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0d66274fb76985d3c62c886d1da7ac4c0903a8c9f754e8fe0f35a6a6cc39e76" +checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" dependencies = [ "futures-channel", "futures-core", @@ -364,6 +357,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", + "pin-project", "pin-utils", "proc-macro-hack", "proc-macro-nested", @@ -392,9 +386,9 @@ dependencies = [ [[package]] name = "glib" -version = "0.9.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27bafffe3fc615449d5a87705f93f6fe4fcf749662b9d08cc9d5451f6c1b0f21" +checksum = "40fb573a09841b6386ddf15fd4bc6655b4f5b106ca962f57ecaecde32a0061c0" dependencies = [ "bitflags", "futures-channel", @@ -431,9 +425,9 @@ dependencies = [ [[package]] name = "gstreamer" -version = "0.15.2" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f439859b5f3310518adc0aea572ac89ddd94f8ba3af4626d6156733671599629" +checksum = "5918f609a4309c98347fc58b7259eac99fe5c69adcdd23cf617d967d7d4da1d1" dependencies = [ "bitflags", "cfg-if", @@ -453,9 +447,9 @@ dependencies = [ [[package]] name = "gstreamer-sdp" -version = "0.15.0" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "674df58b85cb077a357c581c29796fbeb5aa36e8362269807a11f938e5c7b973" +checksum = "1b9f2fcf072e733c9ecd29ce40df432148304995827b481a16e3a2adf57a6dfa" dependencies = [ "glib", "glib-sys", @@ -492,9 +486,9 @@ dependencies = [ [[package]] name = "gstreamer-webrtc" -version = "0.15.0" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64235af90676896b01f3cbe50808fefaf498057ca288ce4a31a89de81464e04" +checksum = "1f433d1294266fb1d65e1dc2d4de365f7f4caf23cb72db3a3bd6904eeec88cf1" dependencies = [ "glib", "glib-sys", @@ -531,18 +525,18 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.6" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff2656d88f158ce120947499e971d743c05dbcbed62e5bd2f38f1698bbc3772" +checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" dependencies = [ "libc", ] [[package]] name = "http" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b708cc7f06493459026f53b9a61a7a121a5d1ec6238dee58ea4941132b30156b" +checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9" dependencies = [ "bytes", "fnv", @@ -602,9 +596,9 @@ dependencies = [ [[package]] name = "kv-log-macro" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c54d9f465d530a752e6ebdc217e081a7a614b48cb200f6f0aee21ba6bc9aabb" +checksum = "2a2d3beed37e5483887d81eb39de6de03a8346531410e1306ca48a9a89bd3a51" dependencies = [ "log", ] @@ -617,9 +611,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.66" +version = "0.2.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +checksum = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f" [[package]] name = "log" @@ -637,25 +631,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" [[package]] -name = "memchr" -version = "2.3.0" +name = "maybe-uninit" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" + +[[package]] +name = "memchr" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" [[package]] name = "memoffset" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9" +checksum = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8" dependencies = [ - "rustc_version", + "autocfg", ] [[package]] name = "mio" -version = "0.6.21" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" +checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" dependencies = [ "cfg-if", "fuchsia-zircon", @@ -672,9 +672,9 @@ dependencies = [ [[package]] name = "mio-uds" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" +checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" dependencies = [ "iovec", "libc", @@ -701,9 +701,9 @@ checksum = "0419348c027fa7be448d2ae7ea0e4e04c2334c31dc4e74ab29f00a2a7ca69204" [[package]] name = "native-tls" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e" +checksum = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d" dependencies = [ "lazy_static", "libc", @@ -719,9 +719,9 @@ dependencies = [ [[package]] name = "net2" -version = "0.2.33" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" +checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" dependencies = [ "cfg-if", "libc", @@ -734,17 +734,17 @@ version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" dependencies = [ - "autocfg 1.0.0", + "autocfg", "num-traits", ] [[package]] name = "num-rational" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da4dc79f9e6c81bef96148c8f6b8e72ad4541caa4a24373e900a36da07de03a3" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" dependencies = [ - "autocfg 1.0.0", + "autocfg", "num-integer", "num-traits", ] @@ -755,14 +755,14 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" dependencies = [ - "autocfg 1.0.0", + "autocfg", ] [[package]] name = "num_cpus" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" dependencies = [ "hermit-abi", "libc", @@ -770,9 +770,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" +checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" [[package]] name = "opaque-debug" @@ -782,9 +782,9 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "openssl" -version = "0.10.27" +version = "0.10.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e176a45fedd4c990e26580847a525e39e16ec32ac78957dbf62ded31b3abfd6f" +checksum = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd" dependencies = [ "bitflags", "cfg-if", @@ -802,11 +802,11 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" [[package]] name = "openssl-sys" -version = "0.9.54" +version = "0.9.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1024c0a59774200a555087a6da3f253a9095a5f344e353b212ac4c8b8e450986" +checksum = "f02309a7f127000ed50594f0b50ecc69e7c654e16d41b4e8156d1b3df8e0b52e" dependencies = [ - "autocfg 1.0.0", + "autocfg", "cc", "libc", "pkg-config", @@ -815,9 +815,9 @@ dependencies = [ [[package]] name = "paste" -version = "0.1.6" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "423a519e1c6e828f1e73b720f9d9ed2fa643dce8a7737fb43235ce0b41eeaa49" +checksum = "0a229b1c58c692edcaa5b9b0948084f130f55d2dcc15b02fcc5340b2b4521476" dependencies = [ "paste-impl", "proc-macro-hack", @@ -825,9 +825,9 @@ dependencies = [ [[package]] name = "paste-impl" -version = "0.1.6" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5" +checksum = "2e0bf239e447e67ff6d16a8bb5e4d4bd2343acf5066061c0e8e06ac5ba8ca68c" dependencies = [ "proc-macro-hack", "proc-macro2", @@ -843,18 +843,18 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pin-project" -version = "0.4.8" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7804a463a8d9572f13453c516a5faea534a2403d7ced2f0c7e100eeff072772c" +checksum = "edc93aeee735e60ecb40cf740eb319ff23eab1c5748abfdb5c180e4ce49f7791" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "0.4.8" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385322a45f2ecf3410c68d2a549a4a2685e8051d0f278e39743ff4e451cb9b3f" +checksum = "e58db2081ba5b4c93bd6be09c40fd36cb9193a8336c384f3b40012e531aa7e40" dependencies = [ "proc-macro2", "quote", @@ -863,15 +863,15 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" +checksum = "f7505eeebd78492e0f6108f7171c4948dbb120ee8119d9d77d0afa5469bef67f" [[package]] name = "pin-utils" -version = "0.1.0-alpha.4" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" @@ -881,67 +881,62 @@ checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" [[package]] name = "ppv-lite86" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" +checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" [[package]] name = "proc-macro-error" -version = "0.4.8" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875077759af22fa20b610ad4471d8155b321c89c3f2785526c9839b099be4e0a" +checksum = "98e9e4b82e0ef281812565ea4751049f1bdcdfccda7d3f459f2e138a40c08678" dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "rustversion", "syn", + "version_check", ] [[package]] name = "proc-macro-error-attr" -version = "0.4.8" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5717d9fa2664351a01ed73ba5ef6df09c01a521cb42cb65a061432a826f3c7a" +checksum = "4f5444ead4e9935abd7f27dc51f7e852a0569ac888096d5ec2499470794e2e53" dependencies = [ "proc-macro2", "quote", - "rustversion", "syn", "syn-mid", + "version_check", ] [[package]] name = "proc-macro-hack" -version = "0.5.11" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63" [[package]] name = "proc-macro-nested" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" +checksum = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694" [[package]] name = "proc-macro2" -version = "1.0.8" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" +checksum = "70a50b9351bfa8d65a7d93ce712dc63d2fd15ddbf2c36990fc7cac344859c04f" dependencies = [ "unicode-xid", ] [[package]] name = "quote" -version = "1.0.2" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" dependencies = [ "proc-macro2", ] @@ -961,11 +956,11 @@ dependencies = [ [[package]] name = "rand_chacha" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ - "c2-chacha", + "ppv-lite86", "rand_core", ] @@ -1002,37 +997,17 @@ dependencies = [ "winapi 0.3.8", ] -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver", -] - -[[package]] -name = "rustversion" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3bba175698996010c4f6dce5e7f173b6eb781fce25d2cfc45e27091ce0b79f6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "ryu" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" +checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" [[package]] name = "schannel" -version = "0.1.16" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f550b06b6cba9c8b8be3ee73f391990116bf527450d2556e9b9ce263b9a021" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" dependencies = [ "lazy_static", "winapi 0.3.8", @@ -1040,16 +1015,17 @@ dependencies = [ [[package]] name = "scopeguard" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "security-framework" -version = "0.3.4" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ef2429d7cefe5fd28bd1d2ed41c944547d4ff84776f5935b456da44593a16df" +checksum = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535" dependencies = [ + "bitflags", "core-foundation", "core-foundation-sys", "libc", @@ -1058,39 +1034,25 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "0.3.3" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31493fc37615debb8c5090a7aeb4a9730bc61e77ab10b9af59f1a202284f895" +checksum = "17bf11d99252f512695eb468de5516e5cf75455521e69dfe343f3b74e4748405" dependencies = [ "core-foundation-sys", + "libc", ] -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "serde" -version = "1.0.104" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" +checksum = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c" [[package]] name = "serde_derive" -version = "1.0.104" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" +checksum = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984" dependencies = [ "proc-macro2", "quote", @@ -1099,9 +1061,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.45" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab8f15f15d6c41a154c1b128a22f2dfabe350ef53c40953d84e36155c91192b" +checksum = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2" dependencies = [ "itoa", "ryu", @@ -1128,15 +1090,15 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] name = "smallvec" -version = "1.2.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" +checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" [[package]] name = "structopt" -version = "0.3.9" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1bcbed7d48956fcbb5d80c6b95aedb553513de0a1b451ea92679d999c010e98" +checksum = "863246aaf5ddd0d6928dfeb1a9ca65f505599e4e1b399935ef7e75107516b4ef" dependencies = [ "clap", "lazy_static", @@ -1145,9 +1107,9 @@ dependencies = [ [[package]] name = "structopt-derive" -version = "0.4.2" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "095064aa1f5b94d14e635d0a5684cf140c43ae40a0fd990708d38f5d669e5f64" +checksum = "d239ca4b13aee7a2142e6795cbd69e457665ff8037aed33b3effdc430d2f927a" dependencies = [ "heck", "proc-macro-error", @@ -1158,9 +1120,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.14" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" +checksum = "95b5f192649e48a5302a13f2feb224df883b98933222369e4b3b0fe2a5447269" dependencies = [ "proc-macro2", "quote", @@ -1203,18 +1165,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.10" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "205684fd018ca14432b12cce6ea3d46763311a571c3d294e71ba3f01adcf1aad" +checksum = "b13f926965ad00595dd129fa12823b04bbf866e9085ab0a5f2b05b850fbfc344" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.10" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57e4d2e50ca050ed44fb58309bdce3efa79948f84f9993ad1978de5eebdce5a7" +checksum = "893582086c2f98cde18f906265a65b5030a074b1046c674ae898be6519a7f479" dependencies = [ "proc-macro2", "quote", @@ -1223,9 +1185,9 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f33f14c64af9dfc6e50e6812261178d6bd434c0e501f87c7683b8aaa9eb8d3" +checksum = "cfea31758bf674f990918962e8e5f07071a3161bd7c4138ed23e416e1ac4264e" dependencies = [ "base64", "byteorder", @@ -1242,9 +1204,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.11.2" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" [[package]] name = "unicode-bidi" @@ -1305,6 +1267,12 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" +[[package]] +name = "version_check" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" diff --git a/webrtc/sendrecv/gst-rust/Cargo.toml b/webrtc/sendrecv/gst-rust/Cargo.toml index 94e6533f6c..ed9812ed71 100644 --- a/webrtc/sendrecv/gst-rust/Cargo.toml +++ b/webrtc/sendrecv/gst-rust/Cargo.toml @@ -10,7 +10,7 @@ async-std = "1" structopt = { version = "0.3", default-features = false } anyhow = "1" rand = "0.7" -async-tungstenite = { version = "0.4", features = ["async-std-runtime", "async-native-tls"] } +async-tungstenite = { version = "0.5", features = ["async-std-runtime", "async-native-tls"] } gst = { package = "gstreamer", version = "0.15", features = ["v1_14"] } gst-webrtc = { package = "gstreamer-webrtc", version = "0.15" } gst-sdp = { package = "gstreamer-sdp", version = "0.15", features = ["v1_14"] } From 78df1ca74ccee09a83b9e30eb307b7f93056e469 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Mon, 25 May 2020 18:28:29 +0000 Subject: [PATCH 122/130] simple_server: Correctly pass health option It was completely ignored. Also don't de-serialize options. Just parse them directly in `__init__`. Less error-prone. --- webrtc/signalling/simple_server.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/webrtc/signalling/simple_server.py b/webrtc/signalling/simple_server.py index 7da614bb05..7f9123e116 100755 --- a/webrtc/signalling/simple_server.py +++ b/webrtc/signalling/simple_server.py @@ -17,9 +17,10 @@ import argparse import http import concurrent + class WebRTCSimpleServer(object): - def __init__(self, addr, port, keepalive_timeout, disable_ssl, certpath, health_path=None): + def __init__(self, options): ############### Global data ############### # Format: {uid: (Peer WebSocketServerProtocol, @@ -34,17 +35,18 @@ class WebRTCSimpleServer(object): # Room dict with a set of peers in each room self.rooms = dict() - self.keepalive_timeout = keepalive_timeout - self.addr = addr - self.port = port - self.disable_ssl = disable_ssl - self.certpath = certpath - self.health_path = health_path + # Options + self.addr = options.addr + self.port = options.port + self.keepalive_timeout = options.keepalive_timeout + self.cert_path = options.cert_path + self.disable_ssl = options.disable_ssl + self.health_path = options.health ############### Helper functions ############### async def health_check(self, path, request_headers): - if path == self.health_part: + if path == self.health_path: return http.HTTPStatus.OK, [], b"OK\n" return None @@ -280,7 +282,7 @@ def main(): loop = asyncio.get_event_loop() - r = WebRTCSimpleServer(options.addr, options.port, options.keepalive_timeout, options.disable_ssl, options.cert_path) + r = WebRTCSimpleServer(options) loop.run_until_complete (r.run()) loop.run_forever () From b8c1bd1fa344acb6f6ca591e0687cd0cba9330a3 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Mon, 25 May 2020 18:29:53 +0000 Subject: [PATCH 123/130] simple_server: Fix init of websockets log handler This has changed since the original code was written: https://websockets.readthedocs.io/en/stable/cheatsheet.html#debugging --- webrtc/signalling/simple_server.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webrtc/signalling/simple_server.py b/webrtc/signalling/simple_server.py index 7f9123e116..9cf2e36ece 100755 --- a/webrtc/signalling/simple_server.py +++ b/webrtc/signalling/simple_server.py @@ -261,9 +261,9 @@ class WebRTCSimpleServer(object): # https://websockets.readthedocs.io/en/stable/api.html#websockets.protocol.WebSocketCommonProtocol max_queue=16) - logger = logging.getLogger('websockets.server') - - logger.setLevel(logging.ERROR) + # Setup logging + logger = logging.getLogger('websockets') + logger.setLevel(logging.INFO) logger.addHandler(logging.StreamHandler()) return wsd From 7b96b06752c349ebb3b1e93a6476b46e698adfac Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Mon, 25 May 2020 18:32:43 +0000 Subject: [PATCH 124/130] simple_server: Make the server class loop-aware First step in making the class able to manage its own state. --- webrtc/signalling/simple_server.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/webrtc/signalling/simple_server.py b/webrtc/signalling/simple_server.py index 9cf2e36ece..2e43786ed6 100755 --- a/webrtc/signalling/simple_server.py +++ b/webrtc/signalling/simple_server.py @@ -20,7 +20,7 @@ import concurrent class WebRTCSimpleServer(object): - def __init__(self, options): + def __init__(self, loop, options): ############### Global data ############### # Format: {uid: (Peer WebSocketServerProtocol, @@ -35,6 +35,11 @@ class WebRTCSimpleServer(object): # Room dict with a set of peers in each room self.rooms = dict() + # Event loop + self.loop = loop + # Websocket Server Instance + self.server = None + # Options self.addr = options.addr self.port = options.port @@ -266,7 +271,8 @@ class WebRTCSimpleServer(object): logger.setLevel(logging.INFO) logger.addHandler(logging.StreamHandler()) - return wsd + # Run the server + self.server = self.loop.run_until_complete(wsd) def main(): parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) @@ -282,11 +288,12 @@ def main(): loop = asyncio.get_event_loop() - r = WebRTCSimpleServer(options) + r = WebRTCSimpleServer(loop, options) - loop.run_until_complete (r.run()) - loop.run_forever () - print ("Goodbye!") + print('Starting server...') + r.run() + loop.run_forever() + print("Goodbye!") if __name__ == "__main__": main() From 4761396d870b329353ffb3f33d9f1bfc78c465ea Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Mon, 25 May 2020 18:33:32 +0000 Subject: [PATCH 125/130] simple_server: Abstract out ssl context generation --- webrtc/signalling/simple_server.py | 49 +++++++++++++++++------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/webrtc/signalling/simple_server.py b/webrtc/signalling/simple_server.py index 2e43786ed6..f7ee545505 100755 --- a/webrtc/signalling/simple_server.py +++ b/webrtc/signalling/simple_server.py @@ -222,28 +222,33 @@ class WebRTCSimpleServer(object): await ws.send('HELLO') return uid + def get_ssl_certs(self): + if 'letsencrypt' in self.cert_path: + chain_pem = os.path.join(self.cert_path, 'fullchain.pem') + key_pem = os.path.join(self.cert_path, 'privkey.pem') + else: + chain_pem = os.path.join(self.cert_path, 'cert.pem') + key_pem = os.path.join(self.cert_path, 'key.pem') + return chain_pem, key_pem + + def get_ssl_ctx(self): + if self.disable_ssl: + return None + # Create an SSL context to be used by the websocket server + print('Using TLS with keys in {!r}'.format(self.cert_path)) + chain_pem, key_pem = self.get_ssl_certs() + sslctx = ssl.create_default_context() + try: + sslctx.load_cert_chain(chain_pem, keyfile=key_pem) + except FileNotFoundError: + print("Certificates not found, did you run generate_cert.sh?") + sys.exit(1) + # FIXME + sslctx.check_hostname = False + sslctx.verify_mode = ssl.CERT_NONE + return sslctx + def run(self): - sslctx = None - if not self.disable_ssl: - # Create an SSL context to be used by the websocket server - print('Using TLS with keys in {!r}'.format(self.certpath)) - if 'letsencrypt' in self.certpath: - chain_pem = os.path.join(self.certpath, 'fullchain.pem') - key_pem = os.path.join(self.certpath, 'privkey.pem') - else: - chain_pem = os.path.join(self.certpath, 'cert.pem') - key_pem = os.path.join(self.certpath, 'key.pem') - - sslctx = ssl.create_default_context() - try: - sslctx.load_cert_chain(chain_pem, keyfile=key_pem) - except FileNotFoundError: - print("Certificates not found, did you run generate_cert.sh?") - sys.exit(1) - # FIXME - sslctx.check_hostname = False - sslctx.verify_mode = ssl.CERT_NONE - async def handler(ws, path): ''' All incoming messages are handled here. @path is unused. @@ -258,6 +263,8 @@ class WebRTCSimpleServer(object): finally: await self.remove_peer(peer_id) + sslctx = self.get_ssl_ctx() + print("Listening on https://{}:{}".format(self.addr, self.port)) # Websocket server wsd = websockets.serve(handler, self.addr, self.port, ssl=sslctx, process_request=self.health_check if self.health_path else None, From 77ae10ab663bda858c03a5becd452a269c70ca19 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Mon, 25 May 2020 18:34:11 +0000 Subject: [PATCH 126/130] simple_server: Restart when the certificate changes Reload the SSL context and restart the server if the certificate changes. Without this, new connections will continue to use the old expired certificate. --- webrtc/signalling/simple_server.py | 43 ++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/webrtc/signalling/simple_server.py b/webrtc/signalling/simple_server.py index f7ee545505..12153a0f85 100755 --- a/webrtc/signalling/simple_server.py +++ b/webrtc/signalling/simple_server.py @@ -44,10 +44,14 @@ class WebRTCSimpleServer(object): self.addr = options.addr self.port = options.port self.keepalive_timeout = options.keepalive_timeout + self.cert_restart = options.cert_restart self.cert_path = options.cert_path self.disable_ssl = options.disable_ssl self.health_path = options.health + # Certificate mtime, used to detect when to restart the server + self.cert_mtime = -1 + ############### Helper functions ############### async def health_check(self, path, request_headers): @@ -280,6 +284,38 @@ class WebRTCSimpleServer(object): # Run the server self.server = self.loop.run_until_complete(wsd) + # Stop the server if certificate changes + self.loop.run_until_complete(self.check_server_needs_restart()) + + async def stop(self): + print('Stopping server... ', end='') + self.server.close() + await self.server.wait_closed() + self.loop.stop() + print('Stopped.') + + def check_cert_changed(self): + chain_pem, key_pem = self.get_ssl_certs() + mtime = max(os.stat(key_pem).st_mtime, os.stat(chain_pem).st_mtime) + if self.cert_mtime < 0: + self.cert_mtime = mtime + return False + if mtime > self.cert_mtime: + self.cert_mtime = mtime + return True + return False + + async def check_server_needs_restart(self): + "When the certificate changes, we need to restart the server" + if not self.cert_restart: + return + while True: + await asyncio.sleep(10) + if self.check_cert_changed(): + print('Certificate changed, stopping server...') + await self.stop() + return + def main(): parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) @@ -290,6 +326,7 @@ def main(): parser.add_argument('--cert-path', default=os.path.dirname(__file__)) parser.add_argument('--disable-ssl', default=False, help='Disable ssl', action='store_true') parser.add_argument('--health', default='/health', help='Health check route') + parser.add_argument('--restart-on-cert-change', default=False, dest='cert_restart', action='store_true', help='Automatically restart if the SSL certificate changes') options = parser.parse_args(sys.argv[1:]) @@ -298,8 +335,10 @@ def main(): r = WebRTCSimpleServer(loop, options) print('Starting server...') - r.run() - loop.run_forever() + while True: + r.run() + loop.run_forever() + print('Restarting server...') print("Goodbye!") if __name__ == "__main__": From 0776def18cf80317bb6864d69b3cfdc88bd723c8 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Mon, 25 May 2020 18:39:16 +0000 Subject: [PATCH 127/130] simple_server: asyncio TimeoutError has moved We didn't notice this because the logging was broken. --- webrtc/signalling/simple_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webrtc/signalling/simple_server.py b/webrtc/signalling/simple_server.py index 12153a0f85..0cae633211 100755 --- a/webrtc/signalling/simple_server.py +++ b/webrtc/signalling/simple_server.py @@ -68,7 +68,7 @@ class WebRTCSimpleServer(object): while msg is None: try: msg = await asyncio.wait_for(ws.recv(), self.keepalive_timeout) - except (asyncio.exceptions.TimeoutError, concurrent.futures._base.TimeoutError): + except (asyncio.TimeoutError, concurrent.futures._base.TimeoutError): print('Sending keepalive ping to {!r} in recv'.format(raddr)) await ws.ping() return msg From 17f84bfd81ada54d77509f0fe24a4587331308c4 Mon Sep 17 00:00:00 2001 From: Corey Cole Date: Fri, 5 Jun 2020 16:19:12 -0700 Subject: [PATCH 128/130] fix: python webrtc_sendrecv.py typo --- webrtc/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webrtc/README.md b/webrtc/README.md index 09000ba0f3..59bec73646 100644 --- a/webrtc/README.md +++ b/webrtc/README.md @@ -78,7 +78,7 @@ $ gcc webrtc-sendrecv.c $(pkg-config --cflags --libs gstreamer-webrtc-1.0 gstrea #### Running the Python version * python3 -m pip install --user websockets -* run `python3 sendrecv/gst/webrtc-sendrecv.py ID` with the `id` from the browser. You will see state changes and an SDP exchange. +* run `python3 sendrecv/gst/webrtc_sendrecv.py ID` with the `id` from the browser. You will see state changes and an SDP exchange. > The python version requires at least version 1.14.2 of gstreamer and its plugins. From 751d06af6fc2c5a3570138ebb7c4f0b56a0af7ab Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Tue, 16 Jun 2020 12:50:21 +0530 Subject: [PATCH 129/130] signalling: Fix simple-server script name in Dockerfile Fixes https://github.com/centricular/gstwebrtc-demos/issues/202 --- webrtc/signalling/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webrtc/signalling/Dockerfile b/webrtc/signalling/Dockerfile index 5902f68ae8..4d0b655890 100644 --- a/webrtc/signalling/Dockerfile +++ b/webrtc/signalling/Dockerfile @@ -5,4 +5,4 @@ RUN pip3 install --user websockets WORKDIR /opt/ COPY . /opt/ -CMD python -u ./simple-server.py --disable-ssl +CMD python -u ./simple_server.py --disable-ssl From d44b2316fa2128e082dbe4b583919cceb8c3197f Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Mon, 22 Jun 2020 17:39:12 +0530 Subject: [PATCH 130/130] sendonly: Don't assume we're building on UNIX Fixes https://github.com/centricular/gstwebrtc-demos/issues/203 --- webrtc/sendonly/webrtc-recvonly-h264.c | 13 ++++++++----- webrtc/sendonly/webrtc-unidirectional-h264.c | 13 ++++++++----- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/webrtc/sendonly/webrtc-recvonly-h264.c b/webrtc/sendonly/webrtc-recvonly-h264.c index 9ba7fe4926..87b24f2f26 100644 --- a/webrtc/sendonly/webrtc-recvonly-h264.c +++ b/webrtc/sendonly/webrtc-recvonly-h264.c @@ -1,9 +1,12 @@ #include #include -#include #include #include +#ifdef G_OS_UNIX +#include +#endif + #define GST_USE_UNSTABLE_API #include @@ -50,8 +53,6 @@ void soup_websocket_handler (G_GNUC_UNUSED SoupServer * server, static gchar *get_string_from_json_object (JsonObject * object); -gboolean exit_sighandler (gpointer user_data); - @@ -649,7 +650,7 @@ get_string_from_json_object (JsonObject * object) return text; } - +#ifdef G_OS_UNIX gboolean exit_sighandler (gpointer user_data) { @@ -658,7 +659,7 @@ exit_sighandler (gpointer user_data) g_main_loop_quit (mainloop); return TRUE; } - +#endif int main (int argc, char *argv[]) @@ -677,8 +678,10 @@ main (int argc, char *argv[]) mainloop = g_main_loop_new (NULL, FALSE); g_assert (mainloop != NULL); +#ifdef G_OS_UNIX g_unix_signal_add (SIGINT, exit_sighandler, mainloop); g_unix_signal_add (SIGTERM, exit_sighandler, mainloop); +#endif soup_server = soup_server_new (SOUP_SERVER_SERVER_HEADER, "webrtc-soup-server", NULL); diff --git a/webrtc/sendonly/webrtc-unidirectional-h264.c b/webrtc/sendonly/webrtc-unidirectional-h264.c index 4887c7eed2..b8060145fe 100644 --- a/webrtc/sendonly/webrtc-unidirectional-h264.c +++ b/webrtc/sendonly/webrtc-unidirectional-h264.c @@ -1,9 +1,12 @@ #include #include -#include #include #include +#ifdef G_OS_UNIX +#include +#endif + #define GST_USE_UNSTABLE_API #include @@ -42,8 +45,6 @@ void soup_websocket_handler (G_GNUC_UNUSED SoupServer * server, static gchar *get_string_from_json_object (JsonObject * object); -gboolean exit_sighandler (gpointer user_data); - struct _ReceiverEntry { SoupWebsocketConnection *connection; @@ -531,7 +532,7 @@ get_string_from_json_object (JsonObject * object) return text; } - +#ifdef G_OS_UNIX gboolean exit_sighandler (gpointer user_data) { @@ -540,7 +541,7 @@ exit_sighandler (gpointer user_data) g_main_loop_quit (mainloop); return TRUE; } - +#endif int main (int argc, char *argv[]) @@ -559,8 +560,10 @@ main (int argc, char *argv[]) mainloop = g_main_loop_new (NULL, FALSE); g_assert (mainloop != NULL); +#ifdef G_OS_UNIX g_unix_signal_add (SIGINT, exit_sighandler, mainloop); g_unix_signal_add (SIGTERM, exit_sighandler, mainloop); +#endif soup_server = soup_server_new (SOUP_SERVER_SERVER_HEADER, "webrtc-soup-server", NULL);