gst-plugin-webrtc 0.13.4

GStreamer plugin for high level WebRTC elements and a simple signaling server
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
# webrtcsink and webrtcsrc

All-batteries included GStreamer WebRTC producer and consumer, that try their
best to do The Right Thing™.

It also provides a flexible and all-purposes WebRTC signalling server
([gst-webrtc-signalling-server](signalling/src/bin/server.rs)) and a Javascript
API ([gstwebrtc-api](gstwebrtc-api)) to produce and consume compatible WebRTC
streams from a web browser.

## Use case

The [webrtcbin] element in GStreamer is extremely flexible and powerful, but
using it can be a difficult exercise. When all you want to do is serve a fixed
set of streams to any number of consumers, `webrtcsink` (which wraps
`webrtcbin` internally) can be a useful alternative.

[webrtcbin]: https://gstreamer.freedesktop.org/documentation/webrtc/index.html

## Features

`webrtcsink` implements the following features:

* Built-in signaller: when using the default signalling server, this element
  will perform signalling without requiring application interaction.
  This makes it usable directly from `gst-launch`.

* Application-provided signalling: `webrtcsink` can be instantiated by an
  application with a custom signaller. That signaller must be a GObject, and
  must implement the `Signallable` interface as defined
  [here]src/signaller/mod.rs. The [default signaller]src/signaller/imp.rs
  can be used as an example.

  An [example]examples/webrtcsink-custom-signaller/README.md is also available to use as a boilerplate for
  implementing and using a custom signaller.

* Sandboxed consumers: when a consumer is added, its encoder / payloader /
  webrtcbin elements run in a separately managed pipeline. This provides a
  certain level of sandboxing, as opposed to having those elements running
  inside the element itself.

  It is important to note that at this moment, encoding is not shared between
  consumers. While this is not on the roadmap at the moment, nothing in the
  design prevents implementing this optimization.

* Congestion control: the element leverages transport-wide congestion control
  feedback messages in order to adapt the bitrate of individual consumers' video
  encoders to the available bandwidth.

* Configuration: the level of user control over the element is slowly expanding,
  consult `gst-inspect-1.0` for more information on the available properties and
  signals.

* Packet loss mitigation: webrtcsink now supports sending protection packets for
  Forward Error Correction, modulating the amount as a function of the available
  bandwidth, and can honor retransmission requests. Both features can be
  disabled via properties.

It is important to note that full control over the individual elements used by
`webrtcsink` is *not* on the roadmap, as it will act as a black box in that
respect, for example `webrtcsink` wants to reserve control over the bitrate for
congestion control.

A signal is now available however for the application to provide the initial
configuration for the encoders `webrtcsink` instantiates.

If more granular control is required, applications should use `webrtcbin`
directly, `webrtcsink` will focus on trying to just do the right thing, although
it might expose more interfaces to guide and tune the heuristics it employs.

[example project]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/tree/main/net/webrtc/examples/webrtcsink-custom-signaller

## Building

> Make sure to install the development packages for some codec libraries
> beforehand, such as libx264, libvpx and libopusenc, exact names depend
> on your distribution.

``` shell
cargo build
npm --prefix gstwebrtc-api/ run build
```

## Usage

Open three terminals. In the first one, run the signalling server:

``` shell
cd signalling
WEBRTCSINK_SIGNALLING_SERVER_LOG=debug cargo run --bin gst-webrtc-signalling-server
```

In the second one, run a web browser client (can produce and consume streams):

``` shell
cd gstwebrtc-api
npm install
npm start
```

In the third one, run a webrtcsink producer from a GStreamer pipeline:

``` shell
export GST_PLUGIN_PATH=<path-to-gst-plugins-rs>/target/debug:$GST_PLUGIN_PATH
gst-launch-1.0 webrtcsink name=ws meta="meta,name=gst-stream" videotestsrc ! ws. audiotestsrc ! ws.
```

The webrtcsink produced stream will appear in the former web page
(automatically opened at https://localhost:9090) under the name "gst-stream",
if you click on it you should see a test video stream and hear a test tone.

You can also produce WebRTC streams from the web browser and consume them with
a GStreamer pipeline. Click on the "Start Capture" button and copy the
"Client ID" value.

Then open a new terminal and run:

``` shell
export GST_PLUGIN_PATH=<path-to-gst-plugins-rs>/target/debug:$GST_PLUGIN_PATH
gst-launch-1.0 playbin uri=gstwebrtc://127.0.0.1:8443?peer-id=[Client ID]
```

Replacing the "peer-id" value with the previously copied "Client ID" value. You
should see the playbin element opening a window and showing you the content
produced by the web page.

## Configuration

The webrtcsink element itself can be configured through its properties, see
`gst-inspect-1.0 webrtcsink` for more information about that, in addition the
default signaller also exposes properties for configuring it, in
particular setting the signalling server address, those properties
can be accessed through the `gst::ChildProxy` interface, for example
with gst-launch:

``` shell
gst-launch-1.0 webrtcsink signaller::uri="ws://127.0.0.1:8443" ..
```

### Enable 'navigation' a.k.a user interactivity with the content

`webrtcsink` implements the [`GstNavigation`] interface which allows interacting
with the content, for example move with your mouse, entering keys with the
keyboard, etc... On top of that a `WebRTCDataChannel` based protocol has been
implemented and can be activated with the `enable-data-channel-navigation=true`
property allowing a client to send GstNavigation events using the WebRTC data channel.

The [gstwebrtc-api](gstwebrtc-api) and `webrtcsrc` implement the protocol as well
and they can be used as a client to control a remote sever.

You can easily test this feature using the [`wpesrc`] element with the following pipeline
that will start a server that allows you to navigate the GStreamer documentation:

``` shell
gst-launch-1.0 wpesrc location=https://gstreamer.freedesktop.org/documentation/ ! queue ! webrtcsink enable-data-channel-navigation=true meta="meta,name=web-stream"
```

You can control it inside the video running within your web browser (at
https://127.0.0.1:9090 if you followed previous steps in that readme) or
with the following GSteamer pipeline as a client:

``` shell
gst-launch-1.0 webrtcsrc signaller::producer-peer-id=<webrtcsink-peer-id> enable-data-channel-navigation=true ! videoconvert ! autovideosink
```

### Sending HTTP headers

During the initial signalling server handshake, you have the option to transmit 
HTTP headers, which can be utilized, for instance, for authentication purposes or sticky sessions:

``` shell
gst-launch-1.0 webrtcsink signaller::uri="ws://127.0.0.1:8443" signaller::headers="headers,foo=bar,cookie=\"session=1234567890; foo=bar\""
```

[`GstNavigation`]: https://gstreamer.freedesktop.org/documentation/video/gstnavigation.html
[`wpesrc`]: https://gstreamer.freedesktop.org/documentation/wpe/wpesrc.html

## Testing congestion control

For the purpose of testing congestion in a reproducible manner, a
[simple tool] has been used, it has been used on Linux exclusively but it is
also documented as usable on MacOS too. Client web browser has to be launched
on a separate machine on the LAN to test for congestion, although specific
configurations may allow to run it on the same machine.

Testing procedure was:

* identify the server machine network interface (e.g. with `ifconfig` on Linux)

* identify the client machine IP address (e.g. with `ifconfig` on Linux)

* start the various services as explained in the Usage section (use
  `GST_DEBUG=webrtcsink:7` to get detailed logs about congestion control)

* start playback in the client browser

* Run a `comcast` command on the server machine, for instance:

  ``` shell
  $HOME/go/bin/comcast --device=$SERVER_INTERFACE --target-bw 3000 --target-addr=$CLIENT_IP --target-port=1:65535 --target-proto=udp
  ```

* Observe the bitrate sharply decreasing, playback should slow down briefly
  then catch back up

* Remove the bandwidth limitation, and observe the bitrate eventually increasing
  back to a maximum:

  ``` shell
  $HOME/go/bin/comcast --device=$SERVER_INTERFACE --stop
  ```

For comparison, the congestion control property can be set to "disabled" on
webrtcsink, then the above procedure applied again, the expected result is
for playback to simply crawl down to a halt until the bandwidth limitation
is lifted:

``` shell
gst-launch-1.0 webrtcsink congestion-control=disabled
```

[simple tool]: https://github.com/tylertreat/comcast

## Monitoring tool

An example of client/server application for monitoring per-consumer stats
can be found [here].

[here]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/tree/main/net/webrtc/examples

## License

All the rust code in this repository is licensed under the
[Mozilla Public License Version 2.0].

Code in [gstwebrtc-api](gstwebrtc-api) is also licensed under the
[Mozilla Public License Version 2.0].

[Mozilla Public License Version 2.0]: http://opensource.org/licenses/MPL-2.0

## Using the AWS KVS signaller

* Setup AWS Kinesis Video Streams

* Create a channel from the AWS console (<https://us-east-1.console.aws.amazon.com/kinesisvideo/home?region=us-east-1#/signalingChannels/create>)

* Start a producer:

```
AWS_ACCESS_KEY_ID="XXX" AWS_SECRET_ACCESS_KEY="XXX" gst-launch-1.0 videotestsrc pattern=ball ! video/x-raw, width=1280, height=720 ! videoconvert ! textoverlay text="Hello from GStreamer!" ! videoconvert ! awskvswebrtcsink name=ws signaller::channel-name="XXX"
```

* Connect a viewer @ <https://awslabs.github.io/amazon-kinesis-video-streams-webrtc-sdk-js/examples/index.html>

## Using the WHIP Signaller

### WHIP Client

WHIP Client Signaller uses BaseWebRTCSink

Testing the whip client as the signaller can be done by setting up janus and
<https://github.com/meetecho/simple-whip-server/>.

* Set up a [janus] instance with the videoroom plugin configured
  to expose a room with ID 1234 (configuration in `janus.plugin.videoroom.jcfg`)

* Open the <janus/share/janus/demos/videoroomtest.html> web page, click start
  and join the room

* Set up the [simple whip server] as explained in its README

* Navigate to <http://localhost:7080/>, create an endpoint named room1234
  pointing to the Janus room with ID 1234

* Finally, send a stream to the endpoint with:

``` shell
gst-launch-1.0 -e uridecodebin uri=file:///home/meh/path/to/video/file ! \
  videoconvert ! video/x-raw ! queue ! \
  whipclientsink name=ws signaller::whip-endpoint="http://127.0.0.1:7080/whip/endpoint/room1234"
```

You should see a second video displayed in the videoroomtest web page.

### WHIP Server

WHIP Server Signaller uses BaseWebRTCSrc

The WHIP Server as the signaller can be tested in two ways.

Note: The initial version of `whipserversrc` does not check any auth or encryption.
Host application using `whipserversrc` behind an HTTP(s) proxy to enforce the auth and encryption between the WHIP client and server

#### 1. Using  the GStreamer element `whipclientsink`

a. In one tab of the terminal start the WHIP server using the below command

``` shell
RUST_BACKTRACE=full GST_DEBUG=webrtc*:6 GST_PLUGIN_PATH=target/x86_64-unknown-linux-gnu/debug:$GST_PLUGIN_PATH gst-launch-1.0 whipserversrc signaller::host-addr=http://127.0.0.1:8190 stun-server="stun://stun.l.google.com:19302" turn-servers="\<\"turns://user1:pass1@turn.serverone.com:7806\", \"turn://user2:pass2@turn.servertwo.com:7809\"\>" ! videoconvert ! autovideosink
```

b. In the second tab start the WHIP Client by sending a test video as shown in the below command

``` shell
RUST_BACKTRACE=full GST_DEBUG=webrtc*:6 GST_PLUGIN_PATH=target/x86_64-unknown-linux-gnu/debug:$GST_PLUGIN_PATH gst-launch-1.0 videotestsrc ! videoconvert ! video/x-raw ! queue ! \
  whipclientsink name=ws signaller::whip-endpoint="http://127.0.0.1:8190/whip/endpoint"
```

#### 2. Using Meetecho's `simple-whip-client`

Set up the simple whip client using using the instructions present in https://github.com/meetecho/simple-whip-client#readme

a. In one tab of the terminal start the WHIP server using the below command

``` shell
RUST_BACKTRACE=full GST_DEBUG=webrtc*:6 GST_PLUGIN_PATH=target/x86_64-unknown-linux-gnu/debug:$GST_PLUGIN_PATH gst-launch-1.0 whipserversrc signaller::host-addr=http://127.0.0.1:8190 stun-server="stun://stun.l.google.com:19302" turn-servers="\<\"turns://user1:pass1@turn.serverone.com:7806\", \"turn://user2:pass2@turn.servertwo.com:7809\"\>" name=ws ! videoconvert ! autovideosink ws. ! audioconvert ! autoaudiosink
```

b. In the second tab start the `simple-whip-client` as shown in the below command

``` shell
./whip-client --url http://127.0.0.1:8190/whip/endpoint \
        -A "audiotestsrc is-live=true wave=red-noise ! audioconvert ! audioresample ! queue ! opusenc perfect-timestamp=true ! rtpopuspay pt=100 ssrc=1 ! queue ! application/x-rtp,media=audio,encoding-name=OPUS,payload=100" \
        -V "videotestsrc is-live=true pattern=ball ! videoconvert ! queue ! vp8enc deadline=1 ! rtpvp8pay pt=96 ssrc=2 ! queue ! application/x-rtp,media=video,encoding-name=VP8,payload=96" \
        -S stun://stun.l.google.com:19302 \
        -l 7 \
        -n true
```

Terminating the client will close the session and the client should receive 200 (OK) as the response to the DELETE request

## Using the LiveKit Signaller

Testing the LiveKit signaller can be done by setting up [LiveKit] and creating a room.

You can connect either by given the API key and secret:

``` shell
gst-launch-1.0 -e uridecodebin uri=file:///home/meh/path/to/video/file ! \
  videoconvert ! video/x-raw ! queue ! \
  livekitwebrtcsink signaller::ws-url=ws://127.0.0.1:7880 signaller::api-key=devkey signaller::secret-key=secret signaller::room-name=testroom
```

Or by using a separately created authentication token
``` shell
gst-launch-1.0 -e uridecodebin uri=file:///home/meh/path/to/video/file ! \
  videoconvert ! video/x-raw ! queue ! \
  livekitwebrtcsink signaller::ws-url=ws://127.0.0.1:7880 signaller::auth-token=mygeneratedtoken signaller::room-name=testroom
```


You should see a second video displayed in the videoroomtest web page.

## Streaming from LiveKit using the livekitwebrtcsrc element

First, publish a stream to the room using the following command:

```shell
gst-launch-1.0 livekitwebrtcsink name=sink \
    signaller::ws-url=ws://127.0.0.1:7880 \
    signaller::api-key=devkey \
    signaller::secret-key=secret \
    signaller::room-name=testroom \
    signaller::identity=gst-producer \
    signaller::participant-name=gst-producer \
    video-caps='video/x-h264' \
  videotestsrc is-live=1 \
  ! video/x-raw,width=640,height=360,framerate=15/1 \
  ! timeoverlay ! videoconvert ! queue ! sink.
```

Then play back the published stream:

```shell
gst-launch-1.0 livekitwebrtcsrc \
    name=src \
    signaller::ws-url=ws://127.0.0.1:7880 \
    signaller::api-key=devkey \
    signaller::secret-key=secret \
    signaller::room-name=testroom \
    signaller::identity=gst-consumer \
    signaller::participant-name=gst-consumer \
    signaller::producer-peer-id=gst-producer \
    video-codecs='<H264>' \
  src. ! queue ! videoconvert ! autovideosink
```

### Auto-subscribe with livekitwebrtcsrc element

With the LiveKit source element, you can also subscribe to all the peers in
your room, simply by not specifying any value for
`signaller::producer-peer-id`. Unwanted peers can also be ignored by supplying
an array of peer IDs to `signaller::excluded-producer-peer-ids`. Importantly,
it is also necessary to add sinks for all the streams in the room that the
source element has subscribed to.

First, publish a few streams using different connections:

```shell
gst-launch-1.0 \
  livekitwebrtcsink name=sinka \
    signaller::ws-url=ws://127.0.0.1:7880 \
    signaller::api-key=devkey \
    signaller::secret-key=secret \
    signaller::room-name=testroom \
    signaller::identity=gst-producer-a \
    signaller::participant-name=gst-producer-a \
    video-caps='video/x-vp8' \
  livekitwebrtcsink name=sinkb \
    signaller::ws-url=ws://127.0.0.1:7880 \
    signaller::api-key=devkey \
    signaller::secret-key=secret \
    signaller::room-name=testroom \
    signaller::identity=gst-producer-b \
    signaller::participant-name=gst-producer-b \
    video-caps='video/x-vp8' \
  livekitwebrtcsink name=sinkc \
    signaller::ws-url=ws://127.0.0.1:7880 \
    signaller::api-key=devkey \
    signaller::secret-key=secret \
    signaller::room-name=testroom \
    signaller::identity=gst-producer-c \
    signaller::participant-name=gst-producer-c \
    video-caps='video/x-vp8' \
  videotestsrc is-live=1 \
  ! video/x-raw,width=640,height=360,framerate=15/1 \
  ! timeoverlay ! videoconvert ! queue ! sinka. \
  videotestsrc pattern=ball is-live=1 \
  ! video/x-raw,width=320,height=180,framerate=15/1 \
  ! timeoverlay ! videoconvert ! queue ! sinkb.
  videotestsrc is-live=1 \
  ! video/x-raw,width=320,height=180,framerate=15/1 \
  ! timeoverlay ! videoconvert ! queue ! sinkc.
```

Then watch only streams A and B by excluding peer C:

```shell
gst-launch-1.0 livekitwebrtcsrc \
  name=src \
  signaller::ws-url=ws://127.0.0.1:7880 \
  signaller::api-key=devkey \
  signaller::secret-key=secret \
  signaller::room-name=testroom \
  signaller::identity=gst-consumer \
  signaller::participant-name=gst-consumer \
  signaller::excluded-producer-peer-ids='<gst-producer-c>' \
  src. ! queue ! videoconvert ! autovideosink
  src. ! queue ! videoconvert ! autovideosink
```

[LiveKit]: https://livekit.io/
[janus]: https://github.com/meetecho/janus-gateway
[simple whip server]: https://github.com/meetecho/simple-whip-server/